TimedTextPlayer.cpp revision 3b573f7bf1c5736d500e39013b8d32478a1429e6
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
23#include <media/stagefright/MediaDebug.h>
24#include <media/stagefright/MediaDefs.h>
25#include <media/stagefright/MediaErrors.h>
26#include <media/stagefright/MediaSource.h>
27#include <media/stagefright/MetaData.h>
28#include <media/stagefright/MediaBuffer.h>
29#include <media/stagefright/FileSource.h>
30#include <media/stagefright/Utils.h>
31
32#include "include/AwesomePlayer.h"
33#include "TimedTextPlayer.h"
34#include "TimedTextParser.h"
35#include "TextDescriptions.h"
36
37namespace android {
38
39struct TimedTextEvent : public TimedEventQueue::Event {
40    TimedTextEvent(
41            TimedTextPlayer *player,
42            void (TimedTextPlayer::*method)())
43        : mPlayer(player),
44          mMethod(method) {
45    }
46
47protected:
48    virtual ~TimedTextEvent() {}
49
50    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
51        (mPlayer->*mMethod)();
52    }
53
54private:
55    TimedTextPlayer *mPlayer;
56    void (TimedTextPlayer::*mMethod)();
57
58    TimedTextEvent(const TimedTextEvent &);
59    TimedTextEvent &operator=(const TimedTextEvent &);
60};
61
62TimedTextPlayer::TimedTextPlayer(
63        AwesomePlayer *observer,
64        const wp<MediaPlayerBase> &listener,
65        TimedEventQueue *queue)
66    : mSource(NULL),
67      mOutOfBandSource(NULL),
68      mSeekTimeUs(0),
69      mStarted(false),
70      mTextEventPending(false),
71      mQueue(queue),
72      mListener(listener),
73      mObserver(observer),
74      mTextBuffer(NULL),
75      mTextParser(NULL),
76      mTextType(kNoText) {
77    mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent);
78}
79
80TimedTextPlayer::~TimedTextPlayer() {
81    if (mStarted) {
82        reset();
83    }
84
85    mTextTrackVector.clear();
86    mTextOutOfBandVector.clear();
87}
88
89status_t TimedTextPlayer::start(uint8_t index) {
90    CHECK(!mStarted);
91
92    if (index >=
93            mTextTrackVector.size() + mTextOutOfBandVector.size()) {
94        LOGE("Incorrect text track index: %d", index);
95        return BAD_VALUE;
96    }
97
98    status_t err;
99    if (index < mTextTrackVector.size()) { // start an in-band text
100        mSource = mTextTrackVector.itemAt(index);
101
102        err = mSource->start();
103
104        if (err != OK) {
105            return err;
106        }
107        mTextType = kInBandText;
108    } else { // start an out-of-band text
109        OutOfBandText text =
110            mTextOutOfBandVector.itemAt(index - mTextTrackVector.size());
111
112        mOutOfBandSource = text.source;
113        TimedTextParser::FileType fileType = text.type;
114
115        if (mTextParser == NULL) {
116            mTextParser = new TimedTextParser();
117        }
118
119        if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) {
120            return err;
121        }
122        mTextType = kOutOfBandText;
123    }
124
125    // send sample description format
126    if ((err = extractAndSendGlobalDescriptions()) != OK) {
127        return err;
128    }
129
130    int64_t positionUs;
131    mObserver->getPosition(&positionUs);
132    seekTo(positionUs);
133
134    postTextEvent();
135
136    mStarted = true;
137
138    return OK;
139}
140
141void TimedTextPlayer::pause() {
142    CHECK(mStarted);
143
144    cancelTextEvent();
145}
146
147void TimedTextPlayer::resume() {
148    CHECK(mStarted);
149
150    postTextEvent();
151}
152
153void TimedTextPlayer::reset() {
154    CHECK(mStarted);
155
156    // send an empty text to clear the screen
157    notifyListener(MEDIA_TIMED_TEXT);
158
159    cancelTextEvent();
160
161    mSeeking = false;
162    mStarted = false;
163
164    if (mTextType == kInBandText) {
165        if (mTextBuffer != NULL) {
166            mTextBuffer->release();
167            mTextBuffer = NULL;
168        }
169
170        if (mSource != NULL) {
171            mSource->stop();
172            mSource.clear();
173            mSource = NULL;
174        }
175    } else {
176        if (mTextParser != NULL) {
177            mTextParser.clear();
178            mTextParser = NULL;
179        }
180        if (mOutOfBandSource != NULL) {
181            mOutOfBandSource.clear();
182            mOutOfBandSource = NULL;
183        }
184    }
185}
186
187status_t TimedTextPlayer::seekTo(int64_t time_us) {
188    Mutex::Autolock autoLock(mLock);
189
190    mSeeking = true;
191    mSeekTimeUs = time_us;
192
193    postTextEvent();
194
195    return OK;
196}
197
198status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) {
199    if (index >=
200            (int)(mTextTrackVector.size() + mTextOutOfBandVector.size())) {
201        return BAD_VALUE;
202    }
203
204    if (mStarted) {
205        reset();
206    }
207
208    if (index >= 0) {
209        return start(index);
210    }
211    return OK;
212}
213
214void TimedTextPlayer::onTextEvent() {
215    Mutex::Autolock autoLock(mLock);
216
217    if (!mTextEventPending) {
218        return;
219    }
220    mTextEventPending = false;
221
222    if (mData.dataSize() > 0) {
223        notifyListener(MEDIA_TIMED_TEXT, &mData);
224        mData.freeData();
225    }
226
227    MediaSource::ReadOptions options;
228    if (mSeeking) {
229        options.setSeekTo(mSeekTimeUs,
230                MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
231        mSeeking = false;
232
233        notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen
234    }
235
236    int64_t positionUs, timeUs;
237    mObserver->getPosition(&positionUs);
238
239    if (mTextType == kInBandText) {
240        if (mSource->read(&mTextBuffer, &options) != OK) {
241            return;
242        }
243
244        mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs);
245    } else {
246        int64_t endTimeUs;
247        if (mTextParser->getText(
248                    &mText, &timeUs, &endTimeUs, &options) != OK) {
249            return;
250        }
251    }
252
253    if (timeUs > 0) {
254        extractAndAppendLocalDescriptions(timeUs);
255    }
256
257    if (mTextType == kInBandText) {
258        if (mTextBuffer != NULL) {
259            mTextBuffer->release();
260            mTextBuffer = NULL;
261        }
262    } else {
263        mText.clear();
264    }
265
266    //send the text now
267    if (timeUs <= positionUs + 100000ll) {
268        postTextEvent();
269    } else {
270        postTextEvent(timeUs - positionUs - 100000ll);
271    }
272}
273
274void TimedTextPlayer::postTextEvent(int64_t delayUs) {
275    if (mTextEventPending) {
276        return;
277    }
278
279    mTextEventPending = true;
280    mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs);
281}
282
283void TimedTextPlayer::cancelTextEvent() {
284    mQueue->cancelEvent(mTextEvent->eventID());
285    mTextEventPending = false;
286}
287
288void TimedTextPlayer::addTextSource(sp<MediaSource> source) {
289    Mutex::Autolock autoLock(mLock);
290    mTextTrackVector.add(source);
291}
292
293status_t TimedTextPlayer::setParameter(int key, const Parcel &request) {
294    Mutex::Autolock autoLock(mLock);
295
296    if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) {
297        const String16 uri16 = request.readString16();
298        String8 uri = String8(uri16);
299        KeyedVector<String8, String8> headers;
300
301        // To support local subtitle file only for now
302        if (strncasecmp("file://", uri.string(), 7)) {
303            return INVALID_OPERATION;
304        }
305        sp<DataSource> dataSource =
306            DataSource::CreateFromURI(uri, &headers);
307        status_t err = dataSource->initCheck();
308
309        if (err != OK) {
310            return err;
311        }
312
313        OutOfBandText text;
314        text.source = dataSource;
315        if (uri.getPathExtension() == String8(".srt")) {
316            text.type = TimedTextParser::OUT_OF_BAND_FILE_SRT;
317        } else {
318            return ERROR_UNSUPPORTED;
319        }
320
321        mTextOutOfBandVector.add(text);
322
323        return OK;
324    }
325    return INVALID_OPERATION;
326}
327
328void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) {
329    if (mListener != NULL) {
330        sp<MediaPlayerBase> listener = mListener.promote();
331
332        if (listener != NULL) {
333            if (parcel && (parcel->dataSize() > 0)) {
334                listener->sendEvent(msg, 0, 0, parcel);
335            } else { // send an empty timed text to clear the screen
336                listener->sendEvent(msg);
337            }
338        }
339    }
340}
341
342// Each text sample consists of a string of text, optionally with sample
343// modifier description. The modifier description could specify a new
344// text style for the string of text. These descriptions are present only
345// if they are needed. This method is used to extract the modifier
346// description and append it at the end of the text.
347status_t TimedTextPlayer::extractAndAppendLocalDescriptions(int64_t timeUs) {
348    const void *data;
349    size_t size = 0;
350    int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS;
351
352    if (mTextType == kInBandText) {
353        const char *mime;
354        CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
355
356        if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
357            flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
358            data = mTextBuffer->data();
359            size = mTextBuffer->size();
360        } else {
361            // support 3GPP only for now
362            return ERROR_UNSUPPORTED;
363        }
364    } else {
365        data = mText.c_str();
366        size = mText.size();
367        flag |= TextDescriptions::OUT_OF_BAND_TEXT_SRT;
368    }
369
370    if ((size > 0) && (flag != TextDescriptions::LOCAL_DESCRIPTIONS)) {
371        mData.freeData();
372        return TextDescriptions::getParcelOfDescriptions(
373                (const uint8_t *)data, size, flag, timeUs / 1000, &mData);
374    }
375
376    return OK;
377}
378
379// To extract and send the global text descriptions for all the text samples
380// in the text track or text file.
381status_t TimedTextPlayer::extractAndSendGlobalDescriptions() {
382    const void *data;
383    size_t size = 0;
384    int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS;
385
386    if (mTextType == kInBandText) {
387        const char *mime;
388        CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
389
390        // support 3GPP only for now
391        if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
392            uint32_t type;
393            // get the 'tx3g' box content. This box contains the text descriptions
394            // used to render the text track
395            if (!mSource->getFormat()->findData(
396                        kKeyTextFormatData, &type, &data, &size)) {
397                return ERROR_MALFORMED;
398            }
399
400            flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
401        }
402    }
403
404    if ((size > 0) && (flag != TextDescriptions::GLOBAL_DESCRIPTIONS)) {
405        Parcel parcel;
406        if (TextDescriptions::getParcelOfDescriptions(
407                (const uint8_t *)data, size, flag, 0, &parcel) == OK) {
408            if (parcel.dataSize() > 0) {
409                notifyListener(MEDIA_TIMED_TEXT, &parcel);
410            }
411        }
412    }
413
414    return OK;
415}
416}
417