最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器
=====================================================
最简单的基于FFmpeg的视频播放器系列文章列表:
100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)
最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)
最简单的基于FFmpeg的解码器-纯净版(不包含libavformat)
最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器
=====================================================
本文补充记录《最简单的基于FFMPEG+SDL的视频播放器》中的两个例子:FFmpeg视频解码器和SDL像素数据播放器。这两个部分是从视频播放器中拆分出来的两个例子。FFmpeg视频解码器实现了视频数据到YUV数据的解码,而SDL像素数据播放器实现了YUV数据的显示。简而言之,原先的FFmpeg+SDL视频播放器实现了:
视频数据->YUV->显示器
FFmpeg视频解码器实现了:
视频数据->YUV
SDL像素数据播放器实现了:
FFmpeg视频解码器
源代码
- /**
 - * 最简单的基于FFmpeg的视频解码器
 - * Simplest FFmpeg Decoder
 - *
 - * 雷霄骅 Lei Xiaohua
 - * leixiaohua1020@126.com
 - * 中国传媒大学/数字电视技术
 - * Communication University of China / Digital TV Technology
 - * http://blog.csdn.net/leixiaohua1020
 - *
 - *
 - * 本程序实现了视频文件解码为YUV数据。它使用了libavcodec和
 - * libavformat。是最简单的FFmpeg视频解码方面的教程。
 - * 通过学习本例子可以了解FFmpeg的解码流程。
 - * This software is a simplest decoder based on FFmpeg.
 - * It decodes video to YUV pixel data.
 - * It uses libavcodec and libavformat.
 - * Suitable for beginner of FFmpeg.
 - *
 - */
 - #include <stdio.h>
 - #define __STDC_CONSTANT_MACROS
 - #ifdef _WIN32
 - //Windows
 - extern "C"
 - {
 - #include "libavcodec/avcodec.h"
 - #include "libavformat/avformat.h"
 - #include "libswscale/swscale.h"
 - #include "libavutil/imgutils.h"
 - };
 - #else
 - //Linux...
 - #ifdef __cplusplus
 - extern "C"
 - {
 - #endif
 - #include <libavcodec/avcodec.h>
 - #include <libavformat/avformat.h>
 - #include <libswscale/swscale.h>
 - #include <libavutil/imgutils.h>
 - #ifdef __cplusplus
 - };
 - #endif
 - #endif
 - int main(int argc, char* argv[])
 - {
 - AVFormatContext *pFormatCtx;
 - int i, videoindex;
 - AVCodecContext *pCodecCtx;
 - AVCodec *pCodec;
 - AVFrame *pFrame,*pFrameYUV;
 - unsigned char *out_buffer;
 - AVPacket *packet;
 - int y_size;
 - int ret, got_picture;
 - struct SwsContext *img_convert_ctx;
 - char filepath[]="Titanic.mkv";
 - FILE *fp_yuv=fopen("output.yuv","wb+");
 - av_register_all();
 - avformat_network_init();
 - pFormatCtx = avformat_alloc_context();
 - if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){
 - printf("Couldn't open input stream.\n");
 - return -1;
 - }
 - if(avformat_find_stream_info(pFormatCtx,NULL)<0){
 - printf("Couldn't find stream information.\n");
 - return -1;
 - }
 - videoindex=-1;
 - for(i=0; i<pFormatCtx->nb_streams; i++)
 - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
 - videoindex=i;
 - break;
 - }
 - if(videoindex==-1){
 - printf("Didn't find a video stream.\n");
 - return -1;
 - }
 - pCodecCtx=pFormatCtx->streams[videoindex]->codec;
 - pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
 - if(pCodec==NULL){
 - printf("Codec not found.\n");
 - return -1;
 - }
 - if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){
 - printf("Could not open codec.\n");
 - return -1;
 - }
 - pFrame=av_frame_alloc();
 - pFrameYUV=av_frame_alloc();
 - out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height,1));
 - av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,
 - AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);
 - packet=(AVPacket *)av_malloc(sizeof(AVPacket));
 - //Output Info-----------------------------
 - printf("--------------- File Information ----------------\n");
 - av_dump_format(pFormatCtx,0,filepath,0);
 - printf("-------------------------------------------------\n");
 - img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
 - pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
 - while(av_read_frame(pFormatCtx, packet)>=0){
 - if(packet->stream_index==videoindex){
 - ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
 - if(ret < 0){
 - printf("Decode Error.\n");
 - return -1;
 - }
 - if(got_picture){
 - sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
 - pFrameYUV->data, pFrameYUV->linesize);
 - y_size=pCodecCtx->width*pCodecCtx->height;
 - fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y
 - fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U
 - fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V
 - printf("Succeed to decode 1 frame!\n");
 - }
 - }
 - av_free_packet(packet);
 - }
 - //flush decoder
 - //FIX: Flush Frames remained in Codec
 - while (1) {
 - ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
 - if (ret < 0)
 - break;
 - if (!got_picture)
 - break;
 - sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
 - pFrameYUV->data, pFrameYUV->linesize);
 - int y_size=pCodecCtx->width*pCodecCtx->height;
 - fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y
 - fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U
 - fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V
 - printf("Flush Decoder: Succeed to decode 1 frame!\n");
 - }
 - sws_freeContext(img_convert_ctx);
 - fclose(fp_yuv);
 - av_frame_free(&pFrameYUV);
 - av_frame_free(&pFrame);
 - avcodec_close(pCodecCtx);
 - avformat_close_input(&pFormatCtx);
 - return 0;
 - }
 
