2017-02-28 24 views
0

AndroidEncoderを使用して、RTMPネットワーク経由で送信し、MediaMuxerを使用してMP4ファイルにローカルに保存するAACオーディオを含むH264ビデオを制作しています。MediaMuxerでブラックビデオが表示される場合があります

ブロードキャストはRTMPよりうまく機能しますが、ローカルに保存すると、結果のビデオが正常である場合もありますし、音声付きの黒いフレーム(または一部の情報ブロックがある最初の部分フレーム)があり、 、次にランダムなFPSスピードでビデオを再生します。

これは私のマクサーラッパークラスとMPEG4Writer SDKクラスによって示される出力です:

02-28 11:57:38.521 6420-6475/com.myapp.broadcast W/AndroidMuxer: addTrack: Requested adding of track to class AndroidMuxer of type AUDIO 
02-28 11:57:38.522 6420-6475/com.myapp.broadcast W/AndroidMuxer: addTrack: Adding track for audio/mp4a-latm 
02-28 11:57:38.522 6420-6475/com.myapp.broadcast W/AndroidMuxer: addTrack: Track index to class AndroidMuxer of type AUDIO, track saved for future use! 
02-28 11:57:38.621 6420-6470/com.myapp.broadcast W/AndroidMuxer: addTrack: Requested adding of track to class AndroidMuxer of type VIDEO 
02-28 11:57:38.621 6420-6470/com.myapp.broadcast W/AndroidMuxer: addTrack: Adding track for video/avc 
02-28 11:57:38.621 6420-6470/com.myapp.broadcast W/AndroidMuxer: addTrack: All tracks added, marking Local muxer as ready! 
02-28 11:57:38.622 6420-6470/com.myapp.broadcast W/AndroidMuxer: addTrack: Track index to class AndroidMuxer of type VIDEO, track saved for future use! 
02-28 11:57:52.050 6420-6420/com.myapp.broadcast I/AndroidMuxer: init: Added audio track widh id 0 
02-28 11:57:52.050 6420-6420/com.myapp.broadcast I/AndroidMuxer: init: Added video track widh id 1 
02-28 11:57:52.050 6420-6420/com.myapp.broadcast W/AndroidMuxer: init: Muxer was successfully created! Starting to record 
02-28 11:57:52.056 6420-6420/com.myapp.broadcast I/MPEG4Writer: limits: 4294967295/0 bytes/us, bit rate: -1 bps and the estimated moov size 3195 bytes 
02-28 11:57:52.057 6420-6420/com.myapp.broadcast W/AndroidMuxer: start: Muxer started! 
02-28 11:57:52.070 6420-6965/com.myapp.broadcast I/MPEG4Writer: setStartTimestampUs: 0 
02-28 11:57:52.070 6420-6965/com.myapp.broadcast I/MPEG4Writer: Earliest track starting time: 0 
02-28 11:57:52.083 6420-6966/com.myapp.broadcast I/MPEG4Writer: setStartTimestampUs: 0 
02-28 11:58:02.617 6420-6475/com.myapp.broadcast W/AndroidMuxer: writeSampleData: All tracks finished! Calling Stop 
02-28 11:58:02.617 6420-6475/com.myapp.broadcast W/AndroidMuxer: stop: Calling stop to the muxer 
02-28 11:58:02.617 6420-6475/com.myapp.broadcast D/MPEG4Writer: Audio track stopping 
02-28 11:58:02.617 6420-6475/com.myapp.broadcast D/MPEG4Writer: Audio track source stopping 
02-28 11:58:02.617 6420-6475/com.myapp.broadcast D/MPEG4Writer: Audio track source stopped 
02-28 11:58:02.617 6420-6965/com.myapp.broadcast I/MPEG4Writer: Received total/0-length (452/0) buffers and encoded 452 frames. - Audio 
02-28 11:58:02.617 6420-6965/com.myapp.broadcast I/MPEG4Writer: Audio track drift time: 0 us 
02-28 11:58:02.617 6420-6475/com.myapp.broadcast D/MPEG4Writer: Audio track stopped 
02-28 11:58:02.618 6420-6475/com.myapp.broadcast D/MPEG4Writer: Video track stopping 
02-28 11:58:02.618 6420-6475/com.myapp.broadcast D/MPEG4Writer: Video track source stopping 
02-28 11:58:02.618 6420-6475/com.myapp.broadcast D/MPEG4Writer: Video track source stopped 
02-28 11:58:02.618 6420-6966/com.myapp.broadcast I/MPEG4Writer: Received total/0-length (315/0) buffers and encoded 315 frames. - Video 
02-28 11:58:02.618 6420-6475/com.myapp.broadcast D/MPEG4Writer: Video track stopped 
02-28 11:58:02.618 6420-6475/com.myapp.broadcast D/MPEG4Writer: Duration from tracks range is [10143021, 10495420] us 
02-28 11:58:02.618 6420-6475/com.myapp.broadcast D/MPEG4Writer: Stopping writer thread 
02-28 11:58:02.620 6420-6964/com.myapp.broadcast D/MPEG4Writer: 0 chunks are written in the last batch 
02-28 11:58:02.620 6420-6475/com.myapp.broadcast D/MPEG4Writer: Writer thread stopped 
02-28 11:58:02.621 6420-6475/com.myapp.broadcast I/MPEG4Writer: The mp4 file will not be streamable. 
02-28 11:58:02.621 6420-6475/com.myapp.broadcast W/AndroidMuxer: stop: muxer stopped! 

