1

セットアップは次のとおりです。

  1. マルチキャスト サーバー 1000Mbs、UDP、Mpeg2-TS Part 1 (H.222) ストリーミング ライブ TV チャンネル。
  2. クアッドコア 1.5Ghz Android 4.2.2 GLES 2.0 レンダラー。
  3. FFMpeg ライブラリ。
  4. Eclipse Kepler、Android SDK/NDK など。Windows 8.1 上で動作します。
  5. 出力画面 1920 x 1080、2048 x 1024 のテクスチャを使用し、毎秒 35 ~ 45 フレームを取得しています。

アプリ:

  1. レンダラー スレッドは継続的に実行され、メディア イメージの準備ができたときに GPU にセグメントをアップロードすることで単一のテクスチャを更新します。
  2. メディア ハンドラー スレッドは、サーバー/ローカル ストレージからメディアをダウンロードして処理します。
  3. 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 パケット リーダー スレッドはそのままにしました。

その間に、パケット リーダー スレッドとパケット デコーダ スレッドの優先度をリアルタイムに変更しました。それを行ってから、ドロップされたパケットが大量に流されることはありません。

4

1 に答える 1

1

(最終的に回答ボタンがどこにあるかを把握した後)

I-Frame ハックはまったく役に立たず、レンダラーでのスレッドの過負荷を軽減するために、sws_scale がデコーダー スレッドに移動されました。

また、sws_scale を完全に取り除き、個々の YUV フレームを GPU にアップロードし、フラグメント シェーダーを使用して RGB に変換することで、これから先に進みました。

ここで YUV を RGB に変換するためのシェーダーに興味がある人は誰でも、それは非常に簡単です:

頂点シェーダー

attribute vec4 qt_Vertex;
attribute vec2 qt_InUVCoords;
attribute vec4 qt_InColor;
uniform mat4 qt_OrthoMatrix;

varying vec2 qt_TexCoord0;
varying vec4 qt_OutColor;

void main(void)
{
    gl_Position = qt_OrthoMatrix * qt_Vertex;
    qt_TexCoord0 = qt_InUVCoords;
    qt_OutColor = qt_InColor;
}

フラグメント シェーダー:

precision mediump float;

uniform sampler2D qt_TextureY;
uniform sampler2D qt_TextureU;
uniform sampler2D qt_TextureV;

varying vec4 qt_OutColor;

varying vec2 qt_TexCoord0;

const float num1 = 1.403;   // line 1

const float num2 = 0.344;   // line 2
const float num3 = 0.714;

const float num4 = 1.770;   // line 3
const float num5 = 1.0;

const float half1 = 0.5;

void main(void)
{
    float y = texture2D(qt_TextureY, qt_TexCoord0).r;
    float u = texture2D(qt_TextureU, qt_TexCoord0).r - half1;
    float v = texture2D(qt_TextureV, qt_TexCoord0).r - half1;

    gl_FragColor = vec4( y + num1 * v,
                         y - num2 * u - num3 * v,
                         y + num4 * u, num5) * qt_OutColor;
}
于 2014-10-10T11:33:38.240 に答える