NuMediaExtractor.cpp revision 1608735ef488ecd8c3c012a3b0d4b1d4ef3d93c7
1/*
2 * Copyright 2012, 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 "NuMediaExtractor"
19#include <utils/Log.h>
20
21#include <media/stagefright/NuMediaExtractor.h>
22
23#include "include/ESDS.h"
24#include "include/WVMExtractor.h"
25
26#include <media/stagefright/foundation/ABuffer.h>
27#include <media/stagefright/foundation/ADebug.h>
28#include <media/stagefright/foundation/AMessage.h>
29#include <media/stagefright/DataSource.h>
30#include <media/stagefright/FileSource.h>
31#include <media/stagefright/MediaBuffer.h>
32#include <media/stagefright/MediaDefs.h>
33#include <media/stagefright/MediaErrors.h>
34#include <media/stagefright/MediaExtractor.h>
35#include <media/stagefright/MediaSource.h>
36#include <media/stagefright/MetaData.h>
37#include <media/stagefright/Utils.h>
38
39namespace android {
40
41NuMediaExtractor::NuMediaExtractor() {
42}
43
44NuMediaExtractor::~NuMediaExtractor() {
45    releaseTrackSamples();
46
47    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
48        TrackInfo *info = &mSelectedTracks.editItemAt(i);
49
50        CHECK_EQ((status_t)OK, info->mSource->stop());
51    }
52
53    mSelectedTracks.clear();
54}
55
56status_t NuMediaExtractor::setDataSource(
57        const char *path, const KeyedVector<String8, String8> *headers) {
58    if (mImpl != NULL) {
59        return -EINVAL;
60    }
61
62    sp<DataSource> dataSource =
63        DataSource::CreateFromURI(path, headers);
64
65    if (dataSource == NULL) {
66        return -ENOENT;
67    }
68
69    if (!strncasecmp("widevine://", path, 11)) {
70        String8 mimeType;
71        float confidence;
72        sp<AMessage> dummy;
73        bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
74
75        if (!success
76                || strcasecmp(
77                    mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
78            return ERROR_UNSUPPORTED;
79        }
80
81        sp<WVMExtractor> extractor = new WVMExtractor(dataSource);
82        extractor->setAdaptiveStreamingMode(true);
83
84        mImpl = extractor;
85    } else {
86        mImpl = MediaExtractor::Create(dataSource);
87    }
88
89    if (mImpl == NULL) {
90        return ERROR_UNSUPPORTED;
91    }
92
93    return OK;
94}
95
96status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
97    if (mImpl != NULL) {
98        return -EINVAL;
99    }
100
101    sp<FileSource> fileSource = new FileSource(dup(fd), offset, size);
102
103    status_t err = fileSource->initCheck();
104    if (err != OK) {
105        return err;
106    }
107
108    mImpl = MediaExtractor::Create(fileSource);
109
110    if (mImpl == NULL) {
111        return ERROR_UNSUPPORTED;
112    }
113
114    return OK;
115}
116
117size_t NuMediaExtractor::countTracks() const {
118    return mImpl == NULL ? 0 : mImpl->countTracks();
119}
120
121status_t NuMediaExtractor::getTrackFormat(
122        size_t index, sp<AMessage> *format) const {
123    *format = NULL;
124
125    if (mImpl == NULL) {
126        return -EINVAL;
127    }
128
129    if (index >= mImpl->countTracks()) {
130        return -ERANGE;
131    }
132
133    sp<MetaData> meta = mImpl->getTrackMetaData(index);
134
135    const char *mime;
136    CHECK(meta->findCString(kKeyMIMEType, &mime));
137
138    sp<AMessage> msg = new AMessage;
139    msg->setString("mime", mime);
140
141    int64_t durationUs;
142    if (meta->findInt64(kKeyDuration, &durationUs)) {
143        msg->setInt64("durationUs", durationUs);
144    }
145
146    if (!strncasecmp("video/", mime, 6)) {
147        int32_t width, height;
148        CHECK(meta->findInt32(kKeyWidth, &width));
149        CHECK(meta->findInt32(kKeyHeight, &height));
150
151        msg->setInt32("width", width);
152        msg->setInt32("height", height);
153    } else {
154        CHECK(!strncasecmp("audio/", mime, 6));
155
156        int32_t numChannels, sampleRate;
157        CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
158        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
159
160        msg->setInt32("channel-count", numChannels);
161        msg->setInt32("sample-rate", sampleRate);
162
163        int32_t isADTS;
164        if (meta->findInt32(kKeyIsADTS, &isADTS)) {
165            msg->setInt32("is-adts", true);
166        }
167    }
168
169    int32_t maxInputSize;
170    if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
171        msg->setInt32("max-input-size", maxInputSize);
172    }
173
174    uint32_t type;
175    const void *data;
176    size_t size;
177    if (meta->findData(kKeyAVCC, &type, &data, &size)) {
178        // Parse the AVCDecoderConfigurationRecord
179
180        const uint8_t *ptr = (const uint8_t *)data;
181
182        CHECK(size >= 7);
183        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
184        uint8_t profile = ptr[1];
185        uint8_t level = ptr[3];
186
187        // There is decodable content out there that fails the following
188        // assertion, let's be lenient for now...
189        // CHECK((ptr[4] >> 2) == 0x3f);  // reserved
190
191        size_t lengthSize = 1 + (ptr[4] & 3);
192
193        // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
194        // violates it...
195        // CHECK((ptr[5] >> 5) == 7);  // reserved
196
197        size_t numSeqParameterSets = ptr[5] & 31;
198
199        ptr += 6;
200        size -= 6;
201
202        sp<ABuffer> buffer = new ABuffer(1024);
203        buffer->setRange(0, 0);
204
205        for (size_t i = 0; i < numSeqParameterSets; ++i) {
206            CHECK(size >= 2);
207            size_t length = U16_AT(ptr);
208
209            ptr += 2;
210            size -= 2;
211
212            CHECK(size >= length);
213
214            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
215            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
216            buffer->setRange(0, buffer->size() + 4 + length);
217
218            ptr += length;
219            size -= length;
220        }
221
222        buffer->meta()->setInt32("csd", true);
223        buffer->meta()->setInt64("timeUs", 0);
224
225        msg->setBuffer("csd-0", buffer);
226
227        buffer = new ABuffer(1024);
228        buffer->setRange(0, 0);
229
230        CHECK(size >= 1);
231        size_t numPictureParameterSets = *ptr;
232        ++ptr;
233        --size;
234
235        for (size_t i = 0; i < numPictureParameterSets; ++i) {
236            CHECK(size >= 2);
237            size_t length = U16_AT(ptr);
238
239            ptr += 2;
240            size -= 2;
241
242            CHECK(size >= length);
243
244            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
245            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
246            buffer->setRange(0, buffer->size() + 4 + length);
247
248            ptr += length;
249            size -= length;
250        }
251
252        buffer->meta()->setInt32("csd", true);
253        buffer->meta()->setInt64("timeUs", 0);
254        msg->setBuffer("csd-1", buffer);
255    } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
256        ESDS esds((const char *)data, size);
257        CHECK_EQ(esds.InitCheck(), (status_t)OK);
258
259        const void *codec_specific_data;
260        size_t codec_specific_data_size;
261        esds.getCodecSpecificInfo(
262                &codec_specific_data, &codec_specific_data_size);
263
264        sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
265
266        memcpy(buffer->data(), codec_specific_data,
267               codec_specific_data_size);
268
269        buffer->meta()->setInt32("csd", true);
270        buffer->meta()->setInt64("timeUs", 0);
271        msg->setBuffer("csd-0", buffer);
272    } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
273        sp<ABuffer> buffer = new ABuffer(size);
274        memcpy(buffer->data(), data, size);
275
276        buffer->meta()->setInt32("csd", true);
277        buffer->meta()->setInt64("timeUs", 0);
278        msg->setBuffer("csd-0", buffer);
279
280        if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) {
281            return -EINVAL;
282        }
283
284        buffer = new ABuffer(size);
285        memcpy(buffer->data(), data, size);
286
287        buffer->meta()->setInt32("csd", true);
288        buffer->meta()->setInt64("timeUs", 0);
289        msg->setBuffer("csd-1", buffer);
290    }
291
292    if (meta->findData(kKeyEMM, &type, &data, &size)) {
293        sp<ABuffer> emm = new ABuffer(size);
294        memcpy(emm->data(), data, size);
295
296        msg->setBuffer("emm", emm);
297    }
298
299    if (meta->findData(kKeyECM, &type, &data, &size)) {
300        sp<ABuffer> ecm = new ABuffer(size);
301        memcpy(ecm->data(), data, size);
302
303        msg->setBuffer("ecm", ecm);
304    }
305
306    *format = msg;
307
308    return OK;
309}
310
311status_t NuMediaExtractor::selectTrack(size_t index) {
312    if (mImpl == NULL) {
313        return -EINVAL;
314    }
315
316    if (index >= mImpl->countTracks()) {
317        return -ERANGE;
318    }
319
320    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
321        TrackInfo *info = &mSelectedTracks.editItemAt(i);
322
323        if (info->mTrackIndex == index) {
324            // This track has already been selected.
325            return OK;
326        }
327    }
328
329    sp<MediaSource> source = mImpl->getTrack(index);
330
331    CHECK_EQ((status_t)OK, source->start());
332
333    mSelectedTracks.push();
334    TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
335
336    info->mSource = source;
337    info->mTrackIndex = index;
338    info->mFinalResult = OK;
339    info->mSample = NULL;
340    info->mSampleTimeUs = -1ll;
341    info->mSampleFlags = 0;
342    info->mTrackFlags = 0;
343
344    const char *mime;
345    CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
346
347    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
348        info->mTrackFlags |= kIsVorbis;
349    }
350
351    return OK;
352}
353
354void NuMediaExtractor::releaseTrackSamples() {
355    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
356        TrackInfo *info = &mSelectedTracks.editItemAt(i);
357
358        if (info->mSample != NULL) {
359            info->mSample->release();
360            info->mSample = NULL;
361
362            info->mSampleTimeUs = -1ll;
363            info->mSampleFlags = 0;
364        }
365    }
366}
367
368ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) {
369    TrackInfo *minInfo = NULL;
370    ssize_t minIndex = -1;
371
372    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
373        TrackInfo *info = &mSelectedTracks.editItemAt(i);
374
375        if (seekTimeUs >= 0ll) {
376            info->mFinalResult = OK;
377
378            if (info->mSample != NULL) {
379                info->mSample->release();
380                info->mSample = NULL;
381                info->mSampleTimeUs = -1ll;
382                info->mSampleFlags = 0;
383            }
384        } else if (info->mFinalResult != OK) {
385            continue;
386        }
387
388        if (info->mSample == NULL) {
389            MediaSource::ReadOptions options;
390            if (seekTimeUs >= 0ll) {
391                options.setSeekTo(seekTimeUs);
392            }
393            status_t err = info->mSource->read(&info->mSample, &options);
394
395            if (err != OK) {
396                CHECK(info->mSample == NULL);
397
398                info->mFinalResult = err;
399                info->mSampleTimeUs = -1ll;
400                info->mSampleFlags = 0;
401                continue;
402            } else {
403                CHECK(info->mSample != NULL);
404                CHECK(info->mSample->meta_data()->findInt64(
405                            kKeyTime, &info->mSampleTimeUs));
406
407                info->mSampleFlags = 0;
408
409                int32_t val;
410                if (info->mSample->meta_data()->findInt32(
411                            kKeyIsSyncFrame, &val) && val != 0) {
412                    info->mSampleFlags |= SAMPLE_FLAG_SYNC;
413                }
414
415                if (info->mSample->meta_data()->findInt32(
416                            kKeyScrambling, &val) && val != 0) {
417                    info->mSampleFlags |= SAMPLE_FLAG_ENCRYPTED;
418                }
419            }
420        }
421
422        if (minInfo == NULL  || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
423            minInfo = info;
424            minIndex = i;
425        }
426    }
427
428    return minIndex;
429}
430
431status_t NuMediaExtractor::seekTo(int64_t timeUs) {
432    return fetchTrackSamples(timeUs);
433}
434
435status_t NuMediaExtractor::advance() {
436    ssize_t minIndex = fetchTrackSamples();
437
438    if (minIndex < 0) {
439        return ERROR_END_OF_STREAM;
440    }
441
442    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
443
444    info->mSample->release();
445    info->mSample = NULL;
446    info->mSampleTimeUs = -1ll;
447
448    return OK;
449}
450
451status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
452    ssize_t minIndex = fetchTrackSamples();
453
454    if (minIndex < 0) {
455        return ERROR_END_OF_STREAM;
456    }
457
458    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
459
460    size_t sampleSize = info->mSample->range_length();
461
462    if (info->mTrackFlags & kIsVorbis) {
463        // Each sample's data is suffixed by the number of page samples
464        // or -1 if not available.
465        sampleSize += sizeof(int32_t);
466    }
467
468    if (buffer->capacity() < sampleSize) {
469        return -ENOMEM;
470    }
471
472    const uint8_t *src =
473        (const uint8_t *)info->mSample->data()
474            + info->mSample->range_offset();
475
476    memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
477
478    if (info->mTrackFlags & kIsVorbis) {
479        int32_t numPageSamples;
480        if (!info->mSample->meta_data()->findInt32(
481                    kKeyValidSamples, &numPageSamples)) {
482            numPageSamples = -1;
483        }
484
485        memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
486               &numPageSamples,
487               sizeof(numPageSamples));
488    }
489
490    buffer->setRange(0, sampleSize);
491
492    return OK;
493}
494
495status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
496    ssize_t minIndex = fetchTrackSamples();
497
498    if (minIndex < 0) {
499        return ERROR_END_OF_STREAM;
500    }
501
502    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
503    *trackIndex = info->mTrackIndex;
504
505    return OK;
506}
507
508status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
509    ssize_t minIndex = fetchTrackSamples();
510
511    if (minIndex < 0) {
512        return ERROR_END_OF_STREAM;
513    }
514
515    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
516    *sampleTimeUs = info->mSampleTimeUs;
517
518    return OK;
519}
520
521status_t NuMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
522    ssize_t minIndex = fetchTrackSamples();
523
524    if (minIndex < 0) {
525        return ERROR_END_OF_STREAM;
526    }
527
528    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
529    *sampleFlags = info->mSampleFlags;
530
531    return OK;
532}
533
534}  // namespace android
535