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