Android编解码系列(2)—MediaExtractor

一、概述

MediaExtractor的主要功能就是解封裝。它可以从各种媒体文件(如MP4、MP3、AAC、AV等)中提取出音频和视频的原始数据流,这个过程就是所谓的"解封装"。这些原始数据流可以直接被MedliaCodec进行编解码处理。 解封装的过程就是把包含在媒体文件中的音频和视频数据分离出来,以便于后续的处理和播放。这个过程并不涉及到编解码,所以通常来说,解封装的过程比编解码的过程要快很多。

MediaExtractor这个类主要负责打开媒体文件,井识别媒体文件的格式。它会根据媒体文件的格式,创建对应的Extractor (如MP4Extractor、 MPSExtractor等),井通过这个Extractor来读取媒体文件中的音频和视频数据。

MediaExtractor的解封装过程大致如下:

  1. 打开媒体文件:MediaExtractor首先通过openFd或setDataSource方法打开媒体文件。
  2. 识別媒体文件格式:MediaExtractor会读取媒体文件的头部信息,识别媒体文件的格式。
  3. 创建Extractor:根据媒体文件的格式,MediaExtractor会创建对应的Extractor 。

刚刚我们提到的setDataSource会调用nativeSetDataSource方法,我们可以发现他对应了jni层的frameworks/base/media/jni/android_media_MediaExtractor.cpp 里的android_media_MediaExtractor_setDataSource,参考源码我们在上述流程里的"2.识别媒体文件格式"和"3.创建Extractor"都是在这里执行的。

MediaExtractor内核心Api为:

  • setDataSource(String path):设置数据源,可以是本地文件或者网络文件
  • getTrackCount():得到源文件的轨道数
  • getTrackFormat(int index): 获取指定(index)的轨道格式
  • getSampleTime():返回当前的时间戳
  • readSampleData(ByteBuffer byteBuf, int offset): 把指定轨道中的数据按偏移量读取到ByteBuffer中;
  • advance():读取下一顿数据
  • release():读取结束后释放资源

核心实现前置描述: MediaExtractor.cpp的核心实现为NuMediaExtractor NuMediaExtractor的核心实现为sp<IMedlaExtractor>mImpL,也就是IMediaExtractor

二、源码解析

1、初始化(实例化)

java层的MediaExtractor#mNativeContext是什么时候被创建的: 在创建MediaExtractor对象的时候,会调用native_setup(),其对应的jn层的方法:android_media_MediaExtractor_native_setup (jni层文件指路:frameworks/base/media/jni/android_media_MediaExtractor.cpp),这个函数内部实现为:

cpp 复制代码
static void android_media_MediaExtractor_native_setup(
        JNIEnv *env, jobject thiz) {
    sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
    setMediaExtractor(env,thiz, extractor);
}

其实就创建了JMediaExtractor对象并存到了MediaExtractor#mNativeContext里面了,他也是jni层进行解封装的关键类。 而JMediaExtractor的实例化代码如下:

cpp 复制代码
JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
    : mClass(NULL),
      mObject(NULL) {
    jclass clazz = env->GetObjectClass(thiz);
    CHECK(clazz != NULL);

    mClass = (jclass)env->NewGlobalRef(clazz);
    mObject = env->NewWeakGlobalRef(thiz);

    mImpl = new NuMediaExtractor(NuMediaExtractor::EntryPoint::SDK);
}

2、setDataSource设置视频源

接下来我们来解析下jni层的android_media_MediaExtractor_setDataSource的实现,其源码如下:

