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