ない、ローカルに保存された映像は、同じ映像のライブストリーム大丈夫ではないながらという(同起因しますエンコーディングプロセス)は問題ないので、問題はマルチプレクサ自体(または、より正確には、私がそれを処理する方法)になければなりません。

これは私のマクサーラッパー(重くbigflakeのmediacodec samplesに触発さ)である:

import android.media.MediaCodec; 
import android.media.MediaFormat; 
import android.media.MediaMuxer; 
import android.util.Log; 

import java.io.IOException; 
import java.nio.ByteBuffer; 

/** 
* @hide 
*/ 
public class AndroidMuxer extends Muxer { 
    private static final String TAG = "AndroidMuxer"; 
    private static final boolean VERBOSE = false; 

    private MediaMuxer mMuxer; 
    private boolean mStarted = false; 
    private boolean mStopped = false; 
    private boolean mReadyToStart; 
    private MediaFormat mVideoTrackFormat; 
    private MediaFormat mAudioTrackFormat; 

    private long mStartTimeUs = 0; 
    @Override 
    public void reset() { 
     Log.w(TAG, "reset: Reset is called on LOCAL MUXER!"); 

      mStarted = false; 
      mReadyToStart = false; 
      mNumTracks = 0; 
      mNumTracksFinished = 0; 
      Log.w(TAG, "reset: Reset worked on the Muxer"); 

    } 

    /** 
    * This will only initialize the fields, we only want the real muxer to be initialized 
    * when we go live. 
    * @param outputFile outputPath of the file 
    * @param format Format of the muxer (will always be MPEG4 TBH) 
    */ 
    public AndroidMuxer(String outputFile, FORMAT format) { 
     super(outputFile, format); 
     mStarted = false; 
    } 

    @Override 
    public void init() { 
     if(!mReadyToStart) { 
      return; 
     } 

     if(mStarted){ 
      Log.i(TAG, "init: Muxer already created! Skipping!"); 
      return; 
     } 


     try { 
      switch (mFormat) { 
       case MPEG4: 
        mMuxer = new MediaMuxer(mOutputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); 
        int audioTrack = mMuxer.addTrack(mAudioTrackFormat); 
        Log.i(TAG, "init: Added audio track widh id " + audioTrack); 
        mAudioTrackIndex = audioTrack; 

        int videoTrack = mMuxer.addTrack(mVideoTrackFormat); 
        Log.i(TAG, "init: Added video track widh id " + videoTrack); 
        mVideoTrackIndex = videoTrack; 
        Log.w(TAG, "init: Muxer was successfully created! Starting to record"); 
        start(); 
        break; 
       default: 
        throw new IllegalArgumentException("Unrecognized format!"); 
      } 
     } catch (IOException e) { 
      throw new RuntimeException("MediaMuxer creation failed", e); 
     } 
    } 

    public static AndroidMuxer create(String outputFile, FORMAT format) { 
     return new AndroidMuxer(outputFile, format); 
    } 

    @Override 
    public int addTrack(MediaFormat trackFormat, AndroidEncoder.TrackType trackType) { 
     Log.w(TAG, "addTrack: Requested adding of track to class " + getClass().getSimpleName() + " of type " + trackType); 
     //Super method just keeps track of amount of tracks added 
     super.addTrack(trackFormat, trackType); 
     Log.w(TAG, "addTrack: Adding track for " + trackFormat.getString(MediaFormat.KEY_MIME)); 
     if (mStarted) 
      throw new RuntimeException("format changed twice"); 

     //What we do is store the media info for later use once the muxer is created. 
     //This is done to prevent file creation until the event goes live. 

     if(trackType == AndroidEncoder.TrackType.VIDEO){ 
      mVideoTrackFormat = trackFormat; 
     } 
     else if(trackType == AndroidEncoder.TrackType.AUDIO){ 
      mAudioTrackFormat = trackFormat; 
     } 



     if (allTracksAdded()) { 
      Log.w(TAG, "addTrack: All tracks added, marking Local muxer as ready!"); 
      markReadyToStart(); 
     } 
     Log.w(TAG, "addTrack: Track index to class " + getClass().getSimpleName() + " of type " + trackType + ", track saved for future use!"); 
     //Return dummy 
     return 1; 
    } 


