多媒體本地播放流程video playback--base on jellybean (三) .

   上一篇咱們講了多媒體的整體框架,本章咱們先來討論媒體文件的本地播放,也是手機的基本功能。如今市面上的手機配置愈來愈高,支持高清視頻(1920x1080P)已不在話下。那如今android主流播放器都支持哪些媒體格式呢?通常來講mp3,mp4,m4a,m4v,amr等大衆格式都是支持的,具體支持成什麼樣這得看手機廠商和芯片廠商了。具體格式大全能夠看framework/base/media/java/android/media/MediaFile.java

      咱們下面進入正題研究多媒體文件的本地播放(video playback),具體用到的工具備source insightastah(免費的畫流程圖工具),android 4.1代碼。代碼如何獲取能夠到google source下下載:http://source.android.com/source/downloading.htmlhtml

      通常上層應用要本地播放播放一個媒體文件,須要通過以下過程:     java

MediaPlayer  mMediaPlayer = new MediaPlayer( );

mMediaPlayer.setDataSource(mContext, mUri);- 

mMediaPlayer.setDisplay(mSurfaceHolder); 

mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

mMediaPlayer.prepareAsync();

mMediaPlayer.start();

 首先咱們先來分析setDataSource方法,這個方法有兩個功能:一個是根據文件類型得到相應的player,一個是建立相應文件類型的mediaExtractor,解析媒體文件,記錄metadata的主要信息。android

代碼以下:cookie

framework/av/media/libmedia/ MediaPlayer.cpp架構

status_t MediaPlayer::setDataSource(

        const char *url, const KeyedVector<String8, String8> *headers)

{

    ALOGV("setDataSource(%s)", url);

    status_t err = BAD_VALUE;

    if (url != NULL) {

        const sp<IMediaPlayerService>& service(getMediaPlayerService());

        if (service != 0) {

            sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId));

            if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||

                (NO_ERROR != player->setDataSource(url, headers))) {

                player.clear();

            }

            err = attachNewPlayer(player);

        }

    }

    return err;

}


   
咱們先來看下setDataSource方法中如何獲取player。大致的流程圖以下圖: 框架

 

    

    咱們知道MediaplayerService是負責外部請求,針對每一個APP player mediaplayerservice都會開闢一個client端來專門處理。ide

  client 定義以下:函數

framework/av/media/libmediaplayerservice/ MediaPlayerService.h工具

class Client : public BnMediaPlayer {...
 private:

        friend class MediaPlayerService;

                                Client( const sp<MediaPlayerService>& service,

                                        pid_t pid,

                                        int32_t connId,

                                        const sp<IMediaPlayerClient>& client,

                                        int audioSessionId,

                                        uid_t uid);

}
}

  從代碼看就是一個BnMediaplayer的子類(即local binder)。既然有了BnMediaplayer,客戶端也應該有相應的BpMediaplayer。獲取這個BpMediaplayer要分兩步驟走:第一,獲取BpMediaplayerService;第二就是在setDataSource方法中的:ui

    sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId)); 這個函數會返回一個BpMediaplayer

  獲取BpMediaplayerService,首先要去ServiceManager獲取相應的服務Mediaplayer,裏面的流程是這樣檢查是否存在要找的service,沒有就建立,有就返回BpXX

  有了BpMediaplayerService,咱們就能夠跟MediaplayerService通訊了,天然就能夠建立對應的client端來服務對應的BpMediaplayer(客戶端):

framework/av/media/libmediaplayerservice/ MediaPlayerService.cpp

sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client,

        int audioSessionId)

{

    int32_t connId = android_atomic_inc(&mNextConnId);

 

    sp<Client> c = new Client(

            this, pid, connId, client, audioSessionId,

            IPCThreadState::self()->getCallingUid());

 

    ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,

         IPCThreadState::self()->getCallingUid());

 

    wp<Client> w = c;

    {

        Mutex::Autolock lock(mLock);

        mClients.add(w);

    }

    return c;

}

 到此咱們的 sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId));

變成了:sp<IMediaPlayer> player(Client); 那它是如何變成咱們須要的BpMediaplayer呢,請看下面的定義原型,INTERFACE就是mediaplayer,大夥把宏取代下就知道了:

  frameworks/av/media/include/IMediapalyer.h

