// 打开音视频文件,存储在AVFormatContext中
avformat_open_input(&AVFormatContext, file, nullptr, nullptr);
// 搜索流信息,信息存储在AVFormatContext->streams中
avformat_find_stream_info(AVFormatContext, nullptr);
// 打印相关信息
// av_dump_format(AVFormatContext, 0, file, 0);
// 查找第一个音频流/视频流
for (int i = 0; i < AVFormatContext->nb_streams; ++i) {
if (AVFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
v_index = i;
}
if (AVFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
a_index = i;
}
}
// 获取解码器参数->获取解码器->构建解码器
AVCodecParameters = AVFormatContext->streams[a_index/v_index]->codecpar;
AVCodec = avcodec_find_decoder(AVCodecParameters->codec_id);
AVCodecContext = avcodec_alloc_context3(AVCodec);
avcodec_parameters_to_context(AVCodecContext, AVCodecParameters);
avcodec_open2(AVCodecContext, AVCodec, nullptr);
// 视频解码为yuv
AVPacket packet = (AVPacket*)av_malloc(sizeof(AVPacket));
AVFrame* raw = av_frame_alloc();
AVFrame* yuv = av_frame_alloc();
// 视频数据缓冲区
buf_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, AVCodecContext->width, AVCodecContext->height, 1);
buffer = (uint8_t*)av_malloc(buf_size);
// 设定yuv->data, yuv->linesize
// ret = av_image_fill_arrays(p_frm_yuv->data, // dst data[]
// p_frm_yuv->linesize, // dst linesize[]
// buffer, // src buffer
// AV_PIX_FMT_YUV420P, // pixel format
// p_codec_ctx->width, // width
// p_codec_ctx->height, // height
// 1 // align
// );
av_image_fill_arrays(yuv->data, yuv->linesize, buffer, AV_PIX_FMT_YUV420P, AVCodecContext->width, AVCodecContext->height, 1);
// 初始化SWS context 用于图像转换
// sws_ctx = sws_getContext(p_codec_ctx->width, // src width
// p_codec_ctx->height, // src height
// p_codec_ctx->pix_fmt, // src format
// p_codec_ctx->width, // dst width
// p_codec_ctx->height, // dst height
// AV_PIX_FMT_YUV420P, // dst format
// SWS_BICUBIC, // flags
// NULL, // src filter
// NULL, // dst filter
// NULL // param
// );
SwsContext* sws_ctx = sws_getContext(p_codec_ctx->width, p_codec_ctx->height, p_codec_ctx->pix_fmt, p_codec_ctx->width, p_codec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, nullptr, nullptr, nullptr);
// SDL窗口
SDL_Window* screan = SDL_CreateWindow("window name", SDL_WINDOWPOS_UNDEFINED, SDLWINDOWPOS_UNDEFINED, p_codec_ctx->width, p_codec_ctx->height, SDL_WINDOW_OPENGL);
// SDL 渲染
SDL_Renderer* sdl_renderer = SDL_CreateRenderer(screen, -1, 0);
// SDL 纹理一个texture对应一帧yuv数据
SDL_Texture* sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, p_codec_ctx->width, p_codec_ctx->height);
// 数据流向: p_fmt_ctx -> p_packet -> p_codec_ctx -> p_frm_raw
// 向解码器喂数据
av_read_frame(p_fmt_ctx, p_packet);
avcodec_send_packet(p_codec_ctx, p_packet);
av_packet_unref(p_packet);
// 接收解码器输出的数据
avcodec_receive_frame(p_codec_ctx, p_frm_raw);
// 图像转换: p_frm_raw->data --> p_frm_yuv->data.
// YUV有Y、U、V三个plane;slice是图像中一篇连续的行;stride/pitch是一行图像所占的字节数(如需要进行字节对齐)
// AVFrame->data[]:元素指向对应的plane
// AVFrame->linesize[]:元素表示对应plane中一行图像所占的字节数
sws_scale(sws_ctx, // sws context
(const uint8_t* const*)p_frm_raw->data, // src slice
p_frm_raw->linesize, // src stride
0, // src slice y
p_codec_ctx->height, // src slice height
p_frm_yuv->data, // dst planes
p_frm_yuv->linesize // dst strides
);
// 使用新的YUV数据更新SDL_Rect
SDL_UpdateYUVTexture(sdl_texture, // sdl texture
&sdl_rect, // sdl rect
p_frm_yuv->data[0], // y plane
p_frm_yuv->linesize[0], // y pitch
p_frm_yuv->data[1], // u plane
p_frm_yuv->linesize[1], // u pitch
p_frm_yuv->data[2], // v plane
p_frm_yuv->linesize[2] // v pitch
);
// 使用特定颜色清空当前渲染目标
SDL_RenderClear(sdl_renderer);
// 使用部分图像数据(texture)更新当前渲染目标
SDL_RenderCopy(sdl_renderer, // sdl renderer
sdl_texture, // sdl texture
NULL, // src rect, if NULL copy texture
&sdl_rect // dst rect
);
// 执行渲染,更新屏幕显示
SDL_RenderPresent(sdl_renderer);