some fixes for crash when extractor creation fails

- prefetch data for sniffing

- notify error instead of crashing if extractor is NULL

Bug: 16818302

Change-Id: I56ff4996d99ac2811d19d141f7ff7acdd7c1da17
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 32842bb..a18407f 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -37,10 +37,6 @@
 
 NuPlayer::GenericSource::GenericSource(
         const sp<AMessage> &notify,
-        const sp<IMediaHTTPService> &httpService,
-        const char *url,
-        const KeyedVector<String8, String8> *headers,
-        bool isWidevine,
         bool uidValid,
         uid_t uid)
     : Source(notify),
@@ -48,38 +44,41 @@
       mFetchTimedTextDataGeneration(0),
       mDurationUs(0ll),
       mAudioIsVorbis(false),
-      mIsWidevine(isWidevine),
+      mIsWidevine(false),
       mUIDValid(uidValid),
       mUID(uid) {
     DataSource::RegisterDefaultSniffers();
+}
+
+status_t NuPlayer::GenericSource::init(
+        const sp<IMediaHTTPService> &httpService,
+        const char *url,
+        const KeyedVector<String8, String8> *headers) {
+    mIsWidevine = !strncasecmp(url, "widevine://", 11);
+
+    AString sniffedMIME;
 
     sp<DataSource> dataSource =
-        DataSource::CreateFromURI(httpService, url, headers);
-    CHECK(dataSource != NULL);
+        DataSource::CreateFromURI(httpService, url, headers, &sniffedMIME);
 
-    initFromDataSource(dataSource);
+    if (dataSource == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    return initFromDataSource(
+            dataSource, sniffedMIME.empty() ? NULL : sniffedMIME.c_str());
 }
 
-NuPlayer::GenericSource::GenericSource(
-        const sp<AMessage> &notify,
-        int fd, int64_t offset, int64_t length)
-    : Source(notify),
-      mFetchSubtitleDataGeneration(0),
-      mFetchTimedTextDataGeneration(0),
-      mDurationUs(0ll),
-      mAudioIsVorbis(false),
-      mIsWidevine(false),
-      mUIDValid(false),
-      mUID(0) {
-    DataSource::RegisterDefaultSniffers();
-
+status_t NuPlayer::GenericSource::init(
+        int fd, int64_t offset, int64_t length) {
     sp<DataSource> dataSource = new FileSource(dup(fd), offset, length);
 
-    initFromDataSource(dataSource);
+    return initFromDataSource(dataSource, NULL);
 }
 
-void NuPlayer::GenericSource::initFromDataSource(
-        const sp<DataSource> &dataSource) {
+status_t NuPlayer::GenericSource::initFromDataSource(
+        const sp<DataSource> &dataSource,
+        const char* mime) {
     sp<MediaExtractor> extractor;
 
     if (mIsWidevine) {
@@ -93,7 +92,7 @@
                 || strcasecmp(
                     mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
             ALOGE("unsupported widevine mime: %s", mimeType.string());
-            return;
+            return UNKNOWN_ERROR;
         }
 
         sp<WVMExtractor> wvmExtractor = new WVMExtractor(dataSource);
@@ -103,10 +102,12 @@
         }
         extractor = wvmExtractor;
     } else {
-        extractor = MediaExtractor::Create(dataSource);
+        extractor = MediaExtractor::Create(dataSource, mime);
     }
 
-    CHECK(extractor != NULL);
+    if (extractor == NULL) {
+        return UNKNOWN_ERROR;
+    }
 
     sp<MetaData> fileMeta = extractor->getMetaData();
     if (fileMeta != NULL) {
@@ -144,6 +145,9 @@
                 int32_t secure;
                 if (meta->findInt32(kKeyRequiresSecureBuffers, &secure) && secure) {
                     mIsWidevine = true;
+                    if (mUIDValid) {
+                        extractor->setUID(mUID);
+                    }
                 }
             }
         }
@@ -158,6 +162,8 @@
             }
         }
     }
