SDL2播放FFmpeg解压的视频
Posted hbg200
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SDL2播放FFmpeg解压的视频相关的知识,希望对你有一定的参考价值。
SDL2简化了播放过程,这里引入播放视频。
1. 以我的《FFmpeg入门测试》为工程。
2. 到http://www.libsdl.org/index.php下载SDL2-devel-2.0.9-VC.zip (Visual C++ 32/64-bit)最新版。解压将SDL2-2.0.9libx86目录内的SDL2.dll考入解决方案的Debug目录中。在解决方案目录中新建SDL目录,拷入SDL2-2.0.9内的include和lib两个目录。
3. 创建开发环境:
3.1 包含编译文件
3.1.1 包含头目录:项目->属性->VC++目录->包含目录:F:FFmpegTestSDLinclude。
3.1.2 包含库目录:项目->属性->VC++目录->库目录:F:FFmpegTestSDLlibx86。
3.1.3 包含链接库文件:项目->属性->链接器->输入->附加依赖项:SDL2.lib;SDL2main.lib。
4. 输入源代码:
#include <iostream> #define __STDC_CONSTANT_MACROS //使用FFmpeg定义宏 extern "C" { #include "libavutil/imgutils.h" //av_image使用 #include "libavcodec/avcodec.h" //编解码处理 #include "libavformat/avformat.h" //文件封装处理 #include "libswscale/swscale.h"//图像变换 #include "libavdevice/avdevice.h" #include "SDL.h" }; void PrintErrStr(const char *str) { printf(str); printf(" "); system("pause"); } int Decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt)//解码 { int ret = avcodec_send_packet(dec_ctx, pkt);// 先发送包数据到解码上下文中 if (ret < 0) { PrintErrStr("发送数据包进行解码时出错."); return ret; } return avcodec_receive_frame(dec_ctx, frame);// 然后从解码上下文中读取帧数据到frame对象中 } #undef main int main() { AVFormatContext *pFormatCtx = NULL;//主要用于处理封装格式(FLV/MKV/RMVB等) AVCodecContext *pVideoCodecCtx = NULL; AVCodecContext *pAudioCodecCtx = NULL; AVCodec *pVideoCodec = NULL; AVCodec *pAudioCodec = NULL; AVFrame *pFrame; //存储非压缩的数据(视频对应RGB/YUV像素数据,音频对应PCM采样数据) AVPacket packet; //存储压缩数据(视频对应H.264等码流数据,音频对应AAC/MP3等码流数据) //SDL变量 SDL_Window *screen; SDL_Renderer* sdlRenderer; SDL_Texture* sdlTexture; SDL_Rect sdlRect; int iHour, iMinute, iSecond, iTotalSeconds;//HH:MM:SS int videoStreamIndex, audioStreamIndex; //音视频流索引 int ret, FrameBytes; const char In_FileName[] = "F:\TestMov\Titanic.ts";//输入文件 const char Out_h264_FileName[] = "F:\TestMov\output.h264";//输出h264文件 const char Out_rgb24_FileName[] = "F:\TestMov\output.rgb24";//输出h264文件 FILE *fp_Out_h264_File = fopen(Out_h264_FileName, "wb+"); FILE *fp_Out_rgb24_File = fopen(Out_rgb24_FileName, "wb+"); int OutImgW = 640; int OutImgH = 272; int Img_linesize = OutImgW * 3; int OutImg_linesize[3] = {Img_linesize, 0, 0}; sdlRect.x = 0; sdlRect.y = 0; sdlRect.w = OutImgW; sdlRect.h = OutImgH; avdevice_register_all();//注册所有组件 if (avformat_open_input(&pFormatCtx, In_FileName, NULL, NULL) != 0)//打开输入视频文件 { PrintErrStr("打开输入文件失败。"); return -1; } if (avformat_find_stream_info(pFormatCtx, NULL) < 0)//获取文件信息 { PrintErrStr("没有发现流信息。"); return -1; } videoStreamIndex = -1; audioStreamIndex = -1; for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++)//寻找流索引 { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)//查找视频流索引 { videoStreamIndex = i; } else if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)//查找音频流索引 { audioStreamIndex = i; } } if ((videoStreamIndex == -1)||(audioStreamIndex == -1)) { PrintErrStr("没有发现视频或音频流。"); return -1; } pVideoCodec = avcodec_find_decoder(pFormatCtx->streams[videoStreamIndex]->codecpar->codec_id);//查找视频解码器 if (pVideoCodec == NULL) { PrintErrStr("没有找到视频解码器。"); return -1; } pAudioCodec = avcodec_find_decoder(pFormatCtx->streams[audioStreamIndex]->codecpar->codec_id);//查找音频解码器 if (pVideoCodec == NULL) { PrintErrStr("没有找到音频解码器。"); return -1; } pVideoCodecCtx = avcodec_alloc_context3(pVideoCodec);//申请视频解码空间 if (pVideoCodecCtx == NULL) { PrintErrStr("无法分配视频解码器。"); return -1; } pAudioCodecCtx = avcodec_alloc_context3(pAudioCodec);//申请音频解码空间 if (pAudioCodecCtx == NULL) { PrintErrStr("无法分配音频解码器。"); return -1; } avcodec_parameters_to_context(pVideoCodecCtx, pFormatCtx->streams[videoStreamIndex]->codecpar); avcodec_parameters_to_context(pAudioCodecCtx, pFormatCtx->streams[audioStreamIndex]->codecpar); if (avcodec_open2(pVideoCodecCtx, pVideoCodec, NULL) < 0) //打开视频解码器 { PrintErrStr("没有打开视频解码器。"); return -1; } if (avcodec_open2(pAudioCodecCtx, pAudioCodec, NULL) < 0) //打开音频解码器 { PrintErrStr("没有打开音频解码器。"); return -1; } if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))//SDL2初始化视频 { printf("无法初始化SDL2:%s ", SDL_GetError()); printf(" "); system("pause"); return -1; } screen = SDL_CreateWindow("SDL2 + ffmpeg player‘s Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, OutImgW, OutImgH, SDL_WINDOW_SHOWN); if (!screen) { printf("创建SDL2窗口失败:%s ", SDL_GetError()); printf(" "); system("pause"); return -1; } sdlRenderer = SDL_CreateRenderer(screen, -1, 0);//SDL2创建渲染 sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, OutImgW, OutImgH);//SDL2创建纹理 pFrame = av_frame_alloc();//申请解压帧缓存 if (pFrame == NULL) { PrintErrStr("申请解压帧缓存失败。"); return -1; } FrameBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pVideoCodecCtx->width, pVideoCodecCtx->height, 1);//获取视频帧字节数 uint8_t *pFrameBuffer = (uint8_t *)av_malloc(FrameBytes);//创建动态视频帧数组 while (av_read_frame(pFormatCtx, &packet) >= 0)//从文件中读取一个packet { if (packet.stream_index == videoStreamIndex)//如果是视频帧 { //fwrite(packet.data, packet.size, 1, fp_Out_h264_File);//写视频包文件 ret = Decode(pVideoCodecCtx, pFrame, &packet);//解码 packet->pFrame if (ret == 0) //已得到解码的图像在pFrame里 { struct SwsContext *pSwsCtx = NULL; pSwsCtx = sws_getContext( pVideoCodecCtx->width, pVideoCodecCtx->height, pVideoCodecCtx->pix_fmt,//源宽度高度和格式YUV420 OutImgW, OutImgH, AV_PIX_FMT_RGB24,//转换到目标的宽度高度和格式 SWS_FAST_BILINEAR,//缩放算法 NULL, NULL, NULL);//初始化转换 if (!pSwsCtx) { PrintErrStr("无法初始化转换Frame帧。"); return -1; } sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, pVideoCodecCtx->height, &pFrameBuffer, OutImg_linesize); //SDL_Update SDL_UpdateTexture(sdlTexture, NULL, pFrameBuffer, Img_linesize);//刷新显示 SDL_RenderClear(sdlRenderer);//清除上次渲染 SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);//拷贝纹理 SDL_RenderPresent(sdlRenderer);//渲染 SDL_Delay(40);//延时 //fwrite(pFrameBuffer, FrameBytes, 1, fp_Out_rgb24_File);//写RGB视频文件 sws_freeContext(pSwsCtx);//释放转换 } } av_packet_unref(&packet); } //输出信息 puts("[文件信息]"); iTotalSeconds = (int)pFormatCtx->duration / 1000000; //S iHour = iTotalSeconds / 3600;//小时 iMinute = iTotalSeconds % 3600 / 60;//分钟 iSecond = iTotalSeconds % 60;//秒 printf("播放时间:%02d:%02d:%02d ", iHour, iMinute, iSecond); printf("流 个 数:%d ", pFormatCtx->nb_streams); printf("封装格式:%s ", pFormatCtx->iformat->long_name); printf(" "); puts("[视频信息]"); printf("编码格式:%s ", pVideoCodec->long_name); printf("视频码率:%I64d kb/s ", pVideoCodecCtx->bit_rate / 1000); printf("分 辨 率:%d * %d ", pVideoCodecCtx->width, pVideoCodecCtx->height); printf(" "); puts("[音频信息]"); printf("编码格式:%s ", pAudioCodec->long_name); printf("音频码率:%I64d kb/s ", pAudioCodecCtx->bit_rate / 1000); printf("通 道 数:%d ", pAudioCodecCtx->channels); printf("采 样 率:%d ", pAudioCodecCtx->sample_rate); printf(" "); SDL_Quit(); fclose(fp_Out_h264_File); fclose(fp_Out_rgb24_File); av_free(pFrameBuffer); av_frame_free(&pFrame); avcodec_close(pVideoCodecCtx); avcodec_close(pAudioCodecCtx); avformat_close_input(&pFormatCtx); printf(" "); system("pause"); }
5. 编译运行:输出一个窗口播放视频,输出文件已无意义,被注释掉。
因SDL内也有一个main,所以在主程序main前输入#undef main,避免错误。
以上是关于SDL2播放FFmpeg解压的视频的主要内容,如果未能解决你的问题,请参考以下文章
基于FFmpeg的视频播放器之五:使用SDL2渲染yuv420p
当SDL_init()时,FFmpeg2.3.3和SDL2-2.0.3的Android Media播放器出错。错误是关于SDL_main.h