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