Continue the analysis in the previous chapter: [5] Android MediaPlayer overall architecture source code analysis - [prepareAsync/prepare data preparation processing flow] [Part 1]
Android source code version analyzed in this series of articles: [Android version 10.0]
[the sub section number of this chapter is arranged next to the previous chapter]
3.2. CreateDataSourceFromIDataSource(source) implementation analysis:
Encapsulate the IDataSource (BpBinder) type data object into the proxy class object DataSource type required by the client
// [frameworks/av/media/libstagefright/InterfaceUtils.cpp] sp<DataSource> CreateDataSourceFromIDataSource(const sp<IDataSource> &source) { if (source == nullptr) { return nullptr; } // Two times of encapsulation class processing, i.e. proxy class implementation // See analysis below return new TinyCacheSource(new CallbackDataSource(source)); }
CallbackDataSource class declaration:
// [frameworks/av/media/libmedia/include/CallbackDataSource.h] // A stagefright DataSource that wraps a binder IDataSource. It's a "Callback" // DataSource because it calls back to the IDataSource for data. class CallbackDataSource : public DataSource {}
CallbackDataSource class constructor definition:
That is, the proxy function implemented by the proxy class
// [frameworks/av/media/libmedia/CallbackDataSource.cpp] CallbackDataSource::CallbackDataSource( const sp<IDataSource>& binderDataSource) : mIDataSource(binderDataSource), mIsClosed(false) { // Set up the buffer to read into. mMemory = mIDataSource->getIMemory(); mName = String8::format("CallbackDataSource(%d->%d, %s)", // Current process ID getpid(), // Current caller process ID (such as the process number of the upper APP) IPCThreadState::self()->getCallingPid(), mIDataSource->toString().string()); }
TinyCacheSource class declaration:
// [frameworks/av/media/libmedia/include/CallbackDataSource.h] // A caching DataSource that wraps a CallbackDataSource. For reads smaller // than kCacheSize it will read up to kCacheSize ahead and cache it. // This reduces the number of binder round trips to the IDataSource and has a significant // impact on time taken for filetype sniffing and metadata extraction. class TinyCacheSource : public DataSource {}
TinyCacheSource class constructor definition:
// [frameworks/av/media/libmedia/CallbackDataSource.cpp] TinyCacheSource::TinyCacheSource(const sp<DataSource>& source) : mSource(source), mCachedOffset(0), mCachedSize(0) { mName = String8::format("TinyCacheSource(%s)", mSource->toString().string()); }
3.3. notifyPreparedAndCleanup(UNKNOWN_ERROR) implementation analysis:
Notify the upper application that the prepare process is completed and report the error
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp] void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) { if (err != OK) { // If unsuccessful, clear the work { Mutex::Autolock _l_d(mDisconnectLock); mDataSource.clear(); mHttpSource.clear(); } mCachedSource.clear(); mBitrate = -1; mPrevBufferPercentage = -1; // Pulling the buffer data generation value plus 1 will interrupt the process of obtaining buffer data ++mPollBufferingGeneration; } // Notify prepare to complete and carry the completion status notifyPrepared(err); }
notifyPrepared(err) implementation analysis:
Note that this method is not in genericsource CPP, but in nuplayer Implemented in CPP.
Note: for the implementation principle of ALooper message circulation system, please refer to another chapter: Source code analysis of media communication architecture AHandler/ALooper mechanism in Android native layer
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp] void NuPlayer::Source::notifyPrepared(status_t err) { ALOGV("Source::notifyPrepared %d", err); // dupNotify() copies an idle event notification message object // See analysis below sp<AMessage> notify = dupNotify(); notify->setInt32("what", kWhatPrepared); notify->setInt32("err", err); // Send the message to the message receiver AHandler immediately after carrying two parameters notify->post(); }
dupNotify() copies an event notification message object:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerSource.h] struct NuPlayer::Source : public AHandler { // The provides message is used to notify the player about various // events. explicit Source(const sp<AMessage> ¬ify) : mNotify(notify) { } sp<AMessage> dupNotify() const { return mNotify->dup(); } }
Based on the analysis in the previous chapter, it can be seen that the mNotify notification event message object is actually assigned when creating a GenericSource in the setDataSource process of NuPlayer. That is, the message object definition is implemented as follows:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp] sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
Send this message to NuPlayer's message circulation system for reception, so the processing is as follows:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp] void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatSourceNotify: { onSourceNotify(msg); break; } } }
onSourceNotify(msg) implementation analysis:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp] void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { // Fetch event parameters int32_t what; CHECK(msg->findInt32("what", &what)); switch (what) { case Source::kWhatPrepared: { ALOGV("NuPlayer::onSourceNotify Source::kWhatPrepared source: %p", mSource.get()); // After reset() is called, mSource is set to null, so it is no longer necessary to report this redundant (old) notification if (mSource == NULL) { // This is a stale notification from a source that was // asynchronously preparing when the client called reset(). // We handled the reset, the source is gone. break; } // Take out the error status code int32_t err; CHECK(msg->findInt32("err", &err)); if (err != OK) { // When unsuccessful // At this time, it is necessary to close the possible encryption decoder, for example, when the current client does not call reset // shut down potential secure codecs in case client never calls reset mDeferredActions.push_back( new FlushDecoderAction(FLUSH_CMD_SHUTDOWN /* audio */, FLUSH_CMD_SHUTDOWN /* video */)); // Then perform the above operation processDeferredActions(); // The shutdown process operation will appear in the subsequent reset process analysis chapter. The analysis will not be expanded here } else { // If successful, mark the complete prepare flag mPrepared = true; } sp<NuPlayerDriver> driver = mDriver.promote(); if (driver != NULL) { // notify duration first, so that it's definitely set when // the app received the "prepare complete" callback. int64_t durationUs; // In fact, the method in GenericSource always returns OK, but only the initialization value of -1 can be obtained when prepare fails if (mSource->getDuration(&durationUs) == OK) { // See analysis below driver->notifyDuration(durationUs); } // Notify prepare of completion // See analysis below driver->notifyPrepareCompleted(err); } break; } } }
Driver->notifyduration() implementation analysis:
In fact, it is just the duration value of caching the current file media
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp] void NuPlayerDriver::notifyDuration(int64_t durationUs) { Mutex::Autolock autoLock(mLock); mDurationUs = durationUs; }
Driver->notifypreparecompleted (ERR) implementation analysis:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp] void NuPlayerDriver::notifyPrepareCompleted(status_t err) { ALOGV("notifyPrepareCompleted %d", err); Mutex::Autolock autoLock(mLock); // Check whether the status in the NuPlayerDriver class object is still in the preparing process. // If not, this notification will be ignored because the client has changed the player behavior. The notification has expired. if (mState != STATE_PREPARING) { // We were preparing asynchronously when the client called // reset(), we sent a premature "prepared" notification and // then initiated the reset. This notification is stale. CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE); return; } CHECK_EQ(mState, STATE_PREPARING); // Record the status value of prepare execution mAsyncResult = err; if (err == OK) { // When prepare succeeds, immediately reverse the status to prepare completed // update state before notifying client, so that if client calls back into NuPlayerDriver // in response, NuPlayerDriver has the right state mState = STATE_PREPARED; // If the prepare process is executed asynchronously, the execution notifies the upper APP of the status value of the prepare result // Note: notifyListener_l() the calling process after this method is no longer analyzed. Please refer to the analysis of Listener notification mechanism in the previous chapter. if (mIsAsyncPrepare) { notifyListener_l(MEDIA_PREPARED); } } else { // In case of failure, the previous status, that is, the setDataSource completion status, will be returned, and the upper APP will be notified of the error event mState = STATE_UNPREPARED; if (mIsAsyncPrepare) { notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); } } // Get file metadata information, and return null if prepare fails sp<MetaData> meta = mPlayer->getFileMeta(); int32_t loop; if (meta != NULL && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) { // kKeyAutoLoop the TAG metadata value ID: Ogg files can be marked as automatic looping playback mode. // Therefore, when the android native player plays the ogg file, it may be played in a single loop. Refer to the analysis of this field in the previous chapter mAutoLoop = true; } // Wake up all the threads that may be waiting on the condition variable (for example, when the prepare process is executed synchronously, the calling thread will be blocked and wait for the preparation to complete) mCondition.broadcast(); }
3.4. initFromDataSource() implementation analysis:
Initialize the media extractor from the DataSource data source, that is, load and initialize the specific media demultiplexing module.
It is recommended to take a look at the content analysis in this chapter:
[for the modular implementation principle of android underlying demultiplexing module plug-in, please see: Source code analysis of the modular loading and registration process of demuxers, the demultiplexing module of the Android underlying audio and video playback media extractor]
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp] status_t NuPlayer::GenericSource::initFromDataSource() { sp<IMediaExtractor> extractor; sp<DataSource> dataSource; // Lock the code block to obtain the global DataSource datasource data access object { Mutex::Autolock _l_d(mDisconnectLock); dataSource = mDataSource; } // Must not be empty CHECK(dataSource != NULL); // Note: the reasons for unlocking before locking in the following process: // In fact, during the prepare event processing, the GenericSource has used the automatic lock to lock before calling the onPrepareAsync() method, // Therefore, we must unlock the lock in the place where we do not need to lock, but we must remember that the lock must be added after the method returns, and then let the automatic lock release the final lock. // Unlock mLock.unlock(); // Create factories to create media extractors without locking // Note: it may take time // See section 3.4.1 analysis // This might take long time if data source is not reliable. extractor = MediaExtractorFactory::Create(dataSource, NULL); if (extractor == NULL) { ALOGE("initFromDataSource, cannot create extractor!"); mLock.lock(); return UNKNOWN_ERROR; } // Get file metadata item information, which is actually KV value data item // Note: it can be seen from the implementation principle of the previous MediaExtractor modular loading, // Calling getMetaData() actually calls the method of the specific extractor implementer, // For example, the MMParserExtractor class implemented by the Qualcomm private library demultiplexing module [mm parser] analyzed in the previous chapters, // The getMetaData() method implemented by this class will be called. // About the specific implementation analysis of the specific demultiplexing module, I will have time to analyze it later. sp<MetaData> fileMeta = extractor->getMetaData(); // As above, call the method of the specific extractor implementation class to obtain the number of tracks in the media // [there may be multiple audio streams or multiple video streams in the same file, so the value may be greater than 2] size_t numtracks = extractor->countTracks(); if (numtracks == 0) { ALOGE("initFromDataSource, source has no track!"); mLock.lock(); return UNKNOWN_ERROR; } mLock.lock(); // Cache file media metadata item object mFileMeta = fileMeta; if (mFileMeta != NULL) { // Get the media duration value corresponding to the media duration key field in the metadata information object and cache it int64_t duration; if (mFileMeta->findInt64(kKeyDuration, &duration)) { mDurationUs = duration; } } // Total bit rate bps int32_t totalBitrate = 0; mMimes.clear(); for (size_t i = 0; i < numtracks; ++i) { // As above, call the method of the specific extractor implementation class to obtain the Track object information at the ith position in the media sp<IMediaSource> track = extractor->getTrack(i); if (track == NULL) { // If it is empty, continue to the next track acquisition continue; } // Call this method of the concrete extractor implementation class, // Gets the media metadata information object of the Track object information at the ith position in the media. sp<MetaData> meta = extractor->getTrackMetaData(i); if (meta == NULL) { ALOGE("no metadata for track %zu", i); return UNKNOWN_ERROR; } // Get the file type value of the kkeymime field in the metadata information const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); ALOGV("initFromDataSource track[%zu]: %s", i, mime); // Do the string compare immediately with "mime", // we can't assume "mime" would stay valid after another // extractor operation, some extractors might modify meta // during getTrack() and make it invalid. // mime file type validity must be checked immediately // The reason is that the implementer of the underlying extractor may modify the track information and make it invalid if (!strncasecmp(mime, "audio/", 6)) { // audio file format type // Initialization of audio track information object // Note: the process here will only create the track object once, so if the current media file has multiple audio streams, // Only the first track information will be processed by default, and the first track information may not be decoded by the decoder, // If the decoder fails to decode, it will shut down the decoder after the decoder fails to Decode two frames of data. // If this is a video file, the video stream will be played without sound. if (mAudioTrack.mSource == NULL) { // Record track index and track object mAudioTrack.mIndex = i; mAudioTrack.mSource = track; // Create another packet (data source processing) object, which is mainly used to provide the demultiplexed audio and video data to the audio and video decoder // And caching the read data source track audio and video stream data and the demultiplexed data after demultiplexing. // mAudioTrack. The msource->getformat() implementation is the same as the above analysis, // Call this method of the Track implementation class of the specific extractor implementation class to obtain the Track object information at the ith position in the media. // For AnotherPacketSource class declaration and constructor implementation, see section 3.4.2 for analysis mAudioTrack.mPackets = new AnotherPacketSource(mAudioTrack.mSource->getFormat()); // Check whether the file format is "audio/vorbis", i.e. free music format music data if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { mAudioIsVorbis = true; } else { mAudioIsVorbis = false; } // Add the file format mime information of the current track to the list object mMimes.add(String8(mime)); } } else if (!strncasecmp(mime, "video/", 6)) { // Video file format // As above, the video file will only process the first track video stream information if (mVideoTrack.mSource == NULL) { // Ditto analysis mVideoTrack.mIndex = i; mVideoTrack.mSource = track; mVideoTrack.mPackets = new AnotherPacketSource(mVideoTrack.mSource->getFormat()); // Put the video file format mime information in the item item at the beginning of the list // video always at the beginning mMimes.insertAt(String8(mime), 0); } } // Add the current track information to the track list mSources.push(track); // Get the media duration in the metadata of the current track // Note: note that this value is also obtained once. The metadata above is the file duration of the entire file metadata record, // This value is the duration of data streaming media in the current track. Theoretically, it will only be less than but not greater than the duration of the entire file. int64_t durationUs; if (meta->findInt64(kKeyDuration, &durationUs)) { // The retrieved value is updated only if it is greater than the currently cached value if (durationUs > mDurationUs) { mDurationUs = durationUs; } } // Get the bitstream value of the current track int32_t bitrate; if (totalBitrate >= 0 && meta->findInt32(kKeyBitRate, &bitrate)) { // Then it is added to the total code stream values of other track data streams that have been calculated previously to obtain the total code stream values of all track data streams totalBitrate += bitrate; } else { // Note this processing logic: if it fails to obtain the bitstream value of a track data stream, directly reset the total bitstream value to -1 totalBitrate = -1; } } ALOGV("initFromDataSource mSources.size(): %zu mIsSecure: %d mime[0]: %s", mSources.size(), mIsSecure, (mMimes.isEmpty() ? "NONE" : mMimes[0].string())); // Determine the total number of data flow track s if (mSources.size() == 0) { ALOGE("b/23705695"); return UNKNOWN_ERROR; } // Check DRM information, i.e. copyright version management information, which is not analyzed at present // Modular DRM: The return value doesn't affect source initialization. (void)checkDrmInfo(); // The total code stream of all available track data streams. Its value includes tracks that may not be processed. For example, when multiple audio streams are processed, only the first one will be processed. mBitrate = totalBitrate; return OK; }
3.4.1 MediaExtractorFactory::Create(dataSource, NULL) implementation analysis:
Creating a factory to create a media extractor does not require locking and may take time.
// [frameworks/av/media/libstagefright/MediaExtractorFactory.cpp] // static sp<IMediaExtractor> MediaExtractorFactory::Create( const sp<DataSource> &source, const char *mime) { ALOGV("MediaExtractorFactory::Create %s", mime); // mime incoming value is null if (!property_get_bool("media.stagefright.extractremote", true)) { // local extractor ALOGW("creating media extractor in calling process"); // Create a local (threaded) media Extractor extractor. The process of creating a remote Extractor analyzed below will analyze this implementation. return CreateFromService(source, mime); } else { // Create a remote (cross process) media Extractor extractor // remote extractor ALOGV("get service manager"); // According to the analysis in the previous chapter, get the BpMediaExtractorService class object on the Bp proxy side of IMediaExtractorService sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor")); if (binder != 0) { // Using interface_cast is converted to the corresponding service to obtain the Bp proxy object, // That is, the BpMediaExtractorService implementation is obtained. Here, its parent class type is converted and assigned to the mediaExService object sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder)); // The Binder mechanism calls this method across processes to obtain IMediaExtractor objects // Note: according to the Binder implementation mechanism, the returned object must be a BpBinder type object on the Bp proxy side, that is, BpMediaExtractor, // It is defined and implemented in [frameworks/av/media/libmedia/IMediaExtractor.cpp]. // See analysis below sp<IMediaExtractor> ex = mediaExService->makeExtractor( CreateIDataSourceFromDataSource(source), mime); // Returns the BpMediaExtractor proxy object (null if failed) return ex; } else { ALOGE("extractor service not running"); return NULL; } } return NULL; }
Mediaexservice->makeextractor (createidatasourceformdatasource (source), mime) implements analysis:
The CreateIDataSourceFromDataSource(source) implementation mainly refers to the encapsulation and conversion of data objects into Binder objects. Its implementation analysis has been analyzed in section 3.1.2 of the previous chapter. Its main function is to create IDataSource by encapsulating DataSource objects.
For the analysis of makeExtractor() implementation, the Binder mechanism processing process will not be described here (please refer to another chapter). Go directly to the BnMediaExtractorService subclass MediaExtractorService implementer on the Bn implementation side to analyze this method, as follows
// [frameworks/av/services/mediaextractor/MediaExtractorService.cpp] sp<IMediaExtractor> MediaExtractorService::makeExtractor( const sp<IDataSource> &remoteSource, const char *mime) { ALOGV("@@@ MediaExtractorService::makeExtractor for %s", mime); // Here, the reverse operation is performed when the parameter is passed in, that is, the DataSource object is created by encapsulating the IDataSource (BpBinder) object // See Section 3.2 above for analysis sp<DataSource> localSource = CreateDataSourceFromIDataSource(remoteSource); // Create factory create media extractor IMediaExtractor object from service // See analysis below sp<IMediaExtractor> extractor = MediaExtractorFactory::CreateFromService(localSource, mime); ALOGV("extractor service created %p (%s)", extractor.get(), extractor == nullptr ? "" : extractor->name()); if (extractor != nullptr) { // If the creation is successful, register it // See analysis below registerMediaExtractor(extractor, localSource, mime); return extractor; } return nullptr; }
MediaExtractorFactory::CreateFromService(localSource, mime) implementation analysis:
Create factory create media extractor IMediaExtractor object from service
// [frameworks/av/media/libstagefright/MediaExtractorFactory.cpp] sp<IMediaExtractor> MediaExtractorFactory::CreateFromService( const sp<DataSource> &source, const char *mime) { ALOGV("MediaExtractorFactory::CreateFromService %s", mime); // Involving the digital rights management function processing [i.e. decryptor], no analysis // initialize source decryption if needed source->DrmInitialization(nullptr /* mime */); void *meta = nullptr; void *creator = NULL; // Method pointer to release media metadata FreeMetaFunc freeMeta = nullptr; // The score of the media extractor. The highest score will be specified as the demultiplexing module that should be used. The score range: 0 ~ 1 float confidence; sp<ExtractorPlugin> plugin; uint32_t creatorVersion = 0; // Returns the creation method pointer of the highest scorer of a media demultiplexer extractor // See subsection 3.4.1.1 for analysis creator = sniff(source, &confidence, &meta, &freeMeta, plugin, &creatorVersion); if (!creator) { ALOGV("FAILED to autodetect media content."); return NULL; } MediaExtractor *ex = nullptr; if (creatorVersion == EXTRACTORDEF_VERSION_NDK_V1 || creatorVersion == EXTRACTORDEF_VERSION_NDK_V2) { // Cast to the createrfunc method pointer type, then execute to create the extractor method pointer, and pass in the corresponding parameters, // After the creation is successful, a pointer to the cmedianextractor object is returned. // Cmedianextractor and creationfunc for the method pointer type, please refer to the above recommended chapters. // According to the analysis in the previous chapters, the source object is actually a FileSource object, so its wrap() method is called. See the analysis in section 3.4.1.2. CMediaExtractor *ret = ((CreatorFunc)creator)(source->wrap(), meta); if (meta != nullptr && freeMeta != nullptr) { // Request to release the relevant memory after the creation is successful freeMeta(meta); } // If the creation is successful, encapsulate the cmedianextractor object into the proxy object again, and then return // See section 3.4.1.3 for the implementation of MediaExtractorCUnwrapper class declaration. ex = ret != nullptr ? new MediaExtractorCUnwrapper(ret) : nullptr; } ALOGV("Created an extractor '%s' with confidence %.2f", ex != nullptr ? ex->name() : "<null>", confidence); // Because the MediaExtractorCUnwrapper object does not implement the Binder mechanism function, // Therefore, it is encapsulated again as a Binder type object related to IMediaExtractor, which can interact across processes. // See analysis in subsection 3.4.1.4 return CreateIMediaExtractorFromMediaExtractor(ex, source, plugin); }
registerMediaExtractor(extractor, localSource, mime) implementation analysis:
In fact, the information about the currently created media extractor object is stored in the ExtractorInstance object of the media extractor instance, and then cached in the extractor list global variable sExtractors. All mediaextractors requested to be created by the process will be recorded here.
// [frameworks/av/media/libmedia/IMediaExtractor.cpp] void registerMediaExtractor( const sp<IMediaExtractor> &extractor, const sp<DataSource> &source, const char *mime) { // Media extractor instance ExtractorInstance object, caching extractor plug-in information ExtractorInstance ex; ex.mime = mime == NULL ? "NULL" : mime; ex.name = extractor->name(); ex.sourceDescription = source->toString(); // Caller process ID (such as the process number of the upper APP) ex.owner = IPCThreadState::self()->getCallingPid(); ex.extractor = extractor; ex.when = time(NULL); // Lock code block processing { Mutex::Autolock lock(sExtractorsLock); // If the extractor size of the current record is greater than 10, the capacity and size of 10 are reset, and 10 empty elements are allocated if (sExtractors.size() > 10) { sExtractors.resize(10); } // Add at the beginning of the list sExtractors.push_front(ex); } }
3.4.1.1. Implementation of sniff() method:
// [frameworks/av/media/libstagefright/MediaExtractorFactory.cpp] // static void *MediaExtractorFactory::sniff( const sp<DataSource> &source, float *confidence, void **meta, FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin, uint32_t *creatorVersion) { *confidence = 0.0f; *meta = nullptr; // Lock the code block to obtain the global variable of the demultiplexing module plug-in list loaded during the initialization of the MediaExtractorService service process // For the modular implementation and registration of demultiplexing module plug-ins, please refer to another article recommended above std::shared_ptr<std::list<sp<ExtractorPlugin>>> plugins; { Mutex::Autolock autoLock(gPluginMutex); if (!gPluginsRegistered) { // uninitialized return NULL; } plugins = gPlugins; } // Loop processing to get the best extractor plug-in creation method pointer void *bestCreator = NULL; for (auto it = plugins->begin(); it != plugins->end(); ++it) { ALOGV("sniffing %s", (*it)->def.extractor_name); float newConfidence; void *newMeta = nullptr; FreeMetaFunc newFreeMeta = nullptr; // Get the configuration information of the current extractor plug-in, execute the implementation of different versions according to its version number, and call the snipf method pointer implemented when its plug-in is registered, // This pointer will return a pointer to the MediaExtractor implementation method used to create the extractor plug-in to create it. // For this part, please refer to the chapter of loading principle of demultiplexing module recommended above void *curCreator = NULL; if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V1) { curCreator = (void*) (*it)->def.u.v2.sniff( source->wrap(), &newConfidence, &newMeta, &newFreeMeta); } else if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) { curCreator = (void*) (*it)->def.u.v3.sniff( source->wrap(), &newConfidence, &newMeta, &newFreeMeta); } if (curCreator) { // When matching to an extractor whose current file format can be de multiplexed, compare the extractor score with the previous extractor. If the score is high, the low score will be discarded if (newConfidence > *confidence) { *confidence = newConfidence; if (*meta != nullptr && *freeMeta != nullptr) { // Free previous low separator metadata memory (*freeMeta)(*meta); } // Record new extractor information *meta = newMeta; *freeMeta = newFreeMeta; plugin = *it; bestCreator = curCreator; *creatorVersion = (*it)->def.def_version; } else { // Clear work if matching fails if (newMeta != nullptr && newFreeMeta != nullptr) { newFreeMeta(newMeta); } } } } // Finally, a creation method pointer of the highest score of the media demultiplexer extractor will be returned return bestCreator; }
3.4.1.2. Source->wrap() implementation analysis:
It can be seen from the analysis in the previous chapters that the source object is actually a FileSource object, and the wrap() method is a method implemented by its parent class DataSource. Its implementation function is the same as its name. It is an encapsulated proxy implementation, as follows
// [frameworks/av/media/libstagefright/include/media/stagefright/DataSource.h] CDataSource *wrap() { if (mWrapper) { return mWrapper; } mWrapper = new CDataSource(); mWrapper->handle = this; // The following is the assignment method pointer. Note that the following implementations are defined and implemented by anonymous methods, and their execution is ultimately the implementation corresponding to the DataSource itself // Read file data of specified size as required mWrapper->readAt = [](void *handle, off64_t offset, void *data, size_t size) -> ssize_t { return ((DataSource*)handle)->readAt(offset, data, size); }; // Get file data size mWrapper->getSize = [](void *handle, off64_t *size) -> status_t { return ((DataSource*)handle)->getSize(size); }; // Type of current data source class object mWrapper->flags = [](void *handle) -> uint32_t { return ((DataSource*)handle)->flags(); }; // Obtain the Uri of the current data source, i.e. URL path, etc mWrapper->getUri = [](void *handle, char *uriString, size_t bufferSize) -> bool { return ((DataSource*)handle)->getUri(uriString, bufferSize); }; // Return encapsulated proxy implementation class object return mWrapper; }
3.4.1.3 MediaExtractorCUnwrapper encapsulates proxy class declaration and constructor definition:
Encapsulate the cmedianextractor object into a proxy object.
MediaExtractorCUnwrapper encapsulates the proxy class declaration
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaExtractor.h] class MediaExtractorCUnwrapper : public MediaExtractor {} // [frameworks/av/media/libstagefright/include/media/stagefright/MediaExtractor.h] class MediaExtractor // : public ExtractorAllocTracker {}
MediaExtractorCUnwrapper encapsulates proxy class constructor definitions
// [frameworks/av/media/libstagefright/MediaExtractor.cpp] MediaExtractorCUnwrapper::MediaExtractorCUnwrapper(CMediaExtractor *plugin) { this->plugin = plugin; }
3.4.1.4 CreateIMediaExtractorFromMediaExtractor(ex, source, plugin) implementation analysis:
Since the MediaExtractorCUnwrapper object does not implement the Binder mechanism function, it is encapsulated into a Binder type object related to IMediaExtractor that can interact across processes.
// [frameworks/av/media/libstagefright/InterfaceUtils.cpp] sp<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor( MediaExtractor *extractor, const sp<DataSource> &source, const sp<RefBase> &plugin) { if (extractor == nullptr) { return nullptr; } // Encapsulation is converted to IMediaExtractor type object, which can be used for cross process communication return RemoteMediaExtractor::wrap(extractor, source, plugin); }
RemoteMediaExtractor::wrap(extractor, source, plugin) implementation analysis:
In fact, it can be seen from this that it needs to be encapsulated into an operation class that can be called across processes through the Binder mechanism, that is, the IMediaExtractor subclass RemoteMediaExtractor, which implements the Binder interface and becomes a subclass of BnMediaExtractor on the Bn implementation end, so it can be returned to other process callers for cross process use.
// [frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp] // static sp<IMediaExtractor> RemoteMediaExtractor::wrap( MediaExtractor *extractor, const sp<DataSource> &source, const sp<RefBase> &plugin) { if (extractor == nullptr) { return nullptr; } // Create encapsulated proxy class object return new RemoteMediaExtractor(extractor, source, plugin); }
RemoteMediaExtractor encapsulates the proxy class declaration:
// [frameworks/av/media/libmedia/include/media/IMediaExtractor.h] class IMediaExtractor : public IInterface {} // [frameworks/av/media/libmedia/include/media/IMediaExtractor.h] class BnMediaExtractor: public BnInterface<IMediaExtractor> {} // [frameworks/av/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h] // IMediaExtractor wrapper to the MediaExtractor. class RemoteMediaExtractor : public BnMediaExtractor {}
RemoteMediaExtractor encapsulates the proxy class constructor implementation:
// [frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp] RemoteMediaExtractor::RemoteMediaExtractor( MediaExtractor *extractor, const sp<DataSource> &source, const sp<RefBase> &plugin) // Cache parameters :mExtractor(extractor), mSource(source), mExtractorPlugin(plugin) { mAnalyticsItem = nullptr; // The debug media data switch macro definition is enabled when it is set to 1 by default. It is mainly used to process the media analysis statistics item (attribute) information // In fact, it can be seen from the English annotation that the value obtained is for the java layer // frameworks/base/media/java/android/media/MediaExtractor.java class to get this information. if (MEDIA_LOG) { mAnalyticsItem = MediaAnalyticsItem::create(kKeyExtractor); // Get the caller process user id // we're in the extractor service, we want to attribute to the app // that invoked us. int uid = IPCThreadState::self()->getCallingUid(); mAnalyticsItem->setUid(uid); // track the container format (mpeg, aac, wvm, etc) size_t ntracks = extractor->countTracks(); mAnalyticsItem->setCString(kExtractorFormat, extractor->name()); // tracks (size_t) mAnalyticsItem->setInt32(kExtractorTracks, ntracks); // Get container specific media metadata information object from media extractor // metadata MetaDataBase pMetaData; if (extractor->getMetaData(pMetaData) == OK) { String8 xx = pMetaData.toString(); // 'titl' -- but this verges into PII // 'mime' // Get the value corresponding to the MIME field, that is, the file type, and record it in the statistical analysis data item const char *mime = nullptr; if (pMetaData.findCString(kKeyMIMEType, &mime)) { mAnalyticsItem->setCString(kExtractorMime, mime); } // You can also add media metadata information items that we are interested in // what else is interesting and not already available? } } }
3.4.2 AnotherPacketSource class declaration and constructor implementation
AnotherPacketSource class declaration:
// [frameworks/av/media/libstagefrigh/mpeg2ts/AnotherPacketSource.h] struct AnotherPacketSource : public MediaSource {} // [frameworks/av/media/libstagefrigh/include/media/stagefright/MediaSource.h] struct MediaSource : public virtual RefBase {}
AnotherPacketSource class constructor implementation:
Initialization value operation
// [frameworks/av/media/libstagefrigh/mpeg2ts/AnotherPacketSource.cpp] AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta) : mIsAudio(false), mIsVideo(false), mEnabled(true), mFormat(NULL), // Media timestamp of the last (most recent) media data queued // That is, the media timestamp of the media data read by the cache [non system timestamp] mLastQueuedTimeUs(0), // Estimated buffer data duration mEstimatedBufferDurationUs(-1), // Mark EOS result status, and OK indicates non EOS mEOSResult(OK), // The message object (AMessage) of the media metadata information of the latest media data added to the queue (read demultiplexed data) mLatestEnqueuedMeta(NULL), // The message object (AMessage) of the media metadata information of the latest media data out of the queue (the decoder obtains the de multiplexed data) mLatestDequeuedMeta(NULL) { // Set file format metadata // See analysis below setFormat(meta); // Discontinuous segment: discontinuous segment data is a continuous access unit between discontinuous tags. There should always be at least one discontinuous data segment. // For the discontinuity segment class declaration, see the following analysis. // Here, at least one discontinuous data segment object is stored in the list. mDiscontinuitySegments.push_back(DiscontinuitySegment()); }
setFormat(meta) implementation analysis:
// [frameworks/av/media/libstagefrigh/mpeg2ts/AnotherPacketSource.cpp] void AnotherPacketSource::setFormat(const sp<MetaData> &meta) { // Media metadata information of track information can only be set once, unless reset is called if (mFormat != NULL) { // Only allowed to be set once. Requires explicit clear to reset. return; } mIsAudio = false; mIsVideo = false; if (meta == NULL) { return; } // Get file format mime type value mFormat = meta; const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); // Process tag current file type if (!strncasecmp("audio/", mime, 6)) { // audio file format type mIsAudio = true; } else if (!strncasecmp("video/", mime, 6)) { // video file type mIsVideo = true; } else { // text file type or application file type should be the network request mode CHECK(!strncasecmp("text/", mime, 5) || !strncasecmp("application/", mime, 12)); } }
Discontinuity segment class declaration:
Inner class of AnotherPacketSource
// [frameworks/av/media/libstagefrigh/mpeg2ts/AnotherPacketSource.h] struct AnotherPacketSource : public MediaSource { private: struct DiscontinuitySegment { int64_t mMaxDequeTimeUs, mMaxEnqueTimeUs; DiscontinuitySegment() : mMaxDequeTimeUs(-1), mMaxEnqueTimeUs(-1) { }; void clear() { mMaxDequeTimeUs = mMaxEnqueTimeUs = -1; } }; }