    /** 
    * 
    * Marking the Muxer as ready to be started but not yet! 
    * 
    */ 
    private void markReadyToStart() { 
     mReadyToStart = true; 
    } 



    private void start() { 
     if(mStarted){ 
      Log.i(TAG, "start: Skipped start due to muxer already started"); 
     } 
     mStartTimeUs = 0; 
     mMuxer.start(); 
     Log.w(TAG, "start: Muxer started!"); 
     mStarted = true; 
    } 

    protected void stop() { 
     if(mStarted) { 
      Log.w(TAG, "stop: Calling stop to the muxer"); 
      mMuxer.stop(); 
      Log.w(TAG, "stop: muxer stopped!"); 
      mStarted = false; 
      mStopped = true; 
      mReadyToStart = false; 
     } 
    } 

    @Override 
    public void release() { 
     super.release(); 
     if(mStopped) { 
      mMuxer.release(); 
      mStopped = false; 
      mStarted = false; 
     } 
    } 

    @Override 
    public boolean isStarted() { 
     return mStarted; 
    } 

    @Override 
    public void writeSampleData(MediaCodec encoder, int trackIndex, int bufferIndex, ByteBuffer encodedData, MediaCodec.BufferInfo bufferInfo) { 
     if(!mStarted) { 

      return; 
     } 

     super.writeSampleData(encoder, trackIndex, bufferIndex, encodedData, bufferInfo); 
     if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { 
      // MediaMuxer gets the codec config info via the addTrack command 
      if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG"); 
      return; 
     } 

     if (bufferInfo.size == 0) { 
      if (VERBOSE) Log.d(TAG, "ignoring zero size buffer"); 
      return; 
     } 

     if (!mStarted) { 
      Log.w(TAG, "writeSampleData called before muxer started. Ignoring packet. Track index: " + trackIndex + " tracks added: " + mNumTracks); 
//   encoder.releaseOutputBuffer(bufferIndex, false); 
      return; 
     } 

//  bufferInfo.presentationTimeUs = getNextRelativePts(bufferInfo.presentationTimeUs, trackIndex); 


     if(mStartTimeUs == 0) { 
       mStartTimeUs = bufferInfo.presentationTimeUs; 
      } 
     bufferInfo.presentationTimeUs -= mStartTimeUs; 
     if(bufferInfo.presentationTimeUs < 0) { 
      bufferInfo.presentationTimeUs = 0; 
     } 
     bufferInfo.presentationTimeUs = getSafePts(bufferInfo.presentationTimeUs, trackIndex); 


     mMuxer.writeSampleData(trackIndex, encodedData, bufferInfo); 

//  encoder.releaseOutputBuffer(bufferIndex, false); 

     if (allTracksFinished()) { 
      Log.w(TAG, "writeSampleData: All tracks finished! Calling Stop"); 
      stop(); 
     } 
    } 

    @Override 
    public void forceStop() { 
     Log.w(TAG, "forceStop: ForceStop called!"); 
     stop(); 
    } 
} 

注私は、メディア情報を格納し、使用する理由RTMPミュクサーとローカルミュクサーが同時に作業を開始しない場合がありますことを、thatsのこれは、ユーザがマルチプレクサを開始するように要求したときだけである。

答えて

0

ローカルマルチプレクサがまだ準備されておらず、パケットの書き込みをスキップした場合は、キーフレームの書き込みをスキップすることがあります。その後のキーフレームではないビデオフレームは、次のキーフレームに到達するまでデコードできません(ファイルフレームが非キーフレームで始まる場合は、ビデオプレーヤーが混乱する可能性があります)。キーフレーム間の間隔が長い場合は、適切な時間内にキーフレームを取得できない場合があります。 (RTMPでこれをストリーミングする場合は、定期的にキーフレームがあるはずです)。

私のアドバイスは、ローカルマルチプレクサが準備される前に生成されたすべてのパケットを保存し、いったん準備が整ったらローカルマルチプレクサに転送し、次のキーフレームに達するまでパケットをスキップし続けます。

+0

私のアプリはライブ放送を開始できるように設計されていて、ローカルで録画を開始する瞬間を選択できます。しかし、私ができることは、キーフレームを受け取ったときにローカルエンコーダに供給を開始することだけです。しかし、これが問題であるかどうかはわかりません。なぜならこれが問題であれば、私はほとんどキーフレームを最初のフレームとして取得しないでしょう。したがって、私はほとんどいつでも適切な録音を行うことができません。b)私はキーフレームを取得すると(私は2秒ごとにGOPが設定されている)、黒の2秒を得てビデオが正常に復帰する問題がありました。 –

+0

しかし私はこのアプローチを試みます。これはどちらも説明していない、私は時々最初のオーディオを取得し、次に一部のローカル録画ファイルのすべてのビデオを取得すると思います。 –

関連する問題