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 1999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnarimport android.annotation.IntDef; 2099f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnarimport android.annotation.NonNull; 2199f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnarimport android.annotation.Nullable; 2299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnarimport android.media.MediaCodec; 2368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport android.media.MediaCodec.BufferInfo; 2468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport dalvik.system.CloseGuard; 2568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 2668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.io.FileDescriptor; 2768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.io.IOException; 281797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shihimport java.io.RandomAccessFile; 2999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnarimport java.lang.annotation.Retention; 3099f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnarimport java.lang.annotation.RetentionPolicy; 3168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.nio.ByteBuffer; 3268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuiimport java.util.Map; 3368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 3468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui/** 3568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * MediaMuxer facilitates muxing elementary streams. Currently only supports an 3668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * mp4 file as the output and at most one audio and/or one video elementary 3768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * stream. 3868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <p> 3968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * It is generally used like this: 4068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * 4168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <pre> 4234a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4); 4334a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat() 4434a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // or MediaExtractor.getTrackFormat(). 4568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * MediaFormat audioFormat = new MediaFormat(...); 4668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * MediaFormat videoFormat = new MediaFormat(...); 4768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * int audioTrackIndex = muxer.addTrack(audioFormat); 4868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * int videoTrackIndex = muxer.addTrack(videoFormat); 4934a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize); 5034a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * boolean finished = false; 5134a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * BufferInfo bufferInfo = new BufferInfo(); 5234a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * 5368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * muxer.start(); 5434a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * while(!finished) { 5534a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // getInputBuffer() will fill the inputBuffer with one frame of encoded 5634a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // sample from either MediaCodec or MediaExtractor, set isAudioSample to 5734a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // true when the sample is audio data, set up all the fields of bufferInfo, 5834a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * // and return true if there are no more samples. 5934a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo); 6034a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * if (!finished) { 6134a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * int currentTrackIndex = isAudioSample ? audioTrackIndex : videoTrackIndex; 6234a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo); 6368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * } 6434a5b011cd3fe3b231770a3bae984175d2c983e2ztenghui * }; 6568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * muxer.stop(); 6668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * muxer.release(); 6768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * </pre> 6868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 6968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 7068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghuifinal public class MediaMuxer { 7168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 7268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui static { 7368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui System.loadLibrary("media_jni"); 7468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 7568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 7668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 7768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Defines the output format. These constants are used with constructor. 7868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 7968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public static final class OutputFormat { 8068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /* Do not change these values without updating their counterparts 8168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * in include/media/stagefright/MediaMuxer.h! 8268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 8368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private OutputFormat() {} 8468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** MPEG4 media file format*/ 8568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public static final int MUXER_OUTPUT_MPEG_4 = 0; 861797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih public static final int MUXER_OUTPUT_WEBM = 1; 8768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui }; 8868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 8999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar /** @hide */ 9099f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar @IntDef({ 9199f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar OutputFormat.MUXER_OUTPUT_MPEG_4, 9299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar OutputFormat.MUXER_OUTPUT_WEBM, 9399f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar }) 9499f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar @Retention(RetentionPolicy.SOURCE) 9599f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar public @interface Format {} 9699f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar 9768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // All the native functions are listed here. 9899f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar private static native long nativeSetup(@NonNull FileDescriptor fd, int format); 99656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native void nativeRelease(long nativeObject); 100656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native void nativeStart(long nativeObject); 101656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native void nativeStop(long nativeObject); 10299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar private static native int nativeAddTrack( 10399f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar long nativeObject, @NonNull String[] keys, @NonNull Object[] values); 10499f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar private static native void nativeSetOrientationHint( 10599f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar long nativeObject, int degrees); 106656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private static native void nativeSetLocation(long nativeObject, int latitude, int longitude); 10799f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar private static native void nativeWriteSampleData( 10899f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar long nativeObject, int trackIndex, @NonNull ByteBuffer byteBuf, 10999f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar int offset, int size, long presentationTimeUs, @MediaCodec.BufferFlag int flags); 11068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 11168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // Muxer internal states. 11268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_UNINITIALIZED = -1; 11368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_INITIALIZED = 0; 11468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_STARTED = 1; 11568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private static final int MUXER_STATE_STOPPED = 2; 11668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 11768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private int mState = MUXER_STATE_UNINITIALIZED; 11868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 11968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private final CloseGuard mCloseGuard = CloseGuard.get(); 12068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui private int mLastTrackIndex = -1; 12168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 122656fd0402613cec0196d5e2ae0a460d044d2805bAshok Bhat private long mNativeObject; 12368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 12468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 125effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * Constructor. 12668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Creates a media muxer that writes to the specified path. 12768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param path The path of the output media file. 12868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param format The format of the output media file. 12968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @see android.media.MediaMuxer.OutputFormat 13068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @throws IOException if failed to open the file for write 13168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 13299f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar public MediaMuxer(@NonNull String path, @Format int format) throws IOException { 13368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (path == null) { 13468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("path must not be null"); 13568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 1361797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 && 1371797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih format != OutputFormat.MUXER_OUTPUT_WEBM) { 13868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("format is invalid"); 13968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 1401797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih // Use RandomAccessFile so we can open the file with RW access; 1411797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih // RW access allows the native writer to memory map the output file. 1421797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih RandomAccessFile file = null; 14368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui try { 1441797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih file = new RandomAccessFile(path, "rws"); 1451797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih FileDescriptor fd = file.getFD(); 14668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mNativeObject = nativeSetup(fd, format); 14768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_INITIALIZED; 14868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mCloseGuard.open("release"); 14968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } finally { 1501797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih if (file != null) { 1511797dc93fb203762eecbc9c026b77263e4e4a4f5Robert Shih file.close(); 15268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 15368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 15468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 15568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 15668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 157effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * Sets the orientation hint for output video playback. 158effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * <p>This method should be called before {@link #start}. Calling this 159effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * method will not rotate the video frame when muxer is generating the file, 160effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * but add a composition matrix containing the rotation angle in the output 161effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * video if the output format is 162effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * {@link OutputFormat#MUXER_OUTPUT_MPEG_4} so that a video player can 163effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * choose the proper orientation for playback. Note that some video players 164effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * may choose to ignore the composition matrix in a video during playback. 165effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * By default, the rotation degree is 0.</p> 166effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * @param degrees the angle to be rotated clockwise in degrees. 167effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui * The supported angles are 0, 90, 180, and 270 degrees. 168effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui */ 169effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui public void setOrientationHint(int degrees) { 170effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui if (degrees != 0 && degrees != 90 && degrees != 180 && degrees != 270) { 171effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui throw new IllegalArgumentException("Unsupported angle: " + degrees); 172effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } 173effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui if (mState == MUXER_STATE_INITIALIZED) { 174effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui nativeSetOrientationHint(mNativeObject, degrees); 175effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } else { 176effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui throw new IllegalStateException("Can't set rotation degrees due" + 177effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui " to wrong state."); 178effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } 179effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui } 180effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui 181effc9b4839f3cc109fe3d8244022f3c898cd080bztenghui /** 182cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * Set and store the geodata (latitude and longitude) in the output file. 183cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * This method should be called before {@link #start}. The geodata is stored 184cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * in udta box if the output format is 185cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * {@link OutputFormat#MUXER_OUTPUT_MPEG_4}, and is ignored for other output 186cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * formats. The geodata is stored according to ISO-6709 standard. 187cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * 188cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @param latitude Latitude in degrees. Its value must be in the range [-90, 189cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * 90]. 190cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @param longitude Longitude in degrees. Its value must be in the range 191cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * [-180, 180]. 192cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @throws IllegalArgumentException If the given latitude or longitude is out 193cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * of range. 194cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He * @throws IllegalStateException If this method is called after {@link #start}. 195cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He */ 196cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He public void setLocation(float latitude, float longitude) { 197cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He int latitudex10000 = (int) (latitude * 10000 + 0.5); 198cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He int longitudex10000 = (int) (longitude * 10000 + 0.5); 199cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He 200cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He if (latitudex10000 > 900000 || latitudex10000 < -900000) { 201cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He String msg = "Latitude: " + latitude + " out of range."; 202cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He throw new IllegalArgumentException(msg); 203cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 204cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He if (longitudex10000 > 1800000 || longitudex10000 < -1800000) { 205cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He String msg = "Longitude: " + longitude + " out of range"; 206cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He throw new IllegalArgumentException(msg); 207cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 208cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He 209cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He if (mState == MUXER_STATE_INITIALIZED && mNativeObject != 0) { 210cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He nativeSetLocation(mNativeObject, latitudex10000, longitudex10000); 211cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } else { 212cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He throw new IllegalStateException("Can't set location due to wrong state."); 213cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 214cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He } 215cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He 216cfd47481d1b375663d4e8e8d0c292d9001aa384bZhijun He /** 21768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Starts the muxer. 21868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <p>Make sure this is called after {@link #addTrack} and before 21968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * {@link #writeSampleData}.</p> 22068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 22168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void start() { 22268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject == 0) { 22368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer has been released!"); 22468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 22568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState == MUXER_STATE_INITIALIZED) { 22668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeStart(mNativeObject); 22768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_STARTED; 22868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } else { 22968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Can't start due to wrong state."); 23068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 23168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 23268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 23368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 23468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Stops the muxer. 23568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * <p>Once the muxer stops, it can not be restarted.</p> 23668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 23768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void stop() { 23868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState == MUXER_STATE_STARTED) { 23968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeStop(mNativeObject); 24068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_STOPPED; 24168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } else { 24268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Can't stop due to wrong state."); 24368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 24468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 24568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 24668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui @Override 24768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui protected void finalize() throws Throwable { 24868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui try { 24968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mCloseGuard != null) { 25068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mCloseGuard.warnIfOpen(); 25168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 25268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject != 0) { 25368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeRelease(mNativeObject); 25468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mNativeObject = 0; 25568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 25668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } finally { 25768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui super.finalize(); 25868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 25968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 26068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 26168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 26268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Adds a track with the specified format. 26399f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar * @param format The media format for the track. This must not be an empty 26499f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar * MediaFormat. 26568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @return The track index for this newly added track, and it should be used 26668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * in the {@link #writeSampleData}. 26768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 26899f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar public int addTrack(@NonNull MediaFormat format) { 26968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (format == null) { 27068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("format must not be null."); 27168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 27268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState != MUXER_STATE_INITIALIZED) { 27368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer is not initialized."); 27468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 27568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject == 0) { 27668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer has been released!"); 27768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 27868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int trackIndex = -1; 27968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // Convert the MediaFormat into key-value pairs and send to the native. 28068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui Map<String, Object> formatMap = format.getMap(); 28168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 28268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui String[] keys = null; 28368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui Object[] values = null; 28468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int mapSize = formatMap.size(); 28568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mapSize > 0) { 28668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui keys = new String[mapSize]; 28768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui values = new Object[mapSize]; 28868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui int i = 0; 28968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui for (Map.Entry<String, Object> entry : formatMap.entrySet()) { 29068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui keys[i] = entry.getKey(); 29168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui values[i] = entry.getValue(); 29268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui ++i; 29368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 29468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui trackIndex = nativeAddTrack(mNativeObject, keys, values); 29568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } else { 29668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("format must not be empty."); 29768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 29868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 29968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // Track index number is expected to incremented as addTrack succeed. 30068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui // However, if format is invalid, it will get a negative trackIndex. 30168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mLastTrackIndex >= trackIndex) { 30268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("Invalid format."); 30368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 30468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mLastTrackIndex = trackIndex; 30568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui return trackIndex; 30668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 30768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 30868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 309f7d3aae32859a52c24713dba30e4d7ef779fdfb1ztenghui * Writes an encoded sample into the muxer. 310f7d3aae32859a52c24713dba30e4d7ef779fdfb1ztenghui * <p>The application needs to make sure that the samples are written into 311f7d3aae32859a52c24713dba30e4d7ef779fdfb1ztenghui * the right tracks. Also, it needs to make sure the samples for each track 312ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * are written in chronological order (e.g. in the order they are provided 313ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * by the encoder.)</p> 31468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param byteBuf The encoded sample. 31568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param trackIndex The track index for this sample. 31668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * @param bufferInfo The buffer information related to this sample. 317ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * MediaMuxer uses the flags provided in {@link MediaCodec.BufferInfo}, 318ecca7f60a69d99a9569c06bdf9c122f853e67d47Lajos Molnar * to signal sync frames. 31968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 32099f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar public void writeSampleData(int trackIndex, @NonNull ByteBuffer byteBuf, 32199f8072386ce9891a5973d591dc1a30e45b50bc6Lajos Molnar @NonNull BufferInfo bufferInfo) { 32268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (trackIndex < 0 || trackIndex > mLastTrackIndex) { 32368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("trackIndex is invalid"); 32468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 32568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 32668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (byteBuf == null) { 32768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("byteBuffer must not be null"); 32868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 32968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 33068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (bufferInfo == null) { 33168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("bufferInfo must not be null"); 33268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 33368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (bufferInfo.size < 0 || bufferInfo.offset < 0 33468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui || (bufferInfo.offset + bufferInfo.size) > byteBuf.capacity() 33568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui || bufferInfo.presentationTimeUs < 0) { 33668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalArgumentException("bufferInfo must specify a" + 33768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui " valid buffer offset, size and presentation time"); 33868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 33968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 34068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject == 0) { 34168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Muxer has been released!"); 34268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 34368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 34468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState != MUXER_STATE_STARTED) { 34568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui throw new IllegalStateException("Can't write, muxer is not started"); 34668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 34768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 34868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeWriteSampleData(mNativeObject, trackIndex, byteBuf, 34968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui bufferInfo.offset, bufferInfo.size, 35068ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui bufferInfo.presentationTimeUs, bufferInfo.flags); 35168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 35268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui 35368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui /** 35468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * Make sure you call this when you're done to free up any resources 35568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * instead of relying on the garbage collector to do this for you at 35668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui * some point in the future. 35768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui */ 35868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui public void release() { 35968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mState == MUXER_STATE_STARTED) { 360a83df76859f0af12105bcea27e2e8d9f0b4d2c31ztenghui stop(); 36168ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 36268ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui if (mNativeObject != 0) { 36368ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui nativeRelease(mNativeObject); 36468ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mNativeObject = 0; 36568ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mCloseGuard.close(); 36668ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 36768ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui mState = MUXER_STATE_UNINITIALIZED; 36868ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui } 36968ccf103a10c674f1db649bb15bb3e790bc6dad3ztenghui} 370