From f7e34cfcb9d649989c8e391f23dde782d4931849 Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Thu, 23 Jan 2014 15:00:53 -0800 Subject: stagefright: use CODECS field in EXT-X_STREAM-INF as fallback Use the CODECS field in EXT-X-STREAM-INF as a viable fallback for identifying available streams in a variant playlist. Bug: 11854054 Change-Id: I5011809dc7c56220b023eb1a16f2d6a392ddd6f1 --- media/libstagefright/httplive/M3UParser.cpp | 160 ++++++++++++++++++++++++++-- media/libstagefright/httplive/M3UParser.h | 4 + 2 files changed, 157 insertions(+), 7 deletions(-) (limited to 'media/libstagefright/httplive') diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 243888c..ae19ffa 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include namespace android { @@ -352,9 +353,27 @@ bool M3UParser::getTypeURI(size_t index, const char *key, AString *uri) const { if (!meta->findString(key, &groupID)) { *uri = mItems.itemAt(index).mURI; - // Assume media without any more specific attribute contains - // audio and video, but no subtitles. - return !strcmp("audio", key) || !strcmp("video", key); + AString codecs; + if (!meta->findString("codecs", &codecs)) { + // Assume media without any more specific attribute contains + // audio and video, but no subtitles. + return !strcmp("audio", key) || !strcmp("video", key); + } else { + // Split the comma separated list of codecs. + size_t offset = 0; + ssize_t commaPos = -1; + codecs.append(','); + while ((commaPos = codecs.find(",", offset)) >= 0) { + AString codec(codecs, offset, commaPos - offset); + // return true only if a codec of type `key` ("audio"/"video") + // is found. + if (codecIsType(codec, key)) { + return true; + } + offset = commaPos + 1; + } + return false; + } } sp group = mMediaGroups.valueFor(groupID); @@ -684,12 +703,22 @@ status_t M3UParser::parseStreamInf( *meta = new AMessage; } (*meta)->setInt32("bandwidth", x); + } else if (!strcasecmp("codecs", key.c_str())) { + if (!isQuotedString(val)) { + ALOGE("Expected quoted string for %s attribute, " + "got '%s' instead.", + key.c_str(), val.c_str());; + + return ERROR_MALFORMED; + } + + key.tolower(); + const AString &codecs = unquoteString(val); + (*meta)->setString(key.c_str(), codecs.c_str()); } else if (!strcasecmp("audio", key.c_str()) || !strcasecmp("video", key.c_str()) || !strcasecmp("subtitles", key.c_str())) { - if (val.size() < 2 - || val.c_str()[0] != '"' - || val.c_str()[val.size() - 1] != '"') { + if (!isQuotedString(val)) { ALOGE("Expected quoted string for %s attribute, " "got '%s' instead.", key.c_str(), val.c_str()); @@ -697,7 +726,7 @@ status_t M3UParser::parseStreamInf( return ERROR_MALFORMED; } - AString groupID(val, 1, val.size() - 2); + const AString &groupID = unquoteString(val); ssize_t groupIndex = mMediaGroups.indexOfKey(groupID); if (groupIndex < 0) { @@ -1086,4 +1115,121 @@ status_t M3UParser::ParseDouble(const char *s, double *x) { return OK; } +// static +bool M3UParser::isQuotedString(const AString &str) { + if (str.size() < 2 + || str.c_str()[0] != '"' + || str.c_str()[str.size() - 1] != '"') { + return false; + } + return true; +} + +// static +AString M3UParser::unquoteString(const AString &str) { + if (!isQuotedString(str)) { + return str; + } + return AString(str, 1, str.size() - 2); +} + +// static +bool M3UParser::codecIsType(const AString &codec, const char *type) { + if (codec.size() < 4) { + return false; + } + const char *c = codec.c_str(); + switch (FOURCC(c[0], c[1], c[2], c[3])) { + // List extracted from http://www.mp4ra.org/codecs.html + case 'ac-3': + case 'alac': + case 'dra1': + case 'dtsc': + case 'dtse': + case 'dtsh': + case 'dtsl': + case 'ec-3': + case 'enca': + case 'g719': + case 'g726': + case 'm4ae': + case 'mlpa': + case 'mp4a': + case 'raw ': + case 'samr': + case 'sawb': + case 'sawp': + case 'sevc': + case 'sqcp': + case 'ssmv': + case 'twos': + case 'agsm': + case 'alaw': + case 'dvi ': + case 'fl32': + case 'fl64': + case 'ima4': + case 'in24': + case 'in32': + case 'lpcm': + case 'Qclp': + case 'QDM2': + case 'QDMC': + case 'ulaw': + case 'vdva': + return !strcmp("audio", type); + + case 'avc1': + case 'avc2': + case 'avcp': + case 'drac': + case 'encv': + case 'mjp2': + case 'mp4v': + case 'mvc1': + case 'mvc2': + case 'resv': + case 's263': + case 'svc1': + case 'vc-1': + case 'CFHD': + case 'civd': + case 'DV10': + case 'dvh5': + case 'dvh6': + case 'dvhp': + case 'DVOO': + case 'DVOR': + case 'DVTV': + case 'DVVT': + case 'flic': + case 'gif ': + case 'h261': + case 'h263': + case 'HD10': + case 'jpeg': + case 'M105': + case 'mjpa': + case 'mjpb': + case 'png ': + case 'PNTG': + case 'rle ': + case 'rpza': + case 'Shr0': + case 'Shr1': + case 'Shr2': + case 'Shr3': + case 'Shr4': + case 'SVQ1': + case 'SVQ3': + case 'tga ': + case 'tiff': + case 'WRLE': + return !strcmp("video", type); + + default: + return false; + } +} + } // namespace android diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h index 5248004..b93b0e5 100644 --- a/media/libstagefright/httplive/M3UParser.h +++ b/media/libstagefright/httplive/M3UParser.h @@ -100,6 +100,10 @@ private: static status_t ParseInt32(const char *s, int32_t *x); static status_t ParseDouble(const char *s, double *x); + static bool isQuotedString(const AString &str); + static AString unquoteString(const AString &str); + static bool codecIsType(const AString &codec, const char *type); + DISALLOW_EVIL_CONSTRUCTORS(M3UParser); }; -- cgit v1.1