Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77cda104 authored by James Dong's avatar James Dong Committed by Android (Google) Code Review
Browse files

Merge "Defines MediaPlayer APIs to support multiple audio/video/timedtext tracks."

parents d5bceea4 41f3f716
Loading
Loading
Loading
Loading
+20 −3
Original line number Diff line number Diff line
@@ -120,6 +120,9 @@ enum media_info_type {
    MEDIA_INFO_NOT_SEEKABLE = 801,
    // New media metadata is available.
    MEDIA_INFO_METADATA_UPDATE = 802,

    //9xx
    MEDIA_INFO_TIMED_TEXT_ERROR = 900,
};


@@ -140,9 +143,6 @@ enum media_player_states {
// The same enum space is used for both set and get, in case there are future keys that
// can be both set and get.  But as of now, all parameters are either set only or get only.
enum media_parameter_keys {
    KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000,                // set only
    KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE = 1001,     // set only

    // Streaming/buffering parameters
    KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100,            // set only

@@ -155,6 +155,23 @@ enum media_parameter_keys {
    KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300,                // set only
};

// Keep INVOKE_ID_* in sync with MediaPlayer.java.
enum media_player_invoke_ids {
    INVOKE_ID_GET_TRACK_INFO = 1,
    INVOKE_ID_ADD_EXTERNAL_SOURCE = 2,
    INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
    INVOKE_ID_SELECT_TRACK = 4,
    INVOKE_ID_UNSELECT_TRACK = 5,
};

// Keep MEDIA_TRACK_TYPE_* in sync with MediaPlayer.java.
enum media_track_type {
    MEDIA_TRACK_TYPE_UNKNOWN = 0,
    MEDIA_TRACK_TYPE_VIDEO = 1,
    MEDIA_TRACK_TYPE_AUDIO = 2,
    MEDIA_TRACK_TYPE_TIMEDTEXT = 3,
};

// ----------------------------------------------------------------------------
// ref-counted object for callbacks
class MediaPlayerListener: virtual public RefBase
+1 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS;
extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;

extern const char *MEDIA_MIMETYPE_TEXT_3GPP;
extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP;

}  // namespace android

+10 −10
Original line number Diff line number Diff line
@@ -37,26 +37,26 @@ public:

    ~TimedTextDriver();

    // TODO: pause-resume pair seems equivalent to stop-start pair.
    // Check if it is replaceable with stop-start.
    status_t start();
    status_t stop();
    status_t pause();
    status_t resume();
    status_t selectTrack(int32_t index);
    status_t unselectTrack(int32_t index);

    status_t seekToAsync(int64_t timeUs);

    status_t addInBandTextSource(const sp<MediaSource>& source);
    status_t addOutOfBandTextSource(const Parcel &request);
    status_t addOutOfBandTextSource(const char *uri, const char *mimeType);
    // Caller owns the file desriptor and caller is responsible for closing it.
    status_t addOutOfBandTextSource(
            int fd, off64_t offset, size_t length, const char *mimeType);

    status_t setTimedTextTrackIndex(int32_t index);
    void getTrackInfo(Parcel *parcel);

private:
    Mutex mLock;

    enum State {
        UNINITIALIZED,
        STOPPED,
        PLAYING,
        PAUSED,
    };
@@ -67,11 +67,11 @@ private:

    // Variables to be guarded by mLock.
    State mState;
    Vector<sp<TimedTextSource> > mTextInBandVector;
    Vector<sp<TimedTextSource> > mTextOutOfBandVector;
    int32_t mCurrentTrackIndex;
    Vector<sp<TimedTextSource> > mTextSourceVector;
    // -- End of variables to be guarded by mLock

    status_t setTimedTextTrackIndex_l(int32_t index);
    status_t selectTrack_l(int32_t index);

    DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver);
};
+316 −44
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.util.Log;
@@ -455,6 +456,22 @@ import java.lang.ref.WeakReference;
 *     <td>Successful invoke of this method in a valid state transfers the
 *         object to the <em>Stopped</em> state. Calling this method in an
 *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
 * <tr><td>getTrackInfo </p></td>
 *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
 *     <td>{Idle, Initialized, Error}</p></td>
 *     <td>Successful invoke of this method does not change the state.</p></td></tr>
 * <tr><td>addExternalSource </p></td>
 *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
 *     <td>{Idle, Initialized, Error}</p></td>
 *     <td>Successful invoke of this method does not change the state.</p></td></tr>
 * <tr><td>selectTrack </p></td>
 *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
 *     <td>{Idle, Initialized, Error}</p></td>
 *     <td>Successful invoke of this method does not change the state.</p></td></tr>
 * <tr><td>disableTrack </p></td>
 *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
 *     <td>{Idle, Initialized, Error}</p></td>
 *     <td>Successful invoke of this method does not change the state.</p></td></tr>
 *
 * </table>
 *
