ffmpeg学习之旅一
本文参考雷神博文《最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)》,
原文链接https://blog.csdn.net/leixiaohua1020/article/details/38868499,替换了原先使用的ffmpeg库和相关API。
主要修改部分
代码如下:
/**
* 最简单的基于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 <stdio.h>
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
#include "SDL2/SDL.h"
};
#pragma comment(lib, "legacy_stdio_definitions.lib")
extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; }
//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;
//Break
SDL_Event event;
event.type = SFM_BREAK_EVENT;
SDL_PushEvent(&event);
return 0;
}
int main(int argc, char* argv[])
{
AVFormatContext *pFormatCtx = nullptr;
int videoindex = -1;
AVCodecContext *pCodecCtx = nullptr;
AVCodec *pCodec = nullptr;
AVFrame *pFrame = nullptr, *pFrameYUV = nullptr;
unsigned char *out_buffer = nullptr;
AVPacket *packet = nullptr;
int y_size = 0;
int ret, got_picture;
struct SwsContext *img_convert_ctx = nullptr;
char filepath[] = "bigbuckbunny_480x272.hevc";
//SDL---------------------------
int screen_w = 0, screen_h = 0;
SDL_Window *screen;
SDL_Renderer* sdlRenderer;
SDL_Texture* sdlTexture;
SDL_Rect sdlRect;
FILE *fp_yuv = nullptr;
//av_register_all();
//avformat_network_init();
pFormatCtx = avformat_alloc_context();
if (pFormatCtx == nullptr)
{
printf("Could not allocate AVCodecContext\n");
return -1;
}
if (avformat_open_input(&pFormatCtx, filepath, nullptr, nullptr) != 0) {
printf("Couldn't open input stream.\n");
return -1;
}
if (avformat_find_stream_info(pFormatCtx, nullptr) < 0) {
printf("Couldn't find stream information.\n");
return -1;
}
for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++)
if (pFormatCtx->streams[i]->codecpar->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;
pCodecCtx = avcodec_alloc_context3(nullptr);
avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoindex]->codecpar);
if (pCodecCtx == nullptr)
{
printf("codecCtx not found.\n");
return -1;
}
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == nullptr) {
printf("Codec not found.\n");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {
printf("Could not open codec.\n");
return -1;
}
pFrame = av_frame_alloc();
if (pFrame == nullptr)
{
return -1;
}
pFrameYUV = av_frame_alloc();
if (pFrameYUV == nullptr)
{
return -1;
}
out_buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));
if (out_buffer == nullptr)
{
return -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));
if (packet == nullptr)
{
return -1;
}
//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, nullptr, nullptr, nullptr);
if (img_convert_ctx == nullptr)
{
return -1;
}
#if OUTPUT_YUV420P
fp_yuv = fopen("output.yuv", "wb+");
#endif
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf("Could not initialize SDL - %s\n", SDL_GetError());
return -1;
}
screen_w = pCodecCtx->width;
screen_h = pCodecCtx->height;
//SDL 2.0 Support for multiple windows
screen = SDL_CreateWindow("Simplest ffmpeg player'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 = SDL_CreateRenderer(screen, -1, 0);
//IYUV: Y + U + V (3 planes)
//YV12: Y + V + U (3 planes)
sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);
sdlRect.x = 0;
sdlRect.y = 0;
sdlRect.w = screen_w;
sdlRect.h = screen_h;
//SDL End----------------------
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;
//}
ret = avcodec_send_packet(pCodecCtx, packet);
if (ret != 0) {
printf("Decode Error.\n");
return -1;
}
got_picture = avcodec_receive_frame(pCodecCtx, pFrame);
if (got_picture == 0) {
sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize);
#if OUTPUT_YUV420P
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---------------------------
#if 0
SDL_UpdateTexture(sdlTexture, nullptr, pFrameYUV->data[0], pFrameYUV->linesize[0]);
#else
SDL_UpdateYUVTexture(sdlTexture, &sdlRect,
pFrameYUV->data[0], pFrameYUV->linesize[0],
pFrameYUV->data[1], pFrameYUV->linesize[1],
pFrameYUV->data[2], pFrameYUV->linesize[2]);
#endif
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, nullptr, &sdlRect);
SDL_RenderPresent(sdlRenderer);
//SDL End-----------------------
//Delay 40ms
SDL_Delay(40);
}
}
}
//flush decoder
//FIX: Flush Frames remained in Codec
while (1) {
ret = avcodec_send_packet(pCodecCtx, packet);
if (ret != 0) {
break;
}
got_picture = avcodec_receive_frame(pCodecCtx, pFrame);
if (got_picture)
break;
sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize);
#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---------------------------
SDL_UpdateTexture(sdlTexture, &sdlRect, pFrameYUV->data[0], pFrameYUV->linesize[0]);
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, nullptr, &sdlRect);
SDL_RenderPresent(sdlRenderer);
//SDL End-----------------------
//Delay 40ms
SDL_Delay(40);
}
if (img_convert_ctx != nullptr)
{
sws_freeContext(img_convert_ctx);
img_convert_ctx = nullptr;
}
#if OUTPUT_YUV420P
fclose(fp_yuv);
#endif
SDL_Quit();
if (packet != nullptr)
{
av_packet_free(&packet);
packet = nullptr;
}
if (pFrameYUV != nullptr)
{
av_frame_free(&pFrameYUV);
pFrameYUV = nullptr;
}
if (pFrame != nullptr)
{
av_frame_free(&pFrame);
pFrame = nullptr;
}
if (pCodecCtx != nullptr)
{
avcodec_close(pCodecCtx);
pCodecCtx = nullptr;
}
if (pFormatCtx != nullptr)
{
avformat_close_input(&pFormatCtx);
pFormatCtx = nullptr;
}
return 0;
}
下载链接
项目工程下载链接:https://download.csdn.net/download/lg15273112290/12246411