最简单的基于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 ...
随机推荐
- 【一起学源码-微服务】Nexflix Eureka 源码五:EurekaClient启动要经历哪些艰难险阻?
前言 在源码分析三.四都有提及到EurekaClient启动的一些过程.因为EurekaServer在集群模式下 自己本身就是一个client,所以之前初始化eurekaServerContext就有 ...
- status100到500http响应对应状态解释
1xx-信息提示 这些状态代码表示临时的响应.客户端在收到常规响应之前,应准备接收一个或多个1xx响应. 100-继续. 101-切换协议. 2xx-成功 这类状态代码表明服务器成功地接受了客户端请求 ...
- 1046 划拳 (15 分)C语言
划拳是古老中国酒文化的一个有趣的组成部分.酒桌上两人划拳的方法为:每人口中喊出一个数字,同时用手比划出一个数字.如果谁比划出的数字正好等于两人喊出的数字之和,谁就赢了,输家罚一杯酒.两人同赢或两人同输 ...
- 通俗易懂理清mybatis中SqlSessionSql、SqlSessionTemplate、SessionFactory和SqlSessionFactoryBean之间的关系
我潇洒的灰大狼又回来啦.今天送大家的一句话是: 保持耐心,永远年轻,永远热泪盈眶. 前言 先容我哭一会儿,呜呜呜~昨晚写了一半的文章,还没保存就盖上盖子准备回家,拔下电源准备把电脑塞进书包带回家完成时 ...
- Eclipse配置运行内存
对于一些大的项目,运行时等待过长可能出现内存溢出现象,需要重新配置IDE运行内存大小,如下: 1.配置Eclipse.ini 如图再改大点: -Xms512m-Xmx512m 2.JDK内存扩大 Wc ...
- Stripe支付对接
一.由于文档丢失原因,我就直接上代码了. 这个Stripe支付可以支持多个币种,我下面就采用"HDK"来参照支付先上一个支付效果图 提示:先上代码,在说明博主自己理解的流程. 一 ...
- React框架随笔
React框架随笔 现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了 ...
- JVM之GC(一)
Java较C而言,最大的区别在于内存管理.JVM设有无用内存空间自动回收复用机制,也就是我们所说的GC. 之前说过,栈是为线程.为函数的执行分配内存的地方,用完即“销毁”,这里留待以后做深入探讨:堆是 ...
- Fabric1.4:链码管理与测试
1 链码介绍 智能合约在 Hyperledger Fabric 中称为链码(chaincode),是提供分布式账本的状态处理逻辑.链码被部署在fabric 的网络节点中,能够独立运行在具有安全特性的受 ...
- 简简单单之Linux命令入门
show me the code and talk to me,做的出来更要说的明白 GitHub 项目JavaHouse同步收录 我是布尔bl,你的支持是我分享的动力! 引入 作为一名合格的后端开发 ...