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 "TimedTextDriver" 19 #include <utils/Log.h> 20 21 #include <binder/IPCThreadState.h> 22 23 #include <media/mediaplayer.h> 24 #include <media/MediaPlayerInterface.h> 25 #include <media/stagefright/DataSource.h> 26 #include <media/stagefright/FileSource.h> 27 #include <media/stagefright/MediaDefs.h> 28 #include <media/stagefright/MediaErrors.h> 29 #include <media/stagefright/MediaSource.h> 30 #include <media/stagefright/MetaData.h> 31 #include <media/stagefright/Utils.h> 32 #include <media/stagefright/foundation/ADebug.h> 33 #include <media/stagefright/foundation/ALooper.h> 34 #include <media/stagefright/timedtext/TimedTextDriver.h> 35 36 #include "TextDescriptions.h" 37 #include "TimedTextPlayer.h" 38 #include "TimedTextSource.h" 39 40 namespace android { 41 42 TimedTextDriver::TimedTextDriver( 43 const wp<MediaPlayerBase> &listener) 44 : mLooper(new ALooper), 45 mListener(listener), 46 mState(UNINITIALIZED), 47 mCurrentTrackIndex(UINT_MAX) { 48 mLooper->setName("TimedTextDriver"); 49 mLooper->start(); 50 mPlayer = new TimedTextPlayer(listener); 51 mLooper->registerHandler(mPlayer); 52 } 53 54 TimedTextDriver::~TimedTextDriver() { 55 mTextSourceVector.clear(); 56 mTextSourceTypeVector.clear(); 57 mLooper->stop(); 58 } 59 60 status_t TimedTextDriver::selectTrack_l(size_t index) { 61 if (mCurrentTrackIndex == index) { 62 return OK; 63 } 64 sp<TimedTextSource> source; 65 source = mTextSourceVector.valueFor(index); 66 mPlayer->setDataSource(source); 67 if (mState == UNINITIALIZED) { 68 mState = PREPARED; 69 } 70 mCurrentTrackIndex = index; 71 return OK; 72 } 73 74 status_t TimedTextDriver::start() { 75 Mutex::Autolock autoLock(mLock); 76 switch (mState) { 77 case UNINITIALIZED: 78 return INVALID_OPERATION; 79 case PLAYING: 80 return OK; 81 case PREPARED: 82 mPlayer->start(); 83 mState = PLAYING; 84 return OK; 85 case PAUSED: 86 mPlayer->resume(); 87 mState = PLAYING; 88 return OK; 89 default: 90 TRESPASS(); 91 } 92 return UNKNOWN_ERROR; 93 } 94 95 status_t TimedTextDriver::pause() { 96 Mutex::Autolock autoLock(mLock); 97 ALOGV("%s() is called", __FUNCTION__); 98 switch (mState) { 99 case UNINITIALIZED: 100 return INVALID_OPERATION; 101 case PLAYING: 102 mPlayer->pause(); 103 mState = PAUSED; 104 return OK; 105 case PREPARED: 106 return INVALID_OPERATION; 107 case PAUSED: 108 return OK; 109 default: 110 TRESPASS(); 111 } 112 return UNKNOWN_ERROR; 113 } 114 115 status_t TimedTextDriver::selectTrack(size_t index) { 116 status_t ret = OK; 117 Mutex::Autolock autoLock(mLock); 118 ALOGV("%s() is called", __FUNCTION__); 119 switch (mState) { 120 case UNINITIALIZED: 121 case PREPARED: 122 case PAUSED: 123 ret = selectTrack_l(index); 124 break; 125 case PLAYING: 126 mPlayer->pause(); 127 ret = selectTrack_l(index); 128 if (ret != OK) { 129 break; 130 } 131 mPlayer->start(); 132 break; 133 defaut: 134 TRESPASS(); 135 } 136 return ret; 137 } 138 139 status_t TimedTextDriver::unselectTrack(size_t index) { 140 Mutex::Autolock autoLock(mLock); 141 ALOGV("%s() is called", __FUNCTION__); 142 if (mCurrentTrackIndex != index) { 143 return INVALID_OPERATION; 144 } 145 mCurrentTrackIndex = UINT_MAX; 146 switch (mState) { 147 case UNINITIALIZED: 148 return INVALID_OPERATION; 149 case PLAYING: 150 mPlayer->setDataSource(NULL); 151 mState = UNINITIALIZED; 152 return OK; 153 case PREPARED: 154 case PAUSED: 155 mState = UNINITIALIZED; 156 return OK; 157 default: 158 TRESPASS(); 159 } 160 return UNKNOWN_ERROR; 161 } 162 163 status_t TimedTextDriver::seekToAsync(int64_t timeUs) { 164 Mutex::Autolock autoLock(mLock); 165 ALOGV("%s() is called", __FUNCTION__); 166 switch (mState) { 167 case UNINITIALIZED: 168 return INVALID_OPERATION; 169 case PREPARED: 170 mPlayer->seekToAsync(timeUs); 171 mPlayer->pause(); 172 mState = PAUSED; 173 return OK; 174 case PAUSED: 175 mPlayer->seekToAsync(timeUs); 176 mPlayer->pause(); 177 return OK; 178 case PLAYING: 179 mPlayer->seekToAsync(timeUs); 180 return OK; 181 defaut: 182 TRESPASS(); 183 } 184 return UNKNOWN_ERROR; 185 } 186 187 status_t TimedTextDriver::addInBandTextSource( 188 size_t trackIndex, const sp<MediaSource>& mediaSource) { 189 sp<TimedTextSource> source = 190 TimedTextSource::CreateTimedTextSource(mediaSource); 191 if (source == NULL) { 192 return ERROR_UNSUPPORTED; 193 } 194 Mutex::Autolock autoLock(mLock); 195 mTextSourceVector.add(trackIndex, source); 196 mTextSourceTypeVector.add(TEXT_SOURCE_TYPE_IN_BAND); 197 return OK; 198 } 199 200 status_t TimedTextDriver::addOutOfBandTextSource( 201 size_t trackIndex, const char *uri, const char *mimeType) { 202 203 // To support local subtitle file only for now 204 if (strncasecmp("file://", uri, 7)) { 205 ALOGE("uri('%s') is not a file", uri); 206 return ERROR_UNSUPPORTED; 207 } 208 209 sp<DataSource> dataSource = 210 DataSource::CreateFromURI(uri); 211 return createOutOfBandTextSource(trackIndex, mimeType, dataSource); 212 } 213 214 status_t TimedTextDriver::addOutOfBandTextSource( 215 size_t trackIndex, int fd, off64_t offset, off64_t length, const char *mimeType) { 216 217 if (fd < 0) { 218 ALOGE("Invalid file descriptor: %d", fd); 219 return ERROR_UNSUPPORTED; 220 } 221 222 sp<DataSource> dataSource = new FileSource(dup(fd), offset, length); 223 return createOutOfBandTextSource(trackIndex, mimeType, dataSource); 224 } 225 226 status_t TimedTextDriver::createOutOfBandTextSource( 227 size_t trackIndex, 228 const char *mimeType, 229 const sp<DataSource>& dataSource) { 230 231 if (dataSource == NULL) { 232 return ERROR_UNSUPPORTED; 233 } 234 235 sp<TimedTextSource> source; 236 if (strcasecmp(mimeType, MEDIA_MIMETYPE_TEXT_SUBRIP) == 0) { 237 source = TimedTextSource::CreateTimedTextSource( 238 dataSource, TimedTextSource::OUT_OF_BAND_FILE_SRT); 239 } 240 241 if (source == NULL) { 242 ALOGE("Failed to create timed text source"); 243 return ERROR_UNSUPPORTED; 244 } 245 246 Mutex::Autolock autoLock(mLock); 247 mTextSourceVector.add(trackIndex, source); 248 mTextSourceTypeVector.add(TEXT_SOURCE_TYPE_OUT_OF_BAND); 249 return OK; 250 } 251 252 size_t TimedTextDriver::countExternalTracks() const { 253 size_t nTracks = 0; 254 for (size_t i = 0, n = mTextSourceTypeVector.size(); i < n; ++i) { 255 if (mTextSourceTypeVector[i] == TEXT_SOURCE_TYPE_OUT_OF_BAND) { 256 ++nTracks; 257 } 258 } 259 return nTracks; 260 } 261 262 void TimedTextDriver::getExternalTrackInfo(Parcel *parcel) { 263 Mutex::Autolock autoLock(mLock); 264 for (size_t i = 0, n = mTextSourceTypeVector.size(); i < n; ++i) { 265 if (mTextSourceTypeVector[i] == TEXT_SOURCE_TYPE_IN_BAND) { 266 continue; 267 } 268 269 sp<MetaData> meta = mTextSourceVector.valueAt(i)->getFormat(); 270 271 // There are two fields. 272 parcel->writeInt32(2); 273 274 // track type. 275 parcel->writeInt32(MEDIA_TRACK_TYPE_TIMEDTEXT); 276 const char *lang = "und"; 277 if (meta != NULL) { 278 meta->findCString(kKeyMediaLanguage, &lang); 279 } 280 parcel->writeString16(String16(lang)); 281 } 282 } 283 284 } // namespace android 285