cpp 复制代码
static void android_media_MediaExtractor_setDataSource(
        JNIEnv *env, jobject thiz,
        jobject httpServiceBinderObj,
        jstring pathObj,
        jobjectArray keysArray,
        jobjectArray valuesArray) {
    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);

    if (extractor == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }

    if (pathObj == NULL) {
        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        return;
    }

    KeyedVector<String8, String8> headers;
    if (!ConvertKeyValueArraysToKeyedVector(
                env, keysArray, valuesArray, &headers)) {
        return;
    }

    const char *path = env->GetStringUTFChars(pathObj, NULL);

    if (path == NULL) {
        return;
    }

    sp<IMediaHTTPService> httpService;
    if (httpServiceBinderObj != NULL) {
        sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
        httpService = interface_cast<IMediaHTTPService>(binder);
    }

    status_t err = extractor->setDataSource(httpService, path, &headers);

    env->ReleaseStringUTFChars(pathObj, path);
    path = NULL;

    if (err != OK) {
        jniThrowException(
                env,
                "java/io/IOException",
                "Failed to instantiate extractor.");
        return;
    }
}
  1. 首先会调用sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz)取出java层的 MediaExtractor#NativeContext成员变量,这个成员变量也在我们初始化的时候完成了创建。
  2. 紧接着会进行安全检查:检查extractor非空;检查视频源路经非空,如果出现问题会抛出异常。
  3. 在之后会把java层设置的参数,转换存到KeyedVector<String8, String8> headers里面。
  4. 之后则取出数据源的字符串。
  5. 然后会把Java对象httpServiceBinderObj转换为C++层的IMediaHTTPService接口。
  6. 然后调用extractor->setDataSource(httpService, path, &headers)设置数据源 在extractor->setDataSource设置数据源这一步,我们发现源码来到了android_media_MediaExtractor.cpp#JMediaExtractor::setDataSource里,其执行的函数为mImpl->setDataSource(httpService, path, headers) 这里的mlmpl实例其实在创建JMediaExtractor里就已经创建,此时代码走到了frameworks/av/media/libstagefright/NuMediaExtractor.cpp#NuMediaExtractor::setDataSource,其实现为
cpp 复制代码
status_t NuMediaExtractor::setDataSource(
        const sp<MediaHTTPService> &httpService,
        const char *path,
        const KeyedVector<String8, String8> *headers) {
    Mutex::Autolock autoLock(mLock);

    if (mImpl != NULL || path == NULL) {
        return -EINVAL;
    }

    sp<DataSource> dataSource =
        DataSourceFactory::getInstance()->CreateFromURI(httpService, path, headers);

    if (dataSource == NULL) {
        return -ENOENT;
    }

    // Initialize MediaExtractor using the data source
    return initMediaExtractor(dataSource);
}

我们可以发现,其核心实现是:

  • 创建DataSource对象:sp<DataSource> dataSource = DataSourceFactory::getInstance()->CreateFromURI(httpService, path, headers);
  • 初始化 NuMediaExtractor::initMediaExtractor(dataSource)

2.1创建DataSource对象

这里是使用工厂DataSourceFactory创建DataSouce,其源码如下:

cpp 复制代码
sp<DataSource> DataSourceFactory::CreateFromURI(
        const sp<MediaHTTPService> &httpService,
        const char *uri,
        const KeyedVector<String8, String8> *headers,
        String8 *contentType,
        HTTPBase *httpSource) {
    if (contentType != NULL) {
        *contentType = "";
    }

    sp<DataSource> source;
    if (!strncasecmp("file://", uri, 7)) {
        source = CreateFileSource(uri + 7);
    } else if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) {
        if (httpService == NULL) {
            ALOGE("Invalid http service!");
            return NULL;
        }

        sp<HTTPBase> mediaHTTP = httpSource;
        if (mediaHTTP == NULL) {
            mediaHTTP = static_cast<HTTPBase *>(CreateMediaHTTP(httpService).get());
            if (mediaHTTP == NULL) {
                return NULL;
            }
        }

        String8 cacheConfig;
        bool disconnectAtHighwatermark = false;
        KeyedVector<String8, String8> nonCacheSpecificHeaders;
        if (headers != NULL) {
            nonCacheSpecificHeaders = *headers;
            NuCachedSource2::RemoveCacheSpecificHeaders(
                    &nonCacheSpecificHeaders,
                    &cacheConfig,
                    &disconnectAtHighwatermark);
        }

        if (mediaHTTP->connect(uri, &nonCacheSpecificHeaders) != OK) {
            ALOGE("Failed to connect http source!");
            return NULL;
        }

        if (contentType != NULL) {
            *contentType = mediaHTTP->getMIMEType();
        }

        source = NuCachedSource2::Create(
                mediaHTTP,
                cacheConfig.empty() ? NULL : cacheConfig.c_str(),
                disconnectAtHighwatermark);
    } else if (!strncasecmp("data:", uri, 5)) {
        source = DataURISource::Create(uri);
    } else {
        // Assume it's a filename.
        source = CreateFileSource(uri);
    }

    if (source == NULL || source->initCheck() != OK) {
        return NULL;
    }

    return source;
}

