android的多媒體框架中, stagefright其實是AwesomePlayer的代理,就是個皮包公司。

status_t StagefrightPlayer::setDataSource( const char *url, const KeyedVector<String8, String8> *headers) { return mPlayer->setDataSource(url, headers); } status_t StagefrightPlayer::prepare() { return mPlayer->prepare(); } status_t StagefrightPlayer::start() { ALOGV("start"); return mPlayer->play(); } status_t StagefrightPlayer::stop() { ALOGV("stop"); return pause(); // what's the difference?
} status_t StagefrightPlayer::pause() { ALOGV("pause"); return mPlayer->pause(); }
功能幾乎都是通過調用

AwesomePlayer *mPlayer;
成員變量來實現的,看來真正實現播放器功能的原來是 AwesomePlayer。Stagefright只是做了一層淺淺的封裝。
AwesomePlayer 不管他有多神秘,說到底還是個多媒體播放器。在播放器的基本模型上,他與VCL、mplayer、ffmpeg等開源的結構是一致的。只是組織實現的方式不同。
深入了解AwesomePlayer 之前,把播放器的基本模型總結一下,然后按照模型的各個部分來深入研究AwesomePlayer 的實現方式。
說白了播放器大致分為4大部分:source、demux、decoder、output。
1.source:數據源,數據的來源不一定都是本地file,也有可能是網路上的各種協議例如:http、rtsp、HLS等。source的任務就是把數據源抽象出來,為下一個demux模塊提供它需要的穩定的數據流。demux不用關信數據到底是從什么地方來的。
2.demux解復用:視頻文件一般情況下都是把音視頻的ES流交織的通過某種規則放在一起。這種規則就是容器規則。現在有很多不同的容器格式。如ts、mp4、flv、mkv、avi、rmvb等等。demux的功能就是把音視頻的ES流從容器中剝離出來,然后分別送到不同的解碼器中。其實音頻和視頻本身就是2個獨立的系統。容器把它們包在了一起。但是他們都是獨立解碼的,所以解碼之前,需要把它分別 獨立出來。demux就是干這活的,他為下一步decoder解碼提供了數據流。
3.decoder解碼:解碼器--播放器的核心模塊。分為音頻和視頻解碼器。影像在錄制后, 原始的音視頻都是占用大量空間, 而且是冗余度較高的數據. 因此, 通常會在制作的時候就會進行某種壓縮 ( 壓縮技術就是將數據中的冗余信息去除數據之間的相關性 ). 這就是我們熟知的音視頻編碼格式, 包括MPEG1(VCD)\ MPEG2(DVD)\ MPEG4 \ H.264 等等. 音視頻解碼器的作用就是把這些壓縮了的數據還原成原始的音視頻數據. 當然, 編碼解碼過程基本上都是有損的 .解碼器的作用就是把編碼后的數據還原成原始數據。
4.output輸出:輸出部分分為音頻和視頻輸出。解碼后的音頻(pcm)和視頻(yuv)的原始數據需要得到音視頻的output模塊的支持才能真正的讓人的感官系統(眼和耳)辨識到。
所以,播放器大致分成上述4部分。怎么抽象的實現這4大部分、以及找到一種合理的方式將這幾部分組織並運動起來。是每個播放器不同的實現方式而已。
接下來就圍繞這4大部分做深入分析。
上文已經大概介紹了播放器的幾大主要部分,但是有了這些功能組件也不能叫做播放器。需要某種方式將這些單獨的功能組件驅動起來,形成一個整體的功能。
視頻處理過程中有很多都是十分耗時的,如果都放在一個大的線程空間中。用戶體驗的效果可想而知。所以通常都是做異步操作。
AwesomePlayer是通過event事件調度來實現這些功能之間的驅動和調用的。
AwesomePlayer中的內部變量

TimedEventQueue mQueue;
這個mQueue就是AwesomePlayer的事件隊列,也是事件調度器。從他類型的名字上就能很清楚的看出他是以時間為基礎事件隊列。接下來看看它是怎么玩轉的。
1.先來看TimedEventQueue的內部結構,TimedEventQueue內部有一個 List<QueueItem>,每個QueueItem包含enent和時間

