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