OSDN Git Service

stagefright-plugins: Fix crash if nal size is not 3/4
[android-x86/external-stagefright-plugins.git] / extractor / FFmpegExtractor.cpp
index c1e841d..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)
@@ -430,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;
 
@@ -1184,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));
@@ -1243,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;
                 }
 
@@ -1264,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) {
@@ -1274,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);
@@ -1291,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);
         }
     }
 
@@ -1410,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);
@@ -1434,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;
@@ -1446,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;
     }
@@ -1462,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");
@@ -1482,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 */
@@ -1491,7 +1524,7 @@ retry:
             ALOGE("convertNal2AnnexB failed");
             mediaBuffer->release();
             mediaBuffer = NULL;
-            av_free_packet(&pkt);
+            av_packet_unref(&pkt);
             return ERROR_MALFORMED;
         }
     } else {
@@ -1503,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) {
@@ -1543,7 +1596,7 @@ retry:
 
     *buffer = mediaBuffer;
 
-    av_free_packet(&pkt);
+    av_packet_unref(&pkt);
 
     return OK;
 }
@@ -1672,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);
@@ -1709,22 +1763,26 @@ 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;
+        }
     }
-
-    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
     }
 }
 
@@ -1842,11 +1900,11 @@ 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)) {
@@ -2029,7 +2087,9 @@ static const char *SniffFFMPEGCommon(const char *url, float *confidence, bool is
     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:
@@ -2074,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
@@ -2099,6 +2162,9 @@ bool SniffFFMPEG(
     // 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;
         }
@@ -2127,16 +2193,6 @@ bool SniffFFMPEG(
     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");