MediaExtractor源碼分析

1.介紹:

MediaExtractor是Android中音視頻解復(fù)用器甫煞,可以將video和audio分離出來罕拂。android kitkat版本上支持本地和http的音視頻解復(fù)用溃卡,打算按照這個設(shè)計模式擴展電信IPTV的RTSP的音視頻解復(fù)用。

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的類型創(chuàng)建出匹配的資源:

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;
}

假如當(dāng)前的path傳入的是http://的網(wǎng)絡(luò)資源串磕蒲,那么就會實例化一個HTTPBase對象.首先看一下HTTPBase的聲明:

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

    HTTPBase();
    ...
}

從代碼中可以看到稠屠,通過createChromiumHTTPDataSource來創(chuàng)建了一個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模塊中,調(diào)用代碼:
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的實現(xiàn)嘉抒,可以去看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已經(jīng)實現(xiàn)很多關(guān)于http袍暴、disk_cache些侍、proxy、socket等網(wǎng)絡(luò)處理容诬。本篇文章不涉及該模塊內(nèi)容娩梨。
connect對http請求做了初始化處理,然后委托給NuCachedSource2

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

NuCachedSource2是一個帶緩存的DataSource览徒,通過調(diào)用底層的 DataSource讀取和緩存數(shù)據(jù)狈定。也就是說NuMediaExtractor委托NuCachedSource2,而NuCachedSource2委托HTTPBase 习蓬, HTTPBase又委托ChromiumHTTPDataSource纽什, ChromiumHTTPDataSource又委托給SfDelegate去完成http連接的發(fā)起和關(guān)閉,反過來通過注冊的回調(diào)函數(shù)(onConnectionEstablished, onDisconnectComplete等)來獲取連接信息躲叼。

根據(jù)代碼實現(xiàn)經(jīng)驗創(chuàng)建一個MediaExtractor后芦缰,再配置資源,然后就可以獲取流(文件或網(wǎng)絡(luò))的Format數(shù)據(jù)枫慷。

    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);
        ...
    }

結(jié)合之前的代碼分析让蕾,在NuMediaExtractor::setDataSource中根據(jù)path創(chuàng)建一個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是數(shù)字版權(quán)管理模塊探孝,可直接跳過不用分析,核心在于通過source->sniff獲取流的元數(shù)據(jù)誉裆,然后根據(jù)元數(shù)據(jù)的描述實例化對應(yīng)的Extractor顿颅,從上面的代碼中可以看到Extractor包括但不限于MPEG4Extractor、MP3Extractor足丢、AMRExtractor粱腻、AACExtractor、MPEG2TSExtractor等斩跌,我們也可以在這個地方創(chuàng)建自定義的Extractor绍些,(分析到這里,我感覺Extractor的設(shè)計模式和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的實現(xiàn):

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;
}

分析到這里,我們就知道該如何創(chuàng)建匹配的Extractor了,通過DataSource去讀取一個數(shù)據(jù)包的大小(ts為188字節(jié))萝快,校驗讀取的Buffer(ts包的第一個字節(jié)為0x47)锻霎,匹配校驗規(guī)則的就可以配置對應(yīng)的Extractor。這里說的DataSource以http://為例揪漩,結(jié)合之前的代碼分析旋恼,其實就是交給了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奄容,然后做相應(yīng)的處理冰更,那么讀取到數(shù)據(jù)后,通過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;
}

從上面的代碼可以知道戈盈,通過調(diào)用MediaExtractor的API奠衔,都是通過從DataSource獲取數(shù)據(jù),然后通過Extractor實例化對相應(yīng)數(shù)據(jù)做處理或回調(diào)塘娶,分析到這里就越是覺得和ExoPlayer的設(shè)計模式相同了归斤。

3.結(jié)束語

關(guān)于MediaExtractor的實現(xiàn)分析告一段落,由于之前有過對Exoplayer中Extractor有過分析刁岸,所以這里理解起來會好梳理些脏里。如果可以,建議兩篇文章一起分析虹曙,感謝持續(xù)關(guān)注迫横!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市酝碳,隨后出現(xiàn)的幾起案子员淫,更是在濱河造成了極大的恐慌,老刑警劉巖击敌,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拴事,居然都是意外死亡沃斤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門刃宵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衡瓶,“玉大人,你說我怎么就攤上這事牲证∠耄” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長十厢。 經(jīng)常有香客問我等太,道長,這世上最難降的妖魔是什么蛮放? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任缩抡,我火速辦了婚禮,結(jié)果婚禮上包颁,老公的妹妹穿的比我還像新娘瞻想。我一直安慰自己,他們只是感情好娩嚼,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布蘑险。 她就那樣靜靜地躺著,像睡著了一般岳悟。 火紅的嫁衣襯著肌膚如雪佃迄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天竿音,我揣著相機與錄音和屎,去河邊找鬼。 笑死春瞬,一個胖子當(dāng)著我的面吹牛柴信,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宽气,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼随常,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了萄涯?” 一聲冷哼從身側(cè)響起绪氛,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涝影,沒想到半個月后枣察,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡燃逻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年序目,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伯襟。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡猿涨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出姆怪,到底是詐尸還是另有隱情叛赚,我是刑警寧澤澡绩,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站俺附,受9級特大地震影響肥卡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昙读,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一召调、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛮浑,春花似錦唠叛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蕴掏,卻和暖如春障般,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盛杰。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工挽荡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人即供。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓定拟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親逗嫡。 傳聞我的和親對象是個殘疾皇子青自,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容