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のこれは、ユーザがマルチプレクサを開始するように要求したときだけである。
私のアプリはライブ放送を開始できるように設計されていて、ローカルで録画を開始する瞬間を選択できます。しかし、私ができることは、キーフレームを受け取ったときにローカルエンコーダに供給を開始することだけです。しかし、これが問題であるかどうかはわかりません。なぜならこれが問題であれば、私はほとんどキーフレームを最初のフレームとして取得しないでしょう。したがって、私はほとんどいつでも適切な録音を行うことができません。b)私はキーフレームを取得すると(私は2秒ごとにGOPが設定されている)、黒の2秒を得てビデオが正常に復帰する問題がありました。 –
しかし私はこのアプローチを試みます。これはどちらも説明していない、私は時々最初のオーディオを取得し、次に一部のローカル録画ファイルのすべてのビデオを取得すると思います。 –