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