it-swarm.com.de

Erstellen Sie eine Videodatei auf Android

In meiner Android-App versuche ich, eine Videodatei zu erstellen, und zwar an einer bestimmten Position im Video eine Audiospur hinzuzufügen. Ich habe einen MediaMuxer verwendet und den Wert von presentationTimeUs geändert, um das Audio zu verschieben Anscheinend ist dies nicht der richtige Weg, da die Startzeit des Videos ebenfalls verschoben ist .. Ein weiteres Problem ist, dass MP3-Audio nicht funktioniert .. Hier ist mein Versuch bisher:

final long audioPositionUs = 10000000;
File fileOut = new File (Environment.getExternalStoragePublicDirectory (
  Environment.DIRECTORY_MOVIES) + "/output.mp4");
fileOut.createNewFile ();
MediaExtractor videoExtractor = new MediaExtractor ();
MediaExtractor audioExtractor = new MediaExtractor ();
AssetFileDescriptor videoDescriptor = getAssets ().openFd ("video.mp4");
// AssetFileDescriptor audioDescriptor = getAssets ().openFd ("audio.mp3"); // ?!
AssetFileDescriptor audioDescriptor = getAssets ().openFd ("audio.aac");
videoExtractor.setDataSource (videoDescriptor.getFileDescriptor (),
    videoDescriptor.getStartOffset (), videoDescriptor.getLength ());
audioExtractor.setDataSource (audioDescriptor.getFileDescriptor (),
    audioDescriptor.getStartOffset (), audioDescriptor.getLength ());
MediaFormat videoFormat = null;
for (int i = 0; i < videoExtractor.getTrackCount (); i++) {
  if (videoExtractor.getTrackFormat (i).getString (
      MediaFormat.KEY_MIME).startsWith ("video/")) {
    videoExtractor.selectTrack (i);
    videoFormat = videoExtractor.getTrackFormat (i);
    break;
  }
}
audioExtractor.selectTrack (0);
MediaFormat audioFormat = audioExtractor.getTrackFormat (0);
MediaMuxer muxer = new MediaMuxer (fileOut.getAbsolutePath (),
    MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
int videoTrack = muxer.addTrack (videoFormat);
int audioTrack = muxer.addTrack (audioFormat);
boolean end = false;
int sampleSize = 256 * 1024;
ByteBuffer videoBuffer = ByteBuffer.allocate (sampleSize);
ByteBuffer audioBuffer = ByteBuffer.allocate (sampleSize);
MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo ();
MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo ();
videoExtractor.seekTo (0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
audioExtractor.seekTo (0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
muxer.start ();
while (!end) {
  videoBufferInfo.size = videoExtractor.readSampleData (videoBuffer, 0);
  if (videoBufferInfo.size < 0) {
    end = true;
    videoBufferInfo.size = 0;
  } else {
    videoBufferInfo.presentationTimeUs = videoExtractor.getSampleTime ();
    videoBufferInfo.flags = videoExtractor.getSampleFlags ();
    muxer.writeSampleData (videoTrack, videoBuffer, videoBufferInfo);
    videoExtractor.advance ();
  }
}
end = false;
while (!end) {
  audioBufferInfo.size = audioExtractor.readSampleData (audioBuffer, 0);
  if (audioBufferInfo.size < 0) {
    end = true;
    audioBufferInfo.size = 0;
  } else {
    audioBufferInfo.presentationTimeUs = audioExtractor.getSampleTime () +
        audioPositionUs;
    audioBufferInfo.flags = audioExtractor.getSampleFlags ();
    muxer.writeSampleData (audioTrack, audioBuffer, audioBufferInfo);
    audioExtractor.advance ();
  }
}
muxer.stop ();
muxer.release ();

Können Sie bitte Details (und den Code, falls möglich) angeben, um mir zu helfen, dieses Problem zu lösen?

7
Patrick

Senden Sie AudioRecords Samples an einen MediaCodec + MediaMuxer-Wrapper. Die Verwendung der Systemzeit bei audioRecord.read (...) ist ausreichend gut als Audiozeitstempel, vorausgesetzt, Sie rufen häufig genug ab, um den internen Puffer von AudioRecord aufzufüllen (um zu vermeiden, dass zwischen dem Zeitpunkt, zu dem Sie den Aufruf aufrufen, und dem Zeitpunkt, zu dem AudioRecord die Samples aufgenommen hat ). Schade, dass AudioRecord die Zeitstempel nicht direkt kommuniziert ...

// AudioRecord einrichten

while (isRecording) {
    audioPresentationTimeNs = System.nanoTime();
    audioRecord.read(dataBuffer, 0, samplesPerFrame);
    hwEncoder.offerAudioEncoder(dataBuffer.clone(), audioPresentationTimeNs);
}

Beachten Sie, dass AudioRecord nur die Unterstützung für 16-Bit-PCM-Samples garantiert, obwohl MediaCodec.queueInputBuffer die Eingabe als Byte [] akzeptiert. Wenn Sie ein Byte [] an audioRecord.read (dataBuffer, ...) übergeben, werden die 16-Bit-Samples für Sie getrennt.

Ich fand heraus, dass das Abrufen auf diese Weise immer noch gelegentlich einen Zeitstempel für XXX <lastTimestampUs XXX für Audiospurfehler erzeugte. Daher habe ich eine Logik eingefügt, um die von mediaCodec.dequeueOutputBuffer (bufferInfo, timeoutMs) gemeldeten bufferInfo.presentationTimeUs zu verfolgen und ggf. anzupassen mediaMuxer.writeSampleData (trackIndex, encodedData, bufferInfo).

1
umer farooq

Als Workaround können Sie eine temporäre Audiospur erstellen, indem Sie Ihre Audiospur am Kopf mit einer stillen Spur auffüllen und diese dann mit addTrack verwenden.

PS: Ich hätte gedacht, dass presentationTimeUs auch funktionieren sollte.

PS2: Vielleicht hilft die set-Methode von MediaCodec.BufferInfo .

0
sancho.s