M3UParser.cpp revision 2a4d22d79e927f2245537921e10fc5fda1c47a29
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 "include/M3UParser.h" 18 19#include <media/stagefright/foundation/AMessage.h> 20#include <media/stagefright/MediaDebug.h> 21#include <media/stagefright/MediaErrors.h> 22 23namespace android { 24 25M3UParser::M3UParser( 26 const char *baseURI, const void *data, size_t size) 27 : mInitCheck(NO_INIT), 28 mBaseURI(baseURI), 29 mIsExtM3U(false), 30 mIsVariantPlaylist(false) { 31 mInitCheck = parse(data, size); 32} 33 34M3UParser::~M3UParser() { 35} 36 37status_t M3UParser::initCheck() const { 38 return mInitCheck; 39} 40 41bool M3UParser::isExtM3U() const { 42 return mIsExtM3U; 43} 44 45bool M3UParser::isVariantPlaylist() const { 46 return mIsVariantPlaylist; 47} 48 49sp<AMessage> M3UParser::meta() { 50 return mMeta; 51} 52 53size_t M3UParser::size() { 54 return mItems.size(); 55} 56 57bool M3UParser::itemAt(size_t index, AString *uri, sp<AMessage> *meta) { 58 uri->clear(); 59 if (meta) { *meta = NULL; } 60 61 if (index >= mItems.size()) { 62 return false; 63 } 64 65 *uri = mItems.itemAt(index).mURI; 66 67 if (meta) { 68 *meta = mItems.itemAt(index).mMeta; 69 } 70 71 return true; 72} 73 74static bool MakeURL(const char *baseURL, const char *url, AString *out) { 75 out->clear(); 76 77 if (strncasecmp("http://", baseURL, 7) 78 && strncasecmp("file://", baseURL, 7)) { 79 // Base URL must be absolute 80 return false; 81 } 82 83 if (!strncasecmp("http://", url, 7)) { 84 // "url" is already an absolute URL, ignore base URL. 85 out->setTo(url); 86 return true; 87 } 88 89 size_t n = strlen(baseURL); 90 if (baseURL[n - 1] == '/') { 91 out->setTo(baseURL); 92 out->append(url); 93 } else { 94 char *slashPos = strrchr(baseURL, '/'); 95 96 if (slashPos > &baseURL[6]) { 97 out->setTo(baseURL, slashPos - baseURL); 98 } else { 99 out->setTo(baseURL); 100 } 101 102 out->append("/"); 103 out->append(url); 104 } 105 106 return true; 107} 108 109status_t M3UParser::parse(const void *_data, size_t size) { 110 int32_t lineNo = 0; 111 112 sp<AMessage> itemMeta; 113 114 const char *data = (const char *)_data; 115 size_t offset = 0; 116 while (offset < size) { 117 size_t offsetLF = offset; 118 while (offsetLF < size && data[offsetLF] != '\n') { 119 ++offsetLF; 120 } 121 if (offsetLF >= size) { 122 break; 123 } 124 125 AString line; 126 if (offsetLF > offset && data[offsetLF - 1] == '\r') { 127 line.setTo(&data[offset], offsetLF - offset - 1); 128 } else { 129 line.setTo(&data[offset], offsetLF - offset); 130 } 131 132 // LOGI("#%s#", line.c_str()); 133 134 if (line.empty()) { 135 offset = offsetLF + 1; 136 continue; 137 } 138 139 if (lineNo == 0 && line == "#EXTM3U") { 140 mIsExtM3U = true; 141 } 142 143 if (mIsExtM3U) { 144 status_t err = OK; 145 146 if (line.startsWith("#EXT-X-TARGETDURATION")) { 147 if (mIsVariantPlaylist) { 148 return ERROR_MALFORMED; 149 } 150 err = parseMetaData(line, &mMeta, "target-duration"); 151 } else if (line.startsWith("#EXT-X-MEDIA-SEQUENCE")) { 152 if (mIsVariantPlaylist) { 153 return ERROR_MALFORMED; 154 } 155 err = parseMetaData(line, &mMeta, "media-sequence"); 156 } else if (line.startsWith("#EXTINF")) { 157 if (mIsVariantPlaylist) { 158 return ERROR_MALFORMED; 159 } 160 err = parseMetaData(line, &itemMeta, "duration"); 161 } else if (line.startsWith("#EXT-X-DISCONTINUITY")) { 162 if (mIsVariantPlaylist) { 163 return ERROR_MALFORMED; 164 } 165 if (itemMeta == NULL) { 166 itemMeta = new AMessage; 167 } 168 itemMeta->setInt32("discontinuity", true); 169 } else if (line.startsWith("#EXT-X-STREAM-INF")) { 170 if (mMeta != NULL) { 171 return ERROR_MALFORMED; 172 } 173 mIsVariantPlaylist = true; 174 err = parseStreamInf(line, &itemMeta); 175 } 176 177 if (err != OK) { 178 return err; 179 } 180 } 181 182 if (!line.startsWith("#")) { 183 if (!mIsVariantPlaylist) { 184 int32_t durationSecs; 185 if (itemMeta == NULL 186 || !itemMeta->findInt32("duration", &durationSecs)) { 187 return ERROR_MALFORMED; 188 } 189 } 190 191 mItems.push(); 192 Item *item = &mItems.editItemAt(mItems.size() - 1); 193 194 CHECK(MakeURL(mBaseURI.c_str(), line.c_str(), &item->mURI)); 195 196 item->mMeta = itemMeta; 197 198 itemMeta.clear(); 199 } 200 201 offset = offsetLF + 1; 202 ++lineNo; 203 } 204 205 return OK; 206} 207 208// static 209status_t M3UParser::parseMetaData( 210 const AString &line, sp<AMessage> *meta, const char *key) { 211 ssize_t colonPos = line.find(":"); 212 213 if (colonPos < 0) { 214 return ERROR_MALFORMED; 215 } 216 217 int32_t x; 218 status_t err = ParseInt32(line.c_str() + colonPos + 1, &x); 219 220 if (err != OK) { 221 return err; 222 } 223 224 if (meta->get() == NULL) { 225 *meta = new AMessage; 226 } 227 (*meta)->setInt32(key, x); 228 229 return OK; 230} 231 232// static 233status_t M3UParser::parseStreamInf( 234 const AString &line, sp<AMessage> *meta) { 235 ssize_t colonPos = line.find(":"); 236 237 if (colonPos < 0) { 238 return ERROR_MALFORMED; 239 } 240 241 size_t offset = colonPos + 1; 242 243 while (offset < line.size()) { 244 ssize_t end = line.find(",", offset); 245 if (end < 0) { 246 end = line.size(); 247 } 248 249 AString attr(line, offset, end - offset); 250 attr.trim(); 251 252 offset = end + 1; 253 254 ssize_t equalPos = attr.find("="); 255 if (equalPos < 0) { 256 continue; 257 } 258 259 AString key(attr, 0, equalPos); 260 key.trim(); 261 262 AString val(attr, equalPos + 1, attr.size() - equalPos - 1); 263 val.trim(); 264 265 LOGV("key=%s value=%s", key.c_str(), val.c_str()); 266 267 if (!strcasecmp("bandwidth", key.c_str())) { 268 const char *s = val.c_str(); 269 char *end; 270 unsigned long x = strtoul(s, &end, 10); 271 272 if (end == s || *end != '\0') { 273 // malformed 274 continue; 275 } 276 277 if (meta->get() == NULL) { 278 *meta = new AMessage; 279 } 280 (*meta)->setInt32("bandwidth", x); 281 } 282 } 283 284 return OK; 285} 286 287// static 288status_t M3UParser::ParseInt32(const char *s, int32_t *x) { 289 char *end; 290 long lval = strtol(s, &end, 10); 291 292 if (end == s || (*end != '\0' && *end != ',')) { 293 return ERROR_MALFORMED; 294 } 295 296 *x = (int32_t)lval; 297 298 return OK; 299} 300 301} // namespace android 302