Merge changes Icd6d4fc4,Idff221fc,I99f0f79a,Ic310f937,I74879023, ...
* changes:
stagefright: rework MediaCodecSource
stagefright/rtsp: Remove dependence on OMXSource
cmds/stagefright: use MediaCodec* instead of OMXCodec
stagefright: add SimpleDecodingSource
stagefright/foundation: add Mutexed syntactic sugar
stagefright: add PREFER_SOFTWARE_CODEC flag to MediaCodecSource
stagefright: move MetadataRetriever off of OMXCodec
stagefright: change signature of MediaCodec::Create methods
stagefright: create findCodec methods in MediaCodecList
stagefright: move getOMXChannelMapping to ACodec
stagefright: Remove unused ClockEstimator and TimeSource
stagefright: Remove unused TimedTextDriver and Sources
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index f3b8f10..b79abce 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -9,7 +9,7 @@
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia libutils libbinder libstagefright_foundation \
- libjpeg libgui libcutils liblog libui
+ libjpeg libgui libcutils liblog
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
@@ -31,8 +31,8 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- SineSource.cpp \
- record.cpp
+ SineSource.cpp \
+ record.cpp
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation
@@ -55,8 +55,8 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- SineSource.cpp \
- recordvideo.cpp
+ SineSource.cpp \
+ recordvideo.cpp
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation
@@ -80,8 +80,8 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- SineSource.cpp \
- audioloop.cpp
+ SineSource.cpp \
+ audioloop.cpp
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation
@@ -108,7 +108,7 @@
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libgui \
- libstagefright_foundation libmedia libcutils
+ libstagefright_foundation libmedia libcutils
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
@@ -128,11 +128,11 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- sf2.cpp \
+ sf2.cpp \
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation \
- libmedia libgui libcutils libui
+ libmedia libgui libcutils libui
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
@@ -152,12 +152,12 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- codec.cpp \
- SimplePlayer.cpp \
+ codec.cpp \
+ SimplePlayer.cpp \
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation \
- libmedia libgui libcutils libui
+ libmedia libgui libcutils libui
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
@@ -220,11 +220,11 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- muxer.cpp \
+ muxer.cpp \
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation \
- libmedia libgui libcutils libui libc
+ libmedia libgui libcutils libui libc
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index 6e9e6ec..67017eb 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -23,13 +23,14 @@
#include <binder/ProcessState.h>
#include <media/mediarecorder.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/AMRWriter.h>
#include <media/stagefright/AudioPlayer.h>
#include <media/stagefright/AudioSource.h>
+#include <media/stagefright/MediaCodecSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/SimpleDecodingSource.h>
#include "SineSource.h"
using namespace android;
@@ -79,8 +80,6 @@
const int32_t kBitRate = outputWBAMR ? 16000 : 8000;
android::ProcessState::self()->startThreadPool();
- OMXClient client;
- CHECK_EQ(client.connect(), (status_t)OK);
sp<MediaSource> source;
if (useMic) {
@@ -95,24 +94,25 @@
source = new SineSource(kSampleRate, channels);
}
- sp<MetaData> meta = new MetaData;
- meta->setCString(
- kKeyMIMEType,
+ sp<AMessage> meta = new AMessage;
+ meta->setString(
+ "mime",
outputWBAMR ? MEDIA_MIMETYPE_AUDIO_AMR_WB
: MEDIA_MIMETYPE_AUDIO_AMR_NB);
- meta->setInt32(kKeyChannelCount, channels);
- meta->setInt32(kKeySampleRate, kSampleRate);
- meta->setInt32(kKeyBitRate, kBitRate);
+ meta->setInt32("channel-count", channels);
+ meta->setInt32("sample-rate", kSampleRate);
+ meta->setInt32("bitrate", kBitRate);
int32_t maxInputSize;
if (source->getFormat()->findInt32(kKeyMaxInputSize, &maxInputSize)) {
- meta->setInt32(kKeyMaxInputSize, maxInputSize);
+ meta->setInt32("max-input-size", maxInputSize);
}
- sp<MediaSource> encoder = OMXCodec::Create(
- client.interface(),
- meta, true /* createEncoder */,
- source);
+ sp<ALooper> looper = new ALooper;
+ looper->setName("audioloop");
+ looper->start();
+
+ sp<MediaSource> encoder = MediaCodecSource::Create(looper, meta, source);
if (fileOut != NULL) {
// target file specified, write encoded AMR output
@@ -128,17 +128,15 @@
writer->stop();
} else {
// otherwise decode to speaker
- sp<MediaSource> decoder = OMXCodec::Create(
- client.interface(),
- meta, false /* createEncoder */,
- encoder);
+ sp<MediaSource> decoder = SimpleDecodingSource::Create(encoder);
if (playToSpeaker) {
AudioPlayer *player = new AudioPlayer(NULL);
player->setSource(decoder);
player->start();
sleep(duration);
- source->stop(); // must stop source otherwise delete player will hang
+
+ decoder.clear(); // must clear |decoder| otherwise delete player will hang.
delete player; // there is no player->stop()...
} else {
CHECK_EQ(decoder->start(), (status_t)OK);
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 594c933..fbea7e9 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -18,16 +18,18 @@
#include <binder/ProcessState.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/AudioPlayer.h>
#include <media/stagefright/CameraSource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaCodecSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MPEG4Writer.h>
-#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/SimpleDecodingSource.h>
#include <media/MediaPlayerInterface.h>
using namespace android;
@@ -182,9 +184,6 @@
fprintf(stderr, "input color format must be 0 (YUV420SP) or 1 (YUV420P)\n");
return 1;
}
- OMXClient client;
- CHECK_EQ(client.connect(), (status_t)OK);
-
status_t err = OK;
#if 0
@@ -197,8 +196,7 @@
sp<MetaData> meta = source->getFormat();
- sp<MediaSource> decoder = OMXCodec::Create(
- client.interface(), meta, false /* createEncoder */, source);
+ sp<MediaSource> decoder = SimpleDecodingSource::Create(source);
int width, height;
bool success = meta->findInt32(kKeyWidth, &width);
@@ -210,22 +208,21 @@
sp<MediaSource> decoder = new DummySource(width, height, colorFormat);
#endif
- sp<MetaData> enc_meta = new MetaData;
- // enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
- // enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
- enc_meta->setInt32(kKeyWidth, width);
- enc_meta->setInt32(kKeyHeight, height);
- enc_meta->setInt32(kKeySampleRate, kFramerate);
- enc_meta->setInt32(kKeyBitRate, kVideoBitRate);
- enc_meta->setInt32(kKeyStride, width);
- enc_meta->setInt32(kKeySliceHeight, height);
- enc_meta->setInt32(kKeyIFramesInterval, kIFramesIntervalSec);
- enc_meta->setInt32(kKeyColorFormat, colorFormat);
+ sp<AMessage> enc_meta = new AMessage;
+ // enc_meta->setString("mime", MEDIA_MIMETYPE_VIDEO_H263);
+ // enc_meta->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4);
+ enc_meta->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
+ enc_meta->setInt32("width", width);
+ enc_meta->setInt32("height", height);
+ enc_meta->setInt32("sample-rate", kFramerate);
+ enc_meta->setInt32("bit-rate", kVideoBitRate);
+ // enc_meta->setInt32("stride", width);
+ // enc_meta->setInt32("slice-height", height);
+ enc_meta->setInt32("i-frame-interval", kIFramesIntervalSec);
+ enc_meta->setInt32("color-format", colorFormat);
sp<MediaSource> encoder =
- OMXCodec::Create(
- client.interface(), enc_meta, true /* createEncoder */, decoder);
+ MediaCodecSource::Create(looper, format, decoder);
#if 1
sp<MPEG4Writer> writer = new MPEG4Writer("/sdcard/output.mp4");
@@ -260,7 +257,6 @@
#endif
printf("$\n");
- client.disconnect();
#endif
#if 0
@@ -299,9 +295,6 @@
int main(int /* argc */, char ** /* argv */) {
android::ProcessState::self()->startThreadPool();
- OMXClient client;
- CHECK_EQ(client.connect(), (status_t)OK);
-
const int32_t kSampleRate = 22050;
const int32_t kNumChannels = 2;
sp<MediaSource> audioSource = new SineSource(kSampleRate, kNumChannels);
@@ -317,16 +310,20 @@
player->stop();
#endif
- sp<MetaData> encMeta = new MetaData;
- encMeta->setCString(kKeyMIMEType,
+ sp<AMessage> encMeta = new AMessage;
+ encMeta->setString("mime",
0 ? MEDIA_MIMETYPE_AUDIO_AMR_WB : MEDIA_MIMETYPE_AUDIO_AAC);
- encMeta->setInt32(kKeySampleRate, kSampleRate);
- encMeta->setInt32(kKeyChannelCount, kNumChannels);
- encMeta->setInt32(kKeyMaxInputSize, 8192);
- encMeta->setInt32(kKeyBitRate, kAudioBitRate);
+ encMeta->setInt32("sample-rate", kSampleRate);
+ encMeta->setInt32("channel-count", kNumChannels);
+ encMeta->setInt32("max-input-size", 8192);
+ encMeta->setInt32("bitrate", kAudioBitRate);
+
+ sp<ALooper> looper = new ALooper;
+ looper->setName("record");
+ looper->start();
sp<MediaSource> encoder =
- OMXCodec::Create(client.interface(), encMeta, true, audioSource);
+ MediaCodecSource::Create(looper, encMeta, audioSource);
encoder->start();
@@ -348,8 +345,6 @@
encoder->stop();
- client.disconnect();
-
return 0;
}
#endif
diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp
index 2ad40bd..af39d46 100644
--- a/cmds/stagefright/recordvideo.cpp
+++ b/cmds/stagefright/recordvideo.cpp
@@ -23,15 +23,18 @@
#include <binder/ProcessState.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/AudioPlayer.h>
#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaCodecSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MPEG4Writer.h>
-#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
#include <media/MediaPlayerInterface.h>
+#include <OMX_Video.h>
+
using namespace android;
// Print usage showing how to use this utility to record videos
@@ -265,44 +268,45 @@
}
}
- OMXClient client;
- CHECK_EQ(client.connect(), (status_t)OK);
-
status_t err = OK;
sp<MediaSource> source =
new DummySource(width, height, nFrames, frameRateFps, colorFormat);
- sp<MetaData> enc_meta = new MetaData;
+ sp<AMessage> enc_meta = new AMessage;
switch (codec) {
case 1:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ enc_meta->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4);
break;
case 2:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+ enc_meta->setString("mime", MEDIA_MIMETYPE_VIDEO_H263);
break;
default:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ enc_meta->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
break;
}
- enc_meta->setInt32(kKeyWidth, width);
- enc_meta->setInt32(kKeyHeight, height);
- enc_meta->setInt32(kKeyFrameRate, frameRateFps);
- enc_meta->setInt32(kKeyBitRate, bitRateBps);
- enc_meta->setInt32(kKeyStride, width);
- enc_meta->setInt32(kKeySliceHeight, height);
- enc_meta->setInt32(kKeyIFramesInterval, iFramesIntervalSeconds);
- enc_meta->setInt32(kKeyColorFormat, colorFormat);
+ enc_meta->setInt32("width", width);
+ enc_meta->setInt32("height", height);
+ enc_meta->setInt32("frame-rate", frameRateFps);
+ enc_meta->setInt32("bitrate", bitRateBps);
+ enc_meta->setInt32("stride", width);
+ enc_meta->setInt32("slice-height", height);
+ enc_meta->setInt32("i-frame-interval", iFramesIntervalSeconds);
+ enc_meta->setInt32("color-format", colorFormat);
if (level != -1) {
- enc_meta->setInt32(kKeyVideoLevel, level);
+ enc_meta->setInt32("level", level);
}
if (profile != -1) {
- enc_meta->setInt32(kKeyVideoProfile, profile);
+ enc_meta->setInt32("profile", profile);
}
+ sp<ALooper> looper = new ALooper;
+ looper->setName("recordvideo");
+ looper->start();
+
sp<MediaSource> encoder =
- OMXCodec::Create(
- client.interface(), enc_meta, true /* createEncoder */, source,
- 0, preferSoftwareCodec ? OMXCodec::kPreferSoftwareCodecs : 0);
+ MediaCodecSource::Create(
+ looper, enc_meta, source, NULL /* consumer */,
+ preferSoftwareCodec ? MediaCodecSource::FLAG_PREFER_SOFTWARE_CODEC : 0);
int fd = open(fileName, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
if (fd < 0) {
@@ -321,7 +325,6 @@
int64_t end = systemTime();
fprintf(stderr, "$\n");
- client.disconnect();
if (err != OK && err != ERROR_END_OF_STREAM) {
fprintf(stderr, "record failed: %d\n", err);
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index ea8a88e..757c9f6 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -31,20 +31,26 @@
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
+#include <media/ICrypto.h>
#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
+#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
#include "include/NuCachedSource2.h"
#include <media/stagefright/AudioPlayer.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/JPEGSource.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/SimpleDecodingSource.h>
+#include <media/stagefright/Utils.h>
#include <media/mediametadataretriever.h>
#include <media/stagefright/foundation/hexdump.h>
@@ -163,7 +169,7 @@
out = NULL;
}
-static void playSource(OMXClient *client, sp<MediaSource> &source) {
+static void playSource(sp<MediaSource> &source) {
sp<MetaData> meta = source->getFormat();
const char *mime;
@@ -175,20 +181,14 @@
} else {
int flags = 0;
if (gPreferSoftwareCodec) {
- flags |= OMXCodec::kPreferSoftwareCodecs;
+ flags |= MediaCodecList::kPreferSoftwareCodecs;
}
if (gForceToUseHardwareCodec) {
CHECK(!gPreferSoftwareCodec);
- flags |= OMXCodec::kHardwareCodecsOnly;
+ flags |= MediaCodecList::kHardwareCodecsOnly;
}
- rawSource = OMXCodec::Create(
- client->interface(), meta, false /* createEncoder */, source,
- NULL /* matchComponentName */,
- flags,
- gSurface);
-
+ rawSource = SimpleDecodingSource::Create(source, flags, gSurface);
if (rawSource == NULL) {
- fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime);
return;
}
displayAVCProfileLevelIfPossible(meta);
@@ -342,12 +342,6 @@
printf(".");
fflush(stdout);
}
-
- // render buffers from OMXCodec
- if (buffer->graphicBuffer() != NULL && gSurface != NULL) {
- gSurface->queueBuffer(gSurface.get(), buffer->graphicBuffer()->getNativeBuffer(), -1);
- buffer->meta_data()->setInt32(kKeyRendered, 1);
- }
}
sumDecodeUs += delayDecodeUs;
@@ -624,7 +618,7 @@
fprintf(stderr, " -D(ump) output_filename (decoded PCM data to a file)\n");
}
-static void dumpCodecProfiles(const sp<IOMX>& omx, bool queryDecoders) {
+static void dumpCodecProfiles(bool queryDecoders) {
const char *kMimeTypes[] = {
MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4,
MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC,
@@ -637,30 +631,36 @@
const char *codecType = queryDecoders? "decoder" : "encoder";
printf("%s profiles:\n", codecType);
+ sp<IMediaCodecList> list = MediaCodecList::getInstance();
+ size_t numCodecs = list->countCodecs();
+
for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++k) {
printf("type '%s':\n", kMimeTypes[k]);
- Vector<CodecCapabilities> results;
- // will retrieve hardware and software codecs
- CHECK_EQ(QueryCodecs(omx, kMimeTypes[k],
- queryDecoders,
- &results), (status_t)OK);
-
- for (size_t i = 0; i < results.size(); ++i) {
+ for (size_t index = 0; index < numCodecs; ++index) {
+ sp<MediaCodecInfo> info = list->getCodecInfo(index);
+ if (info == NULL || info->isEncoder() != !queryDecoders) {
+ continue;
+ }
+ sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(kMimeTypes[k]);
+ if (caps == NULL) {
+ continue;
+ }
printf(" %s '%s' supports ",
- codecType, results[i].mComponentName.string());
+ codecType, info->getCodecName());
- if (results[i].mProfileLevels.size() == 0) {
- printf("NOTHING.\n");
- continue;
+ Vector<MediaCodecInfo::ProfileLevel> profileLevels;
+ caps->getSupportedProfileLevels(&profileLevels);
+ if (profileLevels.size() == 0) {
+ printf("NOTHING.\n");
+ continue;
}
- for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) {
- const CodecProfileLevel &profileLevel =
- results[i].mProfileLevels[j];
+ for (size_t j = 0; j < profileLevels.size(); ++j) {
+ const MediaCodecInfo::ProfileLevel &profileLevel = profileLevels[j];
- printf("%s%" PRIu32 "/%" PRIu32, j > 0 ? ", " : "",
- profileLevel.mProfile, profileLevel.mLevel);
+ printf("%s%u/%u", j > 0 ? ", " : "",
+ profileLevel.mProfile, profileLevel.mLevel);
}
printf("\n");
@@ -887,17 +887,8 @@
}
if (dumpProfiles) {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.player"));
- sp<IMediaPlayerService> service =
- interface_cast<IMediaPlayerService>(binder);
-
- CHECK(service.get() != NULL);
-
- sp<IOMX> omx = service->getOMX();
- CHECK(omx.get() != NULL);
- dumpCodecProfiles(omx, true /* queryDecoders */);
- dumpCodecProfiles(omx, false /* queryDecoders */);
+ dumpCodecProfiles(true /* queryDecoders */);
+ dumpCodecProfiles(false /* queryDecoders */);
}
if (listComponents) {
@@ -960,16 +951,11 @@
false /* isControlledByApp */);
gSurface = new Surface(producer);
}
-
- CHECK_EQ((status_t)OK,
- native_window_api_connect(
- gSurface.get(), NATIVE_WINDOW_API_MEDIA));
}
DataSource::RegisterDefaultSniffers();
- OMXClient client;
- status_t err = client.connect();
+ status_t err = OK;
for (int k = 0; k < argc && err == OK; ++k) {
bool syncInfoPresent = true;
@@ -1103,31 +1089,16 @@
} else if (dumpStream) {
dumpSource(mediaSource, dumpStreamFilename);
} else if (dumpPCMStream) {
- OMXClient client;
- CHECK_EQ(client.connect(), (status_t)OK);
-
- sp<MediaSource> decSource =
- OMXCodec::Create(
- client.interface(),
- mediaSource->getFormat(),
- false,
- mediaSource,
- 0,
- 0);
-
+ sp<MediaSource> decSource = SimpleDecodingSource::Create(mediaSource);
dumpSource(decSource, dumpStreamFilename);
} else if (seekTest) {
performSeekTest(mediaSource);
} else {
- playSource(&client, mediaSource);
+ playSource(mediaSource);
}
}
if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) {
- CHECK_EQ((status_t)OK,
- native_window_api_disconnect(
- gSurface.get(), NATIVE_WINDOW_API_MEDIA));
-
gSurface.clear();
if (useSurfaceAlloc) {
@@ -1135,7 +1106,5 @@
}
}
- client.disconnect();
-
return 0;
}
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 8b5b862..731b724 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -91,6 +91,14 @@
int width, int height, int rate, int bitrate,
OMX_VIDEO_AVCPROFILETYPE profile = OMX_VIDEO_AVCProfileBaseline);
+ // Quirk still supported, even though deprecated
+ enum Quirks {
+ kRequiresAllocateBufferOnInputPorts = 1,
+ kRequiresAllocateBufferOnOutputPorts = 2,
+ };
+
+ static status_t getOMXChannelMapping(size_t numChannels, OMX_AUDIO_CHANNELTYPE map[]);
+
protected:
virtual ~ACodec();
diff --git a/include/media/stagefright/ClockEstimator.h b/include/media/stagefright/ClockEstimator.h
deleted file mode 100644
index 1455b7f..0000000
--- a/include/media/stagefright/ClockEstimator.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-**
-** Copyright 2014, 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.
-*/
-
-#ifndef CLOCK_ESTIMATOR_H_
-
-#define CLOCK_ESTIMATOR_H_
-
-#include "foundation/ABase.h"
-#include <utils/RefBase.h>
-#include <utils/Vector.h>
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-struct ClockEstimator : RefBase {
- virtual double estimate(double x, double y) = 0;
- virtual void reset() = 0;
-};
-
-struct WindowedLinearFitEstimator : ClockEstimator {
- struct LinearFit {
- /**
- * Fit y = a * x + b, where each input has a weight
- */
- double mX; // sum(w_i * x_i)
- double mXX; // sum(w_i * x_i^2)
- double mY; // sum(w_i * y_i)
- double mYY; // sum(w_i * y_i^2)
- double mXY; // sum(w_i * x_i * y_i)
- double mW; // sum(w_i)
-
- LinearFit();
- void reset();
- void combine(const LinearFit &lf);
- void add(double x, double y, double w);
- void scale(double w);
- double interpolate(double x);
- double size() const;
-
- DISALLOW_EVIL_CONSTRUCTORS(LinearFit);
- };
-
- /**
- * Estimator for f(x) = y' where input y' is noisy, but
- * theoretically linear:
- *
- * y' =~ y = a * x + b
- *
- * It uses linear fit regression over a tapering rolling window
- * to get an estimate for y (from the current and past inputs
- * (x, y')).
- *
- * ____________
- * /| |\
- * / | | \
- * / | | \ <--- new data (x, y')
- * / | main | \
- * <--><----------><-->
- * tail head
- *
- * weight is 1 under the main window, tapers exponentially by
- * the factors given in the head and the tail.
- *
- * Assuming that x and y' are monotonic, that x is somewhat
- * evenly sampled, and that a =~ 1, the estimated y is also
- * going to be monotonic.
- */
- WindowedLinearFitEstimator(
- size_t headLength = 5, double headFactor = 0.5,
- size_t mainLength = 0, double tailFactor = 0.99);
-
- virtual void reset();
-
- // add a new sample (x -> y') and return an estimated value for the true y
- virtual double estimate(double x, double y);
-
-private:
- Vector<double> mXHistory; // circular buffer
- Vector<double> mYHistory; // circular buffer
- LinearFit mHead;
- LinearFit mMain;
- LinearFit mTail;
- double mHeadFactorInv;
- double mTailFactor;
- double mFirstWeight;
- size_t mHistoryLength;
- size_t mHeadLength;
- size_t mNumSamples;
- size_t mSampleIx;
-
- DISALLOW_EVIL_CONSTRUCTORS(WindowedLinearFitEstimator);
-};
-
-}; // namespace android
-
-#endif
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index cdfa159..3ad942c 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -64,11 +64,11 @@
static const pid_t kNoPid = -1;
static sp<MediaCodec> CreateByType(
- const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err = NULL,
+ const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err = NULL,
pid_t pid = kNoPid);
static sp<MediaCodec> CreateByComponentName(
- const sp<ALooper> &looper, const char *name, status_t *err = NULL,
+ const sp<ALooper> &looper, const AString &name, status_t *err = NULL,
pid_t pid = kNoPid);
static sp<PersistentSurface> CreatePersistentInputSurface();
diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h
index bf4db87..44dbde0 100644
--- a/include/media/stagefright/MediaCodecList.h
+++ b/include/media/stagefright/MediaCodecList.h
@@ -65,6 +65,22 @@
// only to be used by MediaPlayerService
void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
+ enum Flags {
+ kPreferSoftwareCodecs = 1,
+ kHardwareCodecsOnly = 2,
+ };
+
+ static void findMatchingCodecs(
+ const char *mime,
+ bool createEncoder,
+ uint32_t flags,
+ Vector<AString> *matching);
+
+ static uint32_t getQuirksFor(const char *mComponentName);
+
+ static bool isSoftwareCodec(const AString &componentName);
+
+
private:
class BinderDeathObserver : public IBinder::DeathRecipient {
void binderDied(const wp<IBinder> &the_late_who __unused);
diff --git a/include/media/stagefright/MediaCodecSource.h b/include/media/stagefright/MediaCodecSource.h
index 71f58a9..d8c70fe 100644
--- a/include/media/stagefright/MediaCodecSource.h
+++ b/include/media/stagefright/MediaCodecSource.h
@@ -19,16 +19,18 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/foundation/Mutexed.h>
#include <media/stagefright/MediaSource.h>
+#include <gui/IGraphicBufferConsumer.h>
+
namespace android {
struct ALooper;
-class AMessage;
+struct AMessage;
struct AReplyToken;
class IGraphicBufferProducer;
-class IGraphicBufferConsumer;
-class MediaCodec;
+struct MediaCodec;
class MetaData;
struct MediaCodecSource : public MediaSource,
@@ -36,6 +38,7 @@
enum FlagBits {
FLAG_USE_SURFACE_INPUT = 1,
FLAG_USE_METADATA_INPUT = 2,
+ FLAG_PREFER_SOFTWARE_CODEC = 4, // used for testing only
};
static sp<MediaCodecSource> Create(
@@ -75,6 +78,7 @@
kWhatStart,
kWhatStop,
kWhatPause,
+ kWhatStopStalled,
};
MediaCodecSource(
@@ -122,12 +126,16 @@
int64_t mFirstSampleTimeUs;
List<int64_t> mDriftTimeQueue;
- // following variables are protected by mOutputBufferLock
- Mutex mOutputBufferLock;
- Condition mOutputBufferCond;
- List<MediaBuffer*> mOutputBufferQueue;
- bool mEncoderReachedEOS;
- status_t mErrorCode;
+ struct Output {
+ Output();
+ List<MediaBuffer*> mBufferQueue;
+ bool mEncoderReachedEOS;
+ status_t mErrorCode;
+ Condition mCond;
+ };
+ Mutexed<Output> mOutput;
+
+ int32_t mGeneration;
DISALLOW_EVIL_CONSTRUCTORS(MediaCodecSource);
};
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 7fabcb3..5dce6e1 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -117,8 +117,6 @@
static uint32_t getComponentQuirks(
const sp<MediaCodecInfo> &list);
- static bool findCodecQuirks(const char *componentName, uint32_t *quirks);
-
protected:
virtual ~OMXCodec();
@@ -403,8 +401,6 @@
bool isEncoder,
CodecCapabilities *caps);
-status_t getOMXChannelMapping(size_t numChannels, OMX_AUDIO_CHANNELTYPE map[]);
-
} // namespace android
#endif // OMX_CODEC_H_
diff --git a/include/media/stagefright/SimpleDecodingSource.h b/include/media/stagefright/SimpleDecodingSource.h
new file mode 100644
index 0000000..e56eeb9
--- /dev/null
+++ b/include/media/stagefright/SimpleDecodingSource.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016, 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.
+ */
+
+#ifndef SIMPLE_DECODING_SOURCE_H_
+#define SIMPLE_DECODING_SOURCE_H_
+
+#include <system/window.h>
+
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/Mutexed.h>
+
+#include <utils/Condition.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+struct ALooper;
+struct AMessage;
+class MediaBuffer;
+struct MediaCodec;
+class MetaData;
+
+class SimpleDecodingSource : public MediaSource {
+public:
+ // Creates a MediaSource that uses MediaCodec to decode a compressed input |source|.
+ // The selected codec can be influenced using |flags|. This source only supports the
+ // kPreferGoogleCodec and kNonGoogleCodecsOnly |flags| - MediaCodecList.
+ // You can pass in a target |nativeWindow| to render video directly onto a surface. In this
+ // case the source will return empty buffers.
+ // This source cannot be restarted (hence the name "Simple"), all reads are blocking, and
+ // does not support secure input or pausing.
+ static sp<SimpleDecodingSource> Create(
+ const sp<MediaSource> &source, uint32_t flags = 0,
+ const sp<ANativeWindow> &nativeWindow = NULL);
+
+ virtual ~SimpleDecodingSource();
+
+ // starts this source (and it's underlying source). |params| is ignored.
+ virtual status_t start(MetaData *params = NULL);
+
+ // stops this source (and it's underlying source).
+ virtual status_t stop();
+
+ // returns the output format of this source.
+ virtual sp<MetaData> getFormat();
+
+ // reads from the source. This call always blocks.
+ virtual status_t read(MediaBuffer **buffer, const ReadOptions *options);
+
+ // unsupported methods
+ virtual status_t pause() { return INVALID_OPERATION; }
+ virtual status_t setBuffers(const Vector<MediaBuffer *> &) { return INVALID_OPERATION; }
+
+private:
+ // Construct this using a codec, source and looper.
+ SimpleDecodingSource(
+ const sp<MediaCodec> &codec, const sp<MediaSource> &source, const sp<ALooper> &looper,
+ bool usingSurface, const sp<AMessage> &format);
+
+ sp<MediaCodec> mCodec;
+ sp<MediaSource> mSource;
+ sp<ALooper> mLooper;
+ bool mUsingSurface;
+ enum State {
+ INIT,
+ STARTED,
+ STOPPING,
+ STOPPED,
+ ERROR,
+ };
+ AString mComponentName;
+
+ struct ProtectedState {
+ ProtectedState(const sp<AMessage> &format);
+ bool mReading;
+ Condition mReadCondition;
+
+ sp<AMessage> mFormat;
+ State mState;
+ bool mQueuedInputEOS;
+ bool mGotOutputEOS;
+ };
+ Mutexed<ProtectedState> mProtectedState;
+
+ // do the actual reading
+ status_t doRead(
+ Mutexed<ProtectedState>::Locked &me, MediaBuffer **buffer, const ReadOptions *options);
+};
+
+} // namespace android
+
+#endif
diff --git a/include/media/stagefright/TimeSource.h b/include/media/stagefright/TimeSource.h
deleted file mode 100644
index 8f11e14..0000000
--- a/include/media/stagefright/TimeSource.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-#ifndef TIME_SOURCE_H_
-
-#define TIME_SOURCE_H_
-
-#include <stdint.h>
-
-namespace android {
-
-class TimeSource {
-public:
- TimeSource() {}
- virtual ~TimeSource() {}
-
- virtual int64_t getRealTimeUs() = 0;
-
-private:
- TimeSource(const TimeSource &);
- TimeSource &operator=(const TimeSource &);
-};
-
-class SystemTimeSource : public TimeSource {
-public:
- SystemTimeSource();
-
- virtual int64_t getRealTimeUs();
-
-private:
- int64_t mStartTimeUs;
-};
-
-} // namespace android
-
-#endif // TIME_SOURCE_H_
diff --git a/include/media/stagefright/foundation/Mutexed.h b/include/media/stagefright/foundation/Mutexed.h
new file mode 100644
index 0000000..d4fd905
--- /dev/null
+++ b/include/media/stagefright/foundation/Mutexed.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2016, 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.
+ */
+
+#ifndef STAGEFRIGHT_FOUNDATION_MUTEXED_H_
+#define STAGEFRIGHT_FOUNDATION_MUTEXED_H_
+
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+
+namespace android {
+
+/*
+ * Wrapper class to programmatically protect a structure using a mutex.
+ *
+ * Mutexed<> objects contain a built-in mutex. Protection is enforced because the structure can
+ * only be accessed by locking the mutex first.
+ *
+ * Usage:
+ *
+ * struct DataToProtect {
+ * State(int var1) : mVar1(var1), mVar2(0) { }
+ * int mVar1;
+ * int mVar2;
+ * Condition mCondition1;
+ * };
+ *
+ * Mutexed<DataToProtect> mProtectedData;
+ *
+ * // members are inaccessible via mProtectedData directly
+ *
+ * void someFunction() {
+ * Mutexed<DataToProtect>::Locked data(mProtectedData); // access the protected data
+ *
+ * // the mutex is locked here, so accessing the data is safe
+ *
+ * if (data->mVar1 < 5) {
+ * ++data->mVar2;
+ * }
+ *
+ * // if you need to temporarily unlock the mutex, you can use unlock/relock mutex locally
+ * // using the accessor object.
+ *
+ * data.unlock();
+ *
+ * // data is inaccessible here
+ *
+ * doSomeLongOperation();
+ *
+ * data.lock();
+ *
+ * // data is now accessible again. Note: it may have changed since unlock().
+ *
+ * // you can use the integral mutex to wait for a condition
+ *
+ * data.waitForCondition(data->mCondition1);
+ *
+ * helper(&data);
+ * }
+ *
+ * void trigger() {
+ * Mutexed<DataToProtect>::Locked data(mProtectedData);
+ * data->mCondition1.signal();
+ * }
+ *
+ * void helper(const Mutexed<DataToProtect>::Locked &data) {
+ * data->mVar1 = 3;
+ * }
+ *
+ */
+
+template<typename T>
+class Mutexed {
+public:
+ /*
+ * Accessor-guard of the mutex-protected structure. This can be dereferenced to
+ * access the structure (using -> or * operators).
+ *
+ * Upon creation, the mutex is locked. You can use lock()/unlock() methods to
+ * temporarily lock/unlock the mutex. Using any references to the underlying
+ * structure or its members defeats the protection of this class, so don't do
+ * it.
+ *
+ * Note: The accessor-guard itself is not thread-safe. E.g. you should not call
+ * unlock() or lock() from different threads; they must be called from the thread
+ * that locked the original wrapper.
+ *
+ * Also note: Recursive locking/unlocking is not supported by the accessor. This
+ * is as intended, as it allows lenient locking/unlocking via multiple code paths.
+ */
+ class Locked {
+ public:
+ inline Locked(Mutexed<T> &mParent);
+ inline ~Locked();
+
+ // dereference the protected structure. This returns nullptr if the
+ // mutex is not locked by this accessor-guard.
+ inline T* operator->() const { return mLocked ? &mTreasure : nullptr; }
+ inline T& operator*() const { return mLocked ? mTreasure : *(T*)nullptr; }
+
+ // Wait on the condition variable using lock. Must be locked.
+ inline status_t waitForCondition(Condition &cond) { return cond.wait(mLock); }
+
+ // same with relative timeout
+ inline status_t waitForConditionRelative(Condition &cond, nsecs_t reltime) {
+ return cond.waitRelative(mLock, reltime);
+ }
+
+ // unlocks the integral mutex. No-op if the mutex was already unlocked.
+ inline void unlock();
+
+ // locks the integral mutex. No-op if the mutex was already locked.
+ inline void lock();
+
+ private:
+ Mutex &mLock;
+ T &mTreasure;
+ bool mLocked;
+
+ // disable copy constructors
+ Locked(const Locked&) = delete;
+ void operator=(const Locked&) = delete;
+ };
+
+ // Wrap all constructors of the underlying structure
+ template<typename ...Args>
+ Mutexed(Args... args) : mTreasure(args...) { }
+
+ ~Mutexed() { }
+
+ // Lock the mutex, and create an accessor-guard (a Locked object) to access the underlying
+ // structure. This returns an object that dereferences to the wrapped structure when the mutex
+ // is locked by it, or otherwise to "null".
+ inline Locked&& lock() {
+ // use rvalue as Locked has no copy constructor
+ return std::move(Locked(*this));
+ }
+
+private:
+ friend class Locked;
+ Mutex mLock;
+ T mTreasure;
+
+ // disable copy constructors
+ Mutexed(const Mutexed<T>&) = delete;
+ void operator=(const Mutexed<T>&) = delete;
+};
+
+template<typename T>
+inline Mutexed<T>::Locked::Locked(Mutexed<T> &mParent)
+ : mLock(mParent.mLock),
+ mTreasure(mParent.mTreasure),
+ mLocked(true) {
+ mLock.lock();
+
+}
+
+template<typename T>
+inline Mutexed<T>::Locked::~Locked() {
+ if (mLocked) {
+ mLock.unlock();
+ }
+}
+
+template<typename T>
+inline void Mutexed<T>::Locked::unlock() {
+ if (mLocked) {
+ mLocked = false;
+ mLock.unlock();
+ }
+}
+
+template<typename T>
+inline void Mutexed<T>::Locked::lock() {
+ if (!mLocked) {
+ mLock.lock();
+ mLocked = true;
+ }
+}
+
+} // namespace android
+
+#endif
diff --git a/include/media/stagefright/timedtext/TimedTextDriver.h b/include/media/stagefright/timedtext/TimedTextDriver.h
deleted file mode 100644
index 6f7c693..0000000
--- a/include/media/stagefright/timedtext/TimedTextDriver.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef TIMED_TEXT_DRIVER_H_
-#define TIMED_TEXT_DRIVER_H_
-
-#include <media/stagefright/foundation/ABase.h> // for DISALLOW_* macro
-#include <utils/Errors.h> // for status_t
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-
-namespace android {
-
-struct ALooper;
-struct IMediaHTTPService;
-class MediaPlayerBase;
-class MediaSource;
-class Parcel;
-class TimedTextPlayer;
-class TimedTextSource;
-class DataSource;
-
-class TimedTextDriver {
-public:
- TimedTextDriver(
- const wp<MediaPlayerBase> &listener,
- const sp<IMediaHTTPService> &httpService);
-
- ~TimedTextDriver();
-
- status_t start();
- status_t pause();
- status_t selectTrack(size_t index);
- status_t unselectTrack(size_t index);
-
- status_t seekToAsync(int64_t timeUs);
-
- status_t addInBandTextSource(
- size_t trackIndex, const sp<MediaSource>& source);
-
- status_t addOutOfBandTextSource(
- size_t trackIndex, const char *uri, const char *mimeType);
-
- // Caller owns the file desriptor and caller is responsible for closing it.
- status_t addOutOfBandTextSource(
- size_t trackIndex, int fd, off64_t offset,
- off64_t length, const char *mimeType);
-
- void getExternalTrackInfo(Parcel *parcel);
- size_t countExternalTracks() const;
-
-private:
- Mutex mLock;
-
- enum State {
- UNINITIALIZED,
- PREPARED,
- PLAYING,
- PAUSED,
- };
-
- enum TextSourceType {
- TEXT_SOURCE_TYPE_IN_BAND = 0,
- TEXT_SOURCE_TYPE_OUT_OF_BAND,
- };
-
- sp<ALooper> mLooper;
- sp<TimedTextPlayer> mPlayer;
- wp<MediaPlayerBase> mListener;
- sp<IMediaHTTPService> mHTTPService;
-
- // Variables to be guarded by mLock.
- State mState;
- size_t mCurrentTrackIndex;
- KeyedVector<size_t, sp<TimedTextSource> > mTextSourceVector;
- Vector<TextSourceType> mTextSourceTypeVector;
-
- // -- End of variables to be guarded by mLock
-
- status_t selectTrack_l(size_t index);
-
- status_t createOutOfBandTextSource(
- size_t trackIndex, const char* mimeType,
- const sp<DataSource>& dataSource);
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_DRIVER_H_
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 36fe800..3b13f2a 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -41,8 +41,6 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaCodecSource.h>
-#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
#include <media/MediaProfiles.h>
#include <camera/ICamera.h>
#include <camera/CameraParameters.h>
@@ -1213,18 +1211,6 @@
}
status_t StagefrightRecorder::checkVideoEncoderCapabilities() {
- /* hardware codecs must support camera source meta data mode */
- Vector<CodecCapabilities> codecs;
- OMXClient client;
- CHECK_EQ(client.connect(), (status_t)OK);
- QueryCodecs(
- client.interface(),
- (mVideoEncoder == VIDEO_ENCODER_H263 ? MEDIA_MIMETYPE_VIDEO_H263 :
- mVideoEncoder == VIDEO_ENCODER_MPEG_4_SP ? MEDIA_MIMETYPE_VIDEO_MPEG4 :
- mVideoEncoder == VIDEO_ENCODER_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8 :
- mVideoEncoder == VIDEO_ENCODER_H264 ? MEDIA_MIMETYPE_VIDEO_AVC : ""),
- false /* decoder */, true /* hwCodec */, &codecs);
-
if (!mCaptureFpsEnable) {
// Dont clip for time lapse capture as encoder will have enough
// time to encode because of slow capture rate of time lapse.
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index fad06b8..23bec9f 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -41,7 +41,6 @@
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
#include <media/stagefright/PersistentSurface.h>
#include <media/stagefright/SurfaceUtils.h>
#include <media/hardware/HardwareAPI.h>
@@ -824,8 +823,8 @@
uint32_t requiresAllocateBufferBit =
(portIndex == kPortIndexInput)
- ? OMXCodec::kRequiresAllocateBufferOnInputPorts
- : OMXCodec::kRequiresAllocateBufferOnOutputPorts;
+ ? kRequiresAllocateBufferOnInputPorts
+ : kRequiresAllocateBufferOnOutputPorts;
if ((portIndex == kPortIndexInput && (mFlags & kFlagIsSecure))
|| (portIndex == kPortIndexOutput && usingMetadataOnEncoderOutput())) {
@@ -5465,7 +5464,7 @@
mDeathNotifier.clear();
}
- Vector<OMXCodec::CodecNameAndQuirks> matchingCodecs;
+ Vector<AString> matchingCodecs;
AString mime;
@@ -5473,13 +5472,9 @@
uint32_t quirks = 0;
int32_t encoder = false;
if (msg->findString("componentName", &componentName)) {
- ssize_t index = matchingCodecs.add();
- OMXCodec::CodecNameAndQuirks *entry = &matchingCodecs.editItemAt(index);
- entry->mName = String8(componentName.c_str());
-
- if (!OMXCodec::findCodecQuirks(
- componentName.c_str(), &entry->mQuirks)) {
- entry->mQuirks = 0;
+ sp<IMediaCodecList> list = MediaCodecList::getInstance();
+ if (list != NULL && list->findCodecByName(componentName.c_str()) >= 0) {
+ matchingCodecs.add(componentName);
}
} else {
CHECK(msg->findString("mime", &mime));
@@ -5488,11 +5483,10 @@
encoder = false;
}
- OMXCodec::findMatchingCodecs(
+ MediaCodecList::findMatchingCodecs(
mime.c_str(),
encoder, // createEncoder
- NULL, // matchComponentName
- 0, // flags
+ 0, // flags
&matchingCodecs);
}
@@ -5502,8 +5496,8 @@
status_t err = NAME_NOT_FOUND;
for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
++matchIndex) {
- componentName = matchingCodecs.itemAt(matchIndex).mName.string();
- quirks = matchingCodecs.itemAt(matchIndex).mQuirks;
+ componentName = matchingCodecs[matchIndex];
+ quirks = MediaCodecList::getQuirksFor(componentName.c_str());
pid_t tid = gettid();
int prevPriority = androidGetThreadPriority(tid);
@@ -6863,4 +6857,68 @@
}
}
+// These are supposed be equivalent to the logic in
+// "audio_channel_out_mask_from_count".
+//static
+status_t ACodec::getOMXChannelMapping(size_t numChannels, OMX_AUDIO_CHANNELTYPE map[]) {
+ switch (numChannels) {
+ case 1:
+ map[0] = OMX_AUDIO_ChannelCF;
+ break;
+ case 2:
+ map[0] = OMX_AUDIO_ChannelLF;
+ map[1] = OMX_AUDIO_ChannelRF;
+ break;
+ case 3:
+ map[0] = OMX_AUDIO_ChannelLF;
+ map[1] = OMX_AUDIO_ChannelRF;
+ map[2] = OMX_AUDIO_ChannelCF;
+ break;
+ case 4:
+ map[0] = OMX_AUDIO_ChannelLF;
+ map[1] = OMX_AUDIO_ChannelRF;
+ map[2] = OMX_AUDIO_ChannelLR;
+ map[3] = OMX_AUDIO_ChannelRR;
+ break;
+ case 5:
+ map[0] = OMX_AUDIO_ChannelLF;
+ map[1] = OMX_AUDIO_ChannelRF;
+ map[2] = OMX_AUDIO_ChannelCF;
+ map[3] = OMX_AUDIO_ChannelLR;
+ map[4] = OMX_AUDIO_ChannelRR;
+ break;
+ case 6:
+ map[0] = OMX_AUDIO_ChannelLF;
+ map[1] = OMX_AUDIO_ChannelRF;
+ map[2] = OMX_AUDIO_ChannelCF;
+ map[3] = OMX_AUDIO_ChannelLFE;
+ map[4] = OMX_AUDIO_ChannelLR;
+ map[5] = OMX_AUDIO_ChannelRR;
+ break;
+ case 7:
+ map[0] = OMX_AUDIO_ChannelLF;
+ map[1] = OMX_AUDIO_ChannelRF;
+ map[2] = OMX_AUDIO_ChannelCF;
+ map[3] = OMX_AUDIO_ChannelLFE;
+ map[4] = OMX_AUDIO_ChannelLR;
+ map[5] = OMX_AUDIO_ChannelRR;
+ map[6] = OMX_AUDIO_ChannelCS;
+ break;
+ case 8:
+ map[0] = OMX_AUDIO_ChannelLF;
+ map[1] = OMX_AUDIO_ChannelRF;
+ map[2] = OMX_AUDIO_ChannelCF;
+ map[3] = OMX_AUDIO_ChannelLFE;
+ map[4] = OMX_AUDIO_ChannelLR;
+ map[5] = OMX_AUDIO_ChannelRR;
+ map[6] = OMX_AUDIO_ChannelLS;
+ map[7] = OMX_AUDIO_ChannelRS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return OK;
+}
+
} // namespace android
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 67a0946..4de1040 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -13,7 +13,6 @@
CallbackDataSource.cpp \
CameraSource.cpp \
CameraSourceTimeLapse.cpp \
- ClockEstimator.cpp \
CodecBase.cpp \
DataSource.cpp \
DataURISource.cpp \
@@ -52,13 +51,13 @@
ProcessInfo.cpp \
SampleIterator.cpp \
SampleTable.cpp \
+ SimpleDecodingSource.cpp \
SkipCutBuffer.cpp \
StagefrightMediaScanner.cpp \
StagefrightMetadataRetriever.cpp \
SurfaceMediaSource.cpp \
SurfaceUtils.cpp \
ThrottledSource.cpp \
- TimeSource.cpp \
Utils.cpp \
VBRISeeker.cpp \
VideoFrameScheduler.cpp \
diff --git a/media/libstagefright/ClockEstimator.cpp b/media/libstagefright/ClockEstimator.cpp
deleted file mode 100644
index 34d1e42..0000000
--- a/media/libstagefright/ClockEstimator.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
-**
-** Copyright 2014, 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 "ClockEstimator"
-#include <utils/Log.h>
-
-#include <math.h>
-#include <media/stagefright/ClockEstimator.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-
-namespace android {
-
-WindowedLinearFitEstimator::WindowedLinearFitEstimator(
- size_t headLength, double headFactor, size_t mainLength, double tailFactor)
- : mHeadFactorInv(1. / headFactor),
- mTailFactor(tailFactor),
- mHistoryLength(mainLength + headLength),
- mHeadLength(headLength) {
- reset();
- mXHistory.resize(mHistoryLength);
- mYHistory.resize(mHistoryLength);
- mFirstWeight = pow(headFactor, mHeadLength);
-}
-
-WindowedLinearFitEstimator::LinearFit::LinearFit() {
- reset();
-}
-
-void WindowedLinearFitEstimator::LinearFit::reset() {
- mX = mXX = mY = mYY = mXY = mW = 0.;
-}
-
-double WindowedLinearFitEstimator::LinearFit::size() const {
- double s = mW * mW + mX * mX + mY * mY + mXX * mXX + mXY * mXY + mYY * mYY;
- if (s > 1e72) {
- // 1e72 corresponds to clock monotonic time of about 8 years
- ALOGW("estimator is overflowing: w=%g x=%g y=%g xx=%g xy=%g yy=%g",
- mW, mX, mY, mXX, mXY, mYY);
- }
- return s;
-}
-
-void WindowedLinearFitEstimator::LinearFit::add(double x, double y, double w) {
- mW += w;
- mX += w * x;
- mY += w * y;
- mXX += w * x * x;
- mXY += w * x * y;
- mYY += w * y * y;
-}
-
-void WindowedLinearFitEstimator::LinearFit::combine(const LinearFit &lf) {
- mW += lf.mW;
- mX += lf.mX;
- mY += lf.mY;
- mXX += lf.mXX;
- mXY += lf.mXY;
- mYY += lf.mYY;
-}
-
-void WindowedLinearFitEstimator::LinearFit::scale(double w) {
- mW *= w;
- mX *= w;
- mY *= w;
- mXX *= w;
- mXY *= w;
- mYY *= w;
-}
-
-double WindowedLinearFitEstimator::LinearFit::interpolate(double x) {
- double div = mW * mXX - mX * mX;
- if (fabs(div) < 1e-5 * mW * mW) {
- // this only should happen on the first value
- return x;
- // assuming a = 1, we could also return x + (mY - mX) / mW;
- }
- double a_div = (mW * mXY - mX * mY);
- double b_div = (mXX * mY - mX * mXY);
- ALOGV("a=%.4g b=%.4g in=%g out=%g",
- a_div / div, b_div / div, x, (a_div * x + b_div) / div);
- return (a_div * x + b_div) / div;
-}
-
-double WindowedLinearFitEstimator::estimate(double x, double y) {
- /*
- * TODO: We could update the head by adding the new sample to it
- * and amplifying it, but this approach can lead to unbounded
- * error. Instead, we recalculate the head at each step, which
- * is computationally more expensive. We could balance the two
- * methods by recalculating just before the error becomes
- * significant.
- */
- const bool update_head = false;
- if (update_head) {
- // add new sample to the head
- mHead.scale(mHeadFactorInv); // amplify head
- mHead.add(x, y, mFirstWeight);
- }
-
- /*
- * TRICKY: place elements into the circular buffer at decreasing
- * indices, so that we can access past elements by addition
- * (thereby avoiding potentially negative indices.)
- */
- if (mNumSamples >= mHeadLength) {
- // move last head sample from head to the main window
- size_t lastHeadIx = (mSampleIx + mHeadLength) % mHistoryLength;
- if (update_head) {
- mHead.add(mXHistory[lastHeadIx], mYHistory[lastHeadIx], -1.); // remove
- }
- mMain.add(mXHistory[lastHeadIx], mYHistory[lastHeadIx], 1.);
- if (mNumSamples >= mHistoryLength) {
- // move last main sample from main window to tail
- mMain.add(mXHistory[mSampleIx], mYHistory[mSampleIx], -1.); // remove
- mTail.add(mXHistory[mSampleIx], mYHistory[mSampleIx], 1.);
- mTail.scale(mTailFactor); // attenuate tail
- }
- }
-
- mXHistory.editItemAt(mSampleIx) = x;
- mYHistory.editItemAt(mSampleIx) = y;
- if (mNumSamples < mHistoryLength) {
- ++mNumSamples;
- }
-
- // recalculate head unless we were using the update method
- if (!update_head) {
- mHead.reset();
- double w = mFirstWeight;
- for (size_t headIx = 0; headIx < mHeadLength && headIx < mNumSamples; ++headIx) {
- size_t ix = (mSampleIx + headIx) % mHistoryLength;
- mHead.add(mXHistory[ix], mYHistory[ix], w);
- w *= mHeadFactorInv;
- }
- }
-
- if (mSampleIx > 0) {
- --mSampleIx;
- } else {
- mSampleIx = mHistoryLength - 1;
- }
-
- // return estimation result
- LinearFit total;
- total.combine(mHead);
- total.combine(mMain);
- total.combine(mTail);
- return total.interpolate(x);
-}
-
-void WindowedLinearFitEstimator::reset() {
- mHead.reset();
- mMain.reset();
- mTail.reset();
- mNumSamples = 0;
- mSampleIx = mHistoryLength - 1;
-}
-
-}; // namespace android
-
-
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index c2ffdf2..ef6d363 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -44,7 +44,6 @@
#include <media/stagefright/MediaFilter.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
#include <media/stagefright/PersistentSurface.h>
#include <media/stagefright/SurfaceUtils.h>
#include <mediautils/BatteryNotifier.h>
@@ -171,7 +170,7 @@
// static
sp<MediaCodec> MediaCodec::CreateByType(
- const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err, pid_t pid) {
+ const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid) {
sp<MediaCodec> codec = new MediaCodec(looper, pid);
const status_t ret = codec->init(mime, true /* nameIsType */, encoder);
@@ -183,7 +182,7 @@
// static
sp<MediaCodec> MediaCodec::CreateByComponentName(
- const sp<ALooper> &looper, const char *name, status_t *err, pid_t pid) {
+ const sp<ALooper> &looper, const AString &name, status_t *err, pid_t pid) {
sp<MediaCodec> codec = new MediaCodec(looper, pid);
const status_t ret = codec->init(name, false /* nameIsType */, false /* encoder */);
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index c657195..c049097 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -30,6 +30,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/OMXClient.h>
@@ -1115,4 +1116,85 @@
return mGlobalSettings;
}
+//static
+bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
+ return componentName.startsWithIgnoreCase("OMX.google.")
+ || !componentName.startsWithIgnoreCase("OMX.");
+}
+
+static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) {
+ // sort order 1: software codecs are first (lower)
+ bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1);
+ bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2);
+ if (isSoftwareCodec1 != isSoftwareCodec2) {
+ return isSoftwareCodec2 - isSoftwareCodec1;
+ }
+
+ // sort order 2: OMX codecs are first (lower)
+ bool isOMX1 = name1->startsWithIgnoreCase("OMX.");
+ bool isOMX2 = name2->startsWithIgnoreCase("OMX.");
+ return isOMX2 - isOMX1;
+}
+
+//static
+void MediaCodecList::findMatchingCodecs(
+ const char *mime, bool encoder, uint32_t flags, Vector<AString> *matches) {
+ matches->clear();
+
+ const sp<IMediaCodecList> list = getInstance();
+ if (list == NULL) {
+ return;
+ }
+
+ size_t index = 0;
+ for (;;) {
+ ssize_t matchIndex =
+ list->findCodecByType(mime, encoder, index);
+
+ if (matchIndex < 0) {
+ break;
+ }
+
+ index = matchIndex + 1;
+
+ const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
+ CHECK(info != NULL);
+ AString componentName = info->getCodecName();
+
+ if (!((flags & kHardwareCodecsOnly) && !isSoftwareCodec(componentName))) {
+ matches->push(componentName);
+ ALOGV("matching '%s'", componentName.c_str());
+ }
+ }
+
+ if (flags & kPreferSoftwareCodecs) {
+ matches->sort(compareSoftwareCodecsFirst);
+ }
+}
+
+// static
+uint32_t MediaCodecList::getQuirksFor(const char *componentName) {
+ const sp<IMediaCodecList> list = getInstance();
+ if (list == NULL) {
+ return 0;
+ }
+
+ ssize_t ix = list->findCodecByName(componentName);
+ if (ix < 0) {
+ return 0;
+ }
+
+ const sp<MediaCodecInfo> info = list->getCodecInfo(ix);
+
+ uint32_t quirks = 0;
+ if (info->hasQuirk("requires-allocate-on-input-ports")) {
+ quirks |= ACodec::kRequiresAllocateBufferOnInputPorts;
+ }
+ if (info->hasQuirk("requires-allocate-on-output-ports")) {
+ quirks |= ACodec::kRequiresAllocateBufferOnOutputPorts;
+ }
+
+ return quirks;
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index 7f9f824..d526bc0 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -30,6 +30,7 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaCodecSource.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
@@ -42,15 +43,19 @@
const int kDefaultSwVideoEncoderFormat = HAL_PIXEL_FORMAT_YCbCr_420_888;
const int kDefaultSwVideoEncoderDataSpace = HAL_DATASPACE_BT709;
+const int kStopTimeoutUs = 300000; // allow 1 sec for shutting down encoder
+
struct MediaCodecSource::Puller : public AHandler {
Puller(const sp<MediaSource> &source);
status_t start(const sp<MetaData> &meta, const sp<AMessage> ¬ify);
void stop();
-
+ void stopSource();
void pause();
void resume();
+ bool readBuffer(MediaBuffer **buffer);
+
protected:
virtual void onMessageReceived(const sp<AMessage> &msg);
virtual ~Puller();
@@ -60,17 +65,31 @@
kWhatStart = 'msta',
kWhatStop,
kWhatPull,
- kWhatPause,
- kWhatResume,
};
sp<MediaSource> mSource;
sp<AMessage> mNotify;
sp<ALooper> mLooper;
- int32_t mPullGeneration;
bool mIsAudio;
- bool mPaused;
- bool mReachedEOS;
+
+ struct Queue {
+ Queue()
+ : mReadPendingSince(0),
+ mPaused(false),
+ mPulling(false) { }
+ int64_t mReadPendingSince;
+ bool mPaused;
+ bool mPulling;
+ Vector<MediaBuffer *> mReadBuffers;
+
+ void flush();
+ // if queue is empty, return false and set *|buffer| to NULL . Otherwise, pop
+ // buffer from front of the queue, place it into *|buffer| and return true.
+ bool readBuffer(MediaBuffer **buffer);
+ // add a buffer to the back of the queue
+ void pushBuffer(MediaBuffer *mbuf);
+ };
+ Mutexed<Queue> mQueue;
status_t postSynchronouslyAndReturnError(const sp<AMessage> &msg);
void schedulePull();
@@ -82,10 +101,8 @@
MediaCodecSource::Puller::Puller(const sp<MediaSource> &source)
: mSource(source),
mLooper(new ALooper()),
- mPullGeneration(0),
- mIsAudio(false),
- mPaused(false),
- mReachedEOS(false) {
+ mIsAudio(false)
+{
sp<MetaData> meta = source->getFormat();
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
@@ -100,6 +117,33 @@
mLooper->stop();
}
+void MediaCodecSource::Puller::Queue::pushBuffer(MediaBuffer *mbuf) {
+ mReadBuffers.push_back(mbuf);
+}
+
+bool MediaCodecSource::Puller::Queue::readBuffer(MediaBuffer **mbuf) {
+ if (mReadBuffers.empty()) {
+ *mbuf = NULL;
+ return false;
+ }
+ *mbuf = *mReadBuffers.begin();
+ mReadBuffers.erase(mReadBuffers.begin());
+ return true;
+}
+
+void MediaCodecSource::Puller::Queue::flush() {
+ MediaBuffer *mbuf;
+ while (readBuffer(&mbuf)) {
+ // there are no null buffers in the queue
+ mbuf->release();
+ }
+}
+
+bool MediaCodecSource::Puller::readBuffer(MediaBuffer **mbuf) {
+ Mutexed<Queue>::Locked queue(mQueue);
+ return queue->readBuffer(mbuf);
+}
+
status_t MediaCodecSource::Puller::postSynchronouslyAndReturnError(
const sp<AMessage> &msg) {
sp<AMessage> response;
@@ -116,8 +160,7 @@
return err;
}
-status_t MediaCodecSource::Puller::start(const sp<MetaData> &meta,
- const sp<AMessage> ¬ify) {
+status_t MediaCodecSource::Puller::start(const sp<MetaData> &meta, const sp<AMessage> ¬ify) {
ALOGV("puller (%s) start", mIsAudio ? "audio" : "video");
mLooper->start(
false /* runOnCallingThread */,
@@ -132,41 +175,46 @@
}
void MediaCodecSource::Puller::stop() {
- // Stop source from caller's thread instead of puller's looper.
- // mSource->stop() is thread-safe, doing it outside the puller's
- // looper allows us to at least stop if source gets stuck.
- // If source gets stuck in read(), the looper would never
- // be able to process the stop(), which could lead to ANR.
+ bool interrupt = false;
+ {
+ // mark stopping before actually reaching kWhatStop on the looper, so the pulling will
+ // stop.
+ Mutexed<Queue>::Locked queue(mQueue);
+ queue->mPulling = false;
+ interrupt = queue->mReadPendingSince && (queue->mReadPendingSince < ALooper::GetNowUs() - 1000000);
+ queue->flush(); // flush any unprocessed pulled buffers
+ }
- ALOGV("source (%s) stopping", mIsAudio ? "audio" : "video");
- mSource->stop();
- ALOGV("source (%s) stopped", mIsAudio ? "audio" : "video");
+ if (interrupt) {
+ // call source->stop if read has been pending for over a second
+ // TODO: we should really call this if kWhatStop has not returned for more than a second.
+ mSource->stop();
+ }
+}
+void MediaCodecSource::Puller::stopSource() {
(new AMessage(kWhatStop, this))->post();
}
void MediaCodecSource::Puller::pause() {
- (new AMessage(kWhatPause, this))->post();
+ Mutexed<Queue>::Locked queue(mQueue);
+ queue->mPaused = true;
}
void MediaCodecSource::Puller::resume() {
- (new AMessage(kWhatResume, this))->post();
+ Mutexed<Queue>::Locked queue(mQueue);
+ queue->mPaused = false;
}
void MediaCodecSource::Puller::schedulePull() {
- sp<AMessage> msg = new AMessage(kWhatPull, this);
- msg->setInt32("generation", mPullGeneration);
- msg->post();
+ (new AMessage(kWhatPull, this))->post();
}
void MediaCodecSource::Puller::handleEOS() {
- if (!mReachedEOS) {
- ALOGV("puller (%s) posting EOS", mIsAudio ? "audio" : "video");
- mReachedEOS = true;
- sp<AMessage> notify = mNotify->dup();
- notify->setPointer("accessUnit", NULL);
- notify->post();
- }
+ ALOGV("puller (%s) posting EOS", mIsAudio ? "audio" : "video");
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("eos", 1);
+ msg->post();
}
void MediaCodecSource::Puller::onMessageReceived(const sp<AMessage> &msg) {
@@ -176,7 +224,10 @@
sp<RefBase> obj;
CHECK(msg->findObject("meta", &obj));
- mReachedEOS = false;
+ {
+ Mutexed<Queue>::Locked queue(mQueue);
+ queue->mPulling = true;
+ }
status_t err = mSource->start(static_cast<MetaData *>(obj.get()));
@@ -195,61 +246,52 @@
case kWhatStop:
{
- ++mPullGeneration;
-
- handleEOS();
+ mSource->stop();
break;
}
case kWhatPull:
{
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
-
- if (generation != mPullGeneration) {
+ Mutexed<Queue>::Locked queue(mQueue);
+ queue->mReadPendingSince = ALooper::GetNowUs();
+ if (!queue->mPulling) {
+ handleEOS();
break;
}
- MediaBuffer *mbuf;
+ queue.unlock();
+ MediaBuffer *mbuf = NULL;
status_t err = mSource->read(&mbuf);
+ queue.lock();
- if (mPaused) {
- if (err == OK) {
+ queue->mReadPendingSince = 0;
+ // if we need to discard buffer
+ if (!queue->mPulling || queue->mPaused || err != OK) {
+ if (mbuf != NULL) {
mbuf->release();
mbuf = NULL;
}
-
- msg->post();
- break;
- }
-
- if (err != OK) {
- if (err == ERROR_END_OF_STREAM) {
+ if (queue->mPulling && err == OK) {
+ msg->post(); // if simply paused, keep pulling source
+ } else if (err == ERROR_END_OF_STREAM) {
ALOGV("stream ended, mbuf %p", mbuf);
- } else {
+ } else if (err != OK) {
ALOGE("error %d reading stream.", err);
}
- handleEOS();
- } else {
- sp<AMessage> notify = mNotify->dup();
-
- notify->setPointer("accessUnit", mbuf);
- notify->post();
-
- msg->post();
}
- break;
- }
- case kWhatPause:
- {
- mPaused = true;
- break;
- }
+ if (mbuf != NULL) {
+ queue->pushBuffer(mbuf);
+ }
- case kWhatResume:
- {
- mPaused = false;
+ queue.unlock();
+
+ if (mbuf != NULL) {
+ mNotify->post();
+ msg->post();
+ } else {
+ handleEOS();
+ }
break;
}
@@ -258,6 +300,11 @@
}
}
+MediaCodecSource::Output::Output()
+ : mEncoderReachedEOS(false),
+ mErrorCode(OK) {
+}
+
// static
sp<MediaCodecSource> MediaCodecSource::Create(
const sp<ALooper> &looper,
@@ -282,21 +329,7 @@
status_t MediaCodecSource::stop() {
sp<AMessage> msg = new AMessage(kWhatStop, mReflector);
- status_t err = postSynchronouslyAndReturnError(msg);
-
- // mPuller->stop() needs to be done outside MediaCodecSource's looper,
- // as it contains a synchronous call to stop the underlying MediaSource,
- // which often waits for all outstanding MediaBuffers to return, but
- // MediaBuffers are only returned when MediaCodecSource looper gets
- // to process them.
-
- if (mPuller != NULL) {
- ALOGI("puller (%s) stopping", mIsVideo ? "video" : "audio");
- mPuller->stop();
- ALOGI("puller (%s) stopped", mIsVideo ? "video" : "audio");
- }
-
- return err;
+ return postSynchronouslyAndReturnError(msg);
}
status_t MediaCodecSource::pause() {
@@ -311,18 +344,18 @@
status_t MediaCodecSource::read(
MediaBuffer** buffer, const ReadOptions* /* options */) {
- Mutex::Autolock autolock(mOutputBufferLock);
+ Mutexed<Output>::Locked output(mOutput);
*buffer = NULL;
- while (mOutputBufferQueue.size() == 0 && !mEncoderReachedEOS) {
- mOutputBufferCond.wait(mOutputBufferLock);
+ while (output->mBufferQueue.size() == 0 && !output->mEncoderReachedEOS) {
+ output.waitForCondition(output->mCond);
}
- if (!mEncoderReachedEOS) {
- *buffer = *mOutputBufferQueue.begin();
- mOutputBufferQueue.erase(mOutputBufferQueue.begin());
+ if (!output->mEncoderReachedEOS) {
+ *buffer = *output->mBufferQueue.begin();
+ output->mBufferQueue.erase(output->mBufferQueue.begin());
return OK;
}
- return mErrorCode;
+ return output->mErrorCode;
}
void MediaCodecSource::signalBufferReturned(MediaBuffer *buffer) {
@@ -349,8 +382,7 @@
mEncoderDataSpace(0),
mGraphicBufferConsumer(consumer),
mFirstSampleTimeUs(-1ll),
- mEncoderReachedEOS(false),
- mErrorCode(OK) {
+ mGeneration(0) {
CHECK(mLooper != NULL);
AString mime;
@@ -401,24 +433,39 @@
AString outputMIME;
CHECK(mOutputFormat->findString("mime", &outputMIME));
- mEncoder = MediaCodec::CreateByType(
- mCodecLooper, outputMIME.c_str(), true /* encoder */);
+ Vector<AString> matchingCodecs;
+ MediaCodecList::findMatchingCodecs(
+ outputMIME.c_str(), true /* encoder */,
+ ((mFlags & FLAG_PREFER_SOFTWARE_CODEC) ? MediaCodecList::kPreferSoftwareCodecs : 0),
+ &matchingCodecs);
- if (mEncoder == NULL) {
- return NO_INIT;
+ status_t err = NO_INIT;
+ for (size_t ix = 0; ix < matchingCodecs.size(); ++ix) {
+ mEncoder = MediaCodec::CreateByComponentName(
+ mCodecLooper, matchingCodecs[ix]);
+
+ if (mEncoder == NULL) {
+ continue;
+ }
+
+ ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
+
+ mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
+ mEncoder->setCallback(mEncoderActivityNotify);
+
+ err = mEncoder->configure(
+ mOutputFormat,
+ NULL /* nativeWindow */,
+ NULL /* crypto */,
+ MediaCodec::CONFIGURE_FLAG_ENCODE);
+
+ if (err == OK) {
+ break;
+ }
+ mEncoder->release();
+ mEncoder = NULL;
}
- ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
-
- mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
- mEncoder->setCallback(mEncoderActivityNotify);
-
- status_t err = mEncoder->configure(
- mOutputFormat,
- NULL /* nativeWindow */,
- NULL /* crypto */,
- MediaCodec::CONFIGURE_FLAG_ENCODE);
-
if (err != OK) {
return err;
}
@@ -462,8 +509,11 @@
return err;
}
- mEncoderReachedEOS = false;
- mErrorCode = OK;
+ {
+ Mutexed<Output>::Locked output(mOutput);
+ output->mEncoderReachedEOS = false;
+ output->mErrorCode = OK;
+ }
return OK;
}
@@ -475,14 +525,6 @@
mEncoder->release();
mEncoder.clear();
-
- while (!mInputBufferQueue.empty()) {
- MediaBuffer *mbuf = *mInputBufferQueue.begin();
- mInputBufferQueue.erase(mInputBufferQueue.begin());
- if (mbuf != NULL) {
- mbuf->release();
- }
- }
}
status_t MediaCodecSource::postSynchronouslyAndReturnError(
@@ -502,25 +544,32 @@
}
void MediaCodecSource::signalEOS(status_t err) {
- if (!mEncoderReachedEOS) {
- ALOGV("encoder (%s) reached EOS", mIsVideo ? "video" : "audio");
- {
- Mutex::Autolock autoLock(mOutputBufferLock);
+ bool reachedEOS = false;
+ {
+ Mutexed<Output>::Locked output(mOutput);
+ reachedEOS = output->mEncoderReachedEOS;
+ if (!reachedEOS) {
+ ALOGV("encoder (%s) reached EOS", mIsVideo ? "video" : "audio");
// release all unread media buffers
- for (List<MediaBuffer*>::iterator it = mOutputBufferQueue.begin();
- it != mOutputBufferQueue.end(); it++) {
+ for (List<MediaBuffer*>::iterator it = output->mBufferQueue.begin();
+ it != output->mBufferQueue.end(); it++) {
(*it)->release();
}
- mOutputBufferQueue.clear();
- mEncoderReachedEOS = true;
- mErrorCode = err;
- mOutputBufferCond.signal();
- }
+ output->mBufferQueue.clear();
+ output->mEncoderReachedEOS = true;
+ output->mErrorCode = err;
+ output->mCond.signal();
- releaseEncoder();
+ reachedEOS = true;
+ output.unlock();
+ releaseEncoder();
+ }
}
- if (mStopping && mEncoderReachedEOS) {
+
+ if (mStopping && reachedEOS) {
ALOGI("encoder (%s) stopped", mIsVideo ? "video" : "audio");
+ mPuller->stopSource();
+ ALOGV("source (%s) stopped", mIsVideo ? "video" : "audio");
// posting reply to everyone that's waiting
List<sp<AReplyToken>>::iterator it;
for (it = mStopReplyIDQueue.begin();
@@ -529,6 +578,7 @@
}
mStopReplyIDQueue.clear();
mStopping = false;
+ ++mGeneration;
}
}
@@ -554,11 +604,8 @@
}
status_t MediaCodecSource::feedEncoderInputBuffers() {
- while (!mInputBufferQueue.empty()
- && !mAvailEncoderInputIndices.empty()) {
- MediaBuffer* mbuf = *mInputBufferQueue.begin();
- mInputBufferQueue.erase(mInputBufferQueue.begin());
-
+ MediaBuffer* mbuf = NULL;
+ while (!mAvailEncoderInputIndices.empty() && mPuller->readBuffer(&mbuf)) {
size_t bufferIndex = *mAvailEncoderInputIndices.begin();
mAvailEncoderInputIndices.erase(mAvailEncoderInputIndices.begin());
@@ -676,30 +723,19 @@
switch (msg->what()) {
case kWhatPullerNotify:
{
- MediaBuffer *mbuf;
- CHECK(msg->findPointer("accessUnit", (void**)&mbuf));
-
- if (mbuf == NULL) {
- ALOGV("puller (%s) reached EOS",
- mIsVideo ? "video" : "audio");
+ int32_t eos = 0;
+ if (msg->findInt32("eos", &eos) && eos) {
+ ALOGV("puller (%s) reached EOS", mIsVideo ? "video" : "audio");
signalEOS();
- }
-
- if (mEncoder == NULL) {
- ALOGV("got msg '%s' after encoder shutdown.",
- msg->debugString().c_str());
-
- if (mbuf != NULL) {
- mbuf->release();
- }
-
break;
}
- mInputBufferQueue.push_back(mbuf);
+ if (mEncoder == NULL) {
+ ALOGV("got msg '%s' after encoder shutdown.", msg->debugString().c_str());
+ break;
+ }
feedEncoderInputBuffers();
-
break;
}
case kWhatEncoderActivity:
@@ -788,9 +824,9 @@
mbuf->add_ref();
{
- Mutex::Autolock autoLock(mOutputBufferLock);
- mOutputBufferQueue.push_back(mbuf);
- mOutputBufferCond.signal();
+ Mutexed<Output>::Locked output(mOutput);
+ output->mBufferQueue.push_back(mbuf);
+ output->mCond.signal();
}
mEncoder->releaseOutputBuffer(index);
@@ -824,7 +860,7 @@
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
- if (mEncoderReachedEOS) {
+ if (mOutput.lock()->mEncoderReachedEOS) {
// if we already reached EOS, reply and return now
ALOGI("encoder (%s) already stopped",
mIsVideo ? "video" : "audio");
@@ -842,17 +878,41 @@
mStopping = true;
// if using surface, signal source EOS and wait for EOS to come back.
- // otherwise, release encoder and post EOS if haven't done already
+ // otherwise, stop puller (which also clears the input buffer queue)
+ // and wait for the EOS message. We cannot call source->stop() because
+ // the encoder may still be processing input buffers.
if (mFlags & FLAG_USE_SURFACE_INPUT) {
mEncoder->signalEndOfInputStream();
} else {
- signalEOS();
+ mPuller->stop();
}
+
+ // complete stop even if encoder/puller stalled
+ sp<AMessage> timeoutMsg = new AMessage(kWhatStopStalled, mReflector);
+ timeoutMsg->setInt32("generation", mGeneration);
+ timeoutMsg->post(kStopTimeoutUs);
break;
}
+
+ case kWhatStopStalled:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation != mGeneration) {
+ break;
+ }
+
+ if (!(mFlags & FLAG_USE_SURFACE_INPUT)) {
+ ALOGV("source (%s) stopping", mIsVideo ? "video" : "audio");
+ mPuller->stopSource();
+ ALOGV("source (%s) stopped", mIsVideo ? "video" : "audio");
+ }
+ signalEOS();
+ }
+
case kWhatPause:
{
- if (mFlags && FLAG_USE_SURFACE_INPUT) {
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
suspend();
} else {
CHECK(mPuller != NULL);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4c39194..164026a 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -269,26 +269,6 @@
}
// static
-bool OMXCodec::findCodecQuirks(const char *componentName, uint32_t *quirks) {
- const sp<IMediaCodecList> list = MediaCodecList::getInstance();
- if (list == NULL) {
- return false;
- }
-
- ssize_t index = list->findCodecByName(componentName);
-
- if (index < 0) {
- return false;
- }
-
- const sp<MediaCodecInfo> info = list->getCodecInfo(index);
- CHECK(info != NULL);
- *quirks = getComponentQuirks(info);
-
- return true;
-}
-
-// static
sp<MediaSource> OMXCodec::Create(
const sp<IOMX> &omx,
const sp<MetaData> &meta, bool createEncoder,
@@ -3222,7 +3202,7 @@
pcmParams.nSamplingRate = sampleRate;
pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear;
- CHECK_EQ(getOMXChannelMapping(
+ CHECK_EQ(ACodec::getOMXChannelMapping(
numChannels, pcmParams.eChannelMapping), (status_t)OK);
err = mOMX->setParameter(
@@ -4391,67 +4371,4 @@
return QueryCodecs(omx, mimeType, queryDecoders, false /*hwCodecOnly*/, results);
}
-// These are supposed be equivalent to the logic in
-// "audio_channel_out_mask_from_count".
-status_t getOMXChannelMapping(size_t numChannels, OMX_AUDIO_CHANNELTYPE map[]) {
- switch (numChannels) {
- case 1:
- map[0] = OMX_AUDIO_ChannelCF;
- break;
- case 2:
- map[0] = OMX_AUDIO_ChannelLF;
- map[1] = OMX_AUDIO_ChannelRF;
- break;
- case 3:
- map[0] = OMX_AUDIO_ChannelLF;
- map[1] = OMX_AUDIO_ChannelRF;
- map[2] = OMX_AUDIO_ChannelCF;
- break;
- case 4:
- map[0] = OMX_AUDIO_ChannelLF;
- map[1] = OMX_AUDIO_ChannelRF;
- map[2] = OMX_AUDIO_ChannelLR;
- map[3] = OMX_AUDIO_ChannelRR;
- break;
- case 5:
- map[0] = OMX_AUDIO_ChannelLF;
- map[1] = OMX_AUDIO_ChannelRF;
- map[2] = OMX_AUDIO_ChannelCF;
- map[3] = OMX_AUDIO_ChannelLR;
- map[4] = OMX_AUDIO_ChannelRR;
- break;
- case 6:
- map[0] = OMX_AUDIO_ChannelLF;
- map[1] = OMX_AUDIO_ChannelRF;
- map[2] = OMX_AUDIO_ChannelCF;
- map[3] = OMX_AUDIO_ChannelLFE;
- map[4] = OMX_AUDIO_ChannelLR;
- map[5] = OMX_AUDIO_ChannelRR;
- break;
- case 7:
- map[0] = OMX_AUDIO_ChannelLF;
- map[1] = OMX_AUDIO_ChannelRF;
- map[2] = OMX_AUDIO_ChannelCF;
- map[3] = OMX_AUDIO_ChannelLFE;
- map[4] = OMX_AUDIO_ChannelLR;
- map[5] = OMX_AUDIO_ChannelRR;
- map[6] = OMX_AUDIO_ChannelCS;
- break;
- case 8:
- map[0] = OMX_AUDIO_ChannelLF;
- map[1] = OMX_AUDIO_ChannelRF;
- map[2] = OMX_AUDIO_ChannelCF;
- map[3] = OMX_AUDIO_ChannelLFE;
- map[4] = OMX_AUDIO_ChannelLR;
- map[5] = OMX_AUDIO_ChannelRR;
- map[6] = OMX_AUDIO_ChannelLS;
- map[7] = OMX_AUDIO_ChannelRS;
- break;
- default:
- return -EINVAL;
- }
-
- return OK;
-}
-
} // namespace android
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
new file mode 100644
index 0000000..e722ca6
--- /dev/null
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2016, 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.
+ */
+
+#include <gui/Surface.h>
+
+#include <media/ICrypto.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaCodecList.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/SimpleDecodingSource.h>
+#include <media/stagefright/Utils.h>
+
+using namespace android;
+
+const int64_t kTimeoutWaitForOutputUs = 500000; // 0.5 seconds
+
+//static
+sp<SimpleDecodingSource> SimpleDecodingSource::Create(
+ const sp<MediaSource> &source, uint32_t flags, const sp<ANativeWindow> &nativeWindow) {
+ sp<Surface> surface = static_cast<Surface*>(nativeWindow.get());
+ const char *mime = NULL;
+ sp<MetaData> meta = source->getFormat();
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ sp<AMessage> format = new AMessage;
+ convertMetaDataToMessage(source->getFormat(), &format);
+
+ Vector<AString> matchingCodecs;
+ MediaCodecList::findMatchingCodecs(
+ mime, false /* encoder */, flags, &matchingCodecs);
+
+ sp<ALooper> looper = new ALooper;
+ looper->setName("stagefright");
+ looper->start();
+
+ sp<MediaCodec> codec;
+
+ for (size_t i = 0; i < matchingCodecs.size(); ++i) {
+ const AString &componentName = matchingCodecs[i];
+
+ ALOGV("Attempting to allocate codec '%s'", componentName.c_str());
+
+ codec = MediaCodec::CreateByComponentName(looper, componentName);
+ if (codec != NULL) {
+ ALOGI("Successfully allocated codec '%s'", componentName.c_str());
+
+ status_t err = codec->configure(format, surface, NULL /* crypto */, 0 /* flags */);
+ if (err == OK) {
+ err = codec->getOutputFormat(&format);
+ }
+ if (err == OK) {
+ return new SimpleDecodingSource(codec, source, looper, surface != NULL, format);
+ }
+
+ ALOGD("Failed to configure codec '%s'", componentName.c_str());
+ codec->release();
+ codec = NULL;
+ }
+ }
+
+ looper->stop();
+ ALOGE("No matching decoder! (mime: %s)", mime);
+ return NULL;
+}
+
+SimpleDecodingSource::SimpleDecodingSource(
+ const sp<MediaCodec> &codec, const sp<MediaSource> &source, const sp<ALooper> &looper,
+ bool usingSurface, const sp<AMessage> &format)
+ : mCodec(codec),
+ mSource(source),
+ mLooper(looper),
+ mUsingSurface(usingSurface),
+ mProtectedState(format) {
+ mCodec->getName(&mComponentName);
+}
+
+SimpleDecodingSource::~SimpleDecodingSource() {
+ mCodec->release();
+ mLooper->stop();
+}
+
+status_t SimpleDecodingSource::start(MetaData *params) {
+ (void)params;
+ Mutexed<ProtectedState>::Locked me(mProtectedState);
+ if (me->mState != INIT) {
+ return -EINVAL;
+ }
+ status_t res = mCodec->start();
+ if (res == OK) {
+ res = mSource->start();
+ }
+
+ if (res == OK) {
+ me->mState = STARTED;
+ me->mQueuedInputEOS = false;
+ me->mGotOutputEOS = false;
+ } else {
+ me->mState = ERROR;
+ }
+
+ return res;
+}
+
+status_t SimpleDecodingSource::stop() {
+ Mutexed<ProtectedState>::Locked me(mProtectedState);
+ if (me->mState != STARTED) {
+ return -EINVAL;
+ }
+
+ // wait for any pending reads to complete
+ me->mState = STOPPING;
+ while (me->mReading) {
+ me.waitForCondition(me->mReadCondition);
+ }
+
+ status_t res1 = mCodec->stop();
+ if (res1 != OK) {
+ mCodec->release();
+ }
+ status_t res2 = mSource->stop();
+ if (res1 == OK && res2 == OK) {
+ me->mState = STOPPED;
+ } else {
+ me->mState = ERROR;
+ }
+ return res1 != OK ? res1 : res2;
+}
+
+sp<MetaData> SimpleDecodingSource::getFormat() {
+ Mutexed<ProtectedState>::Locked me(mProtectedState);
+ if (me->mState == STARTED || me->mState == INIT) {
+ sp<MetaData> meta = new MetaData();
+ convertMessageToMetaData(me->mFormat, meta);
+ return meta;
+ }
+ return NULL;
+}
+
+SimpleDecodingSource::ProtectedState::ProtectedState(const sp<AMessage> &format)
+ : mReading(false),
+ mFormat(format),
+ mState(INIT),
+ mQueuedInputEOS(false),
+ mGotOutputEOS(false) {
+}
+
+status_t SimpleDecodingSource::read(
+ MediaBuffer **buffer, const ReadOptions *options) {
+ *buffer = NULL;
+
+ Mutexed<ProtectedState>::Locked me(mProtectedState);
+ if (me->mState != STARTED) {
+ return ERROR_END_OF_STREAM;
+ }
+ me->mReading = true;
+
+ status_t res = doRead(me, buffer, options);
+
+ me.lock();
+ me->mReading = false;
+ if (me->mState != STARTED) {
+ me->mReadCondition.signal();
+ }
+
+ return res;
+}
+
+status_t SimpleDecodingSource::doRead(
+ Mutexed<ProtectedState>::Locked &me, MediaBuffer **buffer, const ReadOptions *options) {
+ // |me| is always locked on entry, but is allowed to be unlocked on exit
+ CHECK_EQ(me->mState, STARTED);
+
+ size_t out_ix, in_ix, out_offset, out_size;
+ int64_t out_pts;
+ uint32_t out_flags;
+ status_t res;
+
+ // flush codec on seek
+ MediaSource::ReadOptions::SeekMode mode;
+ if (options != NULL && options->getSeekTo(&out_pts, &mode)) {
+ me->mQueuedInputEOS = false;
+ me->mGotOutputEOS = false;
+ mCodec->flush();
+ }
+
+ if (me->mGotOutputEOS) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ for (int retries = 1; ++retries; ) {
+ // If we fill all available input buffers, we should expect that
+ // the codec produces at least one output buffer. Also, the codec
+ // should produce an output buffer in at most 1 seconds. Retry a
+ // few times nonetheless.
+ while (!me->mQueuedInputEOS) {
+ res = mCodec->dequeueInputBuffer(&in_ix, 0);
+ if (res == -EAGAIN) {
+ // no available input buffers
+ break;
+ }
+
+ sp<ABuffer> in_buffer;
+ if (res == OK) {
+ res = mCodec->getInputBuffer(in_ix, &in_buffer);
+ }
+
+ if (res != OK || in_buffer == NULL) {
+ ALOGW("[%s] could not get input buffer #%zu",
+ mComponentName.c_str(), in_ix);
+ me->mState = ERROR;
+ return UNKNOWN_ERROR;
+ }
+
+ MediaBuffer *in_buf;
+ while (true) {
+ in_buf = NULL;
+ me.unlock();
+ res = mSource->read(&in_buf, options);
+ me.lock();
+ if (res != OK || me->mState != STARTED) {
+ if (in_buf != NULL) {
+ in_buf->release();
+ in_buf = NULL;
+ }
+
+ // queue EOS
+ me->mQueuedInputEOS = true;
+ if (mCodec->queueInputBuffer(
+ in_ix, 0 /* offset */, 0 /* size */,
+ 0 /* pts */, MediaCodec::BUFFER_FLAG_EOS) != OK) {
+ ALOGI("[%s] failed to queue input EOS", mComponentName.c_str());
+ me->mState = ERROR;
+ return UNKNOWN_ERROR;
+ }
+
+ // don't stop on EOS, but report error or EOS on stop
+ if (res != ERROR_END_OF_STREAM) {
+ me->mState = ERROR;
+ return res;
+ }
+ if (me->mState != STARTED) {
+ return ERROR_END_OF_STREAM;
+ }
+ break;
+ }
+ if (in_buf == NULL) { // should not happen
+ continue;
+ } else if (in_buf->range_length() != 0) {
+ break;
+ }
+ in_buf->release();
+ }
+
+ if (in_buf != NULL) {
+ int64_t timestampUs = 0;
+ CHECK(in_buf->meta_data()->findInt64(kKeyTime, ×tampUs));
+ if (in_buf->range_length() > in_buffer->capacity()) {
+ ALOGW("'%s' received %zu input bytes for buffer of size %zu",
+ mComponentName.c_str(),
+ in_buf->range_length(), in_buffer->capacity());
+ }
+ memcpy(in_buffer->base(), (uint8_t *)in_buf->data() + in_buf->range_offset(),
+ min(in_buf->range_length(), in_buffer->capacity()));
+
+ res = mCodec->queueInputBuffer(
+ in_ix, 0 /* offset */, in_buf->range_length(),
+ timestampUs, 0 /* flags */);
+ if (res != OK) {
+ ALOGI("[%s] failed to queue input buffer #%zu", mComponentName.c_str(), in_ix);
+ me->mState = ERROR;
+ }
+ in_buf->release();
+ }
+ }
+
+ me.unlock();
+ res = mCodec->dequeueOutputBuffer(
+ &out_ix, &out_offset, &out_size, &out_pts,
+ &out_flags, kTimeoutWaitForOutputUs /* timeoutUs */);
+ me.lock();
+ // abort read on stop
+ if (me->mState != STARTED) {
+ if (res == OK) {
+ mCodec->releaseOutputBuffer(out_ix);
+ }
+ return ERROR_END_OF_STREAM;
+ }
+
+ if (res == -EAGAIN) {
+ ALOGD("[%s] did not produce an output buffer. retry count: %d",
+ mComponentName.c_str(), retries);
+ continue;
+ } else if (res == INFO_FORMAT_CHANGED) {
+ if (mCodec->getOutputFormat(&me->mFormat) != OK) {
+ me->mState = ERROR;
+ res = UNKNOWN_ERROR;
+ }
+ return res;
+ } else if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
+ ALOGV("output buffers changed");
+ continue;
+ } else if (res != OK) {
+ me->mState = ERROR;
+ return res;
+ }
+
+ sp<ABuffer> out_buffer;
+ res = mCodec->getOutputBuffer(out_ix, &out_buffer);
+ if (res != OK) {
+ ALOGW("[%s] could not get output buffer #%zu",
+ mComponentName.c_str(), out_ix);
+ me->mState = ERROR;
+ return UNKNOWN_ERROR;
+ }
+ if (out_flags & MediaCodec::BUFFER_FLAG_EOS) {
+ me->mGotOutputEOS = true;
+ // return EOS immediately if last buffer is empty
+ if (out_size == 0) {
+ mCodec->releaseOutputBuffer(out_ix);
+ return ERROR_END_OF_STREAM;
+ }
+ }
+
+ if (mUsingSurface && out_size > 0) {
+ *buffer = new MediaBuffer(0);
+ mCodec->renderOutputBufferAndRelease(out_ix);
+ } else {
+ *buffer = new MediaBuffer(out_size);
+ CHECK_LE(out_buffer->size(), (*buffer)->size());
+ memcpy((*buffer)->data(), out_buffer->data(), out_buffer->size());
+ (*buffer)->meta_data()->setInt64(kKeyTime, out_pts);
+ mCodec->releaseOutputBuffer(out_ix);
+ }
+ return OK;
+ }
+
+ return TIMED_OUT;
+}
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index e37e909..08342cf 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -35,11 +35,12 @@
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/OMXCodec.h>
#include <media/stagefright/Utils.h>
#include <CharacterEncodingDetector.h>
@@ -55,13 +56,11 @@
ALOGV("StagefrightMetadataRetriever()");
DataSource::RegisterDefaultSniffers();
- CHECK_EQ(mClient.connect(), (status_t)OK);
}
StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
ALOGV("~StagefrightMetadataRetriever()");
clearMetadata();
- mClient.disconnect();
}
status_t StagefrightMetadataRetriever::setDataSource(
@@ -137,7 +136,7 @@
}
static VideoFrame *extractVideoFrame(
- const char *componentName,
+ const AString &componentName,
const sp<MetaData> &trackMeta,
const sp<MediaSource> &source,
int64_t frameTimeUs,
@@ -161,7 +160,7 @@
looper, componentName, &err);
if (decoder.get() == NULL || err != OK) {
- ALOGW("Failed to instantiate decoder [%s]", componentName);
+ ALOGW("Failed to instantiate decoder [%s]", componentName.c_str());
return NULL;
}
@@ -476,23 +475,22 @@
const char *mime;
CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
- Vector<OMXCodec::CodecNameAndQuirks> matchingCodecs;
- OMXCodec::findMatchingCodecs(
+ Vector<AString> matchingCodecs;
+ MediaCodecList::findMatchingCodecs(
mime,
false, /* encoder */
- NULL, /* matchComponentName */
- OMXCodec::kPreferSoftwareCodecs,
+ MediaCodecList::kPreferSoftwareCodecs,
&matchingCodecs);
for (size_t i = 0; i < matchingCodecs.size(); ++i) {
- const char *componentName = matchingCodecs[i].mName.string();
+ const AString &componentName = matchingCodecs[i];
VideoFrame *frame =
extractVideoFrame(componentName, trackMeta, source, timeUs, option);
if (frame != NULL) {
return frame;
}
- ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName);
+ ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str());
}
return NULL;
diff --git a/media/libstagefright/TimeSource.cpp b/media/libstagefright/TimeSource.cpp
deleted file mode 100644
index 041980f..0000000
--- a/media/libstagefright/TimeSource.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-#include <stddef.h>
-#include <sys/time.h>
-
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/TimeSource.h>
-
-namespace android {
-
-SystemTimeSource::SystemTimeSource()
- : mStartTimeUs(ALooper::GetNowUs()) {
-}
-
-int64_t SystemTimeSource::getRealTimeUs() {
- return ALooper::GetNowUs() - mStartTimeUs;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index fd739d0..61bbf6c 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -20,7 +20,6 @@
#include <media/MediaMetadataRetrieverInterface.h>
-#include <media/stagefright/OMXClient.h>
#include <utils/KeyedVector.h>
namespace android {
@@ -45,7 +44,6 @@
virtual const char *extractMetadata(int keyCode);
private:
- OMXClient mClient;
sp<DataSource> mSource;
sp<MediaExtractor> mExtractor;
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index a6f38bb..4f76d16 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -43,17 +43,18 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- rtp_test.cpp
+LOCAL_SRC_FILES := \
+ rtp_test.cpp \
LOCAL_SHARED_LIBRARIES := \
- libstagefright liblog libutils libbinder libstagefright_foundation
+ libstagefright liblog libutils libbinder libstagefright_foundation libmedia
LOCAL_STATIC_LIBRARIES := \
- libstagefright_rtsp
+ libstagefright_rtsp
-LOCAL_C_INCLUDES:= \
+LOCAL_C_INCLUDES := \
frameworks/av/media/libstagefright \
+ frameworks/av/cmds/stagefright \
$(TOP)/frameworks/native/include/media/openmax
LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
diff --git a/media/libstagefright/rtsp/MyTransmitter.h b/media/libstagefright/rtsp/MyTransmitter.h
index 369f276..bf44aff 100644
--- a/media/libstagefright/rtsp/MyTransmitter.h
+++ b/media/libstagefright/rtsp/MyTransmitter.h
@@ -31,9 +31,10 @@
#ifdef ANDROID
#include "VideoSource.h"
-
-#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodecSource.h>
#endif
namespace android {
@@ -109,17 +110,19 @@
sp<MediaSource> source = new VideoSource(width, height);
- sp<MetaData> encMeta = new MetaData;
- encMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
- encMeta->setInt32(kKeyWidth, width);
- encMeta->setInt32(kKeyHeight, height);
+ sp<AMessage> encMeta = new AMessage;
+ encMeta->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
+ encMeta->setInt32("width", width);
+ encMeta->setInt32("height", height);
+ encMeta->setInt32("frame-rate", 30);
+ encMeta->setInt32("bitrate", 256000);
+ encMeta->setInt32("i-frame-interval", 10);
- OMXClient client;
- client.connect();
+ sp<ALooper> encLooper = new ALooper;
+ encLooper->setName("rtsp_transmitter");
+ encLooper->start();
- mEncoder = OMXCodec::Create(
- client.interface(), encMeta,
- true /* createEncoder */, source);
+ mEncoder = MediaCodecSource::Create(encLooper, encMeta, source);
mEncoder->start();
diff --git a/media/libstagefright/rtsp/rtp_test.cpp b/media/libstagefright/rtsp/rtp_test.cpp
index d43cd2a..24f529b 100644
--- a/media/libstagefright/rtsp/rtp_test.cpp
+++ b/media/libstagefright/rtsp/rtp_test.cpp
@@ -20,13 +20,13 @@
#include <binder/ProcessState.h>
+#include <media/stagefright/foundation/base64.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
-#include <media/stagefright/foundation/base64.h>
+#include <media/stagefright/SimpleDecodingSource.h>
#include "ARTPSession.h"
#include "ASessionDescription.h"
@@ -178,15 +178,8 @@
CHECK_EQ(session->countTracks(), 1u);
sp<MediaSource> source = session->trackAt(0);
- OMXClient client;
- CHECK_EQ(client.connect(), (status_t)OK);
-
- sp<MediaSource> decoder = OMXCodec::Create(
- client.interface(),
- source->getFormat(), false /* createEncoder */,
- source,
- NULL,
- 0); // OMXCodec::kPreferSoftwareCodecs);
+ sp<MediaSource> decoder = SimpleDecodingSource::Create(
+ source, 0 /* flags: ACodec::kPreferSoftwareCodecs */);
CHECK(decoder != NULL);
CHECK_EQ(decoder->start(), (status_t)OK);
@@ -213,7 +206,7 @@
int64_t timeUs;
CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
- printf("decoder returned frame of size %d at time %.2f secs\n",
+ printf("decoder returned frame of size %zu at time %.2f secs\n",
buffer->range_length(), timeUs / 1E6);
}
#endif
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index 3860e9b..ad1e684 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -38,11 +38,6 @@
#include <binder/ProcessState.h>
#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
#include <OMX_Component.h>
#include "DummyRecorder.h"
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index 4d4ffba..f2c6365 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -3,11 +3,6 @@
LOCAL_SRC_FILES:= \
TextDescriptions.cpp \
- TimedTextDriver.cpp \
- TimedText3GPPSource.cpp \
- TimedTextSource.cpp \
- TimedTextSRTSource.cpp \
- TimedTextPlayer.cpp
LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
LOCAL_CLANG := true
diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.cpp b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
deleted file mode 100644
index 4854121..0000000
--- a/media/libstagefright/timedtext/TimedText3GPPSource.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
- /*
- * 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 "TimedText3GPPSource"
-#include <utils/Log.h>
-
-#include <binder/Parcel.h>
-#include <media/stagefright/foundation/ADebug.h> // CHECK_XX macro
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-
-#include "TimedText3GPPSource.h"
-#include "TextDescriptions.h"
-
-namespace android {
-
-TimedText3GPPSource::TimedText3GPPSource(const sp<MediaSource>& mediaSource)
- : mSource(mediaSource) {
-}
-
-TimedText3GPPSource::~TimedText3GPPSource() {
-}
-
-status_t TimedText3GPPSource::read(
- int64_t *startTimeUs, int64_t *endTimeUs, Parcel *parcel,
- const MediaSource::ReadOptions *options) {
- MediaBuffer *textBuffer = NULL;
- status_t err = mSource->read(&textBuffer, options);
- if (err != OK) {
- return err;
- }
- CHECK(textBuffer != NULL);
- textBuffer->meta_data()->findInt64(kKeyTime, startTimeUs);
- CHECK_GE(*startTimeUs, 0);
- extractAndAppendLocalDescriptions(*startTimeUs, textBuffer, parcel);
- textBuffer->release();
- // endTimeUs is a dummy parameter for 3gpp timed text format.
- // Set a negative value to it to mark it is unavailable.
- *endTimeUs = -1;
- return OK;
-}
-
-// Each text sample consists of a string of text, optionally with sample
-// modifier description. The modifier description could specify a new
-// text style for the string of text. These descriptions are present only
-// if they are needed. This method is used to extract the modifier
-// description and append it at the end of the text.
-status_t TimedText3GPPSource::extractAndAppendLocalDescriptions(
- int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel) {
- const void *data;
- size_t size = 0;
- int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS;
-
- const char *mime;
- CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
- CHECK(strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0);
-
- data = textBuffer->data();
- size = textBuffer->size();
-
- if (size > 0) {
- parcel->freeData();
- flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
- return TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
- }
- return OK;
-}
-
-// To extract and send the global text descriptions for all the text samples
-// in the text track or text file.
-// TODO: send error message to application via notifyListener()...?
-status_t TimedText3GPPSource::extractGlobalDescriptions(Parcel *parcel) {
- const void *data;
- size_t size = 0;
- int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS;
-
- const char *mime;
- CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
- CHECK(strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0);
-
- uint32_t type;
- // get the 'tx3g' box content. This box contains the text descriptions
- // used to render the text track
- if (!mSource->getFormat()->findData(
- kKeyTextFormatData, &type, &data, &size)) {
- return ERROR_MALFORMED;
- }
-
- if (size > 0) {
- flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
- return TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, 0, parcel);
- }
- return OK;
-}
-
-sp<MetaData> TimedText3GPPSource::getFormat() {
- return mSource->getFormat();
-}
-
-} // namespace android
diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.h b/media/libstagefright/timedtext/TimedText3GPPSource.h
deleted file mode 100644
index 4170940..0000000
--- a/media/libstagefright/timedtext/TimedText3GPPSource.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef TIMED_TEXT_3GPP_SOURCE_H_
-#define TIMED_TEXT_3GPP_SOURCE_H_
-
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-
-#include "TimedTextSource.h"
-
-namespace android {
-
-class MediaBuffer;
-class Parcel;
-
-class TimedText3GPPSource : public TimedTextSource {
-public:
- TimedText3GPPSource(const sp<MediaSource>& mediaSource);
- virtual status_t start() { return mSource->start(); }
- virtual status_t stop() { return mSource->stop(); }
- virtual status_t read(
- int64_t *startTimeUs,
- int64_t *endTimeUs,
- Parcel *parcel,
- const MediaSource::ReadOptions *options = NULL);
- virtual status_t extractGlobalDescriptions(Parcel *parcel);
- virtual sp<MetaData> getFormat();
-
-protected:
- virtual ~TimedText3GPPSource();
-
-private:
- sp<MediaSource> mSource;
-
- status_t extractAndAppendLocalDescriptions(
- int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel);
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedText3GPPSource);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_3GPP_SOURCE_H_
diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp
deleted file mode 100644
index 55a9803..0000000
--- a/media/libstagefright/timedtext/TimedTextDriver.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
- /*
- * 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
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
deleted file mode 100644
index aecf666..0000000
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
- /*
- * 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 "TimedTextPlayer"
-#include <utils/Log.h>
-
-#include <inttypes.h>
-#include <limits.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/timedtext/TimedTextDriver.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/MediaPlayerInterface.h>
-
-#include "TimedTextPlayer.h"
-
-#include "TimedTextSource.h"
-
-namespace android {
-
-// Event should be fired a bit earlier considering the processing time till
-// application actually gets the notification message.
-static const int64_t kAdjustmentProcessingTimeUs = 100000ll;
-static const int64_t kMaxDelayUs = 5000000ll;
-static const int64_t kWaitTimeUsToRetryRead = 100000ll;
-static const int64_t kInvalidTimeUs = INT_MIN;
-
-TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener)
- : mListener(listener),
- mSource(NULL),
- mPendingSeekTimeUs(kInvalidTimeUs),
- mPaused(false),
- mSendSubtitleGeneration(0) {
-}
-
-TimedTextPlayer::~TimedTextPlayer() {
- if (mSource != NULL) {
- mSource->stop();
- mSource.clear();
- mSource = NULL;
- }
-}
-
-void TimedTextPlayer::start() {
- (new AMessage(kWhatStart, this))->post();
-}
-
-void TimedTextPlayer::pause() {
- (new AMessage(kWhatPause, this))->post();
-}
-
-void TimedTextPlayer::resume() {
- (new AMessage(kWhatResume, this))->post();
-}
-
-void TimedTextPlayer::seekToAsync(int64_t timeUs) {
- sp<AMessage> msg = new AMessage(kWhatSeek, this);
- msg->setInt64("seekTimeUs", timeUs);
- msg->post();
-}
-
-void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) {
- sp<AMessage> msg = new AMessage(kWhatSetSource, this);
- msg->setObject("source", source);
- msg->post();
-}
-
-void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatPause: {
- mPaused = true;
- break;
- }
- case kWhatResume: {
- mPaused = false;
- if (mPendingSeekTimeUs != kInvalidTimeUs) {
- seekToAsync(mPendingSeekTimeUs);
- mPendingSeekTimeUs = kInvalidTimeUs;
- } else {
- doRead();
- }
- break;
- }
- case kWhatStart: {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener == NULL) {
- ALOGE("Listener is NULL when kWhatStart is received.");
- break;
- }
- mPaused = false;
- mPendingSeekTimeUs = kInvalidTimeUs;
- int32_t positionMs = 0;
- listener->getCurrentPosition(&positionMs);
- int64_t seekTimeUs = positionMs * 1000ll;
-
- notifyListener();
- mSendSubtitleGeneration++;
- doSeekAndRead(seekTimeUs);
- break;
- }
- case kWhatRetryRead: {
- int32_t generation = -1;
- CHECK(msg->findInt32("generation", &generation));
- if (generation != mSendSubtitleGeneration) {
- // Drop obsolete msg.
- break;
- }
- int64_t seekTimeUs;
- int seekMode;
- if (msg->findInt64("seekTimeUs", &seekTimeUs) &&
- msg->findInt32("seekMode", &seekMode)) {
- MediaSource::ReadOptions options;
- options.setSeekTo(
- seekTimeUs,
- static_cast<MediaSource::ReadOptions::SeekMode>(seekMode));
- doRead(&options);
- } else {
- doRead();
- }
- break;
- }
- case kWhatSeek: {
- int64_t seekTimeUs = kInvalidTimeUs;
- // Clear a displayed timed text before seeking.
- notifyListener();
- msg->findInt64("seekTimeUs", &seekTimeUs);
- if (seekTimeUs == kInvalidTimeUs) {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener != NULL) {
- int32_t positionMs = 0;
- listener->getCurrentPosition(&positionMs);
- seekTimeUs = positionMs * 1000ll;
- }
- }
- if (mPaused) {
- mPendingSeekTimeUs = seekTimeUs;
- break;
- }
- mSendSubtitleGeneration++;
- doSeekAndRead(seekTimeUs);
- break;
- }
- case kWhatSendSubtitle: {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
- if (generation != mSendSubtitleGeneration) {
- // Drop obsolete msg.
- break;
- }
- // If current time doesn't reach to the fire time,
- // re-post the message with the adjusted delay time.
- int64_t fireTimeUs = kInvalidTimeUs;
- if (msg->findInt64("fireTimeUs", &fireTimeUs)) {
- // TODO: check if fireTimeUs is not kInvalidTimeUs.
- int64_t delayUs = delayUsFromCurrentTime(fireTimeUs);
- if (delayUs > 0) {
- msg->post(delayUs);
- break;
- }
- }
- sp<RefBase> obj;
- if (msg->findObject("subtitle", &obj)) {
- sp<ParcelEvent> parcelEvent;
- parcelEvent = static_cast<ParcelEvent*>(obj.get());
- notifyListener(&(parcelEvent->parcel));
- doRead();
- } else {
- notifyListener();
- }
- break;
- }
- case kWhatSetSource: {
- mSendSubtitleGeneration++;
- sp<RefBase> obj;
- msg->findObject("source", &obj);
- if (mSource != NULL) {
- mSource->stop();
- mSource.clear();
- mSource = NULL;
- }
- // null source means deselect track.
- if (obj == NULL) {
- mPendingSeekTimeUs = kInvalidTimeUs;
- mPaused = false;
- notifyListener();
- break;
- }
- mSource = static_cast<TimedTextSource*>(obj.get());
- status_t err = mSource->start();
- if (err != OK) {
- notifyError(err);
- break;
- }
- Parcel parcel;
- err = mSource->extractGlobalDescriptions(&parcel);
- if (err != OK) {
- notifyError(err);
- break;
- }
- notifyListener(&parcel);
- break;
- }
- }
-}
-
-void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) {
- MediaSource::ReadOptions options;
- options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- doRead(&options);
-}
-
-void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) {
- int64_t startTimeUs = 0;
- int64_t endTimeUs = 0;
- sp<ParcelEvent> parcelEvent = new ParcelEvent();
- CHECK(mSource != NULL);
- status_t err = mSource->read(&startTimeUs, &endTimeUs,
- &(parcelEvent->parcel), options);
- if (err == WOULD_BLOCK) {
- sp<AMessage> msg = new AMessage(kWhatRetryRead, this);
- if (options != NULL) {
- int64_t seekTimeUs = kInvalidTimeUs;
- MediaSource::ReadOptions::SeekMode seekMode =
- MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
- CHECK(options->getSeekTo(&seekTimeUs, &seekMode));
- msg->setInt64("seekTimeUs", seekTimeUs);
- msg->setInt32("seekMode", seekMode);
- }
- msg->setInt32("generation", mSendSubtitleGeneration);
- msg->post(kWaitTimeUsToRetryRead);
- return;
- } else if (err != OK) {
- notifyError(err);
- return;
- }
-
- postTextEvent(parcelEvent, startTimeUs);
- if (endTimeUs > 0) {
- CHECK_GE(endTimeUs, startTimeUs);
- // send an empty timed text to clear the subtitle when it reaches to the
- // end time.
- postTextEvent(NULL, endTimeUs);
- }
-}
-
-void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) {
- int64_t delayUs = delayUsFromCurrentTime(timeUs);
- sp<AMessage> msg = new AMessage(kWhatSendSubtitle, this);
- msg->setInt32("generation", mSendSubtitleGeneration);
- if (parcel != NULL) {
- msg->setObject("subtitle", parcel);
- }
- msg->setInt64("fireTimeUs", timeUs);
- msg->post(delayUs);
-}
-
-int64_t TimedTextPlayer::delayUsFromCurrentTime(int64_t fireTimeUs) {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener == NULL) {
- // TODO: it may be better to return kInvalidTimeUs
- ALOGE("%s: Listener is NULL. (fireTimeUs = %" PRId64" )",
- __FUNCTION__, fireTimeUs);
- return 0;
- }
- int32_t positionMs = 0;
- listener->getCurrentPosition(&positionMs);
- int64_t positionUs = positionMs * 1000ll;
-
- if (fireTimeUs <= positionUs + kAdjustmentProcessingTimeUs) {
- return 0;
- } else {
- int64_t delayUs = fireTimeUs - positionUs - kAdjustmentProcessingTimeUs;
- if (delayUs > kMaxDelayUs) {
- return kMaxDelayUs;
- }
- return delayUs;
- }
-}
-
-void TimedTextPlayer::notifyError(int error) {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener == NULL) {
- ALOGE("%s(error=%d): Listener is NULL.", __FUNCTION__, error);
- return;
- }
- listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error);
-}
-
-void TimedTextPlayer::notifyListener(const Parcel *parcel) {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener == NULL) {
- ALOGE("%s: Listener is NULL.", __FUNCTION__);
- return;
- }
- if (parcel != NULL && (parcel->dataSize() > 0)) {
- listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel);
- } else { // send an empty timed text to clear the screen
- listener->sendEvent(MEDIA_TIMED_TEXT);
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h
deleted file mode 100644
index 9cb49ec..0000000
--- a/media/libstagefright/timedtext/TimedTextPlayer.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef TIMEDTEXT_PLAYER_H_
-#define TIMEDTEXT_PLAYER_H_
-
-#include <binder/Parcel.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/MediaSource.h>
-#include <utils/RefBase.h>
-
-#include "TimedTextSource.h"
-
-namespace android {
-
-struct AMessage;
-class MediaPlayerBase;
-class TimedTextDriver;
-class TimedTextSource;
-
-class TimedTextPlayer : public AHandler {
-public:
- TimedTextPlayer(const wp<MediaPlayerBase> &listener);
-
- virtual ~TimedTextPlayer();
-
- void start();
- void pause();
- void resume();
- void seekToAsync(int64_t timeUs);
- void setDataSource(sp<TimedTextSource> source);
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatPause = 'paus',
- kWhatResume = 'resm',
- kWhatStart = 'strt',
- kWhatSeek = 'seek',
- kWhatRetryRead = 'read',
- kWhatSendSubtitle = 'send',
- kWhatSetSource = 'ssrc',
- };
-
- // To add Parcel into an AMessage as an object, it should be 'RefBase'.
- struct ParcelEvent : public RefBase {
- Parcel parcel;
- };
-
- wp<MediaPlayerBase> mListener;
- sp<TimedTextSource> mSource;
- int64_t mPendingSeekTimeUs;
- bool mPaused;
- int32_t mSendSubtitleGeneration;
-
- void doSeekAndRead(int64_t seekTimeUs);
- void doRead(MediaSource::ReadOptions* options = NULL);
- void onTextEvent();
- void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1);
- int64_t delayUsFromCurrentTime(int64_t fireTimeUs);
- void notifyError(int error = 0);
- void notifyListener(const Parcel *parcel = NULL);
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer);
-};
-
-} // namespace android
-
-#endif // TIMEDTEXT_PLAYER_H_
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
deleted file mode 100644
index 2ac1e72..0000000
--- a/media/libstagefright/timedtext/TimedTextSRTSource.cpp
+++ /dev/null
@@ -1,297 +0,0 @@
- /*
- * 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 "TimedTextSRTSource"
-#include <utils/Log.h>
-
-#include <binder/Parcel.h>
-#include <media/stagefright/foundation/ADebug.h> // for CHECK_xx
-#include <media/stagefright/foundation/AString.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-
-#include "TimedTextSRTSource.h"
-#include "TextDescriptions.h"
-
-namespace android {
-
-TimedTextSRTSource::TimedTextSRTSource(const sp<DataSource>& dataSource)
- : mSource(dataSource),
- mMetaData(new MetaData),
- mIndex(0) {
- // TODO: Need to detect the language, because SRT doesn't give language
- // information explicitly.
- mMetaData->setCString(kKeyMediaLanguage, "und");
-}
-
-TimedTextSRTSource::~TimedTextSRTSource() {
-}
-
-status_t TimedTextSRTSource::start() {
- status_t err = scanFile();
- if (err != OK) {
- reset();
- }
- return err;
-}
-
-void TimedTextSRTSource::reset() {
- mTextVector.clear();
- mIndex = 0;
-}
-
-status_t TimedTextSRTSource::stop() {
- reset();
- return OK;
-}
-
-status_t TimedTextSRTSource::read(
- int64_t *startTimeUs,
- int64_t *endTimeUs,
- Parcel *parcel,
- const MediaSource::ReadOptions *options) {
- AString text;
- status_t err = getText(options, &text, startTimeUs, endTimeUs);
- if (err != OK) {
- return err;
- }
-
- CHECK_GE(*startTimeUs, 0);
- extractAndAppendLocalDescriptions(*startTimeUs, text, parcel);
- return OK;
-}
-
-sp<MetaData> TimedTextSRTSource::getFormat() {
- return mMetaData;
-}
-
-status_t TimedTextSRTSource::scanFile() {
- off64_t offset = 0;
- int64_t startTimeUs;
- bool endOfFile = false;
-
- while (!endOfFile) {
- TextInfo info;
- status_t err = getNextSubtitleInfo(&offset, &startTimeUs, &info);
- switch (err) {
- case OK:
- mTextVector.add(startTimeUs, info);
- break;
- case ERROR_END_OF_STREAM:
- endOfFile = true;
- break;
- default:
- return err;
- }
- }
- if (mTextVector.isEmpty()) {
- return ERROR_MALFORMED;
- }
- return OK;
-}
-
-/* SRT format:
- * Subtitle number
- * Start time --> End time
- * Text of subtitle (one or more lines)
- * Blank lines
- *
- * .srt file example:
- * 1
- * 00:00:20,000 --> 00:00:24,400
- * Altocumulus clouds occr between six thousand
- *
- * 2
- * 00:00:24,600 --> 00:00:27,800
- * and twenty thousand feet above ground level.
- */
-status_t TimedTextSRTSource::getNextSubtitleInfo(
- off64_t *offset, int64_t *startTimeUs, TextInfo *info) {
- AString data;
- status_t err;
-
- // To skip blank lines.
- do {
- if ((err = readNextLine(offset, &data)) != OK) {
- return err;
- }
- data.trim();
- } while (data.empty());
-
- // Just ignore the first non-blank line which is subtitle sequence number.
- if ((err = readNextLine(offset, &data)) != OK) {
- return err;
- }
- int hour1, hour2, min1, min2, sec1, sec2, msec1, msec2;
- // the start time format is: hours:minutes:seconds,milliseconds
- // 00:00:24,600 --> 00:00:27,800
- if (sscanf(data.c_str(), "%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d",
- &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) {
- return ERROR_MALFORMED;
- }
-
- *startTimeUs = ((hour1 * 3600 + min1 * 60 + sec1) * 1000 + msec1) * 1000ll;
- info->endTimeUs = ((hour2 * 3600 + min2 * 60 + sec2) * 1000 + msec2) * 1000ll;
- if (info->endTimeUs <= *startTimeUs) {
- return ERROR_MALFORMED;
- }
-
- info->offset = *offset;
- bool needMoreData = true;
- while (needMoreData) {
- if ((err = readNextLine(offset, &data)) != OK) {
- if (err == ERROR_END_OF_STREAM) {
- break;
- } else {
- return err;
- }
- }
-
- data.trim();
- if (data.empty()) {
- // it's an empty line used to separate two subtitles
- needMoreData = false;
- }
- }
- info->textLen = *offset - info->offset;
- return OK;
-}
-
-status_t TimedTextSRTSource::readNextLine(off64_t *offset, AString *data) {
- data->clear();
- while (true) {
- ssize_t readSize;
- char character;
- if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) {
- if (readSize == 0) {
- return ERROR_END_OF_STREAM;
- }
- return ERROR_IO;
- }
-
- (*offset)++;
-
- // a line could end with CR, LF or CR + LF
- if (character == 10) {
- break;
- } else if (character == 13) {
- if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) {
- if (readSize == 0) { // end of the stream
- return OK;
- }
- return ERROR_IO;
- }
-
- (*offset)++;
- if (character != 10) {
- (*offset)--;
- }
- break;
- }
- data->append(character);
- }
- return OK;
-}
-
-status_t TimedTextSRTSource::getText(
- const MediaSource::ReadOptions *options,
- AString *text, int64_t *startTimeUs, int64_t *endTimeUs) {
- if (mTextVector.size() == 0) {
- return ERROR_END_OF_STREAM;
- }
- text->clear();
- int64_t seekTimeUs;
- MediaSource::ReadOptions::SeekMode mode;
- if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
- int64_t lastEndTimeUs =
- mTextVector.valueAt(mTextVector.size() - 1).endTimeUs;
- if (seekTimeUs < 0) {
- return ERROR_OUT_OF_RANGE;
- } else if (seekTimeUs >= lastEndTimeUs) {
- return ERROR_END_OF_STREAM;
- } else {
- // binary search
- size_t low = 0;
- size_t high = mTextVector.size() - 1;
- size_t mid = 0;
-
- while (low <= high) {
- mid = low + (high - low)/2;
- int diff = compareExtendedRangeAndTime(mid, seekTimeUs);
- if (diff == 0) {
- break;
- } else if (diff < 0) {
- low = mid + 1;
- } else {
- high = mid - 1;
- }
- }
- mIndex = mid;
- }
- }
-
- if (mIndex >= mTextVector.size()) {
- return ERROR_END_OF_STREAM;
- }
-
- const TextInfo &info = mTextVector.valueAt(mIndex);
- *startTimeUs = mTextVector.keyAt(mIndex);
- *endTimeUs = info.endTimeUs;
- mIndex++;
-
- char *str = new char[info.textLen];
- if (mSource->readAt(info.offset, str, info.textLen) < info.textLen) {
- delete[] str;
- return ERROR_IO;
- }
- text->append(str, info.textLen);
- delete[] str;
- return OK;
-}
-
-status_t TimedTextSRTSource::extractAndAppendLocalDescriptions(
- int64_t timeUs, const AString &text, Parcel *parcel) {
- const void *data = text.c_str();
- size_t size = text.size();
- int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS |
- TextDescriptions::OUT_OF_BAND_TEXT_SRT;
-
- if (size > 0) {
- return TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
- }
- return OK;
-}
-
-int TimedTextSRTSource::compareExtendedRangeAndTime(size_t index, int64_t timeUs) {
- CHECK_LT(index, mTextVector.size());
- int64_t endTimeUs = mTextVector.valueAt(index).endTimeUs;
- int64_t startTimeUs = (index > 0) ?
- mTextVector.valueAt(index - 1).endTimeUs : 0;
- if (timeUs >= startTimeUs && timeUs < endTimeUs) {
- return 0;
- } else if (endTimeUs <= timeUs) {
- return -1;
- } else {
- return 1;
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.h b/media/libstagefright/timedtext/TimedTextSRTSource.h
deleted file mode 100644
index 232675e..0000000
--- a/media/libstagefright/timedtext/TimedTextSRTSource.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef TIMED_TEXT_SRT_SOURCE_H_
-#define TIMED_TEXT_SRT_SOURCE_H_
-
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <utils/Compat.h> // off64_t
-
-#include "TimedTextSource.h"
-
-namespace android {
-
-struct AString;
-class DataSource;
-class MediaBuffer;
-class Parcel;
-
-class TimedTextSRTSource : public TimedTextSource {
-public:
- TimedTextSRTSource(const sp<DataSource>& dataSource);
- virtual status_t start();
- virtual status_t stop();
- virtual status_t read(
- int64_t *startTimeUs,
- int64_t *endTimeUs,
- Parcel *parcel,
- const MediaSource::ReadOptions *options = NULL);
- virtual sp<MetaData> getFormat();
-
-protected:
- virtual ~TimedTextSRTSource();
-
-private:
- sp<DataSource> mSource;
- sp<MetaData> mMetaData;
-
- struct TextInfo {
- int64_t endTimeUs;
- // The offset of the text in the original file.
- off64_t offset;
- int textLen;
- };
-
- size_t mIndex;
- KeyedVector<int64_t, TextInfo> mTextVector;
-
- void reset();
- status_t scanFile();
- status_t getNextSubtitleInfo(
- off64_t *offset, int64_t *startTimeUs, TextInfo *info);
- status_t readNextLine(off64_t *offset, AString *data);
- status_t getText(
- const MediaSource::ReadOptions *options,
- AString *text, int64_t *startTimeUs, int64_t *endTimeUs);
- status_t extractAndAppendLocalDescriptions(
- int64_t timeUs, const AString &text, Parcel *parcel);
-
- // Compares the time range of the subtitle at index to the given timeUs.
- // The time range of the subtitle to match with given timeUs is extended to
- // [endTimeUs of the previous subtitle, endTimeUs of current subtitle).
- //
- // This compare function is used to find a next subtitle when read() is
- // called with seek options. Note that timeUs within gap ranges, such as
- // [200, 300) in the below example, will be matched to the closest future
- // subtitle, [300, 400).
- //
- // For instance, assuming there are 3 subtitles in mTextVector,
- // 0: [100, 200) ----> [0, 200)
- // 1: [300, 400) ----> [200, 400)
- // 2: [500, 600) ----> [400, 600)
- // If the 'index' parameter contains 1, this function
- // returns 0, if timeUs is in [200, 400)
- // returns -1, if timeUs >= 400,
- // returns 1, if timeUs < 200.
- int compareExtendedRangeAndTime(size_t index, int64_t timeUs);
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextSRTSource);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_SRT_SOURCE_H_
diff --git a/media/libstagefright/timedtext/TimedTextSource.cpp b/media/libstagefright/timedtext/TimedTextSource.cpp
deleted file mode 100644
index 953f7b5..0000000
--- a/media/libstagefright/timedtext/TimedTextSource.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
- /*
- * 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 "TimedTextSource"
-#include <utils/Log.h>
-
-#include <media/stagefright/foundation/ADebug.h> // CHECK_XX macro
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-
-#include "TimedTextSource.h"
-
-#include "TimedText3GPPSource.h"
-#include "TimedTextSRTSource.h"
-
-namespace android {
-
-// static
-sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
- const sp<MediaSource>& mediaSource) {
- const char *mime;
- CHECK(mediaSource->getFormat()->findCString(kKeyMIMEType, &mime));
- if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
- return new TimedText3GPPSource(mediaSource);
- }
- ALOGE("Unsupported mime type for subtitle. : %s", mime);
- return NULL;
-}
-
-// static
-sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
- const sp<DataSource>& dataSource, FileType filetype) {
- switch(filetype) {
- case OUT_OF_BAND_FILE_SRT:
- return new TimedTextSRTSource(dataSource);
- case OUT_OF_BAND_FILE_SMI:
- // TODO: Implement for SMI.
- ALOGE("Supporting SMI is not implemented yet");
- break;
- default:
- ALOGE("Undefined subtitle format. : %d", filetype);
- }
- return NULL;
-}
-
-sp<MetaData> TimedTextSource::getFormat() {
- return NULL;
-}
-
-} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSource.h b/media/libstagefright/timedtext/TimedTextSource.h
deleted file mode 100644
index 8c1c1cd..0000000
--- a/media/libstagefright/timedtext/TimedTextSource.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef TIMED_TEXT_SOURCE_H_
-#define TIMED_TEXT_SOURCE_H_
-
-#include <media/stagefright/foundation/ABase.h> // for DISALLOW_XXX macro.
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h> // for MediaSource::ReadOptions
-#include <utils/RefBase.h>
-
-namespace android {
-
-class DataSource;
-class MetaData;
-class Parcel;
-
-class TimedTextSource : public RefBase {
- public:
- enum FileType {
- OUT_OF_BAND_FILE_SRT = 1,
- OUT_OF_BAND_FILE_SMI = 2,
- };
- static sp<TimedTextSource> CreateTimedTextSource(
- const sp<MediaSource>& source);
- static sp<TimedTextSource> CreateTimedTextSource(
- const sp<DataSource>& source, FileType filetype);
- TimedTextSource() {}
- virtual status_t start() = 0;
- virtual status_t stop() = 0;
- // Returns subtitle parcel and its start time.
- virtual status_t read(
- int64_t *startTimeUs,
- int64_t *endTimeUs,
- Parcel *parcel,
- const MediaSource::ReadOptions *options = NULL) = 0;
- virtual status_t extractGlobalDescriptions(Parcel * /* parcel */) {
- return INVALID_OPERATION;
- }
- virtual sp<MetaData> getFormat();
-
- protected:
- virtual ~TimedTextSource() { }
-
- private:
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextSource);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_SOURCE_H_
diff --git a/media/libstagefright/timedtext/test/Android.mk b/media/libstagefright/timedtext/test/Android.mk
deleted file mode 100644
index e0e0e0d..0000000
--- a/media/libstagefright/timedtext/test/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# ================================================================
-# Unit tests for libstagefright_timedtext
-# ================================================================
-
-# ================================================================
-# A test for TimedTextSRTSource
-# ================================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := TimedTextSRTSource_test
-
-LOCAL_MODULE_TAGS := eng tests
-
-LOCAL_SRC_FILES := TimedTextSRTSource_test.cpp
-
-LOCAL_C_INCLUDES := \
- $(TOP)/external/expat/lib \
- $(TOP)/frameworks/av/media/libstagefright/timedtext
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libexpat \
- libstagefright \
- libstagefright_foundation \
- libutils
-
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CLANG := true
-
-include $(BUILD_NATIVE_TEST)
diff --git a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp b/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
deleted file mode 100644
index 211e732..0000000
--- a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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_TAG "TimedTextSRTSource_test"
-#include <utils/Log.h>
-
-#include <gtest/gtest.h>
-
-#include <binder/Parcel.h>
-#include <media/stagefright/foundation/AString.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaErrors.h>
-#include <utils/misc.h>
-
-#include <TimedTextSource.h>
-#include <TimedTextSRTSource.h>
-
-namespace android {
-namespace test {
-
-static const int kSecToUsec = 1000000;
-static const int kSecToMsec = 1000;
-static const int kMsecToUsec = 1000;
-
-/* SRT format (http://en.wikipedia.org/wiki/SubRip)
- * Subtitle number
- * Start time --> End time
- * Text of subtitle (one or more lines)
- * Blank lines
- */
-static const char *kSRTString =
- "1\n00:00:1,000 --> 00:00:1,500\n1\n\n"
- "2\n00:00:2,000 --> 00:00:2,500\n2\n\n"
- "3\n00:00:3,000 --> 00:00:3,500\n3\n\n"
- "4\n00:00:4,000 --> 00:00:4,500\n4\n\n"
- "5\n00:00:5,000 --> 00:00:5,500\n5\n\n"
- // edge case : previos end time = next start time
- "6\n00:00:5,500 --> 00:00:5,800\n6\n\n"
- "7\n00:00:5,800 --> 00:00:6,000\n7\n\n"
- "8\n00:00:6,000 --> 00:00:7,000\n8\n\n";
-
-class SRTDataSourceStub : public DataSource {
-public:
- SRTDataSourceStub(const char *data, size_t size) :
- mData(data), mSize(size) {}
- virtual ~SRTDataSourceStub() {}
-
- virtual status_t initCheck() const {
- return OK;
- }
-
- virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
- if ((size_t)offset >= mSize) return 0;
-
- ssize_t avail = mSize - offset;
- if ((size_t)avail > size) {
- avail = size;
- }
- memcpy(data, mData + offset, avail);
- return avail;
- }
-
-private:
- const char *mData;
- size_t mSize;
-};
-
-class TimedTextSRTSourceTest : public testing::Test {
-protected:
- void SetUp() {
- sp<DataSource> stub= new SRTDataSourceStub(
- kSRTString,
- strlen(kSRTString));
- mSource = new TimedTextSRTSource(stub);
- mSource->start();
- }
-
- void CheckStartTimeMs(const Parcel& parcel, int32_t timeMs) {
- int32_t intval;
- parcel.setDataPosition(8);
- parcel.readInt32(&intval);
- EXPECT_EQ(timeMs, intval);
- }
-
- void CheckDataEquals(const Parcel& parcel, const char* content) {
- int32_t intval;
- parcel.setDataPosition(16);
- parcel.readInt32(&intval);
- parcel.setDataPosition(24);
- const char* data = (const char*) parcel.readInplace(intval);
-
- int32_t content_len = strlen(content);
- EXPECT_EQ(content_len, intval);
- EXPECT_TRUE(strncmp(data, content, content_len) == 0);
- }
-
- sp<TimedTextSource> mSource;
- int64_t startTimeUs;
- int64_t endTimeUs;
- Parcel parcel;
- AString subtitle;
- status_t err;
-};
-
-TEST_F(TimedTextSRTSourceTest, readAll) {
- for (int i = 1; i <= 5; i++) {
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel);
- EXPECT_EQ(OK, err);
- CheckStartTimeMs(parcel, i * kSecToMsec);
- subtitle = AStringPrintf("%d\n\n", i);
- CheckDataEquals(parcel, subtitle.c_str());
- }
- // read edge cases
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel);
- EXPECT_EQ(OK, err);
- CheckStartTimeMs(parcel, 5500);
- subtitle = AStringPrintf("6\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel);
- EXPECT_EQ(OK, err);
- CheckStartTimeMs(parcel, 5800);
- subtitle = AStringPrintf("7\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel);
- EXPECT_EQ(OK, err);
- CheckStartTimeMs(parcel, 6000);
- subtitle = AStringPrintf("8\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel);
- EXPECT_EQ(ERROR_END_OF_STREAM, err);
-}
-
-TEST_F(TimedTextSRTSourceTest, seekTimeIsEarlierThanFirst) {
- MediaSource::ReadOptions options;
- options.setSeekTo(500, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(1 * kSecToUsec, startTimeUs);
- CheckStartTimeMs(parcel, 1 * kSecToMsec);
-}
-
-TEST_F(TimedTextSRTSourceTest, seekTimeIsLaterThanLast) {
- MediaSource::ReadOptions options;
- options.setSeekTo(7 * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(ERROR_END_OF_STREAM, err);
-
- options.setSeekTo(8 * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(ERROR_END_OF_STREAM, err);
-}
-
-TEST_F(TimedTextSRTSourceTest, seekTimeIsMatched) {
- for (int i = 1; i <= 5; i++) {
- MediaSource::ReadOptions options;
- options.setSeekTo(i * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(i * kSecToUsec, startTimeUs);
-
- options.setSeekTo(i * kSecToUsec + 100, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(i * kSecToUsec, startTimeUs);
- }
-}
-
-TEST_F(TimedTextSRTSourceTest, seekTimeInBetweenTwo) {
- for (int i = 1; i <= 4; i++) {
- MediaSource::ReadOptions options;
- options.setSeekTo(i * kSecToUsec + 500000, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ((i + 1) * kSecToUsec, startTimeUs);
-
- options.setSeekTo(i * kSecToUsec + 600000, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ((i + 1) * kSecToUsec, startTimeUs);
- }
-}
-
-TEST_F(TimedTextSRTSourceTest, checkEdgeCase) {
- MediaSource::ReadOptions options;
- options.setSeekTo(5500 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(5500 * kMsecToUsec, startTimeUs);
- subtitle = AStringPrintf("6\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-
- options.setSeekTo(5800 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(5800 * kMsecToUsec, startTimeUs);
- subtitle = AStringPrintf("7\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-
- options.setSeekTo(6000 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(6000 * kMsecToUsec, startTimeUs);
- subtitle = AStringPrintf("8\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-}
-
-} // namespace test
-} // namespace android