100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)
2016-06-29 13:59
573 查看
转自:
http://blog.csdn.net/leixiaohua1020/article/details/8652605
机器环境:
vs2010
SDL-1.2.15
ffmpeg-20160628-c0cb53c-win32-dev
代码:(代码略有修改, vs2010调试通过)
http://blog.csdn.net/leixiaohua1020/article/details/8652605
机器环境:
vs2010
SDL-1.2.15
ffmpeg-20160628-c0cb53c-win32-dev
代码:(代码略有修改, vs2010调试通过)
/** * 最简单的基于FFmpeg的视频播放器 * Simplest FFmpeg Player * * 雷霄骅 Lei Xiaohua * leixiaohua1020@126.com * 中国传媒大学/数字电视技术 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本程序实现了视频文件的解码和显示(支持HEVC,H.264,MPEG2等)。 * 是最简单的FFmpeg视频解码方面的教程。 * 通过学习本例子可以了解FFmpeg的解码流程。 * This software is a simplest video player based on FFmpeg. * Suitable for beginner of FFmpeg. */ #include <stdio.h> //C99整数常量宏. [纯C程序可以不用, 而C++程序必须定义该宏.] #define __STDC_CONSTANT_MACROS // Windows平台 #ifdef _WIN32 // C++中包含C extern "C" { #include "libavcodec/avcodec.h" // ffmpeg编解码模块 #include "libavformat/avformat.h" // ffmpeg视频格式模块 #include "libswscale/swscale.h" // ffmpeg各种图像像素格式的转换,以及图像大小缩放 #include "SDL.h" // Simple DirectMedia Layer }; #else //Linux... #ifdef __cplusplus // 将C++代码以标准C形式输出 extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <SDL/SDL.h> #ifdef __cplusplus }; #endif #endif //Full Screen #define SHOW_FULLSCREEN 0 //Output YUV420P #define OUTPUT_YUV420P 0 int main(int argc, char* argv[]) { //FFmpeg AVFormatContext *pFormatCtx; // 解封装功能结构体 int i, videoindex; AVCodecContext *pCodecCtx; // 解码功能结构体 AVCodec *pCodec; // 解码器 AVFrame *pFrame,*pFrameYUV; // 解码后数据/帧 AVPacket *packet; // 解码前数据 struct SwsContext *img_convert_ctx; // 图像转换结构体 //SDL int screen_w,screen_h; // 屏幕的宽&高 SDL_Surface *screen; // Graphical Surface Structure SDL_VideoInfo *vi; // Video Target information SDL_Overlay *bmp; // YUV video overlay SDL_Rect rect; // Defines a rectangular area FILE *fp_yuv; // 定义YUV文件 int ret, got_picture; char filepath[]="some.mp4"; // 定义文件路径 av_register_all(); // ffmpeg注册复用器&编码器 avformat_network_init(); // ffmpeg网络流初始化 pFormatCtx = avformat_alloc_context(); // 初始化解封装功能结构体AVFormatContext // 输入输出结构体AVIOContext的初始化 if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){ printf("Couldn't open input stream.\n"); return -1; } // 给每个媒体流(音频/视频)的AVStream结构体赋值 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; // 通过code ID查找一个已经注册的音视频解码器 // 查找解码器之前,必须先调用av_register_all注册所有支持的解码器 pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==NULL){ printf("Codec not found.\n"); return -1; } // 初始化一个视音频编解码器的AVCodecContext if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){ printf("Could not open codec.\n"); return -1; } // 初始化解码后数据AVFrame pFrame=av_frame_alloc(); pFrameYUV=av_frame_alloc(); // SDL初如化(视频|音频|定时器) if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1; } // 是否全屏 #if SHOW_FULLSCREEN vi = SDL_GetVideoInfo(); screen_w = vi->current_w; screen_h = vi->current_h; screen = SDL_SetVideoMode(screen_w, screen_h, 0,SDL_FULLSCREEN); #else screen_w = pCodecCtx->width; screen_h = pCodecCtx->height; // 创建一个窗口 screen = SDL_SetVideoMode(screen_w, screen_h, 0,0); #endif if(!screen) { printf("SDL: could not set video mode - exiting:%s\n",SDL_GetError()); return -1; } // 创建YUV图像 bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,SDL_YV12_OVERLAY, screen); rect.x = 0; rect.y = 0; rect.w = screen_w; rect.h = screen_h; //SDL End------------------------ packet=(AVPacket *)av_malloc(sizeof(AVPacket)); //Output Information----------------------------- printf("------------- File Information ------------------\n"); // 解封装结构体赋值 av_dump_format(pFormatCtx,0,filepath,0); printf("-------------------------------------------------\n"); #if OUTPUT_YUV420P fp_yuv=fopen("output.yuv","wb+"); #endif // 设置窗口标题 SDL_WM_SetCaption("Simplest FFmpeg Player",NULL); // 初始化一个SwsContext 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); //----------------------------------------------------------- // av_read_frame读取码流中的音频若干帧或者视频一帧 // 输入的AVFormatContext,输出的AVPacket while(av_read_frame(pFormatCtx, packet)>=0){ if(packet->stream_index==videoindex){ // 解码 // 变量的定义:int ret, got_picture; // 解码一帧视频数据。输入一个解码前AVCodecContext,输出一个解码后的结构体AVFrame ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); if(ret < 0){ printf("Decode Error.\n"); return -1; } // got_picture为零表示获取失败,非零获取成功 if(got_picture){ // Lock an overlay SDL_LockYUVOverlay(bmp); // SDL_Overlay *bmp; // AVFrame *pFrame,*pFrameYUV; // YUV420P中data[0]存Y,data[1]存U,data[2]存V // 这里为啥交换1,2顺序??? pFrameYUV->data[0]=bmp->pixels[0]; pFrameYUV->data[1]=bmp->pixels[2]; pFrameYUV->data[2]=bmp->pixels[1]; pFrameYUV->linesize[0]=bmp->pitches[0]; pFrameYUV->linesize[1]=bmp->pitches[2]; pFrameYUV->linesize[2]=bmp->pitches[1]; // struct SwsContext *img_convert_ctx; // sws_scale主要用于在2个AVFrame之间进行转换。 sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); #if OUTPUT_YUV420P int y_size=pCodecCtx->width*pCodecCtx->height; // FILE *fp_yuv; // 将转换后的原始数据存成文件 // 保存YUV420P格式的数据 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 #endif // UnLock an overlay SDL_UnlockYUVOverlay(bmp); // Blit the overlay to the display SDL_DisplayYUVOverlay(bmp, &rect); // 设置延迟,最小10ms. // Delay 40ms SDL_Delay(40); } } // 释放AVPacket对象 av_free_packet(packet); } //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 uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); SDL_LockYUVOverlay(bmp); pFrameYUV->data[0]=bmp->pixels[0]; pFrameYUV->data[1]=bmp->pixels[2]; pFrameYUV->data[2]=bmp->pixels[1]; pFrameYUV->linesize[0]=bmp->pitches[0]; pFrameYUV->linesize[1]=bmp->pitches[2]; pFrameYUV->linesize[2]=bmp->pitches[1]; #if OUTPUT_YUV420P 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 #endif SDL_UnlockYUVOverlay(bmp); SDL_DisplayYUVOverlay(bmp, &rect); //Delay 40ms SDL_Delay(40); } sws_freeContext(img_convert_ctx); #if OUTPUT_YUV420P fclose(fp_yuv); #endif SDL_Quit(); //av_free(out_buffer); av_free(pFrameYUV); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); return 0; }
相关文章推荐
- Java中 int和Integer的区别+包装类
- C# Index 定义索---引具体使用
- 常见递归问题 Python解法
- MyBatis高级映射和查询缓存
- PHP 语法
- C# Index 定义索---引具体使用
- PHP 安装
- 关于QProcess的进程中的运行先后测试
- Spring mvc常用的注解
- JavaSE 基础 第14节 关系运算符、逻辑运算符与三元运算符
- Java内存泄漏和内存调优
- 从eclipse官网下载eclipse
- C#在高性能计算领域为什么性能却如此不尽人意
- 由dubbo引起的 java OutOfMemoryError unable to create new native thread
- java编程相关总结(二)
- java读取.properties配置文件的几种方法
- django 报错
- 框架 day66 Mybatis(关联查询映射(1对1/多),延迟加载,一级/二级缓存,与spring整合,逆向工程)
- SpringMvc+Maven框架下简单分页实现
- PHP 异常处理