X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=extractor%2FFFmpegExtractor.cpp;h=3e150bb6d977a42d46860ecf44116b6950a19673;hb=a6cec7da6ab3951065efd0cd44054d8faac887c3;hp=89b5d5acd412362bebc07a27716525c2904b11cb;hpb=f7f46ccb08b54ad0f16b78934f8f7bbc35d235b2;p=android-x86%2Fexternal-stagefright-plugins.git diff --git a/extractor/FFmpegExtractor.cpp b/extractor/FFmpegExtractor.cpp index 89b5d5a..3e150bb 100644 --- a/extractor/FFmpegExtractor.cpp +++ b/extractor/FFmpegExtractor.cpp @@ -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) @@ -94,6 +94,7 @@ private: mutable Mutex mLock; bool mIsAVC; + bool mIsHEVC; size_t mNALLengthSize; bool mNal2AnnexB; @@ -115,7 +116,8 @@ FFmpegExtractor::FFmpegExtractor(const sp &source, const sp 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; @@ -197,6 +199,11 @@ sp FFmpegExtractor::getMetaData() { return NULL; } + if (!mParsedMetadata) { + parseMetadataTags(mFormatCtx, mMeta); + mParsedMetadata = true; + } + return mMeta; } @@ -423,6 +430,23 @@ sp 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; @@ -453,6 +477,7 @@ sp 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); } @@ -539,6 +564,7 @@ sp 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); } @@ -586,8 +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) || + 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) { @@ -792,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: @@ -800,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: @@ -897,6 +927,7 @@ int FFmpegExtractor::initStreams() st_index[AVMEDIA_TYPE_VIDEO] = -1; wanted_stream[AVMEDIA_TYPE_AUDIO] = -1; wanted_stream[AVMEDIA_TYPE_VIDEO] = -1; + AVDictionary *format_opts = NULL, *codec_opts = NULL; const char *mime = NULL; setFFmpegDefaultOpts(); @@ -929,9 +960,12 @@ int FFmpegExtractor::initStreams() ALOGE("Option %s not found.\n", t->key); //ret = AVERROR_OPTION_NOT_FOUND; ret = -1; + av_dict_free(&format_opts); goto fail; } + av_dict_free(&format_opts); + if (mGenPTS) mFormatCtx->flags |= AVFMT_FLAG_GENPTS; @@ -1144,15 +1178,15 @@ void FFmpegExtractor::readerEntry() { ret = avformat_seek_file(mFormatCtx, -1, mSeekMin, mSeekPos, mSeekMax, 0); if (ret < 0) { ALOGE("%s: error while seeking", mFormatCtx->filename); - } else { - if (mAudioStreamIdx >= 0) { - packet_queue_flush(&mAudioQ); - packet_queue_put(&mAudioQ, &mAudioQ.flush_pkt); - } - if (mVideoStreamIdx >= 0) { - packet_queue_flush(&mVideoQ); - packet_queue_put(&mVideoQ, &mVideoQ.flush_pkt); - } + avformat_seek_file(mFormatCtx, -1, 0, 0, 0, 0); + } + if (mAudioStreamIdx >= 0) { + packet_queue_flush(&mAudioQ); + packet_queue_put(&mAudioQ, &mAudioQ.flush_pkt); + } + if (mVideoStreamIdx >= 0) { + packet_queue_flush(&mVideoQ); + packet_queue_put(&mVideoQ, &mVideoQ.flush_pkt); } mSeekIdx = -1; eof = false; @@ -1167,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)); @@ -1195,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; } @@ -1225,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; } @@ -1246,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) { @@ -1256,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); @@ -1273,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); } } @@ -1290,6 +1334,7 @@ FFmpegSource::FFmpegSource( : mExtractor(extractor), mTrackIndex(index), mIsAVC(false), + mIsHEVC(false), mNal2AnnexB(false), mStream(mExtractor->mTracks.itemAt(index).mStream), mQueue(mExtractor->mTracks.itemAt(index).mQueue), @@ -1322,7 +1367,34 @@ FFmpegSource::FFmpegSource( ALOGV("the stream is AVC, the length of a NAL unit: %d", mNALLengthSize); mNal2AnnexB = true; + } else if (avctx->codec_id == AV_CODEC_ID_HEVC + && 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; + const void *data; + size_t size; + CHECK(meta->findData(kKeyHVCC, &type, &data, &size)); + + const uint8_t *ptr = (const uint8_t *)data; + + CHECK(size >= 7); + //CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 + + // The number of bytes used to encode the length of a NAL unit. + mNALLengthSize = 1 + (ptr[21] & 3); + + ALOGD("the stream is HEVC, the length of a NAL unit: %d", mNALLengthSize); + + mNal2AnnexB = true; } + } mMediaType = mStream->codec->codec_type; @@ -1335,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; @@ -1388,7 +1460,7 @@ retry: if (seeking) { if (pkt.data != mQueue->flush_pkt.data) { - av_free_packet(&pkt); + av_packet_unref(&pkt); goto retry; } else { seeking = false; @@ -1400,12 +1472,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; } @@ -1416,7 +1488,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"); @@ -1434,7 +1506,7 @@ retry: mediaBuffer->set_range(0, pkt.size); //copy data - if (mIsAVC && mNal2AnnexB) { + if ((mIsAVC || mIsHEVC) && mNal2AnnexB) { /* This only works for NAL sizes 3-4 */ CHECK(mNALLengthSize == 3 || mNALLengthSize == 4); @@ -1445,7 +1517,7 @@ retry: ALOGE("convertNal2AnnexB failed"); mediaBuffer->release(); mediaBuffer = NULL; - av_free_packet(&pkt); + av_packet_unref(&pkt); return ERROR_MALFORMED; } } else { @@ -1457,6 +1529,21 @@ 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); + 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) { @@ -1497,7 +1584,7 @@ retry: *buffer = mediaBuffer; - av_free_packet(&pkt); + av_packet_unref(&pkt); return OK; } @@ -1546,6 +1633,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; } @@ -1601,7 +1692,6 @@ static bool isCodecSupportedByStagefright(enum AVCodecID codec_id) case AV_CODEC_ID_MP3: case AV_CODEC_ID_AMR_NB: case AV_CODEC_ID_AMR_WB: - case AV_CODEC_ID_FLAC: case AV_CODEC_ID_VORBIS: case AV_CODEC_ID_PCM_MULAW: //g711 case AV_CODEC_ID_PCM_ALAW: //g711 @@ -1623,11 +1713,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); @@ -1660,22 +1751,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; } } @@ -1763,23 +1888,23 @@ 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)) { + *confidence = 0.4f; } else { //todo here } - if (*confidence > 0.08) { - return; - } - //2. check codec adjustCodecConfidence(ic, confidence); } @@ -1856,13 +1981,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)) { @@ -1874,14 +1992,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) { @@ -1896,6 +2017,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) { @@ -1903,37 +2029,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); + + for (i = 0; i < nb_streams; i++) { + av_dict_free(&opts[i]); + } + av_freep(&opts); + + av_dump_format(ic, 0, url, 0); + } - ALOGD("FFmpegExtrator, url: %s, format_name: %s, format_long_name: %s", + 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: @@ -1958,7 +2102,8 @@ static const char *BetterSniffFFMPEG(const sp &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); } @@ -1977,6 +2122,9 @@ static const char *LegacySniffFFMPEG(const sp &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 @@ -1993,15 +2141,29 @@ static const char *LegacySniffFFMPEG(const sp &source, bool SniffFFMPEG( const sp &source, String8 *mimeType, float *confidence, sp *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"); } @@ -2017,17 +2179,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); @@ -2040,11 +2192,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;