class IMediaPlayer: public IInterface

{

public:

    DECLARE_META_INTERFACE(MediaPlayer);

DECLARE_META_INTERFACE宏定義以下:

 

    #define DECLARE_META_INTERFACE(INTERFACE)                               \

    static const android::String16 descriptor;                          \

    static android::sp<I##INTERFACE> asInterface(                       \

            const android::sp<android::IBinder>& obj);                  \

    virtual const android::String16& getInterfaceDescriptor() const;    \

    I##INTERFACE();                                                     \

    virtual ~I##INTERFACE();   


有了DECLARE IMediaplayer.cpp 必有 IMPLEMENT

 

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \

    const android::String16 I##INTERFACE::descriptor(NAME);             \

    const android::String16&                                            \

            I##INTERFACE::getInterfaceDescriptor() const {              \

        return I##INTERFACE::descriptor;                                \

    }                                                                   \

    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \

            const android::sp<android::IBinder>& obj)                   \

    {                                                                   \

        android::sp<I##INTERFACE> intr;                                 \

        if (obj != NULL) {                                              \

            intr = static_cast<I##INTERFACE*>(                          \

                obj->queryLocalInterface(                               \

                        I##INTERFACE::descriptor).get());               \

            if (intr == NULL) {                                         \

                intr = new Bp##INTERFACE(obj);                          \

            }                                                           \

        }                                                               \

        return intr;                                                    \

    }                                                                   \

    I##INTERFACE::I##INTERFACE() { }                                    \

    I##INTERFACE::~I##INTERFACE() { }                                   \


   經過如上方法 ,咱們得到了BpMediaplayerremoteBinder),咱們就能夠經過BpMediaplayer BnMediaplayer通訊了。二者的交互是IBinder

BpMediaplayer具體實如今哪呢?

frameworks/av/media/libmedia/IMediaplayer.cpp:

class BpMediaPlayer: public BpInterface<IMediaPlayer>

{

public:

    BpMediaPlayer(const sp<IBinder>& impl)

        : BpInterface<IMediaPlayer>(impl)

    {

    }

 

    // disconnect from media player service

    void disconnect()

    {

        Parcel data, reply;

        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());

        remote()->transact(DISCONNECT, data, &reply);

    }

 

    status_t setDataSource(const char* url,

            const KeyedVector<String8, String8>* headers)

    {

        Parcel data, reply;

        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());

        data.writeCString(url);

        if (headers == NULL) {

            data.writeInt32(0);

        } else {

            // serialize the headers

            data.writeInt32(headers->size());

            for (size_t i = 0; i < headers->size(); ++i) {

                data.writeString8(headers->keyAt(i));

                data.writeString8(headers->valueAt(i));

            }

        }

        remote()->transact(SET_DATA_SOURCE_URL, data, &reply);

        return reply.readInt32();

    }

 

remote 就是一個IBinder, IBinder 經過transact 方法中的

IPCThreadState::self()->transact(

            mHandle, code, data, reply, flags);

  通知相應BnMediaplayerclient)進行相應的處理。裏面的如何打開binder,如何傳到MediaplayerService::client就不具體說了,有興趣能夠跟下去看看。

  以上咱們運用到了Binder的通訊機制,若是你們對此不太瞭解能夠看:

Android系統進程間通訊(IPC)機制Binder中的ServerClient得到Service Manager接口之路 .

  得到了BpMediaplayer ,咱們就能夠經過調用client端的setDataSource建立 player了:

status_t MediaPlayerService::Client::setDataSource(

        const char *url, const KeyedVector<String8, String8> *headers)

{….

若是是url 以content://開頭要轉換爲file descriptor

    if (strncmp(url, "content://", 10) == 0) {…

        int fd = android::openContentProviderFile(url16);

……….

        setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus

        close(fd);

        return mStatus;

    } else {

        player_type playerType = getPlayerType(url);…. createplayer前要判斷是哪一種類型

        LOGV("player type = %d", playerType);

        // create the right type of player

        sp<MediaPlayerBase> p = createPlayer(playerType);

        mStatus = p->setDataSource(url, headers);

…        

return mStatus;

    }

}

player_type getPlayerType(const char* url) ……………. 根據url的後綴名判斷屬於哪一種playerType,默認是stagefright,咱們如今研究的是本地播放,天然是stagefrightPlayer了