struct QueueItem { sp<Event> event; int64_t realtime_us; };
有一個獨立線程threadEntry是在TimedEventQueue::start被創建,TimedEventQueue::stop被銷毀的。

void TimedEventQueue::start() { if (mRunning) { return; } mStopped = false; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&mThread, &attr, ThreadWrapper, this); pthread_attr_destroy(&attr); mRunning = true; } void TimedEventQueue::stop(bool flush) { if (!mRunning) { return; } if (flush) { postEventToBack(new StopEvent); } else { postTimedEvent(new StopEvent, INT64_MIN); } void *dummy; pthread_join(mThread, &dummy); mQueue.clear(); mRunning = false; }
2.List<QueueItem>目的就是按照延時時間維護一個event事件隊列,threadEntry線程就是不斷的從隊列的頭取出一個event,然后通過 event->fire(this, now_us); 回調到這個event事件提前注冊好的相對應功能函數。
3.然后看看AwesomePlayer是怎么用TimedEventQueue,AwesomePlayer會定義很多類型的event事件,並把和這些事件相關的功能函數一定綁定起來。

mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent); mVideoEventPending = false; mStreamDoneEvent = new AwesomeEvent(this, &AwesomePlayer::onStreamDone); mStreamDoneEventPending = false; mBufferingEvent = new AwesomeEvent(this, &AwesomePlayer::onBufferingUpdate); mBufferingEventPending = false; mVideoLagEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoLagUpdate); mVideoEventPending = false; mCheckAudioStatusEvent = new AwesomeEvent(this, &AwesomePlayer::onCheckAudioStatus);
原因之前也說了,因為好多音視頻處理的功能是十分耗時間的,假如AwesomePlayer 想用某個功能,他並不是直線去調用它,而是抽象成一種AwesomeEvent,將想要調用的功能函數與事件捆綁。通過TimedEventQueue::postTimedEvent(),按照延時的優先順序把它放到TimedEventQueue的隊列之中。然后AwesomePlayer就不管了。TimedEventQueue start之后,自己內部的線程會從隊列中依次取出這些事件,然后通過event->fire回調事件的功能函數。這樣就達到了AwesomePlayer的目的。
4.之前也介紹過mediaPlayer大致流程就是
mediaPlayer.setDataSource(path);
mediaPlayer.prepare();
mediaPlayer.start();
在AwesomePlayer 也是這種流程,在AwesomePlayer prepare()相關函數中。

status_t AwesomePlayer::prepareAsync_l() { if (mFlags & PREPARING) { return UNKNOWN_ERROR; // async prepare already pending
} if (!mQueueStarted) { mQueue.start(); mQueueStarted = true; } modifyFlags(PREPARING, SET); mAsyncPrepareEvent = new AwesomeEvent( this, &AwesomePlayer::onPrepareAsyncEvent); mQueue.postEvent(mAsyncPrepareEvent); return OK; }
並沒有實際的調用onPrepareAsyncEvent()真正的功能函數,他只是把mQueue start之后,然后創建個mAsyncPrepareEvent事件,把它插入到mQueue之中就不管了,具體調用是由mQueue中的threadEntry線程來做。
1.通過setDataSource 指定播放器的數據源。可以是URI或者fd.可以是http:// 、rtsp://、本地地址或者本地文件描述符fd。其最終調用是將上層傳遞來的參數轉化為DataSource,為下一步的demux提供數據支持。
2.在真正Prepare功能函數onPrepareAsyncEvent()會調用finishSetDataSource_l。通過第一步產生的DataSource來生成extractor,因為封裝的格式很多,所以需要通過DataSource的信息,去創建不同的extractor。

extractor = MediaExtractor::Create( dataSource, sniffedMIME.empty() ? NULL : sniffedMIME.c_str());