@@ -572,6 +589,15 @@ public class MediaPlayer
     */
    private native void _setVideoSurface(Surface surface);

    /* Do not change these values (starting with INVOKE_ID) without updating
     * their counterparts in include/media/mediaplayer.h!
     */
    private static final int INVOKE_ID_GET_TRACK_INFO = 1;
    private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE = 2;
    private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;
    private static final int INVOKE_ID_SELECT_TRACK = 4;
    private static final int INVOKE_ID_UNSELECT_TRACK = 5;

    /**
     * Create a request parcel which can be routed to the native media
     * player using {@link #invoke(Parcel, Parcel)}. The Parcel
@@ -1312,23 +1338,6 @@ public class MediaPlayer
    /* Do not change these values (starting with KEY_PARAMETER) without updating
     * their counterparts in include/media/mediaplayer.h!
     */
    /*
     * Key used in setParameter method.
     * Indicates the index of the timed text track to be enabled/disabled.
     * The index includes both the in-band and out-of-band timed text.
     * The index should start from in-band text if any. Application can retrieve the number
     * of in-band text tracks by using MediaMetadataRetriever::extractMetadata().
     * Note it might take a few hundred ms to scan an out-of-band text file
     * before displaying it.
     */
    private static final int KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000;
    /*
     * Key used in setParameter method.
     * Used to add out-of-band timed text source path.
     * Application can add multiple text sources by calling setParameter() with
     * KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE multiple times.
     */
    private static final int KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE = 1001;

    // There are currently no defined keys usable from Java with get*Parameter.
    // But if any keys are defined, the order must be kept in sync with include/media/mediaplayer.h.
