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了

相关推荐
孑么3 小时前
GDPU Android移动应用 重点习题集
android·xml·java·okhttp·kotlin·android studio·webview
@OuYang5 小时前
Audio音频输出通道
android
ta叫我小白6 小时前
Kotlin 中 forEach 的 return@forEach 的使用误区
android·开发语言·kotlin
archko6 小时前
试用kotlin multiplatform
android·开发语言·kotlin
C4rpeDime7 小时前
当歌 - RSS 订阅分发平台开发
android
大雄野比9 小时前
UOS系统mysql服务安装
android·mysql·adb
yozyyyqls9 小时前
自定义Compose Pager实现电影卡片列表
android
张二三9 小时前
flutter 开发笔记(九):原生桥接
android·flutter·ios
梅名智10 小时前
Android studio gradle与gradle插件
android·ide·android studio
Hacker_Fuchen10 小时前
攻防世界 ics-07
android