序文
最近、ffmpeg を使ってビデオを解析する方法を学んでいます。インターネットでの検索方法は似ています。デコード関数 avcodec_send_packet と avcodec_receive_frame について直接話します。これら 2 つは一緒であり、同時に出現する必要があります。ビデオ ファイルを読み込みます。抽出されたパケットを videoQueue または audioQueue に配置し、SDL を使用して videoThread を作成し、ビデオ キューから継続的にパケットを取り出してデコーダに送信します。つまり、avcodec_send_packet を使用してパケットをデコーダに送信します。デコーダを起動し、avcodec_receive_frame を使用して ffmpeg からフレームを解析します。それを取り出しますが、プログレス バーで現在のフレームの時間を確認するか、デコードされた Frame->pts を直接出力します。最後には常に数フレーム少なくなります。 、とても憂鬱です
1. フレーム損失の原因
デコーダ内部にはキャッシュキューがあり、最初に数フレームのデータを保存し、その後有効なフレームの出力を開始するため、最終的には数フレームのデータが残ります。ビデオのサイズによってはデコーダ内にフレームが残っているため、パケット数が一定ではないため、最後に出力されないフレームが残り、フレームロスが発生します。
2. 解決策
最初のアイデアは、フレーム損失を解決する方法をインターネットで検索することですが、数が少なく、ソース コードもありません。特に ffmpeg の avcodec_send_packet と avcodec_receive_frame のフレーム損失解決策は少ないため、公式ドキュメントがついに見つかりました。ちょっとした手掛かりとして、次の図が avcode.h にあります。これは、これら 2 つの関数によって宣言されたヘッダー ファイルでもあります。
上記は、必要に応じて、またはパフォーマンスを向上させるために、コーデックが最後に数フレームのデータをキャッシュする場合があり、コーデックを更新する必要があることを示しています。また、アイデアも提供します。
1. 空のパケットを avcodec_send_packet に送信します。
上記の公式ドキュメントで提供されているアイデアに従って、空のパケットをデコーダーに送信する必要があります。コードは次のとおりです。
packet->data = nullptr;
packet->size = 0;
avcodec_send_packet(pVideoCodecCtx, packet);
2. avcodec_receive_frame を呼び出して
空のパケットを送信した後、avcodec_receive_frame が AVERROR_EOF を返すまで、avcodec_receive_frame を呼び出してデコードされたフレームを取り出します。
/**队列里取数据**/
if (packet_queue_get(&pVideosState->videoQueue, packet, 0) <= 0)
{
if (pVideosState->readFinished)
{
/**队列里面没有数据了且读取完毕了**/
while(1)
{
packet->data = nullptr;
packet->size = 0;
avcodec_send_packet(pVideoCodecCtx, packet);
ret = avcodec_receive_frame(pVideoCodecCtx, pFrame);
if(ret == AVERROR_EOF)
break;
//进行你所需的一系列操作
}
//这个循环跳出后就全部取出解码器缓存的数据了
}