运行结果
程序运行后,会解码下面的视频文件。
解码后的YUV420P数据被保存成了一个文件。使用YUV播放器设置宽高之后可以查看YUV内容。
SDL像素数据播放器
源代码
- /**
 - * 最简单的SDL2播放视频的例子(SDL2播放RGB/YUV)
 - * Simplest Video Play SDL2 (SDL2 play RGB/YUV)
 - *
 - * 雷霄骅 Lei Xiaohua
 - * leixiaohua1020@126.com
 - * 中国传媒大学/数字电视技术
 - * Communication University of China / Digital TV Technology
 - * http://blog.csdn.net/leixiaohua1020
 - *
 - * 本程序使用SDL2播放RGB/YUV视频像素数据。SDL实际上是对底层绘图
 - * API(Direct3D,OpenGL)的封装,使用起来明显简单于直接调用底层
 - * API。
 - *
 - * 函数调用步骤如下:
 - *
 - * [初始化]
 - * SDL_Init(): 初始化SDL。
 - * SDL_CreateWindow(): 创建窗口(Window)。
 - * SDL_CreateRenderer(): 基于窗口创建渲染器(Render)。
 - * SDL_CreateTexture(): 创建纹理(Texture)。
 - *
 - * [循环渲染数据]
 - * SDL_UpdateTexture(): 设置纹理的数据。
 - * SDL_RenderCopy(): 纹理复制给渲染器。
 - * SDL_RenderPresent(): 显示。
 - *
 - * This software plays RGB/YUV raw video data using SDL2.
 - * SDL is a wrapper of low-level API (Direct3D, OpenGL).
 - * Use SDL is much easier than directly call these low-level API.
 - *
 - * The process is shown as follows:
 - *
 - * [Init]
 - * SDL_Init(): Init SDL.
 - * SDL_CreateWindow(): Create a Window.
 - * SDL_CreateRenderer(): Create a Render.
 - * SDL_CreateTexture(): Create a Texture.
 - *
 - * [Loop to Render data]
 - * SDL_UpdateTexture(): Set Texture's data.
 - * SDL_RenderCopy(): Copy Texture to Render.
 - * SDL_RenderPresent(): Show.
 - */
 - #include <stdio.h>
 - extern "C"
 - {
 - #include "sdl/SDL.h"
 - };
 - const int bpp=12;
 - int screen_w=500,screen_h=500;
 - const int pixel_w=320,pixel_h=180;
 - unsigned char buffer[pixel_w*pixel_h*bpp/8];
 - //Refresh Event
 - #define REFRESH_EVENT (SDL_USEREVENT + 1)
 - #define BREAK_EVENT (SDL_USEREVENT + 2)
 - int thread_exit=0;
 - int refresh_video(void *opaque){
 - thread_exit=0;
 - while (!thread_exit) {
 - SDL_Event event;
 - event.type = REFRESH_EVENT;
 - SDL_PushEvent(&event);
 - SDL_Delay(40);
 - }
 - thread_exit=0;
 - //Break
 - SDL_Event event;
 - event.type = BREAK_EVENT;
 - SDL_PushEvent(&event);
 - return 0;
 - }
 - int main(int argc, char* argv[])
 - {
 - if(SDL_Init(SDL_INIT_VIDEO)) {
 - printf( "Could not initialize SDL - %s\n", SDL_GetError());
 - return -1;
 - }
 - SDL_Window *screen;
 - //SDL 2.0 Support for multiple windows
 - screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
 - screen_w, screen_h,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
 - if(!screen) {
 - printf("SDL: could not create window - exiting:%s\n",SDL_GetError());
 - return -1;
 - }
 - SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
 - Uint32 pixformat=0;
 - //IYUV: Y + U + V (3 planes)
 - //YV12: Y + V + U (3 planes)
 - pixformat= SDL_PIXELFORMAT_IYUV;
 - SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer,pixformat, SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h);
 - FILE *fp=NULL;
 - fp=fopen("test_yuv420p_320x180.yuv","rb+");
 - if(fp==NULL){
 - printf("cannot open this file\n");
 - return -1;
 - }
 - SDL_Rect sdlRect;
 - SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video,NULL,NULL);
 - SDL_Event event;
 - while(1){
 - //Wait
 - SDL_WaitEvent(&event);
 - if(event.type==REFRESH_EVENT){
 - if (fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp) != pixel_w*pixel_h*bpp/8){
 - // Loop
 - fseek(fp, 0, SEEK_SET);
 - fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp);
 - }
 - SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w);
 - //FIX: If window is resize
 - sdlRect.x = 0;
 - sdlRect.y = 0;
 - sdlRect.w = screen_w;
 - sdlRect.h = screen_h;
 - SDL_RenderClear( sdlRenderer );
 - SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect);
 - SDL_RenderPresent( sdlRenderer );
 - }else if(event.type==SDL_WINDOWEVENT){
 - //If Resize
 - SDL_GetWindowSize(screen,&screen_w,&screen_h);
 - }else if(event.type==SDL_QUIT){
 - thread_exit=1;
 - }else if(event.type==BREAK_EVENT){
 - break;
 - }
 - }
 - SDL_Quit();
 - return 0;
 - }
 
