1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "MPEG2PSExtractor"
19 #include <utils/Log.h>
20
21 #include "include/MPEG2PSExtractor.h"
22
23 #include "AnotherPacketSource.h"
24 #include "ESQueue.h"
25
26 #include <media/stagefright/foundation/ABitReader.h>
27 #include <media/stagefright/foundation/ABuffer.h>
28 #include <media/stagefright/foundation/ADebug.h>
29 #include <media/stagefright/foundation/AMessage.h>
30 #include <media/stagefright/foundation/hexdump.h>
31 #include <media/stagefright/DataSource.h>
32 #include <media/stagefright/MediaDefs.h>
33 #include <media/stagefright/MediaErrors.h>
34 #include <media/stagefright/MediaSource.h>
35 #include <media/stagefright/MetaData.h>
36 #include <media/stagefright/Utils.h>
37 #include <utils/String8.h>
38
39 namespace android {
40
41 struct MPEG2PSExtractor::Track : public MediaSource {
42 Track(MPEG2PSExtractor *extractor,
43 unsigned stream_id, unsigned stream_type);
44
45 virtual status_t start(MetaData *params);
46 virtual status_t stop();
47 virtual sp<MetaData> getFormat();
48
49 virtual status_t read(
50 MediaBuffer **buffer, const ReadOptions *options);
51
52 protected:
53 virtual ~Track();
54
55 private:
56 friend struct MPEG2PSExtractor;
57
58 MPEG2PSExtractor *mExtractor;
59
60 unsigned mStreamID;
61 unsigned mStreamType;
62 ElementaryStreamQueue *mQueue;
63 sp<AnotherPacketSource> mSource;
64
65 status_t appendPESData(
66 unsigned PTS_DTS_flags,
67 uint64_t PTS, uint64_t DTS,
68 const uint8_t *data, size_t size);
69
70 DISALLOW_EVIL_CONSTRUCTORS(Track);
71 };
72
73 struct MPEG2PSExtractor::WrappedTrack : public MediaSource {
74 WrappedTrack(const sp<MPEG2PSExtractor> &extractor, const sp<Track> &track);
75
76 virtual status_t start(MetaData *params);
77 virtual status_t stop();
78 virtual sp<MetaData> getFormat();
79
80 virtual status_t read(
81 MediaBuffer **buffer, const ReadOptions *options);
82
83 protected:
84 virtual ~WrappedTrack();
85
86 private:
87 sp<MPEG2PSExtractor> mExtractor;
88 sp<MPEG2PSExtractor::Track> mTrack;
89
90 DISALLOW_EVIL_CONSTRUCTORS(WrappedTrack);
91 };
92
93 ////////////////////////////////////////////////////////////////////////////////
94
MPEG2PSExtractor(const sp<DataSource> & source)95 MPEG2PSExtractor::MPEG2PSExtractor(const sp<DataSource> &source)
96 : mDataSource(source),
97 mOffset(0),
98 mFinalResult(OK),
99 mBuffer(new ABuffer(0)),
100 mScanning(true),
101 mProgramStreamMapValid(false) {
102 for (size_t i = 0; i < 500; ++i) {
103 if (feedMore() != OK) {
104 break;
105 }
106 }
107
108 // Remove all tracks that were unable to determine their format.
109 for (size_t i = mTracks.size(); i-- > 0;) {
110 if (mTracks.valueAt(i)->getFormat() == NULL) {
111 mTracks.removeItemsAt(i);
112 }
113 }
114
115 mScanning = false;
116 }
117
~MPEG2PSExtractor()118 MPEG2PSExtractor::~MPEG2PSExtractor() {
119 }
120
countTracks()121 size_t MPEG2PSExtractor::countTracks() {
122 return mTracks.size();
123 }
124
getTrack(size_t index)125 sp<MediaSource> MPEG2PSExtractor::getTrack(size_t index) {
126 if (index >= mTracks.size()) {
127 return NULL;
128 }
129
130 return new WrappedTrack(this, mTracks.valueAt(index));
131 }
132
getTrackMetaData(size_t index,uint32_t flags)133 sp<MetaData> MPEG2PSExtractor::getTrackMetaData(size_t index, uint32_t flags) {
134 if (index >= mTracks.size()) {
135 return NULL;
136 }
137
138 return mTracks.valueAt(index)->getFormat();
139 }
140
getMetaData()141 sp<MetaData> MPEG2PSExtractor::getMetaData() {
142 sp<MetaData> meta = new MetaData;
143 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2PS);
144
145 return meta;
146 }
147
flags() const148 uint32_t MPEG2PSExtractor::flags() const {
149 return CAN_PAUSE;
150 }
151
feedMore()152 status_t MPEG2PSExtractor::feedMore() {
153 Mutex::Autolock autoLock(mLock);
154
155 // How much data we're reading at a time
156 static const size_t kChunkSize = 8192;
157
158 for (;;) {
159 status_t err = dequeueChunk();
160
161 if (err == -EAGAIN && mFinalResult == OK) {
162 memmove(mBuffer->base(), mBuffer->data(), mBuffer->size());
163 mBuffer->setRange(0, mBuffer->size());
164
165 if (mBuffer->size() + kChunkSize > mBuffer->capacity()) {
166 size_t newCapacity = mBuffer->capacity() + kChunkSize;
167 sp<ABuffer> newBuffer = new ABuffer(newCapacity);
168 memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
169 newBuffer->setRange(0, mBuffer->size());
170 mBuffer = newBuffer;
171 }
172
173 ssize_t n = mDataSource->readAt(
174 mOffset, mBuffer->data() + mBuffer->size(), kChunkSize);
175
176 if (n < (ssize_t)kChunkSize) {
177 mFinalResult = (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
178 return mFinalResult;
179 }
180
181 mBuffer->setRange(mBuffer->offset(), mBuffer->size() + n);
182 mOffset += n;
183 } else if (err != OK) {
184 mFinalResult = err;
185 return err;
186 } else {
187 return OK;
188 }
189 }
190 }
191
dequeueChunk()192 status_t MPEG2PSExtractor::dequeueChunk() {
193 if (mBuffer->size() < 4) {
194 return -EAGAIN;
195 }
196
197 if (memcmp("\x00\x00\x01", mBuffer->data(), 3)) {
198 return ERROR_MALFORMED;
199 }
200
201 unsigned chunkType = mBuffer->data()[3];
202
203 ssize_t res;
204
205 switch (chunkType) {
206 case 0xba:
207 {
208 res = dequeuePack();
209 break;
210 }
211
212 case 0xbb:
213 {
214 res = dequeueSystemHeader();
215 break;
216 }
217
218 default:
219 {
220 res = dequeuePES();
221 break;
222 }
223 }
224
225 if (res > 0) {
226 if (mBuffer->size() < (size_t)res) {
227 return -EAGAIN;
228 }
229
230 mBuffer->setRange(mBuffer->offset() + res, mBuffer->size() - res);
231 res = OK;
232 }
233
234 return res;
235 }
236
dequeuePack()237 ssize_t MPEG2PSExtractor::dequeuePack() {
238 // 32 + 2 + 3 + 1 + 15 + 1 + 15+ 1 + 9 + 1 + 22 + 1 + 1 | +5
239
240 if (mBuffer->size() < 14) {
241 return -EAGAIN;
242 }
243
244 unsigned pack_stuffing_length = mBuffer->data()[13] & 7;
245
246 return pack_stuffing_length + 14;
247 }
248
dequeueSystemHeader()249 ssize_t MPEG2PSExtractor::dequeueSystemHeader() {
250 if (mBuffer->size() < 6) {
251 return -EAGAIN;
252 }
253
254 unsigned header_length = U16_AT(mBuffer->data() + 4);
255
256 return header_length + 6;
257 }
258
dequeuePES()259 ssize_t MPEG2PSExtractor::dequeuePES() {
260 if (mBuffer->size() < 6) {
261 return -EAGAIN;
262 }
263
264 unsigned PES_packet_length = U16_AT(mBuffer->data() + 4);
265 CHECK_NE(PES_packet_length, 0u);
266
267 size_t n = PES_packet_length + 6;
268
269 if (mBuffer->size() < n) {
270 return -EAGAIN;
271 }
272
273 ABitReader br(mBuffer->data(), n);
274
275 unsigned packet_startcode_prefix = br.getBits(24);
276
277 ALOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
278
279 if (packet_startcode_prefix != 1) {
280 ALOGV("Supposedly payload_unit_start=1 unit does not start "
281 "with startcode.");
282
283 return ERROR_MALFORMED;
284 }
285
286 CHECK_EQ(packet_startcode_prefix, 0x000001u);
287
288 unsigned stream_id = br.getBits(8);
289 ALOGV("stream_id = 0x%02x", stream_id);
290
291 /* unsigned PES_packet_length = */br.getBits(16);
292
293 if (stream_id == 0xbc) {
294 // program_stream_map
295
296 if (!mScanning) {
297 return n;
298 }
299
300 mStreamTypeByESID.clear();
301
302 /* unsigned current_next_indicator = */br.getBits(1);
303 /* unsigned reserved = */br.getBits(2);
304 /* unsigned program_stream_map_version = */br.getBits(5);
305 /* unsigned reserved = */br.getBits(7);
306 /* unsigned marker_bit = */br.getBits(1);
307 unsigned program_stream_info_length = br.getBits(16);
308
309 size_t offset = 0;
310 while (offset < program_stream_info_length) {
311 if (offset + 2 > program_stream_info_length) {
312 return ERROR_MALFORMED;
313 }
314
315 unsigned descriptor_tag = br.getBits(8);
316 unsigned descriptor_length = br.getBits(8);
317
318 ALOGI("found descriptor tag 0x%02x of length %u",
319 descriptor_tag, descriptor_length);
320
321 if (offset + 2 + descriptor_length > program_stream_info_length) {
322 return ERROR_MALFORMED;
323 }
324
325 br.skipBits(8 * descriptor_length);
326
327 offset += 2 + descriptor_length;
328 }
329
330 unsigned elementary_stream_map_length = br.getBits(16);
331
332 offset = 0;
333 while (offset < elementary_stream_map_length) {
334 if (offset + 4 > elementary_stream_map_length) {
335 return ERROR_MALFORMED;
336 }
337
338 unsigned stream_type = br.getBits(8);
339 unsigned elementary_stream_id = br.getBits(8);
340
341 ALOGI("elementary stream id 0x%02x has stream type 0x%02x",
342 elementary_stream_id, stream_type);
343
344 mStreamTypeByESID.add(elementary_stream_id, stream_type);
345
346 unsigned elementary_stream_info_length = br.getBits(16);
347
348 if (offset + 4 + elementary_stream_info_length
349 > elementary_stream_map_length) {
350 return ERROR_MALFORMED;
351 }
352
353 offset += 4 + elementary_stream_info_length;
354 }
355
356 /* unsigned CRC32 = */br.getBits(32);
357
358 mProgramStreamMapValid = true;
359 } else if (stream_id != 0xbe // padding_stream
360 && stream_id != 0xbf // private_stream_2
361 && stream_id != 0xf0 // ECM
362 && stream_id != 0xf1 // EMM
363 && stream_id != 0xff // program_stream_directory
364 && stream_id != 0xf2 // DSMCC
365 && stream_id != 0xf8) { // H.222.1 type E
366 CHECK_EQ(br.getBits(2), 2u);
367
368 /* unsigned PES_scrambling_control = */br.getBits(2);
369 /* unsigned PES_priority = */br.getBits(1);
370 /* unsigned data_alignment_indicator = */br.getBits(1);
371 /* unsigned copyright = */br.getBits(1);
372 /* unsigned original_or_copy = */br.getBits(1);
373
374 unsigned PTS_DTS_flags = br.getBits(2);
375 ALOGV("PTS_DTS_flags = %u", PTS_DTS_flags);
376
377 unsigned ESCR_flag = br.getBits(1);
378 ALOGV("ESCR_flag = %u", ESCR_flag);
379
380 unsigned ES_rate_flag = br.getBits(1);
381 ALOGV("ES_rate_flag = %u", ES_rate_flag);
382
383 unsigned DSM_trick_mode_flag = br.getBits(1);
384 ALOGV("DSM_trick_mode_flag = %u", DSM_trick_mode_flag);
385
386 unsigned additional_copy_info_flag = br.getBits(1);
387 ALOGV("additional_copy_info_flag = %u", additional_copy_info_flag);
388
389 /* unsigned PES_CRC_flag = */br.getBits(1);
390 /* PES_extension_flag = */br.getBits(1);
391
392 unsigned PES_header_data_length = br.getBits(8);
393 ALOGV("PES_header_data_length = %u", PES_header_data_length);
394
395 unsigned optional_bytes_remaining = PES_header_data_length;
396
397 uint64_t PTS = 0, DTS = 0;
398
399 if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
400 CHECK_GE(optional_bytes_remaining, 5u);
401
402 CHECK_EQ(br.getBits(4), PTS_DTS_flags);
403
404 PTS = ((uint64_t)br.getBits(3)) << 30;
405 CHECK_EQ(br.getBits(1), 1u);
406 PTS |= ((uint64_t)br.getBits(15)) << 15;
407 CHECK_EQ(br.getBits(1), 1u);
408 PTS |= br.getBits(15);
409 CHECK_EQ(br.getBits(1), 1u);
410
411 ALOGV("PTS = %llu", PTS);
412 // ALOGI("PTS = %.2f secs", PTS / 90000.0f);
413
414 optional_bytes_remaining -= 5;
415
416 if (PTS_DTS_flags == 3) {
417 CHECK_GE(optional_bytes_remaining, 5u);
418
419 CHECK_EQ(br.getBits(4), 1u);
420
421 DTS = ((uint64_t)br.getBits(3)) << 30;
422 CHECK_EQ(br.getBits(1), 1u);
423 DTS |= ((uint64_t)br.getBits(15)) << 15;
424 CHECK_EQ(br.getBits(1), 1u);
425 DTS |= br.getBits(15);
426 CHECK_EQ(br.getBits(1), 1u);
427
428 ALOGV("DTS = %llu", DTS);
429
430 optional_bytes_remaining -= 5;
431 }
432 }
433
434 if (ESCR_flag) {
435 CHECK_GE(optional_bytes_remaining, 6u);
436
437 br.getBits(2);
438
439 uint64_t ESCR = ((uint64_t)br.getBits(3)) << 30;
440 CHECK_EQ(br.getBits(1), 1u);
441 ESCR |= ((uint64_t)br.getBits(15)) << 15;
442 CHECK_EQ(br.getBits(1), 1u);
443 ESCR |= br.getBits(15);
444 CHECK_EQ(br.getBits(1), 1u);
445
446 ALOGV("ESCR = %llu", ESCR);
447 /* unsigned ESCR_extension = */br.getBits(9);
448
449 CHECK_EQ(br.getBits(1), 1u);
450
451 optional_bytes_remaining -= 6;
452 }
453
454 if (ES_rate_flag) {
455 CHECK_GE(optional_bytes_remaining, 3u);
456
457 CHECK_EQ(br.getBits(1), 1u);
458 /* unsigned ES_rate = */br.getBits(22);
459 CHECK_EQ(br.getBits(1), 1u);
460
461 optional_bytes_remaining -= 3;
462 }
463
464 br.skipBits(optional_bytes_remaining * 8);
465
466 // ES data follows.
467
468 CHECK_GE(PES_packet_length, PES_header_data_length + 3);
469
470 unsigned dataLength =
471 PES_packet_length - 3 - PES_header_data_length;
472
473 if (br.numBitsLeft() < dataLength * 8) {
474 ALOGE("PES packet does not carry enough data to contain "
475 "payload. (numBitsLeft = %d, required = %d)",
476 br.numBitsLeft(), dataLength * 8);
477
478 return ERROR_MALFORMED;
479 }
480
481 CHECK_GE(br.numBitsLeft(), dataLength * 8);
482
483 ssize_t index = mTracks.indexOfKey(stream_id);
484 if (index < 0 && mScanning) {
485 unsigned streamType;
486
487 ssize_t streamTypeIndex;
488 if (mProgramStreamMapValid
489 && (streamTypeIndex =
490 mStreamTypeByESID.indexOfKey(stream_id)) >= 0) {
491 streamType = mStreamTypeByESID.valueAt(streamTypeIndex);
492 } else if ((stream_id & ~0x1f) == 0xc0) {
493 // ISO/IEC 13818-3 or ISO/IEC 11172-3 or ISO/IEC 13818-7
494 // or ISO/IEC 14496-3 audio
495 streamType = ATSParser::STREAMTYPE_MPEG2_AUDIO;
496 } else if ((stream_id & ~0x0f) == 0xe0) {
497 // ISO/IEC 13818-2 or ISO/IEC 11172-2 or ISO/IEC 14496-2 video
498 streamType = ATSParser::STREAMTYPE_MPEG2_VIDEO;
499 } else {
500 streamType = ATSParser::STREAMTYPE_RESERVED;
501 }
502
503 index = mTracks.add(
504 stream_id, new Track(this, stream_id, streamType));
505 }
506
507 status_t err = OK;
508
509 if (index >= 0) {
510 err =
511 mTracks.editValueAt(index)->appendPESData(
512 PTS_DTS_flags, PTS, DTS, br.data(), dataLength);
513 }
514
515 br.skipBits(dataLength * 8);
516
517 if (err != OK) {
518 return err;
519 }
520 } else if (stream_id == 0xbe) { // padding_stream
521 CHECK_NE(PES_packet_length, 0u);
522 br.skipBits(PES_packet_length * 8);
523 } else {
524 CHECK_NE(PES_packet_length, 0u);
525 br.skipBits(PES_packet_length * 8);
526 }
527
528 return n;
529 }
530
531 ////////////////////////////////////////////////////////////////////////////////
532
Track(MPEG2PSExtractor * extractor,unsigned stream_id,unsigned stream_type)533 MPEG2PSExtractor::Track::Track(
534 MPEG2PSExtractor *extractor, unsigned stream_id, unsigned stream_type)
535 : mExtractor(extractor),
536 mStreamID(stream_id),
537 mStreamType(stream_type),
538 mQueue(NULL) {
539 bool supported = true;
540 ElementaryStreamQueue::Mode mode;
541
542 switch (mStreamType) {
543 case ATSParser::STREAMTYPE_H264:
544 mode = ElementaryStreamQueue::H264;
545 break;
546 case ATSParser::STREAMTYPE_MPEG2_AUDIO_ADTS:
547 mode = ElementaryStreamQueue::AAC;
548 break;
549 case ATSParser::STREAMTYPE_MPEG1_AUDIO:
550 case ATSParser::STREAMTYPE_MPEG2_AUDIO:
551 mode = ElementaryStreamQueue::MPEG_AUDIO;
552 break;
553
554 case ATSParser::STREAMTYPE_MPEG1_VIDEO:
555 case ATSParser::STREAMTYPE_MPEG2_VIDEO:
556 mode = ElementaryStreamQueue::MPEG_VIDEO;
557 break;
558
559 case ATSParser::STREAMTYPE_MPEG4_VIDEO:
560 mode = ElementaryStreamQueue::MPEG4_VIDEO;
561 break;
562
563 default:
564 supported = false;
565 break;
566 }
567
568 if (supported) {
569 mQueue = new ElementaryStreamQueue(mode);
570 } else {
571 ALOGI("unsupported stream ID 0x%02x", stream_id);
572 }
573 }
574
~Track()575 MPEG2PSExtractor::Track::~Track() {
576 delete mQueue;
577 mQueue = NULL;
578 }
579
start(MetaData * params)580 status_t MPEG2PSExtractor::Track::start(MetaData *params) {
581 if (mSource == NULL) {
582 return NO_INIT;
583 }
584
585 return mSource->start(params);
586 }
587
stop()588 status_t MPEG2PSExtractor::Track::stop() {
589 if (mSource == NULL) {
590 return NO_INIT;
591 }
592
593 return mSource->stop();
594 }
595
getFormat()596 sp<MetaData> MPEG2PSExtractor::Track::getFormat() {
597 if (mSource == NULL) {
598 return NULL;
599 }
600
601 return mSource->getFormat();
602 }
603
read(MediaBuffer ** buffer,const ReadOptions * options)604 status_t MPEG2PSExtractor::Track::read(
605 MediaBuffer **buffer, const ReadOptions *options) {
606 if (mSource == NULL) {
607 return NO_INIT;
608 }
609
610 status_t finalResult;
611 while (!mSource->hasBufferAvailable(&finalResult)) {
612 if (finalResult != OK) {
613 return ERROR_END_OF_STREAM;
614 }
615
616 status_t err = mExtractor->feedMore();
617
618 if (err != OK) {
619 mSource->signalEOS(err);
620 }
621 }
622
623 return mSource->read(buffer, options);
624 }
625
appendPESData(unsigned PTS_DTS_flags,uint64_t PTS,uint64_t DTS,const uint8_t * data,size_t size)626 status_t MPEG2PSExtractor::Track::appendPESData(
627 unsigned PTS_DTS_flags,
628 uint64_t PTS, uint64_t DTS,
629 const uint8_t *data, size_t size) {
630 if (mQueue == NULL) {
631 return OK;
632 }
633
634 int64_t timeUs;
635 if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
636 timeUs = (PTS * 100) / 9;
637 } else {
638 timeUs = 0;
639 }
640
641 status_t err = mQueue->appendData(data, size, timeUs);
642
643 if (err != OK) {
644 return err;
645 }
646
647 sp<ABuffer> accessUnit;
648 while ((accessUnit = mQueue->dequeueAccessUnit()) != NULL) {
649 if (mSource == NULL) {
650 sp<MetaData> meta = mQueue->getFormat();
651
652 if (meta != NULL) {
653 ALOGV("Stream ID 0x%02x now has data.", mStreamID);
654
655 mSource = new AnotherPacketSource(meta);
656 mSource->queueAccessUnit(accessUnit);
657 }
658 } else if (mQueue->getFormat() != NULL) {
659 mSource->queueAccessUnit(accessUnit);
660 }
661 }
662
663 return OK;
664 }
665
666 ////////////////////////////////////////////////////////////////////////////////
667
WrappedTrack(const sp<MPEG2PSExtractor> & extractor,const sp<Track> & track)668 MPEG2PSExtractor::WrappedTrack::WrappedTrack(
669 const sp<MPEG2PSExtractor> &extractor, const sp<Track> &track)
670 : mExtractor(extractor),
671 mTrack(track) {
672 }
673
~WrappedTrack()674 MPEG2PSExtractor::WrappedTrack::~WrappedTrack() {
675 }
676
start(MetaData * params)677 status_t MPEG2PSExtractor::WrappedTrack::start(MetaData *params) {
678 return mTrack->start(params);
679 }
680
stop()681 status_t MPEG2PSExtractor::WrappedTrack::stop() {
682 return mTrack->stop();
683 }
684
getFormat()685 sp<MetaData> MPEG2PSExtractor::WrappedTrack::getFormat() {
686 return mTrack->getFormat();
687 }
688
read(MediaBuffer ** buffer,const ReadOptions * options)689 status_t MPEG2PSExtractor::WrappedTrack::read(
690 MediaBuffer **buffer, const ReadOptions *options) {
691 return mTrack->read(buffer, options);
692 }
693
694 ////////////////////////////////////////////////////////////////////////////////
695
SniffMPEG2PS(const sp<DataSource> & source,String8 * mimeType,float * confidence,sp<AMessage> *)696 bool SniffMPEG2PS(
697 const sp<DataSource> &source, String8 *mimeType, float *confidence,
698 sp<AMessage> *) {
699 uint8_t header[5];
700 if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
701 return false;
702 }
703
704 if (memcmp("\x00\x00\x01\xba", header, 4) || (header[4] >> 6) != 1) {
705 return false;
706 }
707
708 *confidence = 0.25f; // Slightly larger than .mp3 extractor's confidence
709
710 mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2PS);
711
712 return true;
713 }
714
715 } // namespace android
716