2 * Copyright 2012 Michael Chen <omxcodec@gmail.com>
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #define LOG_TAG "FFmpegExtractor"
19 #include <utils/Log.h>
21 #include <limits.h> /* INT_MAX */
23 #include <media/stagefright/foundation/ABitReader.h>
24 #include <media/stagefright/foundation/ABuffer.h>
25 #include <media/stagefright/foundation/ADebug.h>
26 #include <media/stagefright/foundation/AMessage.h>
27 #include <media/stagefright/foundation/hexdump.h>
28 #include <media/stagefright/DataSource.h>
29 #include <media/stagefright/MediaBuffer.h>
30 #include <media/stagefright/MediaDebug.h>
31 #include <media/stagefright/MediaDefs.h>
32 #include <media/stagefright/MediaErrors.h>
33 #include <media/stagefright/MediaSource.h>
34 #include <media/stagefright/MetaData.h>
35 #include <media/stagefright/Utils.h>
36 #include <utils/String8.h>
37 #include <utils/misc.h>
39 #include "include/avc_utils.h"
40 #include "ffmpeg_utils/ffmpeg_utils.h"
41 #include "FFmpegExtractor.h"
43 #define DEBUG_READ_ENTRY 0
44 #define DIABLE_VIDEO 0
45 #define DIABLE_AUDIO 0
46 #define WAIT_KEY_PACKET_AFTER_SEEK 1
47 #define DISABLE_NAL_TO_ANNEXB 0
49 #define MAX_QUEUE_SIZE (15 * 1024 * 1024)
50 #define MIN_AUDIOQ_SIZE (20 * 16 * 1024)
52 #define EXTRACTOR_MAX_PROBE_PACKETS 200
54 #define FF_MAX_EXTRADATA_SIZE ((1 << 28) - FF_INPUT_BUFFER_PADDING_SIZE)
61 static AVPacket flush_pkt;
65 struct FFmpegExtractor::Track : public MediaSource {
66 Track(const sp<FFmpegExtractor> &extractor, sp<MetaData> meta, bool isAVC,
67 AVStream *stream, PacketQueue *queue);
69 virtual status_t start(MetaData *params);
70 virtual status_t stop();
71 virtual sp<MetaData> getFormat();
73 virtual status_t read(
74 MediaBuffer **buffer, const ReadOptions *options);
80 friend struct FFmpegExtractor;
82 sp<FFmpegExtractor> mExtractor;
85 enum AVMediaType mMediaType;
90 size_t mNALLengthSize;
96 DISALLOW_EVIL_CONSTRUCTORS(Track);
99 ////////////////////////////////////////////////////////////////////////////////
101 FFmpegExtractor::FFmpegExtractor(const sp<DataSource> &source)
102 : mDataSource(source),
103 mReaderThreadStarted(false),
104 mInitCheck(NO_INIT) {
105 LOGV("FFmpegExtractor::FFmpegExtractor");
108 const char *url = mDataSource->getNamURI();
110 LOGI("url is error!");
114 if (!strcmp(url, "-")) {
115 av_strlcpy(mFilename, "pipe:", strlen("pipe:") + 1);
117 av_strlcpy(mFilename, url, strlen(url) + 1);
119 LOGI("url: %s, mFilename: %s", url, mFilename);
123 LOGE("failed to init ffmpeg");
127 // start reader here, as we want to extract extradata from bitstream if no extradata
130 while(mProbePkts <= EXTRACTOR_MAX_PROBE_PACKETS && !mEOF &&
131 (mFormatCtx->pb ? !mFormatCtx->pb->error : 1) &&
132 (mDefersToCreateVideoTrack || mDefersToCreateAudioTrack)) {
133 // FIXME, i am so lazy! Should use pthread_cond_wait to wait conditions
137 LOGV("mProbePkts: %d, mEOF: %d, pb->error(if has): %d, mDefersToCreateVideoTrack: %d, mDefersToCreateAudioTrack: %d",
138 mProbePkts, mEOF, mFormatCtx->pb ? mFormatCtx->pb->error : 0, mDefersToCreateVideoTrack, mDefersToCreateAudioTrack);
143 FFmpegExtractor::~FFmpegExtractor() {
144 LOGV("FFmpegExtractor::~FFmpegExtractor");
146 // stop reader here if no track!
152 size_t FFmpegExtractor::countTracks() {
153 return mInitCheck == OK ? mTracks.size() : 0;
156 sp<MediaSource> FFmpegExtractor::getTrack(size_t index) {
157 LOGV("FFmpegExtractor::getTrack[%d]", index);
159 if (mInitCheck != OK) {
163 if (index >= mTracks.size()) {
167 return mTracks.valueAt(index);
170 sp<MetaData> FFmpegExtractor::getTrackMetaData(size_t index, uint32_t flags) {
171 LOGV("FFmpegExtractor::getTrackMetaData[%d]", index);
173 if (mInitCheck != OK) {
177 if (index >= mTracks.size()) {
181 return mTracks.valueAt(index)->getFormat();
184 sp<MetaData> FFmpegExtractor::getMetaData() {
185 LOGV("FFmpegExtractor::getMetaData");
187 if (mInitCheck != OK) {
191 sp<MetaData> meta = new MetaData;
193 meta->setCString(kKeyMIMEType, "video/ffmpeg");
198 uint32_t FFmpegExtractor::flags() const {
199 LOGV("FFmpegExtractor::flags");
201 if (mInitCheck != OK) {
205 uint32_t flags = CAN_PAUSE;
207 if (mFormatCtx->duration != AV_NOPTS_VALUE) {
208 flags |= CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK;
214 void FFmpegExtractor::packet_queue_init(PacketQueue *q)
216 memset(q, 0, sizeof(PacketQueue));
217 q->mutex = SDL_CreateMutex();
218 q->cond = SDL_CreateCond();
219 packet_queue_put(q, &flush_pkt);
222 void FFmpegExtractor::packet_queue_flush(PacketQueue *q)
224 AVPacketList *pkt, *pkt1;
226 SDL_LockMutex(q->mutex);
227 for (pkt = q->first_pkt; pkt != NULL; pkt = pkt1) {
229 av_free_packet(&pkt->pkt);
236 SDL_UnlockMutex(q->mutex);
239 void FFmpegExtractor::packet_queue_end(PacketQueue *q)
241 packet_queue_flush(q);
242 SDL_DestroyMutex(q->mutex);
243 SDL_DestroyCond(q->cond);
246 void FFmpegExtractor::packet_queue_abort(PacketQueue *q)
248 SDL_LockMutex(q->mutex);
250 q->abort_request = 1;
252 SDL_CondSignal(q->cond);
254 SDL_UnlockMutex(q->mutex);
257 int FFmpegExtractor::packet_queue_put(PacketQueue *q, AVPacket *pkt)
261 /* duplicate the packet */
262 if (pkt != &flush_pkt && av_dup_packet(pkt) < 0)
265 pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
271 SDL_LockMutex(q->mutex);
277 q->last_pkt->next = pkt1;
280 //q->size += pkt1->pkt.size + sizeof(*pkt1);
281 q->size += pkt1->pkt.size;
282 SDL_CondSignal(q->cond);
284 SDL_UnlockMutex(q->mutex);
288 /* packet queue handling */
289 /* return < 0 if aborted, 0 if no packet and > 0 if packet. */
290 int FFmpegExtractor::packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
295 SDL_LockMutex(q->mutex);
298 if (q->abort_request) {
305 q->first_pkt = pkt1->next;
309 //q->size -= pkt1->pkt.size + sizeof(*pkt1);
310 q->size -= pkt1->pkt.size;
319 SDL_CondWait(q->cond, q->mutex);
322 SDL_UnlockMutex(q->mutex);
326 static void EncodeSize14(uint8_t **_ptr, size_t size) {
327 CHECK_LE(size, 0x3fff);
329 uint8_t *ptr = *_ptr;
331 *ptr++ = 0x80 | (size >> 7);
332 *ptr++ = size & 0x7f;
337 static sp<ABuffer> MakeMPEGVideoESDS(const sp<ABuffer> &csd) {
338 sp<ABuffer> esds = new ABuffer(csd->size() + 25);
340 uint8_t *ptr = esds->data();
342 EncodeSize14(&ptr, 22 + csd->size());
344 *ptr++ = 0x00; // ES_ID
347 *ptr++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag
350 EncodeSize14(&ptr, 16 + csd->size());
352 *ptr++ = 0x40; // Audio ISO/IEC 14496-3
354 for (size_t i = 0; i < 12; ++i) {
359 EncodeSize14(&ptr, csd->size());
361 memcpy(ptr, csd->data(), csd->size());
366 // the same as MakeMPEGVideoESDS
367 static sp<ABuffer> MakeRawCodecSpecificData(const sp<ABuffer> &csd) {
368 sp<ABuffer> esds = new ABuffer(csd->size() + 25);
370 uint8_t *ptr = esds->data();
372 EncodeSize14(&ptr, 22 + csd->size());
374 *ptr++ = 0x00; // ES_ID
377 *ptr++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag
380 EncodeSize14(&ptr, 16 + csd->size());
382 *ptr++ = 0x40; // Audio ISO/IEC 14496-3
384 for (size_t i = 0; i < 12; ++i) {
389 EncodeSize14(&ptr, csd->size());
391 memcpy(ptr, csd->data(), csd->size());
396 // Returns the sample rate based on the sampling frequency index
397 static uint32_t get_sample_rate(const uint8_t sf_index)
399 static const uint32_t sample_rates[] =
401 96000, 88200, 64000, 48000, 44100, 32000,
402 24000, 22050, 16000, 12000, 11025, 8000
405 if (sf_index < sizeof(sample_rates) / sizeof(sample_rates[0])) {
406 return sample_rates[sf_index];
412 int FFmpegExtractor::check_extradata(AVCodecContext *avctx)
415 bool *defersToCreateTrack;
416 AVBitStreamFilterContext **bsfc;
419 if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
421 defersToCreateTrack = &mDefersToCreateVideoTrack;
422 } else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO){
424 defersToCreateTrack = &mDefersToCreateAudioTrack;
428 if (avctx->codec_id == CODEC_ID_MP3 ||
429 avctx->codec_id == CODEC_ID_MP1 ||
430 avctx->codec_id == CODEC_ID_MP2 ||
431 avctx->codec_id == CODEC_ID_AC3 ||
432 avctx->codec_id == CODEC_ID_H263 ||
433 avctx->codec_id == CODEC_ID_H263P ||
434 avctx->codec_id == CODEC_ID_H263I ||
435 avctx->codec_id == CODEC_ID_WMV1)
438 // is extradata compatible with android?
439 if (avctx->codec_id != CODEC_ID_AAC) {
440 int is_compatible = is_extradata_compatible_with_android(avctx);
441 if (!is_compatible) {
442 LOGI("%s extradata is not compatible with android, should to extract it from bitstream",
443 av_get_media_type_string(avctx->codec_type));
444 *defersToCreateTrack = true;
445 *bsfc = NULL; // H264 don't need bsfc, only AAC?
451 if (avctx->codec_id == CODEC_ID_AAC) {
452 name = "aac_adtstoasc";
455 if (avctx->extradata_size <= 0) {
456 LOGI("No %s extradata found, should to extract it from bitstream",
457 av_get_media_type_string(avctx->codec_type));
458 *defersToCreateTrack = true;
459 //CHECK(name != NULL);
460 if (!*bsfc && name) {
461 *bsfc = av_bitstream_filter_init(name);
463 LOGE("Cannot open the %s BSF!", name);
464 *defersToCreateTrack = false;
467 LOGV("open the %s bsf", name);
478 int FFmpegExtractor::stream_component_open(int stream_index)
480 AVCodecContext *avctx;
483 bool supported = false;
489 LOGI("stream_index: %d", stream_index);
490 if (stream_index < 0 || stream_index >= mFormatCtx->nb_streams)
492 avctx = mFormatCtx->streams[stream_index]->codec;
494 switch(avctx->codec_id) {
505 case CODEC_ID_MPEG2VIDEO:
520 LOGE("unsupport the codec, id: 0x%0x", avctx->codec_id);
523 LOGV("support the codec");
526 ssize_t index = mTracks.indexOfKey(stream_index);
529 LOGE("this track already exists");
533 mFormatCtx->streams[stream_index]->discard = AVDISCARD_DEFAULT;
536 av_get_codec_tag_string(tagbuf, sizeof(tagbuf), avctx->codec_tag);
537 LOGV("Tag %s/0x%08x with codec id '%d'\n", tagbuf, avctx->codec_tag, avctx->codec_id);
539 switch (avctx->codec_type) {
540 case AVMEDIA_TYPE_VIDEO:
541 if (mVideoStreamIdx == -1)
542 mVideoStreamIdx = stream_index;
543 if (mVideoStream == NULL)
544 mVideoStream = mFormatCtx->streams[stream_index];
545 if (!mVideoQInited) {
546 packet_queue_init(&mVideoQ);
547 mVideoQInited = true;
550 ret = check_extradata(avctx);
553 // disable the stream
554 mVideoStreamIdx = -1;
556 packet_queue_end(&mVideoQ);
557 mVideoQInited = false;
558 mFormatCtx->streams[stream_index]->discard = AVDISCARD_ALL;
563 if (avctx->extradata) {
564 LOGV("video stream extradata:");
565 hexdump(avctx->extradata, avctx->extradata_size);
567 LOGV("video stream no extradata, but we can ignore it.");
572 switch(avctx->codec_id) {
576 * http://msdn.microsoft.com/en-us/library/dd757808(v=vs.85).aspx
578 //if (avctx->codec_tag && avctx->codec_tag == AV_RL32("avc1")) {
579 if (avctx->extradata[0] == 1 /* configurationVersion */) {
580 // H.264 bitstream without start codes.
584 if (avctx->width == 0 || avctx->height == 0) {
585 int32_t width, height;
586 sp<ABuffer> seqParamSet = new ABuffer(avctx->extradata_size - 8);
587 memcpy(seqParamSet->data(), avctx->extradata + 8, avctx->extradata_size - 8);
588 FindAVCDimensions(seqParamSet, &width, &height);
589 avctx->width = width;
590 avctx->height = height;
593 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
594 meta->setData(kKeyAVCC, kTypeAVCC, avctx->extradata, avctx->extradata_size);
596 // H.264 bitstream with start codes.
600 /* set NULL to release meta as we will new a meta in MakeAVCCodecSpecificData() fxn */
604 sp<ABuffer> buffer = new ABuffer(avctx->extradata_size);
605 memcpy(buffer->data(), avctx->extradata, avctx->extradata_size);
606 meta = MakeAVCCodecSpecificData(buffer);
611 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
613 sp<ABuffer> csd = new ABuffer(avctx->extradata_size);
614 memcpy(csd->data(), avctx->extradata, avctx->extradata_size);
615 sp<ABuffer> esds = MakeMPEGVideoESDS(csd);
616 meta->setData(kKeyESDS, kTypeESDS, esds->data(), esds->size());
623 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
625 case CODEC_ID_MPEG2VIDEO:
627 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
629 sp<ABuffer> csd = new ABuffer(avctx->extradata_size);
630 memcpy(csd->data(), avctx->extradata, avctx->extradata_size);
631 sp<ABuffer> esds = MakeMPEGVideoESDS(csd);
632 meta->setData(kKeyESDS, kTypeESDS, esds->data(), esds->size());
637 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_WMV12);
638 meta->setData(kKeyESDS, kTypeESDS, avctx->extradata, avctx->extradata_size);
642 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_WMV12);
646 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_WMV12);
648 sp<ABuffer> csd = new ABuffer(avctx->extradata_size);
649 memcpy(csd->data(), avctx->extradata, avctx->extradata_size);
650 sp<ABuffer> esds = MakeRawCodecSpecificData(csd);
651 meta->setData(kKeyESDS, kTypeESDS, esds->data(), esds->size());
655 CHECK(!"Should not be here. Unsupported codec.");
659 LOGI("width: %d, height: %d, bit_rate: %d", avctx->width, avctx->height, avctx->bit_rate);
661 meta->setInt32(kKeyWidth, avctx->width);
662 meta->setInt32(kKeyHeight, avctx->height);
663 if (avctx->bit_rate > 0)
664 meta->setInt32(kKeyBitRate, avctx->bit_rate);
665 if (mFormatCtx->duration != AV_NOPTS_VALUE)
666 meta->setInt64(kKeyDuration, mFormatCtx->duration);
668 LOGV("create a video track");
670 stream_index, new Track(this, meta, isAVC, mVideoStream, &mVideoQ));
672 mDefersToCreateVideoTrack = false;
675 case AVMEDIA_TYPE_AUDIO:
676 if (mAudioStreamIdx == -1)
677 mAudioStreamIdx = stream_index;
678 if (mAudioStream == NULL)
679 mAudioStream = mFormatCtx->streams[stream_index];
680 if (!mAudioQInited) {
681 packet_queue_init(&mAudioQ);
682 mAudioQInited = true;
685 ret = check_extradata(avctx);
688 // disable the stream
689 mAudioStreamIdx = -1;
691 packet_queue_end(&mAudioQ);
692 mAudioQInited = false;
693 mFormatCtx->streams[stream_index]->discard = AVDISCARD_ALL;
698 if (avctx->extradata) {
699 LOGV("audio stream extradata:");
700 hexdump(avctx->extradata, avctx->extradata_size);
702 LOGV("audio stream no extradata, but we can ignore it.");
705 switch(avctx->codec_id) {
709 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I);
714 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II);
719 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
724 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
729 const uint8_t *header;
730 uint8_t profile, sf_index, channel;
732 header = avctx->extradata;
733 CHECK(header != NULL);
735 // AudioSpecificInfo follows
736 // oooo offf fccc c000
737 // o - audioObjectType
738 // f - samplingFreqIndex
740 profile = ((header[0] & 0xf8) >> 3) - 1;
741 sf_index = (header[0] & 0x07) << 1 | (header[1] & 0x80) >> 7;
742 sr = get_sample_rate(sf_index);
744 LOGE("unsupport the sample rate");
747 channel = (header[1] >> 3) & 0xf;
748 LOGV("profile: %d, sf_index: %d, channel: %d", profile, sf_index, channel);
750 meta = MakeAACCodecSpecificData(profile, sf_index, channel);
751 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
756 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_WMA);
758 sp<ABuffer> csd = new ABuffer(avctx->extradata_size);
759 memcpy(csd->data(), avctx->extradata, avctx->extradata_size);
760 sp<ABuffer> esds = MakeRawCodecSpecificData(csd);
761 meta->setData(kKeyESDS, kTypeESDS, esds->data(), esds->size());
765 CHECK(!"Should not be here. Unsupported codec.");
769 LOGI("bit_rate: %d, sample_rate: %d, channels: %d", avctx->bit_rate, avctx->sample_rate, avctx->channels);
771 meta->setInt32(kKeySampleRate, avctx->sample_rate);
772 meta->setInt32(kKeyChannelCount, avctx->channels);
773 meta->setInt32(kKeyBitRate, avctx->bit_rate);
774 if (mFormatCtx->duration != AV_NOPTS_VALUE)
775 meta->setInt64(kKeyDuration, mFormatCtx->duration);
777 if (avctx->codec_id != CODEC_ID_MP3 &&
778 avctx->codec_id != CODEC_ID_MP1 &&
779 avctx->codec_id != CODEC_ID_MP2 &&
780 avctx->codec_id != CODEC_ID_AC3) {
781 LOGV("audio meta esds:");
782 CHECK(meta->findData(kKeyESDS, &type, &data, &size));
786 LOGV("create a audio track");
788 stream_index, new Track(this, meta, false, mAudioStream, &mAudioQ));
790 mDefersToCreateAudioTrack = false;
793 case AVMEDIA_TYPE_SUBTITLE:
795 CHECK(!"Should not be here. Unsupported media type.");
798 CHECK(!"Should not be here. Unsupported media type.");
804 void FFmpegExtractor::stream_component_close(int stream_index)
806 AVCodecContext *avctx;
808 if (stream_index < 0 || stream_index >= mFormatCtx->nb_streams)
810 avctx = mFormatCtx->streams[stream_index]->codec;
812 switch (avctx->codec_type) {
813 case AVMEDIA_TYPE_VIDEO:
814 LOGV("packet_queue_abort videoq");
815 packet_queue_abort(&mVideoQ);
816 /* wait until the end */
817 while (!mAbortRequest && !mVideoEOSReceived) {
818 LOGV("wait for video received");
821 LOGV("packet_queue_end videoq");
822 packet_queue_end(&mVideoQ);
824 case AVMEDIA_TYPE_AUDIO:
825 LOGV("packet_queue_abort audioq");
826 packet_queue_abort(&mAudioQ);
827 while (!mAbortRequest && !mAudioEOSReceived) {
828 LOGV("wait for audio received");
831 LOGV("packet_queue_end audioq");
832 packet_queue_end(&mAudioQ);
834 case AVMEDIA_TYPE_SUBTITLE:
840 mFormatCtx->streams[stream_index]->discard = AVDISCARD_ALL;
841 switch (avctx->codec_type) {
842 case AVMEDIA_TYPE_VIDEO:
844 mVideoStreamIdx = -1;
846 av_bitstream_filter_close(mVideoBsfc);
850 case AVMEDIA_TYPE_AUDIO:
852 mAudioStreamIdx = -1;
854 av_bitstream_filter_close(mAudioBsfc);
858 case AVMEDIA_TYPE_SUBTITLE:
865 void FFmpegExtractor::reachedEOS(enum AVMediaType media_type)
867 Mutex::Autolock autoLock(mLock);
869 if (media_type == AVMEDIA_TYPE_VIDEO) {
870 mVideoEOSReceived = true;
871 } else if (media_type == AVMEDIA_TYPE_AUDIO) {
872 mAudioEOSReceived = true;
876 /* seek in the stream */
877 int FFmpegExtractor::stream_seek(int64_t pos, enum AVMediaType media_type)
879 Mutex::Autolock autoLock(mLock);
881 if (mVideoStreamIdx >= 0 &&
882 mAudioStreamIdx >= 0 &&
883 media_type == AVMEDIA_TYPE_AUDIO &&
884 !mVideoEOSReceived) {
889 if (mAudioStreamIdx >= 0)
890 packet_queue_flush(&mAudioQ);
891 if (mVideoStreamIdx >= 0)
892 packet_queue_flush(&mVideoQ);
895 mSeekFlags &= ~AVSEEK_FLAG_BYTE;
902 int FFmpegExtractor::decode_interrupt_cb(void *ctx)
904 FFmpegExtractor *extrator = static_cast<FFmpegExtractor *>(ctx);
905 return extrator->mAbortRequest;
908 void FFmpegExtractor::print_error_ex(const char *filename, int err)
911 const char *errbuf_ptr = errbuf;
913 if (av_strerror(err, errbuf, sizeof(errbuf)) < 0)
914 errbuf_ptr = strerror(AVUNERROR(err));
915 LOGI("%s: %s\n", filename, errbuf_ptr);
918 void FFmpegExtractor::setFFmpegDefaultOpts()
932 mSeekByBytes = 0; /* seek by bytes 0=off 1=on -1=auto" */
933 mStartTime = AV_NOPTS_VALUE;
934 mDuration = AV_NOPTS_VALUE;
935 mSeekPos = AV_NOPTS_VALUE;
939 mVideoStreamIdx = -1;
940 mAudioStreamIdx = -1;
943 mVideoQInited = false;
944 mAudioQInited = false;
945 mDefersToCreateVideoTrack = false;
946 mDefersToCreateAudioTrack = false;
959 int FFmpegExtractor::initStreams()
964 int ret = 0, audio_ret = 0, video_ret = 0;
965 int pkt_in_play_range = 0;
966 AVDictionaryEntry *t;
969 int st_index[AVMEDIA_TYPE_NB] = {0};
970 int wanted_stream[AVMEDIA_TYPE_NB] = {0};
971 st_index[AVMEDIA_TYPE_AUDIO] = -1;
972 st_index[AVMEDIA_TYPE_VIDEO] = -1;
973 wanted_stream[AVMEDIA_TYPE_AUDIO] = -1;
974 wanted_stream[AVMEDIA_TYPE_VIDEO] = -1;
976 setFFmpegDefaultOpts();
978 status = initFFmpeg();
984 av_init_packet(&flush_pkt);
985 flush_pkt.data = (uint8_t *)"FLUSH";
988 mFormatCtx = avformat_alloc_context();
989 mFormatCtx->interrupt_callback.callback = decode_interrupt_cb;
990 mFormatCtx->interrupt_callback.opaque = this;
991 LOGV("mFilename: %s", mFilename);
992 err = avformat_open_input(&mFormatCtx, mFilename, NULL, &format_opts);
994 print_error_ex(mFilename, err);
998 if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
999 LOGE("Option %s not found.\n", t->key);
1000 //ret = AVERROR_OPTION_NOT_FOUND;
1006 mFormatCtx->flags |= AVFMT_FLAG_GENPTS;
1008 opts = setup_find_stream_info_opts(mFormatCtx, codec_opts);
1009 orig_nb_streams = mFormatCtx->nb_streams;
1011 err = avformat_find_stream_info(mFormatCtx, opts);
1013 LOGE("%s: could not find codec parameters\n", mFilename);
1017 for (i = 0; i < orig_nb_streams; i++)
1018 av_dict_free(&opts[i]);
1022 mFormatCtx->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use url_feof() to test for the end
1024 if (mSeekByBytes < 0)
1025 mSeekByBytes = !!(mFormatCtx->iformat->flags & AVFMT_TS_DISCONT);
1027 /* if seeking requested, we execute it */
1028 if (mStartTime != AV_NOPTS_VALUE) {
1031 timestamp = mStartTime;
1032 /* add the stream start time */
1033 if (mFormatCtx->start_time != AV_NOPTS_VALUE)
1034 timestamp += mFormatCtx->start_time;
1035 ret = avformat_seek_file(mFormatCtx, -1, INT64_MIN, timestamp, INT64_MAX, 0);
1037 LOGE("%s: could not seek to position %0.3f",
1038 mFilename, (double)timestamp / AV_TIME_BASE);
1043 for (i = 0; i < mFormatCtx->nb_streams; i++)
1044 mFormatCtx->streams[i]->discard = AVDISCARD_ALL;
1046 st_index[AVMEDIA_TYPE_VIDEO] =
1047 av_find_best_stream(mFormatCtx, AVMEDIA_TYPE_VIDEO,
1048 wanted_stream[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
1050 st_index[AVMEDIA_TYPE_AUDIO] =
1051 av_find_best_stream(mFormatCtx, AVMEDIA_TYPE_AUDIO,
1052 wanted_stream[AVMEDIA_TYPE_AUDIO],
1053 st_index[AVMEDIA_TYPE_VIDEO],
1056 av_dump_format(mFormatCtx, 0, mFilename, 0);
1059 if (mFormatCtx->duration != AV_NOPTS_VALUE) {
1060 int hours, mins, secs, us;
1061 secs = mFormatCtx->duration / AV_TIME_BASE;
1062 us = mFormatCtx->duration % AV_TIME_BASE;
1067 LOGI("the duration is %02d:%02d:%02d.%02d", hours, mins, secs, (100 * us) / AV_TIME_BASE);
1070 if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
1071 audio_ret = stream_component_open(st_index[AVMEDIA_TYPE_AUDIO]);
1074 if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
1075 video_ret = stream_component_open(st_index[AVMEDIA_TYPE_VIDEO]);
1078 if ( audio_ret < 0 && video_ret < 0) {
1079 LOGE("%s: could not open codecs\n", mFilename);
1090 void FFmpegExtractor::deInitStreams()
1093 avformat_close_input(&mFormatCtx);
1099 status_t FFmpegExtractor::startReaderThread() {
1100 LOGV("Starting reader thread");
1101 Mutex::Autolock autoLock(mLock);
1103 if (mReaderThreadStarted)
1106 pthread_attr_t attr;
1107 pthread_attr_init(&attr);
1108 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1109 pthread_create(&mReaderThread, &attr, ReaderWrapper, this);
1110 pthread_attr_destroy(&attr);
1111 mReaderThreadStarted = true;
1112 LOGD("Reader thread started");
1117 void FFmpegExtractor::stopReaderThread() {
1118 LOGV("Stopping reader thread");
1119 Mutex::Autolock autoLock(mLock);
1121 if (!mReaderThreadStarted) {
1122 LOGD("Reader thread have been stopped");
1129 pthread_join(mReaderThread, &dummy);
1130 mReaderThreadStarted = false;
1131 LOGD("Reader thread stopped");
1135 void *FFmpegExtractor::ReaderWrapper(void *me) {
1136 ((FFmpegExtractor *)me)->readerEntry();
1141 void FFmpegExtractor::readerEntry() {
1143 AVPacket pkt1, *pkt = &pkt1;
1145 int pkt_in_play_range = 0;
1147 LOGV("FFmpegExtractor::readerEntry");
1149 mVideoEOSReceived = false;
1150 mAudioEOSReceived = false;
1156 if (mPaused != mLastPaused) {
1157 mLastPaused = mPaused;
1159 mReadPauseReturn = av_read_pause(mFormatCtx);
1161 av_read_play(mFormatCtx);
1163 #if CONFIG_RTSP_DEMUXER || CONFIG_MMSH_PROTOCOL
1165 (!strcmp(mFormatCtx->iformat->name, "rtsp") ||
1166 (mFormatCtx->pb && !strncmp(mFilename, "mmsh:", 5)))) {
1167 /* wait 10 ms to avoid trying to get another packet */
1175 LOGV("readerEntry, mSeekReq: %d", mSeekReq);
1176 ret = avformat_seek_file(mFormatCtx, -1, INT64_MIN, mSeekPos, INT64_MAX, mSeekFlags);
1178 LOGE("%s: error while seeking", mFormatCtx->filename);
1180 if (mAudioStreamIdx >= 0) {
1181 packet_queue_flush(&mAudioQ);
1182 packet_queue_put(&mAudioQ, &flush_pkt);
1184 if (mVideoStreamIdx >= 0) {
1185 packet_queue_flush(&mVideoQ);
1186 packet_queue_put(&mVideoQ, &flush_pkt);
1193 /* if the queue are full, no need to read more */
1194 if ( mAudioQ.size + mVideoQ.size > MAX_QUEUE_SIZE
1195 || ( (mAudioQ .size > MIN_AUDIOQ_SIZE || mAudioStreamIdx < 0)
1196 && (mVideoQ .nb_packets > MIN_FRAMES || mVideoStreamIdx < 0))) {
1197 #if DEBUG_READ_ENTRY
1198 LOGV("readerEntry, is full, fuck");
1206 if (mVideoStreamIdx >= 0) {
1207 av_init_packet(pkt);
1210 pkt->stream_index = mVideoStreamIdx;
1211 packet_queue_put(&mVideoQ, pkt);
1213 if (mAudioStreamIdx >= 0) {
1214 av_init_packet(pkt);
1217 pkt->stream_index = mAudioStreamIdx;
1218 packet_queue_put(&mAudioQ, pkt);
1221 #if DEBUG_READ_ENTRY
1222 LOGV("readerEntry, eof = 1, mVideoQ.size: %d, mVideoQ.nb_packets: %d, mAudioQ.size: %d, mAudioQ.nb_packets: %d",
1223 mVideoQ.size, mVideoQ.nb_packets, mAudioQ.size, mAudioQ.nb_packets);
1225 if (mAudioQ.size + mVideoQ.size == 0) {
1226 if (mLoop != 1 && (!mLoop || --mLoop)) {
1227 if (mVideoStreamIdx >= 0) {
1228 stream_seek(mStartTime != AV_NOPTS_VALUE ? mStartTime : 0, AVMEDIA_TYPE_VIDEO);
1229 } else if (mAudioStreamIdx >= 0) {
1230 stream_seek(mStartTime != AV_NOPTS_VALUE ? mStartTime : 0, AVMEDIA_TYPE_AUDIO);
1232 } else if (mAutoExit) {
1241 ret = av_read_frame(mFormatCtx, pkt);
1244 if (ret == AVERROR_EOF || url_feof(mFormatCtx->pb))
1245 if (ret == AVERROR_EOF) {
1246 //LOGV("ret == AVERROR_EOF");
1248 if (url_feof(mFormatCtx->pb)) {
1249 //LOGV("url_feof(mFormatCtx->pb)");
1254 if (mFormatCtx->pb && mFormatCtx->pb->error) {
1255 LOGE("mFormatCtx->pb->error: %d", mFormatCtx->pb->error);
1262 if (pkt->stream_index == mVideoStreamIdx) {
1263 if (mDefersToCreateVideoTrack) {
1264 AVCodecContext *avctx = mFormatCtx->streams[mVideoStreamIdx]->codec;
1266 int i = parser_split(avctx, pkt->data, pkt->size);
1267 if (i > 0 && i < FF_MAX_EXTRADATA_SIZE) {
1268 if (avctx->extradata)
1269 av_freep(&avctx->extradata);
1270 avctx->extradata_size= i;
1271 avctx->extradata = (uint8_t *)av_malloc(avctx->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
1272 if (!avctx->extradata) {
1273 //return AVERROR(ENOMEM);
1274 ret = AVERROR(ENOMEM);
1277 // sps + pps(there may be sei in it)
1278 memcpy(avctx->extradata, pkt->data, avctx->extradata_size);
1279 memset(avctx->extradata + i, 0, FF_INPUT_BUFFER_PADDING_SIZE);
1281 av_free_packet(pkt);
1285 stream_component_open(mVideoStreamIdx);
1286 if (!mDefersToCreateVideoTrack)
1287 LOGI("probe packet counter: %d when create video track ok", mProbePkts);
1288 if (mProbePkts == EXTRACTOR_MAX_PROBE_PACKETS)
1289 LOGI("probe packet counter to max: %d, create video track: %d",
1290 mProbePkts, !mDefersToCreateVideoTrack);
1292 } else if (pkt->stream_index == mAudioStreamIdx) {
1296 AVCodecContext *avctx = mFormatCtx->streams[mAudioStreamIdx]->codec;
1297 if (mAudioBsfc && pkt && pkt->data) {
1298 ret = av_bitstream_filter_filter(mAudioBsfc, avctx, NULL, &outbuf, &outbuf_size,
1299 pkt->data, pkt->size, pkt->flags & AV_PKT_FLAG_KEY);
1301 if (ret < 0 ||!outbuf_size) {
1302 av_free_packet(pkt);
1305 if (outbuf && outbuf != pkt->data) {
1306 memmove(pkt->data, outbuf, outbuf_size);
1307 pkt->size = outbuf_size;
1310 if (mDefersToCreateAudioTrack) {
1311 if (avctx->extradata_size <= 0) {
1312 av_free_packet(pkt);
1315 stream_component_open(mAudioStreamIdx);
1316 if (!mDefersToCreateAudioTrack)
1317 LOGI("probe packet counter: %d when create audio track ok", mProbePkts);
1318 if (mProbePkts == EXTRACTOR_MAX_PROBE_PACKETS)
1319 LOGI("probe packet counter to max: %d, create audio track: %d",
1320 mProbePkts, !mDefersToCreateAudioTrack);
1324 /* check if packet is in play range specified by user, then queue, otherwise discard */
1325 pkt_in_play_range = mDuration == AV_NOPTS_VALUE ||
1326 (pkt->pts - mFormatCtx->streams[pkt->stream_index]->start_time) *
1327 av_q2d(mFormatCtx->streams[pkt->stream_index]->time_base) -
1328 (double)(mStartTime != AV_NOPTS_VALUE ? mStartTime : 0) / 1000000
1329 <= ((double)mDuration / 1000000);
1330 if (pkt->stream_index == mAudioStreamIdx && pkt_in_play_range) {
1331 packet_queue_put(&mAudioQ, pkt);
1332 } else if (pkt->stream_index == mVideoStreamIdx && pkt_in_play_range) {
1333 packet_queue_put(&mVideoQ, pkt);
1335 av_free_packet(pkt);
1338 /* wait until the end */
1339 while (!mAbortRequest) {
1345 LOGI("reader thread goto end...");
1347 /* close each stream */
1348 if (mAudioStreamIdx >= 0)
1349 stream_component_close(mAudioStreamIdx);
1350 if (mVideoStreamIdx >= 0)
1351 stream_component_close(mVideoStreamIdx);
1353 avformat_close_input(&mFormatCtx);
1357 ////////////////////////////////////////////////////////////////////////////////
1359 FFmpegExtractor::Track::Track(
1360 const sp<FFmpegExtractor> &extractor, sp<MetaData> meta, bool isAVC,
1361 AVStream *stream, PacketQueue *queue)
1362 : mExtractor(extractor),
1369 /* H.264 Video Types */
1371 mNal2AnnexB = false;
1377 CHECK(meta->findData(kKeyAVCC, &type, &data, &size));
1379 const uint8_t *ptr = (const uint8_t *)data;
1382 CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
1384 // The number of bytes used to encode the length of a NAL unit.
1385 mNALLengthSize = 1 + (ptr[4] & 3);
1387 LOGV("the stream is AVC, the length of a NAL unit: %d", mNALLengthSize);
1393 mMediaType = mStream->codec->codec_type;
1396 FFmpegExtractor::Track::~Track() {
1399 status_t FFmpegExtractor::Track::start(MetaData *params) {
1400 Mutex::Autolock autoLock(mLock);
1401 //mExtractor->startReaderThread();
1405 status_t FFmpegExtractor::Track::stop() {
1406 Mutex::Autolock autoLock(mLock);
1407 mExtractor->stopReaderThread();
1411 sp<MetaData> FFmpegExtractor::Track::getFormat() {
1412 Mutex::Autolock autoLock(mLock);
1417 status_t FFmpegExtractor::Track::read(
1418 MediaBuffer **buffer, const ReadOptions *options) {
1421 Mutex::Autolock autoLock(mLock);
1424 bool seeking = false;
1425 bool waitKeyPkt = false;
1426 ReadOptions::SeekMode mode;
1427 int64_t pktTS = AV_NOPTS_VALUE;
1428 int64_t seekTimeUs = AV_NOPTS_VALUE;
1431 status_t status = OK;
1433 if (options && options->getSeekTo(&seekTimeUs, &mode)) {
1434 LOGV("~~~%s seekTimeUs: %lld, mode: %d", av_get_media_type_string(mMediaType), seekTimeUs, mode);
1435 if (mExtractor->stream_seek(seekTimeUs, mMediaType) == SEEK)
1440 if (mExtractor->packet_queue_get(mQueue, &pkt, 1) < 0) {
1441 mExtractor->reachedEOS(mMediaType);
1442 return ERROR_END_OF_STREAM;
1446 if (pkt.data != flush_pkt.data) {
1447 av_free_packet(&pkt);
1451 #if WAIT_KEY_PACKET_AFTER_SEEK
1457 if (pkt.data == flush_pkt.data) {
1458 LOGV("read %s flush pkt", av_get_media_type_string(mMediaType));
1459 av_free_packet(&pkt);
1461 } else if (pkt.data == NULL && pkt.size == 0) {
1462 LOGV("read %s eos pkt", av_get_media_type_string(mMediaType));
1463 av_free_packet(&pkt);
1464 mExtractor->reachedEOS(mMediaType);
1465 return ERROR_END_OF_STREAM;
1468 key = pkt.flags & AV_PKT_FLAG_KEY ? 1 : 0;
1472 LOGV("drop the no key packet");
1473 av_free_packet(&pkt);
1476 LOGV("~~~~~~ got the key packet");
1481 MediaBuffer *mediaBuffer = new MediaBuffer(pkt.size + FF_INPUT_BUFFER_PADDING_SIZE);
1482 mediaBuffer->meta_data()->clear();
1483 mediaBuffer->set_range(0, pkt.size);
1484 #if DISABLE_NAL_TO_ANNEXB
1485 mNal2AnnexB = false;
1487 if (mIsAVC && mNal2AnnexB) {
1488 /* Convert H.264 NAL format to annex b */
1489 if (mNALLengthSize >= 3 && mNALLengthSize <= 4 )
1491 uint8_t *dst = (uint8_t *)mediaBuffer->data();
1493 /* This only works for NAL sizes 3-4 */
1494 size_t len = pkt.size, i;
1495 uint8_t *ptr = pkt.data;
1496 while (len >= mNALLengthSize) {
1497 uint32_t nal_len = 0;
1498 for( i = 0; i < mNALLengthSize; i++ ) {
1499 nal_len = (nal_len << 8) | ptr[i];
1502 dst[mNALLengthSize - 1] = 1;
1503 if (nal_len > INT_MAX || nal_len > (unsigned int)len) {
1504 status = ERROR_MALFORMED;
1507 dst += mNALLengthSize;
1508 ptr += mNALLengthSize;
1509 len -= mNALLengthSize;
1511 memcpy(dst, ptr, nal_len);
1518 status = ERROR_MALFORMED;
1522 LOGV("status != OK");
1523 mediaBuffer->release();
1525 av_free_packet(&pkt);
1526 return ERROR_MALFORMED;
1529 memcpy(mediaBuffer->data(), pkt.data, pkt.size);
1534 if (pkt.pts == AV_NOPTS_VALUE)
1538 // TODO, Stagefright can't handle negative timestamps
1539 // if needed, work around this by offsetting them manually?
1544 timeUs = (int64_t)(pktTS * av_q2d(mStream->time_base) * 1000000);
1547 LOGV("read %s pkt, size: %d, key: %d, pts: %lld, dts: %lld, timeUs: %llu us (%.2f secs)",
1548 av_get_media_type_string(mMediaType), pkt.size, key, pkt.pts, pkt.dts, timeUs, timeUs/1E6);
1552 // TODO, Stagefright can't handle negative timestamps
1553 // if needed, work around this by offsetting them manually?
1558 mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
1559 mediaBuffer->meta_data()->setInt32(kKeyIsSyncFrame, key);
1561 *buffer = mediaBuffer;
1563 av_free_packet(&pkt);
1568 ////////////////////////////////////////////////////////////////////////////////
1570 // LegacySniffFFMPEG
1572 const char *extension;
1573 const char *container;
1576 static extmap FILE_EXTS[] = {
1577 {".mp4", MEDIA_MIMETYPE_CONTAINER_MPEG4},
1578 {".3gp", MEDIA_MIMETYPE_CONTAINER_MPEG4},
1579 {".mp3", MEDIA_MIMETYPE_AUDIO_MPEG},
1580 {".mov", MEDIA_MIMETYPE_CONTAINER_MOV},
1581 {".mkv", MEDIA_MIMETYPE_CONTAINER_MATROSKA},
1582 {".ts", MEDIA_MIMETYPE_CONTAINER_TS},
1583 {".avi", MEDIA_MIMETYPE_CONTAINER_AVI},
1584 {".asf", MEDIA_MIMETYPE_CONTAINER_ASF},
1586 {".wmv", MEDIA_MIMETYPE_CONTAINER_WMV},
1587 {".wma", MEDIA_MIMETYPE_CONTAINER_WMA},
1588 {".mpg", MEDIA_MIMETYPE_CONTAINER_MPG},
1589 {".flv", MEDIA_MIMETYPE_CONTAINER_FLV},
1590 {".divx", MEDIA_MIMETYPE_CONTAINER_DIVX},
1591 {".mp2", MEDIA_MIMETYPE_CONTAINER_MP2},
1592 {".ape", MEDIA_MIMETYPE_CONTAINER_APE},
1593 {".rm ", MEDIA_MIMETYPE_CONTAINER_RM},
1594 {".ra", MEDIA_MIMETYPE_CONTAINER_RA},
1598 const char *LegacySniffFFMPEG(const char * uri)
1601 const char *container = NULL;
1603 LOGI("list the file extensions suppoted by ffmpeg: ");
1604 LOGI("========================================");
1605 for (i = 0; i < NELEM(FILE_EXTS); ++i) {
1606 LOGV("file_exts[%02d]: %s", i, FILE_EXTS[i].extension);
1608 LOGI("========================================");
1610 int lenURI = strlen(uri);
1611 for (i = 0; i < NELEM(FILE_EXTS); ++i) {
1612 int len = strlen(FILE_EXTS[i].extension);
1613 int start = lenURI - len;
1615 if (!av_strncasecmp(uri + start, FILE_EXTS[i].extension, len)) {
1616 container = FILE_EXTS[i].container;
1625 // BetterSniffFFMPEG
1628 const char *container;
1631 static formatmap FILE_FORMATS[] = {
1632 {"mpegts", MEDIA_MIMETYPE_CONTAINER_TS},
1633 {"mov,mp4,m4a,3gp,3g2,mj2", MEDIA_MIMETYPE_CONTAINER_MOV},
1634 {"asf", MEDIA_MIMETYPE_CONTAINER_ASF},
1637 const char *BetterSniffFFMPEG(const char * uri)
1640 const char *container = NULL;
1641 AVFormatContext *ic = NULL;
1643 status_t status = initFFmpeg();
1645 LOGE("could not init ffmpeg");
1649 ic = avformat_alloc_context();
1650 avformat_open_input(&ic, uri, NULL, NULL);
1652 av_dump_format(ic, 0, uri, 0);
1654 LOGI("FFmpegExtrator, uri: %s, format_name: %s, format_long_name: %s", uri, ic->iformat->name, ic->iformat->long_name);
1656 LOGI("list the format suppoted by ffmpeg: ");
1657 LOGI("========================================");
1658 for (i = 0; i < NELEM(FILE_FORMATS); ++i) {
1659 LOGV("format_names[%02d]: %s", i, FILE_FORMATS[i].format);
1661 LOGI("========================================");
1663 for (i = 0; i < NELEM(FILE_FORMATS); ++i) {
1664 int len = strlen(FILE_FORMATS[i].format);
1665 if (!av_strncasecmp(ic->iformat->name, FILE_FORMATS[i].format, len)) {
1666 container = FILE_FORMATS[i].container;
1671 avformat_close_input(&ic);
1678 const sp<DataSource> &source, String8 *mimeType, float *confidence,
1679 sp<AMessage> *meta) {
1680 LOGV("SniffFFMPEG");
1681 const char *uri, *container = NULL;
1683 uri = source->getNamURI();
1688 LOGI("ffmpeg uri: %s", uri);
1690 container = BetterSniffFFMPEG(uri);
1692 LOGW("sniff through LegacySniffFFMPEG, only check the file extension");
1693 container = LegacySniffFFMPEG(uri);
1696 if (container == NULL)
1699 LOGV("found container: %s", container);
1701 *confidence = 0.88f; // Slightly larger than other extractor's confidence
1702 mimeType->setTo(container);
1704 /* use MPEG4Extractor(not extended extractor) for HTTP source only */
1705 if (!av_strcasecmp(container, MEDIA_MIMETYPE_CONTAINER_MPEG4)
1706 && (source->flags() & DataSource::kIsCachingDataSource)) {
1710 *meta = new AMessage;
1711 (*meta)->setString("extended-extractor", "extended-extractor");
1712 (*meta)->setString("extended-extractor-subtype", "ffmpegextractor");
1717 } // namespace android