+
+    return OK;
 }
 
 status_t NuPlayer::GenericSource::setBuffers(bool audio, Vector<MediaBuffer *> &buffers) {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 3c5f55c..76e628b 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -34,18 +34,14 @@
 class MediaBuffer;
 
 struct NuPlayer::GenericSource : public NuPlayer::Source {
-    GenericSource(
-            const sp<AMessage> &notify,
+    GenericSource(const sp<AMessage> &notify, bool uidValid, uid_t uid);
+
+    status_t init(
             const sp<IMediaHTTPService> &httpService,
             const char *url,
-            const KeyedVector<String8, String8> *headers,
-            bool isWidevine = false,
-            bool uidValid = false,
-            uid_t uid = 0);
+            const KeyedVector<String8, String8> *headers);
 
-    GenericSource(
-            const sp<AMessage> &notify,
-            int fd, int64_t offset, int64_t length);
+    status_t init(int fd, int64_t offset, int64_t length);
 
     virtual void prepareAsync();
 
@@ -101,7 +97,9 @@
     bool mUIDValid;
     uid_t mUID;
 
-    void initFromDataSource(const sp<DataSource> &dataSource);
+    status_t initFromDataSource(
+            const sp<DataSource> &dataSource,
+            const char *mime);
 
     void fetchTextData(
             uint32_t what, media_track_type type,
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 17038a4..d56b1f0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -207,6 +207,7 @@
         const sp<IMediaHTTPService> &httpService,
         const char *url,
         const KeyedVector<String8, String8> *headers) {
+
     sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
     size_t len = strlen(url);
 
@@ -224,16 +225,21 @@
                     || strstr(url, ".sdp?"))) {
         source = new RTSPSource(
                 notify, httpService, url, headers, mUIDValid, mUID, true);
-    } else if ((!strncasecmp(url, "widevine://", 11))) {
-        source = new GenericSource(notify, httpService, url, headers,
-                true /* isWidevine */, mUIDValid, mUID);
-        // Don't set FLAG_SECURE on mSourceFlags here, the correct flags
-        // will be updated in Source::kWhatFlagsChanged handler when
-        // GenericSource is prepared.
     } else {
-        source = new GenericSource(notify, httpService, url, headers);
-    }
+        sp<GenericSource> genericSource =
+                new GenericSource(notify, mUIDValid, mUID);
+        // Don't set FLAG_SECURE on mSourceFlags here for widevine.
+        // The correct flags will be updated in Source::kWhatFlagsChanged
+        // handler when  GenericSource is prepared.
 
+        status_t err = genericSource->init(httpService, url, headers);
+
+        if (err == OK) {
+            source = genericSource;
+        } else {
+            ALOGE("Failed to initialize generic source!");
+        }
+    }
     msg->setObject("source", source);
     msg->post();
 }
@@ -243,7 +249,16 @@
 
     sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
 
-    sp<Source> source = new GenericSource(notify, fd, offset, length);
+    sp<GenericSource> source =
+            new GenericSource(notify, mUIDValid, mUID);
+
+    status_t err = source->init(fd, offset, length);
+
+    if (err != OK) {
+        ALOGE("Failed to initialize generic source!");
+        source = NULL;
+    }
+
     msg->setObject("source", source);
     msg->post();
 }
@@ -352,17 +367,20 @@
 
             CHECK(mSource == NULL);
 
+            status_t err = OK;
             sp<RefBase> obj;
             CHECK(msg->findObject("source", &obj));
-
-            mSource = static_cast<Source *>(obj.get());
-
-            looper()->registerHandler(mSource);
+            if (obj != NULL) {
+                mSource = static_cast<Source *>(obj.get());
+                looper()->registerHandler(mSource);
+            } else {
+                err = UNKNOWN_ERROR;
+            }
 
             CHECK(mDriver != NULL);
             sp<NuPlayerDriver> driver = mDriver.promote();
             if (driver != NULL) {
-                driver->notifySetDataSourceCompleted(OK);
+                driver->notifySetDataSourceCompleted(err);
             }
             break;
         }
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 6e0f37a..908cdca 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DataSource"
 
 #include "include/AMRExtractor.h"
 
@@ -33,6 +35,7 @@
 
 #include <media/IMediaHTTPConnection.h>
 #include <media/IMediaHTTPService.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/DataURISource.h>
