#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)
mInitCheck(NO_INIT),
mFFmpegInited(false),
mFormatCtx(NULL),
- mReaderThreadStarted(false) {
+ mReaderThreadStarted(false),
+ mParsedMetadata(false) {
ALOGV("FFmpegExtractor::FFmpegExtractor");
fetchStuffsFromSniffedMeta(meta);
return NULL;
}
+ if (!mParsedMetadata) {
+ parseMetadataTags(mFormatCtx, mMeta);
+ mParsedMetadata = true;
+ }
+
return mMeta;
}
}
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;
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) {
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));
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;
}
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;
}
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) {
}
if (mDefersToCreateAudioTrack) {
if (avctx->extradata_size <= 0) {
- av_free_packet(pkt);
+ av_packet_unref(pkt);
continue;
}
stream_component_open(mAudioStreamIdx);
} else if (pkt->stream_index == mVideoStreamIdx) {
packet_queue_put(&mVideoQ, pkt);
} else {
- av_free_packet(pkt);
+ av_packet_unref(pkt);
}
}
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;
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);
if (seeking) {
if (pkt.data != mQueue->flush_pkt.data) {
- av_free_packet(&pkt);
+ av_packet_unref(&pkt);
goto retry;
} else {
seeking = false;
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;
}
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");
//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 */
ALOGE("convertNal2AnnexB failed");
mediaBuffer->release();
mediaBuffer = NULL;
- av_free_packet(&pkt);
+ av_packet_unref(&pkt);
return ERROR_MALFORMED;
}
} else {
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) {
*buffer = mediaBuffer;
- av_free_packet(&pkt);
+ av_packet_unref(&pkt);
return OK;
}
}
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;
}
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);
//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;
+ }
}
-
- 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;
+ 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
}
}
//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)) {
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:
return NULL;
}
+ if (source->flags() & DataSource::kIsCachingDataSource)
+ return NULL;
+
ALOGV("source url:%s", uri.string());
// pass the addr of smart pointer("source") + file name
// 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;
}
ALOGD("ffmpeg detected media content as '%s' with confidence %.2f",
container, newConfidence);
- /* 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;
- }
-
mimeType->setTo(container);
(*meta)->setString("extended-extractor", "extended-extractor");