編輯:關於Android編程
本文均屬自己閱讀源碼的點滴總結,轉賬請注明出處謝謝。
歡迎和大家交流。qq:1037701636 email: [email protected]
在上一文中,我們分析到setDataSource_pre()函數最終實際返回的是StagefrightPlayer類(class StagefrightPlayer : public MediaPlayerInterface).
1 .繼續分析setDataSource 函數:
// now set data source setDataSource_post(p, p->setDataSource(fd, offset, length));
實際是多態下的StagefrightPlayer的setDataSource 函數的實現:
status_t StagefrightPlayer::setDataSource( const char *url, const KeyedVector*headers) { return mPlayer->setDataSource(url, headers); }
mPlayer這個成員函數是new AwesomePlayer出來的對象,故最終是進入了stagefright中去做進一步的處理:
status_t AwesomePlayer::setDataSource( int fd, int64_t offset, int64_t length) { Mutex::Autolock autoLock(mLock); ... ..... return setDataSource_l(dataSource); }
status_t AwesomePlayer::setDataSource_l( const sp&dataSource) { sp extractor = MediaExtractor::Create(dataSource);//創建一個解析器MPEG4Extractor,mime = NULL if (extractor == NULL) { return UNKNOWN_ERROR; } if (extractor->getDrmFlag()) { checkDrmStatus(dataSource); } return setDataSource_l(extractor); }
MediaExtractor類,可以理解為音視頻數據源的解析器,我們來看其創建過程,傳入是默認參數mime= NULL:
spMediaExtractor::Create( const sp &source, const char *mime) { sp meta; String8 tmp; if (mime == NULL) { float confidence; if (!source->sniff(&tmp, &confidence, &meta)) {//提取mime數值 ALOGV("FAILED to autodetect media content."); return NULL; } mime = tmp.string();//獲取mime值 ALOGV("Autodetected media content as '%s' with confidence %.2f", mime, confidence); } //根據對文件解析的不同格式創建一個Extractor解析器 MediaExtractor *ret = NULL; if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4) || !strcasecmp(mime, "audio/mp4")) { int fragmented = 0; if (meta != NULL && meta->findInt32("fragmented", &fragmented) && fragmented) { ret = new FragmentedMP4Extractor(source); } else { ret = new MPEG4Extractor(source); } ................. return ret; }
在這裡穿插解釋下MIME類型的概念,谷歌來的,應該是表示各種視頻格式的一個字符段:
video/x-ms-asf asf video/mpeg mpeg mpg video/x-msvideo avi application/vnd.rn-realmedia rm audio/x-pn-realaudio ram ra audio/x-aiff aif aiff aifc audio/mpeg mpga mp3 audio/midi mid midi audio/wav wav audio/x-ms-wma wma video/x-ms-wmv wmv
2. 這裡和大家簡單分析source->sniff的實現過程,其目的很清楚就是獲取當前視頻源的MIME類型。
bool DataSource::sniff( String8 *mimeType, float *confidence, sp *meta) { *mimeType = ""; *confidence = 0.0f; meta->clear(); Mutex::Autolock autoLock(gSnifferMutex); for (List::iterator it = gSniffers.begin(); it != gSniffers.end(); ++it) { String8 newMimeType; float newConfidence; sp newMeta; if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) { if (newConfidence > *confidence) { *mimeType = newMimeType; *confidence = newConfidence; *meta = newMeta; } } } return *confidence > 0.0; }
該函數中一個gSnifers的全局變量中查找注冊的函數it,函數指針類型為SnifferFunc。那麼這些函數是如何注冊的呢,我們回到AwesomePlay的構造函數中去:
DataSource::RegisterDefaultSniffers();
void DataSource::RegisterDefaultSniffers() { RegisterSniffer(SniffMPEG4); RegisterSniffer(SniffFragmentedMP4); .......... char value[PROPERTY_VALUE_MAX]; if (property_get("drm.service.enabled", value, NULL) && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { RegisterSniffer(SniffDRM); } }
我們看到其注冊了多個函數指針,而這都是針對不同的格式進行的注冊,將他維護在這個gSniffers迭代器中:
void DataSource::RegisterSniffer(SnifferFunc func) { Mutex::Autolock autoLock(gSnifferMutex); for (List::iterator it = gSniffers.begin(); it != gSniffers.end(); ++it) { if (*it == func) { return; } } gSniffers.push_back(func);//保存函數指針 }
最終都回調到這個函數指針之中去:
bool SniffMPEG4( const sp&source, String8 *mimeType, float *confidence, sp *meta) { if (BetterSniffMPEG4(source, mimeType, confidence, meta)) { return true; } if (LegacySniffMPEG4(source, mimeType, confidence)) { ALOGW("Identified supported mpeg4 through LegacySniffMPEG4."); return true; } return false; }
這裡的BetterSniffMPEG4()函數邏輯比較復雜,但總體思想是讀取出source的一些頭信息,如果好當前的MPEG4格式所需具備的信息一樣則返回。
最終返回一個屬於該source的MIEM類型,如這裡假設的是MEDIA_MIMETYPE_CONTAINER_MPEG4的格式。
3.解析器的創建
經歷過上述的過程,繼續回到sp
ret = new MPEG4Extractor(source);
創建好上述的解析器後,我們回到AwesomePlayer::setDataSource_l()中,繼續執行setDataSource_l(extractor),對新建的這個解析器做處理,其實質是顯示音視頻A/V的分離。
setVideoSource(extractor->getTrack(i));//設置視頻源mVideoTrack ;
setAudioSource(extractor->getTrack(i));//設置音頻源mAudioTrack;
mVideoTrack和mAudioTrack的做為創建的AwesomePlay的成員函數,其類型為MPEG4Source,繼承了MediaSource。
spMPEG4Extractor::getTrack(size_t index) { status_t err; if ((err = readMetaData()) != OK) { return NULL; } Track *track = mFirstTrack; while (index > 0) { if (track == NULL) { return NULL; } track = track->next; --index; } if (track == NULL) { return NULL; } return new MPEG4Source( track->meta, mDataSource, track->timescale, track->sampleTable); }
到此為止就是講視頻源進行了A\V的分離,其過程是通過Stagefrightplay多媒體框架——>Awesomeplay——>MPEG4Extractor——>MPEG4Source.這幾個過程。
4. 准備好解碼器
在完成APP側的setDataSource後,就進入prepare操作。在MPS側由如下函數來實現:
status_t MediaPlayerService::Client::prepareAsync() { ALOGV("[%d] prepareAsync", mConnId); spp = getPlayer();//stragefrightplay類 if (p == 0) return UNKNOWN_ERROR; status_t ret; ret = p->prepareAsync(); #if CALLBACK_ANTAGONIZER ALOGD("start Antagonizer"); if (ret == NO_ERROR) mAntagonizer->start(); #endif return ret; }
getPlayer獲取之前創建的播放器StagefrightPlayer這個對象,繼續執行:
status_t StagefrightPlayer::prepareAsync() { return mPlayer->prepareAsync(); }
mPlayer即為Awesomeplayer,實際的實現如下:
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);//onPrepareAsyncEvent回調函數,事件處理 mQueue.postEvent(mAsyncPrepareEvent);//傳入類對象AwesomeEvent,mAsyncPrepareEvent return OK; }
在這裡將回答在(一)中提到的事件注冊與處理的這個過程。
4.1 AwesomePlayer中的Event處理機制。
a. 首先這裡需要來看mQueue,他是TimedEventQueue類的對象,稱為時間事件隊列。首次調用是,需要進行start。
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; }
顯而易見的是,這個e事件處理機制脫離了Awesomeplayer,獨立創建了一個線程ThradWrapper,其內部調用thredEntry來處理
void TimedEventQueue::threadEntry() { prctl(PR_SET_NAME, (unsigned long)"TimedEventQueue", 0, 0, 0); for (;;) { int64_t now_us = 0; spevent; while (mQueue.empty()) { mQueueNotEmptyCondition.wait(mLock); } ....... }
該函數就是在不斷等待著有事件需要處理,類似於一個Queue裡面非空則一直阻塞,等待signal喚醒。
b.我們來看AwesomeEvent類繼承了TimedEventQueue的Event內部類。其構造函數表明,將一個函數指針作為一個參數維護在mMethod。可以猜測到這個函數將會作為事件發生時的處理函數。那麼這個過程如何觸發呢?
struct AwesomeEvent : public TimedEventQueue::Event { AwesomeEvent( AwesomePlayer *player, void (AwesomePlayer::*method)()) : mPlayer(player), mMethod(method) { } protected: virtual ~AwesomeEvent() {} virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { (mPlayer->*mMethod)();//調用最終的注冊的處理函數 } private: AwesomePlayer *mPlayer; void (AwesomePlayer::*mMethod)(); AwesomeEvent(const AwesomeEvent &); AwesomeEvent &operator=(const AwesomeEvent &); };
c. mQueue.postEvent(mAsyncPrepareEvent);//傳入類對象AwesomeEvent,mAsyncPrepareEvent來實現事件的喚醒與處理
TimedEventQueue::event_id TimedEventQueue::postTimedEvent( const sp&event, int64_t realtime_us) { Mutex::Autolock autoLock(mLock); event->setEventID(mNextEventID++); List ::iterator it = mQueue.begin(); while (it != mQueue.end() && realtime_us >= (*it).realtime_us) { ++it; } QueueItem item; item.event = event; item.realtime_us = realtime_us; if (it == mQueue.begin()) { mQueueHeadChangedCondition.signal(); } mQueue.insert(it, item);//在mQueue中插入event mQueueNotEmptyCondition.signal();//發出信號觸發事件 return event->eventID(); }
將當前的Event對象插入打Awesomeplayer的mQueue隊列中,然後發出signal,喚醒threadEntry線程,讓線程去處理當前的事件。
5.真正進入解碼器創建的世界
void AwesomePlayer::onPrepareAsyncEvent() { //基於隊列和事件機制調用 Mutex::Autolock autoLock(mLock); if (mFlags & PREPARE_CANCELLED) { ALOGI("prepare was cancelled before doing anything"); abortPrepare(UNKNOWN_ERROR); return; } if (mUri.size() > 0) { status_t err = finishSetDataSource_l(); if (err != OK) { abortPrepare(err); return; } } if (mVideoTrack != NULL && mVideoSource == NULL) { status_t err = initVideoDecoder();//初始化視頻解碼器 if (err != OK) { abortPrepare(err); return; } } if (mAudioTrack != NULL && mAudioSource == NULL) { status_t err = initAudioDecoder();//初始化飲品解碼器 if (err != OK) { abortPrepare(err); return; } } modifyFlags(PREPARING_CONNECTED, SET); if (isStreamingHTTP()) { postBufferingEvent_l(); } else { finishAsyncPrepare_l();//完成異步的prepare } }
對於音視頻解碼器的創建和調用這裡不在做分析,考慮到他是一塊獨特的模塊,將在另一文中進行分析,自己也有很多內容需要進一步消化。
今天花了整個下午+晚上的的時間學習了Activity的啟動模式,本來以為這個知識點很簡單,但是在學習的過程中發現,Activity的啟動模式並沒有
相比主頁鍵(HOME)和最近應用鍵(APP_SWITCH)的處理,返回鍵比較簡單,復寫onKeyDown就可以實現,如下:
基本介紹畫廊在很多的App設計中都有,如下圖所示:該例子是我沒事的時候寫的一個小項目,具體源碼地址請訪問https://github.com/AlexSmille/Yin
一 問題描述: 查看android源碼時提示: The JAR of this class file belongs to container ‘Android