我们会发现其核心逻辑是根据文件url格式创建对应的DataSource。

2.2 初始化MediaExtrator

在2.1创建出DataSource对象以后,会调用initMediaExtractor(dataSource)初始化MediaExtractor,其源码为:

cpp 复制代码
status_t NuMediaExtractor::initMediaExtractor(const sp<DataSource>& dataSource) {
    status_t err = OK;

    mImpl = MediaExtractorFactory::Create(dataSource);
    if (mImpl == NULL) {
        ALOGE("%s: failed to create MediaExtractor", __FUNCTION__);
        return ERROR_UNSUPPORTED;
    }

    setEntryPointToRemoteMediaExtractor();

    if (!mCasToken.empty()) {
        err = mImpl->setMediaCas(mCasToken);
        if (err != OK) {
            ALOGE("%s: failed to setMediaCas (%d)", __FUNCTION__, err);
            return err;
        }
    }

    // Get the name of the implementation.
    mName = mImpl->name();

    // Update the duration and bitrate
    err = updateDurationAndBitrate();
    if (err == OK) {
        mDataSource = dataSource;
    }

    return OK;
}

对照上述代码,可以发现其流程为:创建新的MediaExtractor对象

首先是使用MediaExtractorFactory::Create(dataSource)创建新的对象sp<IMediaExtractor> mImpl

cpp 复制代码
sp<IMediaExtractor> MediaExtractorFactory::Create(
        const sp<DataSource> &source, const char *mime) {
    ALOGV("MediaExtractorFactory::Create %s", mime);

    if (!property_get_bool("media.stagefright.extractremote", true)) {
        // local extractor
        ALOGW("creating media extractor in calling process");
        return CreateFromService(source, mime);
    } else {
        // remote extractor
        ALOGV("get service manager");
        sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));

        if (binder != 0) {
            sp<IMediaExtractorService> mediaExService(
                    interface_cast<IMediaExtractorService>(binder));
            sp<IMediaExtractor> ex;
            mediaExService->makeExtractor(
                    CreateIDataSourceFromDataSource(source),
                    mime ? std::optional<std::string>(mime) : std::nullopt,
                    &ex);
            return ex;
        } else {
            ALOGE("extractor service not running");
            return NULL;
        }
    }
    return NULL;
}

对照上述源码我们会发现,他会先检查是否有这个属性media.stagefright.extractremote,如果有这个属性则执行CreateFromService(source, mime)创建lMediaExtractor对象。 media.stagefright.extractremote这个属性就是用来控制媒体提取器是否运行在远程服务中的,如果这个属性为true,那么媒体提取器就会运行在单独的进程中;如果这个属性为talse,那么媒体提取器就会运行在主进程中。 在Android系统中,有一些操作是需要在单独的进程中进行的,以提高安全性和稳定性。例如,解析和提取媒体文件就是这样的操作。如果在解析媒体文件时发生了错误,例如文件格式不正确或者文件包含恶意代码,那么可能会导致媒体提取器崩渍。如果媒体提取播运行在主进程中,那么这种崩溃就可能导致整个应用崩溃。但如果媒体提取器运行在单独的进程中,那么就只有这个进程会崩溃,应用的其他部分可以继续运行。

3、CreatefromService创建单独进程的媒体解码器

接下来我们解析下CreateFromService(source, mime)这个函数:

cpp 复制代码
sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
        const sp<DataSource> &source, const char *mime) {

    ALOGV("MediaExtractorFactory::CreateFromService %s", mime);

    void *meta = nullptr;
    void *creator = NULL;
    FreeMetaFunc freeMeta = nullptr;
    float confidence;
    sp<ExtractorPlugin> plugin;
    uint32_t creatorVersion = 0;
    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) {
        CMediaExtractor *ret = ((CreatorFunc)creator)(source->wrap(), meta);
        if (meta != nullptr && freeMeta != nullptr) {
            freeMeta(meta);
        }
        ex = ret != nullptr ? new MediaExtractorCUnwrapper(ret) : nullptr;
    }

    ALOGV("Created an extractor '%s' with confidence %.2f",
         ex != nullptr ? ex->name() : "<null>", confidence);

    return CreateIMediaExtractorFromMediaExtractor(ex, source, plugin);
}