@@ -1373,7 +1382,7 @@ public class MediaPlayer
        return ret;
    }

    /**
    /*
     * Gets the value of the parameter indicated by key.
     * @param key key indicates the parameter to get.
     * @param reply value of the parameter to get.
@@ -1435,7 +1444,7 @@ public class MediaPlayer
     */
    public native void setAuxEffectSendLevel(float level);

    /**
    /*
     * @param request Parcel destinated to the media player. The
     *                Interface token must be set to the IMediaPlayer
     *                one to be routed correctly through the system.
@@ -1445,7 +1454,7 @@ public class MediaPlayer
    private native final int native_invoke(Parcel request, Parcel reply);


    /**
    /*
     * @param update_only If true fetch only the set of metadata that have
     *                    changed since the last invocation of getMetadata.
     *                    The set is built using the unfiltered
@@ -1462,7 +1471,7 @@ public class MediaPlayer
                                                    boolean apply_filter,
                                                    Parcel reply);

    /**
    /*
     * @param request Parcel with the 2 serialized lists of allowed
     *                metadata types followed by the one to be
     *                dropped. Each list starts with an integer
@@ -1476,33 +1485,289 @@ public class MediaPlayer
    private native final void native_finalize();

    /**
     * @param index The index of the text track to be turned on.
     * @return true if the text track is enabled successfully.
     * Class for MediaPlayer to return each audio/video/subtitle track's metadata.
     *
     * {@see #getTrackInfo()}.
     * {@hide}
     */
    static public class TrackInfo implements Parcelable {
        /**
         * Gets the track type.
         * @return TrackType which indicates if the track is video, audio, timed text.
         */
        public int getTrackType() {
            return mTrackType;
        }

        /**
         * Gets the language code of the track.
         * @return a language code in either way of ISO-639-1 or ISO-639-2.
         * When the language is unknown or could not be determined,
         * ISO-639-2 language code, "und", is returned.
         */
        public String getLanguage() {
            return mLanguage;
        }

        public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0;
        public static final int MEDIA_TRACK_TYPE_VIDEO = 1;
        public static final int MEDIA_TRACK_TYPE_AUDIO = 2;
        public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;

        final int mTrackType;
        final String mLanguage;

        TrackInfo(Parcel in) {
            mTrackType = in.readInt();
            mLanguage = in.readString();
        }

        /*
         * No special parcel contents. Keep it as hide.
         * {@hide}
         */
        @Override
        public int describeContents() {
            return 0;
        }

        /*
         * {@hide}
         */
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(mTrackType);
            dest.writeString(mLanguage);
        }

        /**
         * Used to read a TrackInfo from a Parcel.
         */
        static final Parcelable.Creator<TrackInfo> CREATOR
                = new Parcelable.Creator<TrackInfo>() {
                    @Override
                    public TrackInfo createFromParcel(Parcel in) {
                        return new TrackInfo(in);
                    }

                    @Override
                    public TrackInfo[] newArray(int size) {
                        return new TrackInfo[size];
                    }
                };

    };

    /**
     * Returns an array of track information.
     *
     * @return Array of track info. null if an error occured.
     * {@hide}
     */
    public boolean enableTimedTextTrackIndex(int index) {
        if (index < 0) {
    // FIXME: It returns timed text tracks' information for now. Other types of tracks will be
    // supported in future.
    public TrackInfo[] getTrackInfo() {
        Parcel request = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        request.writeInterfaceToken(IMEDIA_PLAYER);
        request.writeInt(INVOKE_ID_GET_TRACK_INFO);
        invoke(request, reply);
        TrackInfo trackInfo[] = reply.createTypedArray(TrackInfo.CREATOR);
        return trackInfo;
    }

    /*
     * A helper function to check if the mime type is supported by media framework.
     */
    private boolean availableMimeTypeForExternalSource(String mimeType) {
        if (mimeType == MEDIA_MIMETYPE_TEXT_SUBRIP) {
            return true;
        }
        return false;
    }
        return setParameter(KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX, index);

    /* TODO: Limit the total number of external timed text source to a reasonable number.
     */
    /**
     * Adds an external source file.
     *
     * Currently supported format is SubRip with the file extension .srt, case insensitive.
     * Note that a single external source may contain multiple tracks in it.
     * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
     * additional tracks become available after this method call.
     *
     * @param path The file path of external source file.
     * {@hide}
     */
    // FIXME: define error codes and throws exceptions according to the error codes.
    // (IllegalStateException, IOException).
    public void addExternalSource(String path, String mimeType)
            throws IllegalArgumentException {
        if (!availableMimeTypeForExternalSource(mimeType)) {
            throw new IllegalArgumentException("Illegal mimeType for external source: " + mimeType);
        }

        Parcel request = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        request.writeInterfaceToken(IMEDIA_PLAYER);
        request.writeInt(INVOKE_ID_ADD_EXTERNAL_SOURCE);
        request.writeString(path);
        request.writeString(mimeType);
        invoke(request, reply);
    }

    /**
     * Enables the first timed text track if any.
     * @return true if the text track is enabled successfully
     * Adds an external source file (Uri).
     *
     * Currently supported format is SubRip with the file extension .srt, case insensitive.
     * Note that a single external source may contain multiple tracks in it.
     * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
     * additional tracks become available after this method call.
     *
     * @param context the Context to use when resolving the Uri
     * @param uri the Content URI of the data you want to play
     * {@hide}
     */
    public boolean enableTimedText() {
        return enableTimedTextTrackIndex(0);
    // FIXME: define error codes and throws exceptions according to the error codes.
    // (IllegalStateException, IOException).
    public void addExternalSource(Context context, Uri uri, String mimeType)
            throws IOException, IllegalArgumentException {
        String scheme = uri.getScheme();
        if(scheme == null || scheme.equals("file")) {
            addExternalSource(uri.getPath(), mimeType);
            return;
        }

        AssetFileDescriptor fd = null;
        try {
            ContentResolver resolver = context.getContentResolver();
            fd = resolver.openAssetFileDescriptor(uri, "r");
            if (fd == null) {
                return;
            }
            addExternalSource(fd.getFileDescriptor(), mimeType);
            return;
        } catch (SecurityException ex) {
        } catch (IOException ex) {
        } finally {
            if (fd != null) {
                fd.close();
            }
        }

        // TODO: try server side.
    }

    /* Do not change these values without updating their counterparts
     * in include/media/stagefright/MediaDefs.h and media/libstagefright/MediaDefs.cpp!
     */
    /**
     * Disables timed text display.
     * @return true if the text track is disabled successfully.
     * MIME type for SubRip (SRT) container. Used in {@link #addExternalSource()} APIs.
     * {@hide}
     */
    public boolean disableTimedText() {
        return setParameter(KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX, -1);
    public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";

    /**
     * Adds an external source file (FileDescriptor).
     * It is the caller's responsibility to close the file descriptor.
     * It is safe to do so as soon as this call returns.
     *
     * Currently supported format is SubRip with the file extension .srt, case insensitive.
     * Note that a single external source may contain multiple tracks in it.
     * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
     * additional tracks become available after this method call.
     *
     * @param fd the FileDescriptor for the file you want to play
     * @param mimeType A MIME type for the content. It can be null.
     * <ul>
     * <li>{@link #MEDIA_MIMETYPE_TEXT_SUBRIP}
     * </ul>
     * {@hide}
     */
    // FIXME: define error codes and throws exceptions according to the error codes.
    // (IllegalStateException, IOException).
    public void addExternalSource(FileDescriptor fd, String mimeType)
            throws IllegalArgumentException {
        // intentionally less than LONG_MAX
        addExternalSource(fd, 0, 0x7ffffffffffffffL, mimeType);
    }

    /**
     * Adds an external timed text file (FileDescriptor).
     * It is the caller's responsibility to close the file descriptor.
     * It is safe to do so as soon as this call returns.
     *
     * Currently supported format is SubRip with the file extension .srt, case insensitive.
     * Note that a single external source may contain multiple tracks in it.
     * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
     * additional tracks become available after this method call.
     *
     * @param fd the FileDescriptor for the file you want to play
     * @param offset the offset into the file where the data to be played starts, in bytes
     * @param length the length in bytes of the data to be played
     * @param mimeType A MIME type for the content. It can be null.
     * {@hide}
     */
    // FIXME: define error codes and throws exceptions according to the error codes.
    // (IllegalStateException, IOException).
    public void addExternalSource(FileDescriptor fd, long offset, long length, String mimeType)
            throws IllegalArgumentException {
        if (!availableMimeTypeForExternalSource(mimeType)) {
            throw new IllegalArgumentException("Illegal mimeType for external source: " + mimeType);
        }
        Parcel request = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        request.writeInterfaceToken(IMEDIA_PLAYER);
        request.writeInt(INVOKE_ID_ADD_EXTERNAL_SOURCE_FD);
        request.writeFileDescriptor(fd);
        request.writeLong(offset);
        request.writeLong(length);
        request.writeString(mimeType);
        invoke(request, reply);
    }

    /**
     * Selects a track.
     * <p>
     * If a MediaPlayer is in invalid state, it throws exception.
     * If a MediaPlayer is in Started state, the selected track will be presented immediately.
     * If a MediaPlayer is not in Started state, it just marks the track to be played.
     * </p>
     * <p>
     * In any valid state, if it is called multiple times on the same type of track (ie. Video,
     * Audio, Timed Text), the most recent one will be chosen.
     * </p>
     * <p>
     * The first audio and video tracks will be selected by default, even though this function is not
     * called. However, no timed text track will be selected until this function is called.
     * </p>
     * {@hide}
     */
    // FIXME: define error codes and throws exceptions according to the error codes.
    // (IllegalStateException, IOException, IllegalArgumentException).
    public void selectTrack(int index) {
        Parcel request = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        request.writeInterfaceToken(IMEDIA_PLAYER);
        request.writeInt(INVOKE_ID_SELECT_TRACK);
        request.writeInt(index);
        invoke(request, reply);
    }

    /**
     * Unselect a track.
     * If the track identified by index has not been selected before, it throws an exception.
     * {@hide}
     */
    // FIXME: define error codes and throws exceptions according to the error codes.
    // (IllegalStateException, IOException, IllegalArgumentException).
    public void unselectTrack(int index) {
        Parcel request = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        request.writeInterfaceToken(IMEDIA_PLAYER);
        request.writeInt(INVOKE_ID_UNSELECT_TRACK);
        request.writeInt(index);
        invoke(request, reply);
    }

    /**
@@ -1641,7 +1906,8 @@ public class MediaPlayer
                // No real default action so far.
                return;
            case MEDIA_TIMED_TEXT:
                if (mOnTimedTextListener != null) {
                if (mOnTimedTextListener == null)
                    return;
                if (msg.obj == null) {
                    mOnTimedTextListener.onTimedText(mMediaPlayer, null);
                } else {
@@ -1650,7 +1916,6 @@ public class MediaPlayer
                        mOnTimedTextListener.onTimedText(mMediaPlayer, text);
                    }
                }
                }
                return;

            case MEDIA_NOP: // interface test message - ignore
@@ -1663,7 +1928,7 @@ public class MediaPlayer
        }
    }

    /**
    /*
     * Called from native code when an interesting event happens.  This method
     * just uses the EventHandler system to post the event back to the main app thread.
     * We use a weak reference to the original MediaPlayer object so that the native
@@ -1977,6 +2242,13 @@ public class MediaPlayer
     */
    public static final int MEDIA_INFO_METADATA_UPDATE = 802;

    /** Failed to handle timed text track properly.
     * @see android.media.MediaPlayer.OnInfoListener
     *
     * {@hide}
     */
    public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;

    /**
     * Interface definition of a callback to be invoked to communicate some
     * info and/or warning about the media or its playback.
+2 −1
Original line number Diff line number Diff line
@@ -166,7 +166,8 @@ player_type StagefrightPlayer::playerType() {
}

status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
    return INVALID_OPERATION;
    ALOGV("invoke()");
    return mPlayer->invoke(request, reply);
}

void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
Loading