MediaExtractor源码分析

作者: Young_Allen | 来源:发表于2017-08-22 15:46 被阅读157次

    1.介绍:

    MediaExtractor是Android中音视频解复用器,可以将video和audio分离出来。android kitkat版本上支持本地和http的音视频解复用,打算按照这个设计模式扩展电信IPTV的RTSP的音视频解复用。
    

    2.干货:

    MediaExtractor的入口是NuMediaExtractor,通过下面代码即可实例化MediaExtractor:

        sp<NuMediaExtractor> extractor = new NuMediaExtractor;
        if (extractor->setDataSource(path) != OK) {
            fprintf(stderr, "unable to instantiate extractor.\n");
            extractor = NULL;
            return 1;
        }
        
        for (size_t i = 0; i < extractor->countTracks(); ++i) {
            sp<AMessage> decode_format;
    
            status_t err = extractor->getTrackFormat(i, &decode_format);
            CHECK_EQ(err, (status_t)OK);
            
            AString mime;
            CHECK(decode_format->findString("mime", &mime));
    
            bool isAudio = !strncasecmp(mime.c_str(), "audio/", 6);
            bool isVideo = !strncasecmp(mime.c_str(), "video/", 6);
            ...
        }
    

    先来看看NuMediaExtractor的一段代码:

    status_t NuMediaExtractor::setDataSource(
            const char *path, const KeyedVector<String8, String8> *headers) {
        Mutex::Autolock autoLock(mLock);
    
        if (mImpl != NULL) {
            return -EINVAL;
        }
    
        sp<DataSource> dataSource =
            DataSource::CreateFromURI(path, headers);
    
        if (dataSource == NULL) {
            return -ENOENT;
        }
        mIsWidevineExtractor = false;
        if (!strncasecmp("widevine://", path, 11)) {
            String8 mimeType;
            float confidence;
            sp<AMessage> dummy;
            bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
    
            if (!success || strcasecmp(mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
                return ERROR_UNSUPPORTED;
            }
    
            sp<WVMExtractor> extractor = new WVMExtractor(dataSource);
            extractor->setAdaptiveStreamingMode(true);
    
            mImpl = extractor;
            mIsWidevineExtractor = true;
        } else {
            mImpl = MediaExtractor::Create(dataSource);
        }
        if (mImpl == NULL) {
            return ERROR_UNSUPPORTED;
        }
    
        sp<MetaData> fileMeta = mImpl->getMetaData();
        const char *containerMime;
        if (fileMeta != NULL
                && fileMeta->findCString(kKeyMIMEType, &containerMime)
                && !strcasecmp(containerMime, "video/wvm")) {
        static_cast<WVMExtractor *>(mImpl.get())->setCryptoPluginMode(true);
        } else if (mImpl->getDrmFlag()) {
            // For all other drm content, we don't want to expose decrypted
            // content to Java application.
            mImpl.clear();
            mImpl = NULL;
            return ERROR_UNSUPPORTED;
        }
    
        mDataSource = dataSource;
    
        updateDurationAndBitrate();
    
        return OK;
    }
    

    DataSource通过path的类型创建出匹配的资源:

    sp<DataSource> DataSource::CreateFromURI(
            const char *uri, const KeyedVector<String8, String8> *headers) {
        bool isWidevine = !strncasecmp("widevine://", uri, 11);
        sp<DataSource> source;
        if (!strncasecmp("file://", uri, 7)) {
            source = new FileSource(uri + 7);
            setFileName(uri + 7);
        } else if (!strncasecmp("http://", uri, 7)
                || !strncasecmp("https://", uri, 8)
                || isWidevine) {
            sp<HTTPBase> httpSource = HTTPBase::Create();
    
            String8 tmp;
            if (isWidevine) {
                tmp = String8("http://");
                tmp.append(uri + 11);
    
                uri = tmp.string();
            }
    
            if (httpSource->connect(uri, headers) != OK) {
                return NULL;
            }
            if (!isWidevine) {
                String8 cacheConfig;
                bool disconnectAtHighwatermark;
                if (headers != NULL) {
                    KeyedVector<String8, String8> copy = *headers;
                    NuCachedSource2::RemoveCacheSpecificHeaders(
                            &copy, &cacheConfig, &disconnectAtHighwatermark);
                }
    
                source = new NuCachedSource2(
                        httpSource,
                        cacheConfig.isEmpty() ? NULL : cacheConfig.string());
            } else {
               source = httpSource;
            }
    # if CHROMIUM_AVAILABLE
        } else if (!strncasecmp("data:", uri, 5)) {
            source = createDataUriSource(uri);
    #endif
        } else {
            // Assume it's a filename.
            source = new FileSource(uri);
            setFileName(uri);
        }
    
        if (source == NULL || source->initCheck() != OK) {
            return NULL;
        }
    
        return source;
    }
    

    假如当前的path传入的是http://的网络资源串,那么就会实例化一个HTTPBase对象.首先看一下HTTPBase的声明:

    struct HTTPBase : public DataSource {
        enum Flags {
            // Don't log any URLs.
            kFlagIncognito = 1
        };
    
        HTTPBase();
        ...
    }
    

    从代码中可以看到,通过createChromiumHTTPDataSource来创建了一个HTTPBase 对象的指针:

    sp<HTTPBase> HTTPBase::Create(uint32_t flags) {
    #if CHROMIUM_AVAILABLE
            HTTPBase *dataSource = createChromiumHTTPDataSource(flags);
            if (dataSource) {
               return dataSource;
            }
    #endif
        {
            TRESPASS();
    
            return NULL;
        }
    }
    

    createChromiumHTTPDataSource在libstagefright的chromium_http模块中,调用代码:
    chromium_http_stub.h

    #ifndef CHROMIUM_HTTP_STUB_H_
    #define CHROMIUM_HTTP_STUB_H_
    
    #include <include/HTTPBase.h>
    #include <media/stagefright/DataSource.h>
    
    namespace android {
    extern "C" {
    HTTPBase *createChromiumHTTPDataSource(uint32_t flags);
    
    status_t UpdateChromiumHTTPDataSourceProxyConfig(
            const char *host, int32_t port, const char *exclusionList);
    
    DataSource *createDataUriSource(const char *uri);
    }
    }
    
    #endif
    

    chromium_http_stub.cpp

    HTTPBase *(*gLib_createChromiumHTTPDataSource)(uint32_t flags);
    DataSource *(*gLib_createDataUriSource)(const char *uri);
    
    static bool load_libstagefright_chromium_http() {
        Mutex::Autolock autoLock(gLibMutex);
        void *sym;
        if (!gFirst) {
            return (gHandle != NULL);
        }
    
        gFirst = false;
    
        gHandle = dlopen("libstagefright_chromium_http.so", RTLD_NOW);
        if (gHandle == NULL) {
            return false;
        }
    
        sym = dlsym(gHandle, "createChromiumHTTPDataSource");
        if (sym == NULL) {
            gHandle = NULL;
            return false;
        }gLib_createChromiumHTTPDataSource = (HTTPBase *(*)(uint32_t))sym;
    
        sym = dlsym(gHandle, "createDataUriSource");
        if (sym == NULL) {
            gHandle = NULL;
            return false;
        }
        gLib_createDataUriSource = (DataSource *(*)(const char *))sym;
        ...
    }
    
    HTTPBase *createChromiumHTTPDataSource(uint32_t flags) {
        if (!load_libstagefright_chromium_http()) {
            return NULL;
        }
    
        return gLib_createChromiumHTTPDataSource(flags);
    }
    

    libstagefright_chromium_http.so就是libstagefright的chromium_http模块了:

    #include <dlfcn.h>
    
    #include <include/chromium_http_stub.h>
    #include <include/ChromiumHTTPDataSource.h>
    #include <include/DataUriSource.h>
    
    namespace android {
    
    HTTPBase *createChromiumHTTPDataSource(uint32_t flags) {
        return new ChromiumHTTPDataSource(flags);
    }
    
    status_t UpdateChromiumHTTPDataSourceProxyConfig(
            const char *host, int32_t port, const char *exclusionList) {
        return ChromiumHTTPDataSource::UpdateProxyConfig(host, port, exclusionList);
    }
    
    DataSource *createDataUriSource(const char *uri) {
        return new DataUriSource(uri);
    }
    

    ChromiumHTTPDataSource.cpp

    ChromiumHTTPDataSource::ChromiumHTTPDataSource(uint32_t flags)
        : mFlags(flags),
          mState(DISCONNECTED),
          mDelegate(new SfDelegate),
          mCurrentOffset(0),
          mIOResult(OK),
          mContentSize(-1),
          mDecryptHandle(NULL),
          mDrmManagerClient(NULL) {
        mDelegate->setOwner(this);
    }
    
    ChromiumHTTPDataSource::~ChromiumHTTPDataSource() {
        disconnect();
    
        delete mDelegate;
        mDelegate = NULL;
    
        clearDRMState_l();
    
        if (mDrmManagerClient != NULL) {
            delete mDrmManagerClient;
            mDrmManagerClient = NULL;
        }
    }
    

    那么httpSource->connect(uri, headers)做了些什么?

    status_t ChromiumHTTPDataSource::connect(
            const char *uri,
            const KeyedVector<String8, String8> *headers,
            off64_t offset) {
        Mutex::Autolock autoLock(mLock);
    
        uid_t uid;
        if (getUID(&uid)) {
            mDelegate->setUID(uid);
        }
    
    #if defined(LOG_NDEBUG) && !LOG_NDEBUG
        LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG, "connect on behalf of uid %d", uid);
    #endif
    
        return connect_l(uri, headers, offset);
    }
    
    status_t ChromiumHTTPDataSource::connect_l(
            const char *uri,
            const KeyedVector<String8, String8> *headers,
            off64_t offset) {
        if (mState != DISCONNECTED) {
            disconnect_l();
        }
    
    #if defined(LOG_NDEBUG) && !LOG_NDEBUG
        LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG,
                    "connect to <URL suppressed> @%lld", offset);
    #endif
    
        mURI = uri;
        mContentType = String8("application/octet-stream");
    
        if (headers != NULL) {
            mHeaders = *headers;
        } else {
            mHeaders.clear();
        }
    
        mState = CONNECTING;
        mContentSize = -1;
        mCurrentOffset = offset;
    
        mDelegate->initiateConnection(mURI.c_str(), &mHeaders, offset);
    
        while (mState == CONNECTING || mState == DISCONNECTING) {
            mCondition.wait(mLock);
        }
    
        return mState == CONNECTED ? OK : mIOResult;
    }
    

    在chromium_http模块中SfDelegate继承了net::URLRequest::Delegate代理所有http的处理:

    struct SfDelegate : public net::URLRequest::Delegate {
      ...
    }
    

    net::URLRequest::Delegate是libchromium_net的实现,可以去看external\chromium下的代码,MK文件如下:

    ###################################
    # Build the libchromium_net library
    
    LOCAL_PATH := $(call my-dir)
    include external/chromium/third_party/libevent/Android.mk
    include external/chromium/third_party/modp_b64/Android.mk
    include external/chromium/base/third_party/dmg_fp/Android.mk
    
    include $(CLEAR_VARS)
    
    LOCAL_CPP_EXTENSION := .cc
    
    LOCAL_MODULE := libchromium_net
    LOCAL_MODULE_CLASS := SHARED_LIBRARIES
    INTERMEDIATES := $(call local-intermediates-dir)
    
    LOCAL_SRC_FILES := \
        googleurl/src/gurl.cc \
        googleurl/src/url_canon_etc.cc \
        googleurl/src/url_canon_fileurl.cc \
        googleurl/src/url_canon_host.cc \
        googleurl/src/url_canon_icu.cc \
        googleurl/src/url_canon_internal.cc \
        googleurl/src/url_canon_ip.cc \
        googleurl/src/url_canon_mailtourl.cc \
        googleurl/src/url_canon_path.cc \
        googleurl/src/url_canon_pathurl.cc \
        googleurl/src/url_canon_query.cc \
        googleurl/src/url_canon_relative.cc \
        googleurl/src/url_canon_stdurl.cc \
        googleurl/src/url_parse.cc \
        googleurl/src/url_parse_file.cc \
        googleurl/src/url_util.cc \
        \
        android/content/common/url_constants.cc \
        android/execinfo.cc \
        android/jni/autofill_request_url.cc \
        android/jni/mime_utils.cc \
        android/jni/jni_utils.cc \
        android/jni/platform_file_jni.cc \
        android/net/android_network_library_impl.cc \
        android/ui/base/l10n/l10n_util.cc \
        \
        app/sql/connection.cc \
        app/sql/meta_table.cc \
        app/sql/statement.cc \
        app/sql/transaction.cc \
    
    ifeq ($(TARGET_ARCH),x86)
    LOCAL_SRC_FILES += \
        base/atomicops_internals_x86_gcc.cc
    endif
    
    LOCAL_SRC_FILES += \
        base/at_exit.cc \
        base/base64.cc \
        base/environment.cc \
        base/file_descriptor_shuffle.cc \
        base/file_path.cc \
        base/file_util.cc \
        base/file_util_posix.cc \
        base/lazy_instance.cc \
        base/logging.cc \
        base/message_loop.cc \
        base/message_loop_proxy.cc \
        base/message_loop_proxy_impl.cc \
        base/message_pump.cc \
        base/message_pump_default.cc \
        base/message_pump_libevent.cc \
        base/md5.cc \
        base/native_library_linux.cc \
        base/pickle.cc \
        base/platform_file.cc \
        base/platform_file_posix.cc \
        base/process_posix.cc \
        base/process_util.cc \
        base/process_util_linux.cc \
        base/process_util_posix.cc \
        base/rand_util.cc \
        base/rand_util_posix.cc \
        base/safe_strerror_posix.cc \
        base/sha1_portable.cc \
        base/shared_memory_posix.cc \
        base/string_number_conversions.cc \
        base/string_piece.cc \
        base/string_split.cc \
        base/string_util.cc \
        base/string16.cc \
        base/stringprintf.cc \
        base/sys_info_linux.cc \
        base/sys_info_posix.cc \
        base/sys_string_conversions_linux.cc \
        base/task.cc \
        base/time.cc \
        base/time_posix.cc \
        base/timer.cc \
        base/tracked.cc \
        base/tracked_objects.cc \
        base/utf_offset_string_conversions.cc \
        base/utf_string_conversions.cc \
        base/utf_string_conversion_utils.cc \
        base/values.cc \
        base/vlog.cc \
        \
        base/debug/debugger_posix.cc \
        base/debug/stack_trace.cc \
        base/debug/stack_trace_posix.cc \
        \
        base/i18n/file_util_icu.cc \
        base/i18n/icu_string_conversions.cc \
        base/i18n/time_formatting.cc \
        \
        base/json/json_reader.cc \
        base/json/json_writer.cc \
        base/json/string_escape.cc \
        \
        base/memory/ref_counted.cc \
        base/memory/weak_ptr.cc \
        \
        base/metrics/field_trial.cc \
        base/metrics/histogram.cc \
        base/metrics/stats_counters.cc \
        base/metrics/stats_table.cc \
        \
        base/synchronization/cancellation_flag.cc \
        base/synchronization/condition_variable_posix.cc \
        base/synchronization/lock_impl_posix.cc \
        base/synchronization/waitable_event_posix.cc \
        \
        base/threading/platform_thread_posix.cc \
        base/threading/thread.cc \
        base/threading/thread_checker_impl.cc \
        base/threading/thread_collision_warner.cc \
        base/threading/thread_local_posix.cc \
        base/threading/thread_local_storage_posix.cc \
        base/threading/worker_pool_posix.cc \
        \
        base/third_party/icu/icu_utf.cc \
        \
        base/third_party/nspr/prtime.cc \
        \
        chrome/browser/net/sqlite_persistent_cookie_store.cc \
        \
        crypto/openssl_util.cc \
        crypto/secure_hash_default.cc \
        crypto/sha2.cc \
        \
        crypto/third_party/nss/sha512.cc \
        \
        net/base/address_list.cc \
        net/base/address_list_net_log_param.cc \
        net/base/android_network_library.cc \
        net/base/auth.cc \
        net/base/backoff_entry.cc \
        net/base/bandwidth_metrics.cc \
        net/base/capturing_net_log.cc \
        net/base/cert_database.cc \
        net/base/cert_status_flags.cc \
        net/base/cert_verifier.cc \
        net/base/cert_verify_result.cc \
        net/base/connection_type_histograms.cc \
        net/base/cookie_monster.cc \
        net/base/cookie_store.cc \
        net/base/data_url.cc \
        net/base/directory_lister.cc \
        net/base/dns_util.cc \
        net/base/dnsrr_resolver.cc \
        net/base/escape.cc \
        net/base/file_stream_posix.cc \
        net/base/filter.cc \
        net/base/gzip_filter.cc \
        net/base/gzip_header.cc \
        net/base/host_cache.cc \
        net/base/host_mapping_rules.cc \
        net/base/host_port_pair.cc \
        net/base/host_resolver.cc \
        net/base/host_resolver_impl.cc \
        net/base/host_resolver_proc.cc \
        net/base/io_buffer.cc \
        net/base/ip_endpoint.cc \
        net/base/mime_util.cc \
        net/base/net_errors.cc \
        net/base/net_errors_posix.cc \
        net/base/net_log.cc \
        net/base/net_module.cc \
        net/base/net_util.cc \
        net/base/net_util_posix.cc \
        net/base/network_change_notifier.cc \
        net/base/network_change_notifier_linux.cc \
        net/base/network_change_notifier_netlink_linux.cc \
        net/base/network_delegate.cc \
        net/base/openssl_memory_private_key_store.cc \
        net/base/pem_tokenizer.cc \
        net/base/platform_mime_util_android.cc \
        net/base/registry_controlled_domain.cc \
        net/base/sdch_manager.cc \
        net/base/sdch_filter.cc \
        net/base/ssl_cert_request_info.cc \
        net/base/ssl_client_auth_cache.cc \
        net/base/ssl_config_service.cc \
        net/base/ssl_config_service_defaults.cc \
        net/base/ssl_info.cc \
        net/base/transport_security_state.cc \
        net/base/upload_data.cc \
        net/base/upload_data_stream.cc \
        net/base/x509_cert_types.cc \
        net/base/x509_certificate.cc \
        net/base/x509_certificate_openssl.cc \
        net/base/x509_certificate_openssl_android.cc \
        net/base/x509_openssl_util.cc \
        \
        net/disk_cache/addr.cc \
        net/disk_cache/backend_impl.cc \
        net/disk_cache/bitmap.cc \
        net/disk_cache/block_files.cc \
        net/disk_cache/cache_util_posix.cc \
        net/disk_cache/disk_format.cc \
        net/disk_cache/entry_impl.cc \
        net/disk_cache/eviction.cc \
        net/disk_cache/file.cc \
        net/disk_cache/file_lock.cc \
        net/disk_cache/file_posix.cc \
        net/disk_cache/hash.cc \
        net/disk_cache/in_flight_backend_io.cc \
        net/disk_cache/in_flight_io.cc \
        net/disk_cache/mapped_file_posix.cc \
        net/disk_cache/mem_backend_impl.cc \
        net/disk_cache/mem_entry_impl.cc \
        net/disk_cache/mem_rankings.cc \
        net/disk_cache/net_log_parameters.cc \
        net/disk_cache/rankings.cc \
        net/disk_cache/stats.cc \
        net/disk_cache/stats_histogram.cc \
        net/disk_cache/sparse_control.cc \
        net/disk_cache/trace.cc \
        \
        net/ftp/ftp_auth_cache.cc \
        \
        net/http/des.cc \
        net/http/disk_cache_based_ssl_host_info.cc \
        net/http/http_alternate_protocols.cc \
        net/http/http_auth.cc \
        net/http/http_auth_cache.cc \
        net/http/http_auth_controller.cc \
        net/http/http_auth_gssapi_posix.cc \
        net/http/http_auth_handler.cc \
        net/http/http_auth_handler_basic.cc \
        net/http/http_auth_handler_digest.cc \
        net/http/http_auth_handler_factory.cc \
        net/http/http_auth_handler_negotiate.cc \
        net/http/http_auth_handler_ntlm.cc \
        net/http/http_auth_handler_ntlm_portable.cc \
        net/http/http_basic_stream.cc \
        net/http/http_byte_range.cc \
        net/http/http_cache.cc \
        net/http/http_cache_transaction.cc \
        net/http/http_chunked_decoder.cc \
        net/http/http_net_log_params.cc \
        net/http/http_network_layer.cc \
        net/http/http_network_session.cc \
        net/http/http_network_transaction.cc \
        net/http/http_proxy_client_socket.cc \
        net/http/http_proxy_client_socket_pool.cc \
        net/http/http_proxy_utils.cc \
        net/http/http_request_headers.cc \
        net/http/http_request_info.cc \
        net/http/http_response_body_drainer.cc \
        net/http/http_response_headers.cc \
        net/http/http_response_info.cc \
        net/http/http_stream_factory.cc \
        net/http/http_stream_factory_impl.cc \
        net/http/http_stream_factory_impl_job.cc \
        net/http/http_stream_factory_impl_request.cc \
        net/http/http_stream_parser.cc \
        net/http/http_util.cc \
        net/http/http_util_icu.cc \
        net/http/http_vary_data.cc \
        net/http/md4.cc \
        net/http/partial_data.cc \
        \
        net/proxy/init_proxy_resolver.cc \
        net/proxy/multi_threaded_proxy_resolver.cc \
        net/proxy/proxy_bypass_rules.cc \
        net/proxy/proxy_config.cc \
        net/proxy/proxy_config_service_android.cc \
        net/proxy/proxy_config_service_fixed.cc \
        net/proxy/proxy_info.cc \
        net/proxy/proxy_list.cc \
        net/proxy/proxy_resolver_js_bindings.cc \
        net/proxy/proxy_resolver_script_data.cc \
        net/proxy/proxy_server.cc \
        net/proxy/proxy_service.cc \
        net/proxy/sync_host_resolver_bridge.cc \
        \
        net/socket/client_socket.cc \
        net/socket/client_socket_handle.cc \
        net/socket/client_socket_factory.cc \
        net/socket/client_socket_pool.cc \
        net/socket/client_socket_pool_base.cc \
        net/socket/client_socket_pool_histograms.cc \
        net/socket/client_socket_pool_manager.cc \
        net/socket/socks_client_socket.cc \
        net/socket/socks_client_socket_pool.cc \
        net/socket/socks5_client_socket.cc \
        net/socket/ssl_client_socket.cc \
        net/socket/ssl_client_socket_openssl.cc \
        net/socket/ssl_client_socket_pool.cc \
        net/socket/ssl_error_params.cc \
        net/socket/ssl_host_info.cc \
        net/socket/tcp_client_socket.cc \
        net/socket/tcp_client_socket_libevent.cc \
        net/socket/transport_client_socket_pool.cc \
        \
        net/spdy/spdy_framer.cc \
        net/spdy/spdy_frame_builder.cc \
        net/spdy/spdy_http_stream.cc \
        net/spdy/spdy_http_utils.cc \
        net/spdy/spdy_io_buffer.cc \
        net/spdy/spdy_proxy_client_socket.cc \
        net/spdy/spdy_session.cc \
        net/spdy/spdy_session_pool.cc \
        net/spdy/spdy_settings_storage.cc \
        net/spdy/spdy_stream.cc \
        \
        net/url_request/https_prober.cc \
        net/url_request/url_request.cc \
        net/url_request/url_request_context.cc \
        net/url_request/url_request_context_getter.cc \
        net/url_request/url_request_file_job.cc \
        net/url_request/url_request_file_dir_job.cc \
        net/url_request/url_request_http_job.cc \
        net/url_request/url_request_error_job.cc \
        net/url_request/url_request_job.cc \
        net/url_request/url_request_job_manager.cc \
        net/url_request/url_request_job_tracker.cc \
        net/url_request/url_request_netlog_params.cc \
        net/url_request/url_request_redirect_job.cc \
        net/url_request/url_request_throttler_entry.cc \
        net/url_request/url_request_throttler_header_adapter.cc \
        net/url_request/url_request_throttler_manager.cc \
        \
        sdch/open-vcdiff/src/addrcache.cc \
        sdch/open-vcdiff/src/blockhash.cc \
        sdch/open-vcdiff/src/codetable.cc \
        sdch/open-vcdiff/src/encodetable.cc \
        sdch/open-vcdiff/src/decodetable.cc \
        sdch/open-vcdiff/src/headerparser.cc \
        sdch/open-vcdiff/src/instruction_map.cc \
        sdch/open-vcdiff/src/logging.cc \
        sdch/open-vcdiff/src/varint_bigendian.cc \
        sdch/open-vcdiff/src/vcdecoder.cc \
        sdch/open-vcdiff/src/vcdiffengine.cc \
        sdch/open-vcdiff/src/vcencoder.cc \
        \
        ui/gfx/point.cc \
    
    # AutoFill++ source files.
    LOCAL_SRC_FILES += \
        android/autofill/android_url_request_context_getter.cc \
        android/autofill/profile_android.cc \
        android/autofill/url_fetcher_proxy.cc \
        \
        base/base_paths.cc \
        base/base_paths_linux.cc \
        base/path_service.cc \
        \
        chrome/browser/autofill/address.cc \
        chrome/browser/autofill/address_field.cc \
        chrome/browser/autofill/autofill_country.cc \
        chrome/browser/autofill/autofill_download.cc \
        chrome/browser/autofill/autofill_field.cc \
        chrome/browser/autofill/autofill_manager.cc \
        chrome/browser/autofill/autofill_metrics.cc \
        chrome/browser/autofill/autofill_profile.cc \
        chrome/browser/autofill/autofill_type.cc \
        chrome/browser/autofill/autofill_xml_parser.cc \
        chrome/browser/autofill/contact_info.cc \
        chrome/browser/autofill/credit_card.cc \
        chrome/browser/autofill/credit_card_field.cc \
        chrome/browser/autofill/fax_number.cc \
        chrome/browser/autofill/form_field.cc \
        chrome/browser/autofill/form_group.cc \
        chrome/browser/autofill/form_structure.cc \
        chrome/browser/autofill/name_field.cc \
        chrome/browser/autofill/home_phone_number.cc \
        chrome/browser/autofill/personal_data_manager.cc \
        chrome/browser/autofill/phone_field.cc \
        chrome/browser/autofill/phone_number.cc \
        chrome/browser/autofill/select_control_handler.cc \
        \
        chrome/common/guid.cc \
        chrome/common/guid_posix.cc \
        chrome/common/url_constants.cc \
        \
        chrome/common/net/url_fetcher.cc \
        chrome/common/net/url_fetcher_protect.cc \
        \
        third_party/libjingle/overrides/talk/xmllite/qname.cc \
        third_party/libjingle/source/talk/xmllite/xmlbuilder.cc \
        third_party/libjingle/source/talk/xmllite/xmlconstants.cc \
        third_party/libjingle/source/talk/xmllite/xmlelement.cc \
        third_party/libjingle/source/talk/xmllite/xmlnsstack.cc \
        third_party/libjingle/source/talk/xmllite/xmlparser.cc \
        third_party/libjingle/source/talk/xmllite/xmlprinter.cc \
        \
        webkit/glue/form_data.cc \
        webkit/glue/form_field.cc
    
    LOCAL_C_INCLUDES := \
        $(LOCAL_PATH) \
        $(LOCAL_PATH)/chrome \
        $(LOCAL_PATH)/chrome/browser \
        $(LOCAL_PATH)/sdch/open-vcdiff/src \
        $(LOCAL_PATH)/third_party/libevent/compat \
        external/expat \
        external/icu4c/common \
        external/icu4c/i18n \
        external/openssl/include \
        external/skia \
        external/sqlite/dist \
        external/webkit/Source/WebKit/chromium \
        external/zlib \
        external \
        $(LOCAL_PATH)/base/third_party/dmg_fp \
        $(LOCAL_PATH)/third_party/icu/public/common \
        $(LOCAL_PATH)/third_party/libevent/android \
        $(LOCAL_PATH)/third_party/libevent \
        $(LOCAL_PATH)/third_party/libjingle/overrides \
        $(LOCAL_PATH)/third_party/libjingle/source \
        vendor/google/libraries/autofill
    
    # Chromium uses several third party libraries and headers that are already
    # present on Android, but in different include paths. Generate a set of
    # forwarding headers in the location that Chromium expects.
    
    THIRD_PARTY = $(INTERMEDIATES)/third_party
    SCRIPT := $(LOCAL_PATH)/android/generateAndroidForwardingHeader.pl
    
    GEN := $(THIRD_PARTY)/expat/files/lib/expat.h
    $(GEN): $(SCRIPT)
    $(GEN):
        perl $(SCRIPT) $@ "lib/expat.h"
    LOCAL_GENERATED_SOURCES += $(GEN)
    
    GEN := $(THIRD_PARTY)/skia/include/core/SkBitmap.h
    $(GEN): $(SCRIPT)
    $(GEN):
        perl $(SCRIPT) $@ "include/core/SkBitmap.h"
    LOCAL_GENERATED_SOURCES += $(GEN)
    
    GEN := $(THIRD_PARTY)/WebKit/Source/WebKit/chromium/public/WebFormControlElement.h
    $(GEN): $(SCRIPT)
    $(GEN):
        perl $(SCRIPT) $@ "public/WebFormControlElement.h"
    LOCAL_GENERATED_SOURCES += $(GEN)
    
    GEN := $(THIRD_PARTY)/WebKit/Source/WebKit/chromium/public/WebRegularExpression.h
    $(GEN): $(SCRIPT)
    $(GEN):
        perl $(SCRIPT) $@ "public/WebRegularExpression.h"
    LOCAL_GENERATED_SOURCES += $(GEN)
    
    GEN := $(THIRD_PARTY)/WebKit/Source/WebKit/chromium/public/WebString.h
    $(GEN): $(SCRIPT)
    $(GEN):
        perl $(SCRIPT) $@ "public/WebString.h"
    LOCAL_GENERATED_SOURCES += $(GEN)
    
    LOCAL_CFLAGS := -DHAVE_CONFIG_H -DANDROID -DEXPAT_RELATIVE_PATH -DALLOW_QUOTED_COOKIE_VALUES -DCOMPONENT_BUILD -DGURL_DLL
    LOCAL_CPPFLAGS := -Wno-sign-promo -Wno-missing-field-initializers -fvisibility=hidden -fvisibility-inlines-hidden
    
    # Just a few definitions not provided by bionic.
    LOCAL_CFLAGS += -include "android/prefix.h"
    
    # external/chromium/android is a directory to intercept stl headers that we do
    # not support yet.
    LOCAL_C_INCLUDES := \
        $(LOCAL_PATH)/android \
        $(LOCAL_C_INCLUDES)
    
    LOCAL_STATIC_LIBRARIES := libevent modp_b64 dmg_fp
    LOCAL_SHARED_LIBRARIES := libstlport libexpat libcrypto libssl libz libicuuc libicui18n libsqlite libcutils liblog libdl
    
    LOCAL_PRELINK_MODULE := false
    
    # Including this will modify the include path
    include external/stlport/libstlport.mk
    
    ifneq ($(strip $(WITH_ADDRESS_SANITIZER)),)
        LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/asan
        LOCAL_ADDRESS_SANITIZER := true
    endif
    
    include $(BUILD_SHARED_LIBRARY)
    

    可以看到libchromium_net对net已经实现很多关于http、disk_cache、proxy、socket等网络处理。本篇文章不涉及该模块内容。
    connect对http请求做了初始化处理,然后委托给NuCachedSource2

     source = new NuCachedSource2(
                        httpSource,
                        cacheConfig.isEmpty() ? NULL : cacheConfig.string());
    

    NuCachedSource2是一个带缓存的DataSource,通过调用底层的 DataSource读取和缓存数据。也就是说NuMediaExtractor委托NuCachedSource2,而NuCachedSource2委托HTTPBase , HTTPBase又委托ChromiumHTTPDataSource, ChromiumHTTPDataSource又委托给SfDelegate去完成http连接的发起和关闭,反过来通过注册的回调函数(onConnectionEstablished, onDisconnectComplete等)来获取连接信息。

    根据代码实现经验创建一个MediaExtractor后,再配置资源,然后就可以获取流(文件或网络)的Format数据。

        sp<NuMediaExtractor> extractor = new NuMediaExtractor;
        if (extractor->setDataSource(path) != OK) {
            fprintf(stderr, "unable to instantiate extractor.\n");
            extractor = NULL;
            return 1;
        }
        
        for (size_t i = 0; i < extractor->countTracks(); ++i) {
            sp<AMessage> decode_format;
    
            status_t err = extractor->getTrackFormat(i, &decode_format);
            CHECK_EQ(err, (status_t)OK);
            
            AString mime;
            CHECK(decode_format->findString("mime", &mime));
    
            bool isAudio = !strncasecmp(mime.c_str(), "audio/", 6);
            bool isVideo = !strncasecmp(mime.c_str(), "video/", 6);
            ...
        }
    

    结合之前的代码分析,在NuMediaExtractor::setDataSource中根据path创建一个DataSource的指针后,然后就是通过DataSource实例化 MediaExtractor,以http://为例:

    status_t NuMediaExtractor::setDataSource(
            const char *path, const KeyedVector<String8, String8> *headers) {
        ...
        sp<DataSource> dataSource =
            DataSource::CreateFromURI(path, headers);
        ...
        mImpl = MediaExtractor::Create(dataSource);
    }
    
    sp<DataSource> DataSource::CreateFromURI(
            const char *uri, const KeyedVector<String8, String8> *headers) {
            ...
            sp<HTTPBase> httpSource = HTTPBase::Create();
            ...
            if (httpSource->connect(uri, headers) != OK) {
                return NULL;
            }
            ...
            source = httpSource;
            ...
            if (source == NULL || source->initCheck() != OK) {
              return NULL;
            }
            return source;
    }
    
    
    sp<MediaExtractor> MediaExtractor::Create(
            const sp<DataSource> &source, const char *mime) {
        sp<AMessage> meta;
    
        String8 tmp;
        if (mime == NULL) {
            float confidence;
            if (!source->sniff(&tmp, &confidence, &meta)) {
                ALOGV("FAILED to autodetect media content.");
    
                return NULL;
            }
    
            mime = tmp.string();
            ALOGV("Autodetected media content as '%s' with confidence %.2f",
                 mime, confidence);
        }
    
        bool isDrm = false;
        // DRM MIME type syntax is "drm+type+original" where
        // type is "es_based" or "container_based" and
        // original is the content's cleartext MIME type
        if (!strncmp(mime, "drm+", 4)) {
            const char *originalMime = strchr(mime+4, '+');
            if (originalMime == NULL) {
                // second + not found
                return NULL;
            }
            ++originalMime;
            if (!strncmp(mime, "drm+es_based+", 13)) {
                // DRMExtractor sets container metadata kKeyIsDRM to 1
                return new DRMExtractor(source, originalMime);
            } else if (!strncmp(mime, "drm+container_based+", 20)) {
                mime = originalMime;
                isDrm = true;
            } else {
                return NULL;
            }
        }
    
        MediaExtractor *ret = NULL;
        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_CONTAINER_PR)) {
            // SSPRExtractor for playready
            return new SStreamingExtractor(source);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
            ret = new AACExtractor(source, meta);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADIF)) {
            ret = new ADIFExtractor(source);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_LATM)) {
            ret = new LATMExtractor(source);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_ADTS_PROFILE)) {
            ret = new ADTSExtractor(source);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
            ret = new MPEG2PSExtractor(source);
        } else if(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_WMA)||!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_WMAPRO)){
            ret = new AsfExtractor(source);
        }else if(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_DTSHD)){
            ret = new DtshdExtractor(source);
        } else if(!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AIFF)){
            ret = new AIFFExtractor(source);
        } else if(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_TRUEHD)){
            ret = new THDExtractor(source);
        } 
    #ifdef DOLBY_UDC
          else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_DDP)) {
            ret = new DDPExtractor(source);
        }
    #endif
    
        if (ret != NULL) {
           if (isDrm) {
               ret->setDrmFlag(true);
           } else {
               ret->setDrmFlag(false);
           }
        }
    
        return ret;
    }
    

    DRM是数字版权管理模块,可直接跳过不用分析,核心在于通过source->sniff获取流的元数据,然后根据元数据的描述实例化对应的Extractor,从上面的代码中可以看到Extractor包括但不限于MPEG4Extractor、MP3Extractor、AMRExtractor、AACExtractor、MPEG2TSExtractor等,我们也可以在这个地方创建自定义的Extractor,(分析到这里,我感觉Extractor的设计模式和ExoPlayer有点像了)。

    那么source->sniff到底做了什么?我们以TS流为例:

    void DataSource::RegisterDefaultSniffers() {
        Mutex::Autolock autoLock(gSnifferMutex);
        if (gSniffersRegistered) {
            return;
        }
        RegisterSniffer_l(SniffADTS);
        RegisterSniffer_l(SniffMPEG4);
        RegisterSniffer_l(SniffMatroska);
        RegisterSniffer_l(SniffOgg);
        RegisterSniffer_l(SniffWAV);
        RegisterSniffer_l(SniffFLAC);
        RegisterSniffer_l(SniffAMR);
        RegisterSniffer_l(SniffMPEG2TS);
        RegisterSniffer_l(SniffMP3);
        RegisterSniffer_l(SniffAAC);
        RegisterSniffer_l(SniffADIF);
        RegisterSniffer_l(SniffLATM);
        RegisterSniffer_l(SniffMPEG2PS);
        RegisterSniffer_l(SniffWVM);
        RegisterSniffer_l(SniffAsf);
        RegisterSniffer_l(SniffAIFF);
        RegisterSniffer_l(SniffTHD);
        RegisterSniffer_l(SniffDcahd);
    
        char value[PROPERTY_VALUE_MAX];
        if (property_get("drm.service.enabled", value, NULL)
                && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
            RegisterSniffer_l(SniffDRM);
        }
        gSniffersRegistered = true;
    }
    

    我们看SniffMPEG2TS的实现:

    bool SniffMPEG2TS(
            const sp<DataSource> &source, String8 *mimeType, float *confidence,
            sp<AMessage> *) {
        for (int i = 0; i < 5; ++i) {
            char header;
            if (source->readAt(kTSPacketSize * i, &header, 1) != 1
                    || header != 0x47) {
                return false;
            }
        }
    
        *confidence = 0.1f;
        mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
    
        return true;
    }
    

    分析到这里,我们就知道该如何创建匹配的Extractor了,通过DataSource去读取一个数据包的大小(ts为188字节),校验读取的Buffer(ts包的第一个字节为0x47),匹配校验规则的就可以配置对应的Extractor。这里说的DataSource以http://为例,结合之前的代码分析,其实就是交给了chromium_http去处理(本文就不再深入分析):

    ssize_t ChromiumHTTPDataSource::readAt(off64_t offset, void *data, size_t size) {
        Mutex::Autolock autoLock(mLock);
    
        if (mState != CONNECTED) {
            return INVALID_OPERATION;
        }
        if (offset != mCurrentOffset) {
            AString tmp = mURI;
            KeyedVector<String8, String8> tmpHeaders = mHeaders;
    
            disconnect_l();
    
            status_t err = connect_l(tmp.c_str(), &tmpHeaders, offset);
    
            if (err != OK) {
                return err;
            }
        }
    
        mState = READING;
        int64_t startTimeUs = ALooper::GetNowUs();
    
        mDelegate->initiateRead(data, size);
    
        while (mState == READING) {
            mCondition.wait(mLock);
        }
    
        if (mIOResult < OK) {
            return mIOResult;
        }
    
        if (mState == CONNECTED) {
            int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
    
            // The read operation was successful, mIOResult contains
            // the number of bytes read.
            addBandwidthMeasurement(mIOResult, delayUs);
    
            mCurrentOffset += mIOResult;
            return mIOResult;
        }
    
        return ERROR_IO;
    }
    

    实例化Extractor,以TS为例:

    MPEG2TSExtractor::MPEG2TSExtractor(const sp<DataSource> &source)
        : mDataSource(source),
          mParser(new ATSParser),
          mOffset(0) {
        init();
    }
    
    void MPEG2TSExtractor::init() {
        bool haveAudio = false;
        bool haveVideo = false;
        int numPacketsParsed = 0;
    
        while (feedMore() == OK) {
            ATSParser::SourceType type;
            if (haveAudio && haveVideo) {
                break;
            }
            if (!haveVideo) {
                sp<AnotherPacketSource> impl =
                    (AnotherPacketSource *)mParser->getSource(
                            ATSParser::VIDEO).get();
    
                if (impl != NULL) {
                    haveVideo = true;
                    mSourceImpls.push(impl);
                }
            }
    
            if (!haveAudio) {
                sp<AnotherPacketSource> impl =
                    (AnotherPacketSource *)mParser->getSource(
                            ATSParser::AUDIO).get();
    
                if (impl != NULL) {
                    haveAudio = true;
                    mSourceImpls.push(impl);
                }
            }
    
            if (++numPacketsParsed > 10000) {
                break;
            }
        }
    
        ALOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo);
    }
    
    status_t MPEG2TSExtractor::feedMore() {
        Mutex::Autolock autoLock(mLock);
    
        uint8_t packet[kTSPacketSize];
        ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
    
        if (n < (ssize_t)kTSPacketSize) {
            return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
        }
    
        mOffset += n;
        return mParser->feedTSPacket(packet, kTSPacketSize);
    }
    

    可以看到都是从DataSource::readAt读取指定长度的buffer,然后做相应的处理,那么读取到数据后,通过Extractor获取到video/audio的信息也就不难了。
      实例化Extractor后,就是获取Format信息了,比如getTrackFormat:

    status_t NuMediaExtractor::getTrackFormat(
            size_t index, sp<AMessage> *format) const {
        Mutex::Autolock autoLock(mLock);
    
        *format = NULL;
    
        if (mImpl == NULL) {
            return -EINVAL;
        }
    
        if (index >= mImpl->countTracks()) {
            return -ERANGE;
        }
    
        sp<MetaData> meta = mImpl->getTrackMetaData(index);
        return convertMetaDataToMessage(meta, format);
    }
    
    size_t MPEG2TSExtractor::countTracks() {
        return mSourceImpls.size();
    }
    
    sp<MetaData> MPEG2TSExtractor::getTrackMetaData(
            size_t index, uint32_t flags) {
        return index < mSourceImpls.size()
            ? mSourceImpls.editItemAt(index)->getFormat() : NULL;
    }
    

    从上面的代码可以知道,通过调用MediaExtractor的API,都是通过从DataSource获取数据,然后通过Extractor实例化对相应数据做处理或回调,分析到这里就越是觉得和ExoPlayer的设计模式相同了。

    3.结束语

    关于MediaExtractor的实现分析告一段落,由于之前有过对Exoplayer中Extractor有过分析,所以这里理解起来会好梳理些。如果可以,建议两篇文章一起分析,感谢持续关注!

    相关文章

      网友评论

      本文标题:MediaExtractor源码分析

      本文链接:https://www.haomeiwen.com/subject/nfwyrxtx.html