{

    if (TestPlayerStub::canBeUsed(url)) {

        return TEST_PLAYER;

    }

    // use MidiFile for MIDI extensions

    int lenURL = strlen(url);

    for (int i = 0; i < NELEM(FILE_EXTS); ++i) {

        int len = strlen(FILE_EXTS[i].extension);

        int start = lenURL - len;

        if (start > 0) {

            if (!strncasecmp(url + start, FILE_EXTS[i].extension, len)) {

                return FILE_EXTS[i].playertype;

            }

        }

    }

……………….

    return getDefaultPlayerType();

}

    自此咱們得到了想要的player了。這裏最主要的知識點就是Binder的通訊了,Binder的流程咱們能夠用下圖來解釋,你們能夠好好琢磨:

  player已經取得,接下來就是setDataSource的第二步:獲取相應的MediaExtractor並儲存相應的數據。

關於這一步,我也畫了個時序圖:

 

    緊接剛纔咱們得到player的步驟,咱們實例話一個stagefrightPlayer的同時也實例話了一個AwesomePlayer,其實真正幹實事的AwesomePlayerstagefrightPlayer只是個對外的接口,

 代碼以下:framework/av/media/libmediaplayerservice/ StagefrightPlayer.cpp

static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,

        notify_callback_f notifyFunc) {

…..

           case STAGEFRIGHT_PLAYER:

            ALOGV(" create StagefrightPlayer");

            p = new StagefrightPlayer;

            break;

…….

}

 

建立stagefrightplayer實例也new了個AwesomePlayer(mPlayer)

StagefrightPlayer::StagefrightPlayer()

    : mPlayer(new AwesomePlayer) {

    LOGV("StagefrightPlayer");

 

 

    mPlayer->setListener(this);

}


既然Awesomeplayer是幹實事的,咱們直接進去看看吧:

frameworks/av/media/libstagefright/AwesomePlayer.cpp

status_t AwesomePlayer::setDataSource_l(

        const sp<DataSource> &dataSource) {

    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);…….建立對應的extractor

…..

    return setDataSource_l(extractor);

}

status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {

…

for (size_t i = 0; i < extractor->countTracks(); ++i) {

        sp<MetaData> meta = extractor->getTrackMetaData(i);.......獲取相應track的元數據

        int32_t bitrate;

        if (!meta->findInt32(kKeyBitRate, &bitrate)) {

            const char *mime;

            CHECK(meta->findCString(kKeyMIMEType, &mime));

            ALOGV("track of type '%s' does not publish bitrate", mime);

 

            totalBitRate = -1;

            break;

        }

 

        totalBitRate += bitrate;

    }

 

.........

        if (!haveVideo && !strncasecmp(mime, "video/", 6)) {

            setVideoSource(extractor->getTrack(i)); ………>mVideoTrack

            haveVideo = true;

        } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {

            setAudioSource(extractor->getTrack(i));……….>mAudioTrack

            haveAudio = true; 

    return OK;

}

    關於MediaExtractor裏面涉及到媒體文件格式的不少內容,好比track的構成,有幾種track等等,咱們未來在videoRecorder中再詳細講解。這裏只有知道提取相關信息就好了。

    此方法調用完成意味着player進入了MEDIA_PLAYER_INITIALIZED狀態。Player的狀態有以下幾種:   

    MEDIA_PLAYER_STATE_ERROR
    MEDIA_PLAYER_IDLE 
    MEDIA_PLAYER_INITIALIZED 
    MEDIA_PLAYER_PREPARING 
    MEDIA_PLAYER_PREPARED
    MEDIA_PLAYER_STARTED
    MEDIA_PLAYER_PAUSED
    MEDIA_PLAYER_STOPPED
    MEDIA_PLAYER_PLAYBACK_COMPLETE

     setDataSource咱們已經講完了,講流程咱們的目的是熟悉它的架構,但願你們很好好熟悉熟悉,在項目須要的時候根據咱們本身的媒體格式進行,依葫蘆畫瓢進行改造,好比說支持多track,切換track,以達到KTV的功能等等。。。

    下一篇咱們將講解prepare的過程,這個工程主要是匹配codec,初始化codec等。

相關文章
相關標籤/搜索