OSDN Git Service

stagefright-plugins: Fix crash if nal size is not 3/4
[android-x86/external-stagefright-plugins.git] / extractor / FFmpegExtractor.cpp
index 5339e82..1a27f68 100644 (file)
@@ -47,8 +47,8 @@
 
 #include "FFmpegExtractor.h"
 
-#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
-#define MIN_AUDIOQ_SIZE (20 * 16 * 1024)
+#define MAX_QUEUE_SIZE (40 * 1024 * 1024)
+#define MIN_AUDIOQ_SIZE (2 * 1024 * 1024)
 #define MIN_FRAMES 5
 #define EXTRACTOR_MAX_PROBE_PACKETS 200
 #define FF_MAX_EXTRADATA_SIZE ((1 << 28) - FF_INPUT_BUFFER_PADDING_SIZE)
@@ -116,7 +116,8 @@ FFmpegExtractor::FFmpegExtractor(const sp<DataSource> &source, const sp<AMessage
       mInitCheck(NO_INIT),
       mFFmpegInited(false),
       mFormatCtx(NULL),
-      mReaderThreadStarted(false) {
+      mReaderThreadStarted(false),
+      mParsedMetadata(false) {
     ALOGV("FFmpegExtractor::FFmpegExtractor");
 
     fetchStuffsFromSniffedMeta(meta);
@@ -182,10 +183,10 @@ sp<MetaData> FFmpegExtractor::getTrackMetaData(size_t index, uint32_t flags __un
     }
 
     /* Quick and dirty, just get a frame 1/4 in */
-    if (mTracks.itemAt(index).mIndex == mVideoStreamIdx) {
-        int64_t thumb_ts = av_rescale_q((mFormatCtx->streams[mVideoStreamIdx]->duration / 4),
-                mFormatCtx->streams[mVideoStreamIdx]->time_base, AV_TIME_BASE_Q);
-        mTracks.itemAt(index).mMeta->setInt64(kKeyThumbnailTime, thumb_ts);
+    if (mTracks.itemAt(index).mIndex == mVideoStreamIdx &&
+            mFormatCtx->duration != AV_NOPTS_VALUE) {
+        mTracks.itemAt(index).mMeta->setInt64(
+                kKeyThumbnailTime, mFormatCtx->duration / 4);
     }
 
     return mTracks.itemAt(index).mMeta;
@@ -198,6 +199,11 @@ sp<MetaData> FFmpegExtractor::getMetaData() {
         return NULL;
     }
 
+    if (!mParsedMetadata) {
+        parseMetadataTags(mFormatCtx, mMeta);
+        mParsedMetadata = true;
+    }
+
     return mMeta;
 }
 
@@ -424,6 +430,23 @@ sp<MetaData> FFmpegExtractor::setVideoFormat(AVStream *stream)
     }
 
     if (meta != NULL) {
+        // rotation
+        double theta = get_rotation(stream);
+        int rotationDegrees = 0;
+
+        if (fabs(theta - 90) < 1.0) {
+            rotationDegrees = 90;
+        } else if (fabs(theta - 180) < 1.0) {
+            rotationDegrees = 180;
+        } else if (fabs(theta - 270) < 1.0) {
+            rotationDegrees = 270;
+        }
+        if (rotationDegrees != 0) {
+            meta->setInt32(kKeyRotation, rotationDegrees);
+        }
+    }
+
+    if (meta != NULL) {
         float aspect_ratio;
         int width, height;
 
@@ -454,6 +477,7 @@ sp<MetaData> FFmpegExtractor::setVideoFormat(AVStream *stream)
         if (avctx->bit_rate > 0) {
             meta->setInt32(kKeyBitRate, avctx->bit_rate);
         }
+        meta->setCString('ffmt', findMatchingContainer(mFormatCtx->iformat->name));
         setDurationMetaData(stream, meta);
     }
 
@@ -540,6 +564,7 @@ sp<MetaData> FFmpegExtractor::setAudioFormat(AVStream *stream)
         meta->setInt32(kKeyBlockAlign, avctx->block_align);
         meta->setInt32(kKeySampleFormat, avctx->sample_fmt);
         meta->setInt32('pfmt', to_android_audio_format(avctx->sample_fmt));
+        meta->setCString('ffmt', findMatchingContainer(mFormatCtx->iformat->name));
         setDurationMetaData(stream, meta);
     }
 
@@ -587,11 +612,12 @@ int FFmpegExtractor::stream_component_open(int stream_index)
     if (!supported) {
         ALOGE("unsupport the codec(%s)", avcodec_get_name(avctx->codec_id));
         return -1;
-    } else if (mFormatCtx->streams[stream_index]->disposition & AV_DISPOSITION_ATTACHED_PIC) {
+    } else if ((mFormatCtx->streams[stream_index]->disposition & AV_DISPOSITION_ATTACHED_PIC) ||
+                avctx->codec_tag == MKTAG('j', 'p', 'e', 'g')) {
         ALOGD("not opening attached picture(%s)", avcodec_get_name(avctx->codec_id));
         return -1;
     }
-    ALOGI("support the codec(%s)", avcodec_get_name(avctx->codec_id));
+    ALOGI("support the codec(%s) disposition(%x)", avcodec_get_name(avctx->codec_id), mFormatCtx->streams[stream_index]->disposition);
 
     unsigned streamType;
     for (size_t i = 0; i < mTracks.size(); ++i) {
@@ -796,7 +822,7 @@ int FFmpegExtractor::stream_seek(int64_t pos, enum AVMediaType media_type,
 
     switch (mode) {
         case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC:
-            mSeekMin = INT64_MIN;
+            mSeekMin = 0;
             mSeekMax = mSeekPos;
             break;
         case MediaSource::ReadOptions::SEEK_NEXT_SYNC:
@@ -804,11 +830,11 @@ int FFmpegExtractor::stream_seek(int64_t pos, enum AVMediaType media_type,
             mSeekMax = INT64_MAX;
             break;
         case MediaSource::ReadOptions::SEEK_CLOSEST_SYNC:
-            mSeekMin = INT64_MIN;
+            mSeekMin = 0;
             mSeekMax = INT64_MAX;
             break;
         case MediaSource::ReadOptions::SEEK_CLOSEST:
-            mSeekMin = INT64_MIN;
+            mSeekMin = 0;
             mSeekMax = mSeekPos;
             break;
         default:
@@ -1175,6 +1201,15 @@ void FFmpegExtractor::readerEntry() {
             ALOGV("readerEntry, full(wtf!!!), mVideoQ.size: %d, mVideoQ.nb_packets: %d, mAudioQ.size: %d, mAudioQ.nb_packets: %d",
                     mVideoQ.size, mVideoQ.nb_packets, mAudioQ.size, mAudioQ.nb_packets);
 #endif
+            // avoid deadlock, the audio and video data in the video is offset too large.
+            if ((mAudioQ.size == 0) && mAudioQ.wait_for_data) {
+                ALOGE("abort audio queue, since offset to video data too large");
+                packet_queue_abort(&mAudioQ);
+            }
+            if ((mVideoQ.size == 0) && mVideoQ.wait_for_data) {
+                ALOGE("abort video queue, since offset to audio data too large");
+                packet_queue_abort(&mVideoQ);
+            }
             /* wait 10 ms */
             mExtractorMutex.lock();
             mCondition.waitRelative(mExtractorMutex, milliseconds(10));
@@ -1203,7 +1238,8 @@ void FFmpegExtractor::readerEntry() {
         if (ret < 0) {
             mEOF = true;
             eof = true;
-            if (mFormatCtx->pb && mFormatCtx->pb->error) {
+            if (mFormatCtx->pb && mFormatCtx->pb->error &&
+                    mFormatCtx->pb->error != ERROR_END_OF_STREAM) {
                 ALOGE("mFormatCtx->pb->error: %d", mFormatCtx->pb->error);
                 break;
             }
@@ -1233,7 +1269,7 @@ void FFmpegExtractor::readerEntry() {
                     memcpy(avctx->extradata, pkt->data, avctx->extradata_size);
                     memset(avctx->extradata + i, 0, FF_INPUT_BUFFER_PADDING_SIZE);
                 } else {
-                    av_free_packet(pkt);
+                    av_packet_unref(pkt);
                     continue;
                 }
 
@@ -1254,7 +1290,7 @@ void FFmpegExtractor::readerEntry() {
                                    pkt->data, pkt->size, pkt->flags & AV_PKT_FLAG_KEY);
 
                 if (ret < 0 ||!outbuf_size) {
-                    av_free_packet(pkt);
+                    av_packet_unref(pkt);
                     continue;
                 }
                 if (outbuf && outbuf != pkt->data) {
@@ -1264,7 +1300,7 @@ void FFmpegExtractor::readerEntry() {
             }
             if (mDefersToCreateAudioTrack) {
                 if (avctx->extradata_size <= 0) {
-                    av_free_packet(pkt);
+                    av_packet_unref(pkt);
                     continue;
                 }
                 stream_component_open(mAudioStreamIdx);
@@ -1281,7 +1317,7 @@ void FFmpegExtractor::readerEntry() {
         } else if (pkt->stream_index == mVideoStreamIdx) {
             packet_queue_put(&mVideoQ, pkt);
         } else {
-            av_free_packet(pkt);
+            av_packet_unref(pkt);
         }
     }
 
@@ -1332,7 +1368,13 @@ FFmpegSource::FFmpegSource(
 
             mNal2AnnexB = true;
         } else if (avctx->codec_id == AV_CODEC_ID_HEVC
-                && avctx->extradata_size > 0) {
+                && avctx->extradata_size > 3
+                && (avctx->extradata[0] || avctx->extradata[1] ||
+                    avctx->extradata[2] > 1)) {
+            /* It seems the extradata is encoded as hvcC format.
+             * Temporarily, we support configurationVersion==0 until 14496-15 3rd
+             * is finalized. When finalized, configurationVersion will be 1 and we
+             * can recognize hvcC by checking if avctx->extradata[0]==1 or not. */
             mIsHEVC = true;
 
             uint32_t type;
@@ -1352,6 +1394,7 @@ FFmpegSource::FFmpegSource(
 
             mNal2AnnexB = true;
         }
+
     }
 
     mMediaType = mStream->codec->codec_type;
@@ -1364,7 +1407,7 @@ FFmpegSource::~FFmpegSource() {
     mExtractor = NULL;
 }
 
-status_t FFmpegSource::start(MetaData *params __unused) {
+status_t FFmpegSource::start(MetaData * /* params */) {
     ALOGV("FFmpegSource::start %s",
             av_get_media_type_string(mMediaType));
     return OK;
@@ -1393,6 +1436,7 @@ status_t FFmpegSource::read(
     int64_t timeUs = AV_NOPTS_VALUE;
     int key = 0;
     status_t status = OK;
+    int max_negative_time_frame = 100;
 
     int64_t startTimeUs = mStream->start_time == AV_NOPTS_VALUE ? 0 :
         av_rescale_q(mStream->start_time, mStream->time_base, AV_TIME_BASE_Q);
@@ -1417,7 +1461,7 @@ retry:
 
     if (seeking) {
         if (pkt.data != mQueue->flush_pkt.data) {
-            av_free_packet(&pkt);
+            av_packet_unref(&pkt);
             goto retry;
         } else {
             seeking = false;
@@ -1429,12 +1473,12 @@ retry:
 
     if (pkt.data == mQueue->flush_pkt.data) {
         ALOGV("read %s flush pkt", av_get_media_type_string(mMediaType));
-        av_free_packet(&pkt);
+        av_packet_unref(&pkt);
         mFirstKeyPktTimestamp = AV_NOPTS_VALUE;
         goto retry;
     } else if (pkt.data == NULL && pkt.size == 0) {
         ALOGD("read %s eos pkt", av_get_media_type_string(mMediaType));
-        av_free_packet(&pkt);
+        av_packet_unref(&pkt);
         mExtractor->reachedEOS(mMediaType);
         return ERROR_END_OF_STREAM;
     }
@@ -1445,7 +1489,7 @@ retry:
     if (waitKeyPkt) {
         if (!key) {
             ALOGV("drop the non-key packet");
-            av_free_packet(&pkt);
+            av_packet_unref(&pkt);
             goto retry;
         } else {
             ALOGV("~~~~~~ got the key packet");
@@ -1465,7 +1509,13 @@ retry:
     //copy data
     if ((mIsAVC || mIsHEVC) && mNal2AnnexB) {
         /* This only works for NAL sizes 3-4 */
-        CHECK(mNALLengthSize == 3 || mNALLengthSize == 4);
+        if ((mNALLengthSize != 3) && (mNALLengthSize != 4)) {
+            ALOGE("cannot use convertNal2AnnexB, nal size: %d", mNALLengthSize);
+            mediaBuffer->release();
+            mediaBuffer = NULL;
+            av_packet_unref(&pkt);
+            return ERROR_MALFORMED;
+        }
 
         uint8_t *dst = (uint8_t *)mediaBuffer->data();
         /* Convert H.264 NAL format to annex b */
@@ -1474,7 +1524,7 @@ retry:
             ALOGE("convertNal2AnnexB failed");
             mediaBuffer->release();
             mediaBuffer = NULL;
-            av_free_packet(&pkt);
+            av_packet_unref(&pkt);
             return ERROR_MALFORMED;
         }
     } else {
@@ -1486,6 +1536,26 @@ retry:
     else
         timeUs = SF_NOPTS_VALUE; //FIXME AV_NOPTS_VALUE is negative, but stagefright need positive
 
+    // Negative timestamp will cause crash for media_server
+    // in OMXCodec.cpp CHECK(lastBufferTimeUs >= 0).
+    // And we should not get negative timestamp
+    if (timeUs < 0) {
+        ALOGE("negative timestamp encounter: time: %" PRId64
+               " startTimeUs: %" PRId64
+               " packet dts: %" PRId64
+               " packet pts: %" PRId64
+               , timeUs, startTimeUs, pkt.dts, pkt.pts);
+        mediaBuffer->release();
+        mediaBuffer = NULL;
+        av_packet_unref(&pkt);
+        if (max_negative_time_frame-- > 0) {
+            goto retry;
+        } else {
+            ALOGE("too many negative timestamp packets, abort decoding");
+            return ERROR_MALFORMED;
+        }
+    }
+
     // predict the next PTS to use for exact-frame seek below
     int64_t nextPTS = AV_NOPTS_VALUE;
     if (mLastPTS != AV_NOPTS_VALUE && timeUs > mLastPTS) {
@@ -1526,7 +1596,7 @@ retry:
 
     *buffer = mediaBuffer;
 
-    av_free_packet(&pkt);
+    av_packet_unref(&pkt);
 
     return OK;
 }
@@ -1575,6 +1645,10 @@ static AVCodecContext* getCodecContext(AVFormatContext *ic, AVMediaType codec_ty
         }
 
         avctx = ic->streams[idx]->codec;
+        if (avctx->codec_tag == MKTAG('j', 'p', 'e', 'g')) {
+            // Sometimes the disposition isn't set
+            continue;
+        }
         if (avctx->codec_type == codec_type) {
             return avctx;
         }
@@ -1651,11 +1725,12 @@ static bool isCodecSupportedByStagefright(enum AVCodecID codec_id)
     return supported;
 }
 
-static void adjustMPEG4Confidence(AVFormatContext *ic, float *confidence)
+static void adjustMPEG4Confidence(AVFormatContext *ic, float *confidence, bool isStreaming)
 {
     AVDictionary *tags = NULL;
     AVDictionaryEntry *tag = NULL;
     enum AVCodecID codec_id = AV_CODEC_ID_NONE;
+    bool is_mov = false;
 
     //1. check codec id
     codec_id = getCodecId(ic, AVMEDIA_TYPE_VIDEO);
@@ -1688,22 +1763,56 @@ static void adjustMPEG4Confidence(AVFormatContext *ic, float *confidence)
     //NOTE: You can use command to show these tags,
     //e.g. "ffprobe -show_format 2012.mov"
     tag = av_dict_get(tags, "major_brand", NULL, 0);
-    if (!tag) {
-        return;
+    if (tag) {
+        ALOGV("major_brand tag is:%s", tag->value);
+
+        //when MEDIA_MIMETYPE_CONTAINER_MPEG4
+        //WTF, MPEG4Extractor.cpp can not extractor mov format
+        //NOTE: isCompatibleBrand(MPEG4Extractor.cpp)
+        //  Won't promise that the following file types can be played.
+        //  Just give these file types a chance.
+        //  FOURCC('q', 't', ' ', ' '),  // Apple's QuickTime
+        //So......
+        if (!strcmp(tag->value, "qt  ")) {
+            ALOGI("[mp4]format is mov, confidence should be larger than mpeg4");
+            *confidence = 0.41f;
+            is_mov = true;
+        }
+    }
+    if (isStreaming && !is_mov) {
+        ALOGI("support container: video/mp4, but it is caching data source, "
+                "Don't use ffmpegextractor");
+        *confidence = 0; // MP4 and streaming, use AOSP
     }
+}
 
-    ALOGV("major_brand tag is:%s", tag->value);
+static void adjustMPEG2PSConfidence(AVFormatContext *ic, float *confidence)
+{
+    enum AVCodecID codec_id = AV_CODEC_ID_NONE;
 
-    //when MEDIA_MIMETYPE_CONTAINER_MPEG4
-    //WTF, MPEG4Extractor.cpp can not extractor mov format
-    //NOTE: isCompatibleBrand(MPEG4Extractor.cpp)
-    //  Won't promise that the following file types can be played.
-    //  Just give these file types a chance.
-    //  FOURCC('q', 't', ' ', ' '),  // Apple's QuickTime
-    //So......
-    if (!strcmp(tag->value, "qt  ")) {
-        ALOGI("[mp4]format is mov, confidence should be larger than mpeg4");
-        *confidence = 0.41f;
+    codec_id = getCodecId(ic, AVMEDIA_TYPE_VIDEO);
+    if (codec_id != AV_CODEC_ID_NONE
+            && codec_id != AV_CODEC_ID_H264
+            && codec_id != AV_CODEC_ID_MPEG4
+            && codec_id != AV_CODEC_ID_MPEG1VIDEO
+            && codec_id != AV_CODEC_ID_MPEG2VIDEO) {
+        //the MEDIA_MIMETYPE_CONTAINER_MPEG2TS of confidence is 0.25f
+        ALOGI("[mpeg2ps]video codec(%s), confidence should be larger than MPEG2PSExtractor",
+                avcodec_get_name(codec_id));
+        *confidence = 0.26f;
+    }
+
+    codec_id = getCodecId(ic, AVMEDIA_TYPE_AUDIO);
+    if (codec_id != AV_CODEC_ID_NONE
+            && codec_id != AV_CODEC_ID_AAC
+            && codec_id != AV_CODEC_ID_PCM_S16LE
+            && codec_id != AV_CODEC_ID_PCM_S24LE
+            && codec_id != AV_CODEC_ID_MP1
+            && codec_id != AV_CODEC_ID_MP2
+            && codec_id != AV_CODEC_ID_MP3) {
+        ALOGI("[mpeg2ps]audio codec(%s), confidence should be larger than MPEG2PSExtractor",
+                avcodec_get_name(codec_id));
+        *confidence = 0.26f;
     }
 }
 
@@ -1791,13 +1900,15 @@ static void adjustCodecConfidence(AVFormatContext *ic, float *confidence)
 
 //TODO need more checks
 static void adjustConfidenceIfNeeded(const char *mime,
-        AVFormatContext *ic, float *confidence)
+        AVFormatContext *ic, float *confidence, bool isStreaming)
 {
     //1. check mime
     if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)) {
-        adjustMPEG4Confidence(ic, confidence);
+        adjustMPEG4Confidence(ic, confidence, isStreaming);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
         adjustMPEG2TSConfidence(ic, confidence);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
+        adjustMPEG2PSConfidence(ic, confidence);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
         adjustMKVConfidence(ic, confidence);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_DIVX)) {
@@ -1806,10 +1917,6 @@ static void adjustConfidenceIfNeeded(const char *mime,
         //todo here
     }
 
-    if (*confidence > 0.08) {
-        return;
-    }
-
     //2. check codec
     adjustCodecConfidence(ic, confidence);
 }
@@ -1886,13 +1993,6 @@ static const char *findMatchingContainer(const char *name)
     const char *container = NULL;
 #endif
 
-    ALOGV("list the formats suppoted by ffmpeg: ");
-    ALOGV("========================================");
-    for (i = 0; i < NELEM(FILE_FORMATS); ++i) {
-        ALOGV("format_names[%02d]: %s", i, FILE_FORMATS[i].format);
-    }
-    ALOGV("========================================");
-
     for (i = 0; i < NELEM(FILE_FORMATS); ++i) {
         int len = strlen(FILE_FORMATS[i].format);
         if (!strncasecmp(name, FILE_FORMATS[i].format, len)) {
@@ -1904,15 +2004,17 @@ static const char *findMatchingContainer(const char *name)
     return container;
 }
 
-static const char *SniffFFMPEGCommon(const char *url, float *confidence, bool fastMPEG4)
+static const char *SniffFFMPEGCommon(const char *url, float *confidence, bool isStreaming)
 {
     int err = 0;
     size_t i = 0;
     size_t nb_streams = 0;
+    int64_t timeNow = 0;
     const char *container = NULL;
     AVFormatContext *ic = NULL;
     AVDictionary *codec_opts = NULL;
     AVDictionary **opts = NULL;
+    bool needProbe = false;
 
     status_t status = initFFmpeg();
     if (status != OK) {
@@ -1927,6 +2029,11 @@ static const char *SniffFFMPEGCommon(const char *url, float *confidence, bool fa
         goto fail;
     }
 
+    // Don't download more than a meg
+    ic->probesize = 1024 * 1024;
+
+    timeNow = ALooper::GetNowUs();
+
     err = avformat_open_input(&ic, url, NULL, NULL);
 
     if (err < 0) {
@@ -1934,37 +2041,55 @@ static const char *SniffFFMPEGCommon(const char *url, float *confidence, bool fa
         goto fail;
     }
 
-    if (ic->iformat != NULL && ic->iformat->name != NULL &&
-        findMatchingContainer(ic->iformat->name) != NULL &&
-        !strcasecmp(findMatchingContainer(ic->iformat->name),
-        MEDIA_MIMETYPE_CONTAINER_MPEG4)) {
-        if (fastMPEG4) {
-            container = findMatchingContainer(ic->iformat->name);
-            goto fail;
-        }
+    if (ic->iformat != NULL && ic->iformat->name != NULL) {
+        container = findMatchingContainer(ic->iformat->name);
     }
 
-    opts = setup_find_stream_info_opts(ic, codec_opts);
-    nb_streams = ic->nb_streams;
-    err = avformat_find_stream_info(ic, opts);
-    if (err < 0) {
-        ALOGE("%s: could not find stream info, err:%s", url, av_err2str(err));
-        goto fail;
-    }
-    for (i = 0; i < nb_streams; i++) {
-        av_dict_free(&opts[i]);
+    ALOGV("opened, nb_streams: %d container: %s delay: %.2f ms", ic->nb_streams, container,
+            ((float)ALooper::GetNowUs() - timeNow) / 1000);
+
+    // Only probe if absolutely necessary. For formats with headers, avformat_open_input will
+    // figure out the components.
+    for (unsigned int i = 0; i < ic->nb_streams; i++) {
+        AVStream* stream = ic->streams[i];
+        if (!stream->codec || !stream->codec->codec_id) {
+            needProbe = true;
+            break;
+        }
+        ALOGV("found stream %d id %d codec %s", i, stream->codec->codec_id, avcodec_get_name(stream->codec->codec_id));
     }
-    av_freep(&opts);
 
-    av_dump_format(ic, 0, url, 0);
+    // We must go deeper.
+    if (!isStreaming && (!ic->nb_streams || needProbe)) {
+        timeNow = ALooper::GetNowUs();
+
+        opts = setup_find_stream_info_opts(ic, codec_opts);
+        nb_streams = ic->nb_streams;
+        err = avformat_find_stream_info(ic, opts);
+        if (err < 0) {
+            ALOGE("%s: could not find stream info, err:%s", url, av_err2str(err));
+            goto fail;
+        }
+
+        ALOGV("probed stream info after %.2f ms", ((float)ALooper::GetNowUs() - timeNow) / 1000);
 
-    ALOGD("FFmpegExtrator, url: %s, format_name: %s, format_long_name: %s",
+        for (i = 0; i < nb_streams; i++) {
+            av_dict_free(&opts[i]);
+        }
+        av_freep(&opts);
+
+        av_dump_format(ic, 0, url, 0);
+    }
+
+    ALOGV("url: %s, format_name: %s, format_long_name: %s",
             url, ic->iformat->name, ic->iformat->long_name);
 
     container = findMatchingContainer(ic->iformat->name);
     if (container) {
         adjustContainerIfNeeded(&container, ic);
-        adjustConfidenceIfNeeded(container, ic, confidence);
+        adjustConfidenceIfNeeded(container, ic, confidence, isStreaming);
+        if (*confidence == 0)
+            container = NULL;
     }
 
 fail:
@@ -1989,7 +2114,8 @@ static const char *BetterSniffFFMPEG(const sp<DataSource> &source,
     // pass the addr of smart pointer("source")
     snprintf(url, sizeof(url), "android-source:%p", source.get());
 
-    ret = SniffFFMPEGCommon(url, confidence, (source->flags() & DataSource::kIsCachingDataSource));
+    ret = SniffFFMPEGCommon(url, confidence,
+            (source->flags() & DataSource::kIsCachingDataSource));
     if (ret) {
         meta->setString("extended-extractor-url", url);
     }
@@ -2008,6 +2134,9 @@ static const char *LegacySniffFFMPEG(const sp<DataSource> &source,
         return NULL;
     }
 
+    if (source->flags() & DataSource::kIsCachingDataSource)
+       return NULL;
+
     ALOGV("source url:%s", uri.string());
 
     // pass the addr of smart pointer("source") + file name
@@ -2024,15 +2153,29 @@ static const char *LegacySniffFFMPEG(const sp<DataSource> &source,
 bool SniffFFMPEG(
         const sp<DataSource> &source, String8 *mimeType, float *confidence,
         sp<AMessage> *meta) {
-    ALOGV("SniffFFMPEG");
+
+    float newConfidence = 0.08f;
+
+    ALOGV("SniffFFMPEG (initial confidence: %f, mime: %s)", *confidence,
+            mimeType == NULL ? "unknown" : *mimeType);
+
+    // This is a heavyweight sniffer, don't invoke it if Stagefright knows
+    // what it is doing already.
+    if (mimeType != NULL && confidence != NULL) {
+        if (*mimeType == "application/ogg") {
+            return false;
+        }
+        if (*confidence > 0.8f) {
+            return false;
+        }
+    }
 
     *meta = new AMessage;
-    *confidence = 0.08f;  // be the last resort, by default
 
-    const char *container = BetterSniffFFMPEG(source, confidence, *meta);
+    const char *container = BetterSniffFFMPEG(source, &newConfidence, *meta);
     if (!container) {
         ALOGW("sniff through BetterSniffFFMPEG failed, try LegacySniffFFMPEG");
-        container = LegacySniffFFMPEG(source, confidence, *meta);
+        container = LegacySniffFFMPEG(source, &newConfidence, *meta);
         if (container) {
             ALOGV("sniff through LegacySniffFFMPEG success");
         }
@@ -2048,17 +2191,7 @@ bool SniffFFMPEG(
     }
 
     ALOGD("ffmpeg detected media content as '%s' with confidence %.2f",
-            container, *confidence);
-
-    /* use MPEG4Extractor(not extended extractor) for HTTP source only */
-    if (!strcasecmp(container, MEDIA_MIMETYPE_CONTAINER_MPEG4)
-            && (source->flags() & DataSource::kIsCachingDataSource)) {
-        ALOGI("support container: %s, but it is caching data source, "
-                "Don't use ffmpegextractor", container);
-        (*meta)->clear();
-        *meta = NULL;
-        return false;
-    }
+            container, newConfidence);
 
     mimeType->setTo(container);
 
@@ -2071,11 +2204,12 @@ bool SniffFFMPEG(
     property_get("sys.media.parser.ffmpeg", value, "0");
     if (atoi(value)) {
         ALOGD("[debug] use ffmpeg parser");
-        *confidence = 0.88f;
+        newConfidence = 0.88f;
     }
 
-    if (*confidence > 0.08f) {
+    if (newConfidence > *confidence) {
         (*meta)->setString("extended-extractor-use", "ffmpegextractor");
+        *confidence = newConfidence;
     }
 
     return true;