参考100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)
雷神的代码用在VS2022编译需要做些调整
平台环境:windows VS 2022
#pragma comment(lib, "legacy_stdio_definitions.lib") //此为添加的代码 extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavutil/imgutils.h" #include "SDL2/SDL.h" FILE __iob_func[3] = { *stdin,*stdout,*stderr }; };
以及在
项目->项目属性->链接器->命令行,在右侧其他选项中添加“/SAFESEH:NO”,这样就不会再报错了。
使用ffmpeg进行解码的一般步骤
1.初始化FFmpeg库:
在代码中引入相关的FFmpeg头文件,并调用初始化函数。例如:
av_register_all(); avcodec_register_all(); avformat_network_init();
2.打开输入文件:
使用avformat_open_input,avformat_open_input 函数是FFmpeg(一个开源的多媒体处理库)中的一个函数,用于打开音视频文件或者网络流,并将其封装格式的相关信息填充到一个 AVFormatContext 结构体中。这个函数通常是在处理音视频文件时的初始步骤之一。
AVFormatContext *pFormatCtx; int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
参数解释:
ps: 用于存储 AVFormatContext 结构体指针的指针。AVFormatContext 是一个保存音视频文件或流相关信息的结构体,就是句柄。
url: 要打开的音视频文件或者网络流的URL。
fmt: 指定输入格式。通常可以设置为 NULL,表示由FFmpeg自动检测输入格式。
options: 一个指向 AVDictionary 结构体指针的指针,用于传递附加的选项。可以为 NULL,表示没有额外选项。
3.获取流信息
使用 avformat_find_stream_info函数,avformat_find_stream_info 函数是FFmpeg库中用于获取音视频流详细信息的函数。在打开音视频文件或者网络流后,通常需要调用这个函数来获取音视频流的相关信息,如编码格式、分辨率、帧率等。
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
参数解释:
ic: AVFormatContext 结构体,它包含了音视频文件或者网络流的相关信息,通常是通过 avformat_open_input 打开的。
options: 一个指向 AVDictionary 结构体指针的指针,用于传递附加的选项。可以为 NULL,表示没有额外选项。
函数返回值是一个整数,表示函数执行的结果。通常,如果函数成功执行,则返回 0,否则返回一个负数表示错误。
4.找到视频流索引并获取解码器上下文:
通过遍历流信息,找到视频流的索引,然后获取视频解码器的上下文。
int videoStreamIndex = -1; for (int i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break; } } AVCodecContext *pCodecCtx = avcodec_alloc_context3(NULL); avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStreamIndex]->codecpar);
5.查找并打开解码器:
使用avcodec_find_decoder函数查找合适的解码器,并使用avcodec_open2打开解码器。
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id); avcodec_open2(pCodecCtx, pCodec, NULL);
6.创建帧和包:
创建用于存储解码后的帧的AVFrame对象以及用于存储解码前的数据包的AVPacket对象。
AVFrame *pFrame = av_frame_alloc(); AVPacket *pPacket = av_packet_alloc();
7.解码:
使用循环读取每个数据包,将数据包发送到解码器进行解码,得到解码后的帧。
while (av_read_frame(pFormatCtx, pPacket) >= 0) { if (pPacket->stream_index == videoStreamIndex) { avcodec_receive_frame(pCodecCtx, pFrame); // 处理解码后的帧(显示、保存等) } av_packet_unref(pPacket); }
8.释放资源
avformat_close_input(&pFormatCtx); avcodec_free_context(&pCodecCtx); av_frame_free(&pFrame); av_packet_free(&pPacket);
使用ffmpeg解码视频文件
本例子使用FFmpeg库解码视频文件,并将解码后的帧保存为PPM图像文件。
#include#include #include #include #include #include #define IN_FILE_PATH "input_video.mp4" #define OUT_FILE_PATH "output_frame.ppm" int main() { AVFormatContext *pFormatCtx = NULL; AVCodecContext *pCodecCtx = NULL; AVCodec *pCodec = NULL; AVFrame *pFrame = NULL; AVPacket packet; struct SwsContext *pSwsCtx = NULL; // Register all formats and codecs av_register_all(); // Open input file if (avformat_open_input(&pFormatCtx, IN_FILE_PATH, NULL, NULL) != 0) { fprintf(stderr, "Could not open input file\n"); return -1; } // Retrieve stream information if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { fprintf(stderr, "Could not find stream information\n"); return -1; } // Find the first video stream int videoStream = -1; for (int i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; break; } } if (videoStream == -1) { fprintf(stderr, "Could not find a video stream\n"); return -1; } // Get a pointer to the codec context for the video stream pCodec = avcodec_find_decoder(pFormatCtx->streams[videoStream]->codecpar->codec_id); if (pCodec == NULL) { fprintf(stderr, "Unsupported codec\n"); return -1; } pCodecCtx = avcodec_alloc_context3(pCodec); if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar) < 0) { fprintf(stderr, "Failed to copy codec parameters to codec context\n"); return -1; } // Open codec if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { fprintf(stderr, "Could not open codec\n"); return -1; } // Allocate video frame pFrame = av_frame_alloc(); // Setup scaler context to convert video frames to RGB pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL); if (pSwsCtx == NULL) { fprintf(stderr, "Failed to allocate SwsContext\n"); return -1; } // Open output file for writing PPM FILE *ppmFile = fopen(OUT_FILE_PATH, "wb"); if (ppmFile == NULL) { fprintf(stderr, "Could not open output file\n"); return -1; } // Read frames and save as PPM while (av_read_frame(pFormatCtx, &packet) >= 0) { if (packet.stream_index == videoStream) { // Decode video frame int response = avcodec_send_packet(pCodecCtx, &packet); if (response < 0) { fprintf(stderr, "Error decoding frame (avcodec_send_packet)\n"); break; } while (response >= 0) { response = avcodec_receive_frame(pCodecCtx, pFrame); if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) { break; } else if (response < 0) { fprintf(stderr, "Error decoding frame (avcodec_receive_frame)\n"); break; } // Convert the frame to RGB uint8_t *buffer = NULL; int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1); buffer = av_malloc(numBytes * sizeof(uint8_t)); av_image_fill_arrays(pFrame->data, pFrame->linesize, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1); sws_scale(pSwsCtx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrame->data, pFrame->linesize); // Save the frame as PPM fprintf(ppmFile, "P6\n%d %d\n255\n", pCodecCtx->width, pCodecCtx->height); fwrite(buffer, 1, numBytes, ppmFile); av_free(buffer); } } av_packet_unref(&packet); } // Clean up av_frame_free(&pFrame); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); sws_freeContext(pSwsCtx); fclose(ppmFile); return 0; }
猜你喜欢
- 3小时前从零基础到精通:Flutter开发的完整指南
- 3小时前基于微信上海某大学浴室预约小程序系统设计与实现 研究背景和意义、国内外现状
- 3小时前《TrollStore巨魔商店》TrollStore2安装使用教程支持IOS14.0-16.6.1
- 3小时前python:最简单爬虫之使用Scrapy框架爬取小说
- 3小时前爬取的数据可以入表吗?怎样入表?
- 3小时前游戏开发中的噪声算法
- 3小时前JS进阶-作用域、垃圾回收机制、闭包、变量提升(一)
- 3小时前基于JAVA城市文化展示系统设计与实现(springboot框架) 参考文献
- 1小时前格力空调保修期(格力空调保修期是几年免费维修)
- 25分钟前圆通95554怎么转人工(圆通怎么转人工服务电话)
网友评论
- 搜索
- 最新文章
- 热门文章