| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "TimedTextDriver" |
| #include <utils/Log.h> |
| |
| #include <binder/IPCThreadState.h> |
| |
| #include <media/IMediaHTTPService.h> |
| #include <media/mediaplayer.h> |
| #include <media/MediaPlayerInterface.h> |
| #include <media/stagefright/DataSource.h> |
| #include <media/stagefright/FileSource.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/MediaErrors.h> |
| #include <media/stagefright/MediaSource.h> |
| #include <media/stagefright/MetaData.h> |
| #include <media/stagefright/Utils.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/ALooper.h> |
| #include <media/stagefright/timedtext/TimedTextDriver.h> |
| |
| #include "TextDescriptions.h" |
| #include "TimedTextPlayer.h" |
| #include "TimedTextSource.h" |
| |
| namespace android { |
| |
| TimedTextDriver::TimedTextDriver( |
| const wp<MediaPlayerBase> &listener, |
| const sp<IMediaHTTPService> &httpService) |
| : mLooper(new ALooper), |
| mListener(listener), |
| mHTTPService(httpService), |
| mState(UNINITIALIZED), |
| mCurrentTrackIndex(UINT_MAX) { |
| mLooper->setName("TimedTextDriver"); |
| mLooper->start(); |
| mPlayer = new TimedTextPlayer(listener); |
| mLooper->registerHandler(mPlayer); |
| } |
| |
| TimedTextDriver::~TimedTextDriver() { |
| mTextSourceVector.clear(); |
| mTextSourceTypeVector.clear(); |
| mLooper->stop(); |
| } |
| |
| status_t TimedTextDriver::selectTrack_l(size_t index) { |
| if (mCurrentTrackIndex == index) { |
| return OK; |
| } |
| sp<TimedTextSource> source; |
| source = mTextSourceVector.valueFor(index); |
| mPlayer->setDataSource(source); |
| if (mState == UNINITIALIZED) { |
| mState = PREPARED; |
| } |
| mCurrentTrackIndex = index; |
| return OK; |
| } |
| |
| status_t TimedTextDriver::start() { |
| Mutex::Autolock autoLock(mLock); |
| switch (mState) { |
| case UNINITIALIZED: |
| return INVALID_OPERATION; |
| case PLAYING: |
| return OK; |
| case PREPARED: |
| mPlayer->start(); |
| mState = PLAYING; |
| return OK; |
| case PAUSED: |
| mPlayer->resume(); |
| mState = PLAYING; |
| return OK; |
| default: |
| TRESPASS(); |
| } |
| return UNKNOWN_ERROR; |
| } |
| |
| status_t TimedTextDriver::pause() { |
| Mutex::Autolock autoLock(mLock); |
| ALOGV("%s() is called", __FUNCTION__); |
| switch (mState) { |
| case UNINITIALIZED: |
| return INVALID_OPERATION; |
| case PLAYING: |
| mPlayer->pause(); |
| mState = PAUSED; |
| return OK; |
| case PREPARED: |
| return INVALID_OPERATION; |
| case PAUSED: |
| return OK; |
| default: |
| TRESPASS(); |
| } |
| return UNKNOWN_ERROR; |
| } |
| |
| status_t TimedTextDriver::selectTrack(size_t index) { |
| status_t ret = OK; |
| Mutex::Autolock autoLock(mLock); |
| ALOGV("%s() is called", __FUNCTION__); |
| switch (mState) { |
| case UNINITIALIZED: |
| case PREPARED: |
| case PAUSED: |
| ret = selectTrack_l(index); |
| break; |
| case PLAYING: |
| mPlayer->pause(); |
| ret = selectTrack_l(index); |
| if (ret != OK) { |
| break; |
| } |
| mPlayer->start(); |
| break; |
| default: |
| TRESPASS(); |
| } |
| return ret; |
| } |
| |
| status_t TimedTextDriver::unselectTrack(size_t index) { |
| Mutex::Autolock autoLock(mLock); |
| ALOGV("%s() is called", __FUNCTION__); |
| if (mCurrentTrackIndex != index) { |
| return INVALID_OPERATION; |
| } |
| mCurrentTrackIndex = UINT_MAX; |
| switch (mState) { |
| case UNINITIALIZED: |
| return INVALID_OPERATION; |
| case PLAYING: |
| mPlayer->setDataSource(NULL); |
| mState = UNINITIALIZED; |
| return OK; |
| case PREPARED: |
| case PAUSED: |
| mState = UNINITIALIZED; |
| return OK; |
| default: |
| TRESPASS(); |
| } |
| return UNKNOWN_ERROR; |
| } |
| |
| status_t TimedTextDriver::seekToAsync(int64_t timeUs) { |
| Mutex::Autolock autoLock(mLock); |
| ALOGV("%s() is called", __FUNCTION__); |
| switch (mState) { |
| case UNINITIALIZED: |
| return INVALID_OPERATION; |
| case PREPARED: |
| mPlayer->seekToAsync(timeUs); |
| mPlayer->pause(); |
| mState = PAUSED; |
| return OK; |
| case PAUSED: |
| mPlayer->seekToAsync(timeUs); |
| mPlayer->pause(); |
| return OK; |
| case PLAYING: |
| mPlayer->seekToAsync(timeUs); |
| return OK; |
| default: |
| TRESPASS(); |
| } |
| return UNKNOWN_ERROR; |
| } |
| |
| status_t TimedTextDriver::addInBandTextSource( |
| size_t trackIndex, const sp<MediaSource>& mediaSource) { |
| sp<TimedTextSource> source = |
| TimedTextSource::CreateTimedTextSource(mediaSource); |
| if (source == NULL) { |
| return ERROR_UNSUPPORTED; |
| } |
| Mutex::Autolock autoLock(mLock); |
| mTextSourceVector.add(trackIndex, source); |
| mTextSourceTypeVector.add(TEXT_SOURCE_TYPE_IN_BAND); |
| return OK; |
| } |
| |
| status_t TimedTextDriver::addOutOfBandTextSource( |
| size_t trackIndex, const char *uri, const char *mimeType) { |
| |
| // To support local subtitle file only for now |
| if (strncasecmp("file://", uri, 7)) { |
| ALOGE("uri('%s') is not a file", uri); |
| return ERROR_UNSUPPORTED; |
| } |
| |
| sp<DataSource> dataSource = |
| DataSource::CreateFromURI(mHTTPService, uri); |
| return createOutOfBandTextSource(trackIndex, mimeType, dataSource); |
| } |
| |
| status_t TimedTextDriver::addOutOfBandTextSource( |
| size_t trackIndex, int fd, off64_t offset, off64_t length, const char *mimeType) { |
| |
| if (fd < 0) { |
| ALOGE("Invalid file descriptor: %d", fd); |
| return ERROR_UNSUPPORTED; |
| } |
| |
| sp<DataSource> dataSource = new FileSource(dup(fd), offset, length); |
| return createOutOfBandTextSource(trackIndex, mimeType, dataSource); |
| } |
| |
| status_t TimedTextDriver::createOutOfBandTextSource( |
| size_t trackIndex, |
| const char *mimeType, |
| const sp<DataSource>& dataSource) { |
| |
| if (dataSource == NULL) { |
| return ERROR_UNSUPPORTED; |
| } |
| |
| sp<TimedTextSource> source; |
| if (strcasecmp(mimeType, MEDIA_MIMETYPE_TEXT_SUBRIP) == 0) { |
| source = TimedTextSource::CreateTimedTextSource( |
| dataSource, TimedTextSource::OUT_OF_BAND_FILE_SRT); |
| } |
| |
| if (source == NULL) { |
| ALOGE("Failed to create timed text source"); |
| return ERROR_UNSUPPORTED; |
| } |
| |
| Mutex::Autolock autoLock(mLock); |
| mTextSourceVector.add(trackIndex, source); |
| mTextSourceTypeVector.add(TEXT_SOURCE_TYPE_OUT_OF_BAND); |
| return OK; |
| } |
| |
| size_t TimedTextDriver::countExternalTracks() const { |
| size_t nTracks = 0; |
| for (size_t i = 0, n = mTextSourceTypeVector.size(); i < n; ++i) { |
| if (mTextSourceTypeVector[i] == TEXT_SOURCE_TYPE_OUT_OF_BAND) { |
| ++nTracks; |
| } |
| } |
| return nTracks; |
| } |
| |
| void TimedTextDriver::getExternalTrackInfo(Parcel *parcel) { |
| Mutex::Autolock autoLock(mLock); |
| for (size_t i = 0, n = mTextSourceTypeVector.size(); i < n; ++i) { |
| if (mTextSourceTypeVector[i] == TEXT_SOURCE_TYPE_IN_BAND) { |
| continue; |
| } |
| |
| sp<MetaData> meta = mTextSourceVector.valueAt(i)->getFormat(); |
| |
| // There are two fields. |
| parcel->writeInt32(2); |
| |
| // track type. |
| parcel->writeInt32(MEDIA_TRACK_TYPE_TIMEDTEXT); |
| const char *lang = "und"; |
| if (meta != NULL) { |
| meta->findCString(kKeyMediaLanguage, &lang); |
| } |
| parcel->writeString16(String16(lang)); |
| } |
| } |
| |
| } // namespace android |