ffmpeg解封装mp4并解码渲染(SDL2)
一、概述
在前几篇的文章中介绍了从H264文件中读取数据封装成AVPacket并通过解码器解码,然后用SDL进行渲染的案例。本节继上面的内容,通过加载一个mp4文件,通过ffmpeg的解封装功能
,把AVPacket拿出来,然后放到之前封装好的解码器中进行解码,然后使用SDL进行渲染操作。
ps:本节的重点是解封装
二、代码示例
1.初始化解封装上下文,并打开媒体文件
//打开文件流 AVFormatContext* ic = nullptr; int ret = avformat_open_input(&ic, this->fileName.toStdString().c_str(), NULL,//封装器格式 null 自动探测 根据后缀名或者文件头 NULL);//参数设置,rtsp需要设置 if (ret != 0) { qDebug() << "avformat_open_input failed!打开解封装上下文失败"; return; }
2.发现媒体信息:avformat_find_stream_info
//发现媒体流信息 ret = avformat_find_stream_info(ic, NULL); if (ret < 0) { qDebug() << "avformat_find_stream_info failed!"; return; }
3.分别找到音频流和视频流
//找到音视频流 AVStream* vs = nullptr; AVStream* as = nullptr; for (int i = 0;i < ic->nb_streams;i++) { //视频 if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { vs = ic->streams[i]; } else if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)//音频 { as = ic->streams[i]; } }
4.拿到视频的宽高
int videoWidth = vs->codecpar->width; int videoHeight = vs->codecpar->height;
5.通过Qt的信号槽,把视频的宽高发射出去,Qt的槽函数会接收,并更新窗口和label的大小。ps:由于解封装和解码操作都是在子线程中工作,而子线程中没办法直接更新Qt的UI操作。
-
- 初始化信号槽
signals: void updateSize(int videoWidth, int videoHeight); public slots: void onUpdateSize(int videoWidth, int videoHeight);
- 绑定信号槽
connect(this, &DeMuxerAndPlayWindow::updateSize, this, &DeMuxerAndPlayWindow::onUpdateSize, Qt::QueuedConnection);
- 发送信号函数
int videoWidth = vs->codecpar->width; int videoHeight = vs->codecpar->height; emit updateSize(videoWidth, videoHeight);//发送信号到槽函数
- 通过槽函数接收,并更新UI
void DeMuxerAndPlayWindow::onUpdateSize(int videoWidth, int videoHeight) { ui.labelVideo->move(0, 0); ui.labelVideo->resize(QSize(videoWidth, videoHeight)); resize(QSize(videoWidth, videoHeight)); renderView = IVideoRenderView::Create(); renderView->Init(videoWidth, videoHeight, IVideoRenderView::YUV420P, (void*)ui.labelVideo->winId()); }
- 初始化信号槽
6.创建解码器,并初始化参数,此处特别需要注意:需要把解封装上下文的参数赋值给解码器上下文,这样解码器就等于拿到了视频的基础参数,如:宽、高、像素格式等。
//创建一个解码器 YDecoder* decoder = new YDecoder(); AVCodecContext* context = decoder->Create(vs->codecpar->codec_id, false); //解封装的视频编码参数,传递给解码上下文 avcodec_parameters_to_context(context, vs->codecpar); decoder->setContext(context); decoder->Open();
7.解封装
ret = av_read_frame(ic, &pkt); if (ret != 0) { qDebug() << "解码结束"; PrintErr(ret); break; }
8.解码,渲染,并重复解封装、解码、渲染,直到文件读取完成
if (!decoder->Send(&pkt))break; while (decoder->Receive(frame)) { renderView->DrawFrame(frame); std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 25)); } qDebug() << "视频,width=" << vs->codecpar->width << ",height=" << vs->codecpar->height;
9.最后别忘记销毁操作
if (isRunning) { isRunning = false; if (renderThread && renderThread->joinable()) { renderThread->join(); } if (renderView) { renderView->Close(); renderView = nullptr; } }
浙公网安备 33010602011771号