@@ -182,7 +185,12 @@
 sp<DataSource> DataSource::CreateFromURI(
         const sp<IMediaHTTPService> &httpService,
         const char *uri,
-        const KeyedVector<String8, String8> *headers) {
+        const KeyedVector<String8, String8> *headers,
+        AString *sniffedMIME) {
+    if (sniffedMIME != NULL) {
+        *sniffedMIME = "";
+    }
+
     bool isWidevine = !strncasecmp("widevine://", uri, 11);
 
     sp<DataSource> source;
@@ -202,6 +210,7 @@
         }
 
         if (httpSource->connect(uri, headers) != OK) {
+            ALOGE("Failed to connect http source!");
             return NULL;
         }
 
@@ -214,9 +223,76 @@
                         &copy, &cacheConfig, &disconnectAtHighwatermark);
             }
 
-            source = new NuCachedSource2(
+            sp<NuCachedSource2> cachedSource = new NuCachedSource2(
                     httpSource,
                     cacheConfig.isEmpty() ? NULL : cacheConfig.string());
+
+            String8 contentType = httpSource->getMIMEType();
+
+            if (strncasecmp(contentType.string(), "audio/", 6)) {
+                // We're not doing this for streams that appear to be audio-only
+                // streams to ensure that even low bandwidth streams start
+                // playing back fairly instantly.
+
+                // We're going to prefill the cache before trying to instantiate
+                // the extractor below, as the latter is an operation that otherwise
+                // could block on the datasource for a significant amount of time.
+                // During that time we'd be unable to abort the preparation phase
+                // without this prefill.
+
+                // Initially make sure we have at least 192 KB for the sniff
+                // to complete without blocking.
+                static const size_t kMinBytesForSniffing = 192 * 1024;
+
+                off64_t metaDataSize = -1ll;
+                for (;;) {
+                    status_t finalStatus;
+                    size_t cachedDataRemaining =
+                            cachedSource->approxDataRemaining(&finalStatus);
+
+                    if (finalStatus != OK || (metaDataSize >= 0
+                            && (off64_t)cachedDataRemaining >= metaDataSize)) {
+                        ALOGV("stop caching, status %d, "
+                                "metaDataSize %lld, cachedDataRemaining %zu",
+                                finalStatus, metaDataSize, cachedDataRemaining);
+                        break;
+                    }
+
+                    ALOGV("now cached %zu bytes of data", cachedDataRemaining);
+
+                    if (metaDataSize < 0
+                            && cachedDataRemaining >= kMinBytesForSniffing) {
+                        String8 tmp;
+                        float confidence;
+                        sp<AMessage> meta;
+                        if (!cachedSource->sniff(&tmp, &confidence, &meta)) {
+                            return NULL;
+                        }
+
+                        // We successfully identified the file's extractor to
+                        // be, remember this mime type so we don't have to
+                        // sniff it again when we call MediaExtractor::Create()
+                        if (sniffedMIME != NULL) {
+                            *sniffedMIME = tmp.string();
+                        }
+
+                        if (meta == NULL
+                                || !meta->findInt64("meta-data-size",
+                                     reinterpret_cast<int64_t*>(&metaDataSize))) {
+                            metaDataSize = kDefaultMetaSize;
+                        }
+
+                        if (metaDataSize < 0ll) {
+                            ALOGE("invalid metaDataSize = %lld bytes", metaDataSize);
+                            return NULL;
+                        }
+                    }
+
+                    usleep(200000);
+                }
+            }
+
+            source = cachedSource;
         } else {
             // We do not want that prefetching, caching, datasource wrapper
             // in the widevine:// case.
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
index 8e62946..ab7e8b8 100644
--- a/media/libstagefright/include/WVMExtractor.h
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -49,6 +49,7 @@
     virtual sp<MediaSource> getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
     virtual sp<MetaData> getMetaData();
+    virtual void setUID(uid_t uid);
 
     // Return the amount of data cached from the current
     // playback positiion (in us).
@@ -74,8 +75,6 @@
     // codec.
     void setCryptoPluginMode(bool cryptoPluginMode);
 
-    void setUID(uid_t uid);
-
     static bool getVendorLibHandle();
 
     status_t getError();