FFmpeg和SDL的整合实现视频播放
整合方式
• FFmpeg解码器实现了:视频文件->YUV
• SDL视频显示实现了:YUV->屏幕
• FFmpeg+SDL整合之后实现了:视频文件->YUV->屏幕
/*** 最简单的基于FFmpeg的视频播放器2(SDL升级版)* Simplest FFmpeg Player 2(SDL Update)** 雷霄骅 Lei Xiaohua* leixiaohua1020@126.com* 中国传媒大学/数字电视技术* Communication University of China / Digital TV Technology* http://blog.csdn.net/leixiaohua1020** 本程序实现了视频文件的解码和显示(支持HEVC,H.264,MPEG2等)。* 是最简单的FFmpeg视频解码方面的教程。* 通过学习本例子可以了解FFmpeg的解码流程。* 本版本中使用SDL消息机制刷新视频画面。* This software is a simplest video player based on FFmpeg.* Suitable for beginner of FFmpeg.**/#include #define __STDC_CONSTANT_MACROSextern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL.h"
};//Refresh Event
#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)#define SFM_BREAK_EVENT (SDL_USEREVENT + 2)int thread_exit = 0;int sfp_refresh_thread(void* opaque) {thread_exit = 0;while (!thread_exit) {SDL_Event event;event.type = SFM_REFRESH_EVENT;SDL_PushEvent(&event);SDL_Delay(40);}thread_exit = 0;//BreakSDL_Event event;event.type = SFM_BREAK_EVENT;SDL_PushEvent(&event);return 0;
}int main(int argc, char* argv[])
{AVFormatContext* pFormatCtx;int i, videoindex;AVCodecContext* pCodecCtx;AVCodec* pCodec;AVFrame* pFrame, * pFrameYUV;uint8_t* out_buffer;AVPacket* packet;int ret, got_picture;//------------SDL----------------int screen_w, screen_h;SDL_Window* screen;SDL_Renderer* sdlRenderer;SDL_Texture* sdlTexture;SDL_Rect sdlRect;SDL_Thread* video_tid;SDL_Event event;struct SwsContext* img_convert_ctx;char filepath[] &#61; "屌丝男士.mov";//av_register_all();avformat_network_init();pFormatCtx &#61; avformat_alloc_context();if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) !&#61; 0) {printf("Couldn&#39;t open input stream.\n");return -1;}if (avformat_find_stream_info(pFormatCtx, NULL) <0) {printf("Couldn&#39;t find stream information.\n");return -1;}videoindex &#61; -1;for (i &#61; 0; i nb_streams; i&#43;&#43;)if (pFormatCtx->streams[i]->codecpar->codec_type &#61;&#61; AVMEDIA_TYPE_VIDEO) {videoindex &#61; i;break;}if (videoindex &#61;&#61; -1) {printf("Didn&#39;t find a video stream.\n");return -1;}AVCodecParameters* codecParameters &#61; pFormatCtx->streams[videoindex]->codecpar;pCodecCtx &#61; avcodec_alloc_context3(nullptr);avcodec_parameters_to_context(pCodecCtx, codecParameters);//pCodecCtx &#61; pFormatCtx->streams[videoindex]->codec;pCodec &#61; (AVCodec*)avcodec_find_decoder(pCodecCtx->codec_id);if (pCodec &#61;&#61; NULL) {printf("Codec not found.\n");return -1;}if (avcodec_open2(pCodecCtx, pCodec, NULL) <0) {printf("Could not open codec.\n");return -1;}packet &#61; av_packet_alloc();pFrame &#61; av_frame_alloc();pFrameYUV &#61; av_frame_alloc();out_buffer &#61; (uint8_t*)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);img_convert_ctx &#61; sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {printf("Could not initialize SDL - %s\n", SDL_GetError());return -1;}//SDL 2.0 Support for multiple windowsscreen_w &#61; pCodecCtx->width;screen_h &#61; pCodecCtx->height;screen &#61; SDL_CreateWindow("Simplest ffmpeg player&#39;s Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,screen_w, screen_h, SDL_WINDOW_OPENGL);if (!screen) {printf("SDL: could not create window - exiting:%s\n", SDL_GetError());return -1;}sdlRenderer &#61; SDL_CreateRenderer(screen, -1, 0);//IYUV: Y &#43; U &#43; V (3 planes)//YV12: Y &#43; V &#43; U (3 planes)sdlTexture &#61; SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);sdlRect.x &#61; 0;sdlRect.y &#61; 0;sdlRect.w &#61; screen_w;sdlRect.h &#61; screen_h;packet &#61; (AVPacket*)av_malloc(sizeof(AVPacket));video_tid &#61; SDL_CreateThread(sfp_refresh_thread, NULL, NULL);//------------SDL End------------//Event Loopfor (;;) {//WaitSDL_WaitEvent(&event);if (event.type &#61;&#61; SFM_REFRESH_EVENT) {//------------------------------if (av_read_frame(pFormatCtx, packet) >&#61; 0) {if (packet->stream_index &#61;&#61; videoindex) {if (avcodec_send_packet(pCodecCtx, packet) <0) {printf("avcodec_send_packet failed!.\n");continue;}while (1) {ret &#61; avcodec_receive_frame(pCodecCtx, pFrame);if (ret !&#61; 0)break;sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);//SDL---------------------------SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);SDL_RenderClear(sdlRenderer);//SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect ); SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);SDL_RenderPresent(sdlRenderer);//SDL End-----------------------}}av_packet_unref(packet);}else {//Exit Threadthread_exit &#61; 1;}}else if (event.type &#61;&#61; SDL_QUIT) {thread_exit &#61; 1;}else if (event.type &#61;&#61; SFM_BREAK_EVENT) {break;}}sws_freeContext(img_convert_ctx);SDL_Quit();//--------------av_frame_free(&pFrameYUV);av_frame_free(&pFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);return 0;
}
进阶&#xff1a;脱离开发环境的独立播放器
因为VS会生成可执行文件&#xff08;在Debug文件夹中&#xff09;&#xff0c;利用argv参数进行视频文件的选择播放
main()函数的参数
• argc argv&#xff1a;全称为ARGument Counter 和 ARGument Vector。其中argv存储了来自于命令行的参数&#xff1b;而argc存储了参数的个数。
• 例如在命令行中输入“ffmpeg -i test.mkv test.ts ”&#xff0c;则argc取值为4&#xff0c; 而argv[]数组取值如下&#xff1a;
argv[0]&#61;“ffmpeg”
argv[1]&#61;“-i”
argv[2]&#61;“test.mkv”
argv[3]&#61;“test.ts”
• 动态链接库&#xff08;*.dll&#xff09;
动态链接库不能被编译进应用程序。因而使用应用程序的时候必须在相同目录下保存用到的动态链接库文件。
学习地址&#xff1a;https://ke.qq.com/course/5840494?flowToken&#61;1044368
你将获得详询课程
-
掌握ffmpeg开发环境的快速搭建
-
掌握音视频解复用流程以及解码流程
-
掌握声音输出和画面渲染
-
掌握音视频同步原理
训练营品质服务
-
作业讲解 讲解课后作业&#xff0c;老师手把手指导教学
-
专业老师答疑 学中有疑问&#xff0c;工作日24小时内可获得老师响应
-
专属学习群 加入学员学习交流群&#xff0c;与相同目标的同学共同进步