/* * 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 "ASessionDescription" #include #include #include "ASessionDescription.h" #include #include #include #include namespace android { ASessionDescription::ASessionDescription() : mIsValid(false) { } ASessionDescription::~ASessionDescription() { } bool ASessionDescription::setTo(const void *data, size_t size) { mIsValid = parse(data, size); if (!mIsValid) { mTracks.clear(); mFormats.clear(); } return mIsValid; } bool ASessionDescription::parse(const void *data, size_t size) { mTracks.clear(); mFormats.clear(); mTracks.push(Attribs()); mFormats.push(AString("[root]")); AString desc((const char *)data, size); size_t i = 0; for (;;) { ssize_t eolPos = desc.find("\n", i); if (eolPos < 0) { break; } AString line; if ((size_t)eolPos > i && desc.c_str()[eolPos - 1] == '\r') { // We accept both '\n' and '\r\n' line endings, if it's // the latter, strip the '\r' as well. line.setTo(desc, i, eolPos - i - 1); } else { line.setTo(desc, i, eolPos - i); } if (line.empty()) { i = eolPos + 1; continue; } if (line.size() < 2 || line.c_str()[1] != '=') { return false; } ALOGI("%s", line.c_str()); switch (line.c_str()[0]) { case 'v': { if (strcmp(line.c_str(), "v=0")) { return false; } break; } case 'a': case 'b': { AString key, value; ssize_t colonPos = line.find(":", 2); if (colonPos < 0) { key = line; } else { key.setTo(line, 0, colonPos); if (key == "a=fmtp" || key == "a=rtpmap" || key == "a=framesize") { ssize_t spacePos = line.find(" ", colonPos + 1); if (spacePos < 0) { return false; } key.setTo(line, 0, spacePos); colonPos = spacePos; } value.setTo(line, colonPos + 1, line.size() - colonPos - 1); } key.trim(); value.trim(); ALOGV("adding '%s' => '%s'", key.c_str(), value.c_str()); mTracks.editItemAt(mTracks.size() - 1).add(key, value); break; } case 'm': { ALOGV("new section '%s'", AString(line, 2, line.size() - 2).c_str()); mTracks.push(Attribs()); mFormats.push(AString(line, 2, line.size() - 2)); break; } default: { AString key, value; ssize_t equalPos = line.find("="); key = AString(line, 0, equalPos + 1); value = AString(line, equalPos + 1, line.size() - equalPos - 1); key.trim(); value.trim(); ALOGV("adding '%s' => '%s'", key.c_str(), value.c_str()); mTracks.editItemAt(mTracks.size() - 1).add(key, value); break; } } i = eolPos + 1; } return true; } bool ASessionDescription::isValid() const { return mIsValid; } size_t ASessionDescription::countTracks() const { return mTracks.size(); } void ASessionDescription::getFormat(size_t index, AString *value) const { CHECK_GE(index, 0u); CHECK_LT(index, mTracks.size()); *value = mFormats.itemAt(index); } bool ASessionDescription::findAttribute( size_t index, const char *key, AString *value) const { CHECK_GE(index, 0u); CHECK_LT(index, mTracks.size()); value->clear(); const Attribs &track = mTracks.itemAt(index); ssize_t i = track.indexOfKey(AString(key)); if (i < 0) { return false; } *value = track.valueAt(i); return true; } void ASessionDescription::getFormatType( size_t index, unsigned long *PT, AString *desc, AString *params) const { AString format; getFormat(index, &format); const char *lastSpacePos = strrchr(format.c_str(), ' '); CHECK(lastSpacePos != NULL); char *end; unsigned long x = strtoul(lastSpacePos + 1, &end, 10); CHECK_GT(end, lastSpacePos + 1); CHECK_EQ(*end, '\0'); *PT = x; char key[32]; snprintf(key, sizeof(key), "a=rtpmap:%lu", x); CHECK(findAttribute(index, key, desc)); snprintf(key, sizeof(key), "a=fmtp:%lu", x); if (!findAttribute(index, key, params)) { params->clear(); } } bool ASessionDescription::getDimensions( size_t index, unsigned long PT, int32_t *width, int32_t *height) const { *width = 0; *height = 0; char key[33]; snprintf(key, sizeof(key), "a=framesize:%lu", PT); if (PT > 9999999) { android_errorWriteLog(0x534e4554, "25747670"); } AString value; if (!findAttribute(index, key, &value)) { return false; } const char *s = value.c_str(); char *end; *width = strtoul(s, &end, 10); CHECK_GT(end, s); CHECK_EQ(*end, '-'); s = end + 1; *height = strtoul(s, &end, 10); CHECK_GT(end, s); CHECK_EQ(*end, '\0'); return true; } bool ASessionDescription::getDurationUs(int64_t *durationUs) const { *durationUs = 0; CHECK(mIsValid); AString value; if (!findAttribute(0, "a=range", &value)) { return false; } if (strncmp(value.c_str(), "npt=", 4)) { return false; } float from, to; if (!AVMediaServiceUtils::get()->parseNTPRange( value.c_str() + 4, &from, &to)) { return false; } *durationUs = (int64_t)((to - from) * 1E6); return true; } // static void ASessionDescription::ParseFormatDesc( const char *desc, int32_t *timescale, int32_t *numChannels) { const char *slash1 = strchr(desc, '/'); CHECK(slash1 != NULL); const char *s = slash1 + 1; char *end; unsigned long x = strtoul(s, &end, 10); CHECK_GT(end, s); CHECK(*end == '\0' || *end == '/'); *timescale = x; *numChannels = 1; if (*end == '/') { s = end + 1; unsigned long x = strtoul(s, &end, 10); CHECK_GT(end, s); CHECK_EQ(*end, '\0'); *numChannels = x; } } // static bool ASessionDescription::parseNTPRange( const char *s, float *npt1, float *npt2) { if (s[0] == '-') { return false; // no start time available. } if (!strncmp("now", s, 3)) { return false; // no absolute start time available } char *end; *npt1 = strtof(s, &end); if (end == s || *end != '-') { // Failed to parse float or trailing "dash". return false; } s = end + 1; // skip the dash. if (*s == '\0') { *npt2 = FLT_MAX; // open ended. return true; } if (!strncmp("now", s, 3)) { return false; // no absolute end time available } *npt2 = strtof(s, &end); if (end == s || *end != '\0') { return false; } return *npt2 > *npt1; } } // namespace android