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; 2068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport dalvik.system.CloseGuard; 2168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 2268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.io.FileDescriptor; 2368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.io.IOException; 241797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shihimport java.io.RandomAccessFile; 2568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.nio.ByteBuffer; 2668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.util.Map; 2768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 2868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui/** 2968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * MediaMuxer facilitates muxing elementary streams. Currently only supports an 3068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * mp4 file as the output and at most one audio and/or one video elementary 3168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * stream. 3268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <p> 3368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * It is generally used like this: 3468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * 3568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <pre> 3634a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4); 3734a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat() 3834a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // or MediaExtractor.getTrackFormat(). 3968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * MediaFormat audioFormat = new MediaFormat(...); 4068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * MediaFormat videoFormat = new MediaFormat(...); 4168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * int audioTrackIndex = muxer.addTrack(audioFormat); 4268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * int videoTrackIndex = muxer.addTrack(videoFormat); 4334a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize); 4434a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * boolean finished = false; 4534a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * BufferInfo bufferInfo = new BufferInfo(); 4634a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * 4768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * muxer.start(); 4834a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * while(!finished) { 4934a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // getInputBuffer() will fill the inputBuffer with one frame of encoded 5034a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // sample from either MediaCodec or MediaExtractor, set isAudioSample to 5134a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // true when the sample is audio data, set up all the fields of bufferInfo, 5234a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // and return true if there are no more samples. 5334a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo); 5434a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * if (!finished) { 5534a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * int currentTrackIndex = isAudioSample ? audioTrackIndex : videoTrackIndex; 5634a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo); 5768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * } 5834a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * }; 5968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * muxer.stop(); 6068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * muxer.release(); 6168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * </pre> 6268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 6368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 6468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuifinal public class MediaMuxer { 6568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 6668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui static { 6768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui System.loadLibrary("media_jni"); 6868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 6968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 7068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 7168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Defines the output format. These constants are used with constructor. 7268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 7368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public static final class OutputFormat { 7468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /* Do not change these values without updating their counterparts 7568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * in include/media/stagefright/MediaMuxer.h! 7668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 7768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private OutputFormat() {} 7868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** MPEG4 media file format*/ 7968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public static final int MUXER_OUTPUT_MPEG_4 = 0; 801797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih public static final int MUXER_OUTPUT_WEBM = 1; 8168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui }; 8268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 8368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // All the native functions are listed here. 84656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native long nativeSetup(FileDescriptor fd, int format); 85656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native void nativeRelease(long nativeObject); 86656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native void nativeStart(long nativeObject); 87656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native void nativeStop(long nativeObject); 88656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native int nativeAddTrack(long nativeObject, String[] keys, 8968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui Object[] values); 90656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native void nativeSetOrientationHint(long nativeObject, 91effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui int degrees); 92656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native void nativeSetLocation(long nativeObject, int latitude, int longitude); 93656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native void nativeWriteSampleData(long nativeObject, 9468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int trackIndex, ByteBuffer byteBuf, 9568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int offset, int size, long presentationTimeUs, int flags); 9668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 9768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // Muxer internal states. 9868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_UNINITIALIZED = -1; 9968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_INITIALIZED = 0; 10068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_STARTED = 1; 10168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_STOPPED = 2; 10268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 10368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private int mState = MUXER_STATE_UNINITIALIZED; 10468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 10568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private final CloseGuard mCloseGuard = CloseGuard.get(); 10668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private int mLastTrackIndex = -1; 10768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 108656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private long mNativeObject; 10968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 11068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 111effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * Constructor. 11268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Creates a media muxer that writes to the specified path. 11368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param path The path of the output media file. 11468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param format The format of the output media file. 11568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @see android.media.MediaMuxer.OutputFormat 11668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @throws IOException if failed to open the file for write 11768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 11868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public MediaMuxer(String path, int format) throws IOException { 11968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (path == null) { 12068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("path must not be null"); 12168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 1221797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 && 1231797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih format != OutputFormat.MUXER_OUTPUT_WEBM) { 12468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("format is invalid"); 12568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 1261797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih // Use RandomAccessFile so we can open the file with RW access; 1271797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih // RW access allows the native writer to memory map the output file. 1281797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih RandomAccessFile file = null; 12968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui try { 1301797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih file = new RandomAccessFile(path, "rws"); 1311797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih FileDescriptor fd = file.getFD(); 13268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mNativeObject = nativeSetup(fd, format); 13368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_INITIALIZED; 13468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mCloseGuard.open("release"); 13568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } finally { 1361797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih if (file != null) { 1371797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih file.close(); 13868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 13968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 14068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 14168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 14268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 143effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * Sets the orientation hint for output video playback. 144effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * <p>This method should be called before {@link #start}. Calling this 145effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * method will not rotate the video frame when muxer is generating the file, 146effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * but add a composition matrix containing the rotation angle in the output 147effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * video if the output format is 148effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * {@link OutputFormat#MUXER_OUTPUT_MPEG_4} so that a video player can 149effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * choose the proper orientation for playback. Note that some video players 150effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * may choose to ignore the composition matrix in a video during playback. 151effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * By default, the rotation degree is 0.</p> 152effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * @param degrees the angle to be rotated clockwise in degrees. 153effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * The supported angles are 0, 90, 180, and 270 degrees. 154effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui */ 155effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui public void setOrientationHint(int degrees) { 156effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui if (degrees != 0 && degrees != 90 && degrees != 180 && degrees != 270) { 157effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui throw new IllegalArgumentException("Unsupported angle: " + degrees); 158effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } 159effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui if (mState == MUXER_STATE_INITIALIZED) { 160effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui nativeSetOrientationHint(mNativeObject, degrees); 161effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } else { 162effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui throw new IllegalStateException("Can't set rotation degrees due" + 163effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui " to wrong state."); 164effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } 165effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } 166effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui 167effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui /** 168cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * Set and store the geodata (latitude and longitude) in the output file. 169cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * This method should be called before {@link #start}. The geodata is stored 170cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * in udta box if the output format is 171cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * {@link OutputFormat#MUXER_OUTPUT_MPEG_4}, and is ignored for other output 172cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * formats. The geodata is stored according to ISO-6709 standard. 173cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * 174cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @param latitude Latitude in degrees. Its value must be in the range [-90, 175cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * 90]. 176cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @param longitude Longitude in degrees. Its value must be in the range 177cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * [-180, 180]. 178cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @throws IllegalArgumentException If the given latitude or longitude is out 179cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * of range. 180cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @throws IllegalStateException If this method is called after {@link #start}. 181cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He */ 182cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He public void setLocation(float latitude, float longitude) { 183cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He int latitudex10000 = (int) (latitude * 10000 + 0.5); 184cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He int longitudex10000 = (int) (longitude * 10000 + 0.5); 185cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He 186cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He if (latitudex10000 > 900000 || latitudex10000 < -900000) { 187cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He String msg = "Latitude: " + latitude + " out of range."; 188cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He throw new IllegalArgumentException(msg); 189cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 190cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He if (longitudex10000 > 1800000 || longitudex10000 < -1800000) { 191cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He String msg = "Longitude: " + longitude + " out of range"; 192cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He throw new IllegalArgumentException(msg); 193cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 194cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He 195cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He if (mState == MUXER_STATE_INITIALIZED && mNativeObject != 0) { 196cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He nativeSetLocation(mNativeObject, latitudex10000, longitudex10000); 197cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } else { 198cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He throw new IllegalStateException("Can't set location due to wrong state."); 199cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 200cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 201cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He 202cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He /** 20368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Starts the muxer. 20468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <p>Make sure this is called after {@link #addTrack} and before 20568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * {@link #writeSampleData}.</p> 20668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 20768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void start() { 20868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject == 0) { 20968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer has been released!"); 21068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 21168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState == MUXER_STATE_INITIALIZED) { 21268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeStart(mNativeObject); 21368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_STARTED; 21468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } else { 21568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Can't start due to wrong state."); 21668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 21768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 21868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 21968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 22068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Stops the muxer. 22168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <p>Once the muxer stops, it can not be restarted.</p> 22268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 22368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void stop() { 22468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState == MUXER_STATE_STARTED) { 22568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeStop(mNativeObject); 22668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_STOPPED; 22768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } else { 22868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Can't stop due to wrong state."); 22968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 23068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 23168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 23268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui @Override 23368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui protected void finalize() throws Throwable { 23468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui try { 23568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mCloseGuard != null) { 23668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mCloseGuard.warnIfOpen(); 23768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 23868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject != 0) { 23968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeRelease(mNativeObject); 24068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mNativeObject = 0; 24168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 24268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } finally { 24368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui super.finalize(); 24468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 24568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 24668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 24768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 24868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Adds a track with the specified format. 24968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param format The media format for the track. 25068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @return The track index for this newly added track, and it should be used 25168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * in the {@link #writeSampleData}. 25268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 25368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public int addTrack(MediaFormat format) { 25468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (format == null) { 25568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("format must not be null."); 25668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 25768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState != MUXER_STATE_INITIALIZED) { 25868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer is not initialized."); 25968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 26068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject == 0) { 26168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer has been released!"); 26268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 26368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int trackIndex = -1; 26468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // Convert the MediaFormat into key-value pairs and send to the native. 26568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui Map<String, Object> formatMap = format.getMap(); 26668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 26768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui String[] keys = null; 26868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui Object[] values = null; 26968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int mapSize = formatMap.size(); 27068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mapSize > 0) { 27168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui keys = new String[mapSize]; 27268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui values = new Object[mapSize]; 27368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int i = 0; 27468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui for (Map.Entry<String, Object> entry : formatMap.entrySet()) { 27568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui keys[i] = entry.getKey(); 27668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui values[i] = entry.getValue(); 27768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui ++i; 27868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 27968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui trackIndex = nativeAddTrack(mNativeObject, keys, values); 28068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } else { 28168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("format must not be empty."); 28268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 28368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 28468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // Track index number is expected to incremented as addTrack succeed. 28568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // However, if format is invalid, it will get a negative trackIndex. 28668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mLastTrackIndex >= trackIndex) { 28768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("Invalid format."); 28868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 28968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mLastTrackIndex = trackIndex; 29068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui return trackIndex; 29168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 29268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 29368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 294f7d3aae32859a52c24713dba30e4d7ef779fdfb1ztenghui * Writes an encoded sample into the muxer. 295f7d3aae32859a52c24713dba30e4d7ef779fdfb1ztenghui * <p>The application needs to make sure that the samples are written into 296f7d3aae32859a52c24713dba30e4d7ef779fdfb1ztenghui * the right tracks. Also, it needs to make sure the samples for each track 297ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * are written in chronological order (e.g. in the order they are provided 298ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * by the encoder.)</p> 29968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param byteBuf The encoded sample. 30068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param trackIndex The track index for this sample. 30168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param bufferInfo The buffer information related to this sample. 302ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * MediaMuxer uses the flags provided in {@link MediaCodec.BufferInfo}, 303ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * to signal sync frames. 30468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 30568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void writeSampleData(int trackIndex, ByteBuffer byteBuf, 30668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui BufferInfo bufferInfo) { 30768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (trackIndex < 0 || trackIndex > mLastTrackIndex) { 30868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("trackIndex is invalid"); 30968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 31068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 31168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (byteBuf == null) { 31268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("byteBuffer must not be null"); 31368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 31468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 31568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (bufferInfo == null) { 31668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("bufferInfo must not be null"); 31768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 31868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (bufferInfo.size < 0 || bufferInfo.offset < 0 31968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui || (bufferInfo.offset + bufferInfo.size) > byteBuf.capacity() 32068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui || bufferInfo.presentationTimeUs < 0) { 32168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("bufferInfo must specify a" + 32268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui " valid buffer offset, size and presentation time"); 32368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 32468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 32568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject == 0) { 32668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer has been released!"); 32768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 32868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 32968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState != MUXER_STATE_STARTED) { 33068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Can't write, muxer is not started"); 33168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 33268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 33368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeWriteSampleData(mNativeObject, trackIndex, byteBuf, 33468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui bufferInfo.offset, bufferInfo.size, 33568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui bufferInfo.presentationTimeUs, bufferInfo.flags); 33668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 33768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 33868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 33968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Make sure you call this when you're done to free up any resources 34068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * instead of relying on the garbage collector to do this for you at 34168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * some point in the future. 34268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 34368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void release() { 34468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState == MUXER_STATE_STARTED) { 345a83df76859f0af12105bcea27e2e8d9f0b4d2c31ztenghui stop(); 34668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 34768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject != 0) { 34868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeRelease(mNativeObject); 34968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mNativeObject = 0; 35068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mCloseGuard.close(); 35168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 35268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_UNINITIALIZED; 35368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 35468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui} 355