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

 上一篇我們講瞭多媒體的總體框架,本章我們先來討論媒體文件的本地播放,也是手機的基本功能。現在市面上的手機配置越來越高,支持高清視頻(1920x1080P)已不在話下。那現在android主流播放器都支持哪些媒體格式呢?一般來說mp3,mp4,m4a,m4v,amr等大眾格式都是支持的,具體支持成什麼樣這得看手機廠商和芯片廠商瞭。具體格式大全可以看framework/base/media/java/android/media/MediaFile.java。
      我們下面進入正題研究多媒體文件的本地播放(video playback),具體用到的工具有source insight,astah(免費的畫流程圖工具),android 4.1代碼。代碼如何獲取可以到google source下下載:http://source.android.com/source/downloading.html。
      一般上層應用要本地播放播放一個媒體文件,需要經過如下過程:
   
MediaPlayer  mMediaPlayer = new MediaPlayer( );
mMediaPlayer.setDataSource(mContext, mUri);-
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.prepareAsync();
mMediaPlayer.start();
 
 
    首先我們先來分析setDataSource方法,這個方法有兩個功能:一個是根據文件類型獲得相應的player,一個是創建相應文件類型的mediaExtractor,解析媒體文件,記錄metadata的主要信息。
代碼如下:
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端來專門處理。
  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方法中的:
    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() { }                                   \

   通過如上方法 ,我們獲得瞭BpMediaplayer(remoteBinder),我們就可以通過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);
 
通知相應BnMediaplayer(client)進行相應的處理。裡面的如何打開binder,如何傳到MediaplayerService::client就不具體說瞭,有興趣可以跟下去看看。
 
以上我們運用到瞭Binder的通信機制,如果大傢對此不太瞭解可以看:
Android系統進程間通信(IPC)機制Binder中的Server和Client獲得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的通信瞭,  
player已經取得,接下來就是setDataSource的第二步:獲取相應的MediaExtractor並儲存相應的數據。
 
  
    緊接剛才我們獲得player的步驟,我們實例話一個stagefrightPlayer的同時也實例話瞭一個AwesomePlayer,其實真正幹實事的AwesomePlayer,stagefrightPlayer隻是個對外的接口,
 代碼如下: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等。
 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *