TimedTextPlayer.cpp revision 965d08ba16ee82bc85f69546360c18e7da907406
1 /*
2 * Copyright (C) 2011 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 "TimedTextPlayer"
19#include <utils/Log.h>
20
21#include <binder/IPCThreadState.h>
22#include <media/stagefright/MediaDebug.h>
23#include <media/stagefright/MediaDefs.h>
24#include <media/stagefright/MediaErrors.h>
25#include <media/stagefright/MediaSource.h>
26#include <media/stagefright/MetaData.h>
27#include <media/stagefright/MediaBuffer.h>
28#include <media/stagefright/FileSource.h>
29#include <media/stagefright/Utils.h>
30#include "include/AwesomePlayer.h"
31#include "TimedTextPlayer.h"
32#include "TimedTextParser.h"
33
34namespace android {
35
36struct TimedTextEvent : public TimedEventQueue::Event {
37    TimedTextEvent(
38            TimedTextPlayer *player,
39            void (TimedTextPlayer::*method)())
40        : mPlayer(player),
41          mMethod(method) {
42    }
43
44protected:
45    virtual ~TimedTextEvent() {}
46
47    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
48        (mPlayer->*mMethod)();
49    }
50
51private:
52    TimedTextPlayer *mPlayer;
53    void (TimedTextPlayer::*mMethod)();
54
55    TimedTextEvent(const TimedTextEvent &);
56    TimedTextEvent &operator=(const TimedTextEvent &);
57};
58
59TimedTextPlayer::TimedTextPlayer(
60        AwesomePlayer *observer,
61        const wp<MediaPlayerBase> &listener,
62        TimedEventQueue *queue)
63    : mSource(NULL),
64      mOutOfBandSource(NULL),
65      mSeekTimeUs(0),
66      mStarted(false),
67      mTextEventPending(false),
68      mQueue(queue),
69      mListener(listener),
70      mObserver(observer),
71      mTextBuffer(NULL),
72      mTextParser(NULL),
73      mTextType(kNoText) {
74    mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent);
75}
76
77TimedTextPlayer::~TimedTextPlayer() {
78    if (mStarted) {
79        reset();
80    }
81
82    mTextTrackVector.clear();
83    mTextOutOfBandVector.clear();
84}
85
86status_t TimedTextPlayer::start(uint8_t index) {
87    CHECK(!mStarted);
88
89    if (index >=
90            mTextTrackVector.size() + mTextOutOfBandVector.size()) {
91        LOGE("Incorrect text track index: %d", index);
92        return BAD_VALUE;
93    }
94
95    if (index < mTextTrackVector.size()) { // start an in-band text
96        mSource = mTextTrackVector.itemAt(index);
97
98        status_t err = mSource->start();
99
100        if (err != OK) {
101            return err;
102        }
103        mTextType = kInBandText;
104    } else { // start an out-of-band text
105        OutOfBandText text =
106            mTextOutOfBandVector.itemAt(index - mTextTrackVector.size());
107
108        mOutOfBandSource = text.source;
109        TimedTextParser::FileType fileType = text.type;
110
111        if (mTextParser == NULL) {
112            mTextParser = new TimedTextParser();
113        }
114
115        status_t err;
116        if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) {
117            return err;
118        }
119        mTextType = kOutOfBandText;
120    }
121
122    int64_t positionUs;
123    mObserver->getPosition(&positionUs);
124    seekTo(positionUs);
125
126    postTextEvent();
127
128    mStarted = true;
129
130    return OK;
131}
132
133void TimedTextPlayer::pause() {
134    CHECK(mStarted);
135
136    cancelTextEvent();
137}
138
139void TimedTextPlayer::resume() {
140    CHECK(mStarted);
141
142    postTextEvent();
143}
144
145void TimedTextPlayer::reset() {
146    CHECK(mStarted);
147
148    // send an empty text to clear the screen
149    notifyListener(MEDIA_TIMED_TEXT);
150
151    cancelTextEvent();
152
153    mSeeking = false;
154    mStarted = false;
155
156    if (mTextType == kInBandText) {
157        if (mTextBuffer != NULL) {
158            mTextBuffer->release();
159            mTextBuffer = NULL;
160        }
161
162        if (mSource != NULL) {
163            mSource->stop();
164            mSource.clear();
165            mSource = NULL;
166        }
167    } else {
168        if (mTextParser != NULL) {
169            mTextParser.clear();
170            mTextParser = NULL;
171        }
172        if (mOutOfBandSource != NULL) {
173            mOutOfBandSource.clear();
174            mOutOfBandSource = NULL;
175        }
176    }
177}
178
179status_t TimedTextPlayer::seekTo(int64_t time_us) {
180    Mutex::Autolock autoLock(mLock);
181
182    mSeeking = true;
183    mSeekTimeUs = time_us;
184
185    postTextEvent();
186
187    return OK;
188}
189
190status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) {
191    if (index >=
192            (int)(mTextTrackVector.size() + mTextOutOfBandVector.size())) {
193        return BAD_VALUE;
194    }
195
196    if (mStarted) {
197        reset();
198    }
199
200    if (index >= 0) {
201        return start(index);
202    }
203    return OK;
204}
205
206void TimedTextPlayer::onTextEvent() {
207    Mutex::Autolock autoLock(mLock);
208
209    if (!mTextEventPending) {
210        return;
211    }
212    mTextEventPending = false;
213
214    MediaSource::ReadOptions options;
215    if (mSeeking) {
216        options.setSeekTo(mSeekTimeUs,
217                MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
218        mSeeking = false;
219
220        if (mTextType == kInBandText) {
221            if (mTextBuffer != NULL) {
222                mTextBuffer->release();
223                mTextBuffer = NULL;
224            }
225        } else {
226            mText.clear();
227        }
228
229        notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen
230    }
231
232    int64_t positionUs, timeUs;
233    mObserver->getPosition(&positionUs);
234
235    if (mTextType == kInBandText) {
236        if (mTextBuffer != NULL) {
237            uint8_t *tmp = (uint8_t *)(mTextBuffer->data());
238            size_t len = (*tmp) << 8 | (*(tmp + 1));
239
240            notifyListener(MEDIA_TIMED_TEXT,
241                           tmp + 2,
242                           len);
243
244            mTextBuffer->release();
245            mTextBuffer = NULL;
246
247        }
248
249        if (mSource->read(&mTextBuffer, &options) != OK) {
250            return;
251        }
252
253        mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs);
254    } else {
255        if (mText.size() > 0) {
256            notifyListener(MEDIA_TIMED_TEXT,
257                           mText.c_str(),
258                           mText.size());
259            mText.clear();
260        }
261
262        int64_t endTimeUs;
263        if (mTextParser->getText(
264                    &mText, &timeUs, &endTimeUs, &options) != OK) {
265            return;
266        }
267    }
268
269    //send the text now
270    if (timeUs <= positionUs + 100000ll) {
271        postTextEvent();
272    } else {
273        postTextEvent(timeUs - positionUs - 100000ll);
274    }
275}
276
277void TimedTextPlayer::postTextEvent(int64_t delayUs) {
278    if (mTextEventPending) {
279        return;
280    }
281
282    mTextEventPending = true;
283    mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs);
284}
285
286void TimedTextPlayer::cancelTextEvent() {
287    mQueue->cancelEvent(mTextEvent->eventID());
288    mTextEventPending = false;
289}
290
291void TimedTextPlayer::addTextSource(sp<MediaSource> source) {
292    Mutex::Autolock autoLock(mLock);
293    mTextTrackVector.add(source);
294}
295
296status_t TimedTextPlayer::setParameter(int key, const Parcel &request) {
297    Mutex::Autolock autoLock(mLock);
298
299    if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) {
300        String8 uri = request.readString8();
301        KeyedVector<String8, String8> headers;
302
303        // To support local subtitle file only for now
304        if (strncasecmp("file://", uri.string(), 7)) {
305            return INVALID_OPERATION;
306        }
307        sp<DataSource> dataSource =
308            DataSource::CreateFromURI(uri, &headers);
309        status_t err = dataSource->initCheck();
310
311        if (err != OK) {
312            return err;
313        }
314
315        OutOfBandText text;
316        text.source = dataSource;
317        if (uri.getPathExtension() == String8(".srt")) {
318            text.type = TimedTextParser::OUT_OF_BAND_FILE_SRT;
319        } else {
320            return ERROR_UNSUPPORTED;
321        }
322
323        mTextOutOfBandVector.add(text);
324
325        return OK;
326    }
327    return INVALID_OPERATION;
328}
329
330void TimedTextPlayer::notifyListener(
331        int msg, const void *data, size_t size) {
332    if (mListener != NULL) {
333        sp<MediaPlayerBase> listener = mListener.promote();
334
335        if (listener != NULL) {
336            if (size > 0) {
337                mData.freeData();
338                mData.write(data, size);
339
340                listener->sendEvent(msg, 0, 0, &mData);
341            } else { // send an empty timed text to clear the screen
342                listener->sendEvent(msg);
343            }
344        }
345    }
346}
347}
348