GenericSource.cpp revision cb48eacb6f8857c7857bb28d6a13d4a0d417f2bd
1/*
2 * Copyright (C) 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#include "GenericSource.h"
18
19#include "AnotherPacketSource.h"
20
21#include <media/stagefright/foundation/ABuffer.h>
22#include <media/stagefright/foundation/ADebug.h>
23#include <media/stagefright/foundation/AMessage.h>
24#include <media/stagefright/DataSource.h>
25#include <media/stagefright/FileSource.h>
26#include <media/stagefright/MediaBuffer.h>
27#include <media/stagefright/MediaDefs.h>
28#include <media/stagefright/MediaExtractor.h>
29#include <media/stagefright/MediaSource.h>
30#include <media/stagefright/MetaData.h>
31#include "../../libstagefright/include/WVMExtractor.h"
32
33namespace android {
34
35NuPlayer::GenericSource::GenericSource(
36        const sp<AMessage> &notify,
37        const sp<IMediaHTTPService> &httpService,
38        const char *url,
39        const KeyedVector<String8, String8> *headers,
40        bool isWidevine,
41        bool uidValid,
42        uid_t uid)
43    : Source(notify),
44      mDurationUs(0ll),
45      mAudioIsVorbis(false),
46      mIsWidevine(isWidevine),
47      mUIDValid(uidValid),
48      mUID(uid) {
49    DataSource::RegisterDefaultSniffers();
50
51    sp<DataSource> dataSource =
52        DataSource::CreateFromURI(httpService, url, headers);
53    CHECK(dataSource != NULL);
54
55    initFromDataSource(dataSource);
56}
57
58NuPlayer::GenericSource::GenericSource(
59        const sp<AMessage> &notify,
60        int fd, int64_t offset, int64_t length)
61    : Source(notify),
62      mDurationUs(0ll),
63      mAudioIsVorbis(false) {
64    DataSource::RegisterDefaultSniffers();
65
66    sp<DataSource> dataSource = new FileSource(dup(fd), offset, length);
67
68    initFromDataSource(dataSource);
69}
70
71void NuPlayer::GenericSource::initFromDataSource(
72        const sp<DataSource> &dataSource) {
73    sp<MediaExtractor> extractor;
74
75    if (mIsWidevine) {
76        String8 mimeType;
77        float confidence;
78        sp<AMessage> dummy;
79        bool success;
80
81        success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
82        if (!success
83                || strcasecmp(
84                    mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
85            ALOGE("unsupported widevine mime: %s", mimeType.string());
86            return;
87        }
88
89        sp<WVMExtractor> wvmExtractor = new WVMExtractor(dataSource);
90        wvmExtractor->setAdaptiveStreamingMode(true);
91        if (mUIDValid) {
92            wvmExtractor->setUID(mUID);
93        }
94        extractor = wvmExtractor;
95    } else {
96        extractor = MediaExtractor::Create(dataSource);
97    }
98
99    CHECK(extractor != NULL);
100
101    sp<MetaData> fileMeta = extractor->getMetaData();
102    if (fileMeta != NULL) {
103        int64_t duration;
104        if (fileMeta->findInt64(kKeyDuration, &duration)) {
105            mDurationUs = duration;
106        }
107    }
108
109    for (size_t i = 0; i < extractor->countTracks(); ++i) {
110        sp<MetaData> meta = extractor->getTrackMetaData(i);
111
112        const char *mime;
113        CHECK(meta->findCString(kKeyMIMEType, &mime));
114
115        sp<MediaSource> track = extractor->getTrack(i);
116
117        if (!strncasecmp(mime, "audio/", 6)) {
118            if (mAudioTrack.mSource == NULL) {
119                mAudioTrack.mIndex = i;
120                mAudioTrack.mSource = track;
121
122                if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
123                    mAudioIsVorbis = true;
124                } else {
125                    mAudioIsVorbis = false;
126                }
127            }
128        } else if (!strncasecmp(mime, "video/", 6)) {
129            if (mVideoTrack.mSource == NULL) {
130                mVideoTrack.mIndex = i;
131                mVideoTrack.mSource = track;
132            }
133        }
134
135        if (track != NULL) {
136            mSources.push(track);
137            int64_t durationUs;
138            if (meta->findInt64(kKeyDuration, &durationUs)) {
139                if (durationUs > mDurationUs) {
140                    mDurationUs = durationUs;
141                }
142            }
143        }
144    }
145}
146
147status_t NuPlayer::GenericSource::setBuffers(bool audio, Vector<MediaBuffer *> &buffers) {
148    if (mIsWidevine && !audio) {
149        return mVideoTrack.mSource->setBuffers(buffers);
150    }
151    return INVALID_OPERATION;
152}
153
154NuPlayer::GenericSource::~GenericSource() {
155}
156
157void NuPlayer::GenericSource::prepareAsync() {
158    if (mVideoTrack.mSource != NULL) {
159        sp<MetaData> meta = mVideoTrack.mSource->getFormat();
160
161        int32_t width, height;
162        CHECK(meta->findInt32(kKeyWidth, &width));
163        CHECK(meta->findInt32(kKeyHeight, &height));
164
165        notifyVideoSizeChanged(width, height);
166    }
167
168    notifyFlagsChanged(
169            (mIsWidevine ? FLAG_SECURE : 0)
170            | FLAG_CAN_PAUSE
171            | FLAG_CAN_SEEK_BACKWARD
172            | FLAG_CAN_SEEK_FORWARD
173            | FLAG_CAN_SEEK);
174
175    notifyPrepared();
176}
177
178void NuPlayer::GenericSource::start() {
179    ALOGI("start");
180
181    if (mAudioTrack.mSource != NULL) {
182        CHECK_EQ(mAudioTrack.mSource->start(), (status_t)OK);
183
184        mAudioTrack.mPackets =
185            new AnotherPacketSource(mAudioTrack.mSource->getFormat());
186
187        readBuffer(true /* audio */);
188    }
189
190    if (mVideoTrack.mSource != NULL) {
191        CHECK_EQ(mVideoTrack.mSource->start(), (status_t)OK);
192
193        mVideoTrack.mPackets =
194            new AnotherPacketSource(mVideoTrack.mSource->getFormat());
195
196        readBuffer(false /* audio */);
197    }
198}
199
200status_t NuPlayer::GenericSource::feedMoreTSData() {
201    return OK;
202}
203
204sp<MetaData> NuPlayer::GenericSource::getFormatMeta(bool audio) {
205    sp<MediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource;
206
207    if (source == NULL) {
208        return NULL;
209    }
210
211    return source->getFormat();
212}
213
214status_t NuPlayer::GenericSource::dequeueAccessUnit(
215        bool audio, sp<ABuffer> *accessUnit) {
216    Track *track = audio ? &mAudioTrack : &mVideoTrack;
217
218    if (track->mSource == NULL) {
219        return -EWOULDBLOCK;
220    }
221
222    if (mIsWidevine && !audio) {
223        // try to read a buffer as we may not have been able to the last time
224        readBuffer(audio, -1ll);
225    }
226
227    status_t finalResult;
228    if (!track->mPackets->hasBufferAvailable(&finalResult)) {
229        return (finalResult == OK ? -EWOULDBLOCK : finalResult);
230    }
231
232    status_t result = track->mPackets->dequeueAccessUnit(accessUnit);
233
234    readBuffer(audio, -1ll);
235
236    return result;
237}
238
239status_t NuPlayer::GenericSource::getDuration(int64_t *durationUs) {
240    *durationUs = mDurationUs;
241    return OK;
242}
243
244size_t NuPlayer::GenericSource::getTrackCount() const {
245    return mSources.size();
246}
247
248sp<AMessage> NuPlayer::GenericSource::getTrackInfo(size_t trackIndex) const {
249    size_t trackCount = mSources.size();
250    if (trackIndex >= trackCount) {
251        return NULL;
252    }
253
254    sp<AMessage> format = new AMessage();
255    sp<MetaData> meta = mSources.itemAt(trackIndex)->getFormat();
256
257    const char *mime;
258    CHECK(meta->findCString(kKeyMIMEType, &mime));
259
260    int32_t trackType;
261    if (!strncasecmp(mime, "video/", 6)) {
262        trackType = MEDIA_TRACK_TYPE_VIDEO;
263    } else if (!strncasecmp(mime, "audio/", 6)) {
264        trackType = MEDIA_TRACK_TYPE_AUDIO;
265    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
266        trackType = MEDIA_TRACK_TYPE_TIMEDTEXT;
267    } else {
268        trackType = MEDIA_TRACK_TYPE_UNKNOWN;
269    }
270    format->setInt32("type", trackType);
271
272    const char *lang;
273    if (!meta->findCString(kKeyMediaLanguage, &lang)) {
274        lang = "und";
275    }
276    format->setString("language", lang);
277
278    if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
279        format->setString("mime", mime);
280
281        int32_t isAutoselect = 1, isDefault = 0, isForced = 0;
282        meta->findInt32(kKeyTrackIsAutoselect, &isAutoselect);
283        meta->findInt32(kKeyTrackIsDefault, &isDefault);
284        meta->findInt32(kKeyTrackIsForced, &isForced);
285
286        format->setInt32("auto", !!isAutoselect);
287        format->setInt32("default", !!isDefault);
288        format->setInt32("forced", !!isForced);
289    }
290
291    return format;
292}
293
294status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) {
295    if (mVideoTrack.mSource != NULL) {
296        int64_t actualTimeUs;
297        readBuffer(false /* audio */, seekTimeUs, &actualTimeUs);
298
299        seekTimeUs = actualTimeUs;
300    }
301
302    if (mAudioTrack.mSource != NULL) {
303        readBuffer(true /* audio */, seekTimeUs);
304    }
305
306    return OK;
307}
308
309void NuPlayer::GenericSource::readBuffer(
310        bool audio, int64_t seekTimeUs, int64_t *actualTimeUs) {
311    Track *track = audio ? &mAudioTrack : &mVideoTrack;
312    CHECK(track->mSource != NULL);
313
314    if (actualTimeUs) {
315        *actualTimeUs = seekTimeUs;
316    }
317
318    MediaSource::ReadOptions options;
319
320    bool seeking = false;
321
322    if (seekTimeUs >= 0) {
323        options.setSeekTo(seekTimeUs);
324        seeking = true;
325    }
326
327    if (mIsWidevine && !audio) {
328        options.setNonBlocking();
329    }
330
331    for (;;) {
332        MediaBuffer *mbuf;
333        status_t err = track->mSource->read(&mbuf, &options);
334
335        options.clearSeekTo();
336
337        if (err == OK) {
338            size_t outLength = mbuf->range_length();
339
340            if (audio && mAudioIsVorbis) {
341                outLength += sizeof(int32_t);
342            }
343
344            sp<ABuffer> buffer;
345            if (mIsWidevine && !audio) {
346                // data is already provided in the buffer
347                buffer = new ABuffer(NULL, mbuf->range_length());
348                buffer->meta()->setPointer("mediaBuffer", mbuf);
349                mbuf->add_ref();
350            } else {
351                buffer = new ABuffer(outLength);
352                memcpy(buffer->data(),
353                       (const uint8_t *)mbuf->data() + mbuf->range_offset(),
354                       mbuf->range_length());
355            }
356
357            if (audio && mAudioIsVorbis) {
358                int32_t numPageSamples;
359                if (!mbuf->meta_data()->findInt32(
360                            kKeyValidSamples, &numPageSamples)) {
361                    numPageSamples = -1;
362                }
363
364                memcpy(buffer->data() + mbuf->range_length(),
365                       &numPageSamples,
366                       sizeof(numPageSamples));
367            }
368
369            int64_t timeUs;
370            CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
371
372            buffer->meta()->setInt64("timeUs", timeUs);
373
374            if (actualTimeUs) {
375                *actualTimeUs = timeUs;
376            }
377
378            mbuf->release();
379            mbuf = NULL;
380
381            if (seeking) {
382                track->mPackets->queueDiscontinuity(
383                        ATSParser::DISCONTINUITY_SEEK,
384                        NULL,
385                        true /* discard */);
386            }
387
388            track->mPackets->queueAccessUnit(buffer);
389            break;
390        } else if (err == WOULD_BLOCK) {
391            break;
392        } else if (err == INFO_FORMAT_CHANGED) {
393#if 0
394            track->mPackets->queueDiscontinuity(
395                    ATSParser::DISCONTINUITY_FORMATCHANGE,
396                    NULL,
397                    false /* discard */);
398#endif
399        } else {
400            track->mPackets->signalEOS(err);
401            break;
402        }
403    }
404}
405
406}  // namespace android
407