ビデオプレーヤーのためのFFMPEG + SDL

I.はじめに

ffmpegのベースの学習とSDL、ビデオプレーヤーを作成するには、手を練習する良いプロジェクトです。
多くの人々の原理ビデオプレーヤーは、ブログの話、あなた自身の目的のために本明細書にまとめ、またはいくつかのプロファイルを行いました。

II。ビデオプレーヤーの基本原理

image.png

2.1カプセル化解除

したがって、最初のステップは、ビデオファイルは、実質的に良好なオーディオおよびビデオデータは、ビデオストリームに、最初はそれをデカプセル化形成するために一緒にパッケージされ、オーディオストリームが符号化されたデータを圧縮符号化された映像ファイルで取得します。一般的なパッケージング形式はMP4、MKV、FLV、AVI、ある RMVB、TS など 例えば、FLV後のファイル形式はH.264エンコードされたビデオストリームとエンコードされたオーディオストリームのAACを得ることが可能とデカプセル化。
FFMPEGでは、次のように、カプセル化解除のプロセスがある:
image.png
このステップでは、カプセル化を解く構造のコンテキストを取得することが最も重要である「AVFormatContext * m_pFormatCtx」だけでなく、オーディオとビデオは、次の我々のインデックスをデコードするストリーム。

2.2デコード

生データは、実質的に符号化されたデータが圧縮され、復号化プロセスは、PCMオーディオは、典型的には、非圧縮オーディオ/ビデオの生データ、RGBまたは一般YUVビデオデータをデコードにデータ圧縮H.264、AACなどでありますサンプリングデータ。
次のように復号化処理を要約することができます。
image.png

2.3 SDL2プレイ動画データ

我们都知道视频其实都是由连续的一帧帧图像快速播放形成的动态效果,一般视频都设置成了25帧,即1s内播放25幅图片。
我们使用SDL2库来播放视频。这和我之前的SDL2学习(一): 显示一张图片中写到的SDL2显示一张图片就关联了起来,不过这里更加复杂点。
在视频解码完后,我们在avcodec_receive_frame得到的AVFrame对象,就是视频的一帧数据。我们要做的是将这一帧的数据显示到SDL的Render中。总体流程如下:
image.png
首先我们需要对得到的AVFrame数据进行大小格式的变换,这里使用sws_scale函数实现,之后就是更新SDL中的Texture和Render了。下面是关键代码:

AVFrame *frame = m_videoFrameQueue.front();
m_videoFrameQueue.pop();

AVFrame *frameYUV = av_frame_alloc();
int ret = av_image_alloc(frameYUV->data, frameYUV->linesize, m_sdlRect.w, m_sdlRect.h, AV_PIX_FMT_YUV420P, 1);
//Convert image
if (m_imgConvertCtx)
{
sws_scale(m_imgConvertCtx, frame->data, frame->linesize, 0, m_videoCodecParams.height, frameYUV->data, frameYUV->linesize);
SDL_UpdateYUVTexture(m_sdlTexture, NULL, frameYUV->data[0], frameYUV->linesize[0], frameYUV->data[1], frameYUV->linesize[1], frameYUV->data[2], frameYUV->linesize[2]);
SDL_RenderClear(m_sdlRender);
SDL_RenderCopy(m_sdlRender, m_sdlTexture, NULL, &m_sdlRect);

// Present picture
SDL_RenderPresent(m_sdlRender);
}

2.4 SDL2播放音频数据

对于音频数据,avcodec_receive_frame后得到的AVFrame是音频的pcm数据,但是它不向视频那样表示"一帧",它可能包含很多的sample,即多次的采样数据。
播放音频,同样需要对音频数据进行格式转换,以支持音频设备的播放。音频格式转换主要通过swr_convert函数完成。转换后的音频数据可以放到一个公共缓冲区中。
播放音频使用SDL_OpenAudio函数,它需要闯入一个SDL_AudioSpec结构体用于设置播放参数,其中需要设置一个callback用于音频设备取数据时执行,因此我们需要在这个回调里向音频设备"喂"数据:

SDL_AudioSpec m_sdlAudioSpec;
auto audioCtx = m_audioDecoder.GetCodecContext();

m_sdlAudioSpec.freq = audioCtx->sample_rate; //根据你录制的PCM采样率决定
m_sdlAudioSpec.format = AUDIO_S16SYS;
m_sdlAudioSpec.channels = audioCtx->channels;
m_sdlAudioSpec.silence = 0;
m_sdlAudioSpec.samples = SDL_AUDIO_BUFFER_SIZE;
m_sdlAudioSpec.callback = &SDLVideoPlayer::ReadAudioData;
m_sdlAudioSpec.userdata = NULL;

int re = SDL_OpenAudio(&m_sdlAudioSpec, NULL);
if (re < 0)
{
    std::cout << "can't open audio: " << GetErrorInfo(re);
}
else
{
    //Start play audio
    SDL_PauseAudio(0);
}

void SDLVideoPlayer::ReadAudioData(void *udata, Uint8 *stream, int len) {
    SDL_memset(stream, 0, len);
    //需要向stream中填充len长度的音频数据
    ...
    SDL_MixAudio(stream, m_audioPcmDataBuf, len, g_volum);
}

2.5 音视频同步的设计

オーディオとビデオ、オーディオを再生し、その後、あなたが使用できる2つのスレッドをすることができ、データのコールバックのセットに直接供給され、私たちはビデオを使用すると、2つの再生速度の統一を必要とする再生速度を制御する必要が所有しています。
オーディオとビデオの同期再生中に、マスタークロックとして、基本クロックを決定することで、マスタークロック同期基準としては、継続的に再生速度を調整するために、現在のストリームとマスタークロックの時間差を決定します。マスタークロックの異なる種類によるとに分けることができます。

  • マスタークロックとしてビデオクロックに同期したオーディオ、ビデオ、
  • ビデオの同期にオーディオマスタークロックとしてオーディオクロック。
  • オーディオとビデオは外部クロックに同期されています。

多くの場合、オーディオ再生デバイスキャッシュに大量のデータを送信し、人々に高いオーディオ再生感度なので、主にオーディオクロックは、より合理的かつ簡単な方法ですので。具体的な実装は次のとおりです。

  1. あなたが現在のオーディオ再生の進行状況を示す開始PTSタイムスタンプデータに記録された音声データを送るたびに、
  2. 各リフレッシュ写真、レコードが現在の画像フレームのタイムスタンプをPTS。
  3. 画像によれば、現在のオーディオPTSを記録しながら両者の待ち時間の遅延を記録し、記録PTS。
  4. 画像を更新する、遅延の値は、遅延が0に設定されている、2つの通常プラスのオーディオとビデオの間の遅延間隔のビデオ待ち時間の調整、現在のビデオ速くオーディオよりもあれば、に応じて決定され、オーディオビデオ場合よりも速く、次いで均一になるまで現在のビデオフレーム、およびオーディオの時間を破棄します。

おすすめ

転載: www.cnblogs.com/xl2432/p/11791295.html