168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui/* 268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Copyright (C) 2013 The Android Open Source Project 368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * 468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Licensed under the Apache License, Version 2.0 (the "License"); 568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * you may not use this file except in compliance with the License. 668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * You may obtain a copy of the License at 768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * 868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * http://www.apache.org/licenses/LICENSE-2.0 968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * 1068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Unless required by applicable law or agreed to in writing, software 1168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * distributed under the License is distributed on an "AS IS" BASIS, 1268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * See the License for the specific language governing permissions and 1468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * limitations under the License. 1568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 1668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 1768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuipackage android.media; 1868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 1968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport android.media.MediaCodec.BufferInfo; 2068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 2168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport dalvik.system.CloseGuard; 2268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 2368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.io.File; 2468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.io.FileDescriptor; 2568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.io.FileOutputStream; 2668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.io.IOException; 2768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.nio.ByteBuffer; 2868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.util.Map; 2968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 3068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui/** 3168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * MediaMuxer facilitates muxing elementary streams. Currently only supports an 3268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * mp4 file as the output and at most one audio and/or one video elementary 3368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * stream. 3468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <p> 3568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * It is generally used like this: 3668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * 3768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <pre> 3834a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4); 3934a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat() 4034a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // or MediaExtractor.getTrackFormat(). 4168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * MediaFormat audioFormat = new MediaFormat(...); 4268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * MediaFormat videoFormat = new MediaFormat(...); 4368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * int audioTrackIndex = muxer.addTrack(audioFormat); 4468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * int videoTrackIndex = muxer.addTrack(videoFormat); 4534a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize); 4634a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * boolean finished = false; 4734a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * BufferInfo bufferInfo = new BufferInfo(); 4834a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * 4968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * muxer.start(); 5034a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * while(!finished) { 5134a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // getInputBuffer() will fill the inputBuffer with one frame of encoded 5234a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // sample from either MediaCodec or MediaExtractor, set isAudioSample to 5334a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // true when the sample is audio data, set up all the fields of bufferInfo, 5434a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // and return true if there are no more samples. 5534a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo); 5634a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * if (!finished) { 5734a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * int currentTrackIndex = isAudioSample ? audioTrackIndex : videoTrackIndex; 5834a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo); 5968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * } 6034a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * }; 6168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * muxer.stop(); 6268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * muxer.release(); 6368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * </pre> 6468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 6568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 6668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuifinal public class MediaMuxer { 6768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 6868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private int mNativeContext; 6968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 7068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui static { 7168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui System.loadLibrary("media_jni"); 7268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 7368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 7468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 7568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Defines the output format. These constants are used with constructor. 7668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 7768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public static final class OutputFormat { 7868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /* Do not change these values without updating their counterparts 7968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * in include/media/stagefright/MediaMuxer.h! 8068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 8168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private OutputFormat() {} 8268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** MPEG4 media file format*/ 8368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public static final int MUXER_OUTPUT_MPEG_4 = 0; 8468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui }; 8568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 8668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // All the native functions are listed here. 8768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static native int nativeSetup(FileDescriptor fd, int format); 8868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static native void nativeRelease(int nativeObject); 8968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static native void nativeStart(int nativeObject); 9068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static native void nativeStop(int nativeObject); 9168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static native int nativeAddTrack(int nativeObject, String[] keys, 9268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui Object[] values); 93effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui private static native void nativeSetOrientationHint(int nativeObject, 94effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui int degrees); 95cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He private static native void nativeSetLocation(int nativeObject, int latitude, int longitude); 9668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static native void nativeWriteSampleData(int nativeObject, 9768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int trackIndex, ByteBuffer byteBuf, 9868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int offset, int size, long presentationTimeUs, int flags); 9968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 10068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // Muxer internal states. 10168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_UNINITIALIZED = -1; 10268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_INITIALIZED = 0; 10368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_STARTED = 1; 10468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_STOPPED = 2; 10568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 10668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private int mState = MUXER_STATE_UNINITIALIZED; 10768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 10868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private final CloseGuard mCloseGuard = CloseGuard.get(); 10968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private int mLastTrackIndex = -1; 11068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 11168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private int mNativeObject; 11268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 11368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 114effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * Constructor. 11568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Creates a media muxer that writes to the specified path. 11668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param path The path of the output media file. 11768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param format The format of the output media file. 11868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @see android.media.MediaMuxer.OutputFormat 11968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @throws IOException if failed to open the file for write 12068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 12168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public MediaMuxer(String path, int format) throws IOException { 12268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (path == null) { 12368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("path must not be null"); 12468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 12568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (format != OutputFormat.MUXER_OUTPUT_MPEG_4) { 12668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("format is invalid"); 12768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 12868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui FileOutputStream fos = null; 12968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui try { 13068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui File file = new File(path); 13168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui fos = new FileOutputStream(file); 13268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui FileDescriptor fd = fos.getFD(); 13368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mNativeObject = nativeSetup(fd, format); 13468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_INITIALIZED; 13568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mCloseGuard.open("release"); 13668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } finally { 13768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (fos != null) { 13868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui fos.close(); 13968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 14068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 14168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 14268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 14368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 144effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * Sets the orientation hint for output video playback. 145effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * <p>This method should be called before {@link #start}. Calling this 146effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * method will not rotate the video frame when muxer is generating the file, 147effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * but add a composition matrix containing the rotation angle in the output 148effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * video if the output format is 149effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * {@link OutputFormat#MUXER_OUTPUT_MPEG_4} so that a video player can 150effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * choose the proper orientation for playback. Note that some video players 151effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * may choose to ignore the composition matrix in a video during playback. 152effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * By default, the rotation degree is 0.</p> 153effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * @param degrees the angle to be rotated clockwise in degrees. 154effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * The supported angles are 0, 90, 180, and 270 degrees. 155effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui */ 156effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui public void setOrientationHint(int degrees) { 157effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui if (degrees != 0 && degrees != 90 && degrees != 180 && degrees != 270) { 158effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui throw new IllegalArgumentException("Unsupported angle: " + degrees); 159effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } 160effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui if (mState == MUXER_STATE_INITIALIZED) { 161effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui nativeSetOrientationHint(mNativeObject, degrees); 162effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } else { 163effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui throw new IllegalStateException("Can't set rotation degrees due" + 164effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui " to wrong state."); 165effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } 166effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } 167effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui 168effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui /** 169cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * Set and store the geodata (latitude and longitude) in the output file. 170cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * This method should be called before {@link #start}. The geodata is stored 171cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * in udta box if the output format is 172cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * {@link OutputFormat#MUXER_OUTPUT_MPEG_4}, and is ignored for other output 173cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * formats. The geodata is stored according to ISO-6709 standard. 174cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * 175cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @param latitude Latitude in degrees. Its value must be in the range [-90, 176cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * 90]. 177cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @param longitude Longitude in degrees. Its value must be in the range 178cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * [-180, 180]. 179cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @throws IllegalArgumentException If the given latitude or longitude is out 180cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * of range. 181cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @throws IllegalStateException If this method is called after {@link #start}. 182cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He */ 183cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He public void setLocation(float latitude, float longitude) { 184cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He int latitudex10000 = (int) (latitude * 10000 + 0.5); 185cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He int longitudex10000 = (int) (longitude * 10000 + 0.5); 186cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He 187cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He if (latitudex10000 > 900000 || latitudex10000 < -900000) { 188cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He String msg = "Latitude: " + latitude + " out of range."; 189cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He throw new IllegalArgumentException(msg); 190cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 191cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He if (longitudex10000 > 1800000 || longitudex10000 < -1800000) { 192cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He String msg = "Longitude: " + longitude + " out of range"; 193cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He throw new IllegalArgumentException(msg); 194cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 195cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He 196cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He if (mState == MUXER_STATE_INITIALIZED && mNativeObject != 0) { 197cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He nativeSetLocation(mNativeObject, latitudex10000, longitudex10000); 198cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } else { 199cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He throw new IllegalStateException("Can't set location due to wrong state."); 200cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 201cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 202cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He 203cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He /** 20468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Starts the muxer. 20568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <p>Make sure this is called after {@link #addTrack} and before 20668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * {@link #writeSampleData}.</p> 20768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 20868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void start() { 20968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject == 0) { 21068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer has been released!"); 21168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 21268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState == MUXER_STATE_INITIALIZED) { 21368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeStart(mNativeObject); 21468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_STARTED; 21568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } else { 21668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Can't start due to wrong state."); 21768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 21868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 21968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 22068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 22168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Stops the muxer. 22268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <p>Once the muxer stops, it can not be restarted.</p> 22368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 22468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void stop() { 22568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState == MUXER_STATE_STARTED) { 22668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeStop(mNativeObject); 22768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_STOPPED; 22868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } else { 22968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Can't stop due to wrong state."); 23068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 23168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 23268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 23368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui @Override 23468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui protected void finalize() throws Throwable { 23568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui try { 23668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mCloseGuard != null) { 23768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mCloseGuard.warnIfOpen(); 23868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 23968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject != 0) { 24068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeRelease(mNativeObject); 24168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mNativeObject = 0; 24268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 24368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } finally { 24468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui super.finalize(); 24568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 24668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 24768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 24868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 24968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Adds a track with the specified format. 25068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param format The media format for the track. 25168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @return The track index for this newly added track, and it should be used 25268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * in the {@link #writeSampleData}. 25368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 25468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public int addTrack(MediaFormat format) { 25568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (format == null) { 25668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("format must not be null."); 25768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 25868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState != MUXER_STATE_INITIALIZED) { 25968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer is not initialized."); 26068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 26168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject == 0) { 26268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer has been released!"); 26368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 26468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int trackIndex = -1; 26568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // Convert the MediaFormat into key-value pairs and send to the native. 26668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui Map<String, Object> formatMap = format.getMap(); 26768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 26868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui String[] keys = null; 26968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui Object[] values = null; 27068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int mapSize = formatMap.size(); 27168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mapSize > 0) { 27268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui keys = new String[mapSize]; 27368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui values = new Object[mapSize]; 27468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int i = 0; 27568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui for (Map.Entry<String, Object> entry : formatMap.entrySet()) { 27668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui keys[i] = entry.getKey(); 27768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui values[i] = entry.getValue(); 27868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui ++i; 27968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 28068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui trackIndex = nativeAddTrack(mNativeObject, keys, values); 28168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } else { 28268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("format must not be empty."); 28368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 28468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 28568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // Track index number is expected to incremented as addTrack succeed. 28668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // However, if format is invalid, it will get a negative trackIndex. 28768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mLastTrackIndex >= trackIndex) { 28868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("Invalid format."); 28968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 29068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mLastTrackIndex = trackIndex; 29168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui return trackIndex; 29268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 29368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 29468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 295f7d3aae32859a52c24713dba30e4d7ef779fdfb1ztenghui * Writes an encoded sample into the muxer. 296f7d3aae32859a52c24713dba30e4d7ef779fdfb1ztenghui * <p>The application needs to make sure that the samples are written into 297f7d3aae32859a52c24713dba30e4d7ef779fdfb1ztenghui * the right tracks. Also, it needs to make sure the samples for each track 298ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * are written in chronological order (e.g. in the order they are provided 299ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * by the encoder.)</p> 30068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param byteBuf The encoded sample. 30168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param trackIndex The track index for this sample. 30268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param bufferInfo The buffer information related to this sample. 303ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * MediaMuxer uses the flags provided in {@link MediaCodec.BufferInfo}, 304ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * to signal sync frames. 30568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 30668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void writeSampleData(int trackIndex, ByteBuffer byteBuf, 30768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui BufferInfo bufferInfo) { 30868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (trackIndex < 0 || trackIndex > mLastTrackIndex) { 30968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("trackIndex is invalid"); 31068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 31168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 31268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (byteBuf == null) { 31368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("byteBuffer must not be null"); 31468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 31568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 31668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (bufferInfo == null) { 31768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("bufferInfo must not be null"); 31868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 31968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (bufferInfo.size < 0 || bufferInfo.offset < 0 32068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui || (bufferInfo.offset + bufferInfo.size) > byteBuf.capacity() 32168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui || bufferInfo.presentationTimeUs < 0) { 32268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("bufferInfo must specify a" + 32368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui " valid buffer offset, size and presentation time"); 32468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 32568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 32668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject == 0) { 32768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer has been released!"); 32868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 32968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 33068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState != MUXER_STATE_STARTED) { 33168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Can't write, muxer is not started"); 33268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 33368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 33468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeWriteSampleData(mNativeObject, trackIndex, byteBuf, 33568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui bufferInfo.offset, bufferInfo.size, 33668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui bufferInfo.presentationTimeUs, bufferInfo.flags); 33768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 33868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 33968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 34068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Make sure you call this when you're done to free up any resources 34168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * instead of relying on the garbage collector to do this for you at 34268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * some point in the future. 34368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 34468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void release() { 34568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState == MUXER_STATE_STARTED) { 346a83df76859f0af12105bcea27e2e8d9f0b4d2c31ztenghui stop(); 34768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 34868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject != 0) { 34968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeRelease(mNativeObject); 35068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mNativeObject = 0; 35168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mCloseGuard.close(); 35268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 35368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_UNINITIALIZED; 35468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 35568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui} 356