APacketSource.cpp revision ef7af7fec702db2fde72b16dedf9064585e6db77
1/*
2 * Copyright (C) 2010 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 "APacketSource.h"
18
19#include "ASessionDescription.h"
20
21#include <ctype.h>
22
23#include <media/stagefright/foundation/ABuffer.h>
24#include <media/stagefright/foundation/ADebug.h>
25#include <media/stagefright/foundation/AMessage.h>
26#include <media/stagefright/foundation/AString.h>
27#include <media/stagefright/foundation/base64.h>
28#include <media/stagefright/foundation/hexdump.h>
29#include <media/stagefright/MediaBuffer.h>
30#include <media/stagefright/MediaDefs.h>
31#include <media/stagefright/MetaData.h>
32#include <utils/Vector.h>
33
34namespace android {
35
36static bool GetAttribute(const char *s, const char *key, AString *value) {
37    value->clear();
38
39    size_t keyLen = strlen(key);
40
41    for (;;) {
42        while (isspace(*s)) {
43            ++s;
44        }
45
46        const char *colonPos = strchr(s, ';');
47
48        size_t len =
49            (colonPos == NULL) ? strlen(s) : colonPos - s;
50
51        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
52            value->setTo(&s[keyLen + 1], len - keyLen - 1);
53            return true;
54        }
55
56        if (colonPos == NULL) {
57            return false;
58        }
59
60        s = colonPos + 1;
61    }
62}
63
64static sp<ABuffer> decodeHex(const AString &s) {
65    if ((s.size() % 2) != 0) {
66        return NULL;
67    }
68
69    size_t outLen = s.size() / 2;
70    sp<ABuffer> buffer = new ABuffer(outLen);
71    uint8_t *out = buffer->data();
72
73    uint8_t accum = 0;
74    for (size_t i = 0; i < s.size(); ++i) {
75        char c = s.c_str()[i];
76        unsigned value;
77        if (c >= '0' && c <= '9') {
78            value = c - '0';
79        } else if (c >= 'a' && c <= 'f') {
80            value = c - 'a' + 10;
81        } else if (c >= 'A' && c <= 'F') {
82            value = c - 'A' + 10;
83        } else {
84            return NULL;
85        }
86
87        accum = (accum << 4) | value;
88
89        if (i & 1) {
90            *out++ = accum;
91
92            accum = 0;
93        }
94    }
95
96    return buffer;
97}
98
99static sp<ABuffer> MakeAVCCodecSpecificData(const char *params) {
100    AString val;
101    if (!GetAttribute(params, "profile-level-id", &val)) {
102        return NULL;
103    }
104
105    sp<ABuffer> profileLevelID = decodeHex(val);
106    CHECK(profileLevelID != NULL);
107    CHECK_EQ(profileLevelID->size(), 3u);
108
109    Vector<sp<ABuffer> > paramSets;
110
111    size_t numSeqParameterSets = 0;
112    size_t totalSeqParameterSetSize = 0;
113    size_t numPicParameterSets = 0;
114    size_t totalPicParameterSetSize = 0;
115
116    if (!GetAttribute(params, "sprop-parameter-sets", &val)) {
117        return NULL;
118    }
119
120    size_t start = 0;
121    for (;;) {
122        ssize_t commaPos = val.find(",", start);
123        size_t end = (commaPos < 0) ? val.size() : commaPos;
124
125        AString nalString(val, start, end - start);
126        sp<ABuffer> nal = decodeBase64(nalString);
127        CHECK(nal != NULL);
128        CHECK_GT(nal->size(), 0u);
129        CHECK_LE(nal->size(), 65535u);
130
131        uint8_t nalType = nal->data()[0] & 0x1f;
132        if (numSeqParameterSets == 0) {
133            CHECK_EQ((unsigned)nalType, 7u);
134        } else if (numPicParameterSets > 0) {
135            CHECK_EQ((unsigned)nalType, 8u);
136        }
137        if (nalType == 7) {
138            ++numSeqParameterSets;
139            totalSeqParameterSetSize += nal->size();
140        } else  {
141            CHECK_EQ((unsigned)nalType, 8u);
142            ++numPicParameterSets;
143            totalPicParameterSetSize += nal->size();
144        }
145
146        paramSets.push(nal);
147
148        if (commaPos < 0) {
149            break;
150        }
151
152        start = commaPos + 1;
153    }
154
155    CHECK_LT(numSeqParameterSets, 32u);
156    CHECK_LE(numPicParameterSets, 255u);
157
158    size_t csdSize =
159        1 + 3 + 1 + 1
160        + 2 * numSeqParameterSets + totalSeqParameterSetSize
161        + 1 + 2 * numPicParameterSets + totalPicParameterSetSize;
162
163    sp<ABuffer> csd = new ABuffer(csdSize);
164    uint8_t *out = csd->data();
165
166    *out++ = 0x01;  // configurationVersion
167    memcpy(out, profileLevelID->data(), 3);
168    out += 3;
169    *out++ = (0x3f << 2) | 1;  // lengthSize == 2 bytes
170    *out++ = 0xe0 | numSeqParameterSets;
171
172    for (size_t i = 0; i < numSeqParameterSets; ++i) {
173        sp<ABuffer> nal = paramSets.editItemAt(i);
174
175        *out++ = nal->size() >> 8;
176        *out++ = nal->size() & 0xff;
177
178        memcpy(out, nal->data(), nal->size());
179
180        out += nal->size();
181    }
182
183    *out++ = numPicParameterSets;
184
185    for (size_t i = 0; i < numPicParameterSets; ++i) {
186        sp<ABuffer> nal = paramSets.editItemAt(i + numSeqParameterSets);
187
188        *out++ = nal->size() >> 8;
189        *out++ = nal->size() & 0xff;
190
191        memcpy(out, nal->data(), nal->size());
192
193        out += nal->size();
194    }
195
196    hexdump(csd->data(), csd->size());
197
198    return csd;
199}
200
201sp<ABuffer> MakeAACCodecSpecificData(const char *params) {
202    AString val;
203    CHECK(GetAttribute(params, "config", &val));
204
205    sp<ABuffer> config = decodeHex(val);
206    CHECK(config != NULL);
207    CHECK_GE(config->size(), 4u);
208
209    const uint8_t *data = config->data();
210    uint32_t x = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
211    x = (x >> 1) & 0xffff;
212
213    static const uint8_t kStaticESDS[] = {
214        0x03, 22,
215        0x00, 0x00,     // ES_ID
216        0x00,           // streamDependenceFlag, URL_Flag, OCRstreamFlag
217
218        0x04, 17,
219        0x40,                       // Audio ISO/IEC 14496-3
220        0x00, 0x00, 0x00, 0x00,
221        0x00, 0x00, 0x00, 0x00,
222        0x00, 0x00, 0x00, 0x00,
223
224        0x05, 2,
225        // AudioSpecificInfo follows
226    };
227
228    sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
229    memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
230    csd->data()[sizeof(kStaticESDS)] = (x >> 8) & 0xff;
231    csd->data()[sizeof(kStaticESDS) + 1] = x & 0xff;
232
233    hexdump(csd->data(), csd->size());
234
235    return csd;
236}
237
238APacketSource::APacketSource(
239        const sp<ASessionDescription> &sessionDesc, size_t index)
240    : mInitCheck(NO_INIT),
241      mFormat(new MetaData),
242      mEOSResult(OK),
243      mFirstAccessUnit(true),
244      mFirstAccessUnitNTP(0) {
245    unsigned long PT;
246    AString desc;
247    AString params;
248    sessionDesc->getFormatType(index, &PT, &desc, &params);
249
250    int64_t durationUs;
251    if (sessionDesc->getDurationUs(&durationUs)) {
252        mFormat->setInt64(kKeyDuration, durationUs);
253    } else {
254        mFormat->setInt64(kKeyDuration, 60 * 60 * 1000000ll);
255    }
256
257    mInitCheck = OK;
258    if (!strncmp(desc.c_str(), "H264/", 5)) {
259        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
260
261        int32_t width, height;
262        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
263            // TODO: extract dimensions from sequence parameter set.
264            mInitCheck = ERROR_UNSUPPORTED;
265            return;
266        }
267
268        mFormat->setInt32(kKeyWidth, width);
269        mFormat->setInt32(kKeyHeight, height);
270
271        sp<ABuffer> codecSpecificData =
272            MakeAVCCodecSpecificData(params.c_str());
273
274        if (codecSpecificData != NULL) {
275            mFormat->setData(
276                    kKeyAVCC, 0,
277                    codecSpecificData->data(), codecSpecificData->size());
278        }
279    } else if (!strncmp(desc.c_str(), "H263-2000/", 10)
280            || !strncmp(desc.c_str(), "H263-1998/", 10)) {
281        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
282
283        int32_t width, height;
284        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
285            mInitCheck = ERROR_UNSUPPORTED;
286            return;
287        }
288
289        mFormat->setInt32(kKeyWidth, width);
290        mFormat->setInt32(kKeyHeight, height);
291    } else if (!strncmp(desc.c_str(), "MP4A-LATM/", 10)) {
292        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
293
294        int32_t sampleRate, numChannels;
295        ASessionDescription::ParseFormatDesc(
296                desc.c_str(), &sampleRate, &numChannels);
297
298        mFormat->setInt32(kKeySampleRate, sampleRate);
299        mFormat->setInt32(kKeyChannelCount, numChannels);
300
301        sp<ABuffer> codecSpecificData =
302            MakeAACCodecSpecificData(params.c_str());
303
304        mFormat->setData(
305                kKeyESDS, 0,
306                codecSpecificData->data(), codecSpecificData->size());
307    } else if (!strncmp(desc.c_str(), "AMR/", 4)) {
308        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_NB);
309
310        int32_t sampleRate, numChannels;
311        ASessionDescription::ParseFormatDesc(
312                desc.c_str(), &sampleRate, &numChannels);
313
314        mFormat->setInt32(kKeySampleRate, sampleRate);
315        mFormat->setInt32(kKeyChannelCount, numChannels);
316
317        if (sampleRate != 8000 || numChannels != 1) {
318            mInitCheck = ERROR_UNSUPPORTED;
319        }
320    } else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
321        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_WB);
322
323        int32_t sampleRate, numChannels;
324        ASessionDescription::ParseFormatDesc(
325                desc.c_str(), &sampleRate, &numChannels);
326
327        mFormat->setInt32(kKeySampleRate, sampleRate);
328        mFormat->setInt32(kKeyChannelCount, numChannels);
329
330        if (sampleRate != 16000 || numChannels != 1) {
331            mInitCheck = ERROR_UNSUPPORTED;
332        }
333    } else {
334        mInitCheck = ERROR_UNSUPPORTED;
335    }
336}
337
338APacketSource::~APacketSource() {
339}
340
341status_t APacketSource::initCheck() const {
342    return mInitCheck;
343}
344
345status_t APacketSource::start(MetaData *params) {
346    mFirstAccessUnit = true;
347    mFirstAccessUnitNTP = 0;
348
349    return OK;
350}
351
352status_t APacketSource::stop() {
353    return OK;
354}
355
356sp<MetaData> APacketSource::getFormat() {
357    return mFormat;
358}
359
360status_t APacketSource::read(
361        MediaBuffer **out, const ReadOptions *) {
362    *out = NULL;
363
364    Mutex::Autolock autoLock(mLock);
365    while (mEOSResult == OK && mBuffers.empty()) {
366        mCondition.wait(mLock);
367    }
368
369    if (!mBuffers.empty()) {
370        const sp<ABuffer> buffer = *mBuffers.begin();
371
372        MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
373
374        int64_t timeUs;
375        CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
376
377        mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
378
379        memcpy(mediaBuffer->data(), buffer->data(), buffer->size());
380        *out = mediaBuffer;
381
382        mBuffers.erase(mBuffers.begin());
383        return OK;
384    }
385
386    return mEOSResult;
387}
388
389void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
390    int32_t damaged;
391    if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
392        LOG(VERBOSE) << "discarding damaged AU";
393        return;
394    }
395
396    uint64_t ntpTime;
397    CHECK(buffer->meta()->findInt64(
398                "ntp-time", (int64_t *)&ntpTime));
399
400    if (mFirstAccessUnit) {
401        mFirstAccessUnit = false;
402        mFirstAccessUnitNTP = ntpTime;
403    }
404
405    if (ntpTime > mFirstAccessUnitNTP) {
406        ntpTime -= mFirstAccessUnitNTP;
407    } else {
408        ntpTime = 0;
409    }
410
411    int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
412
413    buffer->meta()->setInt64("timeUs", timeUs);
414
415    Mutex::Autolock autoLock(mLock);
416    mBuffers.push_back(buffer);
417    mCondition.signal();
418}
419
420void APacketSource::signalEOS(status_t result) {
421    CHECK(result != OK);
422
423    Mutex::Autolock autoLock(mLock);
424    mEOSResult = result;
425    mCondition.signal();
426}
427
428int64_t APacketSource::getQueuedDuration(bool *eos) {
429    Mutex::Autolock autoLock(mLock);
430
431    *eos = (mEOSResult != OK);
432
433    if (mBuffers.empty()) {
434        return 0;
435    }
436
437    sp<ABuffer> buffer = *mBuffers.begin();
438
439    uint64_t ntpTime;
440    CHECK(buffer->meta()->findInt64(
441                "ntp-time", (int64_t *)&ntpTime));
442
443    int64_t firstTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
444
445    buffer = *--mBuffers.end();
446
447    CHECK(buffer->meta()->findInt64(
448                "ntp-time", (int64_t *)&ntpTime));
449
450    int64_t lastTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
451
452    return lastTimeUs - firstTimeUs;
453}
454
455}  // namespace android
456