if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4) || !strcasecmp(mime, "audio/mp4")) { ret = new MPEG4Extractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { ret = new MP3Extractor(source, meta); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { ret = new AMRExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) { ret = new FLACExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) { ret = new WAVExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) { ret = new OggExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) { ret = new MatroskaExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { ret = new MPEG2TSExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) { // Return now. WVExtractor should not have the DrmFlag set in the block below.
return new WVMExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) { ret = new AACExtractor(source, meta); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) { ret = new MPEG2PSExtractor(source); }
3.得到extractor之后,通過setVideoSource() setAudioSource()產生獨立的mVideoTrack(視頻)、mAudioTrack(音頻)數據流,分別為音視頻解碼器提供有各自需要的數據流。
其實extractor和mVideoTrack、mAudioTrack就組成了播放器模型中的demuxer部分。把封裝格式里面的音視頻流拆分出來,分別的送給音視頻解碼器。
4.接下來就是initVideoDecoder() initAudioDecoder().依賴上面產生的mVideoTrack(視頻)、mAudioTrack(音頻)數據流。生成了mVideoSource和mAudioSource這兩個音視頻解碼器。不同類型匹配不同的解碼器。

mVideoSource = OMXCodec::Create( mClient.interface(), mVideoTrack->getFormat(), false, // createEncoder
mVideoTrack, NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL); mAudioSource = OMXCodec::Create( mClient.interface(), mAudioTrack->getFormat(), false, // createEncoder
mAudioTrack); }
mVideoSource、mAudioSource組成了播放器模型中的decoder部分。
android系統中的編解碼器部分用的是openmax,以后會深入了解。openma x是一套標准接口,各家硬件廠商都可以遵循這個標准來做自己的實現,發揮自己芯片特性。然后提供給android系統來用。因為大部分的機頂盒芯片產品硬件的編解碼是它的優勢,可以把這種優勢完全融入到android平台中。以后手機高清視頻硬解碼也會是個趨勢。
5.解碼完之后的數據就要輸出了。AwesomePlayer分別用了mVideoRenderer做視頻輸出、mAudioPlayer做音頻輸出。他們分別調用android圖像和音頻的相關服務。這倆部分是android平台中十分重要的2塊,以后會深入了解。
mVideoRenderer和mAudioPlayer就組成了播放器中output的部分。
綜上AwesomePlayer的整體框架和流程就清晰了,其實也脫離不了DataSource、demux、decoder、output這4大部分。接下來會分別了解每個部分是怎么實現的。
1.openmax 簡介

2.OpenMax IL簡介
OpenMax IL 處在中間層的位置,OpenMAX IL 作為音頻,視頻和圖像編解碼器 能與多媒體編解碼器交互,並以統一的行為支持組件(例如資源和皮膚)。這些編解碼器或許是軟硬件的混合體,對用戶是 的底層接口應用於嵌入式或 / 和移動設備。它提供了應用程序和媒體框架, 透明的。本質上不存在這種標准化的接口,編解碼器供 應商必須寫私有的或者封閉的接口,集成進移動設備。 IL 的主要目的 是使用特征集合為編解碼器提供一個系統抽象,為解決多個不同媒體系統之間輕便性的問題。

3.OpenMax IL結構
OpenMax IL主要內容如下所示。
客戶端(Client):OpenMax IL的調用者
組件(Component):OpenMax IL的單元,每一個組件實現一種功能
端口(Port):組件的輸入輸出接口
隧道化(Tunneled):讓兩個組件直接連接的方式

4.Component內部結構
Component 的基本模型如上圖,可以把它想象成一個加工車間:
輸入端口輸入材料
輸出端口輸出加工完成品
通過handle 來給車間發送指令或者或者狀態
將事件及時發送給車間外部管理者
1.android中用openmax來干啥?

2.android中openmax實現框架

AwesomePlayer 中有個變量
- OMXClient mClient;

sp<IOMX> MediaPlayerService::getOMX() { Mutex::Autolock autoLock(mLock); if (mOMX.get() == NULL) { mOMX = new OMX; } return mOMX; }

sp<IOMX> interface() { return mOMX; }

mAudioSource = OMXCodec::Create( mClient.interface(), mAudioTrack->getFormat(), false, // createEncoder mAudioTrack);