Merge "AudioFlinger: Get shared heap size from property"
diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk
index 3777d94..de18df85 100644
--- a/camera/tests/Android.mk
+++ b/camera/tests/Android.mk
@@ -21,6 +21,7 @@
CameraBinderTests.cpp
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libutils \
libcutils \
libcamera_metadata \
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 20c0094..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
+ 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 a9c6eda..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);
@@ -618,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,
@@ -631,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");
@@ -881,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) {
@@ -954,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;
@@ -1097,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) {
@@ -1129,7 +1106,5 @@
}
}
- client.disconnect();
-
return 0;
}
diff --git a/include/media/MediaCodecInfo.h b/include/media/MediaCodecInfo.h
index 4067b47..ac5b075 100644
--- a/include/media/MediaCodecInfo.h
+++ b/include/media/MediaCodecInfo.h
@@ -33,7 +33,6 @@
struct AMessage;
class Parcel;
-struct CodecCapabilities;
typedef KeyedVector<AString, AString> CodecSettings;
@@ -44,12 +43,19 @@
};
struct Capabilities : public RefBase {
+ enum {
+ // decoder flags
+ kFlagSupportsAdaptivePlayback = 1 << 0,
+ kFlagSupportsSecurePlayback = 1 << 1,
+ kFlagSupportsTunneledPlayback = 1 << 2,
+ };
+
void getSupportedProfileLevels(Vector<ProfileLevel> *profileLevels) const;
void getSupportedColorFormats(Vector<uint32_t> *colorFormats) const;
uint32_t getFlags() const;
const sp<AMessage> getDetails() const;
- private:
+ protected:
Vector<ProfileLevel> mProfileLevels;
Vector<uint32_t> mColorFormats;
uint32_t mFlags;
@@ -57,6 +63,7 @@
Capabilities();
+ private:
// read object from parcel even if object creation fails
static sp<Capabilities> FromParcel(const Parcel &parcel);
status_t writeToParcel(Parcel *parcel) const;
@@ -66,6 +73,14 @@
friend class MediaCodecInfo;
};
+ // Use a subclass to allow setting fields on construction without allowing
+ // to do the same throughout the framework.
+ struct CapabilitiesBuilder : public Capabilities {
+ void addProfileLevel(uint32_t profile, uint32_t level);
+ void addColorFormat(uint32_t format);
+ void addFlags(uint32_t flags);
+ };
+
bool isEncoder() const;
bool hasQuirk(const char *name) const;
void getSupportedMimes(Vector<AString> *mimes) const;
@@ -107,7 +122,8 @@
void addQuirk(const char *name);
status_t addMime(const char *mime);
status_t updateMime(const char *mime);
- status_t initializeCapabilities(const CodecCapabilities &caps);
+
+ status_t initializeCapabilities(const sp<Capabilities> &caps);
void addDetail(const AString &key, const AString &value);
void addFeature(const AString &key, int32_t value);
void addFeature(const AString &key, const char *value);
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 8b5b862..743a0fd 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -50,6 +50,10 @@
virtual void initiateStart();
virtual void initiateShutdown(bool keepComponentAllocated = false);
+ virtual status_t queryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps);
+
virtual status_t setSurface(const sp<Surface> &surface);
virtual void signalFlush();
@@ -91,6 +95,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();
@@ -301,6 +313,10 @@
ssize_t *index = NULL);
status_t setComponentRole(bool isEncoder, const char *mime);
+ static const char *getComponentRole(bool isEncoder, const char *mime);
+ static status_t setComponentRole(
+ const sp<IOMX> &omx, IOMX::node_id node, const char *role);
+
status_t configureCodec(const char *mime, const sp<AMessage> &msg);
status_t configureTunneledVideoPlayback(int32_t audioHwSync,
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index e0cd965..86ce50d 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -20,7 +20,6 @@
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/TimeSource.h>
#include <utils/threads.h>
namespace android {
@@ -30,7 +29,7 @@
struct AwesomePlayer;
class MediaSource;
-class AudioPlayer : public TimeSource {
+class AudioPlayer {
public:
enum {
REACHED_EOS,
@@ -46,29 +45,18 @@
};
AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink,
- uint32_t flags = 0,
- AwesomePlayer *audioObserver = NULL);
+ uint32_t flags = 0);
virtual ~AudioPlayer();
// Caller retains ownership of "source".
void setSource(const sp<MediaSource> &source);
- // Return time in us.
- virtual int64_t getRealTimeUs();
-
status_t start(bool sourceAlreadyStarted = false);
void pause(bool playPendingSamples = false);
status_t resume();
- // Returns the timestamp of the last buffer played (in us).
- int64_t getMediaTimeUs();
-
- // Returns true iff a mapping is established, i.e. the AudioPlayer
- // has played at least one frame of audio.
- bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us);
-
status_t seekTo(int64_t time_us);
bool isSeeking();
@@ -77,10 +65,7 @@
status_t setPlaybackRate(const AudioPlaybackRate &rate);
status_t getPlaybackRate(AudioPlaybackRate *rate /* nonnull */);
- void notifyAudioEOS();
-
private:
- friend class VideoEditorAudioPlayer;
sp<MediaSource> mSource;
sp<AudioTrack> mAudioTrack;
@@ -109,8 +94,6 @@
MediaBuffer *mFirstBuffer;
sp<MediaPlayerBase::AudioSink> mAudioSink;
- AwesomePlayer *mObserver;
- int64_t mPinnedTimeUs;
bool mPlaying;
int64_t mStartPosUs;
@@ -126,11 +109,8 @@
size_t fillBuffer(void *data, size_t size);
- int64_t getRealTimeUsLocked() const;
-
void reset();
- uint32_t getNumFramesPendingPlayout() const;
int64_t getOutputPlayPositionUs_l();
bool allowDeepBuffering() const { return (mCreateFlags & ALLOW_DEEP_BUFFERING) != 0; }
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/CodecBase.h b/include/media/stagefright/CodecBase.h
index bb36052..1a2bcdb 100644
--- a/include/media/stagefright/CodecBase.h
+++ b/include/media/stagefright/CodecBase.h
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <media/IOMX.h>
+#include <media/MediaCodecInfo.h>
#include <media/stagefright/foundation/AHandler.h>
namespace android {
@@ -59,6 +60,10 @@
// require an explicit message handler
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
+ virtual status_t queryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps /* nonnull */) { return INVALID_OPERATION; }
+
virtual status_t setSurface(const sp<Surface> &surface) { return INVALID_OPERATION; }
virtual void signalFlush() = 0;
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index cdfa159..c187e4b 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -20,6 +20,7 @@
#include <gui/IGraphicBufferProducer.h>
#include <media/hardware/CryptoAPI.h>
+#include <media/MediaCodecInfo.h>
#include <media/MediaResource.h>
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/FrameRenderTracker.h>
@@ -64,15 +65,20 @@
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();
+ // utility method to query capabilities
+ static status_t QueryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps /* nonnull */);
+
status_t configure(
const sp<AMessage> &format,
const sp<Surface> &nativeWindow,
@@ -339,6 +345,8 @@
MediaCodec(const sp<ALooper> &looper, pid_t pid);
+ static sp<CodecBase> GetCodecBase(const AString &name, bool nameIsType = false);
+
static status_t PostAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response);
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/MediaSource.h b/include/media/stagefright/MediaSource.h
index a653db9..ed0d578 100644
--- a/include/media/stagefright/MediaSource.h
+++ b/include/media/stagefright/MediaSource.h
@@ -99,8 +99,9 @@
};
// Causes this source to suspend pulling data from its upstream source
- // until a subsequent read-with-seek. Currently only supported by
- // OMXCodec.
+ // until a subsequent read-with-seek. This is currently not supported
+ // as such by any source. E.g. MediaCodecSource does not susped its
+ // upstream source, and instead discard upstream data while paused.
virtual status_t pause() {
return ERROR_UNSUPPORTED;
}
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
deleted file mode 100644
index 7fabcb3..0000000
--- a/include/media/stagefright/OMXCodec.h
+++ /dev/null
@@ -1,410 +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 OMX_CODEC_H_
-
-#define OMX_CODEC_H_
-
-#include <android/native_window.h>
-#include <media/IOMX.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaSource.h>
-#include <utils/threads.h>
-
-#include <OMX_Audio.h>
-
-namespace android {
-
-struct MediaCodecInfo;
-class MemoryDealer;
-struct OMXCodecObserver;
-struct CodecProfileLevel;
-class SkipCutBuffer;
-
-struct OMXCodec : public MediaSource,
- public MediaBufferObserver {
- enum CreationFlags {
- kPreferSoftwareCodecs = 1,
- kIgnoreCodecSpecificData = 2,
-
- // The client wants to access the output buffer's video
- // data for example for thumbnail extraction.
- kClientNeedsFramebuffer = 4,
-
- // Request for software or hardware codecs. If request
- // can not be fullfilled, Create() returns NULL.
- kSoftwareCodecsOnly = 8,
- kHardwareCodecsOnly = 16,
-
- // Store meta data in video buffers
- kStoreMetaDataInVideoBuffers = 32,
-
- // Only submit one input buffer at one time.
- kOnlySubmitOneInputBufferAtOneTime = 64,
-
- // Enable GRALLOC_USAGE_PROTECTED for output buffers from native window
- kEnableGrallocUsageProtected = 128,
-
- // Secure decoding mode
- kUseSecureInputBuffers = 256,
- };
- static sp<MediaSource> Create(
- const sp<IOMX> &omx,
- const sp<MetaData> &meta, bool createEncoder,
- const sp<MediaSource> &source,
- const char *matchComponentName = NULL,
- uint32_t flags = 0,
- const sp<ANativeWindow> &nativeWindow = NULL);
-
- static void setComponentRole(
- const sp<IOMX> &omx, IOMX::node_id node, bool isEncoder,
- const char *mime);
-
- virtual status_t start(MetaData *params = NULL);
- virtual status_t stop();
-
- virtual sp<MetaData> getFormat();
-
- virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
-
- virtual status_t pause();
-
- // from MediaBufferObserver
- virtual void signalBufferReturned(MediaBuffer *buffer);
-
- enum Quirks {
- kNeedsFlushBeforeDisable = 1,
- kWantsNALFragments = 2,
- kRequiresLoadedToIdleAfterAllocation = 4,
- kRequiresAllocateBufferOnInputPorts = 8,
- kRequiresFlushCompleteEmulation = 16,
- kRequiresAllocateBufferOnOutputPorts = 32,
- kRequiresFlushBeforeShutdown = 64,
- kDefersOutputBufferAllocation = 128,
- kDecoderLiesAboutNumberOfChannels = 256,
- kInputBufferSizesAreBogus = 512,
- kSupportsMultipleFramesPerInputBuffer = 1024,
- kRequiresLargerEncoderOutputBuffer = 2048,
- kOutputBuffersAreUnreadable = 4096,
- };
-
- struct CodecNameAndQuirks {
- String8 mName;
- uint32_t mQuirks;
- };
-
- // for use by ACodec
- static void findMatchingCodecs(
- const char *mime,
- bool createEncoder, const char *matchComponentName,
- uint32_t flags,
- Vector<CodecNameAndQuirks> *matchingCodecNamesAndQuirks);
-
- static uint32_t getComponentQuirks(
- const sp<MediaCodecInfo> &list);
-
- static bool findCodecQuirks(const char *componentName, uint32_t *quirks);
-
-protected:
- virtual ~OMXCodec();
-
-private:
-
- // Make sure mLock is accessible to OMXCodecObserver
- friend class OMXCodecObserver;
-
- // Call this with mLock hold
- void on_message(const omx_message &msg);
-
- enum State {
- DEAD,
- LOADED,
- LOADED_TO_IDLE,
- IDLE_TO_EXECUTING,
- EXECUTING,
- EXECUTING_TO_IDLE,
- IDLE_TO_LOADED,
- RECONFIGURING,
- ERROR
- };
-
- enum {
- kPortIndexInput = 0,
- kPortIndexOutput = 1
- };
-
- enum PortStatus {
- ENABLED,
- DISABLING,
- DISABLED,
- ENABLING,
- SHUTTING_DOWN,
- };
-
- enum BufferStatus {
- OWNED_BY_US,
- OWNED_BY_COMPONENT,
- OWNED_BY_NATIVE_WINDOW,
- OWNED_BY_CLIENT,
- };
-
- struct BufferInfo {
- IOMX::buffer_id mBuffer;
- BufferStatus mStatus;
- sp<IMemory> mMem;
- size_t mSize;
- void *mData;
- MediaBuffer *mMediaBuffer;
- };
-
- struct CodecSpecificData {
- size_t mSize;
- uint8_t mData[1];
- };
-
- sp<IOMX> mOMX;
- bool mOMXLivesLocally;
- IOMX::node_id mNode;
- uint32_t mQuirks;
-
- // Flags specified in the creation of the codec.
- uint32_t mFlags;
-
- bool mIsEncoder;
- bool mIsVideo;
- char *mMIME;
- char *mComponentName;
- sp<MetaData> mOutputFormat;
- sp<MediaSource> mSource;
- Vector<CodecSpecificData *> mCodecSpecificData;
- size_t mCodecSpecificDataIndex;
-
- sp<MemoryDealer> mDealer[2];
-
- State mState;
- Vector<BufferInfo> mPortBuffers[2];
- PortStatus mPortStatus[2];
- bool mInitialBufferSubmit;
- bool mSignalledEOS;
- status_t mFinalStatus;
- bool mNoMoreOutputData;
- bool mOutputPortSettingsHaveChanged;
- int64_t mSeekTimeUs;
- ReadOptions::SeekMode mSeekMode;
- int64_t mTargetTimeUs;
- bool mOutputPortSettingsChangedPending;
- sp<SkipCutBuffer> mSkipCutBuffer;
-
- MediaBuffer *mLeftOverBuffer;
-
- Mutex mLock;
- Condition mAsyncCompletion;
-
- bool mPaused;
-
- sp<ANativeWindow> mNativeWindow;
-
- // The index in each of the mPortBuffers arrays of the buffer that will be
- // submitted to OMX next. This only applies when using buffers from a
- // native window.
- size_t mNextNativeBufferIndex[2];
-
- // A list of indices into mPortStatus[kPortIndexOutput] filled with data.
- List<size_t> mFilledBuffers;
- Condition mBufferFilled;
-
- // Used to record the decoding time for an output picture from
- // a video encoder.
- List<int64_t> mDecodingTimeList;
-
- OMXCodec(const sp<IOMX> &omx, IOMX::node_id node,
- uint32_t quirks, uint32_t flags,
- bool isEncoder, const char *mime, const char *componentName,
- const sp<MediaSource> &source,
- const sp<ANativeWindow> &nativeWindow);
-
- void addCodecSpecificData(const void *data, size_t size);
- void clearCodecSpecificData();
-
- void setComponentRole();
-
- void setAMRFormat(bool isWAMR, int32_t bitRate);
-
- status_t setAACFormat(
- int32_t numChannels, int32_t sampleRate, int32_t bitRate,
- int32_t aacProfile, bool isADTS);
-
- status_t setAC3Format(int32_t numChannels, int32_t sampleRate);
-
- void setG711Format(int32_t sampleRate, int32_t numChannels);
-
- status_t setVideoPortFormatType(
- OMX_U32 portIndex,
- OMX_VIDEO_CODINGTYPE compressionFormat,
- OMX_COLOR_FORMATTYPE colorFormat);
-
- void setVideoInputFormat(
- const char *mime, const sp<MetaData>& meta);
-
- status_t setupBitRate(int32_t bitRate);
- status_t setupErrorCorrectionParameters();
- status_t setupH263EncoderParameters(const sp<MetaData>& meta);
- status_t setupMPEG4EncoderParameters(const sp<MetaData>& meta);
- status_t setupAVCEncoderParameters(const sp<MetaData>& meta);
- status_t findTargetColorFormat(
- const sp<MetaData>& meta, OMX_COLOR_FORMATTYPE *colorFormat);
-
- status_t isColorFormatSupported(
- OMX_COLOR_FORMATTYPE colorFormat, int portIndex);
-
- // If profile/level is set in the meta data, its value in the meta
- // data will be used; otherwise, the default value will be used.
- status_t getVideoProfileLevel(const sp<MetaData>& meta,
- const CodecProfileLevel& defaultProfileLevel,
- CodecProfileLevel& profileLevel);
-
- status_t setVideoOutputFormat(
- const char *mime, const sp<MetaData>& meta);
-
- void setImageOutputFormat(
- OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height);
-
- void setJPEGInputFormat(
- OMX_U32 width, OMX_U32 height, OMX_U32 compressedSize);
-
- void setMinBufferSize(OMX_U32 portIndex, OMX_U32 size);
-
- void setRawAudioFormat(
- OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels);
-
- status_t allocateBuffers();
- status_t allocateBuffersOnPort(OMX_U32 portIndex);
- status_t allocateOutputBuffersFromNativeWindow();
-
- status_t queueBufferToNativeWindow(BufferInfo *info);
- status_t cancelBufferToNativeWindow(BufferInfo *info);
- BufferInfo* dequeueBufferFromNativeWindow();
-
- status_t freeBuffersOnPort(
- OMX_U32 portIndex, bool onlyThoseWeOwn = false);
-
- status_t freeBuffer(OMX_U32 portIndex, size_t bufIndex);
-
- bool drainInputBuffer(IOMX::buffer_id buffer);
- void fillOutputBuffer(IOMX::buffer_id buffer);
- bool drainInputBuffer(BufferInfo *info);
- void fillOutputBuffer(BufferInfo *info);
-
- void drainInputBuffers();
- void fillOutputBuffers();
-
- bool drainAnyInputBuffer();
- BufferInfo *findInputBufferByDataPointer(void *ptr);
- BufferInfo *findEmptyInputBuffer();
-
- // Returns true iff a flush was initiated and a completion event is
- // upcoming, false otherwise (A flush was not necessary as we own all
- // the buffers on that port).
- // This method will ONLY ever return false for a component with quirk
- // "kRequiresFlushCompleteEmulation".
- bool flushPortAsync(OMX_U32 portIndex);
-
- void disablePortAsync(OMX_U32 portIndex);
- status_t enablePortAsync(OMX_U32 portIndex);
-
- static size_t countBuffersWeOwn(const Vector<BufferInfo> &buffers);
- static bool isIntermediateState(State state);
-
- void onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
- void onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data);
- void onStateChange(OMX_STATETYPE newState);
- void onPortSettingsChanged(OMX_U32 portIndex);
-
- void setState(State newState);
-
- status_t init();
- void initOutputFormat(const sp<MetaData> &inputFormat);
- status_t initNativeWindow();
-
- void initNativeWindowCrop();
-
- void dumpPortStatus(OMX_U32 portIndex);
-
- status_t configureCodec(const sp<MetaData> &meta);
-
- status_t waitForBufferFilled_l();
-
- int64_t getDecodingTimeUs();
-
- status_t parseHEVCCodecSpecificData(
- const void *data, size_t size,
- unsigned *profile, unsigned *level);
- status_t parseAVCCodecSpecificData(
- const void *data, size_t size,
- unsigned *profile, unsigned *level);
-
- status_t stopOmxComponent_l();
-
- OMXCodec(const OMXCodec &);
- OMXCodec &operator=(const OMXCodec &);
-};
-
-struct CodecCapabilities {
- enum {
- kFlagSupportsAdaptivePlayback = 1 << 0,
- };
-
- String8 mComponentName;
- Vector<CodecProfileLevel> mProfileLevels;
- Vector<OMX_U32> mColorFormats;
- uint32_t mFlags;
-};
-
-// Return a vector of componentNames with supported profile/level pairs
-// supporting the given mime type, if queryDecoders==true, returns components
-// that decode content of the given type, otherwise returns components
-// that encode content of the given type.
-// profile and level indications only make sense for h.263, mpeg4 and avc
-// video.
-// If hwCodecOnly==true, only returns hardware-based components, software and
-// hardware otherwise.
-// The profile/level values correspond to
-// OMX_VIDEO_H263PROFILETYPE, OMX_VIDEO_MPEG4PROFILETYPE,
-// OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263LEVELTYPE, OMX_VIDEO_MPEG4LEVELTYPE
-// and OMX_VIDEO_AVCLEVELTYPE respectively.
-
-status_t QueryCodecs(
- const sp<IOMX> &omx,
- const char *mimeType, bool queryDecoders, bool hwCodecOnly,
- Vector<CodecCapabilities> *results);
-
-status_t QueryCodecs(
- const sp<IOMX> &omx,
- const char *mimeType, bool queryDecoders,
- Vector<CodecCapabilities> *results);
-
-status_t QueryCodec(
- const sp<IOMX> &omx,
- const char *componentName, const char *mime,
- 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..07e1f79
--- /dev/null
+++ b/include/media/stagefright/SimpleDecodingSource.h
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ // if |desiredCodec| is given, use this specific codec.
+ static sp<SimpleDecodingSource> Create(
+ const sp<MediaSource> &source, uint32_t flags = 0,
+ const sp<ANativeWindow> &nativeWindow = NULL,
+ const char *desiredCodec = 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/img_utils/src/Android.mk b/media/img_utils/src/Android.mk
index 4074849..c1f64ca 100644
--- a/media/img_utils/src/Android.mk
+++ b/media/img_utils/src/Android.mk
@@ -34,6 +34,7 @@
StripSource.cpp \
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libexpat \
libutils \
libcutils \
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 8d3fa7b..06abd8d 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -26,8 +26,6 @@
#include <media/stagefright/foundation/AMessage.h>
#include <binder/Parcel.h>
-#include <media/stagefright/OMXCodec.h>
-
namespace android {
void MediaCodecInfo::Capabilities::getSupportedProfileLevels(
@@ -101,6 +99,21 @@
return OK;
}
+void MediaCodecInfo::CapabilitiesBuilder::addProfileLevel(uint32_t profile, uint32_t level) {
+ ProfileLevel profileLevel;
+ profileLevel.mProfile = profile;
+ profileLevel.mLevel = level;
+ mProfileLevels.push_back(profileLevel);
+}
+
+void MediaCodecInfo::CapabilitiesBuilder::addColorFormat(uint32_t format) {
+ mColorFormats.push(format);
+}
+
+void MediaCodecInfo::CapabilitiesBuilder::addFlags(uint32_t flags) {
+ mFlags |= flags;
+}
+
bool MediaCodecInfo::isEncoder() const {
return mIsEncoder;
}
@@ -225,26 +238,15 @@
}
}
-status_t MediaCodecInfo::initializeCapabilities(const CodecCapabilities &caps) {
- mCurrentCaps->mProfileLevels.clear();
+status_t MediaCodecInfo::initializeCapabilities(const sp<Capabilities> &caps) {
+ // TRICKY: copy data to mCurrentCaps as it is a reference to
+ // an element of the capabilites map.
mCurrentCaps->mColorFormats.clear();
-
- for (size_t i = 0; i < caps.mProfileLevels.size(); ++i) {
- const CodecProfileLevel &src = caps.mProfileLevels.itemAt(i);
-
- ProfileLevel profileLevel;
- profileLevel.mProfile = src.mProfile;
- profileLevel.mLevel = src.mLevel;
- mCurrentCaps->mProfileLevels.push_back(profileLevel);
- }
-
- for (size_t i = 0; i < caps.mColorFormats.size(); ++i) {
- mCurrentCaps->mColorFormats.push_back(caps.mColorFormats.itemAt(i));
- }
-
- mCurrentCaps->mFlags = caps.mFlags;
- mCurrentCaps->mDetails = new AMessage;
-
+ mCurrentCaps->mColorFormats.appendVector(caps->mColorFormats);
+ mCurrentCaps->mProfileLevels.clear();
+ mCurrentCaps->mProfileLevels.appendVector(caps->mProfileLevels);
+ mCurrentCaps->mFlags = caps->mFlags;
+ mCurrentCaps->mDetails = caps->mDetails;
return OK;
}
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 4d1b587..81f2af3 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -18,7 +18,6 @@
MetadataRetrieverClient.cpp \
RemoteDisplay.cpp \
SharedLibrary.cpp \
- StagefrightPlayer.cpp \
StagefrightRecorder.cpp \
TestPlayerStub.cpp \
@@ -45,6 +44,7 @@
LOCAL_STATIC_LIBRARIES := \
libstagefright_nuplayer \
libstagefright_rtsp \
+ libstagefright_timedtext \
LOCAL_C_INCLUDES := \
$(TOP)/frameworks/av/media/libstagefright/include \
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp
index d5d12f7..605c710 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.cpp
+++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp
@@ -31,7 +31,6 @@
#include "MediaPlayerFactory.h"
#include "TestPlayerStub.h"
-#include "StagefrightPlayer.h"
#include "nuplayer/NuPlayerDriver.h"
namespace android {
@@ -64,12 +63,6 @@
}
static player_type getDefaultPlayerType() {
- char value[PROPERTY_VALUE_MAX];
- if (property_get("media.stagefright.use-awesome", value, NULL)
- && (!strcmp("1", value) || !strcasecmp("true", value))) {
- return STAGEFRIGHT_PLAYER;
- }
-
return NU_PLAYER;
}
@@ -176,63 +169,6 @@
* *
*****************************************************************************/
-class StagefrightPlayerFactory :
- public MediaPlayerFactory::IFactory {
- public:
- virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
- int fd,
- int64_t offset,
- int64_t length,
- float /*curScore*/) {
- if (legacyDrm()) {
- sp<DataSource> source = new FileSource(dup(fd), offset, length);
- String8 mimeType;
- float confidence;
- if (SniffWVM(source, &mimeType, &confidence, NULL /* format */)) {
- return 1.0;
- }
- }
-
- if (getDefaultPlayerType() == STAGEFRIGHT_PLAYER) {
- char buf[20];
- lseek(fd, offset, SEEK_SET);
- read(fd, buf, sizeof(buf));
- lseek(fd, offset, SEEK_SET);
-
- uint32_t ident = *((uint32_t*)buf);
-
- // Ogg vorbis?
- if (ident == 0x5367674f) // 'OggS'
- return 1.0;
- }
-
- return 0.0;
- }
-
- virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
- const char* url,
- float /*curScore*/) {
- if (legacyDrm() && !strncasecmp("widevine://", url, 11)) {
- return 1.0;
- }
- return 0.0;
- }
-
- virtual sp<MediaPlayerBase> createPlayer(pid_t /* pid */) {
- ALOGV(" create StagefrightPlayer");
- return new StagefrightPlayer();
- }
- private:
- bool legacyDrm() {
- char value[PROPERTY_VALUE_MAX];
- if (property_get("persist.sys.media.legacy-drm", value, NULL)
- && (!strcmp("1", value) || !strcasecmp("true", value))) {
- return true;
- }
- return false;
- }
-};
-
class NuPlayerFactory : public MediaPlayerFactory::IFactory {
public:
virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
@@ -310,7 +246,6 @@
if (sInitComplete)
return;
- registerFactory_l(new StagefrightPlayerFactory(), STAGEFRIGHT_PLAYER);
registerFactory_l(new NuPlayerFactory(), NU_PLAYER);
registerFactory_l(new TestPlayerFactory(), TEST_PLAYER);
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 8db07ca..7dd8c78 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -57,7 +57,7 @@
#include <media/MemoryLeakTrackUtil.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/Utils.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooperRoster.h>
#include <mediautils/BatteryNotifier.h>
@@ -73,7 +73,6 @@
#include "MediaPlayerFactory.h"
#include "TestPlayerStub.h"
-#include "StagefrightPlayer.h"
#include "nuplayer/NuPlayerDriver.h"
#include <OMX.h>
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
deleted file mode 100644
index 8fc4b29..0000000
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ /dev/null
@@ -1,230 +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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "StagefrightPlayer"
-#include <utils/Log.h>
-
-#include "StagefrightPlayer.h"
-
-#include "AwesomePlayer.h"
-
-#include <media/Metadata.h>
-#include <media/stagefright/MediaExtractor.h>
-
-namespace android {
-
-StagefrightPlayer::StagefrightPlayer()
- : mPlayer(new AwesomePlayer) {
- ALOGV("StagefrightPlayer");
-
- mPlayer->setListener(this);
-}
-
-StagefrightPlayer::~StagefrightPlayer() {
- ALOGV("~StagefrightPlayer");
- reset();
-
- delete mPlayer;
- mPlayer = NULL;
-}
-
-status_t StagefrightPlayer::initCheck() {
- ALOGV("initCheck");
- return OK;
-}
-
-status_t StagefrightPlayer::setUID(uid_t uid) {
- mPlayer->setUID(uid);
-
- return OK;
-}
-
-status_t StagefrightPlayer::setDataSource(
- const sp<IMediaHTTPService> &httpService,
- const char *url,
- const KeyedVector<String8, String8> *headers) {
- return mPlayer->setDataSource(httpService, url, headers);
-}
-
-// Warning: The filedescriptor passed into this method will only be valid until
-// the method returns, if you want to keep it, dup it!
-status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
- ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
- return mPlayer->setDataSource(fd, offset, length);
-}
-
-status_t StagefrightPlayer::setDataSource(const sp<IStreamSource> &source) {
- return mPlayer->setDataSource(source);
-}
-
-status_t StagefrightPlayer::setVideoSurfaceTexture(
- const sp<IGraphicBufferProducer> &bufferProducer) {
- ALOGV("setVideoSurfaceTexture");
-
- return mPlayer->setSurfaceTexture(bufferProducer);
-}
-
-status_t StagefrightPlayer::prepare() {
- return mPlayer->prepare();
-}
-
-status_t StagefrightPlayer::prepareAsync() {
- return mPlayer->prepareAsync();
-}
-
-status_t StagefrightPlayer::start() {
- ALOGV("start");
-
- return mPlayer->play();
-}
-
-status_t StagefrightPlayer::stop() {
- ALOGV("stop");
-
- return pause(); // what's the difference?
-}
-
-status_t StagefrightPlayer::pause() {
- ALOGV("pause");
-
- return mPlayer->pause();
-}
-
-bool StagefrightPlayer::isPlaying() {
- ALOGV("isPlaying");
- return mPlayer->isPlaying();
-}
-
-status_t StagefrightPlayer::seekTo(int msec) {
- ALOGV("seekTo %.2f secs", msec / 1E3);
-
- status_t err = mPlayer->seekTo((int64_t)msec * 1000);
-
- return err;
-}
-
-status_t StagefrightPlayer::getCurrentPosition(int *msec) {
- ALOGV("getCurrentPosition");
-
- int64_t positionUs;
- status_t err = mPlayer->getPosition(&positionUs);
-
- if (err != OK) {
- return err;
- }
-
- *msec = (positionUs + 500) / 1000;
-
- return OK;
-}
-
-status_t StagefrightPlayer::getDuration(int *msec) {
- ALOGV("getDuration");
-
- int64_t durationUs;
- status_t err = mPlayer->getDuration(&durationUs);
-
- if (err != OK) {
- *msec = 0;
- return OK;
- }
-
- *msec = (durationUs + 500) / 1000;
-
- return OK;
-}
-
-status_t StagefrightPlayer::reset() {
- ALOGV("reset");
-
- mPlayer->reset();
-
- return OK;
-}
-
-status_t StagefrightPlayer::setLooping(int loop) {
- ALOGV("setLooping");
-
- return mPlayer->setLooping(loop);
-}
-
-player_type StagefrightPlayer::playerType() {
- ALOGV("playerType");
- return STAGEFRIGHT_PLAYER;
-}
-
-status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
- ALOGV("invoke()");
- return mPlayer->invoke(request, reply);
-}
-
-void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
- MediaPlayerInterface::setAudioSink(audioSink);
-
- mPlayer->setAudioSink(audioSink);
-}
-
-status_t StagefrightPlayer::setParameter(int key, const Parcel &request) {
- ALOGV("setParameter(key=%d)", key);
- return mPlayer->setParameter(key, request);
-}
-
-status_t StagefrightPlayer::getParameter(int key, Parcel *reply) {
- ALOGV("getParameter");
- return mPlayer->getParameter(key, reply);
-}
-
-status_t StagefrightPlayer::setPlaybackSettings(const AudioPlaybackRate &rate) {
- return mPlayer->setPlaybackSettings(rate);
-}
-
-status_t StagefrightPlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
- return mPlayer->getPlaybackSettings(rate);
-}
-
-status_t StagefrightPlayer::getMetadata(
- const media::Metadata::Filter& /* ids */, Parcel *records) {
- using media::Metadata;
-
- uint32_t flags = mPlayer->flags();
-
- Metadata metadata(records);
-
- metadata.appendBool(
- Metadata::kPauseAvailable,
- flags & MediaExtractor::CAN_PAUSE);
-
- metadata.appendBool(
- Metadata::kSeekBackwardAvailable,
- flags & MediaExtractor::CAN_SEEK_BACKWARD);
-
- metadata.appendBool(
- Metadata::kSeekForwardAvailable,
- flags & MediaExtractor::CAN_SEEK_FORWARD);
-
- metadata.appendBool(
- Metadata::kSeekAvailable,
- flags & MediaExtractor::CAN_SEEK);
-
- return OK;
-}
-
-status_t StagefrightPlayer::dump(int fd, const Vector<String16> &args) const {
- return mPlayer->dump(fd, args);
-}
-
-} // namespace android
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
deleted file mode 100644
index 96013df..0000000
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-**
-** Copyright 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 ANDROID_STAGEFRIGHTPLAYER_H
-#define ANDROID_STAGEFRIGHTPLAYER_H
-
-#include <media/MediaPlayerInterface.h>
-
-namespace android {
-
-struct AwesomePlayer;
-
-class StagefrightPlayer : public MediaPlayerInterface {
-public:
- StagefrightPlayer();
- virtual ~StagefrightPlayer();
-
- virtual status_t initCheck();
-
- virtual status_t setUID(uid_t uid);
-
- virtual status_t setDataSource(
- const sp<IMediaHTTPService> &httpService,
- const char *url,
- const KeyedVector<String8, String8> *headers);
-
- virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
-
- virtual status_t setDataSource(const sp<IStreamSource> &source);
-
- virtual status_t setVideoSurfaceTexture(
- const sp<IGraphicBufferProducer> &bufferProducer);
- virtual status_t prepare();
- virtual status_t prepareAsync();
- virtual status_t start();
- virtual status_t stop();
- virtual status_t pause();
- virtual bool isPlaying();
- virtual status_t seekTo(int msec);
- virtual status_t getCurrentPosition(int *msec);
- virtual status_t getDuration(int *msec);
- virtual status_t reset();
- virtual status_t setLooping(int loop);
- virtual player_type playerType();
- virtual status_t invoke(const Parcel &request, Parcel *reply);
- virtual void setAudioSink(const sp<AudioSink> &audioSink);
- virtual status_t setParameter(int key, const Parcel &request);
- virtual status_t getParameter(int key, Parcel *reply);
- virtual status_t setPlaybackSettings(const AudioPlaybackRate &rate);
- virtual status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
-
- virtual status_t getMetadata(
- const media::Metadata::Filter& ids, Parcel *records);
-
- virtual status_t dump(int fd, const Vector<String16> &args) const;
-
-private:
- AwesomePlayer *mPlayer;
-
- StagefrightPlayer(const StagefrightPlayer &);
- StagefrightPlayer &operator=(const StagefrightPlayer &);
-};
-
-} // namespace android
-
-#endif // ANDROID_STAGEFRIGHTPLAYER_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/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 45da218..85a566e 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -213,6 +213,9 @@
sp<MediaSource> track = extractor->getTrack(i);
sp<MetaData> meta = extractor->getTrackMetaData(i);
+ if (meta == NULL) {
+ continue;
+ }
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index fad06b8..10734e4 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>
@@ -56,6 +55,10 @@
namespace android {
+enum {
+ kMaxIndicesToCheck = 32, // used when enumerating supported formats and profiles
+};
+
// OMX errors are directly mapped into status_t range if
// there is no corresponding MediaError status code.
// Use the statusFromOMXError(int32_t omxError) function.
@@ -824,8 +827,8 @@
uint32_t requiresAllocateBufferBit =
(portIndex == kPortIndexInput)
- ? OMXCodec::kRequiresAllocateBufferOnInputPorts
- : OMXCodec::kRequiresAllocateBufferOnOutputPorts;
+ ? kRequiresAllocateBufferOnInputPorts
+ : kRequiresAllocateBufferOnOutputPorts;
if ((portIndex == kPortIndexInput && (mFlags & kFlagIsSecure))
|| (portIndex == kPortIndexOutput && usingMetadataOnEncoderOutput())) {
@@ -1525,6 +1528,21 @@
status_t ACodec::setComponentRole(
bool isEncoder, const char *mime) {
+ const char *role = getComponentRole(isEncoder, mime);
+ if (role == NULL) {
+ return BAD_VALUE;
+ }
+ status_t err = setComponentRole(mOMX, mNode, role);
+ if (err != OK) {
+ ALOGW("[%s] Failed to set standard component role '%s'.",
+ mComponentName.c_str(), role);
+ }
+ return err;
+}
+
+//static
+const char *ACodec::getComponentRole(
+ bool isEncoder, const char *mime) {
struct MimeToRole {
const char *mime;
const char *decoderRole;
@@ -1589,35 +1607,27 @@
}
if (i == kNumMimeToRole) {
- return ERROR_UNSUPPORTED;
+ return NULL;
}
- const char *role =
- isEncoder ? kMimeToRole[i].encoderRole
+ return isEncoder ? kMimeToRole[i].encoderRole
: kMimeToRole[i].decoderRole;
+}
- if (role != NULL) {
- OMX_PARAM_COMPONENTROLETYPE roleParams;
- InitOMXParams(&roleParams);
+//static
+status_t ACodec::setComponentRole(
+ const sp<IOMX> &omx, IOMX::node_id node, const char *role) {
+ OMX_PARAM_COMPONENTROLETYPE roleParams;
+ InitOMXParams(&roleParams);
- strncpy((char *)roleParams.cRole,
- role, OMX_MAX_STRINGNAME_SIZE - 1);
+ strncpy((char *)roleParams.cRole,
+ role, OMX_MAX_STRINGNAME_SIZE - 1);
- roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+ roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
- status_t err = mOMX->setParameter(
- mNode, OMX_IndexParamStandardComponentRole,
- &roleParams, sizeof(roleParams));
-
- if (err != OK) {
- ALOGW("[%s] Failed to set standard component role '%s'.",
- mComponentName.c_str(), role);
-
- return err;
- }
- }
-
- return OK;
+ return omx->setParameter(
+ node, OMX_IndexParamStandardComponentRole,
+ &roleParams, sizeof(roleParams));
}
status_t ACodec::configureCodec(
@@ -2272,9 +2282,8 @@
InitOMXParams(&format);
format.nPortIndex = portIndex;
- for (OMX_U32 index = 0;; ++index) {
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
format.nIndex = index;
-
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamAudioPortFormat,
&format, sizeof(format));
@@ -2286,6 +2295,13 @@
if (format.eEncoding == desiredFormat) {
break;
}
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking formats after %u: %s(%x)",
+ mComponentName.c_str(), index,
+ asString(format.eEncoding), format.eEncoding);
+ return ERROR_UNSUPPORTED;
+ }
}
return mOMX->setParameter(
@@ -2705,8 +2721,7 @@
format.nIndex = 0;
bool found = false;
- OMX_U32 index = 0;
- for (;;) {
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
format.nIndex = index;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoPortFormat,
@@ -2751,7 +2766,12 @@
break;
}
- ++index;
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking formats after %u: %s(%x)/%s(%x)",
+ mComponentName.c_str(), index,
+ asString(format.eCompressionFormat), format.eCompressionFormat,
+ asString(format.eColorFormat), format.eColorFormat);
+ }
}
if (!found) {
@@ -3624,7 +3644,8 @@
InitOMXParams(¶ms);
params.nPortIndex = kPortIndexOutput;
- for (params.nProfileIndex = 0;; ++params.nProfileIndex) {
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
+ params.nProfileIndex = index;
status_t err = mOMX->getParameter(
mNode,
OMX_IndexParamVideoProfileLevelQuerySupported,
@@ -3641,7 +3662,14 @@
if (profile == supportedProfile && level <= supportedLevel) {
return OK;
}
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking profiles after %u: %x/%x",
+ mComponentName.c_str(), index,
+ params.eProfile, params.eLevel);
+ }
}
+ return ERROR_UNSUPPORTED;
}
status_t ACodec::configureBitrate(
@@ -5465,7 +5493,7 @@
mDeathNotifier.clear();
}
- Vector<OMXCodec::CodecNameAndQuirks> matchingCodecs;
+ Vector<AString> matchingCodecs;
AString mime;
@@ -5473,13 +5501,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 +5512,10 @@
encoder = false;
}
- OMXCodec::findMatchingCodecs(
+ MediaCodecList::findMatchingCodecs(
mime.c_str(),
encoder, // createEncoder
- NULL, // matchComponentName
- 0, // flags
+ 0, // flags
&matchingCodecs);
}
@@ -5502,8 +5525,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 +6886,190 @@
}
}
+status_t ACodec::queryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps) {
+ (*caps).clear();
+ const char *role = getComponentRole(isEncoder, mime.c_str());
+ if (role == NULL) {
+ return BAD_VALUE;
+ }
+
+ OMXClient client;
+ status_t err = client.connect();
+ if (err != OK) {
+ return err;
+ }
+
+ sp<IOMX> omx = client.interface();
+ sp<CodecObserver> observer = new CodecObserver;
+ IOMX::node_id node = 0;
+
+ err = omx->allocateNode(name.c_str(), observer, &node);
+ if (err != OK) {
+ client.disconnect();
+ return err;
+ }
+
+ err = setComponentRole(omx, node, role);
+ if (err != OK) {
+ omx->freeNode(node);
+ client.disconnect();
+ return err;
+ }
+
+ sp<MediaCodecInfo::CapabilitiesBuilder> builder = new MediaCodecInfo::CapabilitiesBuilder();
+ bool isVideo = mime.startsWithIgnoreCase("video/");
+
+ if (isVideo) {
+ OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
+ InitOMXParams(¶m);
+ param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
+
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
+ param.nProfileIndex = index;
+ status_t err = omx->getParameter(
+ node, OMX_IndexParamVideoProfileLevelQuerySupported,
+ ¶m, sizeof(param));
+ if (err != OK) {
+ break;
+ }
+ builder->addProfileLevel(param.eProfile, param.eLevel);
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking profiles after %u: %x/%x",
+ name.c_str(), index,
+ param.eProfile, param.eLevel);
+ }
+ }
+
+ // Color format query
+ // return colors in the order reported by the OMX component
+ // prefix "flexible" standard ones with the flexible equivalent
+ OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat;
+ InitOMXParams(&portFormat);
+ portFormat.nPortIndex = isEncoder ? kPortIndexInput : kPortIndexOutput;
+ Vector<uint32_t> supportedColors; // shadow copy to check for duplicates
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
+ portFormat.nIndex = index;
+ status_t err = omx->getParameter(
+ node, OMX_IndexParamVideoPortFormat,
+ &portFormat, sizeof(portFormat));
+ if (err != OK) {
+ break;
+ }
+
+ OMX_U32 flexibleEquivalent;
+ if (isFlexibleColorFormat(
+ omx, node, portFormat.eColorFormat, false /* usingNativeWindow */,
+ &flexibleEquivalent)) {
+ bool marked = false;
+ for (size_t i = 0; i < supportedColors.size(); ++i) {
+ if (supportedColors[i] == flexibleEquivalent) {
+ marked = true;
+ break;
+ }
+ }
+ if (!marked) {
+ supportedColors.push(flexibleEquivalent);
+ builder->addColorFormat(flexibleEquivalent);
+ }
+ }
+ supportedColors.push(portFormat.eColorFormat);
+ builder->addColorFormat(portFormat.eColorFormat);
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking formats after %u: %s(%x)",
+ name.c_str(), index,
+ asString(portFormat.eColorFormat), portFormat.eColorFormat);
+ }
+ }
+ }
+
+ if (isVideo && !isEncoder) {
+ native_handle_t *sidebandHandle = NULL;
+ if (omx->configureVideoTunnelMode(
+ node, kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) {
+ // tunneled playback includes adaptive playback
+ builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
+ | MediaCodecInfo::Capabilities::kFlagSupportsTunneledPlayback);
+ } else if (omx->storeMetaDataInBuffers(
+ node, kPortIndexOutput, OMX_TRUE) == OK ||
+ omx->prepareForAdaptivePlayback(
+ node, kPortIndexOutput, OMX_TRUE,
+ 1280 /* width */, 720 /* height */) == OK) {
+ builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback);
+ }
+ }
+
+ *caps = builder;
+ omx->freeNode(node);
+ client.disconnect();
+ return OK;
+}
+
+// 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 72bae5c..e361749 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -10,11 +10,9 @@
AMRWriter.cpp \
AudioPlayer.cpp \
AudioSource.cpp \
- AwesomePlayer.cpp \
CallbackDataSource.cpp \
CameraSource.cpp \
CameraSourceTimeLapse.cpp \
- ClockEstimator.cpp \
CodecBase.cpp \
DataSource.cpp \
DataURISource.cpp \
@@ -48,19 +46,17 @@
NuCachedSource2.cpp \
NuMediaExtractor.cpp \
OMXClient.cpp \
- OMXCodec.cpp \
OggExtractor.cpp \
ProcessInfo.cpp \
SampleIterator.cpp \
SampleTable.cpp \
+ SimpleDecodingSource.cpp \
SkipCutBuffer.cpp \
StagefrightMediaScanner.cpp \
StagefrightMetadataRetriever.cpp \
SurfaceMediaSource.cpp \
SurfaceUtils.cpp \
ThrottledSource.cpp \
- TimeSource.cpp \
- TimedEventQueue.cpp \
Utils.cpp \
VBRISeeker.cpp \
VideoFrameScheduler.cpp \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index dd9d393..68cac74 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -33,14 +33,11 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
-#include "include/AwesomePlayer.h"
-
namespace android {
AudioPlayer::AudioPlayer(
const sp<MediaPlayerBase::AudioSink> &audioSink,
- uint32_t flags,
- AwesomePlayer *observer)
+ uint32_t flags)
: mInputBuffer(NULL),
mSampleRate(0),
mLatencyUs(0),
@@ -58,8 +55,6 @@
mFirstBufferResult(OK),
mFirstBuffer(NULL),
mAudioSink(audioSink),
- mObserver(observer),
- mPinnedTimeUs(-1ll),
mPlaying(false),
mStartPosUs(0),
mCreateFlags(flags) {
@@ -256,7 +251,6 @@
mStarted = true;
mPlaying = true;
- mPinnedTimeUs = -1ll;
return OK;
}
@@ -279,8 +273,6 @@
} else {
mAudioTrack->pause();
}
-
- mPinnedTimeUs = ALooper::GetNowUs();
}
mPlaying = false;
@@ -386,11 +378,6 @@
static_cast<AudioPlayer *>(user)->AudioCallback(event, info);
}
-bool AudioPlayer::isSeeking() {
- Mutex::Autolock autoLock(mLock);
- return mSeeking;
-}
-
bool AudioPlayer::reachedEOS(status_t *finalStatus) {
*finalStatus = OK;
@@ -399,15 +386,6 @@
return mReachedEOS;
}
-void AudioPlayer::notifyAudioEOS() {
- ALOGV("AudioPlayer@0x%p notifyAudioEOS", this);
-
- if (mObserver != NULL) {
- mObserver->postAudioEOS(0);
- ALOGV("Notified observer of EOS!");
- }
-}
-
status_t AudioPlayer::setPlaybackRate(const AudioPlaybackRate &rate) {
if (mAudioSink.get() != NULL) {
return mAudioSink->setPlaybackRate(rate);
@@ -443,12 +421,10 @@
case MediaPlayerBase::AudioSink::CB_EVENT_STREAM_END:
ALOGV("AudioSinkCallback: stream end");
me->mReachedEOS = true;
- me->notifyAudioEOS();
break;
case MediaPlayerBase::AudioSink::CB_EVENT_TEAR_DOWN:
ALOGV("AudioSinkCallback: Tear down event");
- me->mObserver->postAudioTearDown();
break;
}
@@ -467,31 +443,10 @@
case AudioTrack::EVENT_STREAM_END:
mReachedEOS = true;
- notifyAudioEOS();
break;
}
}
-uint32_t AudioPlayer::getNumFramesPendingPlayout() const {
- uint32_t numFramesPlayedOut;
- status_t err;
-
- if (mAudioSink != NULL) {
- err = mAudioSink->getPosition(&numFramesPlayedOut);
- } else {
- err = mAudioTrack->getPosition(&numFramesPlayedOut);
- }
-
- if (err != OK || mNumFramesPlayed < numFramesPlayedOut) {
- return 0;
- }
-
- // mNumFramesPlayed is the number of frames submitted
- // to the audio sink for playback, but not all of them
- // may have played out by now.
- return mNumFramesPlayed - numFramesPlayedOut;
-}
-
size_t AudioPlayer::fillBuffer(void *data, size_t size) {
if (mNumFramesPlayed == 0) {
ALOGV("AudioCallback");
@@ -501,10 +456,6 @@
return 0;
}
- bool postSeekComplete = false;
- bool postEOS = false;
- int64_t postEOSDelayUs = 0;
-
size_t size_done = 0;
size_t size_remaining = size;
while (size_remaining > 0) {
@@ -532,9 +483,6 @@
}
mSeeking = false;
- if (mObserver) {
- postSeekComplete = true;
- }
}
}
@@ -567,42 +515,6 @@
mAudioTrack->stop();
}
} else {
- if (mObserver) {
- // We don't want to post EOS right away but only
- // after all frames have actually been played out.
-
- // These are the number of frames submitted to the
- // AudioTrack that you haven't heard yet.
- uint32_t numFramesPendingPlayout =
- getNumFramesPendingPlayout();
-
- // These are the number of frames we're going to
- // submit to the AudioTrack by returning from this
- // callback.
- uint32_t numAdditionalFrames = size_done / mFrameSize;
-
- numFramesPendingPlayout += numAdditionalFrames;
-
- int64_t timeToCompletionUs =
- (1000000ll * numFramesPendingPlayout) / mSampleRate;
-
- ALOGV("total number of frames played: %" PRId64 " (%lld us)",
- (mNumFramesPlayed + numAdditionalFrames),
- 1000000ll * (mNumFramesPlayed + numAdditionalFrames)
- / mSampleRate);
-
- ALOGV("%d frames left to play, %" PRId64 " us (%.2f secs)",
- numFramesPendingPlayout,
- timeToCompletionUs, timeToCompletionUs / 1E6);
-
- postEOS = true;
- if (mAudioSink->needsTrailingPadding()) {
- postEOSDelayUs = timeToCompletionUs + mLatencyUs;
- } else {
- postEOSDelayUs = 0;
- }
- }
-
mReachedEOS = true;
}
}
@@ -626,12 +538,6 @@
// might not be able to get the exact seek time requested.
if (refreshSeekTime) {
if (useOffload()) {
- if (postSeekComplete) {
- ALOGV("fillBuffer is going to post SEEK_COMPLETE");
- mObserver->postAudioSeekComplete();
- postSeekComplete = false;
- }
-
mStartPosUs = mPositionTimeMediaUs;
ALOGV("adjust seek time to: %.2f", mStartPosUs/ 1E6);
}
@@ -690,58 +596,11 @@
Mutex::Autolock autoLock(mLock);
mNumFramesPlayed += size_done / mFrameSize;
mNumFramesPlayedSysTimeUs = ALooper::GetNowUs();
-
- if (mReachedEOS) {
- mPinnedTimeUs = mNumFramesPlayedSysTimeUs;
- } else {
- mPinnedTimeUs = -1ll;
- }
- }
-
- if (postEOS) {
- mObserver->postAudioEOS(postEOSDelayUs);
- }
-
- if (postSeekComplete) {
- mObserver->postAudioSeekComplete();
}
return size_done;
}
-int64_t AudioPlayer::getRealTimeUs() {
- Mutex::Autolock autoLock(mLock);
- if (useOffload()) {
- if (mSeeking) {
- return mSeekTimeUs;
- }
- mPositionTimeRealUs = getOutputPlayPositionUs_l();
- return mPositionTimeRealUs;
- }
-
- return getRealTimeUsLocked();
-}
-
-int64_t AudioPlayer::getRealTimeUsLocked() const {
- CHECK(mStarted);
- CHECK_NE(mSampleRate, 0);
- int64_t result = -mLatencyUs + (mNumFramesPlayed * 1000000) / mSampleRate;
-
- // Compensate for large audio buffers, updates of mNumFramesPlayed
- // are less frequent, therefore to get a "smoother" notion of time we
- // compensate using system time.
- int64_t diffUs;
- if (mPinnedTimeUs >= 0ll) {
- diffUs = mPinnedTimeUs;
- } else {
- diffUs = ALooper::GetNowUs();
- }
-
- diffUs -= mNumFramesPlayedSysTimeUs;
-
- return result + diffUs;
-}
-
int64_t AudioPlayer::getOutputPlayPositionUs_l()
{
uint32_t playedSamples = 0;
@@ -770,54 +629,6 @@
return renderedDuration;
}
-int64_t AudioPlayer::getMediaTimeUs() {
- Mutex::Autolock autoLock(mLock);
-
- if (useOffload()) {
- if (mSeeking) {
- return mSeekTimeUs;
- }
- if (mReachedEOS) {
- int64_t durationUs;
- mSource->getFormat()->findInt64(kKeyDuration, &durationUs);
- return durationUs;
- }
- mPositionTimeRealUs = getOutputPlayPositionUs_l();
- ALOGV("getMediaTimeUs getOutputPlayPositionUs_l() mPositionTimeRealUs %" PRId64,
- mPositionTimeRealUs);
- return mPositionTimeRealUs;
- }
-
-
- if (mPositionTimeMediaUs < 0 || mPositionTimeRealUs < 0) {
- // mSeekTimeUs is either seek time while seeking or 0 if playback did not start.
- return mSeekTimeUs;
- }
-
- int64_t realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs;
- if (realTimeOffset < 0) {
- realTimeOffset = 0;
- }
-
- return mPositionTimeMediaUs + realTimeOffset;
-}
-
-bool AudioPlayer::getMediaTimeMapping(
- int64_t *realtime_us, int64_t *mediatime_us) {
- Mutex::Autolock autoLock(mLock);
-
- if (useOffload()) {
- mPositionTimeRealUs = getOutputPlayPositionUs_l();
- *realtime_us = mPositionTimeRealUs;
- *mediatime_us = mPositionTimeRealUs;
- } else {
- *realtime_us = mPositionTimeRealUs;
- *mediatime_us = mPositionTimeMediaUs;
- }
-
- return mPositionTimeRealUs != -1 && mPositionTimeMediaUs != -1;
-}
-
status_t AudioPlayer::seekTo(int64_t time_us) {
Mutex::Autolock autoLock(mLock);
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
deleted file mode 100644
index ba59e00..0000000
--- a/media/libstagefright/AwesomePlayer.cpp
+++ /dev/null
@@ -1,3048 +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.
- */
-
-#undef DEBUG_HDCP
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "AwesomePlayer"
-#define ATRACE_TAG ATRACE_TAG_VIDEO
-
-#include <inttypes.h>
-
-#include <utils/Log.h>
-#include <utils/Trace.h>
-
-#include <dlfcn.h>
-
-#include "include/AwesomePlayer.h"
-#include "include/DRMExtractor.h"
-#include "include/SoftwareRenderer.h"
-#include "include/NuCachedSource2.h"
-#include "include/ThrottledSource.h"
-#include "include/MPEG2TSExtractor.h"
-#include "include/WVMExtractor.h"
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <media/IMediaHTTPConnection.h>
-#include <media/IMediaHTTPService.h>
-#include <media/IMediaPlayerService.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/timedtext/TimedTextDriver.h>
-#include <media/stagefright/AudioPlayer.h>
-#include <media/stagefright/ClockEstimator.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaExtractor.h>
-#include <media/stagefright/MediaHTTP.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/OMXCodec.h>
-#include <media/stagefright/Utils.h>
-
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/Surface.h>
-
-#include <media/stagefright/foundation/AMessage.h>
-
-#include <cutils/properties.h>
-
-#define USE_SURFACE_ALLOC 1
-#define FRAME_DROP_FREQ 0
-
-namespace android {
-
-static int64_t kLowWaterMarkUs = 2000000ll; // 2secs
-static int64_t kHighWaterMarkUs = 5000000ll; // 5secs
-static const size_t kLowWaterMarkBytes = 40000;
-static const size_t kHighWaterMarkBytes = 200000;
-
-// maximum time in paused state when offloading audio decompression. When elapsed, the AudioPlayer
-// is destroyed to allow the audio DSP to power down.
-static int64_t kOffloadPauseMaxUs = 10000000ll;
-
-
-struct AwesomeEvent : public TimedEventQueue::Event {
- AwesomeEvent(
- AwesomePlayer *player,
- void (AwesomePlayer::*method)())
- : mPlayer(player),
- mMethod(method) {
- }
-
-protected:
- virtual ~AwesomeEvent() {}
-
- virtual void fire(TimedEventQueue * /* queue */, int64_t /* now_us */) {
- (mPlayer->*mMethod)();
- }
-
-private:
- AwesomePlayer *mPlayer;
- void (AwesomePlayer::*mMethod)();
-
- AwesomeEvent(const AwesomeEvent &);
- AwesomeEvent &operator=(const AwesomeEvent &);
-};
-
-struct AwesomeLocalRenderer : public AwesomeRenderer {
- AwesomeLocalRenderer(
- const sp<ANativeWindow> &nativeWindow, const sp<AMessage> &format)
- : mFormat(format),
- mTarget(new SoftwareRenderer(nativeWindow)) {
- }
-
- virtual void render(MediaBuffer *buffer) {
- int64_t timeUs;
- CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
-
- render((const uint8_t *)buffer->data() + buffer->range_offset(),
- buffer->range_length(), timeUs, timeUs * 1000);
- }
-
- void render(const void *data, size_t size, int64_t mediaTimeUs, nsecs_t renderTimeNs) {
- (void)mTarget->render(data, size, mediaTimeUs, renderTimeNs, NULL, mFormat);
- }
-
-protected:
- virtual ~AwesomeLocalRenderer() {
- delete mTarget;
- mTarget = NULL;
- }
-
-private:
- sp<AMessage> mFormat;
- SoftwareRenderer *mTarget;
-
- AwesomeLocalRenderer(const AwesomeLocalRenderer &);
- AwesomeLocalRenderer &operator=(const AwesomeLocalRenderer &);;
-};
-
-struct AwesomeNativeWindowRenderer : public AwesomeRenderer {
- AwesomeNativeWindowRenderer(
- const sp<ANativeWindow> &nativeWindow,
- int32_t rotationDegrees)
- : mNativeWindow(nativeWindow) {
- applyRotation(rotationDegrees);
- }
-
- virtual void render(MediaBuffer *buffer) {
- ATRACE_CALL();
- int64_t timeUs;
- CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
- native_window_set_buffers_timestamp(mNativeWindow.get(), timeUs * 1000);
- status_t err = mNativeWindow->queueBuffer(
- mNativeWindow.get(), buffer->graphicBuffer().get(), -1);
- if (err != 0) {
- ALOGE("queueBuffer failed with error %s (%d)", strerror(-err),
- -err);
- return;
- }
-
- sp<MetaData> metaData = buffer->meta_data();
- metaData->setInt32(kKeyRendered, 1);
- }
-
-protected:
- virtual ~AwesomeNativeWindowRenderer() {}
-
-private:
- sp<ANativeWindow> mNativeWindow;
-
- void applyRotation(int32_t rotationDegrees) {
- uint32_t transform;
- switch (rotationDegrees) {
- case 0: transform = 0; break;
- case 90: transform = HAL_TRANSFORM_ROT_90; break;
- case 180: transform = HAL_TRANSFORM_ROT_180; break;
- case 270: transform = HAL_TRANSFORM_ROT_270; break;
- default: transform = 0; break;
- }
-
- if (transform) {
- CHECK_EQ(0, native_window_set_buffers_transform(
- mNativeWindow.get(), transform));
- }
- }
-
- AwesomeNativeWindowRenderer(const AwesomeNativeWindowRenderer &);
- AwesomeNativeWindowRenderer &operator=(
- const AwesomeNativeWindowRenderer &);
-};
-
-// To collect the decoder usage
-void addBatteryData(uint32_t params) {
- sp<IBinder> binder =
- defaultServiceManager()->getService(String16("media.player"));
- sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
- CHECK(service.get() != NULL);
-
- service->addBatteryData(params);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-AwesomePlayer::AwesomePlayer()
- : mQueueStarted(false),
- mUIDValid(false),
- mTimeSource(NULL),
- mVideoRenderingStarted(false),
- mVideoRendererIsPreview(false),
- mMediaRenderingStartGeneration(0),
- mStartGeneration(0),
- mAudioPlayer(NULL),
- mDisplayWidth(0),
- mDisplayHeight(0),
- mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW),
- mFlags(0),
- mExtractorFlags(0),
- mVideoBuffer(NULL),
- mDecryptHandle(NULL),
- mLastVideoTimeUs(-1),
- mTextDriver(NULL),
- mOffloadAudio(false),
- mAudioTearDown(false) {
- CHECK_EQ(mClient.connect(), (status_t)OK);
-
- DataSource::RegisterDefaultSniffers();
-
- mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent);
- mVideoEventPending = false;
- mStreamDoneEvent = new AwesomeEvent(this, &AwesomePlayer::onStreamDone);
- mStreamDoneEventPending = false;
- mBufferingEvent = new AwesomeEvent(this, &AwesomePlayer::onBufferingUpdate);
- mBufferingEventPending = false;
- mVideoLagEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoLagUpdate);
- mVideoLagEventPending = false;
-
- mCheckAudioStatusEvent = new AwesomeEvent(
- this, &AwesomePlayer::onCheckAudioStatus);
-
- mAudioStatusEventPending = false;
-
- mAudioTearDownEvent = new AwesomeEvent(this,
- &AwesomePlayer::onAudioTearDownEvent);
- mAudioTearDownEventPending = false;
-
- mClockEstimator = new WindowedLinearFitEstimator();
-
- mPlaybackSettings = AUDIO_PLAYBACK_RATE_DEFAULT;
-
- reset();
-}
-
-AwesomePlayer::~AwesomePlayer() {
- if (mQueueStarted) {
- mQueue.stop();
- }
-
- reset();
-
- mClient.disconnect();
-}
-
-void AwesomePlayer::cancelPlayerEvents(bool keepNotifications) {
- mQueue.cancelEvent(mVideoEvent->eventID());
- mVideoEventPending = false;
- mQueue.cancelEvent(mVideoLagEvent->eventID());
- mVideoLagEventPending = false;
-
- if (mOffloadAudio) {
- mQueue.cancelEvent(mAudioTearDownEvent->eventID());
- mAudioTearDownEventPending = false;
- }
-
- if (!keepNotifications) {
- mQueue.cancelEvent(mStreamDoneEvent->eventID());
- mStreamDoneEventPending = false;
- mQueue.cancelEvent(mCheckAudioStatusEvent->eventID());
- mAudioStatusEventPending = false;
-
- mQueue.cancelEvent(mBufferingEvent->eventID());
- mBufferingEventPending = false;
- mAudioTearDown = false;
- }
-}
-
-void AwesomePlayer::setListener(const wp<MediaPlayerBase> &listener) {
- Mutex::Autolock autoLock(mLock);
- mListener = listener;
-}
-
-void AwesomePlayer::setUID(uid_t uid) {
- ALOGV("AwesomePlayer running on behalf of uid %d", uid);
-
- mUID = uid;
- mUIDValid = true;
-}
-
-status_t AwesomePlayer::setDataSource(
- const sp<IMediaHTTPService> &httpService,
- const char *uri,
- const KeyedVector<String8, String8> *headers) {
- Mutex::Autolock autoLock(mLock);
- return setDataSource_l(httpService, uri, headers);
-}
-
-status_t AwesomePlayer::setDataSource_l(
- const sp<IMediaHTTPService> &httpService,
- const char *uri,
- const KeyedVector<String8, String8> *headers) {
- reset_l();
-
- mHTTPService = httpService;
- mUri = uri;
-
- if (headers) {
- mUriHeaders = *headers;
-
- ssize_t index = mUriHeaders.indexOfKey(String8("x-hide-urls-from-log"));
- if (index >= 0) {
- // Browser is in "incognito" mode, suppress logging URLs.
-
- // This isn't something that should be passed to the server.
- mUriHeaders.removeItemsAt(index);
-
- modifyFlags(INCOGNITO, SET);
- }
- }
-
- ALOGI("setDataSource_l(%s)", uriDebugString(mUri, mFlags & INCOGNITO).c_str());
-
- // The actual work will be done during preparation in the call to
- // ::finishSetDataSource_l to avoid blocking the calling thread in
- // setDataSource for any significant time.
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- mStats.mFd = -1;
- mStats.mURI = mUri;
- }
-
- return OK;
-}
-
-status_t AwesomePlayer::setDataSource(
- int fd, int64_t offset, int64_t length) {
- Mutex::Autolock autoLock(mLock);
-
- reset_l();
-
- fd = dup(fd);
- sp<DataSource> dataSource = new FileSource(fd, offset, length);
-
- status_t err = dataSource->initCheck();
-
- if (err != OK) {
- return err;
- }
-
- mFileSource = dataSource;
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- mStats.mFd = fd;
- mStats.mURI = String8();
- }
-
- return setDataSource_l(dataSource);
-}
-
-status_t AwesomePlayer::setDataSource(const sp<IStreamSource> &source __unused) {
- return INVALID_OPERATION;
-}
-
-status_t AwesomePlayer::setDataSource_l(
- const sp<DataSource> &dataSource) {
- sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
-
- if (extractor == NULL) {
- return UNKNOWN_ERROR;
- }
-
- if (extractor->getDrmFlag()) {
- checkDrmStatus(dataSource);
- }
-
- return setDataSource_l(extractor);
-}
-
-void AwesomePlayer::checkDrmStatus(const sp<DataSource>& dataSource) {
- dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
- if (mDecryptHandle != NULL) {
- CHECK(mDrmManagerClient);
- if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
- notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
- }
- }
-}
-
-status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
- // Attempt to approximate overall stream bitrate by summing all
- // tracks' individual bitrates, if not all of them advertise bitrate,
- // we have to fail.
-
- int64_t totalBitRate = 0;
-
- mExtractor = extractor;
- for (size_t i = 0; i < extractor->countTracks(); ++i) {
- sp<MetaData> meta = extractor->getTrackMetaData(i);
-
- int32_t bitrate;
- if (!meta->findInt32(kKeyBitRate, &bitrate)) {
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
- ALOGV("track of type '%s' does not publish bitrate", mime);
-
- totalBitRate = -1;
- break;
- }
-
- totalBitRate += bitrate;
- }
- sp<MetaData> fileMeta = mExtractor->getMetaData();
- if (fileMeta != NULL) {
- int64_t duration;
- if (fileMeta->findInt64(kKeyDuration, &duration)) {
- mDurationUs = duration;
- }
- }
-
- mBitrate = totalBitRate;
-
- ALOGV("mBitrate = %lld bits/sec", (long long)mBitrate);
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- mStats.mBitrate = mBitrate;
- mStats.mTracks.clear();
- mStats.mAudioTrackIndex = -1;
- mStats.mVideoTrackIndex = -1;
- }
-
- bool haveAudio = false;
- bool haveVideo = false;
- for (size_t i = 0; i < extractor->countTracks(); ++i) {
- sp<MetaData> meta = extractor->getTrackMetaData(i);
-
- const char *_mime;
- CHECK(meta->findCString(kKeyMIMEType, &_mime));
-
- String8 mime = String8(_mime);
-
- if (!haveVideo && !strncasecmp(mime.string(), "video/", 6)) {
- setVideoSource(extractor->getTrack(i));
- haveVideo = true;
-
- // Set the presentation/display size
- int32_t displayWidth, displayHeight;
- bool success = meta->findInt32(kKeyDisplayWidth, &displayWidth);
- if (success) {
- success = meta->findInt32(kKeyDisplayHeight, &displayHeight);
- }
- if (success) {
- mDisplayWidth = displayWidth;
- mDisplayHeight = displayHeight;
- }
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- mStats.mVideoTrackIndex = mStats.mTracks.size();
- mStats.mTracks.push();
- TrackStat *stat =
- &mStats.mTracks.editItemAt(mStats.mVideoTrackIndex);
- stat->mMIME = mime.string();
- }
- } else if (!haveAudio && !strncasecmp(mime.string(), "audio/", 6)) {
- setAudioSource(extractor->getTrack(i));
- haveAudio = true;
- mActiveAudioTrackIndex = i;
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- mStats.mAudioTrackIndex = mStats.mTracks.size();
- mStats.mTracks.push();
- TrackStat *stat =
- &mStats.mTracks.editItemAt(mStats.mAudioTrackIndex);
- stat->mMIME = mime.string();
- }
-
- if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_AUDIO_VORBIS)) {
- // Only do this for vorbis audio, none of the other audio
- // formats even support this ringtone specific hack and
- // retrieving the metadata on some extractors may turn out
- // to be very expensive.
- sp<MetaData> fileMeta = extractor->getMetaData();
- int32_t loop;
- if (fileMeta != NULL
- && fileMeta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
- modifyFlags(AUTO_LOOPING, SET);
- }
- }
- } else if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_TEXT_3GPP)) {
- addTextSource_l(i, extractor->getTrack(i));
- }
- }
-
- if (!haveAudio && !haveVideo) {
- if (mWVMExtractor != NULL) {
- return mWVMExtractor->getError();
- } else {
- return UNKNOWN_ERROR;
- }
- }
-
- mExtractorFlags = extractor->flags();
-
- return OK;
-}
-
-void AwesomePlayer::reset() {
- Mutex::Autolock autoLock(mLock);
- reset_l();
-}
-
-void AwesomePlayer::reset_l() {
- mVideoRenderingStarted = false;
- mActiveAudioTrackIndex = -1;
- mDisplayWidth = 0;
- mDisplayHeight = 0;
-
- notifyListener_l(MEDIA_STOPPED);
-
- if (mDecryptHandle != NULL) {
- mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
- Playback::STOP, 0);
- mDecryptHandle = NULL;
- mDrmManagerClient = NULL;
- }
-
- if (mFlags & PLAYING) {
- uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder;
- if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) {
- params |= IMediaPlayerService::kBatteryDataTrackAudio;
- }
- if (mVideoSource != NULL) {
- params |= IMediaPlayerService::kBatteryDataTrackVideo;
- }
- addBatteryData(params);
- }
-
- if (mFlags & PREPARING) {
- modifyFlags(PREPARE_CANCELLED, SET);
- if (mConnectingDataSource != NULL) {
- ALOGI("interrupting the connection process");
- mConnectingDataSource->disconnect();
- }
-
- if (mFlags & PREPARING_CONNECTED) {
- // We are basically done preparing, we're just buffering
- // enough data to start playback, we can safely interrupt that.
- finishAsyncPrepare_l();
- }
- }
-
- while (mFlags & PREPARING) {
- mPreparedCondition.wait(mLock);
- }
-
- cancelPlayerEvents();
-
- mWVMExtractor.clear();
- mCachedSource.clear();
- mAudioTrack.clear();
- mVideoTrack.clear();
- mExtractor.clear();
-
- // Shutdown audio first, so that the response to the reset request
- // appears to happen instantaneously as far as the user is concerned
- // If we did this later, audio would continue playing while we
- // shutdown the video-related resources and the player appear to
- // not be as responsive to a reset request.
- if ((mAudioPlayer == NULL || !(mFlags & AUDIOPLAYER_STARTED))
- && mAudioSource != NULL) {
- // If we had an audio player, it would have effectively
- // taken possession of the audio source and stopped it when
- // _it_ is stopped. Otherwise this is still our responsibility.
- mAudioSource->stop();
- }
- mAudioSource.clear();
- mOmxSource.clear();
-
- mTimeSource = NULL;
-
- delete mAudioPlayer;
- mAudioPlayer = NULL;
-
- if (mTextDriver != NULL) {
- delete mTextDriver;
- mTextDriver = NULL;
- }
-
- mVideoRenderer.clear();
-
- if (mVideoSource != NULL) {
- shutdownVideoDecoder_l();
- }
-
- mDurationUs = -1;
- modifyFlags(0, ASSIGN);
- mExtractorFlags = 0;
- mTimeSourceDeltaUs = 0;
- mVideoTimeUs = 0;
-
- mSeeking = NO_SEEK;
- mSeekNotificationSent = true;
- mSeekTimeUs = 0;
-
- mHTTPService.clear();
- mUri.setTo("");
- mUriHeaders.clear();
-
- mFileSource.clear();
-
- mBitrate = -1;
- mLastVideoTimeUs = -1;
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- mStats.mFd = -1;
- mStats.mURI = String8();
- mStats.mBitrate = -1;
- mStats.mAudioTrackIndex = -1;
- mStats.mVideoTrackIndex = -1;
- mStats.mNumVideoFramesDecoded = 0;
- mStats.mNumVideoFramesDropped = 0;
- mStats.mVideoWidth = -1;
- mStats.mVideoHeight = -1;
- mStats.mFlags = 0;
- mStats.mTracks.clear();
- }
-
- mWatchForAudioSeekComplete = false;
- mWatchForAudioEOS = false;
-
- mMediaRenderingStartGeneration = 0;
- mStartGeneration = 0;
-}
-
-void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
- if ((mListener != NULL) && !mAudioTearDown) {
- sp<MediaPlayerBase> listener = mListener.promote();
-
- if (listener != NULL) {
- listener->sendEvent(msg, ext1, ext2);
- }
- }
-}
-
-bool AwesomePlayer::getBitrate(int64_t *bitrate) {
- off64_t size;
- if (mDurationUs > 0 && mCachedSource != NULL
- && mCachedSource->getSize(&size) == OK) {
- *bitrate = size * 8000000ll / mDurationUs; // in bits/sec
- return true;
- }
-
- if (mBitrate >= 0) {
- *bitrate = mBitrate;
- return true;
- }
-
- *bitrate = 0;
-
- return false;
-}
-
-// Returns true iff cached duration is available/applicable.
-bool AwesomePlayer::getCachedDuration_l(int64_t *durationUs, bool *eos) {
- int64_t bitrate;
-
- if (mCachedSource != NULL && getBitrate(&bitrate) && (bitrate > 0)) {
- status_t finalStatus;
- size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&finalStatus);
- *durationUs = cachedDataRemaining * 8000000ll / bitrate;
- *eos = (finalStatus != OK);
- return true;
- } else if (mWVMExtractor != NULL) {
- status_t finalStatus;
- *durationUs = mWVMExtractor->getCachedDurationUs(&finalStatus);
- *eos = (finalStatus != OK);
- return true;
- }
-
- return false;
-}
-
-void AwesomePlayer::ensureCacheIsFetching_l() {
- if (mCachedSource != NULL) {
- mCachedSource->resumeFetchingIfNecessary();
- }
-}
-
-void AwesomePlayer::onVideoLagUpdate() {
- Mutex::Autolock autoLock(mLock);
- if (!mVideoLagEventPending) {
- return;
- }
- mVideoLagEventPending = false;
-
- int64_t audioTimeUs = mAudioPlayer->getMediaTimeUs();
- int64_t videoLateByUs = audioTimeUs - mVideoTimeUs;
-
- if (!(mFlags & VIDEO_AT_EOS) && videoLateByUs > 300000ll) {
- ALOGV("video late by %lld ms.", videoLateByUs / 1000ll);
-
- notifyListener_l(
- MEDIA_INFO,
- MEDIA_INFO_VIDEO_TRACK_LAGGING,
- videoLateByUs / 1000ll);
- }
-
- postVideoLagEvent_l();
-}
-
-void AwesomePlayer::onBufferingUpdate() {
- Mutex::Autolock autoLock(mLock);
- if (!mBufferingEventPending) {
- return;
- }
- mBufferingEventPending = false;
-
- if (mCachedSource != NULL) {
- status_t finalStatus;
- size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&finalStatus);
- bool eos = (finalStatus != OK);
-
- if (eos) {
- if (finalStatus == ERROR_END_OF_STREAM) {
- notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
- }
- if (mFlags & PREPARING) {
- ALOGV("cache has reached EOS, prepare is done.");
- finishAsyncPrepare_l();
- }
- } else {
- bool eos2;
- int64_t cachedDurationUs;
- if (getCachedDuration_l(&cachedDurationUs, &eos2) && mDurationUs > 0) {
- int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
- if (percentage > 100) {
- percentage = 100;
- }
-
- notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
- } else {
- // We don't know the bitrate/duration of the stream, use absolute size
- // limits to maintain the cache.
-
- if ((mFlags & PLAYING) && !eos
- && (cachedDataRemaining < kLowWaterMarkBytes)) {
- ALOGI("cache is running low (< %zu) , pausing.",
- kLowWaterMarkBytes);
- modifyFlags(CACHE_UNDERRUN, SET);
- pause_l();
- ensureCacheIsFetching_l();
- sendCacheStats();
- notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
- } else if (eos || cachedDataRemaining > kHighWaterMarkBytes) {
- if (mFlags & CACHE_UNDERRUN) {
- ALOGI("cache has filled up (> %zu), resuming.",
- kHighWaterMarkBytes);
- modifyFlags(CACHE_UNDERRUN, CLEAR);
- play_l();
- } else if (mFlags & PREPARING) {
- ALOGV("cache has filled up (> %zu), prepare is done",
- kHighWaterMarkBytes);
- finishAsyncPrepare_l();
- }
- }
- }
- }
- } else if (mWVMExtractor != NULL) {
- status_t finalStatus;
-
- int64_t cachedDurationUs
- = mWVMExtractor->getCachedDurationUs(&finalStatus);
-
- bool eos = (finalStatus != OK);
-
- if (eos) {
- if (finalStatus == ERROR_END_OF_STREAM) {
- notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
- }
- if (mFlags & PREPARING) {
- ALOGV("cache has reached EOS, prepare is done.");
- finishAsyncPrepare_l();
- }
- } else {
- int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
- if (percentage > 100) {
- percentage = 100;
- }
-
- notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
- }
- }
-
- int64_t cachedDurationUs;
- bool eos;
- if (getCachedDuration_l(&cachedDurationUs, &eos)) {
- ALOGV("cachedDurationUs = %.2f secs, eos=%d",
- cachedDurationUs / 1E6, eos);
-
- if ((mFlags & PLAYING) && !eos
- && (cachedDurationUs < kLowWaterMarkUs)) {
- modifyFlags(CACHE_UNDERRUN, SET);
- ALOGI("cache is running low (%.2f secs) , pausing.",
- cachedDurationUs / 1E6);
- pause_l();
- ensureCacheIsFetching_l();
- sendCacheStats();
- notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
- } else if (eos || cachedDurationUs > kHighWaterMarkUs) {
- if (mFlags & CACHE_UNDERRUN) {
- modifyFlags(CACHE_UNDERRUN, CLEAR);
- ALOGI("cache has filled up (%.2f secs), resuming.",
- cachedDurationUs / 1E6);
- play_l();
- } else if (mFlags & PREPARING) {
- ALOGV("cache has filled up (%.2f secs), prepare is done",
- cachedDurationUs / 1E6);
- finishAsyncPrepare_l();
- }
- }
- }
-
- if (mFlags & (PLAYING | PREPARING | CACHE_UNDERRUN)) {
- postBufferingEvent_l();
- }
-}
-
-void AwesomePlayer::sendCacheStats() {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener != NULL) {
- int32_t kbps = 0;
- status_t err = UNKNOWN_ERROR;
- if (mCachedSource != NULL) {
- err = mCachedSource->getEstimatedBandwidthKbps(&kbps);
- } else if (mWVMExtractor != NULL) {
- err = mWVMExtractor->getEstimatedBandwidthKbps(&kbps);
- }
- if (err == OK) {
- listener->sendEvent(
- MEDIA_INFO, MEDIA_INFO_NETWORK_BANDWIDTH, kbps);
- }
- }
-}
-
-void AwesomePlayer::onStreamDone() {
- // Posted whenever any stream finishes playing.
- ATRACE_CALL();
-
- Mutex::Autolock autoLock(mLock);
- if (!mStreamDoneEventPending) {
- return;
- }
- mStreamDoneEventPending = false;
-
- if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
- ALOGV("MEDIA_ERROR %d", mStreamDoneStatus);
-
- notifyListener_l(
- MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, mStreamDoneStatus);
-
- pause_l(true /* at eos */);
-
- modifyFlags(AT_EOS, SET);
- return;
- }
-
- const bool allDone =
- (mVideoSource == NULL || (mFlags & VIDEO_AT_EOS))
- && (mAudioSource == NULL || (mFlags & AUDIO_AT_EOS));
-
- if (!allDone) {
- return;
- }
-
- if (mFlags & AUTO_LOOPING) {
- audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
- if (mAudioSink != NULL) {
- streamType = mAudioSink->getAudioStreamType();
- }
- if (streamType == AUDIO_STREAM_NOTIFICATION) {
- ALOGW("disabling auto-loop for notification");
- modifyFlags(AUTO_LOOPING, CLEAR);
- }
- }
- if ((mFlags & LOOPING)
- || (mFlags & AUTO_LOOPING)) {
-
- seekTo_l(0);
-
- if (mVideoSource != NULL) {
- postVideoEvent_l();
- }
- } else {
- ALOGV("MEDIA_PLAYBACK_COMPLETE");
- notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
-
- pause_l(true /* at eos */);
-
- // If audio hasn't completed MEDIA_SEEK_COMPLETE yet,
- // notify MEDIA_SEEK_COMPLETE to observer immediately for state persistence.
- if (mWatchForAudioSeekComplete) {
- notifyListener_l(MEDIA_SEEK_COMPLETE);
- mWatchForAudioSeekComplete = false;
- }
-
- modifyFlags(AT_EOS, SET);
- }
-}
-
-status_t AwesomePlayer::play() {
- ATRACE_CALL();
-
- Mutex::Autolock autoLock(mLock);
-
- modifyFlags(CACHE_UNDERRUN, CLEAR);
-
- return play_l();
-}
-
-status_t AwesomePlayer::play_l() {
- modifyFlags(SEEK_PREVIEW, CLEAR);
-
- if (mFlags & PLAYING) {
- return OK;
- }
-
- mMediaRenderingStartGeneration = ++mStartGeneration;
-
- if (!(mFlags & PREPARED)) {
- status_t err = prepare_l();
-
- if (err != OK) {
- return err;
- }
- }
-
- modifyFlags(PLAYING, SET);
- modifyFlags(FIRST_FRAME, SET);
-
- if (mDecryptHandle != NULL) {
- int64_t position;
- getPosition(&position);
- mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
- Playback::START, position / 1000);
- }
-
- if (mAudioSource != NULL) {
- if (mAudioPlayer == NULL) {
- createAudioPlayer_l();
- }
-
- CHECK(!(mFlags & AUDIO_RUNNING));
-
- if (mVideoSource == NULL) {
-
- // We don't want to post an error notification at this point,
- // the error returned from MediaPlayer::start() will suffice.
-
- status_t err = startAudioPlayer_l(
- false /* sendErrorNotification */);
-
- if ((err != OK) && mOffloadAudio) {
- ALOGI("play_l() cannot create offload output, fallback to sw decode");
- int64_t curTimeUs;
- getPosition(&curTimeUs);
-
- delete mAudioPlayer;
- mAudioPlayer = NULL;
- // if the player was started it will take care of stopping the source when destroyed
- if (!(mFlags & AUDIOPLAYER_STARTED)) {
- mAudioSource->stop();
- }
- modifyFlags((AUDIO_RUNNING | AUDIOPLAYER_STARTED), CLEAR);
- mOffloadAudio = false;
- mAudioSource = mOmxSource;
- if (mAudioSource != NULL) {
- err = mAudioSource->start();
-
- if (err != OK) {
- mAudioSource.clear();
- } else {
- mSeekNotificationSent = true;
- if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
- seekTo_l(curTimeUs);
- }
- createAudioPlayer_l();
- err = startAudioPlayer_l(false);
- }
- }
- }
-
- if (err != OK) {
- delete mAudioPlayer;
- mAudioPlayer = NULL;
-
- modifyFlags((PLAYING | FIRST_FRAME), CLEAR);
-
- if (mDecryptHandle != NULL) {
- mDrmManagerClient->setPlaybackStatus(
- mDecryptHandle, Playback::STOP, 0);
- }
-
- return err;
- }
- }
-
- if (mAudioPlayer != NULL) {
- mAudioPlayer->setPlaybackRate(mPlaybackSettings);
- }
- }
-
- if (mTimeSource == NULL && mAudioPlayer == NULL) {
- mTimeSource = &mSystemTimeSource;
- }
-
- if (mVideoSource != NULL) {
- // Kick off video playback
- postVideoEvent_l();
-
- if (mAudioSource != NULL && mVideoSource != NULL) {
- postVideoLagEvent_l();
- }
- }
-
- if (mFlags & AT_EOS) {
- // Legacy behaviour, if a stream finishes playing and then
- // is started again, we play from the start...
- seekTo_l(0);
- }
-
- uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted
- | IMediaPlayerService::kBatteryDataTrackDecoder;
- if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) {
- params |= IMediaPlayerService::kBatteryDataTrackAudio;
- }
- if (mVideoSource != NULL) {
- params |= IMediaPlayerService::kBatteryDataTrackVideo;
- }
- addBatteryData(params);
-
- if (isStreamingHTTP()) {
- postBufferingEvent_l();
- }
-
- return OK;
-}
-
-void AwesomePlayer::createAudioPlayer_l()
-{
- uint32_t flags = 0;
- int64_t cachedDurationUs;
- bool eos;
-
- if (mOffloadAudio) {
- flags |= AudioPlayer::USE_OFFLOAD;
- } else if (mVideoSource == NULL
- && (mDurationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US ||
- (getCachedDuration_l(&cachedDurationUs, &eos) &&
- cachedDurationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US))) {
- flags |= AudioPlayer::ALLOW_DEEP_BUFFERING;
- }
- if (isStreamingHTTP()) {
- flags |= AudioPlayer::IS_STREAMING;
- }
- if (mVideoSource != NULL) {
- flags |= AudioPlayer::HAS_VIDEO;
- }
-
- mAudioPlayer = new AudioPlayer(mAudioSink, flags, this);
- mAudioPlayer->setSource(mAudioSource);
-
- mTimeSource = mAudioPlayer;
-
- // If there was a seek request before we ever started,
- // honor the request now.
- // Make sure to do this before starting the audio player
- // to avoid a race condition.
- seekAudioIfNecessary_l();
-}
-
-void AwesomePlayer::notifyIfMediaStarted_l() {
- if (mMediaRenderingStartGeneration == mStartGeneration) {
- mMediaRenderingStartGeneration = -1;
- notifyListener_l(MEDIA_STARTED);
- }
-}
-
-status_t AwesomePlayer::startAudioPlayer_l(bool sendErrorNotification) {
- CHECK(!(mFlags & AUDIO_RUNNING));
- status_t err = OK;
-
- if (mAudioSource == NULL || mAudioPlayer == NULL) {
- return OK;
- }
-
- if (mOffloadAudio) {
- mQueue.cancelEvent(mAudioTearDownEvent->eventID());
- mAudioTearDownEventPending = false;
- }
-
- if (!(mFlags & AUDIOPLAYER_STARTED)) {
- bool wasSeeking = mAudioPlayer->isSeeking();
-
- // We've already started the MediaSource in order to enable
- // the prefetcher to read its data.
- err = mAudioPlayer->start(
- true /* sourceAlreadyStarted */);
-
- if (err != OK) {
- if (sendErrorNotification) {
- notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
- }
-
- return err;
- }
-
- modifyFlags(AUDIOPLAYER_STARTED, SET);
-
- if (wasSeeking) {
- CHECK(!mAudioPlayer->isSeeking());
-
- // We will have finished the seek while starting the audio player.
- postAudioSeekComplete();
- } else {
- notifyIfMediaStarted_l();
- }
- } else {
- err = mAudioPlayer->resume();
- }
-
- if (err == OK) {
- err = mAudioPlayer->setPlaybackRate(mPlaybackSettings);
- }
-
- if (err == OK) {
- modifyFlags(AUDIO_RUNNING, SET);
-
- mWatchForAudioEOS = true;
- }
-
- return err;
-}
-
-void AwesomePlayer::notifyVideoSize_l() {
- ATRACE_CALL();
- sp<MetaData> meta = mVideoSource->getFormat();
-
- int32_t cropLeft, cropTop, cropRight, cropBottom;
- if (!meta->findRect(
- kKeyCropRect, &cropLeft, &cropTop, &cropRight, &cropBottom)) {
- int32_t width, height;
- CHECK(meta->findInt32(kKeyWidth, &width));
- CHECK(meta->findInt32(kKeyHeight, &height));
-
- cropLeft = cropTop = 0;
- cropRight = width - 1;
- cropBottom = height - 1;
-
- ALOGV("got dimensions only %d x %d", width, height);
- } else {
- ALOGV("got crop rect %d, %d, %d, %d",
- cropLeft, cropTop, cropRight, cropBottom);
- }
-
- int32_t displayWidth;
- if (meta->findInt32(kKeyDisplayWidth, &displayWidth)) {
- ALOGV("Display width changed (%d=>%d)", mDisplayWidth, displayWidth);
- mDisplayWidth = displayWidth;
- }
- int32_t displayHeight;
- if (meta->findInt32(kKeyDisplayHeight, &displayHeight)) {
- ALOGV("Display height changed (%d=>%d)", mDisplayHeight, displayHeight);
- mDisplayHeight = displayHeight;
- }
-
- int32_t usableWidth = cropRight - cropLeft + 1;
- int32_t usableHeight = cropBottom - cropTop + 1;
- if (mDisplayWidth != 0) {
- usableWidth = mDisplayWidth;
- }
- if (mDisplayHeight != 0) {
- usableHeight = mDisplayHeight;
- }
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- mStats.mVideoWidth = usableWidth;
- mStats.mVideoHeight = usableHeight;
- }
-
- int32_t rotationDegrees;
- if (!mVideoTrack->getFormat()->findInt32(
- kKeyRotation, &rotationDegrees)) {
- rotationDegrees = 0;
- }
-
- if (rotationDegrees == 90 || rotationDegrees == 270) {
- notifyListener_l(
- MEDIA_SET_VIDEO_SIZE, usableHeight, usableWidth);
- } else {
- notifyListener_l(
- MEDIA_SET_VIDEO_SIZE, usableWidth, usableHeight);
- }
-}
-
-void AwesomePlayer::initRenderer_l() {
- ATRACE_CALL();
-
- if (mNativeWindow == NULL) {
- return;
- }
-
- sp<MetaData> meta = mVideoSource->getFormat();
-
- int32_t format;
- const char *component;
- int32_t decodedWidth, decodedHeight;
- CHECK(meta->findInt32(kKeyColorFormat, &format));
- CHECK(meta->findCString(kKeyDecoderComponent, &component));
- CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
- CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
-
- int32_t rotationDegrees;
- if (!mVideoTrack->getFormat()->findInt32(
- kKeyRotation, &rotationDegrees)) {
- rotationDegrees = 0;
- }
-
- mVideoRenderer.clear();
-
- // Must ensure that mVideoRenderer's destructor is actually executed
- // before creating a new one.
- IPCThreadState::self()->flushCommands();
-
- // Even if set scaling mode fails, we will continue anyway
- setVideoScalingMode_l(mVideoScalingMode);
- if (USE_SURFACE_ALLOC
- && !strncmp(component, "OMX.", 4)
- && strncmp(component, "OMX.google.", 11)) {
- // Hardware decoders avoid the CPU color conversion by decoding
- // directly to ANativeBuffers, so we must use a renderer that
- // just pushes those buffers to the ANativeWindow.
- mVideoRenderer =
- new AwesomeNativeWindowRenderer(mNativeWindow, rotationDegrees);
- } else {
- // Other decoders are instantiated locally and as a consequence
- // allocate their buffers in local address space. This renderer
- // then performs a color conversion and copy to get the data
- // into the ANativeBuffer.
- sp<AMessage> format;
- convertMetaDataToMessage(meta, &format);
- mVideoRenderer = new AwesomeLocalRenderer(mNativeWindow, format);
- }
-}
-
-status_t AwesomePlayer::pause() {
- ATRACE_CALL();
-
- Mutex::Autolock autoLock(mLock);
-
- modifyFlags(CACHE_UNDERRUN, CLEAR);
-
- return pause_l();
-}
-
-status_t AwesomePlayer::pause_l(bool at_eos) {
- if (!(mFlags & PLAYING)) {
- if (mAudioTearDown && mAudioTearDownWasPlaying) {
- ALOGV("pause_l() during teardown and finishSetDataSource_l() mFlags %x" , mFlags);
- mAudioTearDownWasPlaying = false;
- notifyListener_l(MEDIA_PAUSED);
- mMediaRenderingStartGeneration = ++mStartGeneration;
- }
- return OK;
- }
-
- notifyListener_l(MEDIA_PAUSED);
- mMediaRenderingStartGeneration = ++mStartGeneration;
-
- cancelPlayerEvents(true /* keepNotifications */);
-
- if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) {
- // If we played the audio stream to completion we
- // want to make sure that all samples remaining in the audio
- // track's queue are played out.
- mAudioPlayer->pause(at_eos /* playPendingSamples */);
- // send us a reminder to tear down the AudioPlayer if paused for too long.
- if (mOffloadAudio) {
- postAudioTearDownEvent(kOffloadPauseMaxUs);
- }
- modifyFlags(AUDIO_RUNNING, CLEAR);
- }
-
- if (mFlags & TEXTPLAYER_INITIALIZED) {
- mTextDriver->pause();
- modifyFlags(TEXT_RUNNING, CLEAR);
- }
-
- modifyFlags(PLAYING, CLEAR);
-
- if (mDecryptHandle != NULL) {
- mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
- Playback::PAUSE, 0);
- }
-
- uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder;
- if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) {
- params |= IMediaPlayerService::kBatteryDataTrackAudio;
- }
- if (mVideoSource != NULL) {
- params |= IMediaPlayerService::kBatteryDataTrackVideo;
- }
-
- addBatteryData(params);
-
- return OK;
-}
-
-bool AwesomePlayer::isPlaying() const {
- return (mFlags & PLAYING) || (mFlags & CACHE_UNDERRUN);
-}
-
-status_t AwesomePlayer::setSurfaceTexture(const sp<IGraphicBufferProducer> &bufferProducer) {
- Mutex::Autolock autoLock(mLock);
-
- status_t err;
- if (bufferProducer != NULL) {
- err = setNativeWindow_l(new Surface(bufferProducer));
- } else {
- err = setNativeWindow_l(NULL);
- }
-
- return err;
-}
-
-void AwesomePlayer::shutdownVideoDecoder_l() {
- if (mVideoBuffer) {
- mVideoBuffer->release();
- mVideoBuffer = NULL;
- }
-
- mVideoSource->stop();
-
- // The following hack is necessary to ensure that the OMX
- // component is completely released by the time we may try
- // to instantiate it again.
- wp<MediaSource> tmp = mVideoSource;
- mVideoSource.clear();
- while (tmp.promote() != NULL) {
- usleep(1000);
- }
- IPCThreadState::self()->flushCommands();
- ALOGV("video decoder shutdown completed");
-}
-
-status_t AwesomePlayer::setNativeWindow_l(const sp<ANativeWindow> &native) {
- mNativeWindow = native;
-
- if (mVideoSource == NULL) {
- return OK;
- }
-
- ALOGV("attempting to reconfigure to use new surface");
-
- bool wasPlaying = (mFlags & PLAYING) != 0;
-
- pause_l();
- mVideoRenderer.clear();
-
- shutdownVideoDecoder_l();
-
- status_t err = initVideoDecoder();
-
- if (err != OK) {
- ALOGE("failed to reinstantiate video decoder after surface change.");
- return err;
- }
-
- if (mLastVideoTimeUs >= 0) {
- mSeeking = SEEK;
- mSeekTimeUs = mLastVideoTimeUs;
- modifyFlags((AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS), CLEAR);
- }
-
- if (wasPlaying) {
- play_l();
- }
-
- return OK;
-}
-
-void AwesomePlayer::setAudioSink(
- const sp<MediaPlayerBase::AudioSink> &audioSink) {
- Mutex::Autolock autoLock(mLock);
-
- mAudioSink = audioSink;
-}
-
-status_t AwesomePlayer::setLooping(bool shouldLoop) {
- Mutex::Autolock autoLock(mLock);
-
- modifyFlags(LOOPING, CLEAR);
-
- if (shouldLoop) {
- modifyFlags(LOOPING, SET);
- }
-
- return OK;
-}
-
-status_t AwesomePlayer::getDuration(int64_t *durationUs) {
- Mutex::Autolock autoLock(mMiscStateLock);
-
- if (mDurationUs < 0) {
- return UNKNOWN_ERROR;
- }
-
- *durationUs = mDurationUs;
-
- return OK;
-}
-
-status_t AwesomePlayer::getPosition(int64_t *positionUs) {
- if (mSeeking != NO_SEEK) {
- *positionUs = mSeekTimeUs;
- } else if (mVideoSource != NULL
- && (mAudioPlayer == NULL || !(mFlags & VIDEO_AT_EOS))) {
- Mutex::Autolock autoLock(mMiscStateLock);
- *positionUs = mVideoTimeUs;
- } else if (mAudioPlayer != NULL) {
- *positionUs = mAudioPlayer->getMediaTimeUs();
- } else {
- *positionUs = 0;
- }
- return OK;
-}
-
-status_t AwesomePlayer::seekTo(int64_t timeUs) {
- ATRACE_CALL();
-
- if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
- Mutex::Autolock autoLock(mLock);
- return seekTo_l(timeUs);
- }
-
- return OK;
-}
-
-status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
- if (mFlags & CACHE_UNDERRUN) {
- modifyFlags(CACHE_UNDERRUN, CLEAR);
- play_l();
- }
-
- if ((mFlags & PLAYING) && mVideoSource != NULL && (mFlags & VIDEO_AT_EOS)) {
- // Video playback completed before, there's no pending
- // video event right now. In order for this new seek
- // to be honored, we need to post one.
-
- postVideoEvent_l();
- }
-
- mSeeking = SEEK;
- mSeekNotificationSent = false;
- mSeekTimeUs = timeUs;
- modifyFlags((AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS), CLEAR);
-
- if (mFlags & PLAYING) {
- notifyListener_l(MEDIA_PAUSED);
- mMediaRenderingStartGeneration = ++mStartGeneration;
- }
-
- seekAudioIfNecessary_l();
-
- if (mFlags & TEXTPLAYER_INITIALIZED) {
- mTextDriver->seekToAsync(mSeekTimeUs);
- }
-
- if (!(mFlags & PLAYING)) {
- ALOGV("seeking while paused, sending SEEK_COMPLETE notification"
- " immediately.");
-
- notifyListener_l(MEDIA_SEEK_COMPLETE);
- mSeekNotificationSent = true;
-
- if ((mFlags & PREPARED) && mVideoSource != NULL) {
- modifyFlags(SEEK_PREVIEW, SET);
- postVideoEvent_l();
- }
- }
-
- return OK;
-}
-
-void AwesomePlayer::seekAudioIfNecessary_l() {
- if (mSeeking != NO_SEEK && mVideoSource == NULL && mAudioPlayer != NULL) {
- mAudioPlayer->seekTo(mSeekTimeUs);
-
- mWatchForAudioSeekComplete = true;
- mWatchForAudioEOS = true;
-
- if (mDecryptHandle != NULL) {
- mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
- Playback::PAUSE, 0);
- mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
- Playback::START, mSeekTimeUs / 1000);
- }
- }
-}
-
-void AwesomePlayer::setAudioSource(sp<MediaSource> source) {
- CHECK(source != NULL);
-
- mAudioTrack = source;
-}
-
-void AwesomePlayer::addTextSource_l(size_t trackIndex, const sp<MediaSource>& source) {
- CHECK(source != NULL);
-
- if (mTextDriver == NULL) {
- mTextDriver = new TimedTextDriver(mListener, mHTTPService);
- }
-
- mTextDriver->addInBandTextSource(trackIndex, source);
-}
-
-status_t AwesomePlayer::initAudioDecoder() {
- ATRACE_CALL();
-
- sp<MetaData> meta = mAudioTrack->getFormat();
-
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
- // Check whether there is a hardware codec for this stream
- // This doesn't guarantee that the hardware has a free stream
- // but it avoids us attempting to open (and re-open) an offload
- // stream to hardware that doesn't have the necessary codec
- audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
- if (mAudioSink != NULL) {
- streamType = mAudioSink->getAudioStreamType();
- }
-
- mOffloadAudio = canOffloadStream(meta, (mVideoSource != NULL),
- isStreamingHTTP(), streamType);
-
- if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
- ALOGV("createAudioPlayer: bypass OMX (raw)");
- mAudioSource = mAudioTrack;
- } else {
- // If offloading we still create a OMX decoder as a fall-back
- // but we don't start it
- mOmxSource = OMXCodec::Create(
- mClient.interface(), mAudioTrack->getFormat(),
- false, // createEncoder
- mAudioTrack);
-
- if (mOffloadAudio) {
- ALOGV("createAudioPlayer: bypass OMX (offload)");
- mAudioSource = mAudioTrack;
- } else {
- mAudioSource = mOmxSource;
- }
- }
-
- if (mAudioSource != NULL) {
- int64_t durationUs;
- if (mAudioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
- Mutex::Autolock autoLock(mMiscStateLock);
- if (mDurationUs < 0 || durationUs > mDurationUs) {
- mDurationUs = durationUs;
- }
- }
-
- status_t err = mAudioSource->start();
-
- if (err != OK) {
- mAudioSource.clear();
- mOmxSource.clear();
- return err;
- }
- } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_QCELP)) {
- // For legacy reasons we're simply going to ignore the absence
- // of an audio decoder for QCELP instead of aborting playback
- // altogether.
- return OK;
- }
-
- if (mAudioSource != NULL) {
- Mutex::Autolock autoLock(mStatsLock);
- TrackStat *stat = &mStats.mTracks.editItemAt(mStats.mAudioTrackIndex);
- const char *component;
- if (!mAudioSource->getFormat()
- ->findCString(kKeyDecoderComponent, &component)) {
- component = "none";
- }
-
- stat->mDecoderName = component;
- }
-
- return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
-}
-
-void AwesomePlayer::setVideoSource(sp<MediaSource> source) {
- CHECK(source != NULL);
-
- mVideoTrack = source;
-}
-
-status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {
- ATRACE_CALL();
-
- // Either the application or the DRM system can independently say
- // that there must be a hardware-protected path to an external video sink.
- // For now we always require a hardware-protected path to external video sink
- // if content is DRMed, but eventually this could be optional per DRM agent.
- // When the application wants protection, then
- // (USE_SURFACE_ALLOC && (mSurface != 0) &&
- // (mSurface->getFlags() & ISurfaceComposer::eProtectedByApp))
- // will be true, but that part is already handled by SurfaceFlinger.
-
-#ifdef DEBUG_HDCP
- // For debugging, we allow a system property to control the protected usage.
- // In case of uninitialized or unexpected property, we default to "DRM only".
- bool setProtectionBit = false;
- char value[PROPERTY_VALUE_MAX];
- if (property_get("persist.sys.hdcp_checking", value, NULL)) {
- if (!strcmp(value, "never")) {
- // nop
- } else if (!strcmp(value, "always")) {
- setProtectionBit = true;
- } else if (!strcmp(value, "drm-only")) {
- if (mDecryptHandle != NULL) {
- setProtectionBit = true;
- }
- // property value is empty, or unexpected value
- } else {
- if (mDecryptHandle != NULL) {
- setProtectionBit = true;
- }
- }
- // can' read property value
- } else {
- if (mDecryptHandle != NULL) {
- setProtectionBit = true;
- }
- }
- // note that usage bit is already cleared, so no need to clear it in the "else" case
- if (setProtectionBit) {
- flags |= OMXCodec::kEnableGrallocUsageProtected;
- }
-#else
- if (mDecryptHandle != NULL) {
- flags |= OMXCodec::kEnableGrallocUsageProtected;
- }
-#endif
- ALOGV("initVideoDecoder flags=0x%x", flags);
- mVideoSource = OMXCodec::Create(
- mClient.interface(), mVideoTrack->getFormat(),
- false, // createEncoder
- mVideoTrack,
- NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);
-
- if (mVideoSource != NULL) {
- int64_t durationUs;
- if (mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
- Mutex::Autolock autoLock(mMiscStateLock);
- if (mDurationUs < 0 || durationUs > mDurationUs) {
- mDurationUs = durationUs;
- }
- }
-
- status_t err = mVideoSource->start();
-
- if (err != OK) {
- ALOGE("failed to start video source");
- mVideoSource.clear();
- return err;
- }
- }
-
- if (mVideoSource != NULL) {
- const char *componentName;
- CHECK(mVideoSource->getFormat()
- ->findCString(kKeyDecoderComponent, &componentName));
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- TrackStat *stat = &mStats.mTracks.editItemAt(mStats.mVideoTrackIndex);
-
- stat->mDecoderName = componentName;
- }
-
- static const char *kPrefix = "OMX.Nvidia.";
- static const char *kSuffix = ".decode";
- static const size_t kSuffixLength = strlen(kSuffix);
-
- size_t componentNameLength = strlen(componentName);
-
- if (!strncmp(componentName, kPrefix, strlen(kPrefix))
- && componentNameLength >= kSuffixLength
- && !strcmp(&componentName[
- componentNameLength - kSuffixLength], kSuffix)) {
- modifyFlags(SLOW_DECODER_HACK, SET);
- }
- }
-
- return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
-}
-
-void AwesomePlayer::finishSeekIfNecessary(int64_t videoTimeUs) {
- ATRACE_CALL();
-
- if (mSeeking == SEEK_VIDEO_ONLY) {
- mSeeking = NO_SEEK;
- return;
- }
-
- if (mSeeking == NO_SEEK || (mFlags & SEEK_PREVIEW)) {
- return;
- }
-
- // If we paused, then seeked, then resumed, it is possible that we have
- // signaled SEEK_COMPLETE at a copmletely different media time than where
- // we are now resuming. Signal new position to media time provider.
- // Cannot signal another SEEK_COMPLETE, as existing clients may not expect
- // multiple SEEK_COMPLETE responses to a single seek() request.
- if (mSeekNotificationSent && llabs((long long)(mSeekTimeUs - videoTimeUs)) > 10000) {
- // notify if we are resuming more than 10ms away from desired seek time
- notifyListener_l(MEDIA_SKIPPED);
- }
-
- if (mAudioPlayer != NULL) {
- ALOGV("seeking audio to %" PRId64 " us (%.2f secs).", videoTimeUs, videoTimeUs / 1E6);
-
- // If we don't have a video time, seek audio to the originally
- // requested seek time instead.
-
- mAudioPlayer->seekTo(videoTimeUs < 0 ? mSeekTimeUs : videoTimeUs);
- mWatchForAudioSeekComplete = true;
- mWatchForAudioEOS = true;
- } else if (!mSeekNotificationSent) {
- // If we're playing video only, report seek complete now,
- // otherwise audio player will notify us later.
- notifyListener_l(MEDIA_SEEK_COMPLETE);
- mSeekNotificationSent = true;
- }
-
- modifyFlags(FIRST_FRAME, SET);
- mSeeking = NO_SEEK;
-
- if (mDecryptHandle != NULL) {
- mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
- Playback::PAUSE, 0);
- mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
- Playback::START, videoTimeUs / 1000);
- }
-}
-
-void AwesomePlayer::onVideoEvent() {
- ATRACE_CALL();
- Mutex::Autolock autoLock(mLock);
- if (!mVideoEventPending) {
- // The event has been cancelled in reset_l() but had already
- // been scheduled for execution at that time.
- return;
- }
- mVideoEventPending = false;
-
- if (mSeeking != NO_SEEK) {
- if (mVideoBuffer) {
- mVideoBuffer->release();
- mVideoBuffer = NULL;
- }
-
- if (mSeeking == SEEK && isStreamingHTTP() && mAudioSource != NULL
- && !(mFlags & SEEK_PREVIEW)) {
- // We're going to seek the video source first, followed by
- // the audio source.
- // In order to avoid jumps in the DataSource offset caused by
- // the audio codec prefetching data from the old locations
- // while the video codec is already reading data from the new
- // locations, we'll "pause" the audio source, causing it to
- // stop reading input data until a subsequent seek.
-
- if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) {
- mAudioPlayer->pause();
-
- modifyFlags(AUDIO_RUNNING, CLEAR);
- }
- mAudioSource->pause();
- }
- }
-
- if (!mVideoBuffer) {
- MediaSource::ReadOptions options;
- if (mSeeking != NO_SEEK) {
- ALOGV("seeking to %" PRId64 " us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6);
-
- options.setSeekTo(
- mSeekTimeUs,
- mSeeking == SEEK_VIDEO_ONLY
- ? MediaSource::ReadOptions::SEEK_NEXT_SYNC
- : MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
- }
- for (;;) {
- status_t err = mVideoSource->read(&mVideoBuffer, &options);
- options.clearSeekTo();
-
- if (err != OK) {
- CHECK(mVideoBuffer == NULL);
-
- if (err == INFO_FORMAT_CHANGED) {
- ALOGV("VideoSource signalled format change.");
-
- notifyVideoSize_l();
-
- if (mVideoRenderer != NULL) {
- mVideoRendererIsPreview = false;
- initRenderer_l();
- }
- continue;
- }
-
- // So video playback is complete, but we may still have
- // a seek request pending that needs to be applied
- // to the audio track.
- if (mSeeking != NO_SEEK) {
- ALOGV("video stream ended while seeking!");
- }
- finishSeekIfNecessary(-1);
-
- if (mAudioPlayer != NULL
- && !(mFlags & (AUDIO_RUNNING | SEEK_PREVIEW))) {
- startAudioPlayer_l();
- }
-
- modifyFlags(VIDEO_AT_EOS, SET);
- postStreamDoneEvent_l(err);
- return;
- }
-
- if (mVideoBuffer->range_length() == 0) {
- // Some decoders, notably the PV AVC software decoder
- // return spurious empty buffers that we just want to ignore.
-
- mVideoBuffer->release();
- mVideoBuffer = NULL;
- continue;
- }
-
- break;
- }
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- ++mStats.mNumVideoFramesDecoded;
- }
- }
-
- int64_t timeUs;
- CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
-
- mLastVideoTimeUs = timeUs;
-
- if (mSeeking == SEEK_VIDEO_ONLY) {
- if (mSeekTimeUs > timeUs) {
- ALOGI("XXX mSeekTimeUs = %" PRId64 " us, timeUs = %" PRId64 " us",
- mSeekTimeUs, timeUs);
- }
- }
-
- {
- Mutex::Autolock autoLock(mMiscStateLock);
- mVideoTimeUs = timeUs;
- }
-
- SeekType wasSeeking = mSeeking;
- finishSeekIfNecessary(timeUs);
-
- if (mAudioPlayer != NULL && !(mFlags & (AUDIO_RUNNING | SEEK_PREVIEW))) {
- status_t err = startAudioPlayer_l();
- if (err != OK) {
- ALOGE("Starting the audio player failed w/ err %d", err);
- return;
- }
- }
-
- if ((mFlags & TEXTPLAYER_INITIALIZED)
- && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
- mTextDriver->start();
- modifyFlags(TEXT_RUNNING, SET);
- }
-
- TimeSource *ts =
- ((mFlags & AUDIO_AT_EOS) || !(mFlags & AUDIOPLAYER_STARTED))
- ? &mSystemTimeSource : mTimeSource;
- int64_t systemTimeUs = mSystemTimeSource.getRealTimeUs();
- int64_t looperTimeUs = ALooper::GetNowUs();
-
- if (mFlags & FIRST_FRAME) {
- modifyFlags(FIRST_FRAME, CLEAR);
- mSinceLastDropped = 0;
- mClockEstimator->reset();
- mTimeSourceDeltaUs = estimateRealTimeUs(ts, systemTimeUs) - timeUs;
- }
-
- int64_t realTimeUs, mediaTimeUs;
- if (!(mFlags & AUDIO_AT_EOS) && mAudioPlayer != NULL
- && mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) {
- ALOGV("updating TSdelta (%" PRId64 " => %" PRId64 " change %" PRId64 ")",
- mTimeSourceDeltaUs, realTimeUs - mediaTimeUs,
- mTimeSourceDeltaUs - (realTimeUs - mediaTimeUs));
- ATRACE_INT("TS delta change (ms)", (mTimeSourceDeltaUs - (realTimeUs - mediaTimeUs)) / 1E3);
- mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;
- }
-
- if (wasSeeking == SEEK_VIDEO_ONLY) {
- int64_t nowUs = estimateRealTimeUs(ts, systemTimeUs) - mTimeSourceDeltaUs;
-
- int64_t latenessUs = nowUs - timeUs;
-
- ATRACE_INT("Video Lateness (ms)", latenessUs / 1E3);
-
- if (latenessUs > 0) {
- ALOGI("after SEEK_VIDEO_ONLY we're late by %.2f secs", latenessUs / 1E6);
- }
- }
-
- int64_t latenessUs = 0;
- if (wasSeeking == NO_SEEK) {
- // Let's display the first frame after seeking right away.
-
- int64_t nowUs = estimateRealTimeUs(ts, systemTimeUs) - mTimeSourceDeltaUs;
-
- latenessUs = nowUs - timeUs;
-
- ATRACE_INT("Video Lateness (ms)", latenessUs / 1E3);
-
- if (latenessUs > 500000ll
- && mAudioPlayer != NULL
- && mAudioPlayer->getMediaTimeMapping(
- &realTimeUs, &mediaTimeUs)) {
- if (mWVMExtractor == NULL) {
- ALOGI("we're much too late (%.2f secs), video skipping ahead",
- latenessUs / 1E6);
-
- mVideoBuffer->release();
- mVideoBuffer = NULL;
-
- mSeeking = SEEK_VIDEO_ONLY;
- mSeekTimeUs = mediaTimeUs;
-
- postVideoEvent_l();
- return;
- } else {
- // The widevine extractor doesn't deal well with seeking
- // audio and video independently. We'll just have to wait
- // until the decoder catches up, which won't be long at all.
- ALOGI("we're very late (%.2f secs)", latenessUs / 1E6);
- }
- }
-
- if (latenessUs > 40000) {
- // We're more than 40ms late.
- ALOGV("we're late by %" PRId64 " us (%.2f secs)",
- latenessUs, latenessUs / 1E6);
-
- if (!(mFlags & SLOW_DECODER_HACK)
- || mSinceLastDropped > FRAME_DROP_FREQ)
- {
- ALOGV("we're late by %" PRId64 " us (%.2f secs) dropping "
- "one after %d frames",
- latenessUs, latenessUs / 1E6, mSinceLastDropped);
-
- mSinceLastDropped = 0;
- mVideoBuffer->release();
- mVideoBuffer = NULL;
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- ++mStats.mNumVideoFramesDropped;
- }
-
- postVideoEvent_l(0);
- return;
- }
- }
-
- if (latenessUs < -30000) {
- // We're more than 30ms early, schedule at most 20 ms before time due
- postVideoEvent_l(latenessUs < -60000 ? 30000 : -latenessUs - 20000);
- return;
- }
- }
-
- if ((mNativeWindow != NULL)
- && (mVideoRendererIsPreview || mVideoRenderer == NULL)) {
- mVideoRendererIsPreview = false;
-
- initRenderer_l();
- }
-
- if (mVideoRenderer != NULL) {
- mSinceLastDropped++;
- mVideoBuffer->meta_data()->setInt64(kKeyTime, looperTimeUs - latenessUs);
-
- mVideoRenderer->render(mVideoBuffer);
- if (!mVideoRenderingStarted) {
- mVideoRenderingStarted = true;
- notifyListener_l(MEDIA_INFO, MEDIA_INFO_RENDERING_START);
- }
-
- if (mFlags & PLAYING) {
- notifyIfMediaStarted_l();
- }
- }
-
- mVideoBuffer->release();
- mVideoBuffer = NULL;
-
- if (wasSeeking != NO_SEEK && (mFlags & SEEK_PREVIEW)) {
- modifyFlags(SEEK_PREVIEW, CLEAR);
- return;
- }
-
- /* get next frame time */
- if (wasSeeking == NO_SEEK) {
- MediaSource::ReadOptions options;
- for (;;) {
- status_t err = mVideoSource->read(&mVideoBuffer, &options);
- if (err != OK) {
- // deal with any errors next time
- CHECK(mVideoBuffer == NULL);
- postVideoEvent_l(0);
- return;
- }
-
- if (mVideoBuffer->range_length() != 0) {
- break;
- }
-
- // Some decoders, notably the PV AVC software decoder
- // return spurious empty buffers that we just want to ignore.
-
- mVideoBuffer->release();
- mVideoBuffer = NULL;
- }
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- ++mStats.mNumVideoFramesDecoded;
- }
-
- int64_t nextTimeUs;
- CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &nextTimeUs));
- systemTimeUs = mSystemTimeSource.getRealTimeUs();
- int64_t delayUs = nextTimeUs - estimateRealTimeUs(ts, systemTimeUs) + mTimeSourceDeltaUs;
- ATRACE_INT("Frame delta (ms)", (nextTimeUs - timeUs) / 1E3);
- ALOGV("next frame in %" PRId64, delayUs);
- // try to schedule 30ms before time due
- postVideoEvent_l(delayUs > 60000 ? 30000 : (delayUs < 30000 ? 0 : delayUs - 30000));
- return;
- }
-
- postVideoEvent_l();
-}
-
-int64_t AwesomePlayer::estimateRealTimeUs(TimeSource *ts, int64_t systemTimeUs) {
- if (ts == &mSystemTimeSource) {
- return systemTimeUs;
- } else {
- return (int64_t)mClockEstimator->estimate(systemTimeUs, ts->getRealTimeUs());
- }
-}
-
-void AwesomePlayer::postVideoEvent_l(int64_t delayUs) {
- ATRACE_CALL();
-
- if (mVideoEventPending) {
- return;
- }
-
- mVideoEventPending = true;
- mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);
-}
-
-void AwesomePlayer::postStreamDoneEvent_l(status_t status) {
- if (mStreamDoneEventPending) {
- return;
- }
- mStreamDoneEventPending = true;
-
- mStreamDoneStatus = status;
- mQueue.postEvent(mStreamDoneEvent);
-}
-
-void AwesomePlayer::postBufferingEvent_l() {
- if (mBufferingEventPending) {
- return;
- }
- mBufferingEventPending = true;
- mQueue.postEventWithDelay(mBufferingEvent, 1000000ll);
-}
-
-void AwesomePlayer::postVideoLagEvent_l() {
- if (mVideoLagEventPending) {
- return;
- }
- mVideoLagEventPending = true;
- mQueue.postEventWithDelay(mVideoLagEvent, 1000000ll);
-}
-
-void AwesomePlayer::postCheckAudioStatusEvent(int64_t delayUs) {
- Mutex::Autolock autoLock(mAudioLock);
- if (mAudioStatusEventPending) {
- return;
- }
- mAudioStatusEventPending = true;
- // Do not honor delay when looping in order to limit audio gap
- if (mFlags & (LOOPING | AUTO_LOOPING)) {
- delayUs = 0;
- }
- mQueue.postEventWithDelay(mCheckAudioStatusEvent, delayUs);
-}
-
-void AwesomePlayer::postAudioTearDownEvent(int64_t delayUs) {
- Mutex::Autolock autoLock(mAudioLock);
- if (mAudioTearDownEventPending) {
- return;
- }
- mAudioTearDownEventPending = true;
- mQueue.postEventWithDelay(mAudioTearDownEvent, delayUs);
-}
-
-void AwesomePlayer::onCheckAudioStatus() {
- {
- Mutex::Autolock autoLock(mAudioLock);
- if (!mAudioStatusEventPending) {
- // Event was dispatched and while we were blocking on the mutex,
- // has already been cancelled.
- return;
- }
-
- mAudioStatusEventPending = false;
- }
-
- Mutex::Autolock autoLock(mLock);
-
- if (mWatchForAudioSeekComplete && !mAudioPlayer->isSeeking()) {
- mWatchForAudioSeekComplete = false;
-
- if (!mSeekNotificationSent) {
- notifyListener_l(MEDIA_SEEK_COMPLETE);
- mSeekNotificationSent = true;
- }
-
- if (mVideoSource == NULL) {
- // For video the mSeeking flag is always reset in finishSeekIfNecessary
- mSeeking = NO_SEEK;
- }
-
- notifyIfMediaStarted_l();
- }
-
- status_t finalStatus;
- if (mWatchForAudioEOS && mAudioPlayer->reachedEOS(&finalStatus)) {
- mWatchForAudioEOS = false;
- modifyFlags(AUDIO_AT_EOS, SET);
- modifyFlags(FIRST_FRAME, SET);
- postStreamDoneEvent_l(finalStatus);
- }
-}
-
-status_t AwesomePlayer::prepare() {
- ATRACE_CALL();
- Mutex::Autolock autoLock(mLock);
- return prepare_l();
-}
-
-status_t AwesomePlayer::prepare_l() {
- if (mFlags & PREPARED) {
- return OK;
- }
-
- if (mFlags & PREPARING) {
- return UNKNOWN_ERROR;
- }
-
- mIsAsyncPrepare = false;
- status_t err = prepareAsync_l();
-
- if (err != OK) {
- return err;
- }
-
- while (mFlags & PREPARING) {
- mPreparedCondition.wait(mLock);
- }
-
- return mPrepareResult;
-}
-
-status_t AwesomePlayer::prepareAsync() {
- ATRACE_CALL();
- Mutex::Autolock autoLock(mLock);
-
- if (mFlags & PREPARING) {
- return UNKNOWN_ERROR; // async prepare already pending
- }
-
- mIsAsyncPrepare = true;
- return prepareAsync_l();
-}
-
-status_t AwesomePlayer::prepareAsync_l() {
- if (mFlags & PREPARING) {
- return UNKNOWN_ERROR; // async prepare already pending
- }
-
- if (!mQueueStarted) {
- mQueue.start();
- mQueueStarted = true;
- }
-
- modifyFlags(PREPARING, SET);
- mAsyncPrepareEvent = new AwesomeEvent(
- this, &AwesomePlayer::onPrepareAsyncEvent);
-
- mQueue.postEvent(mAsyncPrepareEvent);
-
- return OK;
-}
-
-status_t AwesomePlayer::finishSetDataSource_l() {
- ATRACE_CALL();
- sp<DataSource> dataSource;
-
- bool isWidevineStreaming = false;
- if (!strncasecmp("widevine://", mUri.string(), 11)) {
- isWidevineStreaming = true;
-
- String8 newURI = String8("http://");
- newURI.append(mUri.string() + 11);
-
- mUri = newURI;
- }
-
- AString sniffedMIME;
-
- if (!strncasecmp("http://", mUri.string(), 7)
- || !strncasecmp("https://", mUri.string(), 8)
- || isWidevineStreaming) {
- if (mHTTPService == NULL) {
- ALOGE("Attempt to play media from http URI without HTTP service.");
- return UNKNOWN_ERROR;
- }
-
- sp<IMediaHTTPConnection> conn = mHTTPService->makeHTTPConnection();
- mConnectingDataSource = new MediaHTTP(conn);
-
- String8 cacheConfig;
- bool disconnectAtHighwatermark;
- NuCachedSource2::RemoveCacheSpecificHeaders(
- &mUriHeaders, &cacheConfig, &disconnectAtHighwatermark);
-
- mLock.unlock();
- status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders);
- // force connection at this point, to avoid a race condition between getMIMEType and the
- // caching datasource constructed below, which could result in multiple requests to the
- // server, and/or failed connections.
- String8 contentType = mConnectingDataSource->getMIMEType();
- mLock.lock();
-
- if (err != OK) {
- mConnectingDataSource.clear();
-
- ALOGI("mConnectingDataSource->connect() returned %d", err);
- return err;
- }
-
- if (!isWidevineStreaming) {
- // The widevine extractor does its own caching.
-
-#if 0
- mCachedSource = NuCachedSource2::Create(
- new ThrottledSource(
- mConnectingDataSource, 50 * 1024 /* bytes/sec */));
-#else
- mCachedSource = NuCachedSource2::Create(
- mConnectingDataSource,
- cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
- disconnectAtHighwatermark);
-#endif
-
- dataSource = mCachedSource;
- } else {
- dataSource = mConnectingDataSource;
- }
-
- mConnectingDataSource.clear();
-
- if (strncasecmp(contentType.string(), "audio/", 6)) {
- // We're not doing this for streams that appear to be audio-only
- // streams to ensure that even low bandwidth streams start
- // playing back fairly instantly.
-
- // We're going to prefill the cache before trying to instantiate
- // the extractor below, as the latter is an operation that otherwise
- // could block on the datasource for a significant amount of time.
- // During that time we'd be unable to abort the preparation phase
- // without this prefill.
- if (mCachedSource != NULL) {
- // We're going to prefill the cache before trying to instantiate
- // the extractor below, as the latter is an operation that otherwise
- // could block on the datasource for a significant amount of time.
- // During that time we'd be unable to abort the preparation phase
- // without this prefill.
-
- mLock.unlock();
-
- // Initially make sure we have at least 192 KB for the sniff
- // to complete without blocking.
- static const size_t kMinBytesForSniffing = 192 * 1024;
-
- off64_t metaDataSize = -1ll;
- for (;;) {
- status_t finalStatus;
- size_t cachedDataRemaining =
- mCachedSource->approxDataRemaining(&finalStatus);
-
- if (finalStatus != OK
- || (metaDataSize >= 0
- && (off64_t)cachedDataRemaining >= metaDataSize)
- || (mFlags & PREPARE_CANCELLED)) {
- break;
- }
-
- ALOGV("now cached %zu bytes of data", cachedDataRemaining);
-
- if (metaDataSize < 0
- && cachedDataRemaining >= kMinBytesForSniffing) {
- String8 tmp;
- float confidence;
- sp<AMessage> meta;
- if (!dataSource->sniff(&tmp, &confidence, &meta)) {
- mLock.lock();
- return UNKNOWN_ERROR;
- }
-
- // We successfully identified the file's extractor to
- // be, remember this mime type so we don't have to
- // sniff it again when we call MediaExtractor::Create()
- // below.
- sniffedMIME = tmp.string();
-
- if (meta == NULL
- || !meta->findInt64("meta-data-size",
- reinterpret_cast<int64_t*>(&metaDataSize))) {
- metaDataSize = kHighWaterMarkBytes;
- }
-
- CHECK_GE(metaDataSize, 0ll);
- ALOGV("metaDataSize = %lld bytes", (long long)metaDataSize);
- }
-
- usleep(200000);
- }
-
- mLock.lock();
- }
-
- if (mFlags & PREPARE_CANCELLED) {
- ALOGI("Prepare cancelled while waiting for initial cache fill.");
- return UNKNOWN_ERROR;
- }
- }
- } else {
- dataSource = DataSource::CreateFromURI(
- mHTTPService, mUri.string(), &mUriHeaders);
- }
-
- if (dataSource == NULL) {
- return UNKNOWN_ERROR;
- }
-
- sp<MediaExtractor> extractor;
-
- if (isWidevineStreaming) {
- String8 mimeType;
- float confidence;
- sp<AMessage> dummy;
- bool success;
-
- // SniffWVM is potentially blocking since it may require network access.
- // Do not call it with mLock held.
- mLock.unlock();
- success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
- mLock.lock();
-
- if (!success
- || strcasecmp(
- mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
- return ERROR_UNSUPPORTED;
- }
-
- mWVMExtractor = new WVMExtractor(dataSource);
- mWVMExtractor->setAdaptiveStreamingMode(true);
- if (mUIDValid)
- mWVMExtractor->setUID(mUID);
- extractor = mWVMExtractor;
- } else {
- extractor = MediaExtractor::Create(
- dataSource, sniffedMIME.empty() ? NULL : sniffedMIME.c_str());
-
- if (extractor == NULL) {
- return UNKNOWN_ERROR;
- }
- }
-
- if (extractor->getDrmFlag()) {
- checkDrmStatus(dataSource);
- }
-
- status_t err = setDataSource_l(extractor);
-
- if (err != OK) {
- mWVMExtractor.clear();
-
- return err;
- }
-
- return OK;
-}
-
-void AwesomePlayer::abortPrepare(status_t err) {
- CHECK(err != OK);
-
- if (mIsAsyncPrepare) {
- notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
- }
-
- mPrepareResult = err;
- modifyFlags((PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED), CLEAR);
- mAsyncPrepareEvent = NULL;
- mPreparedCondition.broadcast();
- mAudioTearDown = false;
-}
-
-// static
-bool AwesomePlayer::ContinuePreparation(void *cookie) {
- AwesomePlayer *me = static_cast<AwesomePlayer *>(cookie);
-
- return (me->mFlags & PREPARE_CANCELLED) == 0;
-}
-
-void AwesomePlayer::onPrepareAsyncEvent() {
- Mutex::Autolock autoLock(mLock);
- beginPrepareAsync_l();
-}
-
-void AwesomePlayer::beginPrepareAsync_l() {
- if (mFlags & PREPARE_CANCELLED) {
- ALOGI("prepare was cancelled before doing anything");
- abortPrepare(UNKNOWN_ERROR);
- return;
- }
-
- if (mUri.size() > 0) {
- status_t err = finishSetDataSource_l();
-
- if (err != OK) {
- abortPrepare(err);
- return;
- }
- }
-
- if (mVideoTrack != NULL && mVideoSource == NULL) {
- status_t err = initVideoDecoder();
-
- if (err != OK) {
- abortPrepare(err);
- return;
- }
- }
-
- if (mAudioTrack != NULL && mAudioSource == NULL) {
- status_t err = initAudioDecoder();
-
- if (err != OK) {
- abortPrepare(err);
- return;
- }
- }
-
- modifyFlags(PREPARING_CONNECTED, SET);
-
- if (isStreamingHTTP()) {
- postBufferingEvent_l();
- } else {
- finishAsyncPrepare_l();
- }
-}
-
-void AwesomePlayer::finishAsyncPrepare_l() {
- if (mIsAsyncPrepare) {
- if (mVideoSource == NULL) {
- notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
- } else {
- notifyVideoSize_l();
- }
-
- notifyListener_l(MEDIA_PREPARED);
- }
-
- mPrepareResult = OK;
- modifyFlags((PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED), CLEAR);
- modifyFlags(PREPARED, SET);
- mAsyncPrepareEvent = NULL;
- mPreparedCondition.broadcast();
-
- if (mAudioTearDown) {
- if (mPrepareResult == OK) {
- if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
- seekTo_l(mAudioTearDownPosition);
- }
-
- if (mAudioTearDownWasPlaying) {
- modifyFlags(CACHE_UNDERRUN, CLEAR);
- play_l();
- }
- }
- mAudioTearDown = false;
- }
-}
-
-uint32_t AwesomePlayer::flags() const {
- return mExtractorFlags;
-}
-
-void AwesomePlayer::postAudioEOS(int64_t delayUs) {
- postCheckAudioStatusEvent(delayUs);
-}
-
-void AwesomePlayer::postAudioSeekComplete() {
- postCheckAudioStatusEvent(0);
-}
-
-void AwesomePlayer::postAudioTearDown() {
- postAudioTearDownEvent(0);
-}
-
-status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
- switch (key) {
- case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS:
- {
- return setCacheStatCollectFreq(request);
- }
- default:
- {
- return ERROR_UNSUPPORTED;
- }
- }
-}
-
-status_t AwesomePlayer::setCacheStatCollectFreq(const Parcel &request) {
- if (mCachedSource != NULL) {
- int32_t freqMs = request.readInt32();
- ALOGD("Request to keep cache stats in the past %d ms",
- freqMs);
- return mCachedSource->setCacheStatCollectFreq(freqMs);
- }
- return ERROR_UNSUPPORTED;
-}
-
-status_t AwesomePlayer::getParameter(int key, Parcel *reply) {
- switch (key) {
- case KEY_PARAMETER_AUDIO_CHANNEL_COUNT:
- {
- int32_t channelCount;
- if (mAudioTrack == 0 ||
- !mAudioTrack->getFormat()->findInt32(kKeyChannelCount, &channelCount)) {
- channelCount = 0;
- }
- reply->writeInt32(channelCount);
- }
- return OK;
- default:
- {
- return ERROR_UNSUPPORTED;
- }
- }
-}
-
-status_t AwesomePlayer::setPlaybackSettings(const AudioPlaybackRate &rate) {
- Mutex::Autolock autoLock(mLock);
- // cursory sanity check for non-audio and paused cases
- if ((rate.mSpeed != 0.f && rate.mSpeed < AUDIO_TIMESTRETCH_SPEED_MIN)
- || rate.mSpeed > AUDIO_TIMESTRETCH_SPEED_MAX
- || rate.mPitch < AUDIO_TIMESTRETCH_SPEED_MIN
- || rate.mPitch > AUDIO_TIMESTRETCH_SPEED_MAX) {
- return BAD_VALUE;
- }
-
- status_t err = OK;
- if (rate.mSpeed == 0.f) {
- if (mFlags & PLAYING) {
- modifyFlags(CACHE_UNDERRUN, CLEAR); // same as pause
- err = pause_l();
- }
- if (err == OK) {
- // save settings (using old speed) in case player is resumed
- AudioPlaybackRate newRate = rate;
- newRate.mSpeed = mPlaybackSettings.mSpeed;
- mPlaybackSettings = newRate;
- }
- return err;
- }
- if (mAudioPlayer != NULL) {
- err = mAudioPlayer->setPlaybackRate(rate);
- }
- if (err == OK) {
- mPlaybackSettings = rate;
- if (!(mFlags & PLAYING)) {
- play_l();
- }
- }
- return err;
-}
-
-status_t AwesomePlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
- if (mAudioPlayer != NULL) {
- status_t err = mAudioPlayer->getPlaybackRate(rate);
- if (err == OK) {
- mPlaybackSettings = *rate;
- Mutex::Autolock autoLock(mLock);
- if (!(mFlags & PLAYING)) {
- rate->mSpeed = 0.f;
- }
- }
- return err;
- }
- *rate = mPlaybackSettings;
- return OK;
-}
-
-status_t AwesomePlayer::getTrackInfo(Parcel *reply) const {
- Mutex::Autolock autoLock(mLock);
- size_t trackCount = mExtractor->countTracks();
- if (mTextDriver != NULL) {
- trackCount += mTextDriver->countExternalTracks();
- }
-
- reply->writeInt32(trackCount);
- for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
- sp<MetaData> meta = mExtractor->getTrackMetaData(i);
-
- const char *_mime;
- CHECK(meta->findCString(kKeyMIMEType, &_mime));
-
- String8 mime = String8(_mime);
-
- reply->writeInt32(2); // 2 fields
-
- if (!strncasecmp(mime.string(), "video/", 6)) {
- reply->writeInt32(MEDIA_TRACK_TYPE_VIDEO);
- } else if (!strncasecmp(mime.string(), "audio/", 6)) {
- reply->writeInt32(MEDIA_TRACK_TYPE_AUDIO);
- } else if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_TEXT_3GPP)) {
- reply->writeInt32(MEDIA_TRACK_TYPE_TIMEDTEXT);
- } else {
- reply->writeInt32(MEDIA_TRACK_TYPE_UNKNOWN);
- }
-
- const char *lang;
- if (!meta->findCString(kKeyMediaLanguage, &lang)) {
- lang = "und";
- }
- reply->writeString16(String16(lang));
- }
-
- if (mTextDriver != NULL) {
- mTextDriver->getExternalTrackInfo(reply);
- }
- return OK;
-}
-
-status_t AwesomePlayer::selectAudioTrack_l(
- const sp<MediaSource>& source, size_t trackIndex) {
-
- ALOGI("selectAudioTrack_l: trackIndex=%zu, mFlags=0x%x", trackIndex, mFlags);
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- if ((ssize_t)trackIndex == mActiveAudioTrackIndex) {
- ALOGI("Track %zu is active. Does nothing.", trackIndex);
- return OK;
- }
- //mStats.mFlags = mFlags;
- }
-
- if (mSeeking != NO_SEEK) {
- ALOGE("Selecting a track while seeking is not supported");
- return ERROR_UNSUPPORTED;
- }
-
- if ((mFlags & PREPARED) == 0) {
- ALOGE("Data source has not finished preparation");
- return ERROR_UNSUPPORTED;
- }
-
- CHECK(source != NULL);
- bool wasPlaying = (mFlags & PLAYING) != 0;
-
- pause_l();
-
- int64_t curTimeUs;
- CHECK_EQ(getPosition(&curTimeUs), (status_t)OK);
-
- if ((mAudioPlayer == NULL || !(mFlags & AUDIOPLAYER_STARTED))
- && mAudioSource != NULL) {
- // If we had an audio player, it would have effectively
- // taken possession of the audio source and stopped it when
- // _it_ is stopped. Otherwise this is still our responsibility.
- mAudioSource->stop();
- }
- mAudioSource.clear();
- mOmxSource.clear();
-
- mTimeSource = NULL;
-
- delete mAudioPlayer;
- mAudioPlayer = NULL;
-
- modifyFlags(AUDIOPLAYER_STARTED, CLEAR);
-
- setAudioSource(source);
-
- modifyFlags(AUDIO_AT_EOS, CLEAR);
- modifyFlags(AT_EOS, CLEAR);
-
- status_t err;
- if ((err = initAudioDecoder()) != OK) {
- ALOGE("Failed to init audio decoder: 0x%x", err);
- return err;
- }
-
- mSeekNotificationSent = true;
- seekTo_l(curTimeUs);
-
- if (wasPlaying) {
- play_l();
- }
-
- mActiveAudioTrackIndex = trackIndex;
-
- return OK;
-}
-
-status_t AwesomePlayer::selectTrack(size_t trackIndex, bool select) {
- ATRACE_CALL();
- ALOGV("selectTrack: trackIndex = %zu and select=%d", trackIndex, select);
- Mutex::Autolock autoLock(mLock);
- size_t trackCount = mExtractor->countTracks();
- if (mTextDriver != NULL) {
- trackCount += mTextDriver->countExternalTracks();
- }
- if (trackIndex >= trackCount) {
- ALOGE("Track index (%zu) is out of range [0, %zu)", trackIndex, trackCount);
- return ERROR_OUT_OF_RANGE;
- }
-
- bool isAudioTrack = false;
- if (trackIndex < mExtractor->countTracks()) {
- sp<MetaData> meta = mExtractor->getTrackMetaData(trackIndex);
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
- isAudioTrack = !strncasecmp(mime, "audio/", 6);
-
- if (!isAudioTrack && strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) != 0) {
- ALOGE("Track %zu is not either audio or timed text", trackIndex);
- return ERROR_UNSUPPORTED;
- }
- }
-
- if (isAudioTrack) {
- if (!select) {
- ALOGE("Deselect an audio track (%zu) is not supported", trackIndex);
- return ERROR_UNSUPPORTED;
- }
- return selectAudioTrack_l(mExtractor->getTrack(trackIndex), trackIndex);
- }
-
- // Timed text track handling
- if (mTextDriver == NULL) {
- return INVALID_OPERATION;
- }
-
- status_t err = OK;
- if (select) {
- err = mTextDriver->selectTrack(trackIndex);
- if (err == OK) {
- modifyFlags(TEXTPLAYER_INITIALIZED, SET);
- if (mFlags & PLAYING && !(mFlags & TEXT_RUNNING)) {
- mTextDriver->start();
- modifyFlags(TEXT_RUNNING, SET);
- }
- }
- } else {
- err = mTextDriver->unselectTrack(trackIndex);
- if (err == OK) {
- modifyFlags(TEXTPLAYER_INITIALIZED, CLEAR);
- modifyFlags(TEXT_RUNNING, CLEAR);
- }
- }
- return err;
-}
-
-size_t AwesomePlayer::countTracks() const {
- return mExtractor->countTracks() + mTextDriver->countExternalTracks();
-}
-
-status_t AwesomePlayer::setVideoScalingMode(int32_t mode) {
- Mutex::Autolock lock(mLock);
- return setVideoScalingMode_l(mode);
-}
-
-status_t AwesomePlayer::setVideoScalingMode_l(int32_t mode) {
- mVideoScalingMode = mode;
- if (mNativeWindow != NULL) {
- status_t err = native_window_set_scaling_mode(
- mNativeWindow.get(), mVideoScalingMode);
- if (err != OK) {
- ALOGW("Failed to set scaling mode: %d", err);
- }
- return err;
- }
- return OK;
-}
-
-status_t AwesomePlayer::invoke(const Parcel &request, Parcel *reply) {
- ATRACE_CALL();
- if (NULL == reply) {
- return android::BAD_VALUE;
- }
- int32_t methodId;
- status_t ret = request.readInt32(&methodId);
- if (ret != android::OK) {
- return ret;
- }
- switch(methodId) {
- case INVOKE_ID_SET_VIDEO_SCALING_MODE:
- {
- int mode = request.readInt32();
- return setVideoScalingMode(mode);
- }
-
- case INVOKE_ID_GET_TRACK_INFO:
- {
- return getTrackInfo(reply);
- }
- case INVOKE_ID_ADD_EXTERNAL_SOURCE:
- {
- Mutex::Autolock autoLock(mLock);
- if (mTextDriver == NULL) {
- mTextDriver = new TimedTextDriver(mListener, mHTTPService);
- }
- // String values written in Parcel are UTF-16 values.
- String8 uri(request.readString16());
- String8 mimeType(request.readString16());
- size_t nTracks = countTracks();
- return mTextDriver->addOutOfBandTextSource(nTracks, uri, mimeType);
- }
- case INVOKE_ID_ADD_EXTERNAL_SOURCE_FD:
- {
- Mutex::Autolock autoLock(mLock);
- if (mTextDriver == NULL) {
- mTextDriver = new TimedTextDriver(mListener, mHTTPService);
- }
- int fd = request.readFileDescriptor();
- off64_t offset = request.readInt64();
- off64_t length = request.readInt64();
- String8 mimeType(request.readString16());
- size_t nTracks = countTracks();
- return mTextDriver->addOutOfBandTextSource(
- nTracks, fd, offset, length, mimeType);
- }
- case INVOKE_ID_SELECT_TRACK:
- {
- int trackIndex = request.readInt32();
- return selectTrack(trackIndex, true /* select */);
- }
- case INVOKE_ID_UNSELECT_TRACK:
- {
- int trackIndex = request.readInt32();
- return selectTrack(trackIndex, false /* select */);
- }
- default:
- {
- return ERROR_UNSUPPORTED;
- }
- }
- // It will not reach here.
- return OK;
-}
-
-bool AwesomePlayer::isStreamingHTTP() const {
- return mCachedSource != NULL || mWVMExtractor != NULL;
-}
-
-status_t AwesomePlayer::dump(
- int fd, const Vector<String16> & /* args */) const {
- Mutex::Autolock autoLock(mStatsLock);
-
- FILE *out = fdopen(dup(fd), "w");
-
- fprintf(out, " AwesomePlayer\n");
- if (mStats.mFd < 0) {
- fprintf(out, " URI(%s)", uriDebugString(mUri, mFlags & INCOGNITO).c_str());
- } else {
- fprintf(out, " fd(%d)", mStats.mFd);
- }
-
- fprintf(out, ", flags(0x%08x)", mStats.mFlags);
-
- if (mStats.mBitrate >= 0) {
- fprintf(out, ", bitrate(%" PRId64 " bps)", mStats.mBitrate);
- }
-
- fprintf(out, "\n");
-
- for (size_t i = 0; i < mStats.mTracks.size(); ++i) {
- const TrackStat &stat = mStats.mTracks.itemAt(i);
-
- fprintf(out, " Track %zu\n", i + 1);
- fprintf(out, " MIME(%s)", stat.mMIME.string());
-
- if (!stat.mDecoderName.isEmpty()) {
- fprintf(out, ", decoder(%s)", stat.mDecoderName.string());
- }
-
- fprintf(out, "\n");
-
- if ((ssize_t)i == mStats.mVideoTrackIndex) {
- fprintf(out,
- " videoDimensions(%d x %d), "
- "numVideoFramesDecoded(%" PRId64 "), "
- "numVideoFramesDropped(%" PRId64 ")\n",
- mStats.mVideoWidth,
- mStats.mVideoHeight,
- mStats.mNumVideoFramesDecoded,
- mStats.mNumVideoFramesDropped);
- }
- }
-
- fclose(out);
- out = NULL;
-
- return OK;
-}
-
-void AwesomePlayer::modifyFlags(unsigned value, FlagMode mode) {
- switch (mode) {
- case SET:
- mFlags |= value;
- break;
- case CLEAR:
- if ((value & CACHE_UNDERRUN) && (mFlags & CACHE_UNDERRUN)) {
- notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
- }
- mFlags &= ~value;
- break;
- case ASSIGN:
- mFlags = value;
- break;
- default:
- TRESPASS();
- }
-
- {
- Mutex::Autolock autoLock(mStatsLock);
- mStats.mFlags = mFlags;
- }
-}
-
-void AwesomePlayer::onAudioTearDownEvent() {
-
- Mutex::Autolock autoLock(mLock);
- if (!mAudioTearDownEventPending) {
- return;
- }
- mAudioTearDownEventPending = false;
-
- ALOGV("onAudioTearDownEvent");
-
- // stream info is cleared by reset_l() so copy what we need
- mAudioTearDownWasPlaying = (mFlags & PLAYING);
- KeyedVector<String8, String8> uriHeaders(mUriHeaders);
- sp<DataSource> fileSource(mFileSource);
-
- mStatsLock.lock();
- String8 uri(mStats.mURI);
- mStatsLock.unlock();
-
- // get current position so we can start recreated stream from here
- getPosition(&mAudioTearDownPosition);
-
- sp<IMediaHTTPService> savedHTTPService = mHTTPService;
-
- bool wasLooping = mFlags & LOOPING;
- // Reset and recreate
- reset_l();
-
- status_t err;
-
- if (fileSource != NULL) {
- mFileSource = fileSource;
- err = setDataSource_l(fileSource);
- } else {
- err = setDataSource_l(savedHTTPService, uri, &uriHeaders);
- }
-
- mFlags |= PREPARING;
- if ( err != OK ) {
- // This will force beingPrepareAsync_l() to notify
- // a MEDIA_ERROR to the client and abort the prepare
- mFlags |= PREPARE_CANCELLED;
- }
- if (wasLooping) {
- mFlags |= LOOPING;
- }
-
- mAudioTearDown = true;
- mIsAsyncPrepare = true;
-
- // Call prepare for the host decoding
- beginPrepareAsync_l();
-}
-
-} // namespace android
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..01b29fa 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 */);
@@ -194,6 +193,22 @@
}
// static
+status_t MediaCodec::QueryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps /* nonnull */) {
+ // TRICKY: this method is used by MediaCodecList/Info during its
+ // initialization. As such, we cannot create a MediaCodec instance
+ // because that requires an initialized MediaCodecList.
+
+ sp<CodecBase> codec = GetCodecBase(name);
+ if (codec == NULL) {
+ return NAME_NOT_FOUND;
+ }
+
+ return codec->queryCapabilities(name, mime, isEncoder, caps);
+}
+
+// static
sp<PersistentSurface> MediaCodec::CreatePersistentInputSurface() {
OMXClient client;
CHECK_EQ(client.connect(), (status_t)OK);
@@ -299,6 +314,18 @@
response->postReply(replyID);
}
+//static
+sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, bool nameIsType) {
+ // at this time only ACodec specifies a mime type.
+ if (nameIsType || name.startsWithIgnoreCase("omx.")) {
+ return new ACodec;
+ } else if (name.startsWithIgnoreCase("android.filter.")) {
+ return new MediaFilter;
+ } else {
+ return NULL;
+ }
+}
+
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
mResourceManagerService->init();
@@ -312,12 +339,8 @@
// we need to invest in an extra looper to free the main event
// queue.
- if (nameIsType || !strncasecmp(name.c_str(), "omx.", 4)) {
- mCodec = new ACodec;
- } else if (!nameIsType
- && !strncasecmp(name.c_str(), "android.filter.", 15)) {
- mCodec = new MediaFilter;
- } else {
+ mCodec = GetCodecBase(name, nameIsType);
+ if (mCodec == NULL) {
return NAME_NOT_FOUND;
}
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index c657195..0fb5072 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -30,10 +30,11 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
#include <sys/stat.h>
#include <utils/threads.h>
@@ -751,15 +752,19 @@
ALOGV("initializeCapabilities %s:%s",
mCurrentInfo->mName.c_str(), type);
- CodecCapabilities caps;
- status_t err = QueryCodec(
- mOMX,
- mCurrentInfo->mName.c_str(),
+ sp<MediaCodecInfo::Capabilities> caps;
+ status_t err = MediaCodec::QueryCapabilities(
+ mCurrentInfo->mName,
type,
mCurrentInfo->mIsEncoder,
&caps);
if (err != OK) {
return err;
+ } else if (caps == NULL) {
+ ALOGE("MediaCodec::QueryCapabilities returned OK but no capabilities for '%s':'%s':'%s'",
+ mCurrentInfo->mName.c_str(), type,
+ mCurrentInfo->mIsEncoder ? "encoder" : "decoder");
+ return UNKNOWN_ERROR;
}
return mCurrentInfo->initializeCapabilities(caps);
@@ -1115,4 +1120,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/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index f24cf3a..8c67618 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -110,7 +110,8 @@
// at the container mime type.
// The cryptoPluginMode ensures that the extractor will actually
// give us data in a call to MediaSource::read(), unlike its
- // default mode that we use from AwesomePlayer.
+ // default mode that we used in AwesomePlayer.
+ // TODO: change default mode
static_cast<WVMExtractor *>(mImpl.get())->setCryptoPluginMode(true);
} else if (mImpl->getDrmFlag()) {
// For all other drm content, we don't want to expose decrypted
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
deleted file mode 100644
index 4c39194..0000000
--- a/media/libstagefright/OMXCodec.cpp
+++ /dev/null
@@ -1,4457 +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 <inttypes.h>
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "OMXCodec"
-
-#ifdef __LP64__
-#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
-#endif
-
-#include <utils/Log.h>
-
-#include "include/AACEncoder.h"
-
-#include "include/ESDS.h"
-
-#include <binder/IServiceManager.h>
-#include <binder/MemoryDealer.h>
-#include <binder/ProcessState.h>
-#include <HardwareAPI.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/IMediaPlayerService.h>
-#include <media/stagefright/ACodec.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaCodecList.h>
-#include <media/stagefright/MediaExtractor.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/OMXCodec.h>
-#include <media/stagefright/SurfaceUtils.h>
-#include <media/stagefright/Utils.h>
-#include <media/stagefright/SkipCutBuffer.h>
-#include <utils/Vector.h>
-
-#include <OMX_AudioExt.h>
-#include <OMX_Component.h>
-#include <OMX_IndexExt.h>
-#include <OMX_VideoExt.h>
-#include <OMX_AsString.h>
-
-#include "include/avc_utils.h"
-
-namespace android {
-
-// Treat time out as an error if we have not received any output
-// buffers after 3 seconds.
-const static int64_t kBufferFilledEventTimeOutNs = 3000000000LL;
-
-// OMX Spec defines less than 50 color formats. If the query for
-// color format is executed for more than kMaxColorFormatSupported,
-// the query will fail to avoid looping forever.
-// 1000 is more than enough for us to tell whether the omx
-// component in question is buggy or not.
-const static uint32_t kMaxColorFormatSupported = 1000;
-
-#define FACTORY_CREATE_ENCODER(name) \
-static sp<MediaSource> Make##name(const sp<MediaSource> &source, const sp<MetaData> &meta) { \
- return new name(source, meta); \
-}
-
-#define FACTORY_REF(name) { #name, Make##name },
-
-FACTORY_CREATE_ENCODER(AACEncoder)
-
-static sp<MediaSource> InstantiateSoftwareEncoder(
- const char *name, const sp<MediaSource> &source,
- const sp<MetaData> &meta) {
- struct FactoryInfo {
- const char *name;
- sp<MediaSource> (*CreateFunc)(const sp<MediaSource> &, const sp<MetaData> &);
- };
-
- static const FactoryInfo kFactoryInfo[] = {
- FACTORY_REF(AACEncoder)
- };
- for (size_t i = 0;
- i < sizeof(kFactoryInfo) / sizeof(kFactoryInfo[0]); ++i) {
- if (!strcmp(name, kFactoryInfo[i].name)) {
- return (*kFactoryInfo[i].CreateFunc)(source, meta);
- }
- }
-
- return NULL;
-}
-
-#undef FACTORY_CREATE_ENCODER
-#undef FACTORY_REF
-
-#define CODEC_LOGI(x, ...) ALOGI("[%s] " x, mComponentName, ##__VA_ARGS__)
-#define CODEC_LOGV(x, ...) ALOGV("[%s] " x, mComponentName, ##__VA_ARGS__)
-#define CODEC_LOGW(x, ...) ALOGW("[%s] " x, mComponentName, ##__VA_ARGS__)
-#define CODEC_LOGE(x, ...) ALOGE("[%s] " x, mComponentName, ##__VA_ARGS__)
-
-struct OMXCodecObserver : public BnOMXObserver {
- OMXCodecObserver() {
- }
-
- void setCodec(const sp<OMXCodec> &target) {
- mTarget = target;
- }
-
- // from IOMXObserver
- virtual void onMessages(const std::list<omx_message> &messages) {
- sp<OMXCodec> codec = mTarget.promote();
-
- if (codec.get() != NULL) {
- Mutex::Autolock autoLock(codec->mLock);
- for (std::list<omx_message>::const_iterator it = messages.cbegin();
- it != messages.cend(); ++it) {
- codec->on_message(*it);
- }
- codec.clear();
- }
- }
-
-protected:
- virtual ~OMXCodecObserver() {}
-
-private:
- wp<OMXCodec> mTarget;
-
- OMXCodecObserver(const OMXCodecObserver &);
- OMXCodecObserver &operator=(const OMXCodecObserver &);
-};
-
-template<class T>
-static void InitOMXParams(T *params) {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(OMX_PTR) == 4); // check OMX_PTR is 4 bytes.
- params->nSize = sizeof(T);
- params->nVersion.s.nVersionMajor = 1;
- params->nVersion.s.nVersionMinor = 0;
- params->nVersion.s.nRevision = 0;
- params->nVersion.s.nStep = 0;
-}
-
-static bool IsSoftwareCodec(const char *componentName) {
- if (!strncmp("OMX.google.", componentName, 11)) {
- return true;
- }
-
- if (!strncmp("OMX.", componentName, 4)) {
- return false;
- }
-
- return true;
-}
-
-// A sort order in which OMX software codecs are first, followed
-// by other (non-OMX) software codecs, followed by everything else.
-static int CompareSoftwareCodecsFirst(
- const OMXCodec::CodecNameAndQuirks *elem1,
- const OMXCodec::CodecNameAndQuirks *elem2) {
- bool isOMX1 = !strncmp(elem1->mName.string(), "OMX.", 4);
- bool isOMX2 = !strncmp(elem2->mName.string(), "OMX.", 4);
-
- bool isSoftwareCodec1 = IsSoftwareCodec(elem1->mName.string());
- bool isSoftwareCodec2 = IsSoftwareCodec(elem2->mName.string());
-
- if (isSoftwareCodec1) {
- if (!isSoftwareCodec2) { return -1; }
-
- if (isOMX1) {
- if (isOMX2) { return 0; }
-
- return -1;
- } else {
- if (isOMX2) { return 0; }
-
- return 1;
- }
-
- return -1;
- }
-
- if (isSoftwareCodec2) {
- return 1;
- }
-
- return 0;
-}
-
-// static
-void OMXCodec::findMatchingCodecs(
- const char *mime,
- bool createEncoder, const char *matchComponentName,
- uint32_t flags,
- Vector<CodecNameAndQuirks> *matchingCodecs) {
- matchingCodecs->clear();
-
- const sp<IMediaCodecList> list = MediaCodecList::getInstance();
- if (list == NULL) {
- return;
- }
-
- size_t index = 0;
- for (;;) {
- ssize_t matchIndex =
- list->findCodecByType(mime, createEncoder, index);
-
- if (matchIndex < 0) {
- break;
- }
-
- index = matchIndex + 1;
-
- const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
- CHECK(info != NULL);
- const char *componentName = info->getCodecName();
-
- // If a specific codec is requested, skip the non-matching ones.
- if (matchComponentName && strcmp(componentName, matchComponentName)) {
- continue;
- }
-
- // When requesting software-only codecs, only push software codecs
- // When requesting hardware-only codecs, only push hardware codecs
- // When there is request neither for software-only nor for
- // hardware-only codecs, push all codecs
- if (((flags & kSoftwareCodecsOnly) && IsSoftwareCodec(componentName)) ||
- ((flags & kHardwareCodecsOnly) && !IsSoftwareCodec(componentName)) ||
- (!(flags & (kSoftwareCodecsOnly | kHardwareCodecsOnly)))) {
-
- ssize_t index = matchingCodecs->add();
- CodecNameAndQuirks *entry = &matchingCodecs->editItemAt(index);
- entry->mName = String8(componentName);
- entry->mQuirks = getComponentQuirks(info);
-
- ALOGV("matching '%s' quirks 0x%08x",
- entry->mName.string(), entry->mQuirks);
- }
- }
-
- if (flags & kPreferSoftwareCodecs) {
- matchingCodecs->sort(CompareSoftwareCodecsFirst);
- }
-}
-
-// static
-uint32_t OMXCodec::getComponentQuirks(
- const sp<MediaCodecInfo> &info) {
- uint32_t quirks = 0;
- if (info->hasQuirk("requires-allocate-on-input-ports")) {
- quirks |= kRequiresAllocateBufferOnInputPorts;
- }
- if (info->hasQuirk("requires-allocate-on-output-ports")) {
- quirks |= kRequiresAllocateBufferOnOutputPorts;
- }
- if (info->hasQuirk("output-buffers-are-unreadable")) {
- quirks |= kOutputBuffersAreUnreadable;
- }
-
- return quirks;
-}
-
-// 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,
- const sp<MediaSource> &source,
- const char *matchComponentName,
- uint32_t flags,
- const sp<ANativeWindow> &nativeWindow) {
- int32_t requiresSecureBuffers;
- if (source->getFormat()->findInt32(
- kKeyRequiresSecureBuffers,
- &requiresSecureBuffers)
- && requiresSecureBuffers) {
- flags |= kIgnoreCodecSpecificData;
- flags |= kUseSecureInputBuffers;
- }
-
- const char *mime;
- bool success = meta->findCString(kKeyMIMEType, &mime);
- CHECK(success);
-
- Vector<CodecNameAndQuirks> matchingCodecs;
- findMatchingCodecs(
- mime, createEncoder, matchComponentName, flags, &matchingCodecs);
-
- if (matchingCodecs.isEmpty()) {
- ALOGV("No matching codecs! (mime: %s, createEncoder: %s, "
- "matchComponentName: %s, flags: 0x%x)",
- mime, createEncoder ? "true" : "false", matchComponentName, flags);
- return NULL;
- }
-
- sp<OMXCodecObserver> observer = new OMXCodecObserver;
- IOMX::node_id node = 0;
-
- for (size_t i = 0; i < matchingCodecs.size(); ++i) {
- const char *componentNameBase = matchingCodecs[i].mName.string();
- uint32_t quirks = matchingCodecs[i].mQuirks;
- const char *componentName = componentNameBase;
-
- AString tmp;
- if (flags & kUseSecureInputBuffers) {
- tmp = componentNameBase;
- tmp.append(".secure");
-
- componentName = tmp.c_str();
- }
-
- if (createEncoder) {
- sp<MediaSource> softwareCodec =
- InstantiateSoftwareEncoder(componentName, source, meta);
-
- if (softwareCodec != NULL) {
- ALOGV("Successfully allocated software codec '%s'", componentName);
-
- return softwareCodec;
- }
- }
-
- ALOGV("Attempting to allocate OMX node '%s'", componentName);
-
- if (!createEncoder
- && (quirks & kOutputBuffersAreUnreadable)
- && (flags & kClientNeedsFramebuffer)) {
- if (strncmp(componentName, "OMX.SEC.", 8)) {
- // For OMX.SEC.* decoders we can enable a special mode that
- // gives the client access to the framebuffer contents.
-
- ALOGW("Component '%s' does not give the client access to "
- "the framebuffer contents. Skipping.",
- componentName);
-
- continue;
- }
- }
-
- status_t err = omx->allocateNode(componentName, observer, &node);
- if (err == OK) {
- ALOGV("Successfully allocated OMX node '%s'", componentName);
-
- sp<OMXCodec> codec = new OMXCodec(
- omx, node, quirks, flags,
- createEncoder, mime, componentName,
- source, nativeWindow);
-
- observer->setCodec(codec);
-
- err = codec->configureCodec(meta);
- if (err == OK) {
- return codec;
- }
-
- ALOGV("Failed to configure codec '%s'", componentName);
- }
- }
-
- return NULL;
-}
-
-status_t OMXCodec::parseHEVCCodecSpecificData(
- const void *data, size_t size,
- unsigned *profile, unsigned *level) {
- const uint8_t *ptr = (const uint8_t *)data;
-
- // verify minimum size and configurationVersion == 1.
- if (size < 23 || ptr[0] != 1) {
- return ERROR_MALFORMED;
- }
-
- *profile = (ptr[1] & 31);
- *level = ptr[12];
-
- ptr += 22;
- size -= 22;
-
- size_t numofArrays = (char)ptr[0];
- ptr += 1;
- size -= 1;
- size_t j = 0, i = 0;
- for (i = 0; i < numofArrays; i++) {
- if (size < 3) {
- return ERROR_MALFORMED;
- }
- ptr += 1;
- size -= 1;
-
- // Num of nals
- size_t numofNals = U16_AT(ptr);
- ptr += 2;
- size -= 2;
-
- for (j = 0;j < numofNals;j++) {
- if (size < 2) {
- return ERROR_MALFORMED;
- }
-
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- if (size < length) {
- return ERROR_MALFORMED;
- }
- addCodecSpecificData(ptr, length);
-
- ptr += length;
- size -= length;
- }
- }
- return OK;
-}
-
-status_t OMXCodec::parseAVCCodecSpecificData(
- const void *data, size_t size,
- unsigned *profile, unsigned *level) {
- const uint8_t *ptr = (const uint8_t *)data;
-
- // verify minimum size and configurationVersion == 1.
- if (size < 7 || ptr[0] != 1) {
- return ERROR_MALFORMED;
- }
-
- *profile = ptr[1];
- *level = ptr[3];
-
- // There is decodable content out there that fails the following
- // assertion, let's be lenient for now...
- // CHECK((ptr[4] >> 2) == 0x3f); // reserved
-
- size_t lengthSize __unused = 1 + (ptr[4] & 3);
-
- // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
- // violates it...
- // CHECK((ptr[5] >> 5) == 7); // reserved
-
- size_t numSeqParameterSets = ptr[5] & 31;
-
- ptr += 6;
- size -= 6;
-
- for (size_t i = 0; i < numSeqParameterSets; ++i) {
- if (size < 2) {
- return ERROR_MALFORMED;
- }
-
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- if (size < length) {
- return ERROR_MALFORMED;
- }
-
- addCodecSpecificData(ptr, length);
-
- ptr += length;
- size -= length;
- }
-
- if (size < 1) {
- return ERROR_MALFORMED;
- }
-
- size_t numPictureParameterSets = *ptr;
- ++ptr;
- --size;
-
- for (size_t i = 0; i < numPictureParameterSets; ++i) {
- if (size < 2) {
- return ERROR_MALFORMED;
- }
-
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- if (size < length) {
- return ERROR_MALFORMED;
- }
-
- addCodecSpecificData(ptr, length);
-
- ptr += length;
- size -= length;
- }
-
- return OK;
-}
-
-status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
- ALOGV("configureCodec protected=%d",
- (mFlags & kEnableGrallocUsageProtected) ? 1 : 0);
-
- if (!(mFlags & kIgnoreCodecSpecificData)) {
- uint32_t type;
- const void *data;
- size_t size;
- if (meta->findData(kKeyESDS, &type, &data, &size)) {
- ESDS esds((const char *)data, size);
- CHECK_EQ(esds.InitCheck(), (status_t)OK);
-
- const void *codec_specific_data;
- size_t codec_specific_data_size;
- esds.getCodecSpecificInfo(
- &codec_specific_data, &codec_specific_data_size);
-
- addCodecSpecificData(
- codec_specific_data, codec_specific_data_size);
- } else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
- // Parse the AVCDecoderConfigurationRecord
-
- unsigned profile, level;
- status_t err;
- if ((err = parseAVCCodecSpecificData(
- data, size, &profile, &level)) != OK) {
- ALOGE("Malformed AVC codec specific data.");
- return err;
- }
-
- CODEC_LOGI(
- "AVC profile = %u (%s), level = %u",
- profile, AVCProfileToString(profile), level);
- } else if (meta->findData(kKeyHVCC, &type, &data, &size)) {
- // Parse the HEVCDecoderConfigurationRecord
-
- unsigned profile, level;
- status_t err;
- if ((err = parseHEVCCodecSpecificData(
- data, size, &profile, &level)) != OK) {
- ALOGE("Malformed HEVC codec specific data.");
- return err;
- }
-
- CODEC_LOGI(
- "HEVC profile = %u , level = %u",
- profile, level);
- } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
- addCodecSpecificData(data, size);
-
- CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size));
- addCodecSpecificData(data, size);
- } else if (meta->findData(kKeyOpusHeader, &type, &data, &size)) {
- addCodecSpecificData(data, size);
-
- CHECK(meta->findData(kKeyOpusCodecDelay, &type, &data, &size));
- addCodecSpecificData(data, size);
- CHECK(meta->findData(kKeyOpusSeekPreRoll, &type, &data, &size));
- addCodecSpecificData(data, size);
- }
- }
-
- int32_t bitRate = 0;
- if (mIsEncoder) {
- CHECK(meta->findInt32(kKeyBitRate, &bitRate));
- }
- if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mMIME)) {
- setAMRFormat(false /* isWAMR */, bitRate);
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) {
- setAMRFormat(true /* isWAMR */, bitRate);
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) {
- int32_t numChannels, sampleRate, aacProfile;
- CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
- CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
-
- if (!meta->findInt32(kKeyAACProfile, &aacProfile)) {
- aacProfile = OMX_AUDIO_AACObjectNull;
- }
-
- int32_t isADTS;
- if (!meta->findInt32(kKeyIsADTS, &isADTS)) {
- isADTS = false;
- }
-
- status_t err = setAACFormat(numChannels, sampleRate, bitRate, aacProfile, isADTS);
- if (err != OK) {
- CODEC_LOGE("setAACFormat() failed (err = %d)", err);
- return err;
- }
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_MPEG, mMIME)) {
- int32_t numChannels, sampleRate;
- if (meta->findInt32(kKeyChannelCount, &numChannels)
- && meta->findInt32(kKeySampleRate, &sampleRate)) {
- // Since we did not always check for these, leave them optional
- // and have the decoder figure it all out.
- setRawAudioFormat(
- mIsEncoder ? kPortIndexInput : kPortIndexOutput,
- sampleRate,
- numChannels);
- }
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AC3, mMIME)) {
- int32_t numChannels;
- int32_t sampleRate;
- CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
- CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
-
- status_t err = setAC3Format(numChannels, sampleRate);
- if (err != OK) {
- CODEC_LOGE("setAC3Format() failed (err = %d)", err);
- return err;
- }
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_ALAW, mMIME)
- || !strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_MLAW, mMIME)) {
- // These are PCM-like formats with a fixed sample rate but
- // a variable number of channels.
-
- int32_t sampleRate;
- int32_t numChannels;
- CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
- if (!meta->findInt32(kKeySampleRate, &sampleRate)) {
- sampleRate = 8000;
- }
-
- setG711Format(sampleRate, numChannels);
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mMIME)) {
- CHECK(!mIsEncoder);
-
- int32_t numChannels, sampleRate;
- CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
- CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
-
- setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
- }
-
- if (!strncasecmp(mMIME, "video/", 6)) {
-
- if (mIsEncoder) {
- setVideoInputFormat(mMIME, meta);
- } else {
- status_t err = setVideoOutputFormat(
- mMIME, meta);
-
- if (err != OK) {
- return err;
- }
- }
- }
-
- int32_t maxInputSize;
- if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
- setMinBufferSize(kPortIndexInput, (OMX_U32)maxInputSize);
- }
-
- initOutputFormat(meta);
-
- if ((mFlags & kClientNeedsFramebuffer)
- && !strncmp(mComponentName, "OMX.SEC.", 8)) {
- // This appears to no longer be needed???
-
- OMX_INDEXTYPE index;
-
- status_t err =
- mOMX->getExtensionIndex(
- mNode,
- "OMX.SEC.index.ThumbnailMode",
- &index);
-
- if (err != OK) {
- return err;
- }
-
- OMX_BOOL enable = OMX_TRUE;
- err = mOMX->setConfig(mNode, index, &enable, sizeof(enable));
-
- if (err != OK) {
- CODEC_LOGE("setConfig('OMX.SEC.index.ThumbnailMode') "
- "returned error 0x%08x", err);
-
- return err;
- }
-
- mQuirks &= ~kOutputBuffersAreUnreadable;
- }
-
- if (mNativeWindow != NULL
- && !mIsEncoder
- && !strncasecmp(mMIME, "video/", 6)
- && !strncmp(mComponentName, "OMX.", 4)) {
- status_t err = initNativeWindow();
- if (err != OK) {
- return err;
- }
- }
-
- return OK;
-}
-
-void OMXCodec::setMinBufferSize(OMX_U32 portIndex, OMX_U32 size) {
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = portIndex;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- if ((portIndex == kPortIndexInput && (mQuirks & kInputBufferSizesAreBogus))
- || (def.nBufferSize < size)) {
- def.nBufferSize = size;
- }
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- // Make sure the setting actually stuck.
- if (portIndex == kPortIndexInput
- && (mQuirks & kInputBufferSizesAreBogus)) {
- CHECK_EQ(def.nBufferSize, size);
- } else {
- CHECK(def.nBufferSize >= size);
- }
-}
-
-status_t OMXCodec::setVideoPortFormatType(
- OMX_U32 portIndex,
- OMX_VIDEO_CODINGTYPE compressionFormat,
- OMX_COLOR_FORMATTYPE colorFormat) {
- OMX_VIDEO_PARAM_PORTFORMATTYPE format;
- InitOMXParams(&format);
- format.nPortIndex = portIndex;
- format.nIndex = 0;
- bool found = false;
-
- OMX_U32 index = 0;
- for (;;) {
- format.nIndex = index;
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamVideoPortFormat,
- &format, sizeof(format));
-
- if (err != OK) {
- return err;
- }
-
- // The following assertion is violated by TI's video decoder.
- // CHECK_EQ(format.nIndex, index);
-
-#if 1
- CODEC_LOGV("portIndex: %u, index: %u, eCompressionFormat=%d eColorFormat=%d",
- portIndex,
- index, format.eCompressionFormat, format.eColorFormat);
-#endif
-
- if (format.eCompressionFormat == compressionFormat
- && format.eColorFormat == colorFormat) {
- found = true;
- break;
- }
-
- ++index;
- if (index >= kMaxColorFormatSupported) {
- CODEC_LOGE("color format %d or compression format %d is not supported",
- colorFormat, compressionFormat);
- return UNKNOWN_ERROR;
- }
- }
-
- if (!found) {
- return UNKNOWN_ERROR;
- }
-
- CODEC_LOGV("found a match.");
- status_t err = mOMX->setParameter(
- mNode, OMX_IndexParamVideoPortFormat,
- &format, sizeof(format));
-
- return err;
-}
-
-static size_t getFrameSize(
- OMX_COLOR_FORMATTYPE colorFormat, int32_t width, int32_t height) {
- switch (colorFormat) {
- case OMX_COLOR_FormatYCbYCr:
- case OMX_COLOR_FormatCbYCrY:
- return width * height * 2;
-
- case OMX_COLOR_FormatYUV420Planar:
- case OMX_COLOR_FormatYUV420SemiPlanar:
- case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
- /*
- * FIXME: For the Opaque color format, the frame size does not
- * need to be (w*h*3)/2. It just needs to
- * be larger than certain minimum buffer size. However,
- * currently, this opaque foramt has been tested only on
- * YUV420 formats. If that is changed, then we need to revisit
- * this part in the future
- */
- case OMX_COLOR_FormatAndroidOpaque:
- return (width * height * 3) / 2;
-
- default:
- CHECK(!"Should not be here. Unsupported color format.");
- break;
- }
- return 0;
-}
-
-status_t OMXCodec::findTargetColorFormat(
- const sp<MetaData>& meta, OMX_COLOR_FORMATTYPE *colorFormat) {
- ALOGV("findTargetColorFormat");
- CHECK(mIsEncoder);
-
- *colorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
- int32_t targetColorFormat;
- if (meta->findInt32(kKeyColorFormat, &targetColorFormat)) {
- *colorFormat = (OMX_COLOR_FORMATTYPE) targetColorFormat;
- }
-
- // Check whether the target color format is supported.
- return isColorFormatSupported(*colorFormat, kPortIndexInput);
-}
-
-status_t OMXCodec::isColorFormatSupported(
- OMX_COLOR_FORMATTYPE colorFormat, int portIndex) {
- ALOGV("isColorFormatSupported: %d", static_cast<int>(colorFormat));
-
- // Enumerate all the color formats supported by
- // the omx component to see whether the given
- // color format is supported.
- OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat;
- InitOMXParams(&portFormat);
- portFormat.nPortIndex = portIndex;
- OMX_U32 index = 0;
- portFormat.nIndex = index;
- while (true) {
- if (OMX_ErrorNone != mOMX->getParameter(
- mNode, OMX_IndexParamVideoPortFormat,
- &portFormat, sizeof(portFormat))) {
- break;
- }
- // Make sure that omx component does not overwrite
- // the incremented index (bug 2897413).
- CHECK_EQ(index, portFormat.nIndex);
- if (portFormat.eColorFormat == colorFormat) {
- CODEC_LOGV("Found supported color format: %d", portFormat.eColorFormat);
- return OK; // colorFormat is supported!
- }
- ++index;
- portFormat.nIndex = index;
-
- if (index >= kMaxColorFormatSupported) {
- CODEC_LOGE("More than %u color formats are supported???", index);
- break;
- }
- }
-
- CODEC_LOGE("color format %d is not supported", colorFormat);
- return UNKNOWN_ERROR;
-}
-
-void OMXCodec::setVideoInputFormat(
- const char *mime, const sp<MetaData>& meta) {
-
- int32_t width, height, frameRate, bitRate, stride, sliceHeight;
- bool success = meta->findInt32(kKeyWidth, &width);
- success = success && meta->findInt32(kKeyHeight, &height);
- success = success && meta->findInt32(kKeyFrameRate, &frameRate);
- success = success && meta->findInt32(kKeyBitRate, &bitRate);
- success = success && meta->findInt32(kKeyStride, &stride);
- success = success && meta->findInt32(kKeySliceHeight, &sliceHeight);
- CHECK(success);
- CHECK(stride != 0);
-
- OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
- if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
- compressionFormat = OMX_VIDEO_CodingAVC;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
- compressionFormat = OMX_VIDEO_CodingHEVC;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
- compressionFormat = OMX_VIDEO_CodingMPEG4;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
- compressionFormat = OMX_VIDEO_CodingH263;
- } else {
- ALOGE("Not a supported video mime type: %s", mime);
- CHECK(!"Should not be here. Not a supported video mime type.");
- }
-
- OMX_COLOR_FORMATTYPE colorFormat;
- CHECK_EQ((status_t)OK, findTargetColorFormat(meta, &colorFormat));
-
- status_t err;
- OMX_PARAM_PORTDEFINITIONTYPE def;
- OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
-
- //////////////////////// Input port /////////////////////////
- CHECK_EQ(setVideoPortFormatType(
- kPortIndexInput, OMX_VIDEO_CodingUnused,
- colorFormat), (status_t)OK);
-
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexInput;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- def.nBufferSize = getFrameSize(colorFormat,
- stride > 0? stride: -stride, sliceHeight);
-
- CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
-
- video_def->nFrameWidth = width;
- video_def->nFrameHeight = height;
- video_def->nStride = stride;
- video_def->nSliceHeight = sliceHeight;
- video_def->xFramerate = (frameRate << 16); // Q16 format
- video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
- video_def->eColorFormat = colorFormat;
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- //////////////////////// Output port /////////////////////////
- CHECK_EQ(setVideoPortFormatType(
- kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused),
- (status_t)OK);
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexOutput;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
- CHECK_EQ(err, (status_t)OK);
- CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
-
- video_def->nFrameWidth = width;
- video_def->nFrameHeight = height;
- video_def->xFramerate = 0; // No need for output port
- video_def->nBitrate = bitRate; // Q16 format
- video_def->eCompressionFormat = compressionFormat;
- video_def->eColorFormat = OMX_COLOR_FormatUnused;
- if (mQuirks & kRequiresLargerEncoderOutputBuffer) {
- // Increases the output buffer size
- def.nBufferSize = ((def.nBufferSize * 3) >> 1);
- }
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- /////////////////// Codec-specific ////////////////////////
- switch (compressionFormat) {
- case OMX_VIDEO_CodingMPEG4:
- {
- CHECK_EQ(setupMPEG4EncoderParameters(meta), (status_t)OK);
- break;
- }
-
- case OMX_VIDEO_CodingH263:
- CHECK_EQ(setupH263EncoderParameters(meta), (status_t)OK);
- break;
-
- case OMX_VIDEO_CodingAVC:
- {
- CHECK_EQ(setupAVCEncoderParameters(meta), (status_t)OK);
- break;
- }
-
- default:
- CHECK(!"Support for this compressionFormat to be implemented.");
- break;
- }
-}
-
-static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) {
- if (iFramesInterval < 0) {
- return 0xFFFFFFFF;
- } else if (iFramesInterval == 0) {
- return 0;
- }
- OMX_U32 ret = frameRate * iFramesInterval - 1;
- return ret;
-}
-
-status_t OMXCodec::setupErrorCorrectionParameters() {
- OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType;
- InitOMXParams(&errorCorrectionType);
- errorCorrectionType.nPortIndex = kPortIndexOutput;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamVideoErrorCorrection,
- &errorCorrectionType, sizeof(errorCorrectionType));
- if (err != OK) {
- ALOGW("Error correction param query is not supported");
- return OK; // Optional feature. Ignore this failure
- }
-
- errorCorrectionType.bEnableHEC = OMX_FALSE;
- errorCorrectionType.bEnableResync = OMX_TRUE;
- errorCorrectionType.nResynchMarkerSpacing = 256;
- errorCorrectionType.bEnableDataPartitioning = OMX_FALSE;
- errorCorrectionType.bEnableRVLC = OMX_FALSE;
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamVideoErrorCorrection,
- &errorCorrectionType, sizeof(errorCorrectionType));
- if (err != OK) {
- ALOGW("Error correction param configuration is not supported");
- }
-
- // Optional feature. Ignore the failure.
- return OK;
-}
-
-status_t OMXCodec::setupBitRate(int32_t bitRate) {
- OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
- InitOMXParams(&bitrateType);
- bitrateType.nPortIndex = kPortIndexOutput;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamVideoBitrate,
- &bitrateType, sizeof(bitrateType));
- CHECK_EQ(err, (status_t)OK);
-
- bitrateType.eControlRate = OMX_Video_ControlRateVariable;
- bitrateType.nTargetBitrate = bitRate;
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamVideoBitrate,
- &bitrateType, sizeof(bitrateType));
- CHECK_EQ(err, (status_t)OK);
- return OK;
-}
-
-status_t OMXCodec::getVideoProfileLevel(
- const sp<MetaData>& meta,
- const CodecProfileLevel& defaultProfileLevel,
- CodecProfileLevel &profileLevel) {
- CODEC_LOGV("Default profile: %u, level #x%x",
- defaultProfileLevel.mProfile, defaultProfileLevel.mLevel);
-
- // Are the default profile and level overwriten?
- int32_t profile, level;
- if (!meta->findInt32(kKeyVideoProfile, &profile)) {
- profile = defaultProfileLevel.mProfile;
- }
- if (!meta->findInt32(kKeyVideoLevel, &level)) {
- level = defaultProfileLevel.mLevel;
- }
- CODEC_LOGV("Target profile: %d, level: %d", profile, level);
-
- // Are the target profile and level supported by the encoder?
- OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
- InitOMXParams(¶m);
- param.nPortIndex = kPortIndexOutput;
- for (param.nProfileIndex = 0;; ++param.nProfileIndex) {
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamVideoProfileLevelQuerySupported,
- ¶m, sizeof(param));
-
- if (err != OK) break;
-
- int32_t supportedProfile = static_cast<int32_t>(param.eProfile);
- int32_t supportedLevel = static_cast<int32_t>(param.eLevel);
- CODEC_LOGV("Supported profile: %d, level %d",
- supportedProfile, supportedLevel);
-
- if (profile == supportedProfile &&
- level <= supportedLevel) {
- // We can further check whether the level is a valid
- // value; but we will leave that to the omx encoder component
- // via OMX_SetParameter call.
- profileLevel.mProfile = profile;
- profileLevel.mLevel = level;
- return OK;
- }
- }
-
- CODEC_LOGE("Target profile (%d) and level (%d) is not supported",
- profile, level);
- return BAD_VALUE;
-}
-
-status_t OMXCodec::setupH263EncoderParameters(const sp<MetaData>& meta) {
- int32_t iFramesInterval, frameRate, bitRate;
- bool success = meta->findInt32(kKeyBitRate, &bitRate);
- success = success && meta->findInt32(kKeyFrameRate, &frameRate);
- success = success && meta->findInt32(kKeyIFramesInterval, &iFramesInterval);
- CHECK(success);
- OMX_VIDEO_PARAM_H263TYPE h263type;
- InitOMXParams(&h263type);
- h263type.nPortIndex = kPortIndexOutput;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
- CHECK_EQ(err, (status_t)OK);
-
- h263type.nAllowedPictureTypes =
- OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
-
- h263type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate);
- if (h263type.nPFrames == 0) {
- h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
- }
- h263type.nBFrames = 0;
-
- // Check profile and level parameters
- CodecProfileLevel defaultProfileLevel, profileLevel;
- defaultProfileLevel.mProfile = h263type.eProfile;
- defaultProfileLevel.mLevel = h263type.eLevel;
- err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel);
- if (err != OK) return err;
- h263type.eProfile = static_cast<OMX_VIDEO_H263PROFILETYPE>(profileLevel.mProfile);
- h263type.eLevel = static_cast<OMX_VIDEO_H263LEVELTYPE>(profileLevel.mLevel);
-
- h263type.bPLUSPTYPEAllowed = OMX_FALSE;
- h263type.bForceRoundingTypeToZero = OMX_FALSE;
- h263type.nPictureHeaderRepetition = 0;
- h263type.nGOBHeaderInterval = 0;
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
- CHECK_EQ(err, (status_t)OK);
-
- CHECK_EQ(setupBitRate(bitRate), (status_t)OK);
- CHECK_EQ(setupErrorCorrectionParameters(), (status_t)OK);
-
- return OK;
-}
-
-status_t OMXCodec::setupMPEG4EncoderParameters(const sp<MetaData>& meta) {
- int32_t iFramesInterval, frameRate, bitRate;
- bool success = meta->findInt32(kKeyBitRate, &bitRate);
- success = success && meta->findInt32(kKeyFrameRate, &frameRate);
- success = success && meta->findInt32(kKeyIFramesInterval, &iFramesInterval);
- CHECK(success);
- OMX_VIDEO_PARAM_MPEG4TYPE mpeg4type;
- InitOMXParams(&mpeg4type);
- mpeg4type.nPortIndex = kPortIndexOutput;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
- CHECK_EQ(err, (status_t)OK);
-
- mpeg4type.nSliceHeaderSpacing = 0;
- mpeg4type.bSVH = OMX_FALSE;
- mpeg4type.bGov = OMX_FALSE;
-
- mpeg4type.nAllowedPictureTypes =
- OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
-
- mpeg4type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate);
- if (mpeg4type.nPFrames == 0) {
- mpeg4type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
- }
- mpeg4type.nBFrames = 0;
- mpeg4type.nIDCVLCThreshold = 0;
- mpeg4type.bACPred = OMX_TRUE;
- mpeg4type.nMaxPacketSize = 256;
- mpeg4type.nTimeIncRes = 1000;
- mpeg4type.nHeaderExtension = 0;
- mpeg4type.bReversibleVLC = OMX_FALSE;
-
- // Check profile and level parameters
- CodecProfileLevel defaultProfileLevel, profileLevel;
- defaultProfileLevel.mProfile = mpeg4type.eProfile;
- defaultProfileLevel.mLevel = mpeg4type.eLevel;
- err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel);
- if (err != OK) return err;
- mpeg4type.eProfile = static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profileLevel.mProfile);
- mpeg4type.eLevel = static_cast<OMX_VIDEO_MPEG4LEVELTYPE>(profileLevel.mLevel);
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
- CHECK_EQ(err, (status_t)OK);
-
- CHECK_EQ(setupBitRate(bitRate), (status_t)OK);
- CHECK_EQ(setupErrorCorrectionParameters(), (status_t)OK);
-
- return OK;
-}
-
-status_t OMXCodec::setupAVCEncoderParameters(const sp<MetaData>& meta) {
- int32_t iFramesInterval, frameRate, bitRate;
- bool success = meta->findInt32(kKeyBitRate, &bitRate);
- success = success && meta->findInt32(kKeyFrameRate, &frameRate);
- success = success && meta->findInt32(kKeyIFramesInterval, &iFramesInterval);
- CHECK(success);
-
- OMX_VIDEO_PARAM_AVCTYPE h264type;
- InitOMXParams(&h264type);
- h264type.nPortIndex = kPortIndexOutput;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
- CHECK_EQ(err, (status_t)OK);
-
- h264type.nAllowedPictureTypes =
- OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
-
- // Check profile and level parameters
- CodecProfileLevel defaultProfileLevel, profileLevel;
- defaultProfileLevel.mProfile = h264type.eProfile;
- defaultProfileLevel.mLevel = h264type.eLevel;
- err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel);
- if (err != OK) return err;
- h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profileLevel.mProfile);
- h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(profileLevel.mLevel);
-
- // XXX
- if (h264type.eProfile != OMX_VIDEO_AVCProfileBaseline) {
- ALOGW("Use baseline profile instead of %d for AVC recording",
- h264type.eProfile);
- h264type.eProfile = OMX_VIDEO_AVCProfileBaseline;
- }
-
- if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) {
- h264type.nSliceHeaderSpacing = 0;
- h264type.bUseHadamard = OMX_TRUE;
- h264type.nRefFrames = 1;
- h264type.nBFrames = 0;
- h264type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate);
- if (h264type.nPFrames == 0) {
- h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
- }
- h264type.nRefIdx10ActiveMinus1 = 0;
- h264type.nRefIdx11ActiveMinus1 = 0;
- h264type.bEntropyCodingCABAC = OMX_FALSE;
- h264type.bWeightedPPrediction = OMX_FALSE;
- h264type.bconstIpred = OMX_FALSE;
- h264type.bDirect8x8Inference = OMX_FALSE;
- h264type.bDirectSpatialTemporal = OMX_FALSE;
- h264type.nCabacInitIdc = 0;
- }
-
- if (h264type.nBFrames != 0) {
- h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB;
- }
-
- h264type.bEnableUEP = OMX_FALSE;
- h264type.bEnableFMO = OMX_FALSE;
- h264type.bEnableASO = OMX_FALSE;
- h264type.bEnableRS = OMX_FALSE;
- h264type.bFrameMBsOnly = OMX_TRUE;
- h264type.bMBAFF = OMX_FALSE;
- h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
- CHECK_EQ(err, (status_t)OK);
-
- CHECK_EQ(setupBitRate(bitRate), (status_t)OK);
-
- return OK;
-}
-
-status_t OMXCodec::setVideoOutputFormat(
- const char *mime, const sp<MetaData>& meta) {
-
- int32_t width, height;
- bool success = meta->findInt32(kKeyWidth, &width);
- success = success && meta->findInt32(kKeyHeight, &height);
- CHECK(success);
-
- CODEC_LOGV("setVideoOutputFormat width=%d, height=%d", width, height);
-
- OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
- if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
- compressionFormat = OMX_VIDEO_CodingAVC;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
- compressionFormat = OMX_VIDEO_CodingMPEG4;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
- compressionFormat = OMX_VIDEO_CodingHEVC;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
- compressionFormat = OMX_VIDEO_CodingH263;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VP8, mime)) {
- compressionFormat = OMX_VIDEO_CodingVP8;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VP9, mime)) {
- compressionFormat = OMX_VIDEO_CodingVP9;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) {
- compressionFormat = OMX_VIDEO_CodingMPEG2;
- } else {
- ALOGE("Not a supported video mime type: %s", mime);
- CHECK(!"Should not be here. Not a supported video mime type.");
- }
-
- status_t err = setVideoPortFormatType(
- kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
-
- if (err != OK) {
- return err;
- }
-
-#if 1
- {
- OMX_VIDEO_PARAM_PORTFORMATTYPE format;
- InitOMXParams(&format);
- format.nPortIndex = kPortIndexOutput;
- format.nIndex = 0;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamVideoPortFormat,
- &format, sizeof(format));
- CHECK_EQ(err, (status_t)OK);
- CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
-
- int32_t colorFormat;
- if (meta->findInt32(kKeyColorFormat, &colorFormat)
- && colorFormat != OMX_COLOR_FormatUnused
- && colorFormat != format.eColorFormat) {
-
- while (OMX_ErrorNoMore != err) {
- format.nIndex++;
- err = mOMX->getParameter(
- mNode, OMX_IndexParamVideoPortFormat,
- &format, sizeof(format));
- if (format.eColorFormat == colorFormat) {
- break;
- }
- }
- if (format.eColorFormat != colorFormat) {
- CODEC_LOGE("Color format %d is not supported", colorFormat);
- return ERROR_UNSUPPORTED;
- }
- }
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamVideoPortFormat,
- &format, sizeof(format));
-
- if (err != OK) {
- return err;
- }
- }
-#endif
-
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexInput;
-
- OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
- CHECK_EQ(err, (status_t)OK);
-
-#if 1
- // XXX Need a (much) better heuristic to compute input buffer sizes.
- const size_t X = 64 * 1024;
- if (def.nBufferSize < X) {
- def.nBufferSize = X;
- }
-#endif
-
- CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
-
- video_def->nFrameWidth = width;
- video_def->nFrameHeight = height;
-
- video_def->eCompressionFormat = compressionFormat;
- video_def->eColorFormat = OMX_COLOR_FormatUnused;
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
- if (err != OK) {
- return err;
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexOutput;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
- CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
-
-#if 0
- def.nBufferSize =
- (((width + 15) & -16) * ((height + 15) & -16) * 3) / 2; // YUV420
-#endif
-
- video_def->nFrameWidth = width;
- video_def->nFrameHeight = height;
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
- return err;
-}
-
-OMXCodec::OMXCodec(
- const sp<IOMX> &omx, IOMX::node_id node,
- uint32_t quirks, uint32_t flags,
- bool isEncoder,
- const char *mime,
- const char *componentName,
- const sp<MediaSource> &source,
- const sp<ANativeWindow> &nativeWindow)
- : mOMX(omx),
- mOMXLivesLocally(omx->livesLocally(node, getpid())),
- mNode(node),
- mQuirks(quirks),
- mFlags(flags),
- mIsEncoder(isEncoder),
- mIsVideo(!strncasecmp("video/", mime, 6)),
- mMIME(strdup(mime)),
- mComponentName(strdup(componentName)),
- mSource(source),
- mCodecSpecificDataIndex(0),
- mState(LOADED),
- mInitialBufferSubmit(true),
- mSignalledEOS(false),
- mNoMoreOutputData(false),
- mOutputPortSettingsHaveChanged(false),
- mSeekTimeUs(-1),
- mSeekMode(ReadOptions::SEEK_CLOSEST_SYNC),
- mTargetTimeUs(-1),
- mOutputPortSettingsChangedPending(false),
- mSkipCutBuffer(NULL),
- mLeftOverBuffer(NULL),
- mPaused(false),
- mNativeWindow(
- (!strncmp(componentName, "OMX.google.", 11))
- ? NULL : nativeWindow) {
- mPortStatus[kPortIndexInput] = ENABLED;
- mPortStatus[kPortIndexOutput] = ENABLED;
-
- setComponentRole();
-}
-
-// static
-void OMXCodec::setComponentRole(
- const sp<IOMX> &omx, IOMX::node_id node, bool isEncoder,
- const char *mime) {
- struct MimeToRole {
- const char *mime;
- const char *decoderRole;
- const char *encoderRole;
- };
-
- static const MimeToRole kMimeToRole[] = {
- { MEDIA_MIMETYPE_AUDIO_MPEG,
- "audio_decoder.mp3", "audio_encoder.mp3" },
- { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I,
- "audio_decoder.mp1", "audio_encoder.mp1" },
- { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II,
- "audio_decoder.mp2", "audio_encoder.mp2" },
- { MEDIA_MIMETYPE_AUDIO_AMR_NB,
- "audio_decoder.amrnb", "audio_encoder.amrnb" },
- { MEDIA_MIMETYPE_AUDIO_AMR_WB,
- "audio_decoder.amrwb", "audio_encoder.amrwb" },
- { MEDIA_MIMETYPE_AUDIO_AAC,
- "audio_decoder.aac", "audio_encoder.aac" },
- { MEDIA_MIMETYPE_AUDIO_VORBIS,
- "audio_decoder.vorbis", "audio_encoder.vorbis" },
- { MEDIA_MIMETYPE_AUDIO_OPUS,
- "audio_decoder.opus", "audio_encoder.opus" },
- { MEDIA_MIMETYPE_AUDIO_G711_MLAW,
- "audio_decoder.g711mlaw", "audio_encoder.g711mlaw" },
- { MEDIA_MIMETYPE_AUDIO_G711_ALAW,
- "audio_decoder.g711alaw", "audio_encoder.g711alaw" },
- { MEDIA_MIMETYPE_VIDEO_AVC,
- "video_decoder.avc", "video_encoder.avc" },
- { MEDIA_MIMETYPE_VIDEO_HEVC,
- "video_decoder.hevc", "video_encoder.hevc" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4,
- "video_decoder.mpeg4", "video_encoder.mpeg4" },
- { MEDIA_MIMETYPE_VIDEO_H263,
- "video_decoder.h263", "video_encoder.h263" },
- { MEDIA_MIMETYPE_VIDEO_VP8,
- "video_decoder.vp8", "video_encoder.vp8" },
- { MEDIA_MIMETYPE_VIDEO_VP9,
- "video_decoder.vp9", "video_encoder.vp9" },
- { MEDIA_MIMETYPE_AUDIO_RAW,
- "audio_decoder.raw", "audio_encoder.raw" },
- { MEDIA_MIMETYPE_AUDIO_FLAC,
- "audio_decoder.flac", "audio_encoder.flac" },
- { MEDIA_MIMETYPE_AUDIO_MSGSM,
- "audio_decoder.gsm", "audio_encoder.gsm" },
- { MEDIA_MIMETYPE_VIDEO_MPEG2,
- "video_decoder.mpeg2", "video_encoder.mpeg2" },
- { MEDIA_MIMETYPE_AUDIO_AC3,
- "audio_decoder.ac3", "audio_encoder.ac3" },
- };
-
- static const size_t kNumMimeToRole =
- sizeof(kMimeToRole) / sizeof(kMimeToRole[0]);
-
- size_t i;
- for (i = 0; i < kNumMimeToRole; ++i) {
- if (!strcasecmp(mime, kMimeToRole[i].mime)) {
- break;
- }
- }
-
- if (i == kNumMimeToRole) {
- return;
- }
-
- const char *role =
- isEncoder ? kMimeToRole[i].encoderRole
- : kMimeToRole[i].decoderRole;
-
- if (role != NULL) {
- OMX_PARAM_COMPONENTROLETYPE roleParams;
- InitOMXParams(&roleParams);
-
- strncpy((char *)roleParams.cRole,
- role, OMX_MAX_STRINGNAME_SIZE - 1);
-
- roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
-
- status_t err = omx->setParameter(
- node, OMX_IndexParamStandardComponentRole,
- &roleParams, sizeof(roleParams));
-
- if (err != OK) {
- ALOGW("Failed to set standard component role '%s'.", role);
- }
- }
-}
-
-void OMXCodec::setComponentRole() {
- setComponentRole(mOMX, mNode, mIsEncoder, mMIME);
-}
-
-OMXCodec::~OMXCodec() {
- mSource.clear();
-
- CHECK(mState == LOADED || mState == ERROR || mState == LOADED_TO_IDLE);
-
- status_t err = mOMX->freeNode(mNode);
- CHECK_EQ(err, (status_t)OK);
-
- mNode = 0;
- setState(DEAD);
-
- clearCodecSpecificData();
-
- free(mComponentName);
- mComponentName = NULL;
-
- free(mMIME);
- mMIME = NULL;
-}
-
-status_t OMXCodec::init() {
- // mLock is held.
-
- CHECK_EQ((int)mState, (int)LOADED);
-
- status_t err;
- if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) {
- err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
- CHECK_EQ(err, (status_t)OK);
- setState(LOADED_TO_IDLE);
- }
-
- err = allocateBuffers();
- if (err != (status_t)OK) {
- return err;
- }
-
- if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {
- err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
- CHECK_EQ(err, (status_t)OK);
-
- setState(LOADED_TO_IDLE);
- }
-
- while (mState != EXECUTING && mState != ERROR) {
- mAsyncCompletion.wait(mLock);
- }
-
- return mState == ERROR ? UNKNOWN_ERROR : OK;
-}
-
-// static
-bool OMXCodec::isIntermediateState(State state) {
- return state == LOADED_TO_IDLE
- || state == IDLE_TO_EXECUTING
- || state == EXECUTING_TO_IDLE
- || state == IDLE_TO_LOADED
- || state == RECONFIGURING;
-}
-
-status_t OMXCodec::allocateBuffers() {
- status_t err = allocateBuffersOnPort(kPortIndexInput);
-
- if (err != OK) {
- return err;
- }
-
- return allocateBuffersOnPort(kPortIndexOutput);
-}
-
-status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) {
- if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
- return allocateOutputBuffersFromNativeWindow();
- }
-
- if ((mFlags & kEnableGrallocUsageProtected) && portIndex == kPortIndexOutput) {
- ALOGE("protected output buffers must be stent to an ANativeWindow");
- return PERMISSION_DENIED;
- }
-
- status_t err = OK;
- if ((mFlags & kStoreMetaDataInVideoBuffers)
- && portIndex == kPortIndexInput) {
- err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexInput, OMX_TRUE);
- if (err != OK) {
- ALOGE("Storing meta data in video buffers is not supported");
- return err;
- }
- }
-
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = portIndex;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
- if (err != OK) {
- return err;
- }
-
- CODEC_LOGV("allocating %u buffers of size %u on %s port",
- def.nBufferCountActual, def.nBufferSize,
- portIndex == kPortIndexInput ? "input" : "output");
-
- if (def.nBufferSize != 0 && def.nBufferCountActual > SIZE_MAX / def.nBufferSize) {
- return BAD_VALUE;
- }
- size_t totalSize = def.nBufferCountActual * def.nBufferSize;
- mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec");
-
- for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
- sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
- CHECK(mem.get() != NULL);
-
- BufferInfo info;
- info.mData = NULL;
- info.mSize = def.nBufferSize;
-
- IOMX::buffer_id buffer;
- if (portIndex == kPortIndexInput
- && ((mQuirks & kRequiresAllocateBufferOnInputPorts)
- || (mFlags & kUseSecureInputBuffers))) {
- if (mOMXLivesLocally) {
- mem.clear();
-
- err = mOMX->allocateBuffer(
- mNode, portIndex, def.nBufferSize, &buffer,
- &info.mData);
- } else {
- err = mOMX->allocateBufferWithBackup(
- mNode, portIndex, mem, &buffer, mem->size());
- }
- } else if (portIndex == kPortIndexOutput
- && (mQuirks & kRequiresAllocateBufferOnOutputPorts)) {
- if (mOMXLivesLocally) {
- mem.clear();
-
- err = mOMX->allocateBuffer(
- mNode, portIndex, def.nBufferSize, &buffer,
- &info.mData);
- } else {
- err = mOMX->allocateBufferWithBackup(
- mNode, portIndex, mem, &buffer, mem->size());
- }
- } else {
- err = mOMX->useBuffer(mNode, portIndex, mem, &buffer, mem->size());
- }
-
- if (err != OK) {
- ALOGE("allocate_buffer_with_backup failed");
- return err;
- }
-
- if (mem != NULL) {
- info.mData = mem->pointer();
- }
-
- info.mBuffer = buffer;
- info.mStatus = OWNED_BY_US;
- info.mMem = mem;
- info.mMediaBuffer = NULL;
-
- if (portIndex == kPortIndexOutput) {
- // Fail deferred MediaBuffer creation until FILL_BUFFER_DONE;
- // this legacy mode is no longer supported.
- LOG_ALWAYS_FATAL_IF((mOMXLivesLocally
- && (mQuirks & kRequiresAllocateBufferOnOutputPorts)
- && (mQuirks & kDefersOutputBufferAllocation)),
- "allocateBuffersOnPort cannot defer buffer allocation");
-
- info.mMediaBuffer = new MediaBuffer(info.mData, info.mSize);
- info.mMediaBuffer->setObserver(this);
- }
-
- mPortBuffers[portIndex].push(info);
-
- CODEC_LOGV("allocated buffer %u on %s port", buffer,
- portIndex == kPortIndexInput ? "input" : "output");
- }
-
- if (portIndex == kPortIndexOutput) {
-
- sp<MetaData> meta = mSource->getFormat();
- int32_t delay = 0;
- if (!meta->findInt32(kKeyEncoderDelay, &delay)) {
- delay = 0;
- }
- int32_t padding = 0;
- if (!meta->findInt32(kKeyEncoderPadding, &padding)) {
- padding = 0;
- }
- int32_t numchannels = 0;
- if (delay + padding) {
- if (mOutputFormat->findInt32(kKeyChannelCount, &numchannels)) {
- if (mSkipCutBuffer != NULL) {
- size_t prevbuffersize = mSkipCutBuffer->size();
- if (prevbuffersize != 0) {
- ALOGW("Replacing SkipCutBuffer holding %zu bytes", prevbuffersize);
- }
- }
- mSkipCutBuffer = new SkipCutBuffer(delay, padding, numchannels);
- }
- }
- }
-
- // dumpPortStatus(portIndex);
-
- if (portIndex == kPortIndexInput && (mFlags & kUseSecureInputBuffers)) {
- Vector<MediaBuffer *> buffers;
- for (size_t i = 0; i < def.nBufferCountActual; ++i) {
- const BufferInfo &info = mPortBuffers[kPortIndexInput].itemAt(i);
-
- MediaBuffer *mbuf = new MediaBuffer(info.mData, info.mSize);
- buffers.push(mbuf);
- }
-
- status_t err = mSource->setBuffers(buffers);
-
- if (err != OK) {
- for (size_t i = 0; i < def.nBufferCountActual; ++i) {
- buffers.editItemAt(i)->release();
- }
- buffers.clear();
-
- CODEC_LOGE(
- "Codec requested to use secure input buffers but "
- "upstream source didn't support that.");
-
- return err;
- }
- }
-
- return OK;
-}
-
-status_t OMXCodec::allocateOutputBuffersFromNativeWindow() {
- // Get the number of buffers needed.
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexOutput;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- if (err != OK) {
- CODEC_LOGE("getParameter failed: %d", err);
- return err;
- }
-
- sp<MetaData> meta = mSource->getFormat();
-
- int32_t rotationDegrees;
- if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
- rotationDegrees = 0;
- }
-
- // Set up the native window.
- OMX_U32 usage = 0;
- err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
- if (err != 0) {
- ALOGW("querying usage flags from OMX IL component failed: %d", err);
- // XXX: Currently this error is logged, but not fatal.
- usage = 0;
- }
-
- if (mFlags & kEnableGrallocUsageProtected) {
- usage |= GRALLOC_USAGE_PROTECTED;
- }
-
- err = setNativeWindowSizeFormatAndUsage(
- mNativeWindow.get(),
- def.format.video.nFrameWidth,
- def.format.video.nFrameHeight,
- def.format.video.eColorFormat,
- rotationDegrees,
- usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
- if (err != 0) {
- return err;
- }
-
- int minUndequeuedBufs = 0;
- err = mNativeWindow->query(mNativeWindow.get(),
- NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs);
- if (err != 0) {
- ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
- // FIXME: assume that surface is controlled by app (native window
- // returns the number for the case when surface is not controlled by app)
- // FIXME2: This means that minUndeqeueudBufs can be 1 larger than reported
- // For now, try to allocate 1 more buffer, but don't fail if unsuccessful
-
- // Use conservative allocation while also trying to reduce starvation
- //
- // 1. allocate at least nBufferCountMin + minUndequeuedBuffers - that is the
- // minimum needed for the consumer to be able to work
- // 2. try to allocate two (2) additional buffers to reduce starvation from
- // the consumer
- // plus an extra buffer to account for incorrect minUndequeuedBufs
- CODEC_LOGI("OMX-buffers: min=%u actual=%u undeq=%d+1",
- def.nBufferCountMin, def.nBufferCountActual, minUndequeuedBufs);
-
- for (OMX_U32 extraBuffers = 2 + 1; /* condition inside loop */; extraBuffers--) {
- OMX_U32 newBufferCount =
- def.nBufferCountMin + minUndequeuedBufs + extraBuffers;
- def.nBufferCountActual = newBufferCount;
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
- if (err == OK) {
- minUndequeuedBufs += extraBuffers;
- break;
- }
-
- CODEC_LOGW("setting nBufferCountActual to %u failed: %d",
- newBufferCount, err);
- /* exit condition */
- if (extraBuffers == 0) {
- return err;
- }
- }
- CODEC_LOGI("OMX-buffers: min=%u actual=%u undeq=%d+1",
- def.nBufferCountMin, def.nBufferCountActual, minUndequeuedBufs);
-
- err = native_window_set_buffer_count(
- mNativeWindow.get(), def.nBufferCountActual);
- if (err != 0) {
- ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
- -err);
- return err;
- }
-
- CODEC_LOGV("allocating %u buffers from a native window of size %u on "
- "output port", def.nBufferCountActual, def.nBufferSize);
-
- // Dequeue buffers and send them to OMX
- for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) {
- ANativeWindowBuffer* buf;
- err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf);
- if (err != 0) {
- ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
- break;
- }
-
- sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
- BufferInfo info;
- info.mData = NULL;
- info.mSize = def.nBufferSize;
- info.mStatus = OWNED_BY_US;
- info.mMem = NULL;
- info.mMediaBuffer = new MediaBuffer(graphicBuffer);
- info.mMediaBuffer->setObserver(this);
- mPortBuffers[kPortIndexOutput].push(info);
-
- IOMX::buffer_id bufferId;
- err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
- &bufferId);
- if (err != 0) {
- CODEC_LOGE("registering GraphicBuffer with OMX IL component "
- "failed: %d", err);
- break;
- }
-
- mPortBuffers[kPortIndexOutput].editItemAt(i).mBuffer = bufferId;
-
- CODEC_LOGV("registered graphic buffer with ID %u (pointer = %p)",
- bufferId, graphicBuffer.get());
- }
-
- OMX_U32 cancelStart;
- OMX_U32 cancelEnd;
- if (err != 0) {
- // If an error occurred while dequeuing we need to cancel any buffers
- // that were dequeued.
- cancelStart = 0;
- cancelEnd = mPortBuffers[kPortIndexOutput].size();
- } else {
- // Return the last two buffers to the native window.
- cancelStart = def.nBufferCountActual - minUndequeuedBufs;
- cancelEnd = def.nBufferCountActual;
- }
-
- for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
- BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(i);
- cancelBufferToNativeWindow(info);
- }
-
- return err;
-}
-
-status_t OMXCodec::cancelBufferToNativeWindow(BufferInfo *info) {
- CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);
- CODEC_LOGV("Calling cancelBuffer on buffer %u", info->mBuffer);
- int err = mNativeWindow->cancelBuffer(
- mNativeWindow.get(), info->mMediaBuffer->graphicBuffer().get(), -1);
- if (err != 0) {
- CODEC_LOGE("cancelBuffer failed w/ error 0x%08x", err);
-
- setState(ERROR);
- return err;
- }
- info->mStatus = OWNED_BY_NATIVE_WINDOW;
- return OK;
-}
-
-OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() {
- // Dequeue the next buffer from the native window.
- ANativeWindowBuffer* buf;
- int err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf);
- if (err != 0) {
- CODEC_LOGE("dequeueBuffer failed w/ error 0x%08x", err);
-
- setState(ERROR);
- return 0;
- }
-
- // Determine which buffer we just dequeued.
- Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
- BufferInfo *bufInfo = 0;
- for (size_t i = 0; i < buffers->size(); i++) {
- sp<GraphicBuffer> graphicBuffer = buffers->itemAt(i).
- mMediaBuffer->graphicBuffer();
- if (graphicBuffer->handle == buf->handle) {
- bufInfo = &buffers->editItemAt(i);
- break;
- }
- }
-
- if (bufInfo == 0) {
- CODEC_LOGE("dequeued unrecognized buffer: %p", buf);
-
- setState(ERROR);
- return 0;
- }
-
- // The native window no longer owns the buffer.
- CHECK_EQ((int)bufInfo->mStatus, (int)OWNED_BY_NATIVE_WINDOW);
- bufInfo->mStatus = OWNED_BY_US;
-
- return bufInfo;
-}
-
-int64_t OMXCodec::getDecodingTimeUs() {
- CHECK(mIsEncoder && mIsVideo);
-
- if (mDecodingTimeList.empty()) {
- CHECK(mSignalledEOS || mNoMoreOutputData);
- // No corresponding input frame available.
- // This could happen when EOS is reached.
- return 0;
- }
-
- List<int64_t>::iterator it = mDecodingTimeList.begin();
- int64_t timeUs = *it;
- mDecodingTimeList.erase(it);
- return timeUs;
-}
-
-void OMXCodec::on_message(const omx_message &msg) {
- if (mState == ERROR) {
- /*
- * only drop EVENT messages, EBD and FBD are still
- * processed for bookkeeping purposes
- */
- if (msg.type == omx_message::EVENT) {
- ALOGW("Dropping OMX EVENT message - we're in ERROR state.");
- return;
- }
- }
-
- switch (msg.type) {
- case omx_message::EVENT:
- {
- onEvent(
- msg.u.event_data.event, msg.u.event_data.data1,
- msg.u.event_data.data2);
-
- break;
- }
-
- case omx_message::EMPTY_BUFFER_DONE:
- {
- IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
-
- CODEC_LOGV("EMPTY_BUFFER_DONE(buffer: %u)", buffer);
-
- Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
- size_t i = 0;
- while (i < buffers->size() && (*buffers)[i].mBuffer != buffer) {
- ++i;
- }
-
- CHECK(i < buffers->size());
- if ((*buffers)[i].mStatus != OWNED_BY_COMPONENT) {
- ALOGW("We already own input buffer %u, yet received "
- "an EMPTY_BUFFER_DONE.", buffer);
- }
-
- BufferInfo* info = &buffers->editItemAt(i);
- info->mStatus = OWNED_BY_US;
-
- // Buffer could not be released until empty buffer done is called.
- if (info->mMediaBuffer != NULL) {
- info->mMediaBuffer->release();
- info->mMediaBuffer = NULL;
- }
-
- if (mPortStatus[kPortIndexInput] == DISABLING) {
- CODEC_LOGV("Port is disabled, freeing buffer %u", buffer);
-
- status_t err = freeBuffer(kPortIndexInput, i);
- CHECK_EQ(err, (status_t)OK);
- } else if (mState != ERROR
- && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
- CHECK_EQ((int)mPortStatus[kPortIndexInput], (int)ENABLED);
-
- if (mFlags & kUseSecureInputBuffers) {
- drainAnyInputBuffer();
- } else {
- drainInputBuffer(&buffers->editItemAt(i));
- }
- }
- break;
- }
-
- case omx_message::FILL_BUFFER_DONE:
- {
- IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
- OMX_U32 flags = msg.u.extended_buffer_data.flags;
-
- CODEC_LOGV("FILL_BUFFER_DONE(buffer: %u, size: %u, flags: 0x%08x, timestamp: %lld us (%.2f secs))",
- buffer,
- msg.u.extended_buffer_data.range_length,
- flags,
- msg.u.extended_buffer_data.timestamp,
- msg.u.extended_buffer_data.timestamp / 1E6);
-
- Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
- size_t i = 0;
- while (i < buffers->size() && (*buffers)[i].mBuffer != buffer) {
- ++i;
- }
-
- CHECK(i < buffers->size());
- BufferInfo *info = &buffers->editItemAt(i);
-
- if (info->mStatus != OWNED_BY_COMPONENT) {
- ALOGW("We already own output buffer %u, yet received "
- "a FILL_BUFFER_DONE.", buffer);
- }
-
- info->mStatus = OWNED_BY_US;
-
- if (mPortStatus[kPortIndexOutput] == DISABLING) {
- CODEC_LOGV("Port is disabled, freeing buffer %u", buffer);
-
- status_t err = freeBuffer(kPortIndexOutput, i);
- CHECK_EQ(err, (status_t)OK);
-
-#if 0
- } else if (mPortStatus[kPortIndexOutput] == ENABLED
- && (flags & OMX_BUFFERFLAG_EOS)) {
- CODEC_LOGV("No more output data.");
- mNoMoreOutputData = true;
- mBufferFilled.signal();
-#endif
- } else if (mPortStatus[kPortIndexOutput] != SHUTTING_DOWN) {
- CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)ENABLED);
-
- MediaBuffer *buffer = info->mMediaBuffer;
- bool isGraphicBuffer = buffer->graphicBuffer() != NULL;
-
- if (!isGraphicBuffer
- && msg.u.extended_buffer_data.range_offset
- + msg.u.extended_buffer_data.range_length
- > buffer->size()) {
- CODEC_LOGE(
- "Codec lied about its buffer size requirements, "
- "sending a buffer larger than the originally "
- "advertised size in FILL_BUFFER_DONE!");
- }
- buffer->set_range(
- msg.u.extended_buffer_data.range_offset,
- msg.u.extended_buffer_data.range_length);
-
- buffer->meta_data()->clear();
-
- buffer->meta_data()->setInt64(
- kKeyTime, msg.u.extended_buffer_data.timestamp);
-
- if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) {
- buffer->meta_data()->setInt32(kKeyIsSyncFrame, true);
- }
- bool isCodecSpecific = false;
- if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_CODECCONFIG) {
- buffer->meta_data()->setInt32(kKeyIsCodecConfig, true);
- isCodecSpecific = true;
- }
-
- if (isGraphicBuffer || mQuirks & kOutputBuffersAreUnreadable) {
- buffer->meta_data()->setInt32(kKeyIsUnreadable, true);
- }
-
- buffer->meta_data()->setInt32(
- kKeyBufferID,
- msg.u.extended_buffer_data.buffer);
-
- if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_EOS) {
- CODEC_LOGV("No more output data.");
- mNoMoreOutputData = true;
- }
-
- if (mIsEncoder && mIsVideo) {
- int64_t decodingTimeUs = isCodecSpecific? 0: getDecodingTimeUs();
- buffer->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs);
- }
-
- if (mTargetTimeUs >= 0) {
- CHECK(msg.u.extended_buffer_data.timestamp <= mTargetTimeUs);
-
- if (msg.u.extended_buffer_data.timestamp < mTargetTimeUs) {
- CODEC_LOGV(
- "skipping output buffer at timestamp %lld us",
- msg.u.extended_buffer_data.timestamp);
-
- fillOutputBuffer(info);
- break;
- }
-
- CODEC_LOGV(
- "returning output buffer at target timestamp "
- "%lld us",
- msg.u.extended_buffer_data.timestamp);
-
- mTargetTimeUs = -1;
- }
-
- mFilledBuffers.push_back(i);
- mBufferFilled.signal();
- if (mIsEncoder) {
- sched_yield();
- }
- }
-
- break;
- }
-
- default:
- {
- CHECK(!"should not be here.");
- break;
- }
- }
-}
-
-// Has the format changed in any way that the client would have to be aware of?
-static bool formatHasNotablyChanged(
- const sp<MetaData> &from, const sp<MetaData> &to) {
- if (from.get() == NULL && to.get() == NULL) {
- return false;
- }
-
- if ((from.get() == NULL && to.get() != NULL)
- || (from.get() != NULL && to.get() == NULL)) {
- return true;
- }
-
- const char *mime_from, *mime_to;
- CHECK(from->findCString(kKeyMIMEType, &mime_from));
- CHECK(to->findCString(kKeyMIMEType, &mime_to));
-
- if (strcasecmp(mime_from, mime_to)) {
- return true;
- }
-
- if (!strcasecmp(mime_from, MEDIA_MIMETYPE_VIDEO_RAW)) {
- int32_t colorFormat_from, colorFormat_to;
- CHECK(from->findInt32(kKeyColorFormat, &colorFormat_from));
- CHECK(to->findInt32(kKeyColorFormat, &colorFormat_to));
-
- if (colorFormat_from != colorFormat_to) {
- return true;
- }
-
- int32_t width_from, width_to;
- CHECK(from->findInt32(kKeyWidth, &width_from));
- CHECK(to->findInt32(kKeyWidth, &width_to));
-
- if (width_from != width_to) {
- return true;
- }
-
- int32_t height_from, height_to;
- CHECK(from->findInt32(kKeyHeight, &height_from));
- CHECK(to->findInt32(kKeyHeight, &height_to));
-
- if (height_from != height_to) {
- return true;
- }
-
- int32_t left_from, top_from, right_from, bottom_from;
- CHECK(from->findRect(
- kKeyCropRect,
- &left_from, &top_from, &right_from, &bottom_from));
-
- int32_t left_to, top_to, right_to, bottom_to;
- CHECK(to->findRect(
- kKeyCropRect,
- &left_to, &top_to, &right_to, &bottom_to));
-
- if (left_to != left_from || top_to != top_from
- || right_to != right_from || bottom_to != bottom_from) {
- return true;
- }
- } else if (!strcasecmp(mime_from, MEDIA_MIMETYPE_AUDIO_RAW)) {
- int32_t numChannels_from, numChannels_to;
- CHECK(from->findInt32(kKeyChannelCount, &numChannels_from));
- CHECK(to->findInt32(kKeyChannelCount, &numChannels_to));
-
- if (numChannels_from != numChannels_to) {
- return true;
- }
-
- int32_t sampleRate_from, sampleRate_to;
- CHECK(from->findInt32(kKeySampleRate, &sampleRate_from));
- CHECK(to->findInt32(kKeySampleRate, &sampleRate_to));
-
- if (sampleRate_from != sampleRate_to) {
- return true;
- }
- }
-
- return false;
-}
-
-void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
- switch (event) {
- case OMX_EventCmdComplete:
- {
- onCmdComplete((OMX_COMMANDTYPE)data1, data2);
- break;
- }
-
- case OMX_EventError:
- {
- CODEC_LOGE("OMX_EventError(0x%08x, %u)", data1, data2);
-
- setState(ERROR);
- break;
- }
-
- case OMX_EventPortSettingsChanged:
- {
- CODEC_LOGV("OMX_EventPortSettingsChanged(port=%u, data2=0x%08x)",
- data1, data2);
-
- if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
- onPortSettingsChanged(data1);
- } else if (data1 == kPortIndexOutput &&
- (data2 == OMX_IndexConfigCommonOutputCrop ||
- data2 == OMX_IndexConfigCommonScale)) {
-
- sp<MetaData> oldOutputFormat = mOutputFormat;
- initOutputFormat(mSource->getFormat());
-
- if (data2 == OMX_IndexConfigCommonOutputCrop &&
- formatHasNotablyChanged(oldOutputFormat, mOutputFormat)) {
- mOutputPortSettingsHaveChanged = true;
-
- } else if (data2 == OMX_IndexConfigCommonScale) {
- OMX_CONFIG_SCALEFACTORTYPE scale;
- InitOMXParams(&scale);
- scale.nPortIndex = kPortIndexOutput;
-
- // Change display dimension only when necessary.
- if (OK == mOMX->getConfig(
- mNode,
- OMX_IndexConfigCommonScale,
- &scale, sizeof(scale))) {
- int32_t left, top, right, bottom;
- CHECK(mOutputFormat->findRect(kKeyCropRect,
- &left, &top,
- &right, &bottom));
-
- // The scale is in 16.16 format.
- // scale 1.0 = 0x010000. When there is no
- // need to change the display, skip it.
- ALOGV("Get OMX_IndexConfigScale: 0x%x/0x%x",
- scale.xWidth, scale.xHeight);
-
- if (scale.xWidth != 0x010000) {
- mOutputFormat->setInt32(kKeyDisplayWidth,
- ((right - left + 1) * scale.xWidth) >> 16);
- mOutputPortSettingsHaveChanged = true;
- }
-
- if (scale.xHeight != 0x010000) {
- mOutputFormat->setInt32(kKeyDisplayHeight,
- ((bottom - top + 1) * scale.xHeight) >> 16);
- mOutputPortSettingsHaveChanged = true;
- }
- }
- }
- }
- break;
- }
-
-#if 0
- case OMX_EventBufferFlag:
- {
- CODEC_LOGV("EVENT_BUFFER_FLAG(%ld)", data1);
-
- if (data1 == kPortIndexOutput) {
- mNoMoreOutputData = true;
- }
- break;
- }
-#endif
-
- default:
- {
- CODEC_LOGV("EVENT(%d, %u, %u)", event, data1, data2);
- break;
- }
- }
-}
-
-void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
- switch (cmd) {
- case OMX_CommandStateSet:
- {
- onStateChange((OMX_STATETYPE)data);
- break;
- }
-
- case OMX_CommandPortDisable:
- {
- OMX_U32 portIndex = data;
- CODEC_LOGV("PORT_DISABLED(%u)", portIndex);
-
- CHECK(mState == EXECUTING || mState == RECONFIGURING);
- CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLING);
- CHECK_EQ(mPortBuffers[portIndex].size(), 0u);
-
- mPortStatus[portIndex] = DISABLED;
-
- if (mState == RECONFIGURING) {
- CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
-
- sp<MetaData> oldOutputFormat = mOutputFormat;
- initOutputFormat(mSource->getFormat());
-
- // Don't notify clients if the output port settings change
- // wasn't of importance to them, i.e. it may be that just the
- // number of buffers has changed and nothing else.
- bool formatChanged = formatHasNotablyChanged(oldOutputFormat, mOutputFormat);
- if (!mOutputPortSettingsHaveChanged) {
- mOutputPortSettingsHaveChanged = formatChanged;
- }
-
- status_t err = enablePortAsync(portIndex);
- if (err != OK) {
- CODEC_LOGE("enablePortAsync(%u) failed (err = %d)", portIndex, err);
- setState(ERROR);
- } else {
- err = allocateBuffersOnPort(portIndex);
- if (err != OK) {
- CODEC_LOGE("allocateBuffersOnPort (%s) failed "
- "(err = %d)",
- portIndex == kPortIndexInput
- ? "input" : "output",
- err);
-
- setState(ERROR);
- }
- }
- }
- break;
- }
-
- case OMX_CommandPortEnable:
- {
- OMX_U32 portIndex = data;
- CODEC_LOGV("PORT_ENABLED(%u)", portIndex);
-
- CHECK(mState == EXECUTING || mState == RECONFIGURING);
- CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLING);
-
- mPortStatus[portIndex] = ENABLED;
-
- if (mState == RECONFIGURING) {
- CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
-
- setState(EXECUTING);
-
- fillOutputBuffers();
- }
- break;
- }
-
- case OMX_CommandFlush:
- {
- OMX_U32 portIndex = data;
-
- CODEC_LOGV("FLUSH_DONE(%u)", portIndex);
-
- CHECK_EQ((int)mPortStatus[portIndex], (int)SHUTTING_DOWN);
- mPortStatus[portIndex] = ENABLED;
-
- CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]),
- mPortBuffers[portIndex].size());
-
- if (mSkipCutBuffer != NULL && mPortStatus[kPortIndexOutput] == ENABLED) {
- mSkipCutBuffer->clear();
- }
-
- if (mState == RECONFIGURING) {
- CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
-
- disablePortAsync(portIndex);
- } else if (mState == EXECUTING_TO_IDLE) {
- if (mPortStatus[kPortIndexInput] == ENABLED
- && mPortStatus[kPortIndexOutput] == ENABLED) {
- CODEC_LOGV("Finished flushing both ports, now completing "
- "transition from EXECUTING to IDLE.");
-
- mPortStatus[kPortIndexInput] = SHUTTING_DOWN;
- mPortStatus[kPortIndexOutput] = SHUTTING_DOWN;
-
- status_t err =
- mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
- CHECK_EQ(err, (status_t)OK);
- }
- } else {
- // We're flushing both ports in preparation for seeking.
-
- if (mPortStatus[kPortIndexInput] == ENABLED
- && mPortStatus[kPortIndexOutput] == ENABLED) {
- CODEC_LOGV("Finished flushing both ports, now continuing from"
- " seek-time.");
-
- // We implicitly resume pulling on our upstream source.
- mPaused = false;
-
- drainInputBuffers();
- fillOutputBuffers();
- }
-
- if (mOutputPortSettingsChangedPending) {
- CODEC_LOGV(
- "Honoring deferred output port settings change.");
-
- mOutputPortSettingsChangedPending = false;
- onPortSettingsChanged(kPortIndexOutput);
- }
- }
-
- break;
- }
-
- default:
- {
- CODEC_LOGV("CMD_COMPLETE(%d, %u)", cmd, data);
- break;
- }
- }
-}
-
-void OMXCodec::onStateChange(OMX_STATETYPE newState) {
- CODEC_LOGV("onStateChange %d", newState);
-
- switch (newState) {
- case OMX_StateIdle:
- {
- CODEC_LOGV("Now Idle.");
- if (mState == LOADED_TO_IDLE) {
- status_t err = mOMX->sendCommand(
- mNode, OMX_CommandStateSet, OMX_StateExecuting);
-
- CHECK_EQ(err, (status_t)OK);
-
- setState(IDLE_TO_EXECUTING);
- } else {
- CHECK_EQ((int)mState, (int)EXECUTING_TO_IDLE);
-
- if (countBuffersWeOwn(mPortBuffers[kPortIndexInput]) !=
- mPortBuffers[kPortIndexInput].size()) {
- ALOGE("Codec did not return all input buffers "
- "(received %zu / %zu)",
- countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
- mPortBuffers[kPortIndexInput].size());
- TRESPASS();
- }
-
- if (countBuffersWeOwn(mPortBuffers[kPortIndexOutput]) !=
- mPortBuffers[kPortIndexOutput].size()) {
- ALOGE("Codec did not return all output buffers "
- "(received %zu / %zu)",
- countBuffersWeOwn(mPortBuffers[kPortIndexOutput]),
- mPortBuffers[kPortIndexOutput].size());
- TRESPASS();
- }
-
- status_t err = mOMX->sendCommand(
- mNode, OMX_CommandStateSet, OMX_StateLoaded);
-
- CHECK_EQ(err, (status_t)OK);
-
- err = freeBuffersOnPort(kPortIndexInput);
- CHECK_EQ(err, (status_t)OK);
-
- err = freeBuffersOnPort(kPortIndexOutput);
- CHECK_EQ(err, (status_t)OK);
-
- mPortStatus[kPortIndexInput] = ENABLED;
- mPortStatus[kPortIndexOutput] = ENABLED;
-
- if ((mFlags & kEnableGrallocUsageProtected) &&
- mNativeWindow != NULL) {
- // We push enough 1x1 blank buffers to ensure that one of
- // them has made it to the display. This allows the OMX
- // component teardown to zero out any protected buffers
- // without the risk of scanning out one of those buffers.
- pushBlankBuffersToNativeWindow(mNativeWindow.get());
- }
-
- setState(IDLE_TO_LOADED);
- }
- break;
- }
-
- case OMX_StateExecuting:
- {
- CHECK_EQ((int)mState, (int)IDLE_TO_EXECUTING);
-
- CODEC_LOGV("Now Executing.");
-
- mOutputPortSettingsChangedPending = false;
-
- setState(EXECUTING);
-
- // Buffers will be submitted to the component in the first
- // call to OMXCodec::read as mInitialBufferSubmit is true at
- // this point. This ensures that this on_message call returns,
- // releases the lock and ::init can notice the state change and
- // itself return.
- break;
- }
-
- case OMX_StateLoaded:
- {
- CHECK_EQ((int)mState, (int)IDLE_TO_LOADED);
-
- CODEC_LOGV("Now Loaded.");
-
- setState(LOADED);
- break;
- }
-
- case OMX_StateInvalid:
- {
- setState(ERROR);
- break;
- }
-
- default:
- {
- CHECK(!"should not be here.");
- break;
- }
- }
-}
-
-// static
-size_t OMXCodec::countBuffersWeOwn(const Vector<BufferInfo> &buffers) {
- size_t n = 0;
- for (size_t i = 0; i < buffers.size(); ++i) {
- if (buffers[i].mStatus != OWNED_BY_COMPONENT) {
- ++n;
- }
- }
-
- return n;
-}
-
-status_t OMXCodec::freeBuffersOnPort(
- OMX_U32 portIndex, bool onlyThoseWeOwn) {
- Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
-
- status_t stickyErr = OK;
-
- for (size_t i = buffers->size(); i > 0;) {
- i--;
- BufferInfo *info = &buffers->editItemAt(i);
-
- if (onlyThoseWeOwn && info->mStatus == OWNED_BY_COMPONENT) {
- continue;
- }
-
- CHECK(info->mStatus == OWNED_BY_US
- || info->mStatus == OWNED_BY_NATIVE_WINDOW);
-
- CODEC_LOGV("freeing buffer %u on port %u", info->mBuffer, portIndex);
-
- status_t err = freeBuffer(portIndex, i);
-
- if (err != OK) {
- stickyErr = err;
- }
-
- }
-
- CHECK(onlyThoseWeOwn || buffers->isEmpty());
-
- return stickyErr;
-}
-
-status_t OMXCodec::freeBuffer(OMX_U32 portIndex, size_t bufIndex) {
- Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
-
- BufferInfo *info = &buffers->editItemAt(bufIndex);
-
- status_t err = mOMX->freeBuffer(mNode, portIndex, info->mBuffer);
-
- if (err == OK && info->mMediaBuffer != NULL) {
- CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
- info->mMediaBuffer->setObserver(NULL);
-
- // Make sure nobody but us owns this buffer at this point.
- CHECK_EQ(info->mMediaBuffer->refcount(), 0);
-
- // Cancel the buffer if it belongs to an ANativeWindow.
- sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer();
- if (info->mStatus == OWNED_BY_US && graphicBuffer != 0) {
- err = cancelBufferToNativeWindow(info);
- }
-
- info->mMediaBuffer->release();
- info->mMediaBuffer = NULL;
- }
-
- if (err == OK) {
- buffers->removeAt(bufIndex);
- }
-
- return err;
-}
-
-void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) {
- CODEC_LOGV("PORT_SETTINGS_CHANGED(%u)", portIndex);
-
- CHECK(mState == EXECUTING || mState == EXECUTING_TO_IDLE);
- CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
- CHECK(!mOutputPortSettingsChangedPending);
-
- if (mPortStatus[kPortIndexOutput] != ENABLED) {
- CODEC_LOGV("Deferring output port settings change.");
- mOutputPortSettingsChangedPending = true;
- return;
- }
-
- setState(RECONFIGURING);
-
- if (mQuirks & kNeedsFlushBeforeDisable) {
- if (!flushPortAsync(portIndex)) {
- onCmdComplete(OMX_CommandFlush, portIndex);
- }
- } else {
- disablePortAsync(portIndex);
- }
-}
-
-bool OMXCodec::flushPortAsync(OMX_U32 portIndex) {
- CHECK(mState == EXECUTING || mState == RECONFIGURING
- || mState == EXECUTING_TO_IDLE);
-
- CODEC_LOGV("flushPortAsync(%u): we own %zu out of %zu buffers already.",
- portIndex, countBuffersWeOwn(mPortBuffers[portIndex]),
- mPortBuffers[portIndex].size());
-
- CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED);
- mPortStatus[portIndex] = SHUTTING_DOWN;
-
- if ((mQuirks & kRequiresFlushCompleteEmulation)
- && countBuffersWeOwn(mPortBuffers[portIndex])
- == mPortBuffers[portIndex].size()) {
- // No flush is necessary and this component fails to send a
- // flush-complete event in this case.
-
- return false;
- }
-
- status_t err =
- mOMX->sendCommand(mNode, OMX_CommandFlush, portIndex);
- CHECK_EQ(err, (status_t)OK);
-
- return true;
-}
-
-void OMXCodec::disablePortAsync(OMX_U32 portIndex) {
- CHECK(mState == EXECUTING || mState == RECONFIGURING);
-
- CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED);
- mPortStatus[portIndex] = DISABLING;
-
- CODEC_LOGV("sending OMX_CommandPortDisable(%u)", portIndex);
- status_t err =
- mOMX->sendCommand(mNode, OMX_CommandPortDisable, portIndex);
- CHECK_EQ(err, (status_t)OK);
-
- freeBuffersOnPort(portIndex, true);
-}
-
-status_t OMXCodec::enablePortAsync(OMX_U32 portIndex) {
- CHECK(mState == EXECUTING || mState == RECONFIGURING);
-
- CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLED);
- mPortStatus[portIndex] = ENABLING;
-
- CODEC_LOGV("sending OMX_CommandPortEnable(%u)", portIndex);
- return mOMX->sendCommand(mNode, OMX_CommandPortEnable, portIndex);
-}
-
-void OMXCodec::fillOutputBuffers() {
- CHECK_EQ((int)mState, (int)EXECUTING);
-
- // This is a workaround for some decoders not properly reporting
- // end-of-output-stream. If we own all input buffers and also own
- // all output buffers and we already signalled end-of-input-stream,
- // the end-of-output-stream is implied.
- if (mSignalledEOS
- && countBuffersWeOwn(mPortBuffers[kPortIndexInput])
- == mPortBuffers[kPortIndexInput].size()
- && countBuffersWeOwn(mPortBuffers[kPortIndexOutput])
- == mPortBuffers[kPortIndexOutput].size()) {
- mNoMoreOutputData = true;
- mBufferFilled.signal();
-
- return;
- }
-
- Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
- for (size_t i = 0; i < buffers->size(); ++i) {
- BufferInfo *info = &buffers->editItemAt(i);
- if (info->mStatus == OWNED_BY_US) {
- fillOutputBuffer(&buffers->editItemAt(i));
- }
- }
-}
-
-void OMXCodec::drainInputBuffers() {
- CHECK(mState == EXECUTING || mState == RECONFIGURING);
-
- if (mFlags & kUseSecureInputBuffers) {
- Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
- for (size_t i = 0; i < buffers->size(); ++i) {
- if (!drainAnyInputBuffer()
- || (mFlags & kOnlySubmitOneInputBufferAtOneTime)) {
- break;
- }
- }
- } else {
- Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
- for (size_t i = 0; i < buffers->size(); ++i) {
- BufferInfo *info = &buffers->editItemAt(i);
-
- if (info->mStatus != OWNED_BY_US) {
- continue;
- }
-
- if (!drainInputBuffer(info)) {
- break;
- }
-
- if (mFlags & kOnlySubmitOneInputBufferAtOneTime) {
- break;
- }
- }
- }
-}
-
-bool OMXCodec::drainAnyInputBuffer() {
- return drainInputBuffer((BufferInfo *)NULL);
-}
-
-OMXCodec::BufferInfo *OMXCodec::findInputBufferByDataPointer(void *ptr) {
- Vector<BufferInfo> *infos = &mPortBuffers[kPortIndexInput];
- for (size_t i = 0; i < infos->size(); ++i) {
- BufferInfo *info = &infos->editItemAt(i);
-
- if (info->mData == ptr) {
- CODEC_LOGV(
- "input buffer data ptr = %p, buffer_id = %u",
- ptr,
- info->mBuffer);
-
- return info;
- }
- }
-
- TRESPASS();
-}
-
-OMXCodec::BufferInfo *OMXCodec::findEmptyInputBuffer() {
- Vector<BufferInfo> *infos = &mPortBuffers[kPortIndexInput];
- for (size_t i = 0; i < infos->size(); ++i) {
- BufferInfo *info = &infos->editItemAt(i);
-
- if (info->mStatus == OWNED_BY_US) {
- return info;
- }
- }
-
- TRESPASS();
-}
-
-bool OMXCodec::drainInputBuffer(BufferInfo *info) {
- if (info != NULL) {
- CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);
- }
-
- if (mSignalledEOS) {
- return false;
- }
-
- if (mCodecSpecificDataIndex < mCodecSpecificData.size()) {
- CHECK(!(mFlags & kUseSecureInputBuffers));
-
- const CodecSpecificData *specific =
- mCodecSpecificData[mCodecSpecificDataIndex];
-
- size_t size = specific->mSize;
-
- if ((!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mMIME) ||
- !strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mMIME))
- && !(mQuirks & kWantsNALFragments)) {
- static const uint8_t kNALStartCode[4] =
- { 0x00, 0x00, 0x00, 0x01 };
-
- CHECK(info->mSize >= specific->mSize + 4);
-
- size += 4;
-
- memcpy(info->mData, kNALStartCode, 4);
- memcpy((uint8_t *)info->mData + 4,
- specific->mData, specific->mSize);
- } else {
- CHECK(info->mSize >= specific->mSize);
- memcpy(info->mData, specific->mData, specific->mSize);
- }
-
- mNoMoreOutputData = false;
-
- CODEC_LOGV("calling emptyBuffer with codec specific data");
-
- status_t err = mOMX->emptyBuffer(
- mNode, info->mBuffer, 0, size,
- OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,
- 0);
- CHECK_EQ(err, (status_t)OK);
-
- info->mStatus = OWNED_BY_COMPONENT;
-
- ++mCodecSpecificDataIndex;
- return true;
- }
-
- if (mPaused) {
- return false;
- }
-
- status_t err;
-
- bool signalEOS = false;
- int64_t timestampUs = 0;
-
- size_t offset = 0;
- int32_t n = 0;
-
-
- for (;;) {
- MediaBuffer *srcBuffer;
- if (mSeekTimeUs >= 0) {
- if (mLeftOverBuffer) {
- mLeftOverBuffer->release();
- mLeftOverBuffer = NULL;
- }
-
- MediaSource::ReadOptions options;
- options.setSeekTo(mSeekTimeUs, mSeekMode);
-
- mSeekTimeUs = -1;
- mSeekMode = ReadOptions::SEEK_CLOSEST_SYNC;
- mBufferFilled.signal();
-
- err = mSource->read(&srcBuffer, &options);
-
- if (err == OK) {
- int64_t targetTimeUs;
- if (srcBuffer->meta_data()->findInt64(
- kKeyTargetTime, &targetTimeUs)
- && targetTimeUs >= 0) {
- CODEC_LOGV("targetTimeUs = %lld us", (long long)targetTimeUs);
- mTargetTimeUs = targetTimeUs;
- } else {
- mTargetTimeUs = -1;
- }
- }
- } else if (mLeftOverBuffer) {
- srcBuffer = mLeftOverBuffer;
- mLeftOverBuffer = NULL;
-
- err = OK;
- } else {
- err = mSource->read(&srcBuffer);
- }
-
- if (err != OK) {
- signalEOS = true;
- mFinalStatus = err;
- mSignalledEOS = true;
- mBufferFilled.signal();
- break;
- }
-
- if (mFlags & kUseSecureInputBuffers) {
- info = findInputBufferByDataPointer(srcBuffer->data());
- CHECK(info != NULL);
- }
-
- size_t remainingBytes = info->mSize - offset;
-
- if (srcBuffer->range_length() > remainingBytes) {
- if (offset == 0) {
- CODEC_LOGE(
- "Codec's input buffers are too small to accomodate "
- "buffer read from source (info->mSize = %zu, srcLength = %zu)",
- info->mSize, srcBuffer->range_length());
-
- srcBuffer->release();
- srcBuffer = NULL;
-
- setState(ERROR);
- return false;
- }
-
- mLeftOverBuffer = srcBuffer;
- break;
- }
-
- bool releaseBuffer = true;
- if (mFlags & kStoreMetaDataInVideoBuffers) {
- releaseBuffer = false;
- info->mMediaBuffer = srcBuffer;
- }
-
- if (mFlags & kUseSecureInputBuffers) {
- // Data in "info" is already provided at this time.
-
- releaseBuffer = false;
-
- CHECK(info->mMediaBuffer == NULL);
- info->mMediaBuffer = srcBuffer;
- } else {
- CHECK(srcBuffer->data() != NULL) ;
- memcpy((uint8_t *)info->mData + offset,
- (const uint8_t *)srcBuffer->data()
- + srcBuffer->range_offset(),
- srcBuffer->range_length());
- }
-
- int64_t lastBufferTimeUs;
- CHECK(srcBuffer->meta_data()->findInt64(kKeyTime, &lastBufferTimeUs));
- CHECK(lastBufferTimeUs >= 0);
- if (mIsEncoder && mIsVideo) {
- mDecodingTimeList.push_back(lastBufferTimeUs);
- }
-
- if (offset == 0) {
- timestampUs = lastBufferTimeUs;
- }
-
- offset += srcBuffer->range_length();
-
- if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_VORBIS, mMIME)) {
- CHECK(!(mQuirks & kSupportsMultipleFramesPerInputBuffer));
- CHECK_GE(info->mSize, offset + sizeof(int32_t));
-
- int32_t numPageSamples;
- if (!srcBuffer->meta_data()->findInt32(
- kKeyValidSamples, &numPageSamples)) {
- numPageSamples = -1;
- }
-
- memcpy((uint8_t *)info->mData + offset,
- &numPageSamples,
- sizeof(numPageSamples));
-
- offset += sizeof(numPageSamples);
- }
-
- if (releaseBuffer) {
- srcBuffer->release();
- srcBuffer = NULL;
- }
-
- ++n;
-
- if (!(mQuirks & kSupportsMultipleFramesPerInputBuffer)) {
- break;
- }
-
- int64_t coalescedDurationUs = lastBufferTimeUs - timestampUs;
-
- if (coalescedDurationUs > 250000ll) {
- // Don't coalesce more than 250ms worth of encoded data at once.
- break;
- }
- }
-
- if (n > 1) {
- ALOGV("coalesced %d frames into one input buffer", n);
- }
-
- OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
-
- if (signalEOS) {
- flags |= OMX_BUFFERFLAG_EOS;
- } else {
- mNoMoreOutputData = false;
- }
-
- if (info == NULL) {
- CHECK(mFlags & kUseSecureInputBuffers);
- CHECK(signalEOS);
-
- // This is fishy, there's still a MediaBuffer corresponding to this
- // info available to the source at this point even though we're going
- // to use it to signal EOS to the codec.
- info = findEmptyInputBuffer();
- }
-
- CODEC_LOGV("Calling emptyBuffer on buffer %u (length %zu), "
- "timestamp %lld us (%.2f secs)",
- info->mBuffer, offset,
- (long long)timestampUs, timestampUs / 1E6);
-
- err = mOMX->emptyBuffer(
- mNode, info->mBuffer, 0, offset,
- flags, timestampUs);
-
- if (err != OK) {
- setState(ERROR);
- return false;
- }
-
- info->mStatus = OWNED_BY_COMPONENT;
-
- return true;
-}
-
-void OMXCodec::fillOutputBuffer(BufferInfo *info) {
- CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);
-
- if (mNoMoreOutputData) {
- CODEC_LOGV("There is no more output data available, not "
- "calling fillOutputBuffer");
- return;
- }
-
- CODEC_LOGV("Calling fillBuffer on buffer %u", info->mBuffer);
- status_t err = mOMX->fillBuffer(mNode, info->mBuffer);
-
- if (err != OK) {
- CODEC_LOGE("fillBuffer failed w/ error 0x%08x", err);
-
- setState(ERROR);
- return;
- }
-
- info->mStatus = OWNED_BY_COMPONENT;
-}
-
-bool OMXCodec::drainInputBuffer(IOMX::buffer_id buffer) {
- Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
- for (size_t i = 0; i < buffers->size(); ++i) {
- if ((*buffers)[i].mBuffer == buffer) {
- return drainInputBuffer(&buffers->editItemAt(i));
- }
- }
-
- CHECK(!"should not be here.");
-
- return false;
-}
-
-void OMXCodec::fillOutputBuffer(IOMX::buffer_id buffer) {
- Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
- for (size_t i = 0; i < buffers->size(); ++i) {
- if ((*buffers)[i].mBuffer == buffer) {
- fillOutputBuffer(&buffers->editItemAt(i));
- return;
- }
- }
-
- CHECK(!"should not be here.");
-}
-
-void OMXCodec::setState(State newState) {
- mState = newState;
- mAsyncCompletion.signal();
-
- // This may cause some spurious wakeups but is necessary to
- // unblock the reader if we enter ERROR state.
- mBufferFilled.signal();
-}
-
-status_t OMXCodec::waitForBufferFilled_l() {
-
- if (mIsEncoder) {
- // For timelapse video recording, the timelapse video recording may
- // not send an input frame for a _long_ time. Do not use timeout
- // for video encoding.
- return mBufferFilled.wait(mLock);
- }
- status_t err = mBufferFilled.waitRelative(mLock, kBufferFilledEventTimeOutNs);
- if (err != OK) {
- CODEC_LOGE("Timed out waiting for output buffers: %zu/%zu",
- countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
- countBuffersWeOwn(mPortBuffers[kPortIndexOutput]));
- }
- return err;
-}
-
-void OMXCodec::setRawAudioFormat(
- OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels) {
-
- // port definition
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = portIndex;
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
- def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
- CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition,
- &def, sizeof(def)), (status_t)OK);
-
- // pcm param
- OMX_AUDIO_PARAM_PCMMODETYPE pcmParams;
- InitOMXParams(&pcmParams);
- pcmParams.nPortIndex = portIndex;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
-
- CHECK_EQ(err, (status_t)OK);
-
- pcmParams.nChannels = numChannels;
- pcmParams.eNumData = OMX_NumericalDataSigned;
- pcmParams.bInterleaved = OMX_TRUE;
- pcmParams.nBitPerSample = 16;
- pcmParams.nSamplingRate = sampleRate;
- pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear;
-
- CHECK_EQ(getOMXChannelMapping(
- numChannels, pcmParams.eChannelMapping), (status_t)OK);
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
-
- CHECK_EQ(err, (status_t)OK);
-}
-
-static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate(bool isAMRWB, int32_t bps) {
- if (isAMRWB) {
- if (bps <= 6600) {
- return OMX_AUDIO_AMRBandModeWB0;
- } else if (bps <= 8850) {
- return OMX_AUDIO_AMRBandModeWB1;
- } else if (bps <= 12650) {
- return OMX_AUDIO_AMRBandModeWB2;
- } else if (bps <= 14250) {
- return OMX_AUDIO_AMRBandModeWB3;
- } else if (bps <= 15850) {
- return OMX_AUDIO_AMRBandModeWB4;
- } else if (bps <= 18250) {
- return OMX_AUDIO_AMRBandModeWB5;
- } else if (bps <= 19850) {
- return OMX_AUDIO_AMRBandModeWB6;
- } else if (bps <= 23050) {
- return OMX_AUDIO_AMRBandModeWB7;
- }
-
- // 23850 bps
- return OMX_AUDIO_AMRBandModeWB8;
- } else { // AMRNB
- if (bps <= 4750) {
- return OMX_AUDIO_AMRBandModeNB0;
- } else if (bps <= 5150) {
- return OMX_AUDIO_AMRBandModeNB1;
- } else if (bps <= 5900) {
- return OMX_AUDIO_AMRBandModeNB2;
- } else if (bps <= 6700) {
- return OMX_AUDIO_AMRBandModeNB3;
- } else if (bps <= 7400) {
- return OMX_AUDIO_AMRBandModeNB4;
- } else if (bps <= 7950) {
- return OMX_AUDIO_AMRBandModeNB5;
- } else if (bps <= 10200) {
- return OMX_AUDIO_AMRBandModeNB6;
- }
-
- // 12200 bps
- return OMX_AUDIO_AMRBandModeNB7;
- }
-}
-
-void OMXCodec::setAMRFormat(bool isWAMR, int32_t bitRate) {
- OMX_U32 portIndex = mIsEncoder ? kPortIndexOutput : kPortIndexInput;
-
- OMX_AUDIO_PARAM_AMRTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = portIndex;
-
- status_t err =
- mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
-
- CHECK_EQ(err, (status_t)OK);
-
- def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
-
- def.eAMRBandMode = pickModeFromBitRate(isWAMR, bitRate);
- err = mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- ////////////////////////
-
- if (mIsEncoder) {
- sp<MetaData> format = mSource->getFormat();
- int32_t sampleRate;
- int32_t numChannels;
- CHECK(format->findInt32(kKeySampleRate, &sampleRate));
- CHECK(format->findInt32(kKeyChannelCount, &numChannels));
-
- setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
- }
-}
-
-status_t OMXCodec::setAACFormat(
- int32_t numChannels, int32_t sampleRate, int32_t bitRate, int32_t aacProfile, bool isADTS) {
- if (numChannels > 2) {
- ALOGW("Number of channels: (%d) \n", numChannels);
- }
-
- if (mIsEncoder) {
- if (isADTS) {
- return -EINVAL;
- }
-
- //////////////// input port ////////////////////
- setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
-
- //////////////// output port ////////////////////
- // format
- OMX_AUDIO_PARAM_PORTFORMATTYPE format;
- InitOMXParams(&format);
- format.nPortIndex = kPortIndexOutput;
- format.nIndex = 0;
- status_t err = OMX_ErrorNone;
- while (OMX_ErrorNone == err) {
- CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioPortFormat,
- &format, sizeof(format)), (status_t)OK);
- if (format.eEncoding == OMX_AUDIO_CodingAAC) {
- break;
- }
- format.nIndex++;
- }
- CHECK_EQ((status_t)OK, err);
- CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioPortFormat,
- &format, sizeof(format)), (status_t)OK);
-
- // port definition
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexOutput;
- CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamPortDefinition,
- &def, sizeof(def)), (status_t)OK);
- def.format.audio.bFlagErrorConcealment = OMX_TRUE;
- def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
- CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition,
- &def, sizeof(def)), (status_t)OK);
-
- // profile
- OMX_AUDIO_PARAM_AACPROFILETYPE profile;
- InitOMXParams(&profile);
- profile.nPortIndex = kPortIndexOutput;
- CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioAac,
- &profile, sizeof(profile)), (status_t)OK);
- profile.nChannels = numChannels;
- profile.eChannelMode = (numChannels == 1?
- OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo);
- profile.nSampleRate = sampleRate;
- profile.nBitRate = bitRate;
- profile.nAudioBandWidth = 0;
- profile.nFrameLength = 0;
- profile.nAACtools = OMX_AUDIO_AACToolAll;
- profile.nAACERtools = OMX_AUDIO_AACERNone;
- profile.eAACProfile = (OMX_AUDIO_AACPROFILETYPE) aacProfile;
- profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
- err = mOMX->setParameter(mNode, OMX_IndexParamAudioAac,
- &profile, sizeof(profile));
-
- if (err != OK) {
- CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed "
- "(err = %d)",
- err);
- return err;
- }
- } else {
- OMX_AUDIO_PARAM_AACPROFILETYPE profile;
- InitOMXParams(&profile);
- profile.nPortIndex = kPortIndexInput;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
- CHECK_EQ(err, (status_t)OK);
-
- profile.nChannels = numChannels;
- profile.nSampleRate = sampleRate;
-
- profile.eAACStreamFormat =
- isADTS
- ? OMX_AUDIO_AACStreamFormatMP4ADTS
- : OMX_AUDIO_AACStreamFormatMP4FF;
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
-
- if (err != OK) {
- CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed "
- "(err = %d)",
- err);
- return err;
- }
- }
-
- return OK;
-}
-
-status_t OMXCodec::setAC3Format(int32_t numChannels, int32_t sampleRate) {
- OMX_AUDIO_PARAM_ANDROID_AC3TYPE def;
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexInput;
-
- status_t err = mOMX->getParameter(
- mNode,
- (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
- &def,
- sizeof(def));
-
- if (err != OK) {
- return err;
- }
-
- def.nChannels = numChannels;
- def.nSampleRate = sampleRate;
-
- return mOMX->setParameter(
- mNode,
- (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
- &def,
- sizeof(def));
-}
-
-void OMXCodec::setG711Format(int32_t sampleRate, int32_t numChannels) {
- CHECK(!mIsEncoder);
- setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
-}
-
-void OMXCodec::setImageOutputFormat(
- OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height) {
- CODEC_LOGV("setImageOutputFormat(%u, %u)", width, height);
-
-#if 0
- OMX_INDEXTYPE index;
- status_t err = mOMX->get_extension_index(
- mNode, "OMX.TI.JPEG.decode.Config.OutputColorFormat", &index);
- CHECK_EQ(err, (status_t)OK);
-
- err = mOMX->set_config(mNode, index, &format, sizeof(format));
- CHECK_EQ(err, (status_t)OK);
-#endif
-
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexOutput;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainImage);
-
- OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
-
- CHECK_EQ((int)imageDef->eCompressionFormat, (int)OMX_IMAGE_CodingUnused);
- imageDef->eColorFormat = format;
- imageDef->nFrameWidth = width;
- imageDef->nFrameHeight = height;
-
- switch (format) {
- case OMX_COLOR_FormatYUV420PackedPlanar:
- case OMX_COLOR_FormatYUV411Planar:
- {
- def.nBufferSize = (width * height * 3) / 2;
- break;
- }
-
- case OMX_COLOR_FormatCbYCrY:
- {
- def.nBufferSize = width * height * 2;
- break;
- }
-
- case OMX_COLOR_Format32bitARGB8888:
- {
- def.nBufferSize = width * height * 4;
- break;
- }
-
- case OMX_COLOR_Format16bitARGB4444:
- case OMX_COLOR_Format16bitARGB1555:
- case OMX_COLOR_Format16bitRGB565:
- case OMX_COLOR_Format16bitBGR565:
- {
- def.nBufferSize = width * height * 2;
- break;
- }
-
- default:
- CHECK(!"Should not be here. Unknown color format.");
- break;
- }
-
- def.nBufferCountActual = def.nBufferCountMin;
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-}
-
-void OMXCodec::setJPEGInputFormat(
- OMX_U32 width, OMX_U32 height, OMX_U32 compressedSize) {
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexInput;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainImage);
- OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
-
- CHECK_EQ((int)imageDef->eCompressionFormat, (int)OMX_IMAGE_CodingJPEG);
- imageDef->nFrameWidth = width;
- imageDef->nFrameHeight = height;
-
- def.nBufferSize = compressedSize;
- def.nBufferCountActual = def.nBufferCountMin;
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-}
-
-void OMXCodec::addCodecSpecificData(const void *data, size_t size) {
- CodecSpecificData *specific =
- (CodecSpecificData *)malloc(sizeof(CodecSpecificData) + size - 1);
-
- specific->mSize = size;
- memcpy(specific->mData, data, size);
-
- mCodecSpecificData.push(specific);
-}
-
-void OMXCodec::clearCodecSpecificData() {
- for (size_t i = 0; i < mCodecSpecificData.size(); ++i) {
- free(mCodecSpecificData.editItemAt(i));
- }
- mCodecSpecificData.clear();
- mCodecSpecificDataIndex = 0;
-}
-
-status_t OMXCodec::start(MetaData *meta) {
- Mutex::Autolock autoLock(mLock);
-
- if (mState != LOADED) {
- CODEC_LOGE("called start in the unexpected state: %d", mState);
- return UNKNOWN_ERROR;
- }
-
- sp<MetaData> params = new MetaData;
- if (mQuirks & kWantsNALFragments) {
- params->setInt32(kKeyWantsNALFragments, true);
- }
- if (meta) {
- int64_t startTimeUs = 0;
- int64_t timeUs;
- if (meta->findInt64(kKeyTime, &timeUs)) {
- startTimeUs = timeUs;
- }
- params->setInt64(kKeyTime, startTimeUs);
- }
-
- mCodecSpecificDataIndex = 0;
- mInitialBufferSubmit = true;
- mSignalledEOS = false;
- mNoMoreOutputData = false;
- mOutputPortSettingsHaveChanged = false;
- mSeekTimeUs = -1;
- mSeekMode = ReadOptions::SEEK_CLOSEST_SYNC;
- mTargetTimeUs = -1;
- mFilledBuffers.clear();
- mPaused = false;
-
- status_t err;
- if (mIsEncoder) {
- // Calling init() before starting its source so that we can configure,
- // if supported, the source to use exactly the same number of input
- // buffers as requested by the encoder.
- if ((err = init()) != OK) {
- CODEC_LOGE("init failed: %d", err);
- return err;
- }
-
- params->setInt32(kKeyNumBuffers, mPortBuffers[kPortIndexInput].size());
- err = mSource->start(params.get());
- if (err != OK) {
- CODEC_LOGE("source failed to start: %d", err);
- stopOmxComponent_l();
- }
- return err;
- }
-
- // Decoder case
- if ((err = mSource->start(params.get())) != OK) {
- CODEC_LOGE("source failed to start: %d", err);
- return err;
- }
- return init();
-}
-
-status_t OMXCodec::stop() {
- CODEC_LOGV("stop mState=%d", mState);
- Mutex::Autolock autoLock(mLock);
- status_t err = stopOmxComponent_l();
- mSource->stop();
-
- CODEC_LOGV("stopped in state %d", mState);
- return err;
-}
-
-status_t OMXCodec::stopOmxComponent_l() {
- CODEC_LOGV("stopOmxComponent_l mState=%d", mState);
-
- while (isIntermediateState(mState)) {
- mAsyncCompletion.wait(mLock);
- }
-
- bool isError = false;
- switch (mState) {
- case LOADED:
- break;
-
- case ERROR:
- {
- if (mPortStatus[kPortIndexOutput] == ENABLING) {
- // Codec is in a wedged state (technical term)
- // We've seen an output port settings change from the codec,
- // We've disabled the output port, then freed the output
- // buffers, initiated re-enabling the output port but
- // failed to reallocate the output buffers.
- // There doesn't seem to be a way to orderly transition
- // from executing->idle and idle->loaded now that the
- // output port hasn't been reenabled yet...
- // Simply free as many resources as we can and pretend
- // that we're in LOADED state so that the destructor
- // will free the component instance without asserting.
- freeBuffersOnPort(kPortIndexInput, true /* onlyThoseWeOwn */);
- freeBuffersOnPort(kPortIndexOutput, true /* onlyThoseWeOwn */);
- setState(LOADED);
- break;
- } else {
- OMX_STATETYPE state = OMX_StateInvalid;
- status_t err = mOMX->getState(mNode, &state);
- CHECK_EQ(err, (status_t)OK);
-
- if (state != OMX_StateExecuting) {
- break;
- }
- // else fall through to the idling code
- }
-
- isError = true;
- }
-
- case EXECUTING:
- {
- setState(EXECUTING_TO_IDLE);
-
- if (mQuirks & kRequiresFlushBeforeShutdown) {
- CODEC_LOGV("This component requires a flush before transitioning "
- "from EXECUTING to IDLE...");
-
- bool emulateInputFlushCompletion =
- !flushPortAsync(kPortIndexInput);
-
- bool emulateOutputFlushCompletion =
- !flushPortAsync(kPortIndexOutput);
-
- if (emulateInputFlushCompletion) {
- onCmdComplete(OMX_CommandFlush, kPortIndexInput);
- }
-
- if (emulateOutputFlushCompletion) {
- onCmdComplete(OMX_CommandFlush, kPortIndexOutput);
- }
- } else {
- mPortStatus[kPortIndexInput] = SHUTTING_DOWN;
- mPortStatus[kPortIndexOutput] = SHUTTING_DOWN;
-
- status_t err =
- mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
- CHECK_EQ(err, (status_t)OK);
- }
-
- while (mState != LOADED && mState != ERROR) {
- mAsyncCompletion.wait(mLock);
- }
-
- if (isError) {
- // We were in the ERROR state coming in, so restore that now
- // that we've idled the OMX component.
- setState(ERROR);
- }
-
- break;
- }
-
- default:
- {
- CHECK(!"should not be here.");
- break;
- }
- }
-
- if (mLeftOverBuffer) {
- mLeftOverBuffer->release();
- mLeftOverBuffer = NULL;
- }
-
- return OK;
-}
-
-sp<MetaData> OMXCodec::getFormat() {
- Mutex::Autolock autoLock(mLock);
-
- return mOutputFormat;
-}
-
-status_t OMXCodec::read(
- MediaBuffer **buffer, const ReadOptions *options) {
- status_t err = OK;
- *buffer = NULL;
-
- Mutex::Autolock autoLock(mLock);
-
- if (mState != EXECUTING && mState != RECONFIGURING) {
- return UNKNOWN_ERROR;
- }
-
- bool seeking = false;
- int64_t seekTimeUs;
- ReadOptions::SeekMode seekMode;
- if (options && options->getSeekTo(&seekTimeUs, &seekMode)) {
- seeking = true;
- }
-
- if (mInitialBufferSubmit) {
- mInitialBufferSubmit = false;
-
- if (seeking) {
- CHECK(seekTimeUs >= 0);
- mSeekTimeUs = seekTimeUs;
- mSeekMode = seekMode;
-
- // There's no reason to trigger the code below, there's
- // nothing to flush yet.
- seeking = false;
- mPaused = false;
- }
-
- drainInputBuffers();
-
- if (mState == EXECUTING) {
- // Otherwise mState == RECONFIGURING and this code will trigger
- // after the output port is reenabled.
- fillOutputBuffers();
- }
- }
-
- if (seeking) {
- while (mState == RECONFIGURING) {
- if ((err = waitForBufferFilled_l()) != OK) {
- return err;
- }
- }
-
- if (mState != EXECUTING) {
- return UNKNOWN_ERROR;
- }
-
- CODEC_LOGV("seeking to %" PRId64 " us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6);
-
- mSignalledEOS = false;
-
- CHECK(seekTimeUs >= 0);
- mSeekTimeUs = seekTimeUs;
- mSeekMode = seekMode;
-
- mFilledBuffers.clear();
-
- CHECK_EQ((int)mState, (int)EXECUTING);
-
- bool emulateInputFlushCompletion = !flushPortAsync(kPortIndexInput);
- bool emulateOutputFlushCompletion = !flushPortAsync(kPortIndexOutput);
-
- if (emulateInputFlushCompletion) {
- onCmdComplete(OMX_CommandFlush, kPortIndexInput);
- }
-
- if (emulateOutputFlushCompletion) {
- onCmdComplete(OMX_CommandFlush, kPortIndexOutput);
- }
-
- while (mSeekTimeUs >= 0) {
- if ((err = waitForBufferFilled_l()) != OK) {
- return err;
- }
- }
- }
-
- while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {
- if ((err = waitForBufferFilled_l()) != OK) {
- return err;
- }
- }
-
- if (mState == ERROR) {
- return UNKNOWN_ERROR;
- }
-
- if (mFilledBuffers.empty()) {
- return mSignalledEOS ? mFinalStatus : ERROR_END_OF_STREAM;
- }
-
- if (mOutputPortSettingsHaveChanged) {
- mOutputPortSettingsHaveChanged = false;
-
- return INFO_FORMAT_CHANGED;
- }
-
- size_t index = *mFilledBuffers.begin();
- mFilledBuffers.erase(mFilledBuffers.begin());
-
- BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);
- CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);
- info->mStatus = OWNED_BY_CLIENT;
-
- info->mMediaBuffer->add_ref();
- if (mSkipCutBuffer != NULL) {
- mSkipCutBuffer->submit(info->mMediaBuffer);
- }
- *buffer = info->mMediaBuffer;
-
- return OK;
-}
-
-void OMXCodec::signalBufferReturned(MediaBuffer *buffer) {
- Mutex::Autolock autoLock(mLock);
-
- Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
- for (size_t i = 0; i < buffers->size(); ++i) {
- BufferInfo *info = &buffers->editItemAt(i);
-
- if (info->mMediaBuffer == buffer) {
- CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)ENABLED);
- CHECK_EQ((int)info->mStatus, (int)OWNED_BY_CLIENT);
-
- info->mStatus = OWNED_BY_US;
-
- if (buffer->graphicBuffer() == 0) {
- fillOutputBuffer(info);
- } else {
- sp<MetaData> metaData = info->mMediaBuffer->meta_data();
- int32_t rendered = 0;
- if (!metaData->findInt32(kKeyRendered, &rendered)) {
- rendered = 0;
- }
- if (!rendered) {
- status_t err = cancelBufferToNativeWindow(info);
- if (err < 0) {
- return;
- }
- }
-
- info->mStatus = OWNED_BY_NATIVE_WINDOW;
-
- // Dequeue the next buffer from the native window.
- BufferInfo *nextBufInfo = dequeueBufferFromNativeWindow();
- if (nextBufInfo == 0) {
- return;
- }
-
- // Give the buffer to the OMX node to fill.
- fillOutputBuffer(nextBufInfo);
- }
- return;
- }
- }
-
- CHECK(!"should not be here.");
-}
-
-void OMXCodec::dumpPortStatus(OMX_U32 portIndex) {
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = portIndex;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- printf("%s Port = {\n", portIndex == kPortIndexInput ? "Input" : "Output");
-
- CHECK((portIndex == kPortIndexInput && def.eDir == OMX_DirInput)
- || (portIndex == kPortIndexOutput && def.eDir == OMX_DirOutput));
-
- printf(" nBufferCountActual = %" PRIu32 "\n", def.nBufferCountActual);
- printf(" nBufferCountMin = %" PRIu32 "\n", def.nBufferCountMin);
- printf(" nBufferSize = %" PRIu32 "\n", def.nBufferSize);
-
- switch (def.eDomain) {
- case OMX_PortDomainImage:
- {
- const OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
-
- printf("\n");
- printf(" // Image\n");
- printf(" nFrameWidth = %" PRIu32 "\n", imageDef->nFrameWidth);
- printf(" nFrameHeight = %" PRIu32 "\n", imageDef->nFrameHeight);
- printf(" nStride = %" PRIu32 "\n", imageDef->nStride);
-
- printf(" eCompressionFormat = %s\n",
- asString(imageDef->eCompressionFormat));
-
- printf(" eColorFormat = %s\n",
- asString(imageDef->eColorFormat));
-
- break;
- }
-
- case OMX_PortDomainVideo:
- {
- OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video;
-
- printf("\n");
- printf(" // Video\n");
- printf(" nFrameWidth = %" PRIu32 "\n", videoDef->nFrameWidth);
- printf(" nFrameHeight = %" PRIu32 "\n", videoDef->nFrameHeight);
- printf(" nStride = %" PRIu32 "\n", videoDef->nStride);
-
- printf(" eCompressionFormat = %s\n",
- asString(videoDef->eCompressionFormat));
-
- printf(" eColorFormat = %s\n",
- asString(videoDef->eColorFormat));
-
- break;
- }
-
- case OMX_PortDomainAudio:
- {
- OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio;
-
- printf("\n");
- printf(" // Audio\n");
- printf(" eEncoding = %s\n",
- asString(audioDef->eEncoding));
-
- if (audioDef->eEncoding == OMX_AUDIO_CodingPCM) {
- OMX_AUDIO_PARAM_PCMMODETYPE params;
- InitOMXParams(¶ms);
- params.nPortIndex = portIndex;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params));
- CHECK_EQ(err, (status_t)OK);
-
- printf(" nSamplingRate = %" PRIu32 "\n", params.nSamplingRate);
- printf(" nChannels = %" PRIu32 "\n", params.nChannels);
- printf(" bInterleaved = %d\n", params.bInterleaved);
- printf(" nBitPerSample = %" PRIu32 "\n", params.nBitPerSample);
-
- printf(" eNumData = %s\n",
- params.eNumData == OMX_NumericalDataSigned
- ? "signed" : "unsigned");
-
- printf(" ePCMMode = %s\n", asString(params.ePCMMode));
- } else if (audioDef->eEncoding == OMX_AUDIO_CodingAMR) {
- OMX_AUDIO_PARAM_AMRTYPE amr;
- InitOMXParams(&amr);
- amr.nPortIndex = portIndex;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr));
- CHECK_EQ(err, (status_t)OK);
-
- printf(" nChannels = %" PRIu32 "\n", amr.nChannels);
- printf(" eAMRBandMode = %s\n",
- asString(amr.eAMRBandMode));
- printf(" eAMRFrameFormat = %s\n",
- asString(amr.eAMRFrameFormat));
- }
-
- break;
- }
-
- default:
- {
- printf(" // Unknown\n");
- break;
- }
- }
-
- printf("}\n");
-}
-
-status_t OMXCodec::initNativeWindow() {
- // Enable use of a GraphicBuffer as the output for this node. This must
- // happen before getting the IndexParamPortDefinition parameter because it
- // will affect the pixel format that the node reports.
- status_t err = mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_TRUE);
- if (err != 0) {
- return err;
- }
-
- return OK;
-}
-
-void OMXCodec::initNativeWindowCrop() {
- int32_t left, top, right, bottom;
-
- CHECK(mOutputFormat->findRect(
- kKeyCropRect,
- &left, &top, &right, &bottom));
-
- android_native_rect_t crop;
- crop.left = left;
- crop.top = top;
- crop.right = right + 1;
- crop.bottom = bottom + 1;
-
- // We'll ignore any errors here, if the surface is
- // already invalid, we'll know soon enough.
- native_window_set_crop(mNativeWindow.get(), &crop);
-}
-
-void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
- mOutputFormat = new MetaData;
- mOutputFormat->setCString(kKeyDecoderComponent, mComponentName);
- if (mIsEncoder) {
- int32_t timeScale;
- if (inputFormat->findInt32(kKeyTimeScale, &timeScale)) {
- mOutputFormat->setInt32(kKeyTimeScale, timeScale);
- }
- }
-
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexOutput;
-
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, (status_t)OK);
-
- switch (def.eDomain) {
- case OMX_PortDomainImage:
- {
- OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
- CHECK_EQ((int)imageDef->eCompressionFormat,
- (int)OMX_IMAGE_CodingUnused);
-
- mOutputFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
- mOutputFormat->setInt32(kKeyColorFormat, imageDef->eColorFormat);
- mOutputFormat->setInt32(kKeyWidth, imageDef->nFrameWidth);
- mOutputFormat->setInt32(kKeyHeight, imageDef->nFrameHeight);
- break;
- }
-
- case OMX_PortDomainAudio:
- {
- OMX_AUDIO_PORTDEFINITIONTYPE *audio_def = &def.format.audio;
-
- if (audio_def->eEncoding == OMX_AUDIO_CodingPCM) {
- OMX_AUDIO_PARAM_PCMMODETYPE params;
- InitOMXParams(¶ms);
- params.nPortIndex = kPortIndexOutput;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params));
- CHECK_EQ(err, (status_t)OK);
-
- CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned);
- CHECK_EQ(params.nBitPerSample, 16u);
- CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear);
-
- int32_t numChannels, sampleRate;
- inputFormat->findInt32(kKeyChannelCount, &numChannels);
- inputFormat->findInt32(kKeySampleRate, &sampleRate);
-
- if ((OMX_U32)numChannels != params.nChannels) {
- ALOGV("Codec outputs a different number of channels than "
- "the input stream contains (contains %d channels, "
- "codec outputs %u channels).",
- numChannels, params.nChannels);
- }
-
- if (sampleRate != (int32_t)params.nSamplingRate) {
- ALOGV("Codec outputs at different sampling rate than "
- "what the input stream contains (contains data at "
- "%d Hz, codec outputs %u Hz)",
- sampleRate, params.nSamplingRate);
- }
-
- mOutputFormat->setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
-
- // Use the codec-advertised number of channels, as some
- // codecs appear to output stereo even if the input data is
- // mono. If we know the codec lies about this information,
- // use the actual number of channels instead.
- mOutputFormat->setInt32(
- kKeyChannelCount,
- (mQuirks & kDecoderLiesAboutNumberOfChannels)
- ? numChannels : params.nChannels);
-
- mOutputFormat->setInt32(kKeySampleRate, params.nSamplingRate);
- } else if (audio_def->eEncoding == OMX_AUDIO_CodingAMR) {
- OMX_AUDIO_PARAM_AMRTYPE amr;
- InitOMXParams(&amr);
- amr.nPortIndex = kPortIndexOutput;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr));
- CHECK_EQ(err, (status_t)OK);
-
- CHECK_EQ(amr.nChannels, 1u);
- mOutputFormat->setInt32(kKeyChannelCount, 1);
-
- if (amr.eAMRBandMode >= OMX_AUDIO_AMRBandModeNB0
- && amr.eAMRBandMode <= OMX_AUDIO_AMRBandModeNB7) {
- mOutputFormat->setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_NB);
- mOutputFormat->setInt32(kKeySampleRate, 8000);
- } else if (amr.eAMRBandMode >= OMX_AUDIO_AMRBandModeWB0
- && amr.eAMRBandMode <= OMX_AUDIO_AMRBandModeWB8) {
- mOutputFormat->setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_WB);
- mOutputFormat->setInt32(kKeySampleRate, 16000);
- } else {
- CHECK(!"Unknown AMR band mode.");
- }
- } else if (audio_def->eEncoding == OMX_AUDIO_CodingAAC) {
- mOutputFormat->setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
- int32_t numChannels, sampleRate, bitRate;
- inputFormat->findInt32(kKeyChannelCount, &numChannels);
- inputFormat->findInt32(kKeySampleRate, &sampleRate);
- inputFormat->findInt32(kKeyBitRate, &bitRate);
- mOutputFormat->setInt32(kKeyChannelCount, numChannels);
- mOutputFormat->setInt32(kKeySampleRate, sampleRate);
- mOutputFormat->setInt32(kKeyBitRate, bitRate);
- } else if (audio_def->eEncoding ==
- (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidAC3) {
- mOutputFormat->setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
- int32_t numChannels, sampleRate, bitRate;
- inputFormat->findInt32(kKeyChannelCount, &numChannels);
- inputFormat->findInt32(kKeySampleRate, &sampleRate);
- inputFormat->findInt32(kKeyBitRate, &bitRate);
- mOutputFormat->setInt32(kKeyChannelCount, numChannels);
- mOutputFormat->setInt32(kKeySampleRate, sampleRate);
- mOutputFormat->setInt32(kKeyBitRate, bitRate);
- } else {
- CHECK(!"Should not be here. Unknown audio encoding.");
- }
- break;
- }
-
- case OMX_PortDomainVideo:
- {
- OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
-
- if (video_def->eCompressionFormat == OMX_VIDEO_CodingUnused) {
- mOutputFormat->setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
- } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingMPEG4) {
- mOutputFormat->setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
- } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingH263) {
- mOutputFormat->setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
- } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingAVC) {
- mOutputFormat->setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
- } else {
- CHECK(!"Unknown compression format.");
- }
-
- mOutputFormat->setInt32(kKeyWidth, video_def->nFrameWidth);
- mOutputFormat->setInt32(kKeyHeight, video_def->nFrameHeight);
- mOutputFormat->setInt32(kKeyColorFormat, video_def->eColorFormat);
-
- if (!mIsEncoder) {
- OMX_CONFIG_RECTTYPE rect;
- InitOMXParams(&rect);
- rect.nPortIndex = kPortIndexOutput;
- status_t err =
- mOMX->getConfig(
- mNode, OMX_IndexConfigCommonOutputCrop,
- &rect, sizeof(rect));
-
- CODEC_LOGI("video dimensions are %u x %u",
- video_def->nFrameWidth, video_def->nFrameHeight);
-
- if (err == OK) {
- CHECK_GE(rect.nLeft, 0);
- CHECK_GE(rect.nTop, 0);
- CHECK_GE(rect.nWidth, 0u);
- CHECK_GE(rect.nHeight, 0u);
- CHECK_LE(rect.nLeft + rect.nWidth - 1, video_def->nFrameWidth);
- CHECK_LE(rect.nTop + rect.nHeight - 1, video_def->nFrameHeight);
-
- mOutputFormat->setRect(
- kKeyCropRect,
- rect.nLeft,
- rect.nTop,
- rect.nLeft + rect.nWidth - 1,
- rect.nTop + rect.nHeight - 1);
-
- CODEC_LOGI("Crop rect is %u x %u @ (%d, %d)",
- rect.nWidth, rect.nHeight, rect.nLeft, rect.nTop);
- } else {
- mOutputFormat->setRect(
- kKeyCropRect,
- 0, 0,
- video_def->nFrameWidth - 1,
- video_def->nFrameHeight - 1);
- }
-
- if (mNativeWindow != NULL) {
- initNativeWindowCrop();
- }
- }
- break;
- }
-
- default:
- {
- CHECK(!"should not be here, neither audio nor video.");
- break;
- }
- }
-
- // If the input format contains rotation information, flag the output
- // format accordingly.
-
- int32_t rotationDegrees;
- if (mSource->getFormat()->findInt32(kKeyRotation, &rotationDegrees)) {
- mOutputFormat->setInt32(kKeyRotation, rotationDegrees);
- }
-}
-
-status_t OMXCodec::pause() {
- Mutex::Autolock autoLock(mLock);
-
- mPaused = true;
-
- return OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-status_t QueryCodecs(
- const sp<IOMX> &omx,
- const char *mime, bool queryDecoders, bool hwCodecOnly,
- Vector<CodecCapabilities> *results) {
- Vector<OMXCodec::CodecNameAndQuirks> matchingCodecs;
- results->clear();
-
- OMXCodec::findMatchingCodecs(mime,
- !queryDecoders /*createEncoder*/,
- NULL /*matchComponentName*/,
- hwCodecOnly ? OMXCodec::kHardwareCodecsOnly : 0 /*flags*/,
- &matchingCodecs);
-
- for (size_t c = 0; c < matchingCodecs.size(); c++) {
- const char *componentName = matchingCodecs.itemAt(c).mName.string();
-
- results->push();
- CodecCapabilities *caps = &results->editItemAt(results->size() - 1);
-
- status_t err =
- QueryCodec(omx, componentName, mime, !queryDecoders, caps);
-
- if (err != OK) {
- results->removeAt(results->size() - 1);
- }
- }
-
- return OK;
-}
-
-status_t QueryCodec(
- const sp<IOMX> &omx,
- const char *componentName, const char *mime,
- bool isEncoder,
- CodecCapabilities *caps) {
- bool isVideo = !strncasecmp(mime, "video/", 6);
-
- sp<OMXCodecObserver> observer = new OMXCodecObserver;
- IOMX::node_id node;
- status_t err = omx->allocateNode(componentName, observer, &node);
-
- if (err != OK) {
- return err;
- }
-
- OMXCodec::setComponentRole(omx, node, isEncoder, mime);
-
- caps->mFlags = 0;
- caps->mComponentName = componentName;
-
- // NOTE: OMX does not provide a way to query AAC profile support
- if (isVideo) {
- OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
- InitOMXParams(¶m);
-
- param.nPortIndex = !isEncoder ? 0 : 1;
-
- for (param.nProfileIndex = 0;; ++param.nProfileIndex) {
- err = omx->getParameter(
- node, OMX_IndexParamVideoProfileLevelQuerySupported,
- ¶m, sizeof(param));
-
- if (err != OK) {
- break;
- }
-
- CodecProfileLevel profileLevel;
- profileLevel.mProfile = param.eProfile;
- profileLevel.mLevel = param.eLevel;
-
- caps->mProfileLevels.push(profileLevel);
- }
-
- // Color format query
- // return colors in the order reported by the OMX component
- // prefix "flexible" standard ones with the flexible equivalent
- OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat;
- InitOMXParams(&portFormat);
- portFormat.nPortIndex = !isEncoder ? 1 : 0;
- for (portFormat.nIndex = 0;; ++portFormat.nIndex) {
- err = omx->getParameter(
- node, OMX_IndexParamVideoPortFormat,
- &portFormat, sizeof(portFormat));
- if (err != OK) {
- break;
- }
-
- OMX_U32 flexibleEquivalent;
- if (ACodec::isFlexibleColorFormat(
- omx, node, portFormat.eColorFormat, false /* usingNativeWindow */,
- &flexibleEquivalent)) {
- bool marked = false;
- for (size_t i = 0; i < caps->mColorFormats.size(); i++) {
- if (caps->mColorFormats.itemAt(i) == flexibleEquivalent) {
- marked = true;
- break;
- }
- }
- if (!marked) {
- caps->mColorFormats.push(flexibleEquivalent);
- }
- }
- caps->mColorFormats.push(portFormat.eColorFormat);
- }
- }
-
- if (isVideo && !isEncoder) {
- if (omx->storeMetaDataInBuffers(
- node, 1 /* port index */, OMX_TRUE) == OK ||
- omx->prepareForAdaptivePlayback(
- node, 1 /* port index */, OMX_TRUE,
- 1280 /* width */, 720 /* height */) == OK) {
- caps->mFlags |= CodecCapabilities::kFlagSupportsAdaptivePlayback;
- }
- }
-
- CHECK_EQ(omx->freeNode(node), (status_t)OK);
-
- return OK;
-}
-
-status_t QueryCodecs(
- const sp<IOMX> &omx,
- const char *mimeType, bool queryDecoders,
- Vector<CodecCapabilities> *results) {
- 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..d544e35
--- /dev/null
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -0,0 +1,362 @@
+/*
+ * 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
+const int64_t kTimeoutWaitForInputUs = 5000; // 5 milliseconds
+
+//static
+sp<SimpleDecodingSource> SimpleDecodingSource::Create(
+ const sp<MediaSource> &source, uint32_t flags, const sp<ANativeWindow> &nativeWindow,
+ const char *desiredCodec) {
+ 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];
+ if (desiredCodec != NULL && componentName.compare(desiredCodec)) {
+ continue;
+ }
+
+ 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 = 0; ++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) {
+ // allow some time to get input buffer after flush
+ res = mCodec->dequeueInputBuffer(&in_ix, kTimeoutWaitForInputUs);
+ 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/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
deleted file mode 100644
index 7d15220..0000000
--- a/media/libstagefright/TimedEventQueue.cpp
+++ /dev/null
@@ -1,386 +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.
- */
-
-#undef __STRICT_ANSI__
-#define __STDINT_LIMITS
-#define __STDC_LIMIT_MACROS
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <sys/prctl.h>
-#include <sys/time.h>
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "TimedEventQueue"
-#include <utils/Log.h>
-#include <utils/threads.h>
-
-#include "include/TimedEventQueue.h"
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ALooper.h>
-#include <binder/IServiceManager.h>
-#include <powermanager/PowerManager.h>
-#include <binder/IPCThreadState.h>
-#include <utils/CallStack.h>
-
-namespace android {
-
-static int64_t kWakelockMinDelay = 100000ll; // 100ms
-
-TimedEventQueue::TimedEventQueue()
- : mNextEventID(1),
- mRunning(false),
- mStopped(false),
- mDeathRecipient(new PMDeathRecipient(this)),
- mWakeLockCount(0) {
-}
-
-TimedEventQueue::~TimedEventQueue() {
- stop();
- if (mPowerManager != 0) {
- sp<IBinder> binder = IInterface::asBinder(mPowerManager);
- binder->unlinkToDeath(mDeathRecipient);
- }
-}
-
-void TimedEventQueue::start() {
- if (mRunning) {
- return;
- }
-
- mStopped = false;
-
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- pthread_create(&mThread, &attr, ThreadWrapper, this);
-
- pthread_attr_destroy(&attr);
-
- mRunning = true;
-}
-
-void TimedEventQueue::stop(bool flush) {
- if (!mRunning) {
- return;
- }
-
- if (flush) {
- postEventToBack(new StopEvent);
- } else {
- postTimedEvent(new StopEvent, INT64_MIN);
- }
-
- void *dummy;
- pthread_join(mThread, &dummy);
-
- // some events may be left in the queue if we did not flush and the wake lock
- // must be released.
- releaseWakeLock_l(true /*force*/);
- mQueue.clear();
-
- mRunning = false;
-}
-
-TimedEventQueue::event_id TimedEventQueue::postEvent(const sp<Event> &event) {
- // Reserve an earlier timeslot an INT64_MIN to be able to post
- // the StopEvent to the absolute head of the queue.
- return postTimedEvent(event, INT64_MIN + 1);
-}
-
-TimedEventQueue::event_id TimedEventQueue::postEventToBack(
- const sp<Event> &event) {
- return postTimedEvent(event, INT64_MAX);
-}
-
-TimedEventQueue::event_id TimedEventQueue::postEventWithDelay(
- const sp<Event> &event, int64_t delay_us) {
- CHECK(delay_us >= 0);
- return postTimedEvent(event, ALooper::GetNowUs() + delay_us);
-}
-
-TimedEventQueue::event_id TimedEventQueue::postTimedEvent(
- const sp<Event> &event, int64_t realtime_us) {
- Mutex::Autolock autoLock(mLock);
-
- event->setEventID(mNextEventID++);
-
- List<QueueItem>::iterator it = mQueue.begin();
- while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
- ++it;
- }
-
- QueueItem item;
- item.event = event;
- item.realtime_us = realtime_us;
- item.has_wakelock = false;
-
- if (it == mQueue.begin()) {
- mQueueHeadChangedCondition.signal();
- }
-
- if (realtime_us > ALooper::GetNowUs() + kWakelockMinDelay) {
- acquireWakeLock_l();
- item.has_wakelock = true;
- }
- mQueue.insert(it, item);
-
- mQueueNotEmptyCondition.signal();
-
- return event->eventID();
-}
-
-static bool MatchesEventID(
- void *cookie, const sp<TimedEventQueue::Event> &event) {
- TimedEventQueue::event_id *id =
- static_cast<TimedEventQueue::event_id *>(cookie);
-
- if (event->eventID() != *id) {
- return false;
- }
-
- *id = 0;
-
- return true;
-}
-
-bool TimedEventQueue::cancelEvent(event_id id) {
- if (id == 0) {
- return false;
- }
-
- cancelEvents(&MatchesEventID, &id, true /* stopAfterFirstMatch */);
-
- // if MatchesEventID found a match, it will have set id to 0
- // (which is not a valid event_id).
-
- return id == 0;
-}
-
-void TimedEventQueue::cancelEvents(
- bool (*predicate)(void *cookie, const sp<Event> &event),
- void *cookie,
- bool stopAfterFirstMatch) {
- Mutex::Autolock autoLock(mLock);
-
- List<QueueItem>::iterator it = mQueue.begin();
- while (it != mQueue.end()) {
- if (!(*predicate)(cookie, (*it).event)) {
- ++it;
- continue;
- }
-
- if (it == mQueue.begin()) {
- mQueueHeadChangedCondition.signal();
- }
-
- ALOGV("cancelling event %d", (*it).event->eventID());
-
- (*it).event->setEventID(0);
- if ((*it).has_wakelock) {
- releaseWakeLock_l();
- }
- it = mQueue.erase(it);
- if (stopAfterFirstMatch) {
- return;
- }
- }
-}
-
-// static
-void *TimedEventQueue::ThreadWrapper(void *me) {
-
- androidSetThreadPriority(0, ANDROID_PRIORITY_FOREGROUND);
-
- static_cast<TimedEventQueue *>(me)->threadEntry();
-
- return NULL;
-}
-
-void TimedEventQueue::threadEntry() {
- prctl(PR_SET_NAME, (unsigned long)"TimedEventQueue", 0, 0, 0);
-
- for (;;) {
- int64_t now_us = 0;
- sp<Event> event;
- bool wakeLocked = false;
-
- {
- Mutex::Autolock autoLock(mLock);
-
- if (mStopped) {
- break;
- }
-
- while (mQueue.empty()) {
- mQueueNotEmptyCondition.wait(mLock);
- }
-
- event_id eventID = 0;
- for (;;) {
- if (mQueue.empty()) {
- // The only event in the queue could have been cancelled
- // while we were waiting for its scheduled time.
- break;
- }
-
- List<QueueItem>::iterator it = mQueue.begin();
- eventID = (*it).event->eventID();
-
- now_us = ALooper::GetNowUs();
- int64_t when_us = (*it).realtime_us;
-
- int64_t delay_us;
- if (when_us < 0 || when_us == INT64_MAX) {
- delay_us = 0;
- } else {
- delay_us = when_us - now_us;
- }
-
- if (delay_us <= 0) {
- break;
- }
-
- static int64_t kMaxTimeoutUs = 10000000ll; // 10 secs
- bool timeoutCapped = false;
- if (delay_us > kMaxTimeoutUs) {
- ALOGW("delay_us exceeds max timeout: %" PRId64 " us", delay_us);
-
- // We'll never block for more than 10 secs, instead
- // we will split up the full timeout into chunks of
- // 10 secs at a time. This will also avoid overflow
- // when converting from us to ns.
- delay_us = kMaxTimeoutUs;
- timeoutCapped = true;
- }
-
- status_t err = mQueueHeadChangedCondition.waitRelative(
- mLock, delay_us * 1000ll);
-
- if (!timeoutCapped && err == -ETIMEDOUT) {
- // We finally hit the time this event is supposed to
- // trigger.
- now_us = ALooper::GetNowUs();
- break;
- }
- }
-
- // The event w/ this id may have been cancelled while we're
- // waiting for its trigger-time, in that case
- // removeEventFromQueue_l will return NULL.
- // Otherwise, the QueueItem will be removed
- // from the queue and the referenced event returned.
- event = removeEventFromQueue_l(eventID, &wakeLocked);
- }
-
- if (event != NULL) {
- // Fire event with the lock NOT held.
- event->fire(this, now_us);
- if (wakeLocked) {
- Mutex::Autolock autoLock(mLock);
- releaseWakeLock_l();
- }
- }
- }
-}
-
-sp<TimedEventQueue::Event> TimedEventQueue::removeEventFromQueue_l(
- event_id id, bool *wakeLocked) {
- for (List<QueueItem>::iterator it = mQueue.begin();
- it != mQueue.end(); ++it) {
- if ((*it).event->eventID() == id) {
- sp<Event> event = (*it).event;
- event->setEventID(0);
- *wakeLocked = (*it).has_wakelock;
- mQueue.erase(it);
- return event;
- }
- }
-
- ALOGW("Event %d was not found in the queue, already cancelled?", id);
-
- return NULL;
-}
-
-void TimedEventQueue::acquireWakeLock_l()
-{
- if (mWakeLockCount == 0) {
- CHECK(mWakeLockToken == 0);
- if (mPowerManager == 0) {
- // use checkService() to avoid blocking if power service is not up yet
- sp<IBinder> binder =
- defaultServiceManager()->checkService(String16("power"));
- if (binder == 0) {
- ALOGW("cannot connect to the power manager service");
- } else {
- mPowerManager = interface_cast<IPowerManager>(binder);
- binder->linkToDeath(mDeathRecipient);
- }
- }
- if (mPowerManager != 0) {
- sp<IBinder> binder = new BBinder();
- int64_t token = IPCThreadState::self()->clearCallingIdentity();
- status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
- binder,
- String16("TimedEventQueue"),
- String16("media")); // not oneway
- IPCThreadState::self()->restoreCallingIdentity(token);
- if (status == NO_ERROR) {
- mWakeLockToken = binder;
- mWakeLockCount++;
- }
- }
- } else {
- mWakeLockCount++;
- }
-}
-
-void TimedEventQueue::releaseWakeLock_l(bool force)
-{
- if (mWakeLockCount == 0) {
- return;
- }
- if (force) {
- // Force wakelock release below by setting reference count to 1.
- mWakeLockCount = 1;
- }
- if (--mWakeLockCount == 0) {
- CHECK(mWakeLockToken != 0);
- if (mPowerManager != 0) {
- int64_t token = IPCThreadState::self()->clearCallingIdentity();
- mPowerManager->releaseWakeLock(mWakeLockToken, 0); // not oneway
- IPCThreadState::self()->restoreCallingIdentity(token);
- }
- mWakeLockToken.clear();
- }
-}
-
-void TimedEventQueue::clearPowerManager()
-{
- Mutex::Autolock _l(mLock);
- releaseWakeLock_l(true /*force*/);
- mPowerManager.clear();
-}
-
-void TimedEventQueue::PMDeathRecipient::binderDied(
- const wp<IBinder>& /* who */) {
- mQueue->clearPowerManager();
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
index 387d17d..43b4da7 100644
--- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -157,8 +157,7 @@
kProfileLevels, NELEM(kProfileLevels),
176 /* width */, 144 /* height */,
callbacks, appData, component),
- mBitrateUpdated(false),
- mKeyFrameRequested(false),
+ mUpdateFlag(0),
mIvVideoColorFormat(IV_YUV_420P),
mAVCEncProfile(IV_PROFILE_BASE),
mAVCEncLevel(41),
@@ -207,6 +206,7 @@
mEnableAltRef = DEFAULT_ENABLE_ALT_REF;
mEncSpeed = DEFAULT_ENC_SPEED;
mIntra4x4 = DEFAULT_INTRA4x4;
+ mConstrainedIntraFlag = DEFAULT_CONSTRAINED_INTRA;
mAIRMode = DEFAULT_AIR;
mAIRRefreshPeriod = DEFAULT_AIR_REFRESH_PERIOD;
mPSNREnable = DEFAULT_PSNR_ENABLE;
@@ -304,6 +304,7 @@
s_ipe_params_ip.u4_enable_intra_4x4 = mIntra4x4;
s_ipe_params_ip.u4_enc_speed_preset = mEncSpeed;
+ s_ipe_params_ip.u4_constrained_intra_pred = mConstrainedIntraFlag;
s_ipe_params_ip.u4_timestamp_high = -1;
s_ipe_params_ip.u4_timestamp_low = -1;
@@ -1039,7 +1040,9 @@
return OMX_ErrorBadPortIndex;
}
- mKeyFrameRequested = params->IntraRefreshVOP;
+ if (params->IntraRefreshVOP) {
+ mUpdateFlag |= kRequestKeyFrame;
+ }
return OMX_ErrorNone;
}
@@ -1054,7 +1057,7 @@
if (mBitrate != params->nEncodeBitrate) {
mBitrate = params->nEncodeBitrate;
- mBitrateUpdated = true;
+ mUpdateFlag |= kUpdateBitrate;
}
return OMX_ErrorNone;
}
@@ -1071,7 +1074,7 @@
}
mBitrate = bitrate->nTargetBitrate;
- mBitrateUpdated = true;
+ mUpdateFlag |= kUpdateBitrate;
return OMX_ErrorNone;
}
@@ -1291,12 +1294,14 @@
return;
}
- if (mBitrateUpdated) {
- setBitRate();
- }
-
- if (mKeyFrameRequested) {
- setFrameType(IV_IDR_FRAME);
+ if (mUpdateFlag) {
+ if (mUpdateFlag & kUpdateBitrate) {
+ setBitRate();
+ }
+ if (mUpdateFlag & kRequestKeyFrame) {
+ setFrameType(IV_IDR_FRAME);
+ }
+ mUpdateFlag = 0;
}
if ((inputBufferHeader != NULL)
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.h b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h
index 4418a7f..7c2e3ad 100644
--- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.h
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h
@@ -95,8 +95,7 @@
#define DEFAULT_SOC SOC_GENERIC
#define DEFAULT_INTRA4x4 0
#define STRLENGTH 500
-
-
+#define DEFAULT_CONSTRAINED_INTRA 0
#define MIN(a, b) ((a) < (b))? (a) : (b)
#define MAX(a, b) ((a) > (b))? (a) : (b)
@@ -142,6 +141,11 @@
kNumBuffers = 2,
};
+ enum {
+ kUpdateBitrate = 1 << 0,
+ kRequestKeyFrame = 1 << 1,
+ };
+
// OMX input buffer's timestamp and flags
typedef struct {
int64_t mTimeUs;
@@ -153,11 +157,7 @@
struct timeval mTimeStart; // Time at the start of decode()
struct timeval mTimeEnd; // Time at the end of decode()
-
- // If a request for a change it bitrate has been received.
- bool mBitrateUpdated;
-
- bool mKeyFrameRequested;
+ int mUpdateFlag;
#ifdef FILE_DUMP_ENABLE
char mInFile[200];
@@ -180,6 +180,7 @@
bool mReconEnable;
bool mPSNREnable;
bool mEntropyMode;
+ bool mConstrainedIntraFlag;
IVE_SPEED_CONFIG mEncSpeed;
uint8_t *mConversionBuffers[MAX_CONVERSION_BUFFERS];
diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk
index f904212..2c985fc 100644
--- a/media/libstagefright/httplive/Android.mk
+++ b/media/libstagefright/httplive/Android.mk
@@ -18,6 +18,7 @@
LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libbinder \
libcrypto \
libcutils \
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
deleted file mode 100644
index 758b2c9..0000000
--- a/media/libstagefright/include/AwesomePlayer.h
+++ /dev/null
@@ -1,376 +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 AWESOME_PLAYER_H_
-
-#define AWESOME_PLAYER_H_
-
-#include "HTTPBase.h"
-#include "TimedEventQueue.h"
-
-#include <media/AudioResamplerPublic.h>
-#include <media/MediaPlayerInterface.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/TimeSource.h>
-#include <media/stagefright/MetaData.h>
-#include <utils/threads.h>
-#include <drm/DrmManagerClient.h>
-
-namespace android {
-
-class AudioPlayer;
-struct ClockEstimator;
-class IDataSource;
-class MediaBuffer;
-struct MediaExtractor;
-struct MediaSource;
-struct NuCachedSource2;
-class IGraphicBufferProducer;
-
-class DrmManagerClinet;
-class DecryptHandle;
-
-class TimedTextDriver;
-class WVMExtractor;
-
-struct AwesomeRenderer : public RefBase {
- AwesomeRenderer() {}
-
- virtual void render(MediaBuffer *buffer) = 0;
-
-private:
- AwesomeRenderer(const AwesomeRenderer &);
- AwesomeRenderer &operator=(const AwesomeRenderer &);
-};
-
-struct AwesomePlayer {
- AwesomePlayer();
- ~AwesomePlayer();
-
- void setListener(const wp<MediaPlayerBase> &listener);
- void setUID(uid_t uid);
-
- status_t setDataSource(
- const sp<IMediaHTTPService> &httpService,
- const char *uri,
- const KeyedVector<String8, String8> *headers = NULL);
-
- status_t setDataSource(int fd, int64_t offset, int64_t length);
-
- status_t setDataSource(const sp<IStreamSource> &source);
-
- void reset();
-
- status_t prepare();
- status_t prepare_l();
- status_t prepareAsync();
- status_t prepareAsync_l();
-
- status_t play();
- status_t pause();
-
- bool isPlaying() const;
-
- status_t setSurfaceTexture(const sp<IGraphicBufferProducer> &bufferProducer);
- void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
- status_t setLooping(bool shouldLoop);
-
- status_t getDuration(int64_t *durationUs);
- status_t getPosition(int64_t *positionUs);
-
- status_t setParameter(int key, const Parcel &request);
- status_t getParameter(int key, Parcel *reply);
- status_t setPlaybackSettings(const AudioPlaybackRate &rate);
- status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
- status_t invoke(const Parcel &request, Parcel *reply);
- status_t setCacheStatCollectFreq(const Parcel &request);
-
- status_t seekTo(int64_t timeUs);
-
- // This is a mask of MediaExtractor::Flags.
- uint32_t flags() const;
-
- void postAudioEOS(int64_t delayUs = 0ll);
- void postAudioSeekComplete();
- void postAudioTearDown();
- status_t dump(int fd, const Vector<String16> &args) const;
-
-private:
- friend struct AwesomeEvent;
- friend struct PreviewPlayer;
-
- enum {
- PLAYING = 0x01,
- LOOPING = 0x02,
- FIRST_FRAME = 0x04,
- PREPARING = 0x08,
- PREPARED = 0x10,
- AT_EOS = 0x20,
- PREPARE_CANCELLED = 0x40,
- CACHE_UNDERRUN = 0x80,
- AUDIO_AT_EOS = 0x0100,
- VIDEO_AT_EOS = 0x0200,
- AUTO_LOOPING = 0x0400,
-
- // We are basically done preparing but are currently buffering
- // sufficient data to begin playback and finish the preparation phase
- // for good.
- PREPARING_CONNECTED = 0x0800,
-
- // We're triggering a single video event to display the first frame
- // after the seekpoint.
- SEEK_PREVIEW = 0x1000,
-
- AUDIO_RUNNING = 0x2000,
- AUDIOPLAYER_STARTED = 0x4000,
-
- INCOGNITO = 0x8000,
-
- TEXT_RUNNING = 0x10000,
- TEXTPLAYER_INITIALIZED = 0x20000,
-
- SLOW_DECODER_HACK = 0x40000,
- };
-
- mutable Mutex mLock;
- Mutex mMiscStateLock;
- mutable Mutex mStatsLock;
- Mutex mAudioLock;
-
- OMXClient mClient;
- TimedEventQueue mQueue;
- bool mQueueStarted;
- wp<MediaPlayerBase> mListener;
- bool mUIDValid;
- uid_t mUID;
-
- sp<ANativeWindow> mNativeWindow;
- sp<MediaPlayerBase::AudioSink> mAudioSink;
-
- SystemTimeSource mSystemTimeSource;
- TimeSource *mTimeSource;
-
- sp<IMediaHTTPService> mHTTPService;
- String8 mUri;
- KeyedVector<String8, String8> mUriHeaders;
-
- sp<DataSource> mFileSource;
-
- sp<MediaSource> mVideoTrack;
- sp<MediaSource> mVideoSource;
- sp<AwesomeRenderer> mVideoRenderer;
- bool mVideoRenderingStarted;
- bool mVideoRendererIsPreview;
- int32_t mMediaRenderingStartGeneration;
- int32_t mStartGeneration;
-
- ssize_t mActiveAudioTrackIndex;
- sp<MediaSource> mAudioTrack;
- sp<MediaSource> mOmxSource;
- sp<MediaSource> mAudioSource;
- AudioPlayer *mAudioPlayer;
- AudioPlaybackRate mPlaybackSettings;
- int64_t mDurationUs;
-
- int32_t mDisplayWidth;
- int32_t mDisplayHeight;
- int32_t mVideoScalingMode;
-
- uint32_t mFlags;
- uint32_t mExtractorFlags;
- uint32_t mSinceLastDropped;
-
- int64_t mTimeSourceDeltaUs;
- int64_t mVideoTimeUs;
-
- enum SeekType {
- NO_SEEK,
- SEEK,
- SEEK_VIDEO_ONLY
- };
- SeekType mSeeking;
-
- bool mSeekNotificationSent;
- int64_t mSeekTimeUs;
-
- int64_t mBitrate; // total bitrate of the file (in bps) or -1 if unknown.
-
- bool mWatchForAudioSeekComplete;
- bool mWatchForAudioEOS;
-
- sp<TimedEventQueue::Event> mVideoEvent;
- bool mVideoEventPending;
- sp<TimedEventQueue::Event> mStreamDoneEvent;
- bool mStreamDoneEventPending;
- sp<TimedEventQueue::Event> mBufferingEvent;
- bool mBufferingEventPending;
- sp<TimedEventQueue::Event> mCheckAudioStatusEvent;
- bool mAudioStatusEventPending;
- sp<TimedEventQueue::Event> mVideoLagEvent;
- bool mVideoLagEventPending;
- sp<TimedEventQueue::Event> mAudioTearDownEvent;
- bool mAudioTearDownEventPending;
- sp<TimedEventQueue::Event> mAsyncPrepareEvent;
- Condition mPreparedCondition;
- bool mIsAsyncPrepare;
- status_t mPrepareResult;
- status_t mStreamDoneStatus;
-
- void postVideoEvent_l(int64_t delayUs = -1);
- void postBufferingEvent_l();
- void postStreamDoneEvent_l(status_t status);
- void postCheckAudioStatusEvent(int64_t delayUs);
- void postVideoLagEvent_l();
- void postAudioTearDownEvent(int64_t delayUs);
-
- status_t play_l();
-
- MediaBuffer *mVideoBuffer;
-
- sp<ClockEstimator> mClockEstimator;
- sp<HTTPBase> mConnectingDataSource;
- sp<NuCachedSource2> mCachedSource;
-
- DrmManagerClient *mDrmManagerClient;
- sp<DecryptHandle> mDecryptHandle;
-
- int64_t mLastVideoTimeUs;
- TimedTextDriver *mTextDriver;
-
- sp<WVMExtractor> mWVMExtractor;
- sp<MediaExtractor> mExtractor;
-
- status_t setDataSource_l(
- const sp<IMediaHTTPService> &httpService,
- const char *uri,
- const KeyedVector<String8, String8> *headers = NULL);
-
- status_t setDataSource_l(const sp<DataSource> &dataSource);
- status_t setDataSource_l(const sp<MediaExtractor> &extractor);
- void reset_l();
- status_t seekTo_l(int64_t timeUs);
- status_t pause_l(bool at_eos = false);
- void initRenderer_l();
- void notifyVideoSize_l();
- void seekAudioIfNecessary_l();
-
- void cancelPlayerEvents(bool keepNotifications = false);
-
- void setAudioSource(sp<MediaSource> source);
- status_t initAudioDecoder();
-
-
- void setVideoSource(sp<MediaSource> source);
- status_t initVideoDecoder(uint32_t flags = 0);
-
- void addTextSource_l(size_t trackIndex, const sp<MediaSource>& source);
-
- void onStreamDone();
-
- void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0);
-
- void onVideoEvent();
- void onBufferingUpdate();
- void onCheckAudioStatus();
- void onPrepareAsyncEvent();
- void abortPrepare(status_t err);
- void finishAsyncPrepare_l();
- void onVideoLagUpdate();
- void onAudioTearDownEvent();
-
- void beginPrepareAsync_l();
-
- bool getCachedDuration_l(int64_t *durationUs, bool *eos);
-
- status_t finishSetDataSource_l();
-
- static bool ContinuePreparation(void *cookie);
-
- bool getBitrate(int64_t *bitrate);
-
- int64_t estimateRealTimeUs(TimeSource *ts, int64_t systemTimeUs);
- void finishSeekIfNecessary(int64_t videoTimeUs);
- void ensureCacheIsFetching_l();
-
- void notifyIfMediaStarted_l();
- void createAudioPlayer_l();
- status_t startAudioPlayer_l(bool sendErrorNotification = true);
-
- void shutdownVideoDecoder_l();
- status_t setNativeWindow_l(const sp<ANativeWindow> &native);
-
- bool isStreamingHTTP() const;
- void sendCacheStats();
- void checkDrmStatus(const sp<DataSource>& dataSource);
-
- enum FlagMode {
- SET,
- CLEAR,
- ASSIGN
- };
- void modifyFlags(unsigned value, FlagMode mode);
-
- struct TrackStat {
- String8 mMIME;
- String8 mDecoderName;
- };
-
- // protected by mStatsLock
- struct Stats {
- int mFd;
- String8 mURI;
- int64_t mBitrate;
-
- // FIXME:
- // These two indices are just 0 or 1 for now
- // They are not representing the actual track
- // indices in the stream.
- ssize_t mAudioTrackIndex;
- ssize_t mVideoTrackIndex;
-
- int64_t mNumVideoFramesDecoded;
- int64_t mNumVideoFramesDropped;
- int32_t mVideoWidth;
- int32_t mVideoHeight;
- uint32_t mFlags;
- Vector<TrackStat> mTracks;
- } mStats;
-
- bool mOffloadAudio;
- bool mAudioTearDown;
- bool mAudioTearDownWasPlaying;
- int64_t mAudioTearDownPosition;
-
- status_t setVideoScalingMode(int32_t mode);
- status_t setVideoScalingMode_l(int32_t mode);
- status_t getTrackInfo(Parcel* reply) const;
-
- status_t selectAudioTrack_l(const sp<MediaSource>& source, size_t trackIndex);
-
- // when select is true, the given track is selected.
- // otherwise, the given track is unselected.
- status_t selectTrack(size_t trackIndex, bool select);
-
- size_t countTracks() const;
-
- AwesomePlayer(const AwesomePlayer &);
- AwesomePlayer &operator=(const AwesomePlayer &);
-};
-
-} // namespace android
-
-#endif // AWESOME_PLAYER_H_
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/include/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h
deleted file mode 100644
index 890f7e8..0000000
--- a/media/libstagefright/include/TimedEventQueue.h
+++ /dev/null
@@ -1,159 +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 TIMED_EVENT_QUEUE_H_
-
-#define TIMED_EVENT_QUEUE_H_
-
-#include <pthread.h>
-
-#include <utils/List.h>
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-#include <powermanager/IPowerManager.h>
-
-namespace android {
-
-struct TimedEventQueue {
-
- typedef int32_t event_id;
-
- struct Event : public RefBase {
- Event()
- : mEventID(0) {
- }
-
- virtual ~Event() {}
-
- event_id eventID() {
- return mEventID;
- }
-
- protected:
- virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0;
-
- private:
- friend struct TimedEventQueue;
-
- event_id mEventID;
-
- void setEventID(event_id id) {
- mEventID = id;
- }
-
- Event(const Event &);
- Event &operator=(const Event &);
- };
-
- class PMDeathRecipient : public IBinder::DeathRecipient {
- public:
- PMDeathRecipient(TimedEventQueue *queue) : mQueue(queue) {}
- virtual ~PMDeathRecipient() {}
-
- // IBinder::DeathRecipient
- virtual void binderDied(const wp<IBinder>& who);
-
- private:
- PMDeathRecipient(const PMDeathRecipient&);
- PMDeathRecipient& operator = (const PMDeathRecipient&);
-
- TimedEventQueue *mQueue;
- };
-
- TimedEventQueue();
- ~TimedEventQueue();
-
- // Start executing the event loop.
- void start();
-
- // Stop executing the event loop, if flush is false, any pending
- // events are discarded, otherwise the queue will stop (and this call
- // return) once all pending events have been handled.
- void stop(bool flush = false);
-
- // Posts an event to the front of the queue (after all events that
- // have previously been posted to the front but before timed events).
- event_id postEvent(const sp<Event> &event);
-
- event_id postEventToBack(const sp<Event> &event);
-
- // It is an error to post an event with a negative delay.
- event_id postEventWithDelay(const sp<Event> &event, int64_t delay_us);
-
- // If the event is to be posted at a time that has already passed,
- // it will fire as soon as possible.
- event_id postTimedEvent(const sp<Event> &event, int64_t realtime_us);
-
- // Returns true iff event is currently in the queue and has been
- // successfully cancelled. In this case the event will have been
- // removed from the queue and won't fire.
- bool cancelEvent(event_id id);
-
- // Cancel any pending event that satisfies the predicate.
- // If stopAfterFirstMatch is true, only cancels the first event
- // satisfying the predicate (if any).
- void cancelEvents(
- bool (*predicate)(void *cookie, const sp<Event> &event),
- void *cookie,
- bool stopAfterFirstMatch = false);
-
- static int64_t getRealTimeUs();
-
- void clearPowerManager();
-
-private:
- struct QueueItem {
- sp<Event> event;
- int64_t realtime_us;
- bool has_wakelock;
- };
-
- struct StopEvent : public TimedEventQueue::Event {
- virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
- queue->mStopped = true;
- }
- };
-
- pthread_t mThread;
- List<QueueItem> mQueue;
- Mutex mLock;
- Condition mQueueNotEmptyCondition;
- Condition mQueueHeadChangedCondition;
- event_id mNextEventID;
-
- bool mRunning;
- bool mStopped;
-
- sp<IPowerManager> mPowerManager;
- sp<IBinder> mWakeLockToken;
- const sp<PMDeathRecipient> mDeathRecipient;
- uint32_t mWakeLockCount;
-
- static void *ThreadWrapper(void *me);
- void threadEntry();
-
- sp<Event> removeEventFromQueue_l(event_id id, bool *wakeLocked);
-
- void acquireWakeLock_l();
- void releaseWakeLock_l(bool force = false);
-
- TimedEventQueue(const TimedEventQueue &);
- TimedEventQueue &operator=(const TimedEventQueue &);
-};
-
-} // namespace android
-
-#endif // TIMED_EVENT_QUEUE_H_
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 644b6ed..9a4e867 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -37,7 +37,7 @@
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/SimpleDecodingSource.h>
#define DEFAULT_TIMEOUT 500000
@@ -576,9 +576,8 @@
CHECK_EQ(seekSource->start(), (status_t)OK);
- sp<MediaSource> codec = OMXCodec::Create(
- mOMX, source->getFormat(), false /* createEncoder */,
- source, componentName);
+ sp<MediaSource> codec = SimpleDecodingSource::Create(
+ source, 0 /* flags */, NULL /* nativeWindow */, componentName);
CHECK(codec != NULL);
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
diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk
index 8d43b89..ba9b50a 100755
--- a/services/audiopolicy/enginedefault/Android.mk
+++ b/services/audiopolicy/enginedefault/Android.mk
@@ -39,6 +39,7 @@
libaudiopolicycomponents
LOCAL_SHARED_LIBRARIES += \
+ liblog \
libcutils \
libutils \
libaudioutils \
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 3deb396..6cade57 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -81,7 +81,7 @@
sp<CameraService> cs = const_cast<CameraService*>(
static_cast<const CameraService*>(callbacks));
- cs->onDeviceStatusChanged(static_cast<camera_device_status_t>(camera_id),
+ cs->onDeviceStatusChanged(camera_id,
static_cast<camera_device_status_t>(new_status));
}
@@ -252,12 +252,14 @@
}
sp<ICameraServiceProxy> CameraService::getCameraServiceProxy() {
+ sp<ICameraServiceProxy> proxyBinder = nullptr;
+#ifndef __BRILLO__
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.camera.proxy"));
- if (binder == nullptr) {
- return nullptr;
+ if (binder != nullptr) {
+ proxyBinder = interface_cast<ICameraServiceProxy>(binder);
}
- sp<ICameraServiceProxy> proxyBinder = interface_cast<ICameraServiceProxy>(binder);
+#endif
return proxyBinder;
}
@@ -276,7 +278,7 @@
gCameraService = nullptr;
}
-void CameraService::onDeviceStatusChanged(camera_device_status_t cameraId,
+void CameraService::onDeviceStatusChanged(int cameraId,
camera_device_status_t newStatus) {
ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,
cameraId, newStatus);
@@ -864,17 +866,6 @@
int callingPid = getCallingPid();
- if (clientUid == USE_CALLING_UID) {
- clientUid = getCallingUid();
- } else {
- // We only trust our own process to forward client UIDs
- if (callingPid != getpid()) {
- ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid %d)",
- callingPid, clientUid);
- return PERMISSION_DENIED;
- }
- }
-
if (!mModule) {
ALOGE("CameraService::connect X (PID %d) rejected (camera HAL module not loaded)",
callingPid);
@@ -887,6 +878,31 @@
return -ENODEV;
}
+#if !defined(__BRILLO__)
+ status_t allowed = validateClientPermissionsLocked(cameraId, clientUid);
+ if (allowed != OK) {
+ return allowed;
+ }
+#endif // defined(__BRILLO__)
+
+ return checkIfDeviceIsUsable(cameraId);
+}
+
+status_t CameraService::validateClientPermissionsLocked(const String8& cameraId, int& clientUid)
+ const {
+ int callingPid = getCallingPid();
+
+ if (clientUid == USE_CALLING_UID) {
+ clientUid = getCallingUid();
+ } else {
+ // We only trust our own process to forward client UIDs
+ if (callingPid != getpid()) {
+ ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid %d)",
+ callingPid, clientUid);
+ return PERMISSION_DENIED;
+ }
+ }
+
// Check device policy for this camera
char value[PROPERTY_VALUE_MAX];
char key[PROPERTY_KEY_MAX];
@@ -909,7 +925,7 @@
return PERMISSION_DENIED;
}
- return checkIfDeviceIsUsable(cameraId);
+ return OK;
}
status_t CameraService::checkIfDeviceIsUsable(const String8& cameraId) const {
@@ -2353,7 +2369,7 @@
result.appendFormat(" Resource Cost: %d\n", state.second->getCost());
result.appendFormat(" Conflicting Devices:");
for (auto& id : conflicting) {
- result.appendFormat(" %s", cameraId.string());
+ result.appendFormat(" %s", id.string());
}
if (conflicting.size() == 0) {
result.appendFormat(" NONE");
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 4b0eeb7..42c02ca 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -95,7 +95,7 @@
/////////////////////////////////////////////////////////////////////
// HAL Callbacks
- virtual void onDeviceStatusChanged(camera_device_status_t cameraId,
+ virtual void onDeviceStatusChanged(int cameraId,
camera_device_status_t newStatus);
virtual void onTorchStatusChanged(const String8& cameraId,
ICameraServiceListener::TorchStatus
@@ -482,6 +482,7 @@
// Check if we can connect, before we acquire the service lock.
status_t validateConnectLocked(const String8& cameraId, /*inout*/int& clientUid) const;
+ status_t validateClientPermissionsLocked(const String8& cameraId, /*inout*/int& clientUid) const;
// Handle active client evictions, and update service state.
// Only call with with mServiceLock held.