セットアップは次のとおりです。
- マルチキャスト サーバー 1000Mbs、UDP、Mpeg2-TS Part 1 (H.222) ストリーミング ライブ TV チャンネル。
- クアッドコア 1.5Ghz Android 4.2.2 GLES 2.0 レンダラー。
- FFMpeg ライブラリ。
- Eclipse Kepler、Android SDK/NDK など。Windows 8.1 上で動作します。
- 出力画面 1920 x 1080、2048 x 1024 のテクスチャを使用し、毎秒 35 ~ 45 フレームを取得しています。
アプリ:
- レンダラー スレッドは継続的に実行され、メディア イメージの準備ができたときに GPU にセグメントをアップロードすることで単一のテクスチャを更新します。
- メディア ハンドラー スレッドは、サーバー/ローカル ストレージからメディアをダウンロードして処理します。
- UDP パケットをバッファリングするためのビデオ スレッドと、パケットをフレームにデコードするためのビデオ スレッド。
ffmpeg を UDP ストリームに正常に接続しており、パケットはバッファリングされ、一見正常にデコードされています。パケット バッファは十分にあり、アンダー/オーバーフローはありません。私が直面している問題は、フレームを切り刻んでいるように見えることです (つまり、非常に多くのフレームのうち 1 つだけを再生しています)。I/P/B フレームを区別する必要があることは理解していますが、現時点では手がかりがありません。Iフレームを検出するためにハックを試みましたが、役に立ちませんでした。さらに、フレームを画面の 4 分の 1 未満にレンダリングするだけです。だから私は全画面デコードを使用していません。
デコードされたフレームは、ページのティアリングをカットするために別のバッファーにも格納されます。私が変更したバッファの数も、1 から 10 に変更しましたが、うまくいきませんでした。
OpenMax IL について私が見つけたところによると、それは MPEG2-TS Part 3 (H.264 および AAC) のみを処理しますが、独自のデコーダーを使用できます。独自のデコード コンポーネントを追加できることを理解しています。このルートを試す価値はありますか、それともffmpegを続行する必要がありますか?
フレーム デコーダー (レンダラーのみが準備が整ったときにフレームを変換およびスケーリングします) /* * この関数はパケットを処理し、* フレームの準備が整うまで、またはパケットがなくなるまでデコードを続けます */
while (packetsUsed[decCurrent])
{
hack_for_i_frame:
i = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packets[decCurrent]);
packetsUsed[decCurrent] = 0; // finished with this one
i = packets[decCurrent].flags & 0x0001;
decCurrent++;
if (decCurrent >= MAXPACKETS) decCurrent = 0;
if (frameFinished)
{
ready_pFrame = pFrame;
frameReady = true; // notify renderer
frameCounter++;
if (frameCounter>=MAXFRAMES) frameCounter = 0;
pFrame = pFrames[frameCounter];
return 0;
}
else if (i)
goto hack_for_i_frame;
}
return 0;
パケット リーダー (pthread として生成) void *mainPacketReader(void *voidptr) { int res;
while ( threadState == TS_RUNNING )
{
if (packetsUsed[prCurrent])
{
LOGE("Packet buffer overflow, dropping packet...");
av_read_frame( pFormatCtx, &packet );
}
else if ( av_read_frame( pFormatCtx, &packets[prCurrent] ) >= 0 )
{
if ( packets[prCurrent].stream_index == videoStream )
{
packetsUsed[prCurrent] = 1; // flag as used
prCurrent++;
if ( prCurrent >= MAXPACKETS )
{
prCurrent = 0;
}
}
// here check if the packet is audio and add to audio buffer
}
}
return NULL;
レンダラーは単にこれを行うだけです // テクスチャはこの関数を呼び出す前に既にバインドされています
if ( frameReady == false ) return;
AVFrame *temp; // set to frame 'not' currently being decoded
temp = ready_pFrame;
sws_scale(sws_ctx,(uint8_t const* const *)temp->data,
temp->linesize, 0, pCodecCtx->height,
pFrameRGB->data, pFrameRGB->linesize);
glTexSubImage2D(GL_TEXTURE_2D, 0,
XPOS, YPOS, WID, HGT,
GL_RGBA, GL_UNSIGNED_BYTE, buffer);
frameReady = false;
過去には、libvlc にもオーディオ同期の問題があったため、ffmpeg を使用してすべてのロバ作業をゼロから行うことにしました。
ビデオ再生の途切れを止める方法(VLCプレーヤーでうまく機能する)、またはおそらく別の方法でダウンする方法についての指針があれば、真剣に感謝します。
EDIT Iフレームのハックを削除しました(まったく役に立たない)。sws_scale 関数をレンダラーからパケット デコーダーに移動します。そして、udp パケット リーダー スレッドはそのままにしました。
その間に、パケット リーダー スレッドとパケット デコーダ スレッドの優先度をリアルタイムに変更しました。それを行ってから、ドロップされたパケットが大量に流されることはありません。