MediaExtractor源码分析

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有过分析,所以这里理解起来会好梳理些。如果可以,建议两篇文章一起分析,感谢持续关注!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容