对照上述代码我们会发现:

  1. 首先会执行sniff()函数来检测媒体数据的类型:
    • sniff()函数会读取媒体数据的一部分,然后根据这些数据的内容和格式来列断媒体的类型。sniff函数的输出参数包括confidence值,一个meta数据,一个freeMeta通数,一个plugin插件和一个creatorVersion版本号。
    • confidence值表示sniff()函数对检测结果的信心程度。如果confidence值很高,那么就说明sniff()函数很确定媒体的类型。meta数据包含了媒体的一些元信息,例如媒体的时长、分辨率等。freeMeta函数用于释放meta数据。plugin插件用于处理特定类型的媒体数据。creatorVersion版本号表示创建媒体提取器的版本。
  2. 然后,函数根据creatorVersion版本号来创建媒体提取器:
    1. 如果版本号是EXTRACTORDEF_VERSION_NDK_V1或者EXTRACTORDEF_VERSION_NDK_V2,那么就使用creator酒数来创建 媒体提取器。creator函数的输入参数包括一个DataSource对象和一个meta数据,
    2. 如果creator函数创建失败,则直接调用new MediaExtractorCUnwrapper(ret) 创建媒体提取器对象
  3. 最后执行CreateIMediaExtractorFromMediaExtractor(ex, source, plugin),主要作用是将一个MediaExtractor对象转换为一个IMediaExtractor 对象

对照上述流程我们会发现有几个关键点:sniff、creator、MediaExtractorCUnwrapper和CreateIMediaExtractorFromMediaExtractor我们一个个拆解

3.1 sniff函数

cpp 复制代码
void *MediaExtractorFactory::sniff(
        const sp<DataSource> &source, float *confidence, void **meta,
        FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin, uint32_t *creatorVersion) {
    *confidence = 0.0f;
    *meta = nullptr;

    std::shared_ptr<std::list<sp<ExtractorPlugin>>> plugins;
    {
        Mutex::Autolock autoLock(gPluginMutex);
        if (!gPluginsRegistered) {
            return NULL;
        }
        plugins = gPlugins;
    }

    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;

        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) {
            if (newConfidence > *confidence) {
                *confidence = newConfidence;
                if (*meta != nullptr && *freeMeta != nullptr) {
                    (*freeMeta)(*meta);
                }
                *meta = newMeta;
                *freeMeta = newFreeMeta;
                plugin = *it;
                bestCreator = curCreator;
                *creatorVersion = (*it)->def.def_version;
            } else {
                if (newMeta != nullptr && newFreeMeta != nullptr) {
                    newFreeMeta(newMeta);
                }
            }
        }
    }

    return bestCreator;
}

对照上述源码,我们可以发现出现了std::list<sp<ExtractorPlugin>>这个关键对象,ExtractorPlugin是一个接口,它定义了一些用于处理特定类型的媒体数据的方法。例如,对于MP3格式的音频数据,就需要一个能够解析MP3格式的ExtractorPluginsniff函数会遍历list<sp<ExtractorPlugin>>对象,找到对应媒体文件的媒体提取器:

  1. 如果是EXTRACTORDEF_VERSION_NDK_V1版本,则执行(*it)->def.u.v2.sniff
  2. 如果是EXTRACTORDEF_VERSION_NDK_V2,则执行(*it)->def.u.v3.sniff
  3. 然后按最大的confidence,返回媒体提取器bestCreator

那么是list<sp<ExtractorPlugin>在什么时候创建的呢,答案是frameworks/av/media/libstagefright/MediaExtractorFactory.cpp#MediaExtractorFactory::LoadExtractors(),其源码如下:

cpp 复制代码
void MediaExtractorFactory::LoadExtractors() {
    Mutex::Autolock autoLock(gPluginMutex);

    if (gPluginsRegistered) {
        return;
    }

    gIgnoreVersion = property_get_bool("debug.extractor.ignore_version", false);

    std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());

    android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
    if (mediaNs != NULL) {
        const android_dlextinfo dlextinfo = {
            .flags = ANDROID_DLEXT_USE_NAMESPACE,
            .library_namespace = mediaNs,
        };
        RegisterExtractors("/apex/com.android.media/lib"
#ifdef __LP64__
                "64"
#endif
                "/extractors", &dlextinfo, *newList);

    } else {
        ALOGE("couldn't find media namespace.");
    }

    RegisterExtractors("/system/lib"
#ifdef __LP64__
            "64"
#endif
            "/extractors", NULL, *newList);

    RegisterExtractors("/system_ext/lib"
#ifdef __LP64__
            "64"
#endif
            "/extractors", NULL, *newList);

    newList->sort(compareFunc);
    gPlugins = newList;

    for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
        if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
            for (size_t i = 0;; i++) {
                const char* ext = (*it)->def.u.v3.supported_types[i];
                if (ext == nullptr) {
                    break;
                }
                gSupportedExtensions.push_back(std::string(ext));
            }
        }
    }

    gPluginsRegistered = true;
}

对照上述代码,我们可以发现他会执行android_get_exported_namespace获取com_android_media这个命名空间。 Android的命名空间则是一种系统级的资源隔离机制。每个命名空间都有自己的资源集,这些资原可以是库(Library),也可以是其他类 型的资源。命名空间之间是隔离的,一个命名空间中的资源不能直接访问另一个命名空间中的资源。 然后就会执行RegisterExtractors方法:

cpp 复制代码
void MediaExtractorFactory::RegisterExtractors(
        const char *libDirPath, const android_dlextinfo* dlextinfo,
        std::list<sp<ExtractorPlugin>> &pluginList) {
    ALOGV("search for plugins at %s", libDirPath);

    DIR *libDir = opendir(libDirPath);
    if (libDir) {
        struct dirent* libEntry;
        while ((libEntry = readdir(libDir))) {
            if (libEntry->d_name[0] == '.') {
                continue;
            }
            String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
            if (!libPath.contains("extractor.so")) {
                continue;
            }
            void *libHandle = android_dlopen_ext(
                    libPath.c_str(),
                    RTLD_NOW | RTLD_LOCAL, dlextinfo);
            if (libHandle == nullptr) {
                ALOGI("dlopen(%s) reported error %s", libPath.c_str(), strerror(errno));
                continue;
            }

            GetExtractorDef getDef =
                (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
            if (getDef == nullptr) {
                ALOGI("no sniffer found in %s", libPath.c_str());
                dlclose(libHandle);
                continue;
            }

            ALOGV("registering sniffer for %s", libPath.c_str());
            RegisterExtractor(
                    new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
        }
        closedir(libDir);
    } else {
        ALOGI("plugin directory not present (%s)", libDirPath);
    }
}

我们可以发现主要作用是动态加载和注册媒体提取器插件,这样,当需要处理媒体数据时,就可以根据文件的格式选择合适的插件来处理数据,主要步骤为:

  1. 打开指定的库目录(libDirPath),这个目录中包含了一些动态链接库(.so文件)。

  2. 遍历这个目录中的每一个文件。如果文件名包含extractor.so,那么就尝试打开这个文件。

  3. 使用dlopen函数打开文件,获取到库的句柄(libHandle)。如果打开失败,那么就跳过这个文件,处理下一个文件。

  4. 使用dlsym函数从库中查找名为GETEXTRACTORDEF 的符号。这个符号是一个函数,用于获取提取器的定义。

  5. 如果找到了这个符号,那么就创建一个新的ExtractorPlugin对象,然后将这个对象添加到插件列表(pluginList) 中。

    cpp 复制代码
    ExtractorPlugin(ExtractorDef definition, void *handle, String8 &path)
      : def(definition), libHandle(handle), libPath(path) {
        for (size_t i = 0; i < sizeof ExtractorDef::extractor_uuid; i++) {
          uuidString.appendFormat("%02x", def.extractor_uuid.b[i]);
        }
      }

    我们可以发现其实就是把GETEXTRACTORDEF函数地址存到def里,库的句柄存到libHandle里、库路径存到libPath

  6. 如果遍历完所有的文件后,没有找到任何有效的插件。那么就输出一条日志,表示插件目录不存在。

LoadExtractors会调用RegisterExtractors循环执行上述流程,把

  • "/apex/com.android.media/lib"
  • "/system/lib"
  • "/system_ext/lib"

这几个目录下的"extractor.so"都加载进来变成媒体提取器

3.2 creato函数

在刚才解析sniff()函数的时候,我们会发现creator函数赋值是在:

cpp 复制代码
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);
}

