| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "APacketSource" |
| #include <utils/Log.h> |
| |
| #include "APacketSource.h" |
| |
| #include "ASessionDescription.h" |
| |
| #include "avc_utils.h" |
| |
| #include <ctype.h> |
| |
| #include <media/stagefright/foundation/ABitReader.h> |
| #include <media/stagefright/foundation/ABuffer.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/AMessage.h> |
| #include <media/stagefright/foundation/AString.h> |
| #include <media/stagefright/foundation/base64.h> |
| #include <media/stagefright/foundation/hexdump.h> |
| #include <media/stagefright/MediaBuffer.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/MetaData.h> |
| #include <utils/Vector.h> |
| |
| namespace android { |
| |
| static bool GetAttribute(const char *s, const char *key, AString *value) { |
| value->clear(); |
| |
| size_t keyLen = strlen(key); |
| |
| for (;;) { |
| while (isspace(*s)) { |
| ++s; |
| } |
| |
| const char *colonPos = strchr(s, ';'); |
| |
| size_t len = |
| (colonPos == NULL) ? strlen(s) : colonPos - s; |
| |
| if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) { |
| value->setTo(&s[keyLen + 1], len - keyLen - 1); |
| return true; |
| } |
| |
| if (colonPos == NULL) { |
| return false; |
| } |
| |
| s = colonPos + 1; |
| } |
| } |
| |
| static sp<ABuffer> decodeHex(const AString &s) { |
| if ((s.size() % 2) != 0) { |
| return NULL; |
| } |
| |
| size_t outLen = s.size() / 2; |
| sp<ABuffer> buffer = new ABuffer(outLen); |
| uint8_t *out = buffer->data(); |
| |
| uint8_t accum = 0; |
| for (size_t i = 0; i < s.size(); ++i) { |
| char c = s.c_str()[i]; |
| unsigned value; |
| if (c >= '0' && c <= '9') { |
| value = c - '0'; |
| } else if (c >= 'a' && c <= 'f') { |
| value = c - 'a' + 10; |
| } else if (c >= 'A' && c <= 'F') { |
| value = c - 'A' + 10; |
| } else { |
| return NULL; |
| } |
| |
| accum = (accum << 4) | value; |
| |
| if (i & 1) { |
| *out++ = accum; |
| |
| accum = 0; |
| } |
| } |
| |
| return buffer; |
| } |
| |
| static sp<ABuffer> MakeAVCCodecSpecificData( |
| const char *params, int32_t *width, int32_t *height) { |
| *width = 0; |
| *height = 0; |
| |
| AString val; |
| if (!GetAttribute(params, "profile-level-id", &val)) { |
| return NULL; |
| } |
| |
| sp<ABuffer> profileLevelID = decodeHex(val); |
| CHECK(profileLevelID != NULL); |
| CHECK_EQ(profileLevelID->size(), 3u); |
| |
| Vector<sp<ABuffer> > paramSets; |
| |
| size_t numSeqParameterSets = 0; |
| size_t totalSeqParameterSetSize = 0; |
| size_t numPicParameterSets = 0; |
| size_t totalPicParameterSetSize = 0; |
| |
| if (!GetAttribute(params, "sprop-parameter-sets", &val)) { |
| return NULL; |
| } |
| |
| size_t start = 0; |
| for (;;) { |
| ssize_t commaPos = val.find(",", start); |
| size_t end = (commaPos < 0) ? val.size() : commaPos; |
| |
| AString nalString(val, start, end - start); |
| sp<ABuffer> nal = decodeBase64(nalString); |
| CHECK(nal != NULL); |
| CHECK_GT(nal->size(), 0u); |
| CHECK_LE(nal->size(), 65535u); |
| |
| uint8_t nalType = nal->data()[0] & 0x1f; |
| if (numSeqParameterSets == 0) { |
| CHECK_EQ((unsigned)nalType, 7u); |
| } else if (numPicParameterSets > 0) { |
| CHECK_EQ((unsigned)nalType, 8u); |
| } |
| if (nalType == 7) { |
| ++numSeqParameterSets; |
| totalSeqParameterSetSize += nal->size(); |
| } else { |
| CHECK_EQ((unsigned)nalType, 8u); |
| ++numPicParameterSets; |
| totalPicParameterSetSize += nal->size(); |
| } |
| |
| paramSets.push(nal); |
| |
| if (commaPos < 0) { |
| break; |
| } |
| |
| start = commaPos + 1; |
| } |
| |
| CHECK_LT(numSeqParameterSets, 32u); |
| CHECK_LE(numPicParameterSets, 255u); |
| |
| size_t csdSize = |
| 1 + 3 + 1 + 1 |
| + 2 * numSeqParameterSets + totalSeqParameterSetSize |
| + 1 + 2 * numPicParameterSets + totalPicParameterSetSize; |
| |
| sp<ABuffer> csd = new ABuffer(csdSize); |
| uint8_t *out = csd->data(); |
| |
| *out++ = 0x01; // configurationVersion |
| memcpy(out, profileLevelID->data(), 3); |
| out += 3; |
| *out++ = (0x3f << 2) | 1; // lengthSize == 2 bytes |
| *out++ = 0xe0 | numSeqParameterSets; |
| |
| for (size_t i = 0; i < numSeqParameterSets; ++i) { |
| sp<ABuffer> nal = paramSets.editItemAt(i); |
| |
| *out++ = nal->size() >> 8; |
| *out++ = nal->size() & 0xff; |
| |
| memcpy(out, nal->data(), nal->size()); |
| |
| out += nal->size(); |
| |
| if (i == 0) { |
| FindAVCDimensions(nal, width, height); |
| LOGI("dimensions %dx%d", *width, *height); |
| } |
| } |
| |
| *out++ = numPicParameterSets; |
| |
| for (size_t i = 0; i < numPicParameterSets; ++i) { |
| sp<ABuffer> nal = paramSets.editItemAt(i + numSeqParameterSets); |
| |
| *out++ = nal->size() >> 8; |
| *out++ = nal->size() & 0xff; |
| |
| memcpy(out, nal->data(), nal->size()); |
| |
| out += nal->size(); |
| } |
| |
| // hexdump(csd->data(), csd->size()); |
| |
| return csd; |
| } |
| |
| sp<ABuffer> MakeAACCodecSpecificData(const char *params) { |
| AString val; |
| CHECK(GetAttribute(params, "config", &val)); |
| |
| sp<ABuffer> config = decodeHex(val); |
| CHECK(config != NULL); |
| CHECK_GE(config->size(), 4u); |
| |
| const uint8_t *data = config->data(); |
| uint32_t x = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; |
| x = (x >> 1) & 0xffff; |
| |
| static const uint8_t kStaticESDS[] = { |
| 0x03, 22, |
| 0x00, 0x00, // ES_ID |
| 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag |
| |
| 0x04, 17, |
| 0x40, // Audio ISO/IEC 14496-3 |
| 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| |
| 0x05, 2, |
| // AudioSpecificInfo follows |
| }; |
| |
| sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2); |
| memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS)); |
| csd->data()[sizeof(kStaticESDS)] = (x >> 8) & 0xff; |
| csd->data()[sizeof(kStaticESDS) + 1] = x & 0xff; |
| |
| // hexdump(csd->data(), csd->size()); |
| |
| return csd; |
| } |
| |
| // From mpeg4-generic configuration data. |
| sp<ABuffer> MakeAACCodecSpecificData2(const char *params) { |
| AString val; |
| unsigned long objectType; |
| if (GetAttribute(params, "objectType", &val)) { |
| const char *s = val.c_str(); |
| char *end; |
| objectType = strtoul(s, &end, 10); |
| CHECK(end > s && *end == '\0'); |
| } else { |
| objectType = 0x40; // Audio ISO/IEC 14496-3 |
| } |
| |
| CHECK(GetAttribute(params, "config", &val)); |
| |
| sp<ABuffer> config = decodeHex(val); |
| CHECK(config != NULL); |
| |
| // Make sure size fits into a single byte and doesn't have to |
| // be encoded. |
| CHECK_LT(20 + config->size(), 128u); |
| |
| const uint8_t *data = config->data(); |
| |
| static const uint8_t kStaticESDS[] = { |
| 0x03, 22, |
| 0x00, 0x00, // ES_ID |
| 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag |
| |
| 0x04, 17, |
| 0x40, // Audio ISO/IEC 14496-3 |
| 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| |
| 0x05, 2, |
| // AudioSpecificInfo follows |
| }; |
| |
| sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + config->size()); |
| uint8_t *dst = csd->data(); |
| *dst++ = 0x03; |
| *dst++ = 20 + config->size(); |
| *dst++ = 0x00; // ES_ID |
| *dst++ = 0x00; |
| *dst++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag |
| *dst++ = 0x04; |
| *dst++ = 15 + config->size(); |
| *dst++ = objectType; |
| for (int i = 0; i < 12; ++i) { *dst++ = 0x00; } |
| *dst++ = 0x05; |
| *dst++ = config->size(); |
| memcpy(dst, config->data(), config->size()); |
| |
| // hexdump(csd->data(), csd->size()); |
| |
| return csd; |
| } |
| |
| static size_t GetSizeWidth(size_t x) { |
| size_t n = 1; |
| while (x > 127) { |
| ++n; |
| x >>= 7; |
| } |
| return n; |
| } |
| |
| static uint8_t *EncodeSize(uint8_t *dst, size_t x) { |
| while (x > 127) { |
| *dst++ = (x & 0x7f) | 0x80; |
| x >>= 7; |
| } |
| *dst++ = x; |
| return dst; |
| } |
| |
| static bool ExtractDimensionsFromVOLHeader( |
| const sp<ABuffer> &config, int32_t *width, int32_t *height) { |
| *width = 0; |
| *height = 0; |
| |
| const uint8_t *ptr = config->data(); |
| size_t offset = 0; |
| bool foundVOL = false; |
| while (offset + 3 < config->size()) { |
| if (memcmp("\x00\x00\x01", &ptr[offset], 3) |
| || (ptr[offset + 3] & 0xf0) != 0x20) { |
| ++offset; |
| continue; |
| } |
| |
| foundVOL = true; |
| break; |
| } |
| |
| if (!foundVOL) { |
| return false; |
| } |
| |
| ABitReader br(&ptr[offset + 4], config->size() - offset - 4); |
| br.skipBits(1); // random_accessible_vol |
| unsigned video_object_type_indication = br.getBits(8); |
| |
| CHECK_NE(video_object_type_indication, |
| 0x21u /* Fine Granularity Scalable */); |
| |
| unsigned video_object_layer_verid; |
| unsigned video_object_layer_priority; |
| if (br.getBits(1)) { |
| video_object_layer_verid = br.getBits(4); |
| video_object_layer_priority = br.getBits(3); |
| } |
| unsigned aspect_ratio_info = br.getBits(4); |
| if (aspect_ratio_info == 0x0f /* extended PAR */) { |
| br.skipBits(8); // par_width |
| br.skipBits(8); // par_height |
| } |
| if (br.getBits(1)) { // vol_control_parameters |
| br.skipBits(2); // chroma_format |
| br.skipBits(1); // low_delay |
| if (br.getBits(1)) { // vbv_parameters |
| br.skipBits(15); // first_half_bit_rate |
| CHECK(br.getBits(1)); // marker_bit |
| br.skipBits(15); // latter_half_bit_rate |
| CHECK(br.getBits(1)); // marker_bit |
| br.skipBits(15); // first_half_vbv_buffer_size |
| CHECK(br.getBits(1)); // marker_bit |
| br.skipBits(3); // latter_half_vbv_buffer_size |
| br.skipBits(11); // first_half_vbv_occupancy |
| CHECK(br.getBits(1)); // marker_bit |
| br.skipBits(15); // latter_half_vbv_occupancy |
| CHECK(br.getBits(1)); // marker_bit |
| } |
| } |
| unsigned video_object_layer_shape = br.getBits(2); |
| CHECK_EQ(video_object_layer_shape, 0x00u /* rectangular */); |
| |
| CHECK(br.getBits(1)); // marker_bit |
| unsigned vop_time_increment_resolution = br.getBits(16); |
| CHECK(br.getBits(1)); // marker_bit |
| |
| if (br.getBits(1)) { // fixed_vop_rate |
| // range [0..vop_time_increment_resolution) |
| |
| // vop_time_increment_resolution |
| // 2 => 0..1, 1 bit |
| // 3 => 0..2, 2 bits |
| // 4 => 0..3, 2 bits |
| // 5 => 0..4, 3 bits |
| // ... |
| |
| CHECK_GT(vop_time_increment_resolution, 0u); |
| --vop_time_increment_resolution; |
| |
| unsigned numBits = 0; |
| while (vop_time_increment_resolution > 0) { |
| ++numBits; |
| vop_time_increment_resolution >>= 1; |
| } |
| |
| br.skipBits(numBits); // fixed_vop_time_increment |
| } |
| |
| CHECK(br.getBits(1)); // marker_bit |
| unsigned video_object_layer_width = br.getBits(13); |
| CHECK(br.getBits(1)); // marker_bit |
| unsigned video_object_layer_height = br.getBits(13); |
| CHECK(br.getBits(1)); // marker_bit |
| |
| unsigned interlaced = br.getBits(1); |
| |
| *width = video_object_layer_width; |
| *height = video_object_layer_height; |
| |
| LOGI("VOL dimensions = %dx%d", *width, *height); |
| |
| return true; |
| } |
| |
| sp<ABuffer> MakeMPEG4VideoCodecSpecificData( |
| const char *params, int32_t *width, int32_t *height) { |
| *width = 0; |
| *height = 0; |
| |
| AString val; |
| CHECK(GetAttribute(params, "config", &val)); |
| |
| sp<ABuffer> config = decodeHex(val); |
| CHECK(config != NULL); |
| |
| if (!ExtractDimensionsFromVOLHeader(config, width, height)) { |
| return NULL; |
| } |
| |
| size_t len1 = config->size() + GetSizeWidth(config->size()) + 1; |
| size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13; |
| size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3; |
| |
| sp<ABuffer> csd = new ABuffer(len3); |
| uint8_t *dst = csd->data(); |
| *dst++ = 0x03; |
| dst = EncodeSize(dst, len2 + 3); |
| *dst++ = 0x00; // ES_ID |
| *dst++ = 0x00; |
| *dst++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag |
| |
| *dst++ = 0x04; |
| dst = EncodeSize(dst, len1 + 13); |
| *dst++ = 0x01; // Video ISO/IEC 14496-2 Simple Profile |
| for (size_t i = 0; i < 12; ++i) { |
| *dst++ = 0x00; |
| } |
| |
| *dst++ = 0x05; |
| dst = EncodeSize(dst, config->size()); |
| memcpy(dst, config->data(), config->size()); |
| dst += config->size(); |
| |
| // hexdump(csd->data(), csd->size()); |
| |
| return csd; |
| } |
| |
| static bool GetClockRate(const AString &desc, uint32_t *clockRate) { |
| ssize_t slashPos = desc.find("/"); |
| if (slashPos < 0) { |
| return false; |
| } |
| |
| const char *s = desc.c_str() + slashPos + 1; |
| |
| char *end; |
| unsigned long x = strtoul(s, &end, 10); |
| |
| if (end == s || (*end != '\0' && *end != '/')) { |
| return false; |
| } |
| |
| *clockRate = x; |
| |
| return true; |
| } |
| |
| APacketSource::APacketSource( |
| const sp<ASessionDescription> &sessionDesc, size_t index) |
| : mInitCheck(NO_INIT), |
| mFormat(new MetaData), |
| mEOSResult(OK), |
| mIsAVC(false), |
| mScanForIDR(true), |
| mRTPTimeBase(0), |
| mNormalPlayTimeBaseUs(0), |
| mLastNormalPlayTimeUs(0) { |
| unsigned long PT; |
| AString desc; |
| AString params; |
| sessionDesc->getFormatType(index, &PT, &desc, ¶ms); |
| |
| CHECK(GetClockRate(desc, &mClockRate)); |
| |
| int64_t durationUs; |
| if (sessionDesc->getDurationUs(&durationUs)) { |
| mFormat->setInt64(kKeyDuration, durationUs); |
| } else { |
| mFormat->setInt64(kKeyDuration, 60 * 60 * 1000000ll); |
| } |
| |
| mInitCheck = OK; |
| if (!strncmp(desc.c_str(), "H264/", 5)) { |
| mIsAVC = true; |
| |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); |
| |
| int32_t width, height; |
| if (!sessionDesc->getDimensions(index, PT, &width, &height)) { |
| width = -1; |
| height = -1; |
| } |
| |
| int32_t encWidth, encHeight; |
| sp<ABuffer> codecSpecificData = |
| MakeAVCCodecSpecificData(params.c_str(), &encWidth, &encHeight); |
| |
| if (codecSpecificData != NULL) { |
| if (width < 0) { |
| // If no explicit width/height given in the sdp, use the dimensions |
| // extracted from the first sequence parameter set. |
| width = encWidth; |
| height = encHeight; |
| } |
| |
| mFormat->setData( |
| kKeyAVCC, 0, |
| codecSpecificData->data(), codecSpecificData->size()); |
| } else if (width < 0) { |
| mInitCheck = ERROR_UNSUPPORTED; |
| return; |
| } |
| |
| mFormat->setInt32(kKeyWidth, width); |
| mFormat->setInt32(kKeyHeight, height); |
| } else if (!strncmp(desc.c_str(), "H263-2000/", 10) |
| || !strncmp(desc.c_str(), "H263-1998/", 10)) { |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263); |
| |
| int32_t width, height; |
| if (!sessionDesc->getDimensions(index, PT, &width, &height)) { |
| mInitCheck = ERROR_UNSUPPORTED; |
| return; |
| } |
| |
| mFormat->setInt32(kKeyWidth, width); |
| mFormat->setInt32(kKeyHeight, height); |
| } else if (!strncmp(desc.c_str(), "MP4A-LATM/", 10)) { |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); |
| |
| int32_t sampleRate, numChannels; |
| ASessionDescription::ParseFormatDesc( |
| desc.c_str(), &sampleRate, &numChannels); |
| |
| mFormat->setInt32(kKeySampleRate, sampleRate); |
| mFormat->setInt32(kKeyChannelCount, numChannels); |
| |
| sp<ABuffer> codecSpecificData = |
| MakeAACCodecSpecificData(params.c_str()); |
| |
| mFormat->setData( |
| kKeyESDS, 0, |
| codecSpecificData->data(), codecSpecificData->size()); |
| } else if (!strncmp(desc.c_str(), "AMR/", 4)) { |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_NB); |
| |
| int32_t sampleRate, numChannels; |
| ASessionDescription::ParseFormatDesc( |
| desc.c_str(), &sampleRate, &numChannels); |
| |
| mFormat->setInt32(kKeySampleRate, sampleRate); |
| mFormat->setInt32(kKeyChannelCount, numChannels); |
| |
| if (sampleRate != 8000 || numChannels != 1) { |
| mInitCheck = ERROR_UNSUPPORTED; |
| } |
| } else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) { |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_WB); |
| |
| int32_t sampleRate, numChannels; |
| ASessionDescription::ParseFormatDesc( |
| desc.c_str(), &sampleRate, &numChannels); |
| |
| mFormat->setInt32(kKeySampleRate, sampleRate); |
| mFormat->setInt32(kKeyChannelCount, numChannels); |
| |
| if (sampleRate != 16000 || numChannels != 1) { |
| mInitCheck = ERROR_UNSUPPORTED; |
| } |
| } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) { |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); |
| |
| int32_t width, height; |
| if (!sessionDesc->getDimensions(index, PT, &width, &height)) { |
| width = -1; |
| height = -1; |
| } |
| |
| int32_t encWidth, encHeight; |
| sp<ABuffer> codecSpecificData = |
| MakeMPEG4VideoCodecSpecificData( |
| params.c_str(), &encWidth, &encHeight); |
| |
| if (codecSpecificData != NULL) { |
| mFormat->setData( |
| kKeyESDS, 0, |
| codecSpecificData->data(), codecSpecificData->size()); |
| |
| if (width < 0) { |
| width = encWidth; |
| height = encHeight; |
| } |
| } else if (width < 0) { |
| mInitCheck = ERROR_UNSUPPORTED; |
| return; |
| } |
| |
| mFormat->setInt32(kKeyWidth, width); |
| mFormat->setInt32(kKeyHeight, height); |
| } else if (!strncmp(desc.c_str(), "mpeg4-generic/", 14)) { |
| AString val; |
| if (!GetAttribute(params.c_str(), "mode", &val) |
| || (strcasecmp(val.c_str(), "AAC-lbr") |
| && strcasecmp(val.c_str(), "AAC-hbr"))) { |
| mInitCheck = ERROR_UNSUPPORTED; |
| return; |
| } |
| |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); |
| |
| int32_t sampleRate, numChannels; |
| ASessionDescription::ParseFormatDesc( |
| desc.c_str(), &sampleRate, &numChannels); |
| |
| mFormat->setInt32(kKeySampleRate, sampleRate); |
| mFormat->setInt32(kKeyChannelCount, numChannels); |
| |
| sp<ABuffer> codecSpecificData = |
| MakeAACCodecSpecificData2(params.c_str()); |
| |
| mFormat->setData( |
| kKeyESDS, 0, |
| codecSpecificData->data(), codecSpecificData->size()); |
| } else { |
| mInitCheck = ERROR_UNSUPPORTED; |
| } |
| } |
| |
| APacketSource::~APacketSource() { |
| } |
| |
| status_t APacketSource::initCheck() const { |
| return mInitCheck; |
| } |
| |
| status_t APacketSource::start(MetaData *params) { |
| return OK; |
| } |
| |
| status_t APacketSource::stop() { |
| return OK; |
| } |
| |
| sp<MetaData> APacketSource::getFormat() { |
| return mFormat; |
| } |
| |
| status_t APacketSource::read( |
| MediaBuffer **out, const ReadOptions *) { |
| *out = NULL; |
| |
| Mutex::Autolock autoLock(mLock); |
| while (mEOSResult == OK && mBuffers.empty()) { |
| mCondition.wait(mLock); |
| } |
| |
| if (!mBuffers.empty()) { |
| const sp<ABuffer> buffer = *mBuffers.begin(); |
| |
| updateNormalPlayTime_l(buffer); |
| |
| MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size()); |
| |
| int64_t timeUs; |
| CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); |
| |
| mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs); |
| |
| memcpy(mediaBuffer->data(), buffer->data(), buffer->size()); |
| *out = mediaBuffer; |
| |
| mBuffers.erase(mBuffers.begin()); |
| return OK; |
| } |
| |
| return mEOSResult; |
| } |
| |
| void APacketSource::updateNormalPlayTime_l(const sp<ABuffer> &buffer) { |
| uint32_t rtpTime; |
| CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); |
| |
| mLastNormalPlayTimeUs = |
| (((double)rtpTime - (double)mRTPTimeBase) / mClockRate) |
| * 1000000ll |
| + mNormalPlayTimeBaseUs; |
| } |
| |
| void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) { |
| int32_t damaged; |
| if (buffer->meta()->findInt32("damaged", &damaged) && damaged) { |
| LOGV("discarding damaged AU"); |
| return; |
| } |
| |
| if (mScanForIDR && mIsAVC) { |
| // This pretty piece of code ensures that the first access unit |
| // fed to the decoder after stream-start or seek is guaranteed to |
| // be an IDR frame. This is to workaround limitations of a certain |
| // hardware h.264 decoder that requires this to be the case. |
| |
| if (!IsIDR(buffer)) { |
| LOGV("skipping AU while scanning for next IDR frame."); |
| return; |
| } |
| |
| mScanForIDR = false; |
| } |
| |
| Mutex::Autolock autoLock(mLock); |
| mBuffers.push_back(buffer); |
| mCondition.signal(); |
| } |
| |
| void APacketSource::signalEOS(status_t result) { |
| CHECK(result != OK); |
| |
| Mutex::Autolock autoLock(mLock); |
| mEOSResult = result; |
| mCondition.signal(); |
| } |
| |
| void APacketSource::flushQueue() { |
| Mutex::Autolock autoLock(mLock); |
| mBuffers.clear(); |
| |
| mScanForIDR = true; |
| } |
| |
| int64_t APacketSource::getNormalPlayTimeUs() { |
| Mutex::Autolock autoLock(mLock); |
| return mLastNormalPlayTimeUs; |
| } |
| |
| void APacketSource::setNormalPlayTimeMapping( |
| uint32_t rtpTime, int64_t normalPlayTimeUs) { |
| Mutex::Autolock autoLock(mLock); |
| |
| mRTPTimeBase = rtpTime; |
| mNormalPlayTimeBaseUs = normalPlayTimeUs; |
| } |
| |
| int64_t APacketSource::getQueueDurationUs(bool *eos) { |
| Mutex::Autolock autoLock(mLock); |
| |
| *eos = (mEOSResult != OK); |
| |
| if (mBuffers.size() < 2) { |
| return 0; |
| } |
| |
| const sp<ABuffer> first = *mBuffers.begin(); |
| const sp<ABuffer> last = *--mBuffers.end(); |
| |
| int64_t firstTimeUs; |
| CHECK(first->meta()->findInt64("timeUs", &firstTimeUs)); |
| |
| int64_t lastTimeUs; |
| CHECK(last->meta()->findInt64("timeUs", &lastTimeUs)); |
| |
| if (lastTimeUs < firstTimeUs) { |
| LOGE("Huh? Time moving backwards? %lld > %lld", |
| firstTimeUs, lastTimeUs); |
| |
| return 0; |
| } |
| |
| return lastTimeUs - firstTimeUs; |
| } |
| |
| } // namespace android |