1 /* 2 * Copyright (C) 2012 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 //#define LOG_NDEBUG 0 18 #define LOG_TAG "TimedTextSRTSource" 19 #include <utils/Log.h> 20 21 #include <binder/Parcel.h> 22 #include <media/stagefright/foundation/ADebug.h> // for CHECK_xx 23 #include <media/stagefright/foundation/AString.h> 24 #include <media/stagefright/DataSource.h> 25 #include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx 26 #include <media/stagefright/MediaErrors.h> 27 #include <media/stagefright/MediaSource.h> 28 #include <media/stagefright/MetaData.h> 29 30 #include "TimedTextSRTSource.h" 31 #include "TextDescriptions.h" 32 33 namespace android { 34 35 TimedTextSRTSource::TimedTextSRTSource(const sp<DataSource>& dataSource) 36 : mSource(dataSource), 37 mMetaData(new MetaData), 38 mIndex(0) { 39 // TODO: Need to detect the language, because SRT doesn't give language 40 // information explicitly. 41 mMetaData->setCString(kKeyMediaLanguage, "und"); 42 } 43 44 TimedTextSRTSource::~TimedTextSRTSource() { 45 } 46 47 status_t TimedTextSRTSource::start() { 48 status_t err = scanFile(); 49 if (err != OK) { 50 reset(); 51 } 52 return err; 53 } 54 55 void TimedTextSRTSource::reset() { 56 mTextVector.clear(); 57 mIndex = 0; 58 } 59 60 status_t TimedTextSRTSource::stop() { 61 reset(); 62 return OK; 63 } 64 65 status_t TimedTextSRTSource::read( 66 int64_t *startTimeUs, 67 int64_t *endTimeUs, 68 Parcel *parcel, 69 const MediaSource::ReadOptions *options) { 70 AString text; 71 status_t err = getText(options, &text, startTimeUs, endTimeUs); 72 if (err != OK) { 73 return err; 74 } 75 76 CHECK_GE(*startTimeUs, 0); 77 extractAndAppendLocalDescriptions(*startTimeUs, text, parcel); 78 return OK; 79 } 80 81 sp<MetaData> TimedTextSRTSource::getFormat() { 82 return mMetaData; 83 } 84 85 status_t TimedTextSRTSource::scanFile() { 86 off64_t offset = 0; 87 int64_t startTimeUs; 88 bool endOfFile = false; 89 90 while (!endOfFile) { 91 TextInfo info; 92 status_t err = getNextSubtitleInfo(&offset, &startTimeUs, &info); 93 switch (err) { 94 case OK: 95 mTextVector.add(startTimeUs, info); 96 break; 97 case ERROR_END_OF_STREAM: 98 endOfFile = true; 99 break; 100 default: 101 return err; 102 } 103 } 104 if (mTextVector.isEmpty()) { 105 return ERROR_MALFORMED; 106 } 107 return OK; 108 } 109 110 /* SRT format: 111 * Subtitle number 112 * Start time --> End time 113 * Text of subtitle (one or more lines) 114 * Blank lines 115 * 116 * .srt file example: 117 * 1 118 * 00:00:20,000 --> 00:00:24,400 119 * Altocumulus clouds occr between six thousand 120 * 121 * 2 122 * 00:00:24,600 --> 00:00:27,800 123 * and twenty thousand feet above ground level. 124 */ 125 status_t TimedTextSRTSource::getNextSubtitleInfo( 126 off64_t *offset, int64_t *startTimeUs, TextInfo *info) { 127 AString data; 128 status_t err; 129 130 // To skip blank lines. 131 do { 132 if ((err = readNextLine(offset, &data)) != OK) { 133 return err; 134 } 135 data.trim(); 136 } while (data.empty()); 137 138 // Just ignore the first non-blank line which is subtitle sequence number. 139 if ((err = readNextLine(offset, &data)) != OK) { 140 return err; 141 } 142 int hour1, hour2, min1, min2, sec1, sec2, msec1, msec2; 143 // the start time format is: hours:minutes:seconds,milliseconds 144 // 00:00:24,600 --> 00:00:27,800 145 if (sscanf(data.c_str(), "%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d", 146 &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) { 147 return ERROR_MALFORMED; 148 } 149 150 *startTimeUs = ((hour1 * 3600 + min1 * 60 + sec1) * 1000 + msec1) * 1000ll; 151 info->endTimeUs = ((hour2 * 3600 + min2 * 60 + sec2) * 1000 + msec2) * 1000ll; 152 if (info->endTimeUs <= *startTimeUs) { 153 return ERROR_MALFORMED; 154 } 155 156 info->offset = *offset; 157 bool needMoreData = true; 158 while (needMoreData) { 159 if ((err = readNextLine(offset, &data)) != OK) { 160 if (err == ERROR_END_OF_STREAM) { 161 break; 162 } else { 163 return err; 164 } 165 } 166 167 data.trim(); 168 if (data.empty()) { 169 // it's an empty line used to separate two subtitles 170 needMoreData = false; 171 } 172 } 173 info->textLen = *offset - info->offset; 174 return OK; 175 } 176 177 status_t TimedTextSRTSource::readNextLine(off64_t *offset, AString *data) { 178 data->clear(); 179 while (true) { 180 ssize_t readSize; 181 char character; 182 if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) { 183 if (readSize == 0) { 184 return ERROR_END_OF_STREAM; 185 } 186 return ERROR_IO; 187 } 188 189 (*offset)++; 190 191 // a line could end with CR, LF or CR + LF 192 if (character == 10) { 193 break; 194 } else if (character == 13) { 195 if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) { 196 if (readSize == 0) { // end of the stream 197 return OK; 198 } 199 return ERROR_IO; 200 } 201 202 (*offset)++; 203 if (character != 10) { 204 (*offset)--; 205 } 206 break; 207 } 208 data->append(character); 209 } 210 return OK; 211 } 212 213 status_t TimedTextSRTSource::getText( 214 const MediaSource::ReadOptions *options, 215 AString *text, int64_t *startTimeUs, int64_t *endTimeUs) { 216 if (mTextVector.size() == 0) { 217 return ERROR_END_OF_STREAM; 218 } 219 text->clear(); 220 int64_t seekTimeUs; 221 MediaSource::ReadOptions::SeekMode mode; 222 if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) { 223 int64_t lastEndTimeUs = 224 mTextVector.valueAt(mTextVector.size() - 1).endTimeUs; 225 if (seekTimeUs < 0) { 226 return ERROR_OUT_OF_RANGE; 227 } else if (seekTimeUs >= lastEndTimeUs) { 228 return ERROR_END_OF_STREAM; 229 } else { 230 // binary search 231 size_t low = 0; 232 size_t high = mTextVector.size() - 1; 233 size_t mid = 0; 234 235 while (low <= high) { 236 mid = low + (high - low)/2; 237 int diff = compareExtendedRangeAndTime(mid, seekTimeUs); 238 if (diff == 0) { 239 break; 240 } else if (diff < 0) { 241 low = mid + 1; 242 } else { 243 high = mid - 1; 244 } 245 } 246 mIndex = mid; 247 } 248 } 249 250 if (mIndex >= mTextVector.size()) { 251 return ERROR_END_OF_STREAM; 252 } 253 254 const TextInfo &info = mTextVector.valueAt(mIndex); 255 *startTimeUs = mTextVector.keyAt(mIndex); 256 *endTimeUs = info.endTimeUs; 257 mIndex++; 258 259 char *str = new char[info.textLen]; 260 if (mSource->readAt(info.offset, str, info.textLen) < info.textLen) { 261 delete[] str; 262 return ERROR_IO; 263 } 264 text->append(str, info.textLen); 265 delete[] str; 266 return OK; 267 } 268 269 status_t TimedTextSRTSource::extractAndAppendLocalDescriptions( 270 int64_t timeUs, const AString &text, Parcel *parcel) { 271 const void *data = text.c_str(); 272 size_t size = text.size(); 273 int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS | 274 TextDescriptions::OUT_OF_BAND_TEXT_SRT; 275 276 if (size > 0) { 277 return TextDescriptions::getParcelOfDescriptions( 278 (const uint8_t *)data, size, flag, timeUs / 1000, parcel); 279 } 280 return OK; 281 } 282 283 int TimedTextSRTSource::compareExtendedRangeAndTime(size_t index, int64_t timeUs) { 284 CHECK_LT(index, mTextVector.size()); 285 int64_t endTimeUs = mTextVector.valueAt(index).endTimeUs; 286 int64_t startTimeUs = (index > 0) ? 287 mTextVector.valueAt(index - 1).endTimeUs : 0; 288 if (timeUs >= startTimeUs && timeUs < endTimeUs) { 289 return 0; 290 } else if (endTimeUs <= timeUs) { 291 return -1; 292 } else { 293 return 1; 294 } 295 } 296 297 } // namespace android 298