(*it)->def其实就是"extractor.so"里GETEXTRACTORDEF对应的函数,creator函数其实就是执行这个函数得到的返回函数

执行creator函数即可创建CMediaExtractor函数

3.3 MediaExtractorCUnwrapper对象

MediaExtractorCUnwrapper对象其实就是包了下MediaExtractor这个实例,然后创建的新对象

3.4 CreateIMediaExtractorFromMediaExtractor函数

arduino 复制代码
sp<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor(
        MediaExtractor *extractor,
        const sp<DataSource> &source,
        const sp<RefBase> &plugin) {
    if (extractor == nullptr) {
        return nullptr;
    }
    return RemoteMediaExtractor::wrap(extractor, source, plugin);
}

观察源码可以发现其也是包了下MediaExtrator

至此android已经通过setDataSource拿到对应的媒体提取器了。

4、media.extrator主线程模式媒体提取器

我们先来回归下setDataSource是如何创建主线程模式媒体提取器,其源码:

cpp 复制代码
 // remote extractor
ALOGV("get service manager");
sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));

if (binder != 0) {
  sp<IMediaExtractorService> mediaExService(
    interface_cast<IMediaExtractorService>(binder));
  sp<IMediaExtractor> ex;
  mediaExService->makeExtractor(
    CreateIDataSourceFromDataSource(source),
    mime ? std::optional<std::string>(mime) : std::nullopt,
    &ex);
  return ex;
} else {
  ALOGE("extractor service not running");
  return NULL;
}

首先获取系统服务管理器并会尝试获取名为"media.extractor"的服务。这个服务其实就是"media.extractor"是媒体提取服务(Media Extractor Service)的名字。媒体提取服务是Android系统里提供的一种服务,它的主要功能就是从媒体文件中获取音频和视频的数据。

然后会通过类型转换赋值给sp<IMediaExtractorService> mediaExService

最后调用mediaExService->makeExtractor赋值给sp<IMediaExtractor> ex

5、补充

我们在一开始的时候说MediaExtractor里有这几个核心的api:setDataSource、getTrackCount、getTrackFormat、getSampleTime、readSampleData、advance、release。这几个函数的核心实现其实都是调用我们刚才说的sp<IMediaExtractorService> mediaExService

至此MediaExtrator的源码解析部分就已经完成了,下面我们会展开讲讲MediaCodec了

相关推荐
哔哩哔哩技术7 分钟前
B站多模态精细画质分析模型在 ICCV2025 大赛获得佳绩
音视频开发
Jerry1 小时前
Compose 为元素赋予动画特效
android
Jeled2 小时前
协程工具类
android·android studio
阿兰哥5 小时前
【调试篇5】TransactionTooLargeException 原理解析
android·性能优化·源码
爱吃水蜜桃的奥特曼6 小时前
玩Android Flutter版本,通过项目了解Flutter项目快速搭建开发
android·flutter
太过平凡的小蚂蚁6 小时前
Android 版本特性完全解析:从6.0到16.0的实用指南
android
杨筱毅6 小时前
【底层机制】【Android】深入理解UI体系与绘制机制
android·底层机制
介一安全7 小时前
【Frida Android】基础篇8:Java层Hook基础——调用带对象参数的方法
android·网络安全·逆向·安全性测试·frida
puyaCheer7 小时前
Android 13 启动的时候会显示一下logo,很不友好
android·gitee
long_hai_d8 小时前
Aosp14桌面壁纸和锁屏壁纸的设置和加载分析
android