7

StreamPlayer に不可解な問題があり、何か助けが必要です。

私が達成する必要がある主な目標は、MPEG-2 トランスポート ストリームを可能な限り最小のレイテンシで再生できる StreamPlayer です。このために、私はこのアプローチに従っています:

ストリームは、Java ベースの TS パーサーによって解析されます。MediaExtractor に似た、正常に動作する TSExtractor を実装しました。MediaExtractor を使用して可能なのと同じ方法で、選択したトラックのすべてのメディア サンプルを受信できます。

extractor.readSampleData(...);
extractor.advance();

AAC データをデコードするには、MediaCodec のインスタンスを作成して構成します。MediaExtractor クラスを使用して、これは通常

MediaFormat mediaFormat = extractor.getTrackFormat(i);
decoder = MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME));
decoder.configure(mediaFormat, null, null, 0);

TSExtractor.getTrackFormat(int track) メソッドで MediaFormat を初期化する必要があるため、使用します

MediaFormat mf = MediaFormat.createAudioFormat ("audio/mp4a-latm", getSampleRate(), getChannelCount());

すべての AAC サンプルには ADTS が含まれているため、

mediaFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1); 

この投稿を読んだ後、「csd-0」キーを使用して ESDS フレームを最終的に追加します

mediaFormat.setByteBuffer("csd-0", ByteBuffer.allocate(2).put(new byte[]{(byte) 0x11, (byte)0x90}));

ここで、値 0x11 と 0x90 は ADTS から抽出されます。

AAC サンプルをデコードしたいときは、デコーダーがポストします。

AAC decoder returned error 4097, substituting silence

ログに。

TSExtractor がサンプルを正しく抽出することを確認するために、VLC を使用して同じストリームを記録し、トランスコーディングせずに mp4 ファイルに再多重化し、生のストリームを変更しませんでした。これで、記録した mp4 ファイルで MediaExtractor を初期化し、TSExtractor と MediaExtractor によって作成されたサンプルを比較できます。トレイルアンドエラーを使用すると、非常に奇妙な動作が見つかりました。

MediaExtractor によって作成された mediaFormat を使用して MediaCodec を構成すると、MediaCodec は TSExtractor によって返された AAC サンプルを問題なくデコードします。私の TSExtractor によって作成された HashMap を基本的にラップする MediaFormat と MediaExtractor によって作成されたものを比較すると、次のような違いがあります。

MediaExtractor によって作成されました:

mediaFormat: {max-input-size=1212, durationUs=77428875, is-adts=1, channel-count=2, mime=audio/mp4a-latm, csd-0=java.nio.ByteArrayBuffer[position=0,limit =2,容量=2]、サンプルレート=48000}

TSExtractor によって作成:

mediaFormat: {is-adts=1, channel-count=2, mime=audio/mp4a-latm, csd-0=java.nio.ByteArrayBuffer[position=2,limit=2,capacity=2], sample-rate= 48000}

TSExtractor によって作成された MediaFormat を MediaExtractor によって作成されたものと同様に採用しても、デコーダーは、作成された自己を使用して同じエラーを出し、他のものを使用して問題なくデコードします。

どんな助けでも本当に役に立ちます。

4

4 に答える 4

6

理由はよくわかりませんが、この方法で「csd-0」ByteBuffer を初期化することがわかりました。

mediaFormat.setByteBuffer("csd-0", ByteBuffer.allocate(2).put(new byte[]{(byte) 0x11, (byte)0x90}));

動作しませんが、この方法で初期化します

byte[] bytes = new byte[]{(byte) 0x11, (byte)0x90};
ByteBuffer bb = ByteBuffer.wrap(bytes);
mediaFormat.setByteBuffer("csd-0", bb);

します。

ところで、これら2つのbyteBuffersを使用して比較します

bb1.equals(bb2);

true を返します。

非常に奇妙な!

于 2013-09-16T14:24:18.113 に答える
5

csd-0 の値は、ADTS ヘッダーによって異なります。

ADTS ヘッダーの長さは最大 9 バイトです。csd-0 を生成するには、ヘッダーの 2 番目と 3 番目のバイトが必要です。

int profile = (header[2] & 0xC0) >> 6;
int srate = (header[2] & 0x3C) >> 2;
int channel = ((header[2] & 0x01) << 2) | ((header.[3] & 0xC0) >> 6)

ByteBuffer csd = ByteBuffer.allocate(2);
csd.put(0, (byte)( ((profile + 1) << 3) | srate >> 1 ) );
csd.put(1, (byte)( ((srate << 7) & 0x80) | channel << 3 ) );

これで、この aac オーディオ ストリームに有効な csd-0 を取得できました。

于 2015-08-22T08:42:34.390 に答える
3

CSDを計算するための上記のコードをありがとう。残念ながら、これは私にとってはうまくいきませんでした。私のデコーダは、上記の csd 設定で失敗していました。最後に、問題を見つけました。ドキュメントによると、CSD の最初の「5 ビット」はオブジェクト タイプ (プロファイル) です。上記のコード プロファイルは 4 ビットのみに追加されます。したがって、以下のようにコードを変更するとうまくいきます

  int profile = (header[2] & 0xC0) >> 6;
  int srate = (header[2] & 0x3C) >> 2;
  int channel = ((header[2] & 0x01) << 2) | ((header.[3] & 0xC0) >> 6)

  ByteBuffer csd = ByteBuffer.allocate(2);
  csd.put(0, (byte)(profile << 3 | srate >> 1));
  csd.put(1, (byte)((srate & 0x01) << 7 | channel << 3)); 
于 2016-03-29T08:02:09.950 に答える
3

失敗した場合、最初に ByteBuffer の巻き戻しメソッドを呼び出す必要があるかもしれません。注意深く見ると、MediaExtractor と TSExtractor の間で位置が異なることがわかります。

csd-0=java.nio.ByteArrayBuffer[位置=0、制限=2、容量=2]

csd-0=java.nio.ByteArrayBuffer[位置=2、制限=2、容量=2]

ByteBuffer の equals は、不一致になるまで位置の後のバイトのみを比較します。あなたの場合、1つのバッファがすでに最後に配置されているため、不一致はありません。

于 2013-11-24T00:54:02.650 に答える