运行结果
程序运行后,会读取程序文件夹下的一个YUV420P文件,内容如下所示。
接下来会将YUV内容绘制在弹出的窗口中。
下载
Simplest FFmpeg Player
项目主页
SourceForge:https://sourceforge.net/projects/simplestffmpegplayer/
Github:https://github.com/leixiaohua1020/simplest_ffmpeg_player
开源中国:http://git.oschina.net/leixiaohua1020/simplest_ffmpeg_player
CSDN下载地址:http://download.csdn.net/detail/leixiaohua1020/8924321
本程序实现了视频文件的解码和显示(支持HEVC,H.264,MPEG2等)。
是最简单的FFmpeg视频解码方面的教程。
通过学习本例子可以了解FFmpeg的解码流程。
项目包含6个工程:
simplest_ffmpeg_player:标准版,FFmpeg学习的开始。
simplest_ffmpeg_player_su:SU(SDL Update)版,加入了简单的SDL的Event。
simplest_ffmpeg_decoder:一个包含了封装格式处理功能的解码器。使用了libavcodec和libavformat。
simplest_ffmpeg_decoder_pure:一个纯净的解码器。只使用libavcodec(没有使用libavformat)。
simplest_video_play_sdl2:使用SDL2播放YUV的例子。
simplest_ffmpeg_helloworld:输出FFmpeg类库的信息。
最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器的更多相关文章
- 最简单的基于FFMPEG+SDL的视频播放器 ver2 (採用SDL2.0)
		
===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...
 - 最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)
		
===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...
 - 基于<最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)>的一些个人总结
		
最近因为项目接近收尾阶段,所以变的没有之前那么忙了,所以最近重新拿起了之前的一些FFMPEG和SDL的相关流媒体播放器的例子在看. 同时自己也用FFMPEG2.01,SDL2.01结合MFC以及网上罗 ...
 - 用JavaCV改写“100行代码实现最简单的基于FFMPEG+SDL的视频播放器 ”
		
FFMPEG的文档少,JavaCV的文档就更少了.从网上找到这篇100行代码实现最简单的基于FFMPEG+SDL的视频播放器.地址是http://blog.csdn.net/leixiaohua102 ...
 - 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)【转】
		
转自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] ...
 - 最简单的基于FFmpeg的内存读写的例子:内存播放器
		
===================================================== 最简单的基于FFmpeg的内存读写的例子系列文章列表: 最简单的基于FFmpeg的内存读写的 ...
 - (转)最简单的基于FFmpeg的内存读写的例子:内存播放器
		
ffmpeg内存播放解码 目录(?)[+] ===================================================== 最简单的基于FFmpeg的内存读写的例子系列文章 ...
 - 【转】100行代码实现最简单的基于FFMPEG+SDL的视频播放器
		
FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手.我刚接触FFMPEG的时候也感觉不知从何学起. 因此我把自己做项目过程中实现的一个非常简单的视频播放器 ...
 - 最简单的基于FFMPEG+SDL的音频播放器 ver2 (采用SDL2.0)
		
===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...
 
随机推荐
- linux MySQL 5.7+keepalived 主备服务器自主切换
			
一.环境准备1.关闭防火墙与selinux systemctl stop firewalld setenforce 0 sed -i 's/SELINUX=.*/SELINUX=disabled/g' ...
 - 手摸手。完成一个H5 抽奖功能
			
要完成一个这样的抽奖功能 构思 奖励物品是通过接口获取的(img) 奖励结果是通过接口获取的(id) 抽奖的动画需要由慢到快再到慢 抽奖转动时间不能太短 抽奖结束需要回调 业务代码和功能代码要分离 先 ...
 - 「洛谷P1196」「NOI2002」银河英雄传说 解题报告
			
P1196 [NOI2002]银河英雄传说 题目描述 公元五八○一年,地球居民迁至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的 ...
 - js 极简获取表单 元素 !
			
let s =[]; $.each($('#formSearch input'),(m,n)=>{s.push(n)}); //示例获取表单所有 input 下滑线分割的 name 集合.set ...
 - PTA - 堆栈模拟队列
			
设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q. 所谓用堆栈模拟队列,实际上就是通过调用堆栈的下列操作函数: int IsFull(Stack S):判断堆栈S是否已满,返回1或0: in ...
 - Jenkins Pipeline Job构建配置
			
    1.创建pipeline job任务,新建任务>输入任务名称>选择“流水线”>点击[确定]  添加描述,This is my first test pipelin ...
 - 洛谷 UVA1395 苗条的生成树 Slim Span
			
题目链接 题目描述 求所有生成树中最大边权与最小边权差最小的,输出它们的差值. 题目分析 要求所有生成树中边权极差最小值,起初令人无从下手.但既然要求所有生成树中边权极差最小值,我们自然需要对每一棵生 ...
 - [LOJ#3044][动态DP]「ZJOI2019」Minimax 搜索
			
题目传送门 容易想到一种暴力 DP:先转化成对于每个 \(k\) 求出 \(\max_{i\in S}|i-w_i|\le k\) 的方案数,最后差分 然后问题转化成每个叶子的权值有个取值区间,注意这 ...
 - Dart语言学习( 一)  为什么学习Dart?
			
为什么学习Dart? Google及全球的其他开发者,使用 Dart 开发了一系列高质量. 关键的 iOS.Android 和 web 应用. Dart 非常适合移动和 web 应用的开发. 高效 D ...
 - CSS基础应用总结
			
目录 CSS 样式笔记 文字水平居中和垂直居中 如何设置a标签不带下划线 控件右对齐 div上下居中 控件左右居中 控件展示在同一行 设置文字超出部分...显示 CSS 样式笔记 文字水平居中和垂直居 ...