M3UParser.cpp revision 4e2ffa400b82559cab2c5717c8dcdff393d334a9
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 // Base URL must be absolute 79 return false; 80 } 81 82 if (!strncasecmp("http://", url, 7)) { 83 // "url" is already an absolute URL, ignore base URL. 84 out->setTo(url); 85 return true; 86 } 87 88 size_t n = strlen(baseURL); 89 if (baseURL[n - 1] == '/') { 90 out->setTo(baseURL); 91 out->append(url); 92 } else { 93 const char *slashPos = strrchr(baseURL, '/'); 94 95 if (slashPos > &baseURL[6]) { 96 out->setTo(baseURL, slashPos - baseURL); 97 } else { 98 out->setTo(baseURL); 99 } 100 101 out->append("/"); 102 out->append(url); 103 } 104 105 return true; 106} 107 108status_t M3UParser::parse(const void *_data, size_t size) { 109 int32_t lineNo = 0; 110 111 sp<AMessage> itemMeta; 112 113 const char *data = (const char *)_data; 114 size_t offset = 0; 115 while (offset < size) { 116 size_t offsetLF = offset; 117 while (offsetLF < size && data[offsetLF] != '\n') { 118 ++offsetLF; 119 } 120 if (offsetLF >= size) { 121 break; 122 } 123 124 AString line; 125 if (offsetLF > offset && data[offsetLF - 1] == '\r') { 126 line.setTo(&data[offset], offsetLF - offset - 1); 127 } else { 128 line.setTo(&data[offset], offsetLF - offset); 129 } 130 131 LOGI("#%s#", line.c_str()); 132 133 if (lineNo == 0 && line == "#EXTM3U") { 134 mIsExtM3U = true; 135 } 136 137 if (mIsExtM3U) { 138 status_t err = OK; 139 140 if (line.startsWith("#EXT-X-TARGETDURATION")) { 141 if (mIsVariantPlaylist) { 142 return ERROR_MALFORMED; 143 } 144 err = parseMetaData(line, &mMeta, "target-duration"); 145 } else if (line.startsWith("#EXT-X-MEDIA-SEQUENCE")) { 146 if (mIsVariantPlaylist) { 147 return ERROR_MALFORMED; 148 } 149 err = parseMetaData(line, &mMeta, "media-sequence"); 150 } else if (line.startsWith("#EXTINF")) { 151 if (mIsVariantPlaylist) { 152 return ERROR_MALFORMED; 153 } 154 err = parseMetaData(line, &itemMeta, "duration"); 155 } else if (line.startsWith("#EXT-X-STREAM-INF")) { 156 if (mMeta != NULL) { 157 return ERROR_MALFORMED; 158 } 159 mIsVariantPlaylist = true; 160 } 161 162 if (err != OK) { 163 return err; 164 } 165 } 166 167 if (!line.startsWith("#")) { 168 if (!mIsVariantPlaylist) { 169 int32_t durationSecs; 170 if (itemMeta == NULL 171 || !itemMeta->findInt32("duration", &durationSecs)) { 172 return ERROR_MALFORMED; 173 } 174 } 175 176 mItems.push(); 177 Item *item = &mItems.editItemAt(mItems.size() - 1); 178 179 CHECK(MakeURL(mBaseURI.c_str(), line.c_str(), &item->mURI)); 180 181 item->mMeta = itemMeta; 182 183 itemMeta.clear(); 184 } 185 186 offset = offsetLF + 1; 187 ++lineNo; 188 } 189 190 return OK; 191} 192 193// static 194status_t M3UParser::parseMetaData( 195 const AString &line, sp<AMessage> *meta, const char *key) { 196 ssize_t colonPos = line.find(":"); 197 198 if (colonPos < 0) { 199 return ERROR_MALFORMED; 200 } 201 202 int32_t x; 203 status_t err = ParseInt32(line.c_str() + colonPos + 1, &x); 204 205 if (err != OK) { 206 return err; 207 } 208 209 if (meta->get() == NULL) { 210 *meta = new AMessage; 211 } 212 (*meta)->setInt32(key, x); 213 214 return OK; 215} 216 217// static 218status_t M3UParser::ParseInt32(const char *s, int32_t *x) { 219 char *end; 220 long lval = strtol(s, &end, 10); 221 222 if (end == s || (*end != '\0' && *end != ',')) { 223 return ERROR_MALFORMED; 224 } 225 226 *x = (int32_t)lval; 227 228 return OK; 229} 230 231} // namespace android 232