ffmpeg.exe
コンバーターは、次の 3 つの状況でトランスコーディングまたはカプセル化を終了します。
1、入力ファイルの最後まで読み取ります。
2.-t 60
このオプションは コマンド ラインの入力ファイルで使用され 、入力ファイルは 60 秒間だけ処理のために読み取られ、60 秒間のデータを処理した後に終了します。
3. コマンドラインで出力ファイルに -t 60
オプションを使用すると、60 秒間のデータのみがファイルに出力され、保存されます。60 秒分のストレージが十分になると、自動的に終了します。
最初のケースが最も一般的で、2 番目と 3 番目のケースは trim
フィルターを通じて実現されます。
この記事では主に最初のケースのロジックを分析します。読者は、2 番目と 3 番目のケースを自分で調べてください。
次の API 関数は、 AVERROR_EOF のエラー コードを使用して操作の終了を表します。
1、、、 av_read_frame()
AVERROR_EOF は、入力ファイルの終わりが読み取られたことを意味します。
2、 avcodec_send_packet()
、デコーダに送信するときに AVERROR_EOF を返すこともできます。これは 通常AVPacket
、前にこのデコーダに pkt
== null
または pkt.size
== 0を送信しているためです。AVPacket
2 回目に nullまたは 0 に等しいサイズを デコーダに 送信するAVPacket
と、avcodec_send_packet()
関数は AVERROR_EOF を返します。
3、 avcodec_receive_frame()
、デコーダからの読み取りで も AVERROR_EOF が返される場合がありますが、前提として、 このデコーダに以前に == または == 0AVFrame
を送信したことがあります 。この null を送信し た後 、データがなくなるまでデコーダを読み続けます。データがなくなると、AVERROR_EOF が返されます。 この関数には実際には 2 つのエラー コードがあり、1 つは 、 もう 1 つは です 。これは、デコーダには一時的に読み取るデータがないため、さらに を入力する必要があることを意味します 。これは、デコーダがデコードを終了し、これ以上 入力がないことを意味します。4、出口フィルタからの 読み取り も を返す場合があります が、 すべての 入口フィルタが閉じていることが前提となります 。pkt
null
pkt.size
AVPacket
avcodec_receive_frame()
ERROR(EAGAIN)
AVERROR_EOF
ERROR(EAGAIN)
AVPacket
AVERROR_EOF
AVPacket
av_buffersink_get_frame_flags()
buffersink
AVFrame
AVERROR_EOF
av_buffersrc_close()
5、 avfilter_graph_request_oldest()
この関数は、どのくらいのフィルター コンテナーが AVFrame
読み取られていないかを返すものであり、 すべての エントリ フィルターがAVERROR_EOF
閉じられているか、トリム フィルターが使用されている場合は 、 6 の時点で自動的に返されます。 エンコーダからの読み取りでは、 前にこのエンコーダに null を送信した場合に、AVERROR_EOF が返される場合もあり ます。av_buffersrc_close()
AVERROR_EOF
avcodec_receive_packet()
AVPacket
AVFrame
ffmpeg.exe
コンバータが上記の API 関数の AVERROR_EOF 戻り値をどのように使用するかを見てみましょう 。
1 つ目は、 av_read_frame()
関数が AVERROR_EOF を返す場合、どの分岐ロジックが実行されるかということです。次のように:
av_read_frame()
関数は get_input_packet()
関数内にカプセル化されます。
process_input_packet()
ファイルの最後まで読み取った後、関数が 1 つ NULL
ずつ 渡されることがわかります AVPacket
。
注:入力ファイルのすべての入力ストリームはNULL
1 つずつ 渡されます AVPacket
。
「decode_video ビデオフレームをデコードする」で述べたように、NULL はprocess_input_packet()
size = 0 に AVPacket
変換され ます。decode_video()
size = 0
AVPacket
これにより、次のようにデコーダがフラッシュを開始します 。
また、上記の avcodec_send_packet()
戻り値にも注意してください。次のように、 が返されます AVERROR_EOF
が ffmpeg.c
、この状態は処理されません。
//如果是 AVERROR_EOF 直接跳过。
if (ret < 0 && ret != AVERROR_EOF)
return ret;
decode()
関数が を返す と AVERROR_EOF
、どのような動作になりますか? 次のように:
decode()
パブリック関数なので ルートでラップされて いるため 、 ルート の戻り値 decode_video()
は decode_audio()
decode_video()
decode_audio()
AVERROR_EOF
decode_video()
関数が戻った 場合は AVERROR_EOF
どうなりますか? 次のように:
それが 1 につながることがわかります eof_reached
。ちなみに、break は while (ist->decoding_needed){...}
デコード ループから飛び出します。
次に、次のように、呼び出し send_filter_eof()
関数がストリームにバインドされているすべてのエントリ フィルターを閉じます。
process_input_packet()
これにより、次のように 0 が返されます 。
しかし process_input_packet()
これの戻り値、個人的には全然ダメだと思うんです、何箇所か1を返しても0になってしまいます。
の戻り値は役に立ちませんが process_input_packet()
、NULL を渡すと、実行後にデコーダ内のすべてが AVFrame
吐き出され、処理のためにフィルタに送信されます。
そして、 ifile->eof_reached
次のように 1 に設定されます。
したがって、デコーダ全体の終了プロセスはEOF を返し、av_read_frame()
デコーダがすべてのファイルを吐き出し AVFrame
、すべての AVFrame
イベントをフィルタに渡してからエントリ フィルタを閉じ、 ifile->eof_reached
それを 1 に設定します。これは、次のことを意味します。入力ファイルは処理されました。
全体のプロセスは次のとおりです。
上の図にはもう 1 つ重要な点があります。それは、 ifile->eof_reached
1 に設定するとすぐに戻ります AVERROR(EAGAIN)
。 関数が を返す
と 、 実行されなくなります。 process_input()
AVERROR(EAGAIN)
transcode_step()
reap_filters()
、直接 0 を返します。次のように:
入力ストリームが処理され、デコーダがそれを吐き出し AVFrame
、イングレス フィルタが無効になった場合にifile->eof_reached
1 に設定されます send_filter_eof()
。
入口フィルターが閉じるか、av_buffersink_get_frame_flags()
戻る avfilter_graph_request_oldest()
可能性があります AVERROR_EOF
。
av_buffersink_get_frame_flags()
まず、次のように、AVERROR_EOF を返す関数の動作を確認してください 。
av_buffersink_get_frame_flags()
内部にカプセル化されています reap_filters()
。
av_buffersink_get_frame_flags()
AVERROR_EOF を返す関数により NULL が渡されることが わかります が、実際のデバッグ後、この NULL によってエンコーダがフラッシュされ、 エンコーダにdo_video_out()
NULL が渡されないことがわかります。 AVPacket
NULL を渡して AVPacket
エンコーダーをリフレッシュします。これは flush_encoders()
関数内で行われます。
したがって、 av_buffersink_get_frame_flags()
AVERROR_EOF を返す関数の動作に注意を払う必要はありません。
補足:do_video_out(of, ost, NULL);
エンコーダのリフレッシュには使用されません。次のように、フレーム レート変更の残りのフレームをリフレッシュするために使用する必要があります。
//next_picture 等于 NULL
if (!next_picture) {
//end, flushing
nb0_frames = nb_frames = mid_pred(ost->last_nb0_frames[0],
ost->last_nb0_frames[1],
ost->last_nb0_frames[2]);
}
avfilter_graph_request_oldest()
ただし、関数 return AVERROR_EOF
の場合には特別な注意を払う必要があります。
エントリフィルターは以前に閉じられているため、コンテナーに読み取るデータがない場合は、コンテナー avfilter_graph_request_oldest()
が戻ります。
AVERROR_EOF
、次のように:
avfilter_graph_request_oldest()
を返し AVERROR_EOF
、結果として 2 つの動作が発生します。
1、 reap_filter(1)
パラメータは 1 なので、 フレーム レート変更の残りのフレームはフラッシュされますが、エンコーダはフラッシュされません。
2、、、 出力ストリームの状態close_output_stream(graph->outputs[i]->ost)
を設定します 。finished
ost->finished |= ENCODER_FINISHED;
この finished
状態は特に重要です。
前のフローチャートでは、次のようにffmpeg.exe
ループで実行されること がわかります。transcode_step()
ffmpeg.exe
どのような状況でコンバータが上記のサイクルから抜け出すのでしょうか while
?
回答: finished
次のように、出力ストリームの状態によって判断されます。
この時点で、ffmpeg.exe の終了処理ロジックは 80% 完了し、 while (!received_sigterm)
ループから抜け出しました。
ただし、この時点ではエンコーダはフラッシュされておらず、 do_video_out(of, ost, NULL);
エンコーダもフラッシュされていません。
エンコーダのフラッシングは、 while (!received_sigterm)
ループを抜けた後に次のように動作します。
flush_encoder()
機能はそれほど複雑ではないので説明は省略します。
最後に、 av_write_trailer()
次のようにファイルの末尾情報を書き込む操作がまだ残っています。
さまざまなリソースを解放するための操作もいくつかありますが、説明は省略します。
ffmpeg.exe
コンバーターの変換が完了し、解析が完了します。
オリジナルの FFmpeg コンバータのトランスコーディング終了の分析 - Nuggets
★記事末尾の名刺では、オーディオ・ビデオ開発学習教材(FFmpeg、webRTC、rtmp、hls、rtsp、ffplay、srs)やオーディオ・ビデオ学習ロードマップ等を無料で受け取ることができます。
以下を参照してください!