blob: 55a9803dfdd8a0f79fffeae28a4fdf921543da7c [file] [log] [blame]
/*
* 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