Reconcile with honeycomb-release
Change-Id: I83818e0c61f0fcb53b154cd0a85f18924745912f
diff --git a/camera/Android.mk b/camera/Android.mk
index 2f16923..b17b3d2 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -13,7 +13,6 @@
libutils \
libbinder \
libhardware \
- libsurfaceflinger_client \
libui \
libgui
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index 907f119..5eb48da 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -205,22 +205,6 @@
return c->startPreview();
}
-int32_t Camera::getNumberOfVideoBuffers() const
-{
- LOGV("getNumberOfVideoBuffers");
- sp <ICamera> c = mCamera;
- if (c == 0) return 0;
- return c->getNumberOfVideoBuffers();
-}
-
-sp<IMemory> Camera::getVideoBuffer(int32_t index) const
-{
- LOGV("getVideoBuffer: %d", index);
- sp <ICamera> c = mCamera;
- if (c == 0) return 0;
- return c->getVideoBuffer(index);
-}
-
status_t Camera::storeMetaDataInBuffers(bool enabled)
{
LOGV("storeMetaDataInBuffers: %s",
@@ -301,12 +285,12 @@
}
// take a picture
-status_t Camera::takePicture()
+status_t Camera::takePicture(int msgType)
{
- LOGV("takePicture");
+ LOGV("takePicture: 0x%x", msgType);
sp <ICamera> c = mCamera;
if (c == 0) return NO_INIT;
- return c->takePicture();
+ return c->takePicture(msgType);
}
// set preview/capture parameters - key/value pairs
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index e9a5f8c..c295315 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -59,6 +59,8 @@
const char CameraParameters::KEY_SUPPORTED_FLASH_MODES[] = "flash-mode-values";
const char CameraParameters::KEY_FOCUS_MODE[] = "focus-mode";
const char CameraParameters::KEY_SUPPORTED_FOCUS_MODES[] = "focus-mode-values";
+const char CameraParameters::KEY_MAX_NUM_FOCUS_AREAS[] = "max-num-focus-areas";
+const char CameraParameters::KEY_FOCUS_AREAS[] = "focus-areas";
const char CameraParameters::KEY_FOCAL_LENGTH[] = "focal-length";
const char CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE[] = "horizontal-view-angle";
const char CameraParameters::KEY_VERTICAL_VIEW_ANGLE[] = "vertical-view-angle";
@@ -66,6 +68,12 @@
const char CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION[] = "max-exposure-compensation";
const char CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION[] = "min-exposure-compensation";
const char CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP[] = "exposure-compensation-step";
+const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK[] = "auto-exposure-lock";
+const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[] = "auto-exposure-lock-supported";
+const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK[] = "auto-whitebalance-lock";
+const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED[] = "auto-whitebalance-lock-supported";
+const char CameraParameters::KEY_MAX_NUM_METERING_AREAS[] = "max-num-metering-areas";
+const char CameraParameters::KEY_METERING_AREAS[] = "metering-areas";
const char CameraParameters::KEY_ZOOM[] = "zoom";
const char CameraParameters::KEY_MAX_ZOOM[] = "max-zoom";
const char CameraParameters::KEY_ZOOM_RATIOS[] = "zoom-ratios";
@@ -78,6 +86,7 @@
const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] = "preferred-preview-size-for-video";
const char CameraParameters::TRUE[] = "true";
+const char CameraParameters::FALSE[] = "false";
const char CameraParameters::FOCUS_DISTANCE_INFINITY[] = "Infinity";
// Values for white balance settings.
@@ -132,10 +141,10 @@
const char CameraParameters::SCENE_MODE_CANDLELIGHT[] = "candlelight";
const char CameraParameters::SCENE_MODE_BARCODE[] = "barcode";
-const char CameraParameters::PIXEL_FORMAT_YUV420P[] = "yuv420p";
const char CameraParameters::PIXEL_FORMAT_YUV422SP[] = "yuv422sp";
const char CameraParameters::PIXEL_FORMAT_YUV420SP[] = "yuv420sp";
const char CameraParameters::PIXEL_FORMAT_YUV422I[] = "yuv422i-yuyv";
+const char CameraParameters::PIXEL_FORMAT_YUV420P[] = "yuv420p";
const char CameraParameters::PIXEL_FORMAT_RGB565[] = "rgb565";
const char CameraParameters::PIXEL_FORMAT_JPEG[] = "jpeg";
diff --git a/camera/ICamera.cpp b/camera/ICamera.cpp
index 0881d65..5f6e5ef 100644
--- a/camera/ICamera.cpp
+++ b/camera/ICamera.cpp
@@ -46,8 +46,6 @@
STOP_RECORDING,
RECORDING_ENABLED,
RELEASE_RECORDING_FRAME,
- GET_NUM_VIDEO_BUFFERS,
- GET_VIDEO_BUFFER,
STORE_META_DATA_IN_BUFFERS,
};
@@ -149,27 +147,6 @@
remote()->transact(RELEASE_RECORDING_FRAME, data, &reply);
}
- int32_t getNumberOfVideoBuffers() const
- {
- LOGV("getNumberOfVideoBuffers");
- Parcel data, reply;
- data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
- remote()->transact(GET_NUM_VIDEO_BUFFERS, data, &reply);
- return reply.readInt32();
- }
-
- sp<IMemory> getVideoBuffer(int32_t index) const
- {
- LOGV("getVideoBuffer: %d", index);
- Parcel data, reply;
- data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
- data.writeInt32(index);
- remote()->transact(GET_VIDEO_BUFFER, data, &reply);
- sp<IMemory> mem = interface_cast<IMemory>(
- reply.readStrongBinder());
- return mem;
- }
-
status_t storeMetaDataInBuffers(bool enabled)
{
LOGV("storeMetaDataInBuffers: %s", enabled? "true": "false");
@@ -223,11 +200,12 @@
}
// take a picture - returns an IMemory (ref-counted mmap)
- status_t takePicture()
+ status_t takePicture(int msgType)
{
- LOGV("takePicture");
+ LOGV("takePicture: 0x%x", msgType);
Parcel data, reply;
data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+ data.writeInt32(msgType);
remote()->transact(TAKE_PICTURE, data, &reply);
status_t ret = reply.readInt32();
return ret;
@@ -354,19 +332,6 @@
releaseRecordingFrame(mem);
return NO_ERROR;
} break;
- case GET_NUM_VIDEO_BUFFERS: {
- LOGV("GET_NUM_VIDEO_BUFFERS");
- CHECK_INTERFACE(ICamera, data, reply);
- reply->writeInt32(getNumberOfVideoBuffers());
- return NO_ERROR;
- } break;
- case GET_VIDEO_BUFFER: {
- LOGV("GET_VIDEO_BUFFER");
- CHECK_INTERFACE(ICamera, data, reply);
- int32_t index = data.readInt32();
- reply->writeStrongBinder(getVideoBuffer(index)->asBinder());
- return NO_ERROR;
- } break;
case STORE_META_DATA_IN_BUFFERS: {
LOGV("STORE_META_DATA_IN_BUFFERS");
CHECK_INTERFACE(ICamera, data, reply);
@@ -401,7 +366,8 @@
case TAKE_PICTURE: {
LOGV("TAKE_PICTURE");
CHECK_INTERFACE(ICamera, data, reply);
- reply->writeInt32(takePicture());
+ int msgType = data.readInt32();
+ reply->writeInt32(takePicture(msgType));
return NO_ERROR;
} break;
case SET_PARAMETERS: {
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 178032d..e9642f7 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -8,7 +8,7 @@
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia libutils libbinder libstagefright_foundation \
- libskia
+ libskia libgui
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
@@ -107,7 +107,7 @@
stream.cpp \
LOCAL_SHARED_LIBRARIES := \
- libstagefright liblog libutils libbinder libsurfaceflinger_client \
+ libstagefright liblog libutils libbinder libgui \
libstagefright_foundation libmedia
LOCAL_C_INCLUDES:= \
@@ -132,7 +132,7 @@
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation \
- libmedia libsurfaceflinger_client libcutils libui
+ libmedia libgui libcutils libui
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index 8733662..858681f 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -11,6 +11,8 @@
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
+#include <system/audio.h>
+
using namespace android;
int main() {
@@ -31,8 +33,8 @@
AUDIO_SOURCE_DEFAULT,
kSampleRate,
kNumChannels == 1
- ? AudioSystem::CHANNEL_IN_MONO
- : AudioSystem::CHANNEL_IN_STEREO);
+ ? AUDIO_CHANNEL_IN_MONO
+ : AUDIO_CHANNEL_IN_STEREO);
#endif
sp<MetaData> meta = new MetaData;
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 1dc08ea..289665f 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 <binder/ProcessState.h>
#include <media/stagefright/foundation/hexdump.h>
@@ -170,6 +186,7 @@
mCodec->signalResume();
(new AMessage(kWhatSeek, id()))->post(5000000ll);
+ } else if (what == ACodec::kWhatOutputFormatChanged) {
} else {
CHECK_EQ(what, (int32_t)ACodec::kWhatShutdownCompleted);
@@ -526,7 +543,6 @@
CHECK_EQ(composerClient->initCheck(), (status_t)OK);
control = composerClient->createSurface(
- getpid(),
String8("A Surface"),
0,
1280,
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index a43b190..a875c3a 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -55,6 +55,11 @@
#include <fcntl.h>
+#include <gui/SurfaceTextureClient.h>
+
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
using namespace android;
static long gNumRepetitions;
@@ -66,6 +71,10 @@
static bool gDisplayHistogram;
static String8 gWriteMP4Filename;
+static sp<ANativeWindow> gSurface;
+
+#define USE_SURFACE_COMPOSER 0
+
static int64_t getNowUs() {
struct timeval tv;
gettimeofday(&tv, NULL);
@@ -138,7 +147,8 @@
rawSource = OMXCodec::Create(
client->interface(), meta, false /* createEncoder */, source,
NULL /* matchComponentName */,
- gPreferSoftwareCodec ? OMXCodec::kPreferSoftwareCodecs : 0);
+ gPreferSoftwareCodec ? OMXCodec::kPreferSoftwareCodecs : 0,
+ gSurface);
if (rawSource == NULL) {
fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime);
@@ -540,6 +550,7 @@
fprintf(stderr, " -k seek test\n");
fprintf(stderr, " -x display a histogram of decoding times/fps "
"(video only)\n");
+ fprintf(stderr, " -S allocate buffers from a surface\n");
}
int main(int argc, char **argv) {
@@ -550,6 +561,7 @@
bool dumpProfiles = false;
bool extractThumbnail = false;
bool seekTest = false;
+ bool useSurfaceAlloc = false;
gNumRepetitions = 1;
gMaxNumFrames = 0;
gReproduceBug = -1;
@@ -563,7 +575,7 @@
sp<LiveSession> liveSession;
int res;
- while ((res = getopt(argc, argv, "han:lm:b:ptsow:kx")) >= 0) {
+ while ((res = getopt(argc, argv, "han:lm:b:ptsow:kxS")) >= 0) {
switch (res) {
case 'a':
{
@@ -642,6 +654,12 @@
break;
}
+ case 'S':
+ {
+ useSurfaceAlloc = true;
+ break;
+ }
+
case '?':
case 'h':
default:
@@ -780,6 +798,39 @@
}
}
+ sp<SurfaceComposerClient> composerClient;
+ sp<SurfaceControl> control;
+
+ if (useSurfaceAlloc && !audioOnly) {
+#if USE_SURFACE_COMPOSER
+ composerClient = new SurfaceComposerClient;
+ CHECK_EQ(composerClient->initCheck(), (status_t)OK);
+
+ control = composerClient->createSurface(
+ getpid(),
+ String8("A Surface"),
+ 0,
+ 1280,
+ 800,
+ PIXEL_FORMAT_RGB_565,
+ 0);
+
+ CHECK(control != NULL);
+ CHECK(control->isValid());
+
+ CHECK_EQ(composerClient->openTransaction(), (status_t)OK);
+ CHECK_EQ(control->setLayer(30000), (status_t)OK);
+ CHECK_EQ(control->show(), (status_t)OK);
+ CHECK_EQ(composerClient->closeTransaction(), (status_t)OK);
+
+ gSurface = control->getSurface();
+ CHECK(gSurface != NULL);
+#else
+ sp<SurfaceTexture> texture = new SurfaceTexture(0 /* tex */);
+ gSurface = new SurfaceTextureClient(texture);
+#endif
+ }
+
DataSource::RegisterDefaultSniffers();
OMXClient client;
@@ -957,6 +1008,14 @@
}
}
+ if (useSurfaceAlloc && !audioOnly) {
+ gSurface.clear();
+
+#if USE_SURFACE_COMPOSER
+ composerClient->dispose();
+#endif
+ }
+
client.disconnect();
return 0;
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 9246a00..f780afb 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 <binder/ProcessState.h>
#include <media/IStreamSource.h>
@@ -91,7 +107,7 @@
: mEOS(false) {
}
- virtual void notify(int msg, int ext1, int ext2) {
+ virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {
Mutex::Autolock autoLock(mLock);
if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) {
@@ -133,7 +149,6 @@
sp<SurfaceControl> control =
composerClient->createSurface(
- getpid(),
String8("A Surface"),
0,
1280,
diff --git a/drm/common/Android.mk b/drm/common/Android.mk
index c79a91a..f1136c9 100644
--- a/drm/common/Android.mk
+++ b/drm/common/Android.mk
@@ -26,7 +26,6 @@
DrmInfoStatus.cpp \
DrmRights.cpp \
DrmSupportInfo.cpp \
- IDrmIOService.cpp \
IDrmManagerService.cpp \
IDrmServiceListener.cpp \
DrmInfoEvent.cpp \
diff --git a/drm/common/DrmInfoEvent.cpp b/drm/common/DrmInfoEvent.cpp
index 8d115a8..27a5a2d 100644
--- a/drm/common/DrmInfoEvent.cpp
+++ b/drm/common/DrmInfoEvent.cpp
@@ -19,7 +19,7 @@
using namespace android;
-DrmInfoEvent::DrmInfoEvent(int uniqueId, int infoType, const String8& message)
+DrmInfoEvent::DrmInfoEvent(int uniqueId, int infoType, const String8 message)
: mUniqueId(uniqueId),
mInfoType(infoType),
mMessage(message) {
@@ -34,7 +34,7 @@
return mInfoType;
}
-const String8& DrmInfoEvent::getMessage() const {
+const String8 DrmInfoEvent::getMessage() const {
return mMessage;
}
diff --git a/drm/common/DrmSupportInfo.cpp b/drm/common/DrmSupportInfo.cpp
index ffc8953..3dee435 100644
--- a/drm/common/DrmSupportInfo.cpp
+++ b/drm/common/DrmSupportInfo.cpp
@@ -15,6 +15,7 @@
*/
#include <drm/DrmSupportInfo.h>
+#include <strings.h>
using namespace android;
@@ -45,7 +46,7 @@
for (unsigned int i = 0; i < mMimeTypeVector.size(); i++) {
const String8 item = mMimeTypeVector.itemAt(i);
- if (String8("") != mimeType && item.find(mimeType) != -1) {
+ if (!strcasecmp(item.string(), mimeType.string())) {
return true;
}
}
@@ -56,7 +57,7 @@
for (unsigned int i = 0; i < mFileSuffixVector.size(); i++) {
const String8 item = mFileSuffixVector.itemAt(i);
- if (String8("") != fileType && item.find(fileType) != -1) {
+ if (!strcasecmp(item.string(), fileType.string())) {
return true;
}
}
@@ -152,4 +153,3 @@
mIndex++;
return value;
}
-
diff --git a/drm/common/IDrmIOService.cpp b/drm/common/IDrmIOService.cpp
deleted file mode 100644
index e44ca55..0000000
--- a/drm/common/IDrmIOService.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2010 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 <stdint.h>
-#include <sys/types.h>
-#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
-#include <drm/drm_framework_common.h>
-#include "IDrmIOService.h"
-
-using namespace android;
-
-void BpDrmIOService::writeToFile(const String8& filePath, const String8& dataBuffer) {
- Parcel data, reply;
-
- data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor());
- data.writeString8(filePath);
- data.writeString8(dataBuffer);
-
- remote()->transact(WRITE_TO_FILE, data, &reply);
-}
-
-String8 BpDrmIOService::readFromFile(const String8& filePath) {
-
- Parcel data, reply;
-
- data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor());
- data.writeString8(filePath);
-
- remote()->transact(READ_FROM_FILE, data, &reply);
- return reply.readString8();
-}
-
-IMPLEMENT_META_INTERFACE(DrmIOService, "drm.IDrmIOService");
-
-status_t BnDrmIOService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-
- switch (code) {
- case WRITE_TO_FILE:
- {
- CHECK_INTERFACE(IDrmIOService, data, reply);
-
- writeToFile(data.readString8(), data.readString8());
- return DRM_NO_ERROR;
- }
-
- case READ_FROM_FILE:
- {
- CHECK_INTERFACE(IDrmIOService, data, reply);
-
- String8 dataBuffer = readFromFile(data.readString8());
- reply->writeString8(dataBuffer);
- return DRM_NO_ERROR;
- }
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
index ddbd220..c37b4f8 100644
--- a/drm/common/IDrmManagerService.cpp
+++ b/drm/common/IDrmManagerService.cpp
@@ -37,6 +37,78 @@
using namespace android;
+static void writeDecrptHandleToParcelData(
+ const DecryptHandle* handle, Parcel* data) {
+ data->writeInt32(handle->decryptId);
+ data->writeString8(handle->mimeType);
+ data->writeInt32(handle->decryptApiType);
+ data->writeInt32(handle->status);
+
+ int size = handle->copyControlVector.size();
+ data->writeInt32(size);
+ for(int i = 0; i < size; i++) {
+ data->writeInt32(handle->copyControlVector.keyAt(i));
+ data->writeInt32(handle->copyControlVector.valueAt(i));
+ }
+
+ size = handle->extendedData.size();
+ data->writeInt32(size);
+ for(int i = 0; i < size; i++) {
+ data->writeString8(handle->extendedData.keyAt(i));
+ data->writeString8(handle->extendedData.valueAt(i));
+ }
+
+ if (NULL != handle->decryptInfo) {
+ data->writeInt32(handle->decryptInfo->decryptBufferLength);
+ } else {
+ data->writeInt32(INVALID_BUFFER_LENGTH);
+ }
+}
+
+static void readDecryptHandleFromParcelData(
+ DecryptHandle* handle, const Parcel& data) {
+ if (0 == data.dataAvail()) {
+ return;
+ }
+
+ handle->decryptId = data.readInt32();
+ handle->mimeType = data.readString8();
+ handle->decryptApiType = data.readInt32();
+ handle->status = data.readInt32();
+
+ int size = data.readInt32();
+ for (int i = 0; i < size; i ++) {
+ DrmCopyControl key = (DrmCopyControl)data.readInt32();
+ int value = data.readInt32();
+ handle->copyControlVector.add(key, value);
+ }
+
+ size = data.readInt32();
+ for (int i = 0; i < size; i ++) {
+ String8 key = data.readString8();
+ String8 value = data.readString8();
+ handle->extendedData.add(key, value);
+ }
+
+ handle->decryptInfo = NULL;
+ const int bufferLen = data.readInt32();
+ if (INVALID_BUFFER_LENGTH != bufferLen) {
+ handle->decryptInfo = new DecryptInfo();
+ handle->decryptInfo->decryptBufferLength = bufferLen;
+ }
+}
+
+static void clearDecryptHandle(DecryptHandle* handle) {
+ if (handle == NULL) {
+ return;
+ }
+ if (handle->decryptInfo) {
+ delete handle->decryptInfo;
+ handle->decryptInfo = NULL;
+ }
+ handle->copyControlVector.clear();
+}
+
int BpDrmManagerService::addUniqueId(int uniqueId) {
LOGV("add uniqueid");
Parcel data, reply;
@@ -344,16 +416,7 @@
data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
data.writeInt32(uniqueId);
- data.writeInt32(decryptHandle->decryptId);
- data.writeString8(decryptHandle->mimeType);
- data.writeInt32(decryptHandle->decryptApiType);
- data.writeInt32(decryptHandle->status);
-
- if (NULL != decryptHandle->decryptInfo) {
- data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
- } else {
- data.writeInt32(INVALID_BUFFER_LENGTH);
- }
+ writeDecrptHandleToParcelData(decryptHandle, &data);
data.writeInt32(action);
data.writeInt32(static_cast< int>(reserve));
@@ -370,16 +433,7 @@
data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
data.writeInt32(uniqueId);
- data.writeInt32(decryptHandle->decryptId);
- data.writeString8(decryptHandle->mimeType);
- data.writeInt32(decryptHandle->decryptApiType);
- data.writeInt32(decryptHandle->status);
-
- if (NULL != decryptHandle->decryptInfo) {
- data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
- } else {
- data.writeInt32(INVALID_BUFFER_LENGTH);
- }
+ writeDecrptHandleToParcelData(decryptHandle, &data);
data.writeInt32(playbackStatus);
data.writeInt64(position);
@@ -560,15 +614,7 @@
DecryptHandle* handle = NULL;
if (0 != reply.dataAvail()) {
handle = new DecryptHandle();
- handle->decryptId = reply.readInt32();
- handle->mimeType = reply.readString8();
- handle->decryptApiType = reply.readInt32();
- handle->status = reply.readInt32();
- handle->decryptInfo = NULL;
- if (0 != reply.dataAvail()) {
- handle->decryptInfo = new DecryptInfo();
- handle->decryptInfo->decryptBufferLength = reply.readInt32();
- }
+ readDecryptHandleFromParcelData(handle, reply);
}
return handle;
}
@@ -586,17 +632,9 @@
DecryptHandle* handle = NULL;
if (0 != reply.dataAvail()) {
handle = new DecryptHandle();
- handle->decryptId = reply.readInt32();
- handle->mimeType = reply.readString8();
- handle->decryptApiType = reply.readInt32();
- handle->status = reply.readInt32();
- handle->decryptInfo = NULL;
- if (0 != reply.dataAvail()) {
- handle->decryptInfo = new DecryptInfo();
- handle->decryptInfo->decryptBufferLength = reply.readInt32();
- }
+ readDecryptHandleFromParcelData(handle, reply);
} else {
- LOGE("no decryptHandle is generated in service side");
+ LOGV("no decryptHandle is generated in service side");
}
return handle;
}
@@ -608,24 +646,10 @@
data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
data.writeInt32(uniqueId);
- data.writeInt32(decryptHandle->decryptId);
- data.writeString8(decryptHandle->mimeType);
- data.writeInt32(decryptHandle->decryptApiType);
- data.writeInt32(decryptHandle->status);
-
- if (NULL != decryptHandle->decryptInfo) {
- data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
- } else {
- data.writeInt32(INVALID_BUFFER_LENGTH);
- }
+ writeDecrptHandleToParcelData(decryptHandle, &data);
remote()->transact(CLOSE_DECRYPT_SESSION, data, &reply);
- if (NULL != decryptHandle->decryptInfo) {
- LOGV("deleting decryptInfo");
- delete decryptHandle->decryptInfo; decryptHandle->decryptInfo = NULL;
- }
- delete decryptHandle; decryptHandle = NULL;
return reply.readInt32();
}
@@ -638,16 +662,8 @@
data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
data.writeInt32(uniqueId);
- data.writeInt32(decryptHandle->decryptId);
- data.writeString8(decryptHandle->mimeType);
- data.writeInt32(decryptHandle->decryptApiType);
- data.writeInt32(decryptHandle->status);
+ writeDecrptHandleToParcelData(decryptHandle, &data);
- if (NULL != decryptHandle->decryptInfo) {
- data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
- } else {
- data.writeInt32(INVALID_BUFFER_LENGTH);
- }
data.writeInt32(decryptUnitId);
data.writeInt32(headerInfo->length);
@@ -666,16 +682,7 @@
data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
data.writeInt32(uniqueId);
- data.writeInt32(decryptHandle->decryptId);
- data.writeString8(decryptHandle->mimeType);
- data.writeInt32(decryptHandle->decryptApiType);
- data.writeInt32(decryptHandle->status);
-
- if (NULL != decryptHandle->decryptInfo) {
- data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
- } else {
- data.writeInt32(INVALID_BUFFER_LENGTH);
- }
+ writeDecrptHandleToParcelData(decryptHandle, &data);
data.writeInt32(decryptUnitId);
data.writeInt32((*decBuffer)->length);
@@ -708,16 +715,7 @@
data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
data.writeInt32(uniqueId);
- data.writeInt32(decryptHandle->decryptId);
- data.writeString8(decryptHandle->mimeType);
- data.writeInt32(decryptHandle->decryptApiType);
- data.writeInt32(decryptHandle->status);
-
- if (NULL != decryptHandle->decryptInfo) {
- data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
- } else {
- data.writeInt32(INVALID_BUFFER_LENGTH);
- }
+ writeDecrptHandleToParcelData(decryptHandle, &data);
data.writeInt32(decryptUnitId);
@@ -735,16 +733,7 @@
data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
data.writeInt32(uniqueId);
- data.writeInt32(decryptHandle->decryptId);
- data.writeString8(decryptHandle->mimeType);
- data.writeInt32(decryptHandle->decryptApiType);
- data.writeInt32(decryptHandle->status);
-
- if (NULL != decryptHandle->decryptInfo) {
- data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
- } else {
- data.writeInt32(INVALID_BUFFER_LENGTH);
- }
+ writeDecrptHandleToParcelData(decryptHandle, &data);
data.writeInt32(numBytes);
data.writeInt64(offset);
@@ -1069,24 +1058,14 @@
const int uniqueId = data.readInt32();
DecryptHandle handle;
- handle.decryptId = data.readInt32();
- handle.mimeType = data.readString8();
- handle.decryptApiType = data.readInt32();
- handle.status = data.readInt32();
- handle.decryptInfo = NULL;
-
- const int bufferLength = data.readInt32();
- if (INVALID_BUFFER_LENGTH != bufferLength) {
- handle.decryptInfo = new DecryptInfo();
- handle.decryptInfo->decryptBufferLength = bufferLength;
- }
+ readDecryptHandleFromParcelData(&handle, data);
const status_t status
= consumeRights(uniqueId, &handle, data.readInt32(),
static_cast<bool>(data.readInt32()));
reply->writeInt32(status);
- delete handle.decryptInfo; handle.decryptInfo = NULL;
+ clearDecryptHandle(&handle);
return DRM_NO_ERROR;
}
@@ -1098,23 +1077,13 @@
const int uniqueId = data.readInt32();
DecryptHandle handle;
- handle.decryptId = data.readInt32();
- handle.mimeType = data.readString8();
- handle.decryptApiType = data.readInt32();
- handle.status = data.readInt32();
- handle.decryptInfo = NULL;
-
- const int bufferLength = data.readInt32();
- if (INVALID_BUFFER_LENGTH != bufferLength) {
- handle.decryptInfo = new DecryptInfo();
- handle.decryptInfo->decryptBufferLength = bufferLength;
- }
+ readDecryptHandleFromParcelData(&handle, data);
const status_t status
= setPlaybackStatus(uniqueId, &handle, data.readInt32(), data.readInt64());
reply->writeInt32(status);
- delete handle.decryptInfo; handle.decryptInfo = NULL;
+ clearDecryptHandle(&handle);
return DRM_NO_ERROR;
}
@@ -1275,16 +1244,10 @@
= openDecryptSession(uniqueId, fd, data.readInt64(), data.readInt64());
if (NULL != handle) {
- reply->writeInt32(handle->decryptId);
- reply->writeString8(handle->mimeType);
- reply->writeInt32(handle->decryptApiType);
- reply->writeInt32(handle->status);
- if (NULL != handle->decryptInfo) {
- reply->writeInt32(handle->decryptInfo->decryptBufferLength);
- delete handle->decryptInfo; handle->decryptInfo = NULL;
- }
+ writeDecrptHandleToParcelData(handle, reply);
+ clearDecryptHandle(handle);
+ delete handle; handle = NULL;
}
- delete handle; handle = NULL;
return DRM_NO_ERROR;
}
@@ -1299,18 +1262,13 @@
DecryptHandle* handle = openDecryptSession(uniqueId, uri.string());
if (NULL != handle) {
- reply->writeInt32(handle->decryptId);
- reply->writeString8(handle->mimeType);
- reply->writeInt32(handle->decryptApiType);
- reply->writeInt32(handle->status);
- if (NULL != handle->decryptInfo) {
- reply->writeInt32(handle->decryptInfo->decryptBufferLength);
- delete handle->decryptInfo; handle->decryptInfo = NULL;
- }
+ writeDecrptHandleToParcelData(handle, reply);
+
+ clearDecryptHandle(handle);
+ delete handle; handle = NULL;
} else {
- LOGE("NULL decryptHandle is returned");
+ LOGV("NULL decryptHandle is returned");
}
- delete handle; handle = NULL;
return DRM_NO_ERROR;
}
@@ -1322,17 +1280,7 @@
const int uniqueId = data.readInt32();
DecryptHandle* handle = new DecryptHandle();
- handle->decryptId = data.readInt32();
- handle->mimeType = data.readString8();
- handle->decryptApiType = data.readInt32();
- handle->status = data.readInt32();
- handle->decryptInfo = NULL;
-
- const int bufferLength = data.readInt32();
- if (INVALID_BUFFER_LENGTH != bufferLength) {
- handle->decryptInfo = new DecryptInfo();
- handle->decryptInfo->decryptBufferLength = bufferLength;
- }
+ readDecryptHandleFromParcelData(handle, data);
const status_t status = closeDecryptSession(uniqueId, handle);
reply->writeInt32(status);
@@ -1347,17 +1295,8 @@
const int uniqueId = data.readInt32();
DecryptHandle handle;
- handle.decryptId = data.readInt32();
- handle.mimeType = data.readString8();
- handle.decryptApiType = data.readInt32();
- handle.status = data.readInt32();
- handle.decryptInfo = NULL;
+ readDecryptHandleFromParcelData(&handle, data);
- const int bufferLength = data.readInt32();
- if (INVALID_BUFFER_LENGTH != bufferLength) {
- handle.decryptInfo = new DecryptInfo();
- handle.decryptInfo->decryptBufferLength = bufferLength;
- }
const int decryptUnitId = data.readInt32();
//Filling Header info
@@ -1369,7 +1308,7 @@
= initializeDecryptUnit(uniqueId, &handle, decryptUnitId, headerInfo);
reply->writeInt32(status);
- delete handle.decryptInfo; handle.decryptInfo = NULL;
+ clearDecryptHandle(&handle);
delete headerInfo; headerInfo = NULL;
return DRM_NO_ERROR;
}
@@ -1382,17 +1321,8 @@
const int uniqueId = data.readInt32();
DecryptHandle handle;
- handle.decryptId = data.readInt32();
- handle.mimeType = data.readString8();
- handle.decryptApiType = data.readInt32();
- handle.status = data.readInt32();
- handle.decryptInfo = NULL;
+ readDecryptHandleFromParcelData(&handle, data);
- const int bufferLength = data.readInt32();
- if (INVALID_BUFFER_LENGTH != bufferLength) {
- handle.decryptInfo = new DecryptInfo();
- handle.decryptInfo->decryptBufferLength = bufferLength;
- }
const int decryptUnitId = data.readInt32();
const int decBufferSize = data.readInt32();
@@ -1419,7 +1349,7 @@
reply->writeInt32(size);
reply->write(decBuffer->data, size);
- delete handle.decryptInfo; handle.decryptInfo = NULL;
+ clearDecryptHandle(&handle);
delete encBuffer; encBuffer = NULL;
delete decBuffer; decBuffer = NULL;
delete [] buffer; buffer = NULL;
@@ -1435,22 +1365,12 @@
const int uniqueId = data.readInt32();
DecryptHandle handle;
- handle.decryptId = data.readInt32();
- handle.mimeType = data.readString8();
- handle.decryptApiType = data.readInt32();
- handle.status = data.readInt32();
- handle.decryptInfo = NULL;
-
- const int bufferLength = data.readInt32();
- if (INVALID_BUFFER_LENGTH != bufferLength) {
- handle.decryptInfo = new DecryptInfo();
- handle.decryptInfo->decryptBufferLength = bufferLength;
- }
+ readDecryptHandleFromParcelData(&handle, data);
const status_t status = finalizeDecryptUnit(uniqueId, &handle, data.readInt32());
reply->writeInt32(status);
- delete handle.decryptInfo; handle.decryptInfo = NULL;
+ clearDecryptHandle(&handle);
return DRM_NO_ERROR;
}
@@ -1462,17 +1382,7 @@
const int uniqueId = data.readInt32();
DecryptHandle handle;
- handle.decryptId = data.readInt32();
- handle.mimeType = data.readString8();
- handle.decryptApiType = data.readInt32();
- handle.status = data.readInt32();
- handle.decryptInfo = NULL;
-
- const int bufferLength = data.readInt32();
- if (INVALID_BUFFER_LENGTH != bufferLength) {
- handle.decryptInfo = new DecryptInfo();
- handle.decryptInfo->decryptBufferLength = bufferLength;
- }
+ readDecryptHandleFromParcelData(&handle, data);
const int numBytes = data.readInt32();
char* buffer = new char[numBytes];
@@ -1485,7 +1395,7 @@
reply->write(buffer, result);
}
- delete handle.decryptInfo; handle.decryptInfo = NULL;
+ clearDecryptHandle(&handle);
delete [] buffer, buffer = NULL;
return DRM_NO_ERROR;
}
diff --git a/drm/drmserver/Android.mk b/drm/drmserver/Android.mk
index 5df2ff8..e3cd44f 100644
--- a/drm/drmserver/Android.mk
+++ b/drm/drmserver/Android.mk
@@ -19,10 +19,10 @@
LOCAL_SRC_FILES:= \
main_drmserver.cpp \
DrmManager.cpp \
- DrmManagerService.cpp \
- StringTokenizer.cpp
+ DrmManagerService.cpp
LOCAL_SHARED_LIBRARIES := \
+ libmedia \
libutils \
libbinder
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index 9a6f787..1809619 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -37,7 +37,6 @@
using namespace android;
-Vector<int> DrmManager::mUniqueIdVector;
const String8 DrmManager::EMPTY_STRING("");
DrmManager::DrmManager() :
@@ -51,6 +50,7 @@
}
int DrmManager::addUniqueId(int uniqueId) {
+ Mutex::Autolock _l(mLock);
if (0 == uniqueId) {
int temp = 0;
bool foundUniqueId = false;
@@ -78,6 +78,7 @@
}
void DrmManager::removeUniqueId(int uniqueId) {
+ Mutex::Autolock _l(mLock);
for (unsigned int i = 0; i < mUniqueIdVector.size(); i++) {
if (uniqueId == mUniqueIdVector.itemAt(i)) {
mUniqueIdVector.removeAt(i);
@@ -87,7 +88,7 @@
}
status_t DrmManager::loadPlugIns() {
- String8 pluginDirPath("/system/lib/drm/plugins/native");
+ String8 pluginDirPath("/system/lib/drm");
return loadPlugIns(pluginDirPath);
}
@@ -100,6 +101,7 @@
DrmSupportInfo* info = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0);
if (NULL != info) {
mSupportInfoToPlugInIdMap.add(*info, plugInPath);
+ delete info;
}
}
}
@@ -107,6 +109,7 @@
}
status_t DrmManager::unloadPlugIns() {
+ Mutex::Autolock _l(mLock);
mConvertSessionMap.clear();
mDecryptSessionMap.clear();
mPlugInManager.unloadPlugIns();
@@ -116,12 +119,17 @@
status_t DrmManager::setDrmServiceListener(
int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) {
- Mutex::Autolock _l(mLock);
- mServiceListeners.add(uniqueId, drmServiceListener);
+ Mutex::Autolock _l(mListenerLock);
+ if (NULL != drmServiceListener.get()) {
+ mServiceListeners.add(uniqueId, drmServiceListener);
+ } else {
+ mServiceListeners.removeItem(uniqueId);
+ }
return DRM_NO_ERROR;
}
void DrmManager::addClient(int uniqueId) {
+ Mutex::Autolock _l(mLock);
if (!mSupportInfoToPlugInIdMap.isEmpty()) {
Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
for (unsigned int index = 0; index < plugInIdList.size(); index++) {
@@ -133,6 +141,7 @@
}
void DrmManager::removeClient(int uniqueId) {
+ Mutex::Autolock _l(mLock);
Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
for (unsigned int index = 0; index < plugInIdList.size(); index++) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
@@ -141,6 +150,7 @@
}
DrmConstraints* DrmManager::getConstraints(int uniqueId, const String8* path, const int action) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -150,6 +160,7 @@
}
DrmMetadata* DrmManager::getMetadata(int uniqueId, const String8* path) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -159,6 +170,7 @@
}
status_t DrmManager::installDrmEngine(int uniqueId, const String8& absolutePath) {
+ Mutex::Autolock _l(mLock);
mPlugInManager.loadPlugIn(absolutePath);
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(absolutePath);
@@ -167,11 +179,13 @@
DrmSupportInfo* info = rDrmEngine.getSupportInfo(0);
mSupportInfoToPlugInIdMap.add(*info, absolutePath);
+ delete info;
return DRM_NO_ERROR;
}
bool DrmManager::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInId(mimeType);
bool result = (EMPTY_STRING != plugInId) ? true : false;
@@ -180,13 +194,17 @@
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
result = rDrmEngine.canHandle(uniqueId, path);
} else {
- result = canHandle(uniqueId, path);
+ String8 extension = path.getPathExtension();
+ if (String8("") != extension) {
+ result = canHandle(uniqueId, path);
+ }
}
}
return result;
}
DrmInfoStatus* DrmManager::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInId(drmInfo->getMimeType());
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -211,6 +229,7 @@
}
DrmInfo* DrmManager::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInId(drmInfoRequest->getMimeType());
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -221,6 +240,7 @@
status_t DrmManager::saveRights(int uniqueId, const DrmRights& drmRights,
const String8& rightsPath, const String8& contentPath) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInId(drmRights.getMimeType());
status_t result = DRM_ERROR_UNKNOWN;
if (EMPTY_STRING != plugInId) {
@@ -231,6 +251,7 @@
}
String8 DrmManager::getOriginalMimeType(int uniqueId, const String8& path) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -240,6 +261,7 @@
}
int DrmManager::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInId(uniqueId, path, mimeType);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -249,6 +271,7 @@
}
int DrmManager::checkRightsStatus(int uniqueId, const String8& path, int action) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -260,6 +283,7 @@
status_t DrmManager::consumeRights(
int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
status_t result = DRM_ERROR_UNKNOWN;
+ Mutex::Autolock _l(mDecryptLock);
if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
result = drmEngine->consumeRights(uniqueId, decryptHandle, action, reserve);
@@ -270,6 +294,7 @@
status_t DrmManager::setPlaybackStatus(
int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
status_t result = DRM_ERROR_UNKNOWN;
+ Mutex::Autolock _l(mDecryptLock);
if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
result = drmEngine->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
@@ -279,6 +304,7 @@
bool DrmManager::validateAction(
int uniqueId, const String8& path, int action, const ActionDescription& description) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -288,6 +314,7 @@
}
status_t DrmManager::removeRights(int uniqueId, const String8& path) {
+ Mutex::Autolock _l(mLock);
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
status_t result = DRM_ERROR_UNKNOWN;
if (EMPTY_STRING != plugInId) {
@@ -311,6 +338,7 @@
}
int DrmManager::openConvertSession(int uniqueId, const String8& mimeType) {
+ Mutex::Autolock _l(mConvertLock);
int convertId = -1;
const String8 plugInId = getSupportedPlugInId(mimeType);
@@ -318,7 +346,6 @@
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
if (DRM_NO_ERROR == rDrmEngine.openConvertSession(uniqueId, mConvertId + 1)) {
- Mutex::Autolock _l(mConvertLock);
++mConvertId;
convertId = mConvertId;
mConvertSessionMap.add(convertId, &rDrmEngine);
@@ -331,6 +358,7 @@
int uniqueId, int convertId, const DrmBuffer* inputData) {
DrmConvertedStatus *drmConvertedStatus = NULL;
+ Mutex::Autolock _l(mConvertLock);
if (mConvertSessionMap.indexOfKey(convertId) != NAME_NOT_FOUND) {
IDrmEngine* drmEngine = mConvertSessionMap.valueFor(convertId);
drmConvertedStatus = drmEngine->convertData(uniqueId, convertId, inputData);
@@ -339,6 +367,7 @@
}
DrmConvertedStatus* DrmManager::closeConvertSession(int uniqueId, int convertId) {
+ Mutex::Autolock _l(mConvertLock);
DrmConvertedStatus *drmConvertedStatus = NULL;
if (mConvertSessionMap.indexOfKey(convertId) != NAME_NOT_FOUND) {
@@ -351,6 +380,7 @@
status_t DrmManager::getAllSupportInfo(
int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
+ Mutex::Autolock _l(mLock);
Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
int size = plugInPathList.size();
int validPlugins = 0;
@@ -430,7 +460,7 @@
}
if (DRM_NO_ERROR != result) {
delete handle; handle = NULL;
- LOGE("DrmManager::openDecryptSession: no capable plug-in found");
+ LOGV("DrmManager::openDecryptSession: no capable plug-in found");
}
return handle;
}
@@ -451,6 +481,7 @@
status_t DrmManager::initializeDecryptUnit(
int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
status_t result = DRM_ERROR_UNKNOWN;
+ Mutex::Autolock _l(mDecryptLock);
if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
result = drmEngine->initializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo);
@@ -461,6 +492,8 @@
status_t DrmManager::decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
status_t result = DRM_ERROR_UNKNOWN;
+
+ Mutex::Autolock _l(mDecryptLock);
if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
result = drmEngine->decrypt(
@@ -472,6 +505,7 @@
status_t DrmManager::finalizeDecryptUnit(
int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
status_t result = DRM_ERROR_UNKNOWN;
+ Mutex::Autolock _l(mDecryptLock);
if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
result = drmEngine->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
@@ -483,6 +517,7 @@
void* buffer, ssize_t numBytes, off64_t offset) {
ssize_t result = DECRYPT_FILE_ERROR;
+ Mutex::Autolock _l(mDecryptLock);
if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
result = drmEngine->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
@@ -539,7 +574,7 @@
}
void DrmManager::onInfo(const DrmInfoEvent& event) {
- Mutex::Autolock _l(mLock);
+ Mutex::Autolock _l(mListenerLock);
for (unsigned int index = 0; index < mServiceListeners.size(); index++) {
int uniqueId = mServiceListeners.keyAt(index);
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
index 0901a44..583669e 100644
--- a/drm/drmserver/DrmManagerService.cpp
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include <private/android_filesystem_config.h>
+#include <media/MemoryLeakTrackUtil.h>
#include <errno.h>
#include <utils/threads.h>
@@ -256,3 +257,31 @@
return mDrmManager->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
}
+status_t DrmManagerService::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump DrmManagerService from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ result.append(buffer);
+ } else {
+#if DRM_MEMORY_LEAK_TRACK
+ bool dumpMem = false;
+ for (size_t i = 0; i < args.size(); i++) {
+ if (args[i] == String16("-m")) {
+ dumpMem = true;
+ }
+ }
+ if (dumpMem) {
+ dumpMemoryAddresses(fd);
+ }
+#endif
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
diff --git a/drm/drmserver/StringTokenizer.cpp b/drm/drmserver/StringTokenizer.cpp
deleted file mode 100644
index 2130a00..0000000
--- a/drm/drmserver/StringTokenizer.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2010 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 "StringTokenizer.h"
-
-using namespace android;
-
-StringTokenizer::StringTokenizer(const String8& string, const String8& delimiter) {
- splitString(string, delimiter);
-}
-
-void StringTokenizer::splitString(const String8& string, const String8& delimiter) {
- for (unsigned int i = 0; i < string.length(); i++) {
- unsigned int position = string.find(delimiter.string(), i);
- if (string.length() != position) {
- String8 token(string.string()+i, position-i);
- if (token.length()) {
- mStringTokenizerVector.push(token);
- i = position + delimiter.length() - 1;
- }
- } else {
- mStringTokenizerVector.push(String8(string.string()+i, string.length()-i));
- break;
- }
- }
-}
-
-StringTokenizer::Iterator StringTokenizer::iterator() {
- return Iterator(this);
-}
-
-StringTokenizer::Iterator::Iterator(const StringTokenizer::Iterator& iterator) :
- mStringTokenizer(iterator.mStringTokenizer),
- mIndex(iterator.mIndex) {
-}
-
-StringTokenizer::Iterator& StringTokenizer::Iterator::operator=(
- const StringTokenizer::Iterator& iterator) {
- mStringTokenizer = iterator.mStringTokenizer;
- mIndex = iterator.mIndex;
- return *this;
-}
-
-bool StringTokenizer::Iterator::hasNext() {
- return mIndex < mStringTokenizer->mStringTokenizerVector.size();
-}
-
-String8& StringTokenizer::Iterator::next() {
- String8& value = mStringTokenizer->mStringTokenizerVector.editItemAt(mIndex);
- mIndex++;
- return value;
-}
-
diff --git a/drm/libdrmframework/Android.mk b/drm/libdrmframework/Android.mk
index 99133ba..f1526a4 100644
--- a/drm/libdrmframework/Android.mk
+++ b/drm/libdrmframework/Android.mk
@@ -40,7 +40,7 @@
$(TOP)/frameworks/base/drm/libdrmframework/include \
$(TOP)/frameworks/base/include
-LOCAL_PRELINK_MODULE := false
+
LOCAL_MODULE_TAGS := optional
diff --git a/drm/libdrmframework/DrmManagerClient.cpp b/drm/libdrmframework/DrmManagerClient.cpp
index 578e135..b50199f 100644
--- a/drm/libdrmframework/DrmManagerClient.cpp
+++ b/drm/libdrmframework/DrmManagerClient.cpp
@@ -31,7 +31,7 @@
DrmManagerClient::~DrmManagerClient() {
DrmManagerClientImpl::remove(mUniqueId);
mDrmManagerClientImpl->removeClient(mUniqueId);
- delete mDrmManagerClientImpl; mDrmManagerClientImpl = NULL;
+ mDrmManagerClientImpl->setOnInfoListener(mUniqueId, NULL);
}
status_t DrmManagerClient::setOnInfoListener(
@@ -76,13 +76,13 @@
return mDrmManagerClientImpl->checkRightsStatus(mUniqueId, path, action);
}
-status_t DrmManagerClient::consumeRights(DecryptHandle* decryptHandle, int action, bool reserve) {
- Mutex::Autolock _l(mDecryptLock);
+status_t DrmManagerClient::consumeRights(
+ sp<DecryptHandle> &decryptHandle, int action, bool reserve) {
return mDrmManagerClientImpl->consumeRights(mUniqueId, decryptHandle, action, reserve);
}
status_t DrmManagerClient::setPlaybackStatus(
- DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
+ sp<DecryptHandle> &decryptHandle, int playbackStatus, int64_t position) {
return mDrmManagerClientImpl
->setPlaybackStatus(mUniqueId, decryptHandle, playbackStatus, position);
}
@@ -116,41 +116,39 @@
return mDrmManagerClientImpl->getAllSupportInfo(mUniqueId, length, drmSupportInfoArray);
}
-DecryptHandle* DrmManagerClient::openDecryptSession(int fd, off64_t offset, off64_t length) {
+sp<DecryptHandle> DrmManagerClient::openDecryptSession(int fd, off64_t offset, off64_t length) {
return mDrmManagerClientImpl->openDecryptSession(mUniqueId, fd, offset, length);
}
-DecryptHandle* DrmManagerClient::openDecryptSession(const char* uri) {
+sp<DecryptHandle> DrmManagerClient::openDecryptSession(const char* uri) {
return mDrmManagerClientImpl->openDecryptSession(mUniqueId, uri);
}
-status_t DrmManagerClient::closeDecryptSession(DecryptHandle* decryptHandle) {
+status_t DrmManagerClient::closeDecryptSession(sp<DecryptHandle> &decryptHandle) {
return mDrmManagerClientImpl->closeDecryptSession(mUniqueId, decryptHandle);
}
status_t DrmManagerClient::initializeDecryptUnit(
- DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
- Mutex::Autolock _l(mDecryptLock);
+ sp<DecryptHandle> &decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
return mDrmManagerClientImpl->initializeDecryptUnit(
mUniqueId, decryptHandle, decryptUnitId, headerInfo);
}
status_t DrmManagerClient::decrypt(
- DecryptHandle* decryptHandle, int decryptUnitId,
- const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
- Mutex::Autolock _l(mDecryptLock);
+ sp<DecryptHandle> &decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
return mDrmManagerClientImpl->decrypt(
mUniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
}
-status_t DrmManagerClient::finalizeDecryptUnit(DecryptHandle* decryptHandle, int decryptUnitId) {
- Mutex::Autolock _l(mDecryptLock);
- return mDrmManagerClientImpl->finalizeDecryptUnit(mUniqueId, decryptHandle, decryptUnitId);
+status_t DrmManagerClient::finalizeDecryptUnit(
+ sp<DecryptHandle> &decryptHandle, int decryptUnitId) {
+ return mDrmManagerClientImpl->finalizeDecryptUnit(mUniqueId,
+ decryptHandle, decryptUnitId);
}
ssize_t DrmManagerClient::pread(
- DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off64_t offset) {
- Mutex::Autolock _l(mDecryptLock);
+ sp<DecryptHandle> &decryptHandle, void* buffer, ssize_t numBytes, off64_t offset) {
return mDrmManagerClientImpl->pread(mUniqueId, decryptHandle, buffer, numBytes, offset);
}
diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp
index f39131d..a57dd98 100644
--- a/drm/libdrmframework/DrmManagerClientImpl.cpp
+++ b/drm/libdrmframework/DrmManagerClientImpl.cpp
@@ -28,8 +28,9 @@
#define INVALID_VALUE -1
-Mutex DrmManagerClientImpl::mMutex;
-sp<IDrmManagerService> DrmManagerClientImpl::mDrmManagerService;
+Mutex DrmManagerClientImpl::sMutex;
+sp<IDrmManagerService> DrmManagerClientImpl::sDrmManagerService;
+sp<DrmManagerClientImpl::DeathNotifier> DrmManagerClientImpl::sDeathNotifier;
const String8 DrmManagerClientImpl::EMPTY_STRING("");
DrmManagerClientImpl* DrmManagerClientImpl::create(int* pUniqueId) {
@@ -47,8 +48,8 @@
}
const sp<IDrmManagerService>& DrmManagerClientImpl::getDrmManagerService() {
- mMutex.lock();
- if (NULL == mDrmManagerService.get()) {
+ Mutex::Autolock lock(sMutex);
+ if (NULL == sDrmManagerService.get()) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
@@ -62,11 +63,13 @@
reqt.tv_nsec = 500000000; //0.5 sec
nanosleep(&reqt, NULL);
} while (true);
-
- mDrmManagerService = interface_cast<IDrmManagerService>(binder);
+ if (NULL == sDeathNotifier.get()) {
+ sDeathNotifier = new DeathNotifier();
+ }
+ binder->linkToDeath(sDeathNotifier);
+ sDrmManagerService = interface_cast<IDrmManagerService>(binder);
}
- mMutex.unlock();
- return mDrmManagerService;
+ return sDrmManagerService;
}
void DrmManagerClientImpl::addClient(int uniqueId) {
@@ -78,13 +81,16 @@
}
status_t DrmManagerClientImpl::setOnInfoListener(
- int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener) {
+ int uniqueId,
+ const sp<DrmManagerClient::OnInfoListener>& infoListener) {
Mutex::Autolock _l(mLock);
mOnInfoListener = infoListener;
- return getDrmManagerService()->setDrmServiceListener(uniqueId, this);
+ return getDrmManagerService()->setDrmServiceListener(uniqueId,
+ (NULL != infoListener.get()) ? this : NULL);
}
-status_t DrmManagerClientImpl::installDrmEngine(int uniqueId, const String8& drmEngineFile) {
+status_t DrmManagerClientImpl::installDrmEngine(
+ int uniqueId, const String8& drmEngineFile) {
status_t status = DRM_ERROR_UNKNOWN;
if (EMPTY_STRING != drmEngineFile) {
status = getDrmManagerService()->installDrmEngine(uniqueId, drmEngineFile);
@@ -96,7 +102,8 @@
int uniqueId, const String8* path, const int action) {
DrmConstraints *drmConstraints = NULL;
if ((NULL != path) && (EMPTY_STRING != *path)) {
- drmConstraints = getDrmManagerService()->getConstraints(uniqueId, path, action);
+ drmConstraints =
+ getDrmManagerService()->getConstraints(uniqueId, path, action);
}
return drmConstraints;
}
@@ -109,7 +116,8 @@
return drmMetadata;
}
-bool DrmManagerClientImpl::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+bool DrmManagerClientImpl::canHandle(
+ int uniqueId, const String8& path, const String8& mimeType) {
bool retCode = false;
if ((EMPTY_STRING != path) || (EMPTY_STRING != mimeType)) {
retCode = getDrmManagerService()->canHandle(uniqueId, path, mimeType);
@@ -117,7 +125,8 @@
return retCode;
}
-DrmInfoStatus* DrmManagerClientImpl::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+DrmInfoStatus* DrmManagerClientImpl::processDrmInfo(
+ int uniqueId, const DrmInfo* drmInfo) {
DrmInfoStatus *drmInfoStatus = NULL;
if (NULL != drmInfo) {
drmInfoStatus = getDrmManagerService()->processDrmInfo(uniqueId, drmInfo);
@@ -125,7 +134,8 @@
return drmInfoStatus;
}
-DrmInfo* DrmManagerClientImpl::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+DrmInfo* DrmManagerClientImpl::acquireDrmInfo(
+ int uniqueId, const DrmInfoRequest* drmInfoRequest) {
DrmInfo* drmInfo = NULL;
if (NULL != drmInfoRequest) {
drmInfo = getDrmManagerService()->acquireDrmInfo(uniqueId, drmInfoRequest);
@@ -136,13 +146,12 @@
status_t DrmManagerClientImpl::saveRights(int uniqueId, const DrmRights& drmRights,
const String8& rightsPath, const String8& contentPath) {
status_t status = DRM_ERROR_UNKNOWN;
- if (EMPTY_STRING != contentPath) {
- status = getDrmManagerService()->saveRights(uniqueId, drmRights, rightsPath, contentPath);
- }
- return status;
+ return getDrmManagerService()->saveRights(
+ uniqueId, drmRights, rightsPath, contentPath);
}
-String8 DrmManagerClientImpl::getOriginalMimeType(int uniqueId, const String8& path) {
+String8 DrmManagerClientImpl::getOriginalMimeType(
+ int uniqueId, const String8& path) {
String8 mimeType = EMPTY_STRING;
if (EMPTY_STRING != path) {
mimeType = getDrmManagerService()->getOriginalMimeType(uniqueId, path);
@@ -154,7 +163,8 @@
int uniqueId, const String8& path, const String8& mimeType) {
int drmOjectType = DrmObjectType::UNKNOWN;
if ((EMPTY_STRING != path) || (EMPTY_STRING != mimeType)) {
- drmOjectType = getDrmManagerService()->getDrmObjectType(uniqueId, path, mimeType);
+ drmOjectType =
+ getDrmManagerService()->getDrmObjectType(uniqueId, path, mimeType);
}
return drmOjectType;
}
@@ -163,35 +173,41 @@
int uniqueId, const String8& path, int action) {
int rightsStatus = RightsStatus::RIGHTS_INVALID;
if (EMPTY_STRING != path) {
- rightsStatus = getDrmManagerService()->checkRightsStatus(uniqueId, path, action);
+ rightsStatus =
+ getDrmManagerService()->checkRightsStatus(uniqueId, path, action);
}
return rightsStatus;
}
status_t DrmManagerClientImpl::consumeRights(
- int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+ int uniqueId, sp<DecryptHandle> &decryptHandle,
+ int action, bool reserve) {
status_t status = DRM_ERROR_UNKNOWN;
- if (NULL != decryptHandle) {
- status = getDrmManagerService()->consumeRights(uniqueId, decryptHandle, action, reserve);
+ if (NULL != decryptHandle.get()) {
+ status = getDrmManagerService()->consumeRights(
+ uniqueId, decryptHandle.get(), action, reserve);
}
return status;
}
status_t DrmManagerClientImpl::setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
+ int uniqueId, sp<DecryptHandle> &decryptHandle,
+ int playbackStatus, int64_t position) {
status_t status = DRM_ERROR_UNKNOWN;
- if (NULL != decryptHandle) {
+ if (NULL != decryptHandle.get()) {
status = getDrmManagerService()->setPlaybackStatus(
- uniqueId, decryptHandle, playbackStatus, position);
+ uniqueId, decryptHandle.get(), playbackStatus, position);
}
return status;
}
bool DrmManagerClientImpl::validateAction(
- int uniqueId, const String8& path, int action, const ActionDescription& description) {
+ int uniqueId, const String8& path,
+ int action, const ActionDescription& description) {
bool retCode = false;
if (EMPTY_STRING != path) {
- retCode = getDrmManagerService()->validateAction(uniqueId, path, action, description);
+ retCode = getDrmManagerService()->validateAction(
+ uniqueId, path, action, description);
}
return retCode;
}
@@ -208,7 +224,8 @@
return getDrmManagerService()->removeAllRights(uniqueId);
}
-int DrmManagerClientImpl::openConvertSession(int uniqueId, const String8& mimeType) {
+int DrmManagerClientImpl::openConvertSession(
+ int uniqueId, const String8& mimeType) {
int retCode = INVALID_VALUE;
if (EMPTY_STRING != mimeType) {
retCode = getDrmManagerService()->openConvertSession(uniqueId, mimeType);
@@ -220,12 +237,14 @@
int uniqueId, int convertId, const DrmBuffer* inputData) {
DrmConvertedStatus* drmConvertedStatus = NULL;
if (NULL != inputData) {
- drmConvertedStatus = getDrmManagerService()->convertData(uniqueId, convertId, inputData);
+ drmConvertedStatus =
+ getDrmManagerService()->convertData(uniqueId, convertId, inputData);
}
return drmConvertedStatus;
}
-DrmConvertedStatus* DrmManagerClientImpl::closeConvertSession(int uniqueId, int convertId) {
+DrmConvertedStatus* DrmManagerClientImpl::closeConvertSession(
+ int uniqueId, int convertId) {
return getDrmManagerService()->closeConvertSession(uniqueId, convertId);
}
@@ -233,17 +252,19 @@
int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
status_t status = DRM_ERROR_UNKNOWN;
if ((NULL != drmSupportInfoArray) && (NULL != length)) {
- status = getDrmManagerService()->getAllSupportInfo(uniqueId, length, drmSupportInfoArray);
+ status = getDrmManagerService()->getAllSupportInfo(
+ uniqueId, length, drmSupportInfoArray);
}
return status;
}
-DecryptHandle* DrmManagerClientImpl::openDecryptSession(
+sp<DecryptHandle> DrmManagerClientImpl::openDecryptSession(
int uniqueId, int fd, off64_t offset, off64_t length) {
return getDrmManagerService()->openDecryptSession(uniqueId, fd, offset, length);
}
-DecryptHandle* DrmManagerClientImpl::openDecryptSession(int uniqueId, const char* uri) {
+sp<DecryptHandle> DrmManagerClientImpl::openDecryptSession(
+ int uniqueId, const char* uri) {
DecryptHandle* handle = NULL;
if (NULL != uri) {
handle = getDrmManagerService()->openDecryptSession(uniqueId, uri);
@@ -251,50 +272,57 @@
return handle;
}
-status_t DrmManagerClientImpl::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+status_t DrmManagerClientImpl::closeDecryptSession(
+ int uniqueId, sp<DecryptHandle> &decryptHandle) {
status_t status = DRM_ERROR_UNKNOWN;
- if (NULL != decryptHandle) {
- status = getDrmManagerService()->closeDecryptSession( uniqueId, decryptHandle);
+ if (NULL != decryptHandle.get()) {
+ status = getDrmManagerService()->closeDecryptSession(
+ uniqueId, decryptHandle.get());
}
return status;
}
-status_t DrmManagerClientImpl::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
- int decryptUnitId, const DrmBuffer* headerInfo) {
+status_t DrmManagerClientImpl::initializeDecryptUnit(
+ int uniqueId, sp<DecryptHandle> &decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo) {
status_t status = DRM_ERROR_UNKNOWN;
- if ((NULL != decryptHandle) && (NULL != headerInfo)) {
+ if ((NULL != decryptHandle.get()) && (NULL != headerInfo)) {
status = getDrmManagerService()->initializeDecryptUnit(
- uniqueId, decryptHandle, decryptUnitId, headerInfo);
+ uniqueId, decryptHandle.get(), decryptUnitId, headerInfo);
}
return status;
}
-status_t DrmManagerClientImpl::decrypt(int uniqueId, DecryptHandle* decryptHandle,
- int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+status_t DrmManagerClientImpl::decrypt(
+ int uniqueId, sp<DecryptHandle> &decryptHandle,
+ int decryptUnitId, const DrmBuffer* encBuffer,
+ DrmBuffer** decBuffer, DrmBuffer* IV) {
status_t status = DRM_ERROR_UNKNOWN;
- if ((NULL != decryptHandle) && (NULL != encBuffer)
+ if ((NULL != decryptHandle.get()) && (NULL != encBuffer)
&& (NULL != decBuffer) && (NULL != *decBuffer)) {
status = getDrmManagerService()->decrypt(
- uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+ uniqueId, decryptHandle.get(), decryptUnitId,
+ encBuffer, decBuffer, IV);
}
return status;
}
status_t DrmManagerClientImpl::finalizeDecryptUnit(
- int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+ int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId) {
status_t status = DRM_ERROR_UNKNOWN;
- if (NULL != decryptHandle) {
- status
- = getDrmManagerService()->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
+ if (NULL != decryptHandle.get()) {
+ status = getDrmManagerService()->finalizeDecryptUnit(
+ uniqueId, decryptHandle.get(), decryptUnitId);
}
return status;
}
-ssize_t DrmManagerClientImpl::pread(int uniqueId, DecryptHandle* decryptHandle,
+ssize_t DrmManagerClientImpl::pread(int uniqueId, sp<DecryptHandle> &decryptHandle,
void* buffer, ssize_t numBytes, off64_t offset) {
ssize_t retCode = INVALID_VALUE;
- if ((NULL != decryptHandle) && (NULL != buffer) && (0 < numBytes)) {
- retCode = getDrmManagerService()->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
+ if ((NULL != decryptHandle.get()) && (NULL != buffer) && (0 < numBytes)) {
+ retCode = getDrmManagerService()->pread(
+ uniqueId, decryptHandle.get(), buffer, numBytes, offset);
}
return retCode;
}
@@ -308,3 +336,16 @@
return DRM_NO_ERROR;
}
+DrmManagerClientImpl::DeathNotifier::~DeathNotifier() {
+ Mutex::Autolock lock(sMutex);
+ if (NULL != sDrmManagerService.get()) {
+ sDrmManagerService->asBinder()->unlinkToDeath(this);
+ }
+}
+
+void DrmManagerClientImpl::DeathNotifier::binderDied(const wp<IBinder>& who) {
+ Mutex::Autolock lock(sMutex);
+ DrmManagerClientImpl::sDrmManagerService.clear();
+ LOGW("DrmManager server died!");
+}
+
diff --git a/drm/libdrmframework/include/DrmIOService.h b/drm/libdrmframework/include/DrmIOService.h
deleted file mode 100644
index 244124e..0000000
--- a/drm/libdrmframework/include/DrmIOService.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2010 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 __DRM_IO_SERVICE_H__
-#define __DRM_IO_SERVICE_H__
-
-#include "IDrmIOService.h"
-
-namespace android {
-
-/**
- * This is the implementation class for DRM IO service.
- *
- * The instance of this class is created while starting the DRM IO service.
- *
- */
-class DrmIOService : public BnDrmIOService {
-public:
- static void instantiate();
-
-private:
- DrmIOService();
- virtual ~DrmIOService();
-
-public:
- void writeToFile(const String8& filePath, const String8& dataBuffer);
- String8 readFromFile(const String8& filePath);
-};
-
-};
-
-#endif /* __DRM_IO_SERVICE_H__ */
-
diff --git a/drm/libdrmframework/include/DrmManager.h b/drm/libdrmframework/include/DrmManager.h
index e05366d..af2c2a8 100644
--- a/drm/libdrmframework/include/DrmManager.h
+++ b/drm/libdrmframework/include/DrmManager.h
@@ -30,7 +30,6 @@
class DrmRegistrationInfo;
class DrmUnregistrationInfo;
class DrmRightsAcquisitionInfo;
-class DrmContentIds;
class DrmConstraints;
class DrmMetadata;
class DrmRights;
@@ -141,12 +140,13 @@
bool canHandle(int uniqueId, const String8& path);
private:
- static Vector<int> mUniqueIdVector;
+ Vector<int> mUniqueIdVector;
static const String8 EMPTY_STRING;
int mDecryptSessionId;
int mConvertId;
Mutex mLock;
+ Mutex mListenerLock;
Mutex mDecryptLock;
Mutex mConvertLock;
TPlugInManager<IDrmEngine> mPlugInManager;
diff --git a/drm/libdrmframework/include/DrmManagerClientImpl.h b/drm/libdrmframework/include/DrmManagerClientImpl.h
index 0a7fcd1..564896b 100644
--- a/drm/libdrmframework/include/DrmManagerClientImpl.h
+++ b/drm/libdrmframework/include/DrmManagerClientImpl.h
@@ -189,7 +189,7 @@
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+ status_t consumeRights(int uniqueId, sp<DecryptHandle> &decryptHandle, int action, bool reserve);
/**
* Informs the DRM engine about the playback actions performed on the DRM files.
@@ -203,7 +203,7 @@
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
status_t setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
+ int uniqueId, sp<DecryptHandle> &decryptHandle, int playbackStatus, int64_t position);
/**
* Validates whether an action on the DRM content is allowed or not.
@@ -303,7 +303,7 @@
* @return
* Handle for the decryption session
*/
- DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length);
+ sp<DecryptHandle> openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length);
/**
* Open the decrypt session to decrypt the given protected content
@@ -313,7 +313,7 @@
* @return
* Handle for the decryption session
*/
- DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
+ sp<DecryptHandle> openDecryptSession(int uniqueId, const char* uri);
/**
* Close the decrypt session for the given handle
@@ -323,7 +323,7 @@
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+ status_t closeDecryptSession(int uniqueId, sp<DecryptHandle> &decryptHandle);
/**
* Initialize decryption for the given unit of the protected content
@@ -335,7 +335,7 @@
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle,
int decryptUnitId, const DrmBuffer* headerInfo);
/**
@@ -355,7 +355,7 @@
* DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
* DRM_ERROR_DECRYPT for failure.
*/
- status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ status_t decrypt(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId,
const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
/**
@@ -367,7 +367,7 @@
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+ status_t finalizeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId);
/**
* Reads the specified number of bytes from an open DRM file.
@@ -380,7 +380,7 @@
*
* @return Number of bytes read. Returns -1 for Failure.
*/
- ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+ ssize_t pread(int uniqueId, sp<DecryptHandle> &decryptHandle,
void* buffer, ssize_t numBytes, off64_t offset);
/**
@@ -407,9 +407,17 @@
Mutex mLock;
sp<DrmManagerClient::OnInfoListener> mOnInfoListener;
+ class DeathNotifier: public IBinder::DeathRecipient {
+ public:
+ DeathNotifier() {}
+ virtual ~DeathNotifier();
+ virtual void binderDied(const wp<IBinder>& who);
+ };
+
private:
- static Mutex mMutex;
- static sp<IDrmManagerService> mDrmManagerService;
+ static Mutex sMutex;
+ static sp<DeathNotifier> sDeathNotifier;
+ static sp<IDrmManagerService> sDrmManagerService;
static const sp<IDrmManagerService>& getDrmManagerService();
static const String8 EMPTY_STRING;
};
diff --git a/drm/libdrmframework/include/DrmManagerService.h b/drm/libdrmframework/include/DrmManagerService.h
index d0a0db7..227496a 100644
--- a/drm/libdrmframework/include/DrmManagerService.h
+++ b/drm/libdrmframework/include/DrmManagerService.h
@@ -115,6 +115,8 @@
ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
void* buffer, ssize_t numBytes, off64_t offset);
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
private:
DrmManager* mDrmManager;
};
diff --git a/drm/libdrmframework/include/IDrmIOService.h b/drm/libdrmframework/include/IDrmIOService.h
deleted file mode 100644
index 5e0d907..0000000
--- a/drm/libdrmframework/include/IDrmIOService.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2010 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 __IDRM_IO_SERVICE_H__
-#define __IDRM_IO_SERVICE_H__
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-/**
- * This is the interface class for DRM IO service.
- *
- */
-class IDrmIOService : public IInterface
-{
-public:
- enum {
- WRITE_TO_FILE = IBinder::FIRST_CALL_TRANSACTION,
- READ_FROM_FILE
- };
-
-public:
- DECLARE_META_INTERFACE(DrmIOService);
-
-public:
- /**
- * Writes the data into the file path provided
- *
- * @param[in] filePath Path of the file
- * @param[in] dataBuffer Data to write
- */
- virtual void writeToFile(const String8& filePath, const String8& dataBuffer) = 0;
-
- /**
- * Reads the data from the file path provided
- *
- * @param[in] filePath Path of the file
- * @return Data read from the file
- */
- virtual String8 readFromFile(const String8& filePath) = 0;
-};
-
-/**
- * This is the Binder implementation class for DRM IO service.
- */
-class BpDrmIOService: public BpInterface<IDrmIOService>
-{
-public:
- BpDrmIOService(const sp<IBinder>& impl)
- : BpInterface<IDrmIOService>(impl) {}
-
- virtual void writeToFile(const String8& filePath, const String8& dataBuffer);
-
- virtual String8 readFromFile(const String8& filePath);
-};
-
-/**
- * This is the Binder implementation class for DRM IO service.
- */
-class BnDrmIOService: public BnInterface<IDrmIOService>
-{
-public:
- virtual status_t onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
-};
-
-};
-
-#endif /* __IDRM_IO_SERVICE_H__ */
-
diff --git a/drm/libdrmframework/include/IDrmManagerService.h b/drm/libdrmframework/include/IDrmManagerService.h
index 2424ea5..7727e55 100644
--- a/drm/libdrmframework/include/IDrmManagerService.h
+++ b/drm/libdrmframework/include/IDrmManagerService.h
@@ -25,7 +25,6 @@
namespace android {
-class DrmContentIds;
class DrmConstraints;
class DrmMetadata;
class DrmRights;
diff --git a/drm/libdrmframework/include/PlugInManager.h b/drm/libdrmframework/include/PlugInManager.h
index 9ad195f..8029138 100644
--- a/drm/libdrmframework/include/PlugInManager.h
+++ b/drm/libdrmframework/include/PlugInManager.h
@@ -230,11 +230,9 @@
*/
bool isPlugIn(const struct dirent* pEntry) const {
String8 sName(pEntry->d_name);
- int extentionPos = sName.size() - String8(PLUGIN_EXTENSION).size();
- if (extentionPos < 0) {
- return false;
- }
- return extentionPos == (int)sName.find(PLUGIN_EXTENSION);
+ String8 extension(sName.getPathExtension());
+ // Note that the plug-in extension must exactly match case
+ return extension == String8(PLUGIN_EXTENSION);
}
/**
diff --git a/drm/libdrmframework/include/StringTokenizer.h b/drm/libdrmframework/include/StringTokenizer.h
deleted file mode 100644
index 70e7558..0000000
--- a/drm/libdrmframework/include/StringTokenizer.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2010 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 __STRING_TOKENIZER_H__
-#define __STRING_TOKENIZER_H__
-
-#include <drm/drm_framework_common.h>
-
-namespace android {
-
-/**
- * This is an utility class for String manipulation.
- *
- */
-class StringTokenizer {
-public:
- /**
- * Iterator for string tokens
- */
- class Iterator {
- friend class StringTokenizer;
- private:
- Iterator(StringTokenizer* StringTokenizer)
- : mStringTokenizer(StringTokenizer), mIndex(0) {}
-
- public:
- Iterator(const Iterator& iterator);
- Iterator& operator=(const Iterator& iterator);
- virtual ~Iterator() {}
-
- public:
- bool hasNext();
- String8& next();
-
- private:
- StringTokenizer* mStringTokenizer;
- unsigned int mIndex;
- };
-
-public:
- /**
- * Constructor for StringTokenizer
- *
- * @param[in] string Complete string data
- * @param[in] delimeter Delimeter used to split the string
- */
- StringTokenizer(const String8& string, const String8& delimeter);
-
- /**
- * Destructor for StringTokenizer
- */
- ~StringTokenizer() {}
-
-private:
- /**
- * Splits the string according to the delimeter
- */
- void splitString(const String8& string, const String8& delimeter);
-
-public:
- /**
- * Returns Iterator object to walk through the split string values
- *
- * @return Iterator object
- */
- Iterator iterator();
-
-private:
- Vector<String8> mStringTokenizerVector;
-};
-
-};
-#endif /* __STRING_TOKENIZER_H__ */
-
diff --git a/drm/libdrmframework/plugins/common/Android.mk b/drm/libdrmframework/plugins/common/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/Android.mk
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2010 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 $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/common/include/IDrmEngine.h b/drm/libdrmframework/plugins/common/include/IDrmEngine.h
index d05c24f..77460f6 100644
--- a/drm/libdrmframework/plugins/common/include/IDrmEngine.h
+++ b/drm/libdrmframework/plugins/common/include/IDrmEngine.h
@@ -21,7 +21,6 @@
namespace android {
-class DrmContentIds;
class DrmConstraints;
class DrmMetadata;
class DrmRights;
diff --git a/drm/libdrmframework/plugins/common/util/Android.mk b/drm/libdrmframework/plugins/common/util/Android.mk
new file mode 100644
index 0000000..15dda80
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2010 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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ src/MimeTypeUtil.cpp
+
+LOCAL_MODULE := libdrmutility
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ libdl \
+ libdvm \
+ libandroid_runtime \
+ libnativehelper \
+ liblog
+
+
+base := frameworks/base
+
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ $(base)/include \
+ $(base)/include/drm \
+ $(base)/include/drm/plugins \
+ $(LOCAL_PATH)/include
+
+
+ifneq ($(TARGET_BUILD_VARIANT),user)
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/tools
+
+endif
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h b/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h
new file mode 100644
index 0000000..4d12a61
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 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 __MIMETYPEUTIL_H__
+#define __MIMETYPEUTIL_H__
+
+#include <utils/String8.h>
+
+namespace android {
+
+class MimeTypeUtil {
+
+public:
+
+ MimeTypeUtil() {}
+
+ virtual ~MimeTypeUtil() {}
+
+/**
+ * May convert the mimetype if there is a well known
+ * replacement mimetype otherwise the original mimetype
+ * is returned.
+ *
+ * @param mimeType - mimetype in lower case to convert.
+ *
+ * @return mimetype or null.
+ */
+static String8 convertMimeType(String8& mimeType);
+
+};
+};
+
+#endif /* __MIMETYPEUTIL_H__ */
diff --git a/drm/libdrmframework/plugins/common/util/include/SessionMap.h b/drm/libdrmframework/plugins/common/util/include/SessionMap.h
new file mode 100644
index 0000000..3dff58c
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/include/SessionMap.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 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 __SESSIONMAP_H__
+#define __SESSIONMAP_H__
+
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+/**
+ * A wrapper template class for handling DRM Engine sessions.
+ */
+template <typename NODE>
+class SessionMap {
+
+public:
+ KeyedVector<int, NODE> map;
+
+ SessionMap() {}
+
+ virtual ~SessionMap() {
+ destroyMap();
+ }
+
+/**
+ * Adds a new value in the session map table. It expects memory to be allocated already
+ * for the session object
+ *
+ * @param key - key or Session ID
+ * @param value - session object to add
+ *
+ * @return boolean result of adding value. returns false if key is already exist.
+ */
+bool addValue(int key, NODE value) {
+ bool result = false;
+
+ if (!isCreated(key)) {
+ map.add(key, value);
+ result = true;
+ }
+
+ return result;
+}
+
+
+/**
+ * returns the session object by the key
+ *
+ * @param key - key or Session ID
+ *
+ * @return session object as per the key
+ */
+NODE getValue(int key) {
+ NODE value = NULL;
+
+ if (isCreated(key)) {
+ value = (NODE) map.valueFor(key);
+ }
+
+ return value;
+}
+
+/**
+ * returns the number of objects in the session map table
+ *
+ * @return count of number of session objects.
+ */
+int getSize() {
+ return map.size();
+}
+
+/**
+ * returns the session object by the index in the session map table
+ *
+ * @param index - index of the value required
+ *
+ * @return session object as per the index
+ */
+NODE getValueAt(unsigned int index) {
+ NODE value = NULL;
+
+ if (map.size() > index) {
+ value = map.valueAt(index);
+ }
+
+ return value;
+}
+
+/**
+ * deletes the object from session map. It also frees up memory for the session object.
+ *
+ * @param key - key of the value to be deleted
+ *
+ */
+void removeValue(int key) {
+ deleteValue(getValue(key));
+ map.removeItem(key);
+}
+
+/**
+ * decides if session is already created.
+ *
+ * @param key - key of the value for the session
+ *
+ * @return boolean result of whether session is created
+ */
+bool isCreated(int key) {
+ return (0 <= map.indexOfKey(key));
+}
+
+/**
+ * empty the entire session table. It releases all the memory for session objects.
+ */
+void destroyMap() {
+ int size = map.size();
+ int i = 0;
+
+ for (i = 0; i < size; i++) {
+ deleteValue(map.valueAt(i));
+ }
+
+ map.clear();
+}
+
+/**
+ * free up the memory for the session object.
+ * Make sure if any reference to the session object anywhere, otherwise it will be a
+ * dangle pointer after this call.
+ *
+ * @param value - session object to free
+ *
+ */
+void deleteValue(NODE value) {
+ delete value;
+}
+
+};
+
+};
+
+#endif /* __SESSIONMAP_H__ */
diff --git a/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
new file mode 100644
index 0000000..4ee903e
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2010 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 <MimeTypeUtil.h>
+#include <utils/Log.h>
+
+namespace android {
+
+#undef LOG_TAG
+#define LOG_TAG "MimeTypeUtil"
+
+enum {
+ MIMETYPE_AUDIO = 0,
+ MIMETYPE_APPLICATION = 1,
+ MIMETYPE_IMAGE = 2,
+ MIMETYPE_VIDEO = 3,
+ MIMETYPE_LAST = -1,
+};
+
+struct MimeGroup{
+ int type; // Audio, video,.. use the enum values
+ const char* pGroup; // "audio/", "video/",.. should contain the last "/"
+ int size; // Number of bytes. e.g. "audio/" = 6 bytes
+};
+
+struct MimeTypeList{
+ int type;
+ const char* pMimeExt; // Everything after the '/' e.g. audio/x-mpeg -> "x-mpeg"
+ int size; // Number of bytes. e.g. "x-mpeg" = 6 bytes
+ const char* pMimeType; // Mimetype that should be returned
+};
+
+
+// Known mimetypes by android
+static const char mime_type_audio_mpeg[] = "audio/mpeg";
+static const char mime_type_audio_3gpp[] = "audio/3gpp";
+static const char mime_type_audio_amr[] = "audio/amr-wb";
+static const char mime_type_audio_aac[] = "audio/mp4a-latm";
+static const char mime_type_audio_wav[] = "audio/wav";
+
+static const char mime_type_video_mpeg4[] = "video/mpeg4";
+static const char mime_type_video_3gpp[] = "video/3gpp";
+
+// Known mimetype groups
+static const char mime_group_audio[] = "audio/";
+static const char mime_group_application[] = "application/";
+static const char mime_group_image[] = "image/";
+static const char mime_group_video[] = "video/";
+
+static struct MimeGroup mimeGroup[] = {
+ {MIMETYPE_AUDIO, mime_group_audio, sizeof(mime_group_audio)-1},
+ {MIMETYPE_APPLICATION, mime_group_application, sizeof(mime_group_application)-1},
+ {MIMETYPE_IMAGE, mime_group_image, sizeof(mime_group_image)-1},
+ {MIMETYPE_VIDEO, mime_group_video, sizeof(mime_group_video)-1},
+ {MIMETYPE_LAST, NULL, 0} // Must be last entry
+};
+
+// List of all mimetypes that should be converted.
+static struct MimeTypeList mimeTypeList[] = {
+ // Mp3 mime types
+ {MIMETYPE_AUDIO, "mp3", sizeof("mp3")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "x-mpeg", sizeof("x-mpeg")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "x-mp3", sizeof("x-mp3")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "mpg", sizeof("mpg")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "mpg3", sizeof("mpg")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "x-mpg", sizeof("x-mpg")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "x-mpegaudio", sizeof("x-mpegaudio")-1, mime_type_audio_mpeg},
+
+ // 3gpp audio mime types
+ {MIMETYPE_AUDIO, "3gp", sizeof("3gp")-1, mime_type_audio_3gpp},
+
+ // Amr audio mime types
+ {MIMETYPE_AUDIO, "amr", sizeof("amr")-1, mime_type_audio_amr},
+
+ // Aac audio mime types
+ {MIMETYPE_AUDIO, "aac", sizeof("aac")-1, mime_type_audio_aac},
+
+ // Wav audio mime types
+ {MIMETYPE_AUDIO, "x-wav", sizeof("x-wav")-1, mime_type_audio_wav},
+
+ // Mpeg4 video mime types
+ {MIMETYPE_VIDEO, "mpg4", sizeof("mpg4")-1, mime_type_video_mpeg4},
+ {MIMETYPE_VIDEO, "mp4v-es", sizeof("mp4v-es")-1, mime_type_video_mpeg4},
+
+ // 3gpp video mime types
+ {MIMETYPE_VIDEO, "3gp", sizeof("3gp")-1, mime_type_video_3gpp},
+
+ // Must be last entry
+ {MIMETYPE_LAST, NULL, 0, NULL}
+};
+
+/**
+ * May convert the mimetype if there is a well known
+ * replacement mimetype otherwise the original mimetype
+ * is returned.
+ *
+ * @param mimeType - mimetype in lower case to convert.
+ *
+ * @return mimetype or null.
+ */
+String8 MimeTypeUtil::convertMimeType(String8& mimeType) {
+ String8 result = mimeType;
+ const char* pTmp;
+ const char* pMimeType;
+ struct MimeGroup* pGroup;
+ struct MimeTypeList* pMimeItem;
+ int len;
+
+ pMimeType = mimeType.string();
+ if (NULL != pMimeType) {
+ /* Check which group the mimetype is */
+ pGroup = mimeGroup;
+
+ while (MIMETYPE_LAST != pGroup->type) {
+ if (0 == strncmp(pMimeType, pGroup->pGroup, pGroup->size)) {
+ break;
+ }
+ pGroup++;
+ }
+
+ /* Go through the mimetype list. Only check items of the correct group */
+ if (MIMETYPE_LAST != pGroup->type) {
+ pMimeItem = mimeTypeList;
+ len = strlen (pMimeType+pGroup->size);
+
+ while (MIMETYPE_LAST != pMimeItem->type) {
+ if ((len == pMimeItem->size) &&
+ (0 == strcmp(pMimeType+pGroup->size, pMimeItem->pMimeExt))) {
+ result = String8(pMimeItem->pMimeType);
+ break;
+ }
+ pMimeItem++;
+ }
+ }
+ LOGI("convertMimeType got mimetype %s, converted into mimetype %s",
+ pMimeType, result.string());
+ }
+
+ return result;
+}
+};
diff --git a/drm/libdrmframework/plugins/forward-lock/Android.mk b/drm/libdrmframework/plugins/forward-lock/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/Android.mk
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2010 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 $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
new file mode 100644
index 0000000..9805a40
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 2010 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+base := frameworks/base
+
+# Determine whether the DRM framework uses 64-bit data types for file offsets and do the same.
+ifneq ($(shell grep -c 'off64_t offset' $(base)/drm/libdrmframework/plugins/common/include/IDrmEngine.h), 0)
+LOCAL_CFLAGS += -DUSE_64BIT_DRM_API
+endif
+
+LOCAL_SRC_FILES:= \
+ src/FwdLockEngine.cpp
+
+LOCAL_MODULE := libfwdlockengine
+
+LOCAL_SHARED_LIBRARIES := \
+ libicui18n \
+ libicuuc \
+ libutils \
+ libdl \
+ libandroid_runtime \
+ libnativehelper \
+ libcrypto \
+ libssl \
+ libdrmframework
+
+LOCAL_STATIC_LIBRARIES := \
+ libdrmutility \
+ libdrmframeworkcommon \
+ libfwdlock-common \
+ libfwdlock-converter \
+ libfwdlock-decoder
+
+
+
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ $(base)/include/drm \
+ $(base)/drm/libdrmframework/plugins/common/include \
+ $(base)/drm/libdrmframework/plugins/common/util/include \
+ $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+ $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/converter \
+ $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/decoder \
+ $(LOCAL_PATH)/include \
+ external/openssl/include
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
new file mode 100644
index 0000000..34804cf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2010 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 __FWDLOCKENGINE_H__
+#define __FWDLOCKENGINE_H__
+
+#include <DrmEngineBase.h>
+#include <DrmConstraints.h>
+#include <DrmRights.h>
+#include <DrmInfo.h>
+#include <DrmInfoStatus.h>
+#include <DrmConvertedStatus.h>
+#include <DrmInfoRequest.h>
+#include <DrmSupportInfo.h>
+#include <DrmInfoEvent.h>
+
+#include "SessionMap.h"
+#include "FwdLockConv.h"
+
+namespace android {
+
+/**
+ * Forward Lock Engine class.
+ */
+class FwdLockEngine : public android::DrmEngineBase {
+
+public:
+ FwdLockEngine();
+ virtual ~FwdLockEngine();
+
+protected:
+/**
+ * Get constraint information associated with input content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Actions defined such as,
+ * Action::DEFAULT, Action::PLAY, etc
+ * @return DrmConstraints
+ * key-value pairs of constraint are embedded in it
+ * @note
+ * In case of error, return NULL
+ */
+DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action);
+
+/**
+ * Get metadata information associated with input content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return DrmMetadata
+ * For Forward Lock engine, it returns an empty object
+ * @note
+ * In case of error, returns NULL
+ */
+DrmMetadata* onGetMetadata(int uniqueId, const String8* path);
+
+/**
+ * Initialize plug-in.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onInitialize(int uniqueId);
+
+/**
+ * Register a callback to be invoked when the caller required to
+ * receive necessary information.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param infoListener Listener
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
+
+/**
+ * Terminate the plug-in and release resources bound to it.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onTerminate(int uniqueId);
+
+/**
+ * Get whether the given content can be handled by this plugin or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path to the protected object
+ * @return bool
+ * Returns true if this plugin can handle , false in case of not able to handle
+ */
+bool onCanHandle(int uniqueId, const String8& path);
+
+/**
+ * Processes the given DRM information as appropriate for its type.
+ * Not used for Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmInfo Information that needs to be processed
+ * @return DrmInfoStatus
+ * instance as a result of processing given input
+ */
+DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+/**
+ * Save DRM rights to specified rights path
+ * and make association with content path.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmRights DrmRights to be saved
+ * @param rightsPath File path where rights to be saved
+ * @param contentPath File path where content was saved
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onSaveRights(int uniqueId,
+ const DrmRights& drmRights,
+ const String8& rightsPath,
+ const String8& contentPath);
+
+/**
+ * Retrieves necessary information for registration, unregistration or rights
+ * acquisition information.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmInfoRequest Request information to retrieve drmInfo
+ * @return DrmInfo
+ * instance as a result of processing given input
+ */
+DrmInfo* onAcquireDrmInfo(int uniqueId,
+ const DrmInfoRequest* drmInfoRequest);
+
+/**
+ * Retrieves the mime type embedded inside the original content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return String8
+ * Returns mime-type of the original content, such as "video/mpeg"
+ */
+String8 onGetOriginalMimeType(int uniqueId, const String8& path);
+
+/**
+ * Retrieves the type of the protected object (content, rights, etc..)
+ * using specified path or mimetype. At least one parameter should be non null
+ * to retrieve DRM object type.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the content or null.
+ * @param mimeType Mime type of the content or null.
+ * @return type of the DRM content,
+ * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+ */
+int onGetDrmObjectType(int uniqueId,
+ const String8& path,
+ const String8& mimeType);
+
+/**
+ * Check whether the given content has valid rights or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Action to perform (Action::DEFAULT, Action::PLAY, etc)
+ * @return the status of the rights for the protected content,
+ * such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
+ */
+int onCheckRightsStatus(int uniqueId,
+ const String8& path,
+ int action);
+
+/**
+ * Consumes the rights for a content.
+ * If the reserve parameter is true the rights are reserved until the same
+ * application calls this api again with the reserve parameter set to false.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+ * @param reserve True if the rights should be reserved.
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onConsumeRights(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int action,
+ bool reserve);
+
+/**
+ * Informs the DRM Engine about the playback actions performed on the DRM files.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+ * @param position Position in the file (in milliseconds) where the start occurs.
+ * Only valid together with Playback::START.
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+#ifdef USE_64BIT_DRM_API
+status_t onSetPlaybackStatus(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int playbackStatus,
+ int64_t position);
+#else
+status_t onSetPlaybackStatus(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int playbackStatus,
+ int position);
+#endif
+
+/**
+ * Validates whether an action on the DRM content is allowed or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+ * @param description Detailed description of the action
+ * @return true if the action is allowed.
+ */
+bool onValidateAction(int uniqueId,
+ const String8& path,
+ int action,
+ const ActionDescription& description);
+
+/**
+ * Removes the rights associated with the given protected content.
+ * Not used for Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onRemoveRights(int uniqueId, const String8& path);
+
+/**
+ * Removes all the rights information of each plug-in associated with
+ * DRM framework. Will be used in master reset but does nothing for
+ * Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onRemoveAllRights(int uniqueId);
+
+/**
+ * Starts the Forward Lock file conversion session.
+ * Each time the application tries to download a new DRM file
+ * which needs to be converted, then the application has to
+ * begin with calling this API. The convertId is used as the conversion session key
+ * and must not be the same for different convert sessions.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onOpenConvertSession(int uniqueId, int convertId);
+
+/**
+ * Accepts and converts the input data which is part of DRM file.
+ * The resultant converted data and the status is returned in the DrmConvertedInfo
+ * object. This method will be called each time there is a new block
+ * of data received by the application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @param inputData Input Data which need to be converted
+ * @return Return object contains the status of the data conversion,
+ * the output converted data and offset. In this case the
+ * application will ignore the offset information.
+ */
+DrmConvertedStatus* onConvertData(int uniqueId,
+ int convertId,
+ const DrmBuffer* inputData);
+
+/**
+ * Closes the convert session in case of data supply completed or error occurred.
+ * Upon successful conversion of the complete data, it returns signature calculated over
+ * the entire data used over a conversion session. This signature must be copied to the offset
+ * mentioned in the DrmConvertedStatus. Signature is used for data integrity protection.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @return Return object contains the status of the data conversion,
+ * the header and body signature data. It also informs
+ * the application about the file offset at which this
+ * signature data should be written.
+ */
+DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId);
+
+/**
+ * Returns the information about the Drm Engine capabilities which includes
+ * supported MimeTypes and file suffixes.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return DrmSupportInfo
+ * instance which holds the capabilities of a plug-in
+ */
+DrmSupportInfo* onGetSupportInfo(int uniqueId);
+
+/**
+ * Open the decrypt session to decrypt the given protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the current decryption session
+ * @param fd File descriptor of the protected content to be decrypted
+ * @param offset Start position of the content
+ * @param length The length of the protected content
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+#ifdef USE_64BIT_DRM_API
+status_t onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int fd, off64_t offset, off64_t length);
+#else
+status_t onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int fd, int offset, int length);
+#endif
+
+/**
+ * Open the decrypt session to decrypt the given protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the current decryption session
+ * @param uri Path of the protected content to be decrypted
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ const char* uri);
+
+/**
+ * Close the decrypt session for the given handle.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onCloseDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle);
+
+/**
+ * Initialize decryption for the given unit of the protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param headerInfo Information for initializing decryption of this decrypUnit
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onInitializeDecryptUnit(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId,
+ const DrmBuffer* headerInfo);
+
+/**
+ * Decrypt the protected content buffers for the given unit.
+ * This method will be called any number of times, based on number of
+ * encrypted streams received from application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param encBuffer Encrypted data block
+ * @param decBuffer Decrypted data block
+ * @return status_t
+ * Returns the error code for this API
+ * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+ * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+ * DRM_ERROR_DECRYPT for failure.
+ */
+status_t onDecrypt(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId,
+ const DrmBuffer* encBuffer,
+ DrmBuffer** decBuffer);
+
+/**
+ * Decrypt the protected content buffers for the given unit.
+ * This method will be called any number of times, based on number of
+ * encrypted streams received from application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptId Handle for the decryption session
+ * @param decryptUnitId ID Specifies decryption unit, such as track ID
+ * @param encBuffer Encrypted data block
+ * @param decBuffer Decrypted data block
+ * @param IV Optional buffer
+ * @return status_t
+ * Returns the error code for this API
+ * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+ * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+ * DRM_ERROR_DECRYPT for failure.
+ */
+status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* encBuffer,
+ DrmBuffer** decBuffer, DrmBuffer* IV);
+
+/**
+ * Finalize decryption for the given unit of the protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID Specifies decryption unit, such as track ID
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onFinalizeDecryptUnit(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId);
+
+/**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param buffer Reference to the buffer that should receive the read data.
+ * @param numBytes Number of bytes to read.
+ *
+ * @return Number of bytes read.
+ * @retval -1 Failure.
+ */
+ssize_t onRead(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* pBuffer,
+ int numBytes);
+
+/**
+ * Updates the file position within an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param offset Offset with which to update the file position.
+ * @param whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ * These constants are defined in unistd.h.
+ *
+ * @return New file position.
+ * @retval ((off_t)-1) Failure.
+ */
+#ifdef USE_64BIT_DRM_API
+off64_t onLseek(int uniqueId,
+ DecryptHandle* decryptHandle,
+ off64_t offset,
+ int whence);
+#else
+off_t onLseek(int uniqueId,
+ DecryptHandle* decryptHandle,
+ off_t offset,
+ int whence);
+#endif
+
+/**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param buffer Reference to the buffer that should receive the read data.
+ * @param numBytes Number of bytes to read.
+ * @param offset Offset with which to update the file position.
+ *
+ * @return Number of bytes read. Returns -1 for Failure.
+ */
+#ifdef USE_64BIT_DRM_API
+ssize_t onPread(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* buffer,
+ ssize_t numBytes,
+ off64_t offset);
+#else
+ssize_t onPread(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* buffer,
+ ssize_t numBytes,
+ off_t offset);
+#endif
+
+private:
+
+/**
+ * Session Class for Forward Lock Conversion. An object of this class is created
+ * for every conversion.
+ */
+class ConvertSession {
+ public :
+ int uniqueId;
+ FwdLockConv_Output_t output;
+
+ ConvertSession() {
+ uniqueId = 0;
+ memset(&output, 0, sizeof(FwdLockConv_Output_t));
+ }
+
+ virtual ~ConvertSession() {}
+};
+
+/**
+ * Session Class for Forward Lock decoder. An object of this class is created
+ * for every decoding session.
+ */
+class DecodeSession {
+ public :
+ int fileDesc;
+ off_t offset;
+
+ DecodeSession() {
+ fileDesc = -1;
+ offset = 0;
+ }
+
+ DecodeSession(int fd) {
+ fileDesc = fd;
+ offset = 0;
+ }
+
+ virtual ~DecodeSession() {}
+};
+
+/**
+ * Session Map Tables for Conversion and Decoding of forward lock files.
+ */
+SessionMap<ConvertSession*> convertSessionMap;
+SessionMap<DecodeSession*> decodeSessionMap;
+
+/**
+ * Converts the error code from Forward Lock Converter to DrmConvertStatus error code.
+ *
+ * @param Forward Lock Converter error code
+ *
+ * @return Status code from DrmConvertStatus.
+ */
+static int getConvertedStatus(FwdLockConv_Status_t status);
+};
+
+};
+
+#endif /* __FWDLOCKENGINE_H__ */
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h
new file mode 100644
index 0000000..da95d60
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 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 __FWDLOCKENGINECONST_H__
+#define __FWDLOCKENGINECONST_H__
+
+namespace android {
+
+/**
+ * Constants for forward Lock Engine used for exposing engine's capabilities.
+ */
+#define FWDLOCK_EXTENSION_FL ("FL")
+#define FWDLOCK_DOTEXTENSION_FL (".fl")
+#define FWDLOCK_MIMETYPE_FL ("application/x-android-drm-fl")
+
+#define FWDLOCK_EXTENSION_DM ("DM")
+#define FWDLOCK_DOTEXTENSION_DM (".dm")
+#define FWDLOCK_MIMETYPE_DM ("application/vnd.oma.drm.message")
+
+#define FWDLOCK_DESCRIPTION ("OMA V1 Forward Lock")
+
+};
+
+#endif /* __FWDLOCKENGINECONST_H__ */
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
new file mode 100644
index 0000000..d430f72
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2010 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 "SessionMap.h"
+#include "FwdLockEngine.h"
+#include <utils/Log.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "drm_framework_common.h"
+#include <fcntl.h>
+#include <limits.h>
+#include <DrmRights.h>
+#include <DrmConstraints.h>
+#include <DrmMetadata.h>
+#include <DrmInfo.h>
+#include <DrmInfoStatus.h>
+#include <DrmInfoRequest.h>
+#include <DrmSupportInfo.h>
+#include <DrmConvertedStatus.h>
+#include <utils/String8.h>
+#include "FwdLockConv.h"
+#include "FwdLockFile.h"
+#include "FwdLockGlue.h"
+#include "FwdLockEngineConst.h"
+#include "MimeTypeUtil.h"
+
+#undef LOG_TAG
+#define LOG_TAG "FwdLockEngine"
+
+using namespace android;
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" IDrmEngine* create() {
+ return new FwdLockEngine();
+}
+
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" void destroy(IDrmEngine* plugIn) {
+ delete plugIn;
+}
+
+FwdLockEngine::FwdLockEngine() {
+ LOGD("FwdLockEngine Construction");
+}
+
+FwdLockEngine::~FwdLockEngine() {
+ LOGD("FwdLockEngine Destruction");
+
+ convertSessionMap.destroyMap();
+ decodeSessionMap.destroyMap();
+}
+
+int FwdLockEngine::getConvertedStatus(FwdLockConv_Status_t status) {
+ int retStatus = DrmConvertedStatus::STATUS_ERROR;
+
+ switch(status) {
+ case FwdLockConv_Status_OK:
+ retStatus = DrmConvertedStatus::STATUS_OK;
+ break;
+ case FwdLockConv_Status_SyntaxError:
+ case FwdLockConv_Status_InvalidArgument:
+ case FwdLockConv_Status_UnsupportedFileFormat:
+ case FwdLockConv_Status_UnsupportedContentTransferEncoding:
+ LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \
+ "Returning STATUS_INPUTDATA_ERROR", status);
+ retStatus = DrmConvertedStatus::STATUS_INPUTDATA_ERROR;
+ break;
+ default:
+ LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \
+ "Returning STATUS_ERROR", status);
+ retStatus = DrmConvertedStatus::STATUS_ERROR;
+ break;
+ }
+
+ return retStatus;
+}
+
+DrmConstraints* FwdLockEngine::onGetConstraints(int uniqueId, const String8* path, int action) {
+ DrmConstraints* drmConstraints = NULL;
+
+ LOGD("FwdLockEngine::onGetConstraints");
+
+ if (NULL != path &&
+ (RightsStatus::RIGHTS_VALID == onCheckRightsStatus(uniqueId, *path, action))) {
+ // Return the empty constraints to show no error condition.
+ drmConstraints = new DrmConstraints();
+ }
+
+ return drmConstraints;
+}
+
+DrmMetadata* FwdLockEngine::onGetMetadata(int uniqueId, const String8* path) {
+ DrmMetadata* drmMetadata = NULL;
+
+ LOGD("FwdLockEngine::onGetMetadata");
+
+ if (NULL != path) {
+ // Returns empty metadata to show no error condition.
+ drmMetadata = new DrmMetadata();
+ }
+
+ return drmMetadata;
+}
+
+android::status_t FwdLockEngine::onInitialize(int uniqueId) {
+ LOGD("FwdLockEngine::onInitialize");
+
+
+ if (FwdLockGlue_InitializeKeyEncryption()) {
+ LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption succeeded");
+ } else {
+ LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption failed:"
+ "errno = %d", errno);
+ }
+
+ return DRM_NO_ERROR;
+}
+
+android::status_t
+FwdLockEngine::onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener) {
+ // Not used
+ LOGD("FwdLockEngine::onSetOnInfoListener");
+
+ return DRM_NO_ERROR;
+}
+
+android::status_t FwdLockEngine::onTerminate(int uniqueId) {
+ LOGD("FwdLockEngine::onTerminate");
+
+ return DRM_NO_ERROR;
+}
+
+DrmSupportInfo* FwdLockEngine::onGetSupportInfo(int uniqueId) {
+ DrmSupportInfo* pSupportInfo = new DrmSupportInfo();
+
+ LOGD("FwdLockEngine::onGetSupportInfo");
+
+ // fill all Forward Lock mimetypes and extensions
+ if (NULL != pSupportInfo) {
+ pSupportInfo->addMimeType(String8(FWDLOCK_MIMETYPE_FL));
+ pSupportInfo->addFileSuffix(String8(FWDLOCK_DOTEXTENSION_FL));
+ pSupportInfo->addMimeType(String8(FWDLOCK_MIMETYPE_DM));
+ pSupportInfo->addFileSuffix(String8(FWDLOCK_DOTEXTENSION_DM));
+
+ pSupportInfo->setDescription(String8(FWDLOCK_DESCRIPTION));
+ }
+
+ return pSupportInfo;
+}
+
+bool FwdLockEngine::onCanHandle(int uniqueId, const String8& path) {
+ bool result = false;
+
+ String8 extString = path.getPathExtension();
+
+ extString.toLower();
+
+ if ((extString == String8(FWDLOCK_DOTEXTENSION_FL)) ||
+ (extString == String8(FWDLOCK_DOTEXTENSION_DM))) {
+ result = true;
+ }
+ return result;
+}
+
+DrmInfoStatus* FwdLockEngine::onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ DrmInfoStatus *drmInfoStatus = NULL;
+
+ // Nothing to process
+
+ drmInfoStatus = new DrmInfoStatus((int)DrmInfoStatus::STATUS_OK, 0, NULL, String8(""));
+
+ LOGD("FwdLockEngine::onProcessDrmInfo");
+
+ return drmInfoStatus;
+}
+
+status_t FwdLockEngine::onSaveRights(
+ int uniqueId,
+ const DrmRights& drmRights,
+ const String8& rightsPath,
+ const String8& contentPath) {
+ // No rights to save. Return
+ LOGD("FwdLockEngine::onSaveRights");
+ return DRM_ERROR_UNKNOWN;
+}
+
+DrmInfo* FwdLockEngine::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+ DrmInfo* drmInfo = NULL;
+
+ // Nothing to be done for Forward Lock file
+ LOGD("FwdLockEngine::onAcquireDrmInfo");
+
+ return drmInfo;
+}
+
+int FwdLockEngine::onCheckRightsStatus(int uniqueId,
+ const String8& path,
+ int action) {
+ int result = RightsStatus::RIGHTS_INVALID;
+
+ LOGD("FwdLockEngine::onCheckRightsStatus");
+
+ // Only Transfer action is not allowed for forward Lock files.
+ if (onCanHandle(uniqueId, path)) {
+ switch(action) {
+ case Action::DEFAULT:
+ case Action::PLAY:
+ case Action::RINGTONE:
+ case Action::OUTPUT:
+ case Action::PREVIEW:
+ case Action::EXECUTE:
+ case Action::DISPLAY:
+ result = RightsStatus::RIGHTS_VALID;
+ break;
+
+ case Action::TRANSFER:
+ default:
+ result = RightsStatus::RIGHTS_INVALID;
+ break;
+ }
+ }
+
+ return result;
+}
+
+status_t FwdLockEngine::onConsumeRights(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int action,
+ bool reserve) {
+ // No rights consumption
+ LOGD("FwdLockEngine::onConsumeRights");
+ return DRM_NO_ERROR;
+}
+
+bool FwdLockEngine::onValidateAction(int uniqueId,
+ const String8& path,
+ int action,
+ const ActionDescription& description) {
+ LOGD("FwdLockEngine::onValidateAction");
+
+ // For the forwardlock engine checkRights and ValidateAction are the same.
+ return (onCheckRightsStatus(uniqueId, path, action) == RightsStatus::RIGHTS_VALID);
+}
+
+String8 FwdLockEngine::onGetOriginalMimeType(int uniqueId, const String8& path) {
+ LOGD("FwdLockEngine::onGetOriginalMimeType");
+ String8 mimeString = String8("");
+ int fileDesc = FwdLockFile_open(path.string());
+
+ if (-1 < fileDesc) {
+ const char* pMimeType = FwdLockFile_GetContentType(fileDesc);
+
+ if (NULL != pMimeType) {
+ String8 contentType = String8(pMimeType);
+ contentType.toLower();
+ mimeString = MimeTypeUtil::convertMimeType(contentType);
+ }
+
+ FwdLockFile_close(fileDesc);
+ }
+
+ return mimeString;
+}
+
+int FwdLockEngine::onGetDrmObjectType(int uniqueId,
+ const String8& path,
+ const String8& mimeType) {
+ String8 mimeStr = String8(mimeType);
+
+ LOGD("FwdLockEngine::onGetDrmObjectType");
+
+ mimeStr.toLower();
+
+ /* Checks whether
+ * 1. path and mime type both are not empty strings (meaning unavailable) else content is unknown
+ * 2. if one of them is empty string and if other is known then its a DRM Content Object.
+ * 3. if both of them are available, then both may be of known type
+ * (regardless of the relation between them to make it compatible with other DRM Engines)
+ */
+ if (((0 == path.length()) || onCanHandle(uniqueId, path)) &&
+ ((0 == mimeType.length()) || ((mimeStr == String8(FWDLOCK_MIMETYPE_FL)) ||
+ (mimeStr == String8(FWDLOCK_MIMETYPE_DM)))) && (mimeType != path) ) {
+ return DrmObjectType::CONTENT;
+ }
+
+ return DrmObjectType::UNKNOWN;
+}
+
+status_t FwdLockEngine::onRemoveRights(int uniqueId, const String8& path) {
+ // No Rights to remove
+ LOGD("FwdLockEngine::onRemoveRights");
+ return DRM_NO_ERROR;
+}
+
+status_t FwdLockEngine::onRemoveAllRights(int uniqueId) {
+ // No rights to remove
+ LOGD("FwdLockEngine::onRemoveAllRights");
+ return DRM_NO_ERROR;
+}
+
+#ifdef USE_64BIT_DRM_API
+status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+ int playbackStatus, int64_t position) {
+#else
+status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+ int playbackStatus, int position) {
+#endif
+ // Not used
+ LOGD("FwdLockEngine::onSetPlaybackStatus");
+ return DRM_NO_ERROR;
+}
+
+status_t FwdLockEngine::onOpenConvertSession(int uniqueId,
+ int convertId) {
+ status_t result = DRM_ERROR_UNKNOWN;
+ LOGD("FwdLockEngine::onOpenConvertSession");
+ if (!convertSessionMap.isCreated(convertId)) {
+ ConvertSession *newSession = new ConvertSession();
+ if (FwdLockConv_Status_OK ==
+ FwdLockConv_OpenSession(&(newSession->uniqueId), &(newSession->output))) {
+ convertSessionMap.addValue(convertId, newSession);
+ result = DRM_NO_ERROR;
+ } else {
+ LOGD("FwdLockEngine::onOpenConvertSession -- FwdLockConv_OpenSession failed.");
+ delete newSession;
+ }
+ }
+ return result;
+}
+
+DrmConvertedStatus* FwdLockEngine::onConvertData(int uniqueId,
+ int convertId,
+ const DrmBuffer* inputData) {
+ FwdLockConv_Status_t retStatus = FwdLockConv_Status_InvalidArgument;
+ DrmBuffer *convResult = new DrmBuffer(NULL, 0);
+ int offset = -1;
+
+ if (NULL != inputData && convertSessionMap.isCreated(convertId)) {
+ ConvertSession *convSession = convertSessionMap.getValue(convertId);
+
+ if (NULL != convSession) {
+ retStatus = FwdLockConv_ConvertData(convSession->uniqueId,
+ inputData->data,
+ inputData->length,
+ &(convSession->output));
+
+ if (FwdLockConv_Status_OK == retStatus) {
+ // return bytes from conversion if available
+ if (convSession->output.fromConvertData.numBytes > 0) {
+ convResult->data = new char[convSession->output.fromConvertData.numBytes];
+
+ if (NULL != convResult->data) {
+ convResult->length = convSession->output.fromConvertData.numBytes;
+ memcpy(convResult->data,
+ (char *)convSession->output.fromConvertData.pBuffer,
+ convResult->length);
+ }
+ }
+ } else {
+ offset = convSession->output.fromConvertData.errorPos;
+ }
+ }
+ }
+ return new DrmConvertedStatus(getConvertedStatus(retStatus), convResult, offset);
+}
+
+DrmConvertedStatus* FwdLockEngine::onCloseConvertSession(int uniqueId,
+ int convertId) {
+ FwdLockConv_Status_t retStatus = FwdLockConv_Status_InvalidArgument;
+ DrmBuffer *convResult = new DrmBuffer(NULL, 0);
+ int offset = -1;
+
+ LOGD("FwdLockEngine::onCloseConvertSession");
+
+ if (convertSessionMap.isCreated(convertId)) {
+ ConvertSession *convSession = convertSessionMap.getValue(convertId);
+
+ if (NULL != convSession) {
+ retStatus = FwdLockConv_CloseSession(convSession->uniqueId, &(convSession->output));
+
+ if (FwdLockConv_Status_OK == retStatus) {
+ offset = convSession->output.fromCloseSession.fileOffset;
+ convResult->data = new char[FWD_LOCK_SIGNATURES_SIZE];
+
+ if (NULL != convResult->data) {
+ convResult->length = FWD_LOCK_SIGNATURES_SIZE;
+ memcpy(convResult->data,
+ (char *)convSession->output.fromCloseSession.signatures,
+ convResult->length);
+ }
+ }
+ }
+ convertSessionMap.removeValue(convertId);
+ }
+ return new DrmConvertedStatus(getConvertedStatus(retStatus), convResult, offset);
+}
+
+#ifdef USE_64BIT_DRM_API
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int fd,
+ off64_t offset,
+ off64_t length) {
+#else
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int fd,
+ int offset,
+ int length) {
+#endif
+ status_t result = DRM_ERROR_CANNOT_HANDLE;
+ int fileDesc = -1;
+
+ LOGD("FwdLockEngine::onOpenDecryptSession");
+
+ if ((-1 < fd) &&
+ (NULL != decryptHandle) &&
+ (!decodeSessionMap.isCreated(decryptHandle->decryptId))) {
+ fileDesc = dup(fd);
+ } else {
+ LOGD("FwdLockEngine::onOpenDecryptSession parameter error");
+ return result;
+ }
+
+ if (-1 < fileDesc &&
+ -1 < ::lseek(fileDesc, offset, SEEK_SET) &&
+ -1 < FwdLockFile_attach(fileDesc)) {
+ // check for file integrity. This must be done to protect the content mangling.
+ int retVal = FwdLockFile_CheckHeaderIntegrity(fileDesc);
+ DecodeSession* decodeSession = new DecodeSession(fileDesc);
+
+ if (retVal && NULL != decodeSession) {
+ decodeSessionMap.addValue(decryptHandle->decryptId, decodeSession);
+ const char *pmime= FwdLockFile_GetContentType(fileDesc);
+ String8 contentType = String8(pmime == NULL ? "" : pmime);
+ contentType.toLower();
+ decryptHandle->mimeType = MimeTypeUtil::convertMimeType(contentType);
+ decryptHandle->decryptApiType = DecryptApiType::CONTAINER_BASED;
+ decryptHandle->status = RightsStatus::RIGHTS_VALID;
+ decryptHandle->decryptInfo = NULL;
+ result = DRM_NO_ERROR;
+ } else {
+ LOGD("FwdLockEngine::onOpenDecryptSession Integrity Check failed for the fd");
+ FwdLockFile_detach(fileDesc);
+ ::close(fileDesc);
+ delete decodeSession;
+ }
+ }
+
+ LOGD("FwdLockEngine::onOpenDecryptSession Exit. result = %d", result);
+
+ return result;
+}
+
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ const char* uri) {
+ status_t result = DRM_ERROR_CANNOT_HANDLE;
+ const char fileTag [] = "file://";
+
+ if (NULL != decryptHandle && NULL != uri && strlen(uri) > sizeof(fileTag)) {
+ String8 uriTag = String8(uri);
+ uriTag.toLower();
+
+ if (0 == strncmp(uriTag.string(), fileTag, sizeof(fileTag) - 1)) {
+ const char *filePath = strchr(uri + sizeof(fileTag) - 1, '/');
+ if (NULL != filePath && onCanHandle(uniqueId, String8(filePath))) {
+ int fd = open(filePath, O_RDONLY);
+
+ if (-1 < fd) {
+ // offset is always 0 and length is not used. so any positive size.
+ result = onOpenDecryptSession(uniqueId, decryptHandle, fd, 0, 1);
+
+ // fd is duplicated already if success. closing the file
+ close(fd);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+status_t FwdLockEngine::onCloseDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle) {
+ status_t result = DRM_ERROR_UNKNOWN;
+ LOGD("FwdLockEngine::onCloseDecryptSession");
+
+ if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
+ DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+ if (NULL != session && session->fileDesc > -1) {
+ FwdLockFile_detach(session->fileDesc);
+ ::close(session->fileDesc);
+ decodeSessionMap.removeValue(decryptHandle->decryptId);
+ result = DRM_NO_ERROR;
+ }
+ }
+
+ LOGD("FwdLockEngine::onCloseDecryptSession Exit");
+ return result;
+}
+
+status_t FwdLockEngine::onInitializeDecryptUnit(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId,
+ const DrmBuffer* headerInfo) {
+ LOGD("FwdLockEngine::onInitializeDecryptUnit");
+ return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+ LOGD("FwdLockEngine::onDecrypt");
+ return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onDecrypt(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId,
+ const DrmBuffer* encBuffer,
+ DrmBuffer** decBuffer) {
+ LOGD("FwdLockEngine::onDecrypt");
+ return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onFinalizeDecryptUnit(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId) {
+ LOGD("FwdLockEngine::onFinalizeDecryptUnit");
+ return DRM_ERROR_UNKNOWN;
+}
+
+ssize_t FwdLockEngine::onRead(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* buffer,
+ int numBytes) {
+ ssize_t size = -1;
+
+ if (NULL != decryptHandle &&
+ decodeSessionMap.isCreated(decryptHandle->decryptId) &&
+ NULL != buffer &&
+ numBytes > -1) {
+ DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+ if (NULL != session && session->fileDesc > -1) {
+ size = FwdLockFile_read(session->fileDesc, buffer, numBytes);
+
+ if (0 > size) {
+ session->offset = ((off_t)-1);
+ } else {
+ session->offset += size;
+ }
+ }
+ }
+
+ return size;
+}
+
+#ifdef USE_64BIT_DRM_API
+off64_t FwdLockEngine::onLseek(int uniqueId, DecryptHandle* decryptHandle,
+ off64_t offset, int whence) {
+#else
+off_t FwdLockEngine::onLseek(int uniqueId, DecryptHandle* decryptHandle,
+ off_t offset, int whence) {
+#endif
+ off_t offval = -1;
+
+ if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
+ DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+ if (NULL != session && session->fileDesc > -1) {
+ offval = FwdLockFile_lseek(session->fileDesc, offset, whence);
+ session->offset = offval;
+ }
+ }
+
+ return offval;
+}
+
+#ifdef USE_64BIT_DRM_API
+ssize_t FwdLockEngine::onPread(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* buffer,
+ ssize_t numBytes,
+ off64_t offset) {
+#else
+ssize_t FwdLockEngine::onPread(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* buffer,
+ ssize_t numBytes,
+ off_t offset) {
+#endif
+ ssize_t bytesRead = -1;
+
+ DecodeSession* decoderSession = NULL;
+
+ if ((NULL != decryptHandle) &&
+ (NULL != (decoderSession = decodeSessionMap.getValue(decryptHandle->decryptId))) &&
+ (NULL != buffer) &&
+ (numBytes > -1) &&
+ (offset > -1)) {
+ if (offset != decoderSession->offset) {
+ decoderSession->offset = onLseek(uniqueId, decryptHandle, offset, SEEK_SET);
+ }
+
+ if (((off_t)-1) != decoderSession->offset) {
+ bytesRead = onRead(uniqueId, decryptHandle, buffer, numBytes);
+ if (bytesRead < 0) {
+ LOGD("FwdLockEngine::onPread error reading");
+ }
+ }
+ } else {
+ LOGD("FwdLockEngine::onPread decryptId not found");
+ }
+
+ return bytesRead;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2010 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 $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk
new file mode 100644
index 0000000..6c5d3cf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2010 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ FwdLockGlue.c
+
+LOCAL_C_INCLUDES := \
+ external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_MODULE := libfwdlock-common
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c
new file mode 100644
index 0000000..92bda8f
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2010 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define KEY_SIZE 16
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+static int isInitialized = FALSE;
+
+static const char strKeyFilename[] = "/data/drm/fwdlock/kek.dat";
+
+static AES_KEY encryptionRoundKeys;
+static AES_KEY decryptionRoundKeys;
+
+/**
+ * Creates all directories along the fully qualified path of the given file.
+ *
+ * @param[in] path A reference to the fully qualified path of a file.
+ * @param[in] mode The access mode to use for the directories being created.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+static int FwdLockGlue_CreateDirectories(const char *path, mode_t mode) {
+ int result = TRUE;
+ size_t partialPathLength = strlen(path);
+ char *partialPath = malloc(partialPathLength + 1);
+ if (partialPath == NULL) {
+ result = FALSE;
+ } else {
+ size_t i;
+ for (i = 0; i < partialPathLength; ++i) {
+ if (path[i] == '/' && i > 0) {
+ partialPath[i] = '\0';
+ if (mkdir(partialPath, mode) != 0 && errno != EEXIST) {
+ result = FALSE;
+ break;
+ }
+ }
+ partialPath[i] = path[i];
+ }
+ free(partialPath);
+ }
+ return result;
+}
+
+/**
+ * Initializes the round keys used for encryption and decryption of session keys. First creates a
+ * device-unique key-encryption key if none exists yet.
+ */
+static void FwdLockGlue_InitializeRoundKeys() {
+ unsigned char keyEncryptionKey[KEY_SIZE];
+ int fileDesc = open(strKeyFilename, O_RDONLY);
+ if (fileDesc >= 0) {
+ if (read(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
+ isInitialized = TRUE;
+ }
+ (void)close(fileDesc);
+ } else if (errno == ENOENT &&
+ FwdLockGlue_GetRandomNumber(keyEncryptionKey, KEY_SIZE) &&
+ FwdLockGlue_CreateDirectories(strKeyFilename, S_IRWXU)) {
+ fileDesc = open(strKeyFilename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR);
+ if (fileDesc >= 0) {
+ if (write(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
+ isInitialized = TRUE;
+ }
+ (void)close(fileDesc);
+ }
+ }
+ if (isInitialized) {
+ if (AES_set_encrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &encryptionRoundKeys) != 0 ||
+ AES_set_decrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &decryptionRoundKeys) != 0) {
+ isInitialized = FALSE;
+ }
+ }
+ memset(keyEncryptionKey, 0, KEY_SIZE); // Zero out key data.
+}
+
+/**
+ * Validates the padding of a decrypted key.
+ *
+ * @param[in] pData A reference to the buffer containing the decrypted key and padding.
+ * @param[in] decryptedKeyLength The length in bytes of the decrypted key.
+ *
+ * @return A Boolean value indicating whether the padding was valid.
+ */
+static int FwdLockGlue_ValidatePadding(const unsigned char *pData, size_t decryptedKeyLength) {
+ size_t i;
+ size_t padding = AES_BLOCK_SIZE - (decryptedKeyLength % AES_BLOCK_SIZE);
+ pData += decryptedKeyLength;
+ for (i = 0; i < padding; ++i) {
+ if ((size_t)*pData != padding) {
+ return FALSE;
+ }
+ ++pData;
+ }
+ return TRUE;
+}
+
+int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes) {
+ // Generate 'cryptographically secure' random bytes by reading them from "/dev/urandom" (the
+ // non-blocking version of "/dev/random").
+ ssize_t numBytesRead = 0;
+ int fileDesc = open("/dev/urandom", O_RDONLY);
+ if (fileDesc >= 0) {
+ numBytesRead = read(fileDesc, pBuffer, numBytes);
+ (void)close(fileDesc);
+ }
+ return numBytesRead >= 0 && (size_t)numBytesRead == numBytes;
+}
+
+int FwdLockGlue_InitializeKeyEncryption() {
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, FwdLockGlue_InitializeRoundKeys);
+ return isInitialized;
+}
+
+size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength) {
+ return ((plaintextKeyLength / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE;
+}
+
+int FwdLockGlue_EncryptKey(const void *pPlaintextKey,
+ size_t plaintextKeyLength,
+ void *pEncryptedKey,
+ size_t encryptedKeyLength) {
+ int result = FALSE;
+ assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(plaintextKeyLength));
+ if (FwdLockGlue_InitializeKeyEncryption()) {
+ unsigned char initVector[AES_BLOCK_SIZE];
+ if (FwdLockGlue_GetRandomNumber(initVector, AES_BLOCK_SIZE)) {
+ size_t padding = AES_BLOCK_SIZE - (plaintextKeyLength % AES_BLOCK_SIZE);
+ size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
+ memcpy(pEncryptedKey, pPlaintextKey, plaintextKeyLength);
+ memset((unsigned char *)pEncryptedKey + plaintextKeyLength, (int)padding, padding);
+ memcpy((unsigned char *)pEncryptedKey + dataLength, initVector, AES_BLOCK_SIZE);
+ AES_cbc_encrypt(pEncryptedKey, pEncryptedKey, dataLength, &encryptionRoundKeys,
+ initVector, AES_ENCRYPT);
+ result = TRUE;
+ }
+ }
+ return result;
+}
+
+int FwdLockGlue_DecryptKey(const void *pEncryptedKey,
+ size_t encryptedKeyLength,
+ void *pDecryptedKey,
+ size_t decryptedKeyLength) {
+ int result = FALSE;
+ assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(decryptedKeyLength));
+ if (FwdLockGlue_InitializeKeyEncryption()) {
+ size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
+ unsigned char *pData = malloc(dataLength);
+ if (pData != NULL) {
+ unsigned char initVector[AES_BLOCK_SIZE];
+ memcpy(pData, pEncryptedKey, dataLength);
+ memcpy(initVector, (const unsigned char *)pEncryptedKey + dataLength, AES_BLOCK_SIZE);
+ AES_cbc_encrypt(pData, pData, dataLength, &decryptionRoundKeys, initVector,
+ AES_DECRYPT);
+ memcpy(pDecryptedKey, pData, decryptedKeyLength);
+ result = FwdLockGlue_ValidatePadding(pData, decryptedKeyLength);
+ free(pData);
+ }
+ }
+ return result;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h
new file mode 100644
index 0000000..f36f6ea
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 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 __FWDLOCKGLUE_H__
+#define __FWDLOCKGLUE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Generates the specified number of cryptographically secure random bytes.
+ *
+ * @param[out] pBuffer A reference to the buffer that should receive the random data.
+ * @param[in] numBytes The number of random bytes to generate.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes);
+
+/**
+ * Performs initialization of the key-encryption key. Should be called once during startup to
+ * facilitate encryption and decryption of session keys.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_InitializeKeyEncryption();
+
+/**
+ * Returns the length of the encrypted key, given the length of the plaintext key.
+ *
+ * @param[in] plaintextKeyLength The length in bytes of the plaintext key.
+ *
+ * @return The length in bytes of the encrypted key.
+ */
+size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength);
+
+/**
+ * Encrypts the given session key using a key-encryption key unique to this device.
+ *
+ * @param[in] pPlaintextKey A reference to the buffer containing the plaintext key.
+ * @param[in] plaintextKeyLength The length in bytes of the plaintext key.
+ * @param[out] pEncryptedKey A reference to the buffer containing the encrypted key.
+ * @param[in] encryptedKeyLength The length in bytes of the encrypted key.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_EncryptKey(const void *pPlaintextKey,
+ size_t plaintextKeyLength,
+ void *pEncryptedKey,
+ size_t encryptedKeyLength);
+
+/**
+ * Decrypts the given session key using a key-encryption key unique to this device.
+ *
+ * @param[in] pEncryptedKey A reference to the buffer containing the encrypted key.
+ * @param[in] encryptedKeyLength The length in bytes of the encrypted key.
+ * @param[out] pDecryptedKey A reference to the buffer containing the decrypted key.
+ * @param[in] decryptedKeyLength The length in bytes of the decrypted key.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_DecryptKey(const void *pEncryptedKey,
+ size_t encryptedKeyLength,
+ void *pDecryptedKey,
+ size_t decryptedKeyLength);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKGLUE_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
new file mode 100644
index 0000000..00bb788
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2010 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ FwdLockConv.c
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+ external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_MODULE := libfwdlock-converter
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
new file mode 100644
index 0000000..14ea9e9
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
@@ -0,0 +1,1339 @@
+/*
+ * Copyright (C) 2010 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 <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+#include <openssl/hmac.h>
+
+#include "FwdLockConv.h"
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define INVALID_OFFSET ((off64_t)-1)
+
+#define MAX_NUM_SESSIONS 32
+
+#define OUTPUT_BUFFER_SIZE_INCREMENT 1024
+#define READ_BUFFER_SIZE 1024
+
+#define MAX_BOUNDARY_LENGTH 70
+#define MAX_DELIMITER_LENGTH (MAX_BOUNDARY_LENGTH + 4)
+
+#define STRING_LENGTH_INCREMENT 25
+
+#define KEY_SIZE AES_BLOCK_SIZE
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+#define SHA1_HASH_SIZE 20
+
+#define FWD_LOCK_VERSION 0
+#define FWD_LOCK_SUBFORMAT 0
+#define USAGE_RESTRICTION_FLAGS 0
+#define CONTENT_TYPE_LENGTH_POS 7
+#define TOP_HEADER_SIZE 8
+
+/**
+ * Data type for the parser states of the converter.
+ */
+typedef enum FwdLockConv_ParserState {
+ FwdLockConv_ParserState_WantsOpenDelimiter,
+ FwdLockConv_ParserState_WantsMimeHeaders,
+ FwdLockConv_ParserState_WantsBinaryEncodedData,
+ FwdLockConv_ParserState_WantsBase64EncodedData,
+ FwdLockConv_ParserState_Done
+} FwdLockConv_ParserState_t;
+
+/**
+ * Data type for the scanner states of the converter.
+ */
+typedef enum FwdLockConv_ScannerState {
+ FwdLockConv_ScannerState_WantsFirstDash,
+ FwdLockConv_ScannerState_WantsSecondDash,
+ FwdLockConv_ScannerState_WantsCR,
+ FwdLockConv_ScannerState_WantsLF,
+ FwdLockConv_ScannerState_WantsBoundary,
+ FwdLockConv_ScannerState_WantsBoundaryEnd,
+ FwdLockConv_ScannerState_WantsMimeHeaderNameStart,
+ FwdLockConv_ScannerState_WantsMimeHeaderName,
+ FwdLockConv_ScannerState_WantsMimeHeaderNameEnd,
+ FwdLockConv_ScannerState_WantsContentTypeStart,
+ FwdLockConv_ScannerState_WantsContentType,
+ FwdLockConv_ScannerState_WantsContentTransferEncodingStart,
+ FwdLockConv_ScannerState_Wants_A_OR_I,
+ FwdLockConv_ScannerState_Wants_N,
+ FwdLockConv_ScannerState_Wants_A,
+ FwdLockConv_ScannerState_Wants_R,
+ FwdLockConv_ScannerState_Wants_Y,
+ FwdLockConv_ScannerState_Wants_S,
+ FwdLockConv_ScannerState_Wants_E,
+ FwdLockConv_ScannerState_Wants_6,
+ FwdLockConv_ScannerState_Wants_4,
+ FwdLockConv_ScannerState_Wants_B,
+ FwdLockConv_ScannerState_Wants_I,
+ FwdLockConv_ScannerState_Wants_T,
+ FwdLockConv_ScannerState_WantsContentTransferEncodingEnd,
+ FwdLockConv_ScannerState_WantsMimeHeaderValueEnd,
+ FwdLockConv_ScannerState_WantsMimeHeadersEnd,
+ FwdLockConv_ScannerState_WantsByte1,
+ FwdLockConv_ScannerState_WantsByte1_AfterCRLF,
+ FwdLockConv_ScannerState_WantsByte2,
+ FwdLockConv_ScannerState_WantsByte3,
+ FwdLockConv_ScannerState_WantsByte4,
+ FwdLockConv_ScannerState_WantsPadding,
+ FwdLockConv_ScannerState_WantsWhitespace,
+ FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF,
+ FwdLockConv_ScannerState_WantsDelimiter
+} FwdLockConv_ScannerState_t;
+
+/**
+ * Data type for the content transfer encoding.
+ */
+typedef enum FwdLockConv_ContentTransferEncoding {
+ FwdLockConv_ContentTransferEncoding_Undefined,
+ FwdLockConv_ContentTransferEncoding_Binary,
+ FwdLockConv_ContentTransferEncoding_Base64
+} FwdLockConv_ContentTransferEncoding_t;
+
+/**
+ * Data type for a dynamically growing string.
+ */
+typedef struct FwdLockConv_String {
+ char *ptr;
+ size_t length;
+ size_t maxLength;
+ size_t lengthIncrement;
+} FwdLockConv_String_t;
+
+/**
+ * Data type for the per-file state information needed by the converter.
+ */
+typedef struct FwdLockConv_Session {
+ FwdLockConv_ParserState_t parserState;
+ FwdLockConv_ScannerState_t scannerState;
+ FwdLockConv_ScannerState_t savedScannerState;
+ off64_t numCharsConsumed;
+ char delimiter[MAX_DELIMITER_LENGTH];
+ size_t delimiterLength;
+ size_t delimiterMatchPos;
+ FwdLockConv_String_t mimeHeaderName;
+ FwdLockConv_String_t contentType;
+ FwdLockConv_ContentTransferEncoding_t contentTransferEncoding;
+ unsigned char sessionKey[KEY_SIZE];
+ void *pEncryptedSessionKey;
+ size_t encryptedSessionKeyLength;
+ AES_KEY encryptionRoundKeys;
+ HMAC_CTX signingContext;
+ unsigned char topHeader[TOP_HEADER_SIZE];
+ unsigned char counter[AES_BLOCK_SIZE];
+ unsigned char keyStream[AES_BLOCK_SIZE];
+ int keyStreamIndex;
+ unsigned char ch;
+ size_t outputBufferSize;
+ size_t dataOffset;
+ size_t numDataBytes;
+} FwdLockConv_Session_t;
+
+static FwdLockConv_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL };
+
+static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static const FwdLockConv_String_t nullString = { NULL, 0, 0, STRING_LENGTH_INCREMENT };
+
+static const unsigned char topHeaderTemplate[] =
+ { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS };
+
+static const char strContent[] = "content-";
+static const char strType[] = "type";
+static const char strTransferEncoding[] = "transfer-encoding";
+static const char strTextPlain[] = "text/plain";
+static const char strApplicationVndOmaDrmRightsXml[] = "application/vnd.oma.drm.rights+xml";
+static const char strApplicationVndOmaDrmContent[] = "application/vnd.oma.drm.content";
+
+static const size_t strlenContent = sizeof strContent - 1;
+static const size_t strlenTextPlain = sizeof strTextPlain - 1;
+
+static const signed char base64Values[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+/**
+ * Acquires an unused converter session.
+ *
+ * @return A session ID.
+ */
+static int FwdLockConv_AcquireSession() {
+ int sessionId = -1;
+ int i;
+ pthread_mutex_lock(&sessionAcquisitionMutex);
+ for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+ if (sessionPtrs[i] == NULL) {
+ sessionPtrs[i] = malloc(sizeof *sessionPtrs[i]);
+ if (sessionPtrs[i] != NULL) {
+ sessionId = i;
+ }
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sessionAcquisitionMutex);
+ return sessionId;
+}
+
+/**
+ * Checks whether a session ID is in range and currently in use.
+ *
+ * @param[in] sessionID A session ID.
+ *
+ * @return A Boolean value indicating whether the session ID is in range and currently in use.
+ */
+static int FwdLockConv_IsValidSession(int sessionId) {
+ return 0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL;
+}
+
+/**
+ * Releases a converter session.
+ *
+ * @param[in] sessionID A session ID.
+ */
+static void FwdLockConv_ReleaseSession(int sessionId) {
+ pthread_mutex_lock(&sessionAcquisitionMutex);
+ assert(FwdLockConv_IsValidSession(sessionId));
+ memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data.
+ free(sessionPtrs[sessionId]);
+ sessionPtrs[sessionId] = NULL;
+ pthread_mutex_unlock(&sessionAcquisitionMutex);
+}
+
+/**
+ * Derives cryptographically independent keys for encryption and signing from the session key.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static int FwdLockConv_DeriveKeys(FwdLockConv_Session_t *pSession) {
+ FwdLockConv_Status_t status;
+ struct FwdLockConv_DeriveKeys_Data {
+ AES_KEY sessionRoundKeys;
+ unsigned char value[KEY_SIZE];
+ unsigned char key[KEY_SIZE];
+ } *pData = malloc(sizeof *pData);
+ if (pData == NULL) {
+ status = FwdLockConv_Status_OutOfMemory;
+ } else {
+ if (AES_set_encrypt_key(pSession->sessionKey, KEY_SIZE_IN_BITS,
+ &pData->sessionRoundKeys) != 0) {
+ status = FwdLockConv_Status_ProgramError;
+ } else {
+ // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key.
+ memset(pData->value, 0, KEY_SIZE);
+ AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+ if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS,
+ &pSession->encryptionRoundKeys) != 0) {
+ status = FwdLockConv_Status_ProgramError;
+ } else {
+ // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key.
+ ++pData->value[0];
+ AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+ HMAC_CTX_init(&pSession->signingContext);
+ HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL);
+ status = FwdLockConv_Status_OK;
+ }
+ }
+ memset(pData, 0, sizeof pData); // Zero out key data.
+ free(pData);
+ }
+ return status;
+}
+
+/**
+ * Checks whether a given character is valid in a boundary. Note that the boundary may contain
+ * leading and internal spaces.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a boundary.
+ */
+static int FwdLockConv_IsBoundaryChar(int ch) {
+ return isalnum(ch) || ch == '\'' ||
+ ch == '(' || ch == ')' || ch == '+' || ch == '_' || ch == ',' || ch == '-' ||
+ ch == '.' || ch == '/' || ch == ':' || ch == '=' || ch == '?' || ch == ' ';
+}
+
+/**
+ * Checks whether a given character should be considered whitespace, using a narrower definition
+ * than the standard-library isspace() function.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character should be considered whitespace.
+ */
+static int FwdLockConv_IsWhitespace(int ch) {
+ return ch == ' ' || ch == '\t';
+}
+
+/**
+ * Removes trailing spaces from the delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_RightTrimDelimiter(FwdLockConv_Session_t *pSession) {
+ while (pSession->delimiterLength > 4 &&
+ pSession->delimiter[pSession->delimiterLength - 1] == ' ') {
+ --pSession->delimiterLength;
+ }
+ if (pSession->delimiterLength > 4) {
+ return FwdLockConv_Status_OK;
+ }
+ return FwdLockConv_Status_SyntaxError;
+}
+
+/**
+ * Matches the open delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchOpenDelimiter(FwdLockConv_Session_t *pSession,
+ int ch) {
+ FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+ switch (pSession->scannerState) {
+ case FwdLockConv_ScannerState_WantsFirstDash:
+ if (ch == '-') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsSecondDash:
+ if (ch == '-') {
+ // The delimiter starts with "\r\n--" (the open delimiter may omit the initial "\r\n").
+ // The rest is the user-defined boundary that should come next.
+ pSession->delimiter[0] = '\r';
+ pSession->delimiter[1] = '\n';
+ pSession->delimiter[2] = '-';
+ pSession->delimiter[3] = '-';
+ pSession->delimiterLength = 4;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsBoundary;
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsCR:
+ if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsLF:
+ if (ch == '\n') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+ } else if (ch != '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsBoundary:
+ if (FwdLockConv_IsBoundaryChar(ch)) {
+ // The boundary may contain leading and internal spaces, so trailing spaces will also be
+ // matched here. These will be removed later.
+ if (pSession->delimiterLength < MAX_DELIMITER_LENGTH) {
+ pSession->delimiter[pSession->delimiterLength++] = ch;
+ } else if (ch != ' ') {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ } else if (ch == '\r') {
+ status = FwdLockConv_RightTrimDelimiter(pSession);
+ if (status == FwdLockConv_Status_OK) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd;
+ }
+ } else if (ch == '\t') {
+ status = FwdLockConv_RightTrimDelimiter(pSession);
+ if (status == FwdLockConv_Status_OK) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+ }
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsWhitespace:
+ if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsBoundaryEnd:
+ if (ch == '\n') {
+ pSession->parserState = FwdLockConv_ParserState_WantsMimeHeaders;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ default:
+ status = FwdLockConv_Status_ProgramError;
+ break;
+ }
+ return status;
+}
+
+/**
+ * Checks whether a given character is valid in a MIME header name.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a MIME header name.
+ */
+static int FwdLockConv_IsMimeHeaderNameChar(int ch) {
+ return isgraph(ch) && ch != ':';
+}
+
+/**
+ * Checks whether a given character is valid in a MIME header value.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a MIME header value.
+ */
+static int FwdLockConv_IsMimeHeaderValueChar(int ch) {
+ return isgraph(ch) && ch != ';';
+}
+
+/**
+ * Appends a character to the specified dynamically growing string.
+ *
+ * @param[in,out] pString A reference to a dynamically growing string.
+ * @param[in] ch The character to append.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_StringAppend(FwdLockConv_String_t *pString, int ch) {
+ if (pString->length == pString->maxLength) {
+ size_t newMaxLength = pString->maxLength + pString->lengthIncrement;
+ char *newPtr = realloc(pString->ptr, newMaxLength + 1);
+ if (newPtr == NULL) {
+ return FwdLockConv_Status_OutOfMemory;
+ }
+ pString->ptr = newPtr;
+ pString->maxLength = newMaxLength;
+ }
+ pString->ptr[pString->length++] = ch;
+ pString->ptr[pString->length] = '\0';
+ return FwdLockConv_Status_OK;
+}
+
+/**
+ * Attempts to recognize the MIME header name and changes the scanner state accordingly.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_RecognizeMimeHeaderName(FwdLockConv_Session_t *pSession) {
+ FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+ if (strncmp(pSession->mimeHeaderName.ptr, strContent, strlenContent) == 0) {
+ if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strType) == 0) {
+ if (pSession->contentType.ptr == NULL) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentTypeStart;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ } else if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strTransferEncoding) == 0) {
+ if (pSession->contentTransferEncoding ==
+ FwdLockConv_ContentTransferEncoding_Undefined) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingStart;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ } else {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ }
+ } else {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ }
+ return status;
+}
+
+/**
+ * Applies defaults to missing MIME header values.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_ApplyDefaults(FwdLockConv_Session_t *pSession) {
+ if (pSession->contentType.ptr == NULL) {
+ // Content type is missing: default to "text/plain".
+ pSession->contentType.ptr = malloc(sizeof strTextPlain);
+ if (pSession->contentType.ptr == NULL) {
+ return FwdLockConv_Status_OutOfMemory;
+ }
+ memcpy(pSession->contentType.ptr, strTextPlain, sizeof strTextPlain);
+ pSession->contentType.length = strlenTextPlain;
+ pSession->contentType.maxLength = strlenTextPlain;
+ }
+ if (pSession->contentTransferEncoding == FwdLockConv_ContentTransferEncoding_Undefined) {
+ // Content transfer encoding is missing: default to binary.
+ pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+ }
+ return FwdLockConv_Status_OK;
+}
+
+/**
+ * Verifies that the content type is supported.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_VerifyContentType(FwdLockConv_Session_t *pSession) {
+ FwdLockConv_Status_t status;
+ if (pSession->contentType.ptr == NULL) {
+ status = FwdLockConv_Status_ProgramError;
+ } else if (strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmRightsXml) == 0 ||
+ strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmContent) == 0) {
+ status = FwdLockConv_Status_UnsupportedFileFormat;
+ } else {
+ status = FwdLockConv_Status_OK;
+ }
+ return status;
+}
+
+/**
+ * Writes the header of the output file.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_WriteHeader(FwdLockConv_Session_t *pSession,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status;
+ if (pSession->contentType.length > UCHAR_MAX) {
+ status = FwdLockConv_Status_SyntaxError;
+ } else {
+ pSession->outputBufferSize = OUTPUT_BUFFER_SIZE_INCREMENT;
+ pOutput->fromConvertData.pBuffer = malloc(pSession->outputBufferSize);
+ if (pOutput->fromConvertData.pBuffer == NULL) {
+ status = FwdLockConv_Status_OutOfMemory;
+ } else {
+ size_t encryptedSessionKeyPos = TOP_HEADER_SIZE + pSession->contentType.length;
+ size_t dataSignaturePos = encryptedSessionKeyPos + pSession->encryptedSessionKeyLength;
+ size_t headerSignaturePos = dataSignaturePos + SHA1_HASH_SIZE;
+ pSession->dataOffset = headerSignaturePos + SHA1_HASH_SIZE;
+ memcpy(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate);
+ pSession->topHeader[CONTENT_TYPE_LENGTH_POS] =
+ (unsigned char)pSession->contentType.length;
+ memcpy(pOutput->fromConvertData.pBuffer, pSession->topHeader, TOP_HEADER_SIZE);
+ memcpy((char *)pOutput->fromConvertData.pBuffer + TOP_HEADER_SIZE,
+ pSession->contentType.ptr, pSession->contentType.length);
+ memcpy((char *)pOutput->fromConvertData.pBuffer + encryptedSessionKeyPos,
+ pSession->pEncryptedSessionKey, pSession->encryptedSessionKeyLength);
+
+ // Set the signatures to all zeros for now; they will have to be updated later.
+ memset((char *)pOutput->fromConvertData.pBuffer + dataSignaturePos, 0,
+ SHA1_HASH_SIZE);
+ memset((char *)pOutput->fromConvertData.pBuffer + headerSignaturePos, 0,
+ SHA1_HASH_SIZE);
+
+ pOutput->fromConvertData.numBytes = pSession->dataOffset;
+ status = FwdLockConv_Status_OK;
+ }
+ }
+ return status;
+}
+
+/**
+ * Matches the MIME headers.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchMimeHeaders(FwdLockConv_Session_t *pSession,
+ int ch,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+ switch (pSession->scannerState) {
+ case FwdLockConv_ScannerState_WantsMimeHeaderNameStart:
+ if (FwdLockConv_IsMimeHeaderNameChar(ch)) {
+ pSession->mimeHeaderName.length = 0;
+ status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch));
+ if (status == FwdLockConv_Status_OK) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderName;
+ }
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeadersEnd;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsMimeHeaderName:
+ if (FwdLockConv_IsMimeHeaderNameChar(ch)) {
+ status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch));
+ } else if (ch == ':') {
+ status = FwdLockConv_RecognizeMimeHeaderName(pSession);
+ } else if (FwdLockConv_IsWhitespace(ch)) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameEnd;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsMimeHeaderNameEnd:
+ if (ch == ':') {
+ status = FwdLockConv_RecognizeMimeHeaderName(pSession);
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsContentTypeStart:
+ if (FwdLockConv_IsMimeHeaderValueChar(ch)) {
+ status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch));
+ if (status == FwdLockConv_Status_OK) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentType;
+ }
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsContentType:
+ if (FwdLockConv_IsMimeHeaderValueChar(ch)) {
+ status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch));
+ } else if (ch == ';') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (FwdLockConv_IsWhitespace(ch)) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsContentTransferEncodingStart:
+ if (ch == 'b' || ch == 'B') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_A_OR_I;
+ } else if (ch == '7' || ch == '8') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_B;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_A_OR_I:
+ if (ch == 'i' || ch == 'I') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_N;
+ } else if (ch == 'a' || ch == 'A') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_S;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_N:
+ if (ch == 'n' || ch == 'N') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_A;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_A:
+ if (ch == 'a' || ch == 'A') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_R;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_R:
+ if (ch == 'r' || ch == 'R') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_Y;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_Y:
+ if (ch == 'y' || ch == 'Y') {
+ pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_S:
+ if (ch == 's' || ch == 'S') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_E;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_E:
+ if (ch == 'e' || ch == 'E') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_6;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_6:
+ if (ch == '6') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_4;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_4:
+ if (ch == '4') {
+ pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Base64;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_B:
+ if (ch == 'b' || ch == 'B') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_I;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_I:
+ if (ch == 'i' || ch == 'I') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_T;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_T:
+ if (ch == 't' || ch == 'T') {
+ pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsContentTransferEncodingEnd:
+ if (ch == ';') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (FwdLockConv_IsWhitespace(ch)) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsMimeHeaderValueEnd:
+ if (ch == ';') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsCR:
+ if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsLF:
+ if (ch == '\n') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsMimeHeadersEnd:
+ if (ch == '\n') {
+ status = FwdLockConv_ApplyDefaults(pSession);
+ if (status == FwdLockConv_Status_OK) {
+ status = FwdLockConv_VerifyContentType(pSession);
+ }
+ if (status == FwdLockConv_Status_OK) {
+ status = FwdLockConv_WriteHeader(pSession, pOutput);
+ }
+ if (status == FwdLockConv_Status_OK) {
+ if (pSession->contentTransferEncoding ==
+ FwdLockConv_ContentTransferEncoding_Binary) {
+ pSession->parserState = FwdLockConv_ParserState_WantsBinaryEncodedData;
+ } else {
+ pSession->parserState = FwdLockConv_ParserState_WantsBase64EncodedData;
+ }
+ pSession->scannerState = FwdLockConv_ScannerState_WantsByte1;
+ }
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ default:
+ status = FwdLockConv_Status_ProgramError;
+ break;
+ }
+ return status;
+}
+
+/**
+ * Increments the counter, treated as a 16-byte little-endian number, by one.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ */
+static void FwdLockConv_IncrementCounter(FwdLockConv_Session_t *pSession) {
+ size_t i = 0;
+ while ((++pSession->counter[i] == 0) && (++i < AES_BLOCK_SIZE))
+ ;
+}
+
+/**
+ * Encrypts the given character and writes it to the output buffer.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch The character to encrypt and write.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_WriteEncryptedChar(FwdLockConv_Session_t *pSession,
+ unsigned char ch,
+ FwdLockConv_Output_t *pOutput) {
+ if (pOutput->fromConvertData.numBytes == pSession->outputBufferSize) {
+ void *pBuffer;
+ pSession->outputBufferSize += OUTPUT_BUFFER_SIZE_INCREMENT;
+ pBuffer = realloc(pOutput->fromConvertData.pBuffer, pSession->outputBufferSize);
+ if (pBuffer == NULL) {
+ return FwdLockConv_Status_OutOfMemory;
+ }
+ pOutput->fromConvertData.pBuffer = pBuffer;
+ }
+ if (++pSession->keyStreamIndex == AES_BLOCK_SIZE) {
+ FwdLockConv_IncrementCounter(pSession);
+ pSession->keyStreamIndex = 0;
+ }
+ if (pSession->keyStreamIndex == 0) {
+ AES_encrypt(pSession->counter, pSession->keyStream, &pSession->encryptionRoundKeys);
+ }
+ ch ^= pSession->keyStream[pSession->keyStreamIndex];
+ ((unsigned char *)pOutput->fromConvertData.pBuffer)[pOutput->fromConvertData.numBytes++] = ch;
+ ++pSession->numDataBytes;
+ return FwdLockConv_Status_OK;
+}
+
+/**
+ * Matches binary-encoded content data and encrypts it, while looking out for the close delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchBinaryEncodedData(FwdLockConv_Session_t *pSession,
+ int ch,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+ switch (pSession->scannerState) {
+ case FwdLockConv_ScannerState_WantsByte1:
+ if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+ // The partial match of the delimiter turned out to be spurious. Flush the matched bytes
+ // to the output buffer and start over.
+ size_t i;
+ for (i = 0; i < pSession->delimiterMatchPos; ++i) {
+ status = FwdLockConv_WriteEncryptedChar(pSession, pSession->delimiter[i], pOutput);
+ if (status != FwdLockConv_Status_OK) {
+ return status;
+ }
+ }
+ pSession->delimiterMatchPos = 0;
+ }
+ if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+ // The current character isn't part of the delimiter. Write it to the output buffer.
+ status = FwdLockConv_WriteEncryptedChar(pSession, ch, pOutput);
+ } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) {
+ // The entire delimiter has been matched. The only valid characters now are the "--"
+ // that complete the close delimiter (no more message parts are expected).
+ pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsFirstDash:
+ if (ch == '-') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsSecondDash:
+ if (ch == '-') {
+ pSession->parserState = FwdLockConv_ParserState_Done;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ default:
+ status = FwdLockConv_Status_ProgramError;
+ break;
+ }
+ return status;
+}
+
+/**
+ * Checks whether a given character is valid in base64-encoded data.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in base64-encoded data.
+ */
+static int FwdLockConv_IsBase64Char(int ch) {
+ return 0 <= ch && ch <= 'z' && base64Values[ch] >= 0;
+}
+
+/**
+ * Matches base64-encoded content data and encrypts it, while looking out for the close delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchBase64EncodedData(FwdLockConv_Session_t *pSession,
+ int ch,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+ switch (pSession->scannerState) {
+ case FwdLockConv_ScannerState_WantsByte1:
+ case FwdLockConv_ScannerState_WantsByte1_AfterCRLF:
+ if (FwdLockConv_IsBase64Char(ch)) {
+ pSession->ch = base64Values[ch] << 2;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsByte2;
+ } else if (ch == '\r') {
+ pSession->savedScannerState = FwdLockConv_ScannerState_WantsByte1_AfterCRLF;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (ch == '-') {
+ if (pSession->scannerState == FwdLockConv_ScannerState_WantsByte1_AfterCRLF) {
+ pSession->delimiterMatchPos = 3;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsByte2:
+ if (FwdLockConv_IsBase64Char(ch)) {
+ pSession->ch |= base64Values[ch] >> 4;
+ status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+ if (status == FwdLockConv_Status_OK) {
+ pSession->ch = base64Values[ch] << 4;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsByte3;
+ }
+ } else if (ch == '\r') {
+ pSession->savedScannerState = pSession->scannerState;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsByte3:
+ if (FwdLockConv_IsBase64Char(ch)) {
+ pSession->ch |= base64Values[ch] >> 2;
+ status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+ if (status == FwdLockConv_Status_OK) {
+ pSession->ch = base64Values[ch] << 6;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsByte4;
+ }
+ } else if (ch == '\r') {
+ pSession->savedScannerState = pSession->scannerState;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (ch == '=') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsPadding;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsByte4:
+ if (FwdLockConv_IsBase64Char(ch)) {
+ pSession->ch |= base64Values[ch];
+ status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+ if (status == FwdLockConv_Status_OK) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsByte1;
+ }
+ } else if (ch == '\r') {
+ pSession->savedScannerState = pSession->scannerState;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (ch == '=') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsLF:
+ if (ch == '\n') {
+ pSession->scannerState = pSession->savedScannerState;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsPadding:
+ if (ch == '=') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsWhitespace:
+ case FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF:
+ if (ch == '\r') {
+ pSession->savedScannerState = FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (ch == '-') {
+ if (pSession->scannerState == FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF) {
+ pSession->delimiterMatchPos = 3;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ } else if (FwdLockConv_IsWhitespace(ch)) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsDelimiter:
+ if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+ status = FwdLockConv_Status_SyntaxError;
+ } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsFirstDash:
+ if (ch == '-') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsSecondDash:
+ if (ch == '-') {
+ pSession->parserState = FwdLockConv_ParserState_Done;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ default:
+ status = FwdLockConv_Status_ProgramError;
+ break;
+ }
+ return status;
+}
+
+/**
+ * Pushes a single character into the converter's state machine.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_PushChar(FwdLockConv_Session_t *pSession,
+ int ch,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status;
+ ++pSession->numCharsConsumed;
+ switch (pSession->parserState) {
+ case FwdLockConv_ParserState_WantsOpenDelimiter:
+ status = FwdLockConv_MatchOpenDelimiter(pSession, ch);
+ break;
+ case FwdLockConv_ParserState_WantsMimeHeaders:
+ status = FwdLockConv_MatchMimeHeaders(pSession, ch, pOutput);
+ break;
+ case FwdLockConv_ParserState_WantsBinaryEncodedData:
+ status = FwdLockConv_MatchBinaryEncodedData(pSession, ch, pOutput);
+ break;
+ case FwdLockConv_ParserState_WantsBase64EncodedData:
+ status = FwdLockConv_MatchBase64EncodedData(pSession, ch, pOutput);
+ break;
+ case FwdLockConv_ParserState_Done:
+ status = FwdLockConv_Status_OK;
+ break;
+ default:
+ status = FwdLockConv_Status_ProgramError;
+ break;
+ }
+ return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status;
+ if (pSessionId == NULL || pOutput == NULL) {
+ status = FwdLockConv_Status_InvalidArgument;
+ } else {
+ *pSessionId = FwdLockConv_AcquireSession();
+ if (*pSessionId < 0) {
+ status = FwdLockConv_Status_TooManySessions;
+ } else {
+ FwdLockConv_Session_t *pSession = sessionPtrs[*pSessionId];
+ pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE);
+ if (pSession->encryptedSessionKeyLength < AES_BLOCK_SIZE) {
+ // The encrypted session key is used as the CTR-mode nonce, so it must be at least
+ // the size of a single AES block.
+ status = FwdLockConv_Status_ProgramError;
+ } else {
+ pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength);
+ if (pSession->pEncryptedSessionKey == NULL) {
+ status = FwdLockConv_Status_OutOfMemory;
+ } else {
+ if (!FwdLockGlue_GetRandomNumber(pSession->sessionKey, KEY_SIZE)) {
+ status = FwdLockConv_Status_RandomNumberGenerationFailed;
+ } else if (!FwdLockGlue_EncryptKey(pSession->sessionKey, KEY_SIZE,
+ pSession->pEncryptedSessionKey,
+ pSession->encryptedSessionKeyLength)) {
+ status = FwdLockConv_Status_KeyEncryptionFailed;
+ } else {
+ status = FwdLockConv_DeriveKeys(pSession);
+ }
+ if (status == FwdLockConv_Status_OK) {
+ memset(pSession->sessionKey, 0, KEY_SIZE); // Zero out key data.
+ memcpy(pSession->counter, pSession->pEncryptedSessionKey, AES_BLOCK_SIZE);
+ pSession->parserState = FwdLockConv_ParserState_WantsOpenDelimiter;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+ pSession->numCharsConsumed = 0;
+ pSession->delimiterMatchPos = 0;
+ pSession->mimeHeaderName = nullString;
+ pSession->contentType = nullString;
+ pSession->contentTransferEncoding =
+ FwdLockConv_ContentTransferEncoding_Undefined;
+ pSession->keyStreamIndex = -1;
+ pOutput->fromConvertData.pBuffer = NULL;
+ pOutput->fromConvertData.errorPos = INVALID_OFFSET;
+ } else {
+ free(pSession->pEncryptedSessionKey);
+ }
+ }
+ }
+ if (status != FwdLockConv_Status_OK) {
+ FwdLockConv_ReleaseSession(*pSessionId);
+ *pSessionId = -1;
+ }
+ }
+ }
+ return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId,
+ const void *pBuffer,
+ size_t numBytes,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status;
+ if (!FwdLockConv_IsValidSession(sessionId) || pBuffer == NULL || pOutput == NULL) {
+ status = FwdLockConv_Status_InvalidArgument;
+ } else {
+ size_t i;
+ FwdLockConv_Session_t *pSession = sessionPtrs[sessionId];
+ pSession->dataOffset = 0;
+ pSession->numDataBytes = 0;
+ pOutput->fromConvertData.numBytes = 0;
+ status = FwdLockConv_Status_OK;
+
+ for (i = 0; i < numBytes; ++i) {
+ status = FwdLockConv_PushChar(pSession, ((char *)pBuffer)[i], pOutput);
+ if (status != FwdLockConv_Status_OK) {
+ break;
+ }
+ }
+ if (status == FwdLockConv_Status_OK) {
+ // Update the data signature.
+ HMAC_Update(&pSession->signingContext,
+ &((unsigned char *)pOutput->fromConvertData.pBuffer)[pSession->dataOffset],
+ pSession->numDataBytes);
+ } else if (status == FwdLockConv_Status_SyntaxError) {
+ pOutput->fromConvertData.errorPos = pSession->numCharsConsumed;
+ }
+ }
+ return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status;
+ if (!FwdLockConv_IsValidSession(sessionId) || pOutput == NULL) {
+ status = FwdLockConv_Status_InvalidArgument;
+ } else {
+ FwdLockConv_Session_t *pSession = sessionPtrs[sessionId];
+ free(pOutput->fromConvertData.pBuffer);
+ if (pSession->parserState != FwdLockConv_ParserState_Done) {
+ pOutput->fromCloseSession.errorPos = pSession->numCharsConsumed;
+ status = FwdLockConv_Status_SyntaxError;
+ } else {
+ // Finalize the data signature.
+ size_t signatureSize;
+ HMAC_Final(&pSession->signingContext, pOutput->fromCloseSession.signatures,
+ &signatureSize);
+ if (signatureSize != SHA1_HASH_SIZE) {
+ status = FwdLockConv_Status_ProgramError;
+ } else {
+ // Calculate the header signature, which is a signature of the rest of the header
+ // including the data signature.
+ HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+ HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
+ HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->contentType.ptr,
+ pSession->contentType.length);
+ HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
+ pSession->encryptedSessionKeyLength);
+ HMAC_Update(&pSession->signingContext, pOutput->fromCloseSession.signatures,
+ signatureSize);
+ HMAC_Final(&pSession->signingContext, &pOutput->fromCloseSession.
+ signatures[signatureSize], &signatureSize);
+ if (signatureSize != SHA1_HASH_SIZE) {
+ status = FwdLockConv_Status_ProgramError;
+ } else {
+ pOutput->fromCloseSession.fileOffset = TOP_HEADER_SIZE +
+ pSession->contentType.length + pSession->encryptedSessionKeyLength;
+ status = FwdLockConv_Status_OK;
+ }
+ }
+ pOutput->fromCloseSession.errorPos = INVALID_OFFSET;
+ }
+ free(pSession->mimeHeaderName.ptr);
+ free(pSession->contentType.ptr);
+ free(pSession->pEncryptedSessionKey);
+ HMAC_CTX_cleanup(&pSession->signingContext);
+ FwdLockConv_ReleaseSession(sessionId);
+ }
+ return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc,
+ FwdLockConv_ReadFunc_t *fpReadFunc,
+ int outputFileDesc,
+ FwdLockConv_WriteFunc_t *fpWriteFunc,
+ FwdLockConv_LSeekFunc_t *fpLSeekFunc,
+ off64_t *pErrorPos) {
+ FwdLockConv_Status_t status;
+ if (pErrorPos != NULL) {
+ *pErrorPos = INVALID_OFFSET;
+ }
+ if (fpReadFunc == NULL || fpWriteFunc == NULL || fpLSeekFunc == NULL || inputFileDesc < 0 ||
+ outputFileDesc < 0) {
+ status = FwdLockConv_Status_InvalidArgument;
+ } else {
+ char *pReadBuffer = malloc(READ_BUFFER_SIZE);
+ if (pReadBuffer == NULL) {
+ status = FwdLockConv_Status_OutOfMemory;
+ } else {
+ int sessionId;
+ FwdLockConv_Output_t output;
+ status = FwdLockConv_OpenSession(&sessionId, &output);
+ if (status == FwdLockConv_Status_OK) {
+ ssize_t numBytesRead;
+ FwdLockConv_Status_t closeStatus;
+ while ((numBytesRead =
+ fpReadFunc(inputFileDesc, pReadBuffer, READ_BUFFER_SIZE)) > 0) {
+ status = FwdLockConv_ConvertData(sessionId, pReadBuffer, (size_t)numBytesRead,
+ &output);
+ if (status == FwdLockConv_Status_OK) {
+ if (output.fromConvertData.pBuffer != NULL &&
+ output.fromConvertData.numBytes > 0) {
+ ssize_t numBytesWritten = fpWriteFunc(outputFileDesc,
+ output.fromConvertData.pBuffer,
+ output.fromConvertData.numBytes);
+ if (numBytesWritten != (ssize_t)output.fromConvertData.numBytes) {
+ status = FwdLockConv_Status_FileWriteError;
+ break;
+ }
+ }
+ } else {
+ if (status == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) {
+ *pErrorPos = output.fromConvertData.errorPos;
+ }
+ break;
+ }
+ } // end while
+ if (numBytesRead < 0) {
+ status = FwdLockConv_Status_FileReadError;
+ }
+ closeStatus = FwdLockConv_CloseSession(sessionId, &output);
+ if (status == FwdLockConv_Status_OK) {
+ if (closeStatus != FwdLockConv_Status_OK) {
+ if (closeStatus == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) {
+ *pErrorPos = output.fromCloseSession.errorPos;
+ }
+ status = closeStatus;
+ } else if (fpLSeekFunc(outputFileDesc, output.fromCloseSession.fileOffset,
+ SEEK_SET) < 0) {
+ status = FwdLockConv_Status_FileSeekError;
+ } else if (fpWriteFunc(outputFileDesc, output.fromCloseSession.signatures,
+ FWD_LOCK_SIGNATURES_SIZE) != FWD_LOCK_SIGNATURES_SIZE) {
+ status = FwdLockConv_Status_FileWriteError;
+ }
+ }
+ }
+ free(pReadBuffer);
+ }
+ }
+ return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename,
+ const char *pOutputFilename,
+ off64_t *pErrorPos) {
+ FwdLockConv_Status_t status;
+ if (pErrorPos != NULL) {
+ *pErrorPos = INVALID_OFFSET;
+ }
+ if (pInputFilename == NULL || pOutputFilename == NULL) {
+ status = FwdLockConv_Status_InvalidArgument;
+ } else {
+ int inputFileDesc = open(pInputFilename, O_RDONLY);
+ if (inputFileDesc < 0) {
+ status = FwdLockConv_Status_FileNotFound;
+ } else {
+ int outputFileDesc = open(pOutputFilename, O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (outputFileDesc < 0) {
+ status = FwdLockConv_Status_FileCreationFailed;
+ } else {
+ status = FwdLockConv_ConvertOpenFile(inputFileDesc, read, outputFileDesc, write,
+ lseek64, pErrorPos);
+ if (close(outputFileDesc) == 0 && status != FwdLockConv_Status_OK) {
+ remove(pOutputFilename);
+ }
+ }
+ (void)close(inputFileDesc);
+ }
+ }
+ return status;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
new file mode 100644
index 0000000..e20c0c3
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2010 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 __FWDLOCKCONV_H__
+#define __FWDLOCKCONV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/**
+ * The size of the data and header signatures combined. The signatures are adjacent to each other in
+ * the produced output file.
+ */
+#define FWD_LOCK_SIGNATURES_SIZE (2 * 20)
+
+/**
+ * Data type for the output from FwdLockConv_ConvertData.
+ */
+typedef struct FwdLockConv_ConvertData_Output {
+ /// The converted data.
+ void *pBuffer;
+
+ /// The size of the converted data.
+ size_t numBytes;
+
+ /// The file position where the error occurred, in the case of a syntax error.
+ off64_t errorPos;
+} FwdLockConv_ConvertData_Output_t;
+
+/**
+ * Data type for the output from FwdLockConv_CloseSession.
+ */
+typedef struct FwdLockConv_CloseSession_Output {
+ /// The final set of signatures.
+ unsigned char signatures[FWD_LOCK_SIGNATURES_SIZE];
+
+ /// The offset in the produced output file where the signatures are located.
+ off64_t fileOffset;
+
+ /// The file position where the error occurred, in the case of a syntax error.
+ off64_t errorPos;
+} FwdLockConv_CloseSession_Output_t;
+
+/**
+ * Data type for the output from the conversion process.
+ */
+typedef union FwdLockConv_Output {
+ FwdLockConv_ConvertData_Output_t fromConvertData;
+ FwdLockConv_CloseSession_Output_t fromCloseSession;
+} FwdLockConv_Output_t;
+
+/**
+ * Data type for the Posix-style read function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for reading.
+ * @param[out] pBuffer A reference to the buffer that should receive the read data.
+ * @param[in] numBytes The number of bytes to read.
+ *
+ * @return The number of bytes read.
+ * @retval -1 Failure.
+ */
+typedef ssize_t FwdLockConv_ReadFunc_t(int fileDesc, void *pBuffer, size_t numBytes);
+
+/**
+ * Data type for the Posix-style write function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for writing.
+ * @param[in] pBuffer A reference to the buffer containing the data to be written.
+ * @param[in] numBytes The number of bytes to write.
+ *
+ * @return The number of bytes written.
+ * @retval -1 Failure.
+ */
+typedef ssize_t FwdLockConv_WriteFunc_t(int fileDesc, const void *pBuffer, size_t numBytes);
+
+/**
+ * Data type for the Posix-style lseek function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for writing.
+ * @param[in] offset The offset with which to update the file position.
+ * @param[in] whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ *
+ * @return The new file position.
+ * @retval ((off64_t)-1) Failure.
+ */
+typedef off64_t FwdLockConv_LSeekFunc_t(int fileDesc, off64_t offset, int whence);
+
+/**
+ * The status codes returned by the converter functions.
+ */
+typedef enum FwdLockConv_Status {
+ /// The operation was successful.
+ FwdLockConv_Status_OK = 0,
+
+ /// An actual argument to the function is invalid (a program error on the caller's part).
+ FwdLockConv_Status_InvalidArgument = 1,
+
+ /// There is not enough free dynamic memory to complete the operation.
+ FwdLockConv_Status_OutOfMemory = 2,
+
+ /// An error occurred while opening the input file.
+ FwdLockConv_Status_FileNotFound = 3,
+
+ /// An error occurred while creating the output file.
+ FwdLockConv_Status_FileCreationFailed = 4,
+
+ /// An error occurred while reading from the input file.
+ FwdLockConv_Status_FileReadError = 5,
+
+ /// An error occurred while writing to the output file.
+ FwdLockConv_Status_FileWriteError = 6,
+
+ /// An error occurred while seeking to a new file position within the output file.
+ FwdLockConv_Status_FileSeekError = 7,
+
+ /// The input file is not a syntactically correct OMA DRM v1 Forward Lock file.
+ FwdLockConv_Status_SyntaxError = 8,
+
+ /// Support for this DRM file format has been disabled in the current product configuration.
+ FwdLockConv_Status_UnsupportedFileFormat = 9,
+
+ /// The content transfer encoding is not one of "binary", "base64", "7bit", or "8bit"
+ /// (case-insensitive).
+ FwdLockConv_Status_UnsupportedContentTransferEncoding = 10,
+
+ /// The generation of a random number failed.
+ FwdLockConv_Status_RandomNumberGenerationFailed = 11,
+
+ /// Key encryption failed.
+ FwdLockConv_Status_KeyEncryptionFailed = 12,
+
+ /// The calculation of a keyed hash for integrity protection failed.
+ FwdLockConv_Status_IntegrityProtectionFailed = 13,
+
+ /// There are too many ongoing sessions for another one to be opened.
+ FwdLockConv_Status_TooManySessions = 14,
+
+ /// An unexpected error occurred.
+ FwdLockConv_Status_ProgramError = 15
+} FwdLockConv_Status_t;
+
+/**
+ * Opens a session for converting an OMA DRM v1 Forward Lock file to the internal Forward Lock file
+ * format.
+ *
+ * @param[out] pSessionId The session ID.
+ * @param[out] pOutput The output from the conversion process (initialized).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput);
+
+/**
+ * Supplies the converter with data to convert. The caller is expected to write the converted data
+ * to file. Can be called an arbitrary number of times.
+ *
+ * @param[in] sessionId The session ID.
+ * @param[in] pBuffer A reference to a buffer containing the data to convert.
+ * @param[in] numBytes The number of bytes to convert.
+ * @param[in,out] pOutput The output from the conversion process (allocated/reallocated).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId,
+ const void *pBuffer,
+ size_t numBytes,
+ FwdLockConv_Output_t *pOutput);
+
+/**
+ * Closes a session for converting an OMA DRM v1 Forward Lock file to the internal Forward Lock
+ * file format. The caller must update the produced output file at the indicated file offset with
+ * the final set of signatures.
+ *
+ * @param[in] sessionId The session ID.
+ * @param[in,out] pOutput The output from the conversion process (deallocated and overwritten).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ */
+FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput);
+
+/**
+ * Converts an open OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull
+ * mode.
+ *
+ * @param[in] inputFileDesc The file descriptor of the open input file.
+ * @param[in] fpReadFunc A reference to a read function that can operate on the open input file.
+ * @param[in] outputFileDesc The file descriptor of the open output file.
+ * @param[in] fpWriteFunc A reference to a write function that can operate on the open output file.
+ * @param[in] fpLSeekFunc A reference to an lseek function that can operate on the open output file.
+ * @param[out] pErrorPos
+ * The file position where the error occurred, in the case of a syntax error. May be NULL.
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_FileReadError
+ * @retval FwdLockConv_Status_FileWriteError
+ * @retval FwdLockConv_Status_FileSeekError
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc,
+ FwdLockConv_ReadFunc_t *fpReadFunc,
+ int outputFileDesc,
+ FwdLockConv_WriteFunc_t *fpWriteFunc,
+ FwdLockConv_LSeekFunc_t *fpLSeekFunc,
+ off64_t *pErrorPos);
+
+/**
+ * Converts an OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull mode.
+ *
+ * @param[in] pInputFilename A reference to the input filename.
+ * @param[in] pOutputFilename A reference to the output filename.
+ * @param[out] pErrorPos
+ * The file position where the error occurred, in the case of a syntax error. May be NULL.
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_FileNotFound
+ * @retval FwdLockConv_Status_FileCreationFailed
+ * @retval FwdLockConv_Status_FileReadError
+ * @retval FwdLockConv_Status_FileWriteError
+ * @retval FwdLockConv_Status_FileSeekError
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename,
+ const char *pOutputFilename,
+ off64_t *pErrorPos);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKCONV_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
new file mode 100644
index 0000000..b625edf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2010 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ FwdLockFile.c
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+ external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_MODULE := libfwdlock-decoder
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
new file mode 100644
index 0000000..98284e7
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2010 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+#include <openssl/hmac.h>
+
+#include "FwdLockFile.h"
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define INVALID_OFFSET ((off64_t)-1)
+
+#define INVALID_BLOCK_INDEX ((uint64_t)-1)
+
+#define MAX_NUM_SESSIONS 128
+
+#define KEY_SIZE AES_BLOCK_SIZE
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+#define SHA1_HASH_SIZE 20
+#define SHA1_BLOCK_SIZE 64
+
+#define FWD_LOCK_VERSION 0
+#define FWD_LOCK_SUBFORMAT 0
+#define USAGE_RESTRICTION_FLAGS 0
+#define CONTENT_TYPE_LENGTH_POS 7
+#define TOP_HEADER_SIZE 8
+
+#define SIG_CALC_BUFFER_SIZE (16 * SHA1_BLOCK_SIZE)
+
+/**
+ * Data type for the per-file state information needed by the decoder.
+ */
+typedef struct FwdLockFile_Session {
+ int fileDesc;
+ unsigned char topHeader[TOP_HEADER_SIZE];
+ char *pContentType;
+ size_t contentTypeLength;
+ void *pEncryptedSessionKey;
+ size_t encryptedSessionKeyLength;
+ unsigned char dataSignature[SHA1_HASH_SIZE];
+ unsigned char headerSignature[SHA1_HASH_SIZE];
+ off64_t dataOffset;
+ off64_t filePos;
+ AES_KEY encryptionRoundKeys;
+ HMAC_CTX signingContext;
+ unsigned char keyStream[AES_BLOCK_SIZE];
+ uint64_t blockIndex;
+} FwdLockFile_Session_t;
+
+static FwdLockFile_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL };
+
+static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static const unsigned char topHeaderTemplate[] =
+ { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS };
+
+/**
+ * Acquires an unused file session for the given file descriptor.
+ *
+ * @param[in] fileDesc A file descriptor.
+ *
+ * @return A session ID.
+ */
+static int FwdLockFile_AcquireSession(int fileDesc) {
+ int sessionId = -1;
+ if (fileDesc < 0) {
+ errno = EBADF;
+ } else {
+ int i;
+ pthread_mutex_lock(&sessionAcquisitionMutex);
+ for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+ int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS;
+ if (sessionPtrs[candidateSessionId] == NULL) {
+ sessionPtrs[candidateSessionId] = malloc(sizeof **sessionPtrs);
+ if (sessionPtrs[candidateSessionId] != NULL) {
+ sessionPtrs[candidateSessionId]->fileDesc = fileDesc;
+ sessionPtrs[candidateSessionId]->pContentType = NULL;
+ sessionPtrs[candidateSessionId]->pEncryptedSessionKey = NULL;
+ sessionId = candidateSessionId;
+ }
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sessionAcquisitionMutex);
+ if (i == MAX_NUM_SESSIONS) {
+ errno = ENFILE;
+ }
+ }
+ return sessionId;
+}
+
+/**
+ * Finds the file session associated to the given file descriptor.
+ *
+ * @param[in] fileDesc A file descriptor.
+ *
+ * @return A session ID.
+ */
+static int FwdLockFile_FindSession(int fileDesc) {
+ int sessionId = -1;
+ if (fileDesc < 0) {
+ errno = EBADF;
+ } else {
+ int i;
+ pthread_mutex_lock(&sessionAcquisitionMutex);
+ for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+ int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS;
+ if (sessionPtrs[candidateSessionId] != NULL &&
+ sessionPtrs[candidateSessionId]->fileDesc == fileDesc) {
+ sessionId = candidateSessionId;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sessionAcquisitionMutex);
+ if (i == MAX_NUM_SESSIONS) {
+ errno = EBADF;
+ }
+ }
+ return sessionId;
+}
+
+/**
+ * Releases a file session.
+ *
+ * @param[in] sessionID A session ID.
+ */
+static void FwdLockFile_ReleaseSession(int sessionId) {
+ pthread_mutex_lock(&sessionAcquisitionMutex);
+ assert(0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL);
+ free(sessionPtrs[sessionId]->pContentType);
+ free(sessionPtrs[sessionId]->pEncryptedSessionKey);
+ memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data.
+ free(sessionPtrs[sessionId]);
+ sessionPtrs[sessionId] = NULL;
+ pthread_mutex_unlock(&sessionAcquisitionMutex);
+}
+
+/**
+ * Derives keys for encryption and signing from the encrypted session key.
+ *
+ * @param[in,out] pSession A reference to a file session.
+ *
+ * @return A Boolean value indicating whether key derivation was successful.
+ */
+static int FwdLockFile_DeriveKeys(FwdLockFile_Session_t * pSession) {
+ int result;
+ struct FwdLockFile_DeriveKeys_Data {
+ AES_KEY sessionRoundKeys;
+ unsigned char value[KEY_SIZE];
+ unsigned char key[KEY_SIZE];
+ } *pData = malloc(sizeof *pData);
+ if (pData == NULL) {
+ result = FALSE;
+ } else {
+ result = FwdLockGlue_DecryptKey(pSession->pEncryptedSessionKey,
+ pSession->encryptedSessionKeyLength, pData->key, KEY_SIZE);
+ if (result) {
+ if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS, &pData->sessionRoundKeys) != 0) {
+ result = FALSE;
+ } else {
+ // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key.
+ memset(pData->value, 0, KEY_SIZE);
+ AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+ if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS,
+ &pSession->encryptionRoundKeys) != 0) {
+ result = FALSE;
+ } else {
+ // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key.
+ ++pData->value[0];
+ AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+ HMAC_CTX_init(&pSession->signingContext);
+ HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL);
+ }
+ }
+ }
+ if (!result) {
+ errno = ENOSYS;
+ }
+ memset(pData, 0, sizeof pData); // Zero out key data.
+ free(pData);
+ }
+ return result;
+}
+
+/**
+ * Calculates the counter, treated as a 16-byte little-endian number, used to generate the keystream
+ * for the given block.
+ *
+ * @param[in] pNonce A reference to the nonce.
+ * @param[in] blockIndex The index number of the block.
+ * @param[out] pCounter A reference to the counter.
+ */
+static void FwdLockFile_CalculateCounter(const unsigned char *pNonce,
+ uint64_t blockIndex,
+ unsigned char *pCounter) {
+ unsigned char carry = 0;
+ size_t i = 0;
+ for (; i < sizeof blockIndex; ++i) {
+ unsigned char part = pNonce[i] + (unsigned char)(blockIndex >> (i * CHAR_BIT));
+ pCounter[i] = part + carry;
+ carry = (part < pNonce[i] || pCounter[i] < part) ? 1 : 0;
+ }
+ for (; i < AES_BLOCK_SIZE; ++i) {
+ pCounter[i] = pNonce[i] + carry;
+ carry = (pCounter[i] < pNonce[i]) ? 1 : 0;
+ }
+}
+
+/**
+ * Decrypts the byte at the current file position using AES-128-CTR. In CTR (or "counter") mode,
+ * encryption and decryption are performed using the same algorithm.
+ *
+ * @param[in,out] pSession A reference to a file session.
+ * @param[in] pByte The byte to decrypt.
+ */
+void FwdLockFile_DecryptByte(FwdLockFile_Session_t * pSession, unsigned char *pByte) {
+ uint64_t blockIndex = pSession->filePos / AES_BLOCK_SIZE;
+ uint64_t blockOffset = pSession->filePos % AES_BLOCK_SIZE;
+ if (blockIndex != pSession->blockIndex) {
+ // The first 16 bytes of the encrypted session key is used as the nonce.
+ unsigned char counter[AES_BLOCK_SIZE];
+ FwdLockFile_CalculateCounter(pSession->pEncryptedSessionKey, blockIndex, counter);
+ AES_encrypt(counter, pSession->keyStream, &pSession->encryptionRoundKeys);
+ pSession->blockIndex = blockIndex;
+ }
+ *pByte ^= pSession->keyStream[blockOffset];
+}
+
+int FwdLockFile_attach(int fileDesc) {
+ int sessionId = FwdLockFile_AcquireSession(fileDesc);
+ if (sessionId >= 0) {
+ FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+ int isSuccess = FALSE;
+ if (read(fileDesc, pSession->topHeader, TOP_HEADER_SIZE) == TOP_HEADER_SIZE &&
+ memcmp(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate) == 0) {
+ pSession->contentTypeLength = pSession->topHeader[CONTENT_TYPE_LENGTH_POS];
+ assert(pSession->contentTypeLength <= UCHAR_MAX); // Untaint scalar for code checkers.
+ pSession->pContentType = malloc(pSession->contentTypeLength + 1);
+ if (pSession->pContentType != NULL &&
+ read(fileDesc, pSession->pContentType, pSession->contentTypeLength) ==
+ (ssize_t)pSession->contentTypeLength) {
+ pSession->pContentType[pSession->contentTypeLength] = '\0';
+ pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE);
+ pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength);
+ if (pSession->pEncryptedSessionKey != NULL &&
+ read(fileDesc, pSession->pEncryptedSessionKey,
+ pSession->encryptedSessionKeyLength) ==
+ (ssize_t)pSession->encryptedSessionKeyLength &&
+ read(fileDesc, pSession->dataSignature, SHA1_HASH_SIZE) ==
+ SHA1_HASH_SIZE &&
+ read(fileDesc, pSession->headerSignature, SHA1_HASH_SIZE) ==
+ SHA1_HASH_SIZE) {
+ isSuccess = FwdLockFile_DeriveKeys(pSession);
+ }
+ }
+ }
+ if (isSuccess) {
+ pSession->dataOffset = pSession->contentTypeLength +
+ pSession->encryptedSessionKeyLength + TOP_HEADER_SIZE + 2 * SHA1_HASH_SIZE;
+ pSession->filePos = 0;
+ pSession->blockIndex = INVALID_BLOCK_INDEX;
+ } else {
+ FwdLockFile_ReleaseSession(sessionId);
+ sessionId = -1;
+ }
+ }
+ return (sessionId >= 0) ? 0 : -1;
+}
+
+int FwdLockFile_open(const char *pFilename) {
+ int fileDesc = open(pFilename, O_RDONLY);
+ if (fileDesc >= 0 && FwdLockFile_attach(fileDesc) < 0) {
+ (void)close(fileDesc);
+ fileDesc = -1;
+ }
+ return fileDesc;
+}
+
+ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes) {
+ ssize_t numBytesRead;
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ numBytesRead = -1;
+ } else {
+ FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+ ssize_t i;
+ numBytesRead = read(pSession->fileDesc, pBuffer, numBytes);
+ for (i = 0; i < numBytesRead; ++i) {
+ FwdLockFile_DecryptByte(pSession, &((unsigned char *)pBuffer)[i]);
+ ++pSession->filePos;
+ }
+ }
+ return numBytesRead;
+}
+
+off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence) {
+ off64_t newFilePos;
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ newFilePos = INVALID_OFFSET;
+ } else {
+ FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+ switch (whence) {
+ case SEEK_SET:
+ newFilePos = lseek64(pSession->fileDesc, pSession->dataOffset + offset, whence);
+ break;
+ case SEEK_CUR:
+ case SEEK_END:
+ newFilePos = lseek64(pSession->fileDesc, offset, whence);
+ break;
+ default:
+ errno = EINVAL;
+ newFilePos = INVALID_OFFSET;
+ break;
+ }
+ if (newFilePos != INVALID_OFFSET) {
+ if (newFilePos < pSession->dataOffset) {
+ // The new file position is illegal for an internal Forward Lock file. Restore the
+ // original file position.
+ (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
+ SEEK_SET);
+ errno = EINVAL;
+ newFilePos = INVALID_OFFSET;
+ } else {
+ // The return value should be the file position that lseek64() would have returned
+ // for the embedded content file.
+ pSession->filePos = newFilePos - pSession->dataOffset;
+ newFilePos = pSession->filePos;
+ }
+ }
+ }
+ return newFilePos;
+}
+
+int FwdLockFile_detach(int fileDesc) {
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ return -1;
+ }
+ HMAC_CTX_cleanup(&sessionPtrs[sessionId]->signingContext);
+ FwdLockFile_ReleaseSession(sessionId);
+ return 0;
+}
+
+int FwdLockFile_close(int fileDesc) {
+ return (FwdLockFile_detach(fileDesc) == 0) ? close(fileDesc) : -1;
+}
+
+int FwdLockFile_CheckDataIntegrity(int fileDesc) {
+ int result;
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ result = FALSE;
+ } else {
+ struct FwdLockFile_CheckDataIntegrity_Data {
+ unsigned char signature[SHA1_HASH_SIZE];
+ unsigned char buffer[SIG_CALC_BUFFER_SIZE];
+ } *pData = malloc(sizeof *pData);
+ if (pData == NULL) {
+ result = FALSE;
+ } else {
+ FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+ if (lseek64(pSession->fileDesc, pSession->dataOffset, SEEK_SET) !=
+ pSession->dataOffset) {
+ result = FALSE;
+ } else {
+ ssize_t numBytesRead;
+ size_t signatureSize = SHA1_HASH_SIZE;
+ while ((numBytesRead =
+ read(pSession->fileDesc, pData->buffer, SIG_CALC_BUFFER_SIZE)) > 0) {
+ HMAC_Update(&pSession->signingContext, pData->buffer, (size_t)numBytesRead);
+ }
+ if (numBytesRead < 0) {
+ result = FALSE;
+ } else {
+ HMAC_Final(&pSession->signingContext, pData->signature, &signatureSize);
+ assert(signatureSize == SHA1_HASH_SIZE);
+ result = memcmp(pData->signature, pSession->dataSignature, signatureSize) == 0;
+ }
+ HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+ (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
+ SEEK_SET);
+ }
+ free(pData);
+ }
+ }
+ return result;
+}
+
+int FwdLockFile_CheckHeaderIntegrity(int fileDesc) {
+ int result;
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ result = FALSE;
+ } else {
+ FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+ unsigned char signature[SHA1_HASH_SIZE];
+ size_t signatureSize = SHA1_HASH_SIZE;
+ HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
+ HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->pContentType,
+ pSession->contentTypeLength);
+ HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
+ pSession->encryptedSessionKeyLength);
+ HMAC_Update(&pSession->signingContext, pSession->dataSignature, signatureSize);
+ HMAC_Final(&pSession->signingContext, signature, &signatureSize);
+ assert(signatureSize == SHA1_HASH_SIZE);
+ result = memcmp(signature, pSession->headerSignature, signatureSize) == 0;
+ HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+ }
+ return result;
+}
+
+int FwdLockFile_CheckIntegrity(int fileDesc) {
+ return FwdLockFile_CheckHeaderIntegrity(fileDesc) && FwdLockFile_CheckDataIntegrity(fileDesc);
+}
+
+const char *FwdLockFile_GetContentType(int fileDesc) {
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ return NULL;
+ }
+ return sessionPtrs[sessionId]->pContentType;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h
new file mode 100644
index 0000000..fc64050
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010 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 __FWDLOCKFILE_H__
+#define __FWDLOCKFILE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/**
+ * Attaches to an open Forward Lock file. The file position is assumed to be at the beginning of the
+ * file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_attach(int fileDesc);
+
+/**
+ * Opens a Forward Lock file for reading.
+ *
+ * @param[in] pFilename A reference to a filename.
+ *
+ * @return A file descriptor.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_open(const char *pFilename);
+
+/**
+ * Reads the specified number of bytes from an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ * @param[out] pBuffer A reference to the buffer that should receive the read data.
+ * @param[in] numBytes The number of bytes to read.
+ *
+ * @return The number of bytes read.
+ * @retval -1 Failure.
+ */
+ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes);
+
+/**
+ * Updates the file position within an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ * @param[in] offset The offset with which to update the file position.
+ * @param[in] whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ *
+ * @return The new file position.
+ * @retval ((off64_t)-1) Failure.
+ */
+off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence);
+
+/**
+ * Detaches from an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_detach(int fileDesc);
+
+/**
+ * Closes an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_close(int fileDesc);
+
+/**
+ * Checks the data integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckDataIntegrity(int fileDesc);
+
+/**
+ * Checks the header integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckHeaderIntegrity(int fileDesc);
+
+/**
+ * Checks both the data and header integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckIntegrity(int fileDesc);
+
+/**
+ * Returns the content type of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return
+ * A reference to the content type. The reference remains valid as long as the file is kept open.
+ */
+const char *FwdLockFile_GetContentType(int fileDesc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKFILE_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
new file mode 100755
index 0000000..8f95cd2
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
@@ -0,0 +1,1039 @@
+<html>
+
+<head>
+<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
+<meta name=Generator content="Microsoft Word 12 (filtered)">
+<title>Forward Lock Converter and Decoder</title>
+<style>
+<!--
+ /* Font Definitions */
+ @font-face
+ {font-family:SimSun;
+ panose-1:2 1 6 0 3 1 1 1 1 1;}
+@font-face
+ {font-family:"Cambria Math";
+ panose-1:2 4 5 3 5 4 6 3 2 4;}
+@font-face
+ {font-family:Tahoma;
+ panose-1:2 11 6 4 3 5 4 4 2 4;}
+@font-face
+ {font-family:"Lucida Console","DejaVu Sans Mono";
+ panose-1:2 11 6 9 4 5 4 2 2 4;}
+@font-face
+ {font-family:"\@SimSun";
+ panose-1:2 1 6 0 3 1 1 1 1 1;}
+ /* Style Definitions */
+ p.MsoNormal, li.MsoNormal, div.MsoNormal
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";}
+h1
+ {margin-right:0cm;
+ margin-left:21.6pt;
+ text-indent:-21.6pt;
+ page-break-after:avoid;
+ font-size:16.0pt;
+ font-family:"Arial","sans-serif";}
+h2
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:28.8pt;
+ text-indent:-28.8pt;
+ page-break-after:avoid;
+ font-size:14.0pt;
+ font-family:"Arial","sans-serif";
+ font-style:italic;}
+h3
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:36.0pt;
+ text-indent:-36.0pt;
+ page-break-after:avoid;
+ font-size:13.0pt;
+ font-family:"Arial","sans-serif";}
+h4
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:43.2pt;
+ text-indent:-43.2pt;
+ page-break-after:avoid;
+ font-size:14.0pt;
+ font-family:"Times New Roman","serif";}
+h5
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:50.4pt;
+ text-indent:-50.4pt;
+ font-size:13.0pt;
+ font-family:"Times New Roman","serif";
+ font-style:italic;}
+h6
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:57.6pt;
+ text-indent:-57.6pt;
+ font-size:11.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoHeading7, li.MsoHeading7, div.MsoHeading7
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:64.8pt;
+ text-indent:-64.8pt;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoHeading8, li.MsoHeading8, div.MsoHeading8
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:72.0pt;
+ text-indent:-72.0pt;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";
+ font-style:italic;}
+p.MsoHeading9, li.MsoHeading9, div.MsoHeading9
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:79.2pt;
+ text-indent:-79.2pt;
+ font-size:11.0pt;
+ font-family:"Arial","sans-serif";}
+p.MsoToc1, li.MsoToc1, div.MsoToc1
+ {margin-top:6.0pt;
+ margin-right:0cm;
+ margin-bottom:6.0pt;
+ margin-left:0cm;
+ line-height:150%;
+ font-size:10.5pt;
+ font-family:"Times New Roman","serif";
+ text-transform:uppercase;
+ font-weight:bold;}
+p.MsoToc2, li.MsoToc2, div.MsoToc2
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:12.0pt;
+ margin-bottom:.0001pt;
+ line-height:150%;
+ font-size:10.5pt;
+ font-family:"Times New Roman","serif";
+ font-variant:small-caps;}
+p.MsoToc3, li.MsoToc3, div.MsoToc3
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:24.0pt;
+ margin-bottom:.0001pt;
+ line-height:150%;
+ font-size:10.5pt;
+ font-family:"Times New Roman","serif";
+ font-style:italic;}
+p.MsoToc4, li.MsoToc4, div.MsoToc4
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:36.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoToc5, li.MsoToc5, div.MsoToc5
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:48.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoToc6, li.MsoToc6, div.MsoToc6
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:60.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoToc7, li.MsoToc7, div.MsoToc7
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:72.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoToc8, li.MsoToc8, div.MsoToc8
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:84.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoToc9, li.MsoToc9, div.MsoToc9
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:96.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoFootnoteText, li.MsoFootnoteText, div.MsoFootnoteText
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:10.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoHeader, li.MsoHeader, div.MsoHeader
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoFooter, li.MsoFooter, div.MsoFooter
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoCaption, li.MsoCaption, div.MsoCaption
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:11.0pt;
+ font-family:"Times New Roman","serif";
+ font-weight:bold;}
+span.MsoFootnoteReference
+ {vertical-align:super;}
+p.MsoTitle, li.MsoTitle, div.MsoTitle
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:120.0pt;
+ margin-left:0cm;
+ text-align:center;
+ font-size:16.0pt;
+ font-family:"Arial","sans-serif";
+ font-weight:bold;}
+p.MsoBodyText, li.MsoBodyText, div.MsoBodyText
+ {mso-style-link:"Body Text Char";
+ margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:6.0pt;
+ margin-left:0cm;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";}
+a:link, span.MsoHyperlink
+ {color:blue;
+ text-decoration:underline;}
+a:visited, span.MsoHyperlinkFollowed
+ {color:purple;
+ text-decoration:underline;}
+p.MsoAcetate, li.MsoAcetate, div.MsoAcetate
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:8.0pt;
+ font-family:"Tahoma","sans-serif";}
+span.BodyTextChar
+ {mso-style-name:"Body Text Char";
+ mso-style-link:"Body Text";}
+ /* Page Definitions */
+ @page WordSection1
+ {size:595.45pt 841.7pt;
+ margin:72.0pt 90.0pt 72.0pt 90.0pt;}
+div.WordSection1
+ {page:WordSection1;}
+@page WordSection2
+ {size:595.45pt 841.7pt;
+ margin:72.0pt 90.0pt 72.0pt 90.0pt;}
+div.WordSection2
+ {page:WordSection2;}
+ /* List Definitions */
+ ol
+ {margin-bottom:0cm;}
+ul
+ {margin-bottom:0cm;}
+-->
+</style>
+
+</head>
+
+<body lang=EN-US link=blue vlink=purple>
+
+<div class=WordSection1>
+
+<p class=MsoTitle>Forward Lock Converter And Decoder</p>
+
+<p class=MsoToc1><span
+class=MsoHyperlink><a href="#_Toc276471422">1<span style='font-size:12.0pt;
+line-height:150%;color:windowtext;text-transform:none;font-weight:normal;
+text-decoration:none'> </span>Introduction<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471423">2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'> </span>Overview<span
+style='color:windowtext;display:none;text-decoration:none'>... </span><span
+style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471424">3<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'> </span>Use Cases<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471425">3.1<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'> </span>Converter<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471426">3.1.1<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'> </span>Convert Data (Push-Mode Conversion)<span
+style='color:windowtext;display:none;text-decoration:none'> </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471427">3.1.2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'> </span>Convert File (Pull-Mode Conversion)<span
+style='color:windowtext;display:none;text-decoration:none'> </span><span
+style='color:windowtext;display:none;text-decoration:none'>6</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471428">3.2<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'> </span>Decoder<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>7</span></a></span></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471429">3.2.1<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'> </span>Check Integrity<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>8</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471430">3.2.2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'> </span>Get Content Type<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>9</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471431">3.2.3<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'> </span>Decode File<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>10</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471432">4<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'> </span>Definition of the
+Internal Forward Lock File Format<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>11</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471433">4.1<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'> </span>Key Derivation<span style='color:windowtext;display:none;
+text-decoration:none'>.. </span><span
+style='color:windowtext;display:none;text-decoration:none'>11</span></a></span></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471434">4.2<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'> </span>Calculation of the Counters<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471435">5<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'> </span>Unit Test Cases<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471436">6<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'> </span>References<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></p>
+
+<p class=MsoBodyText></p>
+
+</div>
+
+<span style='font-size:12.0pt;font-family:"Times New Roman","serif"'><br
+clear=all style='page-break-before:right'>
+</span>
+
+<div class=WordSection2>
+
+<h1><a name="_Toc276471422"></a><a name="_Ref263085474">1<span
+style='font:7.0pt "Times New Roman"'> </span>Introduction</a></h1>
+
+<p class=MsoBodyText>The internal Forward Lock file format is used for encrypting
+inherently unencrypted OMA DRM version 1 Forward Lock and Combined Delivery
+files so they can be securely stored on externally accessible file system partitions
+such as memory stick.</p>
+
+<p class=MsoBodyText>Our general strategy is to convert such <i>OMA DRM Message</i>
+(‘.dm’) files to internal Forward Lock (‘.fl’) files as soon as they are
+downloaded or otherwise transferred to the phone, and not actually provide any
+decoders for ‘.dm’ files.</p>
+
+<h1><a name="_Toc276471423">2<span style='font:7.0pt "Times New Roman"'>
+</span>Overview</a></h1>
+
+<p class=MsoBodyText>The <i>Forward Lock Converter</i> converts OMA DRM Message
+files to the internal file format. The <i>Forward Lock Decoder</i> provides a
+POSIX-level API for transparent reading and seeking through such a converted
+file as if it were unencrypted. The API also includes functions for checking a
+fileÂ’s integrity and getting the MIME type of its embedded content.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'>The converter and decoder are
+built into two separate libraries, which share common code for random number
+generation and key encryption in a third library. For test purposes there is
+also a unit test application. See Figure 1.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=288 height=364
+src="images/image001.gif"></p>
+
+<p class=MsoCaption style='margin-top:12.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref262730885">Figure </a>1. Block diagram illustrating the dependencies between the executable modules.</p>
+
+<b><span style='font-size:16.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h1><a name="_Toc276471424">3<span style='font:7.0pt "Times New Roman"'>
+</span>Use Cases</a></h1>
+
+<p class=MsoBodyText>This section describes all the use cases for the converter
+and decoder. It shows the sequence of API calls that should be used to solve
+these use cases.</p>
+
+<h2><a name="_Toc276471425">3.1<span style='font:7.0pt "Times New Roman"'>
+</span>Converter</a></h2>
+
+<p class=MsoBodyText>Through the converter API, conversion can be performed in one
+of two ways:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'> </span><i>Push-mode
+conversion</i> is when the client progressively feeds data to the converter as
+it arrives. This is appropriate when data arrives gradually in chunks, with
+idle time in between. Consequently, push mode is used for converting files
+being downloaded through HTTP. See section 3.1.1.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'> </span><i>Pull-mode
+conversion</i> is when the converter drives the process and consumes data from
+the client as it needs it. This is appropriate when the entire file to be
+converted is readily available. Hence, pull mode is used by the unit test application.
+See section 3.1.2.</p>
+
+<p class=MsoBodyText>Internally, pull-mode conversion is implemented in terms
+of the API for push-mode conversion.</p>
+
+<h3><a name="_Toc276471426"></a><a name="_Ref263085478">3.1.1<span
+style='font:7.0pt "Times New Roman"'> </span>Convert Data
+(Push-Mode Conversion)</a></h3>
+
+<p class=MsoBodyText>Push-mode conversion is performed as follows (see also Figure 2):</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'> </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_OpenSession</span>
+initializes the output parameter and returns a <i>session ID</i> to be used in
+subsequent calls to the API. The output parameter is a union of return values
+whose correct use at any given moment is determined by the API function last
+called.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'> </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_ConvertData</span>
+is called repeatedly until no more input data remains. Each call converts the
+maximum amount of data possible and writes it to the output buffer. The client then
+writes this data to file.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span
+style='font:7.0pt "Times New Roman"'> </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_CloseSession</span>
+cleans up the session and deallocates the output buffer. If all has gone well, a
+two-part cryptographic signature of the output file is calculated. The client
+must go back and rewrite part of the file header with this updated signature
+information.</p>
+
+<p class=MsoBodyText>Every time a file is being converted, the converter calls <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetRandomNumber</span>
+to generate a new, unique session key. No two converted files look alike, even
+if the original files are the same.</p>
+
+<p class=MsoBodyText><b>Note:</b> The random bytes cannot come from any bare-minimum
+implementation of the C-library <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>rand</span>
+function—they must be cryptographically secure. Otherwise, security will be
+compromised.</p>
+
+<p class=MsoBodyText>The session key is encrypted and stored within the
+converted file. Key encryption is performed using <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetEncryptedKeyLength</span> and <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_EncryptKey</span>.
+These two functions, together with the corresponding decryption function (<span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_DecryptKey</span>),
+are the integration points where an OEM manufacturer may implement their own
+key-encryption scheme.</p>
+
+<p class=MsoBodyText><b>Note:</b> The key-encryption key must be unique to each
+device; this is what makes the files forward lock–protected. Ideally, it should
+be derived from secret hardware parameters, but at the very least it should be
+persistent from one master reset to the next.</p>
+
+<div style='margin-bottom:24.0pt;border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
+background:#F2F2F2'>
+
+<p class=MsoBodyText style='background:#F2F2F2;border:
+none;padding:0cm'><b>Note:</b> In the open-source implementation of the <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>libfwdlock-common</span>
+library, a random key-encryption key is generated and stored in plaintext in
+the file system, without being obfuscated in any way (doing so would be futile
+since the source code is openly available). This key must be kept secret from
+the user, and shouldnÂ’t be possible to extract through backup-and-restore
+functionality or the like. OEM manufacturers will probably want to implement a
+truly hardware-based device-unique key.</p>
+
+</div>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=531 height=563
+src="images/image002.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263085187">Figure </a>2. Converter UC: Convert Data.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Toc276471427"></a><a name="_Ref263163082">3.1.2<span
+style='font:7.0pt "Times New Roman"'> </span>Convert File
+(Pull-Mode Conversion)</a></h3>
+
+<p class=MsoBodyText>Pull-mode conversion is performed by calling <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertFile</span>
+with the filename, unless there is need for a specialized <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>read</span> function, in
+which case <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertOpenFile</span>
+should be used directly instead. See Figure 3.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'>Internally, <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertFile</span>
+calls <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertOpenFile</span>.
+The latter then proceeds with the conversion using the push-mode API, acting as
+the client in the previous use case; see section 3.1.1.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=531 height=731
+src="images/image003.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263085208">Figure </a>3. Converter UC: Convert File.</p>
+
+<b><i><span style='font-size:14.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></i></b>
+
+<h2><a name="_Toc276471428">3.2<span style='font:7.0pt "Times New Roman"'>
+</span>Decoder</a></h2>
+
+<p class=MsoBodyText>The decoder API allows the client to do the following:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'> </span>Check
+the integrity of an internal Forward Lock file, i.e., detect whether it has
+been manipulated in any way; see section 3.2.1.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'> </span>Get
+the MIME type of the embedded content (the “original” MIME type before DRM protection
+was applied); see section 3.2.2.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span
+style='font:7.0pt "Times New Roman"'> </span>Decode
+the file by random access, i.e., read and seek through it in an arbitrary
+manner; see section 3.2.3.</p>
+
+<p class=MsoBodyText>All subsequent operations on a file first require it to be
+opened. Opening a file returns a <i>file descriptor</i>—a handle to be used in
+these subsequent operations.</p>
+
+<p class=MsoBodyText>If the filename is known, an internal Forward Lock file
+can be opened using <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>.
+If only the file descriptor of an already open file is available, a decoding
+session can instead be initialized using <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>.</p>
+
+<p class=MsoBodyText>Internally, <span style='font-size:10.0pt;font-family:
+"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> calls <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>. For efficiency
+reasons, <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>
+therefore assumes that the file position is at the beginning of the file when
+the function gets called. A client who calls it directly must make sure that
+this assumption holds.</p>
+
+<p class=MsoBodyText>When a file is being attached, the session key stored in
+the file during conversion is decrypted using <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetEncryptedKeyLength</span> and <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_DecryptKey</span>,
+in order to set up for decoding and integrity checking.</p>
+
+<p class=MsoBodyText>For just getting the content type, however, retrieving the
+session key would strictly speaking not be necessary, so there is an
+opportunity here to optimize for that if it proves necessary later.</p>
+
+<p class=MsoBodyText>Symmetrical to <span style='font-size:10.0pt;font-family:
+"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> and <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>, there are also functions
+for closing a file or detaching from it:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'> </span>If
+it was opened with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>
+it should be closed with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_close</span>.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'> </span>If
+it was attached with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>
+it should be detached with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_detach</span>.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Ref263163099"></a><a name="_Toc276471429">3.2.1<span
+style='font:7.0pt "Times New Roman"'> </span>Check Integrity</a></h3>
+
+<p class=MsoBodyText>There are three methods for checking the integrity of an
+internal Forward Lock file, in whole or in part (see also Figure 4):</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'> </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>,
+which checks the integrity of the encrypted content data.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'> </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span>,
+which checks the integrity of the file header, including the content type and
+other fields not currently supported but reserved for future use.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span
+style='font:7.0pt "Times New Roman"'> </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckIntegrity</span>,
+which internally calls first <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span>
+and then <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'><span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span> is
+generally much faster than <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>,
+whose running time is directly proportional to the size of the file.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=575
+src="images/image004.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263163308">Figure </a>4. Decoder UC: Check Integrity.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Toc276471430"></a><a name="_Ref263163117">3.2.2<span
+style='font:7.0pt "Times New Roman"'> </span>Get Content Type</a></h3>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'><span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_GetContentType</span> returns a
+read-only reference to an ASCII string containing the MIME type of the
+embedded content. This reference is valid as long as the file is kept open.
+Clients who need access to the content type after closing the file should make
+a copy of the string. See Figure 5 below.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=488
+src="images/image005.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263163392">Figure </a>5. Decoder UC: Get Content Type.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Toc276471431"></a><a name="_Ref263163137">3.2.3<span
+style='font:7.0pt "Times New Roman"'> </span>Decode File</a></h3>
+
+<p class=MsoBodyText>After opening an internal Forward Lock file (or attaching
+to an already open one), it can be transparently read from as if it were
+unencrypted. Any number of calls to read data from the current file position or
+set it to a new one (which is what <span style='font-size:10.0pt;font-family:
+"Lucida Console","DejaVu Sans Mono"'>lseek</span> does) can be made in any order; this is what we
+call <i>random access</i>. See Figure 6.</p>
+
+<p class=MsoBodyText>The Forward Lock Decoder versions of the <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>read</span>, <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>lseek</span>, and <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>close</span> functions
+have the exact same signatures as their POSIX counterparts. So, for example,
+the call <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_lseek(fd,
+0, SEEK_END)</span> returns the size of the embedded content data, i.e., the
+size of the original file before DRM protection.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'>Moreover, <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>
+is like regular POSIX <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>open</span>
+except it takes only the filename as a parameter—access is always read-only.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=522
+src="images/image006.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263166303">Figure </a>6. Decoder UC: Decode File.</p>
+
+<b><span style='font-size:16.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h1><a name="_Toc276471432">4<span style='font:7.0pt "Times New Roman"'>
+</span>Definition of the Internal Forward Lock File Format</a></h1>
+
+<p class=MsoBodyText style='margin-bottom:12.0pt'>The inner structure of an internal
+Forward Lock file is defined in Table 1 below.</p>
+
+<table class=MsoNormalTable border=1 cellspacing=0 cellpadding=0
+ style='border-collapse:collapse;border:none'>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'><b>Offset [bytes]</b></p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border:solid windowtext 1.0pt;
+ border-left:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'><b>Size [bytes]</b></p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border:solid windowtext 1.0pt;
+ border-left:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'><b>Description</b></p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>0</p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>4</p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>The file signature (so-called
+ <i>magic number</i>): a four-character code consisting of the letters
+ F-W-L-K.</p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>4</p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>1</p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>Version number (0 for the
+ first version).</p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>5</p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>1</p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>Indicates the subformat:</p>
+ <p class=MsoNormal style='page-break-after:avoid'><i>0x00 Forward Lock</i></p>
+ <p class=MsoNormal style='page-break-after:avoid'><i>0x01 Combined Delivery</i></p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>6</p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>1</p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>Usage restriction flags (prohibitions
+ against usage as ringtone or as wallpaper and screen saver). Also indicates
+ if the file is bound to a specific SIM card.</p>
+ <p class=MsoNormal style='page-break-after:avoid'><i>0x00 No usage
+ restrictions</i></p>
+ <p class=MsoNormal style='page-break-after:avoid'><i>0x01 Ringtone usage
+ prohibited</i></p>
+ <p class=MsoNormal style='page-break-after:avoid'><i>0x02 Screen usage
+ prohibited</i></p>
+ <p class=MsoNormal style='page-break-after:avoid'><i>0x80 Bound to SIM</i></p>
+ <p class=MsoNormal style='page-break-after:avoid'>(Any number of these may be
+ OR-ed together.)</p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>7</p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>1</p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>Length of the MIME content
+ type (<i>k</i>).</p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>8</p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'><i>k</i></p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>The MIME content type
+ (ASCII-encoded without null-character termination).</p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i></p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'><i>l </i>= 0 or 16</p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>If the subformat is
+ Combined Delivery, this field contains the auto-generated content ID (16 bytes).
+ If not, this field is zero-size.</p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i></p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'><i>m </i>= 0 or 9</p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>If the file is bound to a
+ specific SIM card, this field contains the 9-byte packed IMSI number. If not,
+ this field is zero-size.</p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i>+<i>m</i></p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'><i>n</i> ≥ 16</p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>The encrypted session key, the
+ first sixteen bytes of which are also used as the CTR-mode <i>nonce</i> (similar
+ to the CBC-mode <i>initialization vector</i>).</p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>20</p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>Data signature—the SHA-1
+ HMAC of the encrypted content data.</p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>28+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>20</p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>Header signature—the SHA-1
+ HMAC of all the fields above, including the encrypted session key and data
+ signature.</p>
+ </td>
+ </tr>
+ <tr>
+ <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+ border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>48+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p>
+ </td>
+ <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+ border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'><i><to the end of the
+ file></i></p>
+ </td>
+ <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+ none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+ padding:0cm 5.4pt 0cm 5.4pt'>
+ <p class=MsoNormal style='page-break-after:avoid'>The content data encrypted
+ using 128-bit AES in CTR mode.</p>
+ </td>
+ </tr>
+</table>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm;page-break-after:avoid'><a name="_Ref151269206">Table </a>1. Definition of the fields of an internal Forward Lock file.</p>
+
+<p class=MsoBodyText>As of now, neither Combined Delivery nor usage
+restrictions (including SIM binding) are supported. These fields are reserved
+for future use.</p>
+
+<h2><a name="_Toc276471433">4.1<span style='font:7.0pt "Times New Roman"'>
+</span>Key Derivation</a></h2>
+
+<p class=MsoBodyText>The session key consists of sixteen bytes fetched from a
+cryptographically secure random number generator. From the session key, two
+separate keys are derived: one used for encryption, the other for signing.</p>
+
+<p class=MsoBodyText>The encryption key is the output from encrypting the
+16-byte all-zero input block {0, 0, Â…, 0} using 128-bit AES with the random session
+key as the key. The signing key is the output from encrypting the 16-byte input
+block {1, 0, Â…, 0} the same way. The keys so derived will be cryptographically
+independent from each other.</p>
+
+<p class=MsoBodyText>The session key is encrypted using a hardware-dependent
+key-encryption key unique to each device. The encrypted session key is stored
+inside the file, and its first sixteen bytes are also used as the <i>nonce</i>
+for the CTR-mode encryption of the content data.</p>
+
+<h2><a name="_Toc276471434">4.2<span style='font:7.0pt "Times New Roman"'>
+</span>Calculation of the Counters</a></h2>
+
+<p class=MsoBodyText>Using CTR (“counter”) mode, a block cipher such as AES can
+be turned into a stream cipher. The process of encryption and decryption is
+well defined in [1], except for the specifics of the calculation of the
+counters. For the internal Forward Lock file format, the counters are
+calculated as follows:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'> </span>The
+nonce is interpreted as a 128-bit unsigned integer in little-endian format.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'> </span>The
+zero-based block sequence number (also a little-endian unsigned integer) is
+added modulo 2<sup>128</sup> to the nonce to produce the counter for a given
+block.</p>
+
+<h1><a name="_Toc276471435">5<span style='font:7.0pt "Times New Roman"'>
+</span>Unit Test Cases</a></h1>
+
+<p class=MsoBodyText>Unit test cases for the converter and decoder come in two
+varieties:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'> </span><i>Black-box</i>
+test cases aim to verify that you get sensible results from malformed or
+“tricky” input data.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'> </span><i>White-box</i>
+test cases aim to maximize code coverage using knowledge of code internals.</p>
+
+<p class=MsoBodyText>The black-box test cases are dependent on a specifically
+designed set of input files found in the <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>forward-lock/internal-format/test/res</span>
+directory in the repository. For ‘tests’ variants of the software, these input
+files will be automatically installed in the file system image during build.</p>
+
+<p class=MsoBodyText>Run the test cases from the ADB shell command line as
+follows:</p>
+
+<p class=MsoNormal style='margin-top:0cm;margin-right:0cm;margin-bottom:6.0pt;
+margin-left:21.55pt'><span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>#
+gtest_fwdlock</span></p>
+
+<p class=MsoBodyText>If all black-box but no white-box test cases fail, the
+input files probably canÂ’t be found in the working directory.</p>
+
+<h1><a name="_Toc276471436">6<span style='font:7.0pt "Times New Roman"'>
+</span>References</a></h1>
+
+<p class=MsoBodyText style='margin-left:28.9pt;text-indent:-28.9pt'>[1]<span
+style='font:7.0pt "Times New Roman"'>
+</span><a
+href="http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf">Dworkin,
+Morris: “Recommendation for Block Cipher Modes of Operation—Methods and
+Techniques,” NIST Special Publication 800-38A, December 2001.</a><a
+name="_Ref151269073"></a></p>
+
+</div>
+
+</body>
+
+</html>
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif
new file mode 100644
index 0000000..ee94513
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif
new file mode 100644
index 0000000..8c12f46
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif
new file mode 100644
index 0000000..9e019ca
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif
new file mode 100644
index 0000000..cae1d01
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif
new file mode 100644
index 0000000..0d87be9
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif
new file mode 100644
index 0000000..9445b6b
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/passthru/Android.mk b/drm/libdrmframework/plugins/passthru/Android.mk
index 7856d37..be18b64 100644
--- a/drm/libdrmframework/plugins/passthru/Android.mk
+++ b/drm/libdrmframework/plugins/passthru/Android.mk
@@ -32,7 +32,7 @@
LOCAL_SHARED_LIBRARIES += libdl
endif
-LOCAL_PRELINK_MODULE := false
+
LOCAL_C_INCLUDES += \
$(TOP)/frameworks/base/drm/libdrmframework/include \
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index e5f7e62..7106bfa 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -20,121 +20,28 @@
#include <utils/Timers.h>
#include <camera/ICameraClient.h>
#include <gui/ISurfaceTexture.h>
+#include <system/camera.h>
namespace android {
-/*
- * A set of bit masks for specifying how the received preview frames are
- * handled before the previewCallback() call.
- *
- * The least significant 3 bits of an "int" value are used for this purpose:
- *
- * ..... 0 0 0
- * ^ ^ ^
- * | | |---------> determine whether the callback is enabled or not
- * | |-----------> determine whether the callback is one-shot or not
- * |-------------> determine whether the frame is copied out or not
- *
- * WARNING:
- * When a frame is sent directly without copying, it is the frame receiver's
- * responsiblity to make sure that the frame data won't get corrupted by
- * subsequent preview frames filled by the camera. This flag is recommended
- * only when copying out data brings significant performance price and the
- * handling/processing of the received frame data is always faster than
- * the preview frame rate so that data corruption won't occur.
- *
- * For instance,
- * 1. 0x00 disables the callback. In this case, copy out and one shot bits
- * are ignored.
- * 2. 0x01 enables a callback without copying out the received frames. A
- * typical use case is the Camcorder application to avoid making costly
- * frame copies.
- * 3. 0x05 is enabling a callback with frame copied out repeatedly. A typical
- * use case is the Camera application.
- * 4. 0x07 is enabling a callback with frame copied out only once. A typical use
- * case is the Barcode scanner application.
- */
-#define FRAME_CALLBACK_FLAG_ENABLE_MASK 0x01
-#define FRAME_CALLBACK_FLAG_ONE_SHOT_MASK 0x02
-#define FRAME_CALLBACK_FLAG_COPY_OUT_MASK 0x04
-
-// Typical use cases
-#define FRAME_CALLBACK_FLAG_NOOP 0x00
-#define FRAME_CALLBACK_FLAG_CAMCORDER 0x01
-#define FRAME_CALLBACK_FLAG_CAMERA 0x05
-#define FRAME_CALLBACK_FLAG_BARCODE_SCANNER 0x07
-
-// msgType in notifyCallback and dataCallback functions
-enum {
- CAMERA_MSG_ERROR = 0x001,
- CAMERA_MSG_SHUTTER = 0x002,
- CAMERA_MSG_FOCUS = 0x004,
- CAMERA_MSG_ZOOM = 0x008,
- CAMERA_MSG_PREVIEW_FRAME = 0x010,
- CAMERA_MSG_VIDEO_FRAME = 0x020,
- CAMERA_MSG_POSTVIEW_FRAME = 0x040,
- CAMERA_MSG_RAW_IMAGE = 0x080,
- CAMERA_MSG_COMPRESSED_IMAGE = 0x100,
- CAMERA_MSG_ALL_MSGS = 0x1FF
-};
-
-// cmdType in sendCommand functions
-enum {
- CAMERA_CMD_START_SMOOTH_ZOOM = 1,
- CAMERA_CMD_STOP_SMOOTH_ZOOM = 2,
- // Set the clockwise rotation of preview display (setPreviewDisplay) in
- // degrees. This affects the preview frames and the picture displayed after
- // snapshot. This method is useful for portrait mode applications. Note that
- // preview display of front-facing cameras is flipped horizontally before
- // the rotation, that is, the image is reflected along the central vertical
- // axis of the camera sensor. So the users can see themselves as looking
- // into a mirror.
- //
- // This does not affect the order of byte array of CAMERA_MSG_PREVIEW_FRAME,
- // CAMERA_MSG_VIDEO_FRAME, CAMERA_MSG_POSTVIEW_FRAME, CAMERA_MSG_RAW_IMAGE,
- // or CAMERA_MSG_COMPRESSED_IMAGE. This is not allowed to be set during
- // preview.
- CAMERA_CMD_SET_DISPLAY_ORIENTATION = 3,
-
- // cmdType to disable/enable shutter sound.
- // In sendCommand passing arg1 = 0 will disable,
- // while passing arg1 = 1 will enable the shutter sound.
- CAMERA_CMD_ENABLE_SHUTTER_SOUND = 4,
-
- // cmdType to play recording sound.
- CAMERA_CMD_PLAY_RECORDING_SOUND = 5,
-};
-
-// camera fatal errors
-enum {
- CAMERA_ERROR_UNKNOWN = 1,
- CAMERA_ERROR_SERVER_DIED = 100
-};
-
-enum {
- CAMERA_FACING_BACK = 0, /* The facing of the camera is opposite to that of the screen. */
- CAMERA_FACING_FRONT = 1 /* The facing of the camera is the same as that of the screen. */
-};
-
struct CameraInfo {
-
/**
- * The direction that the camera faces to. It should be
- * CAMERA_FACING_BACK or CAMERA_FACING_FRONT.
+ * The direction that the camera faces to. It should be CAMERA_FACING_BACK
+ * or CAMERA_FACING_FRONT.
*/
int facing;
/**
* The orientation of the camera image. The value is the angle that the
- * camera image needs to be rotated clockwise so it shows correctly on
- * the display in its natural orientation. It should be 0, 90, 180, or 270.
+ * camera image needs to be rotated clockwise so it shows correctly on the
+ * display in its natural orientation. It should be 0, 90, 180, or 270.
*
* For example, suppose a device has a naturally tall screen. The
* back-facing camera sensor is mounted in landscape. You are looking at
* the screen. If the top side of the camera sensor is aligned with the
* right edge of the screen in natural orientation, the value should be
- * 90. If the top side of a front-facing camera sensor is aligned with
- * the right of the screen, the value should be 270.
+ * 90. If the top side of a front-facing camera sensor is aligned with the
+ * right of the screen, the value should be 270.
*/
int orientation;
};
@@ -207,7 +114,7 @@
status_t cancelAutoFocus();
// take a picture - picture returned from callback
- status_t takePicture();
+ status_t takePicture(int msgType);
// set preview/capture parameters - key/value pairs
status_t setParameters(const String8& params);
@@ -218,12 +125,6 @@
// send command to camera driver
status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
- // return the total number of available video buffers.
- int32_t getNumberOfVideoBuffers() const;
-
- // return the individual video buffer corresponding to the given index.
- sp<IMemory> getVideoBuffer(int32_t index) const;
-
// tell camera hal to store meta data or real YUV in video buffers.
status_t storeMetaDataInBuffers(bool enabled);
diff --git a/include/camera/CameraHardwareInterface.h b/include/camera/CameraHardwareInterface.h
deleted file mode 100644
index 86bd849..0000000
--- a/include/camera/CameraHardwareInterface.h
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2008 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_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
-#define ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
-
-#include <binder/IMemory.h>
-#include <ui/egl/android_natives.h>
-#include <utils/RefBase.h>
-#include <surfaceflinger/ISurface.h>
-#include <ui/android_native_buffer.h>
-#include <ui/GraphicBuffer.h>
-#include <camera/Camera.h>
-#include <camera/CameraParameters.h>
-
-namespace android {
-
-/**
- * The size of image for display.
- */
-typedef struct image_rect_struct
-{
- uint32_t width; /* Image width */
- uint32_t height; /* Image height */
-} image_rect_type;
-
-
-typedef void (*notify_callback)(int32_t msgType,
- int32_t ext1,
- int32_t ext2,
- void* user);
-
-typedef void (*data_callback)(int32_t msgType,
- const sp<IMemory>& dataPtr,
- void* user);
-
-typedef void (*data_callback_timestamp)(nsecs_t timestamp,
- int32_t msgType,
- const sp<IMemory>& dataPtr,
- void* user);
-
-/**
- * CameraHardwareInterface.h defines the interface to the
- * camera hardware abstraction layer, used for setting and getting
- * parameters, live previewing, and taking pictures.
- *
- * It is a referenced counted interface with RefBase as its base class.
- * CameraService calls openCameraHardware() to retrieve a strong pointer to the
- * instance of this interface and may be called multiple times. The
- * following steps describe a typical sequence:
- *
- * -# After CameraService calls openCameraHardware(), getParameters() and
- * setParameters() are used to initialize the camera instance.
- * CameraService calls getPreviewHeap() to establish access to the
- * preview heap so it can be registered with SurfaceFlinger for
- * efficient display updating while in preview mode.
- * -# startPreview() is called. The camera instance then periodically
- * sends the message CAMERA_MSG_PREVIEW_FRAME (if enabled) each time
- * a new preview frame is available. If data callback code needs to use
- * this memory after returning, it must copy the data.
- *
- * Prior to taking a picture, CameraService calls autofocus(). When auto
- * focusing has completed, the camera instance sends a CAMERA_MSG_FOCUS notification,
- * which informs the application whether focusing was successful. The camera instance
- * only sends this message once and it is up to the application to call autoFocus()
- * again if refocusing is desired.
- *
- * CameraService calls takePicture() to request the camera instance take a
- * picture. At this point, if a shutter, postview, raw, and/or compressed callback
- * is desired, the corresponding message must be enabled. As with CAMERA_MSG_PREVIEW_FRAME,
- * any memory provided in a data callback must be copied if it's needed after returning.
- */
-class CameraHardwareInterface : public virtual RefBase {
-public:
- virtual ~CameraHardwareInterface() { }
-
- /** Set the ANativeWindow to which preview frames are sent */
- virtual status_t setPreviewWindow(const sp<ANativeWindow>& buf) = 0;
-
- /** Return the IMemoryHeap for the raw image heap */
- virtual sp<IMemoryHeap> getRawHeap() const = 0;
-
- /** Set the notification and data callbacks */
- virtual void setCallbacks(notify_callback notify_cb,
- data_callback data_cb,
- data_callback_timestamp data_cb_timestamp,
- void* user) = 0;
-
- /**
- * The following three functions all take a msgtype,
- * which is a bitmask of the messages defined in
- * include/ui/Camera.h
- */
-
- /**
- * Enable a message, or set of messages.
- */
- virtual void enableMsgType(int32_t msgType) = 0;
-
- /**
- * Disable a message, or a set of messages.
- *
- * Once received a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), camera hal
- * should not rely on its client to call releaseRecordingFrame() to release
- * video recording frames sent out by the cameral hal before and after the
- * disableMsgType(CAMERA_MSG_VIDEO_FRAME) call. Camera hal clients must not
- * modify/access any video recording frame after calling
- * disableMsgType(CAMERA_MSG_VIDEO_FRAME).
- */
- virtual void disableMsgType(int32_t msgType) = 0;
-
- /**
- * Query whether a message, or a set of messages, is enabled.
- * Note that this is operates as an AND, if any of the messages
- * queried are off, this will return false.
- */
- virtual bool msgTypeEnabled(int32_t msgType) = 0;
-
- /**
- * Start preview mode.
- */
- virtual status_t startPreview() = 0;
-
- /**
- * Stop a previously started preview.
- */
- virtual void stopPreview() = 0;
-
- /**
- * Returns true if preview is enabled.
- */
- virtual bool previewEnabled() = 0;
-
- /**
- * Retrieve the total number of available buffers from camera hal for passing
- * video frame data in a recording session. Must be called again if a new
- * recording session is started.
- *
- * This method should be called after startRecording(), since
- * the some camera hal may choose to allocate the video buffers only after
- * recording is started.
- *
- * Some camera hal may not implement this method, and 0 can be returned to
- * indicate that this feature is not available.
- *
- * @return the number of video buffers that camera hal makes available.
- * Zero (0) is returned to indicate that camera hal does not support
- * this feature.
- */
- virtual int32_t getNumberOfVideoBuffers() const { return 0; }
-
- /**
- * Retrieve the video buffer corresponding to the given index in a
- * recording session. Must be called again if a new recording session
- * is started.
- *
- * It allows a client to retrieve all video buffers that camera hal makes
- * available to passing video frame data by calling this method with all
- * valid index values. The valid index value ranges from 0 to n, where
- * n = getNumberOfVideoBuffers() - 1. With an index outside of the valid
- * range, 0 must be returned. This method should be called after
- * startRecording().
- *
- * The video buffers should NOT be modified/released by camera hal
- * until stopRecording() is called and all outstanding video buffers
- * previously sent out via CAMERA_MSG_VIDEO_FRAME have been released
- * via releaseVideoBuffer().
- *
- * @param index an index to retrieve the corresponding video buffer.
- *
- * @return the video buffer corresponding to the given index.
- */
- virtual sp<IMemory> getVideoBuffer(int32_t index) const { return 0; }
-
- /**
- * Request the camera hal to store meta data or real YUV data in
- * the video buffers send out via CAMERA_MSG_VIDEO_FRRAME for a
- * recording session. If it is not called, the default camera
- * hal behavior is to store real YUV data in the video buffers.
- *
- * This method should be called before startRecording() in order
- * to be effective.
- *
- * If meta data is stored in the video buffers, it is up to the
- * receiver of the video buffers to interpret the contents and
- * to find the actual frame data with the help of the meta data
- * in the buffer. How this is done is outside of the scope of
- * this method.
- *
- * Some camera hal may not support storing meta data in the video
- * buffers, but all camera hal should support storing real YUV data
- * in the video buffers. If the camera hal does not support storing
- * the meta data in the video buffers when it is requested to do
- * do, INVALID_OPERATION must be returned. It is very useful for
- * the camera hal to pass meta data rather than the actual frame
- * data directly to the video encoder, since the amount of the
- * uncompressed frame data can be very large if video size is large.
- *
- * @param enable if true to instruct the camera hal to store
- * meta data in the video buffers; false to instruct
- * the camera hal to store real YUV data in the video
- * buffers.
- *
- * @return OK on success.
- */
- virtual status_t storeMetaDataInBuffers(bool enable) {
- return enable? INVALID_OPERATION: OK;
- }
-
- /**
- * Start record mode. When a record image is available a CAMERA_MSG_VIDEO_FRAME
- * message is sent with the corresponding frame. Every record frame must be released
- * by a cameral hal client via releaseRecordingFrame() before the client calls
- * disableMsgType(CAMERA_MSG_VIDEO_FRAME). After the client calls
- * disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's responsibility
- * to manage the life-cycle of the video recording frames, and the client must
- * not modify/access any video recording frames.
- */
- virtual status_t startRecording() = 0;
-
- /**
- * Stop a previously started recording.
- */
- virtual void stopRecording() = 0;
-
- /**
- * Returns true if recording is enabled.
- */
- virtual bool recordingEnabled() = 0;
-
- /**
- * Release a record frame previously returned by CAMERA_MSG_VIDEO_FRAME.
- *
- * It is camera hal client's responsibility to release video recording
- * frames sent out by the camera hal before the camera hal receives
- * a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME). After it receives
- * the call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's
- * responsibility of managing the life-cycle of the video recording
- * frames.
- */
- virtual void releaseRecordingFrame(const sp<IMemory>& mem) = 0;
-
- /**
- * Start auto focus, the notification callback routine is called
- * with CAMERA_MSG_FOCUS once when focusing is complete. autoFocus()
- * will be called again if another auto focus is needed.
- */
- virtual status_t autoFocus() = 0;
-
- /**
- * Cancels auto-focus function. If the auto-focus is still in progress,
- * this function will cancel it. Whether the auto-focus is in progress
- * or not, this function will return the focus position to the default.
- * If the camera does not support auto-focus, this is a no-op.
- */
- virtual status_t cancelAutoFocus() = 0;
-
- /**
- * Take a picture.
- */
- virtual status_t takePicture() = 0;
-
- /**
- * Cancel a picture that was started with takePicture. Calling this
- * method when no picture is being taken is a no-op.
- */
- virtual status_t cancelPicture() = 0;
-
- /**
- * Set the camera parameters. This returns BAD_VALUE if any parameter is
- * invalid or not supported. */
- virtual status_t setParameters(const CameraParameters& params) = 0;
-
- /** Return the camera parameters. */
- virtual CameraParameters getParameters() const = 0;
-
- /**
- * Send command to camera driver.
- */
- virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) = 0;
-
- /**
- * Release the hardware resources owned by this object. Note that this is
- * *not* done in the destructor.
- */
- virtual void release() = 0;
-
- /**
- * Dump state of the camera hardware
- */
- virtual status_t dump(int fd, const Vector<String16>& args) const = 0;
-};
-
-/**
- * The functions need to be provided by the camera HAL.
- *
- * If getNumberOfCameras() returns N, the valid cameraId for getCameraInfo()
- * and openCameraHardware() is 0 to N-1.
- */
-extern "C" int HAL_getNumberOfCameras();
-extern "C" void HAL_getCameraInfo(int cameraId, struct CameraInfo* cameraInfo);
-/* HAL should return NULL if it fails to open camera hardware. */
-extern "C" sp<CameraHardwareInterface> HAL_openCameraHardware(int cameraId);
-
-}; // namespace android
-
-#endif
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 431aaa4..dc5fc84 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -247,6 +247,45 @@
// Supported focus modes.
// Example value: "auto,macro,fixed". Read only.
static const char KEY_SUPPORTED_FOCUS_MODES[];
+ // The maximum number of focus areas supported. This is the maximum length
+ // of KEY_FOCUS_AREAS.
+ // Example value: "0" or "2". Read only.
+ static const char KEY_MAX_NUM_FOCUS_AREAS[];
+ // Current focus areas.
+ //
+ // Before accessing this parameter, apps should check
+ // KEY_MAX_NUM_FOCUS_AREAS first to know the maximum number of focus areas
+ // first. If the value is 0, focus area is not supported.
+ //
+ // Each focus area is a five-element int array. The first four elements are
+ // the rectangle of the area (left, top, right, bottom). The direction is
+ // relative to the sensor orientation, that is, what the sensor sees. The
+ // direction is not affected by the rotation or mirroring of
+ // CAMERA_CMD_SET_DISPLAY_ORIENTATION. Coordinates range from -1000 to 1000.
+ // (-1000,-1000) is the upper left point. (1000, 1000) is the lower right
+ // point. The length and width of focus areas cannot be 0 or negative.
+ //
+ // The fifth element is the weight. Values for weight must range from 1 to
+ // 1000. The weight should be interpreted as a per-pixel weight - all
+ // pixels in the area have the specified weight. This means a small area
+ // with the same weight as a larger area will have less influence on the
+ // focusing than the larger area. Focus areas can partially overlap and the
+ // driver will add the weights in the overlap region.
+ //
+ // A special case of single focus area (0,0,0,0,0) means driver to decide
+ // the focus area. For example, the driver may use more signals to decide
+ // focus areas and change them dynamically. Apps can set (0,0,0,0,0) if they
+ // want the driver to decide focus areas.
+ //
+ // Focus areas are relative to the current field of view (KEY_ZOOM). No
+ // matter what the zoom level is, (-1000,-1000) represents the top of the
+ // currently visible camera frame. The focus area cannot be set to be
+ // outside the current field of view, even when using zoom.
+ //
+ // Focus area only has effect if the current focus mode is FOCUS_MODE_AUTO,
+ // FOCUS_MODE_MACRO, or FOCUS_MODE_CONTINOUS_VIDEO.
+ // Example value: "(-10,-10,0,0,300),(0,0,10,10,700)". Read/write.
+ static const char KEY_FOCUS_AREAS[];
// Focal length in millimeter.
// Example value: "4.31". Read only.
static const char KEY_FOCAL_LENGTH[];
@@ -270,6 +309,94 @@
// 0.3333, EV is -2.
// Example value: "0.333333333" or "0.5". Read only.
static const char KEY_EXPOSURE_COMPENSATION_STEP[];
+ // The state of the auto-exposure lock. "true" means that
+ // auto-exposure is locked to its current value and will not
+ // change. "false" means the auto-exposure routine is free to
+ // change exposure values. If auto-exposure is already locked,
+ // setting this to true again has no effect (the driver will not
+ // recalculate exposure values). Changing exposure compensation
+ // settings will still affect the exposure settings while
+ // auto-exposure is locked. Stopping preview or taking a still
+ // image will release the lock. However, the lock can be
+ // re-enabled prior to preview being re-started, to keep the
+ // exposure values from the previous lock. In conjunction with
+ // exposure compensation, this allows for capturing multi-exposure
+ // brackets with known relative exposure values. Locking
+ // auto-exposure after open but before the first call to
+ // startPreview may result in severely over- or under-exposed
+ // images. The driver may independently enable the AE lock after
+ // auto-focus completes. If it does so, this key must have its
+ // value updated to reflect the lock's existence. Applications are
+ // free to release such a lock, to re-enable AE without restarting
+ // preview.
+ static const char KEY_AUTO_EXPOSURE_LOCK[];
+ // Whether locking the auto-exposure is supported. "true" means it is, and
+ // "false" or this key not existing means it is not supported.
+ static const char KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[];
+ // The state of the auto-white balance lock. "true" means that
+ // auto-white balance is locked to its current value and will not
+ // change. "false" means the auto-white balance routine is free to
+ // change white balance values. If auto-white balance is already
+ // locked, setting this to true again has no effect (the driver
+ // will not recalculate white balance values). Stopping preview or
+ // taking a still image will release the lock. However, the lock
+ // can be re-enabled prior to preview being re-started, to keep
+ // the white balance values from the previous lock. In conjunction
+ // with exposure compensation, this allows for capturing
+ // multi-exposure brackets with fixed white balance. Locking
+ // auto-white balance after open but before the first call to
+ // startPreview may result in severely incorrect color. The
+ // driver may independently enable the AWB lock after auto-focus
+ // completes. If it does so, this key must have its value updated
+ // to reflect the lock's existence. Applications are free to
+ // release such a lock, to re-enable AWB without restarting
+ // preview.
+ static const char KEY_AUTO_WHITEBALANCE_LOCK[];
+ // Whether locking the auto-white balance is supported. "true"
+ // means it is, and "false" or this key not existing means it is
+ // not supported.
+ static const char KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED[];
+
+ // The maximum number of metering areas supported. This is the maximum
+ // length of KEY_METERING_AREAS.
+ // Example value: "0" or "2". Read only.
+ static const char KEY_MAX_NUM_METERING_AREAS[];
+ // Current metering areas. Camera driver uses these areas to decide
+ // exposure.
+ //
+ // Before accessing this parameter, apps should check
+ // KEY_MAX_NUM_METERING_AREAS first to know the maximum number of metering
+ // areas first. If the value is 0, metering area is not supported.
+ //
+ // Each metering area is a rectangle with specified weight. The direction is
+ // relative to the sensor orientation, that is, what the sensor sees. The
+ // direction is not affected by the rotation or mirroring of
+ // CAMERA_CMD_SET_DISPLAY_ORIENTATION. Coordinates of the rectangle range
+ // from -1000 to 1000. (-1000, -1000) is the upper left point. (1000, 1000)
+ // is the lower right point. The length and width of metering areas cannot
+ // be 0 or negative.
+ //
+ // The fifth element is the weight. Values for weight must range from 1 to
+ // 1000. The weight should be interpreted as a per-pixel weight - all
+ // pixels in the area have the specified weight. This means a small area
+ // with the same weight as a larger area will have less influence on the
+ // metering than the larger area. Metering areas can partially overlap and
+ // the driver will add the weights in the overlap region.
+ //
+ // A special case of all-zero single metering area means driver to decide
+ // the metering area. For example, the driver may use more signals to decide
+ // metering areas and change them dynamically. Apps can set all-zero if they
+ // want the driver to decide metering areas.
+ //
+ // Metering areas are relative to the current field of view (KEY_ZOOM).
+ // No matter what the zoom level is, (-1000,-1000) represents the top of the
+ // currently visible camera frame. The metering area cannot be set to be
+ // outside the current field of view, even when using zoom.
+ //
+ // No matter what metering areas are, the final exposure are compensated
+ // by KEY_EXPOSURE_COMPENSATION.
+ // Example value: "(-10,-10,0,0,300),(0,0,10,10,700)". Read/write.
+ static const char KEY_METERING_AREAS[];
// Current zoom value.
// Example value: "0" or "6". Read/write.
static const char KEY_ZOOM[];
@@ -349,6 +476,7 @@
// Value for KEY_ZOOM_SUPPORTED or KEY_SMOOTH_ZOOM_SUPPORTED.
static const char TRUE[];
+ static const char FALSE[];
// Value for KEY_FOCUS_DISTANCES.
static const char FOCUS_DISTANCE_INFINITY[];
@@ -417,11 +545,10 @@
// Pixel color formats for KEY_PREVIEW_FORMAT, KEY_PICTURE_FORMAT,
// and KEY_VIDEO_FRAME_FORMAT
- // Planar variant of the YUV420 color format
- static const char PIXEL_FORMAT_YUV420P[];
static const char PIXEL_FORMAT_YUV422SP[];
static const char PIXEL_FORMAT_YUV420SP[]; // NV21
static const char PIXEL_FORMAT_YUV422I[]; // YUY2
+ static const char PIXEL_FORMAT_YUV420P[]; // YV12
static const char PIXEL_FORMAT_RGB565[];
static const char PIXEL_FORMAT_JPEG[];
diff --git a/include/camera/ICamera.h b/include/camera/ICamera.h
index b2310a6..400d7f4 100644
--- a/include/camera/ICamera.h
+++ b/include/camera/ICamera.h
@@ -70,7 +70,7 @@
virtual status_t startRecording() = 0;
// stop recording mode
- virtual void stopRecording() = 0;
+ virtual void stopRecording() = 0;
// get recording state
virtual bool recordingEnabled() = 0;
@@ -84,8 +84,14 @@
// cancel auto focus
virtual status_t cancelAutoFocus() = 0;
- // take a picture
- virtual status_t takePicture() = 0;
+ /*
+ * take a picture.
+ * @param msgType the message type an application selectively turn on/off
+ * on a photo-by-photo basis. The supported message types are:
+ * CAMERA_MSG_SHUTTER, CAMERA_MSG_RAW_IMAGE, CAMERA_MSG_COMPRESSED_IMAGE,
+ * and CAMERA_MSG_POSTVIEW_FRAME. Any other message types will be ignored.
+ */
+ virtual status_t takePicture(int msgType) = 0;
// set preview/capture parameters - key/value pairs
virtual status_t setParameters(const String8& params) = 0;
@@ -96,12 +102,6 @@
// send command to camera driver
virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) = 0;
- // return the total number of available video buffers
- virtual int32_t getNumberOfVideoBuffers() const = 0;
-
- // return the individual video buffer corresponding to the given index.
- virtual sp<IMemory> getVideoBuffer(int32_t index) const = 0;
-
// tell the camera hal to store meta data or real YUV data in video buffers.
virtual status_t storeMetaDataInBuffers(bool enabled) = 0;
};
diff --git a/include/drm/DrmInfoEvent.h b/include/drm/DrmInfoEvent.h
index 7b409ff..dfca228 100644
--- a/include/drm/DrmInfoEvent.h
+++ b/include/drm/DrmInfoEvent.h
@@ -43,6 +43,8 @@
//! TYPE_ACCOUNT_ALREADY_REGISTERED, when registration has been
//! already done for the given account.
static const int TYPE_ACCOUNT_ALREADY_REGISTERED = 5;
+ //! TYPE_RIGHTS_REMOVED, when the rights has been removed.
+ static const int TYPE_RIGHTS_REMOVED = 6;
/**
* The following constant values should be in sync with DrmErrorEvent.java
@@ -61,6 +63,11 @@
static const int TYPE_NO_INTERNET_CONNECTION = 2005;
//! TYPE_PROCESS_DRM_INFO_FAILED, when failed to process DrmInfo.
static const int TYPE_PROCESS_DRM_INFO_FAILED = 2006;
+ //! TYPE_REMOVE_ALL_RIGHTS_FAILED, when failed to remove all the rights objects
+ //! associated with all DRM schemes.
+ static const int TYPE_REMOVE_ALL_RIGHTS_FAILED = 2007;
+ //! TYPE_ACQUIRE_DRM_INFO_FAILED, when failed to acquire DrmInfo.
+ static const int TYPE_ACQUIRE_DRM_INFO_FAILED = 2008;
public:
/**
@@ -70,7 +77,7 @@
* @param[in] infoType Type of information
* @param[in] message Message description
*/
- DrmInfoEvent(int uniqueId, int infoType, const String8& message);
+ DrmInfoEvent(int uniqueId, int infoType, const String8 message);
/**
* Destructor for DrmInfoEvent
@@ -97,12 +104,12 @@
*
* @return Message description
*/
- const String8& getMessage() const;
+ const String8 getMessage() const;
private:
int mUniqueId;
int mInfoType;
- const String8& mMessage;
+ const String8 mMessage;
};
};
diff --git a/include/drm/DrmManagerClient.h b/include/drm/DrmManagerClient.h
index e6ba3c4..b8fe46d 100644
--- a/include/drm/DrmManagerClient.h
+++ b/include/drm/DrmManagerClient.h
@@ -49,6 +49,9 @@
class OnInfoListener: virtual public RefBase {
public:
+ virtual ~OnInfoListener() {}
+
+ public:
virtual void onInfo(const DrmInfoEvent& event) = 0;
};
@@ -66,7 +69,7 @@
* @return
* Handle for the decryption session
*/
- DecryptHandle* openDecryptSession(int fd, off64_t offset, off64_t length);
+ sp<DecryptHandle> openDecryptSession(int fd, off64_t offset, off64_t length);
/**
* Open the decrypt session to decrypt the given protected content
@@ -75,7 +78,7 @@
* @return
* Handle for the decryption session
*/
- DecryptHandle* openDecryptSession(const char* uri);
+ sp<DecryptHandle> openDecryptSession(const char* uri);
/**
* Close the decrypt session for the given handle
@@ -84,7 +87,7 @@
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t closeDecryptSession(DecryptHandle* decryptHandle);
+ status_t closeDecryptSession(sp<DecryptHandle> &decryptHandle);
/**
* Consumes the rights for a content.
@@ -98,7 +101,7 @@
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure.
* In case license has been expired, DRM_ERROR_LICENSE_EXPIRED will be returned.
*/
- status_t consumeRights(DecryptHandle* decryptHandle, int action, bool reserve);
+ status_t consumeRights(sp<DecryptHandle> &decryptHandle, int action, bool reserve);
/**
* Informs the DRM engine about the playback actions performed on the DRM files.
@@ -110,7 +113,8 @@
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t setPlaybackStatus(DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
+ status_t setPlaybackStatus(
+ sp<DecryptHandle> &decryptHandle, int playbackStatus, int64_t position);
/**
* Initialize decryption for the given unit of the protected content
@@ -122,7 +126,7 @@
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
status_t initializeDecryptUnit(
- DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo);
+ sp<DecryptHandle> &decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo);
/**
* Decrypt the protected content buffers for the given unit
@@ -141,7 +145,7 @@
* DRM_ERROR_DECRYPT for failure.
*/
status_t decrypt(
- DecryptHandle* decryptHandle, int decryptUnitId,
+ sp<DecryptHandle> &decryptHandle, int decryptUnitId,
const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV = NULL);
/**
@@ -152,7 +156,8 @@
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t finalizeDecryptUnit(DecryptHandle* decryptHandle, int decryptUnitId);
+ status_t finalizeDecryptUnit(
+ sp<DecryptHandle> &decryptHandle, int decryptUnitId);
/**
* Reads the specified number of bytes from an open DRM file.
@@ -164,7 +169,8 @@
*
* @return Number of bytes read. Returns -1 for Failure.
*/
- ssize_t pread(DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off64_t offset);
+ ssize_t pread(sp<DecryptHandle> &decryptHandle,
+ void* buffer, ssize_t numBytes, off64_t offset);
/**
* Validates whether an action on the DRM content is allowed or not.
@@ -362,8 +368,7 @@
private:
int mUniqueId;
- Mutex mDecryptLock;
- DrmManagerClientImpl* mDrmManagerClientImpl;
+ sp<DrmManagerClientImpl> mDrmManagerClientImpl;
};
};
diff --git a/include/drm/drm_framework_common.h b/include/drm/drm_framework_common.h
index 1758cdd..2632cbd 100644
--- a/include/drm/drm_framework_common.h
+++ b/include/drm/drm_framework_common.h
@@ -19,6 +19,7 @@
#include <utils/Vector.h>
#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/Errors.h>
@@ -30,19 +31,34 @@
* Error code for DRM Frameowrk
*/
enum {
- DRM_ERROR_BASE = -2000,
+ // The following constant values should be in sync with
+ // media/stagefright/MediaErrors.h
+ ERROR_BASE = -2000,
- DRM_ERROR_UNKNOWN = DRM_ERROR_BASE,
- DRM_ERROR_LICENSE_EXPIRED = DRM_ERROR_BASE - 1,
- DRM_ERROR_SESSION_NOT_OPENED = DRM_ERROR_BASE - 2,
- DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED = DRM_ERROR_BASE - 3,
- DRM_ERROR_DECRYPT = DRM_ERROR_BASE - 4,
- DRM_ERROR_CANNOT_HANDLE = DRM_ERROR_BASE - 5,
+ DRM_ERROR_UNKNOWN = ERROR_BASE,
+ DRM_ERROR_NO_LICENSE = ERROR_BASE - 1,
+ DRM_ERROR_LICENSE_EXPIRED = ERROR_BASE - 2,
+ DRM_ERROR_SESSION_NOT_OPENED = ERROR_BASE - 3,
+ DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED = ERROR_BASE - 4,
+ DRM_ERROR_DECRYPT = ERROR_BASE - 5,
+ DRM_ERROR_CANNOT_HANDLE = ERROR_BASE - 6,
+ DRM_ERROR_TAMPER_DETECTED = ERROR_BASE - 7,
DRM_NO_ERROR = NO_ERROR
};
/**
+ * copy control settings used in DecryptHandle::copyControlVector
+ */
+enum DrmCopyControl {
+ DRM_COPY_CONTROL_BASE = 1000,
+ // the key used to set the value for HDCP
+ // if the associated value is 1, then HDCP is required
+ // otherwise, HDCP is not required
+ DRM_COPY_CONTROL_HDCP = DRM_COPY_CONTROL_BASE
+};
+
+/**
* Defines DRM Buffer
*/
class DrmBuffer {
@@ -240,7 +256,7 @@
/**
* Defines decryption handle
*/
-class DecryptHandle {
+class DecryptHandle : public RefBase {
public:
/**
* Decryption session Handle
@@ -279,16 +295,32 @@
* e.g. size of memory to be allocated to get the decrypted content.
*/
DecryptInfo* decryptInfo;
+ /**
+ * Defines a vector for the copy control settings sent from the DRM plugin
+ * to the player
+ */
+ KeyedVector<DrmCopyControl, int> copyControlVector;
+
+ /**
+ * Defines a vector for any extra data the DRM plugin wants to send
+ * to the native code
+ */
+ KeyedVector<String8, String8> extendedData;
public:
DecryptHandle():
decryptId(INVALID_VALUE),
mimeType(""),
decryptApiType(INVALID_VALUE),
- status(INVALID_VALUE) {
+ status(INVALID_VALUE),
+ decryptInfo(NULL) {
}
+ ~DecryptHandle() {
+ delete decryptInfo; decryptInfo = NULL;
+ }
+
bool operator<(const DecryptHandle& handle) const {
return (decryptId < handle.decryptId);
}
diff --git a/include/media/AudioParameter.h b/include/media/AudioParameter.h
new file mode 100644
index 0000000..79d5d82
--- /dev/null
+++ b/include/media/AudioParameter.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008-2011 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_AUDIOPARAMETER_H_
+#define ANDROID_AUDIOPARAMETER_H_
+
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class AudioParameter {
+
+public:
+ AudioParameter() {}
+ AudioParameter(const String8& keyValuePairs);
+ virtual ~AudioParameter();
+
+ // reserved parameter keys for changing standard parameters with setParameters() function.
+ // Using these keys is mandatory for AudioFlinger to properly monitor audio output/input
+ // configuration changes and act accordingly.
+ // keyRouting: to change audio routing, value is an int in audio_devices_t
+ // keySamplingRate: to change sampling rate routing, value is an int
+ // keyFormat: to change audio format, value is an int in audio_format_t
+ // keyChannels: to change audio channel configuration, value is an int in audio_channels_t
+ // keyFrameCount: to change audio output frame count, value is an int
+ // keyInputSource: to change audio input source, value is an int in audio_source_t
+ // (defined in media/mediarecorder.h)
+ static const char *keyRouting;
+ static const char *keySamplingRate;
+ static const char *keyFormat;
+ static const char *keyChannels;
+ static const char *keyFrameCount;
+ static const char *keyInputSource;
+
+ String8 toString();
+
+ status_t add(const String8& key, const String8& value);
+ status_t addInt(const String8& key, const int value);
+ status_t addFloat(const String8& key, const float value);
+
+ status_t remove(const String8& key);
+
+ status_t get(const String8& key, String8& value);
+ status_t getInt(const String8& key, int& value);
+ status_t getFloat(const String8& key, float& value);
+ status_t getAt(size_t index, String8& key, String8& value);
+
+ size_t size() { return mParameters.size(); }
+
+private:
+ String8 mKeyValuePairs;
+ KeyedVector <String8, String8> mParameters;
+};
+
+}; // namespace android
+
+#endif /*ANDROID_AUDIOPARAMETER_H_*/
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 5f7cd90..baab2e8 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -30,6 +30,7 @@
#include <binder/IMemory.h>
#include <utils/threads.h>
+#include <system/audio.h>
namespace android {
@@ -127,9 +128,9 @@
*
* inputSource: Select the audio input to record to (e.g. AUDIO_SOURCE_DEFAULT).
* sampleRate: Track sampling rate in Hz.
- * format: Audio format (e.g AudioSystem::PCM_16_BIT for signed
+ * format: Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
* 16 bits per sample).
- * channels: Channel mask: see AudioSystem::audio_channels.
+ * channels: Channel mask: see audio_channels_t.
* frameCount: Total size of track PCM buffer in frames. This defines the
* latency of the track.
* flags: A bitmask of acoustic values from enum record_flags. It enables
@@ -142,15 +143,15 @@
*/
enum record_flags {
- RECORD_AGC_ENABLE = AudioSystem::AGC_ENABLE,
- RECORD_NS_ENABLE = AudioSystem::NS_ENABLE,
- RECORD_IIR_ENABLE = AudioSystem::TX_IIR_ENABLE
+ RECORD_AGC_ENABLE = AUDIO_IN_ACOUSTICS_AGC_ENABLE,
+ RECORD_NS_ENABLE = AUDIO_IN_ACOUSTICS_NS_ENABLE,
+ RECORD_IIR_ENABLE = AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE,
};
AudioRecord(int inputSource,
uint32_t sampleRate = 0,
int format = 0,
- uint32_t channels = AudioSystem::CHANNEL_IN_MONO,
+ uint32_t channels = AUDIO_CHANNEL_IN_MONO,
int frameCount = 0,
uint32_t flags = 0,
callback_t cbf = 0,
@@ -176,7 +177,7 @@
status_t set(int inputSource = 0,
uint32_t sampleRate = 0,
int format = 0,
- uint32_t channels = AudioSystem::CHANNEL_IN_MONO,
+ uint32_t channels = AUDIO_CHANNEL_IN_MONO,
int frameCount = 0,
uint32_t flags = 0,
callback_t cbf = 0,
@@ -346,12 +347,14 @@
};
bool processAudioBuffer(const sp<ClientRecordThread>& thread);
- status_t openRecord(uint32_t sampleRate,
+ status_t openRecord_l(uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
uint32_t flags,
audio_io_handle_t input);
+ audio_io_handle_t getInput_l();
+ status_t restoreRecord_l(audio_track_cblk_t*& cblk);
sp<IAudioRecord> mAudioRecord;
sp<IMemory> mCblkMemory;
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 03f8944..68cd188 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -21,10 +21,15 @@
#include <utils/threads.h>
#include <media/IAudioFlinger.h>
+#include <system/audio.h>
+#include <hardware/audio_policy.h>
+
+/* XXX: Should be include by all the users instead */
+#include <media/AudioParameter.h>
+
namespace android {
typedef void (*audio_error_callback)(status_t err);
-typedef int audio_io_handle_t;
class IAudioPolicyService;
class String8;
@@ -33,151 +38,6 @@
{
public:
- enum stream_type {
- DEFAULT =-1,
- VOICE_CALL = 0,
- SYSTEM = 1,
- RING = 2,
- MUSIC = 3,
- ALARM = 4,
- NOTIFICATION = 5,
- BLUETOOTH_SCO = 6,
- ENFORCED_AUDIBLE = 7, // Sounds that cannot be muted by user and must be routed to speaker
- DTMF = 8,
- TTS = 9,
- NUM_STREAM_TYPES
- };
-
- // Audio sub formats (see AudioSystem::audio_format).
- enum pcm_sub_format {
- PCM_SUB_16_BIT = 0x1, // must be 1 for backward compatibility
- PCM_SUB_8_BIT = 0x2, // must be 2 for backward compatibility
- };
-
- // MP3 sub format field definition : can use 11 LSBs in the same way as MP3 frame header to specify
- // bit rate, stereo mode, version...
- enum mp3_sub_format {
- //TODO
- };
-
- // AMR NB/WB sub format field definition: specify frame block interleaving, bandwidth efficient or octet aligned,
- // encoding mode for recording...
- enum amr_sub_format {
- //TODO
- };
-
- // AAC sub format field definition: specify profile or bitrate for recording...
- enum aac_sub_format {
- //TODO
- };
-
- // VORBIS sub format field definition: specify quality for recording...
- enum vorbis_sub_format {
- //TODO
- };
-
- // Audio format consists in a main format field (upper 8 bits) and a sub format field (lower 24 bits).
- // The main format indicates the main codec type. The sub format field indicates options and parameters
- // for each format. The sub format is mainly used for record to indicate for instance the requested bitrate
- // or profile. It can also be used for certain formats to give informations not present in the encoded
- // audio stream (e.g. octet alignement for AMR).
- enum audio_format {
- INVALID_FORMAT = -1,
- FORMAT_DEFAULT = 0,
- PCM = 0x00000000, // must be 0 for backward compatibility
- MP3 = 0x01000000,
- AMR_NB = 0x02000000,
- AMR_WB = 0x03000000,
- AAC = 0x04000000,
- HE_AAC_V1 = 0x05000000,
- HE_AAC_V2 = 0x06000000,
- VORBIS = 0x07000000,
- MAIN_FORMAT_MASK = 0xFF000000,
- SUB_FORMAT_MASK = 0x00FFFFFF,
- // Aliases
- PCM_16_BIT = (PCM|PCM_SUB_16_BIT),
- PCM_8_BIT = (PCM|PCM_SUB_8_BIT)
- };
-
-
- // Channel mask definitions must be kept in sync with JAVA values in /media/java/android/media/AudioFormat.java
- enum audio_channels {
- // output channels
- CHANNEL_OUT_FRONT_LEFT = 0x4,
- CHANNEL_OUT_FRONT_RIGHT = 0x8,
- CHANNEL_OUT_FRONT_CENTER = 0x10,
- CHANNEL_OUT_LOW_FREQUENCY = 0x20,
- CHANNEL_OUT_BACK_LEFT = 0x40,
- CHANNEL_OUT_BACK_RIGHT = 0x80,
- CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100,
- CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200,
- CHANNEL_OUT_BACK_CENTER = 0x400,
- CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT,
- CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT),
- CHANNEL_OUT_QUAD = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
- CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT),
- CHANNEL_OUT_SURROUND = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
- CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER),
- CHANNEL_OUT_5POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
- CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT),
- CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
- CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
- CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
- CHANNEL_OUT_ALL = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
- CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
- CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | CHANNEL_OUT_BACK_CENTER),
-
- // input channels
- CHANNEL_IN_LEFT = 0x4,
- CHANNEL_IN_RIGHT = 0x8,
- CHANNEL_IN_FRONT = 0x10,
- CHANNEL_IN_BACK = 0x20,
- CHANNEL_IN_LEFT_PROCESSED = 0x40,
- CHANNEL_IN_RIGHT_PROCESSED = 0x80,
- CHANNEL_IN_FRONT_PROCESSED = 0x100,
- CHANNEL_IN_BACK_PROCESSED = 0x200,
- CHANNEL_IN_PRESSURE = 0x400,
- CHANNEL_IN_X_AXIS = 0x800,
- CHANNEL_IN_Y_AXIS = 0x1000,
- CHANNEL_IN_Z_AXIS = 0x2000,
- CHANNEL_IN_VOICE_UPLINK = 0x4000,
- CHANNEL_IN_VOICE_DNLINK = 0x8000,
- CHANNEL_IN_MONO = CHANNEL_IN_FRONT,
- CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT),
- CHANNEL_IN_ALL = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT | CHANNEL_IN_FRONT | CHANNEL_IN_BACK|
- CHANNEL_IN_LEFT_PROCESSED | CHANNEL_IN_RIGHT_PROCESSED | CHANNEL_IN_FRONT_PROCESSED | CHANNEL_IN_BACK_PROCESSED|
- CHANNEL_IN_PRESSURE | CHANNEL_IN_X_AXIS | CHANNEL_IN_Y_AXIS | CHANNEL_IN_Z_AXIS |
- CHANNEL_IN_VOICE_UPLINK | CHANNEL_IN_VOICE_DNLINK)
- };
-
- enum audio_mode {
- MODE_INVALID = -2,
- MODE_CURRENT = -1,
- MODE_NORMAL = 0,
- MODE_RINGTONE,
- MODE_IN_CALL,
- MODE_IN_COMMUNICATION,
- NUM_MODES // not a valid entry, denotes end-of-list
- };
-
- enum audio_in_acoustics {
- AGC_ENABLE = 0x0001,
- AGC_DISABLE = 0,
- NS_ENABLE = 0x0002,
- NS_DISABLE = 0,
- TX_IIR_ENABLE = 0x0004,
- TX_DISABLE = 0
- };
-
- // special audio session values
- enum audio_sessions {
- SESSION_OUTPUT_STAGE = -1, // session for effects attached to a particular output stream
- // (value must be less than 0)
- SESSION_OUTPUT_MIX = 0, // session for effects applied to output mix. These effects can
- // be moved by audio policy manager to another output stream
- // (value must be 0)
- };
-
/* These are static methods to control the system-wide AudioFlinger
* only privileged processes can have access to them
*/
@@ -189,6 +49,7 @@
// set/get master volume
static status_t setMasterVolume(float value);
static status_t getMasterVolume(float* volume);
+
// mute/unmute audio outputs
static status_t setMasterMute(bool mute);
static status_t getMasterMute(bool* mute);
@@ -201,7 +62,7 @@
static status_t setStreamMute(int stream, bool mute);
static status_t getStreamMute(int stream, bool* mute);
- // set audio mode in audio hardware (see AudioSystem::audio_mode)
+ // set audio mode in audio hardware (see audio_mode_t)
static status_t setMode(int mode);
// returns true in *state if tracks are active on the specified stream or has been active
@@ -222,9 +83,9 @@
static float linearToLog(int volume);
static int logToLinear(float volume);
- static status_t getOutputSamplingRate(int* samplingRate, int stream = DEFAULT);
- static status_t getOutputFrameCount(int* frameCount, int stream = DEFAULT);
- static status_t getOutputLatency(uint32_t* latency, int stream = DEFAULT);
+ static status_t getOutputSamplingRate(int* samplingRate, int stream = AUDIO_STREAM_DEFAULT);
+ static status_t getOutputFrameCount(int* frameCount, int stream = AUDIO_STREAM_DEFAULT);
+ static status_t getOutputLatency(uint32_t* latency, int stream = AUDIO_STREAM_DEFAULT);
static bool routedToA2dpOutput(int streamType);
@@ -234,7 +95,7 @@
static status_t setVoiceVolume(float volume);
// return the number of audio frames written by AudioFlinger to audio HAL and
- // audio dsp to DAC since the output on which the specificed stream is playing
+ // audio dsp to DAC since the output on which the specified stream is playing
// has exited standby.
// returned status (from utils/Errors.h) can be:
// - NO_ERROR: successful operation, halFrames and dspFrames point to valid data
@@ -242,93 +103,11 @@
// - BAD_VALUE: invalid parameter
// NOTE: this feature is not supported on all hardware platforms and it is
// necessary to check returned status before using the returned values.
- static status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int stream = DEFAULT);
+ static status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int stream = AUDIO_STREAM_DEFAULT);
static unsigned int getInputFramesLost(audio_io_handle_t ioHandle);
static int newAudioSessionId();
- //
- // AudioPolicyService interface
- //
-
- enum audio_devices {
- // output devices
- DEVICE_OUT_EARPIECE = 0x1,
- DEVICE_OUT_SPEAKER = 0x2,
- DEVICE_OUT_WIRED_HEADSET = 0x4,
- DEVICE_OUT_WIRED_HEADPHONE = 0x8,
- DEVICE_OUT_BLUETOOTH_SCO = 0x10,
- DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,
- DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,
- DEVICE_OUT_BLUETOOTH_A2DP = 0x80,
- DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
- DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
- DEVICE_OUT_AUX_DIGITAL = 0x400,
- DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800,
- DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000,
- DEVICE_OUT_DEFAULT = 0x8000,
- DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET |
- DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
- DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
- DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL |
- DEVICE_OUT_ANLG_DOCK_HEADSET | DEVICE_OUT_DGTL_DOCK_HEADSET |
- DEVICE_OUT_DEFAULT),
- DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
- DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
-
- // input devices
- DEVICE_IN_COMMUNICATION = 0x10000,
- DEVICE_IN_AMBIENT = 0x20000,
- DEVICE_IN_BUILTIN_MIC = 0x40000,
- DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000,
- DEVICE_IN_WIRED_HEADSET = 0x100000,
- DEVICE_IN_AUX_DIGITAL = 0x200000,
- DEVICE_IN_VOICE_CALL = 0x400000,
- DEVICE_IN_BACK_MIC = 0x800000,
- DEVICE_IN_DEFAULT = 0x80000000,
-
- DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION | DEVICE_IN_AMBIENT | DEVICE_IN_BUILTIN_MIC |
- DEVICE_IN_BLUETOOTH_SCO_HEADSET | DEVICE_IN_WIRED_HEADSET | DEVICE_IN_AUX_DIGITAL |
- DEVICE_IN_VOICE_CALL | DEVICE_IN_BACK_MIC | DEVICE_IN_DEFAULT)
- };
-
- // device connection states used for setDeviceConnectionState()
- enum device_connection_state {
- DEVICE_STATE_UNAVAILABLE,
- DEVICE_STATE_AVAILABLE,
- NUM_DEVICE_STATES
- };
-
- // request to open a direct output with getOutput() (by opposition to sharing an output with other AudioTracks)
- enum output_flags {
- OUTPUT_FLAG_INDIRECT = 0x0,
- OUTPUT_FLAG_DIRECT = 0x1
- };
-
- // device categories used for setForceUse()
- enum forced_config {
- FORCE_NONE,
- FORCE_SPEAKER,
- FORCE_HEADPHONES,
- FORCE_BT_SCO,
- FORCE_BT_A2DP,
- FORCE_WIRED_ACCESSORY,
- FORCE_BT_CAR_DOCK,
- FORCE_BT_DESK_DOCK,
- FORCE_ANALOG_DOCK,
- FORCE_DIGITAL_DOCK,
- NUM_FORCE_CONFIG,
- FORCE_DEFAULT = FORCE_NONE
- };
-
- // usages used for setForceUse()
- enum force_use {
- FOR_COMMUNICATION,
- FOR_MEDIA,
- FOR_RECORD,
- FOR_DOCK,
- NUM_FORCE_USE
- };
// types of io configuration change events received with ioConfigChanged()
enum io_config_event {
@@ -359,39 +138,40 @@
//
// IAudioPolicyService interface (see AudioPolicyInterface for method descriptions)
//
- static status_t setDeviceConnectionState(audio_devices device, device_connection_state state, const char *device_address);
- static device_connection_state getDeviceConnectionState(audio_devices device, const char *device_address);
+ static status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, const char *device_address);
+ static audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address);
static status_t setPhoneState(int state);
static status_t setRingerMode(uint32_t mode, uint32_t mask);
- static status_t setForceUse(force_use usage, forced_config config);
- static forced_config getForceUse(force_use usage);
- static audio_io_handle_t getOutput(stream_type stream,
+ static status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
+ static audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
+ static audio_io_handle_t getOutput(audio_stream_type_t stream,
uint32_t samplingRate = 0,
- uint32_t format = FORMAT_DEFAULT,
- uint32_t channels = CHANNEL_OUT_STEREO,
- output_flags flags = OUTPUT_FLAG_INDIRECT);
+ uint32_t format = AUDIO_FORMAT_DEFAULT,
+ uint32_t channels = AUDIO_CHANNEL_OUT_STEREO,
+ audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_INDIRECT);
static status_t startOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session = 0);
static status_t stopOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session = 0);
static void releaseOutput(audio_io_handle_t output);
static audio_io_handle_t getInput(int inputSource,
uint32_t samplingRate = 0,
- uint32_t format = FORMAT_DEFAULT,
- uint32_t channels = CHANNEL_IN_MONO,
- audio_in_acoustics acoustics = (audio_in_acoustics)0);
+ uint32_t format = AUDIO_FORMAT_DEFAULT,
+ uint32_t channels = AUDIO_CHANNEL_IN_MONO,
+ audio_in_acoustics_t acoustics = (audio_in_acoustics_t)0);
static status_t startInput(audio_io_handle_t input);
static status_t stopInput(audio_io_handle_t input);
static void releaseInput(audio_io_handle_t input);
- static status_t initStreamVolume(stream_type stream,
+ static status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax);
- static status_t setStreamVolumeIndex(stream_type stream, int index);
- static status_t getStreamVolumeIndex(stream_type stream, int *index);
+ static status_t setStreamVolumeIndex(audio_stream_type_t stream, int index);
+ static status_t getStreamVolumeIndex(audio_stream_type_t stream, int *index);
- static uint32_t getStrategyForStream(stream_type stream);
+ static uint32_t getStrategyForStream(audio_stream_type_t stream);
+ static uint32_t getDevicesForStream(audio_stream_type_t stream);
static audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc);
static status_t registerEffect(effect_descriptor_t *desc,
@@ -405,17 +185,6 @@
// ----------------------------------------------------------------------------
- static uint32_t popCount(uint32_t u);
- static bool isOutputDevice(audio_devices device);
- static bool isInputDevice(audio_devices device);
- static bool isA2dpDevice(audio_devices device);
- static bool isBluetoothScoDevice(audio_devices device);
- static bool isLowVisibility(stream_type stream);
- static bool isOutputChannel(uint32_t channel);
- static bool isInputChannel(uint32_t channel);
- static bool isValidFormat(uint32_t format);
- static bool isLinearPCM(uint32_t format);
-
private:
class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
@@ -467,50 +236,6 @@
static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
};
-class AudioParameter {
-
-public:
- AudioParameter() {}
- AudioParameter(const String8& keyValuePairs);
- virtual ~AudioParameter();
-
- // reserved parameter keys for changing standard parameters with setParameters() function.
- // Using these keys is mandatory for AudioFlinger to properly monitor audio output/input
- // configuration changes and act accordingly.
- // keyRouting: to change audio routing, value is an int in AudioSystem::audio_devices
- // keySamplingRate: to change sampling rate routing, value is an int
- // keyFormat: to change audio format, value is an int in AudioSystem::audio_format
- // keyChannels: to change audio channel configuration, value is an int in AudioSystem::audio_channels
- // keyFrameCount: to change audio output frame count, value is an int
- // keyInputSource: to change audio input source, value is an int in audio_source
- // (defined in media/mediarecorder.h)
- static const char *keyRouting;
- static const char *keySamplingRate;
- static const char *keyFormat;
- static const char *keyChannels;
- static const char *keyFrameCount;
- static const char *keyInputSource;
-
- String8 toString();
-
- status_t add(const String8& key, const String8& value);
- status_t addInt(const String8& key, const int value);
- status_t addFloat(const String8& key, const float value);
-
- status_t remove(const String8& key);
-
- status_t get(const String8& key, String8& value);
- status_t getInt(const String8& key, int& value);
- status_t getFloat(const String8& key, float& value);
- status_t getAt(size_t index, String8& key, String8& value);
-
- size_t size() { return mParameters.size(); }
-
-private:
- String8 mKeyValuePairs;
- KeyedVector <String8, String8> mParameters;
-};
-
}; // namespace android
#endif /*ANDROID_AUDIOSYSTEM_H_*/
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 813a905..de928da 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -30,7 +30,6 @@
#include <binder/IMemory.h>
#include <utils/threads.h>
-
namespace android {
// ----------------------------------------------------------------------------
@@ -126,11 +125,11 @@
* Parameters:
*
* streamType: Select the type of audio stream this track is attached to
- * (e.g. AudioSystem::MUSIC).
+ * (e.g. AUDIO_STREAM_MUSIC).
* sampleRate: Track sampling rate in Hz.
- * format: Audio format (e.g AudioSystem::PCM_16_BIT for signed
+ * format: Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
* 16 bits per sample).
- * channels: Channel mask: see AudioSystem::audio_channels.
+ * channels: Channel mask: see audio_channels_t.
* frameCount: Total size of track PCM buffer in frames. This defines the
* latency of the track.
* flags: Reserved for future use.
@@ -437,7 +436,7 @@
};
bool processAudioBuffer(const sp<AudioTrackThread>& thread);
- status_t createTrack(int streamType,
+ status_t createTrack_l(int streamType,
uint32_t sampleRate,
int format,
int channelCount,
@@ -446,6 +445,10 @@
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
bool enforceFrameCount);
+ void flush_l();
+ status_t setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount);
+ audio_io_handle_t getOutput_l();
+ status_t restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart);
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
diff --git a/include/media/EffectApi.h b/include/media/EffectApi.h
index b97c22e..a5ad846 100644
--- a/include/media/EffectApi.h
+++ b/include/media/EffectApi.h
@@ -602,9 +602,9 @@
// Audio mode
enum audio_mode_e {
- AUDIO_MODE_NORMAL, // device idle
- AUDIO_MODE_RINGTONE, // device ringing
- AUDIO_MODE_IN_CALL // audio call connected (VoIP or telephony)
+ AUDIO_EFFECT_MODE_NORMAL, // device idle
+ AUDIO_EFFECT_MODE_RINGTONE, // device ringing
+ AUDIO_EFFECT_MODE_IN_CALL, // audio call connected (VoIP or telephony)
};
// Values for "accessMode" field of buffer_config_t:
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index 5afceaa..09b2bfe 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -26,6 +26,7 @@
#include <binder/IInterface.h>
#include <media/AudioSystem.h>
+#include <hardware/audio_policy.h>
namespace android {
@@ -39,41 +40,42 @@
//
// IAudioPolicyService interface (see AudioPolicyInterface for method descriptions)
//
- virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
- AudioSystem::device_connection_state state,
+ virtual status_t setDeviceConnectionState(audio_devices_t device,
+ audio_policy_dev_state_t state,
const char *device_address) = 0;
- virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device,
+ virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device,
const char *device_address) = 0;
virtual status_t setPhoneState(int state) = 0;
virtual status_t setRingerMode(uint32_t mode, uint32_t mask) = 0;
- virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) = 0;
- virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage) = 0;
- virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
+ virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) = 0;
+ virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) = 0;
+ virtual audio_io_handle_t getOutput(audio_stream_type_t stream,
uint32_t samplingRate = 0,
- uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
- AudioSystem::output_flags flags = AudioSystem::OUTPUT_FLAG_INDIRECT) = 0;
+ audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_INDIRECT) = 0;
virtual status_t startOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session = 0) = 0;
virtual status_t stopOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session = 0) = 0;
virtual void releaseOutput(audio_io_handle_t output) = 0;
virtual audio_io_handle_t getInput(int inputSource,
uint32_t samplingRate = 0,
- uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
- AudioSystem::audio_in_acoustics acoustics = (AudioSystem::audio_in_acoustics)0) = 0;
+ audio_in_acoustics_t acoustics = (audio_in_acoustics_t)0) = 0;
virtual status_t startInput(audio_io_handle_t input) = 0;
virtual status_t stopInput(audio_io_handle_t input) = 0;
virtual void releaseInput(audio_io_handle_t input) = 0;
- virtual status_t initStreamVolume(AudioSystem::stream_type stream,
+ virtual status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax) = 0;
- virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index) = 0;
- virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index) = 0;
- virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream) = 0;
+ virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, int index) = 0;
+ virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, int *index) = 0;
+ virtual uint32_t getStrategyForStream(audio_stream_type_t stream) = 0;
+ virtual uint32_t getDevicesForStream(audio_stream_type_t stream) = 0;
virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc) = 0;
virtual status_t registerEffect(effect_descriptor_t *desc,
audio_io_handle_t output,
diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h
index 8e3cdbb..1c1c268 100644
--- a/include/media/IMediaMetadataRetriever.h
+++ b/include/media/IMediaMetadataRetriever.h
@@ -18,10 +18,11 @@
#ifndef ANDROID_IMEDIAMETADATARETRIEVER_H
#define ANDROID_IMEDIAMETADATARETRIEVER_H
-#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IMemory.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
namespace android {
@@ -30,7 +31,11 @@
public:
DECLARE_META_INTERFACE(MediaMetadataRetriever);
virtual void disconnect() = 0;
- virtual status_t setDataSource(const char* srcUrl) = 0;
+
+ virtual status_t setDataSource(
+ const char *srcUrl,
+ const KeyedVector<String8, String8> *headers = NULL) = 0;
+
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
virtual sp<IMemory> getFrameAtTime(int64_t timeUs, int option) = 0;
virtual sp<IMemory> extractAlbumArt() = 0;
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index bba7ed7..d552b2e 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -24,8 +24,8 @@
namespace android {
class Parcel;
-class ISurface;
class Surface;
+class ISurfaceTexture;
class IMediaPlayer: public IInterface
{
@@ -35,6 +35,8 @@
virtual void disconnect() = 0;
virtual status_t setVideoSurface(const sp<Surface>& surface) = 0;
+ virtual status_t setVideoSurfaceTexture(
+ const sp<ISurfaceTexture>& surfaceTexture) = 0;
virtual status_t prepareAsync() = 0;
virtual status_t start() = 0;
virtual status_t stop() = 0;
@@ -49,6 +51,8 @@
virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
virtual status_t setAuxEffectSendLevel(float level) = 0;
virtual status_t attachAuxEffect(int effectId) = 0;
+ virtual status_t setParameter(int key, const Parcel& request) = 0;
+ virtual status_t getParameter(int key, Parcel* reply) = 0;
// Invoke a generic method on the player by using opaque parcels
// for the request and reply.
diff --git a/include/media/IMediaPlayerClient.h b/include/media/IMediaPlayerClient.h
index eee6c97..daec1c7 100644
--- a/include/media/IMediaPlayerClient.h
+++ b/include/media/IMediaPlayerClient.h
@@ -28,7 +28,7 @@
public:
DECLARE_META_INTERFACE(MediaPlayerClient);
- virtual void notify(int msg, int ext1, int ext2) = 0;
+ virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index 0bfb166..7956788 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -54,6 +54,30 @@
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IOMX> getOMX() = 0;
+
+ // codecs and audio devices usage tracking for the battery app
+ enum BatteryDataBits {
+ // tracking audio codec
+ kBatteryDataTrackAudio = 0x1,
+ // tracking video codec
+ kBatteryDataTrackVideo = 0x2,
+ // codec is started, otherwise codec is paused
+ kBatteryDataCodecStarted = 0x4,
+ // tracking decoder (for media player),
+ // otherwise tracking encoder (for media recorder)
+ kBatteryDataTrackDecoder = 0x8,
+ // start to play an audio on an audio device
+ kBatteryDataAudioFlingerStart = 0x10,
+ // stop/pause the audio playback
+ kBatteryDataAudioFlingerStop = 0x20,
+ // audio is rounted to speaker
+ kBatteryDataSpeakerOn = 0x40,
+ // audio is rounted to devices other than speaker
+ kBatteryDataOtherAudioDeviceOn = 0x80,
+ };
+
+ virtual void addBatteryData(uint32_t params) = 0;
+ virtual status_t pullBatteryData(Parcel* reply) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index cb36bbb..3c65147 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -33,7 +33,6 @@
class IMemory;
class IOMXObserver;
class IOMXRenderer;
-class ISurface;
class Surface;
class IOMX : public IInterface {
@@ -85,6 +84,9 @@
virtual status_t enableGraphicBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable) = 0;
+ virtual status_t getGraphicBufferUsage(
+ node_id node, OMX_U32 port_index, OMX_U32* usage) = 0;
+
virtual status_t useBuffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) = 0;
diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h
index 4b698e6..d310cee 100644
--- a/include/media/IStreamSource.h
+++ b/include/media/IStreamSource.h
@@ -45,6 +45,12 @@
virtual void queueBuffer(size_t index, size_t size) = 0;
+ // When signalling a discontinuity you can optionally
+ // specify an int64_t PTS timestamp in "msg".
+ // If present, rendering of data following the discontinuity
+ // will be suppressed until media time reaches this timestamp.
+ static const char *const kKeyResumeAtPTS;
+
virtual void issueCommand(
Command cmd, bool synchronous, const sp<AMessage> &msg = NULL) = 0;
};
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index 0449122..27b7e4d 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -30,7 +30,11 @@
public:
MediaMetadataRetrieverBase() {}
virtual ~MediaMetadataRetrieverBase() {}
- virtual status_t setDataSource(const char *url) = 0;
+
+ virtual status_t setDataSource(
+ const char *url,
+ const KeyedVector<String8, String8> *headers = NULL) = 0;
+
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) = 0;
virtual MediaAlbumArt* extractAlbumArt() = 0;
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 048f041..f0401cc 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -32,8 +32,8 @@
namespace android {
class Parcel;
-class ISurface;
class Surface;
+class ISurfaceTexture;
template<typename T> class SortedVector;
@@ -55,7 +55,8 @@
// callback mechanism for passing messages to MediaPlayer object
-typedef void (*notify_callback_f)(void* cookie, int msg, int ext1, int ext2);
+typedef void (*notify_callback_f)(void* cookie,
+ int msg, int ext1, int ext2, const Parcel *obj);
// abstract base class - use MediaPlayerInterface
class MediaPlayerBase : public RefBase
@@ -84,7 +85,7 @@
// audio data.
virtual status_t open(
uint32_t sampleRate, int channelCount,
- int format=AudioSystem::PCM_16_BIT,
+ int format=AUDIO_FORMAT_PCM_16_BIT,
int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
AudioCallback cb = NULL,
void *cookie = NULL) = 0;
@@ -112,7 +113,13 @@
return INVALID_OPERATION;
}
+ // pass the buffered Surface to the media player service
virtual status_t setVideoSurface(const sp<Surface>& surface) = 0;
+
+ // pass the buffered ISurfaceTexture to the media player service
+ virtual status_t setVideoSurfaceTexture(
+ const sp<ISurfaceTexture>& surfaceTexture) = 0;
+
virtual status_t prepare() = 0;
virtual status_t prepareAsync() = 0;
virtual status_t start() = 0;
@@ -125,6 +132,8 @@
virtual status_t reset() = 0;
virtual status_t setLooping(int loop) = 0;
virtual player_type playerType() = 0;
+ virtual status_t setParameter(int key, const Parcel &request) = 0;
+ virtual status_t getParameter(int key, Parcel *reply) = 0;
// Invoke a generic method on the player by using opaque parcels
// for the request and reply.
@@ -153,9 +162,10 @@
mCookie = cookie; mNotify = notifyFunc;
}
- void sendEvent(int msg, int ext1=0, int ext2=0) {
+ void sendEvent(int msg, int ext1=0, int ext2=0,
+ const Parcel *obj=NULL) {
Mutex::Autolock autoLock(mNotifyLock);
- if (mNotify) mNotify(mCookie, msg, ext1, ext2);
+ if (mNotify) mNotify(mCookie, msg, ext1, ext2, obj);
}
private:
@@ -177,7 +187,7 @@
sp<AudioSink> mAudioSink;
};
-// Implement this class for media players that output directo to hardware
+// Implement this class for media players that output audio directly to hardware
class MediaPlayerHWInterface : public MediaPlayerBase
{
public:
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index aa97874..ed26e63 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -24,6 +24,7 @@
namespace android {
enum camcorder_quality {
+ CAMCORDER_QUALITY_LIST_START = 0,
CAMCORDER_QUALITY_LOW = 0,
CAMCORDER_QUALITY_HIGH = 1,
CAMCORDER_QUALITY_QCIF = 2,
@@ -31,14 +32,17 @@
CAMCORDER_QUALITY_480P = 4,
CAMCORDER_QUALITY_720P = 5,
CAMCORDER_QUALITY_1080P = 6,
+ CAMCORDER_QUALITY_LIST_END = 6,
+ CAMCORDER_QUALITY_TIME_LAPSE_LIST_START = 1000,
CAMCORDER_QUALITY_TIME_LAPSE_LOW = 1000,
CAMCORDER_QUALITY_TIME_LAPSE_HIGH = 1001,
CAMCORDER_QUALITY_TIME_LAPSE_QCIF = 1002,
CAMCORDER_QUALITY_TIME_LAPSE_CIF = 1003,
CAMCORDER_QUALITY_TIME_LAPSE_480P = 1004,
CAMCORDER_QUALITY_TIME_LAPSE_720P = 1005,
- CAMCORDER_QUALITY_TIME_LAPSE_1080P = 1006
+ CAMCORDER_QUALITY_TIME_LAPSE_1080P = 1006,
+ CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1006,
};
enum video_decoder {
@@ -146,7 +150,18 @@
*/
Vector<int> getImageEncodingQualityLevels(int cameraId) const;
+ /**
+ * Returns the start time offset (in ms) for the given camera Id.
+ * If the given camera Id does not exist, -1 will be returned.
+ */
+ int getStartTimeOffsetMs(int cameraId) const;
+
private:
+ enum {
+ // Camcorder profiles (high/low) and timelapse profiles (high/low)
+ kNumRequiredProfiles = 4,
+ };
+
MediaProfiles& operator=(const MediaProfiles&); // Don't call me
MediaProfiles(const MediaProfiles&); // Don't call me
MediaProfiles() {} // Dummy default constructor
@@ -160,6 +175,14 @@
mFrameHeight(frameHeight),
mFrameRate(frameRate) {}
+ VideoCodec(const VideoCodec& copy) {
+ mCodec = copy.mCodec;
+ mBitRate = copy.mBitRate;
+ mFrameWidth = copy.mFrameWidth;
+ mFrameHeight = copy.mFrameHeight;
+ mFrameRate = copy.mFrameRate;
+ }
+
~VideoCodec() {}
video_encoder mCodec;
@@ -176,6 +199,13 @@
mSampleRate(sampleRate),
mChannels(channels) {}
+ AudioCodec(const AudioCodec& copy) {
+ mCodec = copy.mCodec;
+ mBitRate = copy.mBitRate;
+ mSampleRate = copy.mSampleRate;
+ mChannels = copy.mChannels;
+ }
+
~AudioCodec() {}
audio_encoder mCodec;
@@ -193,6 +223,15 @@
mVideoCodec(0),
mAudioCodec(0) {}
+ CamcorderProfile(const CamcorderProfile& copy) {
+ mCameraId = copy.mCameraId;
+ mFileFormat = copy.mFileFormat;
+ mQuality = copy.mQuality;
+ mDuration = copy.mDuration;
+ mVideoCodec = new VideoCodec(*copy.mVideoCodec);
+ mAudioCodec = new AudioCodec(*copy.mAudioCodec);
+ }
+
~CamcorderProfile() {
delete mVideoCodec;
delete mAudioCodec;
@@ -272,6 +311,8 @@
};
int getCamcorderProfileIndex(int cameraId, camcorder_quality quality) const;
+ void initRequiredProfileRefs(const Vector<int>& cameraIds);
+ int getRequiredProfileRefIndex(int cameraId);
// Debug
static void logVideoCodec(const VideoCodec& codec);
@@ -291,9 +332,14 @@
static VideoDecoderCap* createVideoDecoderCap(const char **atts);
static VideoEncoderCap* createVideoEncoderCap(const char **atts);
static AudioEncoderCap* createAudioEncoderCap(const char **atts);
- static CamcorderProfile* createCamcorderProfile(int cameraId, const char **atts);
+
+ static CamcorderProfile* createCamcorderProfile(
+ int cameraId, const char **atts, Vector<int>& cameraIds);
+
static int getCameraId(const char **atts);
+ void addStartTimeOffset(int cameraId, const char **atts);
+
ImageEncodingQualityLevels* findImageEncodingQualityLevels(int cameraId) const;
void addImageEncodingQualityLevel(int cameraId, const char** atts);
@@ -335,6 +381,21 @@
static int findTagForName(const NameToTagMap *map, size_t nMappings, const char *name);
+ /**
+ * Check on existing profiles with the following criteria:
+ * 1. Low quality profile must have the lowest video
+ * resolution product (width x height)
+ * 2. High quality profile must have the highest video
+ * resolution product (width x height)
+ *
+ * and add required low/high quality camcorder/timelapse
+ * profiles if they are not found. This allows to remove
+ * duplicate profile definitions in the media_profiles.xml
+ * file.
+ */
+ void checkAndAddRequiredProfilesIfNecessary();
+
+
// Mappings from name (for instance, codec name) to enum value
static const NameToTagMap sVideoEncoderNameMap[];
static const NameToTagMap sAudioEncoderNameMap[];
@@ -355,6 +416,21 @@
Vector<VideoDecoderCap*> mVideoDecoders;
Vector<output_format> mEncoderOutputFileFormats;
Vector<ImageEncodingQualityLevels *> mImageEncodingQualityLevels;
+ KeyedVector<int, int> mStartTimeOffsets;
+
+ typedef struct {
+ bool mHasRefProfile; // Refers to an existing profile
+ int mRefProfileIndex; // Reference profile index
+ int mResolutionProduct; // width x height
+ } RequiredProfileRefInfo; // Required low and high profiles
+
+ typedef struct {
+ RequiredProfileRefInfo mRefs[kNumRequiredProfiles];
+ int mCameraId;
+ } RequiredProfiles;
+
+ RequiredProfiles *mRequiredProfileRefs;
+ Vector<int> mCameraIds;
};
}; // namespace android
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index c42346e..7e22a24 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -20,6 +20,8 @@
#include <media/mediarecorder.h>
+#include <system/audio.h>
+
namespace android {
class Surface;
@@ -29,7 +31,7 @@
virtual ~MediaRecorderBase() {}
virtual status_t init() = 0;
- virtual status_t setAudioSource(audio_source as) = 0;
+ virtual status_t setAudioSource(audio_source_t as) = 0;
virtual status_t setVideoSource(video_source vs) = 0;
virtual status_t setOutputFormat(output_format of) = 0;
virtual status_t setAudioEncoder(audio_encoder ae) = 0;
diff --git a/include/media/MemoryLeakTrackUtil.h b/include/media/MemoryLeakTrackUtil.h
new file mode 100644
index 0000000..290b748
--- /dev/null
+++ b/include/media/MemoryLeakTrackUtil.h
@@ -0,0 +1,28 @@
+
+/*
+ * Copyright 2011, 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 MEMORY_LEAK_TRACK_UTIL_H
+#define MEMORY_LEAK_TRACK_UTIL_H
+
+namespace android {
+/*
+ * Dump the memory adddress of the calling process to the given fd.
+ */
+extern void dumpMemoryAddresses(int fd);
+
+};
+
+#endif // MEMORY_LEAK_TRACK_UTIL_H
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index e905006..28f305d 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -47,6 +47,13 @@
METADATA_KEY_ALBUMARTIST = 13,
METADATA_KEY_DISC_NUMBER = 14,
METADATA_KEY_COMPILATION = 15,
+ METADATA_KEY_HAS_AUDIO = 16,
+ METADATA_KEY_HAS_VIDEO = 17,
+ METADATA_KEY_VIDEO_WIDTH = 18,
+ METADATA_KEY_VIDEO_HEIGHT = 19,
+ METADATA_KEY_BITRATE = 20,
+ METADATA_KEY_TIMED_TEXT_LANGUAGES = 21,
+
// Add more here...
};
@@ -56,7 +63,11 @@
MediaMetadataRetriever();
~MediaMetadataRetriever();
void disconnect();
- status_t setDataSource(const char* dataSourceUrl);
+
+ status_t setDataSource(
+ const char *dataSourceUrl,
+ const KeyedVector<String8, String8> *headers = NULL);
+
status_t setDataSource(int fd, int64_t offset, int64_t length);
sp<IMemory> getFrameAtTime(int64_t timeUs, int option);
sp<IMemory> extractAlbumArt();
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 88b0c3e..cfa4cfd 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -28,6 +28,7 @@
namespace android {
class Surface;
+class ISurfaceTexture;
enum media_event_type {
MEDIA_NOP = 0, // interface test message
@@ -36,6 +37,7 @@
MEDIA_BUFFERING_UPDATE = 3,
MEDIA_SEEK_COMPLETE = 4,
MEDIA_SET_VIDEO_SIZE = 5,
+ MEDIA_TIMED_TEXT = 99,
MEDIA_ERROR = 100,
MEDIA_INFO = 200,
};
@@ -123,12 +125,15 @@
MEDIA_PLAYER_PLAYBACK_COMPLETE = 1 << 7
};
+enum media_set_parameter_keys {
+ KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000,
+};
// ----------------------------------------------------------------------------
// ref-counted object for callbacks
class MediaPlayerListener: virtual public RefBase
{
public:
- virtual void notify(int msg, int ext1, int ext2) = 0;
+ virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
};
class MediaPlayer : public BnMediaPlayerClient,
@@ -146,6 +151,8 @@
status_t setDataSource(int fd, int64_t offset, int64_t length);
status_t setVideoSurface(const sp<Surface>& surface);
+ status_t setVideoSurfaceTexture(
+ const sp<ISurfaceTexture>& surfaceTexture);
status_t setListener(const sp<MediaPlayerListener>& listener);
status_t prepare();
status_t prepareAsync();
@@ -163,7 +170,7 @@
status_t setLooping(int loop);
bool isLooping();
status_t setVolume(float leftVolume, float rightVolume);
- void notify(int msg, int ext1, int ext2);
+ void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
status_t invoke(const Parcel& request, Parcel *reply);
@@ -173,6 +180,9 @@
int getAudioSessionId();
status_t setAuxEffectSendLevel(float level);
status_t attachAuxEffect(int effectId);
+ status_t setParameter(int key, const Parcel& request);
+ status_t getParameter(int key, Parcel* reply);
+
private:
void clear_l();
status_t seekTo_l(int msec);
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index a710546..36bf34e 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -33,23 +33,6 @@
typedef void (*media_completion_f)(status_t status, void *cookie);
-/* Do not change these values without updating their counterparts
- * in media/java/android/media/MediaRecorder.java!
- */
-enum audio_source {
- AUDIO_SOURCE_DEFAULT = 0,
- AUDIO_SOURCE_MIC = 1,
- AUDIO_SOURCE_VOICE_UPLINK = 2,
- AUDIO_SOURCE_VOICE_DOWNLINK = 3,
- AUDIO_SOURCE_VOICE_CALL = 4,
- AUDIO_SOURCE_CAMCORDER = 5,
- AUDIO_SOURCE_VOICE_RECOGNITION = 6,
- AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
- AUDIO_SOURCE_MAX = AUDIO_SOURCE_VOICE_COMMUNICATION,
-
- AUDIO_SOURCE_LIST_END // must be last - used to validate audio source type
-};
-
enum video_source {
VIDEO_SOURCE_DEFAULT = 0,
VIDEO_SOURCE_CAMERA = 1,
@@ -104,35 +87,62 @@
};
/*
- * The state machine of the media_recorder uses a set of different state names.
- * The mapping between the media_recorder and the pvauthorengine is shown below:
- *
- * mediarecorder pvauthorengine
- * ----------------------------------------------------------------
- * MEDIA_RECORDER_ERROR ERROR
- * MEDIA_RECORDER_IDLE IDLE
- * MEDIA_RECORDER_INITIALIZED OPENED
- * MEDIA_RECORDER_DATASOURCE_CONFIGURED
- * MEDIA_RECORDER_PREPARED INITIALIZED
- * MEDIA_RECORDER_RECORDING RECORDING
+ * The state machine of the media_recorder.
*/
enum media_recorder_states {
+ // Error state.
MEDIA_RECORDER_ERROR = 0,
+
+ // Recorder was just created.
MEDIA_RECORDER_IDLE = 1 << 0,
+
+ // Recorder has been initialized.
MEDIA_RECORDER_INITIALIZED = 1 << 1,
+
+ // Configuration of the recorder has been completed.
MEDIA_RECORDER_DATASOURCE_CONFIGURED = 1 << 2,
+
+ // Recorder is ready to start.
MEDIA_RECORDER_PREPARED = 1 << 3,
+
+ // Recording is in progress.
MEDIA_RECORDER_RECORDING = 1 << 4,
};
// The "msg" code passed to the listener in notify.
enum media_recorder_event_type {
+ MEDIA_RECORDER_EVENT_LIST_START = 1,
MEDIA_RECORDER_EVENT_ERROR = 1,
- MEDIA_RECORDER_EVENT_INFO = 2
+ MEDIA_RECORDER_EVENT_INFO = 2,
+ MEDIA_RECORDER_EVENT_LIST_END = 99,
+
+ // Track related event types
+ MEDIA_RECORDER_TRACK_EVENT_LIST_START = 100,
+ MEDIA_RECORDER_TRACK_EVENT_ERROR = 100,
+ MEDIA_RECORDER_TRACK_EVENT_INFO = 101,
+ MEDIA_RECORDER_TRACK_EVENT_LIST_END = 1000,
};
+/*
+ * The (part of) "what" code passed to the listener in notify.
+ * When the error or info type is track specific, the what has
+ * the following layout:
+ * the left-most 16-bit is meant for error or info type.
+ * the right-most 4-bit is meant for track id.
+ * the rest is reserved.
+ *
+ * | track id | reserved | error or info type |
+ * 31 28 16 0
+ *
+ */
enum media_recorder_error_type {
- MEDIA_RECORDER_ERROR_UNKNOWN = 1
+ MEDIA_RECORDER_ERROR_UNKNOWN = 1,
+
+ // Track related error type
+ MEDIA_RECORDER_TRACK_ERROR_LIST_START = 100,
+ MEDIA_RECORDER_TRACK_ERROR_GENERAL = 100,
+ MEDIA_RECORDER_ERROR_VIDEO_NO_SYNC_FRAME = 200,
+ MEDIA_RECORDER_TRACK_ERROR_LIST_END = 1000,
};
// The codes are distributed as follow:
@@ -141,11 +151,38 @@
//
enum media_recorder_info_type {
MEDIA_RECORDER_INFO_UNKNOWN = 1,
+
MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800,
MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801,
- MEDIA_RECORDER_INFO_COMPLETION_STATUS = 802,
- MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS = 803,
- MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS = 804,
+
+ // All track related informtional events start here
+ MEDIA_RECORDER_TRACK_INFO_LIST_START = 1000,
+ MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS = 1000,
+ MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME = 1001,
+ MEDIA_RECORDER_TRACK_INFO_TYPE = 1002,
+ MEDIA_RECORDER_TRACK_INFO_DURATION_MS = 1003,
+
+ // The time to measure the max chunk duration
+ MEDIA_RECORDER_TRACK_INFO_MAX_CHUNK_DUR_MS = 1004,
+
+ MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES = 1005,
+
+ // The time to measure how well the audio and video
+ // track data is interleaved.
+ MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS = 1006,
+
+ // The time to measure system response. Note that
+ // the delay does not include the intentional delay
+ // we use to eliminate the recording sound.
+ MEDIA_RECORDER_TRACK_INFO_INITIAL_DELAY_MS = 1007,
+
+ // The time used to compensate for initial A/V sync.
+ MEDIA_RECORDER_TRACK_INFO_START_OFFSET_MS = 1008,
+
+ // Total number of bytes of the media data.
+ MEDIA_RECORDER_TRACK_INFO_DATA_KBYTES = 1009,
+
+ MEDIA_RECORDER_TRACK_INFO_LIST_END = 2000,
};
// ----------------------------------------------------------------------------
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h
index df5be32..765c039 100644
--- a/include/media/mediascanner.h
+++ b/include/media/mediascanner.h
@@ -55,7 +55,7 @@
status_t doProcessDirectory(
char *path, int pathRemaining, MediaScannerClient &client,
- ExceptionCheck exceptionCheck, void *exceptionEnv);
+ bool noMedia, ExceptionCheck exceptionCheck, void *exceptionEnv);
MediaScanner(const MediaScanner &);
MediaScanner &operator=(const MediaScanner &);
@@ -72,10 +72,9 @@
void endFile();
virtual bool scanFile(const char* path, long long lastModified,
- long long fileSize, bool isDirectory) = 0;
+ long long fileSize, bool isDirectory, bool noMedia) = 0;
virtual bool handleStringTag(const char* name, const char* value) = 0;
virtual bool setMimeType(const char* mimeType) = 0;
- virtual bool addNoMediaFolder(const char* path) = 0;
protected:
void convertValues(uint32_t encoding);
diff --git a/include/media/stagefright/AACWriter.h b/include/media/stagefright/AACWriter.h
new file mode 100644
index 0000000..fa3ab8a
--- /dev/null
+++ b/include/media/stagefright/AACWriter.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 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 AAC_WRITER_H_
+#define AAC_WRITER_H_
+
+#include <media/stagefright/MediaWriter.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct MediaSource;
+struct MetaData;
+
+struct AACWriter : public MediaWriter {
+ AACWriter(const char *filename);
+ AACWriter(int fd);
+
+ status_t initCheck() const;
+
+ virtual status_t addSource(const sp<MediaSource> &source);
+ virtual bool reachedEOS();
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual status_t pause();
+
+protected:
+ virtual ~AACWriter();
+
+private:
+ enum {
+ kAdtsHeaderLength = 7, // # of bytes for the adts header
+ kSamplesPerFrame = 1024, // # of samples in a frame
+ };
+
+ int mFd;
+ status_t mInitCheck;
+ sp<MediaSource> mSource;
+ bool mStarted;
+ volatile bool mPaused;
+ volatile bool mResumed;
+ volatile bool mDone;
+ volatile bool mReachedEOS;
+ pthread_t mThread;
+ int64_t mEstimatedSizeBytes;
+ int64_t mEstimatedDurationUs;
+ int32_t mChannelCount;
+ int32_t mSampleRate;
+ int32_t mFrameDurationUs;
+
+ static void *ThreadWrapper(void *);
+ status_t threadFunc();
+ bool exceedsFileSizeLimit();
+ bool exceedsFileDurationLimit();
+ status_t writeAdtsHeader(uint32_t frameLength);
+
+ DISALLOW_EVIL_CONSTRUCTORS(AACWriter);
+};
+
+} // namespace android
+
+#endif // AAC_WRITER_H_
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 4599d70..f13e9bb 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 A_CODEC_H_
#define A_CODEC_H_
@@ -109,7 +125,7 @@
status_t allocateOutputBuffersFromNativeWindow();
status_t cancelBufferToNativeWindow(BufferInfo *info);
- status_t freeOutputBuffersOwnedByNativeWindow();
+ status_t freeOutputBuffersNotOwnedByComponent();
BufferInfo *dequeueBufferFromNativeWindow();
BufferInfo *findBufferByID(
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index d12ee9c..0b79324 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -108,6 +108,8 @@
void reset();
+ uint32_t getNumFramesPendingPlayout() const;
+
AudioPlayer(const AudioPlayer &);
AudioPlayer &operator=(const AudioPlayer &);
};
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index d484d60..19bd31b 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -18,20 +18,24 @@
#define AUDIO_SOURCE_H_
+#include <media/AudioRecord.h>
#include <media/AudioSystem.h>
#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <utils/List.h>
+
+#include <system/audio.h>
namespace android {
class AudioRecord;
-struct MediaBufferGroup;
-struct AudioSource : public MediaSource {
+struct AudioSource : public MediaSource, public MediaBufferObserver {
// Note that the "channels" parameter is _not_ the number of channels,
- // but a bitmask of AudioSystem::audio_channels constants.
+ // but a bitmask of audio_channels_t constants.
AudioSource(
int inputSource, uint32_t sampleRate,
- uint32_t channels = AudioSystem::CHANNEL_IN_MONO);
+ uint32_t channels = AUDIO_CHANNEL_IN_MONO);
status_t initCheck() const;
@@ -45,6 +49,9 @@
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
+ status_t dataCallbackTimestamp(const AudioRecord::Buffer& buffer, int64_t timeUs);
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
protected:
virtual ~AudioSource();
@@ -54,27 +61,31 @@
// After the initial mute, we raise the volume linearly
// over kAutoRampDurationUs.
- kAutoRampDurationUs = 700000,
+ kAutoRampDurationUs = 300000,
// This is the initial mute duration to suppress
// the video recording signal tone
- kAutoRampStartUs = 1000000,
- };
+ kAutoRampStartUs = 0,
+ };
+
+ Mutex mLock;
+ Condition mFrameAvailableCondition;
+ Condition mFrameEncodingCompletionCondition;
AudioRecord *mRecord;
status_t mInitCheck;
bool mStarted;
+ int32_t mSampleRate;
- bool mCollectStats;
bool mTrackMaxAmplitude;
int64_t mStartTimeUs;
int16_t mMaxAmplitude;
int64_t mPrevSampleTimeUs;
- int64_t mTotalLostFrames;
- int64_t mPrevLostBytes;
int64_t mInitialReadTimeUs;
+ int64_t mNumFramesReceived;
+ int64_t mNumClientOwnedBuffers;
- MediaBufferGroup *mGroup;
+ List<MediaBuffer * > mBuffersReceived;
void trackMaxAmplitude(int16_t *data, int nSamples);
@@ -84,6 +95,9 @@
int32_t startFrame, int32_t rampDurationFrames,
uint8_t *data, size_t bytes);
+ void releaseQueuedFrames_l();
+ void waitOutstandingEncodingFrames_l();
+
AudioSource(const AudioSource &);
AudioSource &operator=(const AudioSource &);
};
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index 4a39fbf..bb25bae 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -99,34 +99,6 @@
virtual sp<MetaData> getFormat();
/**
- * Retrieve the total number of video buffers available from
- * this source.
- *
- * This method is useful if these video buffers are used
- * for passing video frame data to other media components,
- * such as OMX video encoders, in order to eliminate the
- * memcpy of the data.
- *
- * @return the total numbner of video buffers. Returns 0 to
- * indicate that this source does not make the video
- * buffer information availalble.
- */
- size_t getNumberOfVideoBuffers() const;
-
- /**
- * Retrieve the individual video buffer available from
- * this source.
- *
- * @param index the index corresponding to the video buffer.
- * Valid range of the index is [0, n], where n =
- * getNumberOfVideoBuffers() - 1.
- *
- * @return the video buffer corresponding to the given index.
- * If index is out of range, 0 should be returned.
- */
- sp<IMemory> getVideoBuffer(size_t index) const;
-
- /**
* Tell whether this camera source stores meta data or real YUV
* frame data in video buffers.
*
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index d4f1733..6b6fcdf 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -75,15 +75,17 @@
static void RegisterDefaultSniffers();
// for DRM
- virtual DecryptHandle* DrmInitialization(DrmManagerClient *client) {
+ virtual sp<DecryptHandle> DrmInitialization() {
return NULL;
}
- virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {};
+ virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {};
virtual String8 getUri() {
return String8();
}
+ virtual String8 getMIMEType() const;
+
protected:
virtual ~DataSource() {}
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
index 72a0403..6cf86dc 100644
--- a/include/media/stagefright/FileSource.h
+++ b/include/media/stagefright/FileSource.h
@@ -38,9 +38,9 @@
virtual status_t getSize(off64_t *size);
- virtual DecryptHandle* DrmInitialization(DrmManagerClient *client);
+ virtual sp<DecryptHandle> DrmInitialization();
- virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client);
+ virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
protected:
virtual ~FileSource();
@@ -52,7 +52,7 @@
Mutex mLock;
/*for DRM*/
- DecryptHandle *mDecryptHandle;
+ sp<DecryptHandle> mDecryptHandle;
DrmManagerClient *mDrmManagerClient;
int64_t mDrmBufOffset;
int64_t mDrmBufSize;
diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h
index 17908b4..946a0aa 100644
--- a/include/media/stagefright/HardwareAPI.h
+++ b/include/media/stagefright/HardwareAPI.h
@@ -84,7 +84,19 @@
OMX_U32 nPortIndex;
OMX_PTR pAppPrivate;
OMX_BUFFERHEADERTYPE **bufferHeader;
- const sp<android_native_buffer_t>& nativeBuffer;
+ const sp<ANativeWindowBuffer>& nativeBuffer;
+};
+
+// A pointer to this struct is passed to OMX_GetParameter when the extension
+// index for the 'OMX.google.android.index.getAndroidNativeBufferUsage'
+// extension is given. The usage bits returned from this query will be used to
+// allocate the Gralloc buffers that get passed to the useAndroidNativeBuffer
+// command.
+struct GetAndroidNativeBufferUsageParams {
+ OMX_U32 nSize; // IN
+ OMX_VERSIONTYPE nVersion; // IN
+ OMX_U32 nPortIndex; // IN
+ OMX_U32 nUsage; // OUT
};
} // namespace android
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index f7618e9..904ce2a 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -55,6 +55,10 @@
status_t setInterleaveDuration(uint32_t duration);
int32_t getTimeScale() const { return mTimeScale; }
+ status_t setGeoData(int latitudex10000, int longitudex10000);
+ void setStartTimeOffsetMs(int ms) { mStartTimeOffsetMs = ms; }
+ int32_t getStartTimeOffsetMs() const { return mStartTimeOffsetMs; }
+
protected:
virtual ~MPEG4Writer();
@@ -79,6 +83,10 @@
uint32_t mInterleaveDurationUs;
int32_t mTimeScale;
int64_t mStartTimestampUs;
+ int mLatitudex10000;
+ int mLongitudex10000;
+ bool mAreGeoTagsAvailable;
+ int32_t mStartTimeOffsetMs;
Mutex mLock;
@@ -98,6 +106,8 @@
List<MediaBuffer *> mSamples; // Sample data
// Convenient constructor
+ Chunk(): mTrack(NULL), mTimeStampUs(0) {}
+
Chunk(Track *track, int64_t timeUs, List<MediaBuffer *> samples)
: mTrack(track), mTimeStampUs(timeUs), mSamples(samples) {
}
@@ -106,6 +116,13 @@
struct ChunkInfo {
Track *mTrack; // Owner
List<Chunk> mChunks; // Remaining chunks to be written
+
+ // Previous chunk timestamp that has been written
+ int64_t mPrevChunkTimestampUs;
+
+ // Max time interval between neighboring chunks
+ int64_t mMaxInterChunkDurUs;
+
};
bool mIsFirstChunk;
@@ -124,13 +141,14 @@
void bufferChunk(const Chunk& chunk);
// Write all buffered chunks from all tracks
- void writeChunks();
+ void writeAllChunks();
- // Write a chunk if there is one
- status_t writeOneChunk();
+ // Retrieve the proper chunk to write if there is one
+ // Return true if a chunk is found; otherwise, return false.
+ bool findChunkToWrite(Chunk *chunk);
- // Write the first chunk from the given ChunkInfo.
- void writeFirstChunk(ChunkInfo* info);
+ // Actually write the given chunk to the file.
+ void writeChunkToFile(Chunk* chunk);
// Adjust other track media clock (presumably wall clock)
// based on audio track media clock with the drift time.
@@ -154,8 +172,16 @@
bool use32BitFileOffset() const;
bool exceedsFileDurationLimit();
bool isFileStreamable() const;
- void trackProgressStatus(const Track* track, int64_t timeUs, status_t err = OK);
+ void trackProgressStatus(size_t trackId, int64_t timeUs, status_t err = OK);
void writeCompositionMatrix(int32_t degrees);
+ void writeMvhdBox(int64_t durationUs);
+ void writeMoovBox(int64_t durationUs);
+ void writeFtypBox(MetaData *param);
+ void writeUdtaBox();
+ void writeGeoDataBox();
+ void writeLatitude(int degreex10000);
+ void writeLongitude(int degreex10000);
+ void sendSessionSummary();
MPEG4Writer(const MPEG4Writer &);
MPEG4Writer &operator=(const MPEG4Writer &);
diff --git a/include/media/stagefright/MediaDebug.h b/include/media/stagefright/MediaDebug.h
index c8a8f00..2ca9667 100644
--- a/include/media/stagefright/MediaDebug.h
+++ b/include/media/stagefright/MediaDebug.h
@@ -1,3 +1,19 @@
+/*
+ * 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 MEDIA_DEBUG_H_
#define MEDIA_DEBUG_H_
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 2d50ca5..5e471c1 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -37,15 +37,20 @@
extern const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW;
extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW;
extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
+extern const char *MEDIA_MIMETYPE_AUDIO_FLAC;
+extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
extern const char *MEDIA_MIMETYPE_CONTAINER_OGG;
extern const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS;
+extern const char *MEDIA_MIMETYPE_CONTAINER_AVI;
extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
+extern const char *MEDIA_MIMETYPE_TEXT_3GPP;
+
} // namespace android
#endif // MEDIA_DEFS_H_
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
index 6df4d86..21d00b8 100644
--- a/include/media/stagefright/MediaErrors.h
+++ b/include/media/stagefright/MediaErrors.h
@@ -41,7 +41,29 @@
INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12,
INFO_DISCONTINUITY = MEDIA_ERROR_BASE - 13,
- ERROR_NO_LICENSE = MEDIA_ERROR_BASE - 14,
+ // The following constant values should be in sync with
+ // drm/drm_framework_common.h
+ DRM_ERROR_BASE = -2000,
+
+ ERROR_DRM_UNKNOWN = DRM_ERROR_BASE,
+ ERROR_DRM_NO_LICENSE = DRM_ERROR_BASE - 1,
+ ERROR_DRM_LICENSE_EXPIRED = DRM_ERROR_BASE - 2,
+ ERROR_DRM_SESSION_NOT_OPENED = DRM_ERROR_BASE - 3,
+ ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED = DRM_ERROR_BASE - 4,
+ ERROR_DRM_DECRYPT = DRM_ERROR_BASE - 5,
+ ERROR_DRM_CANNOT_HANDLE = DRM_ERROR_BASE - 6,
+ ERROR_DRM_TAMPER_DETECTED = DRM_ERROR_BASE - 7,
+
+ // Heartbeat Error Codes
+ HEARTBEAT_ERROR_BASE = -3000,
+
+ ERROR_HEARTBEAT_AUTHENTICATION_FAILURE = HEARTBEAT_ERROR_BASE,
+ ERROR_HEARTBEAT_NO_ACTIVE_PURCHASE_AGREEMENT = HEARTBEAT_ERROR_BASE - 1,
+ ERROR_HEARTBEAT_CONCURRENT_PLAYBACK = HEARTBEAT_ERROR_BASE - 2,
+ ERROR_HEARTBEAT_UNUSUAL_ACTIVITY = HEARTBEAT_ERROR_BASE - 3,
+ ERROR_HEARTBEAT_STREAMING_UNAVAILABLE = HEARTBEAT_ERROR_BASE - 4,
+ ERROR_HEARTBEAT_CANNOT_ACTIVATE_RENTAL = HEARTBEAT_ERROR_BASE - 5,
+ ERROR_HEARTBEAT_TERMINATE_REQUESTED = HEARTBEAT_ERROR_BASE - 6,
};
} // namespace android
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 18fd90e..4044c5d 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -48,6 +48,7 @@
kKeyBitRate = 'brte', // int32_t (bps)
kKeyESDS = 'esds', // raw data
kKeyAVCC = 'avcc', // raw data
+ kKeyD263 = 'd263', // raw data
kKeyVorbisInfo = 'vinf', // raw data
kKeyVorbisBooks = 'vboo', // raw data
kKeyWantsNALFragments = 'NALf',
@@ -113,11 +114,15 @@
// An indication that a video buffer has been rendered.
kKeyRendered = 'rend', // bool (int32_t)
+
+ // The language code for this media
+ kKeyMediaLanguage = 'lang', // cstring
};
enum {
kTypeESDS = 'esds',
kTypeAVCC = 'avcc',
+ kTypeD263 = 'd263',
};
class MetaData : public RefBase {
diff --git a/include/media/stagefright/NativeWindowWrapper.h b/include/media/stagefright/NativeWindowWrapper.h
new file mode 100644
index 0000000..f323cbc
--- /dev/null
+++ b/include/media/stagefright/NativeWindowWrapper.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 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 NATIVE_WINDOW_WRAPPER_H_
+
+#define NATIVE_WINDOW_WRAPPER_H_
+
+#include <surfaceflinger/Surface.h>
+#include <gui/SurfaceTextureClient.h>
+
+namespace android {
+
+// Both Surface and SurfaceTextureClient are RefBase that implement the
+// ANativeWindow interface, but at different addresses. ANativeWindow is not
+// a RefBase but acts like one for use with sp<>. This wrapper converts a
+// Surface or SurfaceTextureClient into a single reference-counted object
+// that holds an sp reference to the underlying Surface or SurfaceTextureClient,
+// It provides a method to get the ANativeWindow.
+
+struct NativeWindowWrapper : RefBase {
+ NativeWindowWrapper(
+ const sp<Surface> &surface) :
+ mSurface(surface) { }
+
+ NativeWindowWrapper(
+ const sp<SurfaceTextureClient> &surfaceTextureClient) :
+ mSurfaceTextureClient(surfaceTextureClient) { }
+
+ sp<ANativeWindow> getNativeWindow() const {
+ if (mSurface != NULL) {
+ return mSurface;
+ } else {
+ return mSurfaceTextureClient;
+ }
+ }
+
+ // If needed later we can provide a method to ask what kind of native window
+
+private:
+ // At most one of mSurface and mSurfaceTextureClient will be non-NULL
+ const sp<Surface> mSurface;
+ const sp<SurfaceTextureClient> mSurfaceTextureClient;
+
+ DISALLOW_EVIL_CONSTRUCTORS(NativeWindowWrapper);
+};
+
+} // namespace android
+
+#endif // NATIVE_WINDOW_WRAPPER_H_
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 82948cb..70daafa 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -50,6 +50,9 @@
// Only submit one input buffer at one time.
kOnlySubmitOneInputBufferAtOneTime = 64,
+
+ // Enable GRALLOC_USAGE_PROTECTED for output buffers from native window
+ kEnableGrallocUsageProtected = 128,
};
static sp<MediaSource> Create(
const sp<IOMX> &omx,
@@ -175,6 +178,7 @@
int64_t mSeekTimeUs;
ReadOptions::SeekMode mSeekMode;
int64_t mTargetTimeUs;
+ bool mOutputPortSettingsChangedPending;
MediaBuffer *mLeftOverBuffer;
@@ -196,6 +200,7 @@
bool mIsMetaDataStoredInVideoBuffers;
bool mOnlySubmitOneBufferAtOneTime;
+ bool mEnableGrallocUsageProtected;
OMXCodec(const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
bool isEncoder, const char *mime, const char *componentName,
@@ -209,6 +214,7 @@
void setAMRFormat(bool isWAMR, int32_t bitRate);
void setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate);
+ void setG711Format(int32_t numChannels);
status_t setVideoPortFormatType(
OMX_U32 portIndex,
diff --git a/include/media/stagefright/foundation/ABitReader.h b/include/media/stagefright/foundation/ABitReader.h
index 5135211..5510b12 100644
--- a/include/media/stagefright/foundation/ABitReader.h
+++ b/include/media/stagefright/foundation/ABitReader.h
@@ -31,6 +31,8 @@
uint32_t getBits(size_t n);
void skipBits(size_t n);
+ void putBits(uint32_t x, size_t n);
+
size_t numBitsLeft() const;
const uint8_t *data() const;
@@ -43,7 +45,6 @@
size_t mNumBitsLeft;
void fillReservoir();
- void putBits(uint32_t x, size_t n);
DISALLOW_EVIL_CONSTRUCTORS(ABitReader);
};
diff --git a/include/media/stagefright/foundation/AHandlerReflector.h b/include/media/stagefright/foundation/AHandlerReflector.h
index 857866a..9d201b5 100644
--- a/include/media/stagefright/foundation/AHandlerReflector.h
+++ b/include/media/stagefright/foundation/AHandlerReflector.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 A_HANDLER_REFLECTOR_H_
#define A_HANDLER_REFLECTOR_H_
diff --git a/include/media/stagefright/foundation/AHierarchicalStateMachine.h b/include/media/stagefright/foundation/AHierarchicalStateMachine.h
index b5786fb..d2e6b28 100644
--- a/include/media/stagefright/foundation/AHierarchicalStateMachine.h
+++ b/include/media/stagefright/foundation/AHierarchicalStateMachine.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 A_HIERARCHICAL_STATE_MACHINE_H_
#define A_HIERARCHICAL_STATE_MACHINE_H_
diff --git a/include/media/stagefright/openmax/OMX_Video.h b/include/media/stagefright/openmax/OMX_Video.h
index 2738bdc..4f8485d 100644
--- a/include/media/stagefright/openmax/OMX_Video.h
+++ b/include/media/stagefright/openmax/OMX_Video.h
@@ -85,6 +85,7 @@
OMX_VIDEO_CodingRV, /**< all versions of Real Video */
OMX_VIDEO_CodingAVC, /**< H.264/AVC */
OMX_VIDEO_CodingMJPEG, /**< Motion JPEG */
+ OMX_VIDEO_CodingVPX, /**< Google VPX, formerly known as On2 VP8 */
OMX_VIDEO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_VIDEO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_VIDEO_CodingMax = 0x7FFFFFFF
diff --git a/include/private/hwui/DrawGlInfo.h b/include/private/hwui/DrawGlInfo.h
new file mode 100644
index 0000000..1e9912b
--- /dev/null
+++ b/include/private/hwui/DrawGlInfo.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 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_HWUI_DRAW_GL_INFO_H
+#define ANDROID_HWUI_DRAW_GL_INFO_H
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Structure used by OpenGLRenderer::callDrawGLFunction() to pass and
+ * receive data from OpenGL functors.
+ */
+struct DrawGlInfo {
+ // Input: current clip rect
+ int clipLeft;
+ int clipTop;
+ int clipRight;
+ int clipBottom;
+
+ // Input: is the render target an FBO
+ bool isLayer;
+
+ // Input: current transform matrix, in OpenGL format
+ float transform[16];
+
+ // Output: dirty region to redraw
+ float dirtyLeft;
+ float dirtyTop;
+ float dirtyRight;
+ float dirtyBottom;
+}; // struct DrawGlInfo
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DRAW_GL_INFO_H
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index c6990bf..1827c3e 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -31,6 +31,7 @@
#define MAX_STARTUP_TIMEOUT_MS 3000 // Longer timeout period at startup to cope with A2DP init time
#define MAX_RUN_TIMEOUT_MS 1000
#define WAIT_PERIOD_MS 10
+#define RESTORE_TIMEOUT_MS 5000 // Maximum waiting time for a track to be restored
#define CBLK_UNDERRUN_MSK 0x0001
#define CBLK_UNDERRUN_ON 0x0001 // underrun (out) or overrrun (in) indication
@@ -47,6 +48,12 @@
#define CBLK_DISABLED_MSK 0x0010
#define CBLK_DISABLED_ON 0x0010 // track disabled by AudioFlinger due to underrun:
#define CBLK_DISABLED_OFF 0x0000 // must be re-started
+#define CBLK_RESTORING_MSK 0x0020
+#define CBLK_RESTORING_ON 0x0020 // track is being restored after invalidation
+#define CBLK_RESTORING_OFF 0x0000 // by AudioFlinger
+#define CBLK_RESTORED_MSK 0x0040
+#define CBLK_RESTORED_ON 0x0040 // track has been restored after invalidation
+#define CBLK_RESTORED_OFF 0x0040 // by AudioFlinger
struct audio_track_cblk_t
{
@@ -76,13 +83,12 @@
uint8_t frameSize;
uint8_t channelCount;
- uint16_t flags;
-
uint16_t bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger
- uint16_t waitTimeMs; // Cumulated wait time
+ uint16_t waitTimeMs; // Cumulated wait time
uint16_t sendLevel;
- uint16_t reserved;
+ volatile int32_t flags;
+
// Cache line boundary (32 bytes)
audio_track_cblk_t();
uint32_t stepUser(uint32_t frameCount);
@@ -91,6 +97,7 @@
uint32_t framesAvailable();
uint32_t framesAvailable_l();
uint32_t framesReady();
+ bool tryLock();
};
diff --git a/include/private/opengles/gl_context.h b/include/private/opengles/gl_context.h
index c7db9a6..6b1fa77 100644
--- a/include/private/opengles/gl_context.h
+++ b/include/private/opengles/gl_context.h
@@ -26,14 +26,11 @@
#endif
#include <private/pixelflinger/ggl_context.h>
-#include <hardware/copybit.h>
#include <hardware/gralloc.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
-struct android_native_buffer_t;
-
namespace android {
@@ -604,14 +601,6 @@
void (*renderTriangle)(GL, vertex_t*, vertex_t*, vertex_t*);
};
-struct copybits_context_t {
- // A handle to the blit engine, if it exists, else NULL.
- copybit_device_t* blitEngine;
- int32_t minScale;
- int32_t maxScale;
- android_native_buffer_t* drawSurfaceBuffer;
-};
-
struct ogles_context_t {
context_t rasterizer;
array_machine_t arrays __attribute__((aligned(32)));
@@ -636,13 +625,6 @@
EGLSurfaceManager* surfaceManager;
EGLBufferObjectManager* bufferObjectManager;
- // copybits is only used if LIBAGL_USE_GRALLOC_COPYBITS is
- // defined, but it is always present because ogles_context_t is a public
- // struct that is used by clients of libagl. We want the size and offsets
- // to stay the same, whether or not LIBAGL_USE_GRALLOC_COPYBITS is defined.
-
- copybits_context_t copybits;
-
GLenum error;
static inline ogles_context_t* get() {
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index eb599b5..717f837 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -65,7 +65,7 @@
// When changing these values, the COMPILE_TIME_ASSERT at the end of this
// file need to be updated.
static const unsigned int NUM_LAYERS_MAX = 31;
- static const unsigned int NUM_BUFFER_MAX = 16;
+ static const unsigned int NUM_BUFFER_MAX = 32;
static const unsigned int NUM_BUFFER_MIN = 2;
static const unsigned int NUM_DISPLAY_MAX = 4;
@@ -123,7 +123,7 @@
// ----------------------------------------------------------------------------
-// 32 KB max
+// 64 KB max
class SharedClient
{
public:
@@ -394,7 +394,7 @@
// ---------------------------------------------------------------------------
-COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 32768)
+COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 65536)
COMPILE_TIME_ASSERT(sizeof(surface_flinger_cblk_t) <= 4096)
// ---------------------------------------------------------------------------
diff --git a/media/libeffects/lvm/lib/Android.mk b/media/libeffects/lvm/lib/Android.mk
index ff34707..f49267e 100644
--- a/media/libeffects/lvm/lib/Android.mk
+++ b/media/libeffects/lvm/lib/Android.mk
@@ -105,7 +105,7 @@
LOCAL_MODULE:= libmusicbundle
-LOCAL_PRELINK_MODULE := false
+
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/Eq/lib \
@@ -168,7 +168,7 @@
LOCAL_MODULE:= libreverb
-LOCAL_PRELINK_MODULE := false
+
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/Reverb/lib \
diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk
index 2e9b9b4..99cfdfa 100644
--- a/media/libeffects/lvm/wrapper/Android.mk
+++ b/media/libeffects/lvm/wrapper/Android.mk
@@ -13,7 +13,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
-LOCAL_PRELINK_MODULE := false
+
LOCAL_STATIC_LIBRARIES += libmusicbundle
@@ -47,7 +47,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
-LOCAL_PRELINK_MODULE := false
+
LOCAL_STATIC_LIBRARIES += libreverb
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index ebe3302..0b061db 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -53,6 +53,11 @@
namespace android {
namespace {
+// Flag to allow a one time init of global memory, only happens on first call ever
+int LvmInitFlag = LVM_FALSE;
+SessionContext GlobalSessionMemory[LVM_MAX_SESSIONS];
+int SessionIndex[LVM_MAX_SESSIONS];
+
/* local functions */
#define CHECK_ARG(cond) { \
if (!(cond)) { \
@@ -61,11 +66,6 @@
} \
}
-// Flag to allow a one time init of global memory, only happens on first call ever
-int LvmInitFlag = LVM_FALSE;
-SessionContext GlobalSessionMemory[LVM_MAX_SESSIONS];
-
-int SessionIndex[LVM_MAX_SESSIONS];
// NXP SW BassBoost UUID
const effect_descriptor_t gBassBoostDescriptor = {
@@ -2588,9 +2588,11 @@
pContext->pBundledContext->SamplesToExitCountBb -= outBuffer->frameCount * 2; // STEREO
//LOGV("\tEffect_process: Waiting to turn off BASS_BOOST, %d samples left",
// pContext->pBundledContext->SamplesToExitCountBb);
- } else {
+ }
+ if(pContext->pBundledContext->SamplesToExitCountBb <= 0) {
status = -ENODATA;
pContext->pBundledContext->NumberEffectsEnabled--;
+ LOGV("\tEffect_process() this is the last frame for LVM_BASS_BOOST");
}
}
if ((pContext->pBundledContext->bVolumeEnabled == LVM_FALSE)&&
@@ -2606,9 +2608,11 @@
pContext->pBundledContext->SamplesToExitCountEq -= outBuffer->frameCount * 2; // STEREO
//LOGV("\tEffect_process: Waiting to turn off EQUALIZER, %d samples left",
// pContext->pBundledContext->SamplesToExitCountEq);
- } else {
+ }
+ if(pContext->pBundledContext->SamplesToExitCountEq <= 0) {
status = -ENODATA;
pContext->pBundledContext->NumberEffectsEnabled--;
+ LOGV("\tEffect_process() this is the last frame for LVM_EQUALIZER");
}
}
if ((pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE)&&
@@ -2618,9 +2622,11 @@
pContext->pBundledContext->SamplesToExitCountVirt -= outBuffer->frameCount * 2;// STEREO
//LOGV("\tEffect_process: Waiting for to turn off VIRTUALIZER, %d samples left",
// pContext->pBundledContext->SamplesToExitCountVirt);
- } else {
+ }
+ if(pContext->pBundledContext->SamplesToExitCountVirt <= 0) {
status = -ENODATA;
pContext->pBundledContext->NumberEffectsEnabled--;
+ LOGV("\tEffect_process() this is the last frame for LVM_VIRTUALIZER");
}
}
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 26c5aca..9097e20 100755
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -166,7 +166,7 @@
REVERB_VOLUME_RAMP,
};
-#define REVERB_DEFAULT_PRESET REVERB_PRESET_MEDIUMROOM
+#define REVERB_DEFAULT_PRESET REVERB_PRESET_NONE
#define REVERB_SEND_LEVEL (0x0C00) // 0.75 in 4.12 format
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
index e6ff654..3a0f438 100644
--- a/media/libeffects/visualizer/Android.mk
+++ b/media/libeffects/visualizer/Android.mk
@@ -25,6 +25,6 @@
LOCAL_C_INCLUDES := \
$(call include-path-for, graphics corecg)
-LOCAL_PRELINK_MODULE := false
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 74fb531..121e38a 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -1,4 +1,14 @@
LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ AudioParameter.cpp
+LOCAL_MODULE:= libmedia_helper
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
+
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
@@ -33,11 +43,15 @@
IEffectClient.cpp \
AudioEffect.cpp \
Visualizer.cpp \
+ MemoryLeakTrackUtil.cpp \
fixedfft.cpp.arm
LOCAL_SHARED_LIBRARIES := \
libui libcutils libutils libbinder libsonivox libicuuc libexpat \
- libsurfaceflinger_client libcamera_client libstagefright_foundation
+ libcamera_client libstagefright_foundation \
+ libgui
+
+LOCAL_WHOLE_STATIC_LIBRARY := libmedia_helper
LOCAL_MODULE:= libmedia
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index aadeba5..a043329 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -170,7 +170,6 @@
LOGV("Destructor %p", this);
if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) {
- setEnabled(false);
if (mIEffect != NULL) {
mIEffect->disconnect();
mIEffect->asBinder()->unlinkToDeath(mIEffectClient);
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
new file mode 100644
index 0000000..59ccfd0
--- /dev/null
+++ b/media/libmedia/AudioParameter.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2006-2011 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 "AudioParameter"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+
+#include <media/AudioParameter.h>
+
+namespace android {
+
+const char *AudioParameter::keyRouting = "routing";
+const char *AudioParameter::keySamplingRate = "sampling_rate";
+const char *AudioParameter::keyFormat = "format";
+const char *AudioParameter::keyChannels = "channels";
+const char *AudioParameter::keyFrameCount = "frame_count";
+const char *AudioParameter::keyInputSource = "input_source";
+
+AudioParameter::AudioParameter(const String8& keyValuePairs)
+{
+ char *str = new char[keyValuePairs.length()+1];
+ mKeyValuePairs = keyValuePairs;
+
+ strcpy(str, keyValuePairs.string());
+ char *pair = strtok(str, ";");
+ while (pair != NULL) {
+ if (strlen(pair) != 0) {
+ size_t eqIdx = strcspn(pair, "=");
+ String8 key = String8(pair, eqIdx);
+ String8 value;
+ if (eqIdx == strlen(pair)) {
+ value = String8("");
+ } else {
+ value = String8(pair + eqIdx + 1);
+ }
+ if (mParameters.indexOfKey(key) < 0) {
+ mParameters.add(key, value);
+ } else {
+ mParameters.replaceValueFor(key, value);
+ }
+ } else {
+ LOGV("AudioParameter() cstor empty key value pair");
+ }
+ pair = strtok(NULL, ";");
+ }
+
+ delete[] str;
+}
+
+AudioParameter::~AudioParameter()
+{
+ mParameters.clear();
+}
+
+String8 AudioParameter::toString()
+{
+ String8 str = String8("");
+
+ size_t size = mParameters.size();
+ for (size_t i = 0; i < size; i++) {
+ str += mParameters.keyAt(i);
+ str += "=";
+ str += mParameters.valueAt(i);
+ if (i < (size - 1)) str += ";";
+ }
+ return str;
+}
+
+status_t AudioParameter::add(const String8& key, const String8& value)
+{
+ if (mParameters.indexOfKey(key) < 0) {
+ mParameters.add(key, value);
+ return NO_ERROR;
+ } else {
+ mParameters.replaceValueFor(key, value);
+ return ALREADY_EXISTS;
+ }
+}
+
+status_t AudioParameter::addInt(const String8& key, const int value)
+{
+ char str[12];
+ if (snprintf(str, 12, "%d", value) > 0) {
+ String8 str8 = String8(str);
+ return add(key, str8);
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioParameter::addFloat(const String8& key, const float value)
+{
+ char str[23];
+ if (snprintf(str, 23, "%.10f", value) > 0) {
+ String8 str8 = String8(str);
+ return add(key, str8);
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioParameter::remove(const String8& key)
+{
+ if (mParameters.indexOfKey(key) >= 0) {
+ mParameters.removeItem(key);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioParameter::get(const String8& key, String8& value)
+{
+ if (mParameters.indexOfKey(key) >= 0) {
+ value = mParameters.valueFor(key);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioParameter::getInt(const String8& key, int& value)
+{
+ String8 str8;
+ status_t result = get(key, str8);
+ value = 0;
+ if (result == NO_ERROR) {
+ int val;
+ if (sscanf(str8.string(), "%d", &val) == 1) {
+ value = val;
+ } else {
+ result = INVALID_OPERATION;
+ }
+ }
+ return result;
+}
+
+status_t AudioParameter::getFloat(const String8& key, float& value)
+{
+ String8 str8;
+ status_t result = get(key, str8);
+ value = 0;
+ if (result == NO_ERROR) {
+ float val;
+ if (sscanf(str8.string(), "%f", &val) == 1) {
+ value = val;
+ } else {
+ result = INVALID_OPERATION;
+ }
+ }
+ return result;
+}
+
+status_t AudioParameter::getAt(size_t index, String8& key, String8& value)
+{
+ if (mParameters.size() > index) {
+ key = mParameters.keyAt(index);
+ value = mParameters.valueAt(index);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+}; // namespace android
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 1d6ffa0..446e3df 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -35,6 +35,10 @@
#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <utils/Timers.h>
+#include <utils/Atomic.h>
+
+#include <system/audio.h>
+#include <cutils/bitops.h>
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
@@ -65,8 +69,8 @@
// We double the size of input buffer for ping pong use of record buffer.
size <<= 1;
- if (AudioSystem::isLinearPCM(format)) {
- size /= channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1);
+ if (audio_is_linear_pcm(format)) {
+ size /= channelCount * (format == AUDIO_FORMAT_PCM_16_BIT ? 2 : 1);
}
*frameCount = size;
@@ -128,6 +132,9 @@
{
LOGV("set(): sampleRate %d, channels %d, frameCount %d",sampleRate, channels, frameCount);
+
+ AutoMutex lock(mLock);
+
if (mAudioRecord != 0) {
return INVALID_OPERATION;
}
@@ -141,22 +148,22 @@
}
// these below should probably come from the audioFlinger too...
if (format == 0) {
- format = AudioSystem::PCM_16_BIT;
+ format = AUDIO_FORMAT_PCM_16_BIT;
}
// validate parameters
- if (!AudioSystem::isValidFormat(format)) {
+ if (!audio_is_valid_format(format)) {
LOGE("Invalid format");
return BAD_VALUE;
}
- if (!AudioSystem::isInputChannel(channels)) {
+ if (!audio_is_input_channel(channels)) {
return BAD_VALUE;
}
- int channelCount = AudioSystem::popCount(channels);
+ int channelCount = popcount(channels);
audio_io_handle_t input = AudioSystem::getInput(inputSource,
- sampleRate, format, channels, (AudioSystem::audio_in_acoustics)flags);
+ sampleRate, format, channels, (audio_in_acoustics_t)flags);
if (input == 0) {
LOGE("Could not get audio input for record source %d", inputSource);
return BAD_VALUE;
@@ -183,7 +190,7 @@
mSessionId = sessionId;
// create the IAudioRecord
- status = openRecord(sampleRate, format, channelCount,
+ status = openRecord_l(sampleRate, format, channelCount,
frameCount, flags, input);
if (status != NO_ERROR) {
return status;
@@ -250,8 +257,8 @@
int AudioRecord::frameSize() const
{
- if (AudioSystem::isLinearPCM(mFormat)) {
- return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t));
+ if (audio_is_linear_pcm(mFormat)) {
+ return channelCount()*((format() == AUDIO_FORMAT_PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t));
} else {
return sizeof(uint8_t);
}
@@ -282,21 +289,31 @@
}
AutoMutex lock(mLock);
+ // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
+ // while we are accessing the cblk
+ sp <IAudioRecord> audioRecord = mAudioRecord;
+ sp <IMemory> iMem = mCblkMemory;
+ audio_track_cblk_t* cblk = mCblk;
if (mActive == 0) {
mActive = 1;
- ret = mAudioRecord->start();
- if (ret == DEAD_OBJECT) {
- LOGV("start() dead IAudioRecord: creating a new one");
- ret = openRecord(mCblk->sampleRate, mFormat, mChannelCount,
- mFrameCount, mFlags, getInput());
- if (ret == NO_ERROR) {
- ret = mAudioRecord->start();
+
+ cblk->lock.lock();
+ if (!(cblk->flags & CBLK_INVALID_MSK)) {
+ cblk->lock.unlock();
+ ret = mAudioRecord->start();
+ cblk->lock.lock();
+ if (ret == DEAD_OBJECT) {
+ android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
}
}
+ if (cblk->flags & CBLK_INVALID_MSK) {
+ ret = restoreRecord_l(cblk);
+ }
+ cblk->lock.unlock();
if (ret == NO_ERROR) {
- mNewPosition = mCblk->user + mUpdatePeriod;
- mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
- mCblk->waitTimeMs = 0;
+ mNewPosition = cblk->user + mUpdatePeriod;
+ cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+ cblk->waitTimeMs = 0;
if (t != 0) {
t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT);
} else {
@@ -353,6 +370,7 @@
uint32_t AudioRecord::getSampleRate()
{
+ AutoMutex lock(mLock);
return mCblk->sampleRate;
}
@@ -400,6 +418,7 @@
{
if (position == 0) return BAD_VALUE;
+ AutoMutex lock(mLock);
*position = mCblk->user;
return NO_ERROR;
@@ -415,7 +434,8 @@
// -------------------------------------------------------------------------
-status_t AudioRecord::openRecord(
+// must be called with mLock held
+status_t AudioRecord::openRecord_l(
uint32_t sampleRate,
int format,
int channelCount,
@@ -451,7 +471,7 @@
mCblkMemory = cblk;
mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
- mCblk->flags &= ~CBLK_DIRECTION_MSK;
+ android_atomic_and(~CBLK_DIRECTION_MSK, &mCblk->flags);
mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
mCblk->waitTimeMs = 0;
return NO_ERROR;
@@ -459,6 +479,7 @@
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
+ AutoMutex lock(mLock);
int active;
status_t result;
audio_track_cblk_t* cblk = mCblk;
@@ -483,7 +504,19 @@
cblk->lock.unlock();
return WOULD_BLOCK;
}
- result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
+ if (!(cblk->flags & CBLK_INVALID_MSK)) {
+ mLock.unlock();
+ result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
+ cblk->lock.unlock();
+ mLock.lock();
+ if (mActive == 0) {
+ return status_t(STOPPED);
+ }
+ cblk->lock.lock();
+ }
+ if (cblk->flags & CBLK_INVALID_MSK) {
+ goto create_new_record;
+ }
if (__builtin_expect(result!=NO_ERROR, false)) {
cblk->waitTimeMs += waitTimeMs;
if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
@@ -491,16 +524,17 @@
"user=%08x, server=%08x", cblk->user, cblk->server);
cblk->lock.unlock();
result = mAudioRecord->start();
- if (result == DEAD_OBJECT) {
- LOGW("obtainBuffer() dead IAudioRecord: creating a new one");
- result = openRecord(cblk->sampleRate, mFormat, mChannelCount,
- mFrameCount, mFlags, getInput());
- if (result == NO_ERROR) {
- cblk = mCblk;
- mAudioRecord->start();
- }
- }
cblk->lock.lock();
+ if (result == DEAD_OBJECT) {
+ android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
+create_new_record:
+ result = AudioRecord::restoreRecord_l(cblk);
+ }
+ if (result != NO_ERROR) {
+ LOGW("obtainBuffer create Track error %d", result);
+ cblk->lock.unlock();
+ return result;
+ }
cblk->waitTimeMs = 0;
}
if (--waitCount == 0) {
@@ -540,16 +574,23 @@
void AudioRecord::releaseBuffer(Buffer* audioBuffer)
{
- audio_track_cblk_t* cblk = mCblk;
- cblk->stepUser(audioBuffer->frameCount);
+ AutoMutex lock(mLock);
+ mCblk->stepUser(audioBuffer->frameCount);
}
audio_io_handle_t AudioRecord::getInput()
{
+ AutoMutex lock(mLock);
+ return getInput_l();
+}
+
+// must be called with mLock held
+audio_io_handle_t AudioRecord::getInput_l()
+{
mInput = AudioSystem::getInput(mInputSource,
mCblk->sampleRate,
mFormat, mChannels,
- (AudioSystem::audio_in_acoustics)mFlags);
+ (audio_in_acoustics_t)mFlags);
return mInput;
}
@@ -573,6 +614,12 @@
return BAD_VALUE;
}
+ mLock.lock();
+ // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
+ // while we are accessing the cblk
+ sp <IAudioRecord> audioRecord = mAudioRecord;
+ sp <IMemory> iMem = mCblkMemory;
+ mLock.unlock();
do {
@@ -613,9 +660,17 @@
uint32_t frames = mRemainingFrames;
size_t readSize;
+ mLock.lock();
+ // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
+ // while we are accessing the cblk
+ sp <IAudioRecord> audioRecord = mAudioRecord;
+ sp <IMemory> iMem = mCblkMemory;
+ audio_track_cblk_t* cblk = mCblk;
+ mLock.unlock();
+
// Manage marker callback
if (!mMarkerReached && (mMarkerPosition > 0)) {
- if (mCblk->user >= mMarkerPosition) {
+ if (cblk->user >= mMarkerPosition) {
mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
mMarkerReached = true;
}
@@ -623,7 +678,7 @@
// Manage new position callback
if (mUpdatePeriod > 0) {
- while (mCblk->user >= mNewPosition) {
+ while (cblk->user >= mNewPosition) {
mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
mNewPosition += mUpdatePeriod;
}
@@ -669,11 +724,10 @@
// Manage overrun callback
- if (mActive && (mCblk->framesAvailable_l() == 0)) {
- LOGV("Overrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags);
- if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
+ if (mActive && (cblk->framesAvailable() == 0)) {
+ LOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
+ if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) {
mCbf(EVENT_OVERRUN, mUserData, 0);
- mCblk->flags |= CBLK_UNDERRUN_ON;
}
}
@@ -685,6 +739,65 @@
return true;
}
+// must be called with mLock and cblk.lock held. Callers must also hold strong references on
+// the IAudioRecord and IMemory in case they are recreated here.
+// If the IAudioRecord is successfully restored, the cblk pointer is updated
+status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk)
+{
+ status_t result;
+
+ if (!(android_atomic_or(CBLK_RESTORING_ON, &cblk->flags) & CBLK_RESTORING_MSK)) {
+ LOGW("dead IAudioRecord, creating a new one");
+ // signal old cblk condition so that other threads waiting for available buffers stop
+ // waiting now
+ cblk->cv.broadcast();
+ cblk->lock.unlock();
+
+ // if the new IAudioRecord is created, openRecord_l() will modify the
+ // following member variables: mAudioRecord, mCblkMemory and mCblk.
+ // It will also delete the strong references on previous IAudioRecord and IMemory
+ result = openRecord_l(cblk->sampleRate, mFormat, mChannelCount,
+ mFrameCount, mFlags, getInput_l());
+ if (result == NO_ERROR) {
+ result = mAudioRecord->start();
+ }
+ if (result != NO_ERROR) {
+ mActive = false;
+ }
+
+ // signal old cblk condition for other threads waiting for restore completion
+ android_atomic_or(CBLK_RESTORED_ON, &cblk->flags);
+ cblk->cv.broadcast();
+ } else {
+ if (!(cblk->flags & CBLK_RESTORED_MSK)) {
+ LOGW("dead IAudioRecord, waiting for a new one to be created");
+ mLock.unlock();
+ result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS));
+ cblk->lock.unlock();
+ mLock.lock();
+ } else {
+ LOGW("dead IAudioRecord, already restored");
+ result = NO_ERROR;
+ cblk->lock.unlock();
+ }
+ if (result != NO_ERROR || mActive == 0) {
+ result = status_t(STOPPED);
+ }
+ }
+ LOGV("restoreRecord_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x",
+ result, mActive, mCblk, cblk, mCblk->flags, cblk->flags);
+
+ if (result == NO_ERROR) {
+ // from now on we switch to the newly created cblk
+ cblk = mCblk;
+ }
+ cblk->lock.lock();
+
+ LOGW_IF(result != NO_ERROR, "restoreRecord_l() error %d", result);
+
+ return result;
+}
+
// =========================================================================
AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver, bool bCanCallJava)
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 9d9b3c0..8a180d8 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -23,6 +23,8 @@
#include <media/IAudioPolicyService.h>
#include <math.h>
+#include <system/audio.h>
+
// ----------------------------------------------------------------------------
// the sim build doesn't have gettid
@@ -45,7 +47,7 @@
// Cached values for recording queries
uint32_t AudioSystem::gPrevInSamplingRate = 16000;
-int AudioSystem::gPrevInFormat = AudioSystem::PCM_16_BIT;
+int AudioSystem::gPrevInFormat = AUDIO_FORMAT_PCM_16_BIT;
int AudioSystem::gPrevInChannelCount = 1;
size_t AudioSystem::gInBuffSize = 0;
@@ -127,7 +129,7 @@
status_t AudioSystem::setStreamVolume(int stream, float value, int output)
{
- if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE;
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
af->setStreamVolume(stream, value, output);
@@ -136,7 +138,7 @@
status_t AudioSystem::setStreamMute(int stream, bool mute)
{
- if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE;
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
af->setStreamMute(stream, mute);
@@ -145,7 +147,7 @@
status_t AudioSystem::getStreamVolume(int stream, float* volume, int output)
{
- if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE;
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
*volume = af->streamVolume(stream, output);
@@ -154,7 +156,7 @@
status_t AudioSystem::getStreamMute(int stream, bool* mute)
{
- if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE;
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
*mute = af->streamMute(stream);
@@ -163,7 +165,7 @@
status_t AudioSystem::setMode(int mode)
{
- if (mode >= NUM_MODES) return BAD_VALUE;
+ if (mode >= AUDIO_MODE_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setMode(mode);
@@ -213,11 +215,11 @@
OutputDescriptor *outputDesc;
audio_io_handle_t output;
- if (streamType == DEFAULT) {
- streamType = MUSIC;
+ if (streamType == AUDIO_STREAM_DEFAULT) {
+ streamType = AUDIO_STREAM_MUSIC;
}
- output = getOutput((stream_type)streamType);
+ output = getOutput((audio_stream_type_t)streamType);
if (output == 0) {
return PERMISSION_DENIED;
}
@@ -246,11 +248,11 @@
OutputDescriptor *outputDesc;
audio_io_handle_t output;
- if (streamType == DEFAULT) {
- streamType = MUSIC;
+ if (streamType == AUDIO_STREAM_DEFAULT) {
+ streamType = AUDIO_STREAM_MUSIC;
}
- output = getOutput((stream_type)streamType);
+ output = getOutput((audio_stream_type_t)streamType);
if (output == 0) {
return PERMISSION_DENIED;
}
@@ -277,11 +279,11 @@
OutputDescriptor *outputDesc;
audio_io_handle_t output;
- if (streamType == DEFAULT) {
- streamType = MUSIC;
+ if (streamType == AUDIO_STREAM_DEFAULT) {
+ streamType = AUDIO_STREAM_MUSIC;
}
- output = getOutput((stream_type)streamType);
+ output = getOutput((audio_stream_type_t)streamType);
if (output == 0) {
return PERMISSION_DENIED;
}
@@ -338,11 +340,11 @@
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
- if (stream == DEFAULT) {
- stream = MUSIC;
+ if (stream == AUDIO_STREAM_DEFAULT) {
+ stream = AUDIO_STREAM_MUSIC;
}
- return af->getRenderPosition(halFrames, dspFrames, getOutput((stream_type)stream));
+ return af->getRenderPosition(halFrames, dspFrames, getOutput((audio_stream_type_t)stream));
}
unsigned int AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle) {
@@ -455,10 +457,10 @@
bool AudioSystem::routedToA2dpOutput(int streamType) {
switch(streamType) {
- case MUSIC:
- case VOICE_CALL:
- case BLUETOOTH_SCO:
- case SYSTEM:
+ case AUDIO_STREAM_MUSIC:
+ case AUDIO_STREAM_VOICE_CALL:
+ case AUDIO_STREAM_BLUETOOTH_SCO:
+ case AUDIO_STREAM_SYSTEM:
return true;
default:
return false;
@@ -497,9 +499,9 @@
return gAudioPolicyService;
}
-status_t AudioSystem::setDeviceConnectionState(audio_devices device,
- device_connection_state state,
- const char *device_address)
+status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
+ audio_policy_dev_state_t state,
+ const char *device_address)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
@@ -507,11 +509,11 @@
return aps->setDeviceConnectionState(device, state, device_address);
}
-AudioSystem::device_connection_state AudioSystem::getDeviceConnectionState(audio_devices device,
+audio_policy_dev_state_t AudioSystem::getDeviceConnectionState(audio_devices_t device,
const char *device_address)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- if (aps == 0) return DEVICE_STATE_UNAVAILABLE;
+ if (aps == 0) return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
return aps->getDeviceConnectionState(device, device_address);
}
@@ -531,26 +533,26 @@
return aps->setRingerMode(mode, mask);
}
-status_t AudioSystem::setForceUse(force_use usage, forced_config config)
+status_t AudioSystem::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setForceUse(usage, config);
}
-AudioSystem::forced_config AudioSystem::getForceUse(force_use usage)
+audio_policy_forced_cfg_t AudioSystem::getForceUse(audio_policy_force_use_t usage)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- if (aps == 0) return FORCE_NONE;
+ if (aps == 0) return AUDIO_POLICY_FORCE_NONE;
return aps->getForceUse(usage);
}
-audio_io_handle_t AudioSystem::getOutput(stream_type stream,
+audio_io_handle_t AudioSystem::getOutput(audio_stream_type_t stream,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
- output_flags flags)
+ audio_policy_output_flags_t flags)
{
audio_io_handle_t output = 0;
// Do not use stream to output map cache if the direct output
@@ -561,9 +563,9 @@
// be reworked for proper operation with direct outputs. This code is too specific
// to the first use case we want to cover (Voice Recognition and Voice Dialer over
// Bluetooth SCO
- if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) == 0 &&
- ((stream != AudioSystem::VOICE_CALL && stream != AudioSystem::BLUETOOTH_SCO) ||
- channels != AudioSystem::CHANNEL_OUT_MONO ||
+ if ((flags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT) == 0 &&
+ ((stream != AUDIO_STREAM_VOICE_CALL && stream != AUDIO_STREAM_BLUETOOTH_SCO) ||
+ channels != AUDIO_CHANNEL_OUT_MONO ||
(samplingRate != 8000 && samplingRate != 16000))) {
Mutex::Autolock _l(gLock);
output = AudioSystem::gStreamOutputMap.valueFor(stream);
@@ -573,7 +575,7 @@
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return 0;
output = aps->getOutput(stream, samplingRate, format, channels, flags);
- if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) == 0) {
+ if ((flags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT) == 0) {
Mutex::Autolock _l(gLock);
AudioSystem::gStreamOutputMap.add(stream, output);
}
@@ -582,7 +584,7 @@
}
status_t AudioSystem::startOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
@@ -591,7 +593,7 @@
}
status_t AudioSystem::stopOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
@@ -610,7 +612,7 @@
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
- audio_in_acoustics acoustics)
+ audio_in_acoustics_t acoustics)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return 0;
@@ -638,7 +640,7 @@
aps->releaseInput(input);
}
-status_t AudioSystem::initStreamVolume(stream_type stream,
+status_t AudioSystem::initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax)
{
@@ -647,27 +649,34 @@
return aps->initStreamVolume(stream, indexMin, indexMax);
}
-status_t AudioSystem::setStreamVolumeIndex(stream_type stream, int index)
+status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream, int index)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setStreamVolumeIndex(stream, index);
}
-status_t AudioSystem::getStreamVolumeIndex(stream_type stream, int *index)
+status_t AudioSystem::getStreamVolumeIndex(audio_stream_type_t stream, int *index)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->getStreamVolumeIndex(stream, index);
}
-uint32_t AudioSystem::getStrategyForStream(AudioSystem::stream_type stream)
+uint32_t AudioSystem::getStrategyForStream(audio_stream_type_t stream)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return 0;
return aps->getStrategyForStream(stream);
}
+uint32_t AudioSystem::getDevicesForStream(audio_stream_type_t stream)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return 0;
+ return aps->getDevicesForStream(stream);
+}
+
audio_io_handle_t AudioSystem::getOutputForEffect(effect_descriptor_t *desc)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
@@ -710,276 +719,5 @@
LOGW("AudioPolicyService server died!");
}
-// ---------------------------------------------------------------------------
-
-
-// use emulated popcount optimization
-// http://www.df.lth.se/~john_e/gems/gem002d.html
-uint32_t AudioSystem::popCount(uint32_t u)
-{
- u = ((u&0x55555555) + ((u>>1)&0x55555555));
- u = ((u&0x33333333) + ((u>>2)&0x33333333));
- u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
- u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
- u = ( u&0x0000ffff) + (u>>16);
- return u;
-}
-
-bool AudioSystem::isOutputDevice(audio_devices device)
-{
- if ((popCount(device) == 1 ) &&
- ((device & ~AudioSystem::DEVICE_OUT_ALL) == 0)) {
- return true;
- } else {
- return false;
- }
-}
-
-bool AudioSystem::isInputDevice(audio_devices device)
-{
- if ((popCount(device) == 1 ) &&
- ((device & ~AudioSystem::DEVICE_IN_ALL) == 0)) {
- return true;
- } else {
- return false;
- }
-}
-
-bool AudioSystem::isA2dpDevice(audio_devices device)
-{
- if ((popCount(device) == 1 ) &&
- (device & (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
- AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
- AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER))) {
- return true;
- } else {
- return false;
- }
-}
-
-bool AudioSystem::isBluetoothScoDevice(audio_devices device)
-{
- if ((popCount(device) == 1 ) &&
- (device & (AudioSystem::DEVICE_OUT_BLUETOOTH_SCO |
- AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
- AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
- AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET))) {
- return true;
- } else {
- return false;
- }
-}
-
-bool AudioSystem::isLowVisibility(stream_type stream)
-{
- if (stream == AudioSystem::SYSTEM ||
- stream == AudioSystem::NOTIFICATION ||
- stream == AudioSystem::RING) {
- return true;
- } else {
- return false;
- }
-}
-
-bool AudioSystem::isInputChannel(uint32_t channel)
-{
- if ((channel & ~AudioSystem::CHANNEL_IN_ALL) == 0) {
- return true;
- } else {
- return false;
- }
-}
-
-bool AudioSystem::isOutputChannel(uint32_t channel)
-{
- if ((channel & ~AudioSystem::CHANNEL_OUT_ALL) == 0) {
- return true;
- } else {
- return false;
- }
-}
-
-bool AudioSystem::isValidFormat(uint32_t format)
-{
- switch (format & MAIN_FORMAT_MASK) {
- case PCM:
- case MP3:
- case AMR_NB:
- case AMR_WB:
- case AAC:
- case HE_AAC_V1:
- case HE_AAC_V2:
- case VORBIS:
- return true;
- default:
- return false;
- }
-}
-
-bool AudioSystem::isLinearPCM(uint32_t format)
-{
- switch (format) {
- case PCM_16_BIT:
- case PCM_8_BIT:
- return true;
- default:
- return false;
- }
-}
-
-//------------------------- AudioParameter class implementation ---------------
-
-const char *AudioParameter::keyRouting = "routing";
-const char *AudioParameter::keySamplingRate = "sampling_rate";
-const char *AudioParameter::keyFormat = "format";
-const char *AudioParameter::keyChannels = "channels";
-const char *AudioParameter::keyFrameCount = "frame_count";
-const char *AudioParameter::keyInputSource = "input_source";
-
-AudioParameter::AudioParameter(const String8& keyValuePairs)
-{
- char *str = new char[keyValuePairs.length()+1];
- mKeyValuePairs = keyValuePairs;
-
- strcpy(str, keyValuePairs.string());
- char *pair = strtok(str, ";");
- while (pair != NULL) {
- if (strlen(pair) != 0) {
- size_t eqIdx = strcspn(pair, "=");
- String8 key = String8(pair, eqIdx);
- String8 value;
- if (eqIdx == strlen(pair)) {
- value = String8("");
- } else {
- value = String8(pair + eqIdx + 1);
- }
- if (mParameters.indexOfKey(key) < 0) {
- mParameters.add(key, value);
- } else {
- mParameters.replaceValueFor(key, value);
- }
- } else {
- LOGV("AudioParameter() cstor empty key value pair");
- }
- pair = strtok(NULL, ";");
- }
-
- delete[] str;
-}
-
-AudioParameter::~AudioParameter()
-{
- mParameters.clear();
-}
-
-String8 AudioParameter::toString()
-{
- String8 str = String8("");
-
- size_t size = mParameters.size();
- for (size_t i = 0; i < size; i++) {
- str += mParameters.keyAt(i);
- str += "=";
- str += mParameters.valueAt(i);
- if (i < (size - 1)) str += ";";
- }
- return str;
-}
-
-status_t AudioParameter::add(const String8& key, const String8& value)
-{
- if (mParameters.indexOfKey(key) < 0) {
- mParameters.add(key, value);
- return NO_ERROR;
- } else {
- mParameters.replaceValueFor(key, value);
- return ALREADY_EXISTS;
- }
-}
-
-status_t AudioParameter::addInt(const String8& key, const int value)
-{
- char str[12];
- if (snprintf(str, 12, "%d", value) > 0) {
- String8 str8 = String8(str);
- return add(key, str8);
- } else {
- return BAD_VALUE;
- }
-}
-
-status_t AudioParameter::addFloat(const String8& key, const float value)
-{
- char str[23];
- if (snprintf(str, 23, "%.10f", value) > 0) {
- String8 str8 = String8(str);
- return add(key, str8);
- } else {
- return BAD_VALUE;
- }
-}
-
-status_t AudioParameter::remove(const String8& key)
-{
- if (mParameters.indexOfKey(key) >= 0) {
- mParameters.removeItem(key);
- return NO_ERROR;
- } else {
- return BAD_VALUE;
- }
-}
-
-status_t AudioParameter::get(const String8& key, String8& value)
-{
- if (mParameters.indexOfKey(key) >= 0) {
- value = mParameters.valueFor(key);
- return NO_ERROR;
- } else {
- return BAD_VALUE;
- }
-}
-
-status_t AudioParameter::getInt(const String8& key, int& value)
-{
- String8 str8;
- status_t result = get(key, str8);
- value = 0;
- if (result == NO_ERROR) {
- int val;
- if (sscanf(str8.string(), "%d", &val) == 1) {
- value = val;
- } else {
- result = INVALID_OPERATION;
- }
- }
- return result;
-}
-
-status_t AudioParameter::getFloat(const String8& key, float& value)
-{
- String8 str8;
- status_t result = get(key, str8);
- value = 0;
- if (result == NO_ERROR) {
- float val;
- if (sscanf(str8.string(), "%f", &val) == 1) {
- value = val;
- } else {
- result = INVALID_OPERATION;
- }
- }
- return result;
-}
-
-status_t AudioParameter::getAt(size_t index, String8& key, String8& value)
-{
- if (mParameters.size() > index) {
- key = mParameters.keyAt(index);
- value = mParameters.valueAt(index);
- return NO_ERROR;
- } else {
- return BAD_VALUE;
- }
-}
}; // namespace android
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index c1bed59..7520ed9 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -35,6 +35,12 @@
#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <utils/Timers.h>
+#include <utils/Atomic.h>
+
+#include <cutils/bitops.h>
+
+#include <system/audio.h>
+#include <hardware/audio_policy.h>
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
@@ -148,6 +154,7 @@
LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
+ AutoMutex lock(mLock);
if (mAudioTrack != 0) {
LOGE("Track already in use");
return INVALID_OPERATION;
@@ -163,39 +170,41 @@
}
// handle default values first.
- if (streamType == AudioSystem::DEFAULT) {
- streamType = AudioSystem::MUSIC;
+ if (streamType == AUDIO_STREAM_DEFAULT) {
+ streamType = AUDIO_STREAM_MUSIC;
}
if (sampleRate == 0) {
sampleRate = afSampleRate;
}
// these below should probably come from the audioFlinger too...
if (format == 0) {
- format = AudioSystem::PCM_16_BIT;
+ format = AUDIO_FORMAT_PCM_16_BIT;
}
if (channels == 0) {
- channels = AudioSystem::CHANNEL_OUT_STEREO;
+ channels = AUDIO_CHANNEL_OUT_STEREO;
}
// validate parameters
- if (!AudioSystem::isValidFormat(format)) {
+ if (!audio_is_valid_format(format)) {
LOGE("Invalid format");
return BAD_VALUE;
}
// force direct flag if format is not linear PCM
- if (!AudioSystem::isLinearPCM(format)) {
- flags |= AudioSystem::OUTPUT_FLAG_DIRECT;
+ if (!audio_is_linear_pcm(format)) {
+ flags |= AUDIO_POLICY_OUTPUT_FLAG_DIRECT;
}
- if (!AudioSystem::isOutputChannel(channels)) {
+ if (!audio_is_output_channel(channels)) {
LOGE("Invalid channel mask");
return BAD_VALUE;
}
- uint32_t channelCount = AudioSystem::popCount(channels);
+ uint32_t channelCount = popcount(channels);
- audio_io_handle_t output = AudioSystem::getOutput((AudioSystem::stream_type)streamType,
- sampleRate, format, channels, (AudioSystem::output_flags)flags);
+ audio_io_handle_t output = AudioSystem::getOutput(
+ (audio_stream_type_t)streamType,
+ sampleRate,format, channels,
+ (audio_policy_output_flags_t)flags);
if (output == 0) {
LOGE("Could not get audio output for stream type %d", streamType);
@@ -211,8 +220,15 @@
mAuxEffectId = 0;
// create the IAudioTrack
- status_t status = createTrack(streamType, sampleRate, format, channelCount,
- frameCount, flags, sharedBuffer, output, true);
+ status_t status = createTrack_l(streamType,
+ sampleRate,
+ format,
+ channelCount,
+ frameCount,
+ flags,
+ sharedBuffer,
+ output,
+ true);
if (status != NO_ERROR) {
return status;
@@ -281,8 +297,8 @@
int AudioTrack::frameSize() const
{
- if (AudioSystem::isLinearPCM(mFormat)) {
- return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t));
+ if (audio_is_linear_pcm(mFormat)) {
+ return channelCount()*((format() == AUDIO_FORMAT_PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t));
} else {
return sizeof(uint8_t);
}
@@ -312,37 +328,38 @@
}
AutoMutex lock(mLock);
+ // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
+ // while we are accessing the cblk
+ sp <IAudioTrack> audioTrack = mAudioTrack;
+ sp <IMemory> iMem = mCblkMemory;
+ audio_track_cblk_t* cblk = mCblk;
+
if (mActive == 0) {
mActive = 1;
- mNewPosition = mCblk->server + mUpdatePeriod;
- mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
- mCblk->waitTimeMs = 0;
- mCblk->flags &= ~CBLK_DISABLED_ON;
+ mNewPosition = cblk->server + mUpdatePeriod;
+ cblk->lock.lock();
+ cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
+ cblk->waitTimeMs = 0;
+ android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags);
if (t != 0) {
t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
} else {
setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
}
- if (mCblk->flags & CBLK_INVALID_MSK) {
- LOGW("start() track %p invalidated, creating a new one", this);
- // no need to clear the invalid flag as this cblk will not be used anymore
- // force new track creation
- status = DEAD_OBJECT;
- } else {
+ LOGV("start %p before lock cblk %p", this, mCblk);
+ if (!(cblk->flags & CBLK_INVALID_MSK)) {
+ cblk->lock.unlock();
status = mAudioTrack->start();
- }
- if (status == DEAD_OBJECT) {
- LOGV("start() dead IAudioTrack: creating a new one");
- status = createTrack(mStreamType, mCblk->sampleRate, mFormat, mChannelCount,
- mFrameCount, mFlags, mSharedBuffer, getOutput(), false);
- if (status == NO_ERROR) {
- status = mAudioTrack->start();
- if (status == NO_ERROR) {
- mNewPosition = mCblk->server + mUpdatePeriod;
- }
+ cblk->lock.lock();
+ if (status == DEAD_OBJECT) {
+ android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
}
}
+ if (cblk->flags & CBLK_INVALID_MSK) {
+ status = restoreTrack_l(cblk, true);
+ }
+ cblk->lock.unlock();
if (status != NO_ERROR) {
LOGV("start() failed");
mActive = 0;
@@ -375,14 +392,14 @@
mAudioTrack->stop();
// Cancel loops (If we are in the middle of a loop, playback
// would not stop until loopCount reaches 0).
- setLoop(0, 0, 0);
+ setLoop_l(0, 0, 0);
// the playback head position will reset to 0, so if a marker is set, we need
// to activate it again
mMarkerReached = false;
// Force flush if a shared buffer is used otherwise audioflinger
// will not stop before end of buffer is reached.
if (mSharedBuffer != 0) {
- flush();
+ flush_l();
}
if (t != 0) {
t->requestExit();
@@ -403,6 +420,13 @@
void AudioTrack::flush()
{
+ AutoMutex lock(mLock);
+ flush_l();
+}
+
+// must be called with mLock held
+void AudioTrack::flush_l()
+{
LOGV("flush");
// clear playback marker and periodic update counter
@@ -445,6 +469,7 @@
return BAD_VALUE;
}
+ AutoMutex lock(mLock);
mVolume[LEFT] = left;
mVolume[RIGHT] = right;
@@ -470,6 +495,7 @@
if (level > 1.0f) {
return BAD_VALUE;
}
+ AutoMutex lock(mLock);
mSendLevel = level;
@@ -495,17 +521,26 @@
// Resampler implementation limits input sampling rate to 2 x output sampling rate.
if (rate <= 0 || rate > afSamplingRate*2 ) return BAD_VALUE;
+ AutoMutex lock(mLock);
mCblk->sampleRate = rate;
return NO_ERROR;
}
uint32_t AudioTrack::getSampleRate()
{
+ AutoMutex lock(mLock);
return mCblk->sampleRate;
}
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
+ AutoMutex lock(mLock);
+ return setLoop_l(loopStart, loopEnd, loopCount);
+}
+
+// must be called with mLock held
+status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount)
+{
audio_track_cblk_t* cblk = mCblk;
Mutex::Autolock _l(cblk->lock);
@@ -519,12 +554,13 @@
}
if (loopStart >= loopEnd ||
- loopEnd - loopStart > cblk->frameCount) {
+ loopEnd - loopStart > cblk->frameCount ||
+ cblk->server > loopStart) {
LOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %d", loopStart, loopEnd, loopCount, cblk->frameCount, cblk->user);
return BAD_VALUE;
}
- if ((mSharedBuffer != 0) && (loopEnd > cblk->frameCount)) {
+ if ((mSharedBuffer != 0) && (loopEnd > cblk->frameCount)) {
LOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, framecount %d",
loopStart, loopEnd, cblk->frameCount);
return BAD_VALUE;
@@ -540,6 +576,7 @@
status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount)
{
+ AutoMutex lock(mLock);
if (loopStart != 0) {
*loopStart = mCblk->loopStart;
}
@@ -599,6 +636,7 @@
status_t AudioTrack::setPosition(uint32_t position)
{
+ AutoMutex lock(mLock);
Mutex::Autolock _l(mCblk->lock);
if (!stopped()) return INVALID_OPERATION;
@@ -606,7 +644,7 @@
if (position > mCblk->user) return BAD_VALUE;
mCblk->server = position;
- mCblk->flags |= CBLK_FORCEREADY_ON;
+ android_atomic_or(CBLK_FORCEREADY_ON, &mCblk->flags);
return NO_ERROR;
}
@@ -614,7 +652,7 @@
status_t AudioTrack::getPosition(uint32_t *position)
{
if (position == 0) return BAD_VALUE;
-
+ AutoMutex lock(mLock);
*position = mCblk->server;
return NO_ERROR;
@@ -622,9 +660,11 @@
status_t AudioTrack::reload()
{
+ AutoMutex lock(mLock);
+
if (!stopped()) return INVALID_OPERATION;
- flush();
+ flush_l();
mCblk->stepUser(mCblk->frameCount);
@@ -633,8 +673,15 @@
audio_io_handle_t AudioTrack::getOutput()
{
- return AudioSystem::getOutput((AudioSystem::stream_type)mStreamType,
- mCblk->sampleRate, mFormat, mChannels, (AudioSystem::output_flags)mFlags);
+ AutoMutex lock(mLock);
+ return getOutput_l();
+}
+
+// must be called with mLock held
+audio_io_handle_t AudioTrack::getOutput_l()
+{
+ return AudioSystem::getOutput((audio_stream_type_t)mStreamType,
+ mCblk->sampleRate, mFormat, mChannels, (audio_policy_output_flags_t)mFlags);
}
int AudioTrack::getSessionId()
@@ -654,7 +701,8 @@
// -------------------------------------------------------------------------
-status_t AudioTrack::createTrack(
+// must be called with mLock held
+status_t AudioTrack::createTrack_l(
int streamType,
uint32_t sampleRate,
int format,
@@ -686,7 +734,7 @@
}
mNotificationFramesAct = mNotificationFramesReq;
- if (!AudioSystem::isLinearPCM(format)) {
+ if (!audio_is_linear_pcm(format)) {
if (sharedBuffer != 0) {
frameCount = sharedBuffer->size();
}
@@ -753,7 +801,7 @@
mCblkMemory.clear();
mCblkMemory = cblk;
mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
- mCblk->flags |= CBLK_DIRECTION_OUT;
+ android_atomic_or(CBLK_DIRECTION_OUT, &mCblk->flags);
if (sharedBuffer == 0) {
mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
} else {
@@ -774,6 +822,7 @@
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
+ AutoMutex lock(mLock);
int active;
status_t result;
audio_track_cblk_t* cblk = mCblk;
@@ -785,6 +834,12 @@
uint32_t framesAvail = cblk->framesAvailable();
+ cblk->lock.lock();
+ if (cblk->flags & CBLK_INVALID_MSK) {
+ goto create_new_track;
+ }
+ cblk->lock.unlock();
+
if (framesAvail == 0) {
cblk->lock.lock();
goto start_loop_here;
@@ -800,12 +855,17 @@
return WOULD_BLOCK;
}
if (!(cblk->flags & CBLK_INVALID_MSK)) {
+ mLock.unlock();
result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
- }
- if (cblk->flags & CBLK_INVALID_MSK) {
- LOGW("obtainBuffer() track %p invalidated, creating a new one", this);
- // no need to clear the invalid flag as this cblk will not be used anymore
cblk->lock.unlock();
+ mLock.lock();
+ if (mActive == 0) {
+ return status_t(STOPPED);
+ }
+ cblk->lock.lock();
+ }
+
+ if (cblk->flags & CBLK_INVALID_MSK) {
goto create_new_track;
}
if (__builtin_expect(result!=NO_ERROR, false)) {
@@ -819,18 +879,17 @@
//unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140)
cblk->lock.unlock();
result = mAudioTrack->start();
- if (result == DEAD_OBJECT) {
- LOGW("obtainBuffer() dead IAudioTrack: creating a new one");
-create_new_track:
- result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount,
- mFrameCount, mFlags, mSharedBuffer, getOutput(), false);
- if (result == NO_ERROR) {
- cblk = mCblk;
- cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
- mAudioTrack->start();
- }
- }
cblk->lock.lock();
+ if (result == DEAD_OBJECT) {
+ android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
+create_new_track:
+ result = restoreTrack_l(cblk, false);
+ }
+ if (result != NO_ERROR) {
+ LOGW("obtainBuffer create Track error %d", result);
+ cblk->lock.unlock();
+ return result;
+ }
}
cblk->waitTimeMs = 0;
}
@@ -848,8 +907,8 @@
}
// restart track if it was disabled by audioflinger due to previous underrun
- if (cblk->flags & CBLK_DISABLED_MSK) {
- cblk->flags &= ~CBLK_DISABLED_ON;
+ if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) {
+ android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags);
LOGW("obtainBuffer() track %p disabled, restarting", this);
mAudioTrack->start();
}
@@ -871,8 +930,8 @@
audioBuffer->channelCount = mChannelCount;
audioBuffer->frameCount = framesReq;
audioBuffer->size = framesReq * cblk->frameSize;
- if (AudioSystem::isLinearPCM(mFormat)) {
- audioBuffer->format = AudioSystem::PCM_16_BIT;
+ if (audio_is_linear_pcm(mFormat)) {
+ audioBuffer->format = AUDIO_FORMAT_PCM_16_BIT;
} else {
audioBuffer->format = mFormat;
}
@@ -883,8 +942,8 @@
void AudioTrack::releaseBuffer(Buffer* audioBuffer)
{
- audio_track_cblk_t* cblk = mCblk;
- cblk->stepUser(audioBuffer->frameCount);
+ AutoMutex lock(mLock);
+ mCblk->stepUser(audioBuffer->frameCount);
}
// -------------------------------------------------------------------------
@@ -903,12 +962,20 @@
LOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive);
+ // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
+ // while we are accessing the cblk
+ mLock.lock();
+ sp <IAudioTrack> audioTrack = mAudioTrack;
+ sp <IMemory> iMem = mCblkMemory;
+ mLock.unlock();
+
ssize_t written = 0;
const int8_t *src = (const int8_t *)buffer;
Buffer audioBuffer;
+ size_t frameSz = (size_t)frameSize();
do {
- audioBuffer.frameCount = userSize/frameSize();
+ audioBuffer.frameCount = userSize/frameSz;
// Calling obtainBuffer() with a negative wait count causes
// an (almost) infinite wait time.
@@ -922,7 +989,7 @@
size_t toWrite;
- if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {
+ if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT)) {
// Divide capacity by 2 to take expansion into account
toWrite = audioBuffer.size>>1;
// 8 to 16 bit conversion
@@ -940,7 +1007,7 @@
written += toWrite;
releaseBuffer(&audioBuffer);
- } while (userSize);
+ } while (userSize >= frameSz);
return written;
}
@@ -953,21 +1020,28 @@
uint32_t frames;
size_t writtenSize;
+ mLock.lock();
+ // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
+ // while we are accessing the cblk
+ sp <IAudioTrack> audioTrack = mAudioTrack;
+ sp <IMemory> iMem = mCblkMemory;
+ audio_track_cblk_t* cblk = mCblk;
+ mLock.unlock();
+
// Manage underrun callback
- if (mActive && (mCblk->framesReady() == 0)) {
- LOGV("Underrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags);
- if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
+ if (mActive && (cblk->framesAvailable() == cblk->frameCount)) {
+ LOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
+ if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) {
mCbf(EVENT_UNDERRUN, mUserData, 0);
- if (mCblk->server == mCblk->frameCount) {
+ if (cblk->server == cblk->frameCount) {
mCbf(EVENT_BUFFER_END, mUserData, 0);
}
- mCblk->flags |= CBLK_UNDERRUN_ON;
if (mSharedBuffer != 0) return false;
}
}
// Manage loop end callback
- while (mLoopCount > mCblk->loopCount) {
+ while (mLoopCount > cblk->loopCount) {
int loopCount = -1;
mLoopCount--;
if (mLoopCount >= 0) loopCount = mLoopCount;
@@ -977,7 +1051,7 @@
// Manage marker callback
if (!mMarkerReached && (mMarkerPosition > 0)) {
- if (mCblk->server >= mMarkerPosition) {
+ if (cblk->server >= mMarkerPosition) {
mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
mMarkerReached = true;
}
@@ -985,7 +1059,7 @@
// Manage new position callback
if (mUpdatePeriod > 0) {
- while (mCblk->server >= mNewPosition) {
+ while (cblk->server >= mNewPosition) {
mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
mNewPosition += mUpdatePeriod;
}
@@ -1018,7 +1092,7 @@
// Divide buffer size by 2 to take into account the expansion
// due to 8 to 16 bit conversion: the callback must fill only half
// of the destination buffer
- if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {
+ if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT)) {
audioBuffer.size >>= 1;
}
@@ -1037,7 +1111,7 @@
}
if (writtenSize > reqSize) writtenSize = reqSize;
- if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {
+ if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT)) {
// 8 to 16 bit conversion
const int8_t *src = audioBuffer.i8 + writtenSize-1;
int count = writtenSize;
@@ -1068,6 +1142,91 @@
return true;
}
+// must be called with mLock and cblk.lock held. Callers must also hold strong references on
+// the IAudioTrack and IMemory in case they are recreated here.
+// If the IAudioTrack is successfully restored, the cblk pointer is updated
+status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart)
+{
+ status_t result;
+
+ if (!(android_atomic_or(CBLK_RESTORING_ON, &cblk->flags) & CBLK_RESTORING_MSK)) {
+ LOGW("dead IAudioTrack, creating a new one from %s",
+ fromStart ? "start()" : "obtainBuffer()");
+
+ // signal old cblk condition so that other threads waiting for available buffers stop
+ // waiting now
+ cblk->cv.broadcast();
+ cblk->lock.unlock();
+
+ // if the new IAudioTrack is created, createTrack_l() will modify the
+ // following member variables: mAudioTrack, mCblkMemory and mCblk.
+ // It will also delete the strong references on previous IAudioTrack and IMemory
+ result = createTrack_l(mStreamType,
+ cblk->sampleRate,
+ mFormat,
+ mChannelCount,
+ mFrameCount,
+ mFlags,
+ mSharedBuffer,
+ getOutput_l(),
+ false);
+
+ if (result == NO_ERROR) {
+ // restore write index and set other indexes to reflect empty buffer status
+ mCblk->user = cblk->user;
+ mCblk->server = cblk->user;
+ mCblk->userBase = cblk->user;
+ mCblk->serverBase = cblk->user;
+ // restore loop: this is not guaranteed to succeed if new frame count is not
+ // compatible with loop length
+ setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount);
+ if (!fromStart) {
+ mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+ }
+ if (mActive) {
+ result = mAudioTrack->start();
+ }
+ if (fromStart && result == NO_ERROR) {
+ mNewPosition = mCblk->server + mUpdatePeriod;
+ }
+ }
+ if (result != NO_ERROR) {
+ mActive = false;
+ }
+
+ // signal old cblk condition for other threads waiting for restore completion
+ android_atomic_or(CBLK_RESTORED_ON, &cblk->flags);
+ cblk->cv.broadcast();
+ } else {
+ if (!(cblk->flags & CBLK_RESTORED_MSK)) {
+ LOGW("dead IAudioTrack, waiting for a new one");
+ mLock.unlock();
+ result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS));
+ cblk->lock.unlock();
+ mLock.lock();
+ } else {
+ LOGW("dead IAudioTrack, already restored");
+ result = NO_ERROR;
+ cblk->lock.unlock();
+ }
+ if (result != NO_ERROR || mActive == 0) {
+ result = status_t(STOPPED);
+ }
+ }
+ LOGV("restoreTrack_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x",
+ result, mActive, mCblk, cblk, mCblk->flags, cblk->flags);
+
+ if (result == NO_ERROR) {
+ // from now on we switch to the newly created cblk
+ cblk = mCblk;
+ }
+ cblk->lock.lock();
+
+ LOGW_IF(result != NO_ERROR, "restoreTrack_l() error %d", result);
+
+ return result;
+}
+
status_t AudioTrack::dump(int fd, const Vector<String16>& args) const
{
@@ -1111,11 +1270,12 @@
// =========================================================================
+
audio_track_cblk_t::audio_track_cblk_t()
: lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
userBase(0), serverBase(0), buffers(0), frameCount(0),
loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
- flags(0), sendLevel(0)
+ sendLevel(0), flags(0)
{
}
@@ -1142,25 +1302,17 @@
this->user = u;
// Clear flow control error condition as new data has been written/read to/from buffer.
- flags &= ~CBLK_UNDERRUN_MSK;
+ if (flags & CBLK_UNDERRUN_MSK) {
+ android_atomic_and(~CBLK_UNDERRUN_MSK, &flags);
+ }
return u;
}
bool audio_track_cblk_t::stepServer(uint32_t frameCount)
{
- // the code below simulates lock-with-timeout
- // we MUST do this to protect the AudioFlinger server
- // as this lock is shared with the client.
- status_t err;
-
- err = lock.tryLock();
- if (err == -EBUSY) { // just wait a bit
- usleep(1000);
- err = lock.tryLock();
- }
- if (err != NO_ERROR) {
- // probably, the client just died.
+ if (!tryLock()) {
+ LOGW("stepServer() could not lock cblk");
return false;
}
@@ -1197,7 +1349,9 @@
this->server = s;
- cv.signal();
+ if (!(flags & CBLK_INVALID_MSK)) {
+ cv.signal();
+ }
lock.unlock();
return true;
}
@@ -1235,18 +1389,42 @@
if (u < loopEnd) {
return u - s;
} else {
- Mutex::Autolock _l(lock);
- if (loopCount >= 0) {
- return (loopEnd - loopStart)*loopCount + u - s;
- } else {
- return UINT_MAX;
+ // do not block on mutex shared with client on AudioFlinger side
+ if (!tryLock()) {
+ LOGW("framesReady() could not lock cblk");
+ return 0;
}
+ uint32_t frames = UINT_MAX;
+ if (loopCount >= 0) {
+ frames = (loopEnd - loopStart)*loopCount + u - s;
+ }
+ lock.unlock();
+ return frames;
}
} else {
return s - u;
}
}
+bool audio_track_cblk_t::tryLock()
+{
+ // the code below simulates lock-with-timeout
+ // we MUST do this to protect the AudioFlinger server
+ // as this lock is shared with the client.
+ status_t err;
+
+ err = lock.tryLock();
+ if (err == -EBUSY) { // just wait a bit
+ usleep(1000);
+ err = lock.tryLock();
+ }
+ if (err != NO_ERROR) {
+ // probably, the client just died.
+ return false;
+ }
+ return true;
+}
+
// -------------------------------------------------------------------------
}; // namespace android
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 457f7ed..9fbcee0 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -25,6 +25,8 @@
#include <media/IAudioPolicyService.h>
+#include <system/audio.h>
+
namespace android {
enum {
@@ -49,7 +51,8 @@
GET_OUTPUT_FOR_EFFECT,
REGISTER_EFFECT,
UNREGISTER_EFFECT,
- IS_STREAM_ACTIVE
+ IS_STREAM_ACTIVE,
+ GET_DEVICES_FOR_STREAM,
};
class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
@@ -61,8 +64,8 @@
}
virtual status_t setDeviceConnectionState(
- AudioSystem::audio_devices device,
- AudioSystem::device_connection_state state,
+ audio_devices_t device,
+ audio_policy_dev_state_t state,
const char *device_address)
{
Parcel data, reply;
@@ -74,8 +77,8 @@
return static_cast <status_t> (reply.readInt32());
}
- virtual AudioSystem::device_connection_state getDeviceConnectionState(
- AudioSystem::audio_devices device,
+ virtual audio_policy_dev_state_t getDeviceConnectionState(
+ audio_devices_t device,
const char *device_address)
{
Parcel data, reply;
@@ -83,7 +86,7 @@
data.writeInt32(static_cast <uint32_t>(device));
data.writeCString(device_address);
remote()->transact(GET_DEVICE_CONNECTION_STATE, data, &reply);
- return static_cast <AudioSystem::device_connection_state>(reply.readInt32());
+ return static_cast <audio_policy_dev_state_t>(reply.readInt32());
}
virtual status_t setPhoneState(int state)
@@ -105,7 +108,7 @@
return static_cast <status_t> (reply.readInt32());
}
- virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
+ virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -115,21 +118,21 @@
return static_cast <status_t> (reply.readInt32());
}
- virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage)
+ virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
data.writeInt32(static_cast <uint32_t>(usage));
remote()->transact(GET_FORCE_USE, data, &reply);
- return static_cast <AudioSystem::forced_config> (reply.readInt32());
+ return static_cast <audio_policy_forced_cfg_t> (reply.readInt32());
}
virtual audio_io_handle_t getOutput(
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
- AudioSystem::output_flags flags)
+ audio_policy_output_flags_t flags)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -143,7 +146,7 @@
}
virtual status_t startOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session)
{
Parcel data, reply;
@@ -156,7 +159,7 @@
}
virtual status_t stopOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session)
{
Parcel data, reply;
@@ -181,7 +184,7 @@
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
- AudioSystem::audio_in_acoustics acoustics)
+ audio_in_acoustics_t acoustics)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -220,7 +223,7 @@
remote()->transact(RELEASE_INPUT, data, &reply);
}
- virtual status_t initStreamVolume(AudioSystem::stream_type stream,
+ virtual status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax)
{
@@ -233,7 +236,7 @@
return static_cast <status_t> (reply.readInt32());
}
- virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+ virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, int index)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -243,7 +246,7 @@
return static_cast <status_t> (reply.readInt32());
}
- virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
+ virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, int *index)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -254,7 +257,7 @@
return static_cast <status_t> (reply.readInt32());
}
- virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream)
+ virtual uint32_t getStrategyForStream(audio_stream_type_t stream)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -263,6 +266,15 @@
return reply.readInt32();
}
+ virtual uint32_t getDevicesForStream(audio_stream_type_t stream)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(static_cast <uint32_t>(stream));
+ remote()->transact(GET_DEVICES_FOR_STREAM, data, &reply);
+ return (uint32_t) reply.readInt32();
+ }
+
virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc)
{
Parcel data, reply;
@@ -320,10 +332,10 @@
switch(code) {
case SET_DEVICE_CONNECTION_STATE: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioSystem::audio_devices device =
- static_cast <AudioSystem::audio_devices>(data.readInt32());
- AudioSystem::device_connection_state state =
- static_cast <AudioSystem::device_connection_state>(data.readInt32());
+ audio_devices_t device =
+ static_cast <audio_devices_t>(data.readInt32());
+ audio_policy_dev_state_t state =
+ static_cast <audio_policy_dev_state_t>(data.readInt32());
const char *device_address = data.readCString();
reply->writeInt32(static_cast<uint32_t> (setDeviceConnectionState(device,
state,
@@ -333,8 +345,8 @@
case GET_DEVICE_CONNECTION_STATE: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioSystem::audio_devices device =
- static_cast<AudioSystem::audio_devices> (data.readInt32());
+ audio_devices_t device =
+ static_cast<audio_devices_t> (data.readInt32());
const char *device_address = data.readCString();
reply->writeInt32(static_cast<uint32_t> (getDeviceConnectionState(device,
device_address)));
@@ -357,29 +369,29 @@
case SET_FORCE_USE: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioSystem::force_use usage = static_cast <AudioSystem::force_use>(data.readInt32());
- AudioSystem::forced_config config =
- static_cast <AudioSystem::forced_config>(data.readInt32());
+ audio_policy_force_use_t usage = static_cast <audio_policy_force_use_t>(data.readInt32());
+ audio_policy_forced_cfg_t config =
+ static_cast <audio_policy_forced_cfg_t>(data.readInt32());
reply->writeInt32(static_cast <uint32_t>(setForceUse(usage, config)));
return NO_ERROR;
} break;
case GET_FORCE_USE: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioSystem::force_use usage = static_cast <AudioSystem::force_use>(data.readInt32());
+ audio_policy_force_use_t usage = static_cast <audio_policy_force_use_t>(data.readInt32());
reply->writeInt32(static_cast <uint32_t>(getForceUse(usage)));
return NO_ERROR;
} break;
case GET_OUTPUT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioSystem::stream_type stream =
- static_cast <AudioSystem::stream_type>(data.readInt32());
+ audio_stream_type_t stream =
+ static_cast <audio_stream_type_t>(data.readInt32());
uint32_t samplingRate = data.readInt32();
uint32_t format = data.readInt32();
uint32_t channels = data.readInt32();
- AudioSystem::output_flags flags =
- static_cast <AudioSystem::output_flags>(data.readInt32());
+ audio_policy_output_flags_t flags =
+ static_cast <audio_policy_output_flags_t>(data.readInt32());
audio_io_handle_t output = getOutput(stream,
samplingRate,
@@ -396,7 +408,7 @@
uint32_t stream = data.readInt32();
int session = data.readInt32();
reply->writeInt32(static_cast <uint32_t>(startOutput(output,
- (AudioSystem::stream_type)stream,
+ (audio_stream_type_t)stream,
session)));
return NO_ERROR;
} break;
@@ -407,7 +419,7 @@
uint32_t stream = data.readInt32();
int session = data.readInt32();
reply->writeInt32(static_cast <uint32_t>(stopOutput(output,
- (AudioSystem::stream_type)stream,
+ (audio_stream_type_t)stream,
session)));
return NO_ERROR;
} break;
@@ -425,8 +437,8 @@
uint32_t samplingRate = data.readInt32();
uint32_t format = data.readInt32();
uint32_t channels = data.readInt32();
- AudioSystem::audio_in_acoustics acoustics =
- static_cast <AudioSystem::audio_in_acoustics>(data.readInt32());
+ audio_in_acoustics_t acoustics =
+ static_cast <audio_in_acoustics_t>(data.readInt32());
audio_io_handle_t input = getInput(inputSource,
samplingRate,
format,
@@ -459,8 +471,8 @@
case INIT_STREAM_VOLUME: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioSystem::stream_type stream =
- static_cast <AudioSystem::stream_type>(data.readInt32());
+ audio_stream_type_t stream =
+ static_cast <audio_stream_type_t>(data.readInt32());
int indexMin = data.readInt32();
int indexMax = data.readInt32();
reply->writeInt32(static_cast <uint32_t>(initStreamVolume(stream, indexMin,indexMax)));
@@ -469,8 +481,8 @@
case SET_STREAM_VOLUME: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioSystem::stream_type stream =
- static_cast <AudioSystem::stream_type>(data.readInt32());
+ audio_stream_type_t stream =
+ static_cast <audio_stream_type_t>(data.readInt32());
int index = data.readInt32();
reply->writeInt32(static_cast <uint32_t>(setStreamVolumeIndex(stream, index)));
return NO_ERROR;
@@ -478,8 +490,8 @@
case GET_STREAM_VOLUME: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioSystem::stream_type stream =
- static_cast <AudioSystem::stream_type>(data.readInt32());
+ audio_stream_type_t stream =
+ static_cast <audio_stream_type_t>(data.readInt32());
int index;
status_t status = getStreamVolumeIndex(stream, &index);
reply->writeInt32(index);
@@ -489,12 +501,20 @@
case GET_STRATEGY_FOR_STREAM: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioSystem::stream_type stream =
- static_cast <AudioSystem::stream_type>(data.readInt32());
+ audio_stream_type_t stream =
+ static_cast <audio_stream_type_t>(data.readInt32());
reply->writeInt32(getStrategyForStream(stream));
return NO_ERROR;
} break;
+ case GET_DEVICES_FOR_STREAM: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_stream_type_t stream =
+ static_cast <audio_stream_type_t>(data.readInt32());
+ reply->writeInt32(static_cast <int>(getDevicesForStream(stream)));
+ return NO_ERROR;
+ } break;
+
case GET_OUTPUT_FOR_EFFECT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
effect_descriptor_t desc;
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index d5298c9..ebe821f 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -20,6 +20,7 @@
#include <binder/Parcel.h>
#include <SkBitmap.h>
#include <media/IMediaMetadataRetriever.h>
+#include <utils/String8.h>
// The binder is supposed to propagate the scheduler group across
// the binder interface so that remote calls are executed with
@@ -102,11 +103,24 @@
remote()->transact(DISCONNECT, data, &reply);
}
- status_t setDataSource(const char* srcUrl)
+ status_t setDataSource(
+ const char *srcUrl, const KeyedVector<String8, String8> *headers)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
data.writeCString(srcUrl);
+
+ if (headers == NULL) {
+ data.writeInt32(0);
+ } else {
+ // serialize the headers
+ data.writeInt32(headers->size());
+ for (size_t i = 0; i < headers->size(); ++i) {
+ data.writeString8(headers->keyAt(i));
+ data.writeString8(headers->valueAt(i));
+ }
+ }
+
remote()->transact(SET_DATA_SOURCE_URL, data, &reply);
return reply.readInt32();
}
@@ -188,7 +202,18 @@
case SET_DATA_SOURCE_URL: {
CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
const char* srcUrl = data.readCString();
- reply->writeInt32(setDataSource(srcUrl));
+
+ KeyedVector<String8, String8> headers;
+ int32_t numHeaders = data.readInt32();
+ for (int i = 0; i < numHeaders; ++i) {
+ String8 key = data.readString8();
+ String8 value = data.readString8();
+ headers.add(key, value);
+ }
+
+ reply->writeInt32(
+ setDataSource(srcUrl, numHeaders > 0 ? &headers : NULL));
+
return NO_ERROR;
} break;
case SET_DATA_SOURCE_FD: {
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index c287c0a..76a8a91 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -23,6 +23,7 @@
#include <media/IMediaPlayer.h>
#include <surfaceflinger/ISurface.h>
#include <surfaceflinger/Surface.h>
+#include <gui/ISurfaceTexture.h>
namespace android {
@@ -45,7 +46,10 @@
SET_METADATA_FILTER,
GET_METADATA,
SET_AUX_EFFECT_SEND_LEVEL,
- ATTACH_AUX_EFFECT
+ ATTACH_AUX_EFFECT,
+ SET_VIDEO_SURFACETEXTURE,
+ SET_PARAMETER,
+ GET_PARAMETER,
};
class BpMediaPlayer: public BpInterface<IMediaPlayer>
@@ -64,6 +68,7 @@
remote()->transact(DISCONNECT, data, &reply);
}
+ // pass the buffered Surface to the media player service
status_t setVideoSurface(const sp<Surface>& surface)
{
Parcel data, reply;
@@ -73,6 +78,17 @@
return reply.readInt32();
}
+ // pass the buffered ISurfaceTexture to the media player service
+ status_t setVideoSurfaceTexture(const sp<ISurfaceTexture>& surfaceTexture)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ sp<IBinder> b(surfaceTexture->asBinder());
+ data.writeStrongBinder(b);
+ remote()->transact(SET_VIDEO_SURFACETEXTURE, data, &reply);
+ return reply.readInt32();
+ }
+
status_t prepareAsync()
{
Parcel data, reply;
@@ -178,8 +194,9 @@
}
status_t invoke(const Parcel& request, Parcel *reply)
- { // Avoid doing any extra copy. The interface descriptor should
- // have been set by MediaPlayer.java.
+ {
+ // Avoid doing any extra copy. The interface descriptor should
+ // have been set by MediaPlayer.java.
return remote()->transact(INVOKE, request, reply);
}
@@ -220,6 +237,27 @@
remote()->transact(ATTACH_AUX_EFFECT, data, &reply);
return reply.readInt32();
}
+
+ status_t setParameter(int key, const Parcel& request)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ data.writeInt32(key);
+ if (request.dataSize() > 0) {
+ data.appendFrom(const_cast<Parcel *>(&request), 0, request.dataSize());
+ }
+ remote()->transact(SET_PARAMETER, data, &reply);
+ return reply.readInt32();
+ }
+
+ status_t getParameter(int key, Parcel *reply)
+ {
+ Parcel data;
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ data.writeInt32(key);
+ return remote()->transact(GET_PARAMETER, data, reply);
+ }
+
};
IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
@@ -241,6 +279,13 @@
reply->writeInt32(setVideoSurface(surface));
return NO_ERROR;
} break;
+ case SET_VIDEO_SURFACETEXTURE: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ sp<ISurfaceTexture> surfaceTexture =
+ interface_cast<ISurfaceTexture>(data.readStrongBinder());
+ reply->writeInt32(setVideoSurfaceTexture(surfaceTexture));
+ return NO_ERROR;
+ } break;
case PREPARE_ASYNC: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(prepareAsync());
@@ -312,8 +357,8 @@
} break;
case INVOKE: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
- invoke(data, reply);
- return NO_ERROR;
+ status_t result = invoke(data, reply);
+ return result;
} break;
case SET_METADATA_FILTER: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
@@ -338,6 +383,23 @@
reply->writeInt32(attachAuxEffect(data.readInt32()));
return NO_ERROR;
} break;
+ case SET_PARAMETER: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ int key = data.readInt32();
+
+ Parcel request;
+ if (data.dataAvail() > 0) {
+ request.appendFrom(
+ const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail());
+ }
+ request.setDataPosition(0);
+ reply->writeInt32(setParameter(key, request));
+ return NO_ERROR;
+ } break;
+ case GET_PARAMETER: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ return getParameter(data.readInt32(), reply);
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IMediaPlayerClient.cpp b/media/libmedia/IMediaPlayerClient.cpp
index bf51829..1f135c4 100644
--- a/media/libmedia/IMediaPlayerClient.cpp
+++ b/media/libmedia/IMediaPlayerClient.cpp
@@ -35,13 +35,16 @@
{
}
- virtual void notify(int msg, int ext1, int ext2)
+ virtual void notify(int msg, int ext1, int ext2, const Parcel *obj)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor());
data.writeInt32(msg);
data.writeInt32(ext1);
data.writeInt32(ext2);
+ if (obj && obj->dataSize() > 0) {
+ data.appendFrom(const_cast<Parcel *>(obj), 0, obj->dataSize());
+ }
remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
}
};
@@ -59,7 +62,12 @@
int msg = data.readInt32();
int ext1 = data.readInt32();
int ext2 = data.readInt32();
- notify(msg, ext1, ext2);
+ Parcel obj;
+ if (data.dataAvail() > 0) {
+ obj.appendFrom(const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail());
+ }
+
+ notify(msg, ext1, ext2, &obj);
return NO_ERROR;
} break;
default:
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 77199e1..17a0362 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -37,7 +37,9 @@
DECODE_FD,
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
- GET_OMX
+ GET_OMX,
+ ADD_BATTERY_DATA,
+ PULL_BATTERY_DATA
};
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
@@ -156,6 +158,19 @@
remote()->transact(GET_OMX, data, &reply);
return interface_cast<IOMX>(reply.readStrongBinder());
}
+
+ virtual void addBatteryData(uint32_t params) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ data.writeInt32(params);
+ remote()->transact(ADD_BATTERY_DATA, data, &reply);
+ }
+
+ virtual status_t pullBatteryData(Parcel* reply) {
+ Parcel data;
+ data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ return remote()->transact(PULL_BATTERY_DATA, data, reply);
+ }
};
IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService");
@@ -270,6 +285,17 @@
reply->writeStrongBinder(omx->asBinder());
return NO_ERROR;
} break;
+ case ADD_BATTERY_DATA: {
+ CHECK_INTERFACE(IMediaPlayerService, data, reply);
+ uint32_t params = data.readInt32();
+ addBatteryData(params);
+ return NO_ERROR;
+ } break;
+ case PULL_BATTERY_DATA: {
+ CHECK_INTERFACE(IMediaPlayerService, data, reply);
+ pullBatteryData(reply);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index af67175..d3aab08 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -1,3 +1,19 @@
+/*
+ * 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 "IOMX"
#include <utils/Log.h>
@@ -33,6 +49,7 @@
EMPTY_BUFFER,
GET_EXTENSION_INDEX,
OBSERVER_ON_MSG,
+ GET_GRAPHIC_BUFFER_USAGE,
};
class BpOMX : public BpInterface<IOMX> {
@@ -194,6 +211,19 @@
return err;
}
+ virtual status_t getGraphicBufferUsage(
+ node_id node, OMX_U32 port_index, OMX_U32* usage) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(port_index);
+ remote()->transact(GET_GRAPHIC_BUFFER_USAGE, data, &reply);
+
+ status_t err = reply.readInt32();
+ *usage = reply.readInt32();
+ return err;
+ }
+
virtual status_t useBuffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) {
@@ -508,6 +538,21 @@
return NO_ERROR;
}
+ case GET_GRAPHIC_BUFFER_USAGE:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_U32 port_index = data.readInt32();
+
+ OMX_U32 usage = 0;
+ status_t err = getGraphicBufferUsage(node, port_index, &usage);
+ reply->writeInt32(err);
+ reply->writeInt32(usage);
+
+ return NO_ERROR;
+ }
+
case USE_BUFFER:
{
CHECK_INTERFACE(IOMX, data, reply);
diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp
index 5069002..c14ee82 100644
--- a/media/libmedia/IStreamSource.cpp
+++ b/media/libmedia/IStreamSource.cpp
@@ -26,6 +26,9 @@
namespace android {
+// static
+const char *const IStreamListener::kKeyResumeAtPTS = "resume-at-PTS";
+
enum {
// IStreamSource
SET_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index ee9e1d8..88157d2 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -96,10 +96,10 @@
// create the output AudioTrack
mAudioTrack = new AudioTrack();
- mAudioTrack->set(AudioSystem::MUSIC, //TODO parametrize this
+ mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parametrize this
pLibConfig->sampleRate,
1, // format = PCM 16bits per sample,
- (pLibConfig->numChannels == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
+ (pLibConfig->numChannels == 2) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO,
mTrackBufferSize,
0);
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 9ad63f0..069bbb7 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -284,8 +284,17 @@
return static_cast<output_format>(format);
}
+static bool isCameraIdFound(int cameraId, const Vector<int>& cameraIds) {
+ for (int i = 0, n = cameraIds.size(); i < n; ++i) {
+ if (cameraId == cameraIds[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
/*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createCamcorderProfile(int cameraId, const char **atts)
+MediaProfiles::createCamcorderProfile(int cameraId, const char **atts, Vector<int>& cameraIds)
{
CHECK(!strcmp("quality", atts[0]) &&
!strcmp("fileFormat", atts[2]) &&
@@ -301,6 +310,9 @@
MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
profile->mCameraId = cameraId;
+ if (!isCameraIdFound(cameraId, cameraIds)) {
+ cameraIds.add(cameraId);
+ }
profile->mFileFormat = static_cast<output_format>(fileFormat);
profile->mQuality = static_cast<camcorder_quality>(quality);
profile->mDuration = atoi(atts[5]);
@@ -344,6 +356,18 @@
return atoi(atts[1]);
}
+void MediaProfiles::addStartTimeOffset(int cameraId, const char** atts)
+{
+ int offsetTimeMs = 700;
+ if (atts[2]) {
+ CHECK(!strcmp("startOffsetMs", atts[2]));
+ offsetTimeMs = atoi(atts[3]);
+ }
+
+ LOGV("%s: cameraId=%d, offset=%d ms", __func__, cameraId, offsetTimeMs);
+ mStartTimeOffsets.replaceValueFor(cameraId, offsetTimeMs);
+}
+
/*static*/ void
MediaProfiles::startElementHandler(void *userData, const char *name, const char **atts)
{
@@ -368,14 +392,170 @@
profiles->mEncoderOutputFileFormats.add(createEncoderOutputFileFormat(atts));
} else if (strcmp("CamcorderProfiles", name) == 0) {
profiles->mCurrentCameraId = getCameraId(atts);
+ profiles->addStartTimeOffset(profiles->mCurrentCameraId, atts);
} else if (strcmp("EncoderProfile", name) == 0) {
profiles->mCamcorderProfiles.add(
- createCamcorderProfile(profiles->mCurrentCameraId, atts));
+ createCamcorderProfile(profiles->mCurrentCameraId, atts, profiles->mCameraIds));
} else if (strcmp("ImageEncoding", name) == 0) {
profiles->addImageEncodingQualityLevel(profiles->mCurrentCameraId, atts);
}
}
+static bool isCamcorderProfile(camcorder_quality quality) {
+ return quality >= CAMCORDER_QUALITY_LIST_START &&
+ quality <= CAMCORDER_QUALITY_LIST_END;
+}
+
+static bool isTimelapseProfile(camcorder_quality quality) {
+ return quality >= CAMCORDER_QUALITY_TIME_LAPSE_LIST_START &&
+ quality <= CAMCORDER_QUALITY_TIME_LAPSE_LIST_END;
+}
+
+void MediaProfiles::initRequiredProfileRefs(const Vector<int>& cameraIds) {
+ LOGV("Number of camera ids: %d", cameraIds.size());
+ CHECK(cameraIds.size() > 0);
+ mRequiredProfileRefs = new RequiredProfiles[cameraIds.size()];
+ for (size_t i = 0, n = cameraIds.size(); i < n; ++i) {
+ mRequiredProfileRefs[i].mCameraId = cameraIds[i];
+ for (size_t j = 0; j < kNumRequiredProfiles; ++j) {
+ mRequiredProfileRefs[i].mRefs[j].mHasRefProfile = false;
+ mRequiredProfileRefs[i].mRefs[j].mRefProfileIndex = -1;
+ if ((j & 1) == 0) { // low resolution
+ mRequiredProfileRefs[i].mRefs[j].mResolutionProduct = 0x7FFFFFFF;
+ } else { // high resolution
+ mRequiredProfileRefs[i].mRefs[j].mResolutionProduct = 0;
+ }
+ }
+ }
+}
+
+int MediaProfiles::getRequiredProfileRefIndex(int cameraId) {
+ for (size_t i = 0, n = mCameraIds.size(); i < n; ++i) {
+ if (mCameraIds[i] == cameraId) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void MediaProfiles::checkAndAddRequiredProfilesIfNecessary() {
+ if (sIsInitialized) {
+ return;
+ }
+
+ initRequiredProfileRefs(mCameraIds);
+
+ for (size_t i = 0, n = mCamcorderProfiles.size(); i < n; ++i) {
+ int product = mCamcorderProfiles[i]->mVideoCodec->mFrameWidth *
+ mCamcorderProfiles[i]->mVideoCodec->mFrameHeight;
+
+ camcorder_quality quality = mCamcorderProfiles[i]->mQuality;
+ int cameraId = mCamcorderProfiles[i]->mCameraId;
+ int index = -1;
+ int refIndex = getRequiredProfileRefIndex(cameraId);
+ CHECK(refIndex != -1);
+ RequiredProfileRefInfo *info;
+ camcorder_quality refQuality;
+ VideoCodec *codec = NULL;
+
+ // Check high and low from either camcorder profile or timelapse profile
+ // but not both. Default, check camcorder profile
+ size_t j = 0;
+ size_t n = 2;
+ if (isTimelapseProfile(quality)) {
+ // Check timelapse profile instead.
+ j = 2;
+ n = kNumRequiredProfiles;
+ } else {
+ // Must be camcorder profile.
+ CHECK(isCamcorderProfile(quality));
+ }
+ for (; j < n; ++j) {
+ info = &(mRequiredProfileRefs[refIndex].mRefs[j]);
+ if ((j % 2 == 0 && product > info->mResolutionProduct) || // low
+ (j % 2 != 0 && product < info->mResolutionProduct)) { // high
+ continue;
+ }
+ switch (j) {
+ case 0:
+ refQuality = CAMCORDER_QUALITY_LOW;
+ break;
+ case 1:
+ refQuality = CAMCORDER_QUALITY_HIGH;
+ break;
+ case 2:
+ refQuality = CAMCORDER_QUALITY_TIME_LAPSE_LOW;
+ break;
+ case 3:
+ refQuality = CAMCORDER_QUALITY_TIME_LAPSE_HIGH;
+ break;
+ default:
+ CHECK(!"Should never reach here");
+ }
+
+ if (!info->mHasRefProfile) {
+ index = getCamcorderProfileIndex(cameraId, refQuality);
+ }
+ if (index == -1) {
+ // New high or low quality profile is found.
+ // Update its reference.
+ info->mHasRefProfile = true;
+ info->mRefProfileIndex = i;
+ info->mResolutionProduct = product;
+ }
+ }
+ }
+
+ for (size_t cameraId = 0; cameraId < mCameraIds.size(); ++cameraId) {
+ for (size_t j = 0; j < kNumRequiredProfiles; ++j) {
+ int refIndex = getRequiredProfileRefIndex(cameraId);
+ CHECK(refIndex != -1);
+ RequiredProfileRefInfo *info =
+ &mRequiredProfileRefs[refIndex].mRefs[j];
+
+ if (info->mHasRefProfile) {
+
+ CamcorderProfile *profile =
+ new CamcorderProfile(
+ *mCamcorderProfiles[info->mRefProfileIndex]);
+
+ // Overwrite the quality
+ switch (j % kNumRequiredProfiles) {
+ case 0:
+ profile->mQuality = CAMCORDER_QUALITY_LOW;
+ break;
+ case 1:
+ profile->mQuality = CAMCORDER_QUALITY_HIGH;
+ break;
+ case 2:
+ profile->mQuality = CAMCORDER_QUALITY_TIME_LAPSE_LOW;
+ break;
+ case 3:
+ profile->mQuality = CAMCORDER_QUALITY_TIME_LAPSE_HIGH;
+ break;
+ default:
+ CHECK(!"Should never come here");
+ }
+
+ int index = getCamcorderProfileIndex(cameraId, profile->mQuality);
+ if (index != -1) {
+ LOGV("Profile quality %d for camera %d already exists",
+ profile->mQuality, cameraId);
+ CHECK(index == refIndex);
+ continue;
+ }
+
+ // Insert the new profile
+ LOGV("Add a profile: quality %d=>%d for camera %d",
+ mCamcorderProfiles[info->mRefProfileIndex]->mQuality,
+ profile->mQuality, cameraId);
+
+ mCamcorderProfiles.add(profile);
+ }
+ }
+ }
+}
+
/*static*/ MediaProfiles*
MediaProfiles::getInstance()
{
@@ -396,6 +576,9 @@
} else {
sInstance = createInstanceFromXmlFile(value);
}
+ CHECK(sInstance != NULL);
+ sInstance->checkAndAddRequiredProfilesIfNecessary();
+ sIsInitialized = true;
}
return sInstance;
@@ -551,6 +734,11 @@
createDefaultCamcorderTimeLapseHighProfiles(&highTimeLapseProfile, &highSpecificTimeLapseProfile);
profiles->mCamcorderProfiles.add(highTimeLapseProfile);
profiles->mCamcorderProfiles.add(highSpecificTimeLapseProfile);
+
+ // For emulator and other legacy devices which does not have a
+ // media_profiles.xml file, We assume that the default camera id
+ // is 0 and that is the only camera available.
+ profiles->mCameraIds.push(0);
}
/*static*/ void
@@ -613,7 +801,6 @@
createDefaultAudioDecoders(profiles);
createDefaultEncoderOutputFileFormats(profiles);
createDefaultImageEncodingQualityLevels(profiles);
- sIsInitialized = true;
return profiles;
}
@@ -667,9 +854,6 @@
exit:
::XML_ParserFree(parser);
::fclose(fp);
- if (profiles) {
- sIsInitialized = true;
- }
return profiles;
}
@@ -826,6 +1010,16 @@
return result;
}
+int MediaProfiles::getStartTimeOffsetMs(int cameraId) const {
+ int offsetTimeMs = -1;
+ ssize_t index = mStartTimeOffsets.indexOfKey(cameraId);
+ if (index >= 0) {
+ offsetTimeMs = mStartTimeOffsets.valueFor(cameraId);
+ }
+ LOGV("%s: offsetTime=%d ms and cameraId=%d", offsetTimeMs, cameraId);
+ return offsetTimeMs;
+}
+
MediaProfiles::~MediaProfiles()
{
CHECK("destructor should never be called" == 0);
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index 5ec573e..28c8642 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -70,8 +70,7 @@
client.setLocale(locale());
status_t result =
- doProcessDirectory(
- pathBuffer, pathRemaining, client, exceptionCheck, exceptionEnv);
+ doProcessDirectory(pathBuffer, pathRemaining, client, false, exceptionCheck, exceptionEnv);
free(pathBuffer);
@@ -80,20 +79,18 @@
status_t MediaScanner::doProcessDirectory(
char *path, int pathRemaining, MediaScannerClient &client,
- ExceptionCheck exceptionCheck, void *exceptionEnv) {
+ bool noMedia, ExceptionCheck exceptionCheck, void *exceptionEnv) {
// place to copy file or directory name
char* fileSpot = path + strlen(path);
struct dirent* entry;
struct stat statbuf;
- // ignore directories that contain a ".nomedia" file
+ // Treat all files as non-media in directories that contain a ".nomedia" file
if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
strcpy(fileSpot, ".nomedia");
if (access(path, F_OK) == 0) {
- LOGD("found .nomedia, skipping directory\n");
- fileSpot[0] = 0;
- client.addNoMediaFolder(path);
- return OK;
+ LOGD("found .nomedia, setting noMedia flag\n");
+ noMedia = true;
}
// restore path
@@ -138,19 +135,21 @@
}
if (type == DT_REG || type == DT_DIR) {
if (type == DT_DIR) {
- // ignore directories with a name that starts with '.'
+ bool childNoMedia = noMedia;
+ // set noMedia flag on directories with a name that starts with '.'
// for example, the Mac ".Trashes" directory
- if (name[0] == '.') continue;
+ if (name[0] == '.')
+ childNoMedia = true;
// report the directory to the client
if (stat(path, &statbuf) == 0) {
- client.scanFile(path, statbuf.st_mtime, 0, true);
+ client.scanFile(path, statbuf.st_mtime, 0, true, childNoMedia);
}
// and now process its contents
strcat(fileSpot, "/");
int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client,
- exceptionCheck, exceptionEnv);
+ childNoMedia, exceptionCheck, exceptionEnv);
if (err) {
// pass exceptions up - ignore other errors
if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
@@ -159,7 +158,7 @@
}
} else {
stat(path, &statbuf);
- client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false);
+ client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false, noMedia);
if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
}
}
diff --git a/media/libmedia/MemoryLeakTrackUtil.cpp b/media/libmedia/MemoryLeakTrackUtil.cpp
new file mode 100644
index 0000000..6a108ae
--- /dev/null
+++ b/media/libmedia/MemoryLeakTrackUtil.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2011, 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 <media/MemoryLeakTrackUtil.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/*
+ * The code here originally resided in MediaPlayerService.cpp and was
+ * shamelessly copied over to support memory leak tracking from
+ * multiple places.
+ */
+namespace android {
+
+#if defined(__arm__)
+
+extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
+ size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
+
+extern "C" void free_malloc_leak_info(uint8_t* info);
+
+// Use the String-class below instead of String8 to allocate all memory
+// beforehand and not reenter the heap while we are examining it...
+struct MyString8 {
+ static const size_t MAX_SIZE = 256 * 1024;
+
+ MyString8()
+ : mPtr((char *)malloc(MAX_SIZE)) {
+ *mPtr = '\0';
+ }
+
+ ~MyString8() {
+ free(mPtr);
+ }
+
+ void append(const char *s) {
+ strcat(mPtr, s);
+ }
+
+ const char *string() const {
+ return mPtr;
+ }
+
+ size_t size() const {
+ return strlen(mPtr);
+ }
+
+private:
+ char *mPtr;
+
+ MyString8(const MyString8 &);
+ MyString8 &operator=(const MyString8 &);
+};
+
+void dumpMemoryAddresses(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ MyString8 result;
+
+ typedef struct {
+ size_t size;
+ size_t dups;
+ intptr_t * backtrace;
+ } AllocEntry;
+
+ uint8_t *info = NULL;
+ size_t overallSize = 0;
+ size_t infoSize = 0;
+ size_t totalMemory = 0;
+ size_t backtraceSize = 0;
+
+ get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, &backtraceSize);
+ if (info) {
+ uint8_t *ptr = info;
+ size_t count = overallSize / infoSize;
+
+ snprintf(buffer, SIZE, " Allocation count %i\n", count);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Total memory %i\n", totalMemory);
+ result.append(buffer);
+
+ AllocEntry * entries = new AllocEntry[count];
+
+ for (size_t i = 0; i < count; i++) {
+ // Each entry should be size_t, size_t, intptr_t[backtraceSize]
+ AllocEntry *e = &entries[i];
+
+ e->size = *reinterpret_cast<size_t *>(ptr);
+ ptr += sizeof(size_t);
+
+ e->dups = *reinterpret_cast<size_t *>(ptr);
+ ptr += sizeof(size_t);
+
+ e->backtrace = reinterpret_cast<intptr_t *>(ptr);
+ ptr += sizeof(intptr_t) * backtraceSize;
+ }
+
+ // Now we need to sort the entries. They come sorted by size but
+ // not by stack trace which causes problems using diff.
+ bool moved;
+ do {
+ moved = false;
+ for (size_t i = 0; i < (count - 1); i++) {
+ AllocEntry *e1 = &entries[i];
+ AllocEntry *e2 = &entries[i+1];
+
+ bool swap = e1->size < e2->size;
+ if (e1->size == e2->size) {
+ for(size_t j = 0; j < backtraceSize; j++) {
+ if (e1->backtrace[j] == e2->backtrace[j]) {
+ continue;
+ }
+ swap = e1->backtrace[j] < e2->backtrace[j];
+ break;
+ }
+ }
+ if (swap) {
+ AllocEntry t = entries[i];
+ entries[i] = entries[i+1];
+ entries[i+1] = t;
+ moved = true;
+ }
+ }
+ } while (moved);
+
+ for (size_t i = 0; i < count; i++) {
+ AllocEntry *e = &entries[i];
+
+ snprintf(buffer, SIZE, "size %8i, dup %4i, ", e->size, e->dups);
+ result.append(buffer);
+ for (size_t ct = 0; (ct < backtraceSize) && e->backtrace[ct]; ct++) {
+ if (ct) {
+ result.append(", ");
+ }
+ snprintf(buffer, SIZE, "0x%08x", e->backtrace[ct]);
+ result.append(buffer);
+ }
+ result.append("\n");
+ }
+
+ delete[] entries;
+ free_malloc_leak_info(info);
+ }
+
+ write(fd, result.string(), result.size());
+}
+
+#else
+// Does nothing
+void dumpMemoryAddresses(int fd) {}
+
+#endif
+} // namespace android
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 82fe2d4..9f1b3d6 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -1026,8 +1026,8 @@
mpAudioTrack->set(mStreamType,
0,
- AudioSystem::PCM_16_BIT,
- AudioSystem::CHANNEL_OUT_MONO,
+ AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_OUT_MONO,
0,
0,
audioCallback,
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 43571cf..366707c 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -24,6 +24,8 @@
#include <sys/types.h>
#include <limits.h>
+#include <cutils/bitops.h>
+
#include <media/Visualizer.h>
extern void fixed_fft_real(int n, int32_t *v);
@@ -127,7 +129,7 @@
{
if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
size < VISUALIZER_CAPTURE_SIZE_MIN ||
- AudioSystem::popCount(size) != 1) {
+ popcount(size) != 1) {
return BAD_VALUE;
}
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index 8dfcb3b..cee06ab 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -92,7 +92,8 @@
}
}
-status_t MediaMetadataRetriever::setDataSource(const char* srcUrl)
+status_t MediaMetadataRetriever::setDataSource(
+ const char *srcUrl, const KeyedVector<String8, String8> *headers)
{
LOGV("setDataSource");
Mutex::Autolock _l(mLock);
@@ -105,7 +106,7 @@
return UNKNOWN_ERROR;
}
LOGV("data source (%s)", srcUrl);
- return mRetriever->setDataSource(srcUrl);
+ return mRetriever->setDataSource(srcUrl, headers);
}
status_t MediaMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 87c8fe4..7b7ba74 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -37,6 +37,8 @@
#include <utils/KeyedVector.h>
#include <utils/String8.h>
+#include <system/audio.h>
+
namespace android {
MediaPlayer::MediaPlayer()
@@ -45,7 +47,7 @@
mListener = NULL;
mCookie = NULL;
mDuration = -1;
- mStreamType = AudioSystem::MUSIC;
+ mStreamType = AUDIO_STREAM_MUSIC;
mCurrentPosition = -1;
mSeekPosition = -1;
mCurrentState = MEDIA_PLAYER_IDLE;
@@ -201,6 +203,16 @@
return mPlayer->setVideoSurface(surface);
}
+status_t MediaPlayer::setVideoSurfaceTexture(
+ const sp<ISurfaceTexture>& surfaceTexture)
+{
+ LOGV("setVideoSurfaceTexture");
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == 0) return NO_INIT;
+
+ return mPlayer->setVideoSurfaceTexture(surfaceTexture);
+}
+
// must call with lock held
status_t MediaPlayer::prepareAsync_l()
{
@@ -541,7 +553,29 @@
return mPlayer->attachAuxEffect(effectId);
}
-void MediaPlayer::notify(int msg, int ext1, int ext2)
+status_t MediaPlayer::setParameter(int key, const Parcel& request)
+{
+ LOGV("MediaPlayer::setParameter(%d)", key);
+ Mutex::Autolock _l(mLock);
+ if (mPlayer != NULL) {
+ return mPlayer->setParameter(key, request);
+ }
+ LOGV("setParameter: no active player");
+ return INVALID_OPERATION;
+}
+
+status_t MediaPlayer::getParameter(int key, Parcel *reply)
+{
+ LOGV("MediaPlayer::getParameter(%d)", key);
+ Mutex::Autolock _l(mLock);
+ if (mPlayer != NULL) {
+ return mPlayer->getParameter(key, reply);
+ }
+ LOGV("getParameter: no active player");
+ return INVALID_OPERATION;
+}
+
+void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
bool send = true;
@@ -631,6 +665,9 @@
mVideoWidth = ext1;
mVideoHeight = ext2;
break;
+ case MEDIA_TIMED_TEXT:
+ LOGV("Received timed text message");
+ break;
default:
LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
break;
@@ -643,7 +680,7 @@
if ((listener != 0) && send) {
Mutex::Autolock _l(mNotifyLock);
LOGV("callback application");
- listener->notify(msg, ext1, ext2);
+ listener->notify(msg, ext1, ext2, obj);
LOGV("back from callback");
}
}
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index fd575fe..0100a17 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -298,6 +298,17 @@
return INVALID_OPERATION;
}
+ // It appears that if an invalid file descriptor is passed through
+ // binder calls, the server-side of the inter-process function call
+ // is skipped. As a result, the check at the server-side to catch
+ // the invalid file descritpor never gets invoked. This is to workaround
+ // this issue by checking the file descriptor first before passing
+ // it through binder call.
+ if (fd < 0) {
+ LOGE("Invalid file descriptor: %d", fd);
+ return BAD_VALUE;
+ }
+
status_t ret = mMediaRecorder->setOutputFile(fd, offset, length);
if (OK != ret) {
LOGV("setOutputFile failed: %d", ret);
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index f7f0d95..fadad28 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -31,8 +31,8 @@
libandroid_runtime \
libstagefright \
libstagefright_omx \
- libstagefright_foundation \
- libsurfaceflinger_client
+ libstagefright_foundation \
+ libgui
LOCAL_STATIC_LIBRARIES := \
libstagefright_rtsp \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 439e4ce..d51c946 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <dirent.h>
#include <unistd.h>
@@ -50,6 +51,11 @@
#include <media/MediaMetadataRetrieverInterface.h>
#include <media/Metadata.h>
#include <media/AudioTrack.h>
+#include <media/MemoryLeakTrackUtil.h>
+
+#include <system/audio.h>
+
+#include <private/android_filesystem_config.h>
#include "MediaRecorderClient.h"
#include "MediaPlayerService.h"
@@ -211,6 +217,15 @@
{
LOGV("MediaPlayerService created");
mNextConnId = 1;
+
+ mBatteryAudio.refCount = 0;
+ for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+ mBatteryAudio.deviceOn[i] = 0;
+ mBatteryAudio.lastTime[i] = 0;
+ mBatteryAudio.totalTime[i] = 0;
+ }
+ // speaker is on by default
+ mBatteryAudio.deviceOn[SPEAKER] = 1;
}
MediaPlayerService::~MediaPlayerService()
@@ -380,139 +395,6 @@
#endif
}
-#if defined(__arm__)
-extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
- size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
-extern "C" void free_malloc_leak_info(uint8_t* info);
-
-// Use the String-class below instead of String8 to allocate all memory
-// beforehand and not reenter the heap while we are examining it...
-struct MyString8 {
- static const size_t MAX_SIZE = 256 * 1024;
-
- MyString8()
- : mPtr((char *)malloc(MAX_SIZE)) {
- *mPtr = '\0';
- }
-
- ~MyString8() {
- free(mPtr);
- }
-
- void append(const char *s) {
- strcat(mPtr, s);
- }
-
- const char *string() const {
- return mPtr;
- }
-
- size_t size() const {
- return strlen(mPtr);
- }
-
-private:
- char *mPtr;
-
- MyString8(const MyString8 &);
- MyString8 &operator=(const MyString8 &);
-};
-
-void memStatus(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- MyString8 result;
-
- typedef struct {
- size_t size;
- size_t dups;
- intptr_t * backtrace;
- } AllocEntry;
-
- uint8_t *info = NULL;
- size_t overallSize = 0;
- size_t infoSize = 0;
- size_t totalMemory = 0;
- size_t backtraceSize = 0;
-
- get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, &backtraceSize);
- if (info) {
- uint8_t *ptr = info;
- size_t count = overallSize / infoSize;
-
- snprintf(buffer, SIZE, " Allocation count %i\n", count);
- result.append(buffer);
- snprintf(buffer, SIZE, " Total memory %i\n", totalMemory);
- result.append(buffer);
-
- AllocEntry * entries = new AllocEntry[count];
-
- for (size_t i = 0; i < count; i++) {
- // Each entry should be size_t, size_t, intptr_t[backtraceSize]
- AllocEntry *e = &entries[i];
-
- e->size = *reinterpret_cast<size_t *>(ptr);
- ptr += sizeof(size_t);
-
- e->dups = *reinterpret_cast<size_t *>(ptr);
- ptr += sizeof(size_t);
-
- e->backtrace = reinterpret_cast<intptr_t *>(ptr);
- ptr += sizeof(intptr_t) * backtraceSize;
- }
-
- // Now we need to sort the entries. They come sorted by size but
- // not by stack trace which causes problems using diff.
- bool moved;
- do {
- moved = false;
- for (size_t i = 0; i < (count - 1); i++) {
- AllocEntry *e1 = &entries[i];
- AllocEntry *e2 = &entries[i+1];
-
- bool swap = e1->size < e2->size;
- if (e1->size == e2->size) {
- for(size_t j = 0; j < backtraceSize; j++) {
- if (e1->backtrace[j] == e2->backtrace[j]) {
- continue;
- }
- swap = e1->backtrace[j] < e2->backtrace[j];
- break;
- }
- }
- if (swap) {
- AllocEntry t = entries[i];
- entries[i] = entries[i+1];
- entries[i+1] = t;
- moved = true;
- }
- }
- } while (moved);
-
- for (size_t i = 0; i < count; i++) {
- AllocEntry *e = &entries[i];
-
- snprintf(buffer, SIZE, "size %8i, dup %4i, ", e->size, e->dups);
- result.append(buffer);
- for (size_t ct = 0; (ct < backtraceSize) && e->backtrace[ct]; ct++) {
- if (ct) {
- result.append(", ");
- }
- snprintf(buffer, SIZE, "0x%08x", e->backtrace[ct]);
- result.append(buffer);
- }
- result.append("\n");
- }
-
- delete[] entries;
- free_malloc_leak_info(info);
- }
-
- write(fd, result.string(), result.size());
-}
-#endif
-
status_t MediaPlayerService::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
@@ -611,7 +493,6 @@
result.append("\n");
}
-#if defined(__arm__)
bool dumpMem = false;
for (size_t i = 0; i < args.size(); i++) {
if (args[i] == String16("-m")) {
@@ -619,9 +500,8 @@
}
}
if (dumpMem) {
- memStatus(fd, args);
+ dumpMemoryAddresses(fd);
}
-#endif
}
write(fd, result.string(), result.size());
return NO_ERROR;
@@ -732,18 +612,15 @@
return TEST_PLAYER;
}
- char value[PROPERTY_VALUE_MAX];
- if (!property_get("media.httplive.disable-nuplayer", value, NULL)
- || (strcasecmp(value, "true") && strcmp(value, "1"))) {
- if (!strncasecmp("http://", url, 7)) {
- size_t len = strlen(url);
- if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
- return NU_PLAYER;
- }
+ if (!strncasecmp("http://", url, 7)
+ || !strncasecmp("https://", url, 8)) {
+ size_t len = strlen(url);
+ if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
+ return NU_PLAYER;
+ }
- if (strstr(url,"m3u8")) {
- return NU_PLAYER;
- }
+ if (strstr(url,"m3u8")) {
+ return NU_PLAYER;
}
}
@@ -936,6 +813,15 @@
return p->setVideoSurface(surface);
}
+status_t MediaPlayerService::Client::setVideoSurfaceTexture(
+ const sp<ISurfaceTexture>& surfaceTexture)
+{
+ LOGV("[%d] setVideoSurfaceTexture(%p)", mConnId, surfaceTexture.get());
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+ return p->setVideoSurfaceTexture(surfaceTexture);
+}
+
status_t MediaPlayerService::Client::invoke(const Parcel& request,
Parcel *reply)
{
@@ -1138,7 +1024,22 @@
return NO_ERROR;
}
-void MediaPlayerService::Client::notify(void* cookie, int msg, int ext1, int ext2)
+status_t MediaPlayerService::Client::setParameter(int key, const Parcel &request) {
+ LOGV("[%d] setParameter(%d)", mConnId, key);
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+ return p->setParameter(key, request);
+}
+
+status_t MediaPlayerService::Client::getParameter(int key, Parcel *reply) {
+ LOGV("[%d] getParameter(%d)", mConnId, key);
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+ return p->getParameter(key, reply);
+}
+
+void MediaPlayerService::Client::notify(
+ void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
{
Client* client = static_cast<Client*>(cookie);
@@ -1155,7 +1056,7 @@
client->addNewMetadataUpdate(metadata_type);
}
LOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);
- client->mClient->notify(msg, ext1, ext2);
+ client->mClient->notify(msg, ext1, ext2, obj);
}
@@ -1324,7 +1225,7 @@
mSessionId(sessionId) {
LOGV("AudioOutput(%d)", sessionId);
mTrack = 0;
- mStreamType = AudioSystem::MUSIC;
+ mStreamType = AUDIO_STREAM_MUSIC;
mLeftVolume = 1.0;
mRightVolume = 1.0;
mLatency = 0;
@@ -1434,7 +1335,7 @@
mStreamType,
sampleRate,
format,
- (channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
+ (channelCount == 2) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO,
frameCount,
0 /* flags */,
CallbackWrapper,
@@ -1446,7 +1347,7 @@
mStreamType,
sampleRate,
format,
- (channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
+ (channelCount == 2) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO,
frameCount,
0,
NULL,
@@ -1565,8 +1466,15 @@
size_t actualSize = (*me->mCallback)(
me, buffer->raw, buffer->size, me->mCallbackCookie);
- buffer->size = actualSize;
+ if (actualSize == 0 && buffer->size > 0) {
+ // We've reached EOS but the audio track is not stopped yet,
+ // keep playing silence.
+ memset(buffer->raw, 0, buffer->size);
+ actualSize = buffer->size;
+ }
+
+ buffer->size = actualSize;
}
int MediaPlayerService::AudioOutput::getSessionId()
@@ -1732,7 +1640,8 @@
return mError;
}
-void MediaPlayerService::AudioCache::notify(void* cookie, int msg, int ext1, int ext2)
+void MediaPlayerService::AudioCache::notify(
+ void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
{
LOGV("notify(%p, %d, %d, %d)", cookie, msg, ext1, ext2);
AudioCache* p = static_cast<AudioCache*>(cookie);
@@ -1766,4 +1675,192 @@
return 0;
}
+void MediaPlayerService::addBatteryData(uint32_t params)
+{
+ Mutex::Autolock lock(mLock);
+
+ int32_t time = systemTime() / 1000000L;
+
+ // change audio output devices. This notification comes from AudioFlinger
+ if ((params & kBatteryDataSpeakerOn)
+ || (params & kBatteryDataOtherAudioDeviceOn)) {
+
+ int deviceOn[NUM_AUDIO_DEVICES];
+ for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+ deviceOn[i] = 0;
+ }
+
+ if ((params & kBatteryDataSpeakerOn)
+ && (params & kBatteryDataOtherAudioDeviceOn)) {
+ deviceOn[SPEAKER_AND_OTHER] = 1;
+ } else if (params & kBatteryDataSpeakerOn) {
+ deviceOn[SPEAKER] = 1;
+ } else {
+ deviceOn[OTHER_AUDIO_DEVICE] = 1;
+ }
+
+ for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+ if (mBatteryAudio.deviceOn[i] != deviceOn[i]){
+
+ if (mBatteryAudio.refCount > 0) { // if playing audio
+ if (!deviceOn[i]) {
+ mBatteryAudio.lastTime[i] += time;
+ mBatteryAudio.totalTime[i] += mBatteryAudio.lastTime[i];
+ mBatteryAudio.lastTime[i] = 0;
+ } else {
+ mBatteryAudio.lastTime[i] = 0 - time;
+ }
+ }
+
+ mBatteryAudio.deviceOn[i] = deviceOn[i];
+ }
+ }
+ return;
+ }
+
+ // an sudio stream is started
+ if (params & kBatteryDataAudioFlingerStart) {
+ // record the start time only if currently no other audio
+ // is being played
+ if (mBatteryAudio.refCount == 0) {
+ for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+ if (mBatteryAudio.deviceOn[i]) {
+ mBatteryAudio.lastTime[i] -= time;
+ }
+ }
+ }
+
+ mBatteryAudio.refCount ++;
+ return;
+
+ } else if (params & kBatteryDataAudioFlingerStop) {
+ if (mBatteryAudio.refCount <= 0) {
+ LOGW("Battery track warning: refCount is <= 0");
+ return;
+ }
+
+ // record the stop time only if currently this is the only
+ // audio being played
+ if (mBatteryAudio.refCount == 1) {
+ for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+ if (mBatteryAudio.deviceOn[i]) {
+ mBatteryAudio.lastTime[i] += time;
+ mBatteryAudio.totalTime[i] += mBatteryAudio.lastTime[i];
+ mBatteryAudio.lastTime[i] = 0;
+ }
+ }
+ }
+
+ mBatteryAudio.refCount --;
+ return;
+ }
+
+ int uid = IPCThreadState::self()->getCallingUid();
+ if (uid == AID_MEDIA) {
+ return;
+ }
+ int index = mBatteryData.indexOfKey(uid);
+
+ if (index < 0) { // create a new entry for this UID
+ BatteryUsageInfo info;
+ info.audioTotalTime = 0;
+ info.videoTotalTime = 0;
+ info.audioLastTime = 0;
+ info.videoLastTime = 0;
+ info.refCount = 0;
+
+ if (mBatteryData.add(uid, info) == NO_MEMORY) {
+ LOGE("Battery track error: no memory for new app");
+ return;
+ }
+ }
+
+ BatteryUsageInfo &info = mBatteryData.editValueFor(uid);
+
+ if (params & kBatteryDataCodecStarted) {
+ if (params & kBatteryDataTrackAudio) {
+ info.audioLastTime -= time;
+ info.refCount ++;
+ }
+ if (params & kBatteryDataTrackVideo) {
+ info.videoLastTime -= time;
+ info.refCount ++;
+ }
+ } else {
+ if (info.refCount == 0) {
+ LOGW("Battery track warning: refCount is already 0");
+ return;
+ } else if (info.refCount < 0) {
+ LOGE("Battery track error: refCount < 0");
+ mBatteryData.removeItem(uid);
+ return;
+ }
+
+ if (params & kBatteryDataTrackAudio) {
+ info.audioLastTime += time;
+ info.refCount --;
+ }
+ if (params & kBatteryDataTrackVideo) {
+ info.videoLastTime += time;
+ info.refCount --;
+ }
+
+ // no stream is being played by this UID
+ if (info.refCount == 0) {
+ info.audioTotalTime += info.audioLastTime;
+ info.audioLastTime = 0;
+ info.videoTotalTime += info.videoLastTime;
+ info.videoLastTime = 0;
+ }
+ }
+}
+
+status_t MediaPlayerService::pullBatteryData(Parcel* reply) {
+ Mutex::Autolock lock(mLock);
+
+ // audio output devices usage
+ int32_t time = systemTime() / 1000000L; //in ms
+ int32_t totalTime;
+
+ for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+ totalTime = mBatteryAudio.totalTime[i];
+
+ if (mBatteryAudio.deviceOn[i]
+ && (mBatteryAudio.lastTime[i] != 0)) {
+ int32_t tmpTime = mBatteryAudio.lastTime[i] + time;
+ totalTime += tmpTime;
+ }
+
+ reply->writeInt32(totalTime);
+ // reset the total time
+ mBatteryAudio.totalTime[i] = 0;
+ }
+
+ // codec usage
+ BatteryUsageInfo info;
+ int size = mBatteryData.size();
+
+ reply->writeInt32(size);
+ int i = 0;
+
+ while (i < size) {
+ info = mBatteryData.valueAt(i);
+
+ reply->writeInt32(mBatteryData.keyAt(i)); //UID
+ reply->writeInt32(info.audioTotalTime);
+ reply->writeInt32(info.videoTotalTime);
+
+ info.audioTotalTime = 0;
+ info.videoTotalTime = 0;
+
+ // remove the UID entry where no stream is being played
+ if (info.refCount <= 0) {
+ mBatteryData.removeItemsAt(i);
+ size --;
+ i --;
+ }
+ i++;
+ }
+ return NO_ERROR;
+}
} // namespace android
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 62f8ed6..8bab471 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -30,6 +30,8 @@
#include <media/MediaPlayerInterface.h>
#include <media/Metadata.h>
+#include <system/audio.h>
+
namespace android {
class IMediaRecorder;
@@ -130,7 +132,7 @@
virtual ssize_t bufferSize() const { return frameSize() * mFrameCount; }
virtual ssize_t frameCount() const { return mFrameCount; }
virtual ssize_t channelCount() const { return (ssize_t)mChannelCount; }
- virtual ssize_t frameSize() const { return ssize_t(mChannelCount * ((mFormat == AudioSystem::PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); }
+ virtual ssize_t frameSize() const { return ssize_t(mChannelCount * ((mFormat == AUDIO_FORMAT_PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); }
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
virtual status_t getPosition(uint32_t *position);
@@ -156,7 +158,8 @@
sp<IMemoryHeap> getHeap() const { return mHeap; }
- static void notify(void* cookie, int msg, int ext1, int ext2);
+ static void notify(void* cookie, int msg,
+ int ext1, int ext2, const Parcel *obj);
virtual status_t dump(int fd, const Vector<String16>& args) const;
private:
@@ -204,7 +207,50 @@
void removeClient(wp<Client> client);
+ // For battery usage tracking purpose
+ struct BatteryUsageInfo {
+ // how many streams are being played by one UID
+ int refCount;
+ // a temp variable to store the duration(ms) of audio codecs
+ // when we start a audio codec, we minus the system time from audioLastTime
+ // when we pause it, we add the system time back to the audioLastTime
+ // so after the pause, audioLastTime = pause time - start time
+ // if multiple audio streams are played (or recorded), then audioLastTime
+ // = the total playing time of all the streams
+ int32_t audioLastTime;
+ // when all the audio streams are being paused, we assign audioLastTime to
+ // this variable, so this value could be provided to the battery app
+ // in the next pullBatteryData call
+ int32_t audioTotalTime;
+ int32_t videoLastTime;
+ int32_t videoTotalTime;
+ };
+ KeyedVector<int, BatteryUsageInfo> mBatteryData;
+
+ enum {
+ SPEAKER,
+ OTHER_AUDIO_DEVICE,
+ SPEAKER_AND_OTHER,
+ NUM_AUDIO_DEVICES
+ };
+
+ struct BatteryAudioFlingerUsageInfo {
+ int refCount; // how many audio streams are being played
+ int deviceOn[NUM_AUDIO_DEVICES]; // whether the device is currently used
+ int32_t lastTime[NUM_AUDIO_DEVICES]; // in ms
+ // totalTime[]: total time of audio output devices usage
+ int32_t totalTime[NUM_AUDIO_DEVICES]; // in ms
+ };
+
+ // This varialble is used to record the usage of audio output device
+ // for battery app
+ BatteryAudioFlingerUsageInfo mBatteryAudio;
+
+ // Collect info of the codec usage from media player and media recorder
+ virtual void addBatteryData(uint32_t params);
+ // API for the Battery app to pull the data of codecs usage
+ virtual status_t pullBatteryData(Parcel* reply);
private:
class Client : public BnMediaPlayer {
@@ -212,6 +258,8 @@
// IMediaPlayer interface
virtual void disconnect();
virtual status_t setVideoSurface(const sp<Surface>& surface);
+ virtual status_t setVideoSurfaceTexture(
+ const sp<ISurfaceTexture>& surfaceTexture);
virtual status_t prepareAsync();
virtual status_t start();
virtual status_t stop();
@@ -231,6 +279,8 @@
Parcel *reply);
virtual status_t setAuxEffectSendLevel(float level);
virtual status_t attachAuxEffect(int effectId);
+ virtual status_t setParameter(int key, const Parcel &request);
+ virtual status_t getParameter(int key, Parcel *reply);
sp<MediaPlayerBase> createPlayer(player_type playerType);
@@ -242,7 +292,8 @@
status_t setDataSource(const sp<IStreamSource> &source);
- static void notify(void* cookie, int msg, int ext1, int ext2);
+ static void notify(void* cookie, int msg,
+ int ext1, int ext2, const Parcel *obj);
pid_t pid() const { return mPid; }
virtual status_t dump(int fd, const Vector<String16>& args) const;
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 1a1780c..29cc019 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -35,6 +35,8 @@
#include <media/AudioTrack.h>
+#include <system/audio.h>
+
#include "MediaRecorderClient.h"
#include "MediaPlayerService.h"
@@ -102,7 +104,7 @@
LOGE("recorder is not initialized");
return NO_INIT;
}
- return mRecorder->setAudioSource((audio_source)as);
+ return mRecorder->setAudioSource((audio_source_t)as);
}
status_t MediaRecorderClient::setOutputFormat(int of)
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 5fcf2a7..8f776b4 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -120,7 +120,8 @@
return p;
}
-status_t MetadataRetrieverClient::setDataSource(const char *url)
+status_t MetadataRetrieverClient::setDataSource(
+ const char *url, const KeyedVector<String8, String8> *headers)
{
LOGV("setDataSource(%s)", url);
Mutex::Autolock lock(mLock);
@@ -131,7 +132,7 @@
LOGV("player type = %d", playerType);
sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
if (p == NULL) return NO_INIT;
- status_t ret = p->setDataSource(url);
+ status_t ret = p->setDataSource(url, headers);
if (ret == NO_ERROR) mRetriever = p;
return ret;
}
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index b834715..f08f933 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -41,7 +41,10 @@
// Implements IMediaMetadataRetriever interface
// These methods are called in IMediaMetadataRetriever.cpp?
virtual void disconnect();
- virtual status_t setDataSource(const char *url);
+
+ virtual status_t setDataSource(
+ const char *url, const KeyedVector<String8, String8> *headers);
+
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual sp<IMemory> getFrameAtTime(int64_t timeUs, int option);
virtual sp<IMemory> extractAlbumArt();
diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp
index 1b0b05f..589c625 100644
--- a/media/libmediaplayerservice/MidiFile.cpp
+++ b/media/libmediaplayerservice/MidiFile.cpp
@@ -30,6 +30,8 @@
#include <sys/types.h>
#include <sys/stat.h>
+#include <system/audio.h>
+
#include "MidiFile.h"
#ifdef HAVE_GETTID
@@ -58,7 +60,7 @@
MidiFile::MidiFile() :
mEasData(NULL), mEasHandle(NULL), mAudioBuffer(NULL),
mPlayTime(-1), mDuration(-1), mState(EAS_STATE_ERROR),
- mStreamType(AudioSystem::MUSIC), mLoop(false), mExit(false),
+ mStreamType(AUDIO_STREAM_MUSIC), mLoop(false), mExit(false),
mPaused(false), mRender(false), mTid(-1)
{
LOGV("constructor");
@@ -423,7 +425,7 @@
}
status_t MidiFile::createOutputTrack() {
- if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, AudioSystem::PCM_16_BIT, 2) != NO_ERROR) {
+ if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, AUDIO_FORMAT_PCM_16_BIT, 2) != NO_ERROR) {
LOGE("mAudioSink open failed");
return ERROR_OPEN_FAILED;
}
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index aa8f3f0..b35696f 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -36,6 +36,9 @@
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual status_t setVideoSurface(const sp<Surface>& surface) { return UNKNOWN_ERROR; }
+ virtual status_t setVideoSurfaceTexture(
+ const sp<ISurfaceTexture>& surfaceTexture)
+ { return UNKNOWN_ERROR; }
virtual status_t prepare();
virtual status_t prepareAsync();
virtual status_t start();
@@ -52,6 +55,13 @@
virtual status_t invoke(const Parcel& request, Parcel *reply) {
return INVALID_OPERATION;
}
+ virtual status_t setParameter(int key, const Parcel &request) {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getParameter(int key, Parcel *reply) {
+ return INVALID_OPERATION;
+ }
+
private:
status_t createOutputTrack();
diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.cpp b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
index ad95fac..aaf2d18 100644
--- a/media/libmediaplayerservice/MidiMetadataRetriever.cpp
+++ b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
@@ -35,7 +35,8 @@
mMetadataValues[0][0] = '\0';
}
-status_t MidiMetadataRetriever::setDataSource(const char *url)
+status_t MidiMetadataRetriever::setDataSource(
+ const char *url, const KeyedVector<String8, String8> *headers)
{
LOGV("setDataSource: %s", url? url: "NULL pointer");
Mutex::Autolock lock(mLock);
@@ -43,8 +44,7 @@
if (mMidiPlayer == 0) {
mMidiPlayer = new MidiFile();
}
- // TODO: support headers in MetadataRetriever interface!
- return mMidiPlayer->setDataSource(url, NULL /* headers */);
+ return mMidiPlayer->setDataSource(url, headers);
}
status_t MidiMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.h b/media/libmediaplayerservice/MidiMetadataRetriever.h
index 73ff347..4cee42d 100644
--- a/media/libmediaplayerservice/MidiMetadataRetriever.h
+++ b/media/libmediaplayerservice/MidiMetadataRetriever.h
@@ -31,7 +31,9 @@
MidiMetadataRetriever() {}
~MidiMetadataRetriever() {}
- virtual status_t setDataSource(const char *url);
+ virtual status_t setDataSource(
+ const char *url, const KeyedVector<String8, String8> *headers);
+
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual const char* extractMetadata(int keyCode);
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index da564dc..02ec911 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -1,3 +1,19 @@
+/*
+ * 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>
@@ -33,7 +49,6 @@
status_t StagefrightPlayer::setDataSource(
const char *url, const KeyedVector<String8, String8> *headers) {
- LOGI("setDataSource('%s')", url);
return mPlayer->setDataSource(url, headers);
}
@@ -55,6 +70,14 @@
return OK;
}
+status_t StagefrightPlayer::setVideoSurfaceTexture(
+ const sp<ISurfaceTexture> &surfaceTexture) {
+ LOGV("setVideoSurfaceTexture");
+
+ mPlayer->setSurfaceTexture(surfaceTexture);
+ return OK;
+}
+
status_t StagefrightPlayer::prepare() {
return mPlayer->prepare();
}
@@ -87,7 +110,7 @@
}
status_t StagefrightPlayer::seekTo(int msec) {
- LOGV("seekTo");
+ LOGV("seekTo %.2f secs", msec / 1E3);
status_t err = mPlayer->seekTo((int64_t)msec * 1000);
@@ -154,6 +177,16 @@
mPlayer->setAudioSink(audioSink);
}
+status_t StagefrightPlayer::setParameter(int key, const Parcel &request) {
+ LOGV("setParameter");
+ return mPlayer->setParameter(key, request);
+}
+
+status_t StagefrightPlayer::getParameter(int key, Parcel *reply) {
+ LOGV("getParameter");
+ return mPlayer->getParameter(key, reply);
+}
+
status_t StagefrightPlayer::getMetadata(
const media::Metadata::Filter& ids, Parcel *records) {
using media::Metadata;
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index fc72bfb..ddd37e4 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -39,6 +39,8 @@
virtual status_t setDataSource(const sp<IStreamSource> &source);
virtual status_t setVideoSurface(const sp<Surface> &surface);
+ virtual status_t setVideoSurfaceTexture(
+ const sp<ISurfaceTexture> &surfaceTexture);
virtual status_t prepare();
virtual status_t prepareAsync();
virtual status_t start();
@@ -53,6 +55,8 @@
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 getMetadata(
const media::Metadata::Filter& ids, Parcel *records);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index f134cba..978571c 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -20,8 +20,13 @@
#include "StagefrightRecorder.h"
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <media/IMediaPlayerService.h>
#include <media/stagefright/AudioSource.h>
#include <media/stagefright/AMRWriter.h>
+#include <media/stagefright/AACWriter.h>
#include <media/stagefright/CameraSource.h>
#include <media/stagefright/VideoSourceDownSampler.h>
#include <media/stagefright/CameraSourceTimeLapse.h>
@@ -42,13 +47,29 @@
#include <ctype.h>
#include <unistd.h>
+#include <system/audio.h>
+
#include "ARTPWriter.h"
namespace android {
+// To collect the encoder usage for the battery app
+static 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);
+}
+
+
StagefrightRecorder::StagefrightRecorder()
: mWriter(NULL), mWriterAux(NULL),
- mOutputFd(-1), mOutputFdAux(-1) {
+ mOutputFd(-1), mOutputFdAux(-1),
+ mAudioSource(AUDIO_SOURCE_CNT),
+ mVideoSource(VIDEO_SOURCE_LIST_END),
+ mStarted(false) {
LOGV("Constructor");
reset();
@@ -64,10 +85,10 @@
return OK;
}
-status_t StagefrightRecorder::setAudioSource(audio_source as) {
+status_t StagefrightRecorder::setAudioSource(audio_source_t as) {
LOGV("setAudioSource: %d", as);
if (as < AUDIO_SOURCE_DEFAULT ||
- as >= AUDIO_SOURCE_LIST_END) {
+ as >= AUDIO_SOURCE_CNT) {
LOGE("Invalid audio source: %d", as);
return BAD_VALUE;
}
@@ -244,6 +265,10 @@
// returns true on success, false otherwise.
static bool safe_strtoi64(const char *s, int64_t *val) {
char *end;
+
+ // It is lame, but according to man page, we have to set errno to 0
+ // before calling strtoll().
+ errno = 0;
*val = strtoll(s, &end, 10);
if (end == s || errno == ERANGE) {
@@ -568,6 +593,26 @@
return OK;
}
+status_t StagefrightRecorder::setParamGeoDataLongitude(
+ int32_t longitudex10000) {
+
+ if (longitudex10000 > 1800000 || longitudex10000 < -1800000) {
+ return BAD_VALUE;
+ }
+ mLongitudex10000 = longitudex10000;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamGeoDataLatitude(
+ int32_t latitudex10000) {
+
+ if (latitudex10000 > 900000 || latitudex10000 < -900000) {
+ return BAD_VALUE;
+ }
+ mLatitudex10000 = latitudex10000;
+ return OK;
+}
+
status_t StagefrightRecorder::setParameter(
const String8 &key, const String8 &value) {
LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -596,6 +641,16 @@
if (safe_strtoi32(value.string(), &use64BitOffset)) {
return setParam64BitFileOffset(use64BitOffset != 0);
}
+ } else if (key == "param-geotag-longitude") {
+ int32_t longitudex10000;
+ if (safe_strtoi32(value.string(), &longitudex10000)) {
+ return setParamGeoDataLongitude(longitudex10000);
+ }
+ } else if (key == "param-geotag-latitude") {
+ int32_t latitudex10000;
+ if (safe_strtoi32(value.string(), &latitudex10000)) {
+ return setParamGeoDataLatitude(latitudex10000);
+ }
} else if (key == "param-track-time-status") {
int64_t timeDurationUs;
if (safe_strtoi64(value.string(), &timeDurationUs)) {
@@ -741,30 +796,54 @@
return UNKNOWN_ERROR;
}
+ status_t status = OK;
+
switch (mOutputFormat) {
case OUTPUT_FORMAT_DEFAULT:
case OUTPUT_FORMAT_THREE_GPP:
case OUTPUT_FORMAT_MPEG_4:
- return startMPEG4Recording();
+ status = startMPEG4Recording();
+ break;
case OUTPUT_FORMAT_AMR_NB:
case OUTPUT_FORMAT_AMR_WB:
- return startAMRRecording();
+ status = startAMRRecording();
+ break;
case OUTPUT_FORMAT_AAC_ADIF:
case OUTPUT_FORMAT_AAC_ADTS:
- return startAACRecording();
+ status = startAACRecording();
+ break;
case OUTPUT_FORMAT_RTP_AVP:
- return startRTPRecording();
+ status = startRTPRecording();
+ break;
case OUTPUT_FORMAT_MPEG2TS:
- return startMPEG2TSRecording();
+ status = startMPEG2TSRecording();
+ break;
default:
LOGE("Unsupported output file format: %d", mOutputFormat);
- return UNKNOWN_ERROR;
+ status = UNKNOWN_ERROR;
+ break;
}
+
+ if ((status == OK) && (!mStarted)) {
+ mStarted = true;
+
+ uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted;
+ if (mAudioSource != AUDIO_SOURCE_CNT) {
+ params |= IMediaPlayerService::kBatteryDataTrackAudio;
+ }
+ if (mVideoSource != VIDEO_SOURCE_LIST_END) {
+ params |= IMediaPlayerService::kBatteryDataTrackVideo;
+ }
+
+ addBatteryData(params);
+ }
+
+ return status;
}
sp<MediaSource> StagefrightRecorder::createAudioSource() {
@@ -824,15 +903,21 @@
}
status_t StagefrightRecorder::startAACRecording() {
- CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADIF ||
- mOutputFormat == OUTPUT_FORMAT_AAC_ADTS);
+ // FIXME:
+ // Add support for OUTPUT_FORMAT_AAC_ADIF
+ CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADTS);
CHECK(mAudioEncoder == AUDIO_ENCODER_AAC);
- CHECK(mAudioSource != AUDIO_SOURCE_LIST_END);
+ CHECK(mAudioSource != AUDIO_SOURCE_CNT);
- CHECK(0 == "AACWriter is not implemented yet");
+ mWriter = new AACWriter(mOutputFd);
+ status_t status = startRawAudioRecording();
+ if (status != OK) {
+ mWriter.clear();
+ mWriter = NULL;
+ }
- return OK;
+ return status;
}
status_t StagefrightRecorder::startAMRRecording() {
@@ -854,7 +939,17 @@
}
}
- if (mAudioSource >= AUDIO_SOURCE_LIST_END) {
+ mWriter = new AMRWriter(mOutputFd);
+ status_t status = startRawAudioRecording();
+ if (status != OK) {
+ mWriter.clear();
+ mWriter = NULL;
+ }
+ return status;
+}
+
+status_t StagefrightRecorder::startRawAudioRecording() {
+ if (mAudioSource >= AUDIO_SOURCE_CNT) {
LOGE("Invalid audio source: %d", mAudioSource);
return BAD_VALUE;
}
@@ -869,7 +964,7 @@
return UNKNOWN_ERROR;
}
- mWriter = new AMRWriter(mOutputFd);
+ CHECK(mWriter != 0);
mWriter->addSource(audioEncoder);
if (mMaxFileDurationUs != 0) {
@@ -887,9 +982,9 @@
status_t StagefrightRecorder::startRTPRecording() {
CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_RTP_AVP);
- if ((mAudioSource != AUDIO_SOURCE_LIST_END
+ if ((mAudioSource != AUDIO_SOURCE_CNT
&& mVideoSource != VIDEO_SOURCE_LIST_END)
- || (mAudioSource == AUDIO_SOURCE_LIST_END
+ || (mAudioSource == AUDIO_SOURCE_CNT
&& mVideoSource == VIDEO_SOURCE_LIST_END)) {
// Must have exactly one source.
return BAD_VALUE;
@@ -901,7 +996,7 @@
sp<MediaSource> source;
- if (mAudioSource != AUDIO_SOURCE_LIST_END) {
+ if (mAudioSource != AUDIO_SOURCE_CNT) {
source = createAudioSource();
} else {
@@ -929,7 +1024,7 @@
sp<MediaWriter> writer = new MPEG2TSWriter(mOutputFd);
- if (mAudioSource != AUDIO_SOURCE_LIST_END) {
+ if (mAudioSource != AUDIO_SOURCE_CNT) {
if (mAudioEncoder != AUDIO_ENCODER_AAC) {
return ERROR_UNSUPPORTED;
}
@@ -1337,7 +1432,7 @@
// Audio source is added at the end if it exists.
// This help make sure that the "recoding" sound is suppressed for
// camcorder applications in the recorded files.
- if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_LIST_END)) {
+ if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_CNT)) {
err = setupAudioEncoder(writer);
if (err != OK) return err;
*totalBitRate += mAudioBitRate;
@@ -1347,6 +1442,10 @@
reinterpret_cast<MPEG4Writer *>(writer.get())->
setInterleaveDuration(mInterleaveDurationUs);
}
+ if (mLongitudex10000 > -3600000 && mLatitudex10000 > -3600000) {
+ reinterpret_cast<MPEG4Writer *>(writer.get())->
+ setGeoData(mLatitudex10000, mLongitudex10000);
+ }
if (mMaxFileDurationUs != 0) {
writer->setMaxFileDuration(mMaxFileDurationUs);
}
@@ -1354,6 +1453,12 @@
writer->setMaxFileSize(mMaxFileSizeBytes);
}
+ mStartTimeOffsetMs = mEncoderProfiles->getStartTimeOffsetMs(mCameraId);
+ if (mStartTimeOffsetMs > 0) {
+ reinterpret_cast<MPEG4Writer *>(writer.get())->
+ setStartTimeOffsetMs(mStartTimeOffsetMs);
+ }
+
writer->setListener(mListener);
*mediaWriter = writer;
return OK;
@@ -1454,6 +1559,21 @@
mWriterAux->pause();
}
+ if (mStarted) {
+ mStarted = false;
+
+ uint32_t params = 0;
+ if (mAudioSource != AUDIO_SOURCE_CNT) {
+ params |= IMediaPlayerService::kBatteryDataTrackAudio;
+ }
+ if (mVideoSource != VIDEO_SOURCE_LIST_END) {
+ params |= IMediaPlayerService::kBatteryDataTrackVideo;
+ }
+
+ addBatteryData(params);
+ }
+
+
return OK;
}
@@ -1490,6 +1610,21 @@
}
}
+ if (mStarted) {
+ mStarted = false;
+
+ uint32_t params = 0;
+ if (mAudioSource != AUDIO_SOURCE_CNT) {
+ params |= IMediaPlayerService::kBatteryDataTrackAudio;
+ }
+ if (mVideoSource != VIDEO_SOURCE_LIST_END) {
+ params |= IMediaPlayerService::kBatteryDataTrackVideo;
+ }
+
+ addBatteryData(params);
+ }
+
+
return err;
}
@@ -1505,7 +1640,7 @@
stop();
// No audio or video source by default
- mAudioSource = AUDIO_SOURCE_LIST_END;
+ mAudioSource = AUDIO_SOURCE_CNT;
mVideoSource = VIDEO_SOURCE_LIST_END;
// Default parameters
@@ -1530,6 +1665,7 @@
mAudioTimeScale = -1;
mVideoTimeScale = -1;
mCameraId = 0;
+ mStartTimeOffsetMs = -1;
mVideoEncoderProfile = -1;
mVideoEncoderLevel = -1;
mMaxFileDurationUs = 0;
@@ -1543,6 +1679,8 @@
mIsMetaDataStoredInVideoBuffers = false;
mEncoderProfiles = MediaProfiles::getInstance();
mRotationDegrees = 0;
+ mLatitudex10000 = -3600000;
+ mLongitudex10000 = -3600000;
mOutputFd = -1;
mOutputFdAux = -1;
@@ -1616,6 +1754,8 @@
result.append(buffer);
snprintf(buffer, SIZE, " Camera Id: %d\n", mCameraId);
result.append(buffer);
+ snprintf(buffer, SIZE, " Start time offset (ms): %d\n", mStartTimeOffsetMs);
+ result.append(buffer);
snprintf(buffer, SIZE, " Encoder: %d\n", mVideoEncoder);
result.append(buffer);
snprintf(buffer, SIZE, " Encoder profile: %d\n", mVideoEncoderProfile);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 72225db..aa67aa7 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -22,6 +22,8 @@
#include <camera/CameraParameters.h>
#include <utils/String8.h>
+#include <system/audio.h>
+
namespace android {
class Camera;
@@ -39,7 +41,7 @@
virtual ~StagefrightRecorder();
virtual status_t init();
- virtual status_t setAudioSource(audio_source as);
+ virtual status_t setAudioSource(audio_source_t as);
virtual status_t setVideoSource(video_source vs);
virtual status_t setOutputFormat(output_format of);
virtual status_t setAudioEncoder(audio_encoder ae);
@@ -67,9 +69,10 @@
sp<Surface> mPreviewSurface;
sp<IMediaRecorderClient> mListener;
sp<MediaWriter> mWriter, mWriterAux;
+ int mOutputFd, mOutputFdAux;
sp<AudioSource> mAudioSourceNode;
- audio_source mAudioSource;
+ audio_source_t mAudioSource;
video_source mVideoSource;
output_format mOutputFormat;
audio_encoder mAudioEncoder;
@@ -94,6 +97,9 @@
int64_t mMaxFileDurationUs;
int64_t mTrackEveryTimeDurationUs;
int32_t mRotationDegrees; // Clockwise
+ int32_t mLatitudex10000;
+ int32_t mLongitudex10000;
+ int32_t mStartTimeOffsetMs;
bool mCaptureTimeLapse;
int64_t mTimeBetweenTimeLapseFrameCaptureUs;
@@ -102,11 +108,12 @@
sp<CameraSourceTimeLapse> mCameraSourceTimeLapse;
String8 mParams;
- int mOutputFd, mOutputFdAux;
bool mIsMetaDataStoredInVideoBuffers;
MediaProfiles *mEncoderProfiles;
+ bool mStarted;
+
status_t setupMPEG4Recording(
bool useSplitCameraSource,
int outputFd,
@@ -119,6 +126,7 @@
status_t startMPEG4Recording();
status_t startAMRRecording();
status_t startAACRecording();
+ status_t startRawAudioRecording();
status_t startRTPRecording();
status_t startMPEG2TSRecording();
sp<MediaSource> createAudioSource();
@@ -155,6 +163,8 @@
status_t setParamMaxFileDurationUs(int64_t timeUs);
status_t setParamMaxFileSizeBytes(int64_t bytes);
status_t setParamMovieTimeScale(int32_t timeScale);
+ status_t setParamGeoDataLongitude(int32_t longitudex10000);
+ status_t setParamGeoDataLatitude(int32_t latitudex10000);
void clipVideoBitRate();
void clipVideoFrameRate();
void clipVideoFrameWidth();
diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h
index 6abd8e3..802a11b 100644
--- a/media/libmediaplayerservice/TestPlayerStub.h
+++ b/media/libmediaplayerservice/TestPlayerStub.h
@@ -78,6 +78,10 @@
virtual status_t setVideoSurface(const android::sp<android::Surface>& s) {
return mPlayer->setVideoSurface(s);
}
+ virtual status_t setVideoSurfaceTexture(
+ const android::sp<android::ISurfaceTexture>& st) {
+ return mPlayer->setVideoSurfaceTexture(st);
+ }
virtual status_t prepare() {return mPlayer->prepare();}
virtual status_t prepareAsync() {return mPlayer->prepareAsync();}
virtual status_t start() {return mPlayer->start();}
@@ -95,6 +99,12 @@
virtual status_t invoke(const android::Parcel& in, android::Parcel *out) {
return mPlayer->invoke(in, out);
}
+ virtual status_t setParameter(int key, const Parcel &request) {
+ return mPlayer->setParameter(key, request);
+ }
+ virtual status_t getParameter(int key, Parcel *reply) {
+ return mPlayer->getParameter(key, reply);
+ }
// @return true if the current build is 'eng' or 'test' and the
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index c20e279..e761509 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -8,7 +8,6 @@
NuPlayerDriver.cpp \
NuPlayerRenderer.cpp \
NuPlayerStreamListener.cpp \
- DecoderWrapper.cpp \
StreamingSource.cpp \
LOCAL_C_INCLUDES := \
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
deleted file mode 100644
index 802d1fb..0000000
--- a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
+++ /dev/null
@@ -1,576 +0,0 @@
-/*
- * Copyright (C) 2010 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 "DecoderWrapper"
-#include <utils/Log.h>
-
-#include "DecoderWrapper.h"
-
-#include "AACDecoder.h"
-
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/ACodec.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-
-namespace android {
-
-struct DecoderWrapper::WrapperSource : public MediaSource {
- WrapperSource(
- const sp<MetaData> &meta,
- const sp<AMessage> ¬ify);
-
- virtual status_t start(MetaData *params);
- virtual status_t stop();
- virtual sp<MetaData> getFormat();
-
- virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options);
-
- void queueBuffer(const sp<ABuffer> &buffer);
- void queueEOS(status_t finalResult);
- void clear();
-
-protected:
- virtual ~WrapperSource();
-
-private:
- Mutex mLock;
- Condition mCondition;
-
- sp<MetaData> mMeta;
- sp<AMessage> mNotify;
-
- List<sp<ABuffer> > mQueue;
- status_t mFinalResult;
-
- DISALLOW_EVIL_CONSTRUCTORS(WrapperSource);
-};
-
-DecoderWrapper::WrapperSource::WrapperSource(
- const sp<MetaData> &meta, const sp<AMessage> ¬ify)
- : mMeta(meta),
- mNotify(notify),
- mFinalResult(OK) {
-}
-
-DecoderWrapper::WrapperSource::~WrapperSource() {
-}
-
-status_t DecoderWrapper::WrapperSource::start(MetaData *params) {
- return OK;
-}
-
-status_t DecoderWrapper::WrapperSource::stop() {
- return OK;
-}
-
-sp<MetaData> DecoderWrapper::WrapperSource::getFormat() {
- return mMeta;
-}
-
-status_t DecoderWrapper::WrapperSource::read(
- MediaBuffer **out, const ReadOptions *options) {
- Mutex::Autolock autoLock(mLock);
-
- bool requestedBuffer = false;
-
- while (mQueue.empty() && mFinalResult == OK) {
- if (!requestedBuffer) {
- mNotify->dup()->post();
- requestedBuffer = true;
- }
-
- mCondition.wait(mLock);
- }
-
- if (mQueue.empty()) {
- return mFinalResult;
- }
-
- sp<ABuffer> src = *mQueue.begin();
- mQueue.erase(mQueue.begin());
-
- MediaBuffer *dst = new MediaBuffer(src->size());
- memcpy(dst->data(), src->data(), src->size());
-
- int64_t timeUs;
- CHECK(src->meta()->findInt64("timeUs", &timeUs));
-
- dst->meta_data()->setInt64(kKeyTime, timeUs);
-
- *out = dst;
-
- return OK;
-}
-
-void DecoderWrapper::WrapperSource::queueBuffer(const sp<ABuffer> &buffer) {
- Mutex::Autolock autoLock(mLock);
- mQueue.push_back(buffer);
- mCondition.broadcast();
-}
-
-void DecoderWrapper::WrapperSource::queueEOS(status_t finalResult) {
- CHECK_NE(finalResult, (status_t)OK);
-
- Mutex::Autolock autoLock(mLock);
- mFinalResult = finalResult;
- mCondition.broadcast();
-}
-
-void DecoderWrapper::WrapperSource::clear() {
- Mutex::Autolock autoLock(mLock);
- mQueue.clear();
- mFinalResult = OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-struct DecoderWrapper::WrapperReader : public AHandler {
- WrapperReader(
- const sp<MediaSource> &decoder,
- const sp<AMessage> ¬ify);
-
- void start();
- void stop();
- void readMore(bool flush = false);
-
-protected:
- virtual ~WrapperReader();
-
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatRead
- };
-
- sp<MediaSource> mDecoder;
- sp<AMessage> mNotify;
- bool mEOS;
- bool mSentFormat;
-
- void sendFormatChange();
-
- DISALLOW_EVIL_CONSTRUCTORS(WrapperReader);
-};
-
-DecoderWrapper::WrapperReader::WrapperReader(
- const sp<MediaSource> &decoder, const sp<AMessage> ¬ify)
- : mDecoder(decoder),
- mNotify(notify),
- mEOS(false),
- mSentFormat(false) {
-}
-
-DecoderWrapper::WrapperReader::~WrapperReader() {
-}
-
-void DecoderWrapper::WrapperReader::start() {
- CHECK_EQ(mDecoder->start(), (status_t)OK);
- readMore();
-}
-
-void DecoderWrapper::WrapperReader::stop() {
- CHECK_EQ(mDecoder->stop(), (status_t)OK);
-}
-
-void DecoderWrapper::WrapperReader::readMore(bool flush) {
- if (!flush && mEOS) {
- return;
- }
-
- sp<AMessage> msg = new AMessage(kWhatRead, id());
- msg->setInt32("flush", static_cast<int32_t>(flush));
- msg->post();
-}
-
-void DecoderWrapper::WrapperReader::onMessageReceived(
- const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatRead:
- {
- int32_t flush;
- CHECK(msg->findInt32("flush", &flush));
-
- MediaSource::ReadOptions options;
- if (flush) {
- // Dummy seek
- options.setSeekTo(0);
- mEOS = false;
- }
-
- CHECK(!mEOS);
-
- MediaBuffer *src;
- status_t err = mDecoder->read(&src, &options);
-
- if (err == OK) {
- if (!mSentFormat) {
- sendFormatChange();
- mSentFormat = true;
- }
-
- sp<AMessage> notify = mNotify->dup();
-
- sp<AMessage> realNotify;
- CHECK(notify->findMessage("real-notify", &realNotify));
-
- realNotify->setInt32("what", ACodec::kWhatDrainThisBuffer);
-
- sp<ABuffer> dst = new ABuffer(src->range_length());
- memcpy(dst->data(),
- (const uint8_t *)src->data() + src->range_offset(),
- src->range_length());
-
- int64_t timeUs;
- CHECK(src->meta_data()->findInt64(kKeyTime, &timeUs));
- src->release();
- src = NULL;
-
- dst->meta()->setInt64("timeUs", timeUs);
-
- realNotify->setObject("buffer", dst);
-
- notify->post();
- } else if (err == INFO_FORMAT_CHANGED) {
- sendFormatChange();
-
- readMore(false /* flush */);
- } else {
- sp<AMessage> notify = mNotify->dup();
-
- sp<AMessage> realNotify;
- CHECK(notify->findMessage("real-notify", &realNotify));
-
- realNotify->setInt32("what", ACodec::kWhatEOS);
- mEOS = true;
-
- notify->post();
- }
- break;
- }
-
- default:
- TRESPASS();
- break;
- }
-}
-
-void DecoderWrapper::WrapperReader::sendFormatChange() {
- sp<AMessage> notify = mNotify->dup();
-
- sp<AMessage> realNotify;
- CHECK(notify->findMessage("real-notify", &realNotify));
-
- realNotify->setInt32("what", ACodec::kWhatOutputFormatChanged);
-
- sp<MetaData> meta = mDecoder->getFormat();
-
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
-
- realNotify->setString("mime", mime);
-
- if (!strncasecmp("audio/", mime, 6)) {
- int32_t numChannels;
- CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
-
- int32_t sampleRate;
- CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
-
- realNotify->setInt32("channel-count", numChannels);
- realNotify->setInt32("sample-rate", sampleRate);
- } else {
- CHECK(!strncasecmp("video/", mime, 6));
-
- int32_t width, height;
- CHECK(meta->findInt32(kKeyWidth, &width));
- CHECK(meta->findInt32(kKeyHeight, &height));
-
- realNotify->setInt32("width", width);
- realNotify->setInt32("height", height);
-
- int32_t cropLeft, cropTop, cropRight, cropBottom;
- if (!meta->findRect(
- kKeyCropRect,
- &cropLeft, &cropTop, &cropRight, &cropBottom)) {
- cropLeft = 0;
- cropTop = 0;
- cropRight = width - 1;
- cropBottom = height - 1;
- }
-
- realNotify->setRect("crop", cropLeft, cropTop, cropRight, cropBottom);
- }
-
- notify->post();
-
- mSentFormat = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-DecoderWrapper::DecoderWrapper()
- : mNumOutstandingInputBuffers(0),
- mNumOutstandingOutputBuffers(0),
- mNumPendingDecodes(0),
- mFlushing(false) {
-}
-
-DecoderWrapper::~DecoderWrapper() {
-}
-
-void DecoderWrapper::setNotificationMessage(const sp<AMessage> &msg) {
- mNotify = msg;
-}
-
-void DecoderWrapper::initiateSetup(const sp<AMessage> &msg) {
- msg->setWhat(kWhatSetup);
- msg->setTarget(id());
- msg->post();
-}
-
-void DecoderWrapper::initiateShutdown() {
- (new AMessage(kWhatShutdown, id()))->post();
-}
-
-void DecoderWrapper::signalFlush() {
- (new AMessage(kWhatFlush, id()))->post();
-}
-
-void DecoderWrapper::signalResume() {
- (new AMessage(kWhatResume, id()))->post();
-}
-
-void DecoderWrapper::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatSetup:
- onSetup(msg);
- break;
-
- case kWhatShutdown:
- onShutdown();
- break;
-
- case kWhatInputDataRequested:
- {
- postFillBuffer();
- ++mNumOutstandingInputBuffers;
- break;
- }
-
- case kWhatInputBufferFilled:
- {
- CHECK_GT(mNumOutstandingInputBuffers, 0);
- --mNumOutstandingInputBuffers;
-
- if (mFlushing) {
- mSource->queueEOS(INFO_DISCONTINUITY);
-
- completeFlushIfPossible();
- break;
- }
-
- sp<RefBase> obj;
- if (!msg->findObject("buffer", &obj)) {
- int32_t err = OK;
- CHECK(msg->findInt32("err", &err));
-
- mSource->queueEOS(err);
- break;
- }
-
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
-
- mSource->queueBuffer(buffer);
- break;
- }
-
- case kWhatFillBufferDone:
- {
- sp<AMessage> notify;
- CHECK(msg->findMessage("real-notify", ¬ify));
-
- int32_t what;
- CHECK(notify->findInt32("what", &what));
-
- if (what == ACodec::kWhatDrainThisBuffer) {
- CHECK_GT(mNumPendingDecodes, 0);
- --mNumPendingDecodes;
-
- sp<AMessage> reply =
- new AMessage(kWhatOutputBufferDrained, id());
-
- notify->setMessage("reply", reply);
-
- ++mNumOutstandingOutputBuffers;
- } else if (what == ACodec::kWhatEOS) {
- CHECK_GT(mNumPendingDecodes, 0);
- --mNumPendingDecodes;
-
- if (mFlushing) {
- completeFlushIfPossible();
- break;
- }
- }
-
- notify->post();
- break;
- }
-
- case kWhatOutputBufferDrained:
- {
- CHECK_GT(mNumOutstandingOutputBuffers, 0);
- --mNumOutstandingOutputBuffers;
-
- if (mFlushing) {
- completeFlushIfPossible();
- break;
- }
-
- ++mNumPendingDecodes;
- mReader->readMore();
- break;
- }
-
- case kWhatFlush:
- {
- onFlush();
- break;
- }
-
- case kWhatResume:
- {
- onResume();
- break;
- }
-
- default:
- TRESPASS();
- break;
- }
-}
-
-void DecoderWrapper::onSetup(const sp<AMessage> &msg) {
- AString mime;
- CHECK(msg->findString("mime", &mime));
-
- CHECK(!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC));
-
- int32_t numChannels, sampleRate;
- CHECK(msg->findInt32("channel-count", &numChannels));
- CHECK(msg->findInt32("sample-rate", &sampleRate));
-
- sp<RefBase> obj;
- CHECK(msg->findObject("esds", &obj));
- sp<ABuffer> esds = static_cast<ABuffer *>(obj.get());
-
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, mime.c_str());
- meta->setInt32(kKeySampleRate, sampleRate);
- meta->setInt32(kKeyChannelCount, numChannels);
- meta->setData(kKeyESDS, 0, esds->data(), esds->size());
-
- mSource = new WrapperSource(
- meta, new AMessage(kWhatInputDataRequested, id()));
-
- sp<MediaSource> decoder = new AACDecoder(mSource);
-
- mReaderLooper = new ALooper;
- mReaderLooper->setName("DecoderWrapper looper");
-
- mReaderLooper->start(
- false, /* runOnCallingThread */
- false, /* canCallJava */
- PRIORITY_AUDIO);
-
- sp<AMessage> notify = new AMessage(kWhatFillBufferDone, id());
- notify->setMessage("real-notify", mNotify);
-
- mReader = new WrapperReader(decoder, notify);
- mReaderLooper->registerHandler(mReader);
-
- mReader->start();
- ++mNumPendingDecodes;
-}
-
-void DecoderWrapper::onShutdown() {
- mReaderLooper->stop();
- mReaderLooper.clear();
-
- mReader->stop();
- mReader.clear();
-
- mSource.clear();
-
- mNumOutstandingInputBuffers = 0;
- mNumOutstandingOutputBuffers = 0;
- mNumPendingDecodes = 0;
- mFlushing = false;
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", ACodec::kWhatShutdownCompleted);
- notify->post();
-}
-
-void DecoderWrapper::postFillBuffer() {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", ACodec::kWhatFillThisBuffer);
- sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
- notify->setMessage("reply", reply);
- notify->post();
-}
-
-void DecoderWrapper::onFlush() {
- CHECK(!mFlushing);
- mFlushing = true;
-
- completeFlushIfPossible();
-}
-
-void DecoderWrapper::completeFlushIfPossible() {
- CHECK(mFlushing);
-
- if (mNumOutstandingInputBuffers > 0
- || mNumOutstandingOutputBuffers > 0
- || mNumPendingDecodes > 0) {
- return;
- }
-
- mFlushing = false;
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", ACodec::kWhatFlushCompleted);
- notify->post();
-}
-
-void DecoderWrapper::onResume() {
- CHECK(!mFlushing);
-
- ++mNumPendingDecodes;
-
- mSource->clear();
- mReader->readMore(true /* flush */);
-}
-
-} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.h b/media/libmediaplayerservice/nuplayer/DecoderWrapper.h
deleted file mode 100644
index b9be12c..0000000
--- a/media/libmediaplayerservice/nuplayer/DecoderWrapper.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2010 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 DECODER_WRAPPER_H_
-
-#define DECODER_WRAPPER_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-struct MediaSource;
-
-struct DecoderWrapper : public AHandler {
- DecoderWrapper();
-
- void setNotificationMessage(const sp<AMessage> &msg);
- void initiateSetup(const sp<AMessage> &msg);
- void initiateShutdown();
- void signalFlush();
- void signalResume();
-
-protected:
- virtual ~DecoderWrapper();
-
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- struct WrapperSource;
- struct WrapperReader;
-
- enum {
- kWhatSetup,
- kWhatInputBufferFilled,
- kWhatOutputBufferDrained,
- kWhatShutdown,
- kWhatFillBufferDone,
- kWhatInputDataRequested,
- kWhatFlush,
- kWhatResume,
- };
-
- sp<AMessage> mNotify;
-
- sp<WrapperSource> mSource;
-
- sp<ALooper> mReaderLooper;
- sp<WrapperReader> mReader;
-
- int32_t mNumOutstandingInputBuffers;
- int32_t mNumOutstandingOutputBuffers;
- int32_t mNumPendingDecodes;
- bool mFlushing;
-
- void onSetup(const sp<AMessage> &msg);
- void onShutdown();
- void onFlush();
- void onResume();
-
- void postFillBuffer();
- void completeFlushIfPossible();
-
- DISALLOW_EVIL_CONSTRUCTORS(DecoderWrapper);
-};
-
-} // namespace android
-
-#endif // DECODER_WRAPPER_H_
-
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 6bf6dd3..576a850 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -33,10 +33,25 @@
namespace android {
-NuPlayer::HTTPLiveSource::HTTPLiveSource(const char *url)
+NuPlayer::HTTPLiveSource::HTTPLiveSource(
+ const char *url,
+ const KeyedVector<String8, String8> *headers)
: mURL(url),
+ mFlags(0),
mEOS(false),
mOffset(0) {
+ if (headers) {
+ mExtraHeaders = *headers;
+
+ ssize_t index =
+ mExtraHeaders.indexOfKey(String8("x-hide-urls-from-log"));
+
+ if (index >= 0) {
+ mFlags |= kFlagIncognito;
+
+ mExtraHeaders.removeItemsAt(index);
+ }
+ }
}
NuPlayer::HTTPLiveSource::~HTTPLiveSource() {
@@ -49,10 +64,13 @@
mLiveLooper->setName("http live");
mLiveLooper->start();
- mLiveSession = new LiveSession;
+ mLiveSession = new LiveSession(
+ (mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0);
+
mLiveLooper->registerHandler(mLiveSession);
- mLiveSession->connect(mURL.c_str());
+ mLiveSession->connect(
+ mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
mTSParser = new ATSParser;
}
@@ -93,10 +111,12 @@
} else {
if (buffer[0] == 0x00) {
// XXX legacy
+ sp<AMessage> extra;
mTSParser->signalDiscontinuity(
buffer[1] == 0x00
? ATSParser::DISCONTINUITY_SEEK
- : ATSParser::DISCONTINUITY_FORMATCHANGE);
+ : ATSParser::DISCONTINUITY_FORMATCHANGE,
+ extra);
} else {
mTSParser->feedTSPacket(buffer, sizeof(buffer));
}
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index f3f539a..7a337e9 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -27,7 +27,9 @@
struct LiveSession;
struct NuPlayer::HTTPLiveSource : public NuPlayer::Source {
- HTTPLiveSource(const char *url);
+ HTTPLiveSource(
+ const char *url,
+ const KeyedVector<String8, String8> *headers);
virtual void start();
@@ -45,7 +47,14 @@
virtual ~HTTPLiveSource();
private:
+ enum Flags {
+ // Don't log any URLs.
+ kFlagIncognito = 1,
+ };
+
AString mURL;
+ KeyedVector<String8, String8> mExtraHeaders;
+ uint32_t mFlags;
bool mEOS;
off64_t mOffset;
sp<ALooper> mLiveLooper;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 1fcf92b..effa703 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -37,6 +37,7 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <surfaceflinger/Surface.h>
+#include <gui/ISurfaceTexture.h>
namespace android {
@@ -71,13 +72,21 @@
const char *url, const KeyedVector<String8, String8> *headers) {
sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
- msg->setObject("source", new HTTPLiveSource(url));
+ msg->setObject("source", new HTTPLiveSource(url, headers));
msg->post();
}
void NuPlayer::setVideoSurface(const sp<Surface> &surface) {
- sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, id());
- msg->setObject("surface", surface);
+ sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, id());
+ msg->setObject("native-window", new NativeWindowWrapper(surface));
+ msg->post();
+}
+
+void NuPlayer::setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {
+ sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, id());
+ sp<SurfaceTextureClient> surfaceTextureClient(surfaceTexture != NULL ?
+ new SurfaceTextureClient(surfaceTexture) : NULL);
+ msg->setObject("native-window", new NativeWindowWrapper(surfaceTextureClient));
msg->post();
}
@@ -144,14 +153,14 @@
break;
}
- case kWhatSetVideoSurface:
+ case kWhatSetVideoNativeWindow:
{
- LOGV("kWhatSetVideoSurface");
+ LOGV("kWhatSetVideoNativeWindow");
sp<RefBase> obj;
- CHECK(msg->findObject("surface", &obj));
+ CHECK(msg->findObject("native-window", &obj));
- mSurface = static_cast<Surface *>(obj.get());
+ mNativeWindow = static_cast<NativeWindowWrapper *>(obj.get());
break;
}
@@ -172,6 +181,8 @@
mAudioEOS = false;
mVideoEOS = false;
+ mSkipRenderingAudioUntilMediaTimeUs = -1;
+ mSkipRenderingVideoUntilMediaTimeUs = -1;
mSource->start();
@@ -529,7 +540,8 @@
new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
id());
- *decoder = new Decoder(notify, audio ? NULL : mSurface);
+ *decoder = audio ? new Decoder(notify) :
+ new Decoder(notify, mNativeWindow);
looper()->registerHandler(*decoder);
(*decoder)->configure(meta);
@@ -572,6 +584,31 @@
LOGV("%s discontinuity (formatChange=%d)",
audio ? "audio" : "video", formatChange);
+ if (audio) {
+ mSkipRenderingAudioUntilMediaTimeUs = -1;
+ } else {
+ mSkipRenderingVideoUntilMediaTimeUs = -1;
+ }
+
+ sp<AMessage> extra;
+ if (accessUnit->meta()->findMessage("extra", &extra)
+ && extra != NULL) {
+ int64_t resumeAtMediaTimeUs;
+ if (extra->findInt64(
+ "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
+ LOGI("suppressing rendering of %s until %lld us",
+ audio ? "audio" : "video", resumeAtMediaTimeUs);
+
+ if (audio) {
+ mSkipRenderingAudioUntilMediaTimeUs =
+ resumeAtMediaTimeUs;
+ } else {
+ mSkipRenderingVideoUntilMediaTimeUs =
+ resumeAtMediaTimeUs;
+ }
+ }
+ }
+
flushDecoder(audio, formatChange);
}
@@ -607,6 +644,27 @@
sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ int64_t &skipUntilMediaTimeUs =
+ audio
+ ? mSkipRenderingAudioUntilMediaTimeUs
+ : mSkipRenderingVideoUntilMediaTimeUs;
+
+ if (skipUntilMediaTimeUs >= 0) {
+ int64_t mediaTimeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+ if (mediaTimeUs < skipUntilMediaTimeUs) {
+ LOGV("dropping %s buffer at time %lld as requested.",
+ audio ? "audio" : "video",
+ mediaTimeUs);
+
+ reply->post();
+ return;
+ }
+
+ skipUntilMediaTimeUs = -1;
+ }
+
mRenderer->queueBuffer(audio, buffer, reply);
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index bb65162..fb5b001 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -20,6 +20,9 @@
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/NativeWindowWrapper.h>
+#include <gui/SurfaceTextureClient.h>
+#include <surfaceflinger/Surface.h>
namespace android {
@@ -38,6 +41,7 @@
const char *url, const KeyedVector<String8, String8> *headers);
void setVideoSurface(const sp<Surface> &surface);
+ void setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture);
void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink);
void start();
@@ -65,7 +69,7 @@
enum {
kWhatSetDataSource,
- kWhatSetVideoSurface,
+ kWhatSetVideoNativeWindow,
kWhatSetAudioSink,
kWhatMoreDataQueued,
kWhatStart,
@@ -81,7 +85,7 @@
wp<NuPlayerDriver> mDriver;
sp<Source> mSource;
- sp<Surface> mSurface;
+ sp<NativeWindowWrapper> mNativeWindow;
sp<MediaPlayerBase::AudioSink> mAudioSink;
sp<Decoder> mVideoDecoder;
sp<Decoder> mAudioDecoder;
@@ -108,6 +112,9 @@
bool mResetInProgress;
bool mResetPostponed;
+ int64_t mSkipRenderingAudioUntilMediaTimeUs;
+ int64_t mSkipRenderingVideoUntilMediaTimeUs;
+
status_t instantiateDecoder(bool audio, sp<Decoder> *decoder);
status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 761dfa4..81b41ef 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -20,7 +20,6 @@
#include "NuPlayerDecoder.h"
-#include "DecoderWrapper.h"
#include "ESDS.h"
#include <media/stagefright/foundation/ABuffer.h>
@@ -31,13 +30,15 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
#include <surfaceflinger/Surface.h>
+#include <gui/ISurfaceTexture.h>
namespace android {
NuPlayer::Decoder::Decoder(
- const sp<AMessage> ¬ify, const sp<Surface> &surface)
+ const sp<AMessage> ¬ify,
+ const sp<NativeWindowWrapper> &nativeWindow)
: mNotify(notify),
- mSurface(surface) {
+ mNativeWindow(nativeWindow) {
}
NuPlayer::Decoder::~Decoder() {
@@ -45,7 +46,6 @@
void NuPlayer::Decoder::configure(const sp<MetaData> &meta) {
CHECK(mCodec == NULL);
- CHECK(mWrapper == NULL);
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
@@ -55,23 +55,15 @@
sp<AMessage> format = makeFormat(meta);
- if (mSurface != NULL) {
- format->setObject("surface", mSurface);
+ if (mNativeWindow != NULL) {
+ format->setObject("native-window", mNativeWindow);
}
- if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
- mWrapper = new DecoderWrapper;
- looper()->registerHandler(mWrapper);
+ mCodec = new ACodec;
+ looper()->registerHandler(mCodec);
- mWrapper->setNotificationMessage(notifyMsg);
- mWrapper->initiateSetup(format);
- } else {
- mCodec = new ACodec;
- looper()->registerHandler(mCodec);
-
- mCodec->setNotificationMessage(notifyMsg);
- mCodec->initiateSetup(format);
- }
+ mCodec->setNotificationMessage(notifyMsg);
+ mCodec->initiateSetup(format);
}
void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
@@ -212,7 +204,6 @@
msg->setObject("csd", buffer);
} else if (meta->findData(kKeyESDS, &type, &data, &size)) {
-#if 0
ESDS esds((const char *)data, size);
CHECK_EQ(esds.InitCheck(), (status_t)OK);
@@ -228,12 +219,6 @@
buffer->meta()->setInt32("csd", true);
mCSD.push(buffer);
-#else
- sp<ABuffer> buffer = new ABuffer(size);
- memcpy(buffer->data(), data, size);
-
- msg->setObject("esds", buffer);
-#endif
}
return msg;
@@ -268,27 +253,18 @@
void NuPlayer::Decoder::signalFlush() {
if (mCodec != NULL) {
mCodec->signalFlush();
- } else {
- CHECK(mWrapper != NULL);
- mWrapper->signalFlush();
}
}
void NuPlayer::Decoder::signalResume() {
if (mCodec != NULL) {
mCodec->signalResume();
- } else {
- CHECK(mWrapper != NULL);
- mWrapper->signalResume();
}
}
void NuPlayer::Decoder::initiateShutdown() {
if (mCodec != NULL) {
mCodec->initiateShutdown();
- } else {
- CHECK(mWrapper != NULL);
- mWrapper->initiateShutdown();
}
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 3874cfe..fabc606 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -25,10 +25,10 @@
namespace android {
struct ABuffer;
-struct DecoderWrapper;
struct NuPlayer::Decoder : public AHandler {
- Decoder(const sp<AMessage> ¬ify, const sp<Surface> &surface = NULL);
+ Decoder(const sp<AMessage> ¬ify,
+ const sp<NativeWindowWrapper> &nativeWindow = NULL);
void configure(const sp<MetaData> &meta);
@@ -47,10 +47,9 @@
};
sp<AMessage> mNotify;
- sp<Surface> mSurface;
+ sp<NativeWindowWrapper> mNativeWindow;
sp<ACodec> mCodec;
- sp<DecoderWrapper> mWrapper;
Vector<sp<ABuffer> > mCSD;
size_t mCSDIndex;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index ac19a2f..e1213f4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -86,6 +86,13 @@
return OK;
}
+status_t NuPlayerDriver::setVideoSurfaceTexture(
+ const sp<ISurfaceTexture> &surfaceTexture) {
+ mPlayer->setVideoSurfaceTexture(surfaceTexture);
+
+ return OK;
+}
+
status_t NuPlayerDriver::prepare() {
return OK;
}
@@ -239,6 +246,14 @@
mPlayer->setAudioSink(audioSink);
}
+status_t NuPlayerDriver::setParameter(int key, const Parcel &request) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::getParameter(int key, Parcel *reply) {
+ return INVALID_OPERATION;
+}
+
status_t NuPlayerDriver::getMetadata(
const media::Metadata::Filter& ids, Parcel *records) {
return INVALID_OPERATION;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index e3a5de4..145fd80 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -36,6 +36,8 @@
virtual status_t setDataSource(const sp<IStreamSource> &source);
virtual status_t setVideoSurface(const sp<Surface> &surface);
+ virtual status_t setVideoSurfaceTexture(
+ const sp<ISurfaceTexture> &surfaceTexture);
virtual status_t prepare();
virtual status_t prepareAsync();
virtual status_t start();
@@ -50,6 +52,8 @@
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 getMetadata(
const media::Metadata::Filter& ids, Parcel *records);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 369a3a8..828e008 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -198,18 +198,21 @@
}
void NuPlayer::Renderer::onDrainAudioQueue() {
- uint32_t numFramesPlayed;
- CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
- ssize_t numFramesAvailableToWrite =
- mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
+ for (;;) {
+ uint32_t numFramesPlayed;
+ CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
- CHECK_GE(numFramesAvailableToWrite, 0);
+ ssize_t numFramesAvailableToWrite =
+ mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
- size_t numBytesAvailableToWrite =
- numFramesAvailableToWrite * mAudioSink->frameSize();
+ size_t numBytesAvailableToWrite =
+ numFramesAvailableToWrite * mAudioSink->frameSize();
- while (numBytesAvailableToWrite > 0) {
+ if (numBytesAvailableToWrite == 0) {
+ break;
+ }
+
if (mAudioQueue.empty()) {
break;
}
@@ -264,10 +267,10 @@
if (entry->mOffset == entry->mBuffer->size()) {
entry->mNotifyConsumed->post();
mAudioQueue.erase(mAudioQueue.begin());
+
entry = NULL;
}
- numBytesAvailableToWrite -= copy;
mNumFramesWritten += copy / mAudioSink->frameSize();
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
index a23beb7..885ebe4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -92,9 +92,12 @@
}
}
-ssize_t NuPlayer::NuPlayerStreamListener::read(void *data, size_t size) {
+ssize_t NuPlayer::NuPlayerStreamListener::read(
+ void *data, size_t size, sp<AMessage> *extra) {
CHECK_GT(size, 0u);
+ extra->clear();
+
Mutex::Autolock autoLock(mLock);
if (mEOS) {
@@ -122,6 +125,8 @@
case DISCONTINUITY:
{
+ *extra = entry->mExtra;
+
mQueue.erase(mQueue.begin());
entry = NULL;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
index f88e945..df0935d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -37,7 +37,7 @@
Command cmd, bool synchronous, const sp<AMessage> &extra);
void start();
- ssize_t read(void *data, size_t size);
+ ssize_t read(void *data, size_t size, sp<AMessage> *extra);
private:
enum {
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index b85ac9f..2016282 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -54,7 +54,8 @@
for (int32_t i = 0; i < 10; ++i) {
char buffer[188];
- ssize_t n = mStreamListener->read(buffer, sizeof(buffer));
+ sp<AMessage> extra;
+ ssize_t n = mStreamListener->read(buffer, sizeof(buffer), &extra);
if (n == 0) {
LOGI("input data EOS reached.");
@@ -62,7 +63,8 @@
mEOS = true;
break;
} else if (n == INFO_DISCONTINUITY) {
- mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+ mTSParser->signalDiscontinuity(
+ ATSParser::DISCONTINUITY_SEEK, extra);
} else if (n < 0) {
CHECK_EQ(n, -EWOULDBLOCK);
break;
@@ -72,7 +74,8 @@
mTSParser->signalDiscontinuity(
buffer[1] == 0x00
? ATSParser::DISCONTINUITY_SEEK
- : ATSParser::DISCONTINUITY_FORMATCHANGE);
+ : ATSParser::DISCONTINUITY_FORMATCHANGE,
+ extra);
} else {
mTSParser->feedTSPacket(buffer, sizeof(buffer));
}
diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp
new file mode 100644
index 0000000..4203b6e
--- /dev/null
+++ b/media/libstagefright/AACExtractor.cpp
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2011 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 "AACExtractor"
+#include <utils/Log.h>
+
+#include "include/AACExtractor.h"
+#include "include/avc_utils.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/String8.h>
+
+namespace android {
+
+#define ADTS_HEADER_LENGTH 7
+
+class AACSource : public MediaSource {
+public:
+ AACSource(const sp<DataSource> &source,
+ const sp<MetaData> &meta,
+ const Vector<uint64_t> &offset_vector,
+ int64_t frame_duration_us);
+
+ 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);
+
+protected:
+ virtual ~AACSource();
+
+private:
+ static const size_t kMaxFrameSize;
+ sp<DataSource> mDataSource;
+ sp<MetaData> mMeta;
+
+ off64_t mOffset;
+ int64_t mCurrentTimeUs;
+ bool mStarted;
+ MediaBufferGroup *mGroup;
+
+ Vector<uint64_t> mOffsetVector;
+ int64_t mFrameDurationUs;
+
+ AACSource(const AACSource &);
+ AACSource &operator=(const AACSource &);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Returns the sample rate based on the sampling frequency index
+uint32_t get_sample_rate(const uint8_t sf_index)
+{
+ static const uint32_t sample_rates[] =
+ {
+ 96000, 88200, 64000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000, 11025, 8000
+ };
+
+ if (sf_index < sizeof(sample_rates) / sizeof(sample_rates[0])) {
+ return sample_rates[sf_index];
+ }
+
+ return 0;
+}
+
+static size_t getFrameSize(const sp<DataSource> &source, off64_t offset) {
+ size_t frameSize = 0;
+
+ uint8_t syncword[2];
+ if (source->readAt(0, &syncword, 2) != 2) {
+ return 0;
+ }
+ if ((syncword[0] != 0xff) || ((syncword[1] & 0xf6) != 0xf0)) {
+ return 0;
+ }
+
+ uint8_t protectionAbsent;
+ if (source->readAt(offset + 1, &protectionAbsent, 1) < 1) {
+ return 0;
+ }
+ protectionAbsent &= 0x1;
+
+ uint8_t header[3];
+ if (source->readAt(offset + 3, &header, 3) < 3) {
+ return 0;
+ }
+
+ frameSize = (header[0] & 0x3) << 11 | header[1] << 3 | header[2] >> 5;
+ frameSize += ADTS_HEADER_LENGTH + protectionAbsent ? 0 : 2;
+
+ return frameSize;
+}
+
+AACExtractor::AACExtractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mInitCheck(NO_INIT),
+ mFrameDurationUs(0) {
+ String8 mimeType;
+ float confidence;
+ if (!SniffAAC(mDataSource, &mimeType, &confidence, NULL)) {
+ return;
+ }
+
+ uint8_t profile, sf_index, channel, header[2];
+ if (mDataSource->readAt(2, &header, 2) < 2) {
+ return;
+ }
+
+ profile = (header[0] >> 6) & 0x3;
+ sf_index = (header[0] >> 2) & 0xf;
+ uint32_t sr = get_sample_rate(sf_index);
+ if (sr == 0) {
+ return;
+ }
+ channel = (header[0] & 0x1) << 2 | (header[1] >> 6);
+
+ mMeta = MakeAACCodecSpecificData(profile, sf_index, channel);
+
+ off64_t offset = 0;
+ off64_t streamSize, numFrames = 0;
+ size_t frameSize = 0;
+ int64_t duration = 0;
+
+ if (mDataSource->getSize(&streamSize) == OK) {
+ while (offset < streamSize) {
+ if ((frameSize = getFrameSize(source, offset)) == 0) {
+ return;
+ }
+
+ mOffsetVector.push(offset);
+
+ offset += frameSize;
+ numFrames ++;
+ }
+
+ // Round up and get the duration
+ mFrameDurationUs = (1024 * 1000000ll + (sr - 1)) / sr;
+ duration = numFrames * mFrameDurationUs;
+ mMeta->setInt64(kKeyDuration, duration);
+ }
+
+ mInitCheck = OK;
+}
+
+AACExtractor::~AACExtractor() {
+}
+
+sp<MetaData> AACExtractor::getMetaData() {
+ sp<MetaData> meta = new MetaData;
+
+ if (mInitCheck != OK) {
+ return meta;
+ }
+
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC_ADTS);
+
+ return meta;
+}
+
+size_t AACExtractor::countTracks() {
+ return mInitCheck == OK ? 1 : 0;
+}
+
+sp<MediaSource> AACExtractor::getTrack(size_t index) {
+ if (mInitCheck != OK || index != 0) {
+ return NULL;
+ }
+
+ return new AACSource(mDataSource, mMeta, mOffsetVector, mFrameDurationUs);
+}
+
+sp<MetaData> AACExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+ if (mInitCheck != OK || index != 0) {
+ return NULL;
+ }
+
+ return mMeta;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// 8192 = 2^13, 13bit AAC frame size (in bytes)
+const size_t AACSource::kMaxFrameSize = 8192;
+
+AACSource::AACSource(
+ const sp<DataSource> &source, const sp<MetaData> &meta,
+ const Vector<uint64_t> &offset_vector,
+ int64_t frame_duration_us)
+ : mDataSource(source),
+ mMeta(meta),
+ mOffset(0),
+ mCurrentTimeUs(0),
+ mStarted(false),
+ mGroup(NULL),
+ mOffsetVector(offset_vector),
+ mFrameDurationUs(frame_duration_us) {
+}
+
+AACSource::~AACSource() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t AACSource::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ mOffset = 0;
+ mCurrentTimeUs = 0;
+ mGroup = new MediaBufferGroup;
+ mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+ mStarted = true;
+
+ return OK;
+}
+
+status_t AACSource::stop() {
+ CHECK(mStarted);
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+ return OK;
+}
+
+sp<MetaData> AACSource::getFormat() {
+ return mMeta;
+}
+
+status_t AACSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ if (mFrameDurationUs > 0) {
+ int64_t seekFrame = seekTimeUs / mFrameDurationUs;
+ mCurrentTimeUs = seekFrame * mFrameDurationUs;
+
+ mOffset = mOffsetVector.itemAt(seekFrame);
+ }
+ }
+
+ size_t frameSize, frameSizeWithoutHeader;
+ if ((frameSize = getFrameSize(mDataSource, mOffset)) == 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ frameSizeWithoutHeader = frameSize - ADTS_HEADER_LENGTH;
+ if (mDataSource->readAt(mOffset + ADTS_HEADER_LENGTH, buffer->data(),
+ frameSizeWithoutHeader) != (ssize_t)frameSizeWithoutHeader) {
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_IO;
+ }
+
+ buffer->set_range(0, frameSizeWithoutHeader);
+ buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+
+ mOffset += frameSize;
+ mCurrentTimeUs += mFrameDurationUs;
+
+ *out = buffer;
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SniffAAC(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *) {
+ uint8_t header[2];
+
+ if (source->readAt(0, &header, 2) != 2) {
+ return false;
+ }
+
+ // ADTS syncword
+ if ((header[0] == 0xff) && ((header[1] & 0xf6) == 0xf0)) {
+ *mimeType = MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
+ *confidence = 0.2;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace android
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
new file mode 100644
index 0000000..8413208
--- /dev/null
+++ b/media/libstagefright/AACWriter.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2011 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 "AACWriter"
+#include <utils/Log.h>
+
+#include <media/stagefright/AACWriter.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/mediarecorder.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+
+namespace android {
+
+AACWriter::AACWriter(const char *filename)
+ : mFd(-1),
+ mInitCheck(NO_INIT),
+ mStarted(false),
+ mPaused(false),
+ mResumed(false),
+ mChannelCount(-1),
+ mSampleRate(-1) {
+
+ LOGV("AACWriter Constructor");
+
+ mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR);
+ if (mFd >= 0) {
+ mInitCheck = OK;
+ }
+}
+
+AACWriter::AACWriter(int fd)
+ : mFd(dup(fd)),
+ mInitCheck(mFd < 0? NO_INIT: OK),
+ mStarted(false),
+ mPaused(false),
+ mResumed(false),
+ mChannelCount(-1),
+ mSampleRate(-1) {
+}
+
+AACWriter::~AACWriter() {
+ if (mStarted) {
+ stop();
+ }
+
+ if (mFd != -1) {
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+status_t AACWriter::initCheck() const {
+ return mInitCheck;
+}
+
+static int writeInt8(int fd, uint8_t x) {
+ return ::write(fd, &x, 1);
+}
+
+
+status_t AACWriter::addSource(const sp<MediaSource> &source) {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mSource != NULL) {
+ LOGE("AAC files only support a single track of audio.");
+ return UNKNOWN_ERROR;
+ }
+
+ sp<MetaData> meta = source->getFormat();
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC));
+ CHECK(meta->findInt32(kKeyChannelCount, &mChannelCount));
+ CHECK(meta->findInt32(kKeySampleRate, &mSampleRate));
+ CHECK(mChannelCount >= 1 && mChannelCount <= 2);
+
+ mSource = source;
+ return OK;
+}
+
+status_t AACWriter::start(MetaData *params) {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mSource == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (mStarted && mPaused) {
+ mPaused = false;
+ mResumed = true;
+ return OK;
+ } else if (mStarted) {
+ // Already started, does nothing
+ return OK;
+ }
+
+ mFrameDurationUs = (kSamplesPerFrame * 1000000LL + (mSampleRate >> 1))
+ / mSampleRate;
+
+ status_t err = mSource->start();
+
+ if (err != OK) {
+ return err;
+ }
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ mReachedEOS = false;
+ mDone = false;
+
+ pthread_create(&mThread, &attr, ThreadWrapper, this);
+ pthread_attr_destroy(&attr);
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t AACWriter::pause() {
+ if (!mStarted) {
+ return OK;
+ }
+ mPaused = true;
+ return OK;
+}
+
+status_t AACWriter::stop() {
+ if (!mStarted) {
+ return OK;
+ }
+
+ mDone = true;
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+
+ status_t err = (status_t) dummy;
+ {
+ status_t status = mSource->stop();
+ if (err == OK &&
+ (status != OK && status != ERROR_END_OF_STREAM)) {
+ err = status;
+ }
+ }
+
+ mStarted = false;
+ return err;
+}
+
+bool AACWriter::exceedsFileSizeLimit() {
+ if (mMaxFileSizeLimitBytes == 0) {
+ return false;
+ }
+ return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes;
+}
+
+bool AACWriter::exceedsFileDurationLimit() {
+ if (mMaxFileDurationLimitUs == 0) {
+ return false;
+ }
+ return mEstimatedDurationUs >= mMaxFileDurationLimitUs;
+}
+
+// static
+void *AACWriter::ThreadWrapper(void *me) {
+ return (void *) static_cast<AACWriter *>(me)->threadFunc();
+}
+
+/*
+* Returns an index into the sample rate table if the
+* given sample rate is found; otherwise, returns -1.
+*/
+static bool getSampleRateTableIndex(int sampleRate, uint8_t* tableIndex) {
+ static const int kSampleRateTable[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000, 11025, 8000
+ };
+ const int tableSize =
+ sizeof(kSampleRateTable) / sizeof(kSampleRateTable[0]);
+
+ *tableIndex = 0;
+ for (int index = 0; index < tableSize; ++index) {
+ if (sampleRate == kSampleRateTable[index]) {
+ LOGV("Sample rate: %d and index: %d",
+ sampleRate, index);
+ *tableIndex = index;
+ return true;
+ }
+ }
+
+ LOGE("Sampling rate %d bps is not supported", sampleRate);
+ return false;
+}
+
+/*
+ * ADTS (Audio data transport stream) header structure.
+ * It consists of 7 or 9 bytes (with or without CRC):
+ * 12 bits of syncword 0xFFF, all bits must be 1
+ * 1 bit of field ID. 0 for MPEG-4, and 1 for MPEG-2
+ * 2 bits of MPEG layer. If in MPEG-TS, set to 0
+ * 1 bit of protection absense. Set to 1 if no CRC.
+ * 2 bits of profile code. Set to 1 (The MPEG-4 Audio
+ * object type minus 1. We are using AAC-LC = 2)
+ * 4 bits of sampling frequency index code (15 is not allowed)
+ * 1 bit of private stream. Set to 0.
+ * 3 bits of channel configuration code. 0 resevered for inband PCM
+ * 1 bit of originality. Set to 0.
+ * 1 bit of home. Set to 0.
+ * 1 bit of copyrighted steam. Set to 0.
+ * 1 bit of copyright start. Set to 0.
+ * 13 bits of frame length. It included 7 ot 9 bytes header length.
+ * it is set to (protection absense? 7: 9) + size(AAC frame)
+ * 11 bits of buffer fullness. 0x7FF for VBR.
+ * 2 bits of frames count in one packet. Set to 0.
+ */
+status_t AACWriter::writeAdtsHeader(uint32_t frameLength) {
+ uint8_t data = 0xFF;
+ write(mFd, &data, 1);
+
+ const uint8_t kFieldId = 0;
+ const uint8_t kMpegLayer = 0;
+ const uint8_t kProtectionAbsense = 1; // 1: kAdtsHeaderLength = 7
+ data = 0xF0;
+ data |= (kFieldId << 3);
+ data |= (kMpegLayer << 1);
+ data |= kProtectionAbsense;
+ write(mFd, &data, 1);
+
+ const uint8_t kProfileCode = 1; // AAC-LC
+ uint8_t kSampleFreqIndex;
+ CHECK(getSampleRateTableIndex(mSampleRate, &kSampleFreqIndex));
+ const uint8_t kPrivateStream = 0;
+ const uint8_t kChannelConfigCode = mChannelCount;
+ data = (kProfileCode << 6);
+ data |= (kSampleFreqIndex << 2);
+ data |= (kPrivateStream << 1);
+ data |= (kChannelConfigCode >> 2);
+ write(mFd, &data, 1);
+
+ // 4 bits from originality to copyright start
+ const uint8_t kCopyright = 0;
+ const uint32_t kFrameLength = frameLength;
+ data = ((kChannelConfigCode & 3) << 6);
+ data |= (kCopyright << 2);
+ data |= ((kFrameLength & 0x1800) >> 11);
+ write(mFd, &data, 1);
+
+ data = ((kFrameLength & 0x07F8) >> 3);
+ write(mFd, &data, 1);
+
+ const uint32_t kBufferFullness = 0x7FF; // VBR
+ data = ((kFrameLength & 0x07) << 5);
+ data |= ((kBufferFullness & 0x07C0) >> 6);
+ write(mFd, &data, 1);
+
+ const uint8_t kFrameCount = 0;
+ data = ((kBufferFullness & 0x03F) << 2);
+ data |= kFrameCount;
+ write(mFd, &data, 1);
+
+ return OK;
+}
+
+status_t AACWriter::threadFunc() {
+ mEstimatedDurationUs = 0;
+ mEstimatedSizeBytes = 0;
+ int64_t previousPausedDurationUs = 0;
+ int64_t maxTimestampUs = 0;
+ status_t err = OK;
+
+ prctl(PR_SET_NAME, (unsigned long)"AACWriterThread", 0, 0, 0);
+
+ while (!mDone && err == OK) {
+ MediaBuffer *buffer;
+ err = mSource->read(&buffer);
+
+ if (err != OK) {
+ break;
+ }
+
+ if (mPaused) {
+ buffer->release();
+ buffer = NULL;
+ continue;
+ }
+
+ mEstimatedSizeBytes += kAdtsHeaderLength + buffer->range_length();
+ if (exceedsFileSizeLimit()) {
+ buffer->release();
+ buffer = NULL;
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
+ break;
+ }
+
+ int32_t isCodecSpecific = 0;
+ if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecSpecific) && isCodecSpecific) {
+ LOGV("Drop codec specific info buffer");
+ buffer->release();
+ buffer = NULL;
+ continue;
+ }
+
+ int64_t timestampUs;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs));
+ if (timestampUs > mEstimatedDurationUs) {
+ mEstimatedDurationUs = timestampUs;
+ }
+ if (mResumed) {
+ previousPausedDurationUs += (timestampUs - maxTimestampUs - mFrameDurationUs);
+ mResumed = false;
+ }
+ timestampUs -= previousPausedDurationUs;
+ LOGV("time stamp: %lld, previous paused duration: %lld",
+ timestampUs, previousPausedDurationUs);
+ if (timestampUs > maxTimestampUs) {
+ maxTimestampUs = timestampUs;
+ }
+
+ if (exceedsFileDurationLimit()) {
+ buffer->release();
+ buffer = NULL;
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
+ break;
+ }
+
+ // Each output AAC audio frame to the file contains
+ // 1. an ADTS header, followed by
+ // 2. the compressed audio data.
+ ssize_t dataLength = buffer->range_length();
+ uint8_t *data = (uint8_t *)buffer->data() + buffer->range_offset();
+ if (writeAdtsHeader(kAdtsHeaderLength + dataLength) != OK ||
+ dataLength != write(mFd, data, dataLength)) {
+ err = ERROR_IO;
+ }
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ close(mFd);
+ mFd = -1;
+ mReachedEOS = true;
+ if (err == ERROR_END_OF_STREAM) {
+ return OK;
+ }
+ return err;
+}
+
+bool AACWriter::reachedEOS() {
+ return mReachedEOS;
+}
+
+} // namespace android
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index dfb4e00..4189354 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 "ACodec"
@@ -11,9 +27,11 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/NativeWindowWrapper.h>
#include <media/stagefright/OMXClient.h>
#include <surfaceflinger/Surface.h>
+#include <gui/SurfaceTextureClient.h>
#include <OMX_Component.h>
@@ -197,6 +215,9 @@
// to fill with data.
void resume();
+ // Returns true iff input and output buffers are in play.
+ bool active() const { return mActive; }
+
protected:
virtual PortMode getPortMode(OMX_U32 portIndex);
virtual bool onMessageReceived(const sp<AMessage> &msg);
@@ -205,6 +226,8 @@
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
private:
+ bool mActive;
+
DISALLOW_EVIL_CONSTRUCTORS(ExecutingState);
};
@@ -422,27 +445,51 @@
return err;
}
- // Increase the buffer count by one to allow for the ANativeWindow to hold
- // on to one of the buffers.
- def.nBufferCountActual++;
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
- if (err != OK) {
- return err;
+ // Set up the native window.
+ OMX_U32 usage = 0;
+ err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
+ if (err != 0) {
+ LOGW("querying usage flags from OMX IL component failed: %d", err);
+ // XXX: Currently this error is logged, but not fatal.
+ usage = 0;
}
- // Set up the native window.
- // XXX TODO: Get the gralloc usage flags from the OMX plugin!
err = native_window_set_usage(
mNativeWindow.get(),
- GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
+ usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
if (err != 0) {
LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
return err;
}
+ int minUndequeuedBufs = 0;
+ err = mNativeWindow->query(
+ mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBufs);
+
+ if (err != 0) {
+ LOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+
+ // XXX: Is this the right logic to use? It's not clear to me what the OMX
+ // buffer counts refer to - how do they account for the renderer holding on
+ // to buffers?
+ if (def.nBufferCountActual < def.nBufferCountMin + minUndequeuedBufs) {
+ OMX_U32 newBufferCount = def.nBufferCountMin + minUndequeuedBufs;
+ def.nBufferCountActual = newBufferCount;
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ LOGE("[%s] setting nBufferCountActual to %lu failed: %d",
+ mComponentName.c_str(), newBufferCount, err);
+ return err;
+ }
+ }
+
err = native_window_set_buffer_count(
mNativeWindow.get(), def.nBufferCountActual);
@@ -452,17 +499,13 @@
return err;
}
- // XXX TODO: Do something so the ANativeWindow knows that we'll need to get
- // the same set of buffers.
-
LOGV("[%s] Allocating %lu buffers from a native window of size %lu on "
"output port",
mComponentName.c_str(), def.nBufferCountActual, def.nBufferSize);
// Dequeue buffers and send them to OMX
- OMX_U32 i;
- for (i = 0; i < def.nBufferCountActual; i++) {
- android_native_buffer_t *buf;
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) {
+ ANativeWindowBuffer *buf;
err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
if (err != 0) {
LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
@@ -470,23 +513,26 @@
}
sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
- IOMX::buffer_id bufferId;
- err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
- &bufferId);
- if (err != 0) {
- break;
- }
-
- LOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)",
- mComponentName.c_str(),
- bufferId, graphicBuffer.get());
-
BufferInfo info;
- info.mBufferID = bufferId;
info.mStatus = BufferInfo::OWNED_BY_US;
info.mData = new ABuffer(0);
info.mGraphicBuffer = graphicBuffer;
mBuffers[kPortIndexOutput].push(info);
+
+ IOMX::buffer_id bufferId;
+ err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
+ &bufferId);
+ if (err != 0) {
+ LOGE("registering GraphicBuffer %lu with OMX IL component failed: "
+ "%d", i, err);
+ break;
+ }
+
+ mBuffers[kPortIndexOutput].editItemAt(i).mBufferID = bufferId;
+
+ LOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)",
+ mComponentName.c_str(),
+ bufferId, graphicBuffer.get());
}
OMX_U32 cancelStart;
@@ -496,14 +542,10 @@
// If an error occurred while dequeuing we need to cancel any buffers
// that were dequeued.
cancelStart = 0;
- cancelEnd = i;
+ cancelEnd = mBuffers[kPortIndexOutput].size();
} else {
// Return the last two buffers to the native window.
- // XXX TODO: The number of buffers the native window owns should
- // probably be queried from it when we put the native window in
- // fixed buffer pool mode (which needs to be implemented).
- // Currently it's hard-coded to 2.
- cancelStart = def.nBufferCountActual - 2;
+ cancelStart = def.nBufferCountActual - minUndequeuedBufs;
cancelEnd = def.nBufferCountActual;
}
@@ -532,7 +574,7 @@
}
ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
- android_native_buffer_t *buf;
+ ANativeWindowBuffer *buf;
CHECK_EQ(mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf), 0);
for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
@@ -564,13 +606,17 @@
return OK;
}
-status_t ACodec::freeOutputBuffersOwnedByNativeWindow() {
+status_t ACodec::freeOutputBuffersNotOwnedByComponent() {
for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
BufferInfo *info =
&mBuffers[kPortIndexOutput].editItemAt(i);
- if (info->mStatus ==
- BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+ if (info->mStatus !=
+ BufferInfo::OWNED_BY_COMPONENT) {
+ // We shouldn't have sent out any buffers to the client at this
+ // point.
+ CHECK_NE((int)info->mStatus, (int)BufferInfo::OWNED_BY_DOWNSTREAM);
+
CHECK_EQ((status_t)OK, freeBuffer(kPortIndexOutput, i));
}
}
@@ -1035,8 +1081,8 @@
android_native_rect_t crop;
crop.left = rect.nLeft;
crop.top = rect.nTop;
- crop.right = rect.nLeft + rect.nWidth - 1;
- crop.bottom = rect.nTop + rect.nHeight - 1;
+ crop.right = rect.nLeft + rect.nWidth;
+ crop.bottom = rect.nTop + rect.nHeight;
CHECK_EQ(0, native_window_set_crop(
mNativeWindow.get(), &crop));
@@ -1195,6 +1241,9 @@
}
bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) {
+ LOGV("[%s] onOMXEmptyBufferDone %p",
+ mCodec->mComponentName.c_str(), bufferID);
+
BufferInfo *info =
mCodec->findBufferByID(kPortIndexInput, bufferID);
@@ -1295,7 +1344,7 @@
}
if (buffer != info->mData) {
- if (!(flags & OMX_BUFFERFLAG_CODECCONFIG)) {
+ if (0 && !(flags & OMX_BUFFERFLAG_CODECCONFIG)) {
LOGV("[%s] Needs to copy input data.",
mCodec->mComponentName.c_str());
}
@@ -1304,6 +1353,9 @@
memcpy(info->mData->data(), buffer->data(), buffer->size());
}
+ LOGV("[%s] calling emptyBuffer %p",
+ mCodec->mComponentName.c_str(), bufferID);
+
CHECK_EQ(mCodec->mOMX->emptyBuffer(
mCodec->mNode,
bufferID,
@@ -1320,6 +1372,9 @@
LOGV("[%s] Signalling EOS on the input port",
mCodec->mComponentName.c_str());
+ LOGV("[%s] calling emptyBuffer %p",
+ mCodec->mComponentName.c_str(), bufferID);
+
CHECK_EQ(mCodec->mOMX->emptyBuffer(
mCodec->mNode,
bufferID,
@@ -1378,6 +1433,9 @@
int64_t timeUs,
void *platformPrivate,
void *dataPtr) {
+ LOGV("[%s] onOMXFillBufferDone %p",
+ mCodec->mComponentName.c_str(), bufferID);
+
ssize_t index;
BufferInfo *info =
mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
@@ -1396,6 +1454,9 @@
{
if (rangeLength == 0) {
if (!(flags & OMX_BUFFERFLAG_EOS)) {
+ LOGV("[%s] calling fillBuffer %p",
+ mCodec->mComponentName.c_str(), info->mBufferID);
+
CHECK_EQ(mCodec->mOMX->fillBuffer(
mCodec->mNode, info->mBufferID),
(status_t)OK);
@@ -1503,6 +1564,9 @@
info = mCodec->dequeueBufferFromNativeWindow();
}
+ LOGV("[%s] calling fillBuffer %p",
+ mCodec->mComponentName.c_str(), info->mBufferID);
+
CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
(status_t)OK);
@@ -1580,7 +1644,7 @@
if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
componentName = "OMX.Nvidia.h264.decode";
} else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
- componentName = "OMX.Nvidia.aac.decoder";
+ componentName = "OMX.google.aac.decoder";
} else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) {
componentName = "OMX.Nvidia.mp3.decoder";
} else {
@@ -1600,11 +1664,17 @@
mCodec->mOMX = omx;
mCodec->mNode = node;
+ mCodec->mPortEOS[kPortIndexInput] =
+ mCodec->mPortEOS[kPortIndexOutput] = false;
+
mCodec->configureCodec(mime.c_str(), msg);
sp<RefBase> obj;
- if (msg->findObject("surface", &obj)) {
- mCodec->mNativeWindow = static_cast<Surface *>(obj.get());
+ if (msg->findObject("native-window", &obj)) {
+ sp<NativeWindowWrapper> nativeWindow(
+ static_cast<NativeWindowWrapper *>(obj.get()));
+ CHECK(nativeWindow != NULL);
+ mCodec->mNativeWindow = nativeWindow->getNativeWindow();
}
CHECK_EQ((status_t)OK, mCodec->initNativeWindow());
@@ -1717,7 +1787,8 @@
////////////////////////////////////////////////////////////////////////////////
ACodec::ExecutingState::ExecutingState(ACodec *codec)
- : BaseState(codec) {
+ : BaseState(codec),
+ mActive(false) {
}
ACodec::BaseState::PortMode ACodec::ExecutingState::getPortMode(
@@ -1745,6 +1816,9 @@
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
}
+ LOGV("[%s] calling fillBuffer %p",
+ mCodec->mComponentName.c_str(), info->mBufferID);
+
CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
(status_t)OK);
@@ -1753,6 +1827,13 @@
}
void ACodec::ExecutingState::resume() {
+ if (mActive) {
+ LOGV("[%s] We're already active, no need to resume.",
+ mCodec->mComponentName.c_str());
+
+ return;
+ }
+
submitOutputBuffers();
// Post the first input buffer.
@@ -1760,6 +1841,8 @@
BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(0);
postFillThisBuffer(info);
+
+ mActive = true;
}
void ACodec::ExecutingState::stateEntered() {
@@ -1774,6 +1857,8 @@
switch (msg->what()) {
case kWhatShutdown:
{
+ mActive = false;
+
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),
(status_t)OK);
@@ -1786,6 +1871,8 @@
case kWhatFlush:
{
+ mActive = false;
+
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandFlush, OMX_ALL),
(status_t)OK);
@@ -1825,10 +1912,7 @@
OMX_CommandPortDisable, kPortIndexOutput),
(status_t)OK);
- if (mCodec->mNativeWindow != NULL) {
- CHECK_EQ((status_t)OK,
- mCodec->freeOutputBuffersOwnedByNativeWindow());
- }
+ mCodec->freeOutputBuffersNotOwnedByComponent();
mCodec->changeState(mCodec->mOutputPortSettingsChangedState);
} else if (data2 == OMX_IndexConfigCommonOutputCrop) {
@@ -1876,7 +1960,12 @@
switch (msg->what()) {
case kWhatFlush:
case kWhatShutdown:
+ case kWhatResume:
{
+ if (msg->what() == kWhatResume) {
+ LOGV("[%s] Deferring resume", mCodec->mComponentName.c_str());
+ }
+
mCodec->deferMessage(msg);
handled = true;
break;
@@ -1925,7 +2014,10 @@
LOGV("[%s] Output port now reenabled.",
mCodec->mComponentName.c_str());
- mCodec->mExecutingState->submitOutputBuffers();
+ if (mCodec->mExecutingState->active()) {
+ mCodec->mExecutingState->submitOutputBuffers();
+ }
+
mCodec->changeState(mCodec->mExecutingState);
return true;
@@ -1992,6 +2084,13 @@
return true;
}
+ case OMX_EventPortSettingsChanged:
+ case OMX_EventBufferFlag:
+ {
+ // We're shutting down and don't care about this anymore.
+ return true;
+ }
+
default:
return BaseState::onOMXEvent(event, data1, data2);
}
@@ -2170,6 +2269,23 @@
return true;
}
+ case OMX_EventPortSettingsChanged:
+ {
+ sp<AMessage> msg = new AMessage(kWhatOMXMessage, mCodec->id());
+ msg->setInt32("type", omx_message::EVENT);
+ msg->setPointer("node", mCodec->mNode);
+ msg->setInt32("event", event);
+ msg->setInt32("data1", data1);
+ msg->setInt32("data2", data2);
+
+ LOGV("[%s] Deferring OMX_EventPortSettingsChanged",
+ mCodec->mComponentName.c_str());
+
+ mCodec->deferMessage(msg);
+
+ return true;
+ }
+
default:
return BaseState::onOMXEvent(event, data1, data2);
}
@@ -2205,4 +2321,3 @@
}
} // namespace android
-
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index ac87c29..7eca5e4 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -35,8 +35,9 @@
public:
AMRSource(const sp<DataSource> &source,
const sp<MetaData> &meta,
- size_t frameSize,
- bool isWide);
+ bool isWide,
+ const off64_t *offset_table,
+ size_t offset_table_length);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -52,7 +53,6 @@
private:
sp<DataSource> mDataSource;
sp<MetaData> mMeta;
- size_t mFrameSize;
bool mIsWide;
off64_t mOffset;
@@ -60,6 +60,9 @@
bool mStarted;
MediaBufferGroup *mGroup;
+ off64_t mOffsetTable[OFFSET_TABLE_LEN];
+ size_t mOffsetTableLength;
+
AMRSource(const AMRSource &);
AMRSource &operator=(const AMRSource &);
};
@@ -67,13 +70,25 @@
////////////////////////////////////////////////////////////////////////////////
static size_t getFrameSize(bool isWide, unsigned FT) {
- static const size_t kFrameSizeNB[8] = {
- 95, 103, 118, 134, 148, 159, 204, 244
+ static const size_t kFrameSizeNB[16] = {
+ 95, 103, 118, 134, 148, 159, 204, 244,
+ 39, 43, 38, 37, // SID
+ 0, 0, 0, // future use
+ 0 // no data
};
- static const size_t kFrameSizeWB[9] = {
- 132, 177, 253, 285, 317, 365, 397, 461, 477
+ static const size_t kFrameSizeWB[16] = {
+ 132, 177, 253, 285, 317, 365, 397, 461, 477,
+ 40, // SID
+ 0, 0, 0, 0, // future use
+ 0, // speech lost
+ 0 // no data
};
+ if (FT > 15 || (isWide && FT > 9 && FT < 14) || (!isWide && FT > 11 && FT < 15)) {
+ LOGE("illegal AMR frame type %d", FT);
+ return 0;
+ }
+
size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
// Round up bits to bytes and add 1 for the header byte.
@@ -82,9 +97,26 @@
return frameSize;
}
+static status_t getFrameSizeByOffset(const sp<DataSource> &source,
+ off64_t offset, bool isWide, size_t *frameSize) {
+ uint8_t header;
+ if (source->readAt(offset, &header, 1) < 1) {
+ return ERROR_IO;
+ }
+
+ unsigned FT = (header >> 3) & 0x0f;
+
+ *frameSize = getFrameSize(isWide, FT);
+ if (*frameSize == 0) {
+ return ERROR_MALFORMED;
+ }
+ return OK;
+}
+
AMRExtractor::AMRExtractor(const sp<DataSource> &source)
: mDataSource(source),
- mInitCheck(NO_INIT) {
+ mInitCheck(NO_INIT),
+ mOffsetTableLength(0) {
String8 mimeType;
float confidence;
if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) {
@@ -101,25 +133,29 @@
mMeta->setInt32(kKeyChannelCount, 1);
mMeta->setInt32(kKeySampleRate, mIsWide ? 16000 : 8000);
- size_t offset = mIsWide ? 9 : 6;
- uint8_t header;
- if (mDataSource->readAt(offset, &header, 1) != 1) {
- return;
- }
-
- unsigned FT = (header >> 3) & 0x0f;
-
- if (FT > 8 || (!mIsWide && FT > 7)) {
- return;
- }
-
- mFrameSize = getFrameSize(mIsWide, FT);
-
+ off64_t offset = mIsWide ? 9 : 6;
off64_t streamSize;
- if (mDataSource->getSize(&streamSize) == OK) {
- off64_t numFrames = streamSize / mFrameSize;
+ size_t frameSize, numFrames = 0;
+ int64_t duration = 0;
- mMeta->setInt64(kKeyDuration, 20000ll * numFrames);
+ if (mDataSource->getSize(&streamSize) == OK) {
+ while (offset < streamSize) {
+ if (getFrameSizeByOffset(source, offset, mIsWide, &frameSize) != OK) {
+ return;
+ }
+
+ if ((numFrames % 50 == 0) && (numFrames / 50 < OFFSET_TABLE_LEN)) {
+ CHECK_EQ(mOffsetTableLength, numFrames / 50);
+ mOffsetTable[mOffsetTableLength] = offset - (mIsWide ? 9: 6);
+ mOffsetTableLength ++;
+ }
+
+ offset += frameSize;
+ duration += 20000; // Each frame is 20ms
+ numFrames ++;
+ }
+
+ mMeta->setInt64(kKeyDuration, duration);
}
mInitCheck = OK;
@@ -149,7 +185,8 @@
return NULL;
}
- return new AMRSource(mDataSource, mMeta, mFrameSize, mIsWide);
+ return new AMRSource(mDataSource, mMeta, mIsWide,
+ mOffsetTable, mOffsetTableLength);
}
sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) {
@@ -164,15 +201,18 @@
AMRSource::AMRSource(
const sp<DataSource> &source, const sp<MetaData> &meta,
- size_t frameSize, bool isWide)
+ bool isWide, const off64_t *offset_table, size_t offset_table_length)
: mDataSource(source),
mMeta(meta),
- mFrameSize(frameSize),
mIsWide(isWide),
mOffset(mIsWide ? 9 : 6),
mCurrentTimeUs(0),
mStarted(false),
- mGroup(NULL) {
+ mGroup(NULL),
+ mOffsetTableLength(offset_table_length) {
+ if (mOffsetTableLength > 0 && mOffsetTableLength <= OFFSET_TABLE_LEN) {
+ memcpy ((char*)mOffsetTable, (char*)offset_table, sizeof(off64_t) * mOffsetTableLength);
+ }
}
AMRSource::~AMRSource() {
@@ -214,9 +254,25 @@
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ size_t size;
int64_t seekFrame = seekTimeUs / 20000ll; // 20ms per frame.
mCurrentTimeUs = seekFrame * 20000ll;
- mOffset = seekFrame * mFrameSize + (mIsWide ? 9 : 6);
+
+ int index = seekFrame / 50;
+ if (index >= mOffsetTableLength) {
+ index = mOffsetTableLength - 1;
+ }
+
+ mOffset = mOffsetTable[index] + (mIsWide ? 9 : 6);
+
+ for (int i = 0; i< seekFrame - index * 50; i++) {
+ status_t err;
+ if ((err = getFrameSizeByOffset(mDataSource, mOffset,
+ mIsWide, &size)) != OK) {
+ return err;
+ }
+ mOffset += size;
+ }
}
uint8_t header;
@@ -236,16 +292,11 @@
unsigned FT = (header >> 3) & 0x0f;
- if (FT > 8 || (!mIsWide && FT > 7)) {
-
- LOGE("illegal AMR frame type %d", FT);
-
+ size_t frameSize = getFrameSize(mIsWide, FT);
+ if (frameSize == 0) {
return ERROR_MALFORMED;
}
- size_t frameSize = getFrameSize(mIsWide, FT);
- CHECK_EQ(frameSize, mFrameSize);
-
MediaBuffer *buffer;
status_t err = mGroup->acquire_buffer(&buffer);
if (err != OK) {
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 0db3d1d..b10d52c 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -37,7 +37,7 @@
mPaused(false),
mResumed(false) {
- mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC);
+ mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR);
if (mFd >= 0) {
mInitCheck = OK;
}
@@ -269,7 +269,7 @@
}
if (stoppedPrematurely) {
- notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, UNKNOWN_ERROR);
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS, UNKNOWN_ERROR);
}
close(mFd);
diff --git a/media/libstagefright/AVIExtractor.cpp b/media/libstagefright/AVIExtractor.cpp
new file mode 100644
index 0000000..6313ca3
--- /dev/null
+++ b/media/libstagefright/AVIExtractor.cpp
@@ -0,0 +1,922 @@
+/*
+ * Copyright (C) 2011 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 "AVIExtractor"
+#include <utils/Log.h>
+
+#include "include/AVIExtractor.h"
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+struct AVIExtractor::AVISource : public MediaSource {
+ AVISource(const sp<AVIExtractor> &extractor, size_t trackIndex);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+protected:
+ virtual ~AVISource();
+
+private:
+ sp<AVIExtractor> mExtractor;
+ size_t mTrackIndex;
+ const AVIExtractor::Track &mTrack;
+ MediaBufferGroup *mBufferGroup;
+ size_t mSampleIndex;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AVISource);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+AVIExtractor::AVISource::AVISource(
+ const sp<AVIExtractor> &extractor, size_t trackIndex)
+ : mExtractor(extractor),
+ mTrackIndex(trackIndex),
+ mTrack(mExtractor->mTracks.itemAt(trackIndex)),
+ mBufferGroup(NULL) {
+}
+
+AVIExtractor::AVISource::~AVISource() {
+ if (mBufferGroup) {
+ stop();
+ }
+}
+
+status_t AVIExtractor::AVISource::start(MetaData *params) {
+ CHECK(!mBufferGroup);
+
+ mBufferGroup = new MediaBufferGroup;
+
+ mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize));
+ mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize));
+ mSampleIndex = 0;
+
+ return OK;
+}
+
+status_t AVIExtractor::AVISource::stop() {
+ CHECK(mBufferGroup);
+
+ delete mBufferGroup;
+ mBufferGroup = NULL;
+
+ return OK;
+}
+
+sp<MetaData> AVIExtractor::AVISource::getFormat() {
+ return mTrack.mMeta;
+}
+
+status_t AVIExtractor::AVISource::read(
+ MediaBuffer **buffer, const ReadOptions *options) {
+ CHECK(mBufferGroup);
+
+ *buffer = NULL;
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode seekMode;
+ if (options && options->getSeekTo(&seekTimeUs, &seekMode)) {
+ status_t err =
+ mExtractor->getSampleIndexAtTime(
+ mTrackIndex, seekTimeUs, seekMode, &mSampleIndex);
+
+ if (err != OK) {
+ return ERROR_END_OF_STREAM;
+ }
+ }
+
+ int64_t timeUs =
+ (mSampleIndex * 1000000ll * mTrack.mRate) / mTrack.mScale;
+
+ off64_t offset;
+ size_t size;
+ bool isKey;
+ status_t err = mExtractor->getSampleInfo(
+ mTrackIndex, mSampleIndex, &offset, &size, &isKey);
+
+ ++mSampleIndex;
+
+ if (err != OK) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ MediaBuffer *out;
+ CHECK_EQ(mBufferGroup->acquire_buffer(&out), (status_t)OK);
+
+ ssize_t n = mExtractor->mDataSource->readAt(offset, out->data(), size);
+
+ if (n < (ssize_t)size) {
+ return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED;
+ }
+
+ out->set_range(0, size);
+
+ out->meta_data()->setInt64(kKeyTime, timeUs);
+
+ if (isKey) {
+ out->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ }
+
+ *buffer = out;
+
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+AVIExtractor::AVIExtractor(const sp<DataSource> &dataSource)
+ : mDataSource(dataSource) {
+ mInitCheck = parseHeaders();
+
+ if (mInitCheck != OK) {
+ mTracks.clear();
+ }
+}
+
+AVIExtractor::~AVIExtractor() {
+}
+
+size_t AVIExtractor::countTracks() {
+ return mTracks.size();
+}
+
+sp<MediaSource> AVIExtractor::getTrack(size_t index) {
+ return index < mTracks.size() ? new AVISource(this, index) : NULL;
+}
+
+sp<MetaData> AVIExtractor::getTrackMetaData(
+ size_t index, uint32_t flags) {
+ return index < mTracks.size() ? mTracks.editItemAt(index).mMeta : NULL;
+}
+
+sp<MetaData> AVIExtractor::getMetaData() {
+ sp<MetaData> meta = new MetaData;
+
+ if (mInitCheck == OK) {
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_AVI);
+ }
+
+ return meta;
+}
+
+status_t AVIExtractor::parseHeaders() {
+ mTracks.clear();
+ mMovieOffset = 0;
+ mFoundIndex = false;
+ mOffsetsAreAbsolute = false;
+
+ ssize_t res = parseChunk(0ll, -1ll);
+
+ if (res < 0) {
+ return (status_t)res;
+ }
+
+ if (mMovieOffset == 0ll || !mFoundIndex) {
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+}
+
+ssize_t AVIExtractor::parseChunk(off64_t offset, off64_t size, int depth) {
+ if (size >= 0 && size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t tmp[12];
+ ssize_t n = mDataSource->readAt(offset, tmp, 8);
+
+ if (n < 8) {
+ return (n < 0) ? n : (ssize_t)ERROR_MALFORMED;
+ }
+
+ uint32_t fourcc = U32_AT(tmp);
+ uint32_t chunkSize = U32LE_AT(&tmp[4]);
+
+ if (size >= 0 && chunkSize + 8 > size) {
+ return ERROR_MALFORMED;
+ }
+
+ static const char kPrefix[] = " ";
+ const char *prefix = &kPrefix[strlen(kPrefix) - 2 * depth];
+
+ if (fourcc == FOURCC('L', 'I', 'S', 'T')
+ || fourcc == FOURCC('R', 'I', 'F', 'F')) {
+ // It's a list of chunks
+
+ if (size >= 0 && size < 12) {
+ return ERROR_MALFORMED;
+ }
+
+ n = mDataSource->readAt(offset + 8, &tmp[8], 4);
+
+ if (n < 4) {
+ return (n < 0) ? n : (ssize_t)ERROR_MALFORMED;
+ }
+
+ uint32_t subFourcc = U32_AT(&tmp[8]);
+
+ LOGV("%s offset 0x%08llx LIST of '%c%c%c%c', size %d",
+ prefix,
+ offset,
+ (char)(subFourcc >> 24),
+ (char)((subFourcc >> 16) & 0xff),
+ (char)((subFourcc >> 8) & 0xff),
+ (char)(subFourcc & 0xff),
+ chunkSize - 4);
+
+ if (subFourcc == FOURCC('m', 'o', 'v', 'i')) {
+ // We're not going to parse this, but will take note of the
+ // offset.
+
+ mMovieOffset = offset;
+ } else {
+ off64_t subOffset = offset + 12;
+ off64_t subOffsetLimit = subOffset + chunkSize - 4;
+ while (subOffset < subOffsetLimit) {
+ ssize_t res =
+ parseChunk(subOffset, subOffsetLimit - subOffset, depth + 1);
+
+ if (res < 0) {
+ return res;
+ }
+
+ subOffset += res;
+ }
+ }
+ } else {
+ LOGV("%s offset 0x%08llx CHUNK '%c%c%c%c'",
+ prefix,
+ offset,
+ (char)(fourcc >> 24),
+ (char)((fourcc >> 16) & 0xff),
+ (char)((fourcc >> 8) & 0xff),
+ (char)(fourcc & 0xff));
+
+ status_t err = OK;
+
+ switch (fourcc) {
+ case FOURCC('s', 't', 'r', 'h'):
+ {
+ err = parseStreamHeader(offset + 8, chunkSize);
+ break;
+ }
+
+ case FOURCC('s', 't', 'r', 'f'):
+ {
+ err = parseStreamFormat(offset + 8, chunkSize);
+ break;
+ }
+
+ case FOURCC('i', 'd', 'x', '1'):
+ {
+ err = parseIndex(offset + 8, chunkSize);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (chunkSize & 1) {
+ ++chunkSize;
+ }
+
+ return chunkSize + 8;
+}
+
+static const char *GetMIMETypeForHandler(uint32_t handler) {
+ switch (handler) {
+ // Wow... shamelessly copied from
+ // http://wiki.multimedia.cx/index.php?title=ISO_MPEG-4
+
+ case FOURCC('3', 'I', 'V', '2'):
+ case FOURCC('3', 'i', 'v', '2'):
+ case FOURCC('B', 'L', 'Z', '0'):
+ case FOURCC('D', 'I', 'G', 'I'):
+ case FOURCC('D', 'I', 'V', '1'):
+ case FOURCC('d', 'i', 'v', '1'):
+ case FOURCC('D', 'I', 'V', 'X'):
+ case FOURCC('d', 'i', 'v', 'x'):
+ case FOURCC('D', 'X', '5', '0'):
+ case FOURCC('d', 'x', '5', '0'):
+ case FOURCC('D', 'X', 'G', 'M'):
+ case FOURCC('E', 'M', '4', 'A'):
+ case FOURCC('E', 'P', 'H', 'V'):
+ case FOURCC('F', 'M', 'P', '4'):
+ case FOURCC('f', 'm', 'p', '4'):
+ case FOURCC('F', 'V', 'F', 'W'):
+ case FOURCC('H', 'D', 'X', '4'):
+ case FOURCC('h', 'd', 'x', '4'):
+ case FOURCC('M', '4', 'C', 'C'):
+ case FOURCC('M', '4', 'S', '2'):
+ case FOURCC('m', '4', 's', '2'):
+ case FOURCC('M', 'P', '4', 'S'):
+ case FOURCC('m', 'p', '4', 's'):
+ case FOURCC('M', 'P', '4', 'V'):
+ case FOURCC('m', 'p', '4', 'v'):
+ case FOURCC('M', 'V', 'X', 'M'):
+ case FOURCC('R', 'M', 'P', '4'):
+ case FOURCC('S', 'E', 'D', 'G'):
+ case FOURCC('S', 'M', 'P', '4'):
+ case FOURCC('U', 'M', 'P', '4'):
+ case FOURCC('W', 'V', '1', 'F'):
+ case FOURCC('X', 'V', 'I', 'D'):
+ case FOURCC('X', 'v', 'i', 'D'):
+ case FOURCC('x', 'v', 'i', 'd'):
+ case FOURCC('X', 'V', 'I', 'X'):
+ return MEDIA_MIMETYPE_VIDEO_MPEG4;
+
+ default:
+ return NULL;
+ }
+}
+
+status_t AVIExtractor::parseStreamHeader(off64_t offset, size_t size) {
+ if (size != 56) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mTracks.size() > 99) {
+ return -ERANGE;
+ }
+
+ sp<ABuffer> buffer = new ABuffer(size);
+ ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size());
+
+ if (n < (ssize_t)size) {
+ return n < 0 ? (status_t)n : ERROR_MALFORMED;
+ }
+
+ const uint8_t *data = buffer->data();
+
+ uint32_t type = U32_AT(data);
+ uint32_t handler = U32_AT(&data[4]);
+ uint32_t flags = U32LE_AT(&data[8]);
+
+ sp<MetaData> meta = new MetaData;
+
+ uint32_t rate = U32LE_AT(&data[20]);
+ uint32_t scale = U32LE_AT(&data[24]);
+
+ const char *mime = NULL;
+ Track::Kind kind = Track::OTHER;
+
+ if (type == FOURCC('v', 'i', 'd', 's')) {
+ mime = GetMIMETypeForHandler(handler);
+
+ if (mime && strncasecmp(mime, "video/", 6)) {
+ return ERROR_MALFORMED;
+ }
+
+ kind = Track::VIDEO;
+ } else if (type == FOURCC('a', 'u', 'd', 's')) {
+ if (mime && strncasecmp(mime, "audio/", 6)) {
+ return ERROR_MALFORMED;
+ }
+
+ kind = Track::AUDIO;
+ }
+
+ if (!mime) {
+ mime = "application/octet-stream";
+ }
+
+ meta->setCString(kKeyMIMEType, mime);
+
+ mTracks.push();
+ Track *track = &mTracks.editItemAt(mTracks.size() - 1);
+
+ track->mMeta = meta;
+ track->mRate = rate;
+ track->mScale = scale;
+ track->mKind = kind;
+ track->mNumSyncSamples = 0;
+ track->mThumbnailSampleSize = 0;
+ track->mThumbnailSampleIndex = -1;
+ track->mMaxSampleSize = 0;
+
+ return OK;
+}
+
+status_t AVIExtractor::parseStreamFormat(off64_t offset, size_t size) {
+ if (mTracks.isEmpty()) {
+ return ERROR_MALFORMED;
+ }
+
+ Track *track = &mTracks.editItemAt(mTracks.size() - 1);
+
+ if (track->mKind == Track::OTHER) {
+ // We don't support this content, but that's not a parsing error.
+ return OK;
+ }
+
+ bool isVideo = (track->mKind == Track::VIDEO);
+
+ if ((isVideo && size < 40) || (!isVideo && size < 18)) {
+ // Expected a BITMAPINFO or WAVEFORMATEX structure, respectively.
+ return ERROR_MALFORMED;
+ }
+
+ sp<ABuffer> buffer = new ABuffer(size);
+ ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size());
+
+ if (n < (ssize_t)size) {
+ return n < 0 ? (status_t)n : ERROR_MALFORMED;
+ }
+
+ const uint8_t *data = buffer->data();
+
+ if (isVideo) {
+ uint32_t width = U32LE_AT(&data[4]);
+ uint32_t height = U32LE_AT(&data[8]);
+
+ track->mMeta->setInt32(kKeyWidth, width);
+ track->mMeta->setInt32(kKeyHeight, height);
+ } else {
+ uint32_t format = U16LE_AT(data);
+ if (format == 0x55) {
+ track->mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ }
+
+ uint32_t numChannels = U16LE_AT(&data[2]);
+ uint32_t sampleRate = U32LE_AT(&data[4]);
+
+ track->mMeta->setInt32(kKeyChannelCount, numChannels);
+ track->mMeta->setInt32(kKeySampleRate, sampleRate);
+ }
+
+ return OK;
+}
+
+// static
+bool AVIExtractor::IsCorrectChunkType(
+ ssize_t trackIndex, Track::Kind kind, uint32_t chunkType) {
+ uint32_t chunkBase = chunkType & 0xffff;
+
+ switch (kind) {
+ case Track::VIDEO:
+ {
+ if (chunkBase != FOURCC(0, 0, 'd', 'c')
+ && chunkBase != FOURCC(0, 0, 'd', 'b')) {
+ return false;
+ }
+ break;
+ }
+
+ case Track::AUDIO:
+ {
+ if (chunkBase != FOURCC(0, 0, 'w', 'b')) {
+ return false;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (trackIndex < 0) {
+ return true;
+ }
+
+ uint8_t hi = chunkType >> 24;
+ uint8_t lo = (chunkType >> 16) & 0xff;
+
+ if (hi < '0' || hi > '9' || lo < '0' || lo > '9') {
+ return false;
+ }
+
+ if (trackIndex != (10 * (hi - '0') + (lo - '0'))) {
+ return false;
+ }
+
+ return true;
+}
+
+status_t AVIExtractor::parseIndex(off64_t offset, size_t size) {
+ if ((size % 16) != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ sp<ABuffer> buffer = new ABuffer(size);
+ ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size());
+
+ if (n < (ssize_t)size) {
+ return n < 0 ? (status_t)n : ERROR_MALFORMED;
+ }
+
+ const uint8_t *data = buffer->data();
+
+ while (size > 0) {
+ uint32_t chunkType = U32_AT(data);
+
+ uint8_t hi = chunkType >> 24;
+ uint8_t lo = (chunkType >> 16) & 0xff;
+
+ if (hi < '0' || hi > '9' || lo < '0' || lo > '9') {
+ return ERROR_MALFORMED;
+ }
+
+ size_t trackIndex = 10 * (hi - '0') + (lo - '0');
+
+ if (trackIndex >= mTracks.size()) {
+ return ERROR_MALFORMED;
+ }
+
+ Track *track = &mTracks.editItemAt(trackIndex);
+
+ if (!IsCorrectChunkType(-1, track->mKind, chunkType)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (track->mKind == Track::OTHER) {
+ data += 16;
+ size -= 16;
+ continue;
+ }
+
+ uint32_t flags = U32LE_AT(&data[4]);
+ uint32_t offset = U32LE_AT(&data[8]);
+ uint32_t chunkSize = U32LE_AT(&data[12]);
+
+ if (chunkSize > track->mMaxSampleSize) {
+ track->mMaxSampleSize = chunkSize;
+ }
+
+ track->mSamples.push();
+
+ SampleInfo *info =
+ &track->mSamples.editItemAt(track->mSamples.size() - 1);
+
+ info->mOffset = offset;
+ info->mIsKey = (flags & 0x10) != 0;
+
+ if (info->mIsKey) {
+ static const size_t kMaxNumSyncSamplesToScan = 20;
+
+ if (track->mNumSyncSamples < kMaxNumSyncSamplesToScan) {
+ if (chunkSize > track->mThumbnailSampleSize) {
+ track->mThumbnailSampleSize = chunkSize;
+
+ track->mThumbnailSampleIndex =
+ track->mSamples.size() - 1;
+ }
+ }
+
+ ++track->mNumSyncSamples;
+ }
+
+ data += 16;
+ size -= 16;
+ }
+
+ if (!mTracks.isEmpty()) {
+ off64_t offset;
+ size_t size;
+ bool isKey;
+ status_t err = getSampleInfo(0, 0, &offset, &size, &isKey);
+
+ if (err != OK) {
+ mOffsetsAreAbsolute = !mOffsetsAreAbsolute;
+ err = getSampleInfo(0, 0, &offset, &size, &isKey);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ LOGV("Chunk offsets are %s",
+ mOffsetsAreAbsolute ? "absolute" : "movie-chunk relative");
+ }
+
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ Track *track = &mTracks.editItemAt(i);
+
+ int64_t durationUs =
+ (track->mSamples.size() * 1000000ll * track->mRate) / track->mScale;
+
+ LOGV("track %d duration = %.2f secs", i, durationUs / 1E6);
+
+ track->mMeta->setInt64(kKeyDuration, durationUs);
+ track->mMeta->setInt32(kKeyMaxInputSize, track->mMaxSampleSize);
+
+ const char *tmp;
+ CHECK(track->mMeta->findCString(kKeyMIMEType, &tmp));
+
+ AString mime = tmp;
+
+ if (!strncasecmp("video/", mime.c_str(), 6)
+ && track->mThumbnailSampleIndex >= 0) {
+ int64_t thumbnailTimeUs =
+ (track->mThumbnailSampleIndex * 1000000ll * track->mRate)
+ / track->mScale;
+
+ track->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs);
+
+ if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG4)) {
+ status_t err = addMPEG4CodecSpecificData(i);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+ }
+ }
+
+ mFoundIndex = true;
+
+ return OK;
+}
+
+static size_t GetSizeWidth(size_t x) {
+ size_t n = 1;
+ while (x > 127) {
+ ++n;
+ x >>= 7;
+ }
+ return n;
+}
+
+static uint8_t *EncodeSize(uint8_t *dst, size_t x) {
+ while (x > 127) {
+ *dst++ = (x & 0x7f) | 0x80;
+ x >>= 7;
+ }
+ *dst++ = x;
+ return dst;
+}
+
+sp<ABuffer> MakeMPEG4VideoCodecSpecificData(const sp<ABuffer> &config) {
+ size_t len1 = config->size() + GetSizeWidth(config->size()) + 1;
+ size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13;
+ size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3;
+
+ sp<ABuffer> csd = new ABuffer(len3);
+ uint8_t *dst = csd->data();
+ *dst++ = 0x03;
+ dst = EncodeSize(dst, len2 + 3);
+ *dst++ = 0x00; // ES_ID
+ *dst++ = 0x00;
+ *dst++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+ *dst++ = 0x04;
+ dst = EncodeSize(dst, len1 + 13);
+ *dst++ = 0x01; // Video ISO/IEC 14496-2 Simple Profile
+ for (size_t i = 0; i < 12; ++i) {
+ *dst++ = 0x00;
+ }
+
+ *dst++ = 0x05;
+ dst = EncodeSize(dst, config->size());
+ memcpy(dst, config->data(), config->size());
+ dst += config->size();
+
+ // hexdump(csd->data(), csd->size());
+
+ return csd;
+}
+
+status_t AVIExtractor::addMPEG4CodecSpecificData(size_t trackIndex) {
+ Track *track = &mTracks.editItemAt(trackIndex);
+
+ off64_t offset;
+ size_t size;
+ bool isKey;
+ status_t err = getSampleInfo(trackIndex, 0, &offset, &size, &isKey);
+
+ if (err != OK) {
+ return err;
+ }
+
+ sp<ABuffer> buffer = new ABuffer(size);
+ ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size());
+
+ if (n < (ssize_t)size) {
+ return n < 0 ? (status_t)n : ERROR_MALFORMED;
+ }
+
+ // Extract everything up to the first VOP start code from the first
+ // frame's encoded data and use it to construct an ESDS with the
+ // codec specific data.
+
+ size_t i = 0;
+ bool found = false;
+ while (i + 3 < buffer->size()) {
+ if (!memcmp("\x00\x00\x01\xb6", &buffer->data()[i], 4)) {
+ found = true;
+ break;
+ }
+
+ ++i;
+ }
+
+ if (!found) {
+ return ERROR_MALFORMED;
+ }
+
+ buffer->setRange(0, i);
+
+ sp<ABuffer> csd = MakeMPEG4VideoCodecSpecificData(buffer);
+ track->mMeta->setData(kKeyESDS, kTypeESDS, csd->data(), csd->size());
+
+ return OK;
+}
+
+status_t AVIExtractor::getSampleInfo(
+ size_t trackIndex, size_t sampleIndex,
+ off64_t *offset, size_t *size, bool *isKey) {
+ if (trackIndex >= mTracks.size()) {
+ return -ERANGE;
+ }
+
+ const Track &track = mTracks.itemAt(trackIndex);
+
+ if (sampleIndex >= track.mSamples.size()) {
+ return -ERANGE;
+ }
+
+ const SampleInfo &info = track.mSamples.itemAt(sampleIndex);
+
+ if (!mOffsetsAreAbsolute) {
+ *offset = info.mOffset + mMovieOffset + 8;
+ } else {
+ *offset = info.mOffset;
+ }
+
+ *size = 0;
+
+ uint8_t tmp[8];
+ ssize_t n = mDataSource->readAt(*offset, tmp, 8);
+
+ if (n < 8) {
+ return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED;
+ }
+
+ uint32_t chunkType = U32_AT(tmp);
+
+ if (!IsCorrectChunkType(trackIndex, track.mKind, chunkType)) {
+ return ERROR_MALFORMED;
+ }
+
+ *offset += 8;
+ *size = U32LE_AT(&tmp[4]);
+
+ *isKey = info.mIsKey;
+
+ return OK;
+}
+
+status_t AVIExtractor::getSampleIndexAtTime(
+ size_t trackIndex,
+ int64_t timeUs, MediaSource::ReadOptions::SeekMode mode,
+ size_t *sampleIndex) const {
+ if (trackIndex >= mTracks.size()) {
+ return -ERANGE;
+ }
+
+ const Track &track = mTracks.itemAt(trackIndex);
+
+ ssize_t closestSampleIndex =
+ timeUs / track.mRate * track.mScale / 1000000ll;
+
+ ssize_t numSamples = track.mSamples.size();
+
+ if (closestSampleIndex < 0) {
+ closestSampleIndex = 0;
+ } else if (closestSampleIndex >= numSamples) {
+ closestSampleIndex = numSamples - 1;
+ }
+
+ if (mode == MediaSource::ReadOptions::SEEK_CLOSEST) {
+ *sampleIndex = closestSampleIndex;
+
+ return OK;
+ }
+
+ ssize_t prevSyncSampleIndex = closestSampleIndex;
+ while (prevSyncSampleIndex >= 0) {
+ const SampleInfo &info =
+ track.mSamples.itemAt(prevSyncSampleIndex);
+
+ if (info.mIsKey) {
+ break;
+ }
+
+ --prevSyncSampleIndex;
+ }
+
+ ssize_t nextSyncSampleIndex = closestSampleIndex;
+ while (nextSyncSampleIndex < numSamples) {
+ const SampleInfo &info =
+ track.mSamples.itemAt(nextSyncSampleIndex);
+
+ if (info.mIsKey) {
+ break;
+ }
+
+ ++nextSyncSampleIndex;
+ }
+
+ switch (mode) {
+ case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC:
+ {
+ *sampleIndex = prevSyncSampleIndex;
+
+ return prevSyncSampleIndex >= 0 ? OK : UNKNOWN_ERROR;
+ }
+
+ case MediaSource::ReadOptions::SEEK_NEXT_SYNC:
+ {
+ *sampleIndex = nextSyncSampleIndex;
+
+ return nextSyncSampleIndex < numSamples ? OK : UNKNOWN_ERROR;
+ }
+
+ case MediaSource::ReadOptions::SEEK_CLOSEST_SYNC:
+ {
+ if (prevSyncSampleIndex < 0 && nextSyncSampleIndex >= numSamples) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (prevSyncSampleIndex < 0) {
+ *sampleIndex = nextSyncSampleIndex;
+ return OK;
+ }
+
+ if (nextSyncSampleIndex >= numSamples) {
+ *sampleIndex = prevSyncSampleIndex;
+ return OK;
+ }
+
+ size_t dist1 = closestSampleIndex - prevSyncSampleIndex;
+ size_t dist2 = nextSyncSampleIndex - closestSampleIndex;
+
+ *sampleIndex =
+ (dist1 < dist2) ? prevSyncSampleIndex : nextSyncSampleIndex;
+
+ return OK;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+bool SniffAVI(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *) {
+ char tmp[12];
+ if (source->readAt(0, tmp, 12) < 12) {
+ return false;
+ }
+
+ if (!memcmp(tmp, "RIFF", 4) && !memcmp(&tmp[8], "AVI ", 4)) {
+ mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_AVI);
+ *confidence = 0.2;
+
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace android
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 2d486e3..f731dfb 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -3,10 +3,15 @@
include frameworks/base/media/libstagefright/codecs/common/Config.mk
+BUILD_WITH_SOFTWARE_DECODERS := false
+
LOCAL_SRC_FILES:= \
ACodec.cpp \
+ AACExtractor.cpp \
+ AACWriter.cpp \
AMRExtractor.cpp \
AMRWriter.cpp \
+ AVIExtractor.cpp \
AudioPlayer.cpp \
AudioSource.cpp \
AwesomePlayer.cpp \
@@ -17,6 +22,8 @@
DRMExtractor.cpp \
ESDS.cpp \
FileSource.cpp \
+ FLACExtractor.cpp \
+ HTTPBase.cpp \
HTTPStream.cpp \
JPEGSource.cpp \
MP3Extractor.cpp \
@@ -40,10 +47,10 @@
ShoutcastSource.cpp \
StagefrightMediaScanner.cpp \
StagefrightMetadataRetriever.cpp \
- ThreadedSource.cpp \
ThrottledSource.cpp \
TimeSource.cpp \
TimedEventQueue.cpp \
+ TimedTextPlayer.cpp \
Utils.cpp \
VBRISeeker.cpp \
WAVExtractor.cpp \
@@ -54,8 +61,10 @@
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
$(TOP)/frameworks/base/include/media/stagefright/openmax \
+ $(TOP)/external/flac/include \
$(TOP)/external/tremolo \
- $(TOP)/frameworks/base/media/libstagefright/rtsp
+ $(TOP)/frameworks/base/media/libstagefright/rtsp \
+ $(TOP)/external/openssl/include \
LOCAL_SHARED_LIBRARIES := \
libbinder \
@@ -65,34 +74,101 @@
libui \
libsonivox \
libvorbisidec \
- libsurfaceflinger_client \
libstagefright_yuv \
libcamera_client \
libdrmframework \
- libcrypto
+ libcrypto \
+ libssl \
+ libgui \
LOCAL_STATIC_LIBRARIES := \
libstagefright_color_conversion \
- libstagefright_aacdec \
libstagefright_aacenc \
- libstagefright_amrnbdec \
libstagefright_amrnbenc \
- libstagefright_amrwbdec \
libstagefright_amrwbenc \
- libstagefright_avcdec \
libstagefright_avcenc \
- libstagefright_m4vh263dec \
libstagefright_m4vh263enc \
- libstagefright_mp3dec \
- libstagefright_vorbisdec \
libstagefright_matroska \
- libstagefright_vpxdec \
libvpx \
libstagefright_mpeg2ts \
libstagefright_httplive \
libstagefright_rtsp \
libstagefright_id3 \
+ libFLAC \
+
+ifeq ($(BUILD_WITH_SOFTWARE_DECODERS),true)
+
+LOCAL_SRC_FILES += \
+ ThreadedSource.cpp \
+
+LOCAL_STATIC_LIBRARIES += \
+ libstagefright_aacdec \
+ libstagefright_amrnbdec \
+ libstagefright_amrwbdec \
+ libstagefright_avcdec \
libstagefright_g711dec \
+ libstagefright_mp3dec \
+ libstagefright_m4vh263dec \
+ libstagefright_vorbisdec \
+ libstagefright_vpxdec \
+ libvpx \
+
+endif
+
+
+################################################################################
+
+# The following was shamelessly copied from external/webkit/Android.mk and
+# currently must follow the same logic to determine how webkit was built and
+# if it's safe to link against libchromium.net
+
+# V8 also requires an ARMv7 CPU, and since we must use jsc, we cannot
+# use the Chrome http stack either.
+ifneq ($(strip $(ARCH_ARM_HAVE_ARMV7A)),true)
+ USE_ALT_HTTP := true
+endif
+
+# See if the user has specified a stack they want to use
+HTTP_STACK = $(HTTP)
+# We default to the Chrome HTTP stack.
+DEFAULT_HTTP = chrome
+ALT_HTTP = android
+
+ifneq ($(HTTP_STACK),chrome)
+ ifneq ($(HTTP_STACK),android)
+ # No HTTP stack is specified, pickup the one we want as default.
+ ifeq ($(USE_ALT_HTTP),true)
+ HTTP_STACK = $(ALT_HTTP)
+ else
+ HTTP_STACK = $(DEFAULT_HTTP)
+ endif
+ endif
+endif
+
+ifeq ($(HTTP_STACK),chrome)
+
+LOCAL_SHARED_LIBRARIES += \
+ liblog \
+ libicuuc \
+ libicui18n \
+ libz \
+ libdl \
+
+LOCAL_STATIC_LIBRARIES += \
+ libstagefright_chromium_http \
+ libchromium_net \
+ libwebcore \
+
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SHARED_LIBRARIES += libstlport
+include external/stlport/libstlport.mk
+endif
+
+LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1
+
+endif # ifeq ($(HTTP_STACK),chrome)
+
+################################################################################
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \
@@ -116,6 +192,10 @@
LOCAL_CFLAGS += -Wno-multichar
+ifeq ($(BUILD_WITH_SOFTWARE_DECODERS),true)
+ LOCAL_CFLAGS += -DHAVE_SOFTWARE_DECODERS
+endif
+
LOCAL_MODULE:= libstagefright
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index e7c0299..dd69e6b 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -84,7 +84,13 @@
CHECK(mFirstBuffer == NULL);
- mFirstBufferResult = mSource->read(&mFirstBuffer);
+ MediaSource::ReadOptions options;
+ if (mSeeking) {
+ options.setSeekTo(mSeekTimeUs);
+ mSeeking = false;
+ }
+
+ mFirstBufferResult = mSource->read(&mFirstBuffer, &options);
if (mFirstBufferResult == INFO_FORMAT_CHANGED) {
LOGV("INFO_FORMAT_CHANGED!!!");
@@ -110,7 +116,7 @@
if (mAudioSink.get() != NULL) {
status_t err = mAudioSink->open(
- mSampleRate, numChannels, AudioSystem::PCM_16_BIT,
+ mSampleRate, numChannels, AUDIO_FORMAT_PCM_16_BIT,
DEFAULT_AUDIOSINK_BUFFERCOUNT,
&AudioPlayer::AudioSinkCallback, this);
if (err != OK) {
@@ -132,10 +138,10 @@
mAudioSink->start();
} else {
mAudioTrack = new AudioTrack(
- AudioSystem::MUSIC, mSampleRate, AudioSystem::PCM_16_BIT,
+ AUDIO_STREAM_MUSIC, mSampleRate, AUDIO_FORMAT_PCM_16_BIT,
(numChannels == 2)
- ? AudioSystem::CHANNEL_OUT_STEREO
- : AudioSystem::CHANNEL_OUT_MONO,
+ ? AUDIO_CHANNEL_OUT_STEREO
+ : AUDIO_CHANNEL_OUT_MONO,
0, 0, &AudioCallback, this, 0);
if ((err = mAudioTrack->initCheck()) != OK) {
@@ -280,17 +286,39 @@
buffer->size = numBytesWritten;
}
+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) {
LOGV("AudioCallback");
}
if (mReachedEOS) {
- memset(data, 0, size);
-
- return size;
+ 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) {
@@ -317,7 +345,7 @@
mSeeking = false;
if (mObserver) {
- mObserver->postAudioSeekComplete();
+ postSeekComplete = true;
}
}
}
@@ -342,7 +370,35 @@
if (err != OK) {
if (mObserver && !mReachedEOS) {
- mObserver->postAudioEOS();
+ // 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;
+
+ LOGV("total number of frames played: %lld (%lld us)",
+ (mNumFramesPlayed + numAdditionalFrames),
+ 1000000ll * (mNumFramesPlayed + numAdditionalFrames)
+ / mSampleRate);
+
+ LOGV("%d frames left to play, %lld us (%.2f secs)",
+ numFramesPendingPlayout,
+ timeToCompletionUs, timeToCompletionUs / 1E6);
+
+ postEOS = true;
+ postEOSDelayUs = timeToCompletionUs + mLatencyUs;
}
mReachedEOS = true;
@@ -386,8 +442,18 @@
size_remaining -= copy;
}
- Mutex::Autolock autoLock(mLock);
- mNumFramesPlayed += size_done / mFrameSize;
+ {
+ Mutex::Autolock autoLock(mLock);
+ mNumFramesPlayed += size_done / mFrameSize;
+ }
+
+ if (postEOS) {
+ mObserver->postAudioEOS(postEOSDelayUs);
+ }
+
+ if (postSeekComplete) {
+ mObserver->postAudioSeekComplete();
+ }
return size_done;
}
@@ -405,6 +471,10 @@
Mutex::Autolock autoLock(mLock);
if (mPositionTimeMediaUs < 0 || mPositionTimeRealUs < 0) {
+ if (mSeeking) {
+ return mSeekTimeUs;
+ }
+
return 0;
}
@@ -430,6 +500,7 @@
Mutex::Autolock autoLock(mLock);
mSeeking = true;
+ mPositionTimeRealUs = mPositionTimeMediaUs = -1;
mReachedEOS = false;
mSeekTimeUs = time_us;
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index f96df18..99c3682 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -18,38 +18,54 @@
#define LOG_TAG "AudioSource"
#include <utils/Log.h>
-#include <media/stagefright/AudioSource.h>
-
#include <media/AudioRecord.h>
-#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/AudioSource.h>
+#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <cutils/properties.h>
#include <stdlib.h>
namespace android {
+static void AudioRecordCallbackFunction(int event, void *user, void *info) {
+ AudioSource *source = (AudioSource *) user;
+ switch (event) {
+ case AudioRecord::EVENT_MORE_DATA: {
+ source->dataCallbackTimestamp(*((AudioRecord::Buffer *) info), systemTime() / 1000);
+ break;
+ }
+ case AudioRecord::EVENT_OVERRUN: {
+ LOGW("AudioRecord reported overrun!");
+ break;
+ }
+ default:
+ // does nothing
+ break;
+ }
+}
+
AudioSource::AudioSource(
int inputSource, uint32_t sampleRate, uint32_t channels)
: mStarted(false),
- mCollectStats(false),
+ mSampleRate(sampleRate),
mPrevSampleTimeUs(0),
- mTotalLostFrames(0),
- mPrevLostBytes(0),
- mGroup(NULL) {
+ mNumFramesReceived(0),
+ mNumClientOwnedBuffers(0) {
LOGV("sampleRate: %d, channels: %d", sampleRate, channels);
CHECK(channels == 1 || channels == 2);
uint32_t flags = AudioRecord::RECORD_AGC_ENABLE |
AudioRecord::RECORD_NS_ENABLE |
AudioRecord::RECORD_IIR_ENABLE;
-
mRecord = new AudioRecord(
- inputSource, sampleRate, AudioSystem::PCM_16_BIT,
- channels > 1? AudioSystem::CHANNEL_IN_STEREO: AudioSystem::CHANNEL_IN_MONO,
+ inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
+ channels > 1? AUDIO_CHANNEL_IN_STEREO: AUDIO_CHANNEL_IN_MONO,
4 * kMaxBufferSize / sizeof(int16_t), /* Enable ping-pong buffers */
- flags);
+ flags,
+ AudioRecordCallbackFunction,
+ this);
mInitCheck = mRecord->initCheck();
}
@@ -68,6 +84,7 @@
}
status_t AudioSource::start(MetaData *params) {
+ Mutex::Autolock autoLock(mLock);
if (mStarted) {
return UNKNOWN_ERROR;
}
@@ -76,12 +93,6 @@
return NO_INIT;
}
- char value[PROPERTY_VALUE_MAX];
- if (property_get("media.stagefright.record-stats", value, NULL)
- && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
- mCollectStats = true;
- }
-
mTrackMaxAmplitude = false;
mMaxAmplitude = 0;
mInitialReadTimeUs = 0;
@@ -92,9 +103,6 @@
}
status_t err = mRecord->start();
if (err == OK) {
- mGroup = new MediaBufferGroup;
- mGroup->add_buffer(new MediaBuffer(kMaxBufferSize));
-
mStarted = true;
} else {
delete mRecord;
@@ -105,7 +113,25 @@
return err;
}
+void AudioSource::releaseQueuedFrames_l() {
+ LOGV("releaseQueuedFrames_l");
+ List<MediaBuffer *>::iterator it;
+ while (!mBuffersReceived.empty()) {
+ it = mBuffersReceived.begin();
+ (*it)->release();
+ mBuffersReceived.erase(it);
+ }
+}
+
+void AudioSource::waitOutstandingEncodingFrames_l() {
+ LOGV("waitOutstandingEncodingFrames_l: %lld", mNumClientOwnedBuffers);
+ while (mNumClientOwnedBuffers > 0) {
+ mFrameEncodingCompletionCondition.wait(mLock);
+ }
+}
+
status_t AudioSource::stop() {
+ Mutex::Autolock autoLock(mLock);
if (!mStarted) {
return UNKNOWN_ERROR;
}
@@ -114,29 +140,23 @@
return NO_INIT;
}
- mRecord->stop();
-
- delete mGroup;
- mGroup = NULL;
-
mStarted = false;
-
- if (mCollectStats) {
- LOGI("Total lost audio frames: %lld",
- mTotalLostFrames + (mPrevLostBytes >> 1));
- }
+ mRecord->stop();
+ waitOutstandingEncodingFrames_l();
+ releaseQueuedFrames_l();
return OK;
}
sp<MetaData> AudioSource::getFormat() {
+ Mutex::Autolock autoLock(mLock);
if (mInitCheck != OK) {
return 0;
}
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
- meta->setInt32(kKeySampleRate, mRecord->getSampleRate());
+ meta->setInt32(kKeySampleRate, mSampleRate);
meta->setInt32(kKeyChannelCount, mRecord->channelCount());
meta->setInt32(kKeyMaxInputSize, kMaxBufferSize);
@@ -177,122 +197,131 @@
status_t AudioSource::read(
MediaBuffer **out, const ReadOptions *options) {
+ Mutex::Autolock autoLock(mLock);
+ *out = NULL;
if (mInitCheck != OK) {
return NO_INIT;
}
- int64_t readTimeUs = systemTime() / 1000;
- *out = NULL;
-
- MediaBuffer *buffer;
- CHECK_EQ(mGroup->acquire_buffer(&buffer), OK);
-
- int err = 0;
- if (mStarted) {
-
- uint32_t numFramesRecorded;
- mRecord->getPosition(&numFramesRecorded);
-
-
- if (numFramesRecorded == 0 && mPrevSampleTimeUs == 0) {
- mInitialReadTimeUs = readTimeUs;
- // Initial delay
- if (mStartTimeUs > 0) {
- mStartTimeUs = readTimeUs - mStartTimeUs;
- } else {
- // Assume latency is constant.
- mStartTimeUs += mRecord->latency() * 1000;
- }
- mPrevSampleTimeUs = mStartTimeUs;
- }
-
- uint32_t sampleRate = mRecord->getSampleRate();
-
- // Insert null frames when lost frames are detected.
- int64_t timestampUs = mPrevSampleTimeUs;
- uint32_t numLostBytes = mRecord->getInputFramesLost() << 1;
- numLostBytes += mPrevLostBytes;
-#if 0
- // Simulate lost frames
- numLostBytes = ((rand() * 1.0 / RAND_MAX)) * 2 * kMaxBufferSize;
- numLostBytes &= 0xFFFFFFFE; // Alignment requirement
-
- // Reduce the chance to lose
- if (rand() * 1.0 / RAND_MAX >= 0.05) {
- numLostBytes = 0;
- }
-#endif
- if (numLostBytes > 0) {
- if (numLostBytes > kMaxBufferSize) {
- mPrevLostBytes = numLostBytes - kMaxBufferSize;
- numLostBytes = kMaxBufferSize;
- } else {
- mPrevLostBytes = 0;
- }
-
- CHECK_EQ(numLostBytes & 1, 0);
- timestampUs += ((1000000LL * (numLostBytes >> 1)) +
- (sampleRate >> 1)) / sampleRate;
-
- CHECK(timestampUs > mPrevSampleTimeUs);
- if (mCollectStats) {
- mTotalLostFrames += (numLostBytes >> 1);
- }
- memset(buffer->data(), 0, numLostBytes);
- buffer->set_range(0, numLostBytes);
- if (numFramesRecorded == 0) {
- buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs);
- }
- buffer->meta_data()->setInt64(kKeyTime, mStartTimeUs + mPrevSampleTimeUs);
- buffer->meta_data()->setInt64(kKeyDriftTime, readTimeUs - mInitialReadTimeUs);
- mPrevSampleTimeUs = timestampUs;
- *out = buffer;
- return OK;
- }
-
- ssize_t n = mRecord->read(buffer->data(), buffer->size());
- if (n < 0) {
- buffer->release();
- return (status_t)n;
- }
-
- int64_t recordDurationUs = (1000000LL * n >> 1) / sampleRate;
- timestampUs += recordDurationUs;
-
- if (mPrevSampleTimeUs - mStartTimeUs < kAutoRampStartUs) {
- // Mute the initial video recording signal
- memset((uint8_t *) buffer->data(), 0, n);
- } else if (mPrevSampleTimeUs - mStartTimeUs < kAutoRampStartUs + kAutoRampDurationUs) {
- int32_t autoRampDurationFrames =
- (kAutoRampDurationUs * sampleRate + 500000LL) / 1000000LL;
-
- int32_t autoRampStartFrames =
- (kAutoRampStartUs * sampleRate + 500000LL) / 1000000LL;
-
- int32_t nFrames = numFramesRecorded - autoRampStartFrames;
- rampVolume(nFrames, autoRampDurationFrames, (uint8_t *) buffer->data(), n);
- }
- if (mTrackMaxAmplitude) {
- trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
- }
-
- if (numFramesRecorded == 0) {
- buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs);
- }
-
- buffer->meta_data()->setInt64(kKeyTime, mStartTimeUs + mPrevSampleTimeUs);
- buffer->meta_data()->setInt64(kKeyDriftTime, readTimeUs - mInitialReadTimeUs);
- CHECK(timestampUs > mPrevSampleTimeUs);
- mPrevSampleTimeUs = timestampUs;
- LOGV("initial delay: %lld, sample rate: %d, timestamp: %lld",
- mStartTimeUs, sampleRate, timestampUs);
-
- buffer->set_range(0, n);
-
- *out = buffer;
+ while (mStarted && mBuffersReceived.empty()) {
+ mFrameAvailableCondition.wait(mLock);
+ }
+ if (!mStarted) {
return OK;
}
+ MediaBuffer *buffer = *mBuffersReceived.begin();
+ mBuffersReceived.erase(mBuffersReceived.begin());
+ ++mNumClientOwnedBuffers;
+ buffer->setObserver(this);
+ buffer->add_ref();
+
+ // Mute/suppress the recording sound
+ int64_t timeUs;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+ int64_t elapsedTimeUs = timeUs - mStartTimeUs;
+ if (elapsedTimeUs < kAutoRampStartUs) {
+ memset((uint8_t *) buffer->data(), 0, buffer->range_length());
+ } else if (elapsedTimeUs < kAutoRampStartUs + kAutoRampDurationUs) {
+ int32_t autoRampDurationFrames =
+ (kAutoRampDurationUs * mSampleRate + 500000LL) / 1000000LL;
+
+ int32_t autoRampStartFrames =
+ (kAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL;
+
+ int32_t nFrames = mNumFramesReceived - autoRampStartFrames;
+ rampVolume(nFrames, autoRampDurationFrames,
+ (uint8_t *) buffer->data(), buffer->range_length());
+ }
+
+ // Track the max recording signal amplitude.
+ if (mTrackMaxAmplitude) {
+ trackMaxAmplitude(
+ (int16_t *) buffer->data(), buffer->range_length() >> 1);
+ }
+
+ *out = buffer;
+ return OK;
+}
+
+void AudioSource::signalBufferReturned(MediaBuffer *buffer) {
+ LOGV("signalBufferReturned: %p", buffer->data());
+ Mutex::Autolock autoLock(mLock);
+ --mNumClientOwnedBuffers;
+ buffer->setObserver(0);
+ buffer->release();
+ mFrameEncodingCompletionCondition.signal();
+ return;
+}
+
+status_t AudioSource::dataCallbackTimestamp(
+ const AudioRecord::Buffer& audioBuffer, int64_t timeUs) {
+ LOGV("dataCallbackTimestamp: %lld us", timeUs);
+ Mutex::Autolock autoLock(mLock);
+ if (!mStarted) {
+ LOGW("Spurious callback from AudioRecord. Drop the audio data.");
+ return OK;
+ }
+
+ // Drop retrieved and previously lost audio data.
+ if (mNumFramesReceived == 0 && timeUs < mStartTimeUs) {
+ mRecord->getInputFramesLost();
+ LOGV("Drop audio data at %lld/%lld us", timeUs, mStartTimeUs);
+ return OK;
+ }
+
+ if (mNumFramesReceived == 0 && mPrevSampleTimeUs == 0) {
+ mInitialReadTimeUs = timeUs;
+ // Initial delay
+ if (mStartTimeUs > 0) {
+ mStartTimeUs = timeUs - mStartTimeUs;
+ } else {
+ // Assume latency is constant.
+ mStartTimeUs += mRecord->latency() * 1000;
+ }
+ mPrevSampleTimeUs = mStartTimeUs;
+ }
+
+ int64_t timestampUs = mPrevSampleTimeUs;
+
+ size_t numLostBytes = 0;
+ if (mNumFramesReceived > 0) { // Ignore earlier frame lost
+ // getInputFramesLost() returns the number of lost frames.
+ // Convert number of frames lost to number of bytes lost.
+ numLostBytes = mRecord->getInputFramesLost() * mRecord->frameSize();
+ }
+
+ CHECK_EQ(numLostBytes & 1, 0u);
+ CHECK_EQ(audioBuffer.size & 1, 0u);
+ size_t bufferSize = numLostBytes + audioBuffer.size;
+ MediaBuffer *buffer = new MediaBuffer(bufferSize);
+ if (numLostBytes > 0) {
+ memset(buffer->data(), 0, numLostBytes);
+ memcpy((uint8_t *) buffer->data() + numLostBytes,
+ audioBuffer.i16, audioBuffer.size);
+ } else {
+ if (audioBuffer.size == 0) {
+ LOGW("Nothing is available from AudioRecord callback buffer");
+ buffer->release();
+ return OK;
+ }
+ memcpy((uint8_t *) buffer->data(),
+ audioBuffer.i16, audioBuffer.size);
+ }
+
+ buffer->set_range(0, bufferSize);
+ timestampUs += ((1000000LL * (bufferSize >> 1)) +
+ (mSampleRate >> 1)) / mSampleRate;
+
+ if (mNumFramesReceived == 0) {
+ buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs);
+ }
+ buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
+ buffer->meta_data()->setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs);
+ mPrevSampleTimeUs = timestampUs;
+ mNumFramesReceived += buffer->range_length() / sizeof(int16_t);
+ mBuffersReceived.push_back(buffer);
+ mFrameAvailableCondition.signal();
return OK;
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 1a8e86d..fb7a871 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#undef DEBUG_HDCP
+
//#define LOG_NDEBUG 0
#define LOG_TAG "AwesomePlayer"
#include <utils/Log.h>
@@ -22,17 +24,17 @@
#include "include/ARTSPController.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 "ARTPSession.h"
-#include "APacketSource.h"
-#include "ASessionDescription.h"
-#include "UDPPusher.h"
+#include "include/TimedTextPlayer.h"
+#include "include/WVMExtractor.h"
#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/AudioPlayer.h>
@@ -46,18 +48,22 @@
#include <media/stagefright/OMXCodec.h>
#include <surfaceflinger/Surface.h>
+#include <gui/ISurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
+#include <surfaceflinger/ISurfaceComposer.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
-#include "include/LiveSession.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 = 10000000ll; // 10secs
+static int64_t kHighWaterMarkRTSPUs = 4000000ll; // 4secs
static const size_t kLowWaterMarkBytes = 40000;
static const size_t kHighWaterMarkBytes = 200000;
@@ -86,8 +92,8 @@
struct AwesomeLocalRenderer : public AwesomeRenderer {
AwesomeLocalRenderer(
- const sp<Surface> &surface, const sp<MetaData> &meta)
- : mTarget(new SoftwareRenderer(surface, meta)) {
+ const sp<ANativeWindow> &nativeWindow, const sp<MetaData> &meta)
+ : mTarget(new SoftwareRenderer(nativeWindow, meta)) {
}
virtual void render(MediaBuffer *buffer) {
@@ -160,8 +166,17 @@
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),
mTimeSource(NULL),
@@ -172,7 +187,9 @@
mFlags(0),
mExtractorFlags(0),
mVideoBuffer(NULL),
- mDecryptHandle(NULL) {
+ mDecryptHandle(NULL),
+ mLastVideoTimeUs(-1),
+ mTextPlayer(NULL) {
CHECK_EQ(mClient.connect(), (status_t)OK);
DataSource::RegisterDefaultSniffers();
@@ -237,19 +254,24 @@
mUri = uri;
- if (!strncmp("http://", uri, 7)) {
- // Hack to support http live.
+ if (headers) {
+ mUriHeaders = *headers;
- size_t len = strlen(uri);
- if (!strcasecmp(&uri[len - 5], ".m3u8")
- || strstr(&uri[7], "m3u8") != NULL) {
- mUri = "httplive://";
- mUri.append(&uri[7]);
+ 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);
+
+ mFlags |= INCOGNITO;
}
}
- if (headers) {
- mUriHeaders = *headers;
+ if (!(mFlags & INCOGNITO)) {
+ LOGI("setDataSource_l('%s')", mUri.string());
+ } else {
+ LOGI("setDataSource_l(URL suppressed)");
}
// The actual work will be done during preparation in the call to
@@ -290,10 +312,12 @@
return UNKNOWN_ERROR;
}
- dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
- if (mDecryptHandle != NULL
- && RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
- notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+ 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);
+ }
}
return setDataSource_l(extractor);
@@ -361,10 +385,8 @@
mFlags |= AUTO_LOOPING;
}
}
- }
-
- if (haveAudio && haveVideo) {
- break;
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+ addTextSource(extractor->getTrack(i));
}
}
@@ -389,16 +411,29 @@
if (mDecryptHandle != NULL) {
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
Playback::STOP, 0);
- mDrmManagerClient->closeDecryptSession(mDecryptHandle);
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) {
mFlags |= PREPARE_CANCELLED;
if (mConnectingDataSource != NULL) {
LOGI("interrupting the connection process");
mConnectingDataSource->disconnect();
+ } else if (mConnectingRTSPController != NULL) {
+ LOGI("interrupting the connection process");
+ mConnectingRTSPController->disconnect();
}
if (mFlags & PREPARING_CONNECTED) {
@@ -414,6 +449,7 @@
cancelPlayerEvents();
+ mWVMExtractor.clear();
mCachedSource.clear();
mAudioTrack.clear();
mVideoTrack.clear();
@@ -436,39 +472,20 @@
delete mAudioPlayer;
mAudioPlayer = NULL;
- mVideoRenderer.clear();
-
- if (mVideoBuffer) {
- mVideoBuffer->release();
- mVideoBuffer = NULL;
+ if (mTextPlayer != NULL) {
+ delete mTextPlayer;
+ mTextPlayer = NULL;
}
+ mVideoRenderer.clear();
+
if (mRTSPController != NULL) {
mRTSPController->disconnect();
mRTSPController.clear();
}
- if (mLiveSession != NULL) {
- mLiveSession->disconnect();
- mLiveSession.clear();
- }
-
- mRTPPusher.clear();
- mRTCPPusher.clear();
- mRTPSession.clear();
-
if (mVideoSource != 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();
+ shutdownVideoDecoder_l();
}
mDurationUs = -1;
@@ -487,6 +504,7 @@
mFileSource.clear();
mBitrate = -1;
+ mLastVideoTimeUs = -1;
}
void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
@@ -530,6 +548,11 @@
*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;
@@ -551,7 +574,7 @@
int64_t audioTimeUs = mAudioPlayer->getMediaTimeUs();
int64_t videoLateByUs = audioTimeUs - mVideoTimeUs;
- if (videoLateByUs > 300000ll) {
+ if (!(mFlags & VIDEO_AT_EOS) && videoLateByUs > 300000ll) {
LOGV("video late by %lld ms.", videoLateByUs / 1000ll);
notifyListener_l(
@@ -622,6 +645,30 @@
}
}
}
+ } 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) {
+ LOGV("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;
@@ -630,6 +677,9 @@
LOGV("cachedDurationUs = %.2f secs, eos=%d",
cachedDurationUs / 1E6, eos);
+ int64_t highWaterMarkUs =
+ (mRTSPController != NULL) ? kHighWaterMarkRTSPUs : kHighWaterMarkUs;
+
if ((mFlags & PLAYING) && !eos
&& (cachedDurationUs < kLowWaterMarkUs)) {
LOGI("cache is running low (%.2f secs) , pausing.",
@@ -638,7 +688,7 @@
pause_l();
ensureCacheIsFetching_l();
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
- } else if (eos || cachedDurationUs > kHighWaterMarkUs) {
+ } else if (eos || cachedDurationUs > highWaterMarkUs) {
if (mFlags & CACHE_UNDERRUN) {
LOGI("cache has filled up (%.2f secs), resuming.",
cachedDurationUs / 1E6);
@@ -656,35 +706,6 @@
postBufferingEvent_l();
}
-void AwesomePlayer::partial_reset_l() {
- // Only reset the video renderer and shut down the video decoder.
- // Then instantiate a new video decoder and resume video playback.
-
- mVideoRenderer.clear();
-
- 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();
- }
-
- CHECK_EQ((status_t)OK,
- initVideoDecoder(OMXCodec::kIgnoreCodecSpecificData));
-}
-
void AwesomePlayer::onStreamDone() {
// Posted whenever any stream finishes playing.
@@ -694,21 +715,7 @@
}
mStreamDoneEventPending = false;
- if (mStreamDoneStatus == INFO_DISCONTINUITY) {
- // This special status is returned because an http live stream's
- // video stream switched to a different bandwidth at this point
- // and future data may have been encoded using different parameters.
- // This requires us to shutdown the video decoder and reinstantiate
- // a fresh one.
-
- LOGV("INFO_DISCONTINUITY");
-
- CHECK(mVideoSource != NULL);
-
- partial_reset_l();
- postVideoEvent_l();
- return;
- } else if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
+ if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
LOGV("MEDIA_ERROR %d", mStreamDoneStatus);
notifyListener_l(
@@ -753,6 +760,8 @@
}
status_t AwesomePlayer::play_l() {
+ mFlags &= ~SEEK_PREVIEW;
+
if (mFlags & PLAYING) {
return OK;
}
@@ -768,8 +777,6 @@
mFlags |= PLAYING;
mFlags |= FIRST_FRAME;
- bool deferredAudioSeek = false;
-
if (mDecryptHandle != NULL) {
int64_t position;
getPosition(&position);
@@ -785,10 +792,11 @@
mTimeSource = mAudioPlayer;
- deferredAudioSeek = true;
-
- mWatchForAudioSeekComplete = false;
- mWatchForAudioEOS = true;
+ // 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();
}
}
@@ -826,18 +834,22 @@
}
}
- if (deferredAudioSeek) {
- // If there was a seek request while we were paused
- // and we're just starting up again, honor the request now.
- seekAudioIfNecessary_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);
+
return OK;
}
@@ -851,6 +863,8 @@
if (!(mFlags & AUDIOPLAYER_STARTED)) {
mFlags |= AUDIOPLAYER_STARTED;
+ bool wasSeeking = mAudioPlayer->isSeeking();
+
// We've already started the MediaSource in order to enable
// the prefetcher to read its data.
status_t err = mAudioPlayer->start(
@@ -860,6 +874,13 @@
notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
return err;
}
+
+ if (wasSeeking) {
+ CHECK(!mAudioPlayer->isSeeking());
+
+ // We will have finished the seek while starting the audio player.
+ postAudioSeekComplete_l();
+ }
} else {
mAudioPlayer->resume();
}
@@ -891,6 +912,17 @@
cropLeft, cropTop, cropRight, cropBottom);
}
+ int32_t displayWidth;
+ if (meta->findInt32(kKeyDisplayWidth, &displayWidth)) {
+ LOGV("Display width changed (%d=>%d)", mDisplayWidth, displayWidth);
+ mDisplayWidth = displayWidth;
+ }
+ int32_t displayHeight;
+ if (meta->findInt32(kKeyDisplayHeight, &displayHeight)) {
+ LOGV("Display height changed (%d=>%d)", mDisplayHeight, displayHeight);
+ mDisplayHeight = displayHeight;
+ }
+
int32_t usableWidth = cropRight - cropLeft + 1;
int32_t usableHeight = cropBottom - cropTop + 1;
if (mDisplayWidth != 0) {
@@ -916,7 +948,7 @@
}
void AwesomePlayer::initRenderer_l() {
- if (mSurface == NULL) {
+ if (mNativeWindow == NULL) {
return;
}
@@ -942,18 +974,20 @@
// before creating a new one.
IPCThreadState::self()->flushCommands();
- if (USE_SURFACE_ALLOC && strncmp(component, "OMX.", 4) == 0) {
+ 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(mSurface, rotationDegrees);
+ 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.
- mVideoRenderer = new AwesomeLocalRenderer(mSurface, meta);
+ mVideoRenderer = new AwesomeLocalRenderer(mNativeWindow, meta);
}
}
@@ -985,6 +1019,11 @@
mFlags &= ~AUDIO_RUNNING;
}
+ if (mFlags & TEXTPLAYER_STARTED) {
+ mTextPlayer->pause();
+ mFlags &= ~TEXT_RUNNING;
+ }
+
mFlags &= ~PLAYING;
if (mDecryptHandle != NULL) {
@@ -992,6 +1031,16 @@
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;
}
@@ -1003,6 +1052,65 @@
Mutex::Autolock autoLock(mLock);
mSurface = surface;
+ setNativeWindow_l(surface);
+}
+
+void AwesomePlayer::setSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {
+ Mutex::Autolock autoLock(mLock);
+
+ mSurface.clear();
+ if (surfaceTexture != NULL) {
+ setNativeWindow_l(new SurfaceTextureClient(surfaceTexture));
+ }
+}
+
+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();
+}
+
+void AwesomePlayer::setNativeWindow_l(const sp<ANativeWindow> &native) {
+ mNativeWindow = native;
+
+ if (mVideoSource == NULL) {
+ return;
+ }
+
+ LOGI("attempting to reconfigure to use new surface");
+
+ bool wasPlaying = (mFlags & PLAYING) != 0;
+
+ pause_l();
+ mVideoRenderer.clear();
+
+ shutdownVideoDecoder_l();
+
+ CHECK_EQ(initVideoDecoder(), (status_t)OK);
+
+ if (mLastVideoTimeUs >= 0) {
+ mSeeking = SEEK;
+ mSeekNotificationSent = true;
+ mSeekTimeUs = mLastVideoTimeUs;
+ mFlags &= ~(AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS);
+ }
+
+ if (wasPlaying) {
+ play_l();
+ }
}
void AwesomePlayer::setAudioSink(
@@ -1042,7 +1150,8 @@
}
else if (mSeeking != NO_SEEK) {
*positionUs = mSeekTimeUs;
- } else if (mVideoSource != NULL) {
+ } else if (mVideoSource != NULL
+ && (mAudioPlayer == NULL || !(mFlags & VIDEO_AT_EOS))) {
Mutex::Autolock autoLock(mMiscStateLock);
*positionUs = mVideoTimeUs;
} else if (mAudioPlayer != NULL) {
@@ -1063,6 +1172,32 @@
return OK;
}
+status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) {
+ if (mTextPlayer != NULL) {
+ if (index >= 0) { // to turn on a text track
+ status_t err = mTextPlayer->setTimedTextTrackIndex(index);
+ if (err != OK) {
+ return err;
+ }
+
+ mFlags |= TEXT_RUNNING;
+ mFlags |= TEXTPLAYER_STARTED;
+ return OK;
+ } else { // to turn off the text track display
+ if (mFlags & TEXT_RUNNING) {
+ mFlags &= ~TEXT_RUNNING;
+ }
+ if (mFlags & TEXTPLAYER_STARTED) {
+ mFlags &= ~TEXTPLAYER_STARTED;
+ }
+
+ return mTextPlayer->setTimedTextTrackIndex(index);
+ }
+ } else {
+ return INVALID_OPERATION;
+ }
+}
+
// static
void AwesomePlayer::OnRTSPSeekDoneWrapper(void *cookie) {
static_cast<AwesomePlayer *>(cookie)->onRTSPSeekDone();
@@ -1084,6 +1219,14 @@
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;
@@ -1091,12 +1234,21 @@
seekAudioIfNecessary_l();
+ if (mFlags & TEXTPLAYER_STARTED) {
+ mTextPlayer->seekTo(mSeekTimeUs);
+ }
+
if (!(mFlags & PLAYING)) {
LOGV("seeking while paused, sending SEEK_COMPLETE notification"
" immediately.");
notifyListener_l(MEDIA_SEEK_COMPLETE);
mSeekNotificationSent = true;
+
+ if ((mFlags & PREPARED) && mVideoSource != NULL) {
+ mFlags |= SEEK_PREVIEW;
+ postVideoEvent_l();
+ }
}
return OK;
@@ -1108,7 +1260,6 @@
mWatchForAudioSeekComplete = true;
mWatchForAudioEOS = true;
- mSeekNotificationSent = false;
if (mDecryptHandle != NULL) {
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
@@ -1125,6 +1276,16 @@
mAudioTrack = source;
}
+void AwesomePlayer::addTextSource(sp<MediaSource> source) {
+ CHECK(source != NULL);
+
+ if (mTextPlayer == NULL) {
+ mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue);
+ }
+
+ mTextPlayer->addTextSource(source);
+}
+
status_t AwesomePlayer::initAudioDecoder() {
sp<MetaData> meta = mAudioTrack->getFormat();
@@ -1172,11 +1333,57 @@
}
status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {
+
+ // 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
+ LOGV("initVideoDecoder flags=0x%x", flags);
mVideoSource = OMXCodec::Create(
mClient.interface(), mVideoTrack->getFormat(),
false, // createEncoder
mVideoTrack,
- NULL, flags, USE_SURFACE_ALLOC ? mSurface : NULL);
+ NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);
if (mVideoSource != NULL) {
int64_t durationUs;
@@ -1204,7 +1411,7 @@
return;
}
- if (mSeeking == NO_SEEK) {
+ if (mSeeking == NO_SEEK || (mFlags & SEEK_PREVIEW)) {
return;
}
@@ -1216,15 +1423,16 @@
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;
}
mFlags |= FIRST_FRAME;
mSeeking = NO_SEEK;
- mSeekNotificationSent = false;
if (mDecryptHandle != NULL) {
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
@@ -1249,7 +1457,8 @@
mVideoBuffer = NULL;
}
- if (mSeeking == SEEK && mCachedSource != NULL && mAudioSource != 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
@@ -1305,6 +1514,11 @@
}
finishSeekIfNecessary(-1);
+ if (mAudioPlayer != NULL
+ && !(mFlags & (AUDIO_RUNNING | SEEK_PREVIEW))) {
+ startAudioPlayer_l();
+ }
+
mFlags |= VIDEO_AT_EOS;
postStreamDoneEvent_l(err);
return;
@@ -1326,6 +1540,8 @@
int64_t timeUs;
CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+ mLastVideoTimeUs = timeUs;
+
if (mSeeking == SEEK_VIDEO_ONLY) {
if (mSeekTimeUs > timeUs) {
LOGI("XXX mSeekTimeUs = %lld us, timeUs = %lld us",
@@ -1341,7 +1557,7 @@
SeekType wasSeeking = mSeeking;
finishSeekIfNecessary(timeUs);
- if (mAudioPlayer != NULL && !(mFlags & AUDIO_RUNNING)) {
+ if (mAudioPlayer != NULL && !(mFlags & (AUDIO_RUNNING | SEEK_PREVIEW))) {
status_t err = startAudioPlayer_l();
if (err != OK) {
LOGE("Startung the audio player failed w/ err %d", err);
@@ -1349,11 +1565,15 @@
}
}
+ if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
+ mTextPlayer->resume();
+ mFlags |= TEXT_RUNNING;
+ }
+
TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource;
if (mFlags & FIRST_FRAME) {
mFlags &= ~FIRST_FRAME;
- mSinceLastDropped = 0;
mTimeSourceDeltaUs = ts->getRealTimeUs() - timeUs;
}
@@ -1400,23 +1620,13 @@
if (latenessUs > 40000) {
// We're more than 40ms late.
-
- LOGV("we're late by %lld us (%.2f secs)",
+ LOGV("we're late by %lld us (%.2f secs), dropping frame",
latenessUs, latenessUs / 1E6);
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
- if ( mSinceLastDropped > FRAME_DROP_FREQ)
- {
- LOGV("we're late by %lld us (%.2f secs) dropping one "
- "after %d frames",
- latenessUs, latenessUs / 1E6, mSinceLastDropped);
-
- mSinceLastDropped = 0;
- mVideoBuffer->release();
- mVideoBuffer = NULL;
-
- postVideoEvent_l();
- return;
- }
+ postVideoEvent_l();
+ return;
}
if (latenessUs < -10000) {
@@ -1434,13 +1644,17 @@
}
if (mVideoRenderer != NULL) {
- mSinceLastDropped++;
mVideoRenderer->render(mVideoBuffer);
}
mVideoBuffer->release();
mVideoBuffer = NULL;
+ if (wasSeeking != NO_SEEK && (mFlags & SEEK_PREVIEW)) {
+ mFlags &= ~SEEK_PREVIEW;
+ return;
+ }
+
postVideoEvent_l();
}
@@ -1479,12 +1693,12 @@
mQueue.postEventWithDelay(mVideoLagEvent, 1000000ll);
}
-void AwesomePlayer::postCheckAudioStatusEvent_l() {
+void AwesomePlayer::postCheckAudioStatusEvent_l(int64_t delayUs) {
if (mAudioStatusEventPending) {
return;
}
mAudioStatusEventPending = true;
- mQueue.postEvent(mCheckAudioStatusEvent);
+ mQueue.postEventWithDelay(mCheckAudioStatusEvent, delayUs);
}
void AwesomePlayer::onCheckAudioStatus() {
@@ -1578,8 +1792,23 @@
status_t AwesomePlayer::finishSetDataSource_l() {
sp<DataSource> dataSource;
- if (!strncasecmp("http://", mUri.string(), 7)) {
- mConnectingDataSource = new NuHTTPDataSource;
+ bool isWidevineStreaming = false;
+ if (!strncasecmp("widevine://", mUri.string(), 11)) {
+ isWidevineStreaming = true;
+
+ String8 newURI = String8("http://");
+ newURI.append(mUri.string() + 11);
+
+ mUri = newURI;
+ }
+
+ if (!strncasecmp("http://", mUri.string(), 7)
+ || !strncasecmp("https://", mUri.string(), 8)
+ || isWidevineStreaming) {
+ mConnectingDataSource = HTTPBase::Create(
+ (mFlags & INCOGNITO)
+ ? HTTPBase::kFlagIncognito
+ : 0);
mLock.unlock();
status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders);
@@ -1592,179 +1821,67 @@
return err;
}
+ if (!isWidevineStreaming) {
+ // The widevine extractor does its own caching.
+
#if 0
- mCachedSource = new NuCachedSource2(
- new ThrottledSource(
- mConnectingDataSource, 50 * 1024 /* bytes/sec */));
+ mCachedSource = new NuCachedSource2(
+ new ThrottledSource(
+ mConnectingDataSource, 50 * 1024 /* bytes/sec */));
#else
- mCachedSource = new NuCachedSource2(mConnectingDataSource);
+ mCachedSource = new NuCachedSource2(mConnectingDataSource);
#endif
+
+ dataSource = mCachedSource;
+ } else {
+ dataSource = mConnectingDataSource;
+ }
+
mConnectingDataSource.clear();
- dataSource = mCachedSource;
- // 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.
+ String8 contentType = dataSource->getMIMEType();
- mLock.unlock();
+ 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.
- for (;;) {
- status_t finalStatus;
- size_t cachedDataRemaining =
- mCachedSource->approxDataRemaining(&finalStatus);
+ // 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.
- if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes
- || (mFlags & PREPARE_CANCELLED)) {
- break;
+ mLock.unlock();
+
+ for (;;) {
+ status_t finalStatus;
+ size_t cachedDataRemaining =
+ mCachedSource->approxDataRemaining(&finalStatus);
+
+ if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes
+ || (mFlags & PREPARE_CANCELLED)) {
+ break;
+ }
+
+ usleep(200000);
+ }
+
+ mLock.lock();
}
- usleep(200000);
+ if (mFlags & PREPARE_CANCELLED) {
+ LOGI("Prepare cancelled while waiting for initial cache fill.");
+ return UNKNOWN_ERROR;
+ }
}
-
- mLock.lock();
-
- if (mFlags & PREPARE_CANCELLED) {
- LOGI("Prepare cancelled while waiting for initial cache fill.");
- return UNKNOWN_ERROR;
- }
- } else if (!strncasecmp(mUri.string(), "httplive://", 11)) {
- String8 uri("http://");
- uri.append(mUri.string() + 11);
-
- if (mLooper == NULL) {
- mLooper = new ALooper;
- mLooper->setName("httplive");
- mLooper->start();
- }
-
- mLiveSession = new LiveSession;
- mLooper->registerHandler(mLiveSession);
-
- mLiveSession->connect(uri.string());
- dataSource = mLiveSession->getDataSource();
-
- sp<MediaExtractor> extractor =
- MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
-
- static_cast<MPEG2TSExtractor *>(extractor.get())
- ->setLiveSession(mLiveSession);
-
- return setDataSource_l(extractor);
- } else if (!strncmp("rtsp://gtalk/", mUri.string(), 13)) {
- if (mLooper == NULL) {
- mLooper = new ALooper;
- mLooper->setName("gtalk rtp");
- mLooper->start(
- false /* runOnCallingThread */,
- false /* canCallJava */,
- PRIORITY_HIGHEST);
- }
-
- const char *startOfCodecString = &mUri.string()[13];
- const char *startOfSlash1 = strchr(startOfCodecString, '/');
- if (startOfSlash1 == NULL) {
- return BAD_VALUE;
- }
- const char *startOfWidthString = &startOfSlash1[1];
- const char *startOfSlash2 = strchr(startOfWidthString, '/');
- if (startOfSlash2 == NULL) {
- return BAD_VALUE;
- }
- const char *startOfHeightString = &startOfSlash2[1];
-
- String8 codecString(startOfCodecString, startOfSlash1 - startOfCodecString);
- String8 widthString(startOfWidthString, startOfSlash2 - startOfWidthString);
- String8 heightString(startOfHeightString);
-
-#if 0
- mRTPPusher = new UDPPusher("/data/misc/rtpout.bin", 5434);
- mLooper->registerHandler(mRTPPusher);
-
- mRTCPPusher = new UDPPusher("/data/misc/rtcpout.bin", 5435);
- mLooper->registerHandler(mRTCPPusher);
-#endif
-
- mRTPSession = new ARTPSession;
- mLooper->registerHandler(mRTPSession);
-
-#if 0
- // My AMR SDP
- static const char *raw =
- "v=0\r\n"
- "o=- 64 233572944 IN IP4 127.0.0.0\r\n"
- "s=QuickTime\r\n"
- "t=0 0\r\n"
- "a=range:npt=0-315\r\n"
- "a=isma-compliance:2,2.0,2\r\n"
- "m=audio 5434 RTP/AVP 97\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "b=AS:30\r\n"
- "a=rtpmap:97 AMR/8000/1\r\n"
- "a=fmtp:97 octet-align\r\n";
-#elif 1
- String8 sdp;
- sdp.appendFormat(
- "v=0\r\n"
- "o=- 64 233572944 IN IP4 127.0.0.0\r\n"
- "s=QuickTime\r\n"
- "t=0 0\r\n"
- "a=range:npt=0-315\r\n"
- "a=isma-compliance:2,2.0,2\r\n"
- "m=video 5434 RTP/AVP 97\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "b=AS:30\r\n"
- "a=rtpmap:97 %s/90000\r\n"
- "a=cliprect:0,0,%s,%s\r\n"
- "a=framesize:97 %s-%s\r\n",
-
- codecString.string(),
- heightString.string(), widthString.string(),
- widthString.string(), heightString.string()
- );
- const char *raw = sdp.string();
-
-#endif
-
- sp<ASessionDescription> desc = new ASessionDescription;
- CHECK(desc->setTo(raw, strlen(raw)));
-
- CHECK_EQ(mRTPSession->setup(desc), (status_t)OK);
-
- if (mRTPPusher != NULL) {
- mRTPPusher->start();
- }
-
- if (mRTCPPusher != NULL) {
- mRTCPPusher->start();
- }
-
- CHECK_EQ(mRTPSession->countTracks(), 1u);
- sp<MediaSource> source = mRTPSession->trackAt(0);
-
-#if 0
- bool eos;
- while (((APacketSource *)source.get())
- ->getQueuedDuration(&eos) < 5000000ll && !eos) {
- usleep(100000ll);
- }
-#endif
-
- const char *mime;
- CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
-
- if (!strncasecmp("video/", mime, 6)) {
- setVideoSource(source);
- } else {
- CHECK(!strncasecmp("audio/", mime, 6));
- setAudioSource(source);
- }
-
- mExtractorFlags = MediaExtractor::CAN_PAUSE;
-
- return OK;
} else if (!strncasecmp("rtsp://", mUri.string(), 7)) {
if (mLooper == NULL) {
mLooper = new ALooper;
@@ -1772,7 +1889,13 @@
mLooper->start();
}
mRTSPController = new ARTSPController(mLooper);
+ mConnectingRTSPController = mRTSPController;
+
+ mLock.unlock();
status_t err = mRTSPController->connect(mUri.string());
+ mLock.lock();
+
+ mConnectingRTSPController.clear();
LOGI("ARTSPController::connect returned %d", err);
@@ -1791,25 +1914,49 @@
return UNKNOWN_ERROR;
}
- sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+ sp<MediaExtractor> extractor;
- if (extractor == NULL) {
- return UNKNOWN_ERROR;
- }
+ if (isWidevineStreaming) {
+ String8 mimeType;
+ float confidence;
+ sp<AMessage> dummy;
+ bool success = SniffDRM(dataSource, &mimeType, &confidence, &dummy);
- dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
- if (mDecryptHandle != NULL) {
- if (RightsStatus::RIGHTS_VALID == mDecryptHandle->status) {
- if (DecryptApiType::WV_BASED == mDecryptHandle->decryptApiType) {
- LOGD("Setting mCachedSource to NULL for WVM\n");
- mCachedSource.clear();
- }
- } else {
- notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+ if (!success
+ || strcasecmp(
+ mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mWVMExtractor = new WVMExtractor(dataSource);
+ mWVMExtractor->setAdaptiveStreamingMode(true);
+ extractor = mWVMExtractor;
+ } else {
+ extractor = MediaExtractor::Create(dataSource);
+
+ if (extractor == NULL) {
+ return UNKNOWN_ERROR;
}
}
- return setDataSource_l(extractor);
+ 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 err = setDataSource_l(extractor);
+
+ if (err != OK) {
+ mWVMExtractor.clear();
+
+ return err;
+ }
+
+ return OK;
}
void AwesomePlayer::abortPrepare(status_t err) {
@@ -1870,7 +2017,7 @@
mFlags |= PREPARING_CONNECTED;
- if (mCachedSource != NULL || mRTSPController != NULL) {
+ if (isStreamingHTTP() || mRTSPController != NULL) {
postBufferingEvent_l();
} else {
finishAsyncPrepare_l();
@@ -1899,12 +2046,33 @@
return mExtractorFlags;
}
-void AwesomePlayer::postAudioEOS() {
- postCheckAudioStatusEvent_l();
+void AwesomePlayer::postAudioEOS(int64_t delayUs) {
+ Mutex::Autolock autoLock(mLock);
+ postCheckAudioStatusEvent_l(delayUs);
}
void AwesomePlayer::postAudioSeekComplete() {
- postCheckAudioStatusEvent_l();
+ Mutex::Autolock autoLock(mLock);
+ postAudioSeekComplete_l();
+}
+
+void AwesomePlayer::postAudioSeekComplete_l() {
+ postCheckAudioStatusEvent_l(0 /* delayUs */);
+}
+
+status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
+ if (key == KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX) {
+ return setTimedTextTrackIndex(request.readInt32());
+ }
+ return ERROR_UNSUPPORTED;
+}
+
+status_t AwesomePlayer::getParameter(int key, Parcel *reply) {
+ return OK;
+}
+
+bool AwesomePlayer::isStreamingHTTP() const {
+ return mCachedSource != NULL || mWVMExtractor != NULL;
}
} // namespace android
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 66e0657..a1f04d3 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -598,8 +598,7 @@
}
if (mNumGlitches > 0) {
- LOGW("%d long delays between neighboring video frames during",
- mNumGlitches);
+ LOGW("%d long delays between neighboring video frames", mNumGlitches);
}
CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped);
@@ -696,10 +695,9 @@
int32_t msgType, const sp<IMemory> &data) {
LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs);
Mutex::Autolock autoLock(mLock);
- if (!mStarted) {
+ if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) {
+ LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs);
releaseOneRecordingFrame(data);
- ++mNumFramesReceived;
- ++mNumFramesDropped;
return;
}
@@ -742,28 +740,6 @@
mFrameAvailableCondition.signal();
}
-size_t CameraSource::getNumberOfVideoBuffers() const {
- LOGV("getNumberOfVideoBuffers");
- size_t nBuffers = 0;
- int64_t token = IPCThreadState::self()->clearCallingIdentity();
- if (mInitCheck == OK && mCamera != 0) {
- nBuffers = mCamera->getNumberOfVideoBuffers();
- }
- IPCThreadState::self()->restoreCallingIdentity(token);
- return nBuffers;
-}
-
-sp<IMemory> CameraSource::getVideoBuffer(size_t index) const {
- LOGV("getVideoBuffer: %d", index);
- sp<IMemory> buffer = 0;
- int64_t token = IPCThreadState::self()->clearCallingIdentity();
- if (mInitCheck == OK && mCamera != 0) {
- buffer = mCamera->getVideoBuffer(index);
- }
- IPCThreadState::self()->restoreCallingIdentity(token);
- return buffer;
-}
-
bool CameraSource::isMetaDataStoredInVideoBuffers() const {
LOGV("isMetaDataStoredInVideoBuffers");
return mIsMetaDataStoredInVideoBuffers;
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index e6fe618..3689557 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -277,7 +277,7 @@
// this thread as read() will make a copy of this last frame and keep
// returning it in the quick stop mode.
Mutex::Autolock autoLock(mQuickStopLock);
- CHECK_EQ(OK, mCamera->takePicture());
+ CHECK_EQ(OK, mCamera->takePicture(CAMERA_MSG_RAW_IMAGE));
if (mQuickStop) {
LOGV("threadTimeLapseEntry: Exiting due to mQuickStop = true");
return;
diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp
index 3c98932..1f3d581 100644
--- a/media/libstagefright/DRMExtractor.cpp
+++ b/media/libstagefright/DRMExtractor.cpp
@@ -38,12 +38,12 @@
namespace android {
-DrmManagerClient* gDrmManagerClient = NULL;
-
class DRMSource : public MediaSource {
public:
DRMSource(const sp<MediaSource> &mediaSource,
- DecryptHandle* decryptHandle, int32_t trackId, DrmBuffer* ipmpBox);
+ const sp<DecryptHandle> &decryptHandle,
+ DrmManagerClient *managerClient,
+ int32_t trackId, DrmBuffer *ipmpBox);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -56,7 +56,8 @@
private:
sp<MediaSource> mOriginalMediaSource;
- DecryptHandle* mDecryptHandle;
+ sp<DecryptHandle> mDecryptHandle;
+ DrmManagerClient* mDrmManagerClient;
size_t mTrackId;
mutable Mutex mDRMLock;
size_t mNALLengthSize;
@@ -69,13 +70,17 @@
////////////////////////////////////////////////////////////////////////////////
DRMSource::DRMSource(const sp<MediaSource> &mediaSource,
- DecryptHandle* decryptHandle, int32_t trackId, DrmBuffer* ipmpBox)
+ const sp<DecryptHandle> &decryptHandle,
+ DrmManagerClient *managerClient,
+ int32_t trackId, DrmBuffer *ipmpBox)
: mOriginalMediaSource(mediaSource),
mDecryptHandle(decryptHandle),
+ mDrmManagerClient(managerClient),
mTrackId(trackId),
mNALLengthSize(0),
mWantsNALFragments(false) {
- gDrmManagerClient->initializeDecryptUnit(
+ CHECK(mDrmManagerClient);
+ mDrmManagerClient->initializeDecryptUnit(
mDecryptHandle, trackId, ipmpBox);
const char *mime;
@@ -100,7 +105,7 @@
DRMSource::~DRMSource() {
Mutex::Autolock autoLock(mDRMLock);
- gDrmManagerClient->finalizeDecryptUnit(mDecryptHandle, mTrackId);
+ mDrmManagerClient->finalizeDecryptUnit(mDecryptHandle, mTrackId);
}
status_t DRMSource::start(MetaData *params) {
@@ -140,19 +145,15 @@
decryptedDrmBuffer.data = new char[len];
DrmBuffer *pDecryptedDrmBuffer = &decryptedDrmBuffer;
- if ((err = gDrmManagerClient->decrypt(mDecryptHandle, mTrackId,
- &encryptedDrmBuffer, &pDecryptedDrmBuffer)) != DRM_NO_ERROR) {
+ if ((err = mDrmManagerClient->decrypt(mDecryptHandle, mTrackId,
+ &encryptedDrmBuffer, &pDecryptedDrmBuffer)) != NO_ERROR) {
if (decryptedDrmBuffer.data) {
delete [] decryptedDrmBuffer.data;
decryptedDrmBuffer.data = NULL;
}
- if (err == DRM_ERROR_LICENSE_EXPIRED) {
- return ERROR_NO_LICENSE;
- } else {
- return ERROR_IO;
- }
+ return err;
}
CHECK(pDecryptedDrmBuffer == &decryptedDrmBuffer);
@@ -234,12 +235,13 @@
DRMExtractor::DRMExtractor(const sp<DataSource> &source, const char* mime)
: mDataSource(source),
- mDecryptHandle(NULL) {
+ mDecryptHandle(NULL),
+ mDrmManagerClient(NULL) {
mOriginalExtractor = MediaExtractor::Create(source, mime);
mOriginalExtractor->setDrmFlag(true);
+ mOriginalExtractor->getMetaData()->setInt32(kKeyIsDRM, 1);
- DrmManagerClient *client;
- source->getDrmInfo(&mDecryptHandle, &client);
+ source->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
}
DRMExtractor::~DRMExtractor() {
@@ -260,7 +262,8 @@
ipmpBox.data = mOriginalExtractor->getDrmTrackInfo(trackID, &(ipmpBox.length));
CHECK(ipmpBox.length > 0);
- return new DRMSource(originalMediaSource, mDecryptHandle, trackID, &ipmpBox);
+ return new DRMSource(originalMediaSource, mDecryptHandle, mDrmManagerClient,
+ trackID, &ipmpBox);
}
sp<MetaData> DRMExtractor::getTrackMetaData(size_t index, uint32_t flags) {
@@ -271,22 +274,10 @@
return mOriginalExtractor->getMetaData();
}
-static Mutex gDRMSnifferMutex;
bool SniffDRM(
const sp<DataSource> &source, String8 *mimeType, float *confidence,
sp<AMessage> *) {
- {
- Mutex::Autolock autoLock(gDRMSnifferMutex);
- if (gDrmManagerClient == NULL) {
- gDrmManagerClient = new DrmManagerClient();
- }
-
- if (gDrmManagerClient == NULL) {
- return false;
- }
- }
-
- DecryptHandle *decryptHandle = source->DrmInitialization(gDrmManagerClient);
+ sp<DecryptHandle> decryptHandle = source->DrmInitialization();
if (decryptHandle != NULL) {
if (decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED) {
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index ee0d792..c16b3b5 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -15,14 +15,17 @@
*/
#include "include/AMRExtractor.h"
+#include "include/AVIExtractor.h"
#include "include/MP3Extractor.h"
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
#include "include/MPEG2TSExtractor.h"
#include "include/NuCachedSource2.h"
-#include "include/NuHTTPDataSource.h"
+#include "include/HTTPBase.h"
#include "include/DRMExtractor.h"
+#include "include/FLACExtractor.h"
+#include "include/AACExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -104,9 +107,12 @@
RegisterSniffer(SniffMatroska);
RegisterSniffer(SniffOgg);
RegisterSniffer(SniffWAV);
+ RegisterSniffer(SniffFLAC);
RegisterSniffer(SniffAMR);
RegisterSniffer(SniffMPEG2TS);
RegisterSniffer(SniffMP3);
+ RegisterSniffer(SniffAAC);
+ RegisterSniffer(SniffAVI);
char value[PROPERTY_VALUE_MAX];
if (property_get("drm.service.enabled", value, NULL)
@@ -121,8 +127,9 @@
sp<DataSource> source;
if (!strncasecmp("file://", uri, 7)) {
source = new FileSource(uri + 7);
- } else if (!strncasecmp("http://", uri, 7)) {
- sp<NuHTTPDataSource> httpSource = new NuHTTPDataSource;
+ } else if (!strncasecmp("http://", uri, 7)
+ || !strncasecmp("https://", uri, 8)) {
+ sp<HTTPBase> httpSource = HTTPBase::Create();
if (httpSource->connect(uri, headers) != OK) {
return NULL;
}
@@ -139,4 +146,8 @@
return source;
}
+String8 DataSource::getMIMEType() const {
+ return String8("application/octet-stream");
+}
+
} // namespace android
diff --git a/media/libstagefright/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp
new file mode 100644
index 0000000..8ba5a2d
--- /dev/null
+++ b/media/libstagefright/FLACExtractor.cpp
@@ -0,0 +1,813 @@
+/*
+ * Copyright (C) 2011 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 "FLACExtractor"
+#include <utils/Log.h>
+
+#include "include/FLACExtractor.h"
+// Vorbis comments
+#include "include/OggExtractor.h"
+// libFLAC parser
+#include "FLAC/stream_decoder.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MediaBuffer.h>
+
+namespace android {
+
+class FLACParser;
+
+class FLACSource : public MediaSource {
+
+public:
+ FLACSource(
+ const sp<DataSource> &dataSource,
+ const sp<MetaData> &trackMetadata);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~FLACSource();
+
+private:
+ sp<DataSource> mDataSource;
+ sp<MetaData> mTrackMetadata;
+ sp<FLACParser> mParser;
+ bool mInitCheck;
+ bool mStarted;
+
+ status_t init();
+
+ // no copy constructor or assignment
+ FLACSource(const FLACSource &);
+ FLACSource &operator=(const FLACSource &);
+
+};
+
+// FLACParser wraps a C libFLAC parser aka stream decoder
+
+class FLACParser : public RefBase {
+
+public:
+ FLACParser(
+ const sp<DataSource> &dataSource,
+ // If metadata pointers aren't provided, we don't fill them
+ const sp<MetaData> &fileMetadata = 0,
+ const sp<MetaData> &trackMetadata = 0);
+
+ status_t initCheck() const {
+ return mInitCheck;
+ }
+
+ // stream properties
+ unsigned getMaxBlockSize() const {
+ return mStreamInfo.max_blocksize;
+ }
+ unsigned getSampleRate() const {
+ return mStreamInfo.sample_rate;
+ }
+ unsigned getChannels() const {
+ return mStreamInfo.channels;
+ }
+ unsigned getBitsPerSample() const {
+ return mStreamInfo.bits_per_sample;
+ }
+ FLAC__uint64 getTotalSamples() const {
+ return mStreamInfo.total_samples;
+ }
+
+ // media buffers
+ void allocateBuffers();
+ void releaseBuffers();
+ MediaBuffer *readBuffer() {
+ return readBuffer(false, 0LL);
+ }
+ MediaBuffer *readBuffer(FLAC__uint64 sample) {
+ return readBuffer(true, sample);
+ }
+
+protected:
+ virtual ~FLACParser();
+
+private:
+ sp<DataSource> mDataSource;
+ sp<MetaData> mFileMetadata;
+ sp<MetaData> mTrackMetadata;
+ bool mInitCheck;
+
+ // media buffers
+ size_t mMaxBufferSize;
+ MediaBufferGroup *mGroup;
+ void (*mCopy)(short *dst, const int *const *src, unsigned nSamples);
+
+ // handle to underlying libFLAC parser
+ FLAC__StreamDecoder *mDecoder;
+
+ // current position within the data source
+ off64_t mCurrentPos;
+ bool mEOF;
+
+ // cached when the STREAMINFO metadata is parsed by libFLAC
+ FLAC__StreamMetadata_StreamInfo mStreamInfo;
+ bool mStreamInfoValid;
+
+ // cached when a decoded PCM block is "written" by libFLAC parser
+ bool mWriteRequested;
+ bool mWriteCompleted;
+ FLAC__FrameHeader mWriteHeader;
+ const FLAC__int32 * const *mWriteBuffer;
+
+ // most recent error reported by libFLAC parser
+ FLAC__StreamDecoderErrorStatus mErrorStatus;
+
+ status_t init();
+ MediaBuffer *readBuffer(bool doSeek, FLAC__uint64 sample);
+
+ // no copy constructor or assignment
+ FLACParser(const FLACParser &);
+ FLACParser &operator=(const FLACParser &);
+
+ // FLAC parser callbacks as C++ instance methods
+ FLAC__StreamDecoderReadStatus readCallback(
+ FLAC__byte buffer[], size_t *bytes);
+ FLAC__StreamDecoderSeekStatus seekCallback(
+ FLAC__uint64 absolute_byte_offset);
+ FLAC__StreamDecoderTellStatus tellCallback(
+ FLAC__uint64 *absolute_byte_offset);
+ FLAC__StreamDecoderLengthStatus lengthCallback(
+ FLAC__uint64 *stream_length);
+ FLAC__bool eofCallback();
+ FLAC__StreamDecoderWriteStatus writeCallback(
+ const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
+ void metadataCallback(const FLAC__StreamMetadata *metadata);
+ void errorCallback(FLAC__StreamDecoderErrorStatus status);
+
+ // FLAC parser callbacks as C-callable functions
+ static FLAC__StreamDecoderReadStatus read_callback(
+ const FLAC__StreamDecoder *decoder,
+ FLAC__byte buffer[], size_t *bytes,
+ void *client_data);
+ static FLAC__StreamDecoderSeekStatus seek_callback(
+ const FLAC__StreamDecoder *decoder,
+ FLAC__uint64 absolute_byte_offset,
+ void *client_data);
+ static FLAC__StreamDecoderTellStatus tell_callback(
+ const FLAC__StreamDecoder *decoder,
+ FLAC__uint64 *absolute_byte_offset,
+ void *client_data);
+ static FLAC__StreamDecoderLengthStatus length_callback(
+ const FLAC__StreamDecoder *decoder,
+ FLAC__uint64 *stream_length,
+ void *client_data);
+ static FLAC__bool eof_callback(
+ const FLAC__StreamDecoder *decoder,
+ void *client_data);
+ static FLAC__StreamDecoderWriteStatus write_callback(
+ const FLAC__StreamDecoder *decoder,
+ const FLAC__Frame *frame, const FLAC__int32 * const buffer[],
+ void *client_data);
+ static void metadata_callback(
+ const FLAC__StreamDecoder *decoder,
+ const FLAC__StreamMetadata *metadata,
+ void *client_data);
+ static void error_callback(
+ const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status,
+ void *client_data);
+
+};
+
+// The FLAC parser calls our C++ static callbacks using C calling conventions,
+// inside FLAC__stream_decoder_process_until_end_of_metadata
+// and FLAC__stream_decoder_process_single.
+// We immediately then call our corresponding C++ instance methods
+// with the same parameter list, but discard redundant information.
+
+FLAC__StreamDecoderReadStatus FLACParser::read_callback(
+ const FLAC__StreamDecoder *decoder, FLAC__byte buffer[],
+ size_t *bytes, void *client_data)
+{
+ return ((FLACParser *) client_data)->readCallback(buffer, bytes);
+}
+
+FLAC__StreamDecoderSeekStatus FLACParser::seek_callback(
+ const FLAC__StreamDecoder *decoder,
+ FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+ return ((FLACParser *) client_data)->seekCallback(absolute_byte_offset);
+}
+
+FLAC__StreamDecoderTellStatus FLACParser::tell_callback(
+ const FLAC__StreamDecoder *decoder,
+ FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ return ((FLACParser *) client_data)->tellCallback(absolute_byte_offset);
+}
+
+FLAC__StreamDecoderLengthStatus FLACParser::length_callback(
+ const FLAC__StreamDecoder *decoder,
+ FLAC__uint64 *stream_length, void *client_data)
+{
+ return ((FLACParser *) client_data)->lengthCallback(stream_length);
+}
+
+FLAC__bool FLACParser::eof_callback(
+ const FLAC__StreamDecoder *decoder, void *client_data)
+{
+ return ((FLACParser *) client_data)->eofCallback();
+}
+
+FLAC__StreamDecoderWriteStatus FLACParser::write_callback(
+ const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
+ const FLAC__int32 * const buffer[], void *client_data)
+{
+ return ((FLACParser *) client_data)->writeCallback(frame, buffer);
+}
+
+void FLACParser::metadata_callback(
+ const FLAC__StreamDecoder *decoder,
+ const FLAC__StreamMetadata *metadata, void *client_data)
+{
+ ((FLACParser *) client_data)->metadataCallback(metadata);
+}
+
+void FLACParser::error_callback(
+ const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ ((FLACParser *) client_data)->errorCallback(status);
+}
+
+// These are the corresponding callbacks with C++ calling conventions
+
+FLAC__StreamDecoderReadStatus FLACParser::readCallback(
+ FLAC__byte buffer[], size_t *bytes)
+{
+ size_t requested = *bytes;
+ ssize_t actual = mDataSource->readAt(mCurrentPos, buffer, requested);
+ if (0 > actual) {
+ *bytes = 0;
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ } else if (0 == actual) {
+ *bytes = 0;
+ mEOF = true;
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ } else {
+ assert(actual <= requested);
+ *bytes = actual;
+ mCurrentPos += actual;
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ }
+}
+
+FLAC__StreamDecoderSeekStatus FLACParser::seekCallback(
+ FLAC__uint64 absolute_byte_offset)
+{
+ mCurrentPos = absolute_byte_offset;
+ mEOF = false;
+ return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+FLAC__StreamDecoderTellStatus FLACParser::tellCallback(
+ FLAC__uint64 *absolute_byte_offset)
+{
+ *absolute_byte_offset = mCurrentPos;
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+FLAC__StreamDecoderLengthStatus FLACParser::lengthCallback(
+ FLAC__uint64 *stream_length)
+{
+ off64_t size;
+ if (OK == mDataSource->getSize(&size)) {
+ *stream_length = size;
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+ } else {
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
+ }
+}
+
+FLAC__bool FLACParser::eofCallback()
+{
+ return mEOF;
+}
+
+FLAC__StreamDecoderWriteStatus FLACParser::writeCallback(
+ const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
+{
+ if (mWriteRequested) {
+ mWriteRequested = false;
+ // FLAC parser doesn't free or realloc buffer until next frame or finish
+ mWriteHeader = frame->header;
+ mWriteBuffer = buffer;
+ mWriteCompleted = true;
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+ } else {
+ LOGE("FLACParser::writeCallback unexpected");
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ }
+}
+
+void FLACParser::metadataCallback(const FLAC__StreamMetadata *metadata)
+{
+ switch (metadata->type) {
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ if (!mStreamInfoValid) {
+ mStreamInfo = metadata->data.stream_info;
+ mStreamInfoValid = true;
+ } else {
+ LOGE("FLACParser::metadataCallback unexpected STREAMINFO");
+ }
+ break;
+ case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+ {
+ const FLAC__StreamMetadata_VorbisComment *vc;
+ vc = &metadata->data.vorbis_comment;
+ for (FLAC__uint32 i = 0; i < vc->num_comments; ++i) {
+ FLAC__StreamMetadata_VorbisComment_Entry *vce;
+ vce = &vc->comments[i];
+ if (mFileMetadata != 0) {
+ parseVorbisComment(mFileMetadata, (const char *) vce->entry,
+ vce->length);
+ }
+ }
+ }
+ break;
+ case FLAC__METADATA_TYPE_PICTURE:
+ if (mFileMetadata != 0) {
+ const FLAC__StreamMetadata_Picture *p = &metadata->data.picture;
+ mFileMetadata->setData(kKeyAlbumArt,
+ MetaData::TYPE_NONE, p->data, p->data_length);
+ mFileMetadata->setCString(kKeyAlbumArtMIME, p->mime_type);
+ }
+ break;
+ default:
+ LOGW("FLACParser::metadataCallback unexpected type %u", metadata->type);
+ break;
+ }
+}
+
+void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status)
+{
+ LOGE("FLACParser::errorCallback status=%d", status);
+ mErrorStatus = status;
+}
+
+// Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved.
+// These are candidates for optimization if needed.
+
+static void copyMono8(short *dst, const int *const *src, unsigned nSamples)
+{
+ for (unsigned i = 0; i < nSamples; ++i) {
+ *dst++ = src[0][i] << 8;
+ }
+}
+
+static void copyStereo8(short *dst, const int *const *src, unsigned nSamples)
+{
+ for (unsigned i = 0; i < nSamples; ++i) {
+ *dst++ = src[0][i] << 8;
+ *dst++ = src[1][i] << 8;
+ }
+}
+
+static void copyMono16(short *dst, const int *const *src, unsigned nSamples)
+{
+ for (unsigned i = 0; i < nSamples; ++i) {
+ *dst++ = src[0][i];
+ }
+}
+
+static void copyStereo16(short *dst, const int *const *src, unsigned nSamples)
+{
+ for (unsigned i = 0; i < nSamples; ++i) {
+ *dst++ = src[0][i];
+ *dst++ = src[1][i];
+ }
+}
+
+// 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger
+
+static void copyMono24(short *dst, const int *const *src, unsigned nSamples)
+{
+ for (unsigned i = 0; i < nSamples; ++i) {
+ *dst++ = src[0][i] >> 8;
+ }
+}
+
+static void copyStereo24(short *dst, const int *const *src, unsigned nSamples)
+{
+ for (unsigned i = 0; i < nSamples; ++i) {
+ *dst++ = src[0][i] >> 8;
+ *dst++ = src[1][i] >> 8;
+ }
+}
+
+static void copyTrespass(short *dst, const int *const *src, unsigned nSamples)
+{
+ TRESPASS();
+}
+
+// FLACParser
+
+FLACParser::FLACParser(
+ const sp<DataSource> &dataSource,
+ const sp<MetaData> &fileMetadata,
+ const sp<MetaData> &trackMetadata)
+ : mDataSource(dataSource),
+ mFileMetadata(fileMetadata),
+ mTrackMetadata(trackMetadata),
+ mInitCheck(false),
+ mMaxBufferSize(0),
+ mGroup(NULL),
+ mCopy(copyTrespass),
+ mDecoder(NULL),
+ mCurrentPos(0LL),
+ mEOF(false),
+ mStreamInfoValid(false),
+ mWriteRequested(false),
+ mWriteCompleted(false),
+ mWriteBuffer(NULL),
+ mErrorStatus((FLAC__StreamDecoderErrorStatus) -1)
+{
+ LOGV("FLACParser::FLACParser");
+ memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+ memset(&mWriteHeader, 0, sizeof(mWriteHeader));
+ mInitCheck = init();
+}
+
+FLACParser::~FLACParser()
+{
+ LOGV("FLACParser::~FLACParser");
+ if (mDecoder != NULL) {
+ FLAC__stream_decoder_delete(mDecoder);
+ mDecoder = NULL;
+ }
+}
+
+status_t FLACParser::init()
+{
+ // setup libFLAC parser
+ mDecoder = FLAC__stream_decoder_new();
+ if (mDecoder == NULL) {
+ // The new should succeed, since probably all it does is a malloc
+ // that always succeeds in Android. But to avoid dependence on the
+ // libFLAC internals, we check and log here.
+ LOGE("new failed");
+ return NO_INIT;
+ }
+ FLAC__stream_decoder_set_md5_checking(mDecoder, false);
+ FLAC__stream_decoder_set_metadata_ignore_all(mDecoder);
+ FLAC__stream_decoder_set_metadata_respond(
+ mDecoder, FLAC__METADATA_TYPE_STREAMINFO);
+ FLAC__stream_decoder_set_metadata_respond(
+ mDecoder, FLAC__METADATA_TYPE_PICTURE);
+ FLAC__stream_decoder_set_metadata_respond(
+ mDecoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+ FLAC__StreamDecoderInitStatus initStatus;
+ initStatus = FLAC__stream_decoder_init_stream(
+ mDecoder,
+ read_callback, seek_callback, tell_callback,
+ length_callback, eof_callback, write_callback,
+ metadata_callback, error_callback, (void *) this);
+ if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+ // A failure here probably indicates a programming error and so is
+ // unlikely to happen. But we check and log here similarly to above.
+ LOGE("init_stream failed %d", initStatus);
+ return NO_INIT;
+ }
+ // parse all metadata
+ if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder)) {
+ LOGE("end_of_metadata failed");
+ return NO_INIT;
+ }
+ if (mStreamInfoValid) {
+ // check channel count
+ switch (getChannels()) {
+ case 1:
+ case 2:
+ break;
+ default:
+ LOGE("unsupported channel count %u", getChannels());
+ return NO_INIT;
+ }
+ // check bit depth
+ switch (getBitsPerSample()) {
+ case 8:
+ case 16:
+ case 24:
+ break;
+ default:
+ LOGE("unsupported bits per sample %u", getBitsPerSample());
+ return NO_INIT;
+ }
+ // check sample rate
+ switch (getSampleRate()) {
+ case 8000:
+ case 11025:
+ case 12000:
+ case 16000:
+ case 22050:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ break;
+ default:
+ // 96000 would require a proper downsampler in AudioFlinger
+ LOGE("unsupported sample rate %u", getSampleRate());
+ return NO_INIT;
+ }
+ // configure the appropriate copy function, defaulting to trespass
+ static const struct {
+ unsigned mChannels;
+ unsigned mBitsPerSample;
+ void (*mCopy)(short *dst, const int *const *src, unsigned nSamples);
+ } table[] = {
+ { 1, 8, copyMono8 },
+ { 2, 8, copyStereo8 },
+ { 1, 16, copyMono16 },
+ { 2, 16, copyStereo16 },
+ { 1, 24, copyMono24 },
+ { 2, 24, copyStereo24 },
+ };
+ for (unsigned i = 0; i < sizeof(table)/sizeof(table[0]); ++i) {
+ if (table[i].mChannels == getChannels() &&
+ table[i].mBitsPerSample == getBitsPerSample()) {
+ mCopy = table[i].mCopy;
+ break;
+ }
+ }
+ // populate track metadata
+ if (mTrackMetadata != 0) {
+ mTrackMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ mTrackMetadata->setInt32(kKeyChannelCount, getChannels());
+ mTrackMetadata->setInt32(kKeySampleRate, getSampleRate());
+ // sample rate is non-zero, so division by zero not possible
+ mTrackMetadata->setInt64(kKeyDuration,
+ (getTotalSamples() * 1000000LL) / getSampleRate());
+ }
+ } else {
+ LOGE("missing STREAMINFO");
+ return NO_INIT;
+ }
+ if (mFileMetadata != 0) {
+ mFileMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_FLAC);
+ }
+ return OK;
+}
+
+void FLACParser::allocateBuffers()
+{
+ CHECK(mGroup == NULL);
+ mGroup = new MediaBufferGroup;
+ mMaxBufferSize = getMaxBlockSize() * getChannels() * sizeof(short);
+ mGroup->add_buffer(new MediaBuffer(mMaxBufferSize));
+}
+
+void FLACParser::releaseBuffers()
+{
+ CHECK(mGroup != NULL);
+ delete mGroup;
+ mGroup = NULL;
+}
+
+MediaBuffer *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample)
+{
+ mWriteRequested = true;
+ mWriteCompleted = false;
+ if (doSeek) {
+ // We implement the seek callback, so this works without explicit flush
+ if (!FLAC__stream_decoder_seek_absolute(mDecoder, sample)) {
+ LOGE("FLACParser::readBuffer seek to sample %llu failed", sample);
+ return NULL;
+ }
+ LOGV("FLACParser::readBuffer seek to sample %llu succeeded", sample);
+ } else {
+ if (!FLAC__stream_decoder_process_single(mDecoder)) {
+ LOGE("FLACParser::readBuffer process_single failed");
+ return NULL;
+ }
+ }
+ if (!mWriteCompleted) {
+ LOGV("FLACParser::readBuffer write did not complete");
+ return NULL;
+ }
+ // verify that block header keeps the promises made by STREAMINFO
+ unsigned blocksize = mWriteHeader.blocksize;
+ if (blocksize == 0 || blocksize > getMaxBlockSize()) {
+ LOGE("FLACParser::readBuffer write invalid blocksize %u", blocksize);
+ return NULL;
+ }
+ if (mWriteHeader.sample_rate != getSampleRate() ||
+ mWriteHeader.channels != getChannels() ||
+ mWriteHeader.bits_per_sample != getBitsPerSample()) {
+ LOGE("FLACParser::readBuffer write changed parameters mid-stream");
+ }
+ // acquire a media buffer
+ CHECK(mGroup != NULL);
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+ if (err != OK) {
+ return NULL;
+ }
+ size_t bufferSize = blocksize * getChannels() * sizeof(short);
+ CHECK(bufferSize <= mMaxBufferSize);
+ short *data = (short *) buffer->data();
+ buffer->set_range(0, bufferSize);
+ // copy PCM from FLAC write buffer to our media buffer, with interleaving
+ (*mCopy)(data, mWriteBuffer, blocksize);
+ // fill in buffer metadata
+ CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
+ FLAC__uint64 sampleNumber = mWriteHeader.number.sample_number;
+ int64_t timeUs = (1000000LL * sampleNumber) / getSampleRate();
+ buffer->meta_data()->setInt64(kKeyTime, timeUs);
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ return buffer;
+}
+
+// FLACsource
+
+FLACSource::FLACSource(
+ const sp<DataSource> &dataSource,
+ const sp<MetaData> &trackMetadata)
+ : mDataSource(dataSource),
+ mTrackMetadata(trackMetadata),
+ mParser(0),
+ mInitCheck(false),
+ mStarted(false)
+{
+ LOGV("FLACSource::FLACSource");
+ mInitCheck = init();
+}
+
+FLACSource::~FLACSource()
+{
+ LOGV("~FLACSource::FLACSource");
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t FLACSource::start(MetaData *params)
+{
+ LOGV("FLACSource::start");
+
+ CHECK(!mStarted);
+ mParser->allocateBuffers();
+ mStarted = true;
+
+ return OK;
+}
+
+status_t FLACSource::stop()
+{
+ LOGV("FLACSource::stop");
+
+ CHECK(mStarted);
+ mParser->releaseBuffers();
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> FLACSource::getFormat()
+{
+ return mTrackMetadata;
+}
+
+status_t FLACSource::read(
+ MediaBuffer **outBuffer, const ReadOptions *options)
+{
+ MediaBuffer *buffer;
+ // process an optional seek request
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ if ((NULL != options) && options->getSeekTo(&seekTimeUs, &mode)) {
+ FLAC__uint64 sample;
+ if (seekTimeUs <= 0LL) {
+ sample = 0LL;
+ } else {
+ // sample and total samples are both zero-based, and seek to EOF ok
+ sample = (seekTimeUs * mParser->getSampleRate()) / 1000000LL;
+ if (sample >= mParser->getTotalSamples()) {
+ sample = mParser->getTotalSamples();
+ }
+ }
+ buffer = mParser->readBuffer(sample);
+ // otherwise read sequentially
+ } else {
+ buffer = mParser->readBuffer();
+ }
+ *outBuffer = buffer;
+ return buffer != NULL ? (status_t) OK : (status_t) ERROR_END_OF_STREAM;
+}
+
+status_t FLACSource::init()
+{
+ LOGV("FLACSource::init");
+ // re-use the same track metadata passed into constructor from FLACExtractor
+ mParser = new FLACParser(mDataSource);
+ return mParser->initCheck();
+}
+
+// FLACExtractor
+
+FLACExtractor::FLACExtractor(
+ const sp<DataSource> &dataSource)
+ : mDataSource(dataSource),
+ mInitCheck(false)
+{
+ LOGV("FLACExtractor::FLACExtractor");
+ mInitCheck = init();
+}
+
+FLACExtractor::~FLACExtractor()
+{
+ LOGV("~FLACExtractor::FLACExtractor");
+}
+
+size_t FLACExtractor::countTracks()
+{
+ return mInitCheck == OK ? 1 : 0;
+}
+
+sp<MediaSource> FLACExtractor::getTrack(size_t index)
+{
+ if (mInitCheck != OK || index > 0) {
+ return NULL;
+ }
+ return new FLACSource(mDataSource, mTrackMetadata);
+}
+
+sp<MetaData> FLACExtractor::getTrackMetaData(
+ size_t index, uint32_t flags)
+{
+ if (mInitCheck != OK || index > 0) {
+ return NULL;
+ }
+ return mTrackMetadata;
+}
+
+status_t FLACExtractor::init()
+{
+ mFileMetadata = new MetaData;
+ mTrackMetadata = new MetaData;
+ // FLACParser will fill in the metadata for us
+ mParser = new FLACParser(mDataSource, mFileMetadata, mTrackMetadata);
+ return mParser->initCheck();
+}
+
+sp<MetaData> FLACExtractor::getMetaData()
+{
+ return mFileMetadata;
+}
+
+// Sniffer
+
+bool SniffFLAC(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *)
+{
+ // first 4 is the signature word
+ // second 4 is the sizeof STREAMINFO
+ // 042 is the mandatory STREAMINFO
+ // no need to read rest of the header, as a premature EOF will be caught later
+ uint8_t header[4+4];
+ if (source->readAt(0, header, sizeof(header)) != sizeof(header)
+ || memcmp("fLaC\0\0\0\042", header, 4+4))
+ {
+ return false;
+ }
+
+ *mimeType = MEDIA_MIMETYPE_AUDIO_FLAC;
+ *confidence = 0.5;
+
+ return true;
+}
+
+} // namespace android
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index 98d5b50..f2f3500 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -60,6 +60,18 @@
delete[] mDrmBuf;
mDrmBuf = NULL;
}
+
+ if (mDecryptHandle != NULL) {
+ // To release mDecryptHandle
+ CHECK(mDrmManagerClient);
+ mDrmManagerClient->closeDecryptSession(mDecryptHandle);
+ mDecryptHandle = NULL;
+ }
+
+ if (mDrmManagerClient != NULL) {
+ delete mDrmManagerClient;
+ mDrmManagerClient = NULL;
+ }
}
status_t FileSource::initCheck() const {
@@ -113,11 +125,14 @@
return OK;
}
-DecryptHandle* FileSource::DrmInitialization(DrmManagerClient* client) {
- if (client == NULL) {
+sp<DecryptHandle> FileSource::DrmInitialization() {
+ if (mDrmManagerClient == NULL) {
+ mDrmManagerClient = new DrmManagerClient();
+ }
+
+ if (mDrmManagerClient == NULL) {
return NULL;
}
- mDrmManagerClient = client;
if (mDecryptHandle == NULL) {
mDecryptHandle = mDrmManagerClient->openDecryptSession(
@@ -125,14 +140,15 @@
}
if (mDecryptHandle == NULL) {
+ delete mDrmManagerClient;
mDrmManagerClient = NULL;
}
return mDecryptHandle;
}
-void FileSource::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {
- *handle = mDecryptHandle;
+void FileSource::getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {
+ handle = mDecryptHandle;
*client = mDrmManagerClient;
}
diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp
new file mode 100644
index 0000000..58b17a7
--- /dev/null
+++ b/media/libstagefright/HTTPBase.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 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 "include/HTTPBase.h"
+
+#if CHROMIUM_AVAILABLE
+#include "include/ChromiumHTTPDataSource.h"
+#endif
+
+#include "include/NuHTTPDataSource.h"
+
+#include <cutils/properties.h>
+
+namespace android {
+
+HTTPBase::HTTPBase() {}
+
+// static
+sp<HTTPBase> HTTPBase::Create(uint32_t flags) {
+#if CHROMIUM_AVAILABLE
+ char value[PROPERTY_VALUE_MAX];
+ if (!property_get("media.stagefright.use-chromium", value, NULL)
+ || (strcasecmp("false", value) && strcmp("0", value))) {
+ return new ChromiumHTTPDataSource(flags);
+ } else
+#endif
+ {
+ return new NuHTTPDataSource(flags);
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 77a61a5..2caf211 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -34,6 +34,8 @@
#include <media/stagefright/foundation/ADebug.h>
+#include <openssl/ssl.h>
+
namespace android {
// static
@@ -41,11 +43,18 @@
HTTPStream::HTTPStream()
: mState(READY),
- mSocket(-1) {
+ mSocket(-1),
+ mSSLContext(NULL),
+ mSSL(NULL) {
}
HTTPStream::~HTTPStream() {
disconnect();
+
+ if (mSSLContext != NULL) {
+ SSL_CTX_free((SSL_CTX *)mSSLContext);
+ mSSLContext = NULL;
+ }
}
static bool MakeSocketBlocking(int s, bool blocking) {
@@ -198,7 +207,11 @@
return MySendReceive(s, data, size, flags, false /* sendData */);
}
-status_t HTTPStream::connect(const char *server, int port) {
+status_t HTTPStream::connect(const char *server, int port, bool https) {
+ if (port < 0) {
+ port = https ? 443 : 80;
+ }
+
Mutex::Autolock autoLock(mLock);
status_t err = OK;
@@ -207,40 +220,58 @@
return ERROR_ALREADY_CONNECTED;
}
- struct hostent *ent = gethostbyname(server);
- if (ent == NULL) {
+ if (port < 0 || port > (int) USHRT_MAX) {
+ return UNKNOWN_ERROR;
+ }
+
+ char service[sizeof("65536")];
+ sprintf(service, "%d", port);
+ struct addrinfo hints, *ai;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
+ hints.ai_socktype = SOCK_STREAM;
+
+ int ret = getaddrinfo(server, service, &hints, &ai);
+ if (ret) {
return ERROR_UNKNOWN_HOST;
}
CHECK_EQ(mSocket, -1);
- mSocket = socket(AF_INET, SOCK_STREAM, 0);
-
- if (mSocket < 0) {
- return UNKNOWN_ERROR;
- }
-
- setReceiveTimeout(30); // Time out reads after 30 secs by default
mState = CONNECTING;
+ status_t res = -1;
+ struct addrinfo *tmp;
+ for (tmp = ai; tmp; tmp = tmp->ai_next) {
+ mSocket = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol);
+ if (mSocket < 0) {
+ continue;
+ }
- int s = mSocket;
+ setReceiveTimeout(30); // Time out reads after 30 secs by default.
- mLock.unlock();
+ int s = mSocket;
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
- memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+ mLock.unlock();
- status_t res = MyConnect(s, (const struct sockaddr *)&addr, sizeof(addr));
+ res = MyConnect(s, tmp->ai_addr, tmp->ai_addrlen);
- mLock.lock();
+ mLock.lock();
- if (mState != CONNECTING) {
- return UNKNOWN_ERROR;
+ if (mState != CONNECTING) {
+ close(s);
+ freeaddrinfo(ai);
+ return UNKNOWN_ERROR;
+ }
+
+ if (res == OK) {
+ break;
+ }
+
+ close(s);
}
+ freeaddrinfo(ai);
+
if (res != OK) {
close(mSocket);
mSocket = -1;
@@ -249,6 +280,47 @@
return res;
}
+ if (https) {
+ CHECK(mSSL == NULL);
+
+ if (mSSLContext == NULL) {
+ SSL_library_init();
+
+ mSSLContext = SSL_CTX_new(TLSv1_client_method());
+
+ if (mSSLContext == NULL) {
+ LOGE("failed to create SSL context");
+ mState = READY;
+ return ERROR_IO;
+ }
+ }
+
+ mSSL = SSL_new((SSL_CTX *)mSSLContext);
+
+ if (mSSL == NULL) {
+ LOGE("failed to create SSL session");
+
+ mState = READY;
+ return ERROR_IO;
+ }
+
+ int res = SSL_set_fd((SSL *)mSSL, mSocket);
+
+ if (res == 1) {
+ res = SSL_connect((SSL *)mSSL);
+ }
+
+ if (res != 1) {
+ SSL_free((SSL *)mSSL);
+ mSSL = NULL;
+
+ LOGE("failed to connect over SSL");
+ mState = READY;
+
+ return ERROR_IO;
+ }
+ }
+
mState = CONNECTED;
return OK;
@@ -261,6 +333,13 @@
return ERROR_NOT_CONNECTED;
}
+ if (mSSL != NULL) {
+ SSL_shutdown((SSL *)mSSL);
+
+ SSL_free((SSL *)mSSL);
+ mSSL = NULL;
+ }
+
CHECK(mSocket >= 0);
close(mSocket);
mSocket = -1;
@@ -276,7 +355,16 @@
}
while (size > 0) {
- ssize_t n = MySend(mSocket, data, size, 0);
+ ssize_t n;
+ if (mSSL != NULL) {
+ n = SSL_write((SSL *)mSSL, data, size);
+
+ if (n < 0) {
+ n = -SSL_get_error((SSL *)mSSL, n);
+ }
+ } else {
+ n = MySend(mSocket, data, size, 0);
+ }
if (n < 0) {
disconnect();
@@ -317,7 +405,17 @@
for (;;) {
char c;
- ssize_t n = MyReceive(mSocket, &c, 1, 0);
+ ssize_t n;
+ if (mSSL != NULL) {
+ n = SSL_read((SSL *)mSSL, &c, 1);
+
+ if (n < 0) {
+ n = -SSL_get_error((SSL *)mSSL, n);
+ }
+ } else {
+ n = MyReceive(mSocket, &c, 1, 0);
+ }
+
if (n < 0) {
disconnect();
@@ -437,7 +535,16 @@
ssize_t HTTPStream::receive(void *data, size_t size) {
size_t total = 0;
while (total < size) {
- ssize_t n = MyReceive(mSocket, (char *)data + total, size - total, 0);
+ ssize_t n;
+ if (mSSL != NULL) {
+ n = SSL_read((SSL *)mSSL, (char *)data + total, size - total);
+
+ if (n < 0) {
+ n = -SSL_get_error((SSL *)mSSL, n);
+ }
+ } else {
+ n = MyReceive(mSocket, (char *)data + total, size - total, 0);
+ }
if (n < 0) {
LOGE("recv failed, errno = %d (%s)", (int)n, strerror(-n));
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 00a4dd5..4bdfc6f 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -39,16 +39,16 @@
namespace android {
// Everything must match except for
-// protection, bitrate, padding, private bits, mode extension,
+// protection, bitrate, padding, private bits, mode, mode extension,
// copyright bit, original bit and emphasis.
// Yes ... there are things that must indeed match...
-static const uint32_t kMask = 0xfffe0cc0;
+static const uint32_t kMask = 0xfffe0c00;
// static
bool MP3Extractor::get_mp3_frame_size(
uint32_t header, size_t *frame_size,
int *out_sampling_rate, int *out_channels,
- int *out_bitrate) {
+ int *out_bitrate, int *out_num_samples) {
*frame_size = 0;
if (out_sampling_rate) {
@@ -63,6 +63,10 @@
*out_bitrate = 0;
}
+ if (out_num_samples) {
+ *out_num_samples = 1152;
+ }
+
if ((header & 0xffe00000) != 0xffe00000) {
return false;
}
@@ -127,6 +131,10 @@
}
*frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+
+ if (out_num_samples) {
+ *out_num_samples = 384;
+ }
} else {
// layer II or III
@@ -150,10 +158,17 @@
bitrate = (layer == 2 /* L2 */)
? kBitrateV1L2[bitrate_index - 1]
: kBitrateV1L3[bitrate_index - 1];
+
+ if (out_num_samples) {
+ *out_num_samples = 1152;
+ }
} else {
// V2 (or 2.5)
bitrate = kBitrateV2[bitrate_index - 1];
+ if (out_num_samples) {
+ *out_num_samples = 576;
+ }
}
if (out_bitrate) {
@@ -217,7 +232,7 @@
*inout_pos += len;
- LOGV("skipped ID3 tag, new starting offset is %ld (0x%08lx)",
+ LOGV("skipped ID3 tag, new starting offset is %lld (0x%016llx)",
*inout_pos, *inout_pos);
}
@@ -241,7 +256,7 @@
do {
if (pos >= *inout_pos + kMaxBytesChecked) {
// Don't scan forever.
- LOGV("giving up at offset %ld", pos);
+ LOGV("giving up at offset %lld", pos);
break;
}
@@ -251,7 +266,15 @@
} else {
memcpy(buf, tmp, remainingBytes);
bytesToRead = kMaxReadBytes - remainingBytes;
- totalBytesRead = source->readAt(pos, buf + remainingBytes, bytesToRead);
+
+ /*
+ * The next read position should start from the end of
+ * the last buffer, and thus should include the remaining
+ * bytes in the buffer.
+ */
+ totalBytesRead = source->readAt(pos + remainingBytes,
+ buf + remainingBytes,
+ bytesToRead);
if (totalBytesRead <= 0) {
break;
}
@@ -283,7 +306,7 @@
continue;
}
- LOGV("found possible 1st frame at %ld (header = 0x%08x)", pos, header);
+ LOGV("found possible 1st frame at %lld (header = 0x%08x)", pos, header);
// We found what looks like a valid frame,
// now find its successors.
@@ -314,7 +337,7 @@
break;
}
- LOGV("found subsequent frame #%d at %ld", j + 2, test_pos);
+ LOGV("found subsequent frame #%d at %lld", j + 2, test_pos);
test_pos += test_frame_size;
}
@@ -366,6 +389,9 @@
sp<MP3Seeker> mSeeker;
MediaBufferGroup *mGroup;
+ int64_t mBasisTimeUs;
+ int64_t mSamplesRead;
+
MP3Source(const MP3Source &);
MP3Source &operator=(const MP3Source &);
};
@@ -481,7 +507,9 @@
mCurrentTimeUs(0),
mStarted(false),
mSeeker(seeker),
- mGroup(NULL) {
+ mGroup(NULL),
+ mBasisTimeUs(0),
+ mSamplesRead(0) {
}
MP3Source::~MP3Source() {
@@ -501,6 +529,9 @@
mCurrentPos = mFirstFramePos;
mCurrentTimeUs = 0;
+ mBasisTimeUs = mCurrentTimeUs;
+ mSamplesRead = 0;
+
mStarted = true;
return OK;
@@ -544,6 +575,9 @@
} else {
mCurrentTimeUs = actualSeekTimeUs;
}
+
+ mBasisTimeUs = mCurrentTimeUs;
+ mSamplesRead = 0;
}
MediaBuffer *buffer;
@@ -554,6 +588,8 @@
size_t frame_size;
int bitrate;
+ int num_samples;
+ int sample_rate;
for (;;) {
ssize_t n = mDataSource->readAt(mCurrentPos, buffer->data(), 4);
if (n < 4) {
@@ -567,7 +603,7 @@
if ((header & kMask) == (mFixedHeader & kMask)
&& MP3Extractor::get_mp3_frame_size(
- header, &frame_size, NULL, NULL, &bitrate)) {
+ header, &frame_size, &sample_rate, NULL, &bitrate, &num_samples)) {
break;
}
@@ -605,7 +641,9 @@
buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
mCurrentPos += frame_size;
- mCurrentTimeUs += frame_size * 8000ll / bitrate;
+
+ mSamplesRead += num_samples;
+ mCurrentTimeUs = mBasisTimeUs + ((mSamplesRead * 1000000) / sample_rate);
*out = buffer;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index e6e98aa..6692809 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -19,6 +19,8 @@
#include "include/MPEG4Extractor.h"
#include "include/SampleTable.h"
+#include "include/ESDS.h"
+#include "include/TimedTextPlayer.h"
#include <arpa/inet.h>
@@ -29,7 +31,6 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
-#include "include/ESDS.h"
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
@@ -262,7 +263,7 @@
MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
: mDataSource(source),
- mHaveMetadata(false),
+ mInitCheck(NO_INIT),
mHasVideo(false),
mFirstTrack(NULL),
mLastTrack(NULL),
@@ -361,8 +362,8 @@
}
status_t MPEG4Extractor::readMetaData() {
- if (mHaveMetadata) {
- return OK;
+ if (mInitCheck != NO_INIT) {
+ return mInitCheck;
}
off64_t offset = 0;
@@ -370,17 +371,20 @@
while ((err = parseChunk(&offset, 0)) == OK) {
}
- if (mHaveMetadata) {
+ if (mInitCheck == OK) {
if (mHasVideo) {
mFileMetaData->setCString(kKeyMIMEType, "video/mp4");
} else {
mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
}
- return OK;
+ mInitCheck = OK;
+ } else {
+ mInitCheck = err;
}
- return err;
+ CHECK_NE(err, (status_t)NO_INIT);
+ return mInitCheck;
}
void MPEG4Extractor::setDrmFlag(bool flag) {
@@ -755,7 +759,7 @@
return err;
}
} else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
- mHaveMetadata = true;
+ mInitCheck = OK;
if (!mIsDrm) {
return UNKNOWN_ERROR; // Return a dummy error.
@@ -829,6 +833,33 @@
mLastTrack->meta->setInt64(
kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
+ uint8_t lang[2];
+ off64_t lang_offset;
+ if (version == 1) {
+ lang_offset = timescale_offset + 4 + 8;
+ } else if (version == 0) {
+ lang_offset = timescale_offset + 4 + 4;
+ } else {
+ return ERROR_IO;
+ }
+
+ if (mDataSource->readAt(lang_offset, &lang, sizeof(lang))
+ < (ssize_t)sizeof(lang)) {
+ return ERROR_IO;
+ }
+
+ // To get the ISO-639-2/T three character language code
+ // 1 bit pad followed by 3 5-bits characters. Each character
+ // is packed as the difference between its ASCII value and 0x60.
+ char lang_code[4];
+ lang_code[0] = ((lang[0] >> 2) & 0x1f) + 0x60;
+ lang_code[1] = ((lang[0] & 0x3) << 3 | (lang[1] >> 5)) + 0x60;
+ lang_code[2] = (lang[1] & 0x1f) + 0x60;
+ lang_code[3] = '\0';
+
+ mLastTrack->meta->setCString(
+ kKeyMediaLanguage, lang_code);
+
*offset += chunk_size;
break;
}
@@ -1074,6 +1105,20 @@
break;
}
+ case FOURCC('c', 't', 't', 's'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setCompositionTimeToSampleParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
case FOURCC('s', 't', 's', 's'):
{
status_t err =
@@ -1150,6 +1195,37 @@
break;
}
+ case FOURCC('d', '2', '6', '3'):
+ {
+ /*
+ * d263 contains a fixed 7 bytes part:
+ * vendor - 4 bytes
+ * version - 1 byte
+ * level - 1 byte
+ * profile - 1 byte
+ * optionally, "d263" box itself may contain a 16-byte
+ * bit rate box (bitr)
+ * average bit rate - 4 bytes
+ * max bit rate - 4 bytes
+ */
+ char buffer[23];
+ if (chunk_data_size != 7 &&
+ chunk_data_size != 23) {
+ LOGE("Incorrect D263 box size %lld", chunk_data_size);
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset, buffer, chunk_data_size) < chunk_data_size) {
+ return ERROR_IO;
+ }
+
+ mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
+
+ *offset += chunk_size;
+ break;
+ }
+
case FOURCC('m', 'e', 't', 'a'):
{
uint8_t buffer[4];
@@ -1247,6 +1323,14 @@
return parseDrmSINF(offset, data_offset);
}
+ case FOURCC('t', 'x', '3', 'g'):
+ {
+ mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+
+ *offset += chunk_size;
+ break;
+ }
+
default:
{
*offset += chunk_size;
@@ -1551,6 +1635,14 @@
return OK;
}
+ if (objectTypeIndication == 0x6b) {
+ // The media subtype is MP3 audio
+ // Our software MP3 audio decoder may not be able to handle
+ // packetized MP3 audio; for now, lets just return ERROR_UNSUPPORTED
+ LOGE("MP3 track in MP4/3GPP file is not supported");
+ return ERROR_UNSUPPORTED;
+ }
+
const uint8_t *csd;
size_t csd_size;
if (esds.getCodecSpecificInfo(
@@ -1848,7 +1940,7 @@
off64_t offset;
size_t size;
- uint32_t dts;
+ uint32_t cts;
bool isSyncSample;
bool newBuffer = false;
if (mBuffer == NULL) {
@@ -1856,7 +1948,7 @@
status_t err =
mSampleTable->getMetaDataForSample(
- mCurrentSampleIndex, &offset, &size, &dts, &isSyncSample);
+ mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample);
if (err != OK) {
return err;
@@ -1886,7 +1978,7 @@
mBuffer->set_range(0, size);
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64(
- kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
+ kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
@@ -1972,14 +2064,18 @@
size_t dstOffset = 0;
while (srcOffset < size) {
- CHECK(srcOffset + mNALLengthSize <= size);
- size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
- srcOffset += mNALLengthSize;
+ bool isMalFormed = (srcOffset + mNALLengthSize > size);
+ size_t nalLength = 0;
+ if (!isMalFormed) {
+ nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
+ srcOffset += mNALLengthSize;
+ isMalFormed = srcOffset + nalLength > size;
+ }
- if (srcOffset + nalLength > size) {
+ if (isMalFormed) {
+ LOGE("Video is malformed");
mBuffer->release();
mBuffer = NULL;
-
return ERROR_MALFORMED;
}
@@ -2004,7 +2100,7 @@
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64(
- kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
+ kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
@@ -2024,6 +2120,20 @@
}
}
+MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(
+ const char *mimePrefix) {
+ for (Track *track = mFirstTrack; track != NULL; track = track->next) {
+ const char *mime;
+ if (track->meta != NULL
+ && track->meta->findCString(kKeyMIMEType, &mime)
+ && !strncasecmp(mime, mimePrefix, strlen(mimePrefix))) {
+ return track;
+ }
+ }
+
+ return NULL;
+}
+
static bool LegacySniffMPEG4(
const sp<DataSource> &source, String8 *mimeType, float *confidence) {
uint8_t header[8];
@@ -2056,6 +2166,14 @@
FOURCC('3', 'g', 'p', '4'),
FOURCC('m', 'p', '4', '1'),
FOURCC('m', 'p', '4', '2'),
+
+ // Won't promise that the following file types can be played.
+ // Just give these file types a chance.
+ FOURCC('q', 't', ' ', ' '), // Apple's QuickTime
+ FOURCC('M', 'S', 'N', 'V'), // Sony's PSP
+
+ FOURCC('3', 'g', '2', 'a'), // 3GPP2
+ FOURCC('3', 'g', '2', 'b'),
};
for (size_t i = 0;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index d1a497f..f6a8b17 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -33,6 +33,7 @@
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/Utils.h>
#include <media/mediarecorder.h>
+#include <cutils/properties.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -45,6 +46,7 @@
static const int64_t kMax32BitFileSize = 0x007fffffffLL;
static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
static const uint8_t kNalUnitTypePicParamSet = 0x08;
+static const int64_t kInitialDelayTimeUs = 700000LL;
// Using longer adjustment period to suppress fluctuations in
// the audio encoding paths
@@ -52,7 +54,7 @@
class MPEG4Writer::Track {
public:
- Track(MPEG4Writer *owner, const sp<MediaSource> &source);
+ Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
~Track();
@@ -63,12 +65,13 @@
int64_t getDurationUs() const;
int64_t getEstimatedTrackSizeBytes() const;
- void writeTrackHeader(int32_t trackID, bool use32BitOffset = true);
+ void writeTrackHeader(bool use32BitOffset = true);
void bufferChunk(int64_t timestampUs);
bool isAvc() const { return mIsAvc; }
bool isAudio() const { return mIsAudio; }
bool isMPEG4() const { return mIsMPEG4; }
void addChunkOffset(off64_t offset);
+ int32_t getTrackId() const { return mTrackId; }
status_t dump(int fd, const Vector<String16>& args) const;
private:
@@ -82,7 +85,9 @@
bool mIsAvc;
bool mIsAudio;
bool mIsMPEG4;
+ int32_t mTrackId;
int64_t mTrackDurationUs;
+ int64_t mMaxChunkDurationUs;
// For realtime applications, we need to adjust the media clock
// for video track based on the audio media clock
@@ -155,6 +160,8 @@
bool mReachedEOS;
int64_t mStartTimestampUs;
+ int64_t mStartTimeRealUs;
+ int64_t mFirstSampleTimeRealUs;
int64_t mPreviousTrackTimeUs;
int64_t mTrackEveryTimeDurationUs;
@@ -186,12 +193,9 @@
const uint8_t *parseParamSet(
const uint8_t *data, size_t length, int type, size_t *paramSetLen);
- status_t makeAVCCodecSpecificData(
- const uint8_t *data, size_t size);
- status_t copyAVCCodecSpecificData(
- const uint8_t *data, size_t size);
- status_t parseAVCCodecSpecificData(
- const uint8_t *data, size_t size);
+ status_t makeAVCCodecSpecificData(const uint8_t *data, size_t size);
+ status_t copyAVCCodecSpecificData(const uint8_t *data, size_t size);
+ status_t parseAVCCodecSpecificData(const uint8_t *data, size_t size);
// Track authoring progress status
void trackProgressStatus(int64_t timeUs, status_t err = OK);
@@ -213,6 +217,31 @@
void addOneStscTableEntry(size_t chunkId, size_t sampleId);
void addOneStssTableEntry(size_t sampleId);
void addOneSttsTableEntry(size_t sampleCount, int64_t durationUs);
+ void sendTrackSummary(bool hasMultipleTracks);
+
+ // Write the boxes
+ void writeStcoBox(bool use32BitOffset);
+ void writeStscBox();
+ void writeStszBox();
+ void writeStssBox();
+ void writeSttsBox();
+ void writeD263Box();
+ void writePaspBox();
+ void writeAvccBox();
+ void writeUrlBox();
+ void writeDrefBox();
+ void writeDinfBox();
+ void writeDamrBox();
+ void writeMdhdBox(time_t now);
+ void writeSmhdBox();
+ void writeVmhdBox();
+ void writeHdlrBox();
+ void writeTkhdBox(time_t now);
+ void writeMp4aEsdsBox();
+ void writeMp4vEsdsBox();
+ void writeAudioFourCCBox();
+ void writeVideoFourCCBox();
+ void writeStblBox(bool use32BitOffset);
Track(const Track &);
Track &operator=(const Track &);
@@ -229,9 +258,13 @@
mOffset(0),
mMdatOffset(0),
mEstimatedMoovBoxSize(0),
- mInterleaveDurationUs(1000000) {
+ mInterleaveDurationUs(1000000),
+ mLatitudex10000(0),
+ mLongitudex10000(0),
+ mAreGeoTagsAvailable(false),
+ mStartTimeOffsetMs(-1) {
- mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC);
+ mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR);
if (mFd >= 0) {
mInitCheck = OK;
}
@@ -248,7 +281,11 @@
mOffset(0),
mMdatOffset(0),
mEstimatedMoovBoxSize(0),
- mInterleaveDurationUs(1000000) {
+ mInterleaveDurationUs(1000000),
+ mLatitudex10000(0),
+ mLongitudex10000(0),
+ mAreGeoTagsAvailable(false),
+ mStartTimeOffsetMs(-1) {
}
MPEG4Writer::~MPEG4Writer() {
@@ -295,7 +332,12 @@
}
status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
- Track *track = new Track(this, source);
+ Mutex::Autolock l(mLock);
+ if (mStarted) {
+ LOGE("Attempt to add source AFTER recording is started");
+ return UNKNOWN_ERROR;
+ }
+ Track *track = new Track(this, source, mTracks.size());
mTracks.push_back(track);
return OK;
@@ -444,20 +486,7 @@
mMoovBoxBuffer = NULL;
mMoovBoxBufferOffset = 0;
- beginBox("ftyp");
- {
- int32_t fileType;
- if (param && param->findInt32(kKeyFileType, &fileType) &&
- fileType != OUTPUT_FORMAT_MPEG_4) {
- writeFourcc("3gp4");
- } else {
- writeFourcc("isom");
- }
- }
- writeInt32(0);
- writeFourcc("isom");
- writeFourcc("3gp4");
- endBox();
+ writeFtypBox(param);
mFreeBoxOffset = mOffset;
@@ -637,43 +666,12 @@
}
lseek64(mFd, mOffset, SEEK_SET);
- time_t now = time(NULL);
const off64_t moovOffset = mOffset;
mWriteMoovBoxToMemory = true;
mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
mMoovBoxBufferOffset = 0;
CHECK(mMoovBoxBuffer != NULL);
- int32_t duration = (maxDurationUs * mTimeScale + 5E5) / 1E6;
-
- beginBox("moov");
-
- beginBox("mvhd");
- writeInt32(0); // version=0, flags=0
- writeInt32(now); // creation time
- writeInt32(now); // modification time
- writeInt32(mTimeScale); // mvhd timescale
- writeInt32(duration);
- writeInt32(0x10000); // rate: 1.0
- writeInt16(0x100); // volume
- writeInt16(0); // reserved
- writeInt32(0); // reserved
- writeInt32(0); // reserved
- writeCompositionMatrix(0); // matrix
- writeInt32(0); // predefined
- writeInt32(0); // predefined
- writeInt32(0); // predefined
- writeInt32(0); // predefined
- writeInt32(0); // predefined
- writeInt32(0); // predefined
- writeInt32(mTracks.size() + 1); // nextTrackID
- endBox(); // mvhd
-
- int32_t id = 1;
- for (List<Track *>::iterator it = mTracks.begin();
- it != mTracks.end(); ++it, ++id) {
- (*it)->writeTrackHeader(id, mUse32BitOffset);
- }
- endBox(); // moov
+ writeMoovBox(maxDurationUs);
mWriteMoovBoxToMemory = false;
if (mStreamableFile) {
@@ -703,9 +701,96 @@
mFd = -1;
mInitCheck = NO_INIT;
mStarted = false;
+
return err;
}
+void MPEG4Writer::writeMvhdBox(int64_t durationUs) {
+ time_t now = time(NULL);
+ beginBox("mvhd");
+ writeInt32(0); // version=0, flags=0
+ writeInt32(now); // creation time
+ writeInt32(now); // modification time
+ writeInt32(mTimeScale); // mvhd timescale
+ int32_t duration = (durationUs * mTimeScale + 5E5) / 1E6;
+ writeInt32(duration);
+ writeInt32(0x10000); // rate: 1.0
+ writeInt16(0x100); // volume
+ writeInt16(0); // reserved
+ writeInt32(0); // reserved
+ writeInt32(0); // reserved
+ writeCompositionMatrix(0); // matrix
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(mTracks.size() + 1); // nextTrackID
+ endBox(); // mvhd
+}
+
+void MPEG4Writer::writeMoovBox(int64_t durationUs) {
+ beginBox("moov");
+ writeMvhdBox(durationUs);
+ if (mAreGeoTagsAvailable) {
+ writeUdtaBox();
+ }
+ int32_t id = 1;
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it, ++id) {
+ (*it)->writeTrackHeader(mUse32BitOffset);
+ }
+ endBox(); // moov
+}
+
+void MPEG4Writer::writeFtypBox(MetaData *param) {
+ beginBox("ftyp");
+
+ int32_t fileType;
+ if (param && param->findInt32(kKeyFileType, &fileType) &&
+ fileType != OUTPUT_FORMAT_MPEG_4) {
+ writeFourcc("3gp4");
+ } else {
+ writeFourcc("isom");
+ }
+
+ writeInt32(0);
+ writeFourcc("isom");
+ writeFourcc("3gp4");
+ endBox();
+}
+
+static bool isTestModeEnabled() {
+#if (PROPERTY_VALUE_MAX < 5)
+#error "PROPERTY_VALUE_MAX must be at least 5"
+#endif
+
+ // Test mode is enabled only if rw.media.record.test system
+ // property is enabled.
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("rw.media.record.test", value, NULL) &&
+ (!strcasecmp(value, "true") || !strcasecmp(value, "1"))) {
+ return true;
+ }
+ return false;
+}
+
+void MPEG4Writer::sendSessionSummary() {
+ // Send session summary only if test mode is enabled
+ if (!isTestModeEnabled()) {
+ return;
+ }
+
+ for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
+ it != mChunkInfos.end(); ++it) {
+ int trackNum = it->mTrack->getTrackId() << 28;
+ notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+ trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS,
+ it->mMaxInterChunkDurUs);
+ }
+}
+
status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
mInterleaveDurationUs = durationUs;
return OK;
@@ -868,6 +953,77 @@
write(s, 1, 4);
}
+
+// Written in +/-DD.DDDD format
+void MPEG4Writer::writeLatitude(int degreex10000) {
+ bool isNegative = (degreex10000 < 0);
+ char sign = isNegative? '-': '+';
+
+ // Handle the whole part
+ char str[9];
+ int wholePart = degreex10000 / 10000;
+ if (wholePart == 0) {
+ snprintf(str, 5, "%c%.2d.", sign, wholePart);
+ } else {
+ snprintf(str, 5, "%+.2d.", wholePart);
+ }
+
+ // Handle the fractional part
+ int fractionalPart = degreex10000 - (wholePart * 10000);
+ if (fractionalPart < 0) {
+ fractionalPart = -fractionalPart;
+ }
+ snprintf(&str[4], 5, "%.4d", fractionalPart);
+
+ // Do not write the null terminator
+ write(str, 1, 8);
+}
+
+// Written in +/- DDD.DDDD format
+void MPEG4Writer::writeLongitude(int degreex10000) {
+ bool isNegative = (degreex10000 < 0);
+ char sign = isNegative? '-': '+';
+
+ // Handle the whole part
+ char str[10];
+ int wholePart = degreex10000 / 10000;
+ if (wholePart == 0) {
+ snprintf(str, 6, "%c%.3d.", sign, wholePart);
+ } else {
+ snprintf(str, 6, "%+.3d.", wholePart);
+ }
+
+ // Handle the fractional part
+ int fractionalPart = degreex10000 - (wholePart * 10000);
+ if (fractionalPart < 0) {
+ fractionalPart = -fractionalPart;
+ }
+ snprintf(&str[5], 5, "%.4d", fractionalPart);
+
+ // Do not write the null terminator
+ write(str, 1, 9);
+}
+
+/*
+ * Geodata is stored according to ISO-6709 standard.
+ * latitudex10000 is latitude in degrees times 10000, and
+ * longitudex10000 is longitude in degrees times 10000.
+ * The range for the latitude is in [-90, +90], and
+ * The range for the longitude is in [-180, +180]
+ */
+status_t MPEG4Writer::setGeoData(int latitudex10000, int longitudex10000) {
+ // Is latitude or longitude out of range?
+ if (latitudex10000 < -900000 || latitudex10000 > 900000 ||
+ longitudex10000 < -1800000 || longitudex10000 > 1800000) {
+ return BAD_VALUE;
+ }
+
+ mLatitudex10000 = latitudex10000;
+ mLongitudex10000 = longitudex10000;
+ mAreGeoTagsAvailable = true;
+ return OK;
+}
+
void MPEG4Writer::write(const void *data, size_t size) {
write(data, 1, size);
}
@@ -945,7 +1101,7 @@
////////////////////////////////////////////////////////////////////////////////
MPEG4Writer::Track::Track(
- MPEG4Writer *owner, const sp<MediaSource> &source)
+ MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId)
: mOwner(owner),
mMeta(source->getFormat()),
mSource(source),
@@ -953,6 +1109,7 @@
mPaused(false),
mResumed(false),
mStarted(false),
+ mTrackId(trackId),
mTrackDurationUs(0),
mEstimatedTrackSizeBytes(0),
mSamplesHaveSameSize(true),
@@ -1122,54 +1279,47 @@
CHECK("Received a chunk for a unknown track" == 0);
}
-void MPEG4Writer::writeFirstChunk(ChunkInfo* info) {
- LOGV("writeFirstChunk: %p", info->mTrack);
+void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
+ LOGV("writeChunkToFile: %lld from %s track",
+ chunk.mTimestampUs, chunk.mTrack->isAudio()? "audio": "video");
- List<Chunk>::iterator chunkIt = info->mChunks.begin();
- for (List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin();
- it != chunkIt->mSamples.end(); ++it) {
+ int32_t isFirstSample = true;
+ while (!chunk->mSamples.empty()) {
+ List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
- off64_t offset = info->mTrack->isAvc()
- ? addLengthPrefixedSample_l(*it)
- : addSample_l(*it);
- if (it == chunkIt->mSamples.begin()) {
- info->mTrack->addChunkOffset(offset);
+ off64_t offset = chunk->mTrack->isAvc()
+ ? addLengthPrefixedSample_l(*it)
+ : addSample_l(*it);
+
+ if (isFirstSample) {
+ chunk->mTrack->addChunkOffset(offset);
+ isFirstSample = false;
}
- }
- // Done with the current chunk.
- // Release all the samples in this chunk.
- while (!chunkIt->mSamples.empty()) {
- List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin();
(*it)->release();
(*it) = NULL;
- chunkIt->mSamples.erase(it);
+ chunk->mSamples.erase(it);
}
- chunkIt->mSamples.clear();
- info->mChunks.erase(chunkIt);
+ chunk->mSamples.clear();
}
-void MPEG4Writer::writeChunks() {
- LOGV("writeChunks");
+void MPEG4Writer::writeAllChunks() {
+ LOGV("writeAllChunks");
size_t outstandingChunks = 0;
- while (!mChunkInfos.empty()) {
- List<ChunkInfo>::iterator it = mChunkInfos.begin();
- while (!it->mChunks.empty()) {
- CHECK_EQ(OK, writeOneChunk());
- ++outstandingChunks;
- }
- it->mTrack = NULL;
- mChunkInfos.erase(it);
+ Chunk chunk;
+ while (findChunkToWrite(&chunk)) {
+ ++outstandingChunks;
}
+
+ sendSessionSummary();
+
mChunkInfos.clear();
LOGD("%d chunks are written in the last batch", outstandingChunks);
}
-status_t MPEG4Writer::writeOneChunk() {
- LOGV("writeOneChunk");
+bool MPEG4Writer::findChunkToWrite(Chunk *chunk) {
+ LOGV("findChunkToWrite");
- // Find the smallest timestamp, and write that chunk out
- // XXX: What if some track is just too slow?
int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL;
Track *track = NULL;
for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
@@ -1185,38 +1335,57 @@
if (track == NULL) {
LOGV("Nothing to be written after all");
- return OK;
+ return false;
}
if (mIsFirstChunk) {
mIsFirstChunk = false;
}
+
for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
it != mChunkInfos.end(); ++it) {
if (it->mTrack == track) {
- writeFirstChunk(&(*it));
+ *chunk = *(it->mChunks.begin());
+ it->mChunks.erase(it->mChunks.begin());
+ CHECK_EQ(chunk->mTrack, track);
+
+ int64_t interChunkTimeUs =
+ chunk->mTimeStampUs - it->mPrevChunkTimestampUs;
+ if (interChunkTimeUs > it->mPrevChunkTimestampUs) {
+ it->mMaxInterChunkDurUs = interChunkTimeUs;
+ }
+
+ return true;
}
}
- return OK;
+
+ return false;
}
void MPEG4Writer::threadFunc() {
LOGV("threadFunc");
prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0);
+
+ Mutex::Autolock autoLock(mLock);
while (!mDone) {
- {
- Mutex::Autolock autolock(mLock);
+ Chunk chunk;
+ bool chunkFound = false;
+
+ while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) {
mChunkReadyCondition.wait(mLock);
- CHECK_EQ(writeOneChunk(), OK);
+ }
+
+ // Actual write without holding the lock in order to
+ // reduce the blocking time for media track threads.
+ if (chunkFound) {
+ mLock.unlock();
+ writeChunkToFile(&chunk);
+ mLock.lock();
}
}
- {
- // Write ALL samples
- Mutex::Autolock autolock(mLock);
- writeChunks();
- }
+ writeAllChunks();
}
status_t MPEG4Writer::startWriterThread() {
@@ -1229,6 +1398,8 @@
it != mTracks.end(); ++it) {
ChunkInfo info;
info.mTrack = *it;
+ info.mPrevChunkTimestampUs = 0;
+ info.mMaxInterChunkDurUs = 0;
mChunkInfos.push_back(info);
}
@@ -1252,6 +1423,7 @@
if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) {
startTimeUs = 0;
}
+ mStartTimeRealUs = startTimeUs;
int32_t rotationDegrees;
if (!mIsAudio && params && params->findInt32(kKeyRotation, &rotationDegrees)) {
@@ -1269,7 +1441,26 @@
initTrackingProgressStatus(params);
sp<MetaData> meta = new MetaData;
+ if (mIsRealTimeRecording && mOwner->numTracks() > 1) {
+ /*
+ * This extra delay of accepting incoming audio/video signals
+ * helps to align a/v start time at the beginning of a recording
+ * session, and it also helps eliminate the "recording" sound for
+ * camcorder applications.
+ *
+ * If client does not set the start time offset, we fall back to
+ * use the default initial delay value.
+ */
+ int64_t startTimeOffsetUs = mOwner->getStartTimeOffsetMs() * 1000LL;
+ if (startTimeOffsetUs < 0) { // Start time offset was not set
+ startTimeOffsetUs = kInitialDelayTimeUs;
+ }
+ startTimeUs += startTimeOffsetUs;
+ LOGI("Start time offset: %lld us", startTimeOffsetUs);
+ }
+
meta->setInt64(kKeyTime, startTimeUs);
+
status_t err = mSource->start(meta.get());
if (err != OK) {
mDone = mReachedEOS = true;
@@ -1296,6 +1487,7 @@
mPrevMediaTimeAdjustSample = 0;
mTotalDriftTimeToAdjustUs = 0;
mPrevTotalAccumDriftTimeUs = 0;
+ mMaxChunkDurationUs = 0;
pthread_create(&mThread, &attr, ThreadWrapper, this);
pthread_attr_destroy(&attr);
@@ -1733,6 +1925,7 @@
status_t MPEG4Writer::Track::threadEntry() {
int32_t count = 0;
const int64_t interleaveDurationUs = mOwner->interleaveDuration();
+ const bool hasMultipleTracks = (mOwner->numTracks() > 1);
int64_t chunkTimestampUs = 0;
int32_t nChunks = 0;
int32_t nZeroLengthFrames = 0;
@@ -1870,7 +2063,8 @@
LOGV("%s timestampUs: %lld", mIsAudio? "Audio": "Video", timestampUs);
////////////////////////////////////////////////////////////////////////////////
- if (mSampleSizes.empty()) {
+ if (mNumSamples == 0) {
+ mFirstSampleTimeRealUs = systemTime() / 1000;
mStartTimestampUs = timestampUs;
mOwner->setStartTimestampUs(mStartTimestampUs);
previousPausedDurationUs = mStartTimestampUs;
@@ -1932,7 +2126,11 @@
((timestampUs * mTimeScale + 500000LL) / 1000000LL -
(lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
- if (currDurationTicks != lastDurationTicks) {
+ // Force the first sample to have its own stts entry so that
+ // we can adjust its value later to maintain the A/V sync.
+ if (mNumSamples == 3 || currDurationTicks != lastDurationTicks) {
+ LOGV("%s lastDurationUs: %lld us, currDurationTicks: %lld us",
+ mIsAudio? "Audio": "Video", lastDurationUs, currDurationTicks);
addOneSttsTableEntry(sampleCount, lastDurationUs);
sampleCount = 1;
} else {
@@ -1945,6 +2143,8 @@
}
previousSampleSize = sampleSize;
}
+ LOGV("%s timestampUs/lastTimestampUs: %lld/%lld",
+ mIsAudio? "Audio": "Video", timestampUs, lastTimestampUs);
lastDurationUs = timestampUs - lastTimestampUs;
lastDurationTicks = currDurationTicks;
lastTimestampUs = timestampUs;
@@ -1959,7 +2159,7 @@
}
trackProgressStatus(timestampUs);
}
- if (mOwner->numTracks() == 1) {
+ if (!hasMultipleTracks) {
off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
: mOwner->addSample_l(copy);
if (mChunkOffsets.empty()) {
@@ -1978,7 +2178,11 @@
if (chunkTimestampUs == 0) {
chunkTimestampUs = timestampUs;
} else {
- if (timestampUs - chunkTimestampUs > interleaveDurationUs) {
+ int64_t chunkDurationUs = timestampUs - chunkTimestampUs;
+ if (chunkDurationUs > interleaveDurationUs) {
+ if (chunkDurationUs > mMaxChunkDurationUs) {
+ mMaxChunkDurationUs = chunkDurationUs;
+ }
++nChunks;
if (nChunks == 1 || // First chunk
(--(mStscTableEntries.end()))->samplesPerChunk !=
@@ -1998,10 +2202,10 @@
(OK != checkCodecSpecificData())) { // no codec specific data
err = ERROR_MALFORMED;
}
- mOwner->trackProgressStatus(this, -1, err);
+ mOwner->trackProgressStatus(mTrackId, -1, err);
// Last chunk
- if (mOwner->numTracks() == 1) {
+ if (!hasMultipleTracks) {
addOneStscTableEntry(1, mNumSamples);
} else if (!mChunkSamples.empty()) {
addOneStscTableEntry(++nChunks, mChunkSamples.size());
@@ -2016,9 +2220,21 @@
} else {
++sampleCount; // Count for the last sample
}
- addOneSttsTableEntry(sampleCount, lastDurationUs);
+
+ if (mNumSamples <= 2) {
+ addOneSttsTableEntry(1, lastDurationUs);
+ if (sampleCount - 1 > 0) {
+ addOneSttsTableEntry(sampleCount - 1, lastDurationUs);
+ }
+ } else {
+ addOneSttsTableEntry(sampleCount, lastDurationUs);
+ }
+
mTrackDurationUs += lastDurationUs;
mReachedEOS = true;
+
+ sendTrackSummary(hasMultipleTracks);
+
LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
count, nZeroLengthFrames, mNumSamples, mIsAudio? "audio": "video");
if (mIsAudio) {
@@ -2031,46 +2247,94 @@
return err;
}
+void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) {
+
+ // Send track summary only if test mode is enabled.
+ if (!isTestModeEnabled()) {
+ return;
+ }
+
+ int trackNum = (mTrackId << 28);
+
+ mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+ trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE,
+ mIsAudio? 0: 1);
+
+ mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+ trackNum | MEDIA_RECORDER_TRACK_INFO_DURATION_MS,
+ mTrackDurationUs / 1000);
+
+ mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+ trackNum | MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES,
+ mNumSamples);
+
+ {
+ // The system delay time excluding the requested initial delay that
+ // is used to eliminate the recording sound.
+ int64_t startTimeOffsetUs = mOwner->getStartTimeOffsetMs() * 1000LL;
+ if (startTimeOffsetUs < 0) { // Start time offset was not set
+ startTimeOffsetUs = kInitialDelayTimeUs;
+ }
+ int64_t initialDelayUs =
+ mFirstSampleTimeRealUs - mStartTimeRealUs - startTimeOffsetUs;
+
+ mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+ trackNum | MEDIA_RECORDER_TRACK_INFO_INITIAL_DELAY_MS,
+ (initialDelayUs) / 1000);
+ }
+
+ mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+ trackNum | MEDIA_RECORDER_TRACK_INFO_DATA_KBYTES,
+ mMdatSizeBytes / 1024);
+
+ if (hasMultipleTracks) {
+ mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+ trackNum | MEDIA_RECORDER_TRACK_INFO_MAX_CHUNK_DUR_MS,
+ mMaxChunkDurationUs / 1000);
+
+ int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
+ if (mStartTimestampUs != moovStartTimeUs) {
+ int64_t startTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
+ mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+ trackNum | MEDIA_RECORDER_TRACK_INFO_START_OFFSET_MS,
+ startTimeOffsetUs / 1000);
+ }
+ }
+}
+
void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
LOGV("trackProgressStatus: %lld us", timeUs);
if (mTrackEveryTimeDurationUs > 0 &&
timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
LOGV("Fire time tracking progress status at %lld us", timeUs);
- mOwner->trackProgressStatus(this, timeUs - mPreviousTrackTimeUs, err);
+ mOwner->trackProgressStatus(mTrackId, timeUs - mPreviousTrackTimeUs, err);
mPreviousTrackTimeUs = timeUs;
}
}
void MPEG4Writer::trackProgressStatus(
- const MPEG4Writer::Track* track, int64_t timeUs, status_t err) {
+ size_t trackId, int64_t timeUs, status_t err) {
Mutex::Autolock lock(mLock);
- int32_t nTracks = mTracks.size();
- CHECK(nTracks >= 1);
- CHECK(nTracks < 64); // Arbitrary number
-
- int32_t trackNum = 0;
- CHECK(trackNum < nTracks);
- trackNum <<= 16;
+ int32_t trackNum = (trackId << 28);
// Error notification
// Do not consider ERROR_END_OF_STREAM an error
if (err != OK && err != ERROR_END_OF_STREAM) {
- notify(MEDIA_RECORDER_EVENT_ERROR,
- trackNum | MEDIA_RECORDER_ERROR_UNKNOWN,
+ notify(MEDIA_RECORDER_TRACK_EVENT_ERROR,
+ trackNum | MEDIA_RECORDER_TRACK_ERROR_GENERAL,
err);
return;
}
if (timeUs == -1) {
// Send completion notification
- notify(MEDIA_RECORDER_EVENT_INFO,
- trackNum | MEDIA_RECORDER_INFO_COMPLETION_STATUS,
+ notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+ trackNum | MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS,
err);
- return;
} else {
// Send progress status
- notify(MEDIA_RECORDER_EVENT_INFO,
- trackNum | MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS,
+ notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+ trackNum | MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME,
timeUs / 1000);
}
}
@@ -2128,403 +2392,484 @@
return OK;
}
-void MPEG4Writer::Track::writeTrackHeader(
- int32_t trackID, bool use32BitOffset) {
- const char *mime;
- bool success = mMeta->findCString(kKeyMIMEType, &mime);
- CHECK(success);
+void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) {
LOGV("%s track time scale: %d",
mIsAudio? "Audio": "Video", mTimeScale);
time_t now = time(NULL);
- int32_t mvhdTimeScale = mOwner->getTimeScale();
- int64_t trakDurationUs = getDurationUs();
-
mOwner->beginBox("trak");
-
- mOwner->beginBox("tkhd");
- // Flags = 7 to indicate that the track is enabled, and
- // part of the presentation
- mOwner->writeInt32(0x07); // version=0, flags=7
- mOwner->writeInt32(now); // creation time
- mOwner->writeInt32(now); // modification time
- mOwner->writeInt32(trackID);
- mOwner->writeInt32(0); // reserved
- int32_t tkhdDuration =
- (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
- mOwner->writeInt32(tkhdDuration); // in mvhd timescale
- mOwner->writeInt32(0); // reserved
- mOwner->writeInt32(0); // reserved
- mOwner->writeInt16(0); // layer
- mOwner->writeInt16(0); // alternate group
- mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume
- mOwner->writeInt16(0); // reserved
-
- mOwner->writeCompositionMatrix(mRotation); // matrix
-
- if (mIsAudio) {
- mOwner->writeInt32(0);
- mOwner->writeInt32(0);
- } else {
- int32_t width, height;
- bool success = mMeta->findInt32(kKeyWidth, &width);
- success = success && mMeta->findInt32(kKeyHeight, &height);
- CHECK(success);
-
- mOwner->writeInt32(width << 16); // 32-bit fixed-point value
- mOwner->writeInt32(height << 16); // 32-bit fixed-point value
- }
- mOwner->endBox(); // tkhd
-
- int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
- if (mStartTimestampUs != moovStartTimeUs) {
- mOwner->beginBox("edts");
- mOwner->beginBox("elst");
- mOwner->writeInt32(0); // version=0, flags=0: 32-bit time
- mOwner->writeInt32(2); // never ends with an empty list
-
- // First elst entry: specify the starting time offset
- int64_t offsetUs = mStartTimestampUs - moovStartTimeUs;
- LOGV("OffsetUs: %lld", offsetUs);
- int32_t seg = (offsetUs * mvhdTimeScale + 5E5) / 1E6;
- mOwner->writeInt32(seg); // in mvhd timecale
- mOwner->writeInt32(-1); // starting time offset
- mOwner->writeInt32(1 << 16); // rate = 1.0
-
- // Second elst entry: specify the track duration
- seg = (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
- mOwner->writeInt32(seg); // in mvhd timescale
- mOwner->writeInt32(0);
- mOwner->writeInt32(1 << 16);
- mOwner->endBox();
- mOwner->endBox();
- }
-
- mOwner->beginBox("mdia");
-
- mOwner->beginBox("mdhd");
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(now); // creation time
- mOwner->writeInt32(now); // modification time
- mOwner->writeInt32(mTimeScale); // media timescale
- int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
- mOwner->writeInt32(mdhdDuration); // use media timescale
- // Language follows the three letter standard ISO-639-2/T
- // 'e', 'n', 'g' for "English", for instance.
- // Each character is packed as the difference between its ASCII value and 0x60.
- // For "English", these are 00101, 01110, 00111.
- // XXX: Where is the padding bit located: 0x15C7?
- mOwner->writeInt16(0); // language code
- mOwner->writeInt16(0); // predefined
- mOwner->endBox();
-
- mOwner->beginBox("hdlr");
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(0); // component type: should be mhlr
- mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype
- mOwner->writeInt32(0); // reserved
- mOwner->writeInt32(0); // reserved
- mOwner->writeInt32(0); // reserved
- // Removing "r" for the name string just makes the string 4 byte aligned
- mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name
- mOwner->endBox();
-
- mOwner->beginBox("minf");
- if (mIsAudio) {
- mOwner->beginBox("smhd");
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt16(0); // balance
- mOwner->writeInt16(0); // reserved
- mOwner->endBox();
- } else {
- mOwner->beginBox("vmhd");
- mOwner->writeInt32(0x01); // version=0, flags=1
- mOwner->writeInt16(0); // graphics mode
- mOwner->writeInt16(0); // opcolor
- mOwner->writeInt16(0);
- mOwner->writeInt16(0);
- mOwner->endBox();
- }
-
- mOwner->beginBox("dinf");
- mOwner->beginBox("dref");
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(1); // entry count (either url or urn)
- // The table index here refers to the sample description index
- // in the sample table entries.
- mOwner->beginBox("url ");
- mOwner->writeInt32(1); // version=0, flags=1 (self-contained)
- mOwner->endBox(); // url
- mOwner->endBox(); // dref
- mOwner->endBox(); // dinf
-
- mOwner->beginBox("stbl");
-
- mOwner->beginBox("stsd");
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(1); // entry count
- if (mIsAudio) {
- const char *fourcc = NULL;
- if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
- fourcc = "samr";
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
- fourcc = "sawb";
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
- fourcc = "mp4a";
+ writeTkhdBox(now);
+ mOwner->beginBox("mdia");
+ writeMdhdBox(now);
+ writeHdlrBox();
+ mOwner->beginBox("minf");
+ if (mIsAudio) {
+ writeSmhdBox();
} else {
- LOGE("Unknown mime type '%s'.", mime);
- CHECK(!"should not be here, unknown mime type.");
+ writeVmhdBox();
}
-
- mOwner->beginBox(fourcc); // audio format
- mOwner->writeInt32(0); // reserved
- mOwner->writeInt16(0); // reserved
- mOwner->writeInt16(0x1); // data ref index
- mOwner->writeInt32(0); // reserved
- mOwner->writeInt32(0); // reserved
- int32_t nChannels;
- CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
- mOwner->writeInt16(nChannels); // channel count
- mOwner->writeInt16(16); // sample size
- mOwner->writeInt16(0); // predefined
- mOwner->writeInt16(0); // reserved
-
- int32_t samplerate;
- bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
- CHECK(success);
- mOwner->writeInt32(samplerate << 16);
- if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
- mOwner->beginBox("esds");
- CHECK(mCodecSpecificData);
- CHECK(mCodecSpecificDataSize > 0);
-
- // Make sure all sizes encode to a single byte.
- CHECK(mCodecSpecificDataSize + 23 < 128);
-
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt8(0x03); // ES_DescrTag
- mOwner->writeInt8(23 + mCodecSpecificDataSize);
- mOwner->writeInt16(0x0000);// ES_ID
- mOwner->writeInt8(0x00);
-
- mOwner->writeInt8(0x04); // DecoderConfigDescrTag
- mOwner->writeInt8(15 + mCodecSpecificDataSize);
- mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2
- mOwner->writeInt8(0x15); // streamType AudioStream
-
- mOwner->writeInt16(0x03); // XXX
- mOwner->writeInt8(0x00); // buffer size 24-bit
- mOwner->writeInt32(96000); // max bit rate
- mOwner->writeInt32(96000); // avg bit rate
-
- mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
- mOwner->writeInt8(mCodecSpecificDataSize);
- mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
-
- static const uint8_t kData2[] = {
- 0x06, // SLConfigDescriptorTag
- 0x01,
- 0x02
- };
- mOwner->write(kData2, sizeof(kData2));
-
- mOwner->endBox(); // esds
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) ||
- !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
- // 3gpp2 Spec AMRSampleEntry fields
- mOwner->beginBox("damr");
- mOwner->writeCString(" "); // vendor: 4 bytes
- mOwner->writeInt8(0); // decoder version
- mOwner->writeInt16(0x83FF); // mode set: all enabled
- mOwner->writeInt8(0); // mode change period
- mOwner->writeInt8(1); // frames per sample
- mOwner->endBox();
- }
- mOwner->endBox();
- } else {
- if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
- mOwner->beginBox("mp4v");
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
- mOwner->beginBox("s263");
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
- mOwner->beginBox("avc1");
- } else {
- LOGE("Unknown mime type '%s'.", mime);
- CHECK(!"should not be here, unknown mime type.");
- }
-
- mOwner->writeInt32(0); // reserved
- mOwner->writeInt16(0); // reserved
- mOwner->writeInt16(1); // data ref index
- mOwner->writeInt16(0); // predefined
- mOwner->writeInt16(0); // reserved
- mOwner->writeInt32(0); // predefined
- mOwner->writeInt32(0); // predefined
- mOwner->writeInt32(0); // predefined
-
- int32_t width, height;
- bool success = mMeta->findInt32(kKeyWidth, &width);
- success = success && mMeta->findInt32(kKeyHeight, &height);
- CHECK(success);
-
- mOwner->writeInt16(width);
- mOwner->writeInt16(height);
- mOwner->writeInt32(0x480000); // horiz resolution
- mOwner->writeInt32(0x480000); // vert resolution
- mOwner->writeInt32(0); // reserved
- mOwner->writeInt16(1); // frame count
- mOwner->write(" ", 32);
- mOwner->writeInt16(0x18); // depth
- mOwner->writeInt16(-1); // predefined
-
- CHECK(23 + mCodecSpecificDataSize < 128);
-
- if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
- CHECK(mCodecSpecificData);
- CHECK(mCodecSpecificDataSize > 0);
- mOwner->beginBox("esds");
-
- mOwner->writeInt32(0); // version=0, flags=0
-
- mOwner->writeInt8(0x03); // ES_DescrTag
- mOwner->writeInt8(23 + mCodecSpecificDataSize);
- mOwner->writeInt16(0x0000); // ES_ID
- mOwner->writeInt8(0x1f);
-
- mOwner->writeInt8(0x04); // DecoderConfigDescrTag
- mOwner->writeInt8(15 + mCodecSpecificDataSize);
- mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2
- mOwner->writeInt8(0x11); // streamType VisualStream
-
- static const uint8_t kData[] = {
- 0x01, 0x77, 0x00,
- 0x00, 0x03, 0xe8, 0x00,
- 0x00, 0x03, 0xe8, 0x00
- };
- mOwner->write(kData, sizeof(kData));
-
- mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
-
- mOwner->writeInt8(mCodecSpecificDataSize);
- mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
-
- static const uint8_t kData2[] = {
- 0x06, // SLConfigDescriptorTag
- 0x01,
- 0x02
- };
- mOwner->write(kData2, sizeof(kData2));
-
- mOwner->endBox(); // esds
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
- mOwner->beginBox("d263");
-
- mOwner->writeInt32(0); // vendor
- mOwner->writeInt8(0); // decoder version
- mOwner->writeInt8(10); // level: 10
- mOwner->writeInt8(0); // profile: 0
-
- mOwner->endBox(); // d263
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
- CHECK(mCodecSpecificData);
- CHECK(mCodecSpecificDataSize >= 5);
-
- // Patch avcc's lengthSize field to match the number
- // of bytes we use to indicate the size of a nal unit.
- uint8_t *ptr = (uint8_t *)mCodecSpecificData;
- ptr[4] =
- (ptr[4] & 0xfc)
- | (mOwner->useNalLengthFour() ? 3 : 1);
-
- mOwner->beginBox("avcC");
- mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
- mOwner->endBox(); // avcC
- }
-
- mOwner->beginBox("pasp");
- // This is useful if the pixel is not square
- mOwner->writeInt32(1 << 16); // hspacing
- mOwner->writeInt32(1 << 16); // vspacing
- mOwner->endBox(); // pasp
- mOwner->endBox(); // mp4v, s263 or avc1
- }
- mOwner->endBox(); // stsd
-
- mOwner->beginBox("stts");
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mNumSttsTableEntries);
- int64_t prevTimestampUs = 0;
- for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
- it != mSttsTableEntries.end(); ++it) {
- mOwner->writeInt32(it->sampleCount);
-
- // Make sure that we are calculating the sample duration the exactly
- // same way as we made decision on how to create stts entries.
- int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs;
- int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL -
- (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL);
- prevTimestampUs += (it->sampleCount * it->sampleDurationUs);
-
- mOwner->writeInt32(dur);
- }
- mOwner->endBox(); // stts
-
- if (!mIsAudio) {
- mOwner->beginBox("stss");
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mNumStssTableEntries); // number of sync frames
- for (List<int32_t>::iterator it = mStssTableEntries.begin();
- it != mStssTableEntries.end(); ++it) {
- mOwner->writeInt32(*it);
- }
- mOwner->endBox(); // stss
- }
-
- mOwner->beginBox("stsz");
- mOwner->writeInt32(0); // version=0, flags=0
- if (mSamplesHaveSameSize) {
- List<size_t>::iterator it = mSampleSizes.begin();
- mOwner->writeInt32(*it); // default sample size
- } else {
- mOwner->writeInt32(0);
- }
- mOwner->writeInt32(mNumSamples);
- if (!mSamplesHaveSameSize) {
- for (List<size_t>::iterator it = mSampleSizes.begin();
- it != mSampleSizes.end(); ++it) {
- mOwner->writeInt32(*it);
- }
- }
- mOwner->endBox(); // stsz
-
- mOwner->beginBox("stsc");
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mNumStscTableEntries);
- for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
- it != mStscTableEntries.end(); ++it) {
- mOwner->writeInt32(it->firstChunk);
- mOwner->writeInt32(it->samplesPerChunk);
- mOwner->writeInt32(it->sampleDescriptionId);
- }
- mOwner->endBox(); // stsc
- mOwner->beginBox(use32BitOffset? "stco": "co64");
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mNumStcoTableEntries);
- for (List<off64_t>::iterator it = mChunkOffsets.begin();
- it != mChunkOffsets.end(); ++it) {
- if (use32BitOffset) {
- mOwner->writeInt32(static_cast<int32_t>(*it));
- } else {
- mOwner->writeInt64((*it));
- }
- }
- mOwner->endBox(); // stco or co64
-
- mOwner->endBox(); // stbl
- mOwner->endBox(); // minf
- mOwner->endBox(); // mdia
+ writeDinfBox();
+ writeStblBox(use32BitOffset);
+ mOwner->endBox(); // minf
+ mOwner->endBox(); // mdia
mOwner->endBox(); // trak
}
+void MPEG4Writer::Track::writeStblBox(bool use32BitOffset) {
+ mOwner->beginBox("stbl");
+ mOwner->beginBox("stsd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(1); // entry count
+ if (mIsAudio) {
+ writeAudioFourCCBox();
+ } else {
+ writeVideoFourCCBox();
+ }
+ mOwner->endBox(); // stsd
+ writeSttsBox();
+ if (!mIsAudio) {
+ writeStssBox();
+ }
+ writeStszBox();
+ writeStscBox();
+ writeStcoBox(use32BitOffset);
+ mOwner->endBox(); // stbl
+}
+
+void MPEG4Writer::Track::writeVideoFourCCBox() {
+ const char *mime;
+ bool success = mMeta->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+ mOwner->beginBox("mp4v");
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+ mOwner->beginBox("s263");
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ mOwner->beginBox("avc1");
+ } else {
+ LOGE("Unknown mime type '%s'.", mime);
+ CHECK(!"should not be here, unknown mime type.");
+ }
+
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(0); // reserved
+ mOwner->writeInt16(1); // data ref index
+ mOwner->writeInt16(0); // predefined
+ mOwner->writeInt16(0); // reserved
+ mOwner->writeInt32(0); // predefined
+ mOwner->writeInt32(0); // predefined
+ mOwner->writeInt32(0); // predefined
+
+ int32_t width, height;
+ success = mMeta->findInt32(kKeyWidth, &width);
+ success = success && mMeta->findInt32(kKeyHeight, &height);
+ CHECK(success);
+
+ mOwner->writeInt16(width);
+ mOwner->writeInt16(height);
+ mOwner->writeInt32(0x480000); // horiz resolution
+ mOwner->writeInt32(0x480000); // vert resolution
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(1); // frame count
+ mOwner->write(" ", 32);
+ mOwner->writeInt16(0x18); // depth
+ mOwner->writeInt16(-1); // predefined
+
+ CHECK(23 + mCodecSpecificDataSize < 128);
+
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+ writeMp4vEsdsBox();
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+ writeD263Box();
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ writeAvccBox();
+ }
+
+ writePaspBox();
+ mOwner->endBox(); // mp4v, s263 or avc1
+}
+
+void MPEG4Writer::Track::writeAudioFourCCBox() {
+ const char *mime;
+ bool success = mMeta->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+ const char *fourcc = NULL;
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
+ fourcc = "samr";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
+ fourcc = "sawb";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+ fourcc = "mp4a";
+ } else {
+ LOGE("Unknown mime type '%s'.", mime);
+ CHECK(!"should not be here, unknown mime type.");
+ }
+
+ mOwner->beginBox(fourcc); // audio format
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(0); // reserved
+ mOwner->writeInt16(0x1); // data ref index
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ int32_t nChannels;
+ CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
+ mOwner->writeInt16(nChannels); // channel count
+ mOwner->writeInt16(16); // sample size
+ mOwner->writeInt16(0); // predefined
+ mOwner->writeInt16(0); // reserved
+
+ int32_t samplerate;
+ success = mMeta->findInt32(kKeySampleRate, &samplerate);
+ CHECK(success);
+ mOwner->writeInt32(samplerate << 16);
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+ writeMp4aEsdsBox();
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) ||
+ !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
+ writeDamrBox();
+ }
+ mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeMp4aEsdsBox() {
+ mOwner->beginBox("esds");
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
+
+ // Make sure all sizes encode to a single byte.
+ CHECK(mCodecSpecificDataSize + 23 < 128);
+
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt8(0x03); // ES_DescrTag
+ mOwner->writeInt8(23 + mCodecSpecificDataSize);
+ mOwner->writeInt16(0x0000);// ES_ID
+ mOwner->writeInt8(0x00);
+
+ mOwner->writeInt8(0x04); // DecoderConfigDescrTag
+ mOwner->writeInt8(15 + mCodecSpecificDataSize);
+ mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2
+ mOwner->writeInt8(0x15); // streamType AudioStream
+
+ mOwner->writeInt16(0x03); // XXX
+ mOwner->writeInt8(0x00); // buffer size 24-bit
+ mOwner->writeInt32(96000); // max bit rate
+ mOwner->writeInt32(96000); // avg bit rate
+
+ mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
+ mOwner->writeInt8(mCodecSpecificDataSize);
+ mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+
+ static const uint8_t kData2[] = {
+ 0x06, // SLConfigDescriptorTag
+ 0x01,
+ 0x02
+ };
+ mOwner->write(kData2, sizeof(kData2));
+
+ mOwner->endBox(); // esds
+}
+
+void MPEG4Writer::Track::writeMp4vEsdsBox() {
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
+ mOwner->beginBox("esds");
+
+ mOwner->writeInt32(0); // version=0, flags=0
+
+ mOwner->writeInt8(0x03); // ES_DescrTag
+ mOwner->writeInt8(23 + mCodecSpecificDataSize);
+ mOwner->writeInt16(0x0000); // ES_ID
+ mOwner->writeInt8(0x1f);
+
+ mOwner->writeInt8(0x04); // DecoderConfigDescrTag
+ mOwner->writeInt8(15 + mCodecSpecificDataSize);
+ mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2
+ mOwner->writeInt8(0x11); // streamType VisualStream
+
+ static const uint8_t kData[] = {
+ 0x01, 0x77, 0x00,
+ 0x00, 0x03, 0xe8, 0x00,
+ 0x00, 0x03, 0xe8, 0x00
+ };
+ mOwner->write(kData, sizeof(kData));
+
+ mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
+
+ mOwner->writeInt8(mCodecSpecificDataSize);
+ mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+
+ static const uint8_t kData2[] = {
+ 0x06, // SLConfigDescriptorTag
+ 0x01,
+ 0x02
+ };
+ mOwner->write(kData2, sizeof(kData2));
+
+ mOwner->endBox(); // esds
+}
+
+void MPEG4Writer::Track::writeTkhdBox(time_t now) {
+ mOwner->beginBox("tkhd");
+ // Flags = 7 to indicate that the track is enabled, and
+ // part of the presentation
+ mOwner->writeInt32(0x07); // version=0, flags=7
+ mOwner->writeInt32(now); // creation time
+ mOwner->writeInt32(now); // modification time
+ mOwner->writeInt32(mTrackId + 1); // track id starts with 1
+ mOwner->writeInt32(0); // reserved
+ int64_t trakDurationUs = getDurationUs();
+ int32_t mvhdTimeScale = mOwner->getTimeScale();
+ int32_t tkhdDuration =
+ (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
+ mOwner->writeInt32(tkhdDuration); // in mvhd timescale
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(0); // layer
+ mOwner->writeInt16(0); // alternate group
+ mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume
+ mOwner->writeInt16(0); // reserved
+
+ mOwner->writeCompositionMatrix(mRotation); // matrix
+
+ if (mIsAudio) {
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ } else {
+ int32_t width, height;
+ bool success = mMeta->findInt32(kKeyWidth, &width);
+ success = success && mMeta->findInt32(kKeyHeight, &height);
+ CHECK(success);
+
+ mOwner->writeInt32(width << 16); // 32-bit fixed-point value
+ mOwner->writeInt32(height << 16); // 32-bit fixed-point value
+ }
+ mOwner->endBox(); // tkhd
+}
+
+void MPEG4Writer::Track::writeVmhdBox() {
+ mOwner->beginBox("vmhd");
+ mOwner->writeInt32(0x01); // version=0, flags=1
+ mOwner->writeInt16(0); // graphics mode
+ mOwner->writeInt16(0); // opcolor
+ mOwner->writeInt16(0);
+ mOwner->writeInt16(0);
+ mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeSmhdBox() {
+ mOwner->beginBox("smhd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt16(0); // balance
+ mOwner->writeInt16(0); // reserved
+ mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeHdlrBox() {
+ mOwner->beginBox("hdlr");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(0); // component type: should be mhlr
+ mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ // Removing "r" for the name string just makes the string 4 byte aligned
+ mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name
+ mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeMdhdBox(time_t now) {
+ int64_t trakDurationUs = getDurationUs();
+ mOwner->beginBox("mdhd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(now); // creation time
+ mOwner->writeInt32(now); // modification time
+ mOwner->writeInt32(mTimeScale); // media timescale
+ int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
+ mOwner->writeInt32(mdhdDuration); // use media timescale
+ // Language follows the three letter standard ISO-639-2/T
+ // 'e', 'n', 'g' for "English", for instance.
+ // Each character is packed as the difference between its ASCII value and 0x60.
+ // For "English", these are 00101, 01110, 00111.
+ // XXX: Where is the padding bit located: 0x15C7?
+ mOwner->writeInt16(0); // language code
+ mOwner->writeInt16(0); // predefined
+ mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeDamrBox() {
+ // 3gpp2 Spec AMRSampleEntry fields
+ mOwner->beginBox("damr");
+ mOwner->writeCString(" "); // vendor: 4 bytes
+ mOwner->writeInt8(0); // decoder version
+ mOwner->writeInt16(0x83FF); // mode set: all enabled
+ mOwner->writeInt8(0); // mode change period
+ mOwner->writeInt8(1); // frames per sample
+ mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeUrlBox() {
+ // The table index here refers to the sample description index
+ // in the sample table entries.
+ mOwner->beginBox("url ");
+ mOwner->writeInt32(1); // version=0, flags=1 (self-contained)
+ mOwner->endBox(); // url
+}
+
+void MPEG4Writer::Track::writeDrefBox() {
+ mOwner->beginBox("dref");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(1); // entry count (either url or urn)
+ writeUrlBox();
+ mOwner->endBox(); // dref
+}
+
+void MPEG4Writer::Track::writeDinfBox() {
+ mOwner->beginBox("dinf");
+ writeDrefBox();
+ mOwner->endBox(); // dinf
+}
+
+void MPEG4Writer::Track::writeAvccBox() {
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize >= 5);
+
+ // Patch avcc's lengthSize field to match the number
+ // of bytes we use to indicate the size of a nal unit.
+ uint8_t *ptr = (uint8_t *)mCodecSpecificData;
+ ptr[4] = (ptr[4] & 0xfc) | (mOwner->useNalLengthFour() ? 3 : 1);
+ mOwner->beginBox("avcC");
+ mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+ mOwner->endBox(); // avcC
+}
+
+void MPEG4Writer::Track::writeD263Box() {
+ mOwner->beginBox("d263");
+ mOwner->writeInt32(0); // vendor
+ mOwner->writeInt8(0); // decoder version
+ mOwner->writeInt8(10); // level: 10
+ mOwner->writeInt8(0); // profile: 0
+ mOwner->endBox(); // d263
+}
+
+// This is useful if the pixel is not square
+void MPEG4Writer::Track::writePaspBox() {
+ mOwner->beginBox("pasp");
+ mOwner->writeInt32(1 << 16); // hspacing
+ mOwner->writeInt32(1 << 16); // vspacing
+ mOwner->endBox(); // pasp
+}
+
+void MPEG4Writer::Track::writeSttsBox() {
+ mOwner->beginBox("stts");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mNumSttsTableEntries);
+
+ // Compensate for small start time difference from different media tracks
+ int64_t trackStartTimeOffsetUs = 0;
+ int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
+ if (mStartTimestampUs != moovStartTimeUs) {
+ CHECK(mStartTimestampUs > moovStartTimeUs);
+ trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
+ }
+ int64_t prevTimestampUs = trackStartTimeOffsetUs;
+ for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
+ it != mSttsTableEntries.end(); ++it) {
+ mOwner->writeInt32(it->sampleCount);
+
+ // Make sure that we are calculating the sample duration the exactly
+ // same way as we made decision on how to create stts entries.
+ int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs;
+ int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL -
+ (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+ prevTimestampUs += (it->sampleCount * it->sampleDurationUs);
+
+ mOwner->writeInt32(dur);
+ }
+ mOwner->endBox(); // stts
+}
+
+void MPEG4Writer::Track::writeStssBox() {
+ mOwner->beginBox("stss");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mNumStssTableEntries); // number of sync frames
+ for (List<int32_t>::iterator it = mStssTableEntries.begin();
+ it != mStssTableEntries.end(); ++it) {
+ mOwner->writeInt32(*it);
+ }
+ mOwner->endBox(); // stss
+}
+
+void MPEG4Writer::Track::writeStszBox() {
+ mOwner->beginBox("stsz");
+ mOwner->writeInt32(0); // version=0, flags=0
+ if (mSamplesHaveSameSize) {
+ List<size_t>::iterator it = mSampleSizes.begin();
+ mOwner->writeInt32(*it); // default sample size
+ } else {
+ mOwner->writeInt32(0);
+ }
+ mOwner->writeInt32(mNumSamples);
+ if (!mSamplesHaveSameSize) {
+ for (List<size_t>::iterator it = mSampleSizes.begin();
+ it != mSampleSizes.end(); ++it) {
+ mOwner->writeInt32(*it);
+ }
+ }
+ mOwner->endBox(); // stsz
+}
+
+void MPEG4Writer::Track::writeStscBox() {
+ mOwner->beginBox("stsc");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mNumStscTableEntries);
+ for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
+ it != mStscTableEntries.end(); ++it) {
+ mOwner->writeInt32(it->firstChunk);
+ mOwner->writeInt32(it->samplesPerChunk);
+ mOwner->writeInt32(it->sampleDescriptionId);
+ }
+ mOwner->endBox(); // stsc
+}
+
+void MPEG4Writer::Track::writeStcoBox(bool use32BitOffset) {
+ mOwner->beginBox(use32BitOffset? "stco": "co64");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mNumStcoTableEntries);
+ for (List<off64_t>::iterator it = mChunkOffsets.begin();
+ it != mChunkOffsets.end(); ++it) {
+ if (use32BitOffset) {
+ mOwner->writeInt32(static_cast<int32_t>(*it));
+ } else {
+ mOwner->writeInt64((*it));
+ }
+ }
+ mOwner->endBox(); // stco or co64
+}
+
+void MPEG4Writer::writeUdtaBox() {
+ beginBox("udta");
+ writeGeoDataBox();
+ endBox();
+}
+
+/*
+ * Geodata is stored according to ISO-6709 standard.
+ */
+void MPEG4Writer::writeGeoDataBox() {
+ beginBox("\xA9xyz");
+ /*
+ * For historical reasons, any user data start
+ * with "\0xA9", must be followed by its assoicated
+ * language code.
+ * 0x0012: locale en
+ * 0x15c7: language 5575
+ */
+ writeInt32(0x001215c7);
+ writeLatitude(mLatitudex10000);
+ writeLongitude(mLongitudex10000);
+ writeInt8(0x2F);
+ endBox();
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 4599fca..8cd08bc8 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -35,13 +35,18 @@
const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
+const char *MEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac";
+const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav";
const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts";
+const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi";
const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
+const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt";
+
} // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index d12ac64..af0131e 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include "include/AMRExtractor.h"
+#include "include/AVIExtractor.h"
#include "include/MP3Extractor.h"
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
@@ -26,6 +27,8 @@
#include "include/MPEG2TSExtractor.h"
#include "include/DRMExtractor.h"
#include "include/WVMExtractor.h"
+#include "include/FLACExtractor.h"
+#include "include/AACExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -65,39 +68,59 @@
mime, confidence);
}
- if (!strncmp(mime, "drm", 3)) {
- const char *originalMime = strrchr(mime, '+') + 1;
-
- if (!strncmp(mime, "drm+es_based", 12)) {
+ bool isDrm = false;
+ // DRM MIME type syntax is "drm+type+original" where
+ // type is "es_based" or "container_based" and
+ // original is the content's cleartext MIME type
+ if (!strncmp(mime, "drm+", 4)) {
+ const char *originalMime = strchr(mime+4, '+');
+ if (originalMime == NULL) {
+ // second + not found
+ return NULL;
+ }
+ ++originalMime;
+ if (!strncmp(mime, "drm+es_based+", 13)) {
+ // DRMExtractor sets container metadata kKeyIsDRM to 1
return new DRMExtractor(source, originalMime);
- } else if (!strncmp(mime, "drm+container_based", 19)) {
+ } else if (!strncmp(mime, "drm+container_based+", 20)) {
mime = originalMime;
+ isDrm = true;
} else {
return NULL;
}
}
+ MediaExtractor *ret = NULL;
if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
|| !strcasecmp(mime, "audio/mp4")) {
- return new MPEG4Extractor(source);
+ ret = new MPEG4Extractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
- return new MP3Extractor(source, meta);
+ ret = new MP3Extractor(source, meta);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
- return new AMRExtractor(source);
+ ret = new AMRExtractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) {
+ ret = new FLACExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {
- return new WAVExtractor(source);
+ ret = new WAVExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {
- return new OggExtractor(source);
+ ret = new OggExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
- return new MatroskaExtractor(source);
+ ret = new MatroskaExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
- return new MPEG2TSExtractor(source);
+ ret = new MPEG2TSExtractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AVI)) {
+ ret = new AVIExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) {
- return new WVMExtractor(source);
+ ret = new WVMExtractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
+ ret = new AACExtractor(source);
+ }
+ if (ret != NULL && isDrm) {
+ ret->getMetaData()->setInt32(kKeyIsDRM, 1);
}
- return NULL;
+ return ret;
}
} // namespace android
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 741aa1c..81f2e47 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -135,6 +135,10 @@
void PageCache::copy(size_t from, void *data, size_t size) {
LOGV("copy from %d size %d", from, size);
+ if (size == 0) {
+ return;
+ }
+
CHECK_LE(from + size, mTotalSize);
size_t offset = 0;
@@ -319,25 +323,28 @@
}
void NuCachedSource2::restartPrefetcherIfNecessary_l(
- bool ignoreLowWaterThreshold) {
+ bool ignoreLowWaterThreshold, bool force) {
static const size_t kGrayArea = 1024 * 1024;
if (mFetching || mFinalStatus != OK) {
return;
}
- if (!ignoreLowWaterThreshold
+ if (!ignoreLowWaterThreshold && !force
&& mCacheOffset + mCache->totalSize() - mLastAccessPos
>= kLowWaterThreshold) {
return;
}
size_t maxBytes = mLastAccessPos - mCacheOffset;
- if (maxBytes < kGrayArea) {
- return;
- }
- maxBytes -= kGrayArea;
+ if (!force) {
+ if (maxBytes < kGrayArea) {
+ return;
+ }
+
+ maxBytes -= kGrayArea;
+ }
size_t actualBytes = mCache->releaseFromStart(maxBytes);
mCacheOffset += actualBytes;
@@ -409,10 +416,19 @@
}
ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
+ CHECK_LE(size, (size_t)kHighWaterThreshold);
+
LOGV("readInternal offset %lld size %d", offset, size);
Mutex::Autolock autoLock(mLock);
+ if (!mFetching) {
+ mLastAccessPos = offset;
+ restartPrefetcherIfNecessary_l(
+ false, // ignoreLowWaterThreshold
+ true); // force
+ }
+
if (offset < mCacheOffset
|| offset >= (off64_t)(mCacheOffset + mCache->totalSize())) {
static const off64_t kPadding = 256 * 1024;
@@ -434,6 +450,11 @@
}
size_t avail = mCache->totalSize() - delta;
+
+ if (avail > size) {
+ avail = size;
+ }
+
mCache->copy(delta, data, avail);
return avail;
@@ -477,11 +498,11 @@
restartPrefetcherIfNecessary_l(true /* ignore low water threshold */);
}
-DecryptHandle* NuCachedSource2::DrmInitialization(DrmManagerClient* client) {
- return mSource->DrmInitialization(client);
+sp<DecryptHandle> NuCachedSource2::DrmInitialization() {
+ return mSource->DrmInitialization();
}
-void NuCachedSource2::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {
+void NuCachedSource2::getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {
mSource->getDrmInfo(handle, client);
}
@@ -489,4 +510,8 @@
return mSource->getUri();
}
+String8 NuCachedSource2::getMIMEType() const {
+ return mSource->getMIMEType();
+}
+
} // namespace android
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 653c85e..ce30fc8 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 "NuHTTPDataSource"
#include <utils/Log.h>
@@ -24,22 +40,30 @@
}
static bool ParseURL(
- const char *url, String8 *host, unsigned *port, String8 *path) {
+ const char *url, String8 *host, unsigned *port,
+ String8 *path, bool *https) {
host->setTo("");
*port = 0;
path->setTo("");
- if (strncasecmp("http://", url, 7)) {
+ size_t hostStart;
+ if (!strncasecmp("http://", url, 7)) {
+ hostStart = 7;
+ *https = false;
+ } else if (!strncasecmp("https://", url, 8)) {
+ hostStart = 8;
+ *https = true;
+ } else {
return false;
}
- const char *slashPos = strchr(&url[7], '/');
+ const char *slashPos = strchr(&url[hostStart], '/');
if (slashPos == NULL) {
- host->setTo(&url[7]);
+ host->setTo(&url[hostStart]);
path->setTo("/");
} else {
- host->setTo(&url[7], slashPos - &url[7]);
+ host->setTo(&url[hostStart], slashPos - &url[hostStart]);
path->setTo(slashPos);
}
@@ -57,15 +81,17 @@
String8 tmp(host->string(), colonOffset);
*host = tmp;
} else {
- *port = 80;
+ *port = (*https) ? 443 : 80;
}
return true;
}
-NuHTTPDataSource::NuHTTPDataSource()
- : mState(DISCONNECTED),
+NuHTTPDataSource::NuHTTPDataSource(uint32_t flags)
+ : mFlags(flags),
+ mState(DISCONNECTED),
mPort(0),
+ mHTTPS(false),
mOffset(0),
mContentLength(0),
mContentLengthValid(false),
@@ -79,6 +105,17 @@
}
NuHTTPDataSource::~NuHTTPDataSource() {
+ if (mDecryptHandle != NULL) {
+ // To release mDecryptHandle
+ CHECK(mDrmManagerClient);
+ mDrmManagerClient->closeDecryptSession(mDecryptHandle);
+ mDecryptHandle = NULL;
+ }
+
+ if (mDrmManagerClient != NULL) {
+ delete mDrmManagerClient;
+ mDrmManagerClient = NULL;
+ }
}
status_t NuHTTPDataSource::connect(
@@ -99,12 +136,14 @@
unsigned port;
mUri = uri;
+ mContentType = String8("application/octet-stream");
- if (!ParseURL(uri, &host, &port, &path)) {
+ bool https;
+ if (!ParseURL(uri, &host, &port, &path, &https)) {
return ERROR_MALFORMED;
}
- return connect(host, port, path, headers, offset);
+ return connect(host, port, path, https, headers, offset);
}
static bool IsRedirectStatusCode(int httpStatus) {
@@ -114,14 +153,19 @@
status_t NuHTTPDataSource::connect(
const char *host, unsigned port, const char *path,
+ bool https,
const String8 &headers,
off64_t offset) {
- LOGI("connect to %s:%u%s @%lld", host, port, path, offset);
+ if (!(mFlags & kFlagIncognito)) {
+ LOGI("connect to %s:%u%s @%lld", host, port, path, offset);
+ } else {
+ LOGI("connect to <URL suppressed> @%lld", offset);
+ }
bool needsToReconnect = true;
if (mState == CONNECTED && host == mHost && port == mPort
- && offset == mOffset) {
+ && https == mHTTPS && offset == mOffset) {
if (mContentLengthValid && mOffset == mContentLength) {
LOGI("Didn't have to reconnect, old one's still good.");
needsToReconnect = false;
@@ -131,6 +175,7 @@
mHost = host;
mPort = port;
mPath = path;
+ mHTTPS = https;
mHeaders = headers;
status_t err = OK;
@@ -139,7 +184,7 @@
if (needsToReconnect) {
mHTTP.disconnect();
- err = mHTTP.connect(host, port);
+ err = mHTTP.connect(host, port, https);
}
if (err != OK) {
@@ -221,6 +266,15 @@
}
}
+ {
+ AString value;
+ if (mHTTP.find_header_value("Content-Type", &value)) {
+ mContentType = String8(value.c_str());
+ } else {
+ mContentType = String8("application/octet-stream");
+ }
+ }
+
applyTimeoutResponse();
if (offset == 0) {
@@ -338,11 +392,18 @@
Mutex::Autolock autoLock(mLock);
+ // if it's a DRM container based streaming, call pread() of the DRM plugin
+ // to get the decrypted data
+ if (mDecryptHandle != NULL && DecryptApiType::CONTAINER_BASED
+ == mDecryptHandle->decryptApiType) {
+ return mDrmManagerClient->pread(mDecryptHandle, data, size, offset);
+ }
+
if (offset != mOffset) {
String8 host = mHost;
String8 path = mPath;
String8 headers = mHeaders;
- status_t err = connect(host, mPort, path, headers, offset);
+ status_t err = connect(host, mPort, path, mHTTPS, headers, offset);
if (err != OK) {
return err;
@@ -366,7 +427,14 @@
internalRead((uint8_t *)data + numBytesRead, size - numBytesRead);
if (n < 0) {
- return n;
+ if (numBytesRead == 0 || mContentLengthValid) {
+ return n;
+ }
+
+ // If there was an error we want to at least return the data
+ // we've already successfully read. The next call to read will
+ // then return the error.
+ n = 0;
}
int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
@@ -486,11 +554,14 @@
}
}
-DecryptHandle* NuHTTPDataSource::DrmInitialization(DrmManagerClient* client) {
- if (client == NULL) {
+sp<DecryptHandle> NuHTTPDataSource::DrmInitialization() {
+ if (mDrmManagerClient == NULL) {
+ mDrmManagerClient = new DrmManagerClient();
+ }
+
+ if (mDrmManagerClient == NULL) {
return NULL;
}
- mDrmManagerClient = client;
if (mDecryptHandle == NULL) {
/* Note if redirect occurs, mUri is the redirect uri instead of the
@@ -500,14 +571,15 @@
}
if (mDecryptHandle == NULL) {
+ delete mDrmManagerClient;
mDrmManagerClient = NULL;
}
return mDecryptHandle;
}
-void NuHTTPDataSource::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {
- *handle = mDecryptHandle;
+void NuHTTPDataSource::getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {
+ handle = mDecryptHandle;
*client = mDrmManagerClient;
}
@@ -516,4 +588,8 @@
return mUri;
}
+String8 NuHTTPDataSource::getMIMEType() const {
+ return mContentType;
+}
+
} // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 2a19b25..0f0ffd4 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -53,7 +53,10 @@
#include <OMX_Audio.h>
#include <OMX_Component.h>
+#if HAVE_SOFTWARE_DECODERS
#include "include/ThreadedSource.h"
+#endif
+
#include "include/avc_utils.h"
namespace android {
@@ -65,11 +68,6 @@
const char *codec;
};
-#define FACTORY_CREATE(name) \
-static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \
- return new name(source); \
-}
-
#define FACTORY_CREATE_ENCODER(name) \
static sp<MediaSource> Make##name(const sp<MediaSource> &source, const sp<MetaData> &meta) { \
return new name(source, meta); \
@@ -77,21 +75,30 @@
#define FACTORY_REF(name) { #name, Make##name },
-FACTORY_CREATE(MP3Decoder)
-FACTORY_CREATE(AMRNBDecoder)
-FACTORY_CREATE(AMRWBDecoder)
-FACTORY_CREATE(AACDecoder)
-FACTORY_CREATE(AVCDecoder)
-FACTORY_CREATE(G711Decoder)
-FACTORY_CREATE(M4vH263Decoder)
-FACTORY_CREATE(VorbisDecoder)
-FACTORY_CREATE(VPXDecoder)
FACTORY_CREATE_ENCODER(AMRNBEncoder)
FACTORY_CREATE_ENCODER(AMRWBEncoder)
FACTORY_CREATE_ENCODER(AACEncoder)
FACTORY_CREATE_ENCODER(AVCEncoder)
FACTORY_CREATE_ENCODER(M4vH263Encoder)
+#if HAVE_SOFTWARE_DECODERS
+
+#define FACTORY_CREATE(name) \
+static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \
+ return new name(source); \
+}
+
+FACTORY_CREATE(AMRNBDecoder)
+FACTORY_CREATE(AMRWBDecoder)
+FACTORY_CREATE(AACDecoder)
+FACTORY_CREATE(AVCDecoder)
+FACTORY_CREATE(G711Decoder)
+FACTORY_CREATE(MP3Decoder)
+FACTORY_CREATE(M4vH263Decoder)
+FACTORY_CREATE(VorbisDecoder)
+FACTORY_CREATE(VPXDecoder)
+#endif
+
static sp<MediaSource> InstantiateSoftwareEncoder(
const char *name, const sp<MediaSource> &source,
const sp<MetaData> &meta) {
@@ -119,18 +126,19 @@
static sp<MediaSource> InstantiateSoftwareCodec(
const char *name, const sp<MediaSource> &source) {
+#if HAVE_SOFTWARE_DECODERS
struct FactoryInfo {
const char *name;
sp<MediaSource> (*CreateFunc)(const sp<MediaSource> &);
};
static const FactoryInfo kFactoryInfo[] = {
- FACTORY_REF(MP3Decoder)
FACTORY_REF(AMRNBDecoder)
FACTORY_REF(AMRWBDecoder)
FACTORY_REF(AACDecoder)
FACTORY_REF(AVCDecoder)
FACTORY_REF(G711Decoder)
+ FACTORY_REF(MP3Decoder)
FACTORY_REF(M4vH263Decoder)
FACTORY_REF(VorbisDecoder)
FACTORY_REF(VPXDecoder)
@@ -145,6 +153,7 @@
return (*kFactoryInfo[i].CreateFunc)(source);
}
}
+#endif
return NULL;
}
@@ -156,36 +165,47 @@
{ MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
// { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.Nvidia.mp3.decoder" },
// { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.google.mp3.decoder" },
{ MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" },
// { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },
// { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amr.decoder" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.google.amrnb.decoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" },
// { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amrwb.decoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.decoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" },
// { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.Nvidia.aac.decoder" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
+ { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.decoder" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
+ { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "OMX.google.g711.alaw.decoder" },
{ MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" },
+ { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "OMX.google.g711.mlaw.decoder" },
{ MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.decode" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Decoder" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.google.mpeg4.decoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.decode" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Decoder" },
+ { MEDIA_MIMETYPE_VIDEO_H263, "OMX.google.h263.decoder" },
{ MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.decode" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Decoder" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.google.avc.decoder" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },
+ { MEDIA_MIMETYPE_AUDIO_VORBIS, "OMX.google.vorbis.decoder" },
{ MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" },
+ { MEDIA_MIMETYPE_VIDEO_VPX, "OMX.google.vpx.decoder" },
{ MEDIA_MIMETYPE_VIDEO_VPX, "VPXDecoder" },
};
@@ -277,6 +297,10 @@
}
static bool IsSoftwareCodec(const char *componentName) {
+ if (!strncmp("OMX.google.", componentName, 11)) {
+ return true;
+ }
+
if (!strncmp("OMX.", componentName, 4)) {
return false;
}
@@ -284,26 +308,29 @@
return true;
}
-// A sort order in which non-OMX components are first,
-// followed by software codecs, and followed by all the others.
+// 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 String8 *elem1, const String8 *elem2) {
- bool isNotOMX1 = strncmp(elem1->string(), "OMX.", 4);
- bool isNotOMX2 = strncmp(elem2->string(), "OMX.", 4);
-
- if (isNotOMX1) {
- if (isNotOMX2) { return 0; }
- return -1;
- }
- if (isNotOMX2) {
- return 1;
- }
+ bool isOMX1 = !strncmp(elem1->string(), "OMX.", 4);
+ bool isOMX2 = !strncmp(elem2->string(), "OMX.", 4);
bool isSoftwareCodec1 = IsSoftwareCodec(elem1->string());
bool isSoftwareCodec2 = IsSoftwareCodec(elem2->string());
if (isSoftwareCodec1) {
- if (isSoftwareCodec2) { return 0; }
+ if (!isSoftwareCodec2) { return -1; }
+
+ if (isOMX1) {
+ if (isOMX2) { return 0; }
+
+ return -1;
+ } else {
+ if (isOMX2) { return 0; }
+
+ return 1;
+ }
+
return -1;
}
@@ -528,6 +555,12 @@
mOnlySubmitOneBufferAtOneTime = true;
}
+ mEnableGrallocUsageProtected = false;
+ if (flags & kEnableGrallocUsageProtected) {
+ mEnableGrallocUsageProtected = true;
+ }
+ LOGV("configureCodec protected=%d", mEnableGrallocUsageProtected);
+
if (!(flags & kIgnoreCodecSpecificData)) {
uint32_t type;
const void *data;
@@ -616,6 +649,11 @@
LOGE("Profile and/or level exceed the decoder's capabilities.");
return ERROR_UNSUPPORTED;
}
+ } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
+ addCodecSpecificData(data, size);
+
+ CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size));
+ addCodecSpecificData(data, size);
}
}
@@ -625,16 +663,23 @@
}
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mMIME)) {
setAMRFormat(false /* isWAMR */, bitRate);
- }
- if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) {
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) {
setAMRFormat(true /* isWAMR */, bitRate);
- }
- if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) {
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) {
int32_t numChannels, sampleRate;
CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
setAACFormat(numChannels, sampleRate, bitRate);
+ } 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 numChannels;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+
+ setG711Format(numChannels);
}
if (!strncasecmp(mMIME, "video/", 6)) {
@@ -1310,6 +1355,8 @@
compressionFormat = OMX_VIDEO_CodingMPEG4;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
compressionFormat = OMX_VIDEO_CodingH263;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) {
+ compressionFormat = OMX_VIDEO_CodingVPX;
} else {
LOGE("Not a supported video mime type: %s", mime);
CHECK(!"Should not be here. Not a supported video mime type.");
@@ -1434,9 +1481,11 @@
mSeekTimeUs(-1),
mSeekMode(ReadOptions::SEEK_CLOSEST_SYNC),
mTargetTimeUs(-1),
+ mOutputPortSettingsChangedPending(false),
mLeftOverBuffer(NULL),
mPaused(false),
- mNativeWindow(nativeWindow) {
+ mNativeWindow(!strncmp(componentName, "OMX.google.", 11)
+ ? NULL : nativeWindow) {
mPortStatus[kPortIndexInput] = ENABLED;
mPortStatus[kPortIndexOutput] = ENABLED;
@@ -1586,6 +1635,11 @@
return allocateOutputBuffersFromNativeWindow();
}
+ if (mEnableGrallocUsageProtected && portIndex == kPortIndexOutput) {
+ LOGE("protected output buffers must be stent to an ANativeWindow");
+ return PERMISSION_DENIED;
+ }
+
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = portIndex;
@@ -1737,29 +1791,74 @@
return err;
}
- // Increase the buffer count by one to allow for the ANativeWindow to hold
- // on to one of the buffers.
- def.nBufferCountActual++;
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- if (err != OK) {
- return err;
- }
-
err = applyRotation();
if (err != OK) {
return err;
}
// Set up the native window.
- // XXX TODO: Get the gralloc usage flags from the OMX plugin!
+ OMX_U32 usage = 0;
+ err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
+ if (err != 0) {
+ LOGW("querying usage flags from OMX IL component failed: %d", err);
+ // XXX: Currently this error is logged, but not fatal.
+ usage = 0;
+ }
+ if (mEnableGrallocUsageProtected) {
+ usage |= GRALLOC_USAGE_PROTECTED;
+ }
+
+ // Make sure to check whether either Stagefright or the video decoder
+ // requested protected buffers.
+ if (usage & GRALLOC_USAGE_PROTECTED) {
+ // Verify that the ANativeWindow sends images directly to
+ // SurfaceFlinger.
+ int queuesToNativeWindow = 0;
+ err = mNativeWindow->query(
+ mNativeWindow.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+ &queuesToNativeWindow);
+ if (err != 0) {
+ LOGE("error authenticating native window: %d", err);
+ return err;
+ }
+ if (queuesToNativeWindow != 1) {
+ LOGE("native window could not be authenticated");
+ return PERMISSION_DENIED;
+ }
+ }
+
+ LOGV("native_window_set_usage usage=0x%x", usage);
err = native_window_set_usage(
- mNativeWindow.get(), GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
+ mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
if (err != 0) {
LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
return err;
}
+ int minUndequeuedBufs = 0;
+ err = mNativeWindow->query(mNativeWindow.get(),
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs);
+ if (err != 0) {
+ LOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+
+ // XXX: Is this the right logic to use? It's not clear to me what the OMX
+ // buffer counts refer to - how do they account for the renderer holding on
+ // to buffers?
+ if (def.nBufferCountActual < def.nBufferCountMin + minUndequeuedBufs) {
+ OMX_U32 newBufferCount = def.nBufferCountMin + minUndequeuedBufs;
+ def.nBufferCountActual = newBufferCount;
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ if (err != OK) {
+ CODEC_LOGE("setting nBufferCountActual to %lu failed: %d",
+ newBufferCount, err);
+ return err;
+ }
+ }
+
err = native_window_set_buffer_count(
mNativeWindow.get(), def.nBufferCountActual);
if (err != 0) {
@@ -1768,16 +1867,12 @@
return err;
}
- // XXX TODO: Do something so the ANativeWindow knows that we'll need to get
- // the same set of buffers.
-
CODEC_LOGI("allocating %lu buffers from a native window of size %lu on "
"output port", def.nBufferCountActual, def.nBufferSize);
// Dequeue buffers and send them to OMX
- OMX_U32 i;
- for (i = 0; i < def.nBufferCountActual; i++) {
- android_native_buffer_t* buf;
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) {
+ ANativeWindowBuffer* buf;
err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
if (err != 0) {
LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
@@ -1785,42 +1880,40 @@
}
sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
- IOMX::buffer_id bufferId;
- err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
- &bufferId);
- if (err != 0) {
- break;
- }
-
- CODEC_LOGV("registered graphic buffer with ID %p (pointer = %p)",
- bufferId, graphicBuffer.get());
-
BufferInfo info;
info.mData = NULL;
info.mSize = def.nBufferSize;
- info.mBuffer = bufferId;
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 %p (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 = i;
+ cancelEnd = mPortBuffers[kPortIndexOutput].size();
} else {
// Return the last two buffers to the native window.
- // XXX TODO: The number of buffers the native window owns should probably be
- // queried from it when we put the native window in fixed buffer pool mode
- // (which needs to be implemented). Currently it's hard-coded to 2.
- cancelStart = def.nBufferCountActual - 2;
+ cancelStart = def.nBufferCountActual - minUndequeuedBufs;
cancelEnd = def.nBufferCountActual;
}
@@ -1849,7 +1942,7 @@
OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() {
// Dequeue the next buffer from the native window.
- android_native_buffer_t* buf;
+ ANativeWindowBuffer* buf;
int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
if (err != 0) {
CODEC_LOGE("dequeueBuffer failed w/ error 0x%08x", err);
@@ -1885,6 +1978,11 @@
}
void OMXCodec::on_message(const omx_message &msg) {
+ if (mState == ERROR) {
+ LOGW("Dropping OMX message - we're in ERROR state.");
+ return;
+ }
+
switch (msg.type) {
case omx_message::EVENT:
{
@@ -2188,13 +2286,15 @@
if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
onPortSettingsChanged(data1);
- } else if (data1 == kPortIndexOutput
- && data2 == OMX_IndexConfigCommonOutputCrop) {
+ } else if (data1 == kPortIndexOutput &&
+ (data2 == OMX_IndexConfigCommonOutputCrop ||
+ data2 == OMX_IndexConfigCommonScale)) {
sp<MetaData> oldOutputFormat = mOutputFormat;
initOutputFormat(mSource->getFormat());
- if (formatHasNotablyChanged(oldOutputFormat, mOutputFormat)) {
+ if (data2 == OMX_IndexConfigCommonOutputCrop &&
+ formatHasNotablyChanged(oldOutputFormat, mOutputFormat)) {
mOutputPortSettingsHaveChanged = true;
if (mNativeWindow != NULL) {
@@ -2206,13 +2306,46 @@
android_native_rect_t crop;
crop.left = left;
crop.top = top;
- crop.right = right;
- crop.bottom = bottom;
+ 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);
}
+ } 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.
+ LOGV("Get OMX_IndexConfigScale: 0x%lx/0x%lx",
+ 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;
@@ -2344,6 +2477,14 @@
drainInputBuffers();
fillOutputBuffers();
}
+
+ if (mOutputPortSettingsChangedPending) {
+ CODEC_LOGV(
+ "Honoring deferred output port settings change.");
+
+ mOutputPortSettingsChangedPending = false;
+ onPortSettingsChanged(kPortIndexOutput);
+ }
}
break;
@@ -2407,6 +2548,8 @@
CODEC_LOGV("Now Executing.");
+ mOutputPortSettingsChangedPending = false;
+
setState(EXECUTING);
// Buffers will be submitted to the component in the first
@@ -2520,6 +2663,14 @@
CHECK_EQ((int)mState, (int)EXECUTING);
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) {
@@ -2712,6 +2863,7 @@
if (srcBuffer->meta_data()->findInt64(
kKeyTargetTime, &targetTimeUs)
&& targetTimeUs >= 0) {
+ CODEC_LOGV("targetTimeUs = %lld us", targetTimeUs);
mTargetTimeUs = targetTimeUs;
} else {
mTargetTimeUs = -1;
@@ -2789,6 +2941,23 @@
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;
@@ -3114,6 +3283,11 @@
}
}
+void OMXCodec::setG711Format(int32_t numChannels) {
+ CHECK(!mIsEncoder);
+ setRawAudioFormat(kPortIndexInput, 8000, numChannels);
+}
+
void OMXCodec::setImageOutputFormat(
OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height) {
CODEC_LOGV("setImageOutputFormat(%ld, %ld)", width, height);
@@ -3385,6 +3559,14 @@
}
if (seeking) {
+ while (mState == RECONFIGURING) {
+ mBufferFilled.wait(mLock);
+ }
+
+ if (mState != EXECUTING) {
+ return UNKNOWN_ERROR;
+ }
+
CODEC_LOGV("seeking to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6);
mSignalledEOS = false;
@@ -3895,6 +4077,13 @@
numChannels, params.nChannels);
}
+ if (sampleRate != (int32_t)params.nSamplingRate) {
+ LOGW("Codec outputs at different sampling rate than "
+ "what the input stream contains (contains data at "
+ "%d Hz, codec outputs %lu Hz)",
+ sampleRate, params.nSamplingRate);
+ }
+
mOutputFormat->setCString(
kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
@@ -3907,8 +4096,7 @@
(mQuirks & kDecoderLiesAboutNumberOfChannels)
? numChannels : params.nChannels);
- // The codec-reported sampleRate is not reliable...
- mOutputFormat->setInt32(kKeySampleRate, sampleRate);
+ mOutputFormat->setInt32(kKeySampleRate, params.nSamplingRate);
} else if (audio_def->eEncoding == OMX_AUDIO_CodingAMR) {
OMX_AUDIO_PARAM_AMRTYPE amr;
InitOMXParams(&amr);
@@ -4015,6 +4203,14 @@
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() {
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index cf622af..1560b8e 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -73,6 +73,7 @@
// Returns an approximate bitrate in bits per second.
uint64_t approxBitrate();
+ status_t seekToTime(int64_t timeUs);
status_t seekToOffset(off64_t offset);
status_t readNextPacket(MediaBuffer **buffer);
@@ -90,6 +91,11 @@
uint8_t mLace[255];
};
+ struct TOCEntry {
+ off64_t mPageOffset;
+ int64_t mTimeUs;
+ };
+
sp<DataSource> mSource;
off64_t mOffset;
Page mCurrentPage;
@@ -107,6 +113,8 @@
sp<MetaData> mMeta;
sp<MetaData> mFileMeta;
+ Vector<TOCEntry> mTableOfContents;
+
ssize_t readPage(off64_t offset, Page *page);
status_t findNextPage(off64_t startOffset, off64_t *pageOffset);
@@ -114,14 +122,18 @@
MediaBuffer *buffer, uint8_t type);
void parseFileMetaData();
- void extractAlbumArt(const void *data, size_t size);
- uint64_t findPrevGranulePosition(off64_t pageOffset);
+ status_t findPrevGranulePosition(off64_t pageOffset, uint64_t *granulePos);
+
+ void buildTableOfContents();
MyVorbisExtractor(const MyVorbisExtractor &);
MyVorbisExtractor &operator=(const MyVorbisExtractor &);
};
+static void extractAlbumArt(
+ const sp<MetaData> &fileMeta, const void *data, size_t size);
+
////////////////////////////////////////////////////////////////////////////////
OggSource::OggSource(const sp<OggExtractor> &extractor)
@@ -162,10 +174,7 @@
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
- off64_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
- LOGV("seeking to offset %ld", pos);
-
- if (mExtractor->mImpl->seekToOffset(pos) != OK) {
+ if (mExtractor->mImpl->seekToTime(seekTimeUs) != OK) {
return ERROR_END_OF_STREAM;
}
}
@@ -235,7 +244,7 @@
if (!memcmp(signature, "OggS", 4)) {
if (*pageOffset > startOffset) {
- LOGV("skipped %ld bytes of junk to reach next frame",
+ LOGV("skipped %lld bytes of junk to reach next frame",
*pageOffset - startOffset);
}
@@ -250,7 +259,10 @@
// it (if any) and return its granule position.
// To do this we back up from the "current" page's offset until we find any
// page preceding it and then scan forward to just before the current page.
-uint64_t MyVorbisExtractor::findPrevGranulePosition(off64_t pageOffset) {
+status_t MyVorbisExtractor::findPrevGranulePosition(
+ off64_t pageOffset, uint64_t *granulePos) {
+ *granulePos = 0;
+
off64_t prevPageOffset = 0;
off64_t prevGuess = pageOffset;
for (;;) {
@@ -260,9 +272,12 @@
prevGuess = 0;
}
- LOGV("backing up %ld bytes", pageOffset - prevGuess);
+ LOGV("backing up %lld bytes", pageOffset - prevGuess);
- CHECK_EQ(findNextPage(prevGuess, &prevPageOffset), (status_t)OK);
+ status_t err = findNextPage(prevGuess, &prevPageOffset);
+ if (err != OK) {
+ return err;
+ }
if (prevPageOffset < pageOffset || prevGuess == 0) {
break;
@@ -271,27 +286,64 @@
if (prevPageOffset == pageOffset) {
// We did not find a page preceding this one.
- return 0;
+ return UNKNOWN_ERROR;
}
- LOGV("prevPageOffset at %ld, pageOffset at %ld", prevPageOffset, pageOffset);
+ LOGV("prevPageOffset at %lld, pageOffset at %lld",
+ prevPageOffset, pageOffset);
for (;;) {
Page prevPage;
ssize_t n = readPage(prevPageOffset, &prevPage);
if (n <= 0) {
- return 0;
+ return (status_t)n;
}
prevPageOffset += n;
if (prevPageOffset == pageOffset) {
- return prevPage.mGranulePosition;
+ *granulePos = prevPage.mGranulePosition;
+ return OK;
}
}
}
+status_t MyVorbisExtractor::seekToTime(int64_t timeUs) {
+ if (mTableOfContents.isEmpty()) {
+ // Perform approximate seeking based on avg. bitrate.
+
+ off64_t pos = timeUs * approxBitrate() / 8000000ll;
+
+ LOGV("seeking to offset %lld", pos);
+ return seekToOffset(pos);
+ }
+
+ size_t left = 0;
+ size_t right = mTableOfContents.size();
+ while (left < right) {
+ size_t center = left / 2 + right / 2 + (left & right & 1);
+
+ const TOCEntry &entry = mTableOfContents.itemAt(center);
+
+ if (timeUs < entry.mTimeUs) {
+ right = center;
+ } else if (timeUs > entry.mTimeUs) {
+ left = center + 1;
+ } else {
+ left = right = center;
+ break;
+ }
+ }
+
+ const TOCEntry &entry = mTableOfContents.itemAt(left);
+
+ LOGV("seeking to entry %d / %d at offset %lld",
+ left, mTableOfContents.size(), entry.mPageOffset);
+
+ return seekToOffset(entry.mPageOffset);
+}
+
status_t MyVorbisExtractor::seekToOffset(off64_t offset) {
if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) {
// Once we know where the actual audio data starts (past the headers)
@@ -309,7 +361,7 @@
// We found the page we wanted to seek to, but we'll also need
// the page preceding it to determine how many valid samples are on
// this page.
- mPrevGranulePosition = findPrevGranulePosition(pageOffset);
+ findPrevGranulePosition(pageOffset, &mPrevGranulePosition);
mOffset = pageOffset;
@@ -326,11 +378,19 @@
ssize_t MyVorbisExtractor::readPage(off64_t offset, Page *page) {
uint8_t header[27];
- if (mSource->readAt(offset, header, sizeof(header))
+ ssize_t n;
+ if ((n = mSource->readAt(offset, header, sizeof(header)))
< (ssize_t)sizeof(header)) {
- LOGV("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
+ LOGV("failed to read %d bytes at offset 0x%016llx, got %ld bytes",
+ sizeof(header), offset, n);
- return ERROR_IO;
+ if (n < 0) {
+ return n;
+ } else if (n == 0) {
+ return ERROR_END_OF_STREAM;
+ } else {
+ return ERROR_IO;
+ }
}
if (memcmp(header, "OggS", 4)) {
@@ -445,7 +505,8 @@
packetSize);
if (n < (ssize_t)packetSize) {
- LOGV("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
+ LOGV("failed to read %d bytes at 0x%016llx, got %ld bytes",
+ packetSize, dataOffset, n);
return ERROR_IO;
}
@@ -561,9 +622,66 @@
mFirstDataOffset = mOffset + mCurrentPageSize;
+ off64_t size;
+ uint64_t lastGranulePosition;
+ if (!(mSource->flags() & DataSource::kIsCachingDataSource)
+ && mSource->getSize(&size) == OK
+ && findPrevGranulePosition(size, &lastGranulePosition) == OK) {
+ // Let's assume it's cheap to seek to the end.
+ // The granule position of the final page in the stream will
+ // give us the exact duration of the content, something that
+ // we can only approximate using avg. bitrate if seeking to
+ // the end is too expensive or impossible (live streaming).
+
+ int64_t durationUs = lastGranulePosition * 1000000ll / mVi.rate;
+
+ mMeta->setInt64(kKeyDuration, durationUs);
+
+ buildTableOfContents();
+ }
+
return OK;
}
+void MyVorbisExtractor::buildTableOfContents() {
+ off64_t offset = mFirstDataOffset;
+ Page page;
+ ssize_t pageSize;
+ while ((pageSize = readPage(offset, &page)) > 0) {
+ mTableOfContents.push();
+
+ TOCEntry &entry =
+ mTableOfContents.editItemAt(mTableOfContents.size() - 1);
+
+ entry.mPageOffset = offset;
+ entry.mTimeUs = page.mGranulePosition * 1000000ll / mVi.rate;
+
+ offset += (size_t)pageSize;
+ }
+
+ // Limit the maximum amount of RAM we spend on the table of contents,
+ // if necessary thin out the table evenly to trim it down to maximum
+ // size.
+
+ static const size_t kMaxTOCSize = 8192;
+ static const size_t kMaxNumTOCEntries = kMaxTOCSize / sizeof(TOCEntry);
+
+ size_t numerator = mTableOfContents.size();
+
+ if (numerator > kMaxNumTOCEntries) {
+ size_t denom = numerator - kMaxNumTOCEntries;
+
+ size_t accum = 0;
+ for (ssize_t i = mTableOfContents.size() - 1; i >= 0; --i) {
+ accum += denom;
+ if (accum >= numerator) {
+ mTableOfContents.removeAt(i);
+ accum -= numerator;
+ }
+ }
+ }
+}
+
status_t MyVorbisExtractor::verifyHeader(
MediaBuffer *buffer, uint8_t type) {
const uint8_t *data =
@@ -654,6 +772,17 @@
mFileMeta = new MetaData;
mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG);
+ for (int i = 0; i < mVc.comments; ++i) {
+ const char *comment = mVc.user_comments[i];
+ size_t commentLength = mVc.comment_lengths[i];
+ parseVorbisComment(mFileMeta, comment, commentLength);
+ //LOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]);
+ }
+}
+
+void parseVorbisComment(
+ const sp<MetaData> &fileMeta, const char *comment, size_t commentLength)
+{
struct {
const char *const mTag;
uint32_t mKey;
@@ -675,33 +804,25 @@
{ "ANDROID_LOOP", kKeyAutoLoop },
};
- for (int i = 0; i < mVc.comments; ++i) {
- const char *comment = mVc.user_comments[i];
-
for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
size_t tagLen = strlen(kMap[j].mTag);
if (!strncasecmp(kMap[j].mTag, comment, tagLen)
&& comment[tagLen] == '=') {
if (kMap[j].mKey == kKeyAlbumArt) {
extractAlbumArt(
+ fileMeta,
&comment[tagLen + 1],
- mVc.comment_lengths[i] - tagLen - 1);
+ commentLength - tagLen - 1);
} else if (kMap[j].mKey == kKeyAutoLoop) {
if (!strcasecmp(&comment[tagLen + 1], "true")) {
- mFileMeta->setInt32(kKeyAutoLoop, true);
+ fileMeta->setInt32(kKeyAutoLoop, true);
}
} else {
- mFileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
+ fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
}
}
}
- }
-#if 0
- for (int i = 0; i < mVc.comments; ++i) {
- LOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]);
- }
-#endif
}
// The returned buffer should be free()d.
@@ -769,7 +890,8 @@
return (uint8_t *)buffer;
}
-void MyVorbisExtractor::extractAlbumArt(const void *data, size_t size) {
+static void extractAlbumArt(
+ const sp<MetaData> &fileMeta, const void *data, size_t size) {
LOGV("extractAlbumArt from '%s'", (const char *)data);
size_t flacSize;
@@ -833,10 +955,10 @@
LOGV("got image data, %d trailing bytes",
flacSize - 32 - typeLen - descLen - dataLen);
- mFileMeta->setData(
+ fileMeta->setData(
kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
- mFileMeta->setCString(kKeyAlbumArtMIME, type);
+ fileMeta->setCString(kKeyAlbumArtMIME, type);
exit:
free(flac);
diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
index 062ab9b..c7b00b1 100644
--- a/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/SampleIterator.cpp
@@ -307,6 +307,8 @@
*time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex);
+ *time += mTable->getCompositionTimeOffset(sampleIndex);
+
return OK;
}
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index a9163fc..eb135ab 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -53,6 +53,9 @@
mNumSampleSizes(0),
mTimeToSampleCount(0),
mTimeToSample(NULL),
+ mSampleTimeEntries(NULL),
+ mCompositionTimeDeltaEntries(NULL),
+ mNumCompositionTimeDeltaEntries(0),
mSyncSampleOffset(-1),
mNumSyncSamples(0),
mSyncSamples(NULL),
@@ -68,6 +71,12 @@
delete[] mSyncSamples;
mSyncSamples = NULL;
+ delete[] mCompositionTimeDeltaEntries;
+ mCompositionTimeDeltaEntries = NULL;
+
+ delete[] mSampleTimeEntries;
+ mSampleTimeEntries = NULL;
+
delete[] mTimeToSample;
mTimeToSample = NULL;
@@ -211,7 +220,7 @@
return ERROR_MALFORMED;
}
- mSampleSizeFieldSize = mDefaultSampleSize & 0xf;
+ mSampleSizeFieldSize = mDefaultSampleSize & 0xff;
mDefaultSampleSize = 0;
if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8
@@ -260,6 +269,51 @@
return OK;
}
+status_t SampleTable::setCompositionTimeToSampleParams(
+ off64_t data_offset, size_t data_size) {
+ LOGI("There are reordered frames present.");
+
+ if (mCompositionTimeDeltaEntries != NULL || data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->readAt(
+ data_offset, header, sizeof(header))
+ < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ size_t numEntries = U32_AT(&header[4]);
+
+ if (data_size != (numEntries + 1) * 8) {
+ return ERROR_MALFORMED;
+ }
+
+ mNumCompositionTimeDeltaEntries = numEntries;
+ mCompositionTimeDeltaEntries = new uint32_t[2 * numEntries];
+
+ if (mDataSource->readAt(
+ data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8)
+ < (ssize_t)numEntries * 8) {
+ delete[] mCompositionTimeDeltaEntries;
+ mCompositionTimeDeltaEntries = NULL;
+
+ return ERROR_IO;
+ }
+
+ for (size_t i = 0; i < 2 * numEntries; ++i) {
+ mCompositionTimeDeltaEntries[i] = ntohl(mCompositionTimeDeltaEntries[i]);
+ }
+
+ return OK;
+}
+
status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size) {
if (mSyncSampleOffset >= 0 || data_size < 8) {
return ERROR_MALFORMED;
@@ -331,65 +385,132 @@
return time1 > time2 ? time1 - time2 : time2 - time1;
}
-status_t SampleTable::findSampleAtTime(
- uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
- *sample_index = 0;
+// static
+int SampleTable::CompareIncreasingTime(const void *_a, const void *_b) {
+ const SampleTimeEntry *a = (const SampleTimeEntry *)_a;
+ const SampleTimeEntry *b = (const SampleTimeEntry *)_b;
+ if (a->mCompositionTime < b->mCompositionTime) {
+ return -1;
+ } else if (a->mCompositionTime > b->mCompositionTime) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void SampleTable::buildSampleEntriesTable() {
Mutex::Autolock autoLock(mLock);
- uint32_t cur_sample = 0;
- uint32_t time = 0;
+ if (mSampleTimeEntries != NULL) {
+ return;
+ }
+
+ mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes];
+
+ uint32_t sampleIndex = 0;
+ uint32_t sampleTime = 0;
+
for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
uint32_t n = mTimeToSample[2 * i];
uint32_t delta = mTimeToSample[2 * i + 1];
- if (req_time < time + n * delta) {
- int j = (req_time - time) / delta;
+ for (uint32_t j = 0; j < n; ++j) {
+ CHECK(sampleIndex < mNumSampleSizes);
- uint32_t time1 = time + j * delta;
- uint32_t time2 = time1 + delta;
+ mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
- uint32_t sampleTime;
- if (i+1 == mTimeToSampleCount
- || (abs_difference(req_time, time1)
- < abs_difference(req_time, time2))) {
- *sample_index = cur_sample + j;
- sampleTime = time1;
- } else {
- *sample_index = cur_sample + j + 1;
- sampleTime = time2;
- }
+ mSampleTimeEntries[sampleIndex].mCompositionTime =
+ sampleTime + getCompositionTimeOffset(sampleIndex);
- switch (flags) {
- case kFlagBefore:
- {
- if (sampleTime > req_time && *sample_index > 0) {
- --*sample_index;
- }
- break;
- }
-
- case kFlagAfter:
- {
- if (sampleTime < req_time
- && *sample_index + 1 < mNumSampleSizes) {
- ++*sample_index;
- }
- break;
- }
-
- default:
- break;
- }
-
- return OK;
+ ++sampleIndex;
+ sampleTime += delta;
}
-
- time += delta * n;
- cur_sample += n;
}
- return ERROR_OUT_OF_RANGE;
+ qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry),
+ CompareIncreasingTime);
+}
+
+status_t SampleTable::findSampleAtTime(
+ uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
+ buildSampleEntriesTable();
+
+ uint32_t left = 0;
+ uint32_t right = mNumSampleSizes;
+ while (left < right) {
+ uint32_t center = (left + right) / 2;
+ uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime;
+
+ if (req_time < centerTime) {
+ right = center;
+ } else if (req_time > centerTime) {
+ left = center + 1;
+ } else {
+ left = center;
+ break;
+ }
+ }
+
+ if (left == mNumSampleSizes) {
+ if (flags == kFlagAfter) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ --left;
+ }
+
+ uint32_t closestIndex = left;
+
+ switch (flags) {
+ case kFlagBefore:
+ {
+ while (closestIndex > 0
+ && mSampleTimeEntries[closestIndex].mCompositionTime
+ > req_time) {
+ --closestIndex;
+ }
+ break;
+ }
+
+ case kFlagAfter:
+ {
+ while (closestIndex + 1 < mNumSampleSizes
+ && mSampleTimeEntries[closestIndex].mCompositionTime
+ < req_time) {
+ ++closestIndex;
+ }
+ break;
+ }
+
+ default:
+ {
+ CHECK(flags == kFlagClosest);
+
+ if (closestIndex > 0) {
+ // Check left neighbour and pick closest.
+ uint32_t absdiff1 =
+ abs_difference(
+ mSampleTimeEntries[closestIndex].mCompositionTime,
+ req_time);
+
+ uint32_t absdiff2 =
+ abs_difference(
+ mSampleTimeEntries[closestIndex - 1].mCompositionTime,
+ req_time);
+
+ if (absdiff1 > absdiff2) {
+ closestIndex = closestIndex - 1;
+ }
+ }
+
+ break;
+ }
+ }
+
+ *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex;
+
+ return OK;
}
status_t SampleTable::findSyncSampleNear(
@@ -561,7 +682,7 @@
uint32_t sampleIndex,
off64_t *offset,
size_t *size,
- uint32_t *decodingTime,
+ uint32_t *compositionTime,
bool *isSyncSample) {
Mutex::Autolock autoLock(mLock);
@@ -578,8 +699,8 @@
*size = mSampleIterator->getSampleSize();
}
- if (decodingTime) {
- *decodingTime = mSampleIterator->getSampleTime();
+ if (compositionTime) {
+ *compositionTime = mSampleIterator->getSampleTime();
}
if (isSyncSample) {
@@ -607,5 +728,26 @@
return OK;
}
+uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) const {
+ if (mCompositionTimeDeltaEntries == NULL) {
+ return 0;
+ }
+
+ uint32_t curSample = 0;
+ for (size_t i = 0; i < mNumCompositionTimeDeltaEntries; ++i) {
+ uint32_t sampleCount = mCompositionTimeDeltaEntries[2 * i];
+
+ if (sampleIndex < curSample + sampleCount) {
+ uint32_t sampleDelta = mCompositionTimeDeltaEntries[2 * i + 1];
+
+ return sampleDelta;
+ }
+
+ curSample += sampleCount;
+ }
+
+ return 0;
+}
+
} // namespace android
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index be3df7c..f82ff32 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -37,7 +37,8 @@
".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
- ".mkv", ".mka", ".webm", ".ts", ".fl"
+ ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
+ ".avi",
};
static const size_t kNumValidExtensions =
sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
@@ -124,7 +125,8 @@
|| !strcasecmp(extension, ".xmf")
|| !strcasecmp(extension, ".rtttl")
|| !strcasecmp(extension, ".rtx")
- || !strcasecmp(extension, ".ota")) {
+ || !strcasecmp(extension, ".ota")
+ || !strcasecmp(extension, ".mxmf")) {
status_t status = HandleMIDI(path, &client);
if (status != OK) {
return status;
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 600de7c..4c3dc47 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -27,6 +27,7 @@
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/MediaDefs.h>
namespace android {
@@ -48,7 +49,8 @@
mClient.disconnect();
}
-status_t StagefrightMetadataRetriever::setDataSource(const char *uri) {
+status_t StagefrightMetadataRetriever::setDataSource(
+ const char *uri, const KeyedVector<String8, String8> *headers) {
LOGV("setDataSource(%s)", uri);
mParsedMetaData = false;
@@ -56,7 +58,7 @@
delete mAlbumArt;
mAlbumArt = NULL;
- mSource = DataSource::CreateFromURI(uri);
+ mSource = DataSource::CreateFromURI(uri, headers);
if (mSource == NULL) {
return UNKNOWN_ERROR;
@@ -145,7 +147,8 @@
int64_t thumbNailTime;
if (frameTimeUs < 0) {
- if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) {
+ if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)
+ || thumbNailTime < 0) {
thumbNailTime = 0;
}
options.setSeekTo(thumbNailTime, mode);
@@ -231,6 +234,14 @@
frame->mData = new uint8_t[frame->mSize];
frame->mRotationAngle = rotationAngle;
+ int32_t displayWidth, displayHeight;
+ if (meta->findInt32(kKeyDisplayWidth, &displayWidth)) {
+ frame->mDisplayWidth = displayWidth;
+ }
+ if (meta->findInt32(kKeyDisplayHeight, &displayHeight)) {
+ frame->mDisplayHeight = displayHeight;
+ }
+
int32_t srcFormat;
CHECK(meta->findInt32(kKeyColorFormat, &srcFormat));
@@ -272,6 +283,12 @@
return NULL;
}
+ int32_t drm = 0;
+ if (mExtractor->getMetaData()->findInt32(kKeyIsDRM, &drm) && drm != 0) {
+ LOGE("frame grab not allowed.");
+ return NULL;
+ }
+
size_t n = mExtractor->countTracks();
size_t i;
for (i = 0; i < n; ++i) {
@@ -405,8 +422,15 @@
mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp));
+ bool hasAudio = false;
+ bool hasVideo = false;
+ int32_t videoWidth = -1;
+ int32_t videoHeight = -1;
+ int32_t audioBitrate = -1;
+
// The overall duration is the duration of the longest track.
int64_t maxDurationUs = 0;
+ String8 timedTextLang;
for (size_t i = 0; i < numTracks; ++i) {
sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
@@ -416,12 +440,67 @@
maxDurationUs = durationUs;
}
}
+
+ const char *mime;
+ if (trackMeta->findCString(kKeyMIMEType, &mime)) {
+ if (!hasAudio && !strncasecmp("audio/", mime, 6)) {
+ hasAudio = true;
+
+ if (!trackMeta->findInt32(kKeyBitRate, &audioBitrate)) {
+ audioBitrate = -1;
+ }
+ } else if (!hasVideo && !strncasecmp("video/", mime, 6)) {
+ hasVideo = true;
+
+ CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth));
+ CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight));
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+ const char *lang;
+ trackMeta->findCString(kKeyMediaLanguage, &lang);
+ timedTextLang.append(String8(lang));
+ timedTextLang.append(String8(":"));
+ }
+ }
+ }
+
+ // To save the language codes for all timed text tracks
+ // If multiple text tracks present, the format will look
+ // like "eng:chi"
+ if (!timedTextLang.isEmpty()) {
+ mMetaData.add(METADATA_KEY_TIMED_TEXT_LANGUAGES, timedTextLang);
}
// The duration value is a string representing the duration in ms.
sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000);
mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
+ if (hasAudio) {
+ mMetaData.add(METADATA_KEY_HAS_AUDIO, String8("yes"));
+ }
+
+ if (hasVideo) {
+ mMetaData.add(METADATA_KEY_HAS_VIDEO, String8("yes"));
+
+ sprintf(tmp, "%d", videoWidth);
+ mMetaData.add(METADATA_KEY_VIDEO_WIDTH, String8(tmp));
+
+ sprintf(tmp, "%d", videoHeight);
+ mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp));
+ }
+
+ if (numTracks == 1 && hasAudio && audioBitrate >= 0) {
+ sprintf(tmp, "%d", audioBitrate);
+ mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
+ } else {
+ off64_t sourceSize;
+ if (mSource->getSize(&sourceSize) == OK) {
+ int64_t avgBitRate = (int64_t)(sourceSize * 8E6 / maxDurationUs);
+
+ sprintf(tmp, "%lld", avgBitRate);
+ mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
+ }
+ }
+
if (numTracks == 1) {
const char *fileMIME;
CHECK(meta->findCString(kKeyMIMEType, &fileMIME));
diff --git a/media/libstagefright/TimedTextPlayer.cpp b/media/libstagefright/TimedTextPlayer.cpp
new file mode 100644
index 0000000..1ac22cb
--- /dev/null
+++ b/media/libstagefright/TimedTextPlayer.cpp
@@ -0,0 +1,252 @@
+ /*
+ * Copyright (C) 2011 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 <binder/IPCThreadState.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/Utils.h>
+#include "include/AwesomePlayer.h"
+#include "include/TimedTextPlayer.h"
+
+namespace android {
+
+struct TimedTextEvent : public TimedEventQueue::Event {
+ TimedTextEvent(
+ TimedTextPlayer *player,
+ void (TimedTextPlayer::*method)())
+ : mPlayer(player),
+ mMethod(method) {
+ }
+
+protected:
+ virtual ~TimedTextEvent() {}
+
+ virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+ (mPlayer->*mMethod)();
+ }
+
+private:
+ TimedTextPlayer *mPlayer;
+ void (TimedTextPlayer::*mMethod)();
+
+ TimedTextEvent(const TimedTextEvent &);
+ TimedTextEvent &operator=(const TimedTextEvent &);
+};
+
+TimedTextPlayer::TimedTextPlayer(
+ AwesomePlayer *observer,
+ const wp<MediaPlayerBase> &listener,
+ TimedEventQueue *queue)
+ : mSource(NULL),
+ mSeekTimeUs(0),
+ mStarted(false),
+ mTextEventPending(false),
+ mQueue(queue),
+ mListener(listener),
+ mObserver(observer),
+ mTextBuffer(NULL) {
+ mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent);
+}
+
+TimedTextPlayer::~TimedTextPlayer() {
+ if (mStarted) {
+ reset();
+ }
+
+ mTextTrackVector.clear();
+}
+
+status_t TimedTextPlayer::start(uint8_t index) {
+ CHECK(!mStarted);
+
+ if (index >= mTextTrackVector.size()) {
+ LOGE("Incorrect text track index");
+ return BAD_VALUE;
+ }
+
+ mSource = mTextTrackVector.itemAt(index);
+
+ status_t err = mSource->start();
+
+ if (err != OK) {
+ return err;
+ }
+
+ int64_t positionUs;
+ mObserver->getPosition(&positionUs);
+ seekTo(positionUs);
+
+ postTextEvent();
+
+ mStarted = true;
+
+ return OK;
+}
+
+void TimedTextPlayer::pause() {
+ CHECK(mStarted);
+
+ cancelTextEvent();
+}
+
+void TimedTextPlayer::resume() {
+ CHECK(mStarted);
+
+ postTextEvent();
+}
+
+void TimedTextPlayer::reset() {
+ CHECK(mStarted);
+
+ // send an empty text to clear the screen
+ notifyListener(MEDIA_TIMED_TEXT);
+
+ cancelTextEvent();
+
+ mSeeking = false;
+ mStarted = false;
+
+ if (mTextBuffer != NULL) {
+ mTextBuffer->release();
+ mTextBuffer = NULL;
+ }
+
+ if (mSource != NULL) {
+ mSource->stop();
+ mSource.clear();
+ mSource = NULL;
+ }
+}
+
+status_t TimedTextPlayer::seekTo(int64_t time_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ mSeeking = true;
+ mSeekTimeUs = time_us;
+
+ return OK;
+}
+
+status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) {
+ if (index >= (int)(mTextTrackVector.size())) {
+ return BAD_VALUE;
+ }
+
+ if (mStarted) {
+ reset();
+ }
+
+ if (index >= 0) {
+ return start(index);
+ }
+ return OK;
+}
+
+void TimedTextPlayer::onTextEvent() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (!mTextEventPending) {
+ return;
+ }
+ mTextEventPending = false;
+
+ MediaSource::ReadOptions options;
+ if (mSeeking) {
+ options.setSeekTo(mSeekTimeUs,
+ MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
+ mSeeking = false;
+
+ if (mTextBuffer != NULL) {
+ mTextBuffer->release();
+ mTextBuffer = NULL;
+ }
+
+ notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen
+ }
+
+ if (mTextBuffer != NULL) {
+ uint8_t *tmp = (uint8_t *)(mTextBuffer->data());
+ size_t len = (*tmp) << 8 | (*(tmp + 1));
+
+ notifyListener(MEDIA_TIMED_TEXT,
+ tmp + 2,
+ len);
+
+ mTextBuffer->release();
+ mTextBuffer = NULL;
+
+ }
+
+ if (mSource->read(&mTextBuffer, &options) != OK) {
+ return;
+ }
+
+ int64_t positionUs, timeUs;
+ mObserver->getPosition(&positionUs);
+ mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs);
+
+ //send the text now
+ if (timeUs <= positionUs + 100000ll) {
+ postTextEvent();
+ } else {
+ postTextEvent(timeUs - positionUs - 100000ll);
+ }
+}
+
+void TimedTextPlayer::postTextEvent(int64_t delayUs) {
+ if (mTextEventPending) {
+ return;
+ }
+
+ mTextEventPending = true;
+ mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs);
+}
+
+void TimedTextPlayer::cancelTextEvent() {
+ mQueue->cancelEvent(mTextEvent->eventID());
+ mTextEventPending = false;
+}
+
+void TimedTextPlayer::addTextSource(sp<MediaSource> source) {
+ mTextTrackVector.add(source);
+}
+
+void TimedTextPlayer::notifyListener(
+ int msg, const void *data, size_t size) {
+ if (mListener != NULL) {
+ sp<MediaPlayerBase> listener = mListener.promote();
+
+ if (listener != NULL) {
+ if (size > 0) {
+ mData.freeData();
+ mData.write(data, size);
+
+ listener->sendEvent(msg, 0, 0, &mData);
+ } else { // send an empty timed text to clear the screen
+ listener->sendEvent(msg);
+ }
+ }
+ }
+}
+}
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 9332120..bf978d7 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -264,6 +264,8 @@
mGroup(NULL) {
CHECK(mMeta->findInt32(kKeySampleRate, &mSampleRate));
CHECK(mMeta->findInt32(kKeyChannelCount, &mNumChannels));
+
+ mMeta->setInt32(kKeyMaxInputSize, kMaxFrameSize);
}
WAVSource::~WAVSource() {
@@ -353,8 +355,6 @@
return ERROR_END_OF_STREAM;
}
- mCurrentPos += n;
-
buffer->set_range(0, n);
if (mWaveFormat == WAVE_FORMAT_PCM) {
@@ -406,6 +406,7 @@
/ (mNumChannels * bytesPerSample) / mSampleRate);
buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ mCurrentPos += n;
*out = buffer;
@@ -426,6 +427,11 @@
return false;
}
+ sp<MediaExtractor> extractor = new WAVExtractor(source);
+ if (extractor->countTracks() == 0) {
+ return false;
+ }
+
*mimeType = MEDIA_MIMETYPE_CONTAINER_WAV;
*confidence = 0.3f;
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index 7c72852..83a1eaa4 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -45,7 +45,8 @@
static Mutex gWVMutex;
WVMExtractor::WVMExtractor(const sp<DataSource> &source)
- : mDataSource(source) {
+ : mDataSource(source),
+ mUseAdaptiveStreaming(false) {
{
Mutex::Autolock autoLock(gWVMutex);
if (gVendorLibHandle == NULL) {
@@ -100,5 +101,21 @@
return mImpl->getMetaData();
}
+int64_t WVMExtractor::getCachedDurationUs(status_t *finalStatus) {
+ // TODO: Fill this with life.
+
+ *finalStatus = OK;
+
+ return 0;
+}
+
+void WVMExtractor::setAdaptiveStreamingMode(bool adaptive) {
+ mUseAdaptiveStreaming = adaptive;
+}
+
+bool WVMExtractor::getAdaptiveStreamingMode() const {
+ return mUseAdaptiveStreaming;
+}
+
} //namespace android
diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp
index 616836c..0d0d6c2 100644
--- a/media/libstagefright/XINGSeeker.cpp
+++ b/media/libstagefright/XINGSeeker.cpp
@@ -41,8 +41,6 @@
return NULL;
}
- LOGI("Found XING header.");
-
return seeker;
}
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index fa12cf0..95cf2d3 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -329,5 +329,52 @@
return foundIDR;
}
+sp<MetaData> MakeAACCodecSpecificData(
+ unsigned profile, unsigned sampling_freq_index,
+ unsigned channel_configuration) {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ CHECK_LE(sampling_freq_index, 11u);
+ static const int32_t kSamplingFreq[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+ 16000, 12000, 11025, 8000
+ };
+ meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]);
+ meta->setInt32(kKeyChannelCount, channel_configuration);
+
+ static const uint8_t kStaticESDS[] = {
+ 0x03, 22,
+ 0x00, 0x00, // ES_ID
+ 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+ 0x04, 17,
+ 0x40, // Audio ISO/IEC 14496-3
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x05, 2,
+ // AudioSpecificInfo follows
+
+ // oooo offf fccc c000
+ // o - audioObjectType
+ // f - samplingFreqIndex
+ // c - channelConfig
+ };
+ sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
+ memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
+
+ csd->data()[sizeof(kStaticESDS)] =
+ ((profile + 1) << 3) | (sampling_freq_index >> 1);
+
+ csd->data()[sizeof(kStaticESDS) + 1] =
+ ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
+
+ meta->setData(kKeyESDS, 0, csd->data(), csd->size());
+
+ return meta;
+}
+
} // namespace android
diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk
new file mode 100644
index 0000000..80b2478
--- /dev/null
+++ b/media/libstagefright/chromium_http/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ ChromiumHTTPDataSource.cpp \
+ support.cpp \
+
+LOCAL_C_INCLUDES:= \
+ $(JNI_H_INCLUDE) \
+ frameworks/base/media/libstagefright \
+ $(TOP)/frameworks/base/include/media/stagefright/openmax \
+ external/chromium \
+ external/chromium/android
+
+LOCAL_CFLAGS += -Wno-multichar
+
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SHARED_LIBRARIES += libstlport
+include external/stlport/libstlport.mk
+endif
+
+LOCAL_MODULE:= libstagefright_chromium_http
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
new file mode 100644
index 0000000..1096717
--- /dev/null
+++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2011 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 "ChromiumHTTPDataSource"
+#include <media/stagefright/foundation/ADebug.h>
+
+#include "include/ChromiumHTTPDataSource.h"
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include "support.h"
+
+namespace android {
+
+ChromiumHTTPDataSource::ChromiumHTTPDataSource(uint32_t flags)
+ : mFlags(flags),
+ mState(DISCONNECTED),
+ mDelegate(new SfDelegate),
+ mCurrentOffset(0),
+ mIOResult(OK),
+ mContentSize(-1),
+ mNumBandwidthHistoryItems(0),
+ mTotalTransferTimeUs(0),
+ mTotalTransferBytes(0),
+ mDecryptHandle(NULL),
+ mDrmManagerClient(NULL) {
+ mDelegate->setOwner(this);
+}
+
+ChromiumHTTPDataSource::~ChromiumHTTPDataSource() {
+ disconnect();
+
+ delete mDelegate;
+ mDelegate = NULL;
+
+ if (mDrmManagerClient != NULL) {
+ delete mDrmManagerClient;
+ mDrmManagerClient = NULL;
+ }
+}
+
+status_t ChromiumHTTPDataSource::connect(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers,
+ off64_t offset) {
+ Mutex::Autolock autoLock(mLock);
+
+ return connect_l(uri, headers, offset);
+}
+
+status_t ChromiumHTTPDataSource::connect_l(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers,
+ off64_t offset) {
+ if (mState != DISCONNECTED) {
+ disconnect_l();
+ }
+
+ if (!(mFlags & kFlagIncognito)) {
+ LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "connect to %s @%lld", uri, offset);
+ } else {
+ LOG_PRI(ANDROID_LOG_INFO, LOG_TAG,
+ "connect to <URL suppressed> @%lld", offset);
+ }
+
+ mURI = uri;
+ mContentType = String8("application/octet-stream");
+
+ if (headers != NULL) {
+ mHeaders = *headers;
+ } else {
+ mHeaders.clear();
+ }
+
+ mState = CONNECTING;
+ mContentSize = -1;
+ mCurrentOffset = offset;
+
+ mDelegate->initiateConnection(mURI.c_str(), &mHeaders, offset);
+
+ while (mState == CONNECTING) {
+ mCondition.wait(mLock);
+ }
+
+ return mState == CONNECTED ? OK : mIOResult;
+}
+
+void ChromiumHTTPDataSource::onConnectionEstablished(
+ int64_t contentSize, const char *contentType) {
+ Mutex::Autolock autoLock(mLock);
+ mState = CONNECTED;
+ mContentSize = (contentSize < 0) ? -1 : contentSize + mCurrentOffset;
+ mContentType = String8(contentType);
+ mCondition.broadcast();
+}
+
+void ChromiumHTTPDataSource::onConnectionFailed(status_t err) {
+ Mutex::Autolock autoLock(mLock);
+ mState = DISCONNECTED;
+ mCondition.broadcast();
+
+ mURI.clear();
+
+ mIOResult = err;
+
+ clearDRMState_l();
+}
+
+void ChromiumHTTPDataSource::disconnect() {
+ Mutex::Autolock autoLock(mLock);
+ disconnect_l();
+}
+
+void ChromiumHTTPDataSource::disconnect_l() {
+ if (mState == DISCONNECTED) {
+ return;
+ }
+
+ mState = DISCONNECTING;
+ mIOResult = -EINTR;
+
+ mDelegate->initiateDisconnect();
+
+ while (mState == DISCONNECTING) {
+ mCondition.wait(mLock);
+ }
+
+ CHECK_EQ((int)mState, (int)DISCONNECTED);
+}
+
+status_t ChromiumHTTPDataSource::initCheck() const {
+ Mutex::Autolock autoLock(mLock);
+
+ return mState == CONNECTED ? OK : NO_INIT;
+}
+
+ssize_t ChromiumHTTPDataSource::readAt(off64_t offset, void *data, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mState != CONNECTED) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ if (offset != mCurrentOffset) {
+ AString tmp = mURI;
+ KeyedVector<String8, String8> tmpHeaders = mHeaders;
+
+ disconnect_l();
+
+ status_t err = connect_l(tmp.c_str(), &tmpHeaders, offset);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ mState = READING;
+
+ int64_t startTimeUs = ALooper::GetNowUs();
+
+ mDelegate->initiateRead(data, size);
+
+ while (mState == READING) {
+ mCondition.wait(mLock);
+ }
+
+ if (mIOResult < OK) {
+ return mIOResult;
+ }
+
+ if (mState == CONNECTED) {
+ int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
+
+ // The read operation was successful, mIOResult contains
+ // the number of bytes read.
+ addBandwidthMeasurement_l(mIOResult, delayUs);
+
+ mCurrentOffset += mIOResult;
+ return mIOResult;
+ }
+
+ return ERROR_IO;
+}
+
+void ChromiumHTTPDataSource::onReadCompleted(ssize_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ mIOResult = size;
+
+ if (mState == READING) {
+ mState = CONNECTED;
+ mCondition.broadcast();
+ }
+}
+
+status_t ChromiumHTTPDataSource::getSize(off64_t *size) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mContentSize < 0) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ *size = mContentSize;
+
+ return OK;
+}
+
+uint32_t ChromiumHTTPDataSource::flags() {
+ return kWantsPrefetching;
+}
+
+// static
+void ChromiumHTTPDataSource::InitiateRead(
+ ChromiumHTTPDataSource *me, void *data, size_t size) {
+ me->initiateRead(data, size);
+}
+
+void ChromiumHTTPDataSource::initiateRead(void *data, size_t size) {
+ mDelegate->initiateRead(data, size);
+}
+
+void ChromiumHTTPDataSource::onDisconnectComplete() {
+ Mutex::Autolock autoLock(mLock);
+ CHECK_EQ((int)mState, (int)DISCONNECTING);
+
+ mState = DISCONNECTED;
+ mURI.clear();
+
+ mCondition.broadcast();
+
+ clearDRMState_l();
+}
+
+void ChromiumHTTPDataSource::addBandwidthMeasurement_l(
+ size_t numBytes, int64_t delayUs) {
+ BandwidthEntry entry;
+ entry.mDelayUs = delayUs;
+ entry.mNumBytes = numBytes;
+ mTotalTransferTimeUs += delayUs;
+ mTotalTransferBytes += numBytes;
+
+ mBandwidthHistory.push_back(entry);
+ if (++mNumBandwidthHistoryItems > 100) {
+ BandwidthEntry *entry = &*mBandwidthHistory.begin();
+ mTotalTransferTimeUs -= entry->mDelayUs;
+ mTotalTransferBytes -= entry->mNumBytes;
+ mBandwidthHistory.erase(mBandwidthHistory.begin());
+ --mNumBandwidthHistoryItems;
+ }
+}
+
+bool ChromiumHTTPDataSource::estimateBandwidth(int32_t *bandwidth_bps) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mNumBandwidthHistoryItems < 2) {
+ return false;
+ }
+
+ *bandwidth_bps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
+
+ return true;
+}
+
+sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mDrmManagerClient == NULL) {
+ mDrmManagerClient = new DrmManagerClient();
+ }
+
+ if (mDrmManagerClient == NULL) {
+ return NULL;
+ }
+
+ if (mDecryptHandle == NULL) {
+ /* Note if redirect occurs, mUri is the redirect uri instead of the
+ * original one
+ */
+ mDecryptHandle = mDrmManagerClient->openDecryptSession(
+ String8(mURI.c_str()));
+ }
+
+ if (mDecryptHandle == NULL) {
+ delete mDrmManagerClient;
+ mDrmManagerClient = NULL;
+ }
+
+ return mDecryptHandle;
+}
+
+void ChromiumHTTPDataSource::getDrmInfo(
+ sp<DecryptHandle> &handle, DrmManagerClient **client) {
+ Mutex::Autolock autoLock(mLock);
+
+ handle = mDecryptHandle;
+ *client = mDrmManagerClient;
+}
+
+String8 ChromiumHTTPDataSource::getUri() {
+ Mutex::Autolock autoLock(mLock);
+
+ return String8(mURI.c_str());
+}
+
+String8 ChromiumHTTPDataSource::getMIMEType() const {
+ Mutex::Autolock autoLock(mLock);
+
+ return mContentType;
+}
+
+void ChromiumHTTPDataSource::clearDRMState_l() {
+ if (mDecryptHandle != NULL) {
+ // To release mDecryptHandle
+ CHECK(mDrmManagerClient);
+ mDrmManagerClient->closeDecryptSession(mDecryptHandle);
+ mDecryptHandle = NULL;
+ }
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp
new file mode 100644
index 0000000..3e4e4937
--- /dev/null
+++ b/media/libstagefright/chromium_http/support.cpp
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2011 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 "ChromiumHTTPDataSourceSupport"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/AString.h>
+
+#include "support.h"
+
+#include "android/net/android_network_library_impl.h"
+#include "base/thread.h"
+#include "net/base/cert_verifier.h"
+#include "net/base/host_resolver.h"
+#include "net/base/ssl_config_service.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/http_cache.h"
+#include "net/proxy/proxy_config_service_android.h"
+
+#include "include/ChromiumHTTPDataSource.h"
+
+#include <cutils/properties.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+static Mutex gNetworkThreadLock;
+static base::Thread *gNetworkThread = NULL;
+static scoped_refptr<URLRequestContext> gReqContext;
+
+static void InitializeNetworkThreadIfNecessary() {
+ Mutex::Autolock autoLock(gNetworkThreadLock);
+ if (gNetworkThread == NULL) {
+ gNetworkThread = new base::Thread("network");
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ CHECK(gNetworkThread->StartWithOptions(options));
+
+ gReqContext = new SfRequestContext;
+
+ net::AndroidNetworkLibrary::RegisterSharedInstance(
+ new SfNetworkLibrary);
+ }
+}
+
+static void MY_LOGI(const char *s) {
+ LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "%s", s);
+}
+
+static void MY_LOGV(const char *s) {
+#if !defined(LOG_NDEBUG) || LOG_NDEBUG == 0
+ LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG, "%s", s);
+#endif
+}
+
+SfNetLog::SfNetLog()
+ : mNextID(1) {
+}
+
+void SfNetLog::AddEntry(
+ EventType type,
+ const base::TimeTicks &time,
+ const Source &source,
+ EventPhase phase,
+ EventParameters *params) {
+#if 0
+ MY_LOGI(StringPrintf(
+ "AddEntry time=%s type=%s source=%s phase=%s\n",
+ TickCountToString(time).c_str(),
+ EventTypeToString(type),
+ SourceTypeToString(source.type),
+ EventPhaseToString(phase)).c_str());
+#endif
+}
+
+uint32 SfNetLog::NextID() {
+ return mNextID++;
+}
+
+net::NetLog::LogLevel SfNetLog::GetLogLevel() const {
+ return LOG_ALL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SfRequestContext::SfRequestContext() {
+ AString ua;
+ ua.append("stagefright/1.2 (Linux;Android ");
+
+#if (PROPERTY_VALUE_MAX < 8)
+#error "PROPERTY_VALUE_MAX must be at least 8"
+#endif
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.build.version.release", value, "Unknown");
+ ua.append(value);
+ ua.append(")");
+
+ mUserAgent = ua.c_str();
+
+ net_log_ = new SfNetLog;
+
+ host_resolver_ =
+ net::CreateSystemHostResolver(
+ net::HostResolver::kDefaultParallelism,
+ NULL /* resolver_proc */,
+ net_log_);
+
+ ssl_config_service_ =
+ net::SSLConfigService::CreateSystemSSLConfigService();
+
+ proxy_service_ = net::ProxyService::CreateWithoutProxyResolver(
+ new net::ProxyConfigServiceAndroid, net_log_);
+
+ http_transaction_factory_ = new net::HttpCache(
+ host_resolver_,
+ new net::CertVerifier(),
+ dnsrr_resolver_,
+ dns_cert_checker_.get(),
+ proxy_service_.get(),
+ ssl_config_service_.get(),
+ net::HttpAuthHandlerFactory::CreateDefault(host_resolver_),
+ network_delegate_,
+ net_log_,
+ NULL); // backend_factory
+}
+
+const std::string &SfRequestContext::GetUserAgent(const GURL &url) const {
+ return mUserAgent;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SfNetworkLibrary::SfNetworkLibrary() {}
+
+SfNetworkLibrary::VerifyResult SfNetworkLibrary::VerifyX509CertChain(
+ const std::vector<std::string>& cert_chain,
+ const std::string& hostname,
+ const std::string& auth_type) {
+ return VERIFY_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SfDelegate::SfDelegate()
+ : mOwner(NULL),
+ mURLRequest(NULL),
+ mReadBuffer(new net::IOBufferWithSize(8192)),
+ mNumBytesRead(0),
+ mNumBytesTotal(0),
+ mDataDestination(NULL),
+ mAtEOS(false) {
+ InitializeNetworkThreadIfNecessary();
+}
+
+SfDelegate::~SfDelegate() {
+ CHECK(mURLRequest == NULL);
+}
+
+void SfDelegate::setOwner(ChromiumHTTPDataSource *owner) {
+ mOwner = owner;
+}
+
+void SfDelegate::OnReceivedRedirect(
+ net::URLRequest *request, const GURL &new_url, bool *defer_redirect) {
+ MY_LOGI("OnReceivedRedirect");
+}
+
+void SfDelegate::OnAuthRequired(
+ net::URLRequest *request, net::AuthChallengeInfo *auth_info) {
+ MY_LOGI("OnAuthRequired");
+
+ inherited::OnAuthRequired(request, auth_info);
+}
+
+void SfDelegate::OnCertificateRequested(
+ net::URLRequest *request, net::SSLCertRequestInfo *cert_request_info) {
+ MY_LOGI("OnCertificateRequested");
+
+ inherited::OnCertificateRequested(request, cert_request_info);
+}
+
+void SfDelegate::OnSSLCertificateError(
+ net::URLRequest *request, int cert_error, net::X509Certificate *cert) {
+ fprintf(stderr, "OnSSLCertificateError cert_error=%d\n", cert_error);
+
+ inherited::OnSSLCertificateError(request, cert_error, cert);
+}
+
+void SfDelegate::OnGetCookies(net::URLRequest *request, bool blocked_by_policy) {
+ MY_LOGI("OnGetCookies");
+}
+
+void SfDelegate::OnSetCookie(
+ net::URLRequest *request,
+ const std::string &cookie_line,
+ const net::CookieOptions &options,
+ bool blocked_by_policy) {
+ MY_LOGI("OnSetCookie");
+}
+
+void SfDelegate::OnResponseStarted(net::URLRequest *request) {
+ if (request->status().status() != URLRequestStatus::SUCCESS) {
+ MY_LOGI(StringPrintf(
+ "Request failed with status %d and os_error %d",
+ request->status().status(),
+ request->status().os_error()).c_str());
+
+ delete mURLRequest;
+ mURLRequest = NULL;
+
+ mOwner->onConnectionFailed(ERROR_IO);
+ return;
+ } else if (mRangeRequested && request->GetResponseCode() != 206) {
+ MY_LOGI(StringPrintf(
+ "We requested a content range, but server didn't "
+ "support that. (responded with %d)",
+ request->GetResponseCode()).c_str());
+
+ delete mURLRequest;
+ mURLRequest = NULL;
+
+ mOwner->onConnectionFailed(-EPIPE);
+ return;
+ } else if ((request->GetResponseCode() / 100) != 2) {
+ MY_LOGI(StringPrintf(
+ "Server responded with http status %d",
+ request->GetResponseCode()).c_str());
+
+ delete mURLRequest;
+ mURLRequest = NULL;
+
+ mOwner->onConnectionFailed(ERROR_IO);
+ return;
+ }
+
+ MY_LOGV("OnResponseStarted");
+
+ std::string headers;
+ request->GetAllResponseHeaders(&headers);
+
+ MY_LOGV(StringPrintf("response headers: %s", headers.c_str()).c_str());
+
+ std::string contentType;
+ request->GetResponseHeaderByName("Content-Type", &contentType);
+
+ mOwner->onConnectionEstablished(
+ request->GetExpectedContentSize(), contentType.c_str());
+}
+
+void SfDelegate::OnReadCompleted(net::URLRequest *request, int bytes_read) {
+ if (bytes_read == -1) {
+ MY_LOGI(StringPrintf(
+ "OnReadCompleted, read failed, status %d",
+ request->status().status()).c_str());
+
+ mOwner->onReadCompleted(ERROR_IO);
+ return;
+ }
+
+ MY_LOGV(StringPrintf("OnReadCompleted, read %d bytes", bytes_read).c_str());
+
+ if (bytes_read < 0) {
+ MY_LOGI(StringPrintf(
+ "Read failed w/ status %d\n",
+ request->status().status()).c_str());
+
+ mOwner->onReadCompleted(ERROR_IO);
+ return;
+ } else if (bytes_read == 0) {
+ mAtEOS = true;
+ mOwner->onReadCompleted(mNumBytesRead);
+ return;
+ }
+
+ CHECK_GT(bytes_read, 0);
+ CHECK_LE(mNumBytesRead + bytes_read, mNumBytesTotal);
+
+ memcpy((uint8_t *)mDataDestination + mNumBytesRead,
+ mReadBuffer->data(),
+ bytes_read);
+
+ mNumBytesRead += bytes_read;
+
+ readMore(request);
+}
+
+void SfDelegate::readMore(net::URLRequest *request) {
+ while (mNumBytesRead < mNumBytesTotal) {
+ size_t copy = mNumBytesTotal - mNumBytesRead;
+ if (copy > mReadBuffer->size()) {
+ copy = mReadBuffer->size();
+ }
+
+ int n;
+ if (request->Read(mReadBuffer, copy, &n)) {
+ MY_LOGV(StringPrintf("Read %d bytes directly.", n).c_str());
+
+ CHECK_LE((size_t)n, copy);
+
+ memcpy((uint8_t *)mDataDestination + mNumBytesRead,
+ mReadBuffer->data(),
+ n);
+
+ mNumBytesRead += n;
+
+ if (n == 0) {
+ mAtEOS = true;
+ break;
+ }
+ } else {
+ MY_LOGV("readMore pending read");
+
+ if (request->status().status() != URLRequestStatus::IO_PENDING) {
+ MY_LOGI(StringPrintf(
+ "Direct read failed w/ status %d\n",
+ request->status().status()).c_str());
+
+ mOwner->onReadCompleted(ERROR_IO);
+ return;
+ }
+
+ return;
+ }
+ }
+
+ mOwner->onReadCompleted(mNumBytesRead);
+}
+
+void SfDelegate::initiateConnection(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers,
+ off64_t offset) {
+ GURL url(uri);
+
+ MessageLoop *loop = gNetworkThread->message_loop();
+ loop->PostTask(
+ FROM_HERE,
+ NewRunnableFunction(
+ &SfDelegate::OnInitiateConnectionWrapper,
+ this,
+ url,
+ headers,
+ offset));
+
+}
+
+// static
+void SfDelegate::OnInitiateConnectionWrapper(
+ SfDelegate *me, GURL url,
+ const KeyedVector<String8, String8> *headers,
+ off64_t offset) {
+ me->onInitiateConnection(url, headers, offset);
+}
+
+void SfDelegate::onInitiateConnection(
+ const GURL &url,
+ const KeyedVector<String8, String8> *extra,
+ off64_t offset) {
+ CHECK(mURLRequest == NULL);
+
+ mURLRequest = new net::URLRequest(url, this);
+ mAtEOS = false;
+
+ mRangeRequested = false;
+
+ if (offset != 0 || extra != NULL) {
+ net::HttpRequestHeaders headers =
+ mURLRequest->extra_request_headers();
+
+ if (offset != 0) {
+ headers.AddHeaderFromString(
+ StringPrintf("Range: bytes=%lld-", offset).c_str());
+
+ mRangeRequested = true;
+ }
+
+ if (extra != NULL) {
+ for (size_t i = 0; i < extra->size(); ++i) {
+ AString s;
+ s.append(extra->keyAt(i).string());
+ s.append(": ");
+ s.append(extra->valueAt(i).string());
+
+ headers.AddHeaderFromString(s.c_str());
+ }
+ }
+
+ mURLRequest->SetExtraRequestHeaders(headers);
+ }
+
+ mURLRequest->set_context(gReqContext);
+
+ mURLRequest->Start();
+}
+
+void SfDelegate::initiateDisconnect() {
+ MessageLoop *loop = gNetworkThread->message_loop();
+ loop->PostTask(
+ FROM_HERE,
+ NewRunnableFunction(
+ &SfDelegate::OnInitiateDisconnectWrapper, this));
+}
+
+// static
+void SfDelegate::OnInitiateDisconnectWrapper(SfDelegate *me) {
+ me->onInitiateDisconnect();
+}
+
+void SfDelegate::onInitiateDisconnect() {
+ mURLRequest->Cancel();
+
+ delete mURLRequest;
+ mURLRequest = NULL;
+
+ mOwner->onDisconnectComplete();
+}
+
+void SfDelegate::initiateRead(void *data, size_t size) {
+ MessageLoop *loop = gNetworkThread->message_loop();
+ loop->PostTask(
+ FROM_HERE,
+ NewRunnableFunction(
+ &SfDelegate::OnInitiateReadWrapper, this, data, size));
+}
+
+// static
+void SfDelegate::OnInitiateReadWrapper(
+ SfDelegate *me, void *data, size_t size) {
+ me->onInitiateRead(data, size);
+}
+
+void SfDelegate::onInitiateRead(void *data, size_t size) {
+ CHECK(mURLRequest != NULL);
+
+ mNumBytesRead = 0;
+ mNumBytesTotal = size;
+ mDataDestination = data;
+
+ if (mAtEOS) {
+ mOwner->onReadCompleted(0);
+ return;
+ }
+
+ readMore(mURLRequest);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/chromium_http/support.h b/media/libstagefright/chromium_http/support.h
new file mode 100644
index 0000000..4d03493
--- /dev/null
+++ b/media/libstagefright/chromium_http/support.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2011 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 SUPPORT_H_
+
+#define SUPPORT_H_
+
+#include <assert.h>
+
+#include "net/base/net_log.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/base/android_network_library.h"
+#include "net/base/io_buffer.h"
+
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+namespace android {
+
+struct SfNetLog : public net::NetLog {
+ SfNetLog();
+
+ virtual void AddEntry(
+ EventType type,
+ const base::TimeTicks &time,
+ const Source &source,
+ EventPhase phase,
+ EventParameters *params);
+
+ virtual uint32 NextID();
+ virtual LogLevel GetLogLevel() const;
+
+private:
+ uint32 mNextID;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SfNetLog);
+};
+
+struct SfRequestContext : public URLRequestContext {
+ SfRequestContext();
+
+ virtual const std::string &GetUserAgent(const GURL &url) const;
+
+private:
+ std::string mUserAgent;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SfRequestContext);
+};
+
+// This is required for https support, we don't really verify certificates,
+// we accept anything...
+struct SfNetworkLibrary : public net::AndroidNetworkLibrary {
+ SfNetworkLibrary();
+
+ virtual VerifyResult VerifyX509CertChain(
+ const std::vector<std::string>& cert_chain,
+ const std::string& hostname,
+ const std::string& auth_type);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(SfNetworkLibrary);
+};
+
+struct ChromiumHTTPDataSource;
+
+struct SfDelegate : public net::URLRequest::Delegate {
+ SfDelegate();
+ virtual ~SfDelegate();
+
+ void initiateConnection(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers,
+ off64_t offset);
+
+ void initiateDisconnect();
+ void initiateRead(void *data, size_t size);
+
+ void setOwner(ChromiumHTTPDataSource *mOwner);
+
+ virtual void OnReceivedRedirect(
+ net::URLRequest *request, const GURL &new_url, bool *defer_redirect);
+
+ virtual void OnAuthRequired(
+ net::URLRequest *request, net::AuthChallengeInfo *auth_info);
+
+ virtual void OnCertificateRequested(
+ net::URLRequest *request, net::SSLCertRequestInfo *cert_request_info);
+
+ virtual void OnSSLCertificateError(
+ net::URLRequest *request, int cert_error, net::X509Certificate *cert);
+
+ virtual void OnGetCookies(net::URLRequest *request, bool blocked_by_policy);
+
+ virtual void OnSetCookie(
+ net::URLRequest *request,
+ const std::string &cookie_line,
+ const net::CookieOptions &options,
+ bool blocked_by_policy);
+
+ virtual void OnResponseStarted(net::URLRequest *request);
+
+ virtual void OnReadCompleted(net::URLRequest *request, int bytes_read);
+
+private:
+ typedef Delegate inherited;
+
+ ChromiumHTTPDataSource *mOwner;
+
+ net::URLRequest *mURLRequest;
+ scoped_refptr<net::IOBufferWithSize> mReadBuffer;
+
+ size_t mNumBytesRead;
+ size_t mNumBytesTotal;
+ void *mDataDestination;
+
+ bool mRangeRequested;
+ bool mAtEOS;
+
+ void readMore(net::URLRequest *request);
+
+ static void OnInitiateConnectionWrapper(
+ SfDelegate *me,
+ GURL url,
+ const KeyedVector<String8, String8> *headers,
+ off64_t offset);
+
+ static void OnInitiateDisconnectWrapper(SfDelegate *me);
+
+ static void OnInitiateReadWrapper(
+ SfDelegate *me, void *data, size_t size);
+
+ void onInitiateConnection(
+ const GURL &url,
+ const KeyedVector<String8, String8> *headers,
+ off64_t offset);
+
+ void onInitiateDisconnect();
+ void onInitiateRead(void *data, size_t size);
+
+ DISALLOW_EVIL_CONSTRUCTORS(SfDelegate);
+};
+
+} // namespace android
+
+#endif // SUPPORT_H_
diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
index 208431c..d2e3eaa 100644
--- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp
+++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
@@ -234,6 +234,23 @@
mConfig->aacPlusUpsamplingFactor, mConfig->desiredChannels);
CHECK(mNumDecodedBuffers > 0);
+
+ if (decoderErr != MP4AUDEC_SUCCESS) {
+ // If decoding fails this early, the fields in mConfig may
+ // not be valid and we cannot recover.
+
+ LOGE("Unable to decode aac content, decoder returned error %d",
+ decoderErr);
+
+ buffer->release();
+ buffer = NULL;
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ return ERROR_UNSUPPORTED;
+ }
+
if (mNumDecodedBuffers == 1) {
mUpsamplingFactor = mConfig->aacPlusUpsamplingFactor;
// Check on the sampling rate to see whether it is changed.
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index 69e331f..359a2ec 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -143,14 +143,39 @@
unpack_idx.cpp \
window_tables_fxp.cpp \
pvmp4setaudioconfig.cpp \
- AACDecoder.cpp
+ AACDecoder.cpp \
LOCAL_CFLAGS := -DAAC_PLUS -DHQ_SBR -DPARAMETRICSTEREO -DOSCL_IMPORT_REF= -DOSCL_EXPORT_REF= -DOSCL_UNUSED_ARG=
-LOCAL_C_INCLUDES := frameworks/base/media/libstagefright/include
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
LOCAL_ARM_MODE := arm
LOCAL_MODULE := libstagefright_aacdec
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftAAC.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_aacdec
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_aacdec
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
new file mode 100644
index 0000000..7ce6128
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2011 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 "SoftAAC"
+#include <utils/Log.h>
+
+#include "SoftAAC.h"
+
+#include "pvmp4audiodecoder_api.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+SoftAAC::SoftAAC(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mConfig(new tPVMP4AudioDecoderExternal),
+ mDecoderBuf(NULL),
+ mInputBufferCount(0),
+ mUpsamplingFactor(2),
+ mAnchorTimeUs(0),
+ mNumSamplesOutput(0),
+ mSignalledError(false),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAAC::~SoftAAC() {
+ free(mDecoderBuf);
+ mDecoderBuf = NULL;
+
+ delete mConfig;
+ mConfig = NULL;
+}
+
+void SoftAAC::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.audio.cMIMEType = const_cast<char *>("audio/aac");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+ addPort(def);
+}
+
+status_t SoftAAC::initDecoder() {
+ memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal));
+ mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED;
+ mConfig->aacPlusEnabled = 1;
+
+ // The software decoder doesn't properly support mono output on
+ // AACplus files. Always output stereo.
+ mConfig->desiredChannels = 2;
+
+ UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements();
+ mDecoderBuf = malloc(memRequirements);
+
+ Int err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf);
+ if (err != MP4AUDEC_SUCCESS) {
+ LOGE("Failed to initialize MP4 audio decoder");
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftAAC::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamAudioAac:
+ {
+ OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
+ (OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
+
+ if (aacParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ aacParams->nBitRate = 0;
+ aacParams->nAudioBandWidth = 0;
+ aacParams->nAACtools = 0;
+ aacParams->nAACERtools = 0;
+ aacParams->eAACProfile = OMX_AUDIO_AACObjectMain;
+ aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
+ aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo;
+
+ if (!isConfigured()) {
+ aacParams->nChannels = 1;
+ aacParams->nSampleRate = 44100;
+ aacParams->nFrameLength = 0;
+ } else {
+ aacParams->nChannels = mConfig->encodedChannels;
+ aacParams->nSampleRate = mConfig->samplingRate;
+ aacParams->nFrameLength = mConfig->frameLength;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ pcmParams->eNumData = OMX_NumericalDataSigned;
+ pcmParams->eEndian = OMX_EndianBig;
+ pcmParams->bInterleaved = OMX_TRUE;
+ pcmParams->nBitPerSample = 16;
+ pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+ pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+ if (!isConfigured()) {
+ pcmParams->nChannels = 1;
+ pcmParams->nSamplingRate = 44100;
+ } else {
+ pcmParams->nChannels = mConfig->desiredChannels;
+ pcmParams->nSamplingRate = mConfig->samplingRate;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAAC::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.aac",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioAac:
+ {
+ const OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
+ (const OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
+
+ if (aacParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+bool SoftAAC::isConfigured() const {
+ return mInputBufferCount > 0;
+}
+
+void SoftAAC::onQueueFilled(OMX_U32 portIndex) {
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ if (portIndex == 0 && mInputBufferCount == 0) {
+ ++mInputBufferCount;
+
+ BufferInfo *info = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *header = info->mHeader;
+
+ mConfig->pInputBuffer = header->pBuffer + header->nOffset;
+ mConfig->inputBufferCurrentLength = header->nFilledLen;
+ mConfig->inputBufferMaxLength = 0;
+
+ Int err = PVMP4AudioDecoderConfig(mConfig, mDecoderBuf);
+ if (err != MP4AUDEC_SUCCESS) {
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, err, NULL);
+ return;
+ }
+
+ inQueue.erase(inQueue.begin());
+ info->mOwnedByUs = false;
+ notifyEmptyBufferDone(header);
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return;
+ }
+
+ while (!inQueue.empty() && !outQueue.empty()) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+ return;
+ }
+
+ if (inHeader->nOffset == 0) {
+ mAnchorTimeUs = inHeader->nTimeStamp;
+ mNumSamplesOutput = 0;
+ }
+
+ mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset;
+ mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
+ mConfig->inputBufferMaxLength = 0;
+ mConfig->inputBufferUsedLength = 0;
+ mConfig->remainderBits = 0;
+
+ mConfig->pOutputBuffer =
+ reinterpret_cast<Int16 *>(outHeader->pBuffer + outHeader->nOffset);
+
+ mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048];
+ mConfig->repositionFlag = false;
+
+ Int32 prevSamplingRate = mConfig->samplingRate;
+ Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf);
+
+ /*
+ * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+ * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+ * rate system and the sampling rate in the final output is actually
+ * doubled compared with the core AAC decoder sampling rate.
+ *
+ * Explicit signalling is done by explicitly defining SBR audio object
+ * type in the bitstream. Implicit signalling is done by embedding
+ * SBR content in AAC extension payload specific to SBR, and hence
+ * requires an AAC decoder to perform pre-checks on actual audio frames.
+ *
+ * Thus, we could not say for sure whether a stream is
+ * AAC+/eAAC+ until the first data frame is decoded.
+ */
+ if (mInputBufferCount <= 2) {
+ LOGV("audio/extended audio object type: %d + %d",
+ mConfig->audioObjectType, mConfig->extendedAudioObjectType);
+ LOGV("aac+ upsampling factor: %d desired channels: %d",
+ mConfig->aacPlusUpsamplingFactor, mConfig->desiredChannels);
+
+ if (mInputBufferCount == 1) {
+ mUpsamplingFactor = mConfig->aacPlusUpsamplingFactor;
+ // Check on the sampling rate to see whether it is changed.
+ if (mConfig->samplingRate != prevSamplingRate) {
+ LOGW("Sample rate was %d Hz, but now is %d Hz",
+ prevSamplingRate, mConfig->samplingRate);
+
+ // We'll hold onto the input buffer and will decode
+ // it again once the output port has been reconfigured.
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return;
+ }
+ } else { // mInputBufferCount == 2
+ if (mConfig->extendedAudioObjectType == MP4AUDIO_AAC_LC ||
+ mConfig->extendedAudioObjectType == MP4AUDIO_LTP) {
+ if (mUpsamplingFactor == 2) {
+ // The stream turns out to be not aacPlus mode anyway
+ LOGW("Disable AAC+/eAAC+ since extended audio object "
+ "type is %d",
+ mConfig->extendedAudioObjectType);
+ mConfig->aacPlusEnabled = 0;
+ }
+ } else {
+ if (mUpsamplingFactor == 1) {
+ // aacPlus mode does not buy us anything, but to cause
+ // 1. CPU load to increase, and
+ // 2. a half speed of decoding
+ LOGW("Disable AAC+/eAAC+ since upsampling factor is 1");
+ mConfig->aacPlusEnabled = 0;
+ }
+ }
+ }
+ }
+
+ size_t numOutBytes =
+ mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels;
+
+ if (decoderErr == MP4AUDEC_SUCCESS) {
+ CHECK_LE(mConfig->inputBufferUsedLength, inHeader->nFilledLen);
+
+ inHeader->nFilledLen -= mConfig->inputBufferUsedLength;
+ inHeader->nOffset += mConfig->inputBufferUsedLength;
+ } else {
+ memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes);
+ }
+
+ if (mUpsamplingFactor == 2) {
+ if (mConfig->desiredChannels == 1) {
+ memcpy(&mConfig->pOutputBuffer[1024],
+ &mConfig->pOutputBuffer[2048],
+ numOutBytes * 2);
+ }
+ numOutBytes *= 2;
+ }
+
+ outHeader->nFilledLen = numOutBytes;
+ outHeader->nFlags = 0;
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumSamplesOutput * 1000000ll) / mConfig->samplingRate;
+
+ mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor;
+
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+
+ ++mInputBufferCount;
+ }
+}
+
+void SoftAAC::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0) {
+ // Make sure that the next buffer output does not still
+ // depend on fragments from the last one decoded.
+ PVMP4AudioDecoderResetBuffer(mDecoderBuf);
+ }
+}
+
+void SoftAAC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+ if (portIndex != 1) {
+ return;
+ }
+
+ switch (mOutputPortSettingsChange) {
+ case NONE:
+ break;
+
+ case AWAITING_DISABLED:
+ {
+ CHECK(!enabled);
+ mOutputPortSettingsChange = AWAITING_ENABLED;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+ CHECK(enabled);
+ mOutputPortSettingsChange = NONE;
+ break;
+ }
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftAAC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.h b/media/libstagefright/codecs/aacdec/SoftAAC.h
new file mode 100644
index 0000000..963fd27
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 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 SOFT_AAC_H_
+
+#define SOFT_AAC_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tPVMP4AudioDecoderExternal;
+
+namespace android {
+
+struct SoftAAC : public SimpleSoftOMXComponent {
+ SoftAAC(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftAAC();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+ enum {
+ kNumBuffers = 4
+ };
+
+ tPVMP4AudioDecoderExternal *mConfig;
+ void *mDecoderBuf;
+
+ size_t mInputBufferCount;
+ size_t mUpsamplingFactor;
+ int64_t mAnchorTimeUs;
+ int64_t mNumSamplesOutput;
+
+ bool mSignalledError;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+ bool isConfigured() const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAAC);
+};
+
+} // namespace android
+
+#endif // SOFT_AAC_H_
diff --git a/media/libstagefright/codecs/aacdec/sbr_dec.cpp b/media/libstagefright/codecs/aacdec/sbr_dec.cpp
index 8fcc3ce..8519b17 100644
--- a/media/libstagefright/codecs/aacdec/sbr_dec.cpp
+++ b/media/libstagefright/codecs/aacdec/sbr_dec.cpp
@@ -1,5 +1,5 @@
/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
+ * Copyright (C) 1998-2010 PacketVideo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -447,7 +447,12 @@
if (xoverBand > sbrDec->highSubband)
{
- xoverBand = 32; /* error condition, default to upsampling mode */
+ /*
+ * error condition, default to upsampling mode
+ * and make sure that the number of bands for xover does
+ * not exceed the number of high freq bands.
+ */
+ xoverBand = (sbrDec->highSubband > 32)? 32: sbrDec->highSubband;
}
m = sbrDec->bufReadOffs + i; /* 2 + i */
@@ -558,18 +563,22 @@
/*
* Set Circular buffer for PS hybrid analysis
*/
+
+ int32_t *pt_temp = &scratch_mem[2][32];
+
for (i = 0, j = 0; i < 3; i++)
{
- pv_memmove(&scratch_mem[2][32 + j ],
+ pv_memmove(&pt_temp[ j],
hParametricStereoDec->hHybrid->mQmfBufferReal[i],
HYBRID_FILTER_LENGTH_m_1*sizeof(*hParametricStereoDec->hHybrid->mQmfBufferReal));
- pv_memmove(&scratch_mem[2][32 + j + 44],
+ pv_memmove(&pt_temp[ j + 44],
hParametricStereoDec->hHybrid->mQmfBufferImag[i],
HYBRID_FILTER_LENGTH_m_1*sizeof(*hParametricStereoDec->hHybrid->mQmfBufferImag));
j += 88;
}
+
pv_memset((void *)&qmf_PS_generated_Real[hParametricStereoDec->usb],
0,
(64 - hParametricStereoDec->usb)*sizeof(*qmf_PS_generated_Real));
@@ -626,19 +635,23 @@
* Save Circular buffer history used on PS hybrid analysis
*/
+
+ pt_temp = &scratch_mem[2][64];
+
for (i = 0, j = 0; i < 3; i++)
{
pv_memmove(hParametricStereoDec->hHybrid->mQmfBufferReal[i],
- &scratch_mem[2][ 64 + j ],
+ &pt_temp[ j],
HYBRID_FILTER_LENGTH_m_1*sizeof(*hParametricStereoDec->hHybrid->mQmfBufferReal));
pv_memmove(hParametricStereoDec->hHybrid->mQmfBufferImag[i],
- &scratch_mem[2][ 64 + j + 44],
+ &pt_temp[ j + 44],
HYBRID_FILTER_LENGTH_m_1*sizeof(*hParametricStereoDec->hHybrid->mQmfBufferImag));
j += 88;
}
+
pv_memmove(hFrameData->V, &circular_buffer_s[0], 1152*sizeof(*circular_buffer_s));
/*
@@ -746,7 +759,12 @@
if (xoverBand > sbrDec->highSubband)
{
- xoverBand = 32; /* error condition, default to upsampling mode */
+ /*
+ * error condition, default to upsampling mode
+ * and make sure that the number of bands for xover does
+ * not exceed the number of high freq bands.
+ */
+ xoverBand = (sbrDec->highSubband > 32)? 32: sbrDec->highSubband;
}
}
else
diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
index a8b1292..0bff52d 100644
--- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp
+++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
@@ -84,7 +84,7 @@
params.sampleRate = mSampleRate;
params.bitRate = mBitRate;
params.nChannels = mChannels;
- params.adtsUsed = 0; // For MP4 file, don't use adts format$
+ params.adtsUsed = 0; // We add adts header in the file writer if needed.
if (VO_ERR_NONE != mApiHandle->SetParam(mEncoderHandle, VO_PID_AAC_ENCPARAM, ¶ms)) {
LOGE("Failed to set AAC encoder parameters");
return UNKNOWN_ERROR;
@@ -189,6 +189,9 @@
delete mApiHandle;
mApiHandle = NULL;
+ delete mMemOperator;
+ mMemOperator = NULL;
+
mStarted = false;
return OK;
diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk
index cda4f9d..f9cc6a3 100644
--- a/media/libstagefright/codecs/aacenc/Android.mk
+++ b/media/libstagefright/codecs/aacenc/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
include frameworks/base/media/libstagefright/codecs/common/Config.mk
-LOCAL_PRELINK_MODULE := false
+
LOCAL_SRC_FILES := basic_op/basicop2.c basic_op/oper_32b.c
diff --git a/media/libstagefright/codecs/aacenc/SampleCode/AAC_E_SAMPLES.c b/media/libstagefright/codecs/aacenc/SampleCode/AAC_E_SAMPLES.c
index 64d012d..774da7b 100644
--- a/media/libstagefright/codecs/aacenc/SampleCode/AAC_E_SAMPLES.c
+++ b/media/libstagefright/codecs/aacenc/SampleCode/AAC_E_SAMPLES.c
@@ -188,7 +188,7 @@
useData.memflag = VO_IMF_USERMEMOPERATOR;
useData.memData = (VO_PTR)(&moper);
// open encoder dll;
- handle = dlopen("/data/local/tmp/libvoAACEncv7.so", RTLD_NOW);
+ handle = dlopen("libstagefright.so", RTLD_NOW);
if(handle == 0)
{
printf("open dll error......");
diff --git a/media/libstagefright/codecs/aacenc/SampleCode/Android.mk b/media/libstagefright/codecs/aacenc/SampleCode/Android.mk
index 52c9c07..ba3f4d2 100644
--- a/media/libstagefright/codecs/aacenc/SampleCode/Android.mk
+++ b/media/libstagefright/codecs/aacenc/SampleCode/Android.mk
@@ -1,24 +1,25 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := AAC_E_SAMPLES.c
-
-LOCAL_SRC_FILES += \
- ../../../Common/cmnMemory.c
+LOCAL_SRC_FILES := \
+ AAC_E_SAMPLES.c \
+ ../../common/cmnMemory.c
-LOCAL_MODULE := TestvoAACEnc
+LOCAL_CFLAGS += $(VO_CFLAGS)
+
+LOCAL_MODULE_TAGS := debug
+
+LOCAL_MODULE := AACEncTest
LOCAL_ARM_MODE := arm
-LOCAL_STATIC_LIBRARIES :=
-
-LOCAL_SHARED_LIBRARIES := libvoAACEnc
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright \
+ libdl
LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/ \
- $(LOCAL_PATH)/../../../Common \
- $(LOCAL_PATH)/../../../Include \
+ $(LOCAL_PATH)/ \
+ $(LOCAL_PATH)/../../common \
+ $(LOCAL_PATH)/../../common/include \
-LOCAL_CFLAGS := $(VO_CFLAGS)
-
include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/codecs/aacenc/SampleCode/eclair/Makefile b/media/libstagefright/codecs/aacenc/SampleCode/eclair/Makefile
deleted file mode 100644
index 22c5dc1..0000000
--- a/media/libstagefright/codecs/aacenc/SampleCode/eclair/Makefile
+++ /dev/null
@@ -1,55 +0,0 @@
-#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-# target6
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v7
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= exe
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-# e.g. -DVISUALON, macro VISUALON defined for your module
-VOMM:= #ARMV5E
-
-
-
-# please specify the name of your module
-VOTARGET:= voAACEncTestv7
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=-ldl
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../../../../../Release/
-
-
-# please modify here to be sure to see the doit.mk
-include ../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/aacenc/SampleCode/ms.mk b/media/libstagefright/codecs/aacenc/SampleCode/ms.mk
deleted file mode 100644
index 771a569..0000000
--- a/media/libstagefright/codecs/aacenc/SampleCode/ms.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-# please list all objects needed by your target here
-OBJS:=AAC_E_SAMPLES.o cmnMemory.o
-
-# please list all directories that all source files relative with your module(.h .c .cpp) locate
-VOSRCDIR:=../ ../../../../include ../../../../Common
-
-
diff --git a/media/libstagefright/codecs/aacenc/Tools/doit.mk b/media/libstagefright/codecs/aacenc/Tools/doit.mk
deleted file mode 100644
index dea0b0a..0000000
--- a/media/libstagefright/codecs/aacenc/Tools/doit.mk
+++ /dev/null
@@ -1,133 +0,0 @@
-#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-
-VERBOSE:=@
-
-
-VOMT ?= lib
-
-ifeq ($(VOMT), lib)
-LIB_STATIC=$(VOTARGET).a
-LIB_DYNAMIC=$(VOTARGET).so
-endif
-
-ifeq ($(VOMT), exe)
-TARGET=$(VOTARGET)
-endif
-
-CFLAGS=$(VOCFLAGS) $(addprefix -I, $(VOSRCDIR))
-CPPFLAGS=$(VOCPPFLAGS) $(addprefix -I, $(VOSRCDIR))
-ifneq ($(VOTT), pc)
-ASFLAGS=$(VOASFLAGS) $(addprefix -I, $(VOSRCDIR))
-endif
-
-LDFLAGS:=$(VOLDFLAGS)
-VOTEDEPS+=$(VODEPLIBS)
-VOTLDEPS+=$(VODEPLIBS)
-VOSTCLIBS ?=
-
-vpath %.c $(VOSRCDIR)
-vpath %.cpp $(VOSRCDIR)
-ifneq ($(VOTT), pc)
-vpath %.s $(VOSRCDIR)
-endif
-
-ifeq ($(VOTT), pc)
-BLTDIRS=$(VORELDIR)/Linux/static
-BLTDIRD=$(VORELDIR)/Linux/shared
-else
-BLTDIRS=$(VORELDIR)/Google/$(VONJ)/lib/$(VOTT)
-BLTDIRD=$(VORELDIR)/Google/$(VONJ)/so/$(VOTT)
-endif
-
-
-.PRECIOUS: $(OBJDIR)/%.o
-
-ifeq ($(VOMT), lib)
-all: mkdirs $(LIB_STATIC) $(LIB_DYNAMIC)
-mkdirs: $(OBJDIR) $(BLTDIRS) $(BLTDIRD)
-else
-all: mkdirs $(TARGET)
-mkdirs: $(OBJDIR)
-endif
-
-$(OBJDIR):
- @if test ! -d $@; then \
- mkdir -p $@; \
- fi;
-
-ifeq ($(VOMT), lib)
-$(BLTDIRS):
- @if test ! -d $@; then \
- mkdir -p $@; \
- fi;
-$(BLTDIRD):
- @if test ! -d $@; then \
- mkdir -p $@; \
- fi;
-endif
-
-
-ifeq ($(VOMT), lib)
-$(LIB_STATIC):$(OBJS)
- $(AR) cr $@ $(OBJDIR)/*.o $(VOSTCLIBS)
- $(RANLIB) $@
-ifneq ($(VODBG), yes)
- #$(STRIP) $@
-endif
-
-$(LIB_DYNAMIC):$(OBJS)
- $(GG) $(LDFLAGS) -o $@ $(OBJDIR)/*.o -Wl,--whole-archive $(VOSTCLIBS) -Wl,--no-whole-archive $(VOTLDEPS)
-ifneq ($(VODBG), yes)
- $(STRIP) $@
-endif
-
-else
-
-$(TARGET):$(OBJS)
- $(GG) $(LDFLAGS) -o $@ $(OBJDIR)/*.o -Wl,--whole-archive $(VOSTCLIBS) -Wl,--no-whole-archive $(VOTEDEPS)
-ifneq ($(VODBG), yes)
- $(STRIP) $@
-endif
-
-endif
-
-
-.SUFFIXES: .c .cpp .s .o
-.c.o:
- $(VERBOSE) $(CC) $(CFLAGS) -o $(OBJDIR)/$@ -c $<
-#%.c:$(OBJDIR)/%.o
-# $(VERBOSE) $(CC) $(CFLAGS) -o $@ -c $<
-.cpp.o:
- $(VERBOSE) $(GG) $(CPPFLAGS) -o $(OBJDIR)/$@ -c $<
-ifneq ($(VOTT), pc)
-.s.o:
- $(VERBOSE) $(AS) $(ASFLAGS) -o $(OBJDIR)/$@ $<
-endif
-
-
-.PHONY: clean devel
-clean:
-ifeq ($(VOMT), lib)
- -rm -fr $(OBJDIR) .*.sw* $(VOTARGET).*
-else
- -rm -fr $(OBJDIR) .*.sw* $(VOTARGET)
-endif
-
-devel:
- cp -a $(LIB_STATIC) $(BLTDIRS)
- cp -a $(LIB_DYNAMIC) $(BLTDIRD)
-
diff --git a/media/libstagefright/codecs/aacenc/Tools/eclair.mk b/media/libstagefright/codecs/aacenc/Tools/eclair.mk
deleted file mode 100644
index 1688361..0000000
--- a/media/libstagefright/codecs/aacenc/Tools/eclair.mk
+++ /dev/null
@@ -1,172 +0,0 @@
-#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-
-# special macro definitions for building
-VOPREDEF=-DLINUX -D_LINUX
-
-VOPRJ ?=
-VONJ ?= eclair
-VOTT ?= v6
-# control the version to release out
-# available: eva(evaluation), rel(release)
-VOVER=
-ifeq ($(VOVER), eva)
-VOPREDEF+=-D__VOVER_EVA__
-endif
-
-# for debug or not: yes for debug, any other for release
-VODBG?=ye
-
-# for detecting memory leak
-VODML=
-ifeq ($(VODML), yes)
-VOPREDEF+=-DDMEMLEAK
-endif
-
-VOPREDEF+=-D__VOTT_ARM__ -D__VONJ_ECLAIR__
-TCROOTPATH:=/opt/eclair
-GCCVER:=4.4.0
-TCPATH:=$(TCROOTPATH)/prebuilt/linux-x86/toolchain/arm-eabi-$(GCCVER)
-CCTPRE:=$(TCPATH)/bin/arm-eabi-
-AS:=$(CCTPRE)as
-AR:=$(CCTPRE)ar
-NM:=$(CCTPRE)nm
-CC:=$(CCTPRE)gcc
-GG:=$(CCTPRE)g++
-LD:=$(CCTPRE)ld
-SIZE:=$(CCTPRE)size
-STRIP:=$(CCTPRE)strip
-RANLIB:=$(CCTPRE)ranlib
-OBJCOPY:=$(CCTPRE)objcopy
-OBJDUMP:=$(CCTPRE)objdump
-READELF:=$(CCTPRE)readelf
-STRINGS:=$(CCTPRE)strings
-
-# target product dependcy
-# available: dream, generic
-VOTP:=sapphire-open
-CCTLIB:=$(TCROOTPATH)/out/target/product/$(VOTP)/obj/lib
-CCTINC:=-I$(TCROOTPATH)/system/core/include \
- -I$(TCROOTPATH)/hardware/libhardware/include \
- -I$(TCROOTPATH)/hardware/ril/include \
- -I$(TCROOTPATH)/hardware/libhardware_legacy/include \
- -I$(TCROOTPATH)/dalvik/libnativehelper/include \
- -I$(TCROOTPATH)/dalvik/libnativehelper/include/nativehelper \
- -I$(TCROOTPATH)/frameworks/base/include \
- -I$(TCROOTPATH)/frameworks/base/core/jni \
- -I$(TCROOTPATH)/frameworks/base/libs/audioflinger \
- -I$(TCROOTPATH)/external/skia/include \
- -I$(TCROOTPATH)/out/target/product/$(VOTP)/obj/include \
- -I$(TCROOTPATH)/bionic/libc/arch-arm/include \
- -I$(TCROOTPATH)/bionic/libc/include \
- -I$(TCROOTPATH)/bionic/libstdc++/include \
- -I$(TCROOTPATH)/bionic/libc/kernel/common \
- -I$(TCROOTPATH)/bionic/libc/kernel/arch-arm \
- -I$(TCROOTPATH)/bionic/libm/include \
- -I$(TCROOTPATH)/bionic/libm/include/arm \
- -I$(TCROOTPATH)/bionic/libthread_db/include \
- -I$(TCROOTPATH)/bionic/libm/arm \
- -I$(TCROOTPATH)/bionic/libm \
- -I$(TCROOTPATH)/frameworks/base/include/android_runtime
- #-I$(TCROOTPATH)/out/target/product/$(VOTP)/obj/SHARED_LIBRARIES/libm_intermediates
-
-CCTCFLAGS:=-msoft-float -mthumb-interwork -fno-exceptions -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -fmessage-length=0 -finline-functions -finline-limit=600 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers -fstrict-aliasing -funswitch-loops
-#-fwide-exec-charset=charset=UTF-32
-
-# for target exe
-TELDFLAGS:=-nostdlib -Bdynamic -Wl,-T,$(TCROOTPATH)/build/core/armelf.x -Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc -Wl,--no-undefined -Wl,-rpath-link=$(CCTLIB) -L$(CCTLIB)
-
-VOTEDEPS:=$(CCTLIB)/crtbegin_dynamic.o $(CCTLIB)/crtend_android.o $(TCPATH)/lib/gcc/arm-eabi/$(GCCVER)/interwork/libgcc.a -lc -lm
-
-# for target lib
-TLLDFLAGS:=-nostdlib -Wl,-T,$(TCROOTPATH)/build/core/armelf.xsc -Wl,--gc-sections -Wl,-shared,-Bsymbolic -L$(CCTLIB) -Wl,--no-whole-archive -Wl,--no-undefined $(TCPATH)/lib/gcc/arm-eabi/$(GCCVER)/interwork/libgcc.a
-
-VOTLDEPS:=-lm -lc
-
-
-ifeq ($(VOTT), v4)
-VOCFLAGS:=-mtune=arm9tdmi -march=armv4t
-VOASFLAGS:=-march=armv4t -mfpu=softfpa
-endif
-
-ifeq ($(VOTT), v5)
-VOCFLAGS:=-march=armv5te
-VOASFLAGS:=-march=armv5te -mfpu=vfp
-endif
-
-ifeq ($(VOTT), v5x)
-VOCFLAGS:=-march=armv5te -mtune=xscale
-VOASFLAGS:=-march=armv5te -mfpu=vfp
-endif
-
-ifeq ($(VOTT), v6)
-#VOCFLAGS:=-march=armv6 -mtune=arm1136jf-s
-#VOASFLAGS:=-march=armv6
-VOCFLAGS:=-march=armv6j -mtune=arm1136jf-s -mfpu=vfp -mfloat-abi=softfp -mapcs -mtpcs-leaf-frame -mlong-calls
-VOASFLAGS:=-march=armv6j -mcpu=arm1136jf-s -mfpu=arm1136jf-s -mfloat-abi=softfp -mapcs-float -mapcs-reentrant
-endif
-
-#
-# global link options
-VOLDFLAGS:=-Wl,-x,-X,--as-needed
-
-
-ifeq ($(VOTT), v7)
-VOCFLAGS+=-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp
-VOASFLAGS+=-march=armv7-a -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp
-VOLDFLAGS+=-Wl,--fix-cortex-a8
-endif
-
-#global compiling options for ARM target
-ifneq ($(VOTT), pc)
-VOASFLAGS+=--strip-local-absolute -R
-endif
-
-
-ifeq ($(VODBG), yes)
-VOCFLAGS+=-D_DEBUG -g
-else
-VOCFLAGS+=-DNDEBUG -O3
-endif
-
-VOCFLAGS+=$(VOPREDEF) $(VOMM) -Wall -fsigned-char -fomit-frame-pointer -fno-leading-underscore -fpic -fPIC -pipe -ftracer -fforce-addr -fno-bounds-check #-fvisibility=hidden #-fvisibility-inlines-hidden ##-ftree-loop-linear -mthumb -nostdinc -dD -fprefetch-loop-arrays
-
-
-ifneq ($(VOTT), pc)
-VOCFLAGS+=$(CCTCFLAGS) $(CCTINC)
-VOCPPFLAGS:=-fno-rtti $(VOCFLAGS)
-
-ifeq ($(VOMT), exe)
-VOLDFLAGS+=$(TELDFLAGS)
-endif
-
-ifeq ($(VOMT), lib)
-VOLDFLAGS+=$(TLLDFLAGS)
-endif
-else
-VOCPPFLAGS:=$(VOCFLAGS)
-ifeq ($(VOMT), lib)
-VOLDFLAGS+=-shared
-endif
-endif
-
-ifeq ($(VODBG), yes)
-#VOLDFLAGS:=
-endif
-
-# where to place object files
-OBJDIR=obj
-
diff --git a/media/libstagefright/codecs/aacenc/build/eclair/ARMV5E/Makefile b/media/libstagefright/codecs/aacenc/build/eclair/ARMV5E/Makefile
deleted file mode 100644
index b4f63af..0000000
--- a/media/libstagefright/codecs/aacenc/build/eclair/ARMV5E/Makefile
+++ /dev/null
@@ -1,55 +0,0 @@
-#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-# target6
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v5
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= lib
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-# e.g. -DVISUALON, macro VISUALON defined for your module
-VOMM:= -DARMV5E -DARM_INASM -DARMV5_INASM
-
-
-
-# please specify the name of your module
-VOTARGET:=libvoAACEncv5
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=#-ldl -lstdc++
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../../../../../../Release
-
-
-# please modify here to be sure to see the doit.mk
-include ../../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/aacenc/build/eclair/ARMV7/Makefile b/media/libstagefright/codecs/aacenc/build/eclair/ARMV7/Makefile
deleted file mode 100644
index cdce2c1..0000000
--- a/media/libstagefright/codecs/aacenc/build/eclair/ARMV7/Makefile
+++ /dev/null
@@ -1,55 +0,0 @@
-#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-# target6
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v7
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= lib
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-# e.g. -DVISUALON, macro VISUALON defined for your module
-VOMM:= -DARMV5E -DARMV7Neon -DARM_INASM -DARMV5_INASM
-
-
-
-# please specify the name of your module
-VOTARGET:=libvoAACEncv7
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=#-ldl -lstdc++
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../../../../../../Release
-
-
-# please modify here to be sure to see the doit.mk
-include ../../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/aacenc/build/eclair/makefile b/media/libstagefright/codecs/aacenc/build/eclair/makefile
deleted file mode 100644
index 6bb3c13..0000000
--- a/media/libstagefright/codecs/aacenc/build/eclair/makefile
+++ /dev/null
@@ -1,40 +0,0 @@
-#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-# Just acting as Father Makefile of Modules
-# please keep the name 'makefile' unchanged
-
-# Module Subdirs
-VOMSD:=$(dir $(shell find . -name 'Makefile'))
-
-all:
- for dir in $(VOMSD); \
- do \
- $(MAKE) -C $$dir; \
- done
-
-.PHONY:clean devel
-clean:
- for dir in $(VOMSD); \
- do \
- $(MAKE) -C $$dir clean; \
- done
-
-devel:
- for dir in $(VOMSD); \
- do \
- $(MAKE) -C $$dir devel; \
- done
diff --git a/media/libstagefright/codecs/aacenc/build/ms.mk b/media/libstagefright/codecs/aacenc/build/ms.mk
deleted file mode 100644
index b67efbc..0000000
--- a/media/libstagefright/codecs/aacenc/build/ms.mk
+++ /dev/null
@@ -1,42 +0,0 @@
-#/*
-#** Copyright 2003-2010, VisualOn, Inc.
-#**
-#** 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.
-#*/
-
-
-# please list all objects needed by your target here
-OBJS:=basicop2.o oper_32b.o aac_rom.o aacenc.o aacenc_core.o adj_thr.o \
- band_nrg.o bit_cnt.o bitbuffer.o bitenc.o block_switch.o channel_map.o \
- dyn_bits.o grp_data.o interface.o line_pe.o memalign.o ms_stereo.o \
- pre_echo_control.o psy_configuration.o psy_main.o qc_main.o quantize.o sf_estim.o \
- spreading.o stat_bits.o tns.o transform.o
-
-# please list all directories that all source files relative with your module(.h .c .cpp) locate
-VOSRCDIR:=../../../src \
- ../../../inc \
- ../../../basic_op\
- ../../../../../Include
-
-ifeq ($(VOTT), v5)
-OBJS+= AutoCorrelation_v5.o band_nrg_v5.o CalcWindowEnergy_v5.o \
- PrePostMDCT_v5.o R4R8First_v5.o Radix4FFT_v5.o
-VOSRCDIR+= ../../../src/asm/ARMV5E/
-endif
-
-ifeq ($(VOTT), v7)
-OBJS+= AutoCorrelation_v5.o band_nrg_v5.o CalcWindowEnergy_v5.o \
- PrePostMDCT_v7.o R4R8First_v7.o Radix4FFT_v7.o
-VOSRCDIR+= ../../../src/asm/ARMV5E/
-VOSRCDIR+= ../../../src/asm/ARMV7/
-endif
\ No newline at end of file
diff --git a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
index fb300da..a11d46b 100644
--- a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AMRNBDecoder"
+#include <utils/Log.h>
+
#include "AMRNBDecoder.h"
#include "gsmamr_dec.h"
@@ -154,18 +158,24 @@
const uint8_t *inputPtr =
(const uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset();
- size_t numBytesRead =
+ int32_t numBytesRead =
AMRDecode(mState,
(Frame_Type_3GPP)((inputPtr[0] >> 3) & 0x0f),
(UWord8 *)&inputPtr[1],
static_cast<int16_t *>(buffer->data()),
MIME_IETF);
+ if (numBytesRead == -1 ) {
+ LOGE("PV AMR decoder AMRDecode() call failed");
+ buffer->release();
+ buffer = NULL;
+ return ERROR_MALFORMED;
+ }
++numBytesRead; // Include the frame type header byte.
buffer->set_range(0, kNumSamplesPerFrame * sizeof(int16_t));
- if (numBytesRead > mInputBuffer->range_length()) {
+ if (static_cast<size_t>(numBytesRead) > mInputBuffer->range_length()) {
// This is bad, should never have happened, but did. Abort now.
buffer->release();
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk
index a545762..5862abc 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.mk
+++ b/media/libstagefright/codecs/amrnb/dec/Android.mk
@@ -52,3 +52,33 @@
LOCAL_MODULE := libstagefright_amrnbdec
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftAMR.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/../common/include \
+ $(LOCAL_PATH)/../common \
+ frameworks/base/media/libstagefright/codecs/amrwb/src \
+
+LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_amrnbdec libstagefright_amrwbdec
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright_omx libstagefright_foundation libutils \
+ libstagefright_amrnb_common
+
+LOCAL_MODULE := libstagefright_soft_amrdec
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
new file mode 100644
index 0000000..c0a588f
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2011 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 "SoftAMR"
+#include <utils/Log.h>
+
+#include "SoftAMR.h"
+
+#include "gsmamr_dec.h"
+#include "pvamrwbdecoder.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+SoftAMR::SoftAMR(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mMode(MODE_NARROW),
+ mState(NULL),
+ mDecoderBuf(NULL),
+ mDecoderCookie(NULL),
+ mInputBufferCount(0),
+ mAnchorTimeUs(0),
+ mNumSamplesOutput(0),
+ mSignalledError(false),
+ mOutputPortSettingsChange(NONE) {
+ if (!strcmp(name, "OMX.google.amrwb.decoder")) {
+ mMode = MODE_WIDE;
+ } else {
+ CHECK(!strcmp(name, "OMX.google.amrnb.decoder"));
+ }
+
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAMR::~SoftAMR() {
+ if (mMode == MODE_NARROW) {
+ GSMDecodeFrameExit(&mState);
+ mState = NULL;
+ } else {
+ free(mDecoderBuf);
+ mDecoderBuf = NULL;
+
+ mState = NULL;
+ mDecoderCookie = NULL;
+ }
+}
+
+void SoftAMR::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.audio.cMIMEType =
+ mMode == MODE_NARROW
+ ? const_cast<char *>("audio/amr")
+ : const_cast<char *>("audio/amrwb");
+
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingAMR;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+
+ def.nBufferSize =
+ (mMode == MODE_NARROW ? kNumSamplesPerFrameNB : kNumSamplesPerFrameWB)
+ * sizeof(int16_t);
+
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+ addPort(def);
+}
+
+status_t SoftAMR::initDecoder() {
+ if (mMode == MODE_NARROW) {
+ Word16 err = GSMInitDecode(&mState, (Word8 *)"AMRNBDecoder");
+
+ if (err != 0) {
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ int32_t memReq = pvDecoder_AmrWbMemRequirements();
+ mDecoderBuf = malloc(memReq);
+
+ pvDecoder_AmrWb_Init(&mState, mDecoderBuf, &mDecoderCookie);
+ }
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftAMR::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamAudioAmr:
+ {
+ OMX_AUDIO_PARAM_AMRTYPE *amrParams =
+ (OMX_AUDIO_PARAM_AMRTYPE *)params;
+
+ if (amrParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ amrParams->nChannels = 1;
+ amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
+ amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatConformance;
+
+ if (!isConfigured()) {
+ amrParams->nBitRate = 0;
+ amrParams->eAMRBandMode = OMX_AUDIO_AMRBandModeUnused;
+ } else {
+ amrParams->nBitRate = 0;
+ amrParams->eAMRBandMode =
+ mMode == MODE_NARROW
+ ? OMX_AUDIO_AMRBandModeNB0 : OMX_AUDIO_AMRBandModeWB0;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ pcmParams->nChannels = 1;
+ pcmParams->eNumData = OMX_NumericalDataSigned;
+ pcmParams->eEndian = OMX_EndianBig;
+ pcmParams->bInterleaved = OMX_TRUE;
+ pcmParams->nBitPerSample = 16;
+
+ pcmParams->nSamplingRate =
+ (mMode == MODE_NARROW) ? kSampleRateNB : kSampleRateWB;
+
+ pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+ pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAMR::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (mMode == MODE_NARROW) {
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.amrnb",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ } else {
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.amrwb",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioAmr:
+ {
+ const OMX_AUDIO_PARAM_AMRTYPE *aacParams =
+ (const OMX_AUDIO_PARAM_AMRTYPE *)params;
+
+ if (aacParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+bool SoftAMR::isConfigured() const {
+ return mInputBufferCount > 0;
+}
+
+static size_t getFrameSize(unsigned FT) {
+ static const size_t kFrameSizeWB[9] = {
+ 132, 177, 253, 285, 317, 365, 397, 461, 477
+ };
+
+ size_t frameSize = kFrameSizeWB[FT];
+
+ // Round up bits to bytes and add 1 for the header byte.
+ frameSize = (frameSize + 7) / 8 + 1;
+
+ return frameSize;
+}
+
+void SoftAMR::onQueueFilled(OMX_U32 portIndex) {
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ while (!inQueue.empty() && !outQueue.empty()) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+ return;
+ }
+
+ if (inHeader->nOffset == 0) {
+ mAnchorTimeUs = inHeader->nTimeStamp;
+ mNumSamplesOutput = 0;
+ }
+
+ const uint8_t *inputPtr = inHeader->pBuffer + inHeader->nOffset;
+ int32_t numBytesRead;
+
+ if (mMode == MODE_NARROW) {
+ numBytesRead =
+ AMRDecode(mState,
+ (Frame_Type_3GPP)((inputPtr[0] >> 3) & 0x0f),
+ (UWord8 *)&inputPtr[1],
+ reinterpret_cast<int16_t *>(outHeader->pBuffer),
+ MIME_IETF);
+
+ if (numBytesRead == -1) {
+ LOGE("PV AMR decoder AMRDecode() call failed");
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+
+ return;
+ }
+
+ ++numBytesRead; // Include the frame type header byte.
+
+ if (static_cast<size_t>(numBytesRead) > inHeader->nFilledLen) {
+ // This is bad, should never have happened, but did. Abort now.
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+
+ return;
+ }
+ } else {
+ int16 mode = ((inputPtr[0] >> 3) & 0x0f);
+ size_t frameSize = getFrameSize(mode);
+ CHECK_GE(inHeader->nFilledLen, frameSize);
+
+ int16 frameType;
+ RX_State_wb rx_state;
+ mime_unsorting(
+ const_cast<uint8_t *>(&inputPtr[1]),
+ mInputSampleBuffer,
+ &frameType, &mode, 1, &rx_state);
+
+ int16_t *outPtr = (int16_t *)outHeader->pBuffer;
+
+ int16_t numSamplesOutput;
+ pvDecoder_AmrWb(
+ mode, mInputSampleBuffer,
+ outPtr,
+ &numSamplesOutput,
+ mDecoderBuf, frameType, mDecoderCookie);
+
+ CHECK_EQ((int)numSamplesOutput, (int)kNumSamplesPerFrameWB);
+
+ for (int i = 0; i < kNumSamplesPerFrameWB; ++i) {
+ /* Delete the 2 LSBs (14-bit output) */
+ outPtr[i] &= 0xfffC;
+ }
+
+ numBytesRead = frameSize;
+ }
+
+ inHeader->nOffset += numBytesRead;
+ inHeader->nFilledLen -= numBytesRead;
+
+ outHeader->nFlags = 0;
+ outHeader->nOffset = 0;
+
+ if (mMode == MODE_NARROW) {
+ outHeader->nFilledLen = kNumSamplesPerFrameNB * sizeof(int16_t);
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumSamplesOutput * 1000000ll) / kSampleRateNB;
+
+ mNumSamplesOutput += kNumSamplesPerFrameNB;
+ } else {
+ outHeader->nFilledLen = kNumSamplesPerFrameWB * sizeof(int16_t);
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumSamplesOutput * 1000000ll) / kSampleRateWB;
+
+ mNumSamplesOutput += kNumSamplesPerFrameWB;
+ }
+
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+
+ ++mInputBufferCount;
+ }
+}
+
+void SoftAMR::onPortFlushCompleted(OMX_U32 portIndex) {
+}
+
+void SoftAMR::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+ if (portIndex != 1) {
+ return;
+ }
+
+ switch (mOutputPortSettingsChange) {
+ case NONE:
+ break;
+
+ case AWAITING_DISABLED:
+ {
+ CHECK(!enabled);
+ mOutputPortSettingsChange = AWAITING_ENABLED;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+ CHECK(enabled);
+ mOutputPortSettingsChange = NONE;
+ break;
+ }
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftAMR(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
new file mode 100644
index 0000000..9a596e5
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 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 SOFT_AMR_H_
+
+#define SOFT_AMR_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftAMR : public SimpleSoftOMXComponent {
+ SoftAMR(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftAMR();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+ enum {
+ kNumBuffers = 4,
+ kSampleRateNB = 8000,
+ kSampleRateWB = 16000,
+ kNumSamplesPerFrameNB = 160,
+ kNumSamplesPerFrameWB = 320,
+ };
+
+ enum {
+ MODE_NARROW,
+ MODE_WIDE
+
+ } mMode;
+
+ void *mState;
+ void *mDecoderBuf;
+ int16_t *mDecoderCookie;
+
+ size_t mInputBufferCount;
+ int64_t mAnchorTimeUs;
+ int64_t mNumSamplesOutput;
+
+ bool mSignalledError;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ int16_t mInputSampleBuffer[477];
+
+ void initPorts();
+ status_t initDecoder();
+ bool isConfigured() const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAMR);
+};
+
+} // namespace android
+
+#endif // SOFT_AMR_H_
+
diff --git a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
index 2a21472..5b111ef 100644
--- a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
+++ b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
@@ -177,7 +177,7 @@
CHECK(mInputBuffer->range_length() >= frameSize);
int16 frameType;
- RX_State rx_state;
+ RX_State_wb rx_state;
mime_unsorting(
const_cast<uint8_t *>(&inputPtr[1]),
mInputSampleBuffer,
diff --git a/media/libstagefright/codecs/amrwb/src/mime_io.cpp b/media/libstagefright/codecs/amrwb/src/mime_io.cpp
index 9ff8816..e1966c6 100644
--- a/media/libstagefright/codecs/amrwb/src/mime_io.cpp
+++ b/media/libstagefright/codecs/amrwb/src/mime_io.cpp
@@ -531,7 +531,7 @@
int16 * frame_type,
int16 * mode,
uint8 quality,
- RX_State *st)
+ RX_State_wb *st)
{
int16 i;
diff --git a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h
index 433fc92..c40bc10 100644
--- a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h
+++ b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h
@@ -101,7 +101,7 @@
{
int16 prev_ft;
int16 prev_mode;
-} RX_State;
+} RX_State_wb;
/*----------------------------------------------------------------------------
; ENUMERATED TYPEDEF'S
@@ -141,7 +141,7 @@
int16 *frame_type,
int16 *mode,
uint8 q,
- RX_State *st);
+ RX_State_wb *st);
/*----------------------------------------------------------------------------
diff --git a/media/libstagefright/codecs/amrwbenc/Android.mk b/media/libstagefright/codecs/amrwbenc/Android.mk
index 4293287..5179380 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.mk
+++ b/media/libstagefright/codecs/amrwbenc/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
include frameworks/base/media/libstagefright/codecs/common/Config.mk
-LOCAL_PRELINK_MODULE := false
+
LOCAL_SRC_FILES := \
AMRWBEncoder.cpp \
diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/AMRWB_E_SAMPLE.c b/media/libstagefright/codecs/amrwbenc/SampleCode/AMRWB_E_SAMPLE.c
index 792d3cc..5e71a5b 100644
--- a/media/libstagefright/codecs/amrwbenc/SampleCode/AMRWB_E_SAMPLE.c
+++ b/media/libstagefright/codecs/amrwbenc/SampleCode/AMRWB_E_SAMPLE.c
@@ -129,7 +129,7 @@
useData.memData = (VO_PTR)(&moper);
#ifdef LINUX
- handle = dlopen("/data/local/tmp/voAMRWBEnc.so", RTLD_NOW);
+ handle = dlopen("libstagefright.so", RTLD_NOW);
if(handle == 0)
{
printf("open dll error......");
diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk
index 7edb166..85ddceb 100644
--- a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk
+++ b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk
@@ -1,26 +1,26 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := AMRWB_E_SAMPLE.c
-
-LOCAL_SRC_FILES += \
- ../../../Common/cmnMemory.c
+LOCAL_SRC_FILES := \
+ AMRWB_E_SAMPLE.c \
+ ../../common/cmnMemory.c
-LOCAL_MODULE := TestvoAMRWBEnc
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := AMRWBEncTest
LOCAL_ARM_MODE := arm
-LOCAL_STATIC_LIBRARIES :=
+LOCAL_CFLAGS := $(VO_CFLAGS)
-LOCAL_SHARED_LIBRARIES := libvoAMRWBEnc
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright \
+ libdl
LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/ \
- $(LOCAL_PATH)/../../../Common \
- $(LOCAL_PATH)/../../../Include \
+ $(LOCAL_PATH)/ \
+ $(LOCAL_PATH)/../../common \
+ $(LOCAL_PATH)/../../common/include
-LOCAL_CFLAGS := $(VO_CFLAGS)
-
include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/eclair/Makefile b/media/libstagefright/codecs/amrwbenc/SampleCode/eclair/Makefile
deleted file mode 100644
index 55b876a..0000000
--- a/media/libstagefright/codecs/amrwbenc/SampleCode/eclair/Makefile
+++ /dev/null
@@ -1,56 +0,0 @@
-#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-
-# target6
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v6
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= exe
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-# e.g. -DVISUALON, macro VISUALON defined for your module
-VOMM:= #ARMV5E
-
-
-
-# please specify the name of your module
-VOTARGET:= voAMRWBEnc_Test
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=-ldl
-
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../
-
-
-# please modify here to be sure to see the doit.mk
-include ../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/ms.mk b/media/libstagefright/codecs/amrwbenc/SampleCode/ms.mk
deleted file mode 100644
index 74e8913..0000000
--- a/media/libstagefright/codecs/amrwbenc/SampleCode/ms.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-# please list all objects needed by your target here
-OBJS:=AMRWB_E_SAMPLE.o cmnMemory.o
-
-# please list all directories that all source files relative with your module(.h .c .cpp) locate
-VOSRCDIR:=../ \
- ../../../../Common \
- ../../../../Include
-
-
diff --git a/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV5E/Makefile b/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV5E/Makefile
deleted file mode 100644
index 58fda29..0000000
--- a/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV5E/Makefile
+++ /dev/null
@@ -1,53 +0,0 @@
-#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-
-# target type
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v5
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= lib
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-ifeq ($(VOTT), v5)
-VOMM:=-DARM -DASM_OPT
-endif
-
-# please specify the name of your module
-VOTARGET:= libvoAMRWBEncv5
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=-ldl -lstdc++ -lcutils
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../../../../../../Release
-
-# please modify here to be sure to see the doit.mk
-include ../../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV7/Makefile b/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV7/Makefile
deleted file mode 100644
index 5686411..0000000
--- a/media/libstagefright/codecs/amrwbenc/build/eclair/ARMV7/Makefile
+++ /dev/null
@@ -1,53 +0,0 @@
-#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-
-# target type
-# available: pc, v4(armv4), v5(armv5), v5x(armv5 xscale), v6(armv6), v7(cortex-a8 neon)
-VOTT:= v7
-
-
-# module type
-# please specify the type of your module: lib or exe
-VOMT:= lib
-
-
-# module macros
-# please append the additional macro definitions here for your module if necessary.
-ifeq ($(VOTT), v7)
-VOMM:=-DARM -DARMV7 -DASM_OPT
-endif
-
-# please specify the name of your module
-VOTARGET:= libvoAMRWBEncv7
-
-
-# please modify here to be sure to see the g1.mk
-include ../../../../../Tools/eclair.mk
-
-# dependent libraries.
-VODEPLIBS:=-ldl -lstdc++ -lcutils
-
-# module source
-# please modify here to be sure to see the ms.mk which specifies all source info of your module
-include ../ms.mk
-
-
-# please specify where is the voRelease on your PC, relative path is suggested
-VORELDIR:=../../../../../../Release
-
-# please modify here to be sure to see the doit.mk
-include ../../../../../Tools/doit.mk
-
diff --git a/media/libstagefright/codecs/amrwbenc/build/eclair/makefile b/media/libstagefright/codecs/amrwbenc/build/eclair/makefile
deleted file mode 100644
index 3473a1a..0000000
--- a/media/libstagefright/codecs/amrwbenc/build/eclair/makefile
+++ /dev/null
@@ -1,39 +0,0 @@
-#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-# Just acting as Father Makefile of Modules
-# please keep the name 'makefile' unchanged
-
-# Module Subdirs
-VOMSD:=$(dir $(shell find . -name 'Makefile'))
-
-all:
- for dir in $(VOMSD); \
- do \
- $(MAKE) -C $$dir; \
- done
-
-.PHONY:clean devel
-clean:
- for dir in $(VOMSD); \
- do \
- $(MAKE) -C $$dir clean; \
- done
-
-devel:
- for dir in $(VOMSD); \
- do \
- $(MAKE) -C $$dir devel; \
- done
diff --git a/media/libstagefright/codecs/amrwbenc/build/eclair/ms.mk b/media/libstagefright/codecs/amrwbenc/build/eclair/ms.mk
deleted file mode 100644
index bd6620c..0000000
--- a/media/libstagefright/codecs/amrwbenc/build/eclair/ms.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-#/*
-# ** Copyright 2003-2010, VisualOn, Inc.
-# **
-# ** 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.
-# */
-# please list all directories that all source files relative with your module(.h .c .cpp) locate
-VOSRCDIR:=../../../inc \
- ../../../src \
- ../../../../../Include
-
-# please list all objects needed by your target here
-OBJS:= autocorr.o az_isp.o bits.o c2t64fx.o c4t64fx.o convolve.o cor_h_x.o decim54.o \
- deemph.o dtx.o g_pitch.o gpclip.o homing.o hp400.o hp50.o hp6k.o hp_wsp.o \
- int_lpc.o isp_az.o isp_isf.o lag_wind.o levinson.o log2.o lp_dec2.o math_op.o mem_align.o \
- oper_32b.o p_med_ol.o pit_shrp.o pitch_f4.o pred_lt4.o preemph.o q_gain2.o q_pulse.o \
- qisf_ns.o qpisf_2s.o random.o residu.o scale.o stream.o syn_filt.o updt_tar.o util.o \
- voAMRWBEnc.o voicefac.o wb_vad.o weight_a.o
-
-
-ifeq ($(VOTT), v5)
-OBJS += cor_h_vec_opt.o Deemph_32_opt.o Dot_p_opt.o Filt_6k_7k_opt.o residu_asm_opt.o \
- scale_sig_opt.o Syn_filt_32_opt.o syn_filt_opt.o pred_lt4_1_opt.o convolve_opt.o \
- Norm_Corr_opt.o
-VOSRCDIR+= ../../../src/asm/ARMV5E
-endif
-
-ifeq ($(VOTT), v7)
-OBJS+= cor_h_vec_neon.o Deemph_32_neon.o Dot_p_neon.o Filt_6k_7k_neon.o residu_asm_neon.o \
- scale_sig_neon.o Syn_filt_32_neon.o syn_filt_neon.o pred_lt4_1_neon.o convolve_neon.o \
- Norm_Corr_neon.o
-VOSRCDIR+= ../../../src/asm/ARMV7
-endif
-
diff --git a/media/libstagefright/codecs/avc/common/include/avc_types.h b/media/libstagefright/codecs/avc/common/include/avc_types.h
index 73cad89..ec8b6de 100644
--- a/media/libstagefright/codecs/avc/common/include/avc_types.h
+++ b/media/libstagefright/codecs/avc/common/include/avc_types.h
@@ -1,3 +1,18 @@
+/*
+ * 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 AVC_TYPES_H_
#define AVC_TYPES_H_
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 5bbba35..490129f 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -534,7 +534,8 @@
default:
{
LOGE("Should not be here, unknown nalType %d", nalType);
- CHECK(!"Should not be here");
+
+ err = ERROR_MALFORMED;
break;
}
}
diff --git a/media/libstagefright/codecs/avc/dec/Android.mk b/media/libstagefright/codecs/avc/dec/Android.mk
index 1b00347..4d4533b 100644
--- a/media/libstagefright/codecs/avc/dec/Android.mk
+++ b/media/libstagefright/codecs/avc/dec/Android.mk
@@ -3,25 +3,54 @@
LOCAL_SRC_FILES := \
AVCDecoder.cpp \
- src/avcdec_api.cpp \
- src/avc_bitstream.cpp \
- src/header.cpp \
- src/itrans.cpp \
- src/pred_inter.cpp \
- src/pred_intra.cpp \
- src/residual.cpp \
- src/slice.cpp \
- src/vlc.cpp
+ src/avcdec_api.cpp \
+ src/avc_bitstream.cpp \
+ src/header.cpp \
+ src/itrans.cpp \
+ src/pred_inter.cpp \
+ src/pred_intra.cpp \
+ src/residual.cpp \
+ src/slice.cpp \
+ src/vlc.cpp
LOCAL_MODULE := libstagefright_avcdec
LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/src \
- $(LOCAL_PATH)/include \
- $(LOCAL_PATH)/../common/include \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/../common/include \
$(TOP)/frameworks/base/media/libstagefright/include \
- $(TOP)/frameworks/base/include/media/stagefright/openmax
+ frameworks/base/include/media/stagefright/openmax \
LOCAL_CFLAGS := -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF=
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftAVC.cpp
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/../common/include \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_avcdec
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright_avc_common \
+ libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_avcdec
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.cpp b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp
new file mode 100644
index 0000000..9f141ac
--- /dev/null
+++ b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2011 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 "SoftAVC"
+#include <utils/Log.h>
+
+#include "SoftAVC.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include "avcdec_api.h"
+#include "avcdec_int.h"
+
+namespace android {
+
+static const char kStartCode[4] = { 0x00, 0x00, 0x00, 0x01 };
+
+template<class T>
+static void InitOMXParams(T *params) {
+ 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 int32_t Malloc(void *userData, int32_t size, int32_t attrs) {
+ return reinterpret_cast<int32_t>(malloc(size));
+}
+
+static void Free(void *userData, int32_t ptr) {
+ free(reinterpret_cast<void *>(ptr));
+}
+
+SoftAVC::SoftAVC(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mHandle(new tagAVCHandle),
+ mInputBufferCount(0),
+ mWidth(160),
+ mHeight(120),
+ mCropLeft(0),
+ mCropTop(0),
+ mCropRight(mWidth - 1),
+ mCropBottom(mHeight - 1),
+ mSPSSeen(false),
+ mPPSSeen(false),
+ mCurrentTimeUs(-1),
+ mEOSStatus(INPUT_DATA_AVAILABLE),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAVC::~SoftAVC() {
+ PVAVCCleanUpDecoder(mHandle);
+
+ delete mHandle;
+ mHandle = NULL;
+}
+
+void SoftAVC::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumInputBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_AVC);
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC;
+ def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+ def.format.video.pNativeWindow = NULL;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumOutputBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ def.format.video.pNativeWindow = NULL;
+
+ def.nBufferSize =
+ (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
+
+ addPort(def);
+}
+
+status_t SoftAVC::initDecoder() {
+ memset(mHandle, 0, sizeof(tagAVCHandle));
+ mHandle->AVCObject = NULL;
+ mHandle->userData = this;
+ mHandle->CBAVC_DPBAlloc = ActivateSPSWrapper;
+ mHandle->CBAVC_FrameBind = BindFrameWrapper;
+ mHandle->CBAVC_FrameUnbind = UnbindFrame;
+ mHandle->CBAVC_Malloc = Malloc;
+ mHandle->CBAVC_Free = Free;
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftAVC::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ if (formatParams->nPortIndex == 0) {
+ formatParams->eCompressionFormat = OMX_VIDEO_CodingAVC;
+ formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+ formatParams->xFramerate = 0;
+ } else {
+ CHECK_EQ(formatParams->nPortIndex, 1u);
+
+ formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ formatParams->xFramerate = 0;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAVC::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (strncmp((const char *)roleParams->cRole,
+ "video_decoder.avc",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAVC::getConfig(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexConfigCommonOutputCrop:
+ {
+ OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
+
+ if (rectParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ rectParams->nLeft = mCropLeft;
+ rectParams->nTop = mCropTop;
+ rectParams->nWidth = mCropRight - mCropLeft + 1;
+ rectParams->nHeight = mCropBottom - mCropTop + 1;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return OMX_ErrorUnsupportedIndex;
+ }
+}
+
+static void findNALFragment(
+ const OMX_BUFFERHEADERTYPE *inHeader,
+ const uint8_t **fragPtr, size_t *fragSize) {
+ const uint8_t *data = inHeader->pBuffer + inHeader->nOffset;
+
+ size_t size = inHeader->nFilledLen;
+
+ CHECK(size >= 4);
+ CHECK(!memcmp(kStartCode, data, 4));
+
+ size_t offset = 4;
+ while (offset + 3 < size && memcmp(kStartCode, &data[offset], 4)) {
+ ++offset;
+ }
+
+ *fragPtr = &data[4];
+ if (offset + 3 >= size) {
+ *fragSize = size - 4;
+ } else {
+ *fragSize = offset - 4;
+ }
+}
+
+void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
+ if (mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ if (mEOSStatus == OUTPUT_FRAMES_FLUSHED) {
+ return;
+ }
+
+ while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty())
+ && outQueue.size() == kNumOutputBuffers) {
+ if (mEOSStatus == INPUT_EOS_SEEN) {
+ OMX_BUFFERHEADERTYPE *outHeader;
+ if (drainOutputBuffer(&outHeader)) {
+ List<BufferInfo *>::iterator it = outQueue.begin();
+ while ((*it)->mHeader != outHeader) {
+ ++it;
+ }
+
+ BufferInfo *outInfo = *it;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(it);
+ outInfo = NULL;
+
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ return;
+ }
+
+ BufferInfo *outInfo = *outQueue.begin();
+ outHeader = outInfo->mHeader;
+
+ outHeader->nOffset = 0;
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+ outHeader->nTimeStamp = 0;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+
+ mEOSStatus = OUTPUT_FRAMES_FLUSHED;
+ return;
+ }
+
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ mEOSStatus = INPUT_EOS_SEEN;
+ continue;
+ }
+
+ mCurrentTimeUs = inHeader->nTimeStamp;
+
+ const uint8_t *fragPtr;
+ size_t fragSize;
+ findNALFragment(inHeader, &fragPtr, &fragSize);
+
+ bool releaseFragment;
+ OMX_BUFFERHEADERTYPE *outHeader;
+ status_t err = decodeFragment(
+ fragPtr, fragSize,
+ &releaseFragment, &outHeader);
+
+ if (releaseFragment) {
+ CHECK_GE(inHeader->nFilledLen, fragSize + 4);
+
+ inHeader->nOffset += fragSize + 4;
+ inHeader->nFilledLen -= fragSize + 4;
+
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+ }
+
+ if (outHeader != NULL) {
+ List<BufferInfo *>::iterator it = outQueue.begin();
+ while ((*it)->mHeader != outHeader) {
+ ++it;
+ }
+
+ BufferInfo *outInfo = *it;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(it);
+ outInfo = NULL;
+
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ return;
+ }
+
+ if (err == INFO_FORMAT_CHANGED) {
+ return;
+ }
+
+ if (err != OK) {
+ notify(OMX_EventError, OMX_ErrorUndefined, err, NULL);
+ return;
+ }
+ }
+}
+
+status_t SoftAVC::decodeFragment(
+ const uint8_t *fragPtr, size_t fragSize,
+ bool *releaseFragment,
+ OMX_BUFFERHEADERTYPE **outHeader) {
+ *releaseFragment = true;
+ *outHeader = NULL;
+
+ int nalType;
+ int nalRefIdc;
+ AVCDec_Status res =
+ PVAVCDecGetNALType(
+ const_cast<uint8_t *>(fragPtr), fragSize,
+ &nalType, &nalRefIdc);
+
+ if (res != AVCDEC_SUCCESS) {
+ LOGV("cannot determine nal type");
+ return ERROR_MALFORMED;
+ }
+
+ if (nalType != AVC_NALTYPE_SPS && nalType != AVC_NALTYPE_PPS
+ && (!mSPSSeen || !mPPSSeen)) {
+ // We haven't seen SPS or PPS yet.
+ return OK;
+ }
+
+ switch (nalType) {
+ case AVC_NALTYPE_SPS:
+ {
+ mSPSSeen = true;
+
+ res = PVAVCDecSeqParamSet(
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
+
+ if (res != AVCDEC_SUCCESS) {
+ return ERROR_MALFORMED;
+ }
+
+ AVCDecObject *pDecVid = (AVCDecObject *)mHandle->AVCObject;
+
+ int32_t width =
+ (pDecVid->seqParams[0]->pic_width_in_mbs_minus1 + 1) * 16;
+
+ int32_t height =
+ (pDecVid->seqParams[0]->pic_height_in_map_units_minus1 + 1) * 16;
+
+ int32_t crop_left, crop_right, crop_top, crop_bottom;
+ if (pDecVid->seqParams[0]->frame_cropping_flag)
+ {
+ crop_left = 2 * pDecVid->seqParams[0]->frame_crop_left_offset;
+ crop_right =
+ width - (2 * pDecVid->seqParams[0]->frame_crop_right_offset + 1);
+
+ if (pDecVid->seqParams[0]->frame_mbs_only_flag)
+ {
+ crop_top = 2 * pDecVid->seqParams[0]->frame_crop_top_offset;
+ crop_bottom =
+ height -
+ (2 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1);
+ }
+ else
+ {
+ crop_top = 4 * pDecVid->seqParams[0]->frame_crop_top_offset;
+ crop_bottom =
+ height -
+ (4 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1);
+ }
+ } else {
+ crop_bottom = height - 1;
+ crop_right = width - 1;
+ crop_top = crop_left = 0;
+ }
+
+ status_t err = OK;
+
+ if (mWidth != width || mHeight != height) {
+ mWidth = width;
+ mHeight = height;
+
+ updatePortDefinitions();
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+
+ err = INFO_FORMAT_CHANGED;
+ }
+
+ if (mCropLeft != crop_left
+ || mCropTop != crop_top
+ || mCropRight != crop_right
+ || mCropBottom != crop_bottom) {
+ mCropLeft = crop_left;
+ mCropTop = crop_top;
+ mCropRight = crop_right;
+ mCropBottom = crop_bottom;
+
+ notify(OMX_EventPortSettingsChanged,
+ 1,
+ OMX_IndexConfigCommonOutputCrop,
+ NULL);
+ }
+
+ return err;
+ }
+
+ case AVC_NALTYPE_PPS:
+ {
+ mPPSSeen = true;
+
+ res = PVAVCDecPicParamSet(
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
+
+ if (res != AVCDEC_SUCCESS) {
+ LOGV("PVAVCDecPicParamSet returned error %d", res);
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+ }
+
+ case AVC_NALTYPE_SLICE:
+ case AVC_NALTYPE_IDR:
+ {
+ res = PVAVCDecodeSlice(
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
+
+ if (res == AVCDEC_PICTURE_OUTPUT_READY) {
+ *releaseFragment = false;
+
+ if (!drainOutputBuffer(outHeader)) {
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+ }
+
+ if (res == AVCDEC_PICTURE_READY || res == AVCDEC_SUCCESS) {
+ return OK;
+ } else {
+ LOGV("PVAVCDecodeSlice returned error %d", res);
+ return ERROR_MALFORMED;
+ }
+ }
+
+ case AVC_NALTYPE_SEI:
+ {
+ res = PVAVCDecSEI(
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
+
+ if (res != AVCDEC_SUCCESS) {
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+ }
+
+ case AVC_NALTYPE_AUD:
+ case AVC_NALTYPE_FILL:
+ case AVC_NALTYPE_EOSEQ:
+ {
+ return OK;
+ }
+
+ default:
+ {
+ LOGE("Should not be here, unknown nalType %d", nalType);
+
+ return ERROR_MALFORMED;
+ }
+ }
+
+ return OK;
+}
+
+bool SoftAVC::drainOutputBuffer(OMX_BUFFERHEADERTYPE **outHeader) {
+ int32_t index;
+ int32_t Release;
+ AVCFrameIO Output;
+ Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
+ AVCDec_Status status =
+ PVAVCDecGetOutput(mHandle, &index, &Release, &Output);
+
+ if (status != AVCDEC_SUCCESS) {
+ return false;
+ }
+
+ PortInfo *port = editPortInfo(1);
+ CHECK_GE(index, 0);
+ CHECK_LT((size_t)index, port->mBuffers.size());
+ CHECK(port->mBuffers.editItemAt(index).mOwnedByUs);
+
+ *outHeader = port->mBuffers.editItemAt(index).mHeader;
+ (*outHeader)->nOffset = 0;
+ (*outHeader)->nFilledLen = port->mDef.nBufferSize;
+ (*outHeader)->nFlags = 0;
+
+ return true;
+}
+
+void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0) {
+ PVAVCDecReset(mHandle);
+
+ mEOSStatus = INPUT_DATA_AVAILABLE;
+ }
+}
+
+void SoftAVC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+ if (portIndex != 1) {
+ return;
+ }
+
+ switch (mOutputPortSettingsChange) {
+ case NONE:
+ break;
+
+ case AWAITING_DISABLED:
+ {
+ CHECK(!enabled);
+ mOutputPortSettingsChange = AWAITING_ENABLED;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+ CHECK(enabled);
+ mOutputPortSettingsChange = NONE;
+ break;
+ }
+ }
+}
+
+void SoftAVC::updatePortDefinitions() {
+ OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def = &editPortInfo(1)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def->nBufferSize =
+ (def->format.video.nFrameWidth
+ * def->format.video.nFrameHeight * 3) / 2;
+}
+
+// static
+int32_t SoftAVC::ActivateSPSWrapper(
+ void *userData, unsigned int sizeInMbs, unsigned int numBuffers) {
+ return static_cast<SoftAVC *>(userData)->activateSPS(sizeInMbs, numBuffers);
+}
+
+// static
+int32_t SoftAVC::BindFrameWrapper(
+ void *userData, int32_t index, uint8_t **yuv) {
+ return static_cast<SoftAVC *>(userData)->bindFrame(index, yuv);
+}
+
+// static
+void SoftAVC::UnbindFrame(void *userData, int32_t index) {
+}
+
+int32_t SoftAVC::activateSPS(
+ unsigned int sizeInMbs, unsigned int numBuffers) {
+ PortInfo *port = editPortInfo(1);
+ CHECK_GE(port->mBuffers.size(), numBuffers);
+ CHECK_GE(port->mDef.nBufferSize, (sizeInMbs << 7) * 3);
+
+ return 1;
+}
+
+int32_t SoftAVC::bindFrame(int32_t index, uint8_t **yuv) {
+ PortInfo *port = editPortInfo(1);
+
+ CHECK_GE(index, 0);
+ CHECK_LT((size_t)index, port->mBuffers.size());
+
+ BufferInfo *outBuffer =
+ &port->mBuffers.editItemAt(index);
+
+ CHECK(outBuffer->mOwnedByUs);
+
+ outBuffer->mHeader->nTimeStamp = mCurrentTimeUs;
+ *yuv = outBuffer->mHeader->pBuffer;
+
+ return 1;
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftAVC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.h b/media/libstagefright/codecs/avc/dec/SoftAVC.h
new file mode 100644
index 0000000..1594b4d
--- /dev/null
+++ b/media/libstagefright/codecs/avc/dec/SoftAVC.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2011 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 SOFT_AVC_H_
+
+#define SOFT_AVC_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tagAVCHandle;
+
+namespace android {
+
+struct SoftAVC : public SimpleSoftOMXComponent {
+ SoftAVC(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftAVC();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual OMX_ERRORTYPE getConfig(OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+ enum {
+ kNumInputBuffers = 4,
+ kNumOutputBuffers = 18,
+ };
+
+ enum EOSStatus {
+ INPUT_DATA_AVAILABLE,
+ INPUT_EOS_SEEN,
+ OUTPUT_FRAMES_FLUSHED,
+ };
+
+ tagAVCHandle *mHandle;
+
+ size_t mInputBufferCount;
+
+ int32_t mWidth, mHeight;
+ int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
+
+ bool mSPSSeen, mPPSSeen;
+
+ int64_t mCurrentTimeUs;
+
+ EOSStatus mEOSStatus;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+
+ status_t decodeFragment(
+ const uint8_t *fragPtr, size_t fragSize,
+ bool *releaseFrames,
+ OMX_BUFFERHEADERTYPE **outHeader);
+
+ void updatePortDefinitions();
+ bool drainOutputBuffer(OMX_BUFFERHEADERTYPE **outHeader);
+
+ static int32_t ActivateSPSWrapper(
+ void *userData, unsigned int sizeInMbs, unsigned int numBuffers);
+
+ static int32_t BindFrameWrapper(
+ void *userData, int32_t index, uint8_t **yuv);
+
+ static void UnbindFrame(void *userData, int32_t index);
+
+ int32_t activateSPS(
+ unsigned int sizeInMbs, unsigned int numBuffers);
+
+ int32_t bindFrame(int32_t index, uint8_t **yuv);
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAVC);
+};
+
+} // namespace android
+
+#endif // SOFT_AVC_H_
+
diff --git a/media/libstagefright/codecs/common/Android.mk b/media/libstagefright/codecs/common/Android.mk
index fffb2ad..af8795a 100644
--- a/media/libstagefright/codecs/common/Android.mk
+++ b/media/libstagefright/codecs/common/Android.mk
@@ -1,7 +1,7 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_PRELINK_MODULE := false
+
LOCAL_SRC_FILES := cmnMemory.c
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
index cfb9fe4..6e98559 100644
--- a/media/libstagefright/codecs/g711/dec/Android.mk
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -10,3 +10,22 @@
LOCAL_MODULE := libstagefright_g711dec
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftG711.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_g711dec
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.cpp b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
new file mode 100644
index 0000000..15e2c26
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2011 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 "SoftG711"
+#include <utils/Log.h>
+
+#include "SoftG711.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+SoftG711::SoftG711(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mIsMLaw(true),
+ mNumChannels(1),
+ mSignalledError(false) {
+ if (!strcmp(name, "OMX.google.g711.alaw.decoder")) {
+ mIsMLaw = false;
+ } else {
+ CHECK(!strcmp(name, "OMX.google.g711.mlaw.decoder"));
+ }
+
+ initPorts();
+}
+
+SoftG711::~SoftG711() {
+}
+
+void SoftG711::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.audio.cMIMEType =
+ const_cast<char *>(
+ mIsMLaw
+ ? MEDIA_MIMETYPE_AUDIO_G711_MLAW
+ : MEDIA_MIMETYPE_AUDIO_G711_ALAW);
+
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingG711;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t);
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+ addPort(def);
+}
+
+OMX_ERRORTYPE SoftG711::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ pcmParams->eNumData = OMX_NumericalDataSigned;
+ pcmParams->eEndian = OMX_EndianBig;
+ pcmParams->bInterleaved = OMX_TRUE;
+ pcmParams->nBitPerSample = 16;
+ pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+ pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+ pcmParams->nChannels = mNumChannels;
+ pcmParams->nSamplingRate = 8000;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftG711::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (pcmParams->nChannels < 1 || pcmParams->nChannels > 2) {
+ return OMX_ErrorUndefined;
+ }
+
+ mNumChannels = pcmParams->nChannels;
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (mIsMLaw) {
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.g711mlaw",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ } else {
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.g711alaw",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+void SoftG711::onQueueFilled(OMX_U32 portIndex) {
+ if (mSignalledError) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ while (!inQueue.empty() && !outQueue.empty()) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+ return;
+ }
+
+ if (inHeader->nFilledLen > kMaxNumSamplesPerFrame) {
+ LOGE("input buffer too large (%ld).", inHeader->nFilledLen);
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ }
+
+ const uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset;
+
+ if (mIsMLaw) {
+ DecodeMLaw(
+ reinterpret_cast<int16_t *>(outHeader->pBuffer),
+ inputptr, inHeader->nFilledLen);
+ } else {
+ DecodeALaw(
+ reinterpret_cast<int16_t *>(outHeader->pBuffer),
+ inputptr, inHeader->nFilledLen);
+ }
+
+ outHeader->nTimeStamp = inHeader->nTimeStamp;
+ outHeader->nOffset = 0;
+ outHeader->nFilledLen = inHeader->nFilledLen * sizeof(int16_t);
+ outHeader->nFlags = 0;
+
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ }
+}
+
+// static
+void SoftG711::DecodeALaw(
+ int16_t *out, const uint8_t *in, size_t inSize) {
+ while (inSize-- > 0) {
+ int32_t x = *in++;
+
+ int32_t ix = x ^ 0x55;
+ ix &= 0x7f;
+
+ int32_t iexp = ix >> 4;
+ int32_t mant = ix & 0x0f;
+
+ if (iexp > 0) {
+ mant += 16;
+ }
+
+ mant = (mant << 4) + 8;
+
+ if (iexp > 1) {
+ mant = mant << (iexp - 1);
+ }
+
+ *out++ = (x > 127) ? mant : -mant;
+ }
+}
+
+// static
+void SoftG711::DecodeMLaw(
+ int16_t *out, const uint8_t *in, size_t inSize) {
+ while (inSize-- > 0) {
+ int32_t x = *in++;
+
+ int32_t mantissa = ~x;
+ int32_t exponent = (mantissa >> 4) & 7;
+ int32_t segment = exponent + 1;
+ mantissa &= 0x0f;
+
+ int32_t step = 4 << segment;
+
+ int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33;
+
+ *out++ = (x < 0x80) ? -abs : abs;
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftG711(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.h b/media/libstagefright/codecs/g711/dec/SoftG711.h
new file mode 100644
index 0000000..bff0c68
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/SoftG711.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 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 SOFT_G711_H_
+
+#define SOFT_G711_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftG711 : public SimpleSoftOMXComponent {
+ SoftG711(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftG711();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+
+private:
+ enum {
+ kNumBuffers = 4,
+ kMaxNumSamplesPerFrame = 16384,
+ };
+
+ bool mIsMLaw;
+ OMX_U32 mNumChannels;
+ bool mSignalledError;
+
+ void initPorts();
+
+ static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize);
+ static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize);
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftG711);
+};
+
+} // namespace android
+
+#endif // SOFT_G711_H_
+
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
index 2d9bcc6..f1bec08 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
@@ -48,3 +48,29 @@
LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF=
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftMPEG4.cpp
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_m4vh263dec
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_mpeg4dec
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
new file mode 100644
index 0000000..13e1662
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2011 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 "SoftMPEG4"
+#include <utils/Log.h>
+
+#include "SoftMPEG4.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include "mp4dec_api.h"
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+SoftMPEG4::SoftMPEG4(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mMode(MODE_MPEG4),
+ mHandle(new tagvideoDecControls),
+ mInputBufferCount(0),
+ mWidth(352),
+ mHeight(288),
+ mCropLeft(0),
+ mCropTop(0),
+ mCropRight(mWidth - 1),
+ mCropBottom(mHeight - 1),
+ mSignalledError(false),
+ mInitialized(false),
+ mFramesConfigured(false),
+ mNumSamplesOutput(0),
+ mOutputPortSettingsChange(NONE) {
+ if (!strcmp(name, "OMX.google.h263.decoder")) {
+ mMode = MODE_H263;
+ } else {
+ CHECK(!strcmp(name, "OMX.google.mpeg4.decoder"));
+ }
+
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftMPEG4::~SoftMPEG4() {
+ if (mInitialized) {
+ PVCleanUpVideoDecoder(mHandle);
+ }
+
+ delete mHandle;
+ mHandle = NULL;
+}
+
+void SoftMPEG4::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumInputBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.video.cMIMEType =
+ (mMode == MODE_MPEG4)
+ ? const_cast<char *>(MEDIA_MIMETYPE_VIDEO_MPEG4)
+ : const_cast<char *>(MEDIA_MIMETYPE_VIDEO_H263);
+
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+
+ def.format.video.eCompressionFormat =
+ mMode == MODE_MPEG4 ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263;
+
+ def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+ def.format.video.pNativeWindow = NULL;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumOutputBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ def.format.video.pNativeWindow = NULL;
+
+ def.nBufferSize =
+ (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
+
+ addPort(def);
+}
+
+status_t SoftMPEG4::initDecoder() {
+ memset(mHandle, 0, sizeof(tagvideoDecControls));
+ return OK;
+}
+
+OMX_ERRORTYPE SoftMPEG4::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ if (formatParams->nPortIndex == 0) {
+ formatParams->eCompressionFormat =
+ (mMode == MODE_MPEG4)
+ ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263;
+
+ formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+ formatParams->xFramerate = 0;
+ } else {
+ CHECK_EQ(formatParams->nPortIndex, 1u);
+
+ formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ formatParams->xFramerate = 0;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftMPEG4::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (mMode == MODE_MPEG4) {
+ if (strncmp((const char *)roleParams->cRole,
+ "video_decoder.mpeg4",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ } else {
+ if (strncmp((const char *)roleParams->cRole,
+ "video_decoder.h263",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftMPEG4::getConfig(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexConfigCommonOutputCrop:
+ {
+ OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
+
+ if (rectParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ rectParams->nLeft = mCropLeft;
+ rectParams->nTop = mCropTop;
+ rectParams->nWidth = mCropRight - mCropLeft + 1;
+ rectParams->nHeight = mCropBottom - mCropTop + 1;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return OMX_ErrorUnsupportedIndex;
+ }
+}
+
+void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) {
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ PortInfo *port = editPortInfo(1);
+
+ OMX_BUFFERHEADERTYPE *outHeader =
+ port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ ++mInputBufferCount;
+
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ List<BufferInfo *>::iterator it = outQueue.begin();
+ while ((*it)->mHeader != outHeader) {
+ ++it;
+ }
+
+ BufferInfo *outInfo = *it;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(it);
+ outInfo = NULL;
+
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ return;
+ }
+
+ uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset;
+
+ if (!mInitialized) {
+ uint8_t *vol_data[1];
+ int32_t vol_size = 0;
+
+ vol_data[0] = NULL;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ vol_data[0] = bitstream;
+ vol_size = inHeader->nFilledLen;
+ }
+
+ MP4DecodingMode mode =
+ (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE;
+
+ Bool success = PVInitVideoDecoder(
+ mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode);
+
+ if (!success) {
+ LOGW("PVInitVideoDecoder failed. Unsupported content?");
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle);
+ if (mode != actualMode) {
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ PVSetPostProcType((VideoDecControls *) mHandle, 0);
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+
+ mInitialized = true;
+
+ if (mode == MPEG4_MODE && portSettingsChanged()) {
+ return;
+ }
+
+ continue;
+ }
+
+ if (!mFramesConfigured) {
+ PortInfo *port = editPortInfo(1);
+ OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader;
+
+ PVSetReferenceYUV(mHandle, outHeader->pBuffer);
+
+ mFramesConfigured = true;
+ }
+
+ uint32_t timestamp = 0xFFFFFFFF;
+ int32_t bufferSize = inHeader->nFilledLen;
+
+ uint32_t useExtTimestamp = 0;
+ if (PVDecodeVideoFrame(
+ mHandle, &bitstream, ×tamp, &bufferSize,
+ &useExtTimestamp,
+ outHeader->pBuffer) != PV_TRUE) {
+ LOGE("failed to decode video frame.");
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ if (portSettingsChanged()) {
+ return;
+ }
+
+ outHeader->nTimeStamp = inHeader->nTimeStamp;
+
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+
+ ++mInputBufferCount;
+
+ outHeader->nOffset = 0;
+ outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
+ outHeader->nFlags = 0;
+
+ List<BufferInfo *>::iterator it = outQueue.begin();
+ while ((*it)->mHeader != outHeader) {
+ ++it;
+ }
+
+ BufferInfo *outInfo = *it;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(it);
+ outInfo = NULL;
+
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+
+ ++mNumSamplesOutput;
+ }
+}
+
+bool SoftMPEG4::portSettingsChanged() {
+ int32_t disp_width, disp_height;
+ PVGetVideoDimensions(mHandle, &disp_width, &disp_height);
+
+ int32_t buf_width, buf_height;
+ PVGetBufferDimensions(mHandle, &buf_width, &buf_height);
+
+ CHECK_LE(disp_width, buf_width);
+ CHECK_LE(disp_height, buf_height);
+
+ LOGV("disp_width = %d, disp_height = %d, buf_width = %d, buf_height = %d",
+ disp_width, disp_height, buf_width, buf_height);
+
+ if (mCropRight != disp_width - 1
+ || mCropBottom != disp_height - 1) {
+ mCropLeft = 0;
+ mCropTop = 0;
+ mCropRight = disp_width - 1;
+ mCropBottom = disp_height - 1;
+
+ notify(OMX_EventPortSettingsChanged,
+ 1,
+ OMX_IndexConfigCommonOutputCrop,
+ NULL);
+ }
+
+ if (buf_width != mWidth || buf_height != mHeight) {
+ mWidth = buf_width;
+ mHeight = buf_height;
+
+ updatePortDefinitions();
+
+ if (mMode == MODE_H263) {
+ PVCleanUpVideoDecoder(mHandle);
+
+ uint8_t *vol_data[1];
+ int32_t vol_size = 0;
+
+ vol_data[0] = NULL;
+ if (!PVInitVideoDecoder(
+ mHandle, vol_data, &vol_size, 1, mWidth, mHeight,
+ H263_MODE)) {
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return true;
+ }
+ }
+
+ mFramesConfigured = false;
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return true;
+ }
+
+ return false;
+}
+
+void SoftMPEG4::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0 && mInitialized) {
+ CHECK_EQ((int)PVResetVideoDecoder(mHandle), (int)PV_TRUE);
+ }
+}
+
+void SoftMPEG4::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+ if (portIndex != 1) {
+ return;
+ }
+
+ switch (mOutputPortSettingsChange) {
+ case NONE:
+ break;
+
+ case AWAITING_DISABLED:
+ {
+ CHECK(!enabled);
+ mOutputPortSettingsChange = AWAITING_ENABLED;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+ CHECK(enabled);
+ mOutputPortSettingsChange = NONE;
+ break;
+ }
+ }
+}
+
+void SoftMPEG4::updatePortDefinitions() {
+ OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def = &editPortInfo(1)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def->nBufferSize =
+ (((def->format.video.nFrameWidth + 15) & -16)
+ * ((def->format.video.nFrameHeight + 15) & -16) * 3) / 2;
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftMPEG4(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h
new file mode 100644
index 0000000..dff08a7
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 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 SOFT_MPEG4_H_
+
+#define SOFT_MPEG4_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tagvideoDecControls;
+
+namespace android {
+
+struct SoftMPEG4 : public SimpleSoftOMXComponent {
+ SoftMPEG4(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftMPEG4();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual OMX_ERRORTYPE getConfig(OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+ enum {
+ kNumInputBuffers = 4,
+ kNumOutputBuffers = 2,
+ };
+
+ enum {
+ MODE_MPEG4,
+ MODE_H263,
+
+ } mMode;
+
+ tagvideoDecControls *mHandle;
+
+ size_t mInputBufferCount;
+
+ int32_t mWidth, mHeight;
+ int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
+
+ bool mSignalledError;
+ bool mInitialized;
+ bool mFramesConfigured;
+
+ int32_t mNumSamplesOutput;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+
+ void updatePortDefinitions();
+ bool portSettingsChanged();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4);
+};
+
+} // namespace android
+
+#endif // SOFT_MPEG4_H_
+
+
diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk
index 753500e..229988e 100644
--- a/media/libstagefright/codecs/mp3dec/Android.mk
+++ b/media/libstagefright/codecs/mp3dec/Android.mk
@@ -57,3 +57,26 @@
include $(BUILD_STATIC_LIBRARY)
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftMP3.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_mp3dec
+
+LOCAL_MODULE := libstagefright_soft_mp3dec
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
index 59dd740..0ba42ff 100644
--- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MP3Decoder"
+
#include "MP3Decoder.h"
#include "include/pvmp3decoder_api.h"
@@ -175,7 +178,12 @@
!= NO_DECODING_ERROR) {
LOGV("mp3 decoder returned error %d", decoderErr);
- if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR) {
+ if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR ||
+ mConfig->outputFrameSize == 0) {
+
+ if (mConfig->outputFrameSize == 0) {
+ LOGE("Output frame size is 0");
+ }
buffer->release();
buffer = NULL;
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
new file mode 100644
index 0000000..f6770b0
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2011 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 "SoftMP3"
+#include <utils/Log.h>
+
+#include "SoftMP3.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+#include "include/pvmp3decoder_api.h"
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+SoftMP3::SoftMP3(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mConfig(new tPVMP3DecoderExternal),
+ mDecoderBuf(NULL),
+ mAnchorTimeUs(0),
+ mNumFramesOutput(0),
+ mNumChannels(2),
+ mSamplingRate(44100),
+ mSignalledError(false),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ initDecoder();
+}
+
+SoftMP3::~SoftMP3() {
+ if (mDecoderBuf != NULL) {
+ free(mDecoderBuf);
+ mDecoderBuf = NULL;
+ }
+
+ delete mConfig;
+ mConfig = NULL;
+}
+
+void SoftMP3::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.audio.cMIMEType =
+ const_cast<char *>(MEDIA_MIMETYPE_AUDIO_MPEG);
+
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingMP3;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = kOutputBufferSize;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+ addPort(def);
+}
+
+void SoftMP3::initDecoder() {
+ mConfig->equalizerType = flat;
+ mConfig->crcEnabled = false;
+
+ uint32_t memRequirements = pvmp3_decoderMemRequirements();
+ mDecoderBuf = malloc(memRequirements);
+
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+}
+
+OMX_ERRORTYPE SoftMP3::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ pcmParams->eNumData = OMX_NumericalDataSigned;
+ pcmParams->eEndian = OMX_EndianBig;
+ pcmParams->bInterleaved = OMX_TRUE;
+ pcmParams->nBitPerSample = 16;
+ pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+ pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+ pcmParams->nChannels = mNumChannels;
+ pcmParams->nSamplingRate = mSamplingRate;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftMP3::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.mp3",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+void SoftMP3::onQueueFilled(OMX_U32 portIndex) {
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ while (!inQueue.empty() && !outQueue.empty()) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+ return;
+ }
+
+ if (inHeader->nOffset == 0) {
+ mAnchorTimeUs = inHeader->nTimeStamp;
+ mNumFramesOutput = 0;
+ }
+
+ mConfig->pInputBuffer =
+ inHeader->pBuffer + inHeader->nOffset;
+
+ mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
+ mConfig->inputBufferMaxLength = 0;
+ mConfig->inputBufferUsedLength = 0;
+
+ mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t);
+
+ mConfig->pOutputBuffer =
+ reinterpret_cast<int16_t *>(outHeader->pBuffer);
+
+ ERROR_CODE decoderErr;
+ if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf))
+ != NO_DECODING_ERROR) {
+ LOGV("mp3 decoder returned error %d", decoderErr);
+
+ if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR ||
+ mConfig->outputFrameSize == 0) {
+
+ if (mConfig->outputFrameSize == 0) {
+ LOGE("Output frame size is 0");
+ }
+
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ // This is recoverable, just ignore the current frame and
+ // play silence instead.
+ memset(outHeader->pBuffer,
+ 0,
+ mConfig->outputFrameSize * sizeof(int16_t));
+
+ mConfig->inputBufferUsedLength = inHeader->nFilledLen;
+ } else if (mConfig->samplingRate != mSamplingRate
+ || mConfig->num_channels != mNumChannels) {
+ mSamplingRate = mConfig->samplingRate;
+ mNumChannels = mConfig->num_channels;
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return;
+ }
+
+ outHeader->nOffset = 0;
+ outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t);
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumFramesOutput * 1000000ll) / mConfig->samplingRate;
+
+ outHeader->nFlags = 0;
+
+ CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength);
+
+ inHeader->nOffset += mConfig->inputBufferUsedLength;
+ inHeader->nFilledLen -= mConfig->inputBufferUsedLength;
+
+ mNumFramesOutput += mConfig->outputFrameSize / mNumChannels;
+
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ }
+}
+
+void SoftMP3::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0) {
+ // Make sure that the next buffer output does not still
+ // depend on fragments from the last one decoded.
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+ }
+}
+
+void SoftMP3::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+ if (portIndex != 1) {
+ return;
+ }
+
+ switch (mOutputPortSettingsChange) {
+ case NONE:
+ break;
+
+ case AWAITING_DISABLED:
+ {
+ CHECK(!enabled);
+ mOutputPortSettingsChange = AWAITING_ENABLED;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+ CHECK(enabled);
+ mOutputPortSettingsChange = NONE;
+ break;
+ }
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftMP3(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h
new file mode 100644
index 0000000..70d0682
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 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 SOFT_MP3_H_
+
+#define SOFT_MP3_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tPVMP3DecoderExternal;
+
+namespace android {
+
+struct SoftMP3 : public SimpleSoftOMXComponent {
+ SoftMP3(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftMP3();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+ enum {
+ kNumBuffers = 4,
+ kOutputBufferSize = 4608 * 2
+ };
+
+ tPVMP3DecoderExternal *mConfig;
+ void *mDecoderBuf;
+ int64_t mAnchorTimeUs;
+ int64_t mNumFramesOutput;
+
+ int32_t mNumChannels;
+ int32_t mSamplingRate;
+
+ bool mConfigured;
+
+ bool mSignalledError;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ void initDecoder();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftMP3);
+};
+
+} // namespace android
+
+#endif // SOFT_MP3_H_
+
+
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
index 8b0250a..d443b7c 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
@@ -121,9 +121,11 @@
uint32 temp;
/*
- * Verify that at least the header is complete
+ * Verify that at least the header is complete
+ * Note that SYNC_WORD_LNGTH is in unit of bits, but inputBufferCurrentLength
+ * is in unit of bytes.
*/
- if (inputStream->inputBufferCurrentLength < (SYNC_WORD_LNGTH + 21))
+ if (inputStream->inputBufferCurrentLength < ((SYNC_WORD_LNGTH + 21) >> 3))
{
return NO_ENOUGH_MAIN_DATA_ERROR;
}
diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk
index b769f0d..832b885 100644
--- a/media/libstagefright/codecs/on2/dec/Android.mk
+++ b/media/libstagefright/codecs/on2/dec/Android.mk
@@ -2,15 +2,42 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- VPXDecoder.cpp
+ VPXDecoder.cpp \
LOCAL_MODULE := libstagefright_vpxdec
LOCAL_C_INCLUDES := \
$(TOP)/frameworks/base/media/libstagefright/include \
- $(TOP)/frameworks/base/include/media/stagefright/openmax \
+ frameworks/base/include/media/stagefright/openmax \
$(TOP)/external/libvpx \
$(TOP)/external/libvpx/vpx_codec \
$(TOP)/external/libvpx/vpx_ports
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftVPX.cpp
+
+LOCAL_C_INCLUDES := \
+ $(TOP)/external/libvpx \
+ $(TOP)/external/libvpx/vpx_codec \
+ $(TOP)/external/libvpx/vpx_ports \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_vpxdec \
+ libvpx
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_vpxdec
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
new file mode 100644
index 0000000..e9ce719
--- /dev/null
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2011 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 "SoftVPX"
+#include <utils/Log.h>
+
+#include "SoftVPX.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+#include "vpx/vpx_decoder.h"
+#include "vpx/vpx_codec.h"
+#include "vpx/vp8dx.h"
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+SoftVPX::SoftVPX(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mCtx(NULL),
+ mWidth(320),
+ mHeight(240),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftVPX::~SoftVPX() {
+ vpx_codec_destroy((vpx_codec_ctx_t *)mCtx);
+ delete (vpx_codec_ctx_t *)mCtx;
+ mCtx = NULL;
+}
+
+void SoftVPX::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_VPX);
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX;
+ def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+ def.format.video.pNativeWindow = NULL;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainVideo;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
+ def.format.video.pNativeRender = NULL;
+ def.format.video.nFrameWidth = mWidth;
+ def.format.video.nFrameHeight = mHeight;
+ def.format.video.nStride = def.format.video.nFrameWidth;
+ def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+ def.format.video.nBitrate = 0;
+ def.format.video.xFramerate = 0;
+ def.format.video.bFlagErrorConcealment = OMX_FALSE;
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ def.format.video.pNativeWindow = NULL;
+
+ def.nBufferSize =
+ (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
+
+ addPort(def);
+}
+
+status_t SoftVPX::initDecoder() {
+ mCtx = new vpx_codec_ctx_t;
+ vpx_codec_err_t vpx_err;
+ if ((vpx_err = vpx_codec_dec_init(
+ (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0))) {
+ LOGE("on2 decoder failed to initialize. (%d)", vpx_err);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftVPX::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ if (formatParams->nPortIndex == 0) {
+ formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX;
+ formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+ formatParams->xFramerate = 0;
+ } else {
+ CHECK_EQ(formatParams->nPortIndex, 1u);
+
+ formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ formatParams->xFramerate = 0;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftVPX::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (strncmp((const char *)roleParams->cRole,
+ "video_decoder.vpx",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamVideoPortFormat:
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex != 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+void SoftVPX::onQueueFilled(OMX_U32 portIndex) {
+ if (mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ while (!inQueue.empty() && !outQueue.empty()) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+ return;
+ }
+
+ if (vpx_codec_decode(
+ (vpx_codec_ctx_t *)mCtx,
+ inHeader->pBuffer + inHeader->nOffset,
+ inHeader->nFilledLen,
+ NULL,
+ 0)) {
+ LOGE("on2 decoder failed to decode frame.");
+
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ vpx_codec_iter_t iter = NULL;
+ vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter);
+
+ if (img != NULL) {
+ CHECK_EQ(img->fmt, IMG_FMT_I420);
+
+ int32_t width = img->d_w;
+ int32_t height = img->d_h;
+
+ if (width != mWidth || height != mHeight) {
+ mWidth = width;
+ mHeight = height;
+
+ updatePortDefinitions();
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return;
+ }
+
+ outHeader->nOffset = 0;
+ outHeader->nFilledLen = (width * height * 3) / 2;
+ outHeader->nFlags = 0;
+ outHeader->nTimeStamp = inHeader->nTimeStamp;
+
+ const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y];
+ uint8_t *dst = outHeader->pBuffer;
+ for (size_t i = 0; i < img->d_h; ++i) {
+ memcpy(dst, srcLine, img->d_w);
+
+ srcLine += img->stride[PLANE_Y];
+ dst += img->d_w;
+ }
+
+ srcLine = (const uint8_t *)img->planes[PLANE_U];
+ for (size_t i = 0; i < img->d_h / 2; ++i) {
+ memcpy(dst, srcLine, img->d_w / 2);
+
+ srcLine += img->stride[PLANE_U];
+ dst += img->d_w / 2;
+ }
+
+ srcLine = (const uint8_t *)img->planes[PLANE_V];
+ for (size_t i = 0; i < img->d_h / 2; ++i) {
+ memcpy(dst, srcLine, img->d_w / 2);
+
+ srcLine += img->stride[PLANE_V];
+ dst += img->d_w / 2;
+ }
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ }
+
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+}
+
+void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) {
+}
+
+void SoftVPX::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+ if (portIndex != 1) {
+ return;
+ }
+
+ switch (mOutputPortSettingsChange) {
+ case NONE:
+ break;
+
+ case AWAITING_DISABLED:
+ {
+ CHECK(!enabled);
+ mOutputPortSettingsChange = AWAITING_ENABLED;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+ CHECK(enabled);
+ mOutputPortSettingsChange = NONE;
+ break;
+ }
+ }
+}
+
+void SoftVPX::updatePortDefinitions() {
+ OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def = &editPortInfo(1)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
+ def->format.video.nStride = def->format.video.nFrameWidth;
+ def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+ def->nBufferSize =
+ (def->format.video.nFrameWidth
+ * def->format.video.nFrameHeight * 3) / 2;
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftVPX(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h
new file mode 100644
index 0000000..3e814a2
--- /dev/null
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 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 SOFT_VPX_H_
+
+#define SOFT_VPX_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftVPX : public SimpleSoftOMXComponent {
+ SoftVPX(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftVPX();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+ enum {
+ kNumBuffers = 4
+ };
+
+ void *mCtx;
+
+ int32_t mWidth;
+ int32_t mHeight;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+
+ void updatePortDefinitions();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftVPX);
+};
+
+} // namespace android
+
+#endif // SOFT_VPX_H_
diff --git a/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp b/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp
index 9433178..489e5ad 100644
--- a/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp
+++ b/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp
@@ -205,7 +205,9 @@
vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter);
if (img == NULL) {
- LOGI("on2 decoder did not return a frame.");
+ // The VPX format supports "internal-only" frames that are
+ // referenced by future content but never actually displayed, so
+ // this is a perfectly valid scenario.
*out = new MediaBuffer(0);
return OK;
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk
index 5c768c8..9251229 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.mk
+++ b/media/libstagefright/codecs/vorbis/dec/Android.mk
@@ -6,8 +6,33 @@
LOCAL_C_INCLUDES := \
frameworks/base/media/libstagefright/include \
- external/tremolo
+ external/tremolo \
LOCAL_MODULE := libstagefright_vorbisdec
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftVorbis.cpp
+
+LOCAL_C_INCLUDES := \
+ external/tremolo \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_vorbisdec
+
+LOCAL_SHARED_LIBRARIES := \
+ libvorbisidec libstagefright libstagefright_omx \
+ libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_vorbisdec
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
new file mode 100644
index 0000000..4091111
--- /dev/null
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2011 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 "SoftVorbis"
+#include <utils/Log.h>
+
+#include "SoftVorbis.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+extern "C" {
+ #include <Tremolo/codec_internal.h>
+
+ int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb);
+ int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb);
+ int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb);
+}
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+SoftVorbis::SoftVorbis(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mInputBufferCount(0),
+ mState(NULL),
+ mVi(NULL),
+ mAnchorTimeUs(0),
+ mNumFramesOutput(0),
+ mNumFramesLeftOnPage(-1),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftVorbis::~SoftVorbis() {
+ if (mState != NULL) {
+ vorbis_dsp_clear(mState);
+ delete mState;
+ mState = NULL;
+ }
+
+ if (mVi != NULL) {
+ vorbis_info_clear(mVi);
+ delete mVi;
+ mVi = NULL;
+ }
+}
+
+void SoftVorbis::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 8192;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.audio.cMIMEType =
+ const_cast<char *>(MEDIA_MIMETYPE_AUDIO_VORBIS);
+
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = kMaxNumSamplesPerBuffer * sizeof(int16_t);
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+ addPort(def);
+}
+
+status_t SoftVorbis::initDecoder() {
+ return OK;
+}
+
+OMX_ERRORTYPE SoftVorbis::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamAudioVorbis:
+ {
+ OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams =
+ (OMX_AUDIO_PARAM_VORBISTYPE *)params;
+
+ if (vorbisParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ vorbisParams->nBitRate = 0;
+ vorbisParams->nMinBitRate = 0;
+ vorbisParams->nMaxBitRate = 0;
+ vorbisParams->nAudioBandWidth = 0;
+ vorbisParams->nQuality = 3;
+ vorbisParams->bManaged = OMX_FALSE;
+ vorbisParams->bDownmix = OMX_FALSE;
+
+ if (!isConfigured()) {
+ vorbisParams->nChannels = 1;
+ vorbisParams->nSampleRate = 44100;
+ } else {
+ vorbisParams->nChannels = mVi->channels;
+ vorbisParams->nSampleRate = mVi->rate;
+ vorbisParams->nBitRate = mVi->bitrate_nominal;
+ vorbisParams->nMinBitRate = mVi->bitrate_lower;
+ vorbisParams->nMaxBitRate = mVi->bitrate_upper;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ pcmParams->eNumData = OMX_NumericalDataSigned;
+ pcmParams->eEndian = OMX_EndianBig;
+ pcmParams->bInterleaved = OMX_TRUE;
+ pcmParams->nBitPerSample = 16;
+ pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+ pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+ if (!isConfigured()) {
+ pcmParams->nChannels = 1;
+ pcmParams->nSamplingRate = 44100;
+ } else {
+ pcmParams->nChannels = mVi->channels;
+ pcmParams->nSamplingRate = mVi->rate;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftVorbis::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.vorbis",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioVorbis:
+ {
+ const OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams =
+ (const OMX_AUDIO_PARAM_VORBISTYPE *)params;
+
+ if (vorbisParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+bool SoftVorbis::isConfigured() const {
+ return mInputBufferCount >= 2;
+}
+
+static void makeBitReader(
+ const void *data, size_t size,
+ ogg_buffer *buf, ogg_reference *ref, oggpack_buffer *bits) {
+ buf->data = (uint8_t *)data;
+ buf->size = size;
+ buf->refcount = 1;
+ buf->ptr.owner = NULL;
+
+ ref->buffer = buf;
+ ref->begin = 0;
+ ref->length = size;
+ ref->next = NULL;
+
+ oggpack_readinit(bits, ref);
+}
+
+void SoftVorbis::onQueueFilled(OMX_U32 portIndex) {
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ if (mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ if (portIndex == 0 && mInputBufferCount < 2) {
+ BufferInfo *info = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *header = info->mHeader;
+
+ const uint8_t *data = header->pBuffer + header->nOffset;
+ size_t size = header->nFilledLen;
+
+ ogg_buffer buf;
+ ogg_reference ref;
+ oggpack_buffer bits;
+
+ makeBitReader(
+ (const uint8_t *)data + 7, size - 7,
+ &buf, &ref, &bits);
+
+ if (mInputBufferCount == 0) {
+ CHECK(mVi == NULL);
+ mVi = new vorbis_info;
+ vorbis_info_init(mVi);
+
+ CHECK_EQ(0, _vorbis_unpack_info(mVi, &bits));
+ } else {
+ CHECK_EQ(0, _vorbis_unpack_books(mVi, &bits));
+
+ CHECK(mState == NULL);
+ mState = new vorbis_dsp_state;
+ CHECK_EQ(0, vorbis_dsp_init(mState, mVi));
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ }
+
+ inQueue.erase(inQueue.begin());
+ info->mOwnedByUs = false;
+ notifyEmptyBufferDone(header);
+
+ ++mInputBufferCount;
+
+ return;
+ }
+
+ while (!inQueue.empty() && !outQueue.empty()) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+ return;
+ }
+
+ int32_t numPageSamples;
+ CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples));
+ memcpy(&numPageSamples,
+ inHeader->pBuffer
+ + inHeader->nOffset + inHeader->nFilledLen - 4,
+ sizeof(numPageSamples));
+
+ if (numPageSamples >= 0) {
+ mNumFramesLeftOnPage = numPageSamples;
+ }
+
+ if (inHeader->nOffset == 0) {
+ mAnchorTimeUs = inHeader->nTimeStamp;
+ mNumFramesOutput = 0;
+ }
+
+ inHeader->nFilledLen -= sizeof(numPageSamples);;
+
+ ogg_buffer buf;
+ buf.data = inHeader->pBuffer + inHeader->nOffset;
+ buf.size = inHeader->nFilledLen;
+ buf.refcount = 1;
+ buf.ptr.owner = NULL;
+
+ ogg_reference ref;
+ ref.buffer = &buf;
+ ref.begin = 0;
+ ref.length = buf.size;
+ ref.next = NULL;
+
+ ogg_packet pack;
+ pack.packet = &ref;
+ pack.bytes = ref.length;
+ pack.b_o_s = 0;
+ pack.e_o_s = 0;
+ pack.granulepos = 0;
+ pack.packetno = 0;
+
+ int numFrames = 0;
+
+ int err = vorbis_dsp_synthesis(mState, &pack, 1);
+ if (err != 0) {
+ LOGW("vorbis_dsp_synthesis returned %d", err);
+ } else {
+ numFrames = vorbis_dsp_pcmout(
+ mState, (int16_t *)outHeader->pBuffer,
+ kMaxNumSamplesPerBuffer);
+
+ if (numFrames < 0) {
+ LOGE("vorbis_dsp_pcmout returned %d", numFrames);
+ numFrames = 0;
+ }
+ }
+
+ if (mNumFramesLeftOnPage >= 0) {
+ if (numFrames > mNumFramesLeftOnPage) {
+ LOGV("discarding %d frames at end of page",
+ numFrames - mNumFramesLeftOnPage);
+ numFrames = mNumFramesLeftOnPage;
+ }
+ mNumFramesLeftOnPage -= numFrames;
+ }
+
+ outHeader->nFilledLen = numFrames * sizeof(int16_t) * mVi->channels;
+ outHeader->nOffset = 0;
+ outHeader->nFlags = 0;
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumFramesOutput * 1000000ll) / mVi->rate;
+
+ mNumFramesOutput += numFrames;
+
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+
+ ++mInputBufferCount;
+ }
+}
+
+void SoftVorbis::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0 && mState != NULL) {
+ // Make sure that the next buffer output does not still
+ // depend on fragments from the last one decoded.
+
+ mNumFramesOutput = 0;
+ vorbis_dsp_restart(mState);
+ }
+}
+
+void SoftVorbis::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+ if (portIndex != 1) {
+ return;
+ }
+
+ switch (mOutputPortSettingsChange) {
+ case NONE:
+ break;
+
+ case AWAITING_DISABLED:
+ {
+ CHECK(!enabled);
+ mOutputPortSettingsChange = AWAITING_ENABLED;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+ CHECK(enabled);
+ mOutputPortSettingsChange = NONE;
+ break;
+ }
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftVorbis(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h
new file mode 100644
index 0000000..e252f55
--- /dev/null
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 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 SOFT_VORBIS_H_
+
+#define SOFT_VORBIS_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct vorbis_dsp_state;
+struct vorbis_info;
+
+namespace android {
+
+struct SoftVorbis : public SimpleSoftOMXComponent {
+ SoftVorbis(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftVorbis();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+ enum {
+ kNumBuffers = 4,
+ kMaxNumSamplesPerBuffer = 8192 * 2
+ };
+
+ size_t mInputBufferCount;
+
+ vorbis_dsp_state *mState;
+ vorbis_info *mVi;
+
+ int64_t mAnchorTimeUs;
+ int64_t mNumFramesOutput;
+ int32_t mNumFramesLeftOnPage;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+ bool isConfigured() const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftVorbis);
+};
+
+} // namespace android
+
+#endif // SOFT_VORBIS_H_
+
diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk
index 62ba40f..702a7b4 100644
--- a/media/libstagefright/colorconversion/Android.mk
+++ b/media/libstagefright/colorconversion/Android.mk
@@ -9,6 +9,10 @@
$(TOP)/frameworks/base/include/media/stagefright/openmax \
$(TOP)/hardware/msm7k
+ifneq ($(filter crespo crespo4g,$(TARGET_DEVICE)),)
+LOCAL_CFLAGS += -DTHIS_IS_CRESPO=1
+endif
+
LOCAL_MODULE:= libstagefright_color_conversion
include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index d518c97..4b72a53 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -187,8 +187,7 @@
status_t ColorConverter::convertYUV420Planar(
const BitmapParams &src, const BitmapParams &dst) {
- if (!((dst.mWidth & 3) == 0
- && (src.mCropLeft & 1) == 0
+ if (!((src.mCropLeft & 1) == 0
&& src.cropWidth() == dst.cropWidth()
&& src.cropHeight() == dst.cropHeight())) {
return ERROR_UNSUPPORTED;
@@ -196,8 +195,8 @@
uint8_t *kAdjustedClip = initClip();
- uint32_t *dst_ptr = (uint32_t *)dst.mBits
- + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+ uint16_t *dst_ptr = (uint16_t *)dst.mBits
+ + dst.mCropTop * dst.mWidth + dst.mCropLeft;
const uint8_t *src_y =
(const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
@@ -260,7 +259,11 @@
| ((kAdjustedClip[g2] >> 2) << 5)
| (kAdjustedClip[b2] >> 3);
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ if (x + 1 < src.cropWidth()) {
+ *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
+ } else {
+ dst_ptr[x] = rgb1;
+ }
}
src_y += src.mWidth;
@@ -270,7 +273,7 @@
src_v += src.mWidth / 2;
}
- dst_ptr += dst.mWidth / 2;
+ dst_ptr += dst.mWidth;
}
return OK;
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 70408d7..1828ac8 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -26,14 +26,15 @@
#include <surfaceflinger/Surface.h>
#include <ui/android_native_buffer.h>
#include <ui/GraphicBufferMapper.h>
+#include <gui/ISurfaceTexture.h>
namespace android {
SoftwareRenderer::SoftwareRenderer(
- const sp<Surface> &surface, const sp<MetaData> &meta)
+ const sp<ANativeWindow> &nativeWindow, const sp<MetaData> &meta)
: mConverter(NULL),
mYUVMode(None),
- mSurface(surface) {
+ mNativeWindow(nativeWindow) {
int32_t tmp;
CHECK(meta->findInt32(kKeyColorFormat, &tmp));
mColorFormat = (OMX_COLOR_FORMATTYPE)tmp;
@@ -55,9 +56,23 @@
}
int halFormat;
+ size_t bufWidth, bufHeight;
+
switch (mColorFormat) {
+#ifndef THIS_IS_CRESPO
+ case OMX_COLOR_FormatYUV420Planar:
+ {
+ halFormat = HAL_PIXEL_FORMAT_YV12;
+ bufWidth = (mWidth + 1) & ~1;
+ bufHeight = (mHeight + 1) & ~1;
+ break;
+ }
+#endif
+
default:
halFormat = HAL_PIXEL_FORMAT_RGB_565;
+ bufWidth = mWidth;
+ bufHeight = mHeight;
mConverter = new ColorConverter(
mColorFormat, OMX_COLOR_Format16bitRGB565);
@@ -65,24 +80,26 @@
break;
}
- CHECK(mSurface.get() != NULL);
+ CHECK(mNativeWindow != NULL);
CHECK(mWidth > 0);
CHECK(mHeight > 0);
CHECK(mConverter == NULL || mConverter->isValid());
CHECK_EQ(0,
native_window_set_usage(
- mSurface.get(),
+ mNativeWindow.get(),
GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN
- | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP));
-
- CHECK_EQ(0, native_window_set_buffer_count(mSurface.get(), 2));
+ | GRALLOC_USAGE_HW_TEXTURE
+#ifndef THIS_IS_CRESPO
+ | GRALLOC_USAGE_EXTERNAL_DISP
+#endif
+ ));
// Width must be multiple of 32???
CHECK_EQ(0, native_window_set_buffers_geometry(
- mSurface.get(),
- mCropRight - mCropLeft + 1,
- mCropBottom - mCropTop + 1,
+ mNativeWindow.get(),
+ bufWidth,
+ bufHeight,
halFormat));
uint32_t transform;
@@ -96,8 +113,16 @@
if (transform) {
CHECK_EQ(0, native_window_set_buffers_transform(
- mSurface.get(), transform));
+ mNativeWindow.get(), transform));
}
+
+ android_native_rect_t crop;
+ crop.left = mCropLeft;
+ crop.top = mCropTop;
+ crop.right = mCropRight + 1;
+ crop.bottom = mCropBottom + 1;
+
+ CHECK_EQ(0, native_window_set_crop(mNativeWindow.get(), &crop));
}
SoftwareRenderer::~SoftwareRenderer() {
@@ -105,16 +130,21 @@
mConverter = NULL;
}
+static int ALIGN(int x, int y) {
+ // y must be a power of 2.
+ return (x + y - 1) & ~(y - 1);
+}
+
void SoftwareRenderer::render(
const void *data, size_t size, void *platformPrivate) {
- android_native_buffer_t *buf;
+ ANativeWindowBuffer *buf;
int err;
- if ((err = mSurface->dequeueBuffer(mSurface.get(), &buf)) != 0) {
+ if ((err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf)) != 0) {
LOGW("Surface::dequeueBuffer returned error %d", err);
return;
}
- CHECK_EQ(0, mSurface->lockBuffer(mSurface.get(), buf));
+ CHECK_EQ(0, mNativeWindow->lockBuffer(mNativeWindow.get(), buf));
GraphicBufferMapper &mapper = GraphicBufferMapper::get();
@@ -128,19 +158,45 @@
mConverter->convert(
data,
mWidth, mHeight,
- mCropLeft, mCropTop, mCropRight, mCropBottom,
+ 0, 0, mWidth - 1, mHeight - 1,
dst,
buf->stride, buf->height,
- 0, 0,
- mCropRight - mCropLeft,
- mCropBottom - mCropTop);
+ 0, 0, mWidth - 1, mHeight - 1);
} else {
- TRESPASS();
+ CHECK_EQ(mColorFormat, OMX_COLOR_FormatYUV420Planar);
+
+ const uint8_t *src_y = (const uint8_t *)data;
+ const uint8_t *src_u = (const uint8_t *)data + mWidth * mHeight;
+ const uint8_t *src_v = src_u + (mWidth / 2 * mHeight / 2);
+
+ uint8_t *dst_y = (uint8_t *)dst;
+ size_t dst_y_size = buf->stride * buf->height;
+ size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
+ size_t dst_c_size = dst_c_stride * buf->height / 2;
+ uint8_t *dst_v = dst_y + dst_y_size;
+ uint8_t *dst_u = dst_v + dst_c_size;
+
+ for (int y = 0; y < mHeight; ++y) {
+ memcpy(dst_y, src_y, mWidth);
+
+ src_y += mWidth;
+ dst_y += buf->stride;
+ }
+
+ for (int y = 0; y < (mHeight + 1) / 2; ++y) {
+ memcpy(dst_u, src_u, (mWidth + 1) / 2);
+ memcpy(dst_v, src_v, (mWidth + 1) / 2);
+
+ src_u += mWidth / 2;
+ src_v += mWidth / 2;
+ dst_u += dst_c_stride;
+ dst_v += dst_c_stride;
+ }
}
CHECK_EQ(0, mapper.unlock(buf->handle));
- if ((err = mSurface->queueBuffer(mSurface.get(), buf)) != 0) {
+ if ((err = mNativeWindow->queueBuffer(mNativeWindow.get(), buf)) != 0) {
LOGW("Surface::queueBuffer returned error %d", err);
}
buf = NULL;
diff --git a/media/libstagefright/foundation/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp
index 24c8df8..f07dd4f 100644
--- a/media/libstagefright/foundation/ABitReader.cpp
+++ b/media/libstagefright/foundation/ABitReader.cpp
@@ -90,9 +90,7 @@
}
const uint8_t *ABitReader::data() const {
- CHECK_EQ(mNumBitsLeft % 8, 0u);
-
- return mData - mNumBitsLeft / 8;
+ return mData - (mNumBitsLeft + 7) / 8;
}
} // namespace android
diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
index 30286d8..3b3f786 100644
--- a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
+++ b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 <media/stagefright/foundation/AHierarchicalStateMachine.h>
#include <media/stagefright/foundation/ADebug.h>
diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp
index b7087f8..a5b316d 100644
--- a/media/libstagefright/foundation/ALooper.cpp
+++ b/media/libstagefright/foundation/ALooper.cpp
@@ -33,18 +33,30 @@
struct ALooper::LooperThread : public Thread {
LooperThread(ALooper *looper, bool canCallJava)
: Thread(canCallJava),
- mLooper(looper) {
+ mLooper(looper),
+ mThreadId(NULL) {
+ }
+
+ virtual status_t readyToRun() {
+ mThreadId = androidGetThreadId();
+
+ return Thread::readyToRun();
}
virtual bool threadLoop() {
return mLooper->loop();
}
+ bool isCurrentThread() const {
+ return mThreadId == androidGetThreadId();
+ }
+
protected:
virtual ~LooperThread() {}
private:
ALooper *mLooper;
+ android_thread_id_t mThreadId;
DISALLOW_EVIL_CONSTRUCTORS(LooperThread);
};
@@ -136,7 +148,9 @@
mQueueChangedCondition.signal();
- if (!runningLocally) {
+ if (!runningLocally && !thread->isCurrentThread()) {
+ // If not running locally and this thread _is_ the looper thread,
+ // the loop() function will return and never be called again.
thread->requestExitAndWait();
}
@@ -197,6 +211,11 @@
gLooperRoster.deliverMessage(event.mMessage);
+ // NOTE: It's important to note that at this point our "ALooper" object
+ // may no longer exist (its final reference may have gone away while
+ // delivering the message). We have made sure, however, that loop()
+ // won't be called again.
+
return true;
}
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index 4e07f6f..d5025a1 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -25,6 +25,6 @@
LOCAL_MODULE:= libstagefright_foundation
-LOCAL_PRELINK_MODULE:= false
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 0bed3ca..012d9ad 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -23,7 +23,7 @@
#include "LiveDataSource.h"
#include "include/M3UParser.h"
-#include "include/NuHTTPDataSource.h"
+#include "include/HTTPBase.h"
#include <cutils/properties.h>
#include <media/stagefright/foundation/hexdump.h>
@@ -41,9 +41,14 @@
const int64_t LiveSession::kMaxPlaylistAgeUs = 15000000ll;
-LiveSession::LiveSession()
- : mDataSource(new LiveDataSource),
- mHTTPDataSource(new NuHTTPDataSource),
+LiveSession::LiveSession(uint32_t flags)
+ : mFlags(flags),
+ mDataSource(new LiveDataSource),
+ mHTTPDataSource(
+ HTTPBase::Create(
+ (mFlags & kFlagIncognito)
+ ? HTTPBase::kFlagIncognito
+ : 0)),
mPrevBandwidthIndex(-1),
mLastPlaylistFetchTimeUs(-1),
mSeqNumber(-1),
@@ -62,9 +67,17 @@
return mDataSource;
}
-void LiveSession::connect(const char *url) {
+void LiveSession::connect(
+ const char *url, const KeyedVector<String8, String8> *headers) {
sp<AMessage> msg = new AMessage(kWhatConnect, id());
msg->setString("url", url);
+
+ if (headers != NULL) {
+ msg->setPointer(
+ "headers",
+ new KeyedVector<String8, String8>(*headers));
+ }
+
msg->post();
}
@@ -139,7 +152,21 @@
AString url;
CHECK(msg->findString("url", &url));
- LOGI("onConnect '%s'", url.c_str());
+ KeyedVector<String8, String8> *headers = NULL;
+ if (!msg->findPointer("headers", (void **)&headers)) {
+ mExtraHeaders.clear();
+ } else {
+ mExtraHeaders = *headers;
+
+ delete headers;
+ headers = NULL;
+ }
+
+ if (!(mFlags & kFlagIncognito)) {
+ LOGI("onConnect '%s'", url.c_str());
+ } else {
+ LOGI("onConnect <URL suppressed>");
+ }
mMasterURL = url;
@@ -168,18 +195,6 @@
CHECK_GT(mBandwidthItems.size(), 0u);
mBandwidthItems.sort(SortByBandwidth);
-
- char value[PROPERTY_VALUE_MAX];
- if (property_get("media.httplive.disable-nuplayer", value, NULL)
- && (!strcasecmp(value, "true") || !strcmp(value, "1"))) {
- // The "legacy" player cannot deal with audio format changes,
- // some streams use different audio encoding parameters for
- // their lowest bandwidth stream.
- if (mBandwidthItems.size() > 1) {
- // XXX Remove the lowest bitrate stream for now...
- mBandwidthItems.removeAt(0);
- }
- }
}
postMonitorQueue();
@@ -201,7 +216,8 @@
if (!strncasecmp(url, "file://", 7)) {
source = new FileSource(url + 7);
- } else if (strncasecmp(url, "http://", 7)) {
+ } else if (strncasecmp(url, "http://", 7)
+ && strncasecmp(url, "https://", 8)) {
return ERROR_UNSUPPORTED;
} else {
{
@@ -212,7 +228,8 @@
}
}
- status_t err = mHTTPDataSource->connect(url);
+ status_t err = mHTTPDataSource->connect(
+ url, mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
if (err != OK) {
return err;
@@ -627,7 +644,12 @@
} else {
key = new ABuffer(16);
- sp<NuHTTPDataSource> keySource = new NuHTTPDataSource;
+ sp<HTTPBase> keySource =
+ HTTPBase::Create(
+ (mFlags & kFlagIncognito)
+ ? HTTPBase::kFlagIncognito
+ : 0);
+
status_t err = keySource->connect(keyURI.c_str());
if (err == OK) {
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 95f6741..2eb180a 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -20,8 +20,8 @@
#include "include/M3UParser.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaErrors.h>
namespace android {
@@ -306,6 +306,29 @@
return OK;
}
+// Find the next occurence of the character "what" at or after "offset",
+// but ignore occurences between quotation marks.
+// Return the index of the occurrence or -1 if not found.
+static ssize_t FindNextUnquoted(
+ const AString &line, char what, size_t offset) {
+ CHECK_NE((int)what, (int)'"');
+
+ bool quoted = false;
+ while (offset < line.size()) {
+ char c = line.c_str()[offset];
+
+ if (c == '"') {
+ quoted = !quoted;
+ } else if (c == what && !quoted) {
+ return offset;
+ }
+
+ ++offset;
+ }
+
+ return -1;
+}
+
// static
status_t M3UParser::parseCipherInfo(
const AString &line, sp<AMessage> *meta, const AString &baseURI) {
@@ -318,7 +341,7 @@
size_t offset = colonPos + 1;
while (offset < line.size()) {
- ssize_t end = line.find(",", offset);
+ ssize_t end = FindNextUnquoted(line, ',', offset);
if (end < 0) {
end = line.size();
}
diff --git a/media/libstagefright/include/AACExtractor.h b/media/libstagefright/include/AACExtractor.h
new file mode 100644
index 0000000..8e5657b
--- /dev/null
+++ b/media/libstagefright/include/AACExtractor.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 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 AAC_EXTRACTOR_H_
+
+#define AAC_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+#include <utils/Vector.h>
+
+namespace android {
+
+struct AMessage;
+class String8;
+
+class AACExtractor : public MediaExtractor {
+public:
+ AACExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+
+protected:
+ virtual ~AACExtractor();
+
+private:
+ sp<DataSource> mDataSource;
+ sp<MetaData> mMeta;
+ status_t mInitCheck;
+
+ Vector<uint64_t> mOffsetVector;
+ int64_t mFrameDurationUs;
+
+ AACExtractor(const AACExtractor &);
+ AACExtractor &operator=(const AACExtractor &);
+};
+
+bool SniffAAC(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *);
+
+} // namespace android
+
+#endif // AAC_EXTRACTOR_H_
diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h
index 1cdf36d..4a1c827 100644
--- a/media/libstagefright/include/AMRExtractor.h
+++ b/media/libstagefright/include/AMRExtractor.h
@@ -18,12 +18,14 @@
#define AMR_EXTRACTOR_H_
+#include <utils/Errors.h>
#include <media/stagefright/MediaExtractor.h>
namespace android {
struct AMessage;
class String8;
+#define OFFSET_TABLE_LEN 300
class AMRExtractor : public MediaExtractor {
public:
@@ -42,9 +44,11 @@
sp<DataSource> mDataSource;
sp<MetaData> mMeta;
status_t mInitCheck;
- size_t mFrameSize;
bool mIsWide;
+ off64_t mOffsetTable[OFFSET_TABLE_LEN]; //5 min
+ size_t mOffsetTableLength;
+
AMRExtractor(const AMRExtractor &);
AMRExtractor &operator=(const AMRExtractor &);
};
diff --git a/media/libstagefright/include/AVIExtractor.h b/media/libstagefright/include/AVIExtractor.h
new file mode 100644
index 0000000..375a94d
--- /dev/null
+++ b/media/libstagefright/include/AVIExtractor.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 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 AVI_EXTRACTOR_H_
+
+#define AVI_EXTRACTOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct AVIExtractor : public MediaExtractor {
+ AVIExtractor(const sp<DataSource> &dataSource);
+
+ virtual size_t countTracks();
+
+ virtual sp<MediaSource> getTrack(size_t index);
+
+ virtual sp<MetaData> getTrackMetaData(
+ size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+
+protected:
+ virtual ~AVIExtractor();
+
+private:
+ struct AVISource;
+
+ struct SampleInfo {
+ uint32_t mOffset;
+ bool mIsKey;
+ };
+
+ struct Track {
+ sp<MetaData> mMeta;
+ Vector<SampleInfo> mSamples;
+ uint32_t mRate;
+ uint32_t mScale;
+
+ enum Kind {
+ AUDIO,
+ VIDEO,
+ OTHER
+
+ } mKind;
+
+ size_t mNumSyncSamples;
+ size_t mThumbnailSampleSize;
+ ssize_t mThumbnailSampleIndex;
+ size_t mMaxSampleSize;
+ };
+
+ sp<DataSource> mDataSource;
+ status_t mInitCheck;
+ Vector<Track> mTracks;
+
+ off64_t mMovieOffset;
+ bool mFoundIndex;
+ bool mOffsetsAreAbsolute;
+
+ ssize_t parseChunk(off64_t offset, off64_t size, int depth = 0);
+ status_t parseStreamHeader(off64_t offset, size_t size);
+ status_t parseStreamFormat(off64_t offset, size_t size);
+ status_t parseIndex(off64_t offset, size_t size);
+
+ status_t parseHeaders();
+
+ status_t getSampleInfo(
+ size_t trackIndex, size_t sampleIndex,
+ off64_t *offset, size_t *size, bool *isKey);
+
+ status_t getSampleIndexAtTime(
+ size_t trackIndex,
+ int64_t timeUs, MediaSource::ReadOptions::SeekMode mode,
+ size_t *sampleIndex) const;
+
+ status_t addMPEG4CodecSpecificData(size_t trackIndex);
+
+ static bool IsCorrectChunkType(
+ ssize_t trackIndex, Track::Kind kind, uint32_t chunkType);
+
+ DISALLOW_EVIL_CONSTRUCTORS(AVIExtractor);
+};
+
+class String8;
+struct AMessage;
+
+bool SniffAVI(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *);
+
+} // namespace android
+
+#endif // AVI_EXTRACTOR_H_
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index d5819a4..3c9a121 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -18,7 +18,7 @@
#define AWESOME_PLAYER_H_
-#include "NuHTTPDataSource.h"
+#include "HTTPBase.h"
#include "TimedEventQueue.h"
#include <media/MediaPlayerInterface.h>
@@ -36,16 +36,16 @@
struct MediaExtractor;
struct MediaSource;
struct NuCachedSource2;
+struct ISurfaceTexture;
struct ALooper;
struct ARTSPController;
-struct ARTPSession;
-struct UDPPusher;
class DrmManagerClinet;
class DecryptHandle;
-struct LiveSession;
+class TimedTextPlayer;
+struct WVMExtractor;
struct AwesomeRenderer : public RefBase {
AwesomeRenderer() {}
@@ -84,44 +84,59 @@
bool isPlaying() const;
void setSurface(const sp<Surface> &surface);
+ void setSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture);
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 seekTo(int64_t timeUs);
// This is a mask of MediaExtractor::Flags.
uint32_t flags() const;
- void postAudioEOS();
+ void postAudioEOS(int64_t delayUs = 0ll);
void postAudioSeekComplete();
+ status_t setTimedTextTrackIndex(int32_t index);
+
private:
friend struct AwesomeEvent;
friend struct PreviewPlayer;
enum {
- PLAYING = 1,
- LOOPING = 2,
- FIRST_FRAME = 4,
- PREPARING = 8,
- PREPARED = 16,
- AT_EOS = 32,
- PREPARE_CANCELLED = 64,
- CACHE_UNDERRUN = 128,
- AUDIO_AT_EOS = 256,
- VIDEO_AT_EOS = 512,
- AUTO_LOOPING = 1024,
+ 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 = 2048,
+ PREPARING_CONNECTED = 0x0800,
- AUDIO_RUNNING = 8192,
- AUDIOPLAYER_STARTED = 16384,
+ // 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_STARTED = 0x20000,
};
mutable Mutex mLock;
@@ -133,6 +148,7 @@
wp<MediaPlayerBase> mListener;
sp<Surface> mSurface;
+ sp<ANativeWindow> mNativeWindow;
sp<MediaPlayerBase::AudioSink> mAudioSink;
SystemTimeSource mSystemTimeSource;
@@ -158,7 +174,6 @@
uint32_t mFlags;
uint32_t mExtractorFlags;
- uint32_t mSinceLastDropped;
int64_t mTimeSourceDeltaUs;
int64_t mVideoTimeUs;
@@ -198,24 +213,26 @@
void postVideoEvent_l(int64_t delayUs = -1);
void postBufferingEvent_l();
void postStreamDoneEvent_l(status_t status);
- void postCheckAudioStatusEvent_l();
+ void postCheckAudioStatusEvent_l(int64_t delayUs);
void postVideoLagEvent_l();
status_t play_l();
MediaBuffer *mVideoBuffer;
- sp<NuHTTPDataSource> mConnectingDataSource;
+ sp<HTTPBase> mConnectingDataSource;
sp<NuCachedSource2> mCachedSource;
sp<ALooper> mLooper;
sp<ARTSPController> mRTSPController;
- sp<ARTPSession> mRTPSession;
- sp<UDPPusher> mRTPPusher, mRTCPPusher;
-
- sp<LiveSession> mLiveSession;
+ sp<ARTSPController> mConnectingRTSPController;
DrmManagerClient *mDrmManagerClient;
- DecryptHandle *mDecryptHandle;
+ sp<DecryptHandle> mDecryptHandle;
+
+ int64_t mLastVideoTimeUs;
+ TimedTextPlayer *mTextPlayer;
+
+ sp<WVMExtractor> mWVMExtractor;
status_t setDataSource_l(
const char *uri,
@@ -224,7 +241,6 @@
status_t setDataSource_l(const sp<DataSource> &dataSource);
status_t setDataSource_l(const sp<MediaExtractor> &extractor);
void reset_l();
- void partial_reset_l();
status_t seekTo_l(int64_t timeUs);
status_t pause_l(bool at_eos = false);
void initRenderer_l();
@@ -239,6 +255,8 @@
void setVideoSource(sp<MediaSource> source);
status_t initVideoDecoder(uint32_t flags = 0);
+ void addTextSource(sp<MediaSource> source);
+
void onStreamDone();
void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0);
@@ -266,6 +284,12 @@
void ensureCacheIsFetching_l();
status_t startAudioPlayer_l();
+ void postAudioSeekComplete_l();
+
+ void shutdownVideoDecoder_l();
+ void setNativeWindow_l(const sp<ANativeWindow> &native);
+
+ bool isStreamingHTTP() const;
AwesomePlayer(const AwesomePlayer &);
AwesomePlayer &operator=(const AwesomePlayer &);
diff --git a/media/libstagefright/include/ChromiumHTTPDataSource.h b/media/libstagefright/include/ChromiumHTTPDataSource.h
new file mode 100644
index 0000000..0e2927d
--- /dev/null
+++ b/media/libstagefright/include/ChromiumHTTPDataSource.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2011 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 CHROME_HTTP_DATA_SOURCE_H_
+
+#define CHROME_HTTP_DATA_SOURCE_H_
+
+#include <media/stagefright/foundation/AString.h>
+#include <utils/threads.h>
+
+#include "HTTPBase.h"
+
+namespace android {
+
+struct SfDelegate;
+
+struct ChromiumHTTPDataSource : public HTTPBase {
+ ChromiumHTTPDataSource(uint32_t flags = 0);
+
+ virtual status_t connect(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers = NULL,
+ off64_t offset = 0);
+
+ virtual void disconnect();
+
+ virtual status_t initCheck() const;
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+ virtual status_t getSize(off64_t *size);
+ virtual uint32_t flags();
+
+ virtual bool estimateBandwidth(int32_t *bandwidth_bps);
+
+ virtual sp<DecryptHandle> DrmInitialization();
+
+ virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
+
+ virtual String8 getUri();
+
+ virtual String8 getMIMEType() const;
+
+protected:
+ virtual ~ChromiumHTTPDataSource();
+
+private:
+ friend struct SfDelegate;
+
+ enum State {
+ DISCONNECTED,
+ CONNECTING,
+ CONNECTED,
+ READING,
+ DISCONNECTING
+ };
+
+ struct BandwidthEntry {
+ int64_t mDelayUs;
+ size_t mNumBytes;
+ };
+
+ const uint32_t mFlags;
+
+ mutable Mutex mLock;
+ Condition mCondition;
+
+ State mState;
+
+ SfDelegate *mDelegate;
+
+ AString mURI;
+ KeyedVector<String8, String8> mHeaders;
+
+ off64_t mCurrentOffset;
+
+ // Any connection error or the result of a read operation
+ // (for the lattter this is the number of bytes read, if successful).
+ ssize_t mIOResult;
+
+ int64_t mContentSize;
+
+ String8 mContentType;
+
+ List<BandwidthEntry> mBandwidthHistory;
+ size_t mNumBandwidthHistoryItems;
+ int64_t mTotalTransferTimeUs;
+ size_t mTotalTransferBytes;
+
+ sp<DecryptHandle> mDecryptHandle;
+ DrmManagerClient *mDrmManagerClient;
+
+ void disconnect_l();
+
+ status_t connect_l(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers,
+ off64_t offset);
+
+ static void InitiateRead(
+ ChromiumHTTPDataSource *me, void *data, size_t size);
+
+ void initiateRead(void *data, size_t size);
+
+ void onConnectionEstablished(
+ int64_t contentSize, const char *contentType);
+
+ void onConnectionFailed(status_t err);
+ void onReadCompleted(ssize_t size);
+ void onDisconnectComplete();
+
+ void addBandwidthMeasurement_l(size_t numBytes, int64_t delayUs);
+
+ void clearDRMState_l();
+
+ DISALLOW_EVIL_CONSTRUCTORS(ChromiumHTTPDataSource);
+};
+
+} // namespace android
+
+#endif // CHROME_HTTP_DATA_SOURCE_H_
diff --git a/media/libstagefright/include/DRMExtractor.h b/media/libstagefright/include/DRMExtractor.h
index cafc812..b4e4afb 100644
--- a/media/libstagefright/include/DRMExtractor.h
+++ b/media/libstagefright/include/DRMExtractor.h
@@ -45,7 +45,8 @@
sp<DataSource> mDataSource;
sp<MediaExtractor> mOriginalExtractor;
- DecryptHandle* mDecryptHandle;
+ sp<DecryptHandle> mDecryptHandle;
+ DrmManagerClient* mDrmManagerClient;
DRMExtractor(const DRMExtractor &);
DRMExtractor &operator=(const DRMExtractor &);
diff --git a/media/libstagefright/include/FLACExtractor.h b/media/libstagefright/include/FLACExtractor.h
new file mode 100644
index 0000000..ded91c2
--- /dev/null
+++ b/media/libstagefright/include/FLACExtractor.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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 FLAC_EXTRACTOR_H_
+#define FLAC_EXTRACTOR_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class FLACParser;
+
+class FLACExtractor : public MediaExtractor {
+
+public:
+ // Extractor assumes ownership of source
+ FLACExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+
+protected:
+ virtual ~FLACExtractor();
+
+private:
+ sp<DataSource> mDataSource;
+ sp<FLACParser> mParser;
+ status_t mInitCheck;
+ sp<MetaData> mFileMetadata;
+
+ // There is only one track
+ sp<MetaData> mTrackMetadata;
+
+ status_t init();
+
+ FLACExtractor(const FLACExtractor &);
+ FLACExtractor &operator=(const FLACExtractor &);
+
+};
+
+bool SniffFLAC(const sp<DataSource> &source, String8 *mimeType,
+ float *confidence, sp<AMessage> *);
+
+} // namespace android
+
+#endif // FLAC_EXTRACTOR_H_
diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h
new file mode 100644
index 0000000..6cec390
--- /dev/null
+++ b/media/libstagefright/include/HTTPBase.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 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 HTTP_BASE_H_
+
+#define HTTP_BASE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/DataSource.h>
+
+namespace android {
+
+struct HTTPBase : public DataSource {
+ enum Flags {
+ // Don't log any URLs.
+ kFlagIncognito = 1
+ };
+
+ HTTPBase();
+
+ virtual status_t connect(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers = NULL,
+ off64_t offset = 0) = 0;
+
+ virtual void disconnect() = 0;
+
+ // Returns true if bandwidth could successfully be estimated,
+ // false otherwise.
+ virtual bool estimateBandwidth(int32_t *bandwidth_bps) = 0;
+
+ static sp<HTTPBase> Create(uint32_t flags = 0);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(HTTPBase);
+};
+
+} // namespace android
+
+#endif // HTTP_BASE_H_
diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h
index 545cd0c..09e6a5f 100644
--- a/media/libstagefright/include/HTTPStream.h
+++ b/media/libstagefright/include/HTTPStream.h
@@ -32,7 +32,7 @@
HTTPStream();
~HTTPStream();
- status_t connect(const char *server, int port = 80);
+ status_t connect(const char *server, int port = -1, bool https = false);
status_t disconnect();
status_t send(const char *data, size_t size);
@@ -71,6 +71,9 @@
KeyedVector<AString, AString> mHeaders;
+ void *mSSLContext;
+ void *mSSL;
+
HTTPStream(const HTTPStream &);
HTTPStream &operator=(const HTTPStream &);
};
diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h
index f1188c4..99abe64 100644
--- a/media/libstagefright/include/LiveSession.h
+++ b/media/libstagefright/include/LiveSession.h
@@ -20,20 +20,29 @@
#include <media/stagefright/foundation/AHandler.h>
+#include <utils/String8.h>
+
namespace android {
struct ABuffer;
struct DataSource;
struct LiveDataSource;
struct M3UParser;
-struct NuHTTPDataSource;
+struct HTTPBase;
struct LiveSession : public AHandler {
- LiveSession();
+ enum Flags {
+ // Don't log any URLs.
+ kFlagIncognito = 1,
+ };
+ LiveSession(uint32_t flags = 0);
sp<DataSource> getDataSource();
- void connect(const char *url);
+ void connect(
+ const char *url,
+ const KeyedVector<String8, String8> *headers = NULL);
+
void disconnect();
// Blocks until seek is complete.
@@ -67,11 +76,15 @@
unsigned long mBandwidth;
};
+ uint32_t mFlags;
+
sp<LiveDataSource> mDataSource;
- sp<NuHTTPDataSource> mHTTPDataSource;
+ sp<HTTPBase> mHTTPDataSource;
AString mMasterURL;
+ KeyedVector<String8, String8> mExtraHeaders;
+
Vector<BandwidthItem> mBandwidthItems;
KeyedVector<AString, sp<ABuffer> > mAESKeyForURI;
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index 728980e..cf1146b 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -18,6 +18,7 @@
#define MP3_EXTRACTOR_H_
+#include <utils/Errors.h>
#include <media/stagefright/MediaExtractor.h>
namespace android {
@@ -41,7 +42,7 @@
static bool get_mp3_frame_size(
uint32_t header, size_t *frame_size,
int *out_sampling_rate = NULL, int *out_channels = NULL,
- int *out_bitrate = NULL);
+ int *out_bitrate = NULL, int *out_num_samples = NULL);
private:
status_t mInitCheck;
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index efe7496..fe74a42 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 MPEG2_TS_EXTRACTOR_H_
#define MPEG2_TS_EXTRACTOR_H_
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 04e8a6a..3bd4c7e 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -57,7 +57,7 @@
};
sp<DataSource> mDataSource;
- bool mHaveMetadata;
+ status_t mInitCheck;
bool mHasVideo;
Track *mFirstTrack, *mLastTrack;
@@ -90,6 +90,8 @@
status_t parseTrackHeader(off64_t data_offset, off64_t data_size);
+ Track *findTrackByMimePrefix(const char *mimePrefix);
+
MPEG4Extractor(const MPEG4Extractor &);
MPEG4Extractor &operator=(const MPEG4Extractor &);
};
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 28840be..ed3e265 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -37,9 +37,12 @@
virtual status_t getSize(off64_t *size);
virtual uint32_t flags();
- virtual DecryptHandle* DrmInitialization(DrmManagerClient *client);
- virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client);
+ virtual sp<DecryptHandle> DrmInitialization();
+ virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
virtual String8 getUri();
+
+ virtual String8 getMIMEType() const;
+
////////////////////////////////////////////////////////////////////////////
size_t cachedSize();
@@ -93,7 +96,9 @@
status_t seekInternal_l(off64_t offset);
size_t approxDataRemaining_l(status_t *finalStatus);
- void restartPrefetcherIfNecessary_l(bool ignoreLowWaterThreshold = false);
+
+ void restartPrefetcherIfNecessary_l(
+ bool ignoreLowWaterThreshold = false, bool force = false);
DISALLOW_EVIL_CONSTRUCTORS(NuCachedSource2);
};
diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h
index c8e93be..2ab1f19 100644
--- a/media/libstagefright/include/NuHTTPDataSource.h
+++ b/media/libstagefright/include/NuHTTPDataSource.h
@@ -1,25 +1,41 @@
+/*
+ * Copyright (C) 2010 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 NU_HTTP_DATA_SOURCE_H_
#define NU_HTTP_DATA_SOURCE_H_
-#include <media/stagefright/DataSource.h>
#include <utils/List.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include "HTTPStream.h"
+#include "include/HTTPBase.h"
namespace android {
-struct NuHTTPDataSource : public DataSource {
- NuHTTPDataSource();
+struct NuHTTPDataSource : public HTTPBase {
+ NuHTTPDataSource(uint32_t flags = 0);
- status_t connect(
+ virtual status_t connect(
const char *uri,
const KeyedVector<String8, String8> *headers = NULL,
off64_t offset = 0);
- void disconnect();
+ virtual void disconnect();
virtual status_t initCheck() const;
@@ -29,12 +45,14 @@
// Returns true if bandwidth could successfully be estimated,
// false otherwise.
- bool estimateBandwidth(int32_t *bandwidth_bps);
+ virtual bool estimateBandwidth(int32_t *bandwidth_bps);
- virtual DecryptHandle* DrmInitialization(DrmManagerClient *client);
- virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client);
+ virtual sp<DecryptHandle> DrmInitialization();
+ virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
virtual String8 getUri();
+ virtual String8 getMIMEType() const;
+
protected:
virtual ~NuHTTPDataSource();
@@ -52,11 +70,14 @@
Mutex mLock;
+ uint32_t mFlags;
+
State mState;
String8 mHost;
unsigned mPort;
String8 mPath;
+ bool mHTTPS;
String8 mHeaders;
String8 mUri;
@@ -66,6 +87,8 @@
bool mContentLengthValid;
bool mHasChunkedTransferEncoding;
+ String8 mContentType;
+
// The number of data bytes in the current chunk before any subsequent
// chunk header (or -1 if no more chunks).
ssize_t mChunkDataBytesLeft;
@@ -75,7 +98,7 @@
int64_t mTotalTransferTimeUs;
size_t mTotalTransferBytes;
- DecryptHandle *mDecryptHandle;
+ sp<DecryptHandle> mDecryptHandle;
DrmManagerClient *mDrmManagerClient;
status_t connect(
@@ -83,6 +106,7 @@
status_t connect(
const char *host, unsigned port, const char *path,
+ bool https,
const String8 &headers,
off64_t offset);
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 5fed98a..ec3e5fa 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -62,6 +62,9 @@
virtual status_t enableGraphicBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable);
+ virtual status_t getGraphicBufferUsage(
+ node_id node, OMX_U32 port_index, OMX_U32* usage);
+
virtual status_t storeMetaDataInBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index 86c102c..ca2578f 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -50,6 +50,9 @@
status_t setConfig(OMX_INDEXTYPE index, const void *params, size_t size);
status_t enableGraphicBuffers(OMX_U32 portIndex, OMX_BOOL enable);
+
+ status_t getGraphicBufferUsage(OMX_U32 portIndex, OMX_U32* usage);
+
status_t storeMetaDataInBuffers(OMX_U32 portIndex, OMX_BOOL enable);
status_t useBuffer(
diff --git a/media/libstagefright/include/OggExtractor.h b/media/libstagefright/include/OggExtractor.h
index 1eda025..e97c8cd 100644
--- a/media/libstagefright/include/OggExtractor.h
+++ b/media/libstagefright/include/OggExtractor.h
@@ -18,6 +18,7 @@
#define OGG_EXTRACTOR_H_
+#include <utils/Errors.h>
#include <media/stagefright/MediaExtractor.h>
namespace android {
@@ -57,6 +58,9 @@
const sp<DataSource> &source, String8 *mimeType, float *confidence,
sp<AMessage> *);
+void parseVorbisComment(
+ const sp<MetaData> &fileMeta, const char *comment, size_t commentLength);
+
} // namespace android
#endif // OGG_EXTRACTOR_H_
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index c5e8136..f44e0a2 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -46,6 +46,9 @@
status_t setTimeToSampleParams(off64_t data_offset, size_t data_size);
+ status_t setCompositionTimeToSampleParams(
+ off64_t data_offset, size_t data_size);
+
status_t setSyncSampleParams(off64_t data_offset, size_t data_size);
////////////////////////////////////////////////////////////////////////////
@@ -60,7 +63,7 @@
uint32_t sampleIndex,
off64_t *offset,
size_t *size,
- uint32_t *decodingTime,
+ uint32_t *compositionTime,
bool *isSyncSample = NULL);
enum {
@@ -104,6 +107,15 @@
uint32_t mTimeToSampleCount;
uint32_t *mTimeToSample;
+ struct SampleTimeEntry {
+ uint32_t mSampleIndex;
+ uint32_t mCompositionTime;
+ };
+ SampleTimeEntry *mSampleTimeEntries;
+
+ uint32_t *mCompositionTimeDeltaEntries;
+ size_t mNumCompositionTimeDeltaEntries;
+
off64_t mSyncSampleOffset;
uint32_t mNumSyncSamples;
uint32_t *mSyncSamples;
@@ -122,6 +134,12 @@
status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size);
+ uint32_t getCompositionTimeOffset(uint32_t sampleIndex) const;
+
+ static int CompareIncreasingTime(const void *, const void *);
+
+ void buildSampleEntriesTable();
+
SampleTable(const SampleTable &);
SampleTable &operator=(const SampleTable &);
};
diff --git a/media/libstagefright/include/SimpleSoftOMXComponent.h b/media/libstagefright/include/SimpleSoftOMXComponent.h
new file mode 100644
index 0000000..2a29a7d
--- /dev/null
+++ b/media/libstagefright/include/SimpleSoftOMXComponent.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2011 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_SOFT_OMX_COMPONENT_H_
+
+#define SIMPLE_SOFT_OMX_COMPONENT_H_
+
+#include "SoftOMXComponent.h"
+
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct ALooper;
+
+struct SimpleSoftOMXComponent : public SoftOMXComponent {
+ SimpleSoftOMXComponent(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+ virtual ~SimpleSoftOMXComponent();
+
+ void onMessageReceived(const sp<AMessage> &msg);
+
+protected:
+ struct BufferInfo {
+ OMX_BUFFERHEADERTYPE *mHeader;
+ bool mOwnedByUs;
+ };
+
+ struct PortInfo {
+ OMX_PARAM_PORTDEFINITIONTYPE mDef;
+ Vector<BufferInfo> mBuffers;
+ List<BufferInfo *> mQueue;
+
+ enum {
+ NONE,
+ DISABLING,
+ ENABLING,
+ } mTransition;
+ };
+
+ void addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def);
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ List<BufferInfo *> &getPortQueue(OMX_U32 portIndex);
+
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+ PortInfo *editPortInfo(OMX_U32 portIndex);
+
+private:
+ enum {
+ kWhatSendCommand,
+ kWhatEmptyThisBuffer,
+ kWhatFillThisBuffer,
+ };
+
+ Mutex mLock;
+
+ sp<ALooper> mLooper;
+ sp<AHandlerReflector<SimpleSoftOMXComponent> > mHandler;
+
+ OMX_STATETYPE mState;
+ OMX_STATETYPE mTargetState;
+
+ Vector<PortInfo> mPorts;
+
+ bool isSetParameterAllowed(
+ OMX_INDEXTYPE index, const OMX_PTR params) const;
+
+ virtual OMX_ERRORTYPE sendCommand(
+ OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data);
+
+ virtual OMX_ERRORTYPE getParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE setParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual OMX_ERRORTYPE useBuffer(
+ OMX_BUFFERHEADERTYPE **buffer,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size,
+ OMX_U8 *ptr);
+
+ virtual OMX_ERRORTYPE allocateBuffer(
+ OMX_BUFFERHEADERTYPE **buffer,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size);
+
+ virtual OMX_ERRORTYPE freeBuffer(
+ OMX_U32 portIndex,
+ OMX_BUFFERHEADERTYPE *buffer);
+
+ virtual OMX_ERRORTYPE emptyThisBuffer(
+ OMX_BUFFERHEADERTYPE *buffer);
+
+ virtual OMX_ERRORTYPE fillThisBuffer(
+ OMX_BUFFERHEADERTYPE *buffer);
+
+ virtual OMX_ERRORTYPE getState(OMX_STATETYPE *state);
+
+ void onSendCommand(OMX_COMMANDTYPE cmd, OMX_U32 param);
+ void onChangeState(OMX_STATETYPE state);
+ void onPortEnable(OMX_U32 portIndex, bool enable);
+ void onPortFlush(OMX_U32 portIndex, bool sendFlushComplete);
+
+ void checkTransitions();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SimpleSoftOMXComponent);
+};
+
+} // namespace android
+
+#endif // SIMPLE_SOFT_OMX_COMPONENT_H_
diff --git a/media/libstagefright/include/SoftOMXComponent.h b/media/libstagefright/include/SoftOMXComponent.h
new file mode 100644
index 0000000..053bc22
--- /dev/null
+++ b/media/libstagefright/include/SoftOMXComponent.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2011 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 SOFT_OMX_COMPONENT_H_
+
+#define SOFT_OMX_COMPONENT_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/RefBase.h>
+
+#include <OMX_Component.h>
+
+namespace android {
+
+struct SoftOMXComponent : public RefBase {
+ SoftOMXComponent(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+ virtual OMX_ERRORTYPE initCheck() const;
+
+ void setLibHandle(void *libHandle);
+ void *libHandle() const;
+
+protected:
+ virtual ~SoftOMXComponent();
+
+ const char *name() const;
+
+ void notify(
+ OMX_EVENTTYPE event,
+ OMX_U32 data1, OMX_U32 data2, OMX_PTR data);
+
+ void notifyEmptyBufferDone(OMX_BUFFERHEADERTYPE *header);
+ void notifyFillBufferDone(OMX_BUFFERHEADERTYPE *header);
+
+ virtual OMX_ERRORTYPE sendCommand(
+ OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data);
+
+ virtual OMX_ERRORTYPE getParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE setParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual OMX_ERRORTYPE getConfig(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE setConfig(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual OMX_ERRORTYPE getExtensionIndex(
+ const char *name, OMX_INDEXTYPE *index);
+
+ virtual OMX_ERRORTYPE useBuffer(
+ OMX_BUFFERHEADERTYPE **buffer,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size,
+ OMX_U8 *ptr);
+
+ virtual OMX_ERRORTYPE allocateBuffer(
+ OMX_BUFFERHEADERTYPE **buffer,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size);
+
+ virtual OMX_ERRORTYPE freeBuffer(
+ OMX_U32 portIndex,
+ OMX_BUFFERHEADERTYPE *buffer);
+
+ virtual OMX_ERRORTYPE emptyThisBuffer(
+ OMX_BUFFERHEADERTYPE *buffer);
+
+ virtual OMX_ERRORTYPE fillThisBuffer(
+ OMX_BUFFERHEADERTYPE *buffer);
+
+ virtual OMX_ERRORTYPE getState(OMX_STATETYPE *state);
+
+private:
+ AString mName;
+ const OMX_CALLBACKTYPE *mCallbacks;
+ OMX_COMPONENTTYPE *mComponent;
+
+ void *mLibHandle;
+
+ static OMX_ERRORTYPE SendCommandWrapper(
+ OMX_HANDLETYPE component,
+ OMX_COMMANDTYPE cmd,
+ OMX_U32 param,
+ OMX_PTR data);
+
+ static OMX_ERRORTYPE GetParameterWrapper(
+ OMX_HANDLETYPE component,
+ OMX_INDEXTYPE index,
+ OMX_PTR params);
+
+ static OMX_ERRORTYPE SetParameterWrapper(
+ OMX_HANDLETYPE component,
+ OMX_INDEXTYPE index,
+ OMX_PTR params);
+
+ static OMX_ERRORTYPE GetConfigWrapper(
+ OMX_HANDLETYPE component,
+ OMX_INDEXTYPE index,
+ OMX_PTR params);
+
+ static OMX_ERRORTYPE SetConfigWrapper(
+ OMX_HANDLETYPE component,
+ OMX_INDEXTYPE index,
+ OMX_PTR params);
+
+ static OMX_ERRORTYPE GetExtensionIndexWrapper(
+ OMX_HANDLETYPE component,
+ OMX_STRING name,
+ OMX_INDEXTYPE *index);
+
+ static OMX_ERRORTYPE UseBufferWrapper(
+ OMX_HANDLETYPE component,
+ OMX_BUFFERHEADERTYPE **buffer,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size,
+ OMX_U8 *ptr);
+
+ static OMX_ERRORTYPE AllocateBufferWrapper(
+ OMX_HANDLETYPE component,
+ OMX_BUFFERHEADERTYPE **buffer,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size);
+
+ static OMX_ERRORTYPE FreeBufferWrapper(
+ OMX_HANDLETYPE component,
+ OMX_U32 portIndex,
+ OMX_BUFFERHEADERTYPE *buffer);
+
+ static OMX_ERRORTYPE EmptyThisBufferWrapper(
+ OMX_HANDLETYPE component,
+ OMX_BUFFERHEADERTYPE *buffer);
+
+ static OMX_ERRORTYPE FillThisBufferWrapper(
+ OMX_HANDLETYPE component,
+ OMX_BUFFERHEADERTYPE *buffer);
+
+ static OMX_ERRORTYPE GetStateWrapper(
+ OMX_HANDLETYPE component,
+ OMX_STATETYPE *state);
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftOMXComponent);
+};
+
+} // namespace android
+
+#endif // SOFT_OMX_COMPONENT_H_
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 90d3fe1..78037b9 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -20,16 +20,16 @@
#include <media/stagefright/ColorConverter.h>
#include <utils/RefBase.h>
+#include <ui/android_native_buffer.h>
namespace android {
struct MetaData;
-class Surface;
class SoftwareRenderer {
public:
SoftwareRenderer(
- const sp<Surface> &surface, const sp<MetaData> &meta);
+ const sp<ANativeWindow> &nativeWindow, const sp<MetaData> &meta);
~SoftwareRenderer();
@@ -44,7 +44,7 @@
OMX_COLOR_FORMATTYPE mColorFormat;
ColorConverter *mConverter;
YUVMode mYUVMode;
- sp<Surface> mSurface;
+ sp<ANativeWindow> mNativeWindow;
int32_t mWidth, mHeight;
int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index 07b1ec8..b02ed0e 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -32,7 +32,10 @@
StagefrightMetadataRetriever();
virtual ~StagefrightMetadataRetriever();
- virtual status_t setDataSource(const char *url);
+ virtual status_t setDataSource(
+ const char *url,
+ const KeyedVector<String8, String8> *headers);
+
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual VideoFrame *getFrameAtTime(int64_t timeUs, int option);
diff --git a/media/libstagefright/include/TimedTextPlayer.h b/media/libstagefright/include/TimedTextPlayer.h
new file mode 100644
index 0000000..ac41b4f
--- /dev/null
+++ b/media/libstagefright/include/TimedTextPlayer.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 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 <media/MediaPlayerInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include "include/TimedEventQueue.h"
+
+namespace android {
+
+class MediaSource;
+class AwesomePlayer;
+class MediaBuffer;
+
+class TimedTextPlayer {
+public:
+ TimedTextPlayer(AwesomePlayer *observer,
+ const wp<MediaPlayerBase> &listener,
+ TimedEventQueue *queue);
+
+ virtual ~TimedTextPlayer();
+
+ // index: the index of the text track which will
+ // be turned on
+ status_t start(uint8_t index);
+
+ void pause();
+
+ void resume();
+
+ status_t seekTo(int64_t time_us);
+
+ void addTextSource(sp<MediaSource> source);
+
+ status_t setTimedTextTrackIndex(int32_t index);
+
+private:
+ Mutex mLock;
+
+ sp<MediaSource> mSource;
+
+ bool mSeeking;
+ int64_t mSeekTimeUs;
+
+ bool mStarted;
+
+ sp<TimedEventQueue::Event> mTextEvent;
+ bool mTextEventPending;
+
+ TimedEventQueue *mQueue;
+
+ wp<MediaPlayerBase> mListener;
+ AwesomePlayer *mObserver;
+
+ MediaBuffer *mTextBuffer;
+ Parcel mData;
+
+ Vector<sp<MediaSource> > mTextTrackVector;
+
+ void reset();
+
+ void onTextEvent();
+ void postTextEvent(int64_t delayUs = -1);
+ void cancelTextEvent();
+
+ void notifyListener(
+ int msg, const void *data = NULL, size_t size = 0);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer);
+};
+
+} // namespace android
+
+#endif // TIMEDTEXT_PLAYER_H_
diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
index 9de197f..ce1f33a 100644
--- a/media/libstagefright/include/WAVExtractor.h
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -18,6 +18,7 @@
#define WAV_EXTRACTOR_H_
+#include <utils/Errors.h>
#include <media/stagefright/MediaExtractor.h>
namespace android {
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
index 0da45a8..62e5aa5 100644
--- a/media/libstagefright/include/WVMExtractor.h
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -19,6 +19,7 @@
#define WVM_EXTRACTOR_H_
#include <media/stagefright/MediaExtractor.h>
+#include <utils/Errors.h>
namespace android {
@@ -33,12 +34,31 @@
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
+ // Return the amount of data cached from the current
+ // playback positiion (in us).
+ // While more data is still being fetched *finalStatus == OK,
+ // Once fetching is completed (no more data available), *finalStatus != OK
+ // If fetching completed normally (i.e. reached EOS instead of IO error)
+ // *finalStatus == ERROR_END_OF_STREAM
+ int64_t getCachedDurationUs(status_t *finalStatus);
+
+ // Set to use adaptive streaming mode by the WV component.
+ // If adaptive == true, adaptive streaming mode will be used.
+ // Default mode is non-adaptive streaming mode.
+ // Should set to use adaptive streaming mode only if widevine:// protocol
+ // is used.
+ void setAdaptiveStreamingMode(bool adaptive);
+
+ // Retrieve the adaptive streaming mode used by the WV component.
+ bool getAdaptiveStreamingMode() const;
+
protected:
virtual ~WVMExtractor();
private:
sp<DataSource> mDataSource;
sp<MediaExtractor> mImpl;
+ bool mUseAdaptiveStreaming;
WVMExtractor(const WVMExtractor &);
WVMExtractor &operator=(const WVMExtractor &);
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
index 3aeb07f..afff824 100644
--- a/media/libstagefright/include/avc_utils.h
+++ b/media/libstagefright/include/avc_utils.h
@@ -19,6 +19,7 @@
#define AVC_UTILS_H_
#include <media/stagefright/foundation/ABuffer.h>
+#include <utils/Errors.h>
namespace android {
@@ -52,6 +53,10 @@
const char *AVCProfileToString(uint8_t profile);
+sp<MetaData> MakeAACCodecSpecificData(
+ unsigned profile, unsigned sampling_freq_index,
+ unsigned channel_configuration);
+
} // namespace android
#endif // AVC_UTILS_H_
diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk
index 33c9d97..1f1c68b 100644
--- a/media/libstagefright/matroska/Android.mk
+++ b/media/libstagefright/matroska/Android.mk
@@ -2,11 +2,11 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- MatroskaExtractor.cpp \
- mkvparser.cpp \
+ MatroskaExtractor.cpp
LOCAL_C_INCLUDES:= \
- $(JNI_H_INCLUDE) \
+ $(JNI_H_INCLUDE) \
+ $(TOP)/external/libvpx/mkvparser \
$(TOP)/frameworks/base/include/media/stagefright/openmax \
LOCAL_CFLAGS += -Wno-multichar
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 733de92b..e1b9991 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -60,7 +60,10 @@
virtual int Length(long long* total, long long* available) {
off64_t size;
if (mSource->getSize(&size) != OK) {
- return -1;
+ *total = -1;
+ *available = (long long)((1ull << 63) - 1);
+
+ return 0;
}
if (total) {
@@ -84,7 +87,7 @@
////////////////////////////////////////////////////////////////////////////////
struct BlockIterator {
- BlockIterator(mkvparser::Segment *segment, unsigned long trackNum);
+ BlockIterator(MatroskaExtractor *extractor, unsigned long trackNum);
bool eos() const;
@@ -96,11 +99,14 @@
int64_t blockTimeUs() const;
private:
- mkvparser::Segment *mSegment;
+ MatroskaExtractor *mExtractor;
unsigned long mTrackNum;
- mkvparser::Cluster *mCluster;
+ const mkvparser::Cluster *mCluster;
const mkvparser::BlockEntry *mBlockEntry;
+ long mBlockEntryIndex;
+
+ void advance_l();
BlockIterator(const BlockIterator &);
BlockIterator &operator=(const BlockIterator &);
@@ -150,7 +156,7 @@
: mExtractor(extractor),
mTrackIndex(index),
mType(OTHER),
- mBlockIter(mExtractor->mSegment,
+ mBlockIter(mExtractor.get(),
mExtractor->mTracks.itemAt(index).mTrackNum),
mNALSizeLen(0) {
sp<MetaData> meta = mExtractor->mTracks.itemAt(index).mMeta;
@@ -199,11 +205,12 @@
////////////////////////////////////////////////////////////////////////////////
BlockIterator::BlockIterator(
- mkvparser::Segment *segment, unsigned long trackNum)
- : mSegment(segment),
+ MatroskaExtractor *extractor, unsigned long trackNum)
+ : mExtractor(extractor),
mTrackNum(trackNum),
mCluster(NULL),
- mBlockEntry(NULL) {
+ mBlockEntry(NULL),
+ mBlockEntryIndex(0) {
reset();
}
@@ -212,45 +219,100 @@
}
void BlockIterator::advance() {
- while (!eos()) {
- if (mBlockEntry != NULL) {
- mBlockEntry = mCluster->GetNext(mBlockEntry);
- } else if (mCluster != NULL) {
- mCluster = mSegment->GetNext(mCluster);
+ Mutex::Autolock autoLock(mExtractor->mLock);
+ advance_l();
+}
- if (eos()) {
+void BlockIterator::advance_l() {
+ for (;;) {
+ long res = mCluster->GetEntry(mBlockEntryIndex, mBlockEntry);
+ LOGV("GetEntry returned %ld", res);
+
+ long long pos;
+ long len;
+ if (res < 0) {
+ // Need to parse this cluster some more
+
+ CHECK_EQ(res, mkvparser::E_BUFFER_NOT_FULL);
+
+ res = mCluster->Parse(pos, len);
+ LOGV("Parse returned %ld", res);
+
+ if (res < 0) {
+ // I/O error
+
+ LOGE("Cluster::Parse returned result %ld", res);
+
+ mCluster = NULL;
break;
}
- mBlockEntry = mCluster->GetFirst();
+ continue;
+ } else if (res == 0) {
+ // We're done with this cluster
+
+ const mkvparser::Cluster *nextCluster;
+ res = mExtractor->mSegment->ParseNext(
+ mCluster, nextCluster, pos, len);
+ LOGV("ParseNext returned %ld", res);
+
+ if (res > 0) {
+ // EOF
+
+ mCluster = NULL;
+ break;
+ }
+
+ CHECK_EQ(res, 0);
+ CHECK(nextCluster != NULL);
+ CHECK(!nextCluster->EOS());
+
+ mCluster = nextCluster;
+
+ res = mCluster->Parse(pos, len);
+ LOGV("Parse (2) returned %ld", res);
+ CHECK_GE(res, 0);
+
+ mBlockEntryIndex = 0;
+ continue;
}
- if (mBlockEntry != NULL
- && mBlockEntry->GetBlock()->GetTrackNumber() == mTrackNum) {
+ CHECK(mBlockEntry != NULL);
+ CHECK(mBlockEntry->GetBlock() != NULL);
+ ++mBlockEntryIndex;
+
+ if (mBlockEntry->GetBlock()->GetTrackNumber() == mTrackNum) {
break;
}
}
}
void BlockIterator::reset() {
- mCluster = mSegment->GetFirst();
- mBlockEntry = mCluster->GetFirst();
+ Mutex::Autolock autoLock(mExtractor->mLock);
- while (!eos() && block()->GetTrackNumber() != mTrackNum) {
- advance();
- }
+ mCluster = mExtractor->mSegment->GetFirst();
+ mBlockEntry = NULL;
+ mBlockEntryIndex = 0;
+
+ do {
+ advance_l();
+ } while (!eos() && block()->GetTrackNumber() != mTrackNum);
}
void BlockIterator::seek(int64_t seekTimeUs) {
- mCluster = mSegment->FindCluster(seekTimeUs * 1000ll);
- mBlockEntry = mCluster != NULL ? mCluster->GetFirst() : NULL;
+ Mutex::Autolock autoLock(mExtractor->mLock);
- while (!eos() && block()->GetTrackNumber() != mTrackNum) {
- advance();
+ mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll);
+ mBlockEntry = NULL;
+ mBlockEntryIndex = 0;
+
+ do {
+ advance_l();
}
+ while (!eos() && block()->GetTrackNumber() != mTrackNum);
while (!eos() && !mBlockEntry->GetBlock()->IsKey()) {
- advance();
+ advance_l();
}
}
@@ -291,16 +353,6 @@
}
}
-#define BAIL(err) \
- do { \
- if (bigbuf) { \
- bigbuf->release(); \
- bigbuf = NULL; \
- } \
- \
- return err; \
- } while (0)
-
status_t MatroskaSource::readBlock() {
CHECK(mPendingFrames.empty());
@@ -310,181 +362,39 @@
const mkvparser::Block *block = mBlockIter.block();
- size_t size = block->GetSize();
int64_t timeUs = mBlockIter.blockTimeUs();
- int32_t isSync = block->IsKey();
- MediaBuffer *bigbuf = new MediaBuffer(size);
+ for (int i = 0; i < block->GetFrameCount(); ++i) {
+ const mkvparser::Block::Frame &frame = block->GetFrame(i);
- long res = block->Read(
- mExtractor->mReader, (unsigned char *)bigbuf->data());
+ MediaBuffer *mbuf = new MediaBuffer(frame.len);
+ mbuf->meta_data()->setInt64(kKeyTime, timeUs);
+ mbuf->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey());
- if (res != 0) {
- bigbuf->release();
- bigbuf = NULL;
+ long n = frame.Read(mExtractor->mReader, (unsigned char *)mbuf->data());
+ if (n != 0) {
+ mPendingFrames.clear();
- return ERROR_END_OF_STREAM;
+ mBlockIter.advance();
+ return ERROR_IO;
+ }
+
+ mPendingFrames.push_back(mbuf);
}
mBlockIter.advance();
- bigbuf->meta_data()->setInt64(kKeyTime, timeUs);
- bigbuf->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
-
- unsigned lacing = (block->Flags() >> 1) & 3;
-
- if (lacing == 0) {
- mPendingFrames.push_back(bigbuf);
- return OK;
- }
-
- LOGV("lacing = %u, size = %d", lacing, size);
-
- const uint8_t *data = (const uint8_t *)bigbuf->data();
- // hexdump(data, size);
-
- if (size == 0) {
- BAIL(ERROR_MALFORMED);
- }
-
- unsigned numFrames = (unsigned)data[0] + 1;
- ++data;
- --size;
-
- Vector<uint64_t> frameSizes;
-
- switch (lacing) {
- case 1: // Xiph
- {
- for (size_t i = 0; i < numFrames - 1; ++i) {
- size_t frameSize = 0;
- uint8_t byte;
- do {
- if (size == 0) {
- BAIL(ERROR_MALFORMED);
- }
- byte = data[0];
- ++data;
- --size;
-
- frameSize += byte;
- } while (byte == 0xff);
-
- frameSizes.push(frameSize);
- }
-
- break;
- }
-
- case 2: // fixed-size
- {
- if ((size % numFrames) != 0) {
- BAIL(ERROR_MALFORMED);
- }
-
- size_t frameSize = size / numFrames;
- for (size_t i = 0; i < numFrames - 1; ++i) {
- frameSizes.push(frameSize);
- }
-
- break;
- }
-
- case 3: // EBML
- {
- uint64_t lastFrameSize = 0;
- for (size_t i = 0; i < numFrames - 1; ++i) {
- uint8_t byte;
-
- if (size == 0) {
- BAIL(ERROR_MALFORMED);
- }
- byte = data[0];
- ++data;
- --size;
-
- size_t numLeadingZeroes = clz(byte);
-
- uint64_t frameSize = byte & ~(0x80 >> numLeadingZeroes);
- for (size_t j = 0; j < numLeadingZeroes; ++j) {
- if (size == 0) {
- BAIL(ERROR_MALFORMED);
- }
-
- frameSize = frameSize << 8;
- frameSize |= data[0];
- ++data;
- --size;
- }
-
- if (i == 0) {
- frameSizes.push(frameSize);
- } else {
- size_t shift =
- 7 - numLeadingZeroes + 8 * numLeadingZeroes;
-
- int64_t delta =
- (int64_t)frameSize - (1ll << (shift - 1)) + 1;
-
- frameSize = lastFrameSize + delta;
-
- frameSizes.push(frameSize);
- }
-
- lastFrameSize = frameSize;
- }
- break;
- }
-
- default:
- TRESPASS();
- }
-
-#if 0
- AString out;
- for (size_t i = 0; i < frameSizes.size(); ++i) {
- if (i > 0) {
- out.append(", ");
- }
- out.append(StringPrintf("%llu", frameSizes.itemAt(i)));
- }
- LOGV("sizes = [%s]", out.c_str());
-#endif
-
- for (size_t i = 0; i < frameSizes.size(); ++i) {
- uint64_t frameSize = frameSizes.itemAt(i);
-
- if (size < frameSize) {
- BAIL(ERROR_MALFORMED);
- }
-
- MediaBuffer *mbuf = new MediaBuffer(frameSize);
- mbuf->meta_data()->setInt64(kKeyTime, timeUs);
- mbuf->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
- memcpy(mbuf->data(), data, frameSize);
- mPendingFrames.push_back(mbuf);
-
- data += frameSize;
- size -= frameSize;
- }
-
- size_t offset = bigbuf->range_length() - size;
- bigbuf->set_range(offset, size);
-
- mPendingFrames.push_back(bigbuf);
-
return OK;
}
-#undef BAIL
-
status_t MatroskaSource::read(
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
- if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ if (options && options->getSeekTo(&seekTimeUs, &mode)
+ && !mExtractor->isLiveStreaming()) {
clearPendingFrames();
mBlockIter.seek(seekTimeUs);
}
@@ -584,6 +494,13 @@
mReader(new DataSourceReader(mDataSource)),
mSegment(NULL),
mExtractedThumbnails(false) {
+ off64_t size;
+ mIsLiveStreaming =
+ (mDataSource->flags()
+ & (DataSource::kWantsPrefetching
+ | DataSource::kIsCachingDataSource))
+ && mDataSource->getSize(&size) != OK;
+
mkvparser::EBMLHeader ebmlHeader;
long long pos;
if (ebmlHeader.Parse(mReader, pos) < 0) {
@@ -598,7 +515,16 @@
return;
}
- ret = mSegment->Load();
+ if (isLiveStreaming()) {
+ ret = mSegment->ParseHeaders();
+ CHECK_EQ(ret, 0);
+
+ long len;
+ ret = mSegment->LoadCluster(pos, len);
+ CHECK_EQ(ret, 0);
+ } else {
+ ret = mSegment->Load();
+ }
if (ret < 0) {
delete mSegment;
@@ -635,7 +561,8 @@
return NULL;
}
- if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails) {
+ if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails
+ && !isLiveStreaming()) {
findThumbnails();
mExtractedThumbnails = true;
}
@@ -643,6 +570,10 @@
return mTracks.itemAt(index).mMeta;
}
+bool MatroskaExtractor::isLiveStreaming() const {
+ return mIsLiveStreaming;
+}
+
static void addESDSFromAudioSpecificInfo(
const sp<MetaData> &meta, const void *asi, size_t asiSize) {
static const uint8_t kStaticESDS[] = {
@@ -660,7 +591,8 @@
// AudioSpecificInfo (with size prefix) follows
};
- CHECK(asiSize < 128);
+ // Make sure all sizes can be coded in a single byte.
+ CHECK(asiSize + 22 - 2 < 128);
size_t esdsSize = sizeof(kStaticESDS) + asiSize + 1;
uint8_t *esds = new uint8_t[esdsSize];
memcpy(esds, kStaticESDS, sizeof(kStaticESDS));
@@ -668,6 +600,11 @@
*ptr++ = asiSize;
memcpy(ptr, asi, asiSize);
+ // Increment by codecPrivateSize less 2 bytes that are accounted for
+ // already in lengths of 22/17
+ esds[1] += asiSize - 2;
+ esds[6] += asiSize - 2;
+
meta->setData(kKeyESDS, 0, esds, esdsSize);
delete[] esds;
@@ -794,7 +731,7 @@
continue;
}
- BlockIterator iter(mSegment, info->mTrackNum);
+ BlockIterator iter(this, info->mTrackNum);
int32_t i = 0;
int64_t thumbnailTimeUs = 0;
size_t maxBlockSize = 0;
@@ -802,7 +739,11 @@
if (iter.block()->IsKey()) {
++i;
- size_t blockSize = iter.block()->GetSize();
+ size_t blockSize = 0;
+ for (int i = 0; i < iter.block()->GetFrameCount(); ++i) {
+ blockSize += iter.block()->GetFrame(i).len;
+ }
+
if (blockSize > maxBlockSize) {
maxBlockSize = blockSize;
thumbnailTimeUs = iter.blockTimeUs();
@@ -821,6 +762,15 @@
return meta;
}
+uint32_t MatroskaExtractor::flags() const {
+ uint32_t x = CAN_PAUSE;
+ if (!isLiveStreaming()) {
+ x |= CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK;
+ }
+
+ return x;
+}
+
bool SniffMatroska(
const sp<DataSource> &source, String8 *mimeType, float *confidence,
sp<AMessage> *) {
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
index fa20b84..38ebd61 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.h
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -20,6 +20,7 @@
#include <media/stagefright/MediaExtractor.h>
#include <utils/Vector.h>
+#include <utils/threads.h>
namespace mkvparser {
struct Segment;
@@ -45,26 +46,34 @@
virtual sp<MetaData> getMetaData();
+ virtual uint32_t flags() const;
+
protected:
virtual ~MatroskaExtractor();
private:
friend struct MatroskaSource;
+ friend struct BlockIterator;
struct TrackInfo {
unsigned long mTrackNum;
sp<MetaData> mMeta;
};
+
+ Mutex mLock;
Vector<TrackInfo> mTracks;
sp<DataSource> mDataSource;
DataSourceReader *mReader;
mkvparser::Segment *mSegment;
bool mExtractedThumbnails;
+ bool mIsLiveStreaming;
void addTracks();
void findThumbnails();
+ bool isLiveStreaming() const;
+
MatroskaExtractor(const MatroskaExtractor &);
MatroskaExtractor &operator=(const MatroskaExtractor &);
};
diff --git a/media/libstagefright/matroska/mkvparser.cpp b/media/libstagefright/matroska/mkvparser.cpp
deleted file mode 100644
index 7448d96..0000000
--- a/media/libstagefright/matroska/mkvparser.cpp
+++ /dev/null
@@ -1,4514 +0,0 @@
-// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#include "mkvparser.hpp"
-#include <cassert>
-#include <cstring>
-#include <new>
-//#include <windows.h>
-//#include "odbgstream.hpp"
-//using std::endl;
-
-mkvparser::IMkvReader::~IMkvReader()
-{
-}
-
-
-void mkvparser::GetVersion(int& major, int& minor, int& build, int& revision)
-{
- major = 1;
- minor = 0;
- build = 0;
- revision = 4;
-}
-
-
-long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(pos < available);
- assert((available - pos) >= 1); //assume here max u-int len is 8
-
- unsigned char b;
-
- hr = pReader->Read(pos, 1, &b);
- if (hr < 0)
- return hr;
-
- assert(hr == 0L);
-
- if (b & 0x80) //1000 0000
- {
- len = 1;
- b &= 0x7F; //0111 1111
- }
- else if (b & 0x40) //0100 0000
- {
- len = 2;
- b &= 0x3F; //0011 1111
- }
- else if (b & 0x20) //0010 0000
- {
- len = 3;
- b &= 0x1F; //0001 1111
- }
- else if (b & 0x10) //0001 0000
- {
- len = 4;
- b &= 0x0F; //0000 1111
- }
- else if (b & 0x08) //0000 1000
- {
- len = 5;
- b &= 0x07; //0000 0111
- }
- else if (b & 0x04) //0000 0100
- {
- len = 6;
- b &= 0x03; //0000 0011
- }
- else if (b & 0x02) //0000 0010
- {
- len = 7;
- b &= 0x01; //0000 0001
- }
- else
- {
- assert(b & 0x01); //0000 0001
- len = 8;
- b = 0; //0000 0000
- }
-
- assert((available - pos) >= len);
-
- long long result = b;
- ++pos;
- for (long i = 1; i < len; ++i)
- {
- hr = pReader->Read(pos, 1, &b);
-
- if (hr < 0)
- return hr;
-
- assert(hr == 0L);
-
- result <<= 8;
- result |= b;
-
- ++pos;
- }
-
- return result;
-}
-
-
-long long mkvparser::GetUIntLength(
- IMkvReader* pReader,
- long long pos,
- long& len)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available <= total);
-
- if (pos >= available)
- return pos; //too few bytes available
-
- unsigned char b;
-
- hr = pReader->Read(pos, 1, &b);
-
- if (hr < 0)
- return hr;
-
- assert(hr == 0L);
-
- if (b == 0) //we can't handle u-int values larger than 8 bytes
- return E_FILE_FORMAT_INVALID;
-
- unsigned char m = 0x80;
- len = 1;
-
- while (!(b & m))
- {
- m >>= 1;
- ++len;
- }
-
- return 0; //success
-}
-
-
-long long mkvparser::SyncReadUInt(
- IMkvReader* pReader,
- long long pos,
- long long stop,
- long& len)
-{
- assert(pReader);
-
- if (pos >= stop)
- return E_FILE_FORMAT_INVALID;
-
- unsigned char b;
-
- long hr = pReader->Read(pos, 1, &b);
-
- if (hr < 0)
- return hr;
-
- if (hr != 0L)
- return E_BUFFER_NOT_FULL;
-
- if (b == 0) //we can't handle u-int values larger than 8 bytes
- return E_FILE_FORMAT_INVALID;
-
- unsigned char m = 0x80;
- len = 1;
-
- while (!(b & m))
- {
- m >>= 1;
- ++len;
- }
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- long long result = b & (~m);
- ++pos;
-
- for (int i = 1; i < len; ++i)
- {
- hr = pReader->Read(pos, 1, &b);
-
- if (hr < 0)
- return hr;
-
- if (hr != 0L)
- return E_BUFFER_NOT_FULL;
-
- result <<= 8;
- result |= b;
-
- ++pos;
- }
-
- return result;
-}
-
-
-long long mkvparser::UnserializeUInt(
- IMkvReader* pReader,
- long long pos,
- long long size)
-{
- assert(pReader);
- assert(pos >= 0);
- assert(size > 0);
- assert(size <= 8);
-
- long long result = 0;
-
- for (long long i = 0; i < size; ++i)
- {
- unsigned char b;
-
- const long hr = pReader->Read(pos, 1, &b);
-
- if (hr < 0)
- return hr;
- result <<= 8;
- result |= b;
-
- ++pos;
- }
-
- return result;
-}
-
-
-float mkvparser::Unserialize4Float(
- IMkvReader* pReader,
- long long pos)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available <= total);
- assert((pos + 4) <= available);
-
- float result;
-
- unsigned char* const p = (unsigned char*)&result;
- unsigned char* q = p + 4;
-
- for (;;)
- {
- hr = pReader->Read(pos, 1, --q);
- assert(hr == 0L);
-
- if (q == p)
- break;
-
- ++pos;
- }
-
- return result;
-}
-
-
-double mkvparser::Unserialize8Double(
- IMkvReader* pReader,
- long long pos)
-{
- assert(pReader);
- assert(pos >= 0);
-
- double result;
-
- unsigned char* const p = (unsigned char*)&result;
- unsigned char* q = p + 8;
-
- for (;;)
- {
- const long hr = pReader->Read(pos, 1, --q);
- assert(hr == 0L);
-
- if (q == p)
- break;
-
- ++pos;
- }
-
- return result;
-}
-
-signed char mkvparser::Unserialize1SInt(
- IMkvReader* pReader,
- long long pos)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr == 0);
- assert(available <= total);
- assert(pos < available);
-
- signed char result;
-
- hr = pReader->Read(pos, 1, (unsigned char*)&result);
- assert(hr == 0);
-
- return result;
-}
-
-short mkvparser::Unserialize2SInt(
- IMkvReader* pReader,
- long long pos)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available <= total);
- assert((pos + 2) <= available);
-
- short result;
-
- unsigned char* const p = (unsigned char*)&result;
- unsigned char* q = p + 2;
-
- for (;;)
- {
- hr = pReader->Read(pos, 1, --q);
- assert(hr == 0L);
-
- if (q == p)
- break;
-
- ++pos;
- }
-
- return result;
-}
-
-
-bool mkvparser::Match(
- IMkvReader* pReader,
- long long& pos,
- unsigned long id_,
- long long& val)
-
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available <= total);
-
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0);
- assert(len > 0);
- assert(len <= 8);
- assert((pos + len) <= available);
-
- if ((unsigned long)id != id_)
- return false;
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0);
- assert(size <= 8);
- assert(len > 0);
- assert(len <= 8);
- assert((pos + len) <= available);
-
- pos += len; //consume length of size of payload
-
- val = UnserializeUInt(pReader, pos, size);
- assert(val >= 0);
-
- pos += size; //consume size of payload
-
- return true;
-}
-
-bool mkvparser::Match(
- IMkvReader* pReader,
- long long& pos,
- unsigned long id_,
- char*& val)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available <= total);
-
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0);
- assert(len > 0);
- assert(len <= 8);
- assert((pos + len) <= available);
-
- if ((unsigned long)id != id_)
- return false;
-
- pos += len; //consume id
-
- const long long size_ = ReadUInt(pReader, pos, len);
- assert(size_ >= 0);
- assert(len > 0);
- assert(len <= 8);
- assert((pos + len) <= available);
-
- pos += len; //consume length of size of payload
- assert((pos + size_) <= available);
-
- const size_t size = static_cast<size_t>(size_);
- val = new char[size+1];
-
- for (size_t i = 0; i < size; ++i)
- {
- char c;
-
- hr = pReader->Read(pos + i, 1, (unsigned char*)&c);
- assert(hr == 0L);
-
- val[i] = c;
-
- if (c == '\0')
- break;
-
- }
-
- val[size] = '\0';
- pos += size_; //consume size of payload
-
- return true;
-}
-
-bool mkvparser::Match(
- IMkvReader* pReader,
- long long& pos,
- unsigned long id_,
- unsigned char*& buf,
- size_t& buflen)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available <= total);
-
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0);
- assert(len > 0);
- assert(len <= 8);
- assert((pos + len) <= available);
-
- if ((unsigned long)id != id_)
- return false;
-
- pos += len; //consume id
-
- const long long size_ = ReadUInt(pReader, pos, len);
- assert(size_ >= 0);
- assert(len > 0);
- assert(len <= 8);
- assert((pos + len) <= available);
-
- pos += len; //consume length of size of payload
- assert((pos + size_) <= available);
-
- const long buflen_ = static_cast<long>(size_);
-
- buf = new (std::nothrow) unsigned char[buflen_];
- assert(buf); //TODO
-
- hr = pReader->Read(pos, buflen_, buf);
- assert(hr == 0L);
-
- buflen = buflen_;
-
- pos += size_; //consume size of payload
- return true;
-}
-
-
-bool mkvparser::Match(
- IMkvReader* pReader,
- long long& pos,
- unsigned long id_,
- double& val)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available <= total);
- long idlen;
- const long long id = ReadUInt(pReader, pos, idlen);
- assert(id >= 0); //TODO
-
- if ((unsigned long)id != id_)
- return false;
-
- long sizelen;
- const long long size = ReadUInt(pReader, pos + idlen, sizelen);
-
- switch (size)
- {
- case 4:
- case 8:
- break;
- default:
- return false;
- }
-
- pos += idlen + sizelen; //consume id and size fields
- assert((pos + size) <= available);
-
- if (size == 4)
- val = Unserialize4Float(pReader, pos);
- else
- {
- assert(size == 8);
- val = Unserialize8Double(pReader, pos);
- }
-
- pos += size; //consume size of payload
-
- return true;
-}
-
-
-bool mkvparser::Match(
- IMkvReader* pReader,
- long long& pos,
- unsigned long id_,
- short& val)
-{
- assert(pReader);
- assert(pos >= 0);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available <= total);
-
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0);
- assert((pos + len) <= available);
-
- if ((unsigned long)id != id_)
- return false;
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size <= 2);
- assert((pos + len) <= available);
-
- pos += len; //consume length of size of payload
- assert((pos + size) <= available);
-
- //TODO:
- // Generalize this to work for any size signed int
- if (size == 1)
- val = Unserialize1SInt(pReader, pos);
- else
- val = Unserialize2SInt(pReader, pos);
-
- pos += size; //consume size of payload
-
- return true;
-}
-
-
-namespace mkvparser
-{
-
-EBMLHeader::EBMLHeader():
- m_docType(NULL)
-{
-}
-
-EBMLHeader::~EBMLHeader()
-{
- delete[] m_docType;
-}
-
-long long EBMLHeader::Parse(
- IMkvReader* pReader,
- long long& pos)
-{
- assert(pReader);
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
-
- if (hr < 0)
- return hr;
-
- pos = 0;
- long long end = (1024 < available)? 1024: available;
-
- for (;;)
- {
- unsigned char b = 0;
-
- while (pos < end)
- {
- hr = pReader->Read(pos, 1, &b);
-
- if (hr < 0)
- return hr;
-
- if (b == 0x1A)
- break;
-
- ++pos;
- }
-
- if (b != 0x1A)
- {
- if ((pos >= 1024) ||
- (available >= total) ||
- ((total - available) < 5))
- return -1;
-
- return available + 5; //5 = 4-byte ID + 1st byte of size
- }
-
- if ((total - pos) < 5)
- return E_FILE_FORMAT_INVALID;
-
- if ((available - pos) < 5)
- return pos + 5; //try again later
-
- long len;
-
- const long long result = ReadUInt(pReader, pos, len);
-
- if (result < 0) //error
- return result;
-
- if (result == 0x0A45DFA3) //ReadId masks-off length indicator bits
- {
- assert(len == 4);
- pos += len;
- break;
- }
-
- ++pos; //throw away just the 0x1A byte, and try again
- }
-
- long len;
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result < 0) //error
- return result;
-
- if (result > 0) //need more data
- return result;
-
- assert(len > 0);
- assert(len <= 8);
-
- if ((total - pos) < len)
- return E_FILE_FORMAT_INVALID;
- if ((available - pos) < len)
- return pos + len; //try again later
-
- result = ReadUInt(pReader, pos, len);
-
- if (result < 0) //error
- return result;
-
- pos += len; //consume u-int
-
- if ((total - pos) < result)
- return E_FILE_FORMAT_INVALID;
-
- if ((available - pos) < result)
- return pos + result;
-
- end = pos + result;
-
- m_version = 1;
- m_readVersion = 1;
- m_maxIdLength = 4;
- m_maxSizeLength = 8;
- m_docTypeVersion = 1;
- m_docTypeReadVersion = 1;
-
- while (pos < end)
- {
- if (Match(pReader, pos, 0x0286, m_version))
- ;
- else if (Match(pReader, pos, 0x02F7, m_readVersion))
- ;
- else if (Match(pReader, pos, 0x02F2, m_maxIdLength))
- ;
- else if (Match(pReader, pos, 0x02F3, m_maxSizeLength))
- ;
- else if (Match(pReader, pos, 0x0282, m_docType))
- ;
- else if (Match(pReader, pos, 0x0287, m_docTypeVersion))
- ;
- else if (Match(pReader, pos, 0x0285, m_docTypeReadVersion))
- ;
- else
- {
- result = ReadUInt(pReader, pos, len);
- assert(result > 0);
- assert(len > 0);
- assert(len <= 8);
-
- pos += len;
- assert(pos < end);
-
- result = ReadUInt(pReader, pos, len);
- assert(result >= 0);
- assert(len > 0);
- assert(len <= 8);
-
- pos += len + result;
- assert(pos <= end);
- }
- }
-
- assert(pos == end);
-
- return 0;
-}
-
-
-Segment::Segment(
- IMkvReader* pReader,
- long long start,
- long long size) :
- m_pReader(pReader),
- m_start(start),
- m_size(size),
- m_pos(start),
- m_pInfo(NULL),
- m_pTracks(NULL),
- m_pCues(NULL),
- m_clusters(NULL),
- m_clusterCount(0),
- m_clusterPreloadCount(0),
- m_clusterSize(0)
-{
-}
-
-
-Segment::~Segment()
-{
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- Cluster** i = m_clusters;
- Cluster** j = m_clusters + count;
-
- while (i != j)
- {
- Cluster* const p = *i++;
- assert(p);
-
- delete p;
- }
-
- delete[] m_clusters;
-
- delete m_pTracks;
- delete m_pInfo;
- delete m_pCues;
-}
-
-
-long long Segment::CreateInstance(
- IMkvReader* pReader,
- long long pos,
- Segment*& pSegment)
-{
- assert(pReader);
- assert(pos >= 0);
-
- pSegment = NULL;
-
- long long total, available;
-
- long hr = pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available <= total);
-
- //I would assume that in practice this loop would execute
- //exactly once, but we allow for other elements (e.g. Void)
- //to immediately follow the EBML header. This is fine for
- //the source filter case (since the entire file is available),
- //but in the splitter case over a network we should probably
- //just give up early. We could for example decide only to
- //execute this loop a maximum of, say, 10 times.
-
- while (pos < total)
- {
- //Read ID
-
- long len;
- long long result = GetUIntLength(pReader, pos, len);
-
- if (result) //error, or too few available bytes
- return result;
-
- if ((pos + len) > total)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- //TODO: if we liberalize the behavior of ReadUInt, we can
- //probably eliminate having to use GetUIntLength here.
- const long long id = ReadUInt(pReader, pos, len);
-
- if (id < 0) //error
- return id;
-
- pos += len; //consume ID
-
- //Read Size
-
- result = GetUIntLength(pReader, pos, len);
-
- if (result) //error, or too few available bytes
- return result;
-
- if ((pos + len) > total)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- //TODO: if we liberalize the behavior of ReadUInt, we can
- //probably eliminate having to use GetUIntLength here.
- const long long size = ReadUInt(pReader, pos, len);
-
- if (size < 0)
- return size;
-
- pos += len; //consume length of size of element
-
- //Pos now points to start of payload
-
- if ((pos + size) > total)
- return E_FILE_FORMAT_INVALID;
-
- if (id == 0x08538067) //Segment ID
- {
- pSegment = new Segment(pReader, pos, size);
- assert(pSegment); //TODO
-
- return 0; //success
- }
-
- pos += size; //consume payload
- }
-
- assert(pos == total);
-
- pSegment = new Segment(pReader, pos, 0);
- assert(pSegment); //TODO
-
- return 0; //success (sort of)
-}
-
-
-long long Segment::ParseHeaders()
-{
- //Outermost (level 0) segment object has been constructed,
- //and pos designates start of payload. We need to find the
- //inner (level 1) elements.
- long long total, available;
-
- long hr = m_pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available <= total);
-
- const long long stop = m_start + m_size;
- assert(stop <= total);
- assert(m_pos <= stop);
-
- bool bQuit = false;
-
- while ((m_pos < stop) && !bQuit)
- {
- long long pos = m_pos;
-
- long len;
- long long result = GetUIntLength(m_pReader, pos, len);
-
- if (result) //error, or too few available bytes
- return result;
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- const long long idpos = pos;
- const long long id = ReadUInt(m_pReader, idpos, len);
-
- if (id < 0) //error
- return id;
-
- pos += len; //consume ID
-
- //Read Size
- result = GetUIntLength(m_pReader, pos, len);
-
- if (result) //error, or too few available bytes
- return result;
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- if ((pos + len) > available)
- return pos + len;
-
- const long long size = ReadUInt(m_pReader, pos, len);
-
- if (size < 0)
- return size;
-
- pos += len; //consume length of size of element
-
- //Pos now points to start of payload
-
- if ((pos + size) > stop)
- return E_FILE_FORMAT_INVALID;
-
- //We read EBML elements either in total or nothing at all.
-
- if ((pos + size) > available)
- return pos + size;
-
- if (id == 0x0549A966) //Segment Info ID
- {
- assert(m_pInfo == NULL);
-
- m_pInfo = new SegmentInfo(this, pos, size);
- assert(m_pInfo); //TODO
- }
- else if (id == 0x0654AE6B) //Tracks ID
- {
- assert(m_pTracks == NULL);
-
- m_pTracks = new Tracks(this, pos, size);
- assert(m_pTracks); //TODO
- }
- else if (id == 0x0C53BB6B) //Cues ID
- {
- if (m_pCues == NULL)
- {
- m_pCues = new Cues(this, pos, size);
- assert(m_pCues); //TODO
- }
- }
- else if (id == 0x014D9B74) //SeekHead ID
- {
- ParseSeekHead(pos, size);
- }
- else if (id == 0x0F43B675) //Cluster ID
- {
- bQuit = true;
- }
-
- if (!bQuit)
- m_pos = pos + size; //consume payload
- }
-
- assert(m_pos <= stop);
-
- if (m_pInfo == NULL) //TODO: liberalize this behavior
- return E_FILE_FORMAT_INVALID;
-
- if (m_pTracks == NULL)
- return E_FILE_FORMAT_INVALID;
-
- return 0; //success
-}
-
-
-#if 0
-long Segment::ParseCluster(Cluster*& pCluster, long long& pos_) const
-{
- pCluster = NULL;
- pos_ = -1;
-
- const long long stop = m_start + m_size;
- assert(m_pos <= stop);
-
- long long pos = m_pos;
- long long off = -1;
-
- while (pos < stop)
- {
- long len;
- const long long idpos = pos;
-
- const long long id = SyncReadUInt(m_pReader, pos, stop, len);
-
- if (id < 0) //error
- return static_cast<long>(id);
-
- if (id == 0)
- return E_FILE_FORMAT_INVALID;
-
- pos += len; //consume id
- assert(pos < stop);
-
- const long long size = SyncReadUInt(m_pReader, pos, stop, len);
-
- if (size < 0) //error
- return static_cast<long>(size);
-
- pos += len; //consume size
- assert(pos <= stop);
-
- if (size == 0) //weird
- continue;
-
- //pos now points to start of payload
-
- pos += size; //consume payload
- assert(pos <= stop);
-
- if (id == 0x0F43B675) //Cluster ID
- {
- off = idpos - m_start; // >= 0 means we found a cluster
- break;
- }
- }
-
- assert(pos <= stop);
-
- //Indicate to caller how much of file has been consumed. This is
- //used later in AddCluster to adjust the current parse position
- //(the value cached in the segment object itself) to the
- //file position value just past the cluster we parsed.
-
- if (off < 0) //we did not found any more clusters
- {
- pos_ = stop; //pos_ >= 0 here means EOF (cluster is NULL)
- return 0; //TODO: confirm this return value
- }
-
- //We found a cluster. Now read something, to ensure that it is
- //fully loaded in the network cache.
-
- if (pos >= stop) //we parsed the entire segment
- {
- //We did find a cluster, but it was very last element in the segment.
- //Our preference is that the loop above runs 1 1/2 times:
- //the first pass finds the cluster, and the second pass
- //finds the element the follows the cluster. In this case, however,
- //we reached the end of the file without finding another element,
- //so we didn't actually read anything yet associated with "end of the
- //cluster". And we must perform an actual read, in order
- //to guarantee that all of the data that belongs to this
- //cluster has been loaded into the network cache. So instead
- //of reading the next element that follows the cluster, we
- //read the last byte of the cluster (which is also the last
- //byte in the file).
-
- //Read the last byte of the file. (Reading 0 bytes at pos
- //might work too -- it would depend on how the reader is
- //implemented. Here we take the more conservative approach,
- //since this makes fewer assumptions about the network
- //reader abstraction.)
-
- unsigned char b;
-
- const int result = m_pReader->Read(pos - 1, 1, &b);
- assert(result == 0);
-
- pos_ = stop;
- }
- else
- {
- long len;
- const long long idpos = pos;
-
- const long long id = SyncReadUInt(m_pReader, pos, stop, len);
-
- if (id < 0) //error
- return static_cast<long>(id);
-
- if (id == 0)
- return E_BUFFER_NOT_FULL;
-
- pos += len; //consume id
- assert(pos < stop);
-
- const long long size = SyncReadUInt(m_pReader, pos, stop, len);
-
- if (size < 0) //error
- return static_cast<long>(size);
-
- pos_ = idpos;
- }
-
- //We found a cluster, and it has been completely loaded into the
- //network cache. (We can guarantee this because we actually read
- //the EBML tag that follows the cluster, or, if we reached EOF,
- //because we actually read the last byte of the cluster).
-
- Segment* const this_ = const_cast<Segment*>(this);
-
- pCluster = Cluster::Parse(this_, m_clusterCount, off);
- assert(pCluster);
- assert(pCluster->m_index == m_clusterCount);
-
- return 0;
-}
-
-
-bool Segment::AddCluster(Cluster* pCluster, long long pos)
-{
- assert(pos >= m_start);
-
- const long long stop = m_start + m_size;
- assert(pos <= stop);
-
- if (pCluster)
- {
- AppendCluster(pCluster);
- assert(m_clusters);
- assert(m_clusterSize > pCluster->m_index);
- assert(m_clusters[pCluster->m_index] == pCluster);
- }
-
- m_pos = pos; //m_pos >= stop is now we know we have all clusters
-
- return (pos >= stop);
-}
-#endif
-
-
-long Segment::LoadCluster()
-{
- const long long stop = m_start + m_size;
-
- while (m_pos < stop)
- {
- long long pos = m_pos;
-
- long len;
-
- long long result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return static_cast<long>(result);
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- const long long idpos = pos;
- const long long id = ReadUInt(m_pReader, idpos, len);
-
- if (id < 0) //error
- return static_cast<long>(id);
-
- pos += len; //consume ID
-
- //Read Size
- result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return static_cast<long>(result);
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- const long long size = ReadUInt(m_pReader, pos, len);
-
- if (size < 0) //error
- return static_cast<long>(size);
-
- pos += len; //consume length of size of element
-
- if (size == 0) //weird
- {
- m_pos = pos;
- continue;
- }
-
- //Pos now points to start of payload
-
- if ((pos + size) > stop)
- return E_FILE_FORMAT_INVALID;
-
- if (id == 0x0C53BB6B) //Cues ID
- {
- if (m_pCues == NULL)
- {
- m_pCues = new Cues(this, pos, size);
- assert(m_pCues); //TODO
- }
-
- m_pos = pos + size; //consume payload
- continue;
- }
-
- if (id != 0x0F43B675) //Cluster ID
- {
- m_pos = pos + size; //consume payload
- continue;
- }
-
- const long idx = m_clusterCount;
- const long long idoff = idpos - m_start;
-
- if (m_clusterPreloadCount > 0)
- {
- assert(idx < m_clusterSize);
-
- Cluster* const pCluster = m_clusters[idx];
- assert(pCluster);
- assert(pCluster->m_index < 0);
-
- const long long off_ = pCluster->m_pos;
- assert(off_);
-
- const long long off = off_ * ((off_ >= 0) ? 1 : -1);
- assert(idoff <= off);
-
- if (idoff == off) //cluster has been preloaded already
- {
- pCluster->m_index = idx;
- ++m_clusterCount;
- --m_clusterPreloadCount;
-
- m_pos = pos + size; //consume payload
- break;
- }
- }
-
- Cluster* const pCluster = Cluster::Parse(this, idx, idoff);
- assert(pCluster);
- assert(pCluster->m_index == idx);
-
- AppendCluster(pCluster);
- assert(m_clusters);
- assert(idx < m_clusterSize);
- assert(m_clusters[idx] == pCluster);
-
- m_pos = pos + size; //consume payload
- break;
- }
-
- assert(m_pos <= stop);
- return 0;
-}
-
-
-void Segment::AppendCluster(Cluster* pCluster)
-{
- assert(pCluster);
- assert(pCluster->m_index >= 0);
-
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- long& size = m_clusterSize;
- assert(size >= count);
-
- const long idx = pCluster->m_index;
- assert(idx == m_clusterCount);
-
- if (count >= size)
- {
- long n;
-
- if (size > 0)
- n = 2 * size;
- else if (m_pInfo == 0)
- n = 2048;
- else
- {
- const long long ns = m_pInfo->GetDuration();
-
- if (ns <= 0)
- n = 2048;
- else
- {
- const long long sec = (ns + 999999999LL) / 1000000000LL;
- n = static_cast<long>(sec);
- }
- }
-
- Cluster** const qq = new Cluster*[n];
- Cluster** q = qq;
-
- Cluster** p = m_clusters;
- Cluster** const pp = p + count;
-
- while (p != pp)
- *q++ = *p++;
-
- delete[] m_clusters;
-
- m_clusters = qq;
- size = n;
- }
-
- if (m_clusterPreloadCount > 0)
- {
- assert(m_clusters);
-
- Cluster** const p = m_clusters + m_clusterCount;
- assert(*p);
- assert((*p)->m_index < 0);
-
- Cluster** q = p + m_clusterPreloadCount;
- assert(q < (m_clusters + size));
-
- for (;;)
- {
- Cluster** const qq = q - 1;
- assert((*qq)->m_index < 0);
-
- *q = *qq;
- q = qq;
-
- if (q == p)
- break;
- }
- }
-
- m_clusters[idx] = pCluster;
- ++m_clusterCount;
-}
-
-
-void Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx)
-{
- assert(pCluster);
- assert(pCluster->m_index < 0);
- assert(idx >= m_clusterCount);
-
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- long& size = m_clusterSize;
- assert(size >= count);
-
- if (count >= size)
- {
- long n;
-
- if (size > 0)
- n = 2 * size;
- else if (m_pInfo == 0)
- n = 2048;
- else
- {
- const long long ns = m_pInfo->GetDuration();
-
- if (ns <= 0)
- n = 2048;
- else
- {
- const long long sec = (ns + 999999999LL) / 1000000000LL;
- n = static_cast<long>(sec);
- }
- }
-
- Cluster** const qq = new Cluster*[n];
- Cluster** q = qq;
-
- Cluster** p = m_clusters;
- Cluster** const pp = p + count;
-
- while (p != pp)
- *q++ = *p++;
-
- delete[] m_clusters;
-
- m_clusters = qq;
- size = n;
- }
-
- assert(m_clusters);
-
- Cluster** const p = m_clusters + idx;
-
- Cluster** q = m_clusters + count;
- assert(q >= p);
- assert(q < (m_clusters + size));
-
- while (q > p)
- {
- Cluster** const qq = q - 1;
- assert((*qq)->m_index < 0);
-
- *q = *qq;
- q = qq;
- }
-
- m_clusters[idx] = pCluster;
- ++m_clusterPreloadCount;
-}
-
-
-long Segment::Load()
-{
- assert(m_clusters == NULL);
- assert(m_clusterSize == 0);
- assert(m_clusterCount == 0);
-
- //Outermost (level 0) segment object has been constructed,
- //and pos designates start of payload. We need to find the
- //inner (level 1) elements.
- const long long stop = m_start + m_size;
-
-#ifdef _DEBUG //TODO: this is really Microsoft-specific
- {
- long long total, available;
-
- long hr = m_pReader->Length(&total, &available);
- assert(hr >= 0);
- assert(available >= total);
- assert(stop <= total);
- }
-#endif
-
- while (m_pos < stop)
- {
- long long pos = m_pos;
-
- long len;
-
- long long result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return static_cast<long>(result);
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- const long long idpos = pos;
- const long long id = ReadUInt(m_pReader, idpos, len);
-
- if (id < 0) //error
- return static_cast<long>(id);
-
- pos += len; //consume ID
-
- //Read Size
- result = GetUIntLength(m_pReader, pos, len);
-
- if (result < 0) //error
- return static_cast<long>(result);
-
- if ((pos + len) > stop)
- return E_FILE_FORMAT_INVALID;
-
- const long long size = ReadUInt(m_pReader, pos, len);
-
- if (size < 0) //error
- return static_cast<long>(size);
-
- pos += len; //consume length of size of element
-
- //Pos now points to start of payload
-
- if ((pos + size) > stop)
- return E_FILE_FORMAT_INVALID;
-
- if (id == 0x0F43B675) //Cluster ID
- {
- const long idx = m_clusterCount;
- const long long off = idpos - m_start;
-
- Cluster* const pCluster = Cluster::Parse(this, idx, off);
- assert(pCluster);
- assert(pCluster->m_index == idx);
-
- AppendCluster(pCluster);
- assert(m_clusters);
- assert(m_clusterSize > idx);
- assert(m_clusters[idx] == pCluster);
- }
- else if (id == 0x0C53BB6B) //Cues ID
- {
- assert(m_pCues == NULL);
-
- m_pCues = new Cues(this, pos, size);
- assert(m_pCues); //TODO
- }
- else if (id == 0x0549A966) //SegmentInfo ID
- {
- assert(m_pInfo == NULL);
-
- m_pInfo = new SegmentInfo(this, pos, size);
- assert(m_pInfo);
- }
- else if (id == 0x0654AE6B) //Tracks ID
- {
- assert(m_pTracks == NULL);
-
- m_pTracks = new Tracks(this, pos, size);
- assert(m_pTracks); //TODO
- }
-
- m_pos = pos + size; //consume payload
- }
-
- assert(m_pos >= stop);
-
- if (m_pInfo == NULL)
- return E_FILE_FORMAT_INVALID; //TODO: ignore this case?
-
- if (m_pTracks == NULL)
- return E_FILE_FORMAT_INVALID;
-
- if (m_clusters == NULL) //TODO: ignore this case?
- return E_FILE_FORMAT_INVALID;
-
- //TODO: decide whether we require Cues element
- //if (m_pCues == NULL)
- // return E_FILE_FORMAT_INVALID;
-
- return 0;
-}
-
-
-void Segment::ParseSeekHead(long long start, long long size_)
-{
- long long pos = start;
- const long long stop = start + size_;
-
- while (pos < stop)
- {
- long len;
-
- const long long id = ReadUInt(m_pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume ID
-
- const long long size = ReadUInt(m_pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume Size field
- assert((pos + size) <= stop);
-
- if (id == 0x0DBB) //SeekEntry ID
- ParseSeekEntry(pos, size);
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
-
- assert(pos == stop);
-}
-
-
-void Segment::ParseCues(long long off)
-{
- if (m_pCues)
- return;
-
- //odbgstream os;
- //os << "Segment::ParseCues (begin)" << endl;
-
- long long pos = m_start + off;
- const long long stop = m_start + m_size;
-
- long len;
-
- long long result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0);
- assert((pos + len) <= stop);
-
- const long long idpos = pos;
-
- const long long id = ReadUInt(m_pReader, idpos, len);
- assert(id == 0x0C53BB6B); //Cues ID
-
- pos += len; //consume ID
- assert(pos < stop);
-
- //Read Size
-
- result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0);
- assert((pos + len) <= stop);
-
- const long long size = ReadUInt(m_pReader, pos, len);
- assert(size >= 0);
-
- pos += len; //consume length of size of element
- assert((pos + size) <= stop);
-
- //Pos now points to start of payload
-
- m_pCues = new Cues(this, pos, size);
- assert(m_pCues); //TODO
-
- //os << "Segment::ParseCues (end)" << endl;
-}
-
-
-void Segment::ParseSeekEntry(
- long long start,
- long long size_)
-{
- long long pos = start;
-
- const long long stop = start + size_;
-
- long len;
-
- const long long seekIdId = ReadUInt(m_pReader, pos, len);
- //seekIdId;
- assert(seekIdId == 0x13AB); //SeekID ID
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long seekIdSize = ReadUInt(m_pReader, pos, len);
- assert(seekIdSize >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume size
-
- const long long seekId = ReadUInt(m_pReader, pos, len); //payload
- assert(seekId >= 0);
- assert(len == seekIdSize);
- assert((pos + len) <= stop);
-
- pos += seekIdSize; //consume payload
-
- const long long seekPosId = ReadUInt(m_pReader, pos, len);
- //seekPosId;
- assert(seekPosId == 0x13AC); //SeekPos ID
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long seekPosSize = ReadUInt(m_pReader, pos, len);
- assert(seekPosSize >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume size
- assert((pos + seekPosSize) <= stop);
-
- const long long seekOff = UnserializeUInt(m_pReader, pos, seekPosSize);
- assert(seekOff >= 0);
- assert(seekOff < m_size);
-
- pos += seekPosSize; //consume payload
- assert(pos == stop);
-
- const long long seekPos = m_start + seekOff;
- assert(seekPos < (m_start + m_size));
-
- if (seekId == 0x0C53BB6B) //Cues ID
- ParseCues(seekOff);
-}
-
-
-Cues::Cues(Segment* pSegment, long long start_, long long size_) :
- m_pSegment(pSegment),
- m_start(start_),
- m_size(size_),
- m_cue_points(NULL),
- m_count(0),
- m_preload_count(0),
- m_pos(start_)
-{
-}
-
-
-Cues::~Cues()
-{
- const size_t n = m_count + m_preload_count;
-
- CuePoint** p = m_cue_points;
- CuePoint** const q = p + n;
-
- while (p != q)
- {
- CuePoint* const pCP = *p++;
- assert(pCP);
-
- delete pCP;
- }
-
- delete[] m_cue_points;
-}
-
-
-void Cues::Init() const
-{
- if (m_cue_points)
- return;
-
- assert(m_count == 0);
- assert(m_preload_count == 0);
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- const long long stop = m_start + m_size;
- long long pos = m_start;
-
- size_t cue_points_size = 0;
-
- while (pos < stop)
- {
- const long long idpos = pos;
-
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume ID
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume Size field
- assert((pos + size) <= stop);
-
- if (id == 0x3B) //CuePoint ID
- PreloadCuePoint(cue_points_size, idpos);
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
-}
-
-
-void Cues::PreloadCuePoint(
- size_t& cue_points_size,
- long long pos) const
-{
- assert(m_count == 0);
-
- if (m_preload_count >= cue_points_size)
- {
- size_t n;
-
- if (cue_points_size > 0)
- n = static_cast<size_t>(2 * cue_points_size);
- else
- {
- const SegmentInfo* const pInfo = m_pSegment->GetInfo();
-
- if (pInfo == NULL)
- n = 2048;
- else
- {
- const long long ns = pInfo->GetDuration();
-
- if (ns <= 0)
- n = 2048;
- else
- {
- const long long sec = (ns + 999999999LL) / 1000000000LL;
- n = static_cast<size_t>(sec);
- }
- }
- }
-
- CuePoint** const qq = new CuePoint*[n];
- CuePoint** q = qq; //beginning of target
-
- CuePoint** p = m_cue_points; //beginning of source
- CuePoint** const pp = p + m_preload_count; //end of source
-
- while (p != pp)
- *q++ = *p++;
-
- delete[] m_cue_points;
-
- m_cue_points = qq;
- cue_points_size = n;
- }
-
- CuePoint* const pCP = new CuePoint(m_preload_count, pos);
- m_cue_points[m_preload_count++] = pCP;
-}
-
-
-bool Cues::LoadCuePoint() const
-{
- //odbgstream os;
- //os << "Cues::LoadCuePoint" << endl;
-
- const long long stop = m_start + m_size;
-
- if (m_pos >= stop)
- return false; //nothing else to do
-
- Init();
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- while (m_pos < stop)
- {
- const long long idpos = m_pos;
-
- long len;
-
- const long long id = ReadUInt(pReader, m_pos, len);
- assert(id >= 0); //TODO
- assert((m_pos + len) <= stop);
-
- m_pos += len; //consume ID
-
- const long long size = ReadUInt(pReader, m_pos, len);
- assert(size >= 0);
- assert((m_pos + len) <= stop);
-
- m_pos += len; //consume Size field
- assert((m_pos + size) <= stop);
-
- if (id != 0x3B) //CuePoint ID
- {
- m_pos += size; //consume payload
- assert(m_pos <= stop);
-
- continue;
- }
-
- assert(m_preload_count > 0);
-
- CuePoint* const pCP = m_cue_points[m_count];
- assert(pCP);
- assert((pCP->GetTimeCode() >= 0) || (-pCP->GetTimeCode() == idpos));
-
- pCP->Load(pReader);
- ++m_count;
- --m_preload_count;
-
- m_pos += size; //consume payload
- assert(m_pos <= stop);
-
- break;
- }
-
- return (m_pos < stop);
-}
-
-
-bool Cues::Find(
- long long time_ns,
- const Track* pTrack,
- const CuePoint*& pCP,
- const CuePoint::TrackPosition*& pTP) const
-{
- assert(time_ns >= 0);
- assert(pTrack);
-
- LoadCuePoint();
-
- assert(m_cue_points);
- assert(m_count > 0);
-
- CuePoint** const ii = m_cue_points;
- CuePoint** i = ii;
-
- CuePoint** const jj = ii + m_count + m_preload_count;
- CuePoint** j = jj;
-
- pCP = *i;
- assert(pCP);
-
- if (time_ns <= pCP->GetTime(m_pSegment))
- {
- pTP = pCP->Find(pTrack);
- return (pTP != NULL);
- }
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- while (i < j)
- {
- //INVARIANT:
- //[ii, i) <= time_ns
- //[i, j) ?
- //[j, jj) > time_ns
-
- CuePoint** const k = i + (j - i) / 2;
- assert(k < jj);
-
- CuePoint* const pCP = *k;
- assert(pCP);
-
- pCP->Load(pReader);
-
- const long long t = pCP->GetTime(m_pSegment);
-
- if (t <= time_ns)
- i = k + 1;
- else
- j = k;
-
- assert(i <= j);
- }
-
- assert(i == j);
- assert(i <= jj);
- assert(i > ii);
-
- pCP = *--i;
- assert(pCP);
- assert(pCP->GetTime(m_pSegment) <= time_ns);
-
- //TODO: here and elsewhere, it's probably not correct to search
- //for the cue point with this time, and then search for a matching
- //track. In principle, the matching track could be on some earlier
- //cue point, and with our current algorithm, we'd miss it. To make
- //this bullet-proof, we'd need to create a secondary structure,
- //with a list of cue points that apply to a track, and then search
- //that track-based structure for a matching cue point.
-
- pTP = pCP->Find(pTrack);
- return (pTP != NULL);
-}
-
-
-#if 0
-bool Cues::FindNext(
- long long time_ns,
- const Track* pTrack,
- const CuePoint*& pCP,
- const CuePoint::TrackPosition*& pTP) const
-{
- pCP = 0;
- pTP = 0;
-
- if (m_count == 0)
- return false;
-
- assert(m_cue_points);
-
- const CuePoint* const* const ii = m_cue_points;
- const CuePoint* const* i = ii;
-
- const CuePoint* const* const jj = ii + m_count;
- const CuePoint* const* j = jj;
-
- while (i < j)
- {
- //INVARIANT:
- //[ii, i) <= time_ns
- //[i, j) ?
- //[j, jj) > time_ns
-
- const CuePoint* const* const k = i + (j - i) / 2;
- assert(k < jj);
-
- pCP = *k;
- assert(pCP);
-
- const long long t = pCP->GetTime(m_pSegment);
-
- if (t <= time_ns)
- i = k + 1;
- else
- j = k;
-
- assert(i <= j);
- }
-
- assert(i == j);
- assert(i <= jj);
-
- if (i >= jj) //time_ns is greater than max cue point
- return false;
-
- pCP = *i;
- assert(pCP);
- assert(pCP->GetTime(m_pSegment) > time_ns);
-
- pTP = pCP->Find(pTrack);
- return (pTP != NULL);
-}
-#endif
-
-
-const CuePoint* Cues::GetFirst() const
-{
- LoadCuePoint(); //init cues
-
- const size_t count = m_count + m_preload_count;
-
- if (count == 0) //weird
- return NULL;
-
- CuePoint* const* const pp = m_cue_points;
- assert(pp);
-
- CuePoint* const pCP = pp[0];
- assert(pCP);
- assert(pCP->GetTimeCode() >= 0);
-
- return pCP;
-}
-
-
-const CuePoint* Cues::GetLast() const
-{
- LoadCuePoint(); //init cues
-
- const size_t count = m_count + m_preload_count;
-
- if (count == 0) //weird
- return NULL;
-
- const size_t index = count - 1;
-
- CuePoint* const* const pp = m_cue_points;
- assert(pp);
-
- CuePoint* const pCP = pp[index];
- assert(pCP);
-
- pCP->Load(m_pSegment->m_pReader);
- assert(pCP->GetTimeCode() >= 0);
-
- return pCP;
-}
-
-
-const CuePoint* Cues::GetNext(const CuePoint* pCurr) const
-{
- if (pCurr == NULL)
- return NULL;
-
- assert(pCurr->GetTimeCode() >= 0);
- assert(m_cue_points);
- assert(m_count >= 1);
-
- const size_t count = m_count + m_preload_count;
-
- size_t index = pCurr->m_index;
- assert(index < count);
-
- CuePoint* const* const pp = m_cue_points;
- assert(pp);
- assert(pp[index] == pCurr);
-
- ++index;
-
- if (index >= count)
- return NULL;
-
- CuePoint* const pNext = pp[index];
- assert(pNext);
-
- pNext->Load(m_pSegment->m_pReader);
-
- return pNext;
-}
-
-
-const BlockEntry* Cues::GetBlock(
- const CuePoint* pCP,
- const CuePoint::TrackPosition* pTP) const
-{
- if (pCP == NULL)
- return NULL;
-
- if (pTP == NULL)
- return NULL;
-
- return m_pSegment->GetBlock(*pCP, *pTP);
-}
-
-
-const BlockEntry* Segment::GetBlock(
- const CuePoint& cp,
- const CuePoint::TrackPosition& tp)
-{
- Cluster** const ii = m_clusters;
- Cluster** i = ii;
-
- const long count = m_clusterCount + m_clusterPreloadCount;
-
- Cluster** const jj = ii + count;
- Cluster** j = jj;
-
- while (i < j)
- {
- //INVARIANT:
- //[ii, i) < pTP->m_pos
- //[i, j) ?
- //[j, jj) > pTP->m_pos
-
- Cluster** const k = i + (j - i) / 2;
- assert(k < jj);
-
- Cluster* const pCluster = *k;
- assert(pCluster);
-
- const long long pos_ = pCluster->m_pos;
- assert(pos_);
-
- const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
-
- if (pos < tp.m_pos)
- i = k + 1;
- else if (pos > tp.m_pos)
- j = k;
- else
- return pCluster->GetEntry(cp, tp);
- }
-
- assert(i == j);
-
- Cluster* const pCluster = Cluster::Parse(this, -1, tp.m_pos);
- const ptrdiff_t idx = i - m_clusters;
-
- PreloadCluster(pCluster, idx);
- assert(m_clusters);
- assert(m_clusterPreloadCount > 0);
- assert(m_clusters[idx] == pCluster);
-
- return pCluster->GetEntry(cp, tp);
-}
-
-
-
-CuePoint::CuePoint(size_t idx, long long pos) :
- m_index(idx),
- m_timecode(-1 * pos),
- m_track_positions(NULL),
- m_track_positions_count(0)
-{
- assert(pos > 0);
-}
-
-
-CuePoint::~CuePoint()
-{
- delete[] m_track_positions;
-}
-
-
-void CuePoint::Load(IMkvReader* pReader)
-{
- //odbgstream os;
- //os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
-
- if (m_timecode >= 0) //already loaded
- return;
-
- assert(m_track_positions == NULL);
- assert(m_track_positions_count == 0);
-
- long long pos_ = -m_timecode;
-
- long long stop;
-
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos_, len);
- assert(id == 0x3B); //CuePoint ID
- //assert((pos + len) <= stop);
-
- pos_ += len; //consume ID
-
- const long long size = ReadUInt(pReader, pos_, len);
- assert(size >= 0);
- //assert((pos + len) <= stop);
-
- pos_ += len; //consume Size field
- //assert((pos + size) <= stop);
-
- //pos_ now points to start of payload
-
- stop = pos_ + size;
- }
-
- long long pos = pos_;
-
- //First count number of track positions
-
- while (pos < stop)
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume ID
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume Size field
- assert((pos + size) <= stop);
-
- if (id == 0x33) //CueTime ID
- m_timecode = UnserializeUInt(pReader, pos, size);
-
- else if (id == 0x37) //CueTrackPosition(s) ID
- ++m_track_positions_count;
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
-
- assert(m_timecode >= 0);
- assert(m_track_positions_count > 0);
-
- //os << "CuePoint::Load(cont'd): idpos=" << idpos
- // << " timecode=" << m_timecode
- // << endl;
-
- m_track_positions = new TrackPosition[m_track_positions_count];
-
- //Now parse track positions
-
- TrackPosition* p = m_track_positions;
- pos = pos_;
-
- while (pos < stop)
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume ID
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume Size field
- assert((pos + size) <= stop);
-
- if (id == 0x37) //CueTrackPosition(s) ID
- {
- TrackPosition& tp = *p++;
- tp.Parse(pReader, pos, size);
- }
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
-
- assert(size_t(p - m_track_positions) == m_track_positions_count);
-}
-
-
-
-void CuePoint::TrackPosition::Parse(
- IMkvReader* pReader,
- long long start_,
- long long size_)
-{
- const long long stop = start_ + size_;
- long long pos = start_;
-
- m_track = -1;
- m_pos = -1;
- m_block = 1; //default
-
- while (pos < stop)
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume ID
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume Size field
- assert((pos + size) <= stop);
-
- if (id == 0x77) //CueTrack ID
- m_track = UnserializeUInt(pReader, pos, size);
-
- else if (id == 0x71) //CueClusterPos ID
- m_pos = UnserializeUInt(pReader, pos, size);
-
- else if (id == 0x1378) //CueBlockNumber
- m_block = UnserializeUInt(pReader, pos, size);
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
-
- assert(m_pos >= 0);
- //assert(m_track > 0);
- //assert(m_block > 0);
-}
-
-
-const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const
-{
- assert(pTrack);
-
- const long long n = pTrack->GetNumber();
-
- const TrackPosition* i = m_track_positions;
- const TrackPosition* const j = i + m_track_positions_count;
-
- while (i != j)
- {
- const TrackPosition& p = *i++;
-
- if (p.m_track == n)
- return &p;
- }
-
- return NULL; //no matching track number found
-}
-
-
-long long CuePoint::GetTimeCode() const
-{
- return m_timecode;
-}
-
-long long CuePoint::GetTime(Segment* pSegment) const
-{
- assert(pSegment);
- assert(m_timecode >= 0);
-
- const SegmentInfo* const pInfo = pSegment->GetInfo();
- assert(pInfo);
-
- const long long scale = pInfo->GetTimeCodeScale();
- assert(scale >= 1);
-
- const long long time = scale * m_timecode;
-
- return time;
-}
-
-
-long long Segment::Unparsed() const
-{
- const long long stop = m_start + m_size;
-
- const long long result = stop - m_pos;
- assert(result >= 0);
-
- return result;
-}
-
-
-Cluster* Segment::GetFirst()
-{
- if ((m_clusters == NULL) || (m_clusterCount <= 0))
- return &m_eos;
-
- Cluster* const pCluster = m_clusters[0];
- assert(pCluster);
-
- return pCluster;
-}
-
-
-Cluster* Segment::GetLast()
-{
- if ((m_clusters == NULL) || (m_clusterCount <= 0))
- return &m_eos;
-
- const long idx = m_clusterCount - 1;
-
- Cluster* const pCluster = m_clusters[idx];
- assert(pCluster);
-
- return pCluster;
-}
-
-
-unsigned long Segment::GetCount() const
-{
- return m_clusterCount;
-}
-
-
-Cluster* Segment::GetNext(const Cluster* pCurr)
-{
- assert(pCurr);
- assert(pCurr != &m_eos);
- assert(m_clusters);
-
- long idx = pCurr->m_index;
-
- if (idx >= 0)
- {
- assert(m_clusterCount > 0);
- assert(idx < m_clusterCount);
- assert(pCurr == m_clusters[idx]);
-
- ++idx;
-
- if (idx >= m_clusterCount)
- return &m_eos; //caller will LoadCluster as desired
-
- Cluster* const pNext = m_clusters[idx];
- assert(pNext);
- assert(pNext->m_index >= 0);
- assert(pNext->m_index == idx);
-
- return pNext;
- }
-
- assert(m_clusterPreloadCount > 0);
-
- const long long off_ = pCurr->m_pos;
- const long long off = off_ * ((off_ < 0) ? -1 : 1);
-
- long long pos = m_start + off;
- const long long stop = m_start + m_size; //end of segment
-
- {
- long len;
-
- long long result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0); //TODO
- assert((pos + len) <= stop); //TODO
-
- const long long id = ReadUInt(m_pReader, pos, len);
- assert(id == 0x0F43B675); //Cluster ID //TODO
-
- pos += len; //consume ID
-
- //Read Size
- result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0); //TODO
- assert((pos + len) <= stop); //TODO
-
- const long long size = ReadUInt(m_pReader, pos, len);
- assert(size > 0); //TODO
- assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
-
- pos += len; //consume length of size of element
- assert((pos + size) <= stop); //TODO
-
- //Pos now points to start of payload
-
- pos += size; //consume payload
- }
-
- long long off_next = 0;
-
- while (pos < stop)
- {
- long len;
-
- long long result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0); //TODO
- assert((pos + len) <= stop); //TODO
-
- const long long idpos = pos; //pos of next (potential) cluster
-
- const long long id = ReadUInt(m_pReader, idpos, len);
- assert(id > 0); //TODO
-
- pos += len; //consume ID
-
- //Read Size
- result = GetUIntLength(m_pReader, pos, len);
- assert(result == 0); //TODO
- assert((pos + len) <= stop); //TODO
-
- const long long size = ReadUInt(m_pReader, pos, len);
- assert(size >= 0); //TODO
-
- pos += len; //consume length of size of element
- assert((pos + size) <= stop); //TODO
-
- //Pos now points to start of payload
-
- if (size == 0) //weird
- continue;
-
- if (id == 0x0F43B675) //Cluster ID
- {
- off_next = idpos - m_start;
- break;
- }
-
- pos += size; //consume payload
- }
-
- if (off_next <= 0)
- return 0;
-
- Cluster** const ii = m_clusters + m_clusterCount;
- Cluster** i = ii;
-
- Cluster** const jj = ii + m_clusterPreloadCount;
- Cluster** j = jj;
-
- while (i < j)
- {
- //INVARIANT:
- //[0, i) < pos_next
- //[i, j) ?
- //[j, jj) > pos_next
-
- Cluster** const k = i + (j - i) / 2;
- assert(k < jj);
-
- Cluster* const pNext = *k;
- assert(pNext);
- assert(pNext->m_index < 0);
-
- const long long pos_ = pNext->m_pos;
- assert(pos_);
-
- pos = pos_ * ((pos_ < 0) ? -1 : 1);
-
- if (pos < off_next)
- i = k + 1;
- else if (pos > off_next)
- j = k;
- else
- return pNext;
- }
-
- assert(i == j);
-
- Cluster* const pNext = Cluster::Parse(this, -1, off_next);
- const ptrdiff_t idx_next = i - m_clusters; //insertion position
-
- PreloadCluster(pNext, idx_next);
- assert(m_clusters);
- assert(idx_next < m_clusterSize);
- assert(m_clusters[idx_next] == pNext);
-
- return pNext;
-}
-
-
-Cluster* Segment::FindCluster(long long time_ns)
-{
- if ((m_clusters == NULL) || (m_clusterCount <= 0))
- return &m_eos;
-
- {
- Cluster* const pCluster = m_clusters[0];
- assert(pCluster);
- assert(pCluster->m_index == 0);
-
- if (time_ns <= pCluster->GetTime())
- return pCluster;
- }
-
- //Binary search of cluster array
-
- long i = 0;
- long j = m_clusterCount;
-
- while (i < j)
- {
- //INVARIANT:
- //[0, i) <= time_ns
- //[i, j) ?
- //[j, m_clusterCount) > time_ns
-
- const long k = i + (j - i) / 2;
- assert(k < m_clusterCount);
-
- Cluster* const pCluster = m_clusters[k];
- assert(pCluster);
- assert(pCluster->m_index == k);
-
- const long long t = pCluster->GetTime();
-
- if (t <= time_ns)
- i = k + 1;
- else
- j = k;
-
- assert(i <= j);
- }
-
- assert(i == j);
- assert(i > 0);
- assert(i <= m_clusterCount);
-
- const long k = i - 1;
-
- Cluster* const pCluster = m_clusters[k];
- assert(pCluster);
- assert(pCluster->m_index == k);
- assert(pCluster->GetTime() <= time_ns);
-
- return pCluster;
-}
-
-
-const BlockEntry* Segment::Seek(
- long long time_ns,
- const Track* pTrack)
-{
- assert(pTrack);
-
- if ((m_clusters == NULL) || (m_clusterCount <= 0))
- return pTrack->GetEOS();
-
- Cluster** const i = m_clusters;
- assert(i);
-
- {
- Cluster* const pCluster = *i;
- assert(pCluster);
- assert(pCluster->m_index == 0); //m_clusterCount > 0
- assert(pCluster->m_pSegment == this);
-
- if (time_ns <= pCluster->GetTime())
- return pCluster->GetEntry(pTrack);
- }
-
- Cluster** const j = i + m_clusterCount;
-
- if (pTrack->GetType() == 2) //audio
- {
- //TODO: we could decide to use cues for this, as we do for video.
- //But we only use it for video because looking around for a keyframe
- //can get expensive. Audio doesn't require anything special so a
- //straight cluster search is good enough (we assume).
-
- Cluster** lo = i;
- Cluster** hi = j;
-
- while (lo < hi)
- {
- //INVARIANT:
- //[i, lo) <= time_ns
- //[lo, hi) ?
- //[hi, j) > time_ns
-
- Cluster** const mid = lo + (hi - lo) / 2;
- assert(mid < hi);
-
- Cluster* const pCluster = *mid;
- assert(pCluster);
- assert(pCluster->m_index == long(mid - m_clusters));
- assert(pCluster->m_pSegment == this);
-
- const long long t = pCluster->GetTime();
-
- if (t <= time_ns)
- lo = mid + 1;
- else
- hi = mid;
-
- assert(lo <= hi);
- }
-
- assert(lo == hi);
- assert(lo > i);
- assert(lo <= j);
-
- Cluster* const pCluster = *--lo;
- assert(pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- return pCluster->GetEntry(pTrack);
- }
-
- assert(pTrack->GetType() == 1); //video
-
- Cluster** lo = i;
- Cluster** hi = j;
-
- while (lo < hi)
- {
- //INVARIANT:
- //[i, lo) <= time_ns
- //[lo, hi) ?
- //[hi, j) > time_ns
-
- Cluster** const mid = lo + (hi - lo) / 2;
- assert(mid < hi);
-
- Cluster* const pCluster = *mid;
- assert(pCluster);
-
- const long long t = pCluster->GetTime();
-
- if (t <= time_ns)
- lo = mid + 1;
- else
- hi = mid;
-
- assert(lo <= hi);
- }
-
- assert(lo == hi);
- assert(lo > i);
- assert(lo <= j);
-
- Cluster* pCluster = *--lo;
- assert(pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- {
- const BlockEntry* const pBlockEntry = pCluster->GetEntry(pTrack);
- assert(pBlockEntry);
-
- if (!pBlockEntry->EOS()) //found a keyframe
- {
- const Block* const pBlock = pBlockEntry->GetBlock();
- assert(pBlock);
-
- //TODO: this isn't necessarily the keyframe we want,
- //since there might another keyframe on this same
- //cluster with a greater timecode that but that is
- //still less than the requested time. For now we
- //simply return the first keyframe we find.
-
- if (pBlock->GetTime(pCluster) <= time_ns)
- return pBlockEntry;
- }
- }
-
- const VideoTrack* const pVideo = static_cast<const VideoTrack*>(pTrack);
-
- while (lo != i)
- {
- pCluster = *--lo;
- assert(pCluster);
- assert(pCluster->GetTime() <= time_ns);
-
- const BlockEntry* const pBlockEntry = pCluster->GetMaxKey(pVideo);
- assert(pBlockEntry);
-
- if (!pBlockEntry->EOS())
- return pBlockEntry;
- }
-
- //weird: we're on the first cluster, but no keyframe found
- //should never happen but we must return something anyway
-
- return pTrack->GetEOS();
-}
-
-
-#if 0
-bool Segment::SearchCues(
- long long time_ns,
- Track* pTrack,
- Cluster*& pCluster,
- const BlockEntry*& pBlockEntry,
- const CuePoint*& pCP,
- const CuePoint::TrackPosition*& pTP)
-{
- if (pTrack->GetType() != 1) //not video
- return false; //TODO: for now, just handle video stream
-
- if (m_pCues == NULL)
- return false;
-
- if (!m_pCues->Find(time_ns, pTrack, pCP, pTP))
- return false; //weird
-
- assert(pCP);
- assert(pTP);
- assert(pTP->m_track == pTrack->GetNumber());
-
- //We have the cue point and track position we want,
- //so we now need to search for the cluster having
- //the indicated position.
-
- return GetCluster(pCP, pTP, pCluster, pBlockEntry);
-}
-#endif
-
-
-Tracks* Segment::GetTracks() const
-{
- return m_pTracks;
-}
-
-
-const SegmentInfo* Segment::GetInfo() const
-{
- return m_pInfo;
-}
-
-
-const Cues* Segment::GetCues() const
-{
- return m_pCues;
-}
-
-
-long long Segment::GetDuration() const
-{
- assert(m_pInfo);
- return m_pInfo->GetDuration();
-}
-
-
-SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_) :
- m_pSegment(pSegment),
- m_start(start),
- m_size(size_),
- m_pMuxingAppAsUTF8(NULL),
- m_pWritingAppAsUTF8(NULL),
- m_pTitleAsUTF8(NULL)
-{
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long long pos = start;
- const long long stop = start + size_;
-
- m_timecodeScale = 1000000;
- m_duration = -1;
-
- while (pos < stop)
- {
- if (Match(pReader, pos, 0x0AD7B1, m_timecodeScale))
- assert(m_timecodeScale > 0);
-
- else if (Match(pReader, pos, 0x0489, m_duration))
- assert(m_duration >= 0);
-
- else if (Match(pReader, pos, 0x0D80, m_pMuxingAppAsUTF8)) //[4D][80]
- assert(m_pMuxingAppAsUTF8);
-
- else if (Match(pReader, pos, 0x1741, m_pWritingAppAsUTF8)) //[57][41]
- assert(m_pWritingAppAsUTF8);
-
- else if (Match(pReader, pos, 0x3BA9, m_pTitleAsUTF8)) //[7B][A9]
- assert(m_pTitleAsUTF8);
-
- else
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- //id;
- assert(id >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume id
- assert((stop - pos) > 0);
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0);
- assert((pos + len) <= stop);
-
- pos += len + size; //consume size and payload
- assert(pos <= stop);
- }
- }
-
- assert(pos == stop);
-}
-
-SegmentInfo::~SegmentInfo()
-{
- if (m_pMuxingAppAsUTF8)
- {
- delete[] m_pMuxingAppAsUTF8;
- m_pMuxingAppAsUTF8 = NULL;
- }
-
- if (m_pWritingAppAsUTF8)
- {
- delete[] m_pWritingAppAsUTF8;
- m_pWritingAppAsUTF8 = NULL;
- }
-
- if (m_pTitleAsUTF8)
- {
- delete[] m_pTitleAsUTF8;
- m_pTitleAsUTF8 = NULL;
- }
-}
-
-long long SegmentInfo::GetTimeCodeScale() const
-{
- return m_timecodeScale;
-}
-
-
-long long SegmentInfo::GetDuration() const
-{
- if (m_duration < 0)
- return -1;
-
- assert(m_timecodeScale >= 1);
-
- const double dd = double(m_duration) * double(m_timecodeScale);
- const long long d = static_cast<long long>(dd);
-
- return d;
-}
-
-const char* SegmentInfo::GetMuxingAppAsUTF8() const
-{
- return m_pMuxingAppAsUTF8;
-}
-
-
-const char* SegmentInfo::GetWritingAppAsUTF8() const
-{
- return m_pWritingAppAsUTF8;
-}
-
-const char* SegmentInfo::GetTitleAsUTF8() const
-{
- return m_pTitleAsUTF8;
-}
-
-Track::Track(Segment* pSegment, const Info& i) :
- m_pSegment(pSegment),
- m_info(i)
-{
-}
-
-Track::~Track()
-{
- Info& info = const_cast<Info&>(m_info);
- info.Clear();
-}
-
-Track::Info::Info():
- type(-1),
- number(-1),
- uid(-1),
- nameAsUTF8(NULL),
- codecId(NULL),
- codecPrivate(NULL),
- codecPrivateSize(0),
- codecNameAsUTF8(NULL)
-{
-}
-
-
-void Track::Info::Clear()
-{
- delete[] nameAsUTF8;
- nameAsUTF8 = NULL;
-
- delete[] codecId;
- codecId = NULL;
-
- delete[] codecPrivate;
- codecPrivate = NULL;
-
- codecPrivateSize = 0;
-
- delete[] codecNameAsUTF8;
- codecNameAsUTF8 = NULL;
-}
-
-const BlockEntry* Track::GetEOS() const
-{
- return &m_eos;
-}
-
-long long Track::GetType() const
-{
- return m_info.type;
-}
-
-long long Track::GetNumber() const
-{
- return m_info.number;
-}
-
-const char* Track::GetNameAsUTF8() const
-{
- return m_info.nameAsUTF8;
-}
-
-const char* Track::GetCodecNameAsUTF8() const
-{
- return m_info.codecNameAsUTF8;
-}
-
-
-const char* Track::GetCodecId() const
-{
- return m_info.codecId;
-}
-
-const unsigned char* Track::GetCodecPrivate(size_t& size) const
-{
- size = m_info.codecPrivateSize;
- return m_info.codecPrivate;
-}
-
-
-long Track::GetFirst(const BlockEntry*& pBlockEntry) const
-{
- Cluster* pCluster = m_pSegment->GetFirst();
-
- //If Segment::GetFirst returns NULL, then this must be a network
- //download, and we haven't loaded any clusters yet. In this case,
- //returning NULL from Track::GetFirst means the same thing.
-
- for (int i = 0; i < 100; ++i) //arbitrary upper bound
- {
- if (pCluster == NULL)
- {
- pBlockEntry = GetEOS();
- return 1;
- }
-
- if (pCluster->EOS())
- {
- if (m_pSegment->Unparsed() <= 0) //all clusters have been loaded
- {
- pBlockEntry = GetEOS();
- return 1;
- }
-
- pBlockEntry = 0;
- return E_BUFFER_NOT_FULL;
- }
-
- pBlockEntry = pCluster->GetFirst();
-
- while (pBlockEntry)
- {
- const Block* const pBlock = pBlockEntry->GetBlock();
- assert(pBlock);
-
- if (pBlock->GetTrackNumber() == m_info.number)
- return 0;
-
- pBlockEntry = pCluster->GetNext(pBlockEntry);
- }
-
- pCluster = m_pSegment->GetNext(pCluster);
- }
-
- //NOTE: if we get here, it means that we didn't find a block with
- //a matching track number. We interpret that as an error (which
- //might be too conservative).
-
- pBlockEntry = GetEOS(); //so we can return a non-NULL value
- return 1;
-}
-
-
-long Track::GetNext(
- const BlockEntry* pCurrEntry,
- const BlockEntry*& pNextEntry) const
-{
- assert(pCurrEntry);
- assert(!pCurrEntry->EOS()); //?
-
- const Block* const pCurrBlock = pCurrEntry->GetBlock();
- assert(pCurrBlock->GetTrackNumber() == m_info.number);
-
- Cluster* pCluster = pCurrEntry->GetCluster();
- assert(pCluster);
- assert(!pCluster->EOS());
-
- pNextEntry = pCluster->GetNext(pCurrEntry);
-
- for (int i = 0; i < 100; ++i) //arbitrary upper bound to search
- {
- while (pNextEntry)
- {
- const Block* const pNextBlock = pNextEntry->GetBlock();
- assert(pNextBlock);
-
- if (pNextBlock->GetTrackNumber() == m_info.number)
- return 0;
-
- pNextEntry = pCluster->GetNext(pNextEntry);
- }
-
- pCluster = m_pSegment->GetNext(pCluster);
-
- if (pCluster == NULL)
- {
- pNextEntry = GetEOS();
- return 1;
- }
-
- if (pCluster->EOS())
- {
- if (m_pSegment->Unparsed() <= 0) //all clusters have been loaded
- {
- pNextEntry = GetEOS();
- return 1;
- }
-
- //TODO: there is a potential O(n^2) problem here: we tell the
- //caller to (pre)load another cluster, which he does, but then he
- //calls GetNext again, which repeats the same search. This is
- //a pathological case, since the only way it can happen is if
- //there exists a long sequence of clusters none of which contain a
- // block from this track. One way around this problem is for the
- //caller to be smarter when he loads another cluster: don't call
- //us back until you have a cluster that contains a block from this
- //track. (Of course, that's not cheap either, since our caller
- //would have to scan the each cluster as it's loaded, so that
- //would just push back the problem.)
-
- pNextEntry = NULL;
- return E_BUFFER_NOT_FULL;
- }
-
- pNextEntry = pCluster->GetFirst();
- }
-
- //NOTE: if we get here, it means that we didn't find a block with
- //a matching track number after lots of searching, so we give
- //up trying.
-
- pNextEntry = GetEOS(); //so we can return a non-NULL value
- return 1;
-}
-
-
-Track::EOSBlock::EOSBlock()
-{
-}
-
-
-bool Track::EOSBlock::EOS() const
-{
- return true;
-}
-
-
-Cluster* Track::EOSBlock::GetCluster() const
-{
- return NULL;
-}
-
-
-size_t Track::EOSBlock::GetIndex() const
-{
- return 0;
-}
-
-
-const Block* Track::EOSBlock::GetBlock() const
-{
- return NULL;
-}
-
-
-bool Track::EOSBlock::IsBFrame() const
-{
- return false;
-}
-
-
-VideoTrack::VideoTrack(Segment* pSegment, const Info& i) :
- Track(pSegment, i),
- m_width(-1),
- m_height(-1),
- m_rate(-1)
-{
- assert(i.type == 1);
- assert(i.number > 0);
-
- IMkvReader* const pReader = pSegment->m_pReader;
-
- const Settings& s = i.settings;
- assert(s.start >= 0);
- assert(s.size >= 0);
-
- long long pos = s.start;
- assert(pos >= 0);
-
- const long long stop = pos + s.size;
-
- while (pos < stop)
- {
-#ifdef _DEBUG
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-#endif
- if (Match(pReader, pos, 0x30, m_width))
- ;
- else if (Match(pReader, pos, 0x3A, m_height))
- ;
- else if (Match(pReader, pos, 0x0383E3, m_rate))
- ;
- else
- {
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-
- pos += len; //consume length of size
- assert((pos + size) <= stop);
-
- //pos now designates start of payload
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
- }
-
- return;
-}
-
-
-bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const
-{
- assert(pBlockEntry);
-
- const Block* const pBlock = pBlockEntry->GetBlock();
- assert(pBlock);
- assert(pBlock->GetTrackNumber() == m_info.number);
-
- return pBlock->IsKey();
-}
-
-
-long long VideoTrack::GetWidth() const
-{
- return m_width;
-}
-
-
-long long VideoTrack::GetHeight() const
-{
- return m_height;
-}
-
-
-double VideoTrack::GetFrameRate() const
-{
- return m_rate;
-}
-
-
-AudioTrack::AudioTrack(Segment* pSegment, const Info& i) :
- Track(pSegment, i),
- m_rate(0.0),
- m_channels(0),
- m_bitDepth(-1)
-{
- assert(i.type == 2);
- assert(i.number > 0);
-
- IMkvReader* const pReader = pSegment->m_pReader;
-
- const Settings& s = i.settings;
- assert(s.start >= 0);
- assert(s.size >= 0);
-
- long long pos = s.start;
- assert(pos >= 0);
-
- const long long stop = pos + s.size;
-
- while (pos < stop)
- {
-#ifdef _DEBUG
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-#endif
- if (Match(pReader, pos, 0x35, m_rate))
- ;
- else if (Match(pReader, pos, 0x1F, m_channels))
- ;
- else if (Match(pReader, pos, 0x2264, m_bitDepth))
- ;
- else
- {
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-
- pos += len; //consume length of size
- assert((pos + size) <= stop);
-
- //pos now designates start of payload
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
- }
-
- return;
-}
-
-
-bool AudioTrack::VetEntry(const BlockEntry* pBlockEntry) const
-{
- assert(pBlockEntry);
-
- const Block* const pBlock = pBlockEntry->GetBlock();
- assert(pBlock);
- assert(pBlock->GetTrackNumber() == m_info.number);
-
- return true;
-}
-
-
-double AudioTrack::GetSamplingRate() const
-{
- return m_rate;
-}
-
-
-long long AudioTrack::GetChannels() const
-{
- return m_channels;
-}
-
-long long AudioTrack::GetBitDepth() const
-{
- return m_bitDepth;
-}
-
-Tracks::Tracks(Segment* pSegment, long long start, long long size_) :
- m_pSegment(pSegment),
- m_start(start),
- m_size(size_),
- m_trackEntries(NULL),
- m_trackEntriesEnd(NULL)
-{
- long long stop = m_start + m_size;
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long long pos1 = m_start;
- int count = 0;
-
- while (pos1 < stop)
- {
- long len;
- const long long id = ReadUInt(pReader, pos1, len);
- assert(id >= 0);
- assert((pos1 + len) <= stop);
-
- pos1 += len; //consume id
-
- const long long size = ReadUInt(pReader, pos1, len);
- assert(size >= 0);
- assert((pos1 + len) <= stop);
-
- pos1 += len; //consume length of size
-
- //pos now desinates start of element
- if (id == 0x2E) //TrackEntry ID
- ++count;
-
- pos1 += size; //consume payload
- assert(pos1 <= stop);
- }
-
- if (count <= 0)
- return;
-
- m_trackEntries = new Track*[count];
- m_trackEntriesEnd = m_trackEntries;
-
- long long pos = m_start;
-
- while (pos < stop)
- {
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long size1 = ReadUInt(pReader, pos, len);
- assert(size1 >= 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume length of size
-
- //pos now desinates start of element
-
- if (id == 0x2E) //TrackEntry ID
- ParseTrackEntry(pos, size1, *m_trackEntriesEnd++);
-
- pos += size1; //consume payload
- assert(pos <= stop);
- }
-}
-
-
-unsigned long Tracks::GetTracksCount() const
-{
- const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
- assert(result >= 0);
-
- return static_cast<unsigned long>(result);
-}
-
-
-void Tracks::ParseTrackEntry(
- long long start,
- long long size,
- Track*& pTrack)
-{
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- long long pos = start;
- const long long stop = start + size;
-
- Track::Info i;
-
- Track::Settings videoSettings;
- videoSettings.start = -1;
-
- Track::Settings audioSettings;
- audioSettings.start = -1;
-
- while (pos < stop)
- {
-#ifdef _DEBUG
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- len;
- id;
-#endif
- if (Match(pReader, pos, 0x57, i.number))
- assert(i.number > 0);
- else if (Match(pReader, pos, 0x33C5, i.uid))
- ;
- else if (Match(pReader, pos, 0x03, i.type))
- ;
- else if (Match(pReader, pos, 0x136E, i.nameAsUTF8))
- assert(i.nameAsUTF8);
- else if (Match(pReader, pos, 0x06, i.codecId))
- ;
- else if (Match(pReader,
- pos,
- 0x23A2,
- i.codecPrivate,
- i.codecPrivateSize))
- ;
- else if (Match(pReader, pos, 0x058688, i.codecNameAsUTF8))
- assert(i.codecNameAsUTF8);
- else
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO: handle error case
- assert((pos + len) <= stop);
-
- pos += len; //consume length of size
- const long long start = pos;
-
- pos += size; //consume payload
- assert(pos <= stop);
-
- if (id == 0x60)
- {
- videoSettings.start = start;
- videoSettings.size = size;
- }
- else if (id == 0x61)
- {
- audioSettings.start = start;
- audioSettings.size = size;
- }
- }
- }
-
- assert(pos == stop);
- //TODO: propertly vet info.number, to ensure both its existence,
- //and that it is unique among all tracks.
- assert(i.number > 0);
-
- //TODO: vet settings, to ensure that video settings (0x60)
- //were specified when type = 1, and that audio settings (0x61)
- //were specified when type = 2.
- if (i.type == 1) //video
- {
- assert(audioSettings.start < 0);
- assert(videoSettings.start >= 0);
-
- i.settings = videoSettings;
-
- VideoTrack* const t = new VideoTrack(m_pSegment, i);
- assert(t); //TODO
- pTrack = t;
- }
- else if (i.type == 2) //audio
- {
- assert(videoSettings.start < 0);
- assert(audioSettings.start >= 0);
-
- i.settings = audioSettings;
-
- AudioTrack* const t = new AudioTrack(m_pSegment, i);
- assert(t); //TODO
- pTrack = t;
- }
- else
- {
- // for now we do not support other track types yet.
- // TODO: support other track types
- i.Clear();
-
- pTrack = NULL;
- }
-
- return;
-}
-
-
-Tracks::~Tracks()
-{
- Track** i = m_trackEntries;
- Track** const j = m_trackEntriesEnd;
-
- while (i != j)
- {
- Track* const pTrack = *i++;
- delete pTrack;
- }
-
- delete[] m_trackEntries;
-}
-
-
-Track* Tracks::GetTrackByNumber(unsigned long tn_) const
-{
- const long long tn = tn_;
-
- Track** i = m_trackEntries;
- Track** const j = m_trackEntriesEnd;
-
- while (i != j)
- {
- Track* const pTrack = *i++;
-
- if (pTrack == NULL)
- continue;
-
- if (tn == pTrack->GetNumber())
- return pTrack;
- }
-
- return NULL; //not found
-}
-
-
-Track* Tracks::GetTrackByIndex(unsigned long idx) const
-{
- const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
-
- if (idx >= static_cast<unsigned long>(count))
- return NULL;
-
- return m_trackEntries[idx];
-}
-
-
-void Cluster::Load()
-{
- assert(m_pSegment);
- assert(m_pos);
- assert(m_size);
-
- if (m_pos > 0) //loaded
- {
- assert(m_size > 0);
- assert(m_timecode >= 0);
- return;
- }
-
- assert(m_pos < 0); //not loaded yet
- assert(m_size < 0);
- assert(m_timecode < 0);
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- m_pos *= -1; //relative to segment
- long long pos = m_pSegment->m_start + m_pos; //absolute
-
- long len;
-
- const long long id_ = ReadUInt(pReader, pos, len);
- assert(id_ >= 0);
- assert(id_ == 0x0F43B675); //Cluster ID
-
- pos += len; //consume id
-
- const long long size_ = ReadUInt(pReader, pos, len);
- assert(size_ >= 0);
-
- pos += len; //consume size
-
- m_size = size_;
- const long long stop = pos + size_;
-
- long long timecode = -1;
-
- while (pos < stop)
- {
- if (Match(pReader, pos, 0x67, timecode))
- break;
- else
- {
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume size
-
- if (id == 0x20) //BlockGroup ID
- break;
-
- if (id == 0x23) //SimpleBlock ID
- break;
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
- }
-
- assert(pos <= stop);
- assert(timecode >= 0);
-
- m_timecode = timecode;
-}
-
-
-Cluster* Cluster::Parse(
- Segment* pSegment,
- long idx,
- long long off)
-{
- assert(pSegment);
- assert(off >= 0);
- assert(off < pSegment->m_size);
-
- Cluster* const pCluster = new Cluster(pSegment, idx, -off);
- assert(pCluster);
-
- return pCluster;
-}
-
-
-Cluster::Cluster() :
- m_pSegment(NULL),
- m_index(0),
- m_pos(0),
- m_size(0),
- m_timecode(0),
- m_entries(NULL),
- m_entriesCount(0)
-{
-}
-
-
-Cluster::Cluster(
- Segment* pSegment,
- long idx,
- long long off) :
- m_pSegment(pSegment),
- m_index(idx),
- m_pos(off),
- m_size(-1),
- m_timecode(-1),
- m_entries(NULL),
- m_entriesCount(0)
-{
-}
-
-
-Cluster::~Cluster()
-{
- BlockEntry** i = m_entries;
- BlockEntry** const j = m_entries + m_entriesCount;
-
- while (i != j)
- {
- BlockEntry* p = *i++;
- assert(p);
-
- delete p;
- }
-
- delete[] m_entries;
-}
-
-
-bool Cluster::EOS() const
-{
- return (m_pSegment == NULL);
-}
-
-
-void Cluster::LoadBlockEntries()
-{
- if (m_entries)
- return;
-
- assert(m_pSegment);
- assert(m_pos);
- assert(m_size);
- assert(m_entriesCount == 0);
-
- IMkvReader* const pReader = m_pSegment->m_pReader;
-
- if (m_pos < 0)
- m_pos *= -1; //relative to segment
-
- long long pos = m_pSegment->m_start + m_pos; //absolute
-
- {
- long len;
-
- const long long id = ReadUInt(pReader, pos, len);
- id;
- assert(id >= 0);
- assert(id == 0x0F43B675); //Cluster ID
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size > 0);
-
- pos += len; //consume size
-
- //pos now points to start of payload
-
- if (m_size >= 0)
- assert(size == m_size);
- else
- m_size = size;
- }
-
- const long long stop = pos + m_size;
- long long timecode = -1; //of cluster itself
-
- //First count the number of entries
-
- long long idx = pos; //points to start of payload
- m_entriesCount = 0;
-
- while (idx < stop)
- {
- if (Match(pReader, idx, 0x67, timecode))
- {
- if (m_timecode >= 0)
- assert(timecode == m_timecode);
- else
- m_timecode = timecode;
- }
- else
- {
- long len;
-
- const long long id = ReadUInt(pReader, idx, len);
- assert(id >= 0); //TODO
- assert((idx + len) <= stop);
-
- idx += len; //consume id
-
- const long long size = ReadUInt(pReader, idx, len);
- assert(size >= 0); //TODO
- assert((idx + len) <= stop);
-
- idx += len; //consume size
-
- if (id == 0x20) //BlockGroup ID
- ++m_entriesCount;
- else if (id == 0x23) //SimpleBlock ID
- ++m_entriesCount;
-
- idx += size; //consume payload
- assert(idx <= stop);
- }
- }
-
- assert(idx == stop);
- assert(m_timecode >= 0);
-
- if (m_entriesCount == 0) //TODO: handle empty clusters
- return;
-
- m_entries = new BlockEntry*[m_entriesCount];
- size_t index = 0;
-
- while (pos < stop)
- {
- if (Match(pReader, pos, 0x67, timecode))
- assert(timecode == m_timecode);
- else
- {
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume id
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume size
-
- if (id == 0x20) //BlockGroup ID
- ParseBlockGroup(pos, size, index++);
- else if (id == 0x23) //SimpleBlock ID
- ParseSimpleBlock(pos, size, index++);
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
- }
-
- assert(pos == stop);
- assert(timecode >= 0);
- assert(index == m_entriesCount);
-}
-
-
-
-long long Cluster::GetTimeCode()
-{
- Load();
- return m_timecode;
-}
-
-
-long long Cluster::GetTime()
-{
- const long long tc = GetTimeCode();
- assert(tc >= 0);
-
- const SegmentInfo* const pInfo = m_pSegment->GetInfo();
- assert(pInfo);
-
- const long long scale = pInfo->GetTimeCodeScale();
- assert(scale >= 1);
-
- const long long t = m_timecode * scale;
-
- return t;
-}
-
-
-long long Cluster::GetFirstTime()
-{
- const BlockEntry* const pEntry = GetFirst();
-
- if (pEntry == NULL) //empty cluster
- return GetTime();
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- return pBlock->GetTime(this);
-}
-
-
-long long Cluster::GetLastTime()
-{
- const BlockEntry* const pEntry = GetLast();
-
- if (pEntry == NULL) //empty cluster
- return GetTime();
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- return pBlock->GetTime(this);
-}
-
-
-void Cluster::ParseBlockGroup(long long start, long long size, size_t index)
-{
- assert(m_entries);
- assert(m_entriesCount);
- assert(index < m_entriesCount);
-
- BlockGroup* const pGroup =
- new (std::nothrow) BlockGroup(this, index, start, size);
- assert(pGroup); //TODO
-
- m_entries[index] = pGroup;
-}
-
-
-
-void Cluster::ParseSimpleBlock(long long start, long long size, size_t index)
-{
- assert(m_entries);
- assert(m_entriesCount);
- assert(index < m_entriesCount);
-
- SimpleBlock* const pSimpleBlock =
- new (std::nothrow) SimpleBlock(this, index, start, size);
- assert(pSimpleBlock); //TODO
-
- m_entries[index] = pSimpleBlock;
-}
-
-
-const BlockEntry* Cluster::GetFirst()
-{
- LoadBlockEntries();
- //assert(m_entries);
- //assert(m_entriesCount >= 1);
-
- if ((m_entries == NULL) || (m_entriesCount == 0))
- return NULL;
-
- const BlockEntry* const pFirst = m_entries[0];
- assert(pFirst);
-
- return pFirst;
-}
-
-
-const BlockEntry* Cluster::GetLast()
-{
- LoadBlockEntries();
- //assert(m_entries);
- //assert(m_entriesCount >= 1);
-
- if ((m_entries == NULL) || (m_entriesCount == 0))
- return NULL;
-
- const size_t idx = m_entriesCount - 1;
-
- const BlockEntry* const pLast = m_entries[idx];
- assert(pLast);
-
- return pLast;
-}
-
-
-const BlockEntry* Cluster::GetNext(const BlockEntry* pEntry) const
-{
- assert(pEntry);
- assert(m_entries);
- assert(m_entriesCount);
-
- size_t idx = pEntry->GetIndex();
- assert(idx < m_entriesCount);
- assert(m_entries[idx] == pEntry);
-
- ++idx;
-
- if (idx >= m_entriesCount)
- return NULL;
-
- return m_entries[idx];
-}
-
-
-const BlockEntry* Cluster::GetEntry(const Track* pTrack)
-{
- assert(pTrack);
-
- if (m_pSegment == NULL) //EOS
- return pTrack->GetEOS();
-
- LoadBlockEntries();
-
- if ((m_entries == NULL) || (m_entriesCount == 0))
- return NULL;
-
- BlockEntry** i = m_entries;
- assert(i);
-
- BlockEntry** const j = i + m_entriesCount;
-
- while (i != j)
- {
- const BlockEntry* const pEntry = *i++;
- assert(pEntry);
- assert(!pEntry->EOS());
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- if (pBlock->GetTrackNumber() != pTrack->GetNumber())
- continue;
-
- if (pTrack->VetEntry(pEntry))
- return pEntry;
- }
-
- return pTrack->GetEOS(); //no satisfactory block found
-}
-
-
-const BlockEntry*
-Cluster::GetEntry(
- const CuePoint& cp,
- const CuePoint::TrackPosition& tp)
-{
- assert(m_pSegment);
-
- LoadBlockEntries();
-
- if (m_entries == NULL)
- return NULL;
-
- const long long count = m_entriesCount;
-
- if (count <= 0)
- return NULL;
-
- const long long tc = cp.GetTimeCode();
-
- if ((tp.m_block > 0) && (tp.m_block <= count))
- {
- const size_t block = static_cast<size_t>(tp.m_block);
- const size_t index = block - 1;
-
- const BlockEntry* const pEntry = m_entries[index];
- assert(pEntry);
- assert(!pEntry->EOS());
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- if ((pBlock->GetTrackNumber() == tp.m_track) &&
- (pBlock->GetTimeCode(this) == tc))
- {
- return pEntry;
- }
- }
-
- const BlockEntry* const* i = m_entries;
- const BlockEntry* const* const j = i + count;
-
- while (i != j)
- {
- const BlockEntry* const pEntry = *i++;
- assert(pEntry);
- assert(!pEntry->EOS());
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- if (pBlock->GetTrackNumber() != tp.m_track)
- continue;
-
- const long long tc_ = pBlock->GetTimeCode(this);
-
- if (tc_ < tc)
- continue;
-
- if (tc_ > tc)
- return NULL;
-
- const Tracks* const pTracks = m_pSegment->GetTracks();
- assert(pTracks);
-
- const long tn = static_cast<long>(tp.m_track);
- const Track* const pTrack = pTracks->GetTrackByNumber(tn);
-
- if (pTrack == NULL)
- return NULL;
-
- const long long type = pTrack->GetType();
-
- if (type == 2) //audio
- return pEntry;
-
- if (type != 1) //not video
- return NULL;
-
- if (!pBlock->IsKey())
- return NULL;
-
- return pEntry;
- }
-
- return NULL;
-}
-
-
-const BlockEntry* Cluster::GetMaxKey(const VideoTrack* pTrack)
-{
- assert(pTrack);
-
- if (m_pSegment == NULL) //EOS
- return pTrack->GetEOS();
-
- LoadBlockEntries();
- //assert(m_entries);
-
- BlockEntry** i = m_entries + m_entriesCount;
- BlockEntry** const j = m_entries;
-
- while (i != j)
- {
- const BlockEntry* const pEntry = *--i;
- assert(pEntry);
- assert(!pEntry->EOS());
-
- const Block* const pBlock = pEntry->GetBlock();
- assert(pBlock);
-
- if (pBlock->GetTrackNumber() != pTrack->GetNumber())
- continue;
-
- if (pBlock->IsKey())
- return pEntry;
- }
-
- return pTrack->GetEOS(); //no satisfactory block found
-}
-
-
-
-BlockEntry::BlockEntry()
-{
-}
-
-
-BlockEntry::~BlockEntry()
-{
-}
-
-
-SimpleBlock::SimpleBlock(
- Cluster* pCluster,
- size_t idx,
- long long start,
- long long size) :
- m_pCluster(pCluster),
- m_index(idx),
- m_block(start, size, pCluster->m_pSegment->m_pReader)
-{
-}
-
-
-bool SimpleBlock::EOS() const
-{
- return false;
-}
-
-
-Cluster* SimpleBlock::GetCluster() const
-{
- return m_pCluster;
-}
-
-
-size_t SimpleBlock::GetIndex() const
-{
- return m_index;
-}
-
-
-const Block* SimpleBlock::GetBlock() const
-{
- return &m_block;
-}
-
-
-bool SimpleBlock::IsBFrame() const
-{
- return false;
-}
-
-
-BlockGroup::BlockGroup(
- Cluster* pCluster,
- size_t idx,
- long long start,
- long long size_) :
- m_pCluster(pCluster),
- m_index(idx),
- m_prevTimeCode(0),
- m_nextTimeCode(0),
- m_pBlock(NULL) //TODO: accept multiple blocks within a block group
-{
- IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
-
- long long pos = start;
- const long long stop = start + size_;
-
- bool bSimpleBlock = false;
- bool bReferenceBlock = false;
-
- while (pos < stop)
- {
- short t;
-
- if (Match(pReader, pos, 0x7B, t))
- {
- if (t < 0)
- m_prevTimeCode = t;
- else if (t > 0)
- m_nextTimeCode = t;
- else
- assert(false);
-
- bReferenceBlock = true;
- }
- else
- {
- long len;
- const long long id = ReadUInt(pReader, pos, len);
- assert(id >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume ID
-
- const long long size = ReadUInt(pReader, pos, len);
- assert(size >= 0); //TODO
- assert((pos + len) <= stop);
-
- pos += len; //consume size
-
- switch (id)
- {
- case 0x23: //SimpleBlock ID
- bSimpleBlock = true;
- //YES, FALL THROUGH TO NEXT CASE
-
- case 0x21: //Block ID
- ParseBlock(pos, size);
- break;
-
- default:
- break;
- }
-
- pos += size; //consume payload
- assert(pos <= stop);
- }
- }
-
- assert(pos == stop);
- assert(m_pBlock);
-
- if (!bSimpleBlock)
- m_pBlock->SetKey(!bReferenceBlock);
-}
-
-
-BlockGroup::~BlockGroup()
-{
- delete m_pBlock;
-}
-
-
-void BlockGroup::ParseBlock(long long start, long long size)
-{
- IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
-
- Block* const pBlock = new Block(start, size, pReader);
- assert(pBlock); //TODO
-
- //TODO: the Matroska spec says you have multiple blocks within the
- //same block group, with blocks ranked by priority (the flag bits).
-
- assert(m_pBlock == NULL);
- m_pBlock = pBlock;
-}
-
-
-bool BlockGroup::EOS() const
-{
- return false;
-}
-
-
-Cluster* BlockGroup::GetCluster() const
-{
- return m_pCluster;
-}
-
-
-size_t BlockGroup::GetIndex() const
-{
- return m_index;
-}
-
-
-const Block* BlockGroup::GetBlock() const
-{
- return m_pBlock;
-}
-
-
-short BlockGroup::GetPrevTimeCode() const
-{
- return m_prevTimeCode;
-}
-
-
-short BlockGroup::GetNextTimeCode() const
-{
- return m_nextTimeCode;
-}
-
-
-bool BlockGroup::IsBFrame() const
-{
- return (m_nextTimeCode > 0);
-}
-
-
-
-Block::Block(long long start, long long size_, IMkvReader* pReader) :
- m_start(start),
- m_size(size_)
-{
- long long pos = start;
- const long long stop = start + size_;
-
- long len;
-
- m_track = ReadUInt(pReader, pos, len);
- assert(m_track > 0);
- assert((pos + len) <= stop);
-
- pos += len; //consume track number
- assert((stop - pos) >= 2);
-
- m_timecode = Unserialize2SInt(pReader, pos);
-
- pos += 2;
- assert((stop - pos) >= 1);
-
- const long hr = pReader->Read(pos, 1, &m_flags);
- assert(hr == 0L);
-
- ++pos;
- assert(pos <= stop);
-
- m_frameOff = pos;
-
- const long long frame_size = stop - pos;
-
- assert(frame_size <= 2147483647L);
-
- m_frameSize = static_cast<long>(frame_size);
-}
-
-
-long long Block::GetTimeCode(Cluster* pCluster) const
-{
- assert(pCluster);
-
- const long long tc0 = pCluster->GetTimeCode();
- assert(tc0 >= 0);
-
- const long long tc = tc0 + static_cast<long long>(m_timecode);
- assert(tc >= 0);
-
- return tc; //unscaled timecode units
-}
-
-
-long long Block::GetTime(Cluster* pCluster) const
-{
- assert(pCluster);
-
- const long long tc = GetTimeCode(pCluster);
-
- const Segment* const pSegment = pCluster->m_pSegment;
- const SegmentInfo* const pInfo = pSegment->GetInfo();
- assert(pInfo);
-
- const long long scale = pInfo->GetTimeCodeScale();
- assert(scale >= 1);
-
- const long long ns = tc * scale;
-
- return ns;
-}
-
-
-long long Block::GetTrackNumber() const
-{
- return m_track;
-}
-
-
-bool Block::IsKey() const
-{
- return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
-}
-
-unsigned char Block::Flags() const {
- return m_flags;
-}
-
-void Block::SetKey(bool bKey)
-{
- if (bKey)
- m_flags |= static_cast<unsigned char>(1 << 7);
- else
- m_flags &= 0x7F;
-}
-
-
-long long Block::GetOffset() const
-{
- return m_frameOff;
-}
-
-
-long Block::GetSize() const
-{
- return m_frameSize;
-}
-
-
-long Block::Read(IMkvReader* pReader, unsigned char* buf) const
-{
-
- assert(pReader);
- assert(buf);
-
- const long hr = pReader->Read(m_frameOff, m_frameSize, buf);
-
- return hr;
-}
-
-
-} //end namespace mkvparser
diff --git a/media/libstagefright/matroska/mkvparser.hpp b/media/libstagefright/matroska/mkvparser.hpp
deleted file mode 100644
index f7d8948..0000000
--- a/media/libstagefright/matroska/mkvparser.hpp
+++ /dev/null
@@ -1,556 +0,0 @@
-// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-
-#ifndef MKVPARSER_HPP
-#define MKVPARSER_HPP
-
-#include <cstdlib>
-#include <cstdio>
-
-namespace mkvparser
-{
-
-const int E_FILE_FORMAT_INVALID = -2;
-const int E_BUFFER_NOT_FULL = -3;
-
-class IMkvReader
-{
-public:
- virtual int Read(long long pos, long len, unsigned char* buf) = 0;
- virtual int Length(long long* total, long long* available) = 0;
-protected:
- virtual ~IMkvReader();
-};
-
-long long GetUIntLength(IMkvReader*, long long, long&);
-long long ReadUInt(IMkvReader*, long long, long&);
-long long SyncReadUInt(IMkvReader*, long long pos, long long stop, long&);
-long long UnserializeUInt(IMkvReader*, long long pos, long long size);
-float Unserialize4Float(IMkvReader*, long long);
-double Unserialize8Double(IMkvReader*, long long);
-short Unserialize2SInt(IMkvReader*, long long);
-signed char Unserialize1SInt(IMkvReader*, long long);
-bool Match(IMkvReader*, long long&, unsigned long, long long&);
-bool Match(IMkvReader*, long long&, unsigned long, char*&);
-bool Match(IMkvReader*, long long&, unsigned long,unsigned char*&, size_t&);
-bool Match(IMkvReader*, long long&, unsigned long, double&);
-bool Match(IMkvReader*, long long&, unsigned long, short&);
-
-void GetVersion(int& major, int& minor, int& build, int& revision);
-
-struct EBMLHeader
-{
- EBMLHeader();
- ~EBMLHeader();
- long long m_version;
- long long m_readVersion;
- long long m_maxIdLength;
- long long m_maxSizeLength;
- char* m_docType;
- long long m_docTypeVersion;
- long long m_docTypeReadVersion;
-
- long long Parse(IMkvReader*, long long&);
-};
-
-
-class Segment;
-class Track;
-class Cluster;
-
-class Block
-{
- Block(const Block&);
- Block& operator=(const Block&);
-
-public:
- const long long m_start;
- const long long m_size;
-
- Block(long long start, long long size, IMkvReader*);
-
- long long GetTrackNumber() const;
- long long GetTimeCode(Cluster*) const; //absolute, but not scaled
- long long GetTime(Cluster*) const; //absolute, and scaled (ns units)
- bool IsKey() const;
- void SetKey(bool);
-
- unsigned char Flags() const;
-
- long long GetOffset() const;
- long GetSize() const;
- long Read(IMkvReader*, unsigned char*) const;
-
-private:
- long long m_track; //Track::Number()
- short m_timecode; //relative to cluster
- unsigned char m_flags;
- long long m_frameOff;
- long m_frameSize;
-
-};
-
-
-class BlockEntry
-{
- BlockEntry(const BlockEntry&);
- BlockEntry& operator=(const BlockEntry&);
-
-public:
- virtual ~BlockEntry();
- virtual bool EOS() const = 0;
- virtual Cluster* GetCluster() const = 0;
- virtual size_t GetIndex() const = 0;
- virtual const Block* GetBlock() const = 0;
- virtual bool IsBFrame() const = 0;
-
-protected:
- BlockEntry();
-
-};
-
-
-class SimpleBlock : public BlockEntry
-{
- SimpleBlock(const SimpleBlock&);
- SimpleBlock& operator=(const SimpleBlock&);
-
-public:
- SimpleBlock(Cluster*, size_t, long long start, long long size);
-
- bool EOS() const;
- Cluster* GetCluster() const;
- size_t GetIndex() const;
- const Block* GetBlock() const;
- bool IsBFrame() const;
-
-protected:
- Cluster* const m_pCluster;
- const size_t m_index;
- Block m_block;
-
-};
-
-
-class BlockGroup : public BlockEntry
-{
- BlockGroup(const BlockGroup&);
- BlockGroup& operator=(const BlockGroup&);
-
-public:
- BlockGroup(Cluster*, size_t, long long, long long);
- ~BlockGroup();
-
- bool EOS() const;
- Cluster* GetCluster() const;
- size_t GetIndex() const;
- const Block* GetBlock() const;
- bool IsBFrame() const;
-
- short GetPrevTimeCode() const; //relative to block's time
- short GetNextTimeCode() const; //as above
-
-protected:
- Cluster* const m_pCluster;
- const size_t m_index;
-
-private:
- BlockGroup(Cluster*, size_t, unsigned long);
- void ParseBlock(long long start, long long size);
-
- short m_prevTimeCode;
- short m_nextTimeCode;
-
- //TODO: the Matroska spec says you can have multiple blocks within the
- //same block group, with blocks ranked by priority (the flag bits).
- //For now we just cache a single block.
-#if 0
- typedef std::deque<Block*> blocks_t;
- blocks_t m_blocks; //In practice should contain only a single element.
-#else
- Block* m_pBlock;
-#endif
-
-};
-
-
-class Track
-{
- Track(const Track&);
- Track& operator=(const Track&);
-
-public:
- Segment* const m_pSegment;
- virtual ~Track();
-
- long long GetType() const;
- long long GetNumber() const;
- const char* GetNameAsUTF8() const;
- const char* GetCodecNameAsUTF8() const;
- const char* GetCodecId() const;
- const unsigned char* GetCodecPrivate(size_t&) const;
-
- const BlockEntry* GetEOS() const;
-
- struct Settings
- {
- long long start;
- long long size;
- };
-
- struct Info
- {
- long long type;
- long long number;
- long long uid;
- char* nameAsUTF8;
- char* codecId;
- unsigned char* codecPrivate;
- size_t codecPrivateSize;
- char* codecNameAsUTF8;
- Settings settings;
- Info();
- void Clear();
- };
-
- long GetFirst(const BlockEntry*&) const;
- long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;
- virtual bool VetEntry(const BlockEntry*) const = 0;
-
-protected:
- Track(Segment*, const Info&);
- const Info m_info;
-
- class EOSBlock : public BlockEntry
- {
- public:
- EOSBlock();
-
- bool EOS() const;
- Cluster* GetCluster() const;
- size_t GetIndex() const;
- const Block* GetBlock() const;
- bool IsBFrame() const;
- };
-
- EOSBlock m_eos;
-
-};
-
-
-class VideoTrack : public Track
-{
- VideoTrack(const VideoTrack&);
- VideoTrack& operator=(const VideoTrack&);
-
-public:
- VideoTrack(Segment*, const Info&);
- long long GetWidth() const;
- long long GetHeight() const;
- double GetFrameRate() const;
-
- bool VetEntry(const BlockEntry*) const;
-
-private:
- long long m_width;
- long long m_height;
- double m_rate;
-
-};
-
-
-class AudioTrack : public Track
-{
- AudioTrack(const AudioTrack&);
- AudioTrack& operator=(const AudioTrack&);
-
-public:
- AudioTrack(Segment*, const Info&);
- double GetSamplingRate() const;
- long long GetChannels() const;
- long long GetBitDepth() const;
- bool VetEntry(const BlockEntry*) const;
-
-private:
- double m_rate;
- long long m_channels;
- long long m_bitDepth;
-};
-
-
-class Tracks
-{
- Tracks(const Tracks&);
- Tracks& operator=(const Tracks&);
-
-public:
- Segment* const m_pSegment;
- const long long m_start;
- const long long m_size;
-
- Tracks(Segment*, long long start, long long size);
- virtual ~Tracks();
-
- Track* GetTrackByNumber(unsigned long tn) const;
- Track* GetTrackByIndex(unsigned long idx) const;
-
-private:
- Track** m_trackEntries;
- Track** m_trackEntriesEnd;
-
- void ParseTrackEntry(long long, long long, Track*&);
-
-public:
- unsigned long GetTracksCount() const;
-};
-
-
-class SegmentInfo
-{
- SegmentInfo(const SegmentInfo&);
- SegmentInfo& operator=(const SegmentInfo&);
-
-public:
- Segment* const m_pSegment;
- const long long m_start;
- const long long m_size;
-
- SegmentInfo(Segment*, long long start, long long size);
- ~SegmentInfo();
- long long GetTimeCodeScale() const;
- long long GetDuration() const; //scaled
- const char* GetMuxingAppAsUTF8() const;
- const char* GetWritingAppAsUTF8() const;
- const char* GetTitleAsUTF8() const;
-
-private:
- long long m_timecodeScale;
- double m_duration;
- char* m_pMuxingAppAsUTF8;
- char* m_pWritingAppAsUTF8;
- char* m_pTitleAsUTF8;
-};
-
-class Cues;
-class CuePoint
-{
- friend class Cues;
-
- CuePoint(size_t, long long);
- ~CuePoint();
-
- CuePoint(const CuePoint&);
- CuePoint& operator=(const CuePoint&);
-
-public:
- void Load(IMkvReader*);
-
- long long GetTimeCode() const; //absolute but unscaled
- long long GetTime(Segment*) const; //absolute and scaled (ns units)
-
- struct TrackPosition
- {
- long long m_track;
- long long m_pos; //of cluster
- long long m_block;
- //codec_state //defaults to 0
- //reference = clusters containing req'd referenced blocks
- // reftime = timecode of the referenced block
-
- void Parse(IMkvReader*, long long, long long);
- };
-
- const TrackPosition* Find(const Track*) const;
-
-private:
- const size_t m_index;
- long long m_timecode;
- TrackPosition* m_track_positions;
- size_t m_track_positions_count;
-
-};
-
-
-class Cues
-{
- friend class Segment;
-
- Cues(Segment*, long long start, long long size);
- ~Cues();
-
- Cues(const Cues&);
- Cues& operator=(const Cues&);
-
-public:
- Segment* const m_pSegment;
- const long long m_start;
- const long long m_size;
-
- bool Find( //lower bound of time_ns
- long long time_ns,
- const Track*,
- const CuePoint*&,
- const CuePoint::TrackPosition*&) const;
-
-#if 0
- bool FindNext( //upper_bound of time_ns
- long long time_ns,
- const Track*,
- const CuePoint*&,
- const CuePoint::TrackPosition*&) const;
-#endif
-
- const CuePoint* GetFirst() const;
- const CuePoint* GetLast() const;
-
- const CuePoint* GetNext(const CuePoint*) const;
-
- const BlockEntry* GetBlock(
- const CuePoint*,
- const CuePoint::TrackPosition*) const;
-
-private:
- void Init() const;
- bool LoadCuePoint() const;
- void PreloadCuePoint(size_t&, long long) const;
-
- mutable CuePoint** m_cue_points;
- mutable size_t m_count;
- mutable size_t m_preload_count;
- mutable long long m_pos;
-
-};
-
-
-class Cluster
-{
- Cluster(const Cluster&);
- Cluster& operator=(const Cluster&);
-
-public:
- Segment* const m_pSegment;
-
-public:
- static Cluster* Parse(Segment*, long, long long off);
-
- Cluster(); //EndOfStream
- ~Cluster();
-
- bool EOS() const;
-
- long long GetTimeCode(); //absolute, but not scaled
- long long GetTime(); //absolute, and scaled (nanosecond units)
- long long GetFirstTime(); //time (ns) of first (earliest) block
- long long GetLastTime(); //time (ns) of last (latest) block
-
- const BlockEntry* GetFirst();
- const BlockEntry* GetLast();
- const BlockEntry* GetNext(const BlockEntry*) const;
- const BlockEntry* GetEntry(const Track*);
- const BlockEntry* GetEntry(
- const CuePoint&,
- const CuePoint::TrackPosition&);
- const BlockEntry* GetMaxKey(const VideoTrack*);
-
-protected:
- Cluster(Segment*, long, long long off);
-
-public:
- //TODO: these should all be private, with public selector functions
- long m_index;
- long long m_pos;
- long long m_size;
-
-private:
- long long m_timecode;
- BlockEntry** m_entries;
- size_t m_entriesCount;
-
- void Load();
- void LoadBlockEntries();
- void ParseBlockGroup(long long, long long, size_t);
- void ParseSimpleBlock(long long, long long, size_t);
-
-};
-
-
-class Segment
-{
- friend class Cues;
-
- Segment(const Segment&);
- Segment& operator=(const Segment&);
-
-private:
- Segment(IMkvReader*, long long pos, long long size);
-
-public:
- IMkvReader* const m_pReader;
- const long long m_start; //posn of segment payload
- const long long m_size; //size of segment payload
- Cluster m_eos; //TODO: make private?
-
- static long long CreateInstance(IMkvReader*, long long, Segment*&);
- ~Segment();
-
- long Load(); //loads headers and all clusters
-
- //for incremental loading (splitter)
- long long Unparsed() const;
- long long ParseHeaders(); //stops when first cluster is found
- long LoadCluster(); //loads one cluster
-
-#if 0
- //This pair parses one cluster, but only changes the state of the
- //segment object when the cluster is actually added to the index.
- long ParseCluster(Cluster*&, long long& newpos) const;
- bool AddCluster(Cluster*, long long);
-#endif
-
- Tracks* GetTracks() const;
- const SegmentInfo* GetInfo() const;
- const Cues* GetCues() const;
-
- long long GetDuration() const;
-
- unsigned long GetCount() const;
- Cluster* GetFirst();
- Cluster* GetLast();
- Cluster* GetNext(const Cluster*);
-
- Cluster* FindCluster(long long time_nanoseconds);
- const BlockEntry* Seek(long long time_nanoseconds, const Track*);
-
-private:
-
- long long m_pos; //absolute file posn; what has been consumed so far
- SegmentInfo* m_pInfo;
- Tracks* m_pTracks;
- Cues* m_pCues;
- Cluster** m_clusters;
- long m_clusterCount; //number of entries for which m_index >= 0
- long m_clusterPreloadCount; //number of entries for which m_index < 0
- long m_clusterSize; //array size
-
- void AppendCluster(Cluster*);
- void PreloadCluster(Cluster*, ptrdiff_t);
-
- void ParseSeekHead(long long pos, long long size);
- void ParseSeekEntry(long long pos, long long size);
- void ParseCues(long long);
-
- const BlockEntry* GetBlock(
- const CuePoint&,
- const CuePoint::TrackPosition&);
-
-};
-
-
-} //end namespace mkvparser
-
-#endif //MKVPARSER_HPP
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 4335b99..7d4bc6e 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -32,6 +32,7 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+#include <media/IStreamSource.h>
#include <utils/KeyedVector.h>
namespace android {
@@ -49,7 +50,9 @@
unsigned pid, unsigned payload_unit_start_indicator,
ABitReader *br);
- void signalDiscontinuity(DiscontinuityType type);
+ void signalDiscontinuity(
+ DiscontinuityType type, const sp<AMessage> &extra);
+
void signalEOS(status_t finalResult);
sp<MediaSource> getSource(SourceType type);
@@ -83,7 +86,9 @@
unsigned payload_unit_start_indicator,
ABitReader *br);
- void signalDiscontinuity(DiscontinuityType type);
+ void signalDiscontinuity(
+ DiscontinuityType type, const sp<AMessage> &extra);
+
void signalEOS(status_t finalResult);
sp<MediaSource> getSource(SourceType type);
@@ -100,6 +105,7 @@
sp<AnotherPacketSource> mSource;
bool mPayloadStarted;
DiscontinuityType mPendingDiscontinuity;
+ sp<AMessage> mPendingDiscontinuityExtra;
ElementaryStreamQueue mQueue;
@@ -112,7 +118,8 @@
void extractAACFrames(const sp<ABuffer> &buffer);
- void deferDiscontinuity(DiscontinuityType type);
+ void deferDiscontinuity(
+ DiscontinuityType type, const sp<AMessage> &extra);
DISALLOW_EVIL_CONSTRUCTORS(Stream);
};
@@ -150,9 +157,10 @@
return true;
}
-void ATSParser::Program::signalDiscontinuity(DiscontinuityType type) {
+void ATSParser::Program::signalDiscontinuity(
+ DiscontinuityType type, const sp<AMessage> &extra) {
for (size_t i = 0; i < mStreams.size(); ++i) {
- mStreams.editValueAt(i)->signalDiscontinuity(type);
+ mStreams.editValueAt(i)->signalDiscontinuity(type, extra);
}
}
@@ -283,7 +291,8 @@
mStreams.add(info.mPID, stream);
if (PIDsChanged) {
- stream->signalDiscontinuity(DISCONTINUITY_FORMATCHANGE);
+ sp<AMessage> extra;
+ stream->signalDiscontinuity(DISCONTINUITY_FORMATCHANGE, extra);
}
}
}
@@ -360,32 +369,29 @@
size_t payloadSizeBits = br->numBitsLeft();
CHECK_EQ(payloadSizeBits % 8, 0u);
- CHECK_LE(mBuffer->size() + payloadSizeBits / 8, mBuffer->capacity());
+ size_t neededSize = mBuffer->size() + payloadSizeBits / 8;
+ if (mBuffer->capacity() < neededSize) {
+ // Increment in multiples of 64K.
+ neededSize = (neededSize + 65535) & ~65535;
+
+ LOGI("resizing buffer to %d bytes", neededSize);
+
+ sp<ABuffer> newBuffer = new ABuffer(neededSize);
+ memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+ newBuffer->setRange(0, mBuffer->size());
+ mBuffer = newBuffer;
+ }
memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8);
mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
}
-void ATSParser::Stream::signalDiscontinuity(DiscontinuityType type) {
+void ATSParser::Stream::signalDiscontinuity(
+ DiscontinuityType type, const sp<AMessage> &extra) {
mPayloadStarted = false;
mBuffer->setRange(0, 0);
switch (type) {
- case DISCONTINUITY_HTTPLIVE:
- {
- mQueue.clear(true);
-
- if (mStreamType == 0x1b) {
- // Don't signal discontinuities on audio streams.
- if (mSource != NULL) {
- mSource->queueDiscontinuity(type);
- } else {
- deferDiscontinuity(type);
- }
- }
- break;
- }
-
case DISCONTINUITY_SEEK:
case DISCONTINUITY_FORMATCHANGE:
{
@@ -393,10 +399,21 @@
mQueue.clear(!isASeek);
+ uint64_t resumeAtPTS;
+ if (extra != NULL
+ && extra->findInt64(
+ IStreamListener::kKeyResumeAtPTS,
+ (int64_t *)&resumeAtPTS)) {
+ int64_t resumeAtMediaTimeUs =
+ mProgram->convertPTSToTimestamp(resumeAtPTS);
+
+ extra->setInt64("resume-at-mediatimeUs", resumeAtMediaTimeUs);
+ }
+
if (mSource != NULL) {
- mSource->queueDiscontinuity(type);
+ mSource->queueDiscontinuity(type, extra);
} else {
- deferDiscontinuity(type);
+ deferDiscontinuity(type, extra);
}
break;
}
@@ -407,10 +424,12 @@
}
}
-void ATSParser::Stream::deferDiscontinuity(DiscontinuityType type) {
+void ATSParser::Stream::deferDiscontinuity(
+ DiscontinuityType type, const sp<AMessage> &extra) {
if (type > mPendingDiscontinuity) {
// Only upgrade discontinuities.
mPendingDiscontinuity = type;
+ mPendingDiscontinuityExtra = extra;
}
}
@@ -611,8 +630,10 @@
mSource = new AnotherPacketSource(meta);
if (mPendingDiscontinuity != DISCONTINUITY_NONE) {
- mSource->queueDiscontinuity(mPendingDiscontinuity);
+ mSource->queueDiscontinuity(
+ mPendingDiscontinuity, mPendingDiscontinuityExtra);
mPendingDiscontinuity = DISCONTINUITY_NONE;
+ mPendingDiscontinuityExtra.clear();
}
mSource->queueAccessUnit(accessUnit);
@@ -654,9 +675,10 @@
parseTS(&br);
}
-void ATSParser::signalDiscontinuity(DiscontinuityType type) {
+void ATSParser::signalDiscontinuity(
+ DiscontinuityType type, const sp<AMessage> &extra) {
for (size_t i = 0; i < mPrograms.size(); ++i) {
- mPrograms.editItemAt(i)->signalDiscontinuity(type);
+ mPrograms.editItemAt(i)->signalDiscontinuity(type, extra);
}
}
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index ec3be84..3936f05 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -34,7 +34,6 @@
struct ATSParser : public RefBase {
enum DiscontinuityType {
DISCONTINUITY_NONE,
- DISCONTINUITY_HTTPLIVE,
DISCONTINUITY_SEEK,
DISCONTINUITY_FORMATCHANGE
};
@@ -42,7 +41,10 @@
ATSParser();
void feedTSPacket(const void *data, size_t size);
- void signalDiscontinuity(DiscontinuityType type);
+
+ void signalDiscontinuity(
+ DiscontinuityType type, const sp<AMessage> &extra);
+
void signalEOS(status_t finalResult);
enum SourceType {
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 0ad883b..59de17e 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -136,9 +136,11 @@
}
void AnotherPacketSource::queueDiscontinuity(
- ATSParser::DiscontinuityType type) {
+ ATSParser::DiscontinuityType type,
+ const sp<AMessage> &extra) {
sp<ABuffer> buffer = new ABuffer(0);
buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type));
+ buffer->meta()->setMessage("extra", extra);
Mutex::Autolock autoLock(mLock);
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 6fe93f8..439c785 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -46,7 +46,10 @@
status_t nextBufferTime(int64_t *timeUs);
void queueAccessUnit(const sp<ABuffer> &buffer);
- void queueDiscontinuity(ATSParser::DiscontinuityType type);
+
+ void queueDiscontinuity(
+ ATSParser::DiscontinuityType type, const sp<AMessage> &extra);
+
void signalEOS(status_t result);
status_t dequeueAccessUnit(sp<ABuffer> *buffer);
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 73efdfe..dcaf9f7 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -341,54 +341,6 @@
return timeUs;
}
-// static
-sp<MetaData> ElementaryStreamQueue::MakeAACCodecSpecificData(
- unsigned profile, unsigned sampling_freq_index,
- unsigned channel_configuration) {
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
-
- CHECK_LE(sampling_freq_index, 11u);
- static const int32_t kSamplingFreq[] = {
- 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
- 16000, 12000, 11025, 8000
- };
- meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]);
- meta->setInt32(kKeyChannelCount, channel_configuration);
-
- static const uint8_t kStaticESDS[] = {
- 0x03, 22,
- 0x00, 0x00, // ES_ID
- 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
-
- 0x04, 17,
- 0x40, // Audio ISO/IEC 14496-3
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
-
- 0x05, 2,
- // AudioSpecificInfo follows
-
- // oooo offf fccc c000
- // o - audioObjectType
- // f - samplingFreqIndex
- // c - channelConfig
- };
- sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
- memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
-
- csd->data()[sizeof(kStaticESDS)] =
- ((profile + 1) << 3) | (sampling_freq_index >> 1);
-
- csd->data()[sizeof(kStaticESDS) + 1] =
- ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
-
- meta->setData(kKeyESDS, 0, csd->data(), csd->size());
-
- return meta;
-}
-
struct NALPosition {
size_t nalOffset;
size_t nalSize;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 5b7957e..153cfe6 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -19,6 +19,7 @@
#define ES_QUEUE_H_
#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
#include <utils/List.h>
#include <utils/RefBase.h>
@@ -61,10 +62,6 @@
// returns its timestamp in us (or -1 if no time information).
int64_t fetchTimestamp(size_t size);
- static sp<MetaData> MakeAACCodecSpecificData(
- unsigned profile, unsigned sampling_freq_index,
- unsigned channel_configuration);
-
DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue);
};
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index a1f0796..dfec47f 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -202,20 +202,13 @@
LOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo);
}
-static bool isDiscontinuity(const uint8_t *data, ssize_t size) {
- return size == 188 && data[0] == 0x00;
-}
-
status_t MPEG2TSExtractor::feedMore() {
Mutex::Autolock autoLock(mLock);
uint8_t packet[kTSPacketSize];
ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
- if (isDiscontinuity(packet, n)) {
- LOGI("XXX discontinuity detected");
- mParser->signalDiscontinuity(ATSParser::DISCONTINUITY_HTTPLIVE);
- } else if (n < (ssize_t)kTSPacketSize) {
+ if (n < (ssize_t)kTSPacketSize) {
return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
} else {
mParser->feedTSPacket(packet, kTSPacketSize);
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 6e069c8..08ad6f3 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -1,41 +1,28 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-ifneq ($(BUILD_WITHOUT_PV),true)
-# Set up the OpenCore variables.
-include external/opencore/Config.mk
-LOCAL_C_INCLUDES := $(PV_INCLUDES)
-LOCAL_CFLAGS := $(PV_CFLAGS_MINUS_VISIBILITY)
-endif
-
LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
LOCAL_SRC_FILES:= \
- OMX.cpp \
+ OMX.cpp \
OMXComponentBase.cpp \
+ OMXMaster.cpp \
OMXNodeInstance.cpp \
- OMXMaster.cpp
+ SimpleSoftOMXComponent.cpp \
+ SoftOMXComponent.cpp \
+ SoftOMXPlugin.cpp \
-ifneq ($(BUILD_WITHOUT_PV),true)
-LOCAL_SRC_FILES += \
- OMXPVCodecsPlugin.cpp
-else
-LOCAL_CFLAGS += -DNO_OPENCORE
-endif
+LOCAL_C_INCLUDES += \
+ frameworks/base/media/libstagefright \
+ $(TOP)/frameworks/base/include/media/stagefright/openmax
-LOCAL_C_INCLUDES += $(TOP)/frameworks/base/include/media/stagefright/openmax
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libmedia \
- libutils \
- libui \
- libcutils \
-
-ifneq ($(BUILD_WITHOUT_PV),true)
-LOCAL_SHARED_LIBRARIES += \
- libopencore_common
-endif
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libmedia \
+ libutils \
+ libui \
+ libcutils \
+ libstagefright_foundation \
ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
LOCAL_LDLIBS += -lpthread -ldl
@@ -49,5 +36,6 @@
include $(BUILD_SHARED_LIBRARY)
-include $(call all-makefiles-under,$(LOCAL_PATH))
+################################################################################
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 3638f41..4b1c3a7 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -297,6 +297,11 @@
return findInstance(node)->enableGraphicBuffers(port_index, enable);
}
+status_t OMX::getGraphicBufferUsage(
+ node_id node, OMX_U32 port_index, OMX_U32* usage) {
+ return findInstance(node)->getGraphicBufferUsage(port_index, usage);
+}
+
status_t OMX::storeMetaDataInBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable) {
return findInstance(node)->storeMetaDataInBuffers(port_index, enable);
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index 56b169a..545e6d4 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -20,23 +20,18 @@
#include "OMXMaster.h"
+#include "SoftOMXPlugin.h"
+
#include <dlfcn.h>
#include <media/stagefright/MediaDebug.h>
-#ifndef NO_OPENCORE
-#include "OMXPVCodecsPlugin.h"
-#endif
-
namespace android {
OMXMaster::OMXMaster()
: mVendorLibHandle(NULL) {
addVendorPlugin();
-
-#ifndef NO_OPENCORE
- addPlugin(new OMXPVCodecsPlugin);
-#endif
+ addPlugin(new SoftOMXPlugin);
}
OMXMaster::~OMXMaster() {
@@ -49,7 +44,11 @@
}
void OMXMaster::addVendorPlugin() {
- mVendorLibHandle = dlopen("libstagefrighthw.so", RTLD_NOW);
+ addPlugin("libstagefrighthw.so");
+}
+
+void OMXMaster::addPlugin(const char *libname) {
+ mVendorLibHandle = dlopen(libname, RTLD_NOW);
if (mVendorLibHandle == NULL) {
return;
diff --git a/media/libstagefright/omx/OMXMaster.h b/media/libstagefright/omx/OMXMaster.h
index 7ba8d18..feee1f9 100644
--- a/media/libstagefright/omx/OMXMaster.h
+++ b/media/libstagefright/omx/OMXMaster.h
@@ -58,6 +58,7 @@
void *mVendorLibHandle;
void addVendorPlugin();
+ void addPlugin(const char *libname);
void addPlugin(OMXPluginBase *plugin);
void clearPlugins();
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index c7c1409..8462988 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "OMXNodeInstance"
#include <utils/Log.h>
@@ -234,6 +234,7 @@
Mutex::Autolock autoLock(mLock);
OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params);
+
return StatusFromOMXError(err);
}
@@ -302,6 +303,45 @@
return OK;
}
+status_t OMXNodeInstance::getGraphicBufferUsage(
+ OMX_U32 portIndex, OMX_U32* usage) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_INDEXTYPE index;
+ OMX_ERRORTYPE err = OMX_GetExtensionIndex(
+ mHandle,
+ const_cast<OMX_STRING>(
+ "OMX.google.android.index.getAndroidNativeBufferUsage"),
+ &index);
+
+ if (err != OMX_ErrorNone) {
+ LOGE("OMX_GetExtensionIndex failed");
+
+ return StatusFromOMXError(err);
+ }
+
+ OMX_VERSIONTYPE ver;
+ ver.s.nVersionMajor = 1;
+ ver.s.nVersionMinor = 0;
+ ver.s.nRevision = 0;
+ ver.s.nStep = 0;
+ GetAndroidNativeBufferUsageParams params = {
+ sizeof(GetAndroidNativeBufferUsageParams), ver, portIndex, 0,
+ };
+
+ err = OMX_GetParameter(mHandle, index, ¶ms);
+
+ if (err != OMX_ErrorNone) {
+ LOGE("OMX_GetAndroidNativeBufferUsage failed with error %d (0x%08x)",
+ err, err);
+ return UNKNOWN_ERROR;
+ }
+
+ *usage = params.nUsage;
+
+ return OK;
+}
+
status_t OMXNodeInstance::storeMetaDataInBuffers(
OMX_U32 portIndex,
OMX_BOOL enable) {
diff --git a/media/libstagefright/omx/OMXPVCodecsPlugin.cpp b/media/libstagefright/omx/OMXPVCodecsPlugin.cpp
deleted file mode 100644
index d1f5be3..0000000
--- a/media/libstagefright/omx/OMXPVCodecsPlugin.cpp
+++ /dev/null
@@ -1,101 +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 "OMXPVCodecsPlugin.h"
-
-#include "pv_omxcore.h"
-
-#include <media/stagefright/MediaDebug.h>
-
-namespace android {
-
-OMXPVCodecsPlugin::OMXPVCodecsPlugin() {
- OMX_MasterInit();
-}
-
-OMXPVCodecsPlugin::~OMXPVCodecsPlugin() {
- OMX_MasterDeinit();
-}
-
-OMX_ERRORTYPE OMXPVCodecsPlugin::makeComponentInstance(
- const char *name,
- const OMX_CALLBACKTYPE *callbacks,
- OMX_PTR appData,
- OMX_COMPONENTTYPE **component) {
- return OMX_MasterGetHandle(
- reinterpret_cast<OMX_HANDLETYPE *>(component),
- const_cast<char *>(name),
- appData,
- const_cast<OMX_CALLBACKTYPE *>(callbacks));
-}
-
-OMX_ERRORTYPE OMXPVCodecsPlugin::destroyComponentInstance(
- OMX_COMPONENTTYPE *component) {
- return OMX_MasterFreeHandle(component);
-}
-
-OMX_ERRORTYPE OMXPVCodecsPlugin::enumerateComponents(
- OMX_STRING name,
- size_t size,
- OMX_U32 index) {
- return OMX_MasterComponentNameEnum(name, size, index);
-}
-
-OMX_ERRORTYPE OMXPVCodecsPlugin::getRolesOfComponent(
- const char *name,
- Vector<String8> *roles) {
- roles->clear();
-
- OMX_U32 numRoles;
- OMX_ERRORTYPE err =
- OMX_MasterGetRolesOfComponent(
- const_cast<char *>(name),
- &numRoles,
- NULL);
-
- if (err != OMX_ErrorNone) {
- return err;
- }
-
- if (numRoles > 0) {
- OMX_U8 **array = new OMX_U8 *[numRoles];
- for (OMX_U32 i = 0; i < numRoles; ++i) {
- array[i] = new OMX_U8[OMX_MAX_STRINGNAME_SIZE];
- }
-
- OMX_U32 numRoles2;
- err = OMX_MasterGetRolesOfComponent(
- const_cast<char *>(name), &numRoles2, array);
-
- CHECK_EQ(err, OMX_ErrorNone);
- CHECK_EQ(numRoles, numRoles2);
-
- for (OMX_U32 i = 0; i < numRoles; ++i) {
- String8 s((const char *)array[i]);
- roles->push(s);
-
- delete[] array[i];
- array[i] = NULL;
- }
-
- delete[] array;
- array = NULL;
- }
-
- return OMX_ErrorNone;
-}
-
-} // namespace android
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
new file mode 100644
index 0000000..179b2a0
--- /dev/null
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2011 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 "SimpleSoftOMXComponent"
+#include <utils/Log.h>
+
+#include "include/SimpleSoftOMXComponent.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+SimpleSoftOMXComponent::SimpleSoftOMXComponent(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SoftOMXComponent(name, callbacks, appData, component),
+ mLooper(new ALooper),
+ mHandler(new AHandlerReflector<SimpleSoftOMXComponent>(this)),
+ mState(OMX_StateLoaded),
+ mTargetState(OMX_StateLoaded) {
+ mLooper->setName(name);
+ mLooper->registerHandler(mHandler);
+
+ mLooper->start(
+ false, // runOnCallingThread
+ false, // canCallJava
+ PRIORITY_AUDIO);
+}
+
+SimpleSoftOMXComponent::~SimpleSoftOMXComponent() {
+ mLooper->unregisterHandler(mHandler->id());
+ mLooper->stop();
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::sendCommand(
+ OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) {
+ CHECK(data == NULL);
+
+ sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler->id());
+ msg->setInt32("cmd", cmd);
+ msg->setInt32("param", param);
+ msg->post();
+
+ return OMX_ErrorNone;
+}
+
+bool SimpleSoftOMXComponent::isSetParameterAllowed(
+ OMX_INDEXTYPE index, const OMX_PTR params) const {
+ if (mState == OMX_StateLoaded) {
+ return true;
+ }
+
+ OMX_U32 portIndex;
+
+ switch (index) {
+ case OMX_IndexParamPortDefinition:
+ {
+ portIndex = ((OMX_PARAM_PORTDEFINITIONTYPE *)params)->nPortIndex;
+ break;
+ }
+
+ case OMX_IndexParamAudioPcm:
+ {
+ portIndex = ((OMX_AUDIO_PARAM_PCMMODETYPE *)params)->nPortIndex;
+ break;
+ }
+
+ case OMX_IndexParamAudioAac:
+ {
+ portIndex = ((OMX_AUDIO_PARAM_AACPROFILETYPE *)params)->nPortIndex;
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ CHECK(portIndex < mPorts.size());
+
+ return !mPorts.itemAt(portIndex).mDef.bEnabled;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::getParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ Mutex::Autolock autoLock(mLock);
+ return internalGetParameter(index, params);
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::setParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK(isSetParameterAllowed(index, params));
+
+ return internalSetParameter(index, params);
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamPortDefinition:
+ {
+ OMX_PARAM_PORTDEFINITIONTYPE *defParams =
+ (OMX_PARAM_PORTDEFINITIONTYPE *)params;
+
+ if (defParams->nPortIndex >= mPorts.size()
+ || defParams->nSize
+ != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) {
+ return OMX_ErrorUndefined;
+ }
+
+ const PortInfo *port =
+ &mPorts.itemAt(defParams->nPortIndex);
+
+ memcpy(defParams, &port->mDef, sizeof(port->mDef));
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return OMX_ErrorUnsupportedIndex;
+ }
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamPortDefinition:
+ {
+ OMX_PARAM_PORTDEFINITIONTYPE *defParams =
+ (OMX_PARAM_PORTDEFINITIONTYPE *)params;
+
+ if (defParams->nPortIndex >= mPorts.size()
+ || defParams->nSize
+ != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) {
+ return OMX_ErrorUndefined;
+ }
+
+ PortInfo *port =
+ &mPorts.editItemAt(defParams->nPortIndex);
+
+ if (defParams->nBufferSize != port->mDef.nBufferSize) {
+ CHECK_GE(defParams->nBufferSize, port->mDef.nBufferSize);
+ port->mDef.nBufferSize = defParams->nBufferSize;
+ }
+
+ if (defParams->nBufferCountActual
+ != port->mDef.nBufferCountActual) {
+ CHECK_GE(defParams->nBufferCountActual,
+ port->mDef.nBufferCountMin);
+
+ port->mDef.nBufferCountActual = defParams->nBufferCountActual;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return OMX_ErrorUnsupportedIndex;
+ }
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::useBuffer(
+ OMX_BUFFERHEADERTYPE **header,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size,
+ OMX_U8 *ptr) {
+ Mutex::Autolock autoLock(mLock);
+ CHECK_LT(portIndex, mPorts.size());
+
+ *header = new OMX_BUFFERHEADERTYPE;
+ (*header)->nSize = sizeof(OMX_BUFFERHEADERTYPE);
+ (*header)->nVersion.s.nVersionMajor = 1;
+ (*header)->nVersion.s.nVersionMinor = 0;
+ (*header)->nVersion.s.nRevision = 0;
+ (*header)->nVersion.s.nStep = 0;
+ (*header)->pBuffer = ptr;
+ (*header)->nAllocLen = size;
+ (*header)->nFilledLen = 0;
+ (*header)->nOffset = 0;
+ (*header)->pAppPrivate = appPrivate;
+ (*header)->pPlatformPrivate = NULL;
+ (*header)->pInputPortPrivate = NULL;
+ (*header)->pOutputPortPrivate = NULL;
+ (*header)->hMarkTargetComponent = NULL;
+ (*header)->pMarkData = NULL;
+ (*header)->nTickCount = 0;
+ (*header)->nTimeStamp = 0;
+ (*header)->nFlags = 0;
+ (*header)->nOutputPortIndex = portIndex;
+ (*header)->nInputPortIndex = portIndex;
+
+ PortInfo *port = &mPorts.editItemAt(portIndex);
+
+ CHECK(mState == OMX_StateLoaded || port->mDef.bEnabled == OMX_FALSE);
+
+ CHECK_LT(port->mBuffers.size(), port->mDef.nBufferCountActual);
+
+ port->mBuffers.push();
+
+ BufferInfo *buffer =
+ &port->mBuffers.editItemAt(port->mBuffers.size() - 1);
+
+ buffer->mHeader = *header;
+ buffer->mOwnedByUs = false;
+
+ if (port->mBuffers.size() == port->mDef.nBufferCountActual) {
+ port->mDef.bPopulated = OMX_TRUE;
+ checkTransitions();
+ }
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::allocateBuffer(
+ OMX_BUFFERHEADERTYPE **header,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size) {
+ OMX_U8 *ptr = new OMX_U8[size];
+
+ OMX_ERRORTYPE err =
+ useBuffer(header, portIndex, appPrivate, size, ptr);
+
+ if (err != OMX_ErrorNone) {
+ delete[] ptr;
+ ptr = NULL;
+
+ return err;
+ }
+
+ CHECK((*header)->pPlatformPrivate == NULL);
+ (*header)->pPlatformPrivate = ptr;
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::freeBuffer(
+ OMX_U32 portIndex,
+ OMX_BUFFERHEADERTYPE *header) {
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK_LT(portIndex, mPorts.size());
+
+ PortInfo *port = &mPorts.editItemAt(portIndex);
+
+#if 0 // XXX
+ CHECK((mState == OMX_StateIdle && mTargetState == OMX_StateLoaded)
+ || port->mDef.bEnabled == OMX_FALSE);
+#endif
+
+ bool found = false;
+ for (size_t i = 0; i < port->mBuffers.size(); ++i) {
+ BufferInfo *buffer = &port->mBuffers.editItemAt(i);
+
+ if (buffer->mHeader == header) {
+ CHECK(!buffer->mOwnedByUs);
+
+ if (header->pPlatformPrivate != NULL) {
+ // This buffer's data was allocated by us.
+ CHECK(header->pPlatformPrivate == header->pBuffer);
+
+ delete[] header->pBuffer;
+ header->pBuffer = NULL;
+ }
+
+ delete header;
+ header = NULL;
+
+ port->mBuffers.removeAt(i);
+ port->mDef.bPopulated = OMX_FALSE;
+
+ checkTransitions();
+
+ found = true;
+ break;
+ }
+ }
+
+ CHECK(found);
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer(
+ OMX_BUFFERHEADERTYPE *buffer) {
+ sp<AMessage> msg = new AMessage(kWhatEmptyThisBuffer, mHandler->id());
+ msg->setPointer("header", buffer);
+ msg->post();
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer(
+ OMX_BUFFERHEADERTYPE *buffer) {
+ sp<AMessage> msg = new AMessage(kWhatFillThisBuffer, mHandler->id());
+ msg->setPointer("header", buffer);
+ msg->post();
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::getState(OMX_STATETYPE *state) {
+ Mutex::Autolock autoLock(mLock);
+
+ *state = mState;
+
+ return OMX_ErrorNone;
+}
+
+void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) {
+ Mutex::Autolock autoLock(mLock);
+
+ switch (msg->what()) {
+ case kWhatSendCommand:
+ {
+ int32_t cmd, param;
+ CHECK(msg->findInt32("cmd", &cmd));
+ CHECK(msg->findInt32("param", ¶m));
+
+ onSendCommand((OMX_COMMANDTYPE)cmd, (OMX_U32)param);
+ break;
+ }
+
+ case kWhatEmptyThisBuffer:
+ case kWhatFillThisBuffer:
+ {
+ OMX_BUFFERHEADERTYPE *header;
+ CHECK(msg->findPointer("header", (void **)&header));
+
+ CHECK(mState == OMX_StateExecuting && mTargetState == mState);
+
+ bool found = false;
+ for (size_t i = 0; i < mPorts.size(); ++i) {
+ PortInfo *port = &mPorts.editItemAt(i);
+
+ for (size_t j = 0; j < port->mBuffers.size(); ++j) {
+ BufferInfo *buffer = &port->mBuffers.editItemAt(j);
+
+ if (buffer->mHeader == header) {
+ CHECK(!buffer->mOwnedByUs);
+
+ buffer->mOwnedByUs = true;
+
+ CHECK((msg->what() == kWhatEmptyThisBuffer
+ && port->mDef.eDir == OMX_DirInput)
+ || (port->mDef.eDir == OMX_DirOutput));
+
+ port->mQueue.push_back(buffer);
+ onQueueFilled(i);
+
+ found = true;
+ break;
+ }
+ }
+ }
+
+ CHECK(found);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void SimpleSoftOMXComponent::onSendCommand(
+ OMX_COMMANDTYPE cmd, OMX_U32 param) {
+ switch (cmd) {
+ case OMX_CommandStateSet:
+ {
+ onChangeState((OMX_STATETYPE)param);
+ break;
+ }
+
+ case OMX_CommandPortEnable:
+ case OMX_CommandPortDisable:
+ {
+ onPortEnable(param, cmd == OMX_CommandPortEnable);
+ break;
+ }
+
+ case OMX_CommandFlush:
+ {
+ onPortFlush(param, true /* sendFlushComplete */);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void SimpleSoftOMXComponent::onChangeState(OMX_STATETYPE state) {
+ // We shouldn't be in a state transition already.
+ CHECK_EQ((int)mState, (int)mTargetState);
+
+ switch (mState) {
+ case OMX_StateLoaded:
+ CHECK_EQ((int)state, (int)OMX_StateIdle);
+ break;
+ case OMX_StateIdle:
+ CHECK(state == OMX_StateLoaded || state == OMX_StateExecuting);
+ break;
+ case OMX_StateExecuting:
+ {
+ CHECK_EQ((int)state, (int)OMX_StateIdle);
+
+ for (size_t i = 0; i < mPorts.size(); ++i) {
+ onPortFlush(i, false /* sendFlushComplete */);
+ }
+
+ mState = OMX_StateIdle;
+ notify(OMX_EventCmdComplete, OMX_CommandStateSet, state, NULL);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+
+ mTargetState = state;
+
+ checkTransitions();
+}
+
+void SimpleSoftOMXComponent::onPortEnable(OMX_U32 portIndex, bool enable) {
+ CHECK_LT(portIndex, mPorts.size());
+
+ PortInfo *port = &mPorts.editItemAt(portIndex);
+ CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE);
+ CHECK(port->mDef.bEnabled == !enable);
+
+ if (!enable) {
+ port->mDef.bEnabled = OMX_FALSE;
+ port->mTransition = PortInfo::DISABLING;
+
+ for (size_t i = 0; i < port->mBuffers.size(); ++i) {
+ BufferInfo *buffer = &port->mBuffers.editItemAt(i);
+
+ if (buffer->mOwnedByUs) {
+ buffer->mOwnedByUs = false;
+
+ if (port->mDef.eDir == OMX_DirInput) {
+ notifyEmptyBufferDone(buffer->mHeader);
+ } else {
+ CHECK_EQ(port->mDef.eDir, OMX_DirOutput);
+ notifyFillBufferDone(buffer->mHeader);
+ }
+ }
+ }
+
+ port->mQueue.clear();
+ } else {
+ port->mTransition = PortInfo::ENABLING;
+ }
+
+ checkTransitions();
+}
+
+void SimpleSoftOMXComponent::onPortFlush(
+ OMX_U32 portIndex, bool sendFlushComplete) {
+ if (portIndex == OMX_ALL) {
+ for (size_t i = 0; i < mPorts.size(); ++i) {
+ onPortFlush(i, sendFlushComplete);
+ }
+
+ if (sendFlushComplete) {
+ notify(OMX_EventCmdComplete, OMX_CommandFlush, OMX_ALL, NULL);
+ }
+
+ return;
+ }
+
+ CHECK_LT(portIndex, mPorts.size());
+
+ PortInfo *port = &mPorts.editItemAt(portIndex);
+ CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE);
+
+ for (size_t i = 0; i < port->mBuffers.size(); ++i) {
+ BufferInfo *buffer = &port->mBuffers.editItemAt(i);
+
+ if (!buffer->mOwnedByUs) {
+ continue;
+ }
+
+ buffer->mHeader->nFilledLen = 0;
+ buffer->mHeader->nOffset = 0;
+ buffer->mHeader->nFlags = 0;
+
+ buffer->mOwnedByUs = false;
+
+ if (port->mDef.eDir == OMX_DirInput) {
+ notifyEmptyBufferDone(buffer->mHeader);
+ } else {
+ CHECK_EQ(port->mDef.eDir, OMX_DirOutput);
+
+ notifyFillBufferDone(buffer->mHeader);
+ }
+ }
+
+ port->mQueue.clear();
+
+ if (sendFlushComplete) {
+ notify(OMX_EventCmdComplete, OMX_CommandFlush, portIndex, NULL);
+
+ onPortFlushCompleted(portIndex);
+ }
+}
+
+void SimpleSoftOMXComponent::checkTransitions() {
+ if (mState != mTargetState) {
+ bool transitionComplete = true;
+
+ if (mState == OMX_StateLoaded) {
+ CHECK_EQ((int)mTargetState, (int)OMX_StateIdle);
+
+ for (size_t i = 0; i < mPorts.size(); ++i) {
+ const PortInfo &port = mPorts.itemAt(i);
+ if (port.mDef.bEnabled == OMX_FALSE) {
+ continue;
+ }
+
+ if (port.mDef.bPopulated == OMX_FALSE) {
+ transitionComplete = false;
+ break;
+ }
+ }
+ } else if (mTargetState == OMX_StateLoaded) {
+ CHECK_EQ((int)mState, (int)OMX_StateIdle);
+
+ for (size_t i = 0; i < mPorts.size(); ++i) {
+ const PortInfo &port = mPorts.itemAt(i);
+ if (port.mDef.bEnabled == OMX_FALSE) {
+ continue;
+ }
+
+ size_t n = port.mBuffers.size();
+
+ if (n > 0) {
+ CHECK_LE(n, port.mDef.nBufferCountActual);
+
+ if (n == port.mDef.nBufferCountActual) {
+ CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_TRUE);
+ } else {
+ CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_FALSE);
+ }
+
+ transitionComplete = false;
+ break;
+ }
+ }
+ }
+
+ if (transitionComplete) {
+ mState = mTargetState;
+
+ notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL);
+ }
+ }
+
+ for (size_t i = 0; i < mPorts.size(); ++i) {
+ PortInfo *port = &mPorts.editItemAt(i);
+
+ if (port->mTransition == PortInfo::DISABLING) {
+ if (port->mBuffers.empty()) {
+ LOGV("Port %d now disabled.", i);
+
+ port->mTransition = PortInfo::NONE;
+ notify(OMX_EventCmdComplete, OMX_CommandPortDisable, i, NULL);
+
+ onPortEnableCompleted(i, false /* enabled */);
+ }
+ } else if (port->mTransition == PortInfo::ENABLING) {
+ if (port->mDef.bPopulated == OMX_TRUE) {
+ LOGV("Port %d now enabled.", i);
+
+ port->mTransition = PortInfo::NONE;
+ port->mDef.bEnabled = OMX_TRUE;
+ notify(OMX_EventCmdComplete, OMX_CommandPortEnable, i, NULL);
+
+ onPortEnableCompleted(i, true /* enabled */);
+ }
+ }
+ }
+}
+
+void SimpleSoftOMXComponent::addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def) {
+ CHECK_EQ(def.nPortIndex, mPorts.size());
+
+ mPorts.push();
+ PortInfo *info = &mPorts.editItemAt(mPorts.size() - 1);
+ info->mDef = def;
+ info->mTransition = PortInfo::NONE;
+}
+
+void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex) {
+}
+
+void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex) {
+}
+
+void SimpleSoftOMXComponent::onPortEnableCompleted(
+ OMX_U32 portIndex, bool enabled) {
+}
+
+List<SimpleSoftOMXComponent::BufferInfo *> &
+SimpleSoftOMXComponent::getPortQueue(OMX_U32 portIndex) {
+ CHECK_LT(portIndex, mPorts.size());
+ return mPorts.editItemAt(portIndex).mQueue;
+}
+
+SimpleSoftOMXComponent::PortInfo *SimpleSoftOMXComponent::editPortInfo(
+ OMX_U32 portIndex) {
+ CHECK_LT(portIndex, mPorts.size());
+ return &mPorts.editItemAt(portIndex);
+}
+
+} // namespace android
diff --git a/media/libstagefright/omx/SoftOMXComponent.cpp b/media/libstagefright/omx/SoftOMXComponent.cpp
new file mode 100644
index 0000000..b1c34dc
--- /dev/null
+++ b/media/libstagefright/omx/SoftOMXComponent.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2011 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 "SoftOMXComponent"
+#include <utils/Log.h>
+
+#include "include/SoftOMXComponent.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+SoftOMXComponent::SoftOMXComponent(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : mName(name),
+ mCallbacks(callbacks),
+ mComponent(new OMX_COMPONENTTYPE),
+ mLibHandle(NULL) {
+ mComponent->nSize = sizeof(*mComponent);
+ mComponent->nVersion.s.nVersionMajor = 1;
+ mComponent->nVersion.s.nVersionMinor = 0;
+ mComponent->nVersion.s.nRevision = 0;
+ mComponent->nVersion.s.nStep = 0;
+ mComponent->pComponentPrivate = this;
+ mComponent->pApplicationPrivate = appData;
+
+ mComponent->GetComponentVersion = NULL;
+ mComponent->SendCommand = SendCommandWrapper;
+ mComponent->GetParameter = GetParameterWrapper;
+ mComponent->SetParameter = SetParameterWrapper;
+ mComponent->GetConfig = GetConfigWrapper;
+ mComponent->SetConfig = SetConfigWrapper;
+ mComponent->GetExtensionIndex = GetExtensionIndexWrapper;
+ mComponent->GetState = GetStateWrapper;
+ mComponent->ComponentTunnelRequest = NULL;
+ mComponent->UseBuffer = UseBufferWrapper;
+ mComponent->AllocateBuffer = AllocateBufferWrapper;
+ mComponent->FreeBuffer = FreeBufferWrapper;
+ mComponent->EmptyThisBuffer = EmptyThisBufferWrapper;
+ mComponent->FillThisBuffer = FillThisBufferWrapper;
+ mComponent->SetCallbacks = NULL;
+ mComponent->ComponentDeInit = NULL;
+ mComponent->UseEGLImage = NULL;
+ mComponent->ComponentRoleEnum = NULL;
+
+ *component = mComponent;
+}
+
+SoftOMXComponent::~SoftOMXComponent() {
+ delete mComponent;
+ mComponent = NULL;
+}
+
+void SoftOMXComponent::setLibHandle(void *libHandle) {
+ CHECK(libHandle != NULL);
+ mLibHandle = libHandle;
+}
+
+void *SoftOMXComponent::libHandle() const {
+ return mLibHandle;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::initCheck() const {
+ return OMX_ErrorNone;
+}
+
+const char *SoftOMXComponent::name() const {
+ return mName.c_str();
+}
+
+void SoftOMXComponent::notify(
+ OMX_EVENTTYPE event,
+ OMX_U32 data1, OMX_U32 data2, OMX_PTR data) {
+ (*mCallbacks->EventHandler)(
+ mComponent,
+ mComponent->pApplicationPrivate,
+ event,
+ data1,
+ data2,
+ data);
+}
+
+void SoftOMXComponent::notifyEmptyBufferDone(OMX_BUFFERHEADERTYPE *header) {
+ (*mCallbacks->EmptyBufferDone)(
+ mComponent, mComponent->pApplicationPrivate, header);
+}
+
+void SoftOMXComponent::notifyFillBufferDone(OMX_BUFFERHEADERTYPE *header) {
+ (*mCallbacks->FillBufferDone)(
+ mComponent, mComponent->pApplicationPrivate, header);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::SendCommandWrapper(
+ OMX_HANDLETYPE component,
+ OMX_COMMANDTYPE cmd,
+ OMX_U32 param,
+ OMX_PTR data) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->sendCommand(cmd, param, data);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::GetParameterWrapper(
+ OMX_HANDLETYPE component,
+ OMX_INDEXTYPE index,
+ OMX_PTR params) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->getParameter(index, params);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::SetParameterWrapper(
+ OMX_HANDLETYPE component,
+ OMX_INDEXTYPE index,
+ OMX_PTR params) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->setParameter(index, params);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::GetConfigWrapper(
+ OMX_HANDLETYPE component,
+ OMX_INDEXTYPE index,
+ OMX_PTR params) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->getConfig(index, params);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::SetConfigWrapper(
+ OMX_HANDLETYPE component,
+ OMX_INDEXTYPE index,
+ OMX_PTR params) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->setConfig(index, params);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::GetExtensionIndexWrapper(
+ OMX_HANDLETYPE component,
+ OMX_STRING name,
+ OMX_INDEXTYPE *index) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->getExtensionIndex(name, index);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::UseBufferWrapper(
+ OMX_HANDLETYPE component,
+ OMX_BUFFERHEADERTYPE **buffer,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size,
+ OMX_U8 *ptr) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->useBuffer(buffer, portIndex, appPrivate, size, ptr);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::AllocateBufferWrapper(
+ OMX_HANDLETYPE component,
+ OMX_BUFFERHEADERTYPE **buffer,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->allocateBuffer(buffer, portIndex, appPrivate, size);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::FreeBufferWrapper(
+ OMX_HANDLETYPE component,
+ OMX_U32 portIndex,
+ OMX_BUFFERHEADERTYPE *buffer) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->freeBuffer(portIndex, buffer);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::EmptyThisBufferWrapper(
+ OMX_HANDLETYPE component,
+ OMX_BUFFERHEADERTYPE *buffer) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->emptyThisBuffer(buffer);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::FillThisBufferWrapper(
+ OMX_HANDLETYPE component,
+ OMX_BUFFERHEADERTYPE *buffer) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->fillThisBuffer(buffer);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::GetStateWrapper(
+ OMX_HANDLETYPE component,
+ OMX_STATETYPE *state) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ return me->getState(state);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+OMX_ERRORTYPE SoftOMXComponent::sendCommand(
+ OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::getParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::setParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::getConfig(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::setConfig(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::getExtensionIndex(
+ const char *name, OMX_INDEXTYPE *index) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::useBuffer(
+ OMX_BUFFERHEADERTYPE **buffer,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size,
+ OMX_U8 *ptr) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::allocateBuffer(
+ OMX_BUFFERHEADERTYPE **buffer,
+ OMX_U32 portIndex,
+ OMX_PTR appPrivate,
+ OMX_U32 size) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::freeBuffer(
+ OMX_U32 portIndex,
+ OMX_BUFFERHEADERTYPE *buffer) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::emptyThisBuffer(
+ OMX_BUFFERHEADERTYPE *buffer) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::fillThisBuffer(
+ OMX_BUFFERHEADERTYPE *buffer) {
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::getState(OMX_STATETYPE *state) {
+ return OMX_ErrorUndefined;
+}
+
+} // namespace android
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
new file mode 100644
index 0000000..6bd6624
--- /dev/null
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2011 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 "SoftOMXPlugin"
+#include <utils/Log.h>
+
+#include "SoftOMXPlugin.h"
+#include "include/SoftOMXComponent.h"
+
+#include <media/stagefright/foundation/AString.h>
+
+#include <dlfcn.h>
+
+namespace android {
+
+static const struct {
+ const char *mName;
+ const char *mLibNameSuffix;
+ const char *mRole;
+
+} kComponents[] = {
+ { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
+ { "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
+ { "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
+ { "OMX.google.avc.decoder", "avcdec", "video_decoder.avc" },
+ { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
+ { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
+ { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
+ { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
+ { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },
+ { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },
+ { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" },
+};
+
+static const size_t kNumComponents =
+ sizeof(kComponents) / sizeof(kComponents[0]);
+
+SoftOMXPlugin::SoftOMXPlugin() {
+}
+
+OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component) {
+ LOGV("makeComponentInstance '%s'", name);
+
+ for (size_t i = 0; i < kNumComponents; ++i) {
+ if (strcmp(name, kComponents[i].mName)) {
+ continue;
+ }
+
+ AString libName = "libstagefright_soft_";
+ libName.append(kComponents[i].mLibNameSuffix);
+ libName.append(".so");
+
+ void *libHandle = dlopen(libName.c_str(), RTLD_NOW);
+
+ if (libHandle == NULL) {
+ LOGE("unable to dlopen %s", libName.c_str());
+
+ return OMX_ErrorComponentNotFound;
+ }
+
+ typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)(
+ const char *, const OMX_CALLBACKTYPE *,
+ OMX_PTR, OMX_COMPONENTTYPE **);
+
+ CreateSoftOMXComponentFunc createSoftOMXComponent =
+ (CreateSoftOMXComponentFunc)dlsym(
+ libHandle,
+ "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE"
+ "PvPP17OMX_COMPONENTTYPE");
+
+ if (createSoftOMXComponent == NULL) {
+ dlclose(libHandle);
+ libHandle = NULL;
+
+ return OMX_ErrorComponentNotFound;
+ }
+
+ sp<SoftOMXComponent> codec =
+ (*createSoftOMXComponent)(name, callbacks, appData, component);
+
+ if (codec == NULL) {
+ dlclose(libHandle);
+ libHandle = NULL;
+
+ return OMX_ErrorInsufficientResources;
+ }
+
+ OMX_ERRORTYPE err = codec->initCheck();
+ if (err != OMX_ErrorNone) {
+ dlclose(libHandle);
+ libHandle = NULL;
+
+ return err;
+ }
+
+ codec->incStrong(this);
+ codec->setLibHandle(libHandle);
+
+ return OMX_ErrorNone;
+ }
+
+ return OMX_ErrorInvalidComponentName;
+}
+
+OMX_ERRORTYPE SoftOMXPlugin::destroyComponentInstance(
+ OMX_COMPONENTTYPE *component) {
+ SoftOMXComponent *me =
+ (SoftOMXComponent *)
+ ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+ void *libHandle = me->libHandle();
+
+ me->decStrong(this);
+ me = NULL;
+
+ dlclose(libHandle);
+ libHandle = NULL;
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftOMXPlugin::enumerateComponents(
+ OMX_STRING name,
+ size_t size,
+ OMX_U32 index) {
+ if (index >= kNumComponents) {
+ return OMX_ErrorNoMore;
+ }
+
+ strcpy(name, kComponents[index].mName);
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftOMXPlugin::getRolesOfComponent(
+ const char *name,
+ Vector<String8> *roles) {
+ for (size_t i = 0; i < kNumComponents; ++i) {
+ if (strcmp(name, kComponents[i].mName)) {
+ continue;
+ }
+
+ roles->clear();
+ roles->push(String8(kComponents[i].mRole));
+
+ return OMX_ErrorNone;
+ }
+
+ return OMX_ErrorInvalidComponentName;
+}
+
+} // namespace android
diff --git a/media/libstagefright/omx/OMXPVCodecsPlugin.h b/media/libstagefright/omx/SoftOMXPlugin.h
similarity index 76%
rename from media/libstagefright/omx/OMXPVCodecsPlugin.h
rename to media/libstagefright/omx/SoftOMXPlugin.h
index c133232..f93c323 100644
--- a/media/libstagefright/omx/OMXPVCodecsPlugin.h
+++ b/media/libstagefright/omx/SoftOMXPlugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2011 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.
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#ifndef OMX_PV_CODECS_PLUGIN_H_
+#ifndef SOFT_OMX_PLUGIN_H_
-#define OMX_PV_CODECS_PLUGIN_H_
+#define SOFT_OMX_PLUGIN_H_
+#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/OMXPluginBase.h>
namespace android {
-struct OMXPVCodecsPlugin : public OMXPluginBase {
- OMXPVCodecsPlugin();
- virtual ~OMXPVCodecsPlugin();
+struct SoftOMXPlugin : public OMXPluginBase {
+ SoftOMXPlugin();
virtual OMX_ERRORTYPE makeComponentInstance(
const char *name,
@@ -45,10 +45,9 @@
Vector<String8> *roles);
private:
- OMXPVCodecsPlugin(const OMXPVCodecsPlugin &);
- OMXPVCodecsPlugin &operator=(const OMXPVCodecsPlugin &);
+ DISALLOW_EVIL_CONSTRUCTORS(SoftOMXPlugin);
};
} // namespace android
-#endif // OMX_PV_CODECS_PLUGIN_H_
+#endif // SOFT_OMX_PLUGIN_H_
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 4f28855..a404f1f 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -29,6 +29,7 @@
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
@@ -454,6 +455,7 @@
{ "video_decoder.avc", "video/avc" },
{ "video_decoder.mpeg4", "video/mp4v-es" },
{ "video_decoder.h263", "video/3gpp" },
+ { "video_decoder.vpx", "video/x-vnd.on2.vp8" },
// we appear to use this as a synonym to amrnb.
{ "audio_decoder.amr", "audio/3gpp" },
@@ -461,7 +463,10 @@
{ "audio_decoder.amrnb", "audio/3gpp" },
{ "audio_decoder.amrwb", "audio/amr-wb" },
{ "audio_decoder.aac", "audio/mp4a-latm" },
- { "audio_decoder.mp3", "audio/mpeg" }
+ { "audio_decoder.mp3", "audio/mpeg" },
+ { "audio_decoder.vorbis", "audio/vorbis" },
+ { "audio_decoder.g711alaw", MEDIA_MIMETYPE_AUDIO_G711_ALAW },
+ { "audio_decoder.g711mlaw", MEDIA_MIMETYPE_AUDIO_G711_MLAW },
};
for (size_t i = 0; i < sizeof(kRoleToMime) / sizeof(kRoleToMime[0]); ++i) {
@@ -487,14 +492,20 @@
{ "audio/3gpp",
"file:///sdcard/media_api/video/H263_500_AMRNB_12.3gp" },
{ "audio/amr-wb",
- "file:///sdcard/media_api/music_perf/AMRWB/"
- "NIN_AMR-WB_15.85kbps_16kbps.amr" },
+ "file:///sdcard/media_api/music/"
+ "AI_AMR-WB_12.65kbps(13kbps)_16khz_mono_NMC.awb" },
{ "audio/mp4a-latm",
- "file:///sdcard/media_api/music_perf/AAC/"
- "WC_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4" },
+ "file:///sdcard/media_api/video/H264_AAC.3gp" },
{ "audio/mpeg",
- "file:///sdcard/media_api/music_perf/MP3/"
- "WC_256kbps_44.1khz_mono_CBR_DPA.mp3" }
+ "file:///sdcard/media_api/music/MP3CBR.mp3" },
+ { "audio/vorbis",
+ "file:///sdcard/media_api/metaDataTestMedias/OGG/"
+ "When You Say Nothing At All.ogg" },
+ { "video/x-vnd.on2.vp8",
+ "file:///sdcard/media_api/webm/big-buck-bunny_trailer.webm" },
+ { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "file:///sdcard/M1F1-Alaw-AFsp.wav" },
+ { MEDIA_MIMETYPE_AUDIO_G711_MLAW,
+ "file:///sdcard/M1F1-mulaw-AFsp.wav" },
};
for (size_t i = 0; i < sizeof(kMimeToURL) / sizeof(kMimeToURL[0]); ++i) {
@@ -626,8 +637,10 @@
requestedSeekTimeUs, requestedSeekTimeUs / 1E6);
}
- MediaBuffer *buffer;
- options.setSeekTo(requestedSeekTimeUs);
+ MediaBuffer *buffer = NULL;
+ options.setSeekTo(
+ requestedSeekTimeUs, MediaSource::ReadOptions::SEEK_NEXT_SYNC);
+
if (seekSource->read(&buffer, &options) != OK) {
CHECK_EQ(buffer, NULL);
actualSeekTimeUs = -1;
@@ -746,6 +759,10 @@
const IOMX::ComponentInfo &info = *it;
const char *componentName = info.mName.string();
+ if (strncmp(componentName, "OMX.google.", 11)) {
+ continue;
+ }
+
for (List<String8>::const_iterator role_it = info.mRoles.begin();
role_it != info.mRoles.end(); ++role_it) {
const char *componentRole = (*role_it).string();
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
index 8bfe285..11d9c22 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AMPEG4AudioAssembler"
+
#include "AMPEG4AudioAssembler.h"
#include "ARTPSource.h"
@@ -139,7 +142,10 @@
return OK;
}
-static status_t parseAudioSpecificConfig(ABitReader *bits) {
+static status_t parseAudioSpecificConfig(ABitReader *bits, sp<ABuffer> *asc) {
+ const uint8_t *dataStart = bits->data();
+ size_t totalNumBits = bits->numBitsLeft();
+
unsigned audioObjectType;
CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
@@ -185,13 +191,13 @@
}
}
-#if 0
- // This is not supported here as the upper layers did not explicitly
- // signal the length of AudioSpecificConfig.
-
if (extensionAudioObjectType != 5 && bits->numBitsLeft() >= 16) {
+ size_t numBitsLeftAtStart = bits->numBitsLeft();
+
unsigned syncExtensionType = bits->getBits(11);
if (syncExtensionType == 0x2b7) {
+ LOGI("found syncExtension");
+
CHECK_EQ(parseAudioObjectType(bits, &extensionAudioObjectType),
(status_t)OK);
@@ -203,9 +209,45 @@
/* unsigned extensionSamplingFrequency = */bits->getBits(24);
}
}
+
+ size_t numBitsInExtension =
+ numBitsLeftAtStart - bits->numBitsLeft();
+
+ if (numBitsInExtension & 7) {
+ // Apparently an extension is always considered an even
+ // multiple of 8 bits long.
+
+ LOGI("Skipping %d bits after sync extension",
+ 8 - (numBitsInExtension & 7));
+
+ bits->skipBits(8 - (numBitsInExtension & 7));
+ }
+ } else {
+ bits->putBits(syncExtensionType, 11);
}
}
-#endif
+
+ if (asc != NULL) {
+ size_t bitpos = totalNumBits & 7;
+
+ ABitReader bs(dataStart, (totalNumBits + 7) / 8);
+
+ totalNumBits -= bits->numBitsLeft();
+
+ size_t numBytes = (totalNumBits + 7) / 8;
+
+ *asc = new ABuffer(numBytes);
+
+ if (bitpos & 7) {
+ bs.skipBits(8 - (bitpos & 7));
+ }
+
+ uint8_t *dstPtr = (*asc)->data();
+ while (numBytes > 0) {
+ *dstPtr++ = bs.getBits(8);
+ --numBytes;
+ }
+ }
return OK;
}
@@ -214,6 +256,7 @@
ABitReader *bits,
unsigned *numSubFrames,
unsigned *frameLengthType,
+ ssize_t *fixedFrameLength,
bool *otherDataPresent,
unsigned *otherDataLenBits) {
unsigned audioMuxVersion = bits->getBits(1);
@@ -242,12 +285,14 @@
if (audioMuxVersion == 0) {
// AudioSpecificConfig
- CHECK_EQ(parseAudioSpecificConfig(bits), (status_t)OK);
+ CHECK_EQ(parseAudioSpecificConfig(bits, NULL /* asc */), (status_t)OK);
} else {
TRESPASS(); // XXX to be implemented
}
*frameLengthType = bits->getBits(3);
+ *fixedFrameLength = -1;
+
switch (*frameLengthType) {
case 0:
{
@@ -260,7 +305,14 @@
case 1:
{
- /* unsigned frameLength = */bits->getBits(9);
+ *fixedFrameLength = bits->getBits(9);
+ break;
+ }
+
+ case 2:
+ {
+ // reserved
+ TRESPASS();
break;
}
@@ -338,9 +390,21 @@
break;
}
- default:
- TRESPASS(); // XXX to be implemented
+ case 2:
+ {
+ // reserved
+
+ TRESPASS();
break;
+ }
+
+ default:
+ {
+ CHECK_GE(mFixedFrameLength, 0);
+
+ payloadLength = mFixedFrameLength;
+ break;
+ }
}
CHECK_LE(offset + payloadLength, buffer->size());
@@ -393,6 +457,7 @@
ABitReader bits(config->data(), config->size());
status_t err = parseStreamMuxConfig(
&bits, &mNumSubFrames, &mFrameLengthType,
+ &mFixedFrameLength,
&mOtherDataPresent, &mOtherDataLenBits);
CHECK_EQ(err, (status_t)NO_ERROR);
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
index 9cef94c..1361cd2 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
@@ -46,6 +46,7 @@
bool mMuxConfigPresent;
unsigned mNumSubFrames;
unsigned mFrameLengthType;
+ ssize_t mFixedFrameLength;
bool mOtherDataPresent;
unsigned mOtherDataLenBits;
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index 13988cd..9f6bd29 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -104,7 +104,7 @@
mNextExpectedSeqNoValid(false),
mNextExpectedSeqNo(0),
mAccessUnitDamaged(false) {
- mIsGeneric = desc.startsWith("mpeg4-generic/");
+ mIsGeneric = !strncasecmp(desc.c_str(),"mpeg4-generic/", 14);
if (mIsGeneric) {
AString value;
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index f0b858d..6819fef 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -20,6 +20,7 @@
#include "APacketSource.h"
+#include "ARawAudioAssembler.h"
#include "ASessionDescription.h"
#include "avc_utils.h"
@@ -637,7 +638,7 @@
mFormat->setInt32(kKeyWidth, width);
mFormat->setInt32(kKeyHeight, height);
- } else if (!strncmp(desc.c_str(), "mpeg4-generic/", 14)) {
+ } else if (!strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) {
AString val;
if (!GetAttribute(params.c_str(), "mode", &val)
|| (strcasecmp(val.c_str(), "AAC-lbr")
@@ -661,6 +662,8 @@
mFormat->setData(
kKeyESDS, 0,
codecSpecificData->data(), codecSpecificData->size());
+ } else if (ARawAudioAssembler::Supports(desc.c_str())) {
+ ARawAudioAssembler::MakeFormat(desc.c_str(), mFormat);
} else {
mInitCheck = ERROR_UNSUPPORTED;
}
diff --git a/media/libstagefright/rtsp/ARTPAssembler.cpp b/media/libstagefright/rtsp/ARTPAssembler.cpp
index 9ba2b37..a897c10 100644
--- a/media/libstagefright/rtsp/ARTPAssembler.cpp
+++ b/media/libstagefright/rtsp/ARTPAssembler.cpp
@@ -65,13 +65,9 @@
// static
void ARTPAssembler::CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from) {
- uint64_t ntpTime;
- CHECK(from->meta()->findInt64("ntp-time", (int64_t *)&ntpTime));
-
uint32_t rtpTime;
CHECK(from->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
- to->meta()->setInt64("ntp-time", ntpTime);
to->meta()->setInt32("rtp-time", rtpTime);
// Copy the seq number.
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 5a1ea5c..47de4e0 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -123,7 +123,7 @@
struct sockaddr_in addr;
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if (bind(*rtpSocket,
@@ -169,12 +169,6 @@
break;
}
- case kWhatFakeTimestamps:
- {
- onFakeTimestamps();
- break;
- }
-
default:
{
TRESPASS();
@@ -346,6 +340,8 @@
}
status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
+ LOGV("receiving %s", receiveRTP ? "RTP" : "RTCP");
+
CHECK(!s->mIsInjected);
sp<ABuffer> buffer = new ABuffer(65536);
@@ -461,12 +457,6 @@
buffer->setInt32Data(u16at(&data[2]));
buffer->setRange(payloadOffset, size - payloadOffset);
- if ((mFlags & kFakeTimestamps) && !source->timeEstablished()) {
- source->timeUpdate(rtpTime, 0);
- source->timeUpdate(rtpTime + 90000, 0x100000000ll);
- CHECK(source->timeEstablished());
- }
-
source->processRTPPacket(buffer);
return OK;
@@ -592,9 +582,7 @@
sp<ARTPSource> source = findSource(s, id);
- if ((mFlags & kFakeTimestamps) == 0) {
- source->timeUpdate(rtpTime, ntpTime);
- }
+ source->timeUpdate(rtpTime, ntpTime);
return 0;
}
@@ -652,27 +640,5 @@
}
}
-void ARTPConnection::fakeTimestamps() {
- (new AMessage(kWhatFakeTimestamps, id()))->post();
-}
-
-void ARTPConnection::onFakeTimestamps() {
- List<StreamInfo>::iterator it = mStreams.begin();
- while (it != mStreams.end()) {
- StreamInfo &info = *it++;
-
- for (size_t j = 0; j < info.mSources.size(); ++j) {
- sp<ARTPSource> source = info.mSources.valueAt(j);
-
- if (!source->timeEstablished()) {
- source->timeUpdate(0, 0);
- source->timeUpdate(0 + 90000, 0x100000000ll);
-
- mFlags |= kFakeTimestamps;
- }
- }
- }
-}
-
} // namespace android
diff --git a/media/libstagefright/rtsp/ARTPConnection.h b/media/libstagefright/rtsp/ARTPConnection.h
index a17b382..edbcc35 100644
--- a/media/libstagefright/rtsp/ARTPConnection.h
+++ b/media/libstagefright/rtsp/ARTPConnection.h
@@ -29,7 +29,6 @@
struct ARTPConnection : public AHandler {
enum Flags {
- kFakeTimestamps = 1,
kRegularlyRequestFIR = 2,
};
@@ -51,8 +50,6 @@
static void MakePortPair(
int *rtpSocket, int *rtcpSocket, unsigned *rtpPort);
- void fakeTimestamps();
-
protected:
virtual ~ARTPConnection();
virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -63,7 +60,6 @@
kWhatRemoveStream,
kWhatPollStreams,
kWhatInjectPacket,
- kWhatFakeTimestamps,
};
static const int64_t kSelectTimeoutUs;
@@ -81,7 +77,6 @@
void onPollStreams();
void onInjectPacket(const sp<AMessage> &msg);
void onSendReceiverReports();
- void onFakeTimestamps();
status_t receive(StreamInfo *info, bool receiveRTP);
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index 39c6619..c6bcb12 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -44,9 +44,7 @@
mDesc = desc;
- mRTPConn = new ARTPConnection(
- ARTPConnection::kFakeTimestamps
- | ARTPConnection::kRegularlyRequestFIR);
+ mRTPConn = new ARTPConnection(ARTPConnection::kRegularlyRequestFIR);
looper()->registerHandler(mRTPConn);
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 5aae4e7..3aa07ce 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -25,6 +25,7 @@
#include "AH263Assembler.h"
#include "AMPEG4AudioAssembler.h"
#include "AMPEG4ElementaryAssembler.h"
+#include "ARawAudioAssembler.h"
#include "ASessionDescription.h"
#include <media/stagefright/foundation/ABuffer.h>
@@ -42,12 +43,12 @@
: mID(id),
mHighestSeqNumber(0),
mNumBuffersReceived(0),
- mNumTimes(0),
mLastNTPTime(0),
mLastNTPTimeUpdateUs(0),
mIssueFIRRequests(false),
mLastFIRRequestUs(-1),
- mNextFIRSeqNo((rand() * 256.0) / RAND_MAX) {
+ mNextFIRSeqNo((rand() * 256.0) / RAND_MAX),
+ mNotify(notify) {
unsigned long PT;
AString desc;
AString params;
@@ -67,9 +68,11 @@
} else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
} else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)
- || !strncmp(desc.c_str(), "mpeg4-generic/", 14)) {
+ || !strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) {
mAssembler = new AMPEG4ElementaryAssembler(notify, desc, params);
mIssueFIRRequests = true;
+ } else if (ARawAudioAssembler::Supports(desc.c_str())) {
+ mAssembler = new ARawAudioAssembler(notify, desc.c_str(), params);
} else {
TRESPASS();
}
@@ -80,52 +83,25 @@
}
void ARTPSource::processRTPPacket(const sp<ABuffer> &buffer) {
- if (queuePacket(buffer)
- && mNumTimes == 2
- && mAssembler != NULL) {
+ if (queuePacket(buffer) && mAssembler != NULL) {
mAssembler->onPacketReceived(this);
}
}
void ARTPSource::timeUpdate(uint32_t rtpTime, uint64_t ntpTime) {
- LOGV("timeUpdate");
-
mLastNTPTime = ntpTime;
mLastNTPTimeUpdateUs = ALooper::GetNowUs();
- if (mNumTimes == 2) {
- mNTPTime[0] = mNTPTime[1];
- mRTPTime[0] = mRTPTime[1];
- mNumTimes = 1;
- }
- mNTPTime[mNumTimes] = ntpTime;
- mRTPTime[mNumTimes++] = rtpTime;
-
- if (timeEstablished()) {
- for (List<sp<ABuffer> >::iterator it = mQueue.begin();
- it != mQueue.end(); ++it) {
- sp<AMessage> meta = (*it)->meta();
-
- uint32_t rtpTime;
- CHECK(meta->findInt32("rtp-time", (int32_t *)&rtpTime));
-
- meta->setInt64("ntp-time", RTP2NTP(rtpTime));
- }
- }
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("time-update", true);
+ notify->setInt32("rtp-time", rtpTime);
+ notify->setInt64("ntp-time", ntpTime);
+ notify->post();
}
bool ARTPSource::queuePacket(const sp<ABuffer> &buffer) {
uint32_t seqNum = (uint32_t)buffer->int32Data();
- if (mNumTimes == 2) {
- sp<AMessage> meta = buffer->meta();
-
- uint32_t rtpTime;
- CHECK(meta->findInt32("rtp-time", (int32_t *)&rtpTime));
-
- meta->setInt64("ntp-time", RTP2NTP(rtpTime));
- }
-
if (mNumBuffersReceived++ == 0) {
mHighestSeqNumber = seqNum;
mQueue.push_back(buffer);
@@ -180,14 +156,6 @@
return true;
}
-uint64_t ARTPSource::RTP2NTP(uint32_t rtpTime) const {
- CHECK_EQ(mNumTimes, 2u);
-
- return mNTPTime[0] + (double)(mNTPTime[1] - mNTPTime[0])
- * ((double)rtpTime - (double)mRTPTime[0])
- / (double)(mRTPTime[1] - mRTPTime[0]);
-}
-
void ARTPSource::byeReceived() {
mAssembler->onByeReceived();
}
diff --git a/media/libstagefright/rtsp/ARTPSource.h b/media/libstagefright/rtsp/ARTPSource.h
index e62c3f1..b70f94e 100644
--- a/media/libstagefright/rtsp/ARTPSource.h
+++ b/media/libstagefright/rtsp/ARTPSource.h
@@ -46,10 +46,6 @@
void addReceiverReport(const sp<ABuffer> &buffer);
void addFIR(const sp<ABuffer> &buffer);
- bool timeEstablished() const {
- return mNumTimes == 2;
- }
-
private:
uint32_t mID;
uint32_t mHighestSeqNumber;
@@ -58,10 +54,6 @@
List<sp<ABuffer> > mQueue;
sp<ARTPAssembler> mAssembler;
- size_t mNumTimes;
- uint64_t mNTPTime[2];
- uint32_t mRTPTime[2];
-
uint64_t mLastNTPTime;
int64_t mLastNTPTimeUpdateUs;
@@ -69,7 +61,7 @@
int64_t mLastFIRRequestUs;
uint8_t mNextFIRSeqNo;
- uint64_t RTP2NTP(uint32_t rtpTime) const;
+ sp<AMessage> mNotify;
bool queuePacket(const sp<ABuffer> &buffer);
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index e936923..c4e0cdc 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -20,6 +20,8 @@
#include "ARTSPConnection.h"
+#include <cutils/properties.h>
+
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -44,6 +46,7 @@
mConnectionID(0),
mNextCSeq(0),
mReceiveResponseEventPending(false) {
+ MakeUserAgent(&mUserAgent);
}
ARTSPConnection::~ARTSPConnection() {
@@ -378,6 +381,7 @@
reply->setString("original-request", request.c_str(), request.size());
addAuthentication(&request);
+ addUserAgent(&request);
// Find the boundary between headers and the body.
ssize_t i = request.find("\r\n\r\n");
@@ -545,6 +549,10 @@
return buffer;
}
+static bool IsRTSPVersion(const AString &s) {
+ return s == "RTSP/1.0";
+}
+
bool ARTSPConnection::receiveRTSPReponse() {
AString statusLine;
@@ -584,13 +592,27 @@
return false;
}
- AString statusCodeStr(
- response->mStatusLine, space1 + 1, space2 - space1 - 1);
+ bool isRequest = false;
- if (!ParseSingleUnsignedLong(
- statusCodeStr.c_str(), &response->mStatusCode)
- || response->mStatusCode < 100 || response->mStatusCode > 999) {
- return false;
+ if (!IsRTSPVersion(AString(response->mStatusLine, 0, space1))) {
+ CHECK(IsRTSPVersion(
+ AString(
+ response->mStatusLine,
+ space2 + 1,
+ response->mStatusLine.size() - space2 - 1)));
+
+ isRequest = true;
+
+ response->mStatusCode = 0;
+ } else {
+ AString statusCodeStr(
+ response->mStatusLine, space1 + 1, space2 - space1 - 1);
+
+ if (!ParseSingleUnsignedLong(
+ statusCodeStr.c_str(), &response->mStatusCode)
+ || response->mStatusCode < 100 || response->mStatusCode > 999) {
+ return false;
+ }
}
AString line;
@@ -680,7 +702,63 @@
}
}
- return notifyResponseListener(response);
+ return isRequest
+ ? handleServerRequest(response)
+ : notifyResponseListener(response);
+}
+
+bool ARTSPConnection::handleServerRequest(const sp<ARTSPResponse> &request) {
+ // Implementation of server->client requests is optional for all methods
+ // but we do need to respond, even if it's just to say that we don't
+ // support the method.
+
+ ssize_t space1 = request->mStatusLine.find(" ");
+ CHECK_GE(space1, 0);
+
+ AString response;
+ response.append("RTSP/1.0 501 Not Implemented\r\n");
+
+ ssize_t i = request->mHeaders.indexOfKey("cseq");
+
+ if (i >= 0) {
+ AString value = request->mHeaders.valueAt(i);
+
+ unsigned long cseq;
+ if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
+ return false;
+ }
+
+ response.append("CSeq: ");
+ response.append(cseq);
+ response.append("\r\n");
+ }
+
+ response.append("\r\n");
+
+ size_t numBytesSent = 0;
+ while (numBytesSent < response.size()) {
+ ssize_t n =
+ send(mSocket, response.c_str() + numBytesSent,
+ response.size() - numBytesSent, 0);
+
+ if (n == 0) {
+ // Server closed the connection.
+ LOGE("Server unexpectedly closed the connection.");
+
+ return false;
+ } else if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+
+ LOGE("Error sending rtsp response.");
+ return false;
+ }
+
+ numBytesSent += (size_t)n;
+ }
+
+ return true;
}
// static
@@ -905,4 +983,27 @@
#endif
}
+// static
+void ARTSPConnection::MakeUserAgent(AString *userAgent) {
+ userAgent->clear();
+ userAgent->setTo("User-Agent: stagefright/1.1 (Linux;Android ");
+
+#if (PROPERTY_VALUE_MAX < 8)
+#error "PROPERTY_VALUE_MAX must be at least 8"
+#endif
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.build.version.release", value, "Unknown");
+ userAgent->append(value);
+ userAgent->append(")\r\n");
+}
+
+void ARTSPConnection::addUserAgent(AString *request) const {
+ // Find the boundary between headers and the body.
+ ssize_t i = request->find("\r\n\r\n");
+ CHECK_GE(i, 0);
+
+ request->insert(mUserAgent, i + 2);
+}
+
} // namespace android
diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h
index 19be2a6..ac2e3ae 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.h
+++ b/media/libstagefright/rtsp/ARTSPConnection.h
@@ -87,6 +87,8 @@
sp<AMessage> mObserveBinaryMessage;
+ AString mUserAgent;
+
void onConnect(const sp<AMessage> &msg);
void onDisconnect(const sp<AMessage> &msg);
void onCompleteConnection(const sp<AMessage> &msg);
@@ -106,12 +108,18 @@
bool parseAuthMethod(const sp<ARTSPResponse> &response);
void addAuthentication(AString *request);
+ void addUserAgent(AString *request) const;
+
status_t findPendingRequest(
const sp<ARTSPResponse> &response, ssize_t *index) const;
+ bool handleServerRequest(const sp<ARTSPResponse> &request);
+
static bool ParseSingleUnsignedLong(
const char *from, unsigned long *x);
+ static void MakeUserAgent(AString *userAgent);
+
DISALLOW_EVIL_CONSTRUCTORS(ARTSPConnection);
};
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index a7563ff..1328d2e 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -69,7 +69,14 @@
void ARTSPController::disconnect() {
Mutex::Autolock autoLock(mLock);
- if (mState != CONNECTED) {
+ if (mState == CONNECTING) {
+ mState = DISCONNECTED;
+ mConnectionResult = ERROR_IO;
+ mCondition.broadcast();
+
+ mHandler.clear();
+ return;
+ } else if (mState != CONNECTED) {
return;
}
diff --git a/media/libstagefright/rtsp/ARawAudioAssembler.cpp b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
new file mode 100644
index 0000000..dd47ea3
--- /dev/null
+++ b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2011 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 "ARawAudioAssembler"
+#include <utils/Log.h>
+
+#include "ARawAudioAssembler.h"
+
+#include "ARTPSource.h"
+#include "ASessionDescription.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+ARawAudioAssembler::ARawAudioAssembler(
+ const sp<AMessage> ¬ify, const char *desc, const AString ¶ms)
+ : mNotifyMsg(notify),
+ mNextExpectedSeqNoValid(false),
+ mNextExpectedSeqNo(0) {
+}
+
+ARawAudioAssembler::~ARawAudioAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus ARawAudioAssembler::assembleMore(
+ const sp<ARTPSource> &source) {
+ return addPacket(source);
+}
+
+ARTPAssembler::AssemblyStatus ARawAudioAssembler::addPacket(
+ const sp<ARTPSource> &source) {
+ List<sp<ABuffer> > *queue = source->queue();
+
+ if (queue->empty()) {
+ return NOT_ENOUGH_DATA;
+ }
+
+ if (mNextExpectedSeqNoValid) {
+ List<sp<ABuffer> >::iterator it = queue->begin();
+ while (it != queue->end()) {
+ if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+ break;
+ }
+
+ it = queue->erase(it);
+ }
+
+ if (queue->empty()) {
+ return NOT_ENOUGH_DATA;
+ }
+ }
+
+ sp<ABuffer> buffer = *queue->begin();
+
+ if (!mNextExpectedSeqNoValid) {
+ mNextExpectedSeqNoValid = true;
+ mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+ } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+ LOGV("Not the sequence number I expected");
+
+ return WRONG_SEQUENCE_NUMBER;
+ }
+
+ // hexdump(buffer->data(), buffer->size());
+
+ if (buffer->size() < 1) {
+ queue->erase(queue->begin());
+ ++mNextExpectedSeqNo;
+
+ LOGV("raw audio packet too short.");
+
+ return MALFORMED_PACKET;
+ }
+
+ sp<AMessage> msg = mNotifyMsg->dup();
+ msg->setObject("access-unit", buffer);
+ msg->post();
+
+ queue->erase(queue->begin());
+ ++mNextExpectedSeqNo;
+
+ return OK;
+}
+
+void ARawAudioAssembler::packetLost() {
+ CHECK(mNextExpectedSeqNoValid);
+ ++mNextExpectedSeqNo;
+}
+
+void ARawAudioAssembler::onByeReceived() {
+ sp<AMessage> msg = mNotifyMsg->dup();
+ msg->setInt32("eos", true);
+ msg->post();
+}
+
+// static
+bool ARawAudioAssembler::Supports(const char *desc) {
+ return !strncmp(desc, "PCMU/", 5)
+ || !strncmp(desc, "PCMA/", 5);
+}
+
+// static
+void ARawAudioAssembler::MakeFormat(
+ const char *desc, const sp<MetaData> &format) {
+ if (!strncmp(desc, "PCMU/", 5)) {
+ format->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW);
+ } else if (!strncmp(desc, "PCMA/", 5)) {
+ format->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW);
+ } else {
+ TRESPASS();
+ }
+
+ int32_t sampleRate, numChannels;
+ ASessionDescription::ParseFormatDesc(
+ desc, &sampleRate, &numChannels);
+
+ format->setInt32(kKeySampleRate, sampleRate);
+ format->setInt32(kKeyChannelCount, numChannels);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/rtsp/ARawAudioAssembler.h b/media/libstagefright/rtsp/ARawAudioAssembler.h
new file mode 100644
index 0000000..ed7af08
--- /dev/null
+++ b/media/libstagefright/rtsp/ARawAudioAssembler.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 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 A_RAW_AUDIO_ASSEMBLER_H_
+
+#define A_RAW_AUDIO_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+namespace android {
+
+struct AMessage;
+struct AString;
+struct MetaData;
+
+struct ARawAudioAssembler : public ARTPAssembler {
+ ARawAudioAssembler(
+ const sp<AMessage> ¬ify,
+ const char *desc, const AString ¶ms);
+
+ static bool Supports(const char *desc);
+
+ static void MakeFormat(
+ const char *desc, const sp<MetaData> &format);
+
+protected:
+ virtual ~ARawAudioAssembler();
+
+ virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+ virtual void onByeReceived();
+ virtual void packetLost();
+
+private:
+ bool mIsWide;
+
+ sp<AMessage> mNotifyMsg;
+ bool mNextExpectedSeqNoValid;
+ uint32_t mNextExpectedSeqNo;
+
+ AssemblyStatus addPacket(const sp<ARTPSource> &source);
+
+ DISALLOW_EVIL_CONSTRUCTORS(ARawAudioAssembler);
+};
+
+} // namespace android
+
+#endif // A_RAW_AUDIO_ASSEMBLER_H_
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 3e710dc..f03f7a2 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -71,6 +71,11 @@
line.setTo(desc, i, eolPos - i);
}
+ if (line.empty()) {
+ i = eolPos + 1;
+ continue;
+ }
+
if (line.size() < 2 || line.c_str()[1] != '=') {
return false;
}
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 0bbadc1..8530ff3 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -9,15 +9,14 @@
AMPEG4AudioAssembler.cpp \
AMPEG4ElementaryAssembler.cpp \
APacketSource.cpp \
+ ARawAudioAssembler.cpp \
ARTPAssembler.cpp \
ARTPConnection.cpp \
- ARTPSession.cpp \
ARTPSource.cpp \
ARTPWriter.cpp \
ARTSPConnection.cpp \
ARTSPController.cpp \
ASessionDescription.cpp \
- UDPPusher.cpp \
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
@@ -57,4 +56,4 @@
LOCAL_MODULE:= rtp_test
-include $(BUILD_EXECUTABLE)
+# include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 306a9c1..d15d9c5 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -38,10 +38,11 @@
#include <arpa/inet.h>
#include <sys/socket.h>
+#include <netdb.h>
-// If no access units are received within 3 secs, assume that the rtp
+// If no access units are received within 5 secs, assume that the rtp
// stream has ended and signal end of stream.
-static int64_t kAccessUnitTimeoutUs = 3000000ll;
+static int64_t kAccessUnitTimeoutUs = 5000000ll;
// If no access units arrive for the first 10 secs after starting the
// stream, assume none ever will and signal EOS or switch transports.
@@ -101,7 +102,9 @@
mSetupTracksSuccessful(false),
mSeekPending(false),
mFirstAccessUnit(true),
- mFirstAccessUnitNTP(0),
+ mNTPAnchorUs(-1),
+ mMediaAnchorUs(-1),
+ mLastMediaTimeUs(0),
mNumAccessUnitsReceived(0),
mCheckPending(false),
mCheckGeneration(0),
@@ -119,9 +122,10 @@
// want to transmit user/pass in cleartext.
AString host, path, user, pass;
unsigned port;
- if (ARTSPConnection::ParseURL(
- mSessionURL.c_str(), &host, &port, &path, &user, &pass)
- && user.size() > 0) {
+ CHECK(ARTSPConnection::ParseURL(
+ mSessionURL.c_str(), &host, &port, &path, &user, &pass));
+
+ if (user.size() > 0) {
mSessionURL.clear();
mSessionURL.append("rtsp://");
mSessionURL.append(host);
@@ -131,6 +135,8 @@
LOGI("rewritten session url: '%s'", mSessionURL.c_str());
}
+
+ mSessionHost = host;
}
void connect(const sp<AMessage> &doneMsg) {
@@ -246,34 +252,64 @@
// In case we're behind NAT, fire off two UDP packets to the remote
// rtp/rtcp ports to poke a hole into the firewall for future incoming
// packets. We're going to send an RR/SDES RTCP packet to both of them.
- void pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) {
+ bool pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) {
+ struct sockaddr_in addr;
+ memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+ addr.sin_family = AF_INET;
+
AString source;
AString server_port;
if (!GetAttribute(transport.c_str(),
"source",
- &source)
- || !GetAttribute(transport.c_str(),
+ &source)) {
+ LOGW("Missing 'source' field in Transport response. Using "
+ "RTSP endpoint address.");
+
+ struct hostent *ent = gethostbyname(mSessionHost.c_str());
+ if (ent == NULL) {
+ LOGE("Failed to look up address of session host '%s'",
+ mSessionHost.c_str());
+
+ return false;
+ }
+
+ addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+ } else {
+ addr.sin_addr.s_addr = inet_addr(source.c_str());
+ }
+
+ if (!GetAttribute(transport.c_str(),
"server_port",
&server_port)) {
- return;
+ LOGI("Missing 'server_port' field in Transport response.");
+ return false;
}
int rtpPort, rtcpPort;
if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
|| rtpPort <= 0 || rtpPort > 65535
|| rtcpPort <=0 || rtcpPort > 65535
- || rtcpPort != rtpPort + 1
- || (rtpPort & 1) != 0) {
- return;
+ || rtcpPort != rtpPort + 1) {
+ LOGE("Server picked invalid RTP/RTCP port pair %s,"
+ " RTP port must be even, RTCP port must be one higher.",
+ server_port.c_str());
+
+ return false;
}
- struct sockaddr_in addr;
- memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr(source.c_str());
+ if (rtpPort & 1) {
+ LOGW("Server picked an odd RTP port, it should've picked an "
+ "even one, we'll let it pass for now, but this may break "
+ "in the future.");
+ }
if (addr.sin_addr.s_addr == INADDR_NONE) {
- return;
+ return true;
+ }
+
+ if (IN_LOOPBACK(ntohl(addr.sin_addr.s_addr))) {
+ // No firewalls to traverse on the loopback interface.
+ return true;
}
// Make up an RR/SDES RTCP packet.
@@ -287,16 +323,26 @@
ssize_t n = sendto(
rtpSocket, buf->data(), buf->size(), 0,
(const sockaddr *)&addr, sizeof(addr));
- CHECK_EQ(n, (ssize_t)buf->size());
+
+ if (n < (ssize_t)buf->size()) {
+ LOGE("failed to poke a hole for RTP packets");
+ return false;
+ }
addr.sin_port = htons(rtcpPort);
n = sendto(
rtcpSocket, buf->data(), buf->size(), 0,
(const sockaddr *)&addr, sizeof(addr));
- CHECK_EQ(n, (ssize_t)buf->size());
+
+ if (n < (ssize_t)buf->size()) {
+ LOGE("failed to poke a hole for RTCP packets");
+ return false;
+ }
LOGV("successfully poked holes.");
+
+ return true;
}
virtual void onMessageReceived(const sp<AMessage> &msg) {
@@ -379,6 +425,7 @@
response->mContent->size());
if (!mSessionDesc->isValid()) {
+ LOGE("Failed to parse session description.");
result = ERROR_MALFORMED;
} else {
ssize_t i = response->mHeaders.indexOfKey("content-base");
@@ -393,6 +440,25 @@
}
}
+ if (!mBaseURL.startsWith("rtsp://")) {
+ // Some misbehaving servers specify a relative
+ // URL in one of the locations above, combine
+ // it with the absolute session URL to get
+ // something usable...
+
+ LOGW("Server specified a non-absolute base URL"
+ ", combining it with the session URL to "
+ "get something usable...");
+
+ AString tmp;
+ CHECK(MakeURL(
+ mSessionURL.c_str(),
+ mBaseURL.c_str(),
+ &tmp));
+
+ mBaseURL = tmp;
+ }
+
CHECK_GT(mSessionDesc->countTracks(), 1u);
setupTrack(1);
}
@@ -453,9 +519,12 @@
if (!track->mUsingInterleavedTCP) {
AString transport = response->mHeaders.valueAt(i);
- pokeAHole(track->mRTPSocket,
- track->mRTCPSocket,
- transport);
+ // We are going to continue even if we were
+ // unable to poke a hole into the firewall...
+ pokeAHole(
+ track->mRTPSocket,
+ track->mRTCPSocket,
+ transport);
}
mRTPConn->addStream(
@@ -551,7 +620,8 @@
mSetupTracksSuccessful = false;
mSeekPending = false;
mFirstAccessUnit = true;
- mFirstAccessUnitNTP = 0;
+ mNTPAnchorUs = -1;
+ mMediaAnchorUs = -1;
mNumAccessUnitsReceived = 0;
mReceivedFirstRTCPPacket = false;
mReceivedFirstRTPPacket = false;
@@ -632,6 +702,20 @@
case 'accu':
{
+ int32_t timeUpdate;
+ if (msg->findInt32("time-update", &timeUpdate) && timeUpdate) {
+ size_t trackIndex;
+ CHECK(msg->findSize("track-index", &trackIndex));
+
+ uint32_t rtpTime;
+ uint64_t ntpTime;
+ CHECK(msg->findInt32("rtp-time", (int32_t *)&rtpTime));
+ CHECK(msg->findInt64("ntp-time", (int64_t *)&ntpTime));
+
+ onTimeUpdate(trackIndex, rtpTime, ntpTime);
+ break;
+ }
+
int32_t first;
if (msg->findInt32("first-rtcp", &first)) {
mReceivedFirstRTCPPacket = true;
@@ -683,51 +767,11 @@
break;
}
- uint64_t ntpTime;
- CHECK(accessUnit->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
- uint32_t rtpTime;
- CHECK(accessUnit->meta()->findInt32(
- "rtp-time", (int32_t *)&rtpTime));
-
if (track->mNewSegment) {
track->mNewSegment = false;
-
- LOGV("first segment unit ntpTime=0x%016llx rtpTime=%u seq=%d",
- ntpTime, rtpTime, seqNum);
}
- if (mFirstAccessUnit) {
- mDoneMsg->setInt32("result", OK);
- mDoneMsg->post();
- mDoneMsg = NULL;
-
- mFirstAccessUnit = false;
- mFirstAccessUnitNTP = ntpTime;
- }
-
- if (ntpTime >= mFirstAccessUnitNTP) {
- ntpTime -= mFirstAccessUnitNTP;
- } else {
- ntpTime = 0;
- }
-
- int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
-
- accessUnit->meta()->setInt64("timeUs", timeUs);
-
-#if 0
- int32_t damaged;
- if (accessUnit->meta()->findInt32("damaged", &damaged)
- && damaged != 0) {
- LOGI("ignoring damaged AU");
- } else
-#endif
- {
- TrackInfo *track = &mTracks.editItemAt(trackIndex);
- track->mPacketSource->queueAccessUnit(accessUnit);
- }
+ onAccessUnitComplete(trackIndex, accessUnit);
break;
}
@@ -778,9 +822,15 @@
{
// Session is paused now.
for (size_t i = 0; i < mTracks.size(); ++i) {
- mTracks.editItemAt(i).mPacketSource->flushQueue();
+ TrackInfo *info = &mTracks.editItemAt(i);
+
+ info->mPacketSource->flushQueue();
+ info->mRTPAnchor = 0;
+ info->mNTPAnchorUs = -1;
}
+ mNTPAnchorUs = -1;
+
int64_t timeUs;
CHECK(msg->findInt64("time", &timeUs));
@@ -831,6 +881,11 @@
} else {
parsePlayResponse(response);
+ ssize_t i = response->mHeaders.indexOfKey("rtp-info");
+ CHECK_GE(i, 0);
+
+ LOGV("rtp-info: %s", response->mHeaders.valueAt(i).c_str());
+
LOGI("seek completed.");
}
}
@@ -865,18 +920,16 @@
case 'tiou':
{
if (!mReceivedFirstRTCPPacket) {
- if (mTryFakeRTCP) {
- LOGW("Never received any data, disconnecting.");
- (new AMessage('abor', id()))->post();
- } else if (mTryTCPInterleaving && mReceivedFirstRTPPacket) {
+ if (mReceivedFirstRTPPacket && !mTryFakeRTCP) {
LOGW("We received RTP packets but no RTCP packets, "
"using fake timestamps.");
mTryFakeRTCP = true;
mReceivedFirstRTCPPacket = true;
- mRTPConn->fakeTimestamps();
- } else {
+
+ fakeTimestamps();
+ } else if (!mReceivedFirstRTPPacket && !mTryTCPInterleaving) {
LOGW("Never received any data, switching transports.");
mTryTCPInterleaving = true;
@@ -884,6 +937,9 @@
sp<AMessage> msg = new AMessage('abor', id());
msg->setInt32("reconnect", true);
msg->post();
+ } else {
+ LOGW("Never received any data, disconnecting.");
+ (new AMessage('abor', id()))->post();
}
}
break;
@@ -980,7 +1036,7 @@
uint32_t rtpTime = strtoul(val.c_str(), &end, 10);
- LOGV("track #%d: rtpTime=%u <=> ntp=%.2f", n, rtpTime, npt1);
+ LOGV("track #%d: rtpTime=%u <=> npt=%.2f", n, rtpTime, npt1);
info->mPacketSource->setNormalPlayTimeMapping(
rtpTime, (int64_t)(npt1 * 1E6));
@@ -1003,6 +1059,25 @@
}
private:
+ struct TrackInfo {
+ AString mURL;
+ int mRTPSocket;
+ int mRTCPSocket;
+ bool mUsingInterleavedTCP;
+ uint32_t mFirstSeqNumInSegment;
+ bool mNewSegment;
+
+ uint32_t mRTPAnchor;
+ int64_t mNTPAnchorUs;
+ int32_t mTimeScale;
+
+ sp<APacketSource> mPacketSource;
+
+ // Stores packets temporarily while no notion of time
+ // has been established yet.
+ List<sp<ABuffer> > mPackets;
+ };
+
sp<ALooper> mLooper;
sp<ALooper> mNetLooper;
sp<ARTSPConnection> mConn;
@@ -1010,12 +1085,17 @@
sp<ASessionDescription> mSessionDesc;
AString mOriginalSessionURL; // This one still has user:pass@
AString mSessionURL;
+ AString mSessionHost;
AString mBaseURL;
AString mSessionID;
bool mSetupTracksSuccessful;
bool mSeekPending;
bool mFirstAccessUnit;
- uint64_t mFirstAccessUnitNTP;
+
+ int64_t mNTPAnchorUs;
+ int64_t mMediaAnchorUs;
+ int64_t mLastMediaTimeUs;
+
int64_t mNumAccessUnitsReceived;
bool mCheckPending;
int32_t mCheckGeneration;
@@ -1025,16 +1105,6 @@
bool mReceivedFirstRTPPacket;
bool mSeekable;
- struct TrackInfo {
- AString mURL;
- int mRTPSocket;
- int mRTCPSocket;
- bool mUsingInterleavedTCP;
- uint32_t mFirstSeqNumInSegment;
- bool mNewSegment;
-
- sp<APacketSource> mPacketSource;
- };
Vector<TrackInfo> mTracks;
sp<AMessage> mDoneMsg;
@@ -1066,6 +1136,20 @@
info->mUsingInterleavedTCP = false;
info->mFirstSeqNumInSegment = 0;
info->mNewSegment = true;
+ info->mRTPAnchor = 0;
+ info->mNTPAnchorUs = -1;
+
+ unsigned long PT;
+ AString formatDesc;
+ AString formatParams;
+ mSessionDesc->getFormatType(index, &PT, &formatDesc, &formatParams);
+
+ int32_t timescale;
+ int32_t numChannels;
+ ASessionDescription::ParseFormatDesc(
+ formatDesc.c_str(), ×cale, &numChannels);
+
+ info->mTimeScale = timescale;
LOGV("track #%d URL=%s", mTracks.size(), trackURL.c_str());
@@ -1144,6 +1228,96 @@
return true;
}
+ void fakeTimestamps() {
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ onTimeUpdate(i, 0, 0ll);
+ }
+ }
+
+ void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) {
+ LOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx",
+ trackIndex, rtpTime, ntpTime);
+
+ int64_t ntpTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+
+ TrackInfo *track = &mTracks.editItemAt(trackIndex);
+
+ track->mRTPAnchor = rtpTime;
+ track->mNTPAnchorUs = ntpTimeUs;
+
+ if (mNTPAnchorUs < 0) {
+ mNTPAnchorUs = ntpTimeUs;
+ mMediaAnchorUs = mLastMediaTimeUs;
+ }
+ }
+
+ void onAccessUnitComplete(
+ int32_t trackIndex, const sp<ABuffer> &accessUnit) {
+ LOGV("onAccessUnitComplete track %d", trackIndex);
+
+ if (mFirstAccessUnit) {
+ mDoneMsg->setInt32("result", OK);
+ mDoneMsg->post();
+ mDoneMsg = NULL;
+
+ mFirstAccessUnit = false;
+ }
+
+ TrackInfo *track = &mTracks.editItemAt(trackIndex);
+
+ if (mNTPAnchorUs < 0 || mMediaAnchorUs < 0 || track->mNTPAnchorUs < 0) {
+ LOGV("storing accessUnit, no time established yet");
+ track->mPackets.push_back(accessUnit);
+ return;
+ }
+
+ while (!track->mPackets.empty()) {
+ sp<ABuffer> accessUnit = *track->mPackets.begin();
+ track->mPackets.erase(track->mPackets.begin());
+
+ if (addMediaTimestamp(trackIndex, track, accessUnit)) {
+ track->mPacketSource->queueAccessUnit(accessUnit);
+ }
+ }
+
+ if (addMediaTimestamp(trackIndex, track, accessUnit)) {
+ track->mPacketSource->queueAccessUnit(accessUnit);
+ }
+ }
+
+ bool addMediaTimestamp(
+ int32_t trackIndex, const TrackInfo *track,
+ const sp<ABuffer> &accessUnit) {
+ uint32_t rtpTime;
+ CHECK(accessUnit->meta()->findInt32(
+ "rtp-time", (int32_t *)&rtpTime));
+
+ int64_t relRtpTimeUs =
+ (((int64_t)rtpTime - (int64_t)track->mRTPAnchor) * 1000000ll)
+ / track->mTimeScale;
+
+ int64_t ntpTimeUs = track->mNTPAnchorUs + relRtpTimeUs;
+
+ int64_t mediaTimeUs = mMediaAnchorUs + ntpTimeUs - mNTPAnchorUs;
+
+ if (mediaTimeUs > mLastMediaTimeUs) {
+ mLastMediaTimeUs = mediaTimeUs;
+ }
+
+ if (mediaTimeUs < 0) {
+ LOGV("dropping early accessUnit.");
+ return false;
+ }
+
+ LOGV("track %d rtpTime=%d mediaTimeUs = %lld us (%.2f secs)",
+ trackIndex, rtpTime, mediaTimeUs, mediaTimeUs / 1E6);
+
+ accessUnit->meta()->setInt64("timeUs", mediaTimeUs);
+
+ return true;
+ }
+
+
DISALLOW_EVIL_CONSTRUCTORS(MyHandler);
};
diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk
index 7697e3c..a4253f6 100644
--- a/media/libstagefright/yuv/Android.mk
+++ b/media/libstagefright/yuv/Android.mk
@@ -10,6 +10,6 @@
LOCAL_MODULE:= libstagefright_yuv
-LOCAL_PRELINK_MODULE := false
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index 70dc340..c25285e 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -21,7 +21,6 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- MtpClient.cpp \
MtpDataPacket.cpp \
MtpDebug.cpp \
MtpDevice.cpp \
@@ -53,7 +52,6 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- MtpClient.cpp \
MtpDataPacket.cpp \
MtpDebug.cpp \
MtpDevice.cpp \
diff --git a/media/mtp/MtpClient.cpp b/media/mtp/MtpClient.cpp
deleted file mode 100644
index c830540..0000000
--- a/media/mtp/MtpClient.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2010 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 "MtpClient"
-
-#include "MtpDebug.h"
-#include "MtpClient.h"
-#include "MtpDevice.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <usbhost/usbhost.h>
-
-struct usb_device;
-
-namespace android {
-
-static bool isMtpDevice(uint16_t vendor, uint16_t product) {
- // Sandisk Sansa Fuze
- if (vendor == 0x0781 && product == 0x74c2)
- return true;
- // Samsung YP-Z5
- if (vendor == 0x04e8 && product == 0x503c)
- return true;
- return false;
-}
-
-class MtpClientThread : public Thread {
-private:
- MtpClient* mClient;
-
-public:
- MtpClientThread(MtpClient* client)
- : mClient(client)
- {
- }
-
- virtual bool threadLoop() {
- return mClient->threadLoop();
- }
-};
-
-
-MtpClient::MtpClient()
- : mThread(NULL),
- mUsbHostContext(NULL),
- mDone(false)
-{
-}
-
-MtpClient::~MtpClient() {
- usb_host_cleanup(mUsbHostContext);
-}
-
-bool MtpClient::start() {
- Mutex::Autolock autoLock(mMutex);
-
- if (mThread)
- return true;
-
- mUsbHostContext = usb_host_init();
- if (!mUsbHostContext)
- return false;
-
- mThread = new MtpClientThread(this);
- mThread->run("MtpClientThread");
- // wait for the thread to do initial device discovery before returning
- mThreadStartCondition.wait(mMutex);
-
- return true;
-}
-
-void MtpClient::stop() {
- mDone = true;
-}
-
-MtpDevice* MtpClient::getDevice(int id) {
- for (int i = 0; i < mDeviceList.size(); i++) {
- MtpDevice* device = mDeviceList[i];
- if (device->getID() == id)
- return device;
- }
- return NULL;
-}
-
-bool MtpClient::usbDeviceAdded(const char *devname) {
- struct usb_descriptor_header* desc;
- struct usb_descriptor_iter iter;
-
- struct usb_device *device = usb_device_open(devname);
- if (!device) {
- LOGE("usb_device_open failed\n");
- return mDone;
- }
-
- usb_descriptor_iter_init(device, &iter);
-
- while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
- if (desc->bDescriptorType == USB_DT_INTERFACE) {
- struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
-
- if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
- interface->bInterfaceSubClass == 1 && // Still Image Capture
- interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
- {
- LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
- usb_device_get_product_name(device));
- } else if (interface->bInterfaceClass == 0xFF &&
- interface->bInterfaceSubClass == 0xFF &&
- interface->bInterfaceProtocol == 0) {
- char* interfaceName = usb_device_get_string(device, interface->iInterface);
- if (!interfaceName || strcmp(interfaceName, "MTP"))
- continue;
- // Looks like an android style MTP device
- LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
- usb_device_get_product_name(device));
- } else {
- // look for special cased devices based on vendor/product ID
- // we are doing this mainly for testing purposes
- uint16_t vendor = usb_device_get_vendor_id(device);
- uint16_t product = usb_device_get_product_id(device);
- if (!isMtpDevice(vendor, product)) {
- // not an MTP or PTP device
- continue;
- }
- // request MTP OS string and descriptor
- // some music players need to see this before entering MTP mode.
- char buffer[256];
- memset(buffer, 0, sizeof(buffer));
- int ret = usb_device_send_control(device,
- USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
- USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
- 0, sizeof(buffer), buffer);
- printf("usb_device_send_control returned %d errno: %d\n", ret, errno);
- if (ret > 0) {
- printf("got MTP string %s\n", buffer);
- ret = usb_device_send_control(device,
- USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
- 0, 4, sizeof(buffer), buffer);
- printf("OS descriptor got %d\n", ret);
- } else {
- printf("no MTP string\n");
- }
- }
-
- // if we got here, then we have a likely MTP or PTP device
-
- // interface should be followed by three endpoints
- struct usb_endpoint_descriptor *ep;
- struct usb_endpoint_descriptor *ep_in_desc = NULL;
- struct usb_endpoint_descriptor *ep_out_desc = NULL;
- struct usb_endpoint_descriptor *ep_intr_desc = NULL;
- for (int i = 0; i < 3; i++) {
- ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
- if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
- LOGE("endpoints not found\n");
- return mDone;
- }
- if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
- if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- ep_in_desc = ep;
- else
- ep_out_desc = ep;
- } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
- ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
- ep_intr_desc = ep;
- }
- }
- if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
- LOGE("endpoints not found\n");
- return mDone;
- }
-
- if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
- LOGE("usb_device_claim_interface failed errno: %d\n", errno);
- return mDone;
- }
-
- MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
- ep_in_desc, ep_out_desc, ep_intr_desc);
- mDeviceList.add(mtpDevice);
- mtpDevice->initialize();
- deviceAdded(mtpDevice);
- return mDone;
- }
- }
-
- usb_device_close(device);
- return mDone;
-}
-
-bool MtpClient::usbDeviceRemoved(const char *devname) {
- for (int i = 0; i < mDeviceList.size(); i++) {
- MtpDevice* device = mDeviceList[i];
- if (!strcmp(devname, device->getDeviceName())) {
- deviceRemoved(device);
- mDeviceList.removeAt(i);
- delete device;
- LOGD("Camera removed!\n");
- break;
- }
- }
- return mDone;
-}
-
-bool MtpClient::usbDiscoveryDone() {
- Mutex::Autolock autoLock(mMutex);
- mThreadStartCondition.signal();
- return mDone;
-}
-
-bool MtpClient::threadLoop() {
- usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, usb_discovery_done, this);
- return false;
-}
-
-int MtpClient::usb_device_added(const char *devname, void* client_data) {
- LOGD("usb_device_added %s\n", devname);
- return ((MtpClient *)client_data)->usbDeviceAdded(devname);
-}
-
-int MtpClient::usb_device_removed(const char *devname, void* client_data) {
- LOGD("usb_device_removed %s\n", devname);
- return ((MtpClient *)client_data)->usbDeviceRemoved(devname);
-}
-
-int MtpClient::usb_discovery_done(void* client_data) {
- LOGD("usb_discovery_done\n");
- return ((MtpClient *)client_data)->usbDiscoveryDone();
-}
-
-} // namespace android
diff --git a/media/mtp/MtpClient.h b/media/mtp/MtpClient.h
deleted file mode 100644
index fa5c527..0000000
--- a/media/mtp/MtpClient.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2010 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 _MTP_CLIENT_H
-#define _MTP_CLIENT_H
-
-#include "MtpTypes.h"
-
-#include <utils/threads.h>
-
-struct usb_host_context;
-
-namespace android {
-
-class MtpClientThread;
-
-class MtpClient {
-private:
- MtpDeviceList mDeviceList;
- MtpClientThread* mThread;
- Condition mThreadStartCondition;
- Mutex mMutex;
- struct usb_host_context* mUsbHostContext;
- bool mDone;
-
-public:
- MtpClient();
- virtual ~MtpClient();
-
- bool start();
- void stop();
-
- inline MtpDeviceList& getDeviceList() { return mDeviceList; }
- MtpDevice* getDevice(int id);
-
-
- virtual void deviceAdded(MtpDevice *device) = 0;
- virtual void deviceRemoved(MtpDevice *device) = 0;
-
-private:
- // these return true if we should stop monitoring USB and clean up
- bool usbDeviceAdded(const char *devname);
- bool usbDeviceRemoved(const char *devname);
- bool usbDiscoveryDone();
-
- friend class MtpClientThread;
- bool threadLoop();
- static int usb_device_added(const char *devname, void* client_data);
- static int usb_device_removed(const char *devname, void* client_data);
- static int usb_discovery_done(void* client_data);
-};
-
-}; // namespace android
-
-#endif // _MTP_CLIENT_H
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 801edb0..817eac0 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -28,7 +28,7 @@
namespace android {
MtpDataPacket::MtpDataPacket()
- : MtpPacket(512),
+ : MtpPacket(16384), // MAX_USBFS_BUFFER_SIZE
mOffset(MTP_CONTAINER_HEADER_SIZE)
{
}
@@ -388,6 +388,16 @@
int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
return (ret < 0 ? ret : 0);
}
+
+int MtpDataPacket::writeData(int fd, void* data, uint32_t length) {
+ MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length + MTP_CONTAINER_HEADER_SIZE);
+ MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+ int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
+ if (ret == MTP_CONTAINER_HEADER_SIZE)
+ ret = ::write(fd, data, length);
+ return (ret < 0 ? ret : 0);
+}
+
#endif // MTP_DEVICE
#ifdef MTP_HOST
@@ -399,10 +409,10 @@
if (length >= MTP_CONTAINER_HEADER_SIZE) {
// look at the length field to see if the data spans multiple packets
uint32_t totalLength = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET);
+ allocate(totalLength);
while (totalLength > length) {
- allocate(length + mAllocationIncrement);
request->buffer = mBuffer + length;
- request->buffer_length = mAllocationIncrement;
+ request->buffer_length = totalLength - length;
int ret = transfer(request);
if (ret >= 0)
length += ret;
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index 577cea1..8a08948 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -100,6 +100,7 @@
// write our data to the given file descriptor
int write(int fd);
int writeDataHeader(int fd, uint32_t length);
+ int writeData(int fd, void* data, uint32_t length);
#endif
#ifdef MTP_HOST
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 4d9a1ae..4e6ac7a 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -23,6 +23,7 @@
class MtpDataPacket;
class MtpProperty;
+class MtpObjectInfo;
class MtpDatabase {
public:
@@ -81,7 +82,9 @@
MtpDataPacket& packet) = 0;
virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle,
- MtpDataPacket& packet) = 0;
+ MtpObjectInfo& info) = 0;
+
+ virtual void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) = 0;
virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle,
MtpString& outFilePath,
diff --git a/media/mtp/MtpDebug.cpp b/media/mtp/MtpDebug.cpp
index 1668ecf..9f3037d 100644
--- a/media/mtp/MtpDebug.cpp
+++ b/media/mtp/MtpDebug.cpp
@@ -63,6 +63,12 @@
{ "MTP_OPERATION_GET_OBJECT_REFERENCES", 0x9810 },
{ "MTP_OPERATION_SET_OBJECT_REFERENCES", 0x9811 },
{ "MTP_OPERATION_SKIP", 0x9820 },
+ // android extensions
+ { "MTP_OPERATION_GET_PARTIAL_OBJECT_64", 0x95C1 },
+ { "MTP_OPERATION_SEND_PARTIAL_OBJECT", 0x95C2 },
+ { "MTP_OPERATION_TRUNCATE_OBJECT", 0x95C3 },
+ { "MTP_OPERATION_BEGIN_EDIT_OBJECT", 0x95C4 },
+ { "MTP_OPERATION_END_EDIT_OBJECT", 0x95C5 },
{ 0, 0 },
};
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index d22c72f..2e86159 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -38,6 +38,140 @@
namespace android {
+#if 0
+static bool isMtpDevice(uint16_t vendor, uint16_t product) {
+ // Sandisk Sansa Fuze
+ if (vendor == 0x0781 && product == 0x74c2)
+ return true;
+ // Samsung YP-Z5
+ if (vendor == 0x04e8 && product == 0x503c)
+ return true;
+ return false;
+}
+#endif
+
+MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
+ struct usb_device *device = usb_device_new(deviceName, fd);
+ if (!device) {
+ LOGE("usb_device_new failed for %s", deviceName);
+ return NULL;
+ }
+
+ struct usb_descriptor_header* desc;
+ struct usb_descriptor_iter iter;
+
+ usb_descriptor_iter_init(device, &iter);
+
+ while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+ if (desc->bDescriptorType == USB_DT_INTERFACE) {
+ struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+
+ if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
+ interface->bInterfaceSubClass == 1 && // Still Image Capture
+ interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
+ {
+ char* manufacturerName = usb_device_get_manufacturer_name(device);
+ char* productName = usb_device_get_product_name(device);
+ LOGD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName);
+ free(manufacturerName);
+ free(productName);
+ } else if (interface->bInterfaceClass == 0xFF &&
+ interface->bInterfaceSubClass == 0xFF &&
+ interface->bInterfaceProtocol == 0) {
+ char* interfaceName = usb_device_get_string(device, interface->iInterface);
+ if (!interfaceName) {
+ continue;
+ } else if (strcmp(interfaceName, "MTP")) {
+ free(interfaceName);
+ continue;
+ }
+ free(interfaceName);
+
+ // Looks like an android style MTP device
+ char* manufacturerName = usb_device_get_manufacturer_name(device);
+ char* productName = usb_device_get_product_name(device);
+ LOGD("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
+ free(manufacturerName);
+ free(productName);
+ }
+#if 0
+ else {
+ // look for special cased devices based on vendor/product ID
+ // we are doing this mainly for testing purposes
+ uint16_t vendor = usb_device_get_vendor_id(device);
+ uint16_t product = usb_device_get_product_id(device);
+ if (!isMtpDevice(vendor, product)) {
+ // not an MTP or PTP device
+ continue;
+ }
+ // request MTP OS string and descriptor
+ // some music players need to see this before entering MTP mode.
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ int ret = usb_device_control_transfer(device,
+ USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
+ USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
+ 0, buffer, sizeof(buffer), 0);
+ printf("usb_device_control_transfer returned %d errno: %d\n", ret, errno);
+ if (ret > 0) {
+ printf("got MTP string %s\n", buffer);
+ ret = usb_device_control_transfer(device,
+ USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
+ 0, 4, buffer, sizeof(buffer), 0);
+ printf("OS descriptor got %d\n", ret);
+ } else {
+ printf("no MTP string\n");
+ }
+ }
+#endif
+ // if we got here, then we have a likely MTP or PTP device
+
+ // interface should be followed by three endpoints
+ struct usb_endpoint_descriptor *ep;
+ struct usb_endpoint_descriptor *ep_in_desc = NULL;
+ struct usb_endpoint_descriptor *ep_out_desc = NULL;
+ struct usb_endpoint_descriptor *ep_intr_desc = NULL;
+ for (int i = 0; i < 3; i++) {
+ ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
+ if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
+ LOGE("endpoints not found\n");
+ usb_device_close(device);
+ return NULL;
+ }
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+ if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ ep_in_desc = ep;
+ else
+ ep_out_desc = ep;
+ } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
+ ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+ ep_intr_desc = ep;
+ }
+ }
+ if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
+ LOGE("endpoints not found\n");
+ usb_device_close(device);
+ return NULL;
+ }
+
+ if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
+ LOGE("usb_device_claim_interface failed errno: %d\n", errno);
+ usb_device_close(device);
+ return NULL;
+ }
+
+ MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
+ ep_in_desc, ep_out_desc, ep_intr_desc);
+ mtpDevice->initialize();
+ return mtpDevice;
+ }
+ }
+
+ usb_device_close(device);
+ LOGE("device not found");
+ return NULL;
+}
+
MtpDevice::MtpDevice(struct usb_device* device, int interface,
const struct usb_endpoint_descriptor *ep_in,
const struct usb_endpoint_descriptor *ep_out,
@@ -49,7 +183,6 @@
mRequestOut(NULL),
mRequestIntr(NULL),
mDeviceInfo(NULL),
- mID(usb_device_get_unique_id(device)),
mSessionID(0),
mTransactionID(0),
mReceivedResponse(false)
@@ -106,6 +239,7 @@
MtpProperty* property = getDevicePropDesc(propCode);
if (property) {
property->print();
+ delete property;
}
}
}
@@ -122,11 +256,13 @@
for (int j = 0; j < props->size(); j++) {
MtpObjectProperty prop = (*props)[j];
MtpProperty* property = getObjectPropDesc(prop, format);
- if (property)
+ if (property) {
property->print();
- else
+ delete property;
+ } else {
LOGE("could not fetch property: %s",
MtpDebug::getObjectPropCodeName(prop));
+ }
}
}
}
@@ -362,18 +498,24 @@
MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
MtpObjectInfo* info = getObjectInfo(handle);
- if (info)
- return info->mParent;
- else
+ if (info) {
+ MtpObjectHandle parent = info->mParent;
+ delete info;
+ return parent;
+ } else {
return -1;
+ }
}
MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
MtpObjectInfo* info = getObjectInfo(handle);
- if (info)
- return info->mStorageID;
- else
+ if (info) {
+ MtpObjectHandle storageId = info->mStorageID;
+ delete info;
+ return storageId;
+ } else {
return -1;
+ }
}
MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
@@ -430,6 +572,98 @@
return NULL;
}
+bool MtpDevice::readObject(MtpObjectHandle handle,
+ bool (* callback)(void* data, int offset, int length, void* clientData),
+ int objectSize, void* clientData) {
+ Mutex::Autolock autoLock(mMutex);
+ bool result = false;
+
+ mRequest.reset();
+ mRequest.setParameter(1, handle);
+ if (sendRequest(MTP_OPERATION_GET_OBJECT)
+ && mData.readDataHeader(mRequestIn1)) {
+ uint32_t length = mData.getContainerLength();
+ if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
+ LOGE("readObject error objectSize: %d, length: %d",
+ objectSize, length);
+ goto fail;
+ }
+ length -= MTP_CONTAINER_HEADER_SIZE;
+ uint32_t remaining = length;
+ int offset = 0;
+
+ int initialDataLength = 0;
+ void* initialData = mData.getData(initialDataLength);
+ if (initialData) {
+ if (initialDataLength > 0) {
+ if (!callback(initialData, 0, initialDataLength, clientData))
+ goto fail;
+ remaining -= initialDataLength;
+ offset += initialDataLength;
+ }
+ free(initialData);
+ }
+
+ // USB reads greater than 16K don't work
+ char buffer1[16384], buffer2[16384];
+ mRequestIn1->buffer = buffer1;
+ mRequestIn2->buffer = buffer2;
+ struct usb_request* req = mRequestIn1;
+ void* writeBuffer = NULL;
+ int writeLength = 0;
+
+ while (remaining > 0 || writeBuffer) {
+ if (remaining > 0) {
+ // queue up a read request
+ req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
+ if (mData.readDataAsync(req)) {
+ LOGE("readDataAsync failed");
+ goto fail;
+ }
+ } else {
+ req = NULL;
+ }
+
+ if (writeBuffer) {
+ // write previous buffer
+ if (!callback(writeBuffer, offset, writeLength, clientData)) {
+ LOGE("write failed");
+ // wait for pending read before failing
+ if (req)
+ mData.readDataWait(mDevice);
+ goto fail;
+ }
+ offset += writeLength;
+ writeBuffer = NULL;
+ }
+
+ // wait for read to complete
+ if (req) {
+ int read = mData.readDataWait(mDevice);
+ if (read < 0)
+ goto fail;
+
+ if (read > 0) {
+ writeBuffer = req->buffer;
+ writeLength = read;
+ remaining -= read;
+ req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+ } else {
+ writeBuffer = NULL;
+ }
+ }
+ }
+
+ MtpResponseCode response = readResponse();
+ if (response == MTP_RESPONSE_OK)
+ result = true;
+ }
+
+fail:
+ return result;
+}
+
+
// reads the object's data and writes it to the specified file path
bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
LOGD("readObject: %s", destPath);
@@ -462,8 +696,10 @@
void* initialData = mData.getData(initialDataLength);
if (initialData) {
if (initialDataLength > 0) {
- if (write(fd, initialData, initialDataLength) != initialDataLength)
+ if (write(fd, initialData, initialDataLength) != initialDataLength) {
+ free(initialData);
goto fail;
+ }
remaining -= initialDataLength;
}
free(initialData);
@@ -507,10 +743,14 @@
if (read < 0)
goto fail;
- writeBuffer = req->buffer;
- writeLength = read;
- remaining -= read;
- req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+ if (read > 0) {
+ writeBuffer = req->buffer;
+ writeLength = read;
+ remaining -= read;
+ req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+ } else {
+ writeBuffer = NULL;
+ }
}
}
@@ -579,6 +819,10 @@
return mResponse.getResponseCode();
}
int ret = mResponse.read(mRequestIn1);
+ // handle zero length packets, which might occur if the data transfer
+ // ends on a packet boundary
+ if (ret == 0)
+ ret = mResponse.read(mRequestIn1);
if (ret >= MTP_CONTAINER_HEADER_SIZE) {
mResponse.dump();
return mResponse.getResponseCode();
diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h
index d0a0fb3..b69203e 100644
--- a/media/mtp/MtpDevice.h
+++ b/media/mtp/MtpDevice.h
@@ -45,9 +45,6 @@
MtpDeviceInfo* mDeviceInfo;
MtpPropertyList mDeviceProperties;
- // a unique ID for the device
- int mID;
-
// current session ID
MtpSessionID mSessionID;
// current transaction ID
@@ -67,9 +64,10 @@
const struct usb_endpoint_descriptor *ep_in,
const struct usb_endpoint_descriptor *ep_out,
const struct usb_endpoint_descriptor *ep_intr);
- virtual ~MtpDevice();
- inline int getID() const { return mID; }
+ static MtpDevice* open(const char* deviceName, int fd);
+
+ virtual ~MtpDevice();
void initialize();
void close();
@@ -97,7 +95,11 @@
MtpProperty* getDevicePropDesc(MtpDeviceProperty code);
MtpProperty* getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format);
- bool readObject(MtpObjectHandle handle, const char* destPath, int group,
+ bool readObject(MtpObjectHandle handle,
+ bool (* callback)(void* data, int offset,
+ int length, void* clientData),
+ int objectSize, void* clientData);
+ bool readObject(MtpObjectHandle handle, const char* destPath, int group,
int perm);
private:
diff --git a/media/mtp/MtpPacket.cpp b/media/mtp/MtpPacket.cpp
index d3f2cb4..baf99e5 100644
--- a/media/mtp/MtpPacket.cpp
+++ b/media/mtp/MtpPacket.cpp
@@ -153,12 +153,13 @@
#ifdef MTP_HOST
int MtpPacket::transfer(struct usb_request* request) {
- if (usb_request_queue(request)) {
- LOGE("usb_endpoint_queue failed, errno: %d", errno);
- return -1;
- }
- request = usb_request_wait(request->dev);
- return (request ? request->actual_length : -1);
+ int result = usb_device_bulk_transfer(request->dev,
+ request->endpoint,
+ request->buffer,
+ request->buffer_length,
+ 0);
+ request->actual_length = result;
+ return result;
}
#endif
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 469fb04..4a8fd3e 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -30,6 +30,7 @@
#include "MtpDebug.h"
#include "MtpDatabase.h"
+#include "MtpObjectInfo.h"
#include "MtpProperty.h"
#include "MtpServer.h"
#include "MtpStorage.h"
@@ -49,7 +50,7 @@
MTP_OPERATION_GET_OBJECT_HANDLES,
MTP_OPERATION_GET_OBJECT_INFO,
MTP_OPERATION_GET_OBJECT,
-// MTP_OPERATION_GET_THUMB,
+ MTP_OPERATION_GET_THUMB,
MTP_OPERATION_DELETE_OBJECT,
MTP_OPERATION_SEND_OBJECT_INFO,
MTP_OPERATION_SEND_OBJECT,
@@ -79,6 +80,12 @@
MTP_OPERATION_GET_OBJECT_REFERENCES,
MTP_OPERATION_SET_OBJECT_REFERENCES,
// MTP_OPERATION_SKIP,
+ // Android extension for direct file IO
+ MTP_OPERATION_GET_PARTIAL_OBJECT_64,
+ MTP_OPERATION_SEND_PARTIAL_OBJECT,
+ MTP_OPERATION_TRUNCATE_OBJECT,
+ MTP_OPERATION_BEGIN_EDIT_OBJECT,
+ MTP_OPERATION_END_EDIT_OBJECT,
};
static const MtpEventCode kSupportedEventCodes[] = {
@@ -218,6 +225,15 @@
}
}
+ // commit any open edits
+ int count = mObjectEditList.size();
+ for (int i = 0; i < count; i++) {
+ ObjectEdit* edit = mObjectEditList[i];
+ commitEdit(edit);
+ delete edit;
+ }
+ mObjectEditList.clear();
+
if (mSessionOpen)
mDatabase->sessionEnded();
}
@@ -252,6 +268,39 @@
}
}
+void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
+ uint64_t size, MtpObjectFormat format, int fd) {
+ ObjectEdit* edit = new ObjectEdit(handle, path, size, format, fd);
+ mObjectEditList.add(edit);
+}
+
+MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
+ int count = mObjectEditList.size();
+ for (int i = 0; i < count; i++) {
+ ObjectEdit* edit = mObjectEditList[i];
+ if (edit->mHandle == handle) return edit;
+ }
+ return NULL;
+}
+
+void MtpServer::removeEditObject(MtpObjectHandle handle) {
+ int count = mObjectEditList.size();
+ for (int i = 0; i < count; i++) {
+ ObjectEdit* edit = mObjectEditList[i];
+ if (edit->mHandle == handle) {
+ delete edit;
+ mObjectEditList.removeAt(i);
+ return;
+ }
+ }
+ LOGE("ObjectEdit not found in removeEditObject");
+}
+
+void MtpServer::commitEdit(ObjectEdit* edit) {
+ mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
+}
+
+
bool MtpServer::handleRequest() {
Mutex::Autolock autoLock(mMutex);
@@ -321,8 +370,12 @@
case MTP_OPERATION_GET_OBJECT:
response = doGetObject();
break;
+ case MTP_OPERATION_GET_THUMB:
+ response = doGetThumb();
+ break;
case MTP_OPERATION_GET_PARTIAL_OBJECT:
- response = doGetPartialObject();
+ case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
+ response = doGetPartialObject(operation);
break;
case MTP_OPERATION_SEND_OBJECT_INFO:
response = doSendObjectInfo();
@@ -339,6 +392,18 @@
case MTP_OPERATION_GET_DEVICE_PROP_DESC:
response = doGetDevicePropDesc();
break;
+ case MTP_OPERATION_SEND_PARTIAL_OBJECT:
+ response = doSendPartialObject();
+ break;
+ case MTP_OPERATION_TRUNCATE_OBJECT:
+ response = doTruncateObject();
+ break;
+ case MTP_OPERATION_BEGIN_EDIT_OBJECT:
+ response = doBeginEditObject();
+ break;
+ case MTP_OPERATION_END_EDIT_OBJECT:
+ response = doEndEditObject();
+ break;
default:
LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
@@ -363,7 +428,7 @@
mData.putUInt16(MTP_STANDARD_VERSION);
mData.putUInt32(6); // MTP Vendor Extension ID
mData.putUInt16(MTP_STANDARD_VERSION);
- string.set("microsoft.com: 1.0;");
+ string.set("microsoft.com: 1.0; android.com: 1.0;");
mData.putString(string); // MTP Extensions
mData.putUInt16(0); //Functional Mode
mData.putAUInt16(kSupportedOperationCodes,
@@ -373,8 +438,9 @@
mData.putAUInt16(deviceProperties); // Device Properties Supported
mData.putAUInt16(captureFormats); // Capture Formats
mData.putAUInt16(playbackFormats); // Playback Formats
- // FIXME
- string.set("Google, Inc.");
+
+ property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
+ string.set(prop_value);
mData.putString(string); // Manufacturer
property_get("ro.product.model", prop_value, "MTP Device");
@@ -600,7 +666,40 @@
if (!hasStorage())
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
MtpObjectHandle handle = mRequest.getParameter(1);
- return mDatabase->getObjectInfo(handle, mData);
+ MtpObjectInfo info(handle);
+ MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
+ if (result == MTP_RESPONSE_OK) {
+ char date[20];
+
+ mData.putUInt32(info.mStorageID);
+ mData.putUInt16(info.mFormat);
+ mData.putUInt16(info.mProtectionStatus);
+
+ // if object is being edited the database size may be out of date
+ uint32_t size = info.mCompressedSize;
+ ObjectEdit* edit = getEditObject(handle);
+ if (edit)
+ size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
+ mData.putUInt32(size);
+
+ mData.putUInt16(info.mThumbFormat);
+ mData.putUInt32(info.mThumbCompressedSize);
+ mData.putUInt32(info.mThumbPixWidth);
+ mData.putUInt32(info.mThumbPixHeight);
+ mData.putUInt32(info.mImagePixWidth);
+ mData.putUInt32(info.mImagePixHeight);
+ mData.putUInt32(info.mImagePixDepth);
+ mData.putUInt32(info.mParent);
+ mData.putUInt16(info.mAssociationType);
+ mData.putUInt32(info.mAssociationDesc);
+ mData.putUInt32(info.mSequenceNumber);
+ mData.putString(info.mName);
+ mData.putEmptyString(); // date created
+ formatDateTime(info.mDateModified, date, sizeof(date));
+ mData.putString(date); // date modified
+ mData.putEmptyString(); // keywords
+ }
+ return result;
}
MtpResponseCode MtpServer::doGetObject() {
@@ -640,12 +739,38 @@
return MTP_RESPONSE_OK;
}
-MtpResponseCode MtpServer::doGetPartialObject() {
+MtpResponseCode MtpServer::doGetThumb() {
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ size_t thumbSize;
+ void* thumb = mDatabase->getThumbnail(handle, thumbSize);
+ if (thumb) {
+ // send data
+ mData.setOperationCode(mRequest.getOperationCode());
+ mData.setTransactionID(mRequest.getTransactionID());
+ mData.writeData(mFD, thumb, thumbSize);
+ free(thumb);
+ return MTP_RESPONSE_OK;
+ } else {
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+}
+
+MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
if (!hasStorage())
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
MtpObjectHandle handle = mRequest.getParameter(1);
- uint32_t offset = mRequest.getParameter(2);
- uint32_t length = mRequest.getParameter(3);
+ uint64_t offset;
+ uint32_t length;
+ offset = mRequest.getParameter(2);
+ if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
+ // android extension with 64 bit offset
+ uint64_t offset2 = mRequest.getParameter(3);
+ offset = offset | (offset2 << 32);
+ length = mRequest.getParameter(4);
+ } else {
+ // standard GetPartialObject
+ length = mRequest.getParameter(3);
+ }
MtpString pathBuf;
int64_t fileLength;
MtpObjectFormat format;
@@ -932,4 +1057,113 @@
return MTP_RESPONSE_OK;
}
+MtpResponseCode MtpServer::doSendPartialObject() {
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ uint64_t offset = mRequest.getParameter(2);
+ uint64_t offset2 = mRequest.getParameter(3);
+ offset = offset | (offset2 << 32);
+ uint32_t length = mRequest.getParameter(4);
+
+ ObjectEdit* edit = getEditObject(handle);
+ if (!edit) {
+ LOGE("object not open for edit in doSendPartialObject");
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ // can't start writing past the end of the file
+ if (offset > edit->mSize) {
+ LOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ // read the header
+ int ret = mData.readDataHeader(mFD);
+ // FIXME - check for errors here.
+
+ // reset so we don't attempt to send this back
+ mData.reset();
+
+ const char* filePath = (const char *)edit->mPath;
+ LOGV("receiving partial %s %lld %ld\n", filePath, offset, length);
+ mtp_file_range mfr;
+ mfr.fd = edit->mFD;
+ mfr.offset = offset;
+ mfr.length = length;
+
+ // transfer the file
+ ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+ LOGV("MTP_RECEIVE_FILE returned %d", ret);
+ if (ret < 0) {
+ mResponse.setParameter(1, 0);
+ if (errno == ECANCELED)
+ return MTP_RESPONSE_TRANSACTION_CANCELLED;
+ else
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+ mResponse.setParameter(1, length);
+ uint64_t end = offset + length;
+ if (end > edit->mSize) {
+ edit->mSize = end;
+ }
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doTruncateObject() {
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ ObjectEdit* edit = getEditObject(handle);
+ if (!edit) {
+ LOGE("object not open for edit in doTruncateObject");
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ uint64_t offset = mRequest.getParameter(2);
+ uint64_t offset2 = mRequest.getParameter(3);
+ offset |= (offset2 << 32);
+ if (ftruncate(edit->mFD, offset) != 0) {
+ return MTP_RESPONSE_GENERAL_ERROR;
+ } else {
+ edit->mSize = offset;
+ return MTP_RESPONSE_OK;
+ }
+}
+
+MtpResponseCode MtpServer::doBeginEditObject() {
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ if (getEditObject(handle)) {
+ LOGE("object already open for edit in doBeginEditObject");
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ MtpString path;
+ int64_t fileLength;
+ MtpObjectFormat format;
+ int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
+ if (result != MTP_RESPONSE_OK)
+ return result;
+
+ int fd = open((const char *)path, O_RDWR | O_EXCL);
+ if (fd < 0) {
+ LOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ addEditObject(handle, path, fileLength, format, fd);
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doEndEditObject() {
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ ObjectEdit* edit = getEditObject(handle);
+ if (!edit) {
+ LOGE("object not open for edit in doEndEditObject");
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ commitEdit(edit);
+ removeEditObject(handle);
+ return MTP_RESPONSE_OK;
+}
+
} // namespace android
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index 1efa715..859a18e 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -65,11 +65,35 @@
Mutex mMutex;
+ // represents an MTP object that is being edited using the android extensions
+ // for direct editing (BeginEditObject, SendPartialObject, TruncateObject and EndEditObject)
+ class ObjectEdit {
+ public:
+ MtpObjectHandle mHandle;
+ MtpString mPath;
+ uint64_t mSize;
+ MtpObjectFormat mFormat;
+ int mFD;
+
+ ObjectEdit(MtpObjectHandle handle, const char* path, uint64_t size,
+ MtpObjectFormat format, int fd)
+ : mHandle(handle), mPath(path), mSize(size), mFormat(format), mFD(fd) {
+ }
+
+ virtual ~ObjectEdit() {
+ close(mFD);
+ }
+ };
+ Vector<ObjectEdit*> mObjectEditList;
+
public:
MtpServer(int fd, MtpDatabase* database,
int fileGroup, int filePerm, int directoryPerm);
virtual ~MtpServer();
+ MtpStorage* getStorage(MtpStorageID id);
+ inline bool hasStorage() { return mStorages.size() > 0; }
+ bool hasStorage(MtpStorageID id);
void addStorage(MtpStorage* storage);
void removeStorage(MtpStorage* storage);
@@ -79,13 +103,16 @@
void sendObjectRemoved(MtpObjectHandle handle);
private:
- MtpStorage* getStorage(MtpStorageID id);
- inline bool hasStorage() { return mStorages.size() > 0; }
- bool hasStorage(MtpStorageID id);
void sendStoreAdded(MtpStorageID id);
void sendStoreRemoved(MtpStorageID id);
void sendEvent(MtpEventCode code, uint32_t param1);
+ void addEditObject(MtpObjectHandle handle, MtpString& path,
+ uint64_t size, MtpObjectFormat format, int fd);
+ ObjectEdit* getEditObject(MtpObjectHandle handle);
+ void removeEditObject(MtpObjectHandle handle);
+ void commitEdit(ObjectEdit* edit);
+
bool handleRequest();
MtpResponseCode doGetDeviceInfo();
@@ -106,12 +133,17 @@
MtpResponseCode doGetObjectPropList();
MtpResponseCode doGetObjectInfo();
MtpResponseCode doGetObject();
- MtpResponseCode doGetPartialObject();
+ MtpResponseCode doGetThumb();
+ MtpResponseCode doGetPartialObject(MtpOperationCode operation);
MtpResponseCode doSendObjectInfo();
MtpResponseCode doSendObject();
MtpResponseCode doDeleteObject();
MtpResponseCode doGetObjectPropDesc();
MtpResponseCode doGetDevicePropDesc();
+ MtpResponseCode doSendPartialObject();
+ MtpResponseCode doTruncateObject();
+ MtpResponseCode doBeginEditObject();
+ MtpResponseCode doEndEditObject();
};
}; // namespace android
diff --git a/media/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp
index 6cb88b3..fef8066 100644
--- a/media/mtp/MtpStorage.cpp
+++ b/media/mtp/MtpStorage.cpp
@@ -32,11 +32,14 @@
namespace android {
-MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, uint64_t reserveSpace)
+MtpStorage::MtpStorage(MtpStorageID id, const char* filePath,
+ const char* description, uint64_t reserveSpace, bool removable)
: mStorageID(id),
mFilePath(filePath),
+ mDescription(description),
mMaxCapacity(0),
- mReserveSpace(reserveSpace)
+ mReserveSpace(reserveSpace),
+ mRemovable(removable)
{
LOGV("MtpStorage id: %d path: %s\n", id, filePath);
}
@@ -45,7 +48,7 @@
}
int MtpStorage::getType() const {
- return MTP_STORAGE_FIXED_RAM;
+ return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM : MTP_STORAGE_FIXED_RAM);
}
int MtpStorage::getFileSystemType() const {
@@ -75,7 +78,7 @@
}
const char* MtpStorage::getDescription() const {
- return "Device Storage";
+ return (const char *)mDescription;
}
} // namespace android
diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h
index 858c9d3..3e4f40d 100644
--- a/media/mtp/MtpStorage.h
+++ b/media/mtp/MtpStorage.h
@@ -29,13 +29,16 @@
private:
MtpStorageID mStorageID;
MtpString mFilePath;
+ MtpString mDescription;
uint64_t mMaxCapacity;
// amount of free space to leave unallocated
uint64_t mReserveSpace;
+ bool mRemovable;
public:
MtpStorage(MtpStorageID id, const char* filePath,
- uint64_t reserveSpace);
+ const char* description, uint64_t reserveSpace,
+ bool removable);
virtual ~MtpStorage();
inline MtpStorageID getStorageID() const { return mStorageID; }
@@ -46,6 +49,7 @@
uint64_t getFreeSpace();
const char* getDescription() const;
inline const char* getPath() const { return (const char *)mFilePath; }
+ inline bool isRemovable() const { return mRemovable; }
};
}; // namespace android
diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h
index 6fedc16..d270df5 100644
--- a/media/mtp/mtp.h
+++ b/media/mtp/mtp.h
@@ -22,8 +22,6 @@
#define MTP_STANDARD_VERSION 100
-#define MTP_FIRST_STORAGE_ID 0x00010001
-
// Container Types
#define MTP_CONTAINER_TYPE_UNDEFINED 0
#define MTP_CONTAINER_TYPE_COMMAND 1
@@ -393,6 +391,19 @@
#define MTP_OPERATION_SET_OBJECT_REFERENCES 0x9811
#define MTP_OPERATION_SKIP 0x9820
+// Android extensions for direct file IO
+
+// Same as GetPartialObject, but with 64 bit offset
+#define MTP_OPERATION_GET_PARTIAL_OBJECT_64 0x95C1
+// Same as GetPartialObject64, but copying host to device
+#define MTP_OPERATION_SEND_PARTIAL_OBJECT 0x95C2
+// Truncates file to 64 bit length
+#define MTP_OPERATION_TRUNCATE_OBJECT 0x95C3
+// Must be called before using SendPartialObject and TruncateObject
+#define MTP_OPERATION_BEGIN_EDIT_OBJECT 0x95C4
+// Called to commit changes made by SendPartialObject and TruncateObject
+#define MTP_OPERATION_END_EDIT_OBJECT 0x95C5
+
// MTP Response Codes
#define MTP_RESPONSE_UNDEFINED 0x2000
#define MTP_RESPONSE_OK 0x2001
diff --git a/services/audioflinger/A2dpAudioInterface.cpp b/services/audioflinger/A2dpAudioInterface.cpp
deleted file mode 100644
index d926cb1..0000000
--- a/services/audioflinger/A2dpAudioInterface.cpp
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * Copyright (C) 2008 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 <math.h>
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "A2dpAudioInterface"
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#include "A2dpAudioInterface.h"
-#include "audio/liba2dp.h"
-#include <hardware_legacy/power.h>
-
-namespace android {
-
-static const char *sA2dpWakeLock = "A2dpOutputStream";
-#define MAX_WRITE_RETRIES 5
-
-// ----------------------------------------------------------------------------
-
-//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface()
-//{
-// AudioHardwareInterface* hw = 0;
-//
-// hw = AudioHardwareInterface::create();
-// LOGD("new A2dpAudioInterface(hw: %p)", hw);
-// hw = new A2dpAudioInterface(hw);
-// return hw;
-//}
-
-A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) :
- mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true), mSuspended(false)
-{
-}
-
-A2dpAudioInterface::~A2dpAudioInterface()
-{
- closeOutputStream((AudioStreamOut *)mOutput);
- delete mHardwareInterface;
-}
-
-status_t A2dpAudioInterface::initCheck()
-{
- if (mHardwareInterface == 0) return NO_INIT;
- return mHardwareInterface->initCheck();
-}
-
-AudioStreamOut* A2dpAudioInterface::openOutputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
-{
- if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {
- LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices);
- return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);
- }
-
- status_t err = 0;
-
- // only one output stream allowed
- if (mOutput) {
- if (status)
- *status = -1;
- return NULL;
- }
-
- // create new output stream
- A2dpAudioStreamOut* out = new A2dpAudioStreamOut();
- if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) {
- mOutput = out;
- mOutput->setBluetoothEnabled(mBluetoothEnabled);
- mOutput->setSuspended(mSuspended);
- } else {
- delete out;
- }
-
- if (status)
- *status = err;
- return mOutput;
-}
-
-void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) {
- if (mOutput == 0 || mOutput != out) {
- mHardwareInterface->closeOutputStream(out);
- }
- else {
- delete mOutput;
- mOutput = 0;
- }
-}
-
-
-AudioStreamIn* A2dpAudioInterface::openInputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status,
- AudioSystem::audio_in_acoustics acoustics)
-{
- return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
-}
-
-void A2dpAudioInterface::closeInputStream(AudioStreamIn* in)
-{
- return mHardwareInterface->closeInputStream(in);
-}
-
-status_t A2dpAudioInterface::setMode(int mode)
-{
- return mHardwareInterface->setMode(mode);
-}
-
-status_t A2dpAudioInterface::setMicMute(bool state)
-{
- return mHardwareInterface->setMicMute(state);
-}
-
-status_t A2dpAudioInterface::getMicMute(bool* state)
-{
- return mHardwareInterface->getMicMute(state);
-}
-
-status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs)
-{
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 value;
- String8 key;
- status_t status = NO_ERROR;
-
- LOGV("setParameters() %s", keyValuePairs.string());
-
- key = "bluetooth_enabled";
- if (param.get(key, value) == NO_ERROR) {
- mBluetoothEnabled = (value == "true");
- if (mOutput) {
- mOutput->setBluetoothEnabled(mBluetoothEnabled);
- }
- param.remove(key);
- }
- key = String8("A2dpSuspended");
- if (param.get(key, value) == NO_ERROR) {
- mSuspended = (value == "true");
- if (mOutput) {
- mOutput->setSuspended(mSuspended);
- }
- param.remove(key);
- }
-
- if (param.size()) {
- status_t hwStatus = mHardwareInterface->setParameters(param.toString());
- if (status == NO_ERROR) {
- status = hwStatus;
- }
- }
-
- return status;
-}
-
-String8 A2dpAudioInterface::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- AudioParameter a2dpParam = AudioParameter();
- String8 value;
- String8 key;
-
- key = "bluetooth_enabled";
- if (param.get(key, value) == NO_ERROR) {
- value = mBluetoothEnabled ? "true" : "false";
- a2dpParam.add(key, value);
- param.remove(key);
- }
- key = "A2dpSuspended";
- if (param.get(key, value) == NO_ERROR) {
- value = mSuspended ? "true" : "false";
- a2dpParam.add(key, value);
- param.remove(key);
- }
-
- String8 keyValuePairs = a2dpParam.toString();
-
- if (param.size()) {
- if (keyValuePairs != "") {
- keyValuePairs += ";";
- }
- keyValuePairs += mHardwareInterface->getParameters(param.toString());
- }
-
- LOGV("getParameters() %s", keyValuePairs.string());
- return keyValuePairs;
-}
-
-size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
-{
- return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount);
-}
-
-status_t A2dpAudioInterface::setVoiceVolume(float v)
-{
- return mHardwareInterface->setVoiceVolume(v);
-}
-
-status_t A2dpAudioInterface::setMasterVolume(float v)
-{
- return mHardwareInterface->setMasterVolume(v);
-}
-
-status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args)
-{
- return mHardwareInterface->dumpState(fd, args);
-}
-
-// ----------------------------------------------------------------------------
-
-A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() :
- mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL),
- // assume BT enabled to start, this is safe because its only the
- // enabled->disabled transition we are worried about
- mBluetoothEnabled(true), mDevice(0), mClosing(false), mSuspended(false)
-{
- // use any address by default
- strcpy(mA2dpAddress, "00:00:00:00:00:00");
- init();
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::set(
- uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate)
-{
- int lFormat = pFormat ? *pFormat : 0;
- uint32_t lChannels = pChannels ? *pChannels : 0;
- uint32_t lRate = pRate ? *pRate : 0;
-
- LOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate);
-
- // fix up defaults
- if (lFormat == 0) lFormat = format();
- if (lChannels == 0) lChannels = channels();
- if (lRate == 0) lRate = sampleRate();
-
- // check values
- if ((lFormat != format()) ||
- (lChannels != channels()) ||
- (lRate != sampleRate())){
- if (pFormat) *pFormat = format();
- if (pChannels) *pChannels = channels();
- if (pRate) *pRate = sampleRate();
- return BAD_VALUE;
- }
-
- if (pFormat) *pFormat = lFormat;
- if (pChannels) *pChannels = lChannels;
- if (pRate) *pRate = lRate;
-
- mDevice = device;
- mBufferDurationUs = ((bufferSize() * 1000 )/ frameSize() / sampleRate()) * 1000;
- return NO_ERROR;
-}
-
-A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut()
-{
- LOGV("A2dpAudioStreamOut destructor");
- close();
- LOGV("A2dpAudioStreamOut destructor returning from close()");
-}
-
-ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
-{
- status_t status = -1;
- {
- Mutex::Autolock lock(mLock);
-
- size_t remaining = bytes;
-
- if (!mBluetoothEnabled || mClosing || mSuspended) {
- LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \
- mBluetoothEnabled %d, mClosing %d, mSuspended %d",
- mBluetoothEnabled, mClosing, mSuspended);
- goto Error;
- }
-
- if (mStandby) {
- acquire_wake_lock (PARTIAL_WAKE_LOCK, sA2dpWakeLock);
- mStandby = false;
- mLastWriteTime = systemTime();
- }
-
- status = init();
- if (status < 0)
- goto Error;
-
- int retries = MAX_WRITE_RETRIES;
- while (remaining > 0 && retries) {
- status = a2dp_write(mData, buffer, remaining);
- if (status < 0) {
- LOGE("a2dp_write failed err: %d\n", status);
- goto Error;
- }
- if (status == 0) {
- retries--;
- }
- remaining -= status;
- buffer = (char *)buffer + status;
- }
-
- // if A2DP sink runs abnormally fast, sleep a little so that audioflinger mixer thread
- // does no spin and starve other threads.
- // NOTE: It is likely that the A2DP headset is being disconnected
- nsecs_t now = systemTime();
- if ((uint32_t)ns2us(now - mLastWriteTime) < (mBufferDurationUs >> 2)) {
- LOGV("A2DP sink runs too fast");
- usleep(mBufferDurationUs - (uint32_t)ns2us(now - mLastWriteTime));
- }
- mLastWriteTime = now;
- return bytes;
-
- }
-Error:
-
- standby();
-
- // Simulate audio output timing in case of error
- usleep(mBufferDurationUs);
-
- return status;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::init()
-{
- if (!mData) {
- status_t status = a2dp_init(44100, 2, &mData);
- if (status < 0) {
- LOGE("a2dp_init failed err: %d\n", status);
- mData = NULL;
- return status;
- }
- a2dp_set_sink(mData, mA2dpAddress);
- }
-
- return 0;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
-{
- Mutex::Autolock lock(mLock);
- return standby_l();
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::standby_l()
-{
- int result = NO_ERROR;
-
- if (!mStandby) {
- LOGV_IF(mClosing || !mBluetoothEnabled, "Standby skip stop: closing %d enabled %d",
- mClosing, mBluetoothEnabled);
- if (!mClosing && mBluetoothEnabled) {
- result = a2dp_stop(mData);
- }
- release_wake_lock(sA2dpWakeLock);
- mStandby = true;
- }
-
- return result;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs)
-{
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 value;
- String8 key = String8("a2dp_sink_address");
- status_t status = NO_ERROR;
- int device;
- LOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string());
-
- if (param.get(key, value) == NO_ERROR) {
- if (value.length() != strlen("00:00:00:00:00:00")) {
- status = BAD_VALUE;
- } else {
- setAddress(value.string());
- }
- param.remove(key);
- }
- key = String8("closing");
- if (param.get(key, value) == NO_ERROR) {
- mClosing = (value == "true");
- if (mClosing) {
- standby();
- }
- param.remove(key);
- }
- key = AudioParameter::keyRouting;
- if (param.getInt(key, device) == NO_ERROR) {
- if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device)) {
- mDevice = device;
- status = NO_ERROR;
- } else {
- status = BAD_VALUE;
- }
- param.remove(key);
- }
-
- if (param.size()) {
- status = BAD_VALUE;
- }
- return status;
-}
-
-String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- String8 value;
- String8 key = String8("a2dp_sink_address");
-
- if (param.get(key, value) == NO_ERROR) {
- value = mA2dpAddress;
- param.add(key, value);
- }
- key = AudioParameter::keyRouting;
- if (param.get(key, value) == NO_ERROR) {
- param.addInt(key, (int)mDevice);
- }
-
- LOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string());
- return param.toString();
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address)
-{
- Mutex::Autolock lock(mLock);
-
- if (strlen(address) != strlen("00:00:00:00:00:00"))
- return -EINVAL;
-
- strcpy(mA2dpAddress, address);
- if (mData)
- a2dp_set_sink(mData, mA2dpAddress);
-
- return NO_ERROR;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enabled)
-{
- LOGD("setBluetoothEnabled %d", enabled);
-
- Mutex::Autolock lock(mLock);
-
- mBluetoothEnabled = enabled;
- if (!enabled) {
- return close_l();
- }
- return NO_ERROR;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::setSuspended(bool onOff)
-{
- LOGV("setSuspended %d", onOff);
- mSuspended = onOff;
- standby();
- return NO_ERROR;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::close()
-{
- Mutex::Autolock lock(mLock);
- LOGV("A2dpAudioStreamOut::close() calling close_l()");
- return close_l();
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l()
-{
- standby_l();
- if (mData) {
- LOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)");
- a2dp_cleanup(mData);
- mData = NULL;
- }
- return NO_ERROR;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args)
-{
- return NO_ERROR;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::getRenderPosition(uint32_t *driverFrames)
-{
- //TODO: enable when supported by driver
- return INVALID_OPERATION;
-}
-
-}; // namespace android
diff --git a/services/audioflinger/A2dpAudioInterface.h b/services/audioflinger/A2dpAudioInterface.h
deleted file mode 100644
index dbe2c6a..0000000
--- a/services/audioflinger/A2dpAudioInterface.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2008 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 A2DP_AUDIO_HARDWARE_H
-#define A2DP_AUDIO_HARDWARE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/threads.h>
-
-#include <hardware_legacy/AudioHardwareBase.h>
-
-
-namespace android {
-
-class A2dpAudioInterface : public AudioHardwareBase
-{
- class A2dpAudioStreamOut;
-
-public:
- A2dpAudioInterface(AudioHardwareInterface* hw);
- virtual ~A2dpAudioInterface();
- virtual status_t initCheck();
-
- virtual status_t setVoiceVolume(float volume);
- virtual status_t setMasterVolume(float volume);
-
- virtual status_t setMode(int mode);
-
- // mic mute
- virtual status_t setMicMute(bool state);
- virtual status_t getMicMute(bool* state);
-
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
-
- virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
-
- // create I/O streams
- virtual AudioStreamOut* openOutputStream(
- uint32_t devices,
- int *format=0,
- uint32_t *channels=0,
- uint32_t *sampleRate=0,
- status_t *status=0);
- virtual void closeOutputStream(AudioStreamOut* out);
-
- virtual AudioStreamIn* openInputStream(
- uint32_t devices,
- int *format,
- uint32_t *channels,
- uint32_t *sampleRate,
- status_t *status,
- AudioSystem::audio_in_acoustics acoustics);
- virtual void closeInputStream(AudioStreamIn* in);
-// static AudioHardwareInterface* createA2dpInterface();
-
-protected:
- virtual status_t dump(int fd, const Vector<String16>& args);
-
-private:
- class A2dpAudioStreamOut : public AudioStreamOut {
- public:
- A2dpAudioStreamOut();
- virtual ~A2dpAudioStreamOut();
- status_t set(uint32_t device,
- int *pFormat,
- uint32_t *pChannels,
- uint32_t *pRate);
- virtual uint32_t sampleRate() const { return 44100; }
- // SBC codec wants a multiple of 512
- virtual size_t bufferSize() const { return 512 * 20; }
- virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
- virtual int format() const { return AudioSystem::PCM_16_BIT; }
- virtual uint32_t latency() const { return ((1000*bufferSize())/frameSize())/sampleRate() + 200; }
- virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; }
- virtual ssize_t write(const void* buffer, size_t bytes);
- status_t standby();
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
- virtual status_t getRenderPosition(uint32_t *dspFrames);
-
- private:
- friend class A2dpAudioInterface;
- status_t init();
- status_t close();
- status_t close_l();
- status_t setAddress(const char* address);
- status_t setBluetoothEnabled(bool enabled);
- status_t setSuspended(bool onOff);
- status_t standby_l();
-
- private:
- int mFd;
- bool mStandby;
- int mStartCount;
- int mRetryCount;
- char mA2dpAddress[20];
- void* mData;
- Mutex mLock;
- bool mBluetoothEnabled;
- uint32_t mDevice;
- bool mClosing;
- bool mSuspended;
- nsecs_t mLastWriteTime;
- uint32_t mBufferDurationUs;
- };
-
- friend class A2dpAudioStreamOut;
-
- A2dpAudioStreamOut* mOutput;
- AudioHardwareInterface *mHardwareInterface;
- char mA2dpAddress[20];
- bool mBluetoothEnabled;
- bool mSuspended;
-};
-
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // A2DP_AUDIO_HARDWARE_H
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 22ecc54..2222e8b 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -1,77 +1,5 @@
LOCAL_PATH:= $(call my-dir)
-#AUDIO_POLICY_TEST := true
-#ENABLE_AUDIO_DUMP := true
-
-include $(CLEAR_VARS)
-
-
-ifeq ($(AUDIO_POLICY_TEST),true)
- ENABLE_AUDIO_DUMP := true
-endif
-
-
-LOCAL_SRC_FILES:= \
- AudioHardwareGeneric.cpp \
- AudioHardwareStub.cpp \
- AudioHardwareInterface.cpp
-
-ifeq ($(ENABLE_AUDIO_DUMP),true)
- LOCAL_SRC_FILES += AudioDumpInterface.cpp
- LOCAL_CFLAGS += -DENABLE_AUDIO_DUMP
-endif
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils \
- libbinder \
- libmedia \
- libhardware_legacy
-
-ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
- LOCAL_CFLAGS += -DGENERIC_AUDIO
-endif
-
-LOCAL_MODULE:= libaudiointerface
-
-ifeq ($(BOARD_HAVE_BLUETOOTH),true)
- LOCAL_SRC_FILES += A2dpAudioInterface.cpp
- LOCAL_SHARED_LIBRARIES += liba2dp
- LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
- LOCAL_C_INCLUDES += $(call include-path-for, bluez)
-endif
-
-include $(BUILD_STATIC_LIBRARY)
-
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- AudioPolicyManagerBase.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils \
- libmedia
-
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
-
-LOCAL_MODULE:= libaudiopolicybase
-
-ifeq ($(BOARD_HAVE_BLUETOOTH),true)
- LOCAL_CFLAGS += -DWITH_A2DP
-endif
-
-ifeq ($(AUDIO_POLICY_TEST),true)
- LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
-endif
-
-include $(BUILD_STATIC_LIBRARY)
-
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
@@ -87,15 +15,12 @@
libutils \
libbinder \
libmedia \
+ libhardware \
libhardware_legacy \
libeffects
-ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
- LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase
- LOCAL_CFLAGS += -DGENERIC_AUDIO
-else
- LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy
-endif
+LOCAL_STATIC_LIBRARIES := \
+ libmedia_helper
ifeq ($(TARGET_SIMULATOR),true)
LOCAL_LDLIBS += -ldl
@@ -105,27 +30,10 @@
LOCAL_MODULE:= libaudioflinger
-ifeq ($(BOARD_HAVE_BLUETOOTH),true)
- LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
- LOCAL_SHARED_LIBRARIES += liba2dp
-endif
-
-ifeq ($(AUDIO_POLICY_TEST),true)
- LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
-endif
-
ifeq ($(TARGET_SIMULATOR),true)
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -lrt -lpthread
endif
endif
-ifeq ($(BOARD_USE_LVMX),true)
- LOCAL_CFLAGS += -DLVMX
- LOCAL_C_INCLUDES += vendor/nxp
- LOCAL_STATIC_LIBRARIES += liblifevibes
- LOCAL_SHARED_LIBRARIES += liblvmxservice
-# LOCAL_SHARED_LIBRARIES += liblvmxipc
-endif
-
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audioflinger/AudioDumpInterface.cpp b/services/audioflinger/AudioDumpInterface.cpp
deleted file mode 100644
index 6c11114..0000000
--- a/services/audioflinger/AudioDumpInterface.cpp
+++ /dev/null
@@ -1,573 +0,0 @@
-/* //device/servers/AudioFlinger/AudioDumpInterface.cpp
-**
-** Copyright 2008, 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 "AudioFlingerDump"
-//#define LOG_NDEBUG 0
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/Log.h>
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "AudioDumpInterface.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw)
- : mPolicyCommands(String8("")), mFileName(String8(""))
-{
- if(hw == 0) {
- LOGE("Dump construct hw = 0");
- }
- mFinalInterface = hw;
- LOGV("Constructor %p, mFinalInterface %p", this, mFinalInterface);
-}
-
-
-AudioDumpInterface::~AudioDumpInterface()
-{
- for (size_t i = 0; i < mOutputs.size(); i++) {
- closeOutputStream((AudioStreamOut *)mOutputs[i]);
- }
-
- for (size_t i = 0; i < mInputs.size(); i++) {
- closeInputStream((AudioStreamIn *)mInputs[i]);
- }
-
- if(mFinalInterface) delete mFinalInterface;
-}
-
-
-AudioStreamOut* AudioDumpInterface::openOutputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
-{
- AudioStreamOut* outFinal = NULL;
- int lFormat = AudioSystem::PCM_16_BIT;
- uint32_t lChannels = AudioSystem::CHANNEL_OUT_STEREO;
- uint32_t lRate = 44100;
-
-
- outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status);
- if (outFinal != 0) {
- lFormat = outFinal->format();
- lChannels = outFinal->channels();
- lRate = outFinal->sampleRate();
- } else {
- if (format != 0) {
- if (*format != 0) {
- lFormat = *format;
- } else {
- *format = lFormat;
- }
- }
- if (channels != 0) {
- if (*channels != 0) {
- lChannels = *channels;
- } else {
- *channels = lChannels;
- }
- }
- if (sampleRate != 0) {
- if (*sampleRate != 0) {
- lRate = *sampleRate;
- } else {
- *sampleRate = lRate;
- }
- }
- if (status) *status = NO_ERROR;
- }
- LOGV("openOutputStream(), outFinal %p", outFinal);
-
- AudioStreamOutDump *dumOutput = new AudioStreamOutDump(this, mOutputs.size(), outFinal,
- devices, lFormat, lChannels, lRate);
- mOutputs.add(dumOutput);
-
- return dumOutput;
-}
-
-void AudioDumpInterface::closeOutputStream(AudioStreamOut* out)
-{
- AudioStreamOutDump *dumpOut = (AudioStreamOutDump *)out;
-
- if (mOutputs.indexOf(dumpOut) < 0) {
- LOGW("Attempt to close invalid output stream");
- return;
- }
-
- LOGV("closeOutputStream() output %p", out);
-
- dumpOut->standby();
- if (dumpOut->finalStream() != NULL) {
- mFinalInterface->closeOutputStream(dumpOut->finalStream());
- }
-
- mOutputs.remove(dumpOut);
- delete dumpOut;
-}
-
-AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format, uint32_t *channels,
- uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics)
-{
- AudioStreamIn* inFinal = NULL;
- int lFormat = AudioSystem::PCM_16_BIT;
- uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO;
- uint32_t lRate = 8000;
-
- inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
- if (inFinal != 0) {
- lFormat = inFinal->format();
- lChannels = inFinal->channels();
- lRate = inFinal->sampleRate();
- } else {
- if (format != 0) {
- if (*format != 0) {
- lFormat = *format;
- } else {
- *format = lFormat;
- }
- }
- if (channels != 0) {
- if (*channels != 0) {
- lChannels = *channels;
- } else {
- *channels = lChannels;
- }
- }
- if (sampleRate != 0) {
- if (*sampleRate != 0) {
- lRate = *sampleRate;
- } else {
- *sampleRate = lRate;
- }
- }
- if (status) *status = NO_ERROR;
- }
- LOGV("openInputStream(), inFinal %p", inFinal);
-
- AudioStreamInDump *dumInput = new AudioStreamInDump(this, mInputs.size(), inFinal,
- devices, lFormat, lChannels, lRate);
- mInputs.add(dumInput);
-
- return dumInput;
-}
-void AudioDumpInterface::closeInputStream(AudioStreamIn* in)
-{
- AudioStreamInDump *dumpIn = (AudioStreamInDump *)in;
-
- if (mInputs.indexOf(dumpIn) < 0) {
- LOGW("Attempt to close invalid input stream");
- return;
- }
- dumpIn->standby();
- if (dumpIn->finalStream() != NULL) {
- mFinalInterface->closeInputStream(dumpIn->finalStream());
- }
-
- mInputs.remove(dumpIn);
- delete dumpIn;
-}
-
-
-status_t AudioDumpInterface::setParameters(const String8& keyValuePairs)
-{
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 value;
- int valueInt;
- LOGV("setParameters %s", keyValuePairs.string());
-
- if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) {
- mFileName = value;
- param.remove(String8("test_cmd_file_name"));
- }
- if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) {
- Mutex::Autolock _l(mLock);
- param.remove(String8("test_cmd_policy"));
- mPolicyCommands = param.toString();
- LOGV("test_cmd_policy command %s written", mPolicyCommands.string());
- return NO_ERROR;
- }
-
- if (mFinalInterface != 0 ) return mFinalInterface->setParameters(keyValuePairs);
- return NO_ERROR;
-}
-
-String8 AudioDumpInterface::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- AudioParameter response;
- String8 value;
-
-// LOGV("getParameters %s", keys.string());
- if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) {
- Mutex::Autolock _l(mLock);
- if (mPolicyCommands.length() != 0) {
- response = AudioParameter(mPolicyCommands);
- response.addInt(String8("test_cmd_policy"), 1);
- } else {
- response.addInt(String8("test_cmd_policy"), 0);
- }
- param.remove(String8("test_cmd_policy"));
-// LOGV("test_cmd_policy command %s read", mPolicyCommands.string());
- }
-
- if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) {
- response.add(String8("test_cmd_file_name"), mFileName);
- param.remove(String8("test_cmd_file_name"));
- }
-
- String8 keyValuePairs = response.toString();
-
- if (param.size() && mFinalInterface != 0 ) {
- keyValuePairs += ";";
- keyValuePairs += mFinalInterface->getParameters(param.toString());
- }
-
- return keyValuePairs;
-}
-
-status_t AudioDumpInterface::setMode(int mode)
-{
- return mFinalInterface->setMode(mode);
-}
-
-size_t AudioDumpInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
-{
- return mFinalInterface->getInputBufferSize(sampleRate, format, channelCount);
-}
-
-// ----------------------------------------------------------------------------
-
-AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface,
- int id,
- AudioStreamOut* finalStream,
- uint32_t devices,
- int format,
- uint32_t channels,
- uint32_t sampleRate)
- : mInterface(interface), mId(id),
- mSampleRate(sampleRate), mFormat(format), mChannels(channels), mLatency(0), mDevice(devices),
- mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0)
-{
- LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream);
-}
-
-
-AudioStreamOutDump::~AudioStreamOutDump()
-{
- LOGV("AudioStreamOutDump destructor");
- Close();
-}
-
-ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes)
-{
- ssize_t ret;
-
- if (mFinalStream) {
- ret = mFinalStream->write(buffer, bytes);
- } else {
- usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000);
- ret = bytes;
- }
- if(!mFile) {
- if (mInterface->fileName() != "") {
- char name[255];
- sprintf(name, "%s_out_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount);
- mFile = fopen(name, "wb");
- LOGV("Opening dump file %s, fh %p", name, mFile);
- }
- }
- if (mFile) {
- fwrite(buffer, bytes, 1, mFile);
- }
- return ret;
-}
-
-status_t AudioStreamOutDump::standby()
-{
- LOGV("AudioStreamOutDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream);
-
- Close();
- if (mFinalStream != 0 ) return mFinalStream->standby();
- return NO_ERROR;
-}
-
-uint32_t AudioStreamOutDump::sampleRate() const
-{
- if (mFinalStream != 0 ) return mFinalStream->sampleRate();
- return mSampleRate;
-}
-
-size_t AudioStreamOutDump::bufferSize() const
-{
- if (mFinalStream != 0 ) return mFinalStream->bufferSize();
- return mBufferSize;
-}
-
-uint32_t AudioStreamOutDump::channels() const
-{
- if (mFinalStream != 0 ) return mFinalStream->channels();
- return mChannels;
-}
-int AudioStreamOutDump::format() const
-{
- if (mFinalStream != 0 ) return mFinalStream->format();
- return mFormat;
-}
-uint32_t AudioStreamOutDump::latency() const
-{
- if (mFinalStream != 0 ) return mFinalStream->latency();
- return 0;
-}
-status_t AudioStreamOutDump::setVolume(float left, float right)
-{
- if (mFinalStream != 0 ) return mFinalStream->setVolume(left, right);
- return NO_ERROR;
-}
-status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs)
-{
- LOGV("AudioStreamOutDump::setParameters %s", keyValuePairs.string());
-
- if (mFinalStream != 0 ) {
- return mFinalStream->setParameters(keyValuePairs);
- }
-
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 value;
- int valueInt;
- status_t status = NO_ERROR;
-
- if (param.getInt(String8("set_id"), valueInt) == NO_ERROR) {
- mId = valueInt;
- }
-
- if (param.getInt(String8("format"), valueInt) == NO_ERROR) {
- if (mFile == 0) {
- mFormat = valueInt;
- } else {
- status = INVALID_OPERATION;
- }
- }
- if (param.getInt(String8("channels"), valueInt) == NO_ERROR) {
- if (valueInt == AudioSystem::CHANNEL_OUT_STEREO || valueInt == AudioSystem::CHANNEL_OUT_MONO) {
- mChannels = valueInt;
- } else {
- status = BAD_VALUE;
- }
- }
- if (param.getInt(String8("sampling_rate"), valueInt) == NO_ERROR) {
- if (valueInt > 0 && valueInt <= 48000) {
- if (mFile == 0) {
- mSampleRate = valueInt;
- } else {
- status = INVALID_OPERATION;
- }
- } else {
- status = BAD_VALUE;
- }
- }
- return status;
-}
-
-String8 AudioStreamOutDump::getParameters(const String8& keys)
-{
- if (mFinalStream != 0 ) return mFinalStream->getParameters(keys);
-
- AudioParameter param = AudioParameter(keys);
- return param.toString();
-}
-
-status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args)
-{
- if (mFinalStream != 0 ) return mFinalStream->dump(fd, args);
- return NO_ERROR;
-}
-
-void AudioStreamOutDump::Close()
-{
- if(mFile) {
- fclose(mFile);
- mFile = 0;
- }
-}
-
-status_t AudioStreamOutDump::getRenderPosition(uint32_t *dspFrames)
-{
- if (mFinalStream != 0 ) return mFinalStream->getRenderPosition(dspFrames);
- return INVALID_OPERATION;
-}
-
-// ----------------------------------------------------------------------------
-
-AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface,
- int id,
- AudioStreamIn* finalStream,
- uint32_t devices,
- int format,
- uint32_t channels,
- uint32_t sampleRate)
- : mInterface(interface), mId(id),
- mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices),
- mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0)
-{
- LOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream);
-}
-
-
-AudioStreamInDump::~AudioStreamInDump()
-{
- Close();
-}
-
-ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes)
-{
- ssize_t ret;
-
- if (mFinalStream) {
- ret = mFinalStream->read(buffer, bytes);
- if(!mFile) {
- if (mInterface->fileName() != "") {
- char name[255];
- sprintf(name, "%s_in_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount);
- mFile = fopen(name, "wb");
- LOGV("Opening input dump file %s, fh %p", name, mFile);
- }
- }
- if (mFile) {
- fwrite(buffer, bytes, 1, mFile);
- }
- } else {
- usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000);
- ret = bytes;
- if(!mFile) {
- char name[255];
- strcpy(name, "/sdcard/music/sine440");
- if (channels() == AudioSystem::CHANNEL_IN_MONO) {
- strcat(name, "_mo");
- } else {
- strcat(name, "_st");
- }
- if (format() == AudioSystem::PCM_16_BIT) {
- strcat(name, "_16b");
- } else {
- strcat(name, "_8b");
- }
- if (sampleRate() < 16000) {
- strcat(name, "_8k");
- } else if (sampleRate() < 32000) {
- strcat(name, "_22k");
- } else if (sampleRate() < 48000) {
- strcat(name, "_44k");
- } else {
- strcat(name, "_48k");
- }
- strcat(name, ".wav");
- mFile = fopen(name, "rb");
- LOGV("Opening input read file %s, fh %p", name, mFile);
- if (mFile) {
- fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
- }
- }
- if (mFile) {
- ssize_t bytesRead = fread(buffer, bytes, 1, mFile);
- if (bytesRead >=0 && bytesRead < bytes) {
- fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
- fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mFile);
- }
- }
- }
-
- return ret;
-}
-
-status_t AudioStreamInDump::standby()
-{
- LOGV("AudioStreamInDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream);
-
- Close();
- if (mFinalStream != 0 ) return mFinalStream->standby();
- return NO_ERROR;
-}
-
-status_t AudioStreamInDump::setGain(float gain)
-{
- if (mFinalStream != 0 ) return mFinalStream->setGain(gain);
- return NO_ERROR;
-}
-
-uint32_t AudioStreamInDump::sampleRate() const
-{
- if (mFinalStream != 0 ) return mFinalStream->sampleRate();
- return mSampleRate;
-}
-
-size_t AudioStreamInDump::bufferSize() const
-{
- if (mFinalStream != 0 ) return mFinalStream->bufferSize();
- return mBufferSize;
-}
-
-uint32_t AudioStreamInDump::channels() const
-{
- if (mFinalStream != 0 ) return mFinalStream->channels();
- return mChannels;
-}
-
-int AudioStreamInDump::format() const
-{
- if (mFinalStream != 0 ) return mFinalStream->format();
- return mFormat;
-}
-
-status_t AudioStreamInDump::setParameters(const String8& keyValuePairs)
-{
- LOGV("AudioStreamInDump::setParameters()");
- if (mFinalStream != 0 ) return mFinalStream->setParameters(keyValuePairs);
- return NO_ERROR;
-}
-
-String8 AudioStreamInDump::getParameters(const String8& keys)
-{
- if (mFinalStream != 0 ) return mFinalStream->getParameters(keys);
-
- AudioParameter param = AudioParameter(keys);
- return param.toString();
-}
-
-unsigned int AudioStreamInDump::getInputFramesLost() const
-{
- if (mFinalStream != 0 ) return mFinalStream->getInputFramesLost();
- return 0;
-}
-
-status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args)
-{
- if (mFinalStream != 0 ) return mFinalStream->dump(fd, args);
- return NO_ERROR;
-}
-
-void AudioStreamInDump::Close()
-{
- if(mFile) {
- fclose(mFile);
- mFile = 0;
- }
-}
-}; // namespace android
diff --git a/services/audioflinger/AudioDumpInterface.h b/services/audioflinger/AudioDumpInterface.h
deleted file mode 100644
index 814ce5f..0000000
--- a/services/audioflinger/AudioDumpInterface.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/* //device/servers/AudioFlinger/AudioDumpInterface.h
-**
-** Copyright 2008, 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_AUDIO_DUMP_INTERFACE_H
-#define ANDROID_AUDIO_DUMP_INTERFACE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/String8.h>
-#include <utils/SortedVector.h>
-
-#include <hardware_legacy/AudioHardwareBase.h>
-
-namespace android {
-
-#define AUDIO_DUMP_WAVE_HDR_SIZE 44
-
-class AudioDumpInterface;
-
-class AudioStreamOutDump : public AudioStreamOut {
-public:
- AudioStreamOutDump(AudioDumpInterface *interface,
- int id,
- AudioStreamOut* finalStream,
- uint32_t devices,
- int format,
- uint32_t channels,
- uint32_t sampleRate);
- ~AudioStreamOutDump();
-
- virtual ssize_t write(const void* buffer, size_t bytes);
- virtual uint32_t sampleRate() const;
- virtual size_t bufferSize() const;
- virtual uint32_t channels() const;
- virtual int format() const;
- virtual uint32_t latency() const;
- virtual status_t setVolume(float left, float right);
- virtual status_t standby();
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
- virtual status_t dump(int fd, const Vector<String16>& args);
- void Close(void);
- AudioStreamOut* finalStream() { return mFinalStream; }
- uint32_t device() { return mDevice; }
- int getId() { return mId; }
- virtual status_t getRenderPosition(uint32_t *dspFrames);
-
-private:
- AudioDumpInterface *mInterface;
- int mId;
- uint32_t mSampleRate; //
- uint32_t mFormat; //
- uint32_t mChannels; // output configuration
- uint32_t mLatency; //
- uint32_t mDevice; // current device this output is routed to
- size_t mBufferSize;
- AudioStreamOut *mFinalStream;
- FILE *mFile; // output file
- int mFileCount;
-};
-
-class AudioStreamInDump : public AudioStreamIn {
-public:
- AudioStreamInDump(AudioDumpInterface *interface,
- int id,
- AudioStreamIn* finalStream,
- uint32_t devices,
- int format,
- uint32_t channels,
- uint32_t sampleRate);
- ~AudioStreamInDump();
-
- virtual uint32_t sampleRate() const;
- virtual size_t bufferSize() const;
- virtual uint32_t channels() const;
- virtual int format() const;
-
- virtual status_t setGain(float gain);
- virtual ssize_t read(void* buffer, ssize_t bytes);
- virtual status_t standby();
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
- virtual unsigned int getInputFramesLost() const;
- virtual status_t dump(int fd, const Vector<String16>& args);
- void Close(void);
- AudioStreamIn* finalStream() { return mFinalStream; }
- uint32_t device() { return mDevice; }
-
-private:
- AudioDumpInterface *mInterface;
- int mId;
- uint32_t mSampleRate; //
- uint32_t mFormat; //
- uint32_t mChannels; // output configuration
- uint32_t mDevice; // current device this output is routed to
- size_t mBufferSize;
- AudioStreamIn *mFinalStream;
- FILE *mFile; // output file
- int mFileCount;
-};
-
-class AudioDumpInterface : public AudioHardwareBase
-{
-
-public:
- AudioDumpInterface(AudioHardwareInterface* hw);
- virtual AudioStreamOut* openOutputStream(
- uint32_t devices,
- int *format=0,
- uint32_t *channels=0,
- uint32_t *sampleRate=0,
- status_t *status=0);
- virtual void closeOutputStream(AudioStreamOut* out);
-
- virtual ~AudioDumpInterface();
-
- virtual status_t initCheck()
- {return mFinalInterface->initCheck();}
- virtual status_t setVoiceVolume(float volume)
- {return mFinalInterface->setVoiceVolume(volume);}
- virtual status_t setMasterVolume(float volume)
- {return mFinalInterface->setMasterVolume(volume);}
-
- virtual status_t setMode(int mode);
-
- // mic mute
- virtual status_t setMicMute(bool state)
- {return mFinalInterface->setMicMute(state);}
- virtual status_t getMicMute(bool* state)
- {return mFinalInterface->getMicMute(state);}
-
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
-
- virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
-
- virtual AudioStreamIn* openInputStream(uint32_t devices, int *format, uint32_t *channels,
- uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics);
- virtual void closeInputStream(AudioStreamIn* in);
-
- virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); }
-
- String8 fileName() const { return mFileName; }
-protected:
-
- AudioHardwareInterface *mFinalInterface;
- SortedVector<AudioStreamOutDump *> mOutputs;
- SortedVector<AudioStreamInDump *> mInputs;
- Mutex mLock;
- String8 mPolicyCommands;
- String8 mFileName;
-};
-
-}; // namespace android
-
-#endif // ANDROID_AUDIO_DUMP_INTERFACE_H
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 4ec16c1..a6ba1a0 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -24,33 +24,31 @@
#include <sys/time.h>
#include <sys/resource.h>
+#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <utils/String16.h>
#include <utils/threads.h>
+#include <utils/Atomic.h>
+#include <cutils/bitops.h>
#include <cutils/properties.h>
#include <media/AudioTrack.h>
#include <media/AudioRecord.h>
+#include <media/IMediaPlayerService.h>
#include <private/media/AudioTrackShared.h>
#include <private/media/AudioEffectShared.h>
-#include <hardware_legacy/AudioHardwareInterface.h>
+
+#include <system/audio.h>
+#include <hardware/audio_hal.h>
#include "AudioMixer.h"
#include "AudioFlinger.h"
-#ifdef WITH_A2DP
-#include "A2dpAudioInterface.h"
-#endif
-
-#ifdef LVMX
-#include "lifevibes.h"
-#endif
-
#include <media/EffectsFactoryApi.h>
#include <media/EffectVisualizerApi.h>
@@ -125,38 +123,122 @@
#endif
}
+// To collect the amplifier usage
+static void addBatteryData(uint32_t params) {
+ sp<IBinder> binder =
+ defaultServiceManager()->getService(String16("media.player"));
+ sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+ if (service.get() == NULL) {
+ LOGW("Cannot connect to the MediaPlayerService for battery tracking");
+ return;
+ }
+
+ service->addBatteryData(params);
+}
+
+static int load_audio_interface(const char *if_name, const hw_module_t **mod,
+ audio_hw_device_t **dev)
+{
+ int rc;
+
+ rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, mod);
+ if (rc)
+ goto out;
+
+ rc = audio_hw_device_open(*mod, dev);
+ LOGE_IF(rc, "couldn't open audio hw device in %s.%s (%s)",
+ AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
+ if (rc)
+ goto out;
+
+ return 0;
+
+out:
+ *mod = NULL;
+ *dev = NULL;
+ return rc;
+}
+
+static const char *audio_interfaces[] = {
+ "primary",
+ "a2dp",
+ "usb",
+};
+#define ARRAY_SIZE(x) (sizeof((x))/sizeof(((x)[0])))
+
// ----------------------------------------------------------------------------
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
- mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
+ mPrimaryHardwareDev(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
{
+}
+
+void AudioFlinger::onFirstRef()
+{
+ int rc = 0;
+
Mutex::Autolock _l(mLock);
+ /* TODO: move all this work into an Init() function */
mHardwareStatus = AUDIO_HW_IDLE;
- mAudioHardware = AudioHardwareInterface::create();
+ for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
+ const hw_module_t *mod;
+ audio_hw_device_t *dev;
+
+ rc = load_audio_interface(audio_interfaces[i], &mod, &dev);
+ if (rc)
+ continue;
+
+ LOGI("Loaded %s audio interface from %s (%s)", audio_interfaces[i],
+ mod->name, mod->id);
+ mAudioHwDevs.push(dev);
+
+ if (!mPrimaryHardwareDev) {
+ mPrimaryHardwareDev = dev;
+ LOGI("Using '%s' (%s.%s) as the primary audio interface",
+ mod->name, mod->id, audio_interfaces[i]);
+ }
+ }
mHardwareStatus = AUDIO_HW_INIT;
- if (mAudioHardware->initCheck() == NO_ERROR) {
- AutoMutex lock(mHardwareLock);
- mMode = AudioSystem::MODE_NORMAL;
- mHardwareStatus = AUDIO_HW_SET_MODE;
- mAudioHardware->setMode(mMode);
- mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- mAudioHardware->setMasterVolume(1.0f);
- mHardwareStatus = AUDIO_HW_IDLE;
- } else {
- LOGE("Couldn't even initialize the stubbed audio hardware!");
+
+ if (!mPrimaryHardwareDev || mAudioHwDevs.size() == 0) {
+ LOGE("Primary audio interface not found");
+ return;
}
-#ifdef LVMX
- LifeVibes::init();
- mLifeVibesClientPid = -1;
-#endif
+
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+
+ mHardwareStatus = AUDIO_HW_INIT;
+ rc = dev->init_check(dev);
+ if (rc == 0) {
+ AutoMutex lock(mHardwareLock);
+
+ mMode = AUDIO_MODE_NORMAL;
+ mHardwareStatus = AUDIO_HW_SET_MODE;
+ dev->set_mode(dev, mMode);
+ mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
+ dev->set_master_volume(dev, 1.0f);
+ mHardwareStatus = AUDIO_HW_IDLE;
+ }
+ }
+}
+
+status_t AudioFlinger::initCheck() const
+{
+ Mutex::Autolock _l(mLock);
+ if (mPrimaryHardwareDev == NULL || mAudioHwDevs.size() == 0)
+ return NO_INIT;
+ return NO_ERROR;
}
AudioFlinger::~AudioFlinger()
{
+ int num_devs = mAudioHwDevs.size();
+
while (!mRecordThreads.isEmpty()) {
// closeInput() will remove first entry from mRecordThreads
closeInput(mRecordThreads.keyAt(0));
@@ -165,12 +247,24 @@
// closeOutput() will remove first entry from mPlaybackThreads
closeOutput(mPlaybackThreads.keyAt(0));
}
- if (mAudioHardware) {
- delete mAudioHardware;
+
+ for (int i = 0; i < num_devs; i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+ audio_hw_device_close(dev);
}
+ mAudioHwDevs.clear();
}
-
+audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(uint32_t devices)
+{
+ /* first matching HW device is returned */
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+ if ((dev->get_supported_devices(dev) & devices) == devices)
+ return dev;
+ }
+ return NULL;
+}
status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
{
@@ -269,8 +363,10 @@
mRecordThreads.valueAt(i)->dump(fd, args);
}
- if (mAudioHardware) {
- mAudioHardware->dumpState(fd, args);
+ // dump all hardware devs
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+ dev->dump(dev, fd);
}
if (locked) mLock.unlock();
}
@@ -301,7 +397,7 @@
status_t lStatus;
int lSessionId;
- if (streamType >= AudioSystem::NUM_STREAM_TYPES) {
+ if (streamType >= AUDIO_STREAM_CNT) {
LOGE("invalid stream type");
lStatus = BAD_VALUE;
goto Exit;
@@ -327,7 +423,7 @@
}
LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
- if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+ if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) {
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
if (mPlaybackThreads.keyAt(i) != output) {
@@ -446,7 +542,7 @@
{ // scope for the lock
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- if (mAudioHardware->setMasterVolume(value) == NO_ERROR) {
+ if (mPrimaryHardwareDev->set_master_volume(mPrimaryHardwareDev, value) == NO_ERROR) {
value = 1.0f;
}
mHardwareStatus = AUDIO_HW_IDLE;
@@ -468,7 +564,7 @@
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
- if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) {
+ if ((mode < 0) || (mode >= AUDIO_MODE_CNT)) {
LOGW("Illegal value: setMode(%d)", mode);
return BAD_VALUE;
}
@@ -476,7 +572,7 @@
{ // scope for the lock
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_MODE;
- ret = mAudioHardware->setMode(mode);
+ ret = mPrimaryHardwareDev->set_mode(mPrimaryHardwareDev, mode);
mHardwareStatus = AUDIO_HW_IDLE;
}
@@ -485,9 +581,6 @@
mMode = mode;
for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
mPlaybackThreads.valueAt(i)->setMode(mode);
-#ifdef LVMX
- LifeVibes::setMode(mode);
-#endif
}
return ret;
@@ -502,16 +595,16 @@
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_MIC_MUTE;
- status_t ret = mAudioHardware->setMicMute(state);
+ status_t ret = mPrimaryHardwareDev->set_mic_mute(mPrimaryHardwareDev, state);
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
}
bool AudioFlinger::getMicMute() const
{
- bool state = AudioSystem::MODE_INVALID;
+ bool state = AUDIO_MODE_INVALID;
mHardwareStatus = AUDIO_HW_GET_MIC_MUTE;
- mAudioHardware->getMicMute(&state);
+ mPrimaryHardwareDev->get_mic_mute(mPrimaryHardwareDev, &state);
mHardwareStatus = AUDIO_HW_IDLE;
return state;
}
@@ -548,7 +641,7 @@
return PERMISSION_DENIED;
}
- if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
@@ -581,8 +674,8 @@
return PERMISSION_DENIED;
}
- if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES ||
- uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) {
+ if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT ||
+ uint32_t(stream) == AUDIO_STREAM_ENFORCED_AUDIBLE) {
return BAD_VALUE;
}
@@ -596,7 +689,7 @@
float AudioFlinger::streamVolume(int stream, int output) const
{
- if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT) {
return 0.0f;
}
@@ -617,7 +710,7 @@
bool AudioFlinger::streamMute(int stream) const
{
- if (stream < 0 || stream >= (int)AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || stream >= (int)AUDIO_STREAM_CNT) {
return true;
}
@@ -635,41 +728,18 @@
return PERMISSION_DENIED;
}
-#ifdef LVMX
- AudioParameter param = AudioParameter(keyValuePairs);
- LifeVibes::setParameters(ioHandle,keyValuePairs);
- String8 key = String8(AudioParameter::keyRouting);
- int device;
- if (NO_ERROR != param.getInt(key, device)) {
- device = -1;
- }
-
- key = String8(LifevibesTag);
- String8 value;
- int musicEnabled = -1;
- if (NO_ERROR == param.get(key, value)) {
- if (value == LifevibesEnable) {
- mLifeVibesClientPid = IPCThreadState::self()->getCallingPid();
- musicEnabled = 1;
- } else if (value == LifevibesDisable) {
- mLifeVibesClientPid = -1;
- musicEnabled = 0;
- }
- }
-#endif
-
// ioHandle == 0 means the parameters are global to the audio hardware interface
if (ioHandle == 0) {
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_SET_PARAMETER;
- result = mAudioHardware->setParameters(keyValuePairs);
-#ifdef LVMX
- if (musicEnabled != -1) {
- LifeVibes::enableMusic((bool) musicEnabled);
+ status_t final_result = NO_ERROR;
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+ result = dev->set_parameters(dev, keyValuePairs.string());
+ final_result = result ?: final_result;
}
-#endif
mHardwareStatus = AUDIO_HW_IDLE;
- return result;
+ return final_result;
}
// hold a strong ref on thread in case closeOutput() or closeInput() is called
@@ -684,11 +754,6 @@
}
if (thread != NULL) {
result = thread->setParameters(keyValuePairs);
-#ifdef LVMX
- if ((NO_ERROR == result) && (device != -1)) {
- LifeVibes::setDevice(LifeVibes::threadIdToAudioOutputType(thread->id()), device);
- }
-#endif
return result;
}
return BAD_VALUE;
@@ -700,7 +765,15 @@
// ioHandle, keys.string(), gettid(), IPCThreadState::self()->getCallingPid());
if (ioHandle == 0) {
- return mAudioHardware->getParameters(keys);
+ String8 out_s8;
+
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+ char *s = dev->get_parameters(dev, keys.string());
+ out_s8 += String8(s);
+ free(s);
+ }
+ return out_s8;
}
Mutex::Autolock _l(mLock);
@@ -718,7 +791,7 @@
size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
- return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount);
+ return mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, sampleRate, format, channelCount);
}
unsigned int AudioFlinger::getInputFramesLost(int ioHandle)
@@ -745,7 +818,7 @@
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_SET_VOICE_VOLUME;
- status_t ret = mAudioHardware->setVoiceVolume(value);
+ status_t ret = mPrimaryHardwareDev->set_voice_volume(mPrimaryHardwareDev, value);
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
@@ -802,13 +875,6 @@
if (index >= 0) {
sp <NotificationClient> client = mNotificationClients.valueFor(pid);
LOGV("removeNotificationClient() %p, pid %d", client.get(), pid);
-#ifdef LVMX
- if (pid == mLifeVibesClientPid) {
- LOGV("Disabling lifevibes");
- LifeVibes::enableMusic(false);
- mLifeVibesClientPid = -1;
- }
-#endif
mNotificationClients.removeItem(pid);
}
}
@@ -1003,7 +1069,7 @@
mMasterVolume = mAudioFlinger->masterVolume();
mMasterMute = mAudioFlinger->masterMute();
- for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
}
@@ -1166,12 +1232,12 @@
// conflicts will happen when tracks are moved from one output to another by audio policy
// manager
uint32_t strategy =
- AudioSystem::getStrategyForStream((AudioSystem::stream_type)streamType);
+ AudioSystem::getStrategyForStream((audio_stream_type_t)streamType);
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> t = mTracks[i];
if (t != 0) {
if (sessionId == t->sessionId() &&
- strategy != AudioSystem::getStrategyForStream((AudioSystem::stream_type)t->type())) {
+ strategy != AudioSystem::getStrategyForStream((audio_stream_type_t)t->type())) {
lStatus = BAD_VALUE;
goto Exit;
}
@@ -1190,7 +1256,8 @@
if (chain != 0) {
LOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
track->setMainBuffer(chain->inBuffer());
- chain->setStrategy(AudioSystem::getStrategyForStream((AudioSystem::stream_type)track->type()));
+ chain->setStrategy(AudioSystem::getStrategyForStream((audio_stream_type_t)track->type()));
+ chain->incTrackCnt();
}
}
lStatus = NO_ERROR;
@@ -1205,7 +1272,7 @@
uint32_t AudioFlinger::PlaybackThread::latency() const
{
if (mOutput) {
- return mOutput->latency();
+ return mOutput->stream->get_latency(mOutput->stream);
}
else {
return 0;
@@ -1214,24 +1281,12 @@
status_t AudioFlinger::PlaybackThread::setMasterVolume(float value)
{
-#ifdef LVMX
- int audioOutputType = LifeVibes::getMixerType(mId, mType);
- if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
- LifeVibes::setMasterVolume(audioOutputType, value);
- }
-#endif
mMasterVolume = value;
return NO_ERROR;
}
status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted)
{
-#ifdef LVMX
- int audioOutputType = LifeVibes::getMixerType(mId, mType);
- if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
- LifeVibes::setMasterMute(audioOutputType, muted);
- }
-#endif
mMasterMute = muted;
return NO_ERROR;
}
@@ -1248,24 +1303,12 @@
status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value)
{
-#ifdef LVMX
- int audioOutputType = LifeVibes::getMixerType(mId, mType);
- if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
- LifeVibes::setStreamVolume(audioOutputType, stream, value);
- }
-#endif
mStreamTypes[stream].volume = value;
return NO_ERROR;
}
status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted)
{
-#ifdef LVMX
- int audioOutputType = LifeVibes::getMixerType(mId, mType);
- if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
- LifeVibes::setStreamMute(audioOutputType, stream, muted);
- }
-#endif
mStreamTypes[stream].mute = muted;
return NO_ERROR;
}
@@ -1298,7 +1341,7 @@
sp<EffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
LOGV("addTrack_l() starting track on chain %p for session %d", chain.get(), track->sessionId());
- chain->startTrack();
+ chain->incActiveTrackCnt();
}
}
@@ -1316,14 +1359,29 @@
{
track->mState = TrackBase::TERMINATED;
if (mActiveTracks.indexOf(track) < 0) {
- mTracks.remove(track);
- deleteTrackName_l(track->name());
+ removeTrack_l(track);
+ }
+}
+
+void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
+{
+ mTracks.remove(track);
+ deleteTrackName_l(track->name());
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ chain->decTrackCnt();
}
}
String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
{
- return mOutput->getParameters(keys);
+ String8 out_s8;
+ char *s;
+
+ s = mOutput->stream->common.get_parameters(&mOutput->stream->common, keys.string());
+ out_s8 = String8(s);
+ free(s);
+ return out_s8;
}
// destroyTrack_l() must be called with AudioFlinger::mLock held
@@ -1355,12 +1413,12 @@
void AudioFlinger::PlaybackThread::readOutputParameters()
{
- mSampleRate = mOutput->sampleRate();
- mChannels = mOutput->channels();
- mChannelCount = (uint16_t)AudioSystem::popCount(mChannels);
- mFormat = mOutput->format();
- mFrameSize = (uint16_t)mOutput->frameSize();
- mFrameCount = mOutput->bufferSize() / mFrameSize;
+ mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common);
+ mChannels = mOutput->stream->common.get_channels(&mOutput->stream->common);
+ mChannelCount = (uint16_t)popcount(mChannels);
+ mFormat = mOutput->stream->common.get_format(&mOutput->stream->common);
+ mFrameSize = (uint16_t)audio_stream_frame_size(&mOutput->stream->common);
+ mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize;
// FIXME - Current mixer implementation only supports stereo output: Always
// Allocate a stereo buffer even if HW output is mono.
@@ -1388,9 +1446,9 @@
if (mOutput == 0) {
return INVALID_OPERATION;
}
- *halFrames = mBytesWritten/mOutput->frameSize();
+ *halFrames = mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
- return mOutput->getRenderPosition(dspFrames);
+ return mOutput->stream->get_render_position(mOutput->stream, dspFrames);
}
uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
@@ -1415,19 +1473,19 @@
uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId)
{
- // session AudioSystem::SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that
+ // session AUDIO_SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that
// it is moved to correct output by audio policy manager when A2DP is connected or disconnected
- if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
- return AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
}
for (size_t i = 0; i < mTracks.size(); i++) {
sp<Track> track = mTracks[i];
if (sessionId == track->sessionId() &&
!(track->mCblk->flags & CBLK_INVALID_MSK)) {
- return AudioSystem::getStrategyForStream((AudioSystem::stream_type) track->type());
+ return AudioSystem::getStrategyForStream((audio_stream_type_t) track->type());
}
}
- return AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+ return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
}
sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain(int sessionId)
@@ -1520,7 +1578,7 @@
mSuspended) {
if (!mStandby) {
LOGV("Audio hardware entering standby, mixer %p, mSuspended %d\n", this, mSuspended);
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
mStandby = true;
mBytesWritten = 0;
}
@@ -1593,17 +1651,11 @@
}
// enable changes in effect chain
unlockEffectChains(effectChains);
-#ifdef LVMX
- int audioOutputType = LifeVibes::getMixerType(mId, mType);
- if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
- LifeVibes::process(audioOutputType, mMixBuffer, mixBufferSize);
- }
-#endif
mLastWriteTime = systemTime();
mInWrite = true;
mBytesWritten += mixBufferSize;
- int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
+ int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
mNumWrites++;
mInWrite = false;
@@ -1638,7 +1690,7 @@
}
if (!mStandby) {
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
}
LOGV("MixerThread %p exiting", this);
@@ -1661,26 +1713,8 @@
if (masterMute) {
masterVolume = 0;
}
-#ifdef LVMX
- bool tracksConnectedChanged = false;
- bool stateChanged = false;
-
- int audioOutputType = LifeVibes::getMixerType(mId, mType);
- if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType))
- {
- int activeTypes = 0;
- for (size_t i=0 ; i<count ; i++) {
- sp<Track> t = activeTracks[i].promote();
- if (t == 0) continue;
- Track* const track = t.get();
- int iTracktype=track->type();
- activeTypes |= 1<<track->type();
- }
- LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute);
- }
-#endif
// Delegate master volume control to effect in output mix effect chain if needed
- sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX);
+ sp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
if (chain != 0) {
uint32_t v = (uint32_t)(masterVolume * (1 << 24));
chain->setVolume_l(&v, &v);
@@ -1728,6 +1762,7 @@
track->mState = TrackBase::ACTIVE;
param = AudioMixer::RAMP_VOLUME;
}
+ mAudioMixer->setParameter(AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
} else if (cblk->server != 0) {
// If the track is stopped before the first frame was mixed,
// do not apply ramp
@@ -1746,17 +1781,6 @@
// read original volumes with volume control
float typeVolume = mStreamTypes[track->type()].volume;
-#ifdef LVMX
- bool streamMute=false;
- // read the volume from the LivesVibes audio engine.
- if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType))
- {
- LifeVibes::getStreamVolumes(audioOutputType, track->type(), &typeVolume, &streamMute);
- if (streamMute) {
- typeVolume = 0;
- }
- }
-#endif
float v = masterVolume * typeVolume;
vl = (uint32_t)(v * cblk->volume[0]) << 12;
vr = (uint32_t)(v * cblk->volume[1]) << 12;
@@ -1789,14 +1813,6 @@
if (va > MAX_GAIN_INT) va = MAX_GAIN_INT;
aux = int16_t(va);
-#ifdef LVMX
- if ( tracksConnectedChanged || stateChanged )
- {
- // only do the ramp when the volume is changed by the user / application
- param = AudioMixer::VOLUME;
- }
-#endif
-
// XXX: these things DON'T need to be done each time
mAudioMixer->setBufferProvider(track);
mAudioMixer->enable(AudioMixer::MIXING);
@@ -1840,7 +1856,7 @@
LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
tracksToRemove->add(track);
// indicate to client process that the track was disabled because of underrun
- cblk->flags |= CBLK_DISABLED_ON;
+ android_atomic_or(CBLK_DISABLED_ON, &cblk->flags);
} else if (mixerStatus != MIXER_TRACKS_READY) {
mixerStatus = MIXER_TRACKS_ENABLED;
}
@@ -1859,12 +1875,11 @@
chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
LOGV("stopping track on chain %p for session Id: %d", chain.get(), track->sessionId());
- chain->stopTrack();
+ chain->decActiveTrackCnt();
}
}
if (track->isTerminated()) {
- mTracks.remove(track);
- deleteTrackName_l(track->mName);
+ removeTrack_l(track);
}
}
}
@@ -1889,10 +1904,8 @@
for (size_t i = 0; i < size; i++) {
sp<Track> t = mTracks[i];
if (t->type() == streamType) {
- t->mCblk->lock.lock();
- t->mCblk->flags |= CBLK_INVALID_ON;
+ android_atomic_or(CBLK_INVALID_ON, &t->mCblk->flags);
t->mCblk->cv.signal();
- t->mCblk->lock.unlock();
}
}
}
@@ -1926,14 +1939,14 @@
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- if (value != AudioSystem::PCM_16_BIT) {
+ if (value != AUDIO_FORMAT_PCM_16_BIT) {
status = BAD_VALUE;
} else {
reconfig = true;
}
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- if (value != AudioSystem::CHANNEL_OUT_STEREO) {
+ if (value != AUDIO_CHANNEL_OUT_STEREO) {
status = BAD_VALUE;
} else {
reconfig = true;
@@ -1950,6 +1963,27 @@
}
}
if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+ // when changing the audio output device, call addBatteryData to notify
+ // the change
+ if ((int)mDevice != value) {
+ uint32_t params = 0;
+ // check whether speaker is on
+ if (value & AUDIO_DEVICE_OUT_SPEAKER) {
+ params |= IMediaPlayerService::kBatteryDataSpeakerOn;
+ }
+
+ int deviceWithoutSpeaker
+ = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
+ // check if any other device (except speaker) is on
+ if (value & deviceWithoutSpeaker ) {
+ params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
+ }
+
+ if (params != 0) {
+ addBatteryData(params);
+ }
+ }
+
// forward device change to effects that have requested to be
// aware of attached audio device.
mDevice = (uint32_t)value;
@@ -1959,12 +1993,14 @@
}
if (status == NO_ERROR) {
- status = mOutput->setParameters(keyValuePair);
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
if (!mStandby && status == INVALID_OPERATION) {
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
mStandby = true;
mBytesWritten = 0;
- status = mOutput->setParameters(keyValuePair);
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
}
if (status == NO_ERROR && reconfig) {
delete mAudioMixer;
@@ -2008,7 +2044,7 @@
uint32_t AudioFlinger::MixerThread::activeSleepTimeUs()
{
- return (uint32_t)(mOutput->latency() * 1000) / 2;
+ return (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2;
}
uint32_t AudioFlinger::MixerThread::idleSleepTimeUs()
@@ -2058,12 +2094,12 @@
void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp)
{
// Do not apply volume on compressed audio
- if (!AudioSystem::isLinearPCM(mFormat)) {
+ if (!audio_is_linear_pcm(mFormat)) {
return;
}
// convert to signed 16 bit before volume calculation
- if (mFormat == AudioSystem::PCM_8_BIT) {
+ if (mFormat == AUDIO_FORMAT_PCM_8_BIT) {
size_t count = mFrameCount * mChannelCount;
uint8_t *src = (uint8_t *)mMixBuffer + count-1;
int16_t *dst = mMixBuffer + count-1;
@@ -2116,7 +2152,7 @@
}
// convert back to unsigned 8 bit after volume calculation
- if (mFormat == AudioSystem::PCM_8_BIT) {
+ if (mFormat == AUDIO_FORMAT_PCM_8_BIT) {
size_t count = mFrameCount * mChannelCount;
int16_t *src = mMixBuffer;
uint8_t *dst = (uint8_t *)mMixBuffer;
@@ -2172,7 +2208,7 @@
// wait until we have something to do...
if (!mStandby) {
LOGV("Audio hardware entering standby, mixer %p\n", this);
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
mStandby = true;
mBytesWritten = 0;
}
@@ -2257,7 +2293,7 @@
// If audio HAL implements volume control,
// force software volume to nominal value
- if (mOutput->setVolume(left, right) == NO_ERROR) {
+ if (mOutput->stream->set_volume(mOutput->stream, left, right) == NO_ERROR) {
left = 1.0f;
right = 1.0f;
}
@@ -2321,11 +2357,10 @@
if (!effectChains.isEmpty()) {
LOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(),
trackToRemove->sessionId());
- effectChains[0]->stopTrack();
+ effectChains[0]->decActiveTrackCnt();
}
if (trackToRemove->isTerminated()) {
- mTracks.remove(trackToRemove);
- deleteTrackName_l(trackToRemove->mName);
+ removeTrack_l(trackToRemove);
}
}
@@ -2358,7 +2393,7 @@
} else {
sleepTime = idleSleepTime;
}
- } else if (mBytesWritten != 0 && AudioSystem::isLinearPCM(mFormat)) {
+ } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
memset (mMixBuffer, 0, mFrameCount * mFrameSize);
sleepTime = 0;
}
@@ -2380,7 +2415,7 @@
mLastWriteTime = systemTime();
mInWrite = true;
mBytesWritten += mixBufferSize;
- int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
+ int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
mNumWrites++;
mInWrite = false;
@@ -2402,7 +2437,7 @@
}
if (!mStandby) {
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
}
LOGV("DirectOutputThread %p exiting", this);
@@ -2442,12 +2477,14 @@
}
}
if (status == NO_ERROR) {
- status = mOutput->setParameters(keyValuePair);
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
if (!mStandby && status == INVALID_OPERATION) {
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
mStandby = true;
mBytesWritten = 0;
- status = mOutput->setParameters(keyValuePair);
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
}
if (status == NO_ERROR && reconfig) {
readOutputParameters();
@@ -2467,8 +2504,8 @@
uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs()
{
uint32_t time;
- if (AudioSystem::isLinearPCM(mFormat)) {
- time = (uint32_t)(mOutput->latency() * 1000) / 2;
+ if (audio_is_linear_pcm(mFormat)) {
+ time = (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2;
} else {
time = 10000;
}
@@ -2478,7 +2515,7 @@
uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs()
{
uint32_t time;
- if (AudioSystem::isLinearPCM(mFormat)) {
+ if (audio_is_linear_pcm(mFormat)) {
time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
} else {
time = 10000;
@@ -2489,7 +2526,7 @@
uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs()
{
uint32_t time;
- if (AudioSystem::isLinearPCM(mFormat)) {
+ if (audio_is_linear_pcm(mFormat)) {
time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
} else {
time = 10000;
@@ -2670,7 +2707,7 @@
mChannelCount,
frameCount);
if (outputTrack->cblk() != NULL) {
- thread->setStreamVolume(AudioSystem::NUM_STREAM_TYPES, 1.0f);
+ thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
mOutputTracks.add(outputTrack);
LOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
updateWaitTime();
@@ -2915,7 +2952,7 @@
mStreamType = streamType;
// NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of
// 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
- mCblk->frameSize = AudioSystem::isLinearPCM(format) ? channelCount * sizeof(int16_t) : sizeof(int8_t);
+ mCblk->frameSize = audio_is_linear_pcm(format) ? channelCount * sizeof(int16_t) : sizeof(int8_t);
}
}
@@ -2946,8 +2983,11 @@
if (!isOutputTrack()) {
if (mState == ACTIVE || mState == RESUMING) {
AudioSystem::stopOutput(thread->id(),
- (AudioSystem::stream_type)mStreamType,
+ (audio_stream_type_t)mStreamType,
mSessionId);
+
+ // to track the speaker usage
+ addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
}
AudioSystem::releaseOutput(thread->id());
}
@@ -3027,7 +3067,7 @@
if (mCblk->framesReady() >= mCblk->frameCount ||
(mCblk->flags & CBLK_FORCEREADY_MSK)) {
mFillingUpStatus = FS_FILLED;
- mCblk->flags &= ~CBLK_FORCEREADY_MSK;
+ android_atomic_and(~CBLK_FORCEREADY_MSK, &mCblk->flags);
return true;
}
return false;
@@ -3055,9 +3095,14 @@
if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
thread->mLock.unlock();
status = AudioSystem::startOutput(thread->id(),
- (AudioSystem::stream_type)mStreamType,
+ (audio_stream_type_t)mStreamType,
mSessionId);
thread->mLock.lock();
+
+ // to track the speaker usage
+ if (status == NO_ERROR) {
+ addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStart);
+ }
}
if (status == NO_ERROR) {
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
@@ -3090,9 +3135,12 @@
if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
thread->mLock.unlock();
AudioSystem::stopOutput(thread->id(),
- (AudioSystem::stream_type)mStreamType,
+ (audio_stream_type_t)mStreamType,
mSessionId);
thread->mLock.lock();
+
+ // to track the speaker usage
+ addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
}
}
}
@@ -3109,9 +3157,12 @@
if (!isOutputTrack()) {
thread->mLock.unlock();
AudioSystem::stopOutput(thread->id(),
- (AudioSystem::stream_type)mStreamType,
+ (audio_stream_type_t)mStreamType,
mSessionId);
thread->mLock.lock();
+
+ // to track the speaker usage
+ addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
}
}
}
@@ -3130,14 +3181,12 @@
// STOPPED state
mState = STOPPED;
- mCblk->lock.lock();
- // NOTE: reset() will reset cblk->user and cblk->server with
- // the risk that at the same time, the AudioMixer is trying to read
- // data. In this case, getNextBuffer() would return a NULL pointer
- // as audio buffer => the AudioMixer code MUST always test that pointer
- // returned by getNextBuffer() is not NULL!
- reset();
- mCblk->lock.unlock();
+ // do not reset the track if it is still in the process of being stopped or paused.
+ // this will be done by prepareTracks_l() when the track is stopped.
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+ reset();
+ }
}
}
@@ -3149,8 +3198,8 @@
TrackBase::reset();
// Force underrun condition to avoid false underrun callback until first data is
// written to buffer
- mCblk->flags |= CBLK_UNDERRUN_ON;
- mCblk->flags &= ~CBLK_FORCEREADY_MSK;
+ android_atomic_and(~CBLK_FORCEREADY_MSK, &mCblk->flags);
+ android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
mFillingUpStatus = FS_FILLING;
mResetDone = true;
}
@@ -3202,9 +3251,9 @@
{
if (mCblk != NULL) {
LOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
- if (format == AudioSystem::PCM_16_BIT) {
+ if (format == AUDIO_FORMAT_PCM_16_BIT) {
mCblk->frameSize = channelCount * sizeof(int16_t);
- } else if (format == AudioSystem::PCM_8_BIT) {
+ } else if (format == AUDIO_FORMAT_PCM_8_BIT) {
mCblk->frameSize = channelCount * sizeof(int8_t);
} else {
mCblk->frameSize = sizeof(int8_t);
@@ -3279,7 +3328,7 @@
TrackBase::reset();
// Force overerrun condition to avoid false overrun callback until first data is
// read from buffer
- mCblk->flags |= CBLK_UNDERRUN_ON;
+ android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
}
}
@@ -3307,7 +3356,7 @@
int format,
int channelCount,
int frameCount)
- : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL, 0),
+ : Track(thread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelCount, frameCount, NULL, 0),
mActive(false), mSourceThread(sourceThread)
{
@@ -3679,7 +3728,7 @@
}
// If no audio session id is provided, create one here
- if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+ if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) {
lSessionId = *sessionId;
} else {
lSessionId = nextUniqueId_l();
@@ -3749,7 +3798,7 @@
ThreadBase(audioFlinger, id),
mInput(input), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0)
{
- mReqChannelCount = AudioSystem::popCount(channels);
+ mReqChannelCount = popcount(channels);
mReqSampleRate = sampleRate;
readInputParameters();
}
@@ -3791,7 +3840,7 @@
checkForNewParameters_l();
if (mActiveTrack == 0 && mConfigEvents.isEmpty()) {
if (!mStandby) {
- mInput->standby();
+ mInput->stream->common.standby(&mInput->stream->common);
mStandby = true;
}
@@ -3806,7 +3855,7 @@
if (mActiveTrack != 0) {
if (mActiveTrack->mState == TrackBase::PAUSING) {
if (!mStandby) {
- mInput->standby();
+ mInput->stream->common.standby(&mInput->stream->common);
mStandby = true;
}
mActiveTrack.clear();
@@ -3851,7 +3900,7 @@
mRsmpInIndex += framesIn;
framesOut -= framesIn;
if ((int)mChannelCount == mReqChannelCount ||
- mFormat != AudioSystem::PCM_16_BIT) {
+ mFormat != AUDIO_FORMAT_PCM_16_BIT) {
memcpy(dst, src, framesIn * mFrameSize);
} else {
int16_t *src16 = (int16_t *)src;
@@ -3871,11 +3920,11 @@
}
if (framesOut && mFrameCount == mRsmpInIndex) {
if (framesOut == mFrameCount &&
- ((int)mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) {
- mBytesRead = mInput->read(buffer.raw, mInputBytes);
+ ((int)mChannelCount == mReqChannelCount || mFormat != AUDIO_FORMAT_PCM_16_BIT)) {
+ mBytesRead = mInput->stream->read(mInput->stream, buffer.raw, mInputBytes);
framesOut = 0;
} else {
- mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
+ mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes);
mRsmpInIndex = 0;
}
if (mBytesRead < 0) {
@@ -3883,7 +3932,7 @@
if (mActiveTrack->mState == TrackBase::ACTIVE) {
// Force input into standby so that it tries to
// recover at next read attempt
- mInput->standby();
+ mInput->stream->common.standby(&mInput->stream->common);
usleep(5000);
}
mRsmpInIndex = mFrameCount;
@@ -3938,7 +3987,7 @@
}
if (!mStandby) {
- mInput->standby();
+ mInput->stream->common.standby(&mInput->stream->common);
}
mActiveTrack.clear();
@@ -3973,9 +4022,12 @@
mActiveTrack.clear();
return status;
}
- mActiveTrack->mState = TrackBase::RESUMING;
mRsmpInIndex = mFrameCount;
mBytesRead = 0;
+ if (mResampler != NULL) {
+ mResampler->reset();
+ }
+ mActiveTrack->mState = TrackBase::RESUMING;
// signal thread to start
LOGV("Signal record thread");
mWaitWorkCV.signal();
@@ -4067,13 +4119,13 @@
int channelCount;
if (framesReady == 0) {
- mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
+ mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes);
if (mBytesRead < 0) {
LOGE("RecordThread::getNextBuffer() Error reading audio input");
if (mActiveTrack->mState == TrackBase::ACTIVE) {
// Force input into standby so that it tries to
// recover at next read attempt
- mInput->standby();
+ mInput->stream->common.standby(&mInput->stream->common);
usleep(5000);
}
buffer->raw = 0;
@@ -4126,7 +4178,7 @@
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- reqChannelCount = AudioSystem::popCount(value);
+ reqChannelCount = popcount(value);
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
@@ -4140,16 +4192,18 @@
}
}
if (status == NO_ERROR) {
- status = mInput->setParameters(keyValuePair);
+ status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string());
if (status == INVALID_OPERATION) {
- mInput->standby();
- status = mInput->setParameters(keyValuePair);
+ mInput->stream->common.standby(&mInput->stream->common);
+ status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string());
}
if (reconfig) {
if (status == BAD_VALUE &&
- reqFormat == mInput->format() && reqFormat == AudioSystem::PCM_16_BIT &&
- ((int)mInput->sampleRate() <= 2 * reqSamplingRate) &&
- (AudioSystem::popCount(mInput->channels()) < 3) && (reqChannelCount < 3)) {
+ reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&
+ reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
+ ((int)mInput->stream->common.get_sample_rate(&mInput->stream->common) <= (2 * reqSamplingRate)) &&
+ (popcount(mInput->stream->common.get_channels(&mInput->stream->common)) < 3) &&
+ (reqChannelCount < 3)) {
status = NO_ERROR;
}
if (status == NO_ERROR) {
@@ -4170,7 +4224,13 @@
String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
{
- return mInput->getParameters(keys);
+ char *s;
+ String8 out_s8;
+
+ s = mInput->stream->common.get_parameters(&mInput->stream->common, keys.string());
+ out_s8 = String8(s);
+ free(s);
+ return out_s8;
}
void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
@@ -4202,12 +4262,12 @@
if (mResampler) delete mResampler;
mResampler = 0;
- mSampleRate = mInput->sampleRate();
- mChannels = mInput->channels();
- mChannelCount = (uint16_t)AudioSystem::popCount(mChannels);
- mFormat = mInput->format();
- mFrameSize = (uint16_t)mInput->frameSize();
- mInputBytes = mInput->bufferSize();
+ mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
+ mChannels = mInput->stream->common.get_channels(&mInput->stream->common);
+ mChannelCount = (uint16_t)popcount(mChannels);
+ mFormat = mInput->stream->common.get_format(&mInput->stream->common);
+ mFrameSize = (uint16_t)audio_stream_frame_size(&mInput->stream->common);
+ mInputBytes = mInput->stream->common.get_buffer_size(&mInput->stream->common);
mFrameCount = mInputBytes / mFrameSize;
mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
@@ -4237,7 +4297,7 @@
unsigned int AudioFlinger::RecordThread::getInputFramesLost()
{
- return mInput->getInputFramesLost();
+ return mInput->stream->get_input_frames_lost(mInput->stream);
}
// ----------------------------------------------------------------------------
@@ -4256,6 +4316,8 @@
uint32_t format = pFormat ? *pFormat : 0;
uint32_t channels = pChannels ? *pChannels : 0;
uint32_t latency = pLatencyMs ? *pLatencyMs : 0;
+ audio_stream_out_t *outStream;
+ audio_hw_device_t *outHwDev;
LOGV("openOutput(), Device %x, SamplingRate %d, Format %d, Channels %x, flags %x",
pDevices ? *pDevices : 0,
@@ -4267,43 +4329,35 @@
if (pDevices == NULL || *pDevices == 0) {
return 0;
}
+
Mutex::Autolock _l(mLock);
- AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices,
- (int *)&format,
- &channels,
- &samplingRate,
- &status);
+ outHwDev = findSuitableHwDev_l(*pDevices);
+ if (outHwDev == NULL)
+ return 0;
+
+ status = outHwDev->open_output_stream(outHwDev, *pDevices, (int *)&format,
+ &channels, &samplingRate, &outStream);
LOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, Channels %x, status %d",
- output,
+ outStream,
samplingRate,
format,
channels,
status);
mHardwareStatus = AUDIO_HW_IDLE;
- if (output != 0) {
+ if (outStream != NULL) {
+ AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream);
int id = nextUniqueId_l();
- if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
- (format != AudioSystem::PCM_16_BIT) ||
- (channels != AudioSystem::CHANNEL_OUT_STEREO)) {
+
+ if ((flags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT) ||
+ (format != AUDIO_FORMAT_PCM_16_BIT) ||
+ (channels != AUDIO_CHANNEL_OUT_STEREO)) {
thread = new DirectOutputThread(this, output, id, *pDevices);
LOGV("openOutput() created direct output: ID %d thread %p", id, thread);
} else {
thread = new MixerThread(this, output, id, *pDevices);
LOGV("openOutput() created mixer output: ID %d thread %p", id, thread);
-
-#ifdef LVMX
- unsigned bitsPerSample =
- (format == AudioSystem::PCM_16_BIT) ? 16 :
- ((format == AudioSystem::PCM_8_BIT) ? 8 : 0);
- unsigned channelCount = (channels == AudioSystem::CHANNEL_OUT_STEREO) ? 2 : 1;
- int audioOutputType = LifeVibes::threadIdToAudioOutputType(thread->id());
-
- LifeVibes::init_aot(audioOutputType, samplingRate, bitsPerSample, channelCount);
- LifeVibes::setDevice(audioOutputType, *pDevices);
-#endif
-
}
mPlaybackThreads.add(id, thread);
@@ -4369,7 +4423,9 @@
thread->exit();
if (thread->type() != PlaybackThread::DUPLICATING) {
- mAudioHardware->closeOutputStream(thread->getOutput());
+ AudioStreamOut *out = thread->getOutput();
+ out->hwDev->close_output_stream(out->hwDev, out->stream);
+ delete out;
}
return NO_ERROR;
}
@@ -4419,20 +4475,25 @@
uint32_t reqSamplingRate = samplingRate;
uint32_t reqFormat = format;
uint32_t reqChannels = channels;
+ audio_stream_in_t *inStream;
+ audio_hw_device_t *inHwDev;
if (pDevices == NULL || *pDevices == 0) {
return 0;
}
+
Mutex::Autolock _l(mLock);
- AudioStreamIn *input = mAudioHardware->openInputStream(*pDevices,
- (int *)&format,
- &channels,
- &samplingRate,
- &status,
- (AudioSystem::audio_in_acoustics)acoustics);
+ inHwDev = findSuitableHwDev_l(*pDevices);
+ if (inHwDev == NULL)
+ return 0;
+
+ status = inHwDev->open_input_stream(inHwDev, *pDevices, (int *)&format,
+ &channels, &samplingRate,
+ (audio_in_acoustics_t)acoustics,
+ &inStream);
LOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, acoustics %x, status %d",
- input,
+ inStream,
samplingRate,
format,
channels,
@@ -4442,20 +4503,20 @@
// If the input could not be opened with the requested parameters and we can handle the conversion internally,
// try to open again with the proposed parameters. The AudioFlinger can resample the input and do mono to stereo
// or stereo to mono conversions on 16 bit PCM inputs.
- if (input == 0 && status == BAD_VALUE &&
- reqFormat == format && format == AudioSystem::PCM_16_BIT &&
+ if (inStream == NULL && status == BAD_VALUE &&
+ reqFormat == format && format == AUDIO_FORMAT_PCM_16_BIT &&
(samplingRate <= 2 * reqSamplingRate) &&
- (AudioSystem::popCount(channels) < 3) && (AudioSystem::popCount(reqChannels) < 3)) {
+ (popcount(channels) < 3) && (popcount(reqChannels) < 3)) {
LOGV("openInput() reopening with proposed sampling rate and channels");
- input = mAudioHardware->openInputStream(*pDevices,
- (int *)&format,
- &channels,
- &samplingRate,
- &status,
- (AudioSystem::audio_in_acoustics)acoustics);
+ status = inHwDev->open_input_stream(inHwDev, *pDevices, (int *)&format,
+ &channels, &samplingRate,
+ (audio_in_acoustics_t)acoustics,
+ &inStream);
}
- if (input != 0) {
+ if (inStream != NULL) {
+ AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream);
+
int id = nextUniqueId_l();
// Start record thread
thread = new RecordThread(this, input, reqSamplingRate, reqChannels, id);
@@ -4465,7 +4526,7 @@
if (pFormat) *pFormat = format;
if (pChannels) *pChannels = reqChannels;
- input->standby();
+ input->stream->common.standby(&input->stream->common);
// notify client processes of the new input creation
thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);
@@ -4494,7 +4555,9 @@
}
thread->exit();
- mAudioHardware->closeInputStream(thread->getInput());
+ AudioStreamIn *in = thread->getInput();
+ in->hwDev->close_input_stream(in->hwDev, in->stream);
+ delete in;
return NO_ERROR;
}
@@ -4648,14 +4711,14 @@
}
// check audio settings permission for global effects
- if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && !settingsAllowed()) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX && !settingsAllowed()) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
- // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
+ // Session AUDIO_SESSION_OUTPUT_STAGE is reserved for output stage effects
// that can only be created by audio policy manager (running in same process)
- if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE && getpid() != pid) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && getpid() != pid) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
@@ -4669,12 +4732,12 @@
}
if (output == 0) {
- if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
// output must be specified by AudioPolicyManager when using session
- // AudioSystem::SESSION_OUTPUT_STAGE
+ // AUDIO_SESSION_OUTPUT_STAGE
lStatus = BAD_VALUE;
goto Exit;
- } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
+ } else if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
// if the output returned by getOutputForEffect() is removed before we lock the
// mutex below, the call to checkPlaybackThread_l(output) below will detect it
// and we will exit safely
@@ -4722,7 +4785,7 @@
// an auxiliary version of this effect type is available
found = true;
memcpy(&d, &desc, sizeof(effect_descriptor_t));
- if (sessionId != AudioSystem::SESSION_OUTPUT_MIX ||
+ if (sessionId != AUDIO_SESSION_OUTPUT_MIX ||
(desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
break;
}
@@ -4735,14 +4798,14 @@
}
// For same effect type, chose auxiliary version over insert version if
// connect to output mix (Compliance to OpenSL ES)
- if (sessionId == AudioSystem::SESSION_OUTPUT_MIX &&
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX &&
(d.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) {
memcpy(&desc, &d, sizeof(effect_descriptor_t));
}
}
// Do not allow auxiliary effects on a session different from 0 (output mix)
- if (sessionId != AudioSystem::SESSION_OUTPUT_MIX &&
+ if (sessionId != AUDIO_SESSION_OUTPUT_MIX &&
(desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
lStatus = INVALID_OPERATION;
goto Exit;
@@ -4915,7 +4978,7 @@
// Do not allow auxiliary effect on session other than 0
if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY &&
- sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+ sessionId != AUDIO_SESSION_OUTPUT_MIX) {
LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
desc->name, sessionId);
lStatus = BAD_VALUE;
@@ -4924,7 +4987,7 @@
// Do not allow effects with session ID 0 on direct output or duplicating threads
// TODO: add rule for hw accelerated effects on direct outputs with non PCM format
- if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && mType != MIXER) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX && mType != MIXER) {
LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
desc->name, sessionId);
lStatus = BAD_VALUE;
@@ -5095,6 +5158,7 @@
if (session == track->sessionId()) {
LOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer);
track->setMainBuffer(buffer);
+ chain->incTrackCnt();
}
}
@@ -5104,20 +5168,20 @@
if (track == 0) continue;
if (session == track->sessionId()) {
LOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
- chain->startTrack();
+ chain->incActiveTrackCnt();
}
}
}
chain->setInBuffer(buffer, ownsBuffer);
chain->setOutBuffer(mMixBuffer);
- // Effect chain for session AudioSystem::SESSION_OUTPUT_STAGE is inserted at end of effect
+ // Effect chain for session AUDIO_SESSION_OUTPUT_STAGE is inserted at end of effect
// chains list in order to be processed last as it contains output stage effects
- // Effect chain for session AudioSystem::SESSION_OUTPUT_MIX is inserted before
- // session AudioSystem::SESSION_OUTPUT_STAGE to be processed
+ // Effect chain for session AUDIO_SESSION_OUTPUT_MIX is inserted before
+ // session AUDIO_SESSION_OUTPUT_STAGE to be processed
// after track specific effects and before output stage
- // It is therefore mandatory that AudioSystem::SESSION_OUTPUT_MIX == 0 and
- // that AudioSystem::SESSION_OUTPUT_STAGE < AudioSystem::SESSION_OUTPUT_MIX
+ // It is therefore mandatory that AUDIO_SESSION_OUTPUT_MIX == 0 and
+ // that AUDIO_SESSION_OUTPUT_STAGE < AUDIO_SESSION_OUTPUT_MIX
// Effect chain for other sessions are inserted at beginning of effect
// chains list to be processed before output mix effects. Relative order between other
// sessions is not important
@@ -5140,11 +5204,23 @@
for (size_t i = 0; i < mEffectChains.size(); i++) {
if (chain == mEffectChains[i]) {
mEffectChains.removeAt(i);
+ // detach all active tracks from the chain
+ for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
+ sp<Track> track = mActiveTracks[i].promote();
+ if (track == 0) continue;
+ if (session == track->sessionId()) {
+ LOGV("removeEffectChain_l(): stopping track on chain %p for session Id: %d",
+ chain.get(), session);
+ chain->decActiveTrackCnt();
+ }
+ }
+
// detach all tracks with same session ID from this chain
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> track = mTracks[i];
if (session == track->sessionId()) {
track->setMainBuffer(mMixBuffer);
+ chain->decTrackCnt();
}
}
break;
@@ -5197,8 +5273,8 @@
if (EffectId == 0) {
track->setAuxBuffer(0, NULL);
} else {
- // Auxiliary effects are always in audio session AudioSystem::SESSION_OUTPUT_MIX
- sp<EffectModule> effect = getEffect_l(AudioSystem::SESSION_OUTPUT_MIX, EffectId);
+ // Auxiliary effects are always in audio session AUDIO_SESSION_OUTPUT_MIX
+ sp<EffectModule> effect = getEffect_l(AUDIO_SESSION_OUTPUT_MIX, EffectId);
if (effect != 0) {
if ((effect->desc().flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
track->setAuxBuffer(EffectId, (int32_t *)effect->inBuffer());
@@ -5426,7 +5502,7 @@
// If an insert effect is idle and input buffer is different from output buffer,
// accumulate input onto output
sp<EffectChain> chain = mChain.promote();
- if (chain != 0 && chain->activeTracks() != 0) {
+ if (chain != 0 && chain->activeTrackCnt() != 0) {
size_t frameCnt = mConfig.inputCfg.buffer.frameCount * 2; //always stereo here
int16_t *in = mConfig.inputCfg.buffer.s16;
int16_t *out = mConfig.outputCfg.buffer.s16;
@@ -5482,7 +5558,7 @@
mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
// Insert effect:
- // - in session AudioSystem::SESSION_OUTPUT_MIX or AudioSystem::SESSION_OUTPUT_STAGE,
+ // - in session AUDIO_SESSION_OUTPUT_MIX or AUDIO_SESSION_OUTPUT_STAGE,
// always overwrites output buffer: input buffer == output buffer
// - in other sessions:
// last effect in the chain accumulates in output buffer: input buffer != output buffer
@@ -5763,17 +5839,17 @@
// update this table when AudioSystem::audio_devices or audio_device_e (in EffectApi.h) are modified
const uint32_t AudioFlinger::EffectModule::sDeviceConvTable[] = {
- DEVICE_EARPIECE, // AudioSystem::DEVICE_OUT_EARPIECE
- DEVICE_SPEAKER, // AudioSystem::DEVICE_OUT_SPEAKER
- DEVICE_WIRED_HEADSET, // case AudioSystem::DEVICE_OUT_WIRED_HEADSET
- DEVICE_WIRED_HEADPHONE, // AudioSystem::DEVICE_OUT_WIRED_HEADPHONE
- DEVICE_BLUETOOTH_SCO, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO
- DEVICE_BLUETOOTH_SCO_HEADSET, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET
- DEVICE_BLUETOOTH_SCO_CARKIT, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT
- DEVICE_BLUETOOTH_A2DP, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP
- DEVICE_BLUETOOTH_A2DP_HEADPHONES, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
- DEVICE_BLUETOOTH_A2DP_SPEAKER, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER
- DEVICE_AUX_DIGITAL // AudioSystem::DEVICE_OUT_AUX_DIGITAL
+ DEVICE_EARPIECE, // AUDIO_DEVICE_OUT_EARPIECE
+ DEVICE_SPEAKER, // AUDIO_DEVICE_OUT_SPEAKER
+ DEVICE_WIRED_HEADSET, // case AUDIO_DEVICE_OUT_WIRED_HEADSET
+ DEVICE_WIRED_HEADPHONE, // AUDIO_DEVICE_OUT_WIRED_HEADPHONE
+ DEVICE_BLUETOOTH_SCO, // AUDIO_DEVICE_OUT_BLUETOOTH_SCO
+ DEVICE_BLUETOOTH_SCO_HEADSET, // AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET
+ DEVICE_BLUETOOTH_SCO_CARKIT, // AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT
+ DEVICE_BLUETOOTH_A2DP, // AUDIO_DEVICE_OUT_BLUETOOTH_A2DP
+ DEVICE_BLUETOOTH_A2DP_HEADPHONES, // AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
+ DEVICE_BLUETOOTH_A2DP_SPEAKER, // AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER
+ DEVICE_AUX_DIGITAL // AUDIO_DEVICE_OUT_AUX_DIGITAL
};
uint32_t AudioFlinger::EffectModule::deviceAudioSystemToEffectApi(uint32_t device)
@@ -5783,7 +5859,7 @@
const uint32_t i = 31 - __builtin_clz(device);
device &= ~(1 << i);
if (i >= sizeof(sDeviceConvTable)/sizeof(uint32_t)) {
- LOGE("device convertion error for AudioSystem device 0x%08x", device);
+ LOGE("device conversion error for AudioSystem device 0x%08x", device);
return 0;
}
deviceOut |= (uint32_t)sDeviceConvTable[i];
@@ -5793,10 +5869,10 @@
// update this table when AudioSystem::audio_mode or audio_mode_e (in EffectApi.h) are modified
const uint32_t AudioFlinger::EffectModule::sModeConvTable[] = {
- AUDIO_MODE_NORMAL, // AudioSystem::MODE_NORMAL
- AUDIO_MODE_RINGTONE, // AudioSystem::MODE_RINGTONE
- AUDIO_MODE_IN_CALL, // AudioSystem::MODE_IN_CALL
- AUDIO_MODE_IN_CALL // AudioSystem::MODE_IN_COMMUNICATION, same conversion as for MODE_IN_CALL
+ AUDIO_EFFECT_MODE_NORMAL, // AUDIO_MODE_NORMAL
+ AUDIO_EFFECT_MODE_RINGTONE, // AUDIO_MODE_RINGTONE
+ AUDIO_EFFECT_MODE_IN_CALL, // AUDIO_MODE_IN_CALL
+ AUDIO_EFFECT_MODE_IN_CALL // AUDIO_MODE_IN_COMMUNICATION, same conversion as for AUDIO_MODE_IN_CALL
};
int AudioFlinger::EffectModule::modeAudioSystemToEffectApi(uint32_t mode)
@@ -6102,11 +6178,11 @@
AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread,
int sessionId)
- : mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mOwnInBuffer(false),
- mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
- mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
+ : mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0),
+ mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
{
- mStrategy = AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+ mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
}
AudioFlinger::EffectChain::~EffectChain()
@@ -6157,12 +6233,19 @@
return;
}
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- bool isGlobalSession = (mSessionId == AudioSystem::SESSION_OUTPUT_MIX) ||
- (mSessionId == AudioSystem::SESSION_OUTPUT_STAGE);
+ bool isGlobalSession = (mSessionId == AUDIO_SESSION_OUTPUT_MIX) ||
+ (mSessionId == AUDIO_SESSION_OUTPUT_STAGE);
bool tracksOnSession = false;
if (!isGlobalSession) {
- tracksOnSession =
- playbackThread->hasAudioSession(mSessionId) & PlaybackThread::TRACK_SESSION;
+ tracksOnSession = (trackCnt() != 0);
+ }
+
+ // if no track is active, input buffer must be cleared here as the mixer process
+ // will not do it
+ if (tracksOnSession &&
+ activeTrackCnt() == 0) {
+ size_t numSamples = playbackThread->frameCount() * playbackThread->channelCount();
+ memset(mInBuffer, 0, numSamples * sizeof(int16_t));
}
size_t size = mEffects.size();
@@ -6175,13 +6258,6 @@
for (size_t i = 0; i < size; i++) {
mEffects[i]->updateState();
}
- // if no track is active, input buffer must be cleared here as the mixer process
- // will not do it
- if (tracksOnSession &&
- activeTracks() == 0) {
- size_t numSamples = playbackThread->frameCount() * playbackThread->channelCount();
- memset(mInBuffer, 0, numSamples * sizeof(int16_t));
- }
}
// addEffect_l() must be called with PlaybackThread::mLock held
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 81f2eb4..39314ad 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -32,12 +32,14 @@
#include <utils/Errors.h>
#include <utils/threads.h>
#include <utils/SortedVector.h>
+#include <utils/TypeHelpers.h>
#include <utils/Vector.h>
#include <binder/BinderService.h>
#include <binder/MemoryDealer.h>
-#include <hardware_legacy/AudioHardwareInterface.h>
+#include <system/audio.h>
+#include <hardware/audio_hal.h>
#include "AudioBufferProvider.h"
@@ -49,7 +51,6 @@
class AudioBuffer;
class AudioResampler;
-
// ----------------------------------------------------------------------------
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
@@ -211,6 +212,9 @@
AudioFlinger();
virtual ~AudioFlinger();
+ status_t initCheck() const;
+ virtual void onFirstRef();
+ audio_hw_device_t* findSuitableHwDev_l(uint32_t devices);
// Internal dump utilites.
status_t dumpPermissionDenial(int fd, const Vector<String16>& args);
@@ -268,6 +272,8 @@
class EffectModule;
class EffectHandle;
class EffectChain;
+ struct AudioStreamOut;
+ struct AudioStreamIn;
class ThreadBase : public Thread {
public:
@@ -495,7 +501,7 @@
void reset();
bool isOutputTrack() const {
- return (mStreamType == AudioSystem::NUM_STREAM_TYPES);
+ return (mStreamType == AUDIO_STREAM_CNT);
}
// we don't really need a lock for these
@@ -678,6 +684,7 @@
status_t addTrack_l(const sp<Track>& track);
void destroyTrack_l(const sp<Track>& track);
+ void removeTrack_l(const sp<Track>& track);
void readOutputParameters();
@@ -689,7 +696,7 @@
SortedVector< sp<Track> > mTracks;
// mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread
- stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES + 1];
+ stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1];
AudioStreamOut* mOutput;
float mMasterVolume;
nsecs_t mLastWriteTime;
@@ -1128,9 +1135,13 @@
return mOutBuffer;
}
- void startTrack() {mActiveTrackCnt++;}
- void stopTrack() {mActiveTrackCnt--;}
- int activeTracks() { return mActiveTrackCnt;}
+ void incTrackCnt() { android_atomic_inc(&mTrackCnt); }
+ void decTrackCnt() { android_atomic_dec(&mTrackCnt); }
+ int32_t trackCnt() { return mTrackCnt;}
+
+ void incActiveTrackCnt() { android_atomic_inc(&mActiveTrackCnt); }
+ void decActiveTrackCnt() { android_atomic_dec(&mActiveTrackCnt); }
+ int32_t activeTrackCnt() { return mActiveTrackCnt;}
uint32_t strategy() { return mStrategy; }
void setStrategy(uint32_t strategy)
@@ -1149,7 +1160,8 @@
int mSessionId; // audio session ID
int16_t *mInBuffer; // chain input buffer
int16_t *mOutBuffer; // chain output buffer
- int mActiveTrackCnt; // number of active tracks connected
+ volatile int32_t mActiveTrackCnt; // number of active tracks connected
+ volatile int32_t mTrackCnt; // number of tracks connected
bool mOwnInBuffer; // true if the chain owns its input buffer
int mVolumeCtrlIdx; // index of insert effect having control over volume
uint32_t mLeftVolume; // previous volume on left channel
@@ -1159,21 +1171,37 @@
uint32_t mStrategy; // strategy for this effect chain
};
+ struct AudioStreamOut {
+ audio_hw_device_t *hwDev;
+ audio_stream_out_t *stream;
+
+ AudioStreamOut(audio_hw_device_t *dev, audio_stream_out_t *out) :
+ hwDev(dev), stream(out) {}
+ };
+
+ struct AudioStreamIn {
+ audio_hw_device_t *hwDev;
+ audio_stream_in_t *stream;
+
+ AudioStreamIn(audio_hw_device_t *dev, audio_stream_in_t *in) :
+ hwDev(dev), stream(in) {}
+ };
+
friend class RecordThread;
friend class PlaybackThread;
-
mutable Mutex mLock;
DefaultKeyedVector< pid_t, wp<Client> > mClients;
mutable Mutex mHardwareLock;
- AudioHardwareInterface* mAudioHardware;
+ audio_hw_device_t* mPrimaryHardwareDev;
+ Vector<audio_hw_device_t*> mAudioHwDevs;
mutable int mHardwareStatus;
DefaultKeyedVector< int, sp<PlaybackThread> > mPlaybackThreads;
- PlaybackThread::stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES];
+ PlaybackThread::stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
float mMasterVolume;
bool mMasterMute;
@@ -1181,13 +1209,11 @@
DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients;
volatile int32_t mNextUniqueId;
-#ifdef LVMX
- int mLifeVibesClientPid;
-#endif
uint32_t mMode;
};
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/services/audioflinger/AudioHardwareGeneric.cpp b/services/audioflinger/AudioHardwareGeneric.cpp
deleted file mode 100644
index d63c031..0000000
--- a/services/audioflinger/AudioHardwareGeneric.cpp
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
-**
-** Copyright 2007, 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 <stdint.h>
-#include <sys/types.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sched.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#define LOG_TAG "AudioHardware"
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#include "AudioHardwareGeneric.h"
-#include <media/AudioRecord.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static char const * const kAudioDeviceName = "/dev/eac";
-
-// ----------------------------------------------------------------------------
-
-AudioHardwareGeneric::AudioHardwareGeneric()
- : mOutput(0), mInput(0), mFd(-1), mMicMute(false)
-{
- mFd = ::open(kAudioDeviceName, O_RDWR);
-}
-
-AudioHardwareGeneric::~AudioHardwareGeneric()
-{
- if (mFd >= 0) ::close(mFd);
- closeOutputStream((AudioStreamOut *)mOutput);
- closeInputStream((AudioStreamIn *)mInput);
-}
-
-status_t AudioHardwareGeneric::initCheck()
-{
- if (mFd >= 0) {
- if (::access(kAudioDeviceName, O_RDWR) == NO_ERROR)
- return NO_ERROR;
- }
- return NO_INIT;
-}
-
-AudioStreamOut* AudioHardwareGeneric::openOutputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
-{
- AutoMutex lock(mLock);
-
- // only one output stream allowed
- if (mOutput) {
- if (status) {
- *status = INVALID_OPERATION;
- }
- return 0;
- }
-
- // create new output stream
- AudioStreamOutGeneric* out = new AudioStreamOutGeneric();
- status_t lStatus = out->set(this, mFd, devices, format, channels, sampleRate);
- if (status) {
- *status = lStatus;
- }
- if (lStatus == NO_ERROR) {
- mOutput = out;
- } else {
- delete out;
- }
- return mOutput;
-}
-
-void AudioHardwareGeneric::closeOutputStream(AudioStreamOut* out) {
- if (mOutput && out == mOutput) {
- delete mOutput;
- mOutput = 0;
- }
-}
-
-AudioStreamIn* AudioHardwareGeneric::openInputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate,
- status_t *status, AudioSystem::audio_in_acoustics acoustics)
-{
- // check for valid input source
- if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
- return 0;
- }
-
- AutoMutex lock(mLock);
-
- // only one input stream allowed
- if (mInput) {
- if (status) {
- *status = INVALID_OPERATION;
- }
- return 0;
- }
-
- // create new output stream
- AudioStreamInGeneric* in = new AudioStreamInGeneric();
- status_t lStatus = in->set(this, mFd, devices, format, channels, sampleRate, acoustics);
- if (status) {
- *status = lStatus;
- }
- if (lStatus == NO_ERROR) {
- mInput = in;
- } else {
- delete in;
- }
- return mInput;
-}
-
-void AudioHardwareGeneric::closeInputStream(AudioStreamIn* in) {
- if (mInput && in == mInput) {
- delete mInput;
- mInput = 0;
- }
-}
-
-status_t AudioHardwareGeneric::setVoiceVolume(float v)
-{
- // Implement: set voice volume
- return NO_ERROR;
-}
-
-status_t AudioHardwareGeneric::setMasterVolume(float v)
-{
- // Implement: set master volume
- // return error - software mixer will handle it
- return INVALID_OPERATION;
-}
-
-status_t AudioHardwareGeneric::setMicMute(bool state)
-{
- mMicMute = state;
- return NO_ERROR;
-}
-
-status_t AudioHardwareGeneric::getMicMute(bool* state)
-{
- *state = mMicMute;
- return NO_ERROR;
-}
-
-status_t AudioHardwareGeneric::dumpInternals(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- result.append("AudioHardwareGeneric::dumpInternals\n");
- snprintf(buffer, SIZE, "\tmFd: %d mMicMute: %s\n", mFd, mMicMute? "true": "false");
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t AudioHardwareGeneric::dump(int fd, const Vector<String16>& args)
-{
- dumpInternals(fd, args);
- if (mInput) {
- mInput->dump(fd, args);
- }
- if (mOutput) {
- mOutput->dump(fd, args);
- }
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-status_t AudioStreamOutGeneric::set(
- AudioHardwareGeneric *hw,
- int fd,
- uint32_t devices,
- int *pFormat,
- uint32_t *pChannels,
- uint32_t *pRate)
-{
- int lFormat = pFormat ? *pFormat : 0;
- uint32_t lChannels = pChannels ? *pChannels : 0;
- uint32_t lRate = pRate ? *pRate : 0;
-
- // fix up defaults
- if (lFormat == 0) lFormat = format();
- if (lChannels == 0) lChannels = channels();
- if (lRate == 0) lRate = sampleRate();
-
- // check values
- if ((lFormat != format()) ||
- (lChannels != channels()) ||
- (lRate != sampleRate())) {
- if (pFormat) *pFormat = format();
- if (pChannels) *pChannels = channels();
- if (pRate) *pRate = sampleRate();
- return BAD_VALUE;
- }
-
- if (pFormat) *pFormat = lFormat;
- if (pChannels) *pChannels = lChannels;
- if (pRate) *pRate = lRate;
-
- mAudioHardware = hw;
- mFd = fd;
- mDevice = devices;
- return NO_ERROR;
-}
-
-AudioStreamOutGeneric::~AudioStreamOutGeneric()
-{
-}
-
-ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes)
-{
- Mutex::Autolock _l(mLock);
- return ssize_t(::write(mFd, buffer, bytes));
-}
-
-status_t AudioStreamOutGeneric::standby()
-{
- // Implement: audio hardware to standby mode
- return NO_ERROR;
-}
-
-status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, SIZE, "AudioStreamOutGeneric::dump\n");
- result.append(buffer);
- snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tformat: %d\n", format());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice);
- result.append(buffer);
- snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware);
- result.append(buffer);
- snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t AudioStreamOutGeneric::setParameters(const String8& keyValuePairs)
-{
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 key = String8(AudioParameter::keyRouting);
- status_t status = NO_ERROR;
- int device;
- LOGV("setParameters() %s", keyValuePairs.string());
-
- if (param.getInt(key, device) == NO_ERROR) {
- mDevice = device;
- param.remove(key);
- }
-
- if (param.size()) {
- status = BAD_VALUE;
- }
- return status;
-}
-
-String8 AudioStreamOutGeneric::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- String8 value;
- String8 key = String8(AudioParameter::keyRouting);
-
- if (param.get(key, value) == NO_ERROR) {
- param.addInt(key, (int)mDevice);
- }
-
- LOGV("getParameters() %s", param.toString().string());
- return param.toString();
-}
-
-status_t AudioStreamOutGeneric::getRenderPosition(uint32_t *dspFrames)
-{
- return INVALID_OPERATION;
-}
-
-// ----------------------------------------------------------------------------
-
-// record functions
-status_t AudioStreamInGeneric::set(
- AudioHardwareGeneric *hw,
- int fd,
- uint32_t devices,
- int *pFormat,
- uint32_t *pChannels,
- uint32_t *pRate,
- AudioSystem::audio_in_acoustics acoustics)
-{
- if (pFormat == 0 || pChannels == 0 || pRate == 0) return BAD_VALUE;
- LOGV("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, *pFormat, *pChannels, *pRate);
- // check values
- if ((*pFormat != format()) ||
- (*pChannels != channels()) ||
- (*pRate != sampleRate())) {
- LOGE("Error opening input channel");
- *pFormat = format();
- *pChannels = channels();
- *pRate = sampleRate();
- return BAD_VALUE;
- }
-
- mAudioHardware = hw;
- mFd = fd;
- mDevice = devices;
- return NO_ERROR;
-}
-
-AudioStreamInGeneric::~AudioStreamInGeneric()
-{
-}
-
-ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes)
-{
- AutoMutex lock(mLock);
- if (mFd < 0) {
- LOGE("Attempt to read from unopened device");
- return NO_INIT;
- }
- return ::read(mFd, buffer, bytes);
-}
-
-status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, SIZE, "AudioStreamInGeneric::dump\n");
- result.append(buffer);
- snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tformat: %d\n", format());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice);
- result.append(buffer);
- snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware);
- result.append(buffer);
- snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t AudioStreamInGeneric::setParameters(const String8& keyValuePairs)
-{
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 key = String8(AudioParameter::keyRouting);
- status_t status = NO_ERROR;
- int device;
- LOGV("setParameters() %s", keyValuePairs.string());
-
- if (param.getInt(key, device) == NO_ERROR) {
- mDevice = device;
- param.remove(key);
- }
-
- if (param.size()) {
- status = BAD_VALUE;
- }
- return status;
-}
-
-String8 AudioStreamInGeneric::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- String8 value;
- String8 key = String8(AudioParameter::keyRouting);
-
- if (param.get(key, value) == NO_ERROR) {
- param.addInt(key, (int)mDevice);
- }
-
- LOGV("getParameters() %s", param.toString().string());
- return param.toString();
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/audioflinger/AudioHardwareGeneric.h b/services/audioflinger/AudioHardwareGeneric.h
deleted file mode 100644
index aa4e78d..0000000
--- a/services/audioflinger/AudioHardwareGeneric.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
-**
-** Copyright 2007, 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_AUDIO_HARDWARE_GENERIC_H
-#define ANDROID_AUDIO_HARDWARE_GENERIC_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/threads.h>
-
-#include <hardware_legacy/AudioHardwareBase.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class AudioHardwareGeneric;
-
-class AudioStreamOutGeneric : public AudioStreamOut {
-public:
- AudioStreamOutGeneric() : mAudioHardware(0), mFd(-1) {}
- virtual ~AudioStreamOutGeneric();
-
- virtual status_t set(
- AudioHardwareGeneric *hw,
- int mFd,
- uint32_t devices,
- int *pFormat,
- uint32_t *pChannels,
- uint32_t *pRate);
-
- virtual uint32_t sampleRate() const { return 44100; }
- virtual size_t bufferSize() const { return 4096; }
- virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
- virtual int format() const { return AudioSystem::PCM_16_BIT; }
- virtual uint32_t latency() const { return 20; }
- virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; }
- virtual ssize_t write(const void* buffer, size_t bytes);
- virtual status_t standby();
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
- virtual status_t getRenderPosition(uint32_t *dspFrames);
-
-private:
- AudioHardwareGeneric *mAudioHardware;
- Mutex mLock;
- int mFd;
- uint32_t mDevice;
-};
-
-class AudioStreamInGeneric : public AudioStreamIn {
-public:
- AudioStreamInGeneric() : mAudioHardware(0), mFd(-1) {}
- virtual ~AudioStreamInGeneric();
-
- virtual status_t set(
- AudioHardwareGeneric *hw,
- int mFd,
- uint32_t devices,
- int *pFormat,
- uint32_t *pChannels,
- uint32_t *pRate,
- AudioSystem::audio_in_acoustics acoustics);
-
- virtual uint32_t sampleRate() const { return 8000; }
- virtual size_t bufferSize() const { return 320; }
- virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; }
- virtual int format() const { return AudioSystem::PCM_16_BIT; }
- virtual status_t setGain(float gain) { return INVALID_OPERATION; }
- virtual ssize_t read(void* buffer, ssize_t bytes);
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t standby() { return NO_ERROR; }
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
- virtual unsigned int getInputFramesLost() const { return 0; }
-
-private:
- AudioHardwareGeneric *mAudioHardware;
- Mutex mLock;
- int mFd;
- uint32_t mDevice;
-};
-
-
-class AudioHardwareGeneric : public AudioHardwareBase
-{
-public:
- AudioHardwareGeneric();
- virtual ~AudioHardwareGeneric();
- virtual status_t initCheck();
- virtual status_t setVoiceVolume(float volume);
- virtual status_t setMasterVolume(float volume);
-
- // mic mute
- virtual status_t setMicMute(bool state);
- virtual status_t getMicMute(bool* state);
-
- // create I/O streams
- virtual AudioStreamOut* openOutputStream(
- uint32_t devices,
- int *format=0,
- uint32_t *channels=0,
- uint32_t *sampleRate=0,
- status_t *status=0);
- virtual void closeOutputStream(AudioStreamOut* out);
-
- virtual AudioStreamIn* openInputStream(
- uint32_t devices,
- int *format,
- uint32_t *channels,
- uint32_t *sampleRate,
- status_t *status,
- AudioSystem::audio_in_acoustics acoustics);
- virtual void closeInputStream(AudioStreamIn* in);
-
- void closeOutputStream(AudioStreamOutGeneric* out);
- void closeInputStream(AudioStreamInGeneric* in);
-protected:
- virtual status_t dump(int fd, const Vector<String16>& args);
-
-private:
- status_t dumpInternals(int fd, const Vector<String16>& args);
-
- Mutex mLock;
- AudioStreamOutGeneric *mOutput;
- AudioStreamInGeneric *mInput;
- int mFd;
- bool mMicMute;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_AUDIO_HARDWARE_GENERIC_H
diff --git a/services/audioflinger/AudioHardwareInterface.cpp b/services/audioflinger/AudioHardwareInterface.cpp
deleted file mode 100644
index f58e4c0..0000000
--- a/services/audioflinger/AudioHardwareInterface.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
-**
-** Copyright 2007, 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 <cutils/properties.h>
-#include <string.h>
-#include <unistd.h>
-//#define LOG_NDEBUG 0
-
-#define LOG_TAG "AudioHardwareInterface"
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#include "AudioHardwareStub.h"
-#include "AudioHardwareGeneric.h"
-#ifdef WITH_A2DP
-#include "A2dpAudioInterface.h"
-#endif
-
-#ifdef ENABLE_AUDIO_DUMP
-#include "AudioDumpInterface.h"
-#endif
-
-
-// change to 1 to log routing calls
-#define LOG_ROUTING_CALLS 1
-
-namespace android {
-
-#if LOG_ROUTING_CALLS
-static const char* routingModeStrings[] =
-{
- "OUT OF RANGE",
- "INVALID",
- "CURRENT",
- "NORMAL",
- "RINGTONE",
- "IN_CALL",
- "IN_COMMUNICATION"
-};
-
-static const char* routeNone = "NONE";
-
-static const char* displayMode(int mode)
-{
- if ((mode < AudioSystem::MODE_INVALID) || (mode >= AudioSystem::NUM_MODES))
- return routingModeStrings[0];
- return routingModeStrings[mode+3];
-}
-#endif
-
-// ----------------------------------------------------------------------------
-
-AudioHardwareInterface* AudioHardwareInterface::create()
-{
- /*
- * FIXME: This code needs to instantiate the correct audio device
- * interface. For now - we use compile-time switches.
- */
- AudioHardwareInterface* hw = 0;
- char value[PROPERTY_VALUE_MAX];
-
-#ifdef GENERIC_AUDIO
- hw = new AudioHardwareGeneric();
-#else
- // if running in emulation - use the emulator driver
- if (property_get("ro.kernel.qemu", value, 0)) {
- LOGD("Running in emulation - using generic audio driver");
- hw = new AudioHardwareGeneric();
- }
- else {
- LOGV("Creating Vendor Specific AudioHardware");
- hw = createAudioHardware();
- }
-#endif
- if (hw->initCheck() != NO_ERROR) {
- LOGW("Using stubbed audio hardware. No sound will be produced.");
- delete hw;
- hw = new AudioHardwareStub();
- }
-
-#ifdef WITH_A2DP
- hw = new A2dpAudioInterface(hw);
-#endif
-
-#ifdef ENABLE_AUDIO_DUMP
- // This code adds a record of buffers in a file to write calls made by AudioFlinger.
- // It replaces the current AudioHardwareInterface object by an intermediate one which
- // will record buffers in a file (after sending them to hardware) for testing purpose.
- // This feature is enabled by defining symbol ENABLE_AUDIO_DUMP.
- // The output file is set with setParameters("test_cmd_file_name=<name>"). Pause are not recorded in the file.
- LOGV("opening PCM dump interface");
- hw = new AudioDumpInterface(hw); // replace interface
-#endif
- return hw;
-}
-
-AudioStreamOut::~AudioStreamOut()
-{
-}
-
-AudioStreamIn::~AudioStreamIn() {}
-
-AudioHardwareBase::AudioHardwareBase()
-{
- mMode = 0;
-}
-
-status_t AudioHardwareBase::setMode(int mode)
-{
-#if LOG_ROUTING_CALLS
- LOGD("setMode(%s)", displayMode(mode));
-#endif
- if ((mode < 0) || (mode >= AudioSystem::NUM_MODES))
- return BAD_VALUE;
- if (mMode == mode)
- return ALREADY_EXISTS;
- mMode = mode;
- return NO_ERROR;
-}
-
-// default implementation
-status_t AudioHardwareBase::setParameters(const String8& keyValuePairs)
-{
- return NO_ERROR;
-}
-
-// default implementation
-String8 AudioHardwareBase::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- return param.toString();
-}
-
-// default implementation
-size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
-{
- if (sampleRate != 8000) {
- LOGW("getInputBufferSize bad sampling rate: %d", sampleRate);
- return 0;
- }
- if (format != AudioSystem::PCM_16_BIT) {
- LOGW("getInputBufferSize bad format: %d", format);
- return 0;
- }
- if (channelCount != 1) {
- LOGW("getInputBufferSize bad channel count: %d", channelCount);
- return 0;
- }
-
- return 320;
-}
-
-status_t AudioHardwareBase::dumpState(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, SIZE, "AudioHardwareBase::dumpState\n");
- result.append(buffer);
- snprintf(buffer, SIZE, "\tmMode: %d\n", mMode);
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- dump(fd, args); // Dump the state of the concrete child.
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/audioflinger/AudioHardwareStub.cpp b/services/audioflinger/AudioHardwareStub.cpp
deleted file mode 100644
index d481150..0000000
--- a/services/audioflinger/AudioHardwareStub.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/* //device/servers/AudioFlinger/AudioHardwareStub.cpp
-**
-** Copyright 2007, 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 <stdint.h>
-#include <sys/types.h>
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <utils/String8.h>
-
-#include "AudioHardwareStub.h"
-#include <media/AudioRecord.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-AudioHardwareStub::AudioHardwareStub() : mMicMute(false)
-{
-}
-
-AudioHardwareStub::~AudioHardwareStub()
-{
-}
-
-status_t AudioHardwareStub::initCheck()
-{
- return NO_ERROR;
-}
-
-AudioStreamOut* AudioHardwareStub::openOutputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
-{
- AudioStreamOutStub* out = new AudioStreamOutStub();
- status_t lStatus = out->set(format, channels, sampleRate);
- if (status) {
- *status = lStatus;
- }
- if (lStatus == NO_ERROR)
- return out;
- delete out;
- return 0;
-}
-
-void AudioHardwareStub::closeOutputStream(AudioStreamOut* out)
-{
- delete out;
-}
-
-AudioStreamIn* AudioHardwareStub::openInputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate,
- status_t *status, AudioSystem::audio_in_acoustics acoustics)
-{
- // check for valid input source
- if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
- return 0;
- }
-
- AudioStreamInStub* in = new AudioStreamInStub();
- status_t lStatus = in->set(format, channels, sampleRate, acoustics);
- if (status) {
- *status = lStatus;
- }
- if (lStatus == NO_ERROR)
- return in;
- delete in;
- return 0;
-}
-
-void AudioHardwareStub::closeInputStream(AudioStreamIn* in)
-{
- delete in;
-}
-
-status_t AudioHardwareStub::setVoiceVolume(float volume)
-{
- return NO_ERROR;
-}
-
-status_t AudioHardwareStub::setMasterVolume(float volume)
-{
- return NO_ERROR;
-}
-
-status_t AudioHardwareStub::dumpInternals(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- result.append("AudioHardwareStub::dumpInternals\n");
- snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false");
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t AudioHardwareStub::dump(int fd, const Vector<String16>& args)
-{
- dumpInternals(fd, args);
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-status_t AudioStreamOutStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate)
-{
- if (pFormat) *pFormat = format();
- if (pChannels) *pChannels = channels();
- if (pRate) *pRate = sampleRate();
-
- return NO_ERROR;
-}
-
-ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes)
-{
- // fake timing for audio output
- usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate());
- return bytes;
-}
-
-status_t AudioStreamOutStub::standby()
-{
- return NO_ERROR;
-}
-
-status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, SIZE, "AudioStreamOutStub::dump\n");
- snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
- snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
- snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
- snprintf(buffer, SIZE, "\tformat: %d\n", format());
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-String8 AudioStreamOutStub::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- return param.toString();
-}
-
-status_t AudioStreamOutStub::getRenderPosition(uint32_t *dspFrames)
-{
- return INVALID_OPERATION;
-}
-
-// ----------------------------------------------------------------------------
-
-status_t AudioStreamInStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate,
- AudioSystem::audio_in_acoustics acoustics)
-{
- return NO_ERROR;
-}
-
-ssize_t AudioStreamInStub::read(void* buffer, ssize_t bytes)
-{
- // fake timing for audio input
- usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate());
- memset(buffer, 0, bytes);
- return bytes;
-}
-
-status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, SIZE, "AudioStreamInStub::dump\n");
- result.append(buffer);
- snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tformat: %d\n", format());
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-String8 AudioStreamInStub::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- return param.toString();
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/audioflinger/AudioHardwareStub.h b/services/audioflinger/AudioHardwareStub.h
deleted file mode 100644
index 06a29de..0000000
--- a/services/audioflinger/AudioHardwareStub.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* //device/servers/AudioFlinger/AudioHardwareStub.h
-**
-** Copyright 2007, 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_AUDIO_HARDWARE_STUB_H
-#define ANDROID_AUDIO_HARDWARE_STUB_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <hardware_legacy/AudioHardwareBase.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class AudioStreamOutStub : public AudioStreamOut {
-public:
- virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate);
- virtual uint32_t sampleRate() const { return 44100; }
- virtual size_t bufferSize() const { return 4096; }
- virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
- virtual int format() const { return AudioSystem::PCM_16_BIT; }
- virtual uint32_t latency() const { return 0; }
- virtual status_t setVolume(float left, float right) { return NO_ERROR; }
- virtual ssize_t write(const void* buffer, size_t bytes);
- virtual status_t standby();
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;}
- virtual String8 getParameters(const String8& keys);
- virtual status_t getRenderPosition(uint32_t *dspFrames);
-};
-
-class AudioStreamInStub : public AudioStreamIn {
-public:
- virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics);
- virtual uint32_t sampleRate() const { return 8000; }
- virtual size_t bufferSize() const { return 320; }
- virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; }
- virtual int format() const { return AudioSystem::PCM_16_BIT; }
- virtual status_t setGain(float gain) { return NO_ERROR; }
- virtual ssize_t read(void* buffer, ssize_t bytes);
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t standby() { return NO_ERROR; }
- virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;}
- virtual String8 getParameters(const String8& keys);
- virtual unsigned int getInputFramesLost() const { return 0; }
-};
-
-class AudioHardwareStub : public AudioHardwareBase
-{
-public:
- AudioHardwareStub();
- virtual ~AudioHardwareStub();
- virtual status_t initCheck();
- virtual status_t setVoiceVolume(float volume);
- virtual status_t setMasterVolume(float volume);
-
- // mic mute
- virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; }
- virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO_ERROR; }
-
- // create I/O streams
- virtual AudioStreamOut* openOutputStream(
- uint32_t devices,
- int *format=0,
- uint32_t *channels=0,
- uint32_t *sampleRate=0,
- status_t *status=0);
- virtual void closeOutputStream(AudioStreamOut* out);
-
- virtual AudioStreamIn* openInputStream(
- uint32_t devices,
- int *format,
- uint32_t *channels,
- uint32_t *sampleRate,
- status_t *status,
- AudioSystem::audio_in_acoustics acoustics);
- virtual void closeInputStream(AudioStreamIn* in);
-
-protected:
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- bool mMicMute;
-private:
- status_t dumpInternals(int fd, const Vector<String16>& args);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_AUDIO_HARDWARE_STUB_H
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 8aaa325..50dcda7 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -220,6 +220,12 @@
return NO_ERROR;
}
}
+ if (name == RESET) {
+ track_t& track = mState.tracks[ mActiveTrack ];
+ track.resetResampler();
+ invalidateState(1<<mActiveTrack);
+ return NO_ERROR;
+ }
break;
case RAMP_VOLUME:
case VOLUME:
@@ -289,6 +295,13 @@
return resampler != 0;
}
+void AudioMixer::track_t::resetResampler()
+{
+ if (resampler != 0) {
+ resampler->reset();
+ }
+}
+
inline
void AudioMixer::track_t::adjustVolumeRamp(bool aux)
{
@@ -975,7 +988,6 @@
{
int32_t* const outTemp = state->outputTemp;
const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
- memset(outTemp, 0, size);
size_t numFrames = state->frameCount;
@@ -997,6 +1009,7 @@
}
e0 &= ~(e1);
int32_t *out = t1.mainBuffer;
+ memset(outTemp, 0, size);
while (e1) {
const int i = 31 - __builtin_clz(e1);
e1 &= ~(1<<i);
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index aee3e17..88408a7 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -67,6 +67,7 @@
AUX_BUFFER = 0x4003,
// for TARGET RESAMPLE
SAMPLE_RATE = 0x4100,
+ RESET = 0x4101,
// for TARGET VOLUME (8 channels max)
VOLUME0 = 0x4200,
VOLUME1 = 0x4201,
@@ -163,6 +164,7 @@
bool setResampler(uint32_t sampleRate, uint32_t devSampleRate);
bool doesResample() const;
+ void resetResampler();
void adjustVolumeRamp(bool aux);
};
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
deleted file mode 100644
index afa9acc..0000000
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ /dev/null
@@ -1,2195 +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_TAG "AudioPolicyManagerBase"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-#include <hardware_legacy/AudioPolicyManagerBase.h>
-#include <media/mediarecorder.h>
-
-namespace android {
-
-
-// ----------------------------------------------------------------------------
-// AudioPolicyInterface implementation
-// ----------------------------------------------------------------------------
-
-
-status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devices device,
- AudioSystem::device_connection_state state,
- const char *device_address)
-{
-
- LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address);
-
- // connect/disconnect only 1 device at a time
- if (AudioSystem::popCount(device) != 1) return BAD_VALUE;
-
- if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) {
- LOGE("setDeviceConnectionState() invalid address: %s", device_address);
- return BAD_VALUE;
- }
-
- // handle output devices
- if (AudioSystem::isOutputDevice(device)) {
-
-#ifndef WITH_A2DP
- if (AudioSystem::isA2dpDevice(device)) {
- LOGE("setDeviceConnectionState() invalid device: %x", device);
- return BAD_VALUE;
- }
-#endif
-
- switch (state)
- {
- // handle output device connection
- case AudioSystem::DEVICE_STATE_AVAILABLE:
- if (mAvailableOutputDevices & device) {
- LOGW("setDeviceConnectionState() device already connected: %x", device);
- return INVALID_OPERATION;
- }
- LOGV("setDeviceConnectionState() connecting device %x", device);
-
- // register new device as available
- mAvailableOutputDevices |= device;
-
-#ifdef WITH_A2DP
- // handle A2DP device connection
- if (AudioSystem::isA2dpDevice(device)) {
- status_t status = handleA2dpConnection(device, device_address);
- if (status != NO_ERROR) {
- mAvailableOutputDevices &= ~device;
- return status;
- }
- } else
-#endif
- {
- if (AudioSystem::isBluetoothScoDevice(device)) {
- LOGV("setDeviceConnectionState() BT SCO device, address %s", device_address);
- // keep track of SCO device address
- mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
- }
- }
- break;
- // handle output device disconnection
- case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
- if (!(mAvailableOutputDevices & device)) {
- LOGW("setDeviceConnectionState() device not connected: %x", device);
- return INVALID_OPERATION;
- }
-
-
- LOGV("setDeviceConnectionState() disconnecting device %x", device);
- // remove device from available output devices
- mAvailableOutputDevices &= ~device;
-
-#ifdef WITH_A2DP
- // handle A2DP device disconnection
- if (AudioSystem::isA2dpDevice(device)) {
- status_t status = handleA2dpDisconnection(device, device_address);
- if (status != NO_ERROR) {
- mAvailableOutputDevices |= device;
- return status;
- }
- } else
-#endif
- {
- if (AudioSystem::isBluetoothScoDevice(device)) {
- mScoDeviceAddress = "";
- }
- }
- } break;
-
- default:
- LOGE("setDeviceConnectionState() invalid state: %x", state);
- return BAD_VALUE;
- }
-
- // request routing change if necessary
- uint32_t newDevice = getNewDevice(mHardwareOutput, false);
-#ifdef WITH_A2DP
- checkOutputForAllStrategies();
- // A2DP outputs must be closed after checkOutputForAllStrategies() is executed
- if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
- closeA2dpOutputs();
- }
- checkA2dpSuspend();
-#endif
- updateDeviceForStrategy();
- setOutputDevice(mHardwareOutput, newDevice);
-
- if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET) {
- device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
- } else if (device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO ||
- device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
- device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
- device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
- } else {
- return NO_ERROR;
- }
- }
- // handle input devices
- if (AudioSystem::isInputDevice(device)) {
-
- switch (state)
- {
- // handle input device connection
- case AudioSystem::DEVICE_STATE_AVAILABLE: {
- if (mAvailableInputDevices & device) {
- LOGW("setDeviceConnectionState() device already connected: %d", device);
- return INVALID_OPERATION;
- }
- mAvailableInputDevices |= device;
- }
- break;
-
- // handle input device disconnection
- case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
- if (!(mAvailableInputDevices & device)) {
- LOGW("setDeviceConnectionState() device not connected: %d", device);
- return INVALID_OPERATION;
- }
- mAvailableInputDevices &= ~device;
- } break;
-
- default:
- LOGE("setDeviceConnectionState() invalid state: %x", state);
- return BAD_VALUE;
- }
-
- audio_io_handle_t activeInput = getActiveInput();
- if (activeInput != 0) {
- AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
- uint32_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
- if (newDevice != inputDesc->mDevice) {
- LOGV("setDeviceConnectionState() changing device from %x to %x for input %d",
- inputDesc->mDevice, newDevice, activeInput);
- inputDesc->mDevice = newDevice;
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
- mpClientInterface->setParameters(activeInput, param.toString());
- }
- }
-
- return NO_ERROR;
- }
-
- LOGW("setDeviceConnectionState() invalid device: %x", device);
- return BAD_VALUE;
-}
-
-AudioSystem::device_connection_state AudioPolicyManagerBase::getDeviceConnectionState(AudioSystem::audio_devices device,
- const char *device_address)
-{
- AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE;
- String8 address = String8(device_address);
- if (AudioSystem::isOutputDevice(device)) {
- if (device & mAvailableOutputDevices) {
-#ifdef WITH_A2DP
- if (AudioSystem::isA2dpDevice(device) &&
- address != "" && mA2dpDeviceAddress != address) {
- return state;
- }
-#endif
- if (AudioSystem::isBluetoothScoDevice(device) &&
- address != "" && mScoDeviceAddress != address) {
- return state;
- }
- state = AudioSystem::DEVICE_STATE_AVAILABLE;
- }
- } else if (AudioSystem::isInputDevice(device)) {
- if (device & mAvailableInputDevices) {
- state = AudioSystem::DEVICE_STATE_AVAILABLE;
- }
- }
-
- return state;
-}
-
-void AudioPolicyManagerBase::setPhoneState(int state)
-{
- LOGV("setPhoneState() state %d", state);
- uint32_t newDevice = 0;
- if (state < 0 || state >= AudioSystem::NUM_MODES) {
- LOGW("setPhoneState() invalid state %d", state);
- return;
- }
-
- if (state == mPhoneState ) {
- LOGW("setPhoneState() setting same state %d", state);
- return;
- }
-
- // if leaving call state, handle special case of active streams
- // pertaining to sonification strategy see handleIncallSonification()
- if (isInCall()) {
- LOGV("setPhoneState() in call state management: new state is %d", state);
- for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
- handleIncallSonification(stream, false, true);
- }
- }
-
- // store previous phone state for management of sonification strategy below
- int oldState = mPhoneState;
- mPhoneState = state;
- bool force = false;
-
- // are we entering or starting a call
- if (!isStateInCall(oldState) && isStateInCall(state)) {
- LOGV(" Entering call in setPhoneState()");
- // force routing command to audio hardware when starting a call
- // even if no device change is needed
- force = true;
- } else if (isStateInCall(oldState) && !isStateInCall(state)) {
- LOGV(" Exiting call in setPhoneState()");
- // force routing command to audio hardware when exiting a call
- // even if no device change is needed
- force = true;
- } else if (isStateInCall(state) && (state != oldState)) {
- LOGV(" Switching between telephony and VoIP in setPhoneState()");
- // force routing command to audio hardware when switching between telephony and VoIP
- // even if no device change is needed
- force = true;
- }
-
- // check for device and output changes triggered by new phone state
- newDevice = getNewDevice(mHardwareOutput, false);
-#ifdef WITH_A2DP
- checkOutputForAllStrategies();
- checkA2dpSuspend();
-#endif
- updateDeviceForStrategy();
-
- AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
-
- // force routing command to audio hardware when ending call
- // even if no device change is needed
- if (isStateInCall(oldState) && newDevice == 0) {
- newDevice = hwOutputDesc->device();
- }
-
- // when changing from ring tone to in call mode, mute the ringing tone
- // immediately and delay the route change to avoid sending the ring tone
- // tail into the earpiece or headset.
- int delayMs = 0;
- if (isStateInCall(state) && oldState == AudioSystem::MODE_RINGTONE) {
- // delay the device change command by twice the output latency to have some margin
- // and be sure that audio buffers not yet affected by the mute are out when
- // we actually apply the route change
- delayMs = hwOutputDesc->mLatency*2;
- setStreamMute(AudioSystem::RING, true, mHardwareOutput);
- }
-
- // change routing is necessary
- setOutputDevice(mHardwareOutput, newDevice, force, delayMs);
-
- // if entering in call state, handle special case of active streams
- // pertaining to sonification strategy see handleIncallSonification()
- if (isStateInCall(state)) {
- LOGV("setPhoneState() in call state management: new state is %d", state);
- // unmute the ringing tone after a sufficient delay if it was muted before
- // setting output device above
- if (oldState == AudioSystem::MODE_RINGTONE) {
- setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS);
- }
- for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
- handleIncallSonification(stream, true, true);
- }
- }
-
- // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
- if (state == AudioSystem::MODE_RINGTONE &&
- isStreamActive(AudioSystem::MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)) {
- mLimitRingtoneVolume = true;
- } else {
- mLimitRingtoneVolume = false;
- }
-}
-
-void AudioPolicyManagerBase::setRingerMode(uint32_t mode, uint32_t mask)
-{
- LOGV("setRingerMode() mode %x, mask %x", mode, mask);
-
- mRingerMode = mode;
-}
-
-void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
-{
- LOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState);
-
- bool forceVolumeReeval = false;
- switch(usage) {
- case AudioSystem::FOR_COMMUNICATION:
- if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO &&
- config != AudioSystem::FORCE_NONE) {
- LOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
- return;
- }
- mForceUse[usage] = config;
- break;
- case AudioSystem::FOR_MEDIA:
- if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP &&
- config != AudioSystem::FORCE_WIRED_ACCESSORY &&
- config != AudioSystem::FORCE_ANALOG_DOCK &&
- config != AudioSystem::FORCE_DIGITAL_DOCK && config != AudioSystem::FORCE_NONE) {
- LOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
- return;
- }
- mForceUse[usage] = config;
- break;
- case AudioSystem::FOR_RECORD:
- if (config != AudioSystem::FORCE_BT_SCO && config != AudioSystem::FORCE_WIRED_ACCESSORY &&
- config != AudioSystem::FORCE_NONE) {
- LOGW("setForceUse() invalid config %d for FOR_RECORD", config);
- return;
- }
- mForceUse[usage] = config;
- break;
- case AudioSystem::FOR_DOCK:
- if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK &&
- config != AudioSystem::FORCE_BT_DESK_DOCK &&
- config != AudioSystem::FORCE_WIRED_ACCESSORY &&
- config != AudioSystem::FORCE_ANALOG_DOCK &&
- config != AudioSystem::FORCE_DIGITAL_DOCK) {
- LOGW("setForceUse() invalid config %d for FOR_DOCK", config);
- }
- forceVolumeReeval = true;
- mForceUse[usage] = config;
- break;
- default:
- LOGW("setForceUse() invalid usage %d", usage);
- break;
- }
-
- // check for device and output changes triggered by new phone state
- uint32_t newDevice = getNewDevice(mHardwareOutput, false);
-#ifdef WITH_A2DP
- checkOutputForAllStrategies();
- checkA2dpSuspend();
-#endif
- updateDeviceForStrategy();
- setOutputDevice(mHardwareOutput, newDevice);
- if (forceVolumeReeval) {
- applyStreamVolumes(mHardwareOutput, newDevice);
- }
-
- audio_io_handle_t activeInput = getActiveInput();
- if (activeInput != 0) {
- AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
- newDevice = getDeviceForInputSource(inputDesc->mInputSource);
- if (newDevice != inputDesc->mDevice) {
- LOGV("setForceUse() changing device from %x to %x for input %d",
- inputDesc->mDevice, newDevice, activeInput);
- inputDesc->mDevice = newDevice;
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
- mpClientInterface->setParameters(activeInput, param.toString());
- }
- }
-
-}
-
-AudioSystem::forced_config AudioPolicyManagerBase::getForceUse(AudioSystem::force_use usage)
-{
- return mForceUse[usage];
-}
-
-void AudioPolicyManagerBase::setSystemProperty(const char* property, const char* value)
-{
- LOGV("setSystemProperty() property %s, value %s", property, value);
- if (strcmp(property, "ro.camera.sound.forced") == 0) {
- if (atoi(value)) {
- LOGV("ENFORCED_AUDIBLE cannot be muted");
- mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false;
- } else {
- LOGV("ENFORCED_AUDIBLE can be muted");
- mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true;
- }
- }
-}
-
-audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream,
- uint32_t samplingRate,
- uint32_t format,
- uint32_t channels,
- AudioSystem::output_flags flags)
-{
- audio_io_handle_t output = 0;
- uint32_t latency = 0;
- routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
- uint32_t device = getDeviceForStrategy(strategy);
- LOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags);
-
-#ifdef AUDIO_POLICY_TEST
- if (mCurOutput != 0) {
- LOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channels %x, mDirectOutput %d",
- mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput);
-
- if (mTestOutputs[mCurOutput] == 0) {
- LOGV("getOutput() opening test output");
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
- outputDesc->mDevice = mTestDevice;
- outputDesc->mSamplingRate = mTestSamplingRate;
- outputDesc->mFormat = mTestFormat;
- outputDesc->mChannels = mTestChannels;
- outputDesc->mLatency = mTestLatencyMs;
- outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0);
- outputDesc->mRefCount[stream] = 0;
- mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
- if (mTestOutputs[mCurOutput]) {
- AudioParameter outputCmd = AudioParameter();
- outputCmd.addInt(String8("set_id"),mCurOutput);
- mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString());
- addOutput(mTestOutputs[mCurOutput], outputDesc);
- }
- }
- return mTestOutputs[mCurOutput];
- }
-#endif //AUDIO_POLICY_TEST
-
- // open a direct output if required by specified parameters
- if (needsDirectOuput(stream, samplingRate, format, channels, flags, device)) {
-
- LOGV("getOutput() opening direct output device %x", device);
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
- outputDesc->mDevice = device;
- outputDesc->mSamplingRate = samplingRate;
- outputDesc->mFormat = format;
- outputDesc->mChannels = channels;
- outputDesc->mLatency = 0;
- outputDesc->mFlags = (AudioSystem::output_flags)(flags | AudioSystem::OUTPUT_FLAG_DIRECT);
- outputDesc->mRefCount[stream] = 0;
- outputDesc->mStopTime[stream] = 0;
- output = mpClientInterface->openOutput(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
-
- // only accept an output with the requeted parameters
- if (output == 0 ||
- (samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) ||
- (format != 0 && format != outputDesc->mFormat) ||
- (channels != 0 && channels != outputDesc->mChannels)) {
- LOGV("getOutput() failed opening direct output: samplingRate %d, format %d, channels %d",
- samplingRate, format, channels);
- if (output != 0) {
- mpClientInterface->closeOutput(output);
- }
- delete outputDesc;
- return 0;
- }
- addOutput(output, outputDesc);
- return output;
- }
-
- if (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO &&
- channels != AudioSystem::CHANNEL_OUT_STEREO) {
- return 0;
- }
- // open a non direct output
-
- // get which output is suitable for the specified stream. The actual routing change will happen
- // when startOutput() will be called
- uint32_t a2dpDevice = device & AudioSystem::DEVICE_OUT_ALL_A2DP;
- if (AudioSystem::popCount((AudioSystem::audio_devices)device) == 2) {
-#ifdef WITH_A2DP
- if (a2dpUsedForSonification() && a2dpDevice != 0) {
- // if playing on 2 devices among which one is A2DP, use duplicated output
- LOGV("getOutput() using duplicated output");
- LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device in multiple %x selected but A2DP output not opened", device);
- output = mDuplicatedOutput;
- } else
-#endif
- {
- // if playing on 2 devices among which none is A2DP, use hardware output
- output = mHardwareOutput;
- }
- LOGV("getOutput() using output %d for 2 devices %x", output, device);
- } else {
-#ifdef WITH_A2DP
- if (a2dpDevice != 0) {
- // if playing on A2DP device, use a2dp output
- LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device %x selected but A2DP output not opened", device);
- output = mA2dpOutput;
- } else
-#endif
- {
- // if playing on not A2DP device, use hardware output
- output = mHardwareOutput;
- }
- }
-
-
- LOGW_IF((output ==0), "getOutput() could not find output for stream %d, samplingRate %d, format %d, channels %x, flags %x",
- stream, samplingRate, format, channels, flags);
-
- return output;
-}
-
-status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
- int session)
-{
- LOGV("startOutput() output %d, stream %d, session %d", output, stream, session);
- ssize_t index = mOutputs.indexOfKey(output);
- if (index < 0) {
- LOGW("startOutput() unknow output %d", output);
- return BAD_VALUE;
- }
-
- AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
- routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
-
-#ifdef WITH_A2DP
- if (mA2dpOutput != 0 && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) {
- setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
- }
-#endif
-
- // incremenent usage count for this stream on the requested output:
- // NOTE that the usage count is the same for duplicated output and hardware output which is
- // necassary for a correct control of hardware output routing by startOutput() and stopOutput()
- outputDesc->changeRefCount(stream, 1);
-
- setOutputDevice(output, getNewDevice(output));
-
- // handle special case for sonification while in call
- if (isInCall()) {
- handleIncallSonification(stream, true, false);
- }
-
- // apply volume rules for current stream and device if necessary
- checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, outputDesc->device());
-
- return NO_ERROR;
-}
-
-status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
- int session)
-{
- LOGV("stopOutput() output %d, stream %d, session %d", output, stream, session);
- ssize_t index = mOutputs.indexOfKey(output);
- if (index < 0) {
- LOGW("stopOutput() unknow output %d", output);
- return BAD_VALUE;
- }
-
- AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
- routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
-
- // handle special case for sonification while in call
- if (isInCall()) {
- handleIncallSonification(stream, false, false);
- }
-
- if (outputDesc->mRefCount[stream] > 0) {
- // decrement usage count of this stream on the output
- outputDesc->changeRefCount(stream, -1);
- // store time at which the stream was stopped - see isStreamActive()
- outputDesc->mStopTime[stream] = systemTime();
-
- setOutputDevice(output, getNewDevice(output));
-
-#ifdef WITH_A2DP
- if (mA2dpOutput != 0 && !a2dpUsedForSonification() &&
- strategy == STRATEGY_SONIFICATION) {
- setStrategyMute(STRATEGY_MEDIA,
- false,
- mA2dpOutput,
- mOutputs.valueFor(mHardwareOutput)->mLatency*2);
- }
-#endif
- if (output != mHardwareOutput) {
- setOutputDevice(mHardwareOutput, getNewDevice(mHardwareOutput), true);
- }
- return NO_ERROR;
- } else {
- LOGW("stopOutput() refcount is already 0 for output %d", output);
- return INVALID_OPERATION;
- }
-}
-
-void AudioPolicyManagerBase::releaseOutput(audio_io_handle_t output)
-{
- LOGV("releaseOutput() %d", output);
- ssize_t index = mOutputs.indexOfKey(output);
- if (index < 0) {
- LOGW("releaseOutput() releasing unknown output %d", output);
- return;
- }
-
-#ifdef AUDIO_POLICY_TEST
- int testIndex = testOutputIndex(output);
- if (testIndex != 0) {
- AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
- if (outputDesc->refCount() == 0) {
- mpClientInterface->closeOutput(output);
- delete mOutputs.valueAt(index);
- mOutputs.removeItem(output);
- mTestOutputs[testIndex] = 0;
- }
- return;
- }
-#endif //AUDIO_POLICY_TEST
-
- if (mOutputs.valueAt(index)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) {
- mpClientInterface->closeOutput(output);
- delete mOutputs.valueAt(index);
- mOutputs.removeItem(output);
- }
-}
-
-audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource,
- uint32_t samplingRate,
- uint32_t format,
- uint32_t channels,
- AudioSystem::audio_in_acoustics acoustics)
-{
- audio_io_handle_t input = 0;
- uint32_t device = getDeviceForInputSource(inputSource);
-
- LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics);
-
- if (device == 0) {
- return 0;
- }
-
- // adapt channel selection to input source
- switch(inputSource) {
- case AUDIO_SOURCE_VOICE_UPLINK:
- channels = AudioSystem::CHANNEL_IN_VOICE_UPLINK;
- break;
- case AUDIO_SOURCE_VOICE_DOWNLINK:
- channels = AudioSystem::CHANNEL_IN_VOICE_DNLINK;
- break;
- case AUDIO_SOURCE_VOICE_CALL:
- channels = (AudioSystem::CHANNEL_IN_VOICE_UPLINK | AudioSystem::CHANNEL_IN_VOICE_DNLINK);
- break;
- default:
- break;
- }
-
- AudioInputDescriptor *inputDesc = new AudioInputDescriptor();
-
- inputDesc->mInputSource = inputSource;
- inputDesc->mDevice = device;
- inputDesc->mSamplingRate = samplingRate;
- inputDesc->mFormat = format;
- inputDesc->mChannels = channels;
- inputDesc->mAcoustics = acoustics;
- inputDesc->mRefCount = 0;
- input = mpClientInterface->openInput(&inputDesc->mDevice,
- &inputDesc->mSamplingRate,
- &inputDesc->mFormat,
- &inputDesc->mChannels,
- inputDesc->mAcoustics);
-
- // only accept input with the exact requested set of parameters
- if (input == 0 ||
- (samplingRate != inputDesc->mSamplingRate) ||
- (format != inputDesc->mFormat) ||
- (channels != inputDesc->mChannels)) {
- LOGV("getInput() failed opening input: samplingRate %d, format %d, channels %d",
- samplingRate, format, channels);
- if (input != 0) {
- mpClientInterface->closeInput(input);
- }
- delete inputDesc;
- return 0;
- }
- mInputs.add(input, inputDesc);
- return input;
-}
-
-status_t AudioPolicyManagerBase::startInput(audio_io_handle_t input)
-{
- LOGV("startInput() input %d", input);
- ssize_t index = mInputs.indexOfKey(input);
- if (index < 0) {
- LOGW("startInput() unknow input %d", input);
- return BAD_VALUE;
- }
- AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
-
-#ifdef AUDIO_POLICY_TEST
- if (mTestInput == 0)
-#endif //AUDIO_POLICY_TEST
- {
- // refuse 2 active AudioRecord clients at the same time
- if (getActiveInput() != 0) {
- LOGW("startInput() input %d failed: other input already started", input);
- return INVALID_OPERATION;
- }
- }
-
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice);
-
- param.addInt(String8(AudioParameter::keyInputSource), (int)inputDesc->mInputSource);
- LOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource);
-
- mpClientInterface->setParameters(input, param.toString());
-
- inputDesc->mRefCount = 1;
- return NO_ERROR;
-}
-
-status_t AudioPolicyManagerBase::stopInput(audio_io_handle_t input)
-{
- LOGV("stopInput() input %d", input);
- ssize_t index = mInputs.indexOfKey(input);
- if (index < 0) {
- LOGW("stopInput() unknow input %d", input);
- return BAD_VALUE;
- }
- AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
-
- if (inputDesc->mRefCount == 0) {
- LOGW("stopInput() input %d already stopped", input);
- return INVALID_OPERATION;
- } else {
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), 0);
- mpClientInterface->setParameters(input, param.toString());
- inputDesc->mRefCount = 0;
- return NO_ERROR;
- }
-}
-
-void AudioPolicyManagerBase::releaseInput(audio_io_handle_t input)
-{
- LOGV("releaseInput() %d", input);
- ssize_t index = mInputs.indexOfKey(input);
- if (index < 0) {
- LOGW("releaseInput() releasing unknown input %d", input);
- return;
- }
- mpClientInterface->closeInput(input);
- delete mInputs.valueAt(index);
- mInputs.removeItem(input);
- LOGV("releaseInput() exit");
-}
-
-void AudioPolicyManagerBase::initStreamVolume(AudioSystem::stream_type stream,
- int indexMin,
- int indexMax)
-{
- LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
- if (indexMin < 0 || indexMin >= indexMax) {
- LOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax);
- return;
- }
- mStreams[stream].mIndexMin = indexMin;
- mStreams[stream].mIndexMax = indexMax;
-}
-
-status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
-{
-
- if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
- return BAD_VALUE;
- }
-
- // Force max volume if stream cannot be muted
- if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax;
-
- LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index);
- mStreams[stream].mIndexCur = index;
-
- // compute and apply stream volume on all outputs according to connected device
- status_t status = NO_ERROR;
- for (size_t i = 0; i < mOutputs.size(); i++) {
- status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), mOutputs.valueAt(i)->device());
- if (volStatus != NO_ERROR) {
- status = volStatus;
- }
- }
- return status;
-}
-
-status_t AudioPolicyManagerBase::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
-{
- if (index == 0) {
- return BAD_VALUE;
- }
- LOGV("getStreamVolumeIndex() stream %d", stream);
- *index = mStreams[stream].mIndexCur;
- return NO_ERROR;
-}
-
-audio_io_handle_t AudioPolicyManagerBase::getOutputForEffect(effect_descriptor_t *desc)
-{
- LOGV("getOutputForEffect()");
- // apply simple rule where global effects are attached to the same output as MUSIC streams
- return getOutput(AudioSystem::MUSIC);
-}
-
-status_t AudioPolicyManagerBase::registerEffect(effect_descriptor_t *desc,
- audio_io_handle_t output,
- uint32_t strategy,
- int session,
- int id)
-{
- ssize_t index = mOutputs.indexOfKey(output);
- if (index < 0) {
- LOGW("registerEffect() unknown output %d", output);
- return INVALID_OPERATION;
- }
-
- if (mTotalEffectsCpuLoad + desc->cpuLoad > getMaxEffectsCpuLoad()) {
- LOGW("registerEffect() CPU Load limit exceeded for Fx %s, CPU %f MIPS",
- desc->name, (float)desc->cpuLoad/10);
- return INVALID_OPERATION;
- }
- if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) {
- LOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB",
- desc->name, desc->memoryUsage);
- return INVALID_OPERATION;
- }
- mTotalEffectsCpuLoad += desc->cpuLoad;
- mTotalEffectsMemory += desc->memoryUsage;
- LOGV("registerEffect() effect %s, output %d, strategy %d session %d id %d",
- desc->name, output, strategy, session, id);
-
- LOGV("registerEffect() CPU %d, memory %d", desc->cpuLoad, desc->memoryUsage);
- LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
-
- EffectDescriptor *pDesc = new EffectDescriptor();
- memcpy (&pDesc->mDesc, desc, sizeof(effect_descriptor_t));
- pDesc->mOutput = output;
- pDesc->mStrategy = (routing_strategy)strategy;
- pDesc->mSession = session;
- mEffects.add(id, pDesc);
-
- return NO_ERROR;
-}
-
-status_t AudioPolicyManagerBase::unregisterEffect(int id)
-{
- ssize_t index = mEffects.indexOfKey(id);
- if (index < 0) {
- LOGW("unregisterEffect() unknown effect ID %d", id);
- return INVALID_OPERATION;
- }
-
- EffectDescriptor *pDesc = mEffects.valueAt(index);
-
- if (mTotalEffectsCpuLoad < pDesc->mDesc.cpuLoad) {
- LOGW("unregisterEffect() CPU load %d too high for total %d",
- pDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad);
- pDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad;
- }
- mTotalEffectsCpuLoad -= pDesc->mDesc.cpuLoad;
- if (mTotalEffectsMemory < pDesc->mDesc.memoryUsage) {
- LOGW("unregisterEffect() memory %d too big for total %d",
- pDesc->mDesc.memoryUsage, mTotalEffectsMemory);
- pDesc->mDesc.memoryUsage = mTotalEffectsMemory;
- }
- mTotalEffectsMemory -= pDesc->mDesc.memoryUsage;
- LOGV("unregisterEffect() effect %s, ID %d, CPU %d, memory %d",
- pDesc->mDesc.name, id, pDesc->mDesc.cpuLoad, pDesc->mDesc.memoryUsage);
- LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
-
- mEffects.removeItem(id);
- delete pDesc;
-
- return NO_ERROR;
-}
-
-bool AudioPolicyManagerBase::isStreamActive(int stream, uint32_t inPastMs) const
-{
- nsecs_t sysTime = systemTime();
- for (size_t i = 0; i < mOutputs.size(); i++) {
- if (mOutputs.valueAt(i)->mRefCount[stream] != 0 ||
- ns2ms(sysTime - mOutputs.valueAt(i)->mStopTime[stream]) < inPastMs) {
- return true;
- }
- }
- return false;
-}
-
-
-status_t AudioPolicyManagerBase::dump(int fd)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
- result.append(buffer);
- snprintf(buffer, SIZE, " Hardware Output: %d\n", mHardwareOutput);
- result.append(buffer);
-#ifdef WITH_A2DP
- snprintf(buffer, SIZE, " A2DP Output: %d\n", mA2dpOutput);
- result.append(buffer);
- snprintf(buffer, SIZE, " Duplicated Output: %d\n", mDuplicatedOutput);
- result.append(buffer);
- snprintf(buffer, SIZE, " A2DP device address: %s\n", mA2dpDeviceAddress.string());
- result.append(buffer);
-#endif
- snprintf(buffer, SIZE, " SCO device address: %s\n", mScoDeviceAddress.string());
- result.append(buffer);
- snprintf(buffer, SIZE, " Output devices: %08x\n", mAvailableOutputDevices);
- result.append(buffer);
- snprintf(buffer, SIZE, " Input devices: %08x\n", mAvailableInputDevices);
- result.append(buffer);
- snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState);
- result.append(buffer);
- snprintf(buffer, SIZE, " Ringer mode: %d\n", mRingerMode);
- result.append(buffer);
- snprintf(buffer, SIZE, " Force use for communications %d\n", mForceUse[AudioSystem::FOR_COMMUNICATION]);
- result.append(buffer);
- snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AudioSystem::FOR_MEDIA]);
- result.append(buffer);
- snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AudioSystem::FOR_RECORD]);
- result.append(buffer);
- snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AudioSystem::FOR_DOCK]);
- result.append(buffer);
- write(fd, result.string(), result.size());
-
- snprintf(buffer, SIZE, "\nOutputs dump:\n");
- write(fd, buffer, strlen(buffer));
- for (size_t i = 0; i < mOutputs.size(); i++) {
- snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i));
- write(fd, buffer, strlen(buffer));
- mOutputs.valueAt(i)->dump(fd);
- }
-
- snprintf(buffer, SIZE, "\nInputs dump:\n");
- write(fd, buffer, strlen(buffer));
- for (size_t i = 0; i < mInputs.size(); i++) {
- snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i));
- write(fd, buffer, strlen(buffer));
- mInputs.valueAt(i)->dump(fd);
- }
-
- snprintf(buffer, SIZE, "\nStreams dump:\n");
- write(fd, buffer, strlen(buffer));
- snprintf(buffer, SIZE, " Stream Index Min Index Max Index Cur Can be muted\n");
- write(fd, buffer, strlen(buffer));
- for (size_t i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
- snprintf(buffer, SIZE, " %02d", i);
- mStreams[i].dump(buffer + 3, SIZE);
- write(fd, buffer, strlen(buffer));
- }
-
- snprintf(buffer, SIZE, "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB\n",
- (float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory);
- write(fd, buffer, strlen(buffer));
-
- snprintf(buffer, SIZE, "Registered effects:\n");
- write(fd, buffer, strlen(buffer));
- for (size_t i = 0; i < mEffects.size(); i++) {
- snprintf(buffer, SIZE, "- Effect %d dump:\n", mEffects.keyAt(i));
- write(fd, buffer, strlen(buffer));
- mEffects.valueAt(i)->dump(fd);
- }
-
-
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-// AudioPolicyManagerBase
-// ----------------------------------------------------------------------------
-
-AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)
- :
-#ifdef AUDIO_POLICY_TEST
- Thread(false),
-#endif //AUDIO_POLICY_TEST
- mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0),
- mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
- mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
- mA2dpSuspended(false)
-{
- mpClientInterface = clientInterface;
-
- for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
- mForceUse[i] = AudioSystem::FORCE_NONE;
- }
-
- // devices available by default are speaker, ear piece and microphone
- mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
- AudioSystem::DEVICE_OUT_SPEAKER;
- mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
-
-#ifdef WITH_A2DP
- mA2dpOutput = 0;
- mDuplicatedOutput = 0;
- mA2dpDeviceAddress = String8("");
-#endif
- mScoDeviceAddress = String8("");
-
- // open hardware output
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
- outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
- mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
-
- if (mHardwareOutput == 0) {
- LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d",
- outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
- } else {
- addOutput(mHardwareOutput, outputDesc);
- setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
- //TODO: configure audio effect output stage here
- }
-
- updateDeviceForStrategy();
-#ifdef AUDIO_POLICY_TEST
- if (mHardwareOutput != 0) {
- AudioParameter outputCmd = AudioParameter();
- outputCmd.addInt(String8("set_id"), 0);
- mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
-
- mTestDevice = AudioSystem::DEVICE_OUT_SPEAKER;
- mTestSamplingRate = 44100;
- mTestFormat = AudioSystem::PCM_16_BIT;
- mTestChannels = AudioSystem::CHANNEL_OUT_STEREO;
- mTestLatencyMs = 0;
- mCurOutput = 0;
- mDirectOutput = false;
- for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
- mTestOutputs[i] = 0;
- }
-
- const size_t SIZE = 256;
- char buffer[SIZE];
- snprintf(buffer, SIZE, "AudioPolicyManagerTest");
- run(buffer, ANDROID_PRIORITY_AUDIO);
- }
-#endif //AUDIO_POLICY_TEST
-}
-
-AudioPolicyManagerBase::~AudioPolicyManagerBase()
-{
-#ifdef AUDIO_POLICY_TEST
- exit();
-#endif //AUDIO_POLICY_TEST
- for (size_t i = 0; i < mOutputs.size(); i++) {
- mpClientInterface->closeOutput(mOutputs.keyAt(i));
- delete mOutputs.valueAt(i);
- }
- mOutputs.clear();
- for (size_t i = 0; i < mInputs.size(); i++) {
- mpClientInterface->closeInput(mInputs.keyAt(i));
- delete mInputs.valueAt(i);
- }
- mInputs.clear();
-}
-
-status_t AudioPolicyManagerBase::initCheck()
-{
- return (mHardwareOutput == 0) ? NO_INIT : NO_ERROR;
-}
-
-#ifdef AUDIO_POLICY_TEST
-bool AudioPolicyManagerBase::threadLoop()
-{
- LOGV("entering threadLoop()");
- while (!exitPending())
- {
- String8 command;
- int valueInt;
- String8 value;
-
- Mutex::Autolock _l(mLock);
- mWaitWorkCV.waitRelative(mLock, milliseconds(50));
-
- command = mpClientInterface->getParameters(0, String8("test_cmd_policy"));
- AudioParameter param = AudioParameter(command);
-
- if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR &&
- valueInt != 0) {
- LOGV("Test command %s received", command.string());
- String8 target;
- if (param.get(String8("target"), target) != NO_ERROR) {
- target = "Manager";
- }
- if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_output"));
- mCurOutput = valueInt;
- }
- if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_direct"));
- if (value == "false") {
- mDirectOutput = false;
- } else if (value == "true") {
- mDirectOutput = true;
- }
- }
- if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_input"));
- mTestInput = valueInt;
- }
-
- if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_format"));
- int format = AudioSystem::INVALID_FORMAT;
- if (value == "PCM 16 bits") {
- format = AudioSystem::PCM_16_BIT;
- } else if (value == "PCM 8 bits") {
- format = AudioSystem::PCM_8_BIT;
- } else if (value == "Compressed MP3") {
- format = AudioSystem::MP3;
- }
- if (format != AudioSystem::INVALID_FORMAT) {
- if (target == "Manager") {
- mTestFormat = format;
- } else if (mTestOutputs[mCurOutput] != 0) {
- AudioParameter outputParam = AudioParameter();
- outputParam.addInt(String8("format"), format);
- mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
- }
- }
- }
- if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_channels"));
- int channels = 0;
-
- if (value == "Channels Stereo") {
- channels = AudioSystem::CHANNEL_OUT_STEREO;
- } else if (value == "Channels Mono") {
- channels = AudioSystem::CHANNEL_OUT_MONO;
- }
- if (channels != 0) {
- if (target == "Manager") {
- mTestChannels = channels;
- } else if (mTestOutputs[mCurOutput] != 0) {
- AudioParameter outputParam = AudioParameter();
- outputParam.addInt(String8("channels"), channels);
- mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
- }
- }
- }
- if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_sampleRate"));
- if (valueInt >= 0 && valueInt <= 96000) {
- int samplingRate = valueInt;
- if (target == "Manager") {
- mTestSamplingRate = samplingRate;
- } else if (mTestOutputs[mCurOutput] != 0) {
- AudioParameter outputParam = AudioParameter();
- outputParam.addInt(String8("sampling_rate"), samplingRate);
- mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
- }
- }
- }
-
- if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_reopen"));
-
- mpClientInterface->closeOutput(mHardwareOutput);
- delete mOutputs.valueFor(mHardwareOutput);
- mOutputs.removeItem(mHardwareOutput);
-
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
- outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
- mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
- if (mHardwareOutput == 0) {
- LOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d",
- outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
- } else {
- AudioParameter outputCmd = AudioParameter();
- outputCmd.addInt(String8("set_id"), 0);
- mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
- addOutput(mHardwareOutput, outputDesc);
- }
- }
-
-
- mpClientInterface->setParameters(0, String8("test_cmd_policy="));
- }
- }
- return false;
-}
-
-void AudioPolicyManagerBase::exit()
-{
- {
- AutoMutex _l(mLock);
- requestExit();
- mWaitWorkCV.signal();
- }
- requestExitAndWait();
-}
-
-int AudioPolicyManagerBase::testOutputIndex(audio_io_handle_t output)
-{
- for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
- if (output == mTestOutputs[i]) return i;
- }
- return 0;
-}
-#endif //AUDIO_POLICY_TEST
-
-// ---
-
-void AudioPolicyManagerBase::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc)
-{
- outputDesc->mId = id;
- mOutputs.add(id, outputDesc);
-}
-
-
-#ifdef WITH_A2DP
-status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices device,
- const char *device_address)
-{
- // when an A2DP device is connected, open an A2DP and a duplicated output
- LOGV("opening A2DP output for device %s", device_address);
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
- outputDesc->mDevice = device;
- mA2dpOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
- if (mA2dpOutput) {
- // add A2DP output descriptor
- addOutput(mA2dpOutput, outputDesc);
-
- //TODO: configure audio effect output stage here
-
- // set initial stream volume for A2DP device
- applyStreamVolumes(mA2dpOutput, device);
- if (a2dpUsedForSonification()) {
- mDuplicatedOutput = mpClientInterface->openDuplicateOutput(mA2dpOutput, mHardwareOutput);
- }
- if (mDuplicatedOutput != 0 ||
- !a2dpUsedForSonification()) {
- // If both A2DP and duplicated outputs are open, send device address to A2DP hardware
- // interface
- AudioParameter param;
- param.add(String8("a2dp_sink_address"), String8(device_address));
- mpClientInterface->setParameters(mA2dpOutput, param.toString());
- mA2dpDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
-
- if (a2dpUsedForSonification()) {
- // add duplicated output descriptor
- AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor();
- dupOutputDesc->mOutput1 = mOutputs.valueFor(mHardwareOutput);
- dupOutputDesc->mOutput2 = mOutputs.valueFor(mA2dpOutput);
- dupOutputDesc->mSamplingRate = outputDesc->mSamplingRate;
- dupOutputDesc->mFormat = outputDesc->mFormat;
- dupOutputDesc->mChannels = outputDesc->mChannels;
- dupOutputDesc->mLatency = outputDesc->mLatency;
- addOutput(mDuplicatedOutput, dupOutputDesc);
- applyStreamVolumes(mDuplicatedOutput, device);
- }
- } else {
- LOGW("getOutput() could not open duplicated output for %d and %d",
- mHardwareOutput, mA2dpOutput);
- mpClientInterface->closeOutput(mA2dpOutput);
- mOutputs.removeItem(mA2dpOutput);
- mA2dpOutput = 0;
- delete outputDesc;
- return NO_INIT;
- }
- } else {
- LOGW("setDeviceConnectionState() could not open A2DP output for device %x", device);
- delete outputDesc;
- return NO_INIT;
- }
- AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
-
- if (!a2dpUsedForSonification()) {
- // mute music on A2DP output if a notification or ringtone is playing
- uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION);
- for (uint32_t i = 0; i < refCount; i++) {
- setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
- }
- }
-
- mA2dpSuspended = false;
-
- return NO_ERROR;
-}
-
-status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devices device,
- const char *device_address)
-{
- if (mA2dpOutput == 0) {
- LOGW("setDeviceConnectionState() disconnecting A2DP and no A2DP output!");
- return INVALID_OPERATION;
- }
-
- if (mA2dpDeviceAddress != device_address) {
- LOGW("setDeviceConnectionState() disconnecting unknow A2DP sink address %s", device_address);
- return INVALID_OPERATION;
- }
-
- // mute media strategy to avoid outputting sound on hardware output while music stream
- // is switched from A2DP output and before music is paused by music application
- setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput);
- setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, MUTE_TIME_MS);
-
- if (!a2dpUsedForSonification()) {
- // unmute music on A2DP output if a notification or ringtone is playing
- uint32_t refCount = mOutputs.valueFor(mHardwareOutput)->strategyRefCount(STRATEGY_SONIFICATION);
- for (uint32_t i = 0; i < refCount; i++) {
- setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput);
- }
- }
- mA2dpDeviceAddress = "";
- mA2dpSuspended = false;
- return NO_ERROR;
-}
-
-void AudioPolicyManagerBase::closeA2dpOutputs()
-{
-
- LOGV("setDeviceConnectionState() closing A2DP and duplicated output!");
-
- if (mDuplicatedOutput != 0) {
- AudioOutputDescriptor *dupOutputDesc = mOutputs.valueFor(mDuplicatedOutput);
- AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
- // As all active tracks on duplicated output will be deleted,
- // and as they were also referenced on hardware output, the reference
- // count for their stream type must be adjusted accordingly on
- // hardware output.
- for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
- int refCount = dupOutputDesc->mRefCount[i];
- hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
- }
-
- mpClientInterface->closeOutput(mDuplicatedOutput);
- delete mOutputs.valueFor(mDuplicatedOutput);
- mOutputs.removeItem(mDuplicatedOutput);
- mDuplicatedOutput = 0;
- }
- if (mA2dpOutput != 0) {
- AudioParameter param;
- param.add(String8("closing"), String8("true"));
- mpClientInterface->setParameters(mA2dpOutput, param.toString());
-
- mpClientInterface->closeOutput(mA2dpOutput);
- delete mOutputs.valueFor(mA2dpOutput);
- mOutputs.removeItem(mA2dpOutput);
- mA2dpOutput = 0;
- }
-}
-
-void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy)
-{
- uint32_t prevDevice = getDeviceForStrategy(strategy);
- uint32_t curDevice = getDeviceForStrategy(strategy, false);
- bool a2dpWasUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(prevDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
- bool a2dpIsUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(curDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
- audio_io_handle_t srcOutput = 0;
- audio_io_handle_t dstOutput = 0;
-
- if (a2dpWasUsed && !a2dpIsUsed) {
- bool dupUsed = a2dpUsedForSonification() && a2dpWasUsed && (AudioSystem::popCount(prevDevice) == 2);
- dstOutput = mHardwareOutput;
- if (dupUsed) {
- LOGV("checkOutputForStrategy() moving strategy %d from duplicated", strategy);
- srcOutput = mDuplicatedOutput;
- } else {
- LOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy);
- srcOutput = mA2dpOutput;
- }
- }
- if (a2dpIsUsed && !a2dpWasUsed) {
- bool dupUsed = a2dpUsedForSonification() && a2dpIsUsed && (AudioSystem::popCount(curDevice) == 2);
- srcOutput = mHardwareOutput;
- if (dupUsed) {
- LOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy);
- dstOutput = mDuplicatedOutput;
- } else {
- LOGV("checkOutputForStrategy() moving strategy %d to a2dp", strategy);
- dstOutput = mA2dpOutput;
- }
- }
-
- if (srcOutput != 0 && dstOutput != 0) {
- // Move effects associated to this strategy from previous output to new output
- for (size_t i = 0; i < mEffects.size(); i++) {
- EffectDescriptor *desc = mEffects.valueAt(i);
- if (desc->mSession != AudioSystem::SESSION_OUTPUT_STAGE &&
- desc->mStrategy == strategy &&
- desc->mOutput == srcOutput) {
- LOGV("checkOutputForStrategy() moving effect %d to output %d", mEffects.keyAt(i), dstOutput);
- mpClientInterface->moveEffects(desc->mSession, srcOutput, dstOutput);
- desc->mOutput = dstOutput;
- }
- }
- // Move tracks associated to this strategy from previous output to new output
- for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
- if (getStrategy((AudioSystem::stream_type)i) == strategy) {
- mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, dstOutput);
- }
- }
- }
-}
-
-void AudioPolicyManagerBase::checkOutputForAllStrategies()
-{
- checkOutputForStrategy(STRATEGY_PHONE);
- checkOutputForStrategy(STRATEGY_SONIFICATION);
- checkOutputForStrategy(STRATEGY_MEDIA);
- checkOutputForStrategy(STRATEGY_DTMF);
-}
-
-void AudioPolicyManagerBase::checkA2dpSuspend()
-{
- // suspend A2DP output if:
- // (NOT already suspended) &&
- // ((SCO device is connected &&
- // (forced usage for communication || for record is SCO))) ||
- // (phone state is ringing || in call)
- //
- // restore A2DP output if:
- // (Already suspended) &&
- // ((SCO device is NOT connected ||
- // (forced usage NOT for communication && NOT for record is SCO))) &&
- // (phone state is NOT ringing && NOT in call)
- //
- if (mA2dpOutput == 0) {
- return;
- }
-
- if (mA2dpSuspended) {
- if (((mScoDeviceAddress == "") ||
- ((mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO) &&
- (mForceUse[AudioSystem::FOR_RECORD] != AudioSystem::FORCE_BT_SCO))) &&
- ((mPhoneState != AudioSystem::MODE_IN_CALL) &&
- (mPhoneState != AudioSystem::MODE_RINGTONE))) {
-
- mpClientInterface->restoreOutput(mA2dpOutput);
- mA2dpSuspended = false;
- }
- } else {
- if (((mScoDeviceAddress != "") &&
- ((mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
- (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO))) ||
- ((mPhoneState == AudioSystem::MODE_IN_CALL) ||
- (mPhoneState == AudioSystem::MODE_RINGTONE))) {
-
- mpClientInterface->suspendOutput(mA2dpOutput);
- mA2dpSuspended = true;
- }
- }
-}
-
-
-#endif
-
-uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
-{
- uint32_t device = 0;
-
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
- // check the following by order of priority to request a routing change if necessary:
- // 1: we are in call or the strategy phone is active on the hardware output:
- // use device for strategy phone
- // 2: the strategy sonification is active on the hardware output:
- // use device for strategy sonification
- // 3: the strategy media is active on the hardware output:
- // use device for strategy media
- // 4: the strategy DTMF is active on the hardware output:
- // use device for strategy DTMF
- if (isInCall() ||
- outputDesc->isUsedByStrategy(STRATEGY_PHONE)) {
- device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
- } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) {
- device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
- } else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
- device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache);
- } else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) {
- device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
- }
-
- LOGV("getNewDevice() selected device %x", device);
- return device;
-}
-
-uint32_t AudioPolicyManagerBase::getStrategyForStream(AudioSystem::stream_type stream) {
- return (uint32_t)getStrategy(stream);
-}
-
-AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(
- AudioSystem::stream_type stream) {
- // stream to strategy mapping
- switch (stream) {
- case AudioSystem::VOICE_CALL:
- case AudioSystem::BLUETOOTH_SCO:
- return STRATEGY_PHONE;
- case AudioSystem::RING:
- case AudioSystem::NOTIFICATION:
- case AudioSystem::ALARM:
- case AudioSystem::ENFORCED_AUDIBLE:
- return STRATEGY_SONIFICATION;
- case AudioSystem::DTMF:
- return STRATEGY_DTMF;
- default:
- LOGE("unknown stream type");
- case AudioSystem::SYSTEM:
- // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
- // while key clicks are played produces a poor result
- case AudioSystem::TTS:
- case AudioSystem::MUSIC:
- return STRATEGY_MEDIA;
- }
-}
-
-uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
-{
- uint32_t device = 0;
-
- if (fromCache) {
- LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);
- return mDeviceForStrategy[strategy];
- }
-
- switch (strategy) {
- case STRATEGY_DTMF:
- if (!isInCall()) {
- // when off call, DTMF strategy follows the same rules as MEDIA strategy
- device = getDeviceForStrategy(STRATEGY_MEDIA, false);
- break;
- }
- // when in call, DTMF and PHONE strategies follow the same rules
- // FALL THROUGH
-
- case STRATEGY_PHONE:
- // for phone strategy, we first consider the forced use and then the available devices by order
- // of priority
- switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {
- case AudioSystem::FORCE_BT_SCO:
- if (!isInCall() || strategy != STRATEGY_DTMF) {
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
- if (device) break;
- }
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;
- if (device) break;
- // if SCO device is requested but no SCO device is available, fall back to default case
- // FALL THROUGH
-
- default: // FORCE_NONE
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
- if (device) break;
-#ifdef WITH_A2DP
- // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
- if (!isInCall()) {
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
- if (device) break;
- }
-#endif
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;
- if (device == 0) {
- LOGE("getDeviceForStrategy() earpiece device not found");
- }
- break;
-
- case AudioSystem::FORCE_SPEAKER:
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
- if (device) break;
-#ifdef WITH_A2DP
- // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
- // A2DP speaker when forcing to speaker output
- if (!isInCall()) {
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
- if (device) break;
- }
-#endif
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
- if (device == 0) {
- LOGE("getDeviceForStrategy() speaker device not found");
- }
- break;
- }
- break;
-
- case STRATEGY_SONIFICATION:
-
- // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by
- // handleIncallSonification().
- if (isInCall()) {
- device = getDeviceForStrategy(STRATEGY_PHONE, false);
- break;
- }
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
- if (device == 0) {
- LOGE("getDeviceForStrategy() speaker device not found");
- }
- // The second device used for sonification is the same as the device used by media strategy
- // FALL THROUGH
-
- case STRATEGY_MEDIA: {
- uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
- }
-#ifdef WITH_A2DP
- if (mA2dpOutput != 0) {
- if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
- break;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
- }
- }
-#endif
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
- }
-
- // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise
- device |= device2;
- if (device == 0) {
- LOGE("getDeviceForStrategy() speaker device not found");
- }
- } break;
-
- default:
- LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
- break;
- }
-
- LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
- return device;
-}
-
-void AudioPolicyManagerBase::updateDeviceForStrategy()
-{
- for (int i = 0; i < NUM_STRATEGIES; i++) {
- mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
- }
-}
-
-void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
-{
- LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
-
-
- if (outputDesc->isDuplicated()) {
- setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
- setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
- return;
- }
-#ifdef WITH_A2DP
- // filter devices according to output selected
- if (output == mA2dpOutput) {
- device &= AudioSystem::DEVICE_OUT_ALL_A2DP;
- } else {
- device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP;
- }
-#endif
-
- uint32_t prevDevice = (uint32_t)outputDesc->device();
- // Do not change the routing if:
- // - the requestede device is 0
- // - the requested device is the same as current device and force is not specified.
- // Doing this check here allows the caller to call setOutputDevice() without conditions
- if ((device == 0 || device == prevDevice) && !force) {
- LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output);
- return;
- }
-
- outputDesc->mDevice = device;
- // mute media streams if both speaker and headset are selected
- if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) {
- setStrategyMute(STRATEGY_MEDIA, true, output);
- // wait for the PCM output buffers to empty before proceeding with the rest of the command
- usleep(outputDesc->mLatency*2*1000);
- }
-
- // do the routing
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), (int)device);
- mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);
- // update stream volumes according to new device
- applyStreamVolumes(output, device, delayMs);
-
- // if changing from a combined headset + speaker route, unmute media streams
- if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
- setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
- }
-}
-
-uint32_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource)
-{
- uint32_t device;
-
- switch(inputSource) {
- case AUDIO_SOURCE_DEFAULT:
- case AUDIO_SOURCE_MIC:
- case AUDIO_SOURCE_VOICE_RECOGNITION:
- case AUDIO_SOURCE_VOICE_COMMUNICATION:
- if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO &&
- mAvailableInputDevices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
- device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
- } else if (mAvailableInputDevices & AudioSystem::DEVICE_IN_WIRED_HEADSET) {
- device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
- } else {
- device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
- }
- break;
- case AUDIO_SOURCE_CAMCORDER:
- if (hasBackMicrophone()) {
- device = AudioSystem::DEVICE_IN_BACK_MIC;
- } else {
- device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
- }
- break;
- case AUDIO_SOURCE_VOICE_UPLINK:
- case AUDIO_SOURCE_VOICE_DOWNLINK:
- case AUDIO_SOURCE_VOICE_CALL:
- device = AudioSystem::DEVICE_IN_VOICE_CALL;
- break;
- default:
- LOGW("getInput() invalid input source %d", inputSource);
- device = 0;
- break;
- }
- LOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
- return device;
-}
-
-audio_io_handle_t AudioPolicyManagerBase::getActiveInput()
-{
- for (size_t i = 0; i < mInputs.size(); i++) {
- if (mInputs.valueAt(i)->mRefCount > 0) {
- return mInputs.keyAt(i);
- }
- }
- return 0;
-}
-
-float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device)
-{
- float volume = 1.0;
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
- StreamDescriptor &streamDesc = mStreams[stream];
-
- if (device == 0) {
- device = outputDesc->device();
- }
-
- int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin);
- volume = AudioSystem::linearToLog(volInt);
-
- // if a headset is connected, apply the following rules to ring tones and notifications
- // to avoid sound level bursts in user's ears:
- // - always attenuate ring tones and notifications volume by 6dB
- // - if music is playing, always limit the volume to current music volume,
- // with a minimum threshold at -36dB so that notification is always perceived.
- if ((device &
- (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
- AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
- AudioSystem::DEVICE_OUT_WIRED_HEADSET |
- AudioSystem::DEVICE_OUT_WIRED_HEADPHONE |
- AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET |
- AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET)) &&
- ((getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) ||
- (stream == AudioSystem::SYSTEM)) &&
- streamDesc.mCanBeMuted) {
- volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
- // when the phone is ringing we must consider that music could have been paused just before
- // by the music application and behave as if music was active if the last music track was
- // just stopped
- if (outputDesc->mRefCount[AudioSystem::MUSIC] || mLimitRingtoneVolume) {
- float musicVol = computeVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, output, device);
- float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
- if (volume > minVol) {
- volume = minVol;
- LOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol);
- }
- }
- }
-
- return volume;
-}
-
-status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force)
-{
-
- // do not change actual stream volume if the stream is muted
- if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
- LOGV("checkAndSetVolume() stream %d muted count %d", stream, mOutputs.valueFor(output)->mMuteCount[stream]);
- return NO_ERROR;
- }
-
- // do not change in call volume if bluetooth is connected and vice versa
- if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
- (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
- LOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
- stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
- return INVALID_OPERATION;
- }
-
- float volume = computeVolume(stream, index, output, device);
- // We actually change the volume if:
- // - the float value returned by computeVolume() changed
- // - the force flag is set
- if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
- force) {
- mOutputs.valueFor(output)->mCurVolume[stream] = volume;
- LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
- if (stream == AudioSystem::VOICE_CALL ||
- stream == AudioSystem::DTMF ||
- stream == AudioSystem::BLUETOOTH_SCO) {
- // offset value to reflect actual hardware volume that never reaches 0
- // 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
- volume = 0.01 + 0.99 * volume;
- }
- mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
- }
-
- if (stream == AudioSystem::VOICE_CALL ||
- stream == AudioSystem::BLUETOOTH_SCO) {
- float voiceVolume;
- // Force voice volume to max for bluetooth SCO as volume is managed by the headset
- if (stream == AudioSystem::VOICE_CALL) {
- voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
- } else {
- voiceVolume = 1.0;
- }
- if (voiceVolume != mLastVoiceVolume && output == mHardwareOutput) {
- mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
- mLastVoiceVolume = voiceVolume;
- }
- }
-
- return NO_ERROR;
-}
-
-void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs)
-{
- LOGV("applyStreamVolumes() for output %d and device %x", output, device);
-
- for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
- checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, device, delayMs);
- }
-}
-
-void AudioPolicyManagerBase::setStrategyMute(routing_strategy strategy, bool on, audio_io_handle_t output, int delayMs)
-{
- LOGV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output);
- for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
- if (getStrategy((AudioSystem::stream_type)stream) == strategy) {
- setStreamMute(stream, on, output, delayMs);
- }
- }
-}
-
-void AudioPolicyManagerBase::setStreamMute(int stream, bool on, audio_io_handle_t output, int delayMs)
-{
- StreamDescriptor &streamDesc = mStreams[stream];
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
-
- LOGV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d", stream, on, output, outputDesc->mMuteCount[stream]);
-
- if (on) {
- if (outputDesc->mMuteCount[stream] == 0) {
- if (streamDesc.mCanBeMuted) {
- checkAndSetVolume(stream, 0, output, outputDesc->device(), delayMs);
- }
- }
- // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
- outputDesc->mMuteCount[stream]++;
- } else {
- if (outputDesc->mMuteCount[stream] == 0) {
- LOGW("setStreamMute() unmuting non muted stream!");
- return;
- }
- if (--outputDesc->mMuteCount[stream] == 0) {
- checkAndSetVolume(stream, streamDesc.mIndexCur, output, outputDesc->device(), delayMs);
- }
- }
-}
-
-void AudioPolicyManagerBase::handleIncallSonification(int stream, bool starting, bool stateChange)
-{
- // if the stream pertains to sonification strategy and we are in call we must
- // mute the stream if it is low visibility. If it is high visibility, we must play a tone
- // in the device used for phone strategy and play the tone if the selected device does not
- // interfere with the device used for phone strategy
- // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as
- // many times as there are active tracks on the output
-
- if (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) {
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput);
- LOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d",
- stream, starting, outputDesc->mDevice, stateChange);
- if (outputDesc->mRefCount[stream]) {
- int muteCount = 1;
- if (stateChange) {
- muteCount = outputDesc->mRefCount[stream];
- }
- if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) {
- LOGV("handleIncallSonification() low visibility, muteCount %d", muteCount);
- for (int i = 0; i < muteCount; i++) {
- setStreamMute(stream, starting, mHardwareOutput);
- }
- } else {
- LOGV("handleIncallSonification() high visibility");
- if (outputDesc->device() & getDeviceForStrategy(STRATEGY_PHONE)) {
- LOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount);
- for (int i = 0; i < muteCount; i++) {
- setStreamMute(stream, starting, mHardwareOutput);
- }
- }
- if (starting) {
- mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL);
- } else {
- mpClientInterface->stopTone();
- }
- }
- }
- }
-}
-
-bool AudioPolicyManagerBase::isInCall()
-{
- return isStateInCall(mPhoneState);
-}
-
-bool AudioPolicyManagerBase::isStateInCall(int state) {
- return ((state == AudioSystem::MODE_IN_CALL) ||
- (state == AudioSystem::MODE_IN_COMMUNICATION));
-}
-
-bool AudioPolicyManagerBase::needsDirectOuput(AudioSystem::stream_type stream,
- uint32_t samplingRate,
- uint32_t format,
- uint32_t channels,
- AudioSystem::output_flags flags,
- uint32_t device)
-{
- return ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
- (format !=0 && !AudioSystem::isLinearPCM(format)));
-}
-
-uint32_t AudioPolicyManagerBase::getMaxEffectsCpuLoad()
-{
- return MAX_EFFECTS_CPU_LOAD;
-}
-
-uint32_t AudioPolicyManagerBase::getMaxEffectsMemory()
-{
- return MAX_EFFECTS_MEMORY;
-}
-
-// --- AudioOutputDescriptor class implementation
-
-AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor()
- : mId(0), mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0),
- mFlags((AudioSystem::output_flags)0), mDevice(0), mOutput1(0), mOutput2(0)
-{
- // clear usage count for all stream types
- for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
- mRefCount[i] = 0;
- mCurVolume[i] = -1.0;
- mMuteCount[i] = 0;
- mStopTime[i] = 0;
- }
-}
-
-uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::device()
-{
- uint32_t device = 0;
- if (isDuplicated()) {
- device = mOutput1->mDevice | mOutput2->mDevice;
- } else {
- device = mDevice;
- }
- return device;
-}
-
-void AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta)
-{
- // forward usage count change to attached outputs
- if (isDuplicated()) {
- mOutput1->changeRefCount(stream, delta);
- mOutput2->changeRefCount(stream, delta);
- }
- if ((delta + (int)mRefCount[stream]) < 0) {
- LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]);
- mRefCount[stream] = 0;
- return;
- }
- mRefCount[stream] += delta;
- LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
-}
-
-uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::refCount()
-{
- uint32_t refcount = 0;
- for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
- refcount += mRefCount[i];
- }
- return refcount;
-}
-
-uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::strategyRefCount(routing_strategy strategy)
-{
- uint32_t refCount = 0;
- for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
- if (getStrategy((AudioSystem::stream_type)i) == strategy) {
- refCount += mRefCount[i];
- }
- }
- return refCount;
-}
-
-status_t AudioPolicyManagerBase::AudioOutputDescriptor::dump(int fd)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
- result.append(buffer);
- snprintf(buffer, SIZE, " Format: %d\n", mFormat);
- result.append(buffer);
- snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
- result.append(buffer);
- snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
- result.append(buffer);
- snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
- result.append(buffer);
- snprintf(buffer, SIZE, " Devices %08x\n", device());
- result.append(buffer);
- snprintf(buffer, SIZE, " Stream volume refCount muteCount\n");
- result.append(buffer);
- for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
- snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", i, mCurVolume[i], mRefCount[i], mMuteCount[i]);
- result.append(buffer);
- }
- write(fd, result.string(), result.size());
-
- return NO_ERROR;
-}
-
-// --- AudioInputDescriptor class implementation
-
-AudioPolicyManagerBase::AudioInputDescriptor::AudioInputDescriptor()
- : mSamplingRate(0), mFormat(0), mChannels(0),
- mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0)
-{
-}
-
-status_t AudioPolicyManagerBase::AudioInputDescriptor::dump(int fd)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
- result.append(buffer);
- snprintf(buffer, SIZE, " Format: %d\n", mFormat);
- result.append(buffer);
- snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
- result.append(buffer);
- snprintf(buffer, SIZE, " Acoustics %08x\n", mAcoustics);
- result.append(buffer);
- snprintf(buffer, SIZE, " Devices %08x\n", mDevice);
- result.append(buffer);
- snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount);
- result.append(buffer);
- write(fd, result.string(), result.size());
-
- return NO_ERROR;
-}
-
-// --- StreamDescriptor class implementation
-
-void AudioPolicyManagerBase::StreamDescriptor::dump(char* buffer, size_t size)
-{
- snprintf(buffer, size, " %02d %02d %02d %d\n",
- mIndexMin,
- mIndexMax,
- mIndexCur,
- mCanBeMuted);
-}
-
-// --- EffectDescriptor class implementation
-
-status_t AudioPolicyManagerBase::EffectDescriptor::dump(int fd)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, " Output: %d\n", mOutput);
- result.append(buffer);
- snprintf(buffer, SIZE, " Strategy: %d\n", mStrategy);
- result.append(buffer);
- snprintf(buffer, SIZE, " Session: %d\n", mSession);
- result.append(buffer);
- snprintf(buffer, SIZE, " Name: %s\n", mDesc.name);
- result.append(buffer);
- write(fd, result.string(), result.size());
-
- return NO_ERROR;
-}
-
-
-
-}; // namespace android
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index b04672d..ef8d957 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -30,11 +30,15 @@
#include <utils/String16.h>
#include <utils/threads.h>
#include "AudioPolicyService.h"
-#include <hardware_legacy/AudioPolicyManagerBase.h>
#include <cutils/properties.h>
#include <dlfcn.h>
#include <hardware_legacy/power.h>
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio_policy.h>
+#include <hardware/audio_policy_hal.h>
+
// ----------------------------------------------------------------------------
// the sim build doesn't have gettid
@@ -44,7 +48,6 @@
namespace android {
-
static const char *kDeadlockedString = "AudioPolicyService may be deadlocked\n";
static const char *kCmdDeadlockedString = "AudioPolicyService command thread may be deadlocked\n";
@@ -61,12 +64,19 @@
return ok;
}
+namespace {
+ extern struct audio_policy_service_ops aps_ops;
+};
+
// ----------------------------------------------------------------------------
AudioPolicyService::AudioPolicyService()
- : BnAudioPolicyService() , mpPolicyManager(NULL)
+ : BnAudioPolicyService() , mpAudioPolicyDev(NULL) , mpAudioPolicy(NULL)
{
char value[PROPERTY_VALUE_MAX];
+ const struct hw_module_t *module;
+ int forced_val;
+ int rc;
Mutex::Autolock _l(mLock);
@@ -75,33 +85,32 @@
// start audio commands thread
mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));
-#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)
- mpPolicyManager = new AudioPolicyManagerBase(this);
- LOGV("build for GENERIC_AUDIO - using generic audio policy");
-#else
- // if running in emulation - use the emulator driver
- if (property_get("ro.kernel.qemu", value, 0)) {
- LOGV("Running in emulation - using generic audio policy");
- mpPolicyManager = new AudioPolicyManagerBase(this);
- }
- else {
- LOGV("Using hardware specific audio policy");
- mpPolicyManager = createAudioPolicyManager(this);
- }
-#endif
+ /* instantiate the audio policy manager */
+ rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
+ if (rc)
+ return;
- if ((mpPolicyManager != NULL) && (mpPolicyManager->initCheck() != NO_ERROR)) {
- delete mpPolicyManager;
- mpPolicyManager = NULL;
- }
+ rc = audio_policy_dev_open(module, &mpAudioPolicyDev);
+ LOGE_IF(rc, "couldn't open audio policy device (%s)", strerror(-rc));
+ if (rc)
+ return;
- if (mpPolicyManager == NULL) {
- LOGE("Could not create AudioPolicyManager");
- } else {
- // load properties
- property_get("ro.camera.sound.forced", value, "0");
- mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);
- }
+ rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,
+ &mpAudioPolicy);
+ LOGE_IF(rc, "couldn't create audio policy (%s)", strerror(-rc));
+ if (rc)
+ return;
+
+ rc = mpAudioPolicy->init_check(mpAudioPolicy);
+ LOGE_IF(rc, "couldn't init_check the audio policy (%s)", strerror(-rc));
+ if (rc)
+ return;
+
+ property_get("ro.camera.sound.forced", value, "0");
+ forced_val = strtol(value, NULL, 0);
+ mpAudioPolicy->set_can_mute_enforced_audible(mpAudioPolicy, !forced_val);
+
+ LOGI("Loaded audio policy from %s (%s)", module->name, module->id);
}
AudioPolicyService::~AudioPolicyService()
@@ -111,57 +120,59 @@
mAudioCommandThread->exit();
mAudioCommandThread.clear();
- if (mpPolicyManager) {
- delete mpPolicyManager;
- }
+ if (mpAudioPolicy && mpAudioPolicyDev)
+ mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy);
+ if (mpAudioPolicyDev)
+ audio_policy_dev_close(mpAudioPolicyDev);
}
-
-status_t AudioPolicyService::setDeviceConnectionState(AudioSystem::audio_devices device,
- AudioSystem::device_connection_state state,
+status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device,
+ audio_policy_dev_state_t state,
const char *device_address)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (!AudioSystem::isOutputDevice(device) && !AudioSystem::isInputDevice(device)) {
+ if (!audio_is_output_device(device) && !audio_is_input_device(device)) {
return BAD_VALUE;
}
- if (state != AudioSystem::DEVICE_STATE_AVAILABLE &&
- state != AudioSystem::DEVICE_STATE_UNAVAILABLE) {
+ if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE &&
+ state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
return BAD_VALUE;
}
LOGV("setDeviceConnectionState() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpPolicyManager->setDeviceConnectionState(device, state, device_address);
+ return mpAudioPolicy->set_device_connection_state(mpAudioPolicy, device,
+ state, device_address);
}
-AudioSystem::device_connection_state AudioPolicyService::getDeviceConnectionState(
- AudioSystem::audio_devices device,
+audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState(
+ audio_devices_t device,
const char *device_address)
{
- if (mpPolicyManager == NULL) {
- return AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ if (mpAudioPolicy == NULL) {
+ return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
}
if (!checkPermission()) {
- return AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
}
- return mpPolicyManager->getDeviceConnectionState(device, device_address);
+ return mpAudioPolicy->get_device_connection_state(mpAudioPolicy, device,
+ device_address);
}
status_t AudioPolicyService::setPhoneState(int state)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (state < 0 || state >= AudioSystem::NUM_MODES) {
+ if (state < 0 || state >= AUDIO_MODE_CNT) {
return BAD_VALUE;
}
@@ -171,207 +182,215 @@
AudioSystem::setMode(state);
Mutex::Autolock _l(mLock);
- mpPolicyManager->setPhoneState(state);
+ mpAudioPolicy->set_phone_state(mpAudioPolicy, state);
return NO_ERROR;
}
status_t AudioPolicyService::setRingerMode(uint32_t mode, uint32_t mask)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- mpPolicyManager->setRingerMode(mode, mask);
+ mpAudioPolicy->set_ringer_mode(mpAudioPolicy, mode, mask);
return NO_ERROR;
}
-status_t AudioPolicyService::setForceUse(AudioSystem::force_use usage,
- AudioSystem::forced_config config)
+status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage,
+ audio_policy_forced_cfg_t config)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) {
+ if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {
return BAD_VALUE;
}
- if (config < 0 || config >= AudioSystem::NUM_FORCE_CONFIG) {
+ if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) {
return BAD_VALUE;
}
LOGV("setForceUse() tid %d", gettid());
Mutex::Autolock _l(mLock);
- mpPolicyManager->setForceUse(usage, config);
+ mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config);
return NO_ERROR;
}
-AudioSystem::forced_config AudioPolicyService::getForceUse(AudioSystem::force_use usage)
+audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage)
{
- if (mpPolicyManager == NULL) {
- return AudioSystem::FORCE_NONE;
+ if (mpAudioPolicy == NULL) {
+ return AUDIO_POLICY_FORCE_NONE;
}
if (!checkPermission()) {
- return AudioSystem::FORCE_NONE;
+ return AUDIO_POLICY_FORCE_NONE;
}
- if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) {
- return AudioSystem::FORCE_NONE;
+ if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {
+ return AUDIO_POLICY_FORCE_NONE;
}
- return mpPolicyManager->getForceUse(usage);
+ return mpAudioPolicy->get_force_use(mpAudioPolicy, usage);
}
-audio_io_handle_t AudioPolicyService::getOutput(AudioSystem::stream_type stream,
+audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
- AudioSystem::output_flags flags)
+ audio_policy_output_flags_t flags)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return 0;
}
LOGV("getOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpPolicyManager->getOutput(stream, samplingRate, format, channels, flags);
+ return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, format, channels, flags);
}
status_t AudioPolicyService::startOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
LOGV("startOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpPolicyManager->startOutput(output, stream, session);
+ return mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session);
}
status_t AudioPolicyService::stopOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
LOGV("stopOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpPolicyManager->stopOutput(output, stream, session);
+ return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session);
}
void AudioPolicyService::releaseOutput(audio_io_handle_t output)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return;
}
LOGV("releaseOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- mpPolicyManager->releaseOutput(output);
+ mpAudioPolicy->release_output(mpAudioPolicy, output);
}
audio_io_handle_t AudioPolicyService::getInput(int inputSource,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
- AudioSystem::audio_in_acoustics acoustics)
+ audio_in_acoustics_t acoustics)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return 0;
}
Mutex::Autolock _l(mLock);
- return mpPolicyManager->getInput(inputSource, samplingRate, format, channels, acoustics);
+ return mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate, format, channels, acoustics);
}
status_t AudioPolicyService::startInput(audio_io_handle_t input)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
- return mpPolicyManager->startInput(input);
+ return mpAudioPolicy->start_input(mpAudioPolicy, input);
}
status_t AudioPolicyService::stopInput(audio_io_handle_t input)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
- return mpPolicyManager->stopInput(input);
+ return mpAudioPolicy->stop_input(mpAudioPolicy, input);
}
void AudioPolicyService::releaseInput(audio_io_handle_t input)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return;
}
Mutex::Autolock _l(mLock);
- mpPolicyManager->releaseInput(input);
+ mpAudioPolicy->release_input(mpAudioPolicy, input);
}
-status_t AudioPolicyService::initStreamVolume(AudioSystem::stream_type stream,
+status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || stream >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
- mpPolicyManager->initStreamVolume(stream, indexMin, indexMax);
+ mpAudioPolicy->init_stream_volume(mpAudioPolicy, stream, indexMin, indexMax);
return NO_ERROR;
}
-status_t AudioPolicyService::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, int index)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || stream >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
- return mpPolicyManager->setStreamVolumeIndex(stream, index);
+ return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index);
}
-status_t AudioPolicyService::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
+status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, int *index)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || stream >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
- return mpPolicyManager->getStreamVolumeIndex(stream, index);
+ return mpAudioPolicy->get_stream_volume_index(mpAudioPolicy, stream, index);
}
-uint32_t AudioPolicyService::getStrategyForStream(AudioSystem::stream_type stream)
+uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return 0;
}
- return mpPolicyManager->getStrategyForStream(stream);
+ return mpAudioPolicy->get_strategy_for_stream(mpAudioPolicy, stream);
+}
+
+uint32_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream)
+{
+ if (mpAudioPolicy == NULL) {
+ return 0;
+ }
+ return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream);
}
audio_io_handle_t AudioPolicyService::getOutputForEffect(effect_descriptor_t *desc)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
- return mpPolicyManager->getOutputForEffect(desc);
+ return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc);
}
status_t AudioPolicyService::registerEffect(effect_descriptor_t *desc,
@@ -380,27 +399,27 @@
int session,
int id)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
- return mpPolicyManager->registerEffect(desc, output, strategy, session, id);
+ return mpAudioPolicy->register_effect(mpAudioPolicy, desc, output, strategy, session, id);
}
status_t AudioPolicyService::unregisterEffect(int id)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
- return mpPolicyManager->unregisterEffect(id);
+ return mpAudioPolicy->unregister_effect(mpAudioPolicy, id);
}
bool AudioPolicyService::isStreamActive(int stream, uint32_t inPastMs) const
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return 0;
}
Mutex::Autolock _l(mLock);
- return mpPolicyManager->isStreamActive(stream, inPastMs);
+ return mpAudioPolicy->is_stream_active(mpAudioPolicy, stream, inPastMs);
}
void AudioPolicyService::binderDied(const wp<IBinder>& who) {
@@ -427,7 +446,7 @@
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpPolicyManager);
+ snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpAudioPolicy);
result.append(buffer);
snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get());
result.append(buffer);
@@ -457,8 +476,8 @@
mTonePlaybackThread->dump(fd);
}
- if (mpPolicyManager) {
- mpPolicyManager->dump(fd);
+ if (mpAudioPolicy) {
+ mpAudioPolicy->dump(mpAudioPolicy, fd);
}
if (locked) mLock.unlock();
@@ -487,161 +506,6 @@
}
-// ----------------------------------------------------------------------------
-void AudioPolicyService::instantiate() {
- defaultServiceManager()->addService(
- String16("media.audio_policy"), new AudioPolicyService());
-}
-
-
-// ----------------------------------------------------------------------------
-// AudioPolicyClientInterface implementation
-// ----------------------------------------------------------------------------
-
-
-audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- uint32_t *pChannels,
- uint32_t *pLatencyMs,
- AudioSystem::output_flags flags)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- LOGW("openOutput() could not get AudioFlinger");
- return 0;
- }
-
- return af->openOutput(pDevices,
- pSamplingRate,
- (uint32_t *)pFormat,
- pChannels,
- pLatencyMs,
- flags);
-}
-
-audio_io_handle_t AudioPolicyService::openDuplicateOutput(audio_io_handle_t output1,
- audio_io_handle_t output2)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- LOGW("openDuplicateOutput() could not get AudioFlinger");
- return 0;
- }
- return af->openDuplicateOutput(output1, output2);
-}
-
-status_t AudioPolicyService::closeOutput(audio_io_handle_t output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
-
- return af->closeOutput(output);
-}
-
-
-status_t AudioPolicyService::suspendOutput(audio_io_handle_t output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- LOGW("suspendOutput() could not get AudioFlinger");
- return PERMISSION_DENIED;
- }
-
- return af->suspendOutput(output);
-}
-
-status_t AudioPolicyService::restoreOutput(audio_io_handle_t output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- LOGW("restoreOutput() could not get AudioFlinger");
- return PERMISSION_DENIED;
- }
-
- return af->restoreOutput(output);
-}
-
-audio_io_handle_t AudioPolicyService::openInput(uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- uint32_t *pChannels,
- uint32_t acoustics)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- LOGW("openInput() could not get AudioFlinger");
- return 0;
- }
-
- return af->openInput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, acoustics);
-}
-
-status_t AudioPolicyService::closeInput(audio_io_handle_t input)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
-
- return af->closeInput(input);
-}
-
-status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream,
- float volume,
- audio_io_handle_t output,
- int delayMs)
-{
- return mAudioCommandThread->volumeCommand((int)stream, volume, (int)output, delayMs);
-}
-
-status_t AudioPolicyService::setStreamOutput(AudioSystem::stream_type stream,
- audio_io_handle_t output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
-
- return af->setStreamOutput(stream, output);
-}
-
-status_t AudioPolicyService::moveEffects(int session, audio_io_handle_t srcOutput,
- audio_io_handle_t dstOutput)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
-
- return af->moveEffects(session, (int)srcOutput, (int)dstOutput);
-}
-
-void AudioPolicyService::setParameters(audio_io_handle_t ioHandle,
- const String8& keyValuePairs,
- int delayMs)
-{
- mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs, delayMs);
-}
-
-String8 AudioPolicyService::getParameters(audio_io_handle_t ioHandle, const String8& keys)
-{
- String8 result = AudioSystem::getParameters(ioHandle, keys);
- return result;
-}
-
-status_t AudioPolicyService::startTone(ToneGenerator::tone_type tone,
- AudioSystem::stream_type stream)
-{
- mTonePlaybackThread->startToneCommand(tone, stream);
- return NO_ERROR;
-}
-
-status_t AudioPolicyService::stopTone()
-{
- mTonePlaybackThread->stopToneCommand();
- return NO_ERROR;
-}
-
-status_t AudioPolicyService::setVoiceVolume(float volume, int delayMs)
-{
- return mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
-}
-
// ----------- AudioPolicyService::AudioCommandThread implementation ----------
AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
@@ -858,7 +722,7 @@
}
status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle,
- const String8& keyValuePairs,
+ const char *keyValuePairs,
int delayMs)
{
status_t status = NO_ERROR;
@@ -867,7 +731,7 @@
command->mCommand = SET_PARAMETERS;
ParametersData *data = new ParametersData();
data->mIO = ioHandle;
- data->mKeyValuePairs = keyValuePairs;
+ data->mKeyValuePairs = String8(keyValuePairs);
command->mParam = data;
if (delayMs == 0) {
command->mWaitStatus = true;
@@ -877,7 +741,7 @@
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);
LOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d",
- keyValuePairs.string(), ioHandle, delayMs);
+ keyValuePairs, ioHandle, delayMs);
mWaitWorkCV.signal();
if (command->mWaitStatus) {
command->mCond.wait(mLock);
@@ -1022,4 +886,226 @@
mParam);
}
+/******* helpers for the service_ops callbacks defined below *********/
+void AudioPolicyService::setParameters(audio_io_handle_t ioHandle,
+ const char *keyValuePairs,
+ int delayMs)
+{
+ mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs,
+ delayMs);
+}
+
+int AudioPolicyService::setStreamVolume(audio_stream_type_t stream,
+ float volume,
+ audio_io_handle_t output,
+ int delayMs)
+{
+ return (int)mAudioCommandThread->volumeCommand((int)stream, volume,
+ (int)output, delayMs);
+}
+
+int AudioPolicyService::startTone(audio_policy_tone_t tone,
+ audio_stream_type_t stream)
+{
+ if (tone != AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION)
+ LOGE("startTone: illegal tone requested (%d)", tone);
+ if (stream != AUDIO_STREAM_VOICE_CALL)
+ LOGE("startTone: illegal stream (%d) requested for tone %d", stream,
+ tone);
+ mTonePlaybackThread->startToneCommand(ToneGenerator::TONE_SUP_CALL_WAITING,
+ AUDIO_STREAM_VOICE_CALL);
+ return 0;
+}
+
+int AudioPolicyService::stopTone()
+{
+ mTonePlaybackThread->stopToneCommand();
+ return 0;
+}
+
+int AudioPolicyService::setVoiceVolume(float volume, int delayMs)
+{
+ return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
+}
+
+/* implementation of the interface to the policy manager */
+extern "C" {
+
+static audio_io_handle_t aps_open_output(void *service,
+ uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ audio_policy_output_flags_t flags)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL) {
+ LOGW("%s: could not get AudioFlinger", __func__);
+ return 0;
+ }
+
+ return af->openOutput(pDevices, pSamplingRate, pFormat, pChannels,
+ pLatencyMs, flags);
+}
+
+static audio_io_handle_t aps_open_dup_output(void *service,
+ audio_io_handle_t output1,
+ audio_io_handle_t output2)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL) {
+ LOGW("%s: could not get AudioFlinger", __func__);
+ return 0;
+ }
+ return af->openDuplicateOutput(output1, output2);
+}
+
+static int aps_close_output(void *service, audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL)
+ return PERMISSION_DENIED;
+
+ return af->closeOutput(output);
+}
+
+static int aps_suspend_output(void *service, audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL) {
+ LOGW("%s: could not get AudioFlinger", __func__);
+ return PERMISSION_DENIED;
+ }
+
+ return af->suspendOutput(output);
+}
+
+static int aps_restore_output(void *service, audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL) {
+ LOGW("%s: could not get AudioFlinger", __func__);
+ return PERMISSION_DENIED;
+ }
+
+ return af->restoreOutput(output);
+}
+
+static audio_io_handle_t aps_open_input(void *service,
+ uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL) {
+ LOGW("%s: could not get AudioFlinger", __func__);
+ return 0;
+ }
+
+ return af->openInput(pDevices, pSamplingRate, pFormat, pChannels,
+ acoustics);
+}
+
+static int aps_close_input(void *service, audio_io_handle_t input)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL)
+ return PERMISSION_DENIED;
+
+ return af->closeInput(input);
+}
+
+static int aps_set_stream_output(void *service, audio_stream_type_t stream,
+ audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL)
+ return PERMISSION_DENIED;
+
+ return af->setStreamOutput(stream, output);
+}
+
+static int aps_move_effects(void *service, int session,
+ audio_io_handle_t src_output,
+ audio_io_handle_t dst_output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL)
+ return PERMISSION_DENIED;
+
+ return af->moveEffects(session, (int)src_output, (int)dst_output);
+}
+
+static char * aps_get_parameters(void *service, audio_io_handle_t io_handle,
+ const char *keys)
+{
+ String8 result = AudioSystem::getParameters(io_handle, String8(keys));
+ return strdup(result.string());
+}
+
+static void aps_set_parameters(void *service, audio_io_handle_t io_handle,
+ const char *kv_pairs, int delay_ms)
+{
+ AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+ audioPolicyService->setParameters(io_handle, kv_pairs, delay_ms);
+}
+
+static int aps_set_stream_volume(void *service, audio_stream_type_t stream,
+ float volume, audio_io_handle_t output,
+ int delay_ms)
+{
+ AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+ return audioPolicyService->setStreamVolume(stream, volume, output,
+ delay_ms);
+}
+
+static int aps_start_tone(void *service, audio_policy_tone_t tone,
+ audio_stream_type_t stream)
+{
+ AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+ return audioPolicyService->startTone(tone, stream);
+}
+
+static int aps_stop_tone(void *service)
+{
+ AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+ return audioPolicyService->stopTone();
+}
+
+static int aps_set_voice_volume(void *service, float volume, int delay_ms)
+{
+ AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+ return audioPolicyService->setVoiceVolume(volume, delay_ms);
+}
+
+}; // extern "C"
+
+namespace {
+ struct audio_policy_service_ops aps_ops = {
+ open_output : aps_open_output,
+ open_duplicate_output : aps_open_dup_output,
+ close_output : aps_close_output,
+ suspend_output : aps_suspend_output,
+ restore_output : aps_restore_output,
+ open_input : aps_open_input,
+ close_input : aps_close_input,
+ set_stream_volume : aps_set_stream_volume,
+ set_stream_output : aps_set_stream_output,
+ set_parameters : aps_set_parameters,
+ get_parameters : aps_get_parameters,
+ start_tone : aps_start_tone,
+ stop_tone : aps_stop_tone,
+ set_voice_volume : aps_set_voice_volume,
+ move_effects : aps_move_effects,
+ };
+}; // namespace <unnamed>
+
}; // namespace android
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 54af1f1..d9b5ada 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -18,9 +18,13 @@
#define ANDROID_AUDIOPOLICYSERVICE_H
#include <media/IAudioPolicyService.h>
-#include <hardware_legacy/AudioPolicyInterface.h>
#include <media/ToneGenerator.h>
#include <utils/Vector.h>
+#include <binder/BinderService.h>
+
+#include <system/audio.h>
+#include <hardware/audio_policy.h>
+#include <hardware/audio_policy_hal.h>
namespace android {
@@ -28,12 +32,17 @@
// ----------------------------------------------------------------------------
-class AudioPolicyService: public BnAudioPolicyService, public AudioPolicyClientInterface,
+class AudioPolicyService :
+ public BinderService<AudioPolicyService>,
+ public BnAudioPolicyService,
+// public AudioPolicyClientInterface,
public IBinder::DeathRecipient
{
+ friend class BinderService<AudioPolicyService>;
public:
- static void instantiate();
+ // for BinderService
+ static const char *getServiceName() { return "media.audio_policy"; }
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -41,45 +50,46 @@
// BnAudioPolicyService (see AudioPolicyInterface for method descriptions)
//
- virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
- AudioSystem::device_connection_state state,
+ virtual status_t setDeviceConnectionState(audio_devices_t device,
+ audio_policy_dev_state_t state,
const char *device_address);
- virtual AudioSystem::device_connection_state getDeviceConnectionState(
- AudioSystem::audio_devices device,
+ virtual audio_policy_dev_state_t getDeviceConnectionState(
+ audio_devices_t device,
const char *device_address);
virtual status_t setPhoneState(int state);
virtual status_t setRingerMode(uint32_t mode, uint32_t mask);
- virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config);
- virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage);
- virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
+ virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
+ virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
+ virtual audio_io_handle_t getOutput(audio_stream_type_t stream,
uint32_t samplingRate = 0,
- uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
- AudioSystem::output_flags flags =
- AudioSystem::OUTPUT_FLAG_INDIRECT);
+ audio_policy_output_flags_t flags =
+ AUDIO_POLICY_OUTPUT_FLAG_INDIRECT);
virtual status_t startOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session = 0);
virtual status_t stopOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session = 0);
virtual void releaseOutput(audio_io_handle_t output);
virtual audio_io_handle_t getInput(int inputSource,
uint32_t samplingRate = 0,
- uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
- AudioSystem::audio_in_acoustics acoustics =
- (AudioSystem::audio_in_acoustics)0);
+ audio_in_acoustics_t acoustics =
+ (audio_in_acoustics_t)0);
virtual status_t startInput(audio_io_handle_t input);
virtual status_t stopInput(audio_io_handle_t input);
virtual void releaseInput(audio_io_handle_t input);
- virtual status_t initStreamVolume(AudioSystem::stream_type stream,
+ virtual status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax);
- virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index);
- virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index);
+ virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, int index);
+ virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, int *index);
- virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream);
+ virtual uint32_t getStrategyForStream(audio_stream_type_t stream);
+ virtual uint32_t getDevicesForStream(audio_stream_type_t stream);
virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc);
virtual status_t registerEffect(effect_descriptor_t *desc,
@@ -100,40 +110,21 @@
virtual void binderDied(const wp<IBinder>& who);
//
- // AudioPolicyClientInterface
+ // Helpers for the struct audio_policy_service_ops implementation.
+ // This is used by the audio policy manager for certain operations that
+ // are implemented by the policy service.
//
- virtual audio_io_handle_t openOutput(uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- uint32_t *pChannels,
- uint32_t *pLatencyMs,
- AudioSystem::output_flags flags);
- virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
- audio_io_handle_t output2);
- virtual status_t closeOutput(audio_io_handle_t output);
- virtual status_t suspendOutput(audio_io_handle_t output);
- virtual status_t restoreOutput(audio_io_handle_t output);
- virtual audio_io_handle_t openInput(uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- uint32_t *pChannels,
- uint32_t acoustics);
- virtual status_t closeInput(audio_io_handle_t input);
- virtual status_t setStreamVolume(AudioSystem::stream_type stream,
+ virtual void setParameters(audio_io_handle_t ioHandle,
+ const char *keyValuePairs,
+ int delayMs);
+
+ virtual status_t setStreamVolume(audio_stream_type_t stream,
float volume,
audio_io_handle_t output,
int delayMs = 0);
- virtual status_t setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output);
- virtual void setParameters(audio_io_handle_t ioHandle,
- const String8& keyValuePairs,
- int delayMs = 0);
- virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys);
- virtual status_t startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream);
+ virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream);
virtual status_t stopTone();
virtual status_t setVoiceVolume(float volume, int delayMs = 0);
- virtual status_t moveEffects(int session,
- audio_io_handle_t srcOutput,
- audio_io_handle_t dstOutput);
private:
AudioPolicyService();
@@ -173,7 +164,7 @@
void startToneCommand(int type = 0, int stream = 0);
void stopToneCommand();
status_t volumeCommand(int stream, float volume, int output, int delayMs = 0);
- status_t parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs = 0);
+ status_t parametersCommand(int ioHandle, const char *keyValuePairs, int delayMs = 0);
status_t voiceVolumeCommand(float volume, int delayMs = 0);
void insertCommand_l(AudioCommand *command, int delayMs = 0);
@@ -233,19 +224,13 @@
mutable Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing
// device connection state or routing
- AudioPolicyInterface* mpPolicyManager; // the platform specific policy manager
sp <AudioCommandThread> mAudioCommandThread; // audio commands thread
sp <AudioCommandThread> mTonePlaybackThread; // tone playback thread
+
+ struct audio_policy_device *mpAudioPolicyDev;
+ struct audio_policy *mpAudioPolicy;
};
}; // namespace android
#endif // ANDROID_AUDIOPOLICYSERVICE_H
-
-
-
-
-
-
-
-
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 5dabacb..dca795c 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -26,11 +26,15 @@
#include "AudioResamplerSinc.h"
#include "AudioResamplerCubic.h"
+#ifdef __arm__
+#include <machine/cpu-features.h>
+#endif
+
namespace android {
-#ifdef __ARM_ARCH_5E__ // optimized asm option
+#ifdef __ARM_HAVE_HALFWORD_MULTIPLY // optimized asm option
#define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1
-#endif // __ARM_ARCH_5E__
+#endif // __ARM_HAVE_HALFWORD_MULTIPLY
// ----------------------------------------------------------------------------
class AudioResamplerOrder1 : public AudioResampler {
@@ -148,6 +152,12 @@
mVolume[1] = right;
}
+void AudioResampler::reset() {
+ mInputIndex = 0;
+ mPhaseFraction = 0;
+ mBuffer.frameCount = 0;
+}
+
// ----------------------------------------------------------------------------
void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 2dfac76..9f06c1c 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -53,6 +53,8 @@
virtual void resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) = 0;
+ virtual void reset();
+
protected:
// number of bits for phase fraction - 30 bits allows nearly 2x downsampling
static const int kNumPhaseBits = 30;
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index b52fc69..e35435e 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -1,38 +1,5 @@
LOCAL_PATH:= $(call my-dir)
-# Set USE_CAMERA_STUB if you don't want to use the hardware camera.
-
-# force these builds to use camera stub only
-ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),)
- USE_CAMERA_STUB:=true
-endif
-
-ifeq ($(USE_CAMERA_STUB),)
- USE_CAMERA_STUB:=false
-endif
-
-ifeq ($(USE_CAMERA_STUB),true)
-#
-# libcamerastub
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- CameraHardwareStub.cpp \
- FakeCamera.cpp
-
-LOCAL_MODULE:= libcamerastub
-
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_CFLAGS += -DSINGLE_PROCESS
-endif
-
-LOCAL_SHARED_LIBRARIES:= libui
-
-include $(BUILD_STATIC_LIBRARY)
-endif # USE_CAMERA_STUB
-
#
# libcameraservice
#
@@ -49,19 +16,9 @@
libcutils \
libmedia \
libcamera_client \
- libsurfaceflinger_client \
- libgui
+ libgui \
+ libhardware
LOCAL_MODULE:= libcameraservice
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_CFLAGS += -DSINGLE_PROCESS
-endif
-
-ifeq ($(USE_CAMERA_STUB), true)
-LOCAL_STATIC_LIBRARIES += libcamerastub
-else
-LOCAL_SHARED_LIBRARIES += libcamera
-endif
-
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h
new file mode 100644
index 0000000..f9fa30e
--- /dev/null
+++ b/services/camera/libcameraservice/CameraHardwareInterface.h
@@ -0,0 +1,619 @@
+/*
+ * Copyright (C) 2008 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_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
+#define ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
+
+#include <binder/IMemory.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <utils/RefBase.h>
+#include <surfaceflinger/ISurface.h>
+#include <ui/android_native_buffer.h>
+#include <ui/GraphicBuffer.h>
+#include <camera/Camera.h>
+#include <camera/CameraParameters.h>
+#include <system/window.h>
+#include <hardware/camera.h>
+
+namespace android {
+
+typedef void (*notify_callback)(int32_t msgType,
+ int32_t ext1,
+ int32_t ext2,
+ void* user);
+
+typedef void (*data_callback)(int32_t msgType,
+ const sp<IMemory> &dataPtr,
+ void* user);
+
+typedef void (*data_callback_timestamp)(nsecs_t timestamp,
+ int32_t msgType,
+ const sp<IMemory> &dataPtr,
+ void *user);
+
+/**
+ * CameraHardwareInterface.h defines the interface to the
+ * camera hardware abstraction layer, used for setting and getting
+ * parameters, live previewing, and taking pictures.
+ *
+ * It is a referenced counted interface with RefBase as its base class.
+ * CameraService calls openCameraHardware() to retrieve a strong pointer to the
+ * instance of this interface and may be called multiple times. The
+ * following steps describe a typical sequence:
+ *
+ * -# After CameraService calls openCameraHardware(), getParameters() and
+ * setParameters() are used to initialize the camera instance.
+ * CameraService calls getPreviewHeap() to establish access to the
+ * preview heap so it can be registered with SurfaceFlinger for
+ * efficient display updating while in preview mode.
+ * -# startPreview() is called. The camera instance then periodically
+ * sends the message CAMERA_MSG_PREVIEW_FRAME (if enabled) each time
+ * a new preview frame is available. If data callback code needs to use
+ * this memory after returning, it must copy the data.
+ *
+ * Prior to taking a picture, CameraService calls autofocus(). When auto
+ * focusing has completed, the camera instance sends a CAMERA_MSG_FOCUS notification,
+ * which informs the application whether focusing was successful. The camera instance
+ * only sends this message once and it is up to the application to call autoFocus()
+ * again if refocusing is desired.
+ *
+ * CameraService calls takePicture() to request the camera instance take a
+ * picture. At this point, if a shutter, postview, raw, and/or compressed callback
+ * is desired, the corresponding message must be enabled. As with CAMERA_MSG_PREVIEW_FRAME,
+ * any memory provided in a data callback must be copied if it's needed after returning.
+ */
+
+class CameraHardwareInterface : public virtual RefBase {
+public:
+ CameraHardwareInterface(hw_module_t *module, const char *name)
+ {
+ mDevice = 0;
+ mName = name;
+ LOGI("Opening camera %s, this %p", name, this);
+ int rc = module->methods->open(module, name,
+ (hw_device_t **)&mDevice);
+ if (rc != OK)
+ LOGE("Could not open camera %s: %d", name, rc);
+ initHalPreviewWindow();
+ }
+
+ ~CameraHardwareInterface()
+ {
+ LOGI("Destroying camera %s", mName.string());
+ int rc = mDevice->common.close(&mDevice->common);
+ if (rc != OK)
+ LOGE("Could not close camera %s: %d", mName.string(), rc);
+ }
+
+ /** Set the ANativeWindow to which preview frames are sent */
+ status_t setPreviewWindow(const sp<ANativeWindow>& buf)
+ {
+ LOGV("%s(%s) buf %p", __FUNCTION__, mName.string(), buf.get());
+
+ if (mDevice->ops->set_preview_window) {
+ mPreviewWindow = buf;
+ mHalPreviewWindow.user = this;
+ LOGV("%s &mHalPreviewWindow %p mHalPreviewWindow.user %p", __FUNCTION__,
+ &mHalPreviewWindow, mHalPreviewWindow.user);
+ return mDevice->ops->set_preview_window(mDevice,
+ buf.get() ? &mHalPreviewWindow.nw : 0);
+ }
+ return INVALID_OPERATION;
+ }
+
+ /** Set the notification and data callbacks */
+ void setCallbacks(notify_callback notify_cb,
+ data_callback data_cb,
+ data_callback_timestamp data_cb_timestamp,
+ void* user)
+ {
+ mNotifyCb = notify_cb;
+ mDataCb = data_cb;
+ mDataCbTimestamp = data_cb_timestamp;
+ mCbUser = user;
+
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+
+ if (mDevice->ops->set_callbacks) {
+ mDevice->ops->set_callbacks(mDevice,
+ __notify_cb,
+ __data_cb,
+ __data_cb_timestamp,
+ __get_memory,
+ this);
+ }
+ }
+
+ /**
+ * The following three functions all take a msgtype,
+ * which is a bitmask of the messages defined in
+ * include/ui/Camera.h
+ */
+
+ /**
+ * Enable a message, or set of messages.
+ */
+ void enableMsgType(int32_t msgType)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->enable_msg_type)
+ mDevice->ops->enable_msg_type(mDevice, msgType);
+ }
+
+ /**
+ * Disable a message, or a set of messages.
+ *
+ * Once received a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), camera hal
+ * should not rely on its client to call releaseRecordingFrame() to release
+ * video recording frames sent out by the cameral hal before and after the
+ * disableMsgType(CAMERA_MSG_VIDEO_FRAME) call. Camera hal clients must not
+ * modify/access any video recording frame after calling
+ * disableMsgType(CAMERA_MSG_VIDEO_FRAME).
+ */
+ void disableMsgType(int32_t msgType)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->disable_msg_type)
+ mDevice->ops->disable_msg_type(mDevice, msgType);
+ }
+
+ /**
+ * Query whether a message, or a set of messages, is enabled.
+ * Note that this is operates as an AND, if any of the messages
+ * queried are off, this will return false.
+ */
+ int msgTypeEnabled(int32_t msgType)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->msg_type_enabled)
+ return mDevice->ops->msg_type_enabled(mDevice, msgType);
+ return false;
+ }
+
+ /**
+ * Start preview mode.
+ */
+ status_t startPreview()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->start_preview)
+ return mDevice->ops->start_preview(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Stop a previously started preview.
+ */
+ void stopPreview()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->stop_preview)
+ mDevice->ops->stop_preview(mDevice);
+ }
+
+ /**
+ * Returns true if preview is enabled.
+ */
+ int previewEnabled()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->preview_enabled)
+ return mDevice->ops->preview_enabled(mDevice);
+ return false;
+ }
+
+ /**
+ * Request the camera hal to store meta data or real YUV data in
+ * the video buffers send out via CAMERA_MSG_VIDEO_FRRAME for a
+ * recording session. If it is not called, the default camera
+ * hal behavior is to store real YUV data in the video buffers.
+ *
+ * This method should be called before startRecording() in order
+ * to be effective.
+ *
+ * If meta data is stored in the video buffers, it is up to the
+ * receiver of the video buffers to interpret the contents and
+ * to find the actual frame data with the help of the meta data
+ * in the buffer. How this is done is outside of the scope of
+ * this method.
+ *
+ * Some camera hal may not support storing meta data in the video
+ * buffers, but all camera hal should support storing real YUV data
+ * in the video buffers. If the camera hal does not support storing
+ * the meta data in the video buffers when it is requested to do
+ * do, INVALID_OPERATION must be returned. It is very useful for
+ * the camera hal to pass meta data rather than the actual frame
+ * data directly to the video encoder, since the amount of the
+ * uncompressed frame data can be very large if video size is large.
+ *
+ * @param enable if true to instruct the camera hal to store
+ * meta data in the video buffers; false to instruct
+ * the camera hal to store real YUV data in the video
+ * buffers.
+ *
+ * @return OK on success.
+ */
+
+ status_t storeMetaDataInBuffers(int enable)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->store_meta_data_in_buffers)
+ return mDevice->ops->store_meta_data_in_buffers(mDevice, enable);
+ return enable ? INVALID_OPERATION: OK;
+ }
+
+ /**
+ * Start record mode. When a record image is available a CAMERA_MSG_VIDEO_FRAME
+ * message is sent with the corresponding frame. Every record frame must be released
+ * by a cameral hal client via releaseRecordingFrame() before the client calls
+ * disableMsgType(CAMERA_MSG_VIDEO_FRAME). After the client calls
+ * disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's responsibility
+ * to manage the life-cycle of the video recording frames, and the client must
+ * not modify/access any video recording frames.
+ */
+ status_t startRecording()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->start_recording)
+ return mDevice->ops->start_recording(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Stop a previously started recording.
+ */
+ void stopRecording()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->stop_recording)
+ mDevice->ops->stop_recording(mDevice);
+ }
+
+ /**
+ * Returns true if recording is enabled.
+ */
+ int recordingEnabled()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->recording_enabled)
+ return mDevice->ops->recording_enabled(mDevice);
+ return false;
+ }
+
+ /**
+ * Release a record frame previously returned by CAMERA_MSG_VIDEO_FRAME.
+ *
+ * It is camera hal client's responsibility to release video recording
+ * frames sent out by the camera hal before the camera hal receives
+ * a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME). After it receives
+ * the call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's
+ * responsibility of managing the life-cycle of the video recording
+ * frames.
+ */
+ void releaseRecordingFrame(const sp<IMemory>& mem)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->release_recording_frame) {
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ void *data = ((uint8_t *)heap->base()) + offset;
+ return mDevice->ops->release_recording_frame(mDevice, data);
+ }
+ }
+
+ /**
+ * Start auto focus, the notification callback routine is called
+ * with CAMERA_MSG_FOCUS once when focusing is complete. autoFocus()
+ * will be called again if another auto focus is needed.
+ */
+ status_t autoFocus()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->auto_focus)
+ return mDevice->ops->auto_focus(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Cancels auto-focus function. If the auto-focus is still in progress,
+ * this function will cancel it. Whether the auto-focus is in progress
+ * or not, this function will return the focus position to the default.
+ * If the camera does not support auto-focus, this is a no-op.
+ */
+ status_t cancelAutoFocus()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->cancel_auto_focus)
+ return mDevice->ops->cancel_auto_focus(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Take a picture.
+ */
+ status_t takePicture()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->take_picture)
+ return mDevice->ops->take_picture(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Cancel a picture that was started with takePicture. Calling this
+ * method when no picture is being taken is a no-op.
+ */
+ status_t cancelPicture()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->cancel_picture)
+ return mDevice->ops->cancel_picture(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Set the camera parameters. This returns BAD_VALUE if any parameter is
+ * invalid or not supported. */
+ status_t setParameters(const CameraParameters ¶ms)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->set_parameters)
+ return mDevice->ops->set_parameters(mDevice,
+ params.flatten().string());
+ return INVALID_OPERATION;
+ }
+
+ /** Return the camera parameters. */
+ CameraParameters getParameters() const
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ CameraParameters parms;
+ if (mDevice->ops->get_parameters) {
+ char *temp = mDevice->ops->get_parameters(mDevice);
+ String8 str_parms(temp);
+ free(temp);
+ parms.unflatten(str_parms);
+ }
+ return parms;
+ }
+
+ /**
+ * Send command to camera driver.
+ */
+ status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->send_command)
+ return mDevice->ops->send_command(mDevice, cmd, arg1, arg2);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Release the hardware resources owned by this object. Note that this is
+ * *not* done in the destructor.
+ */
+ void release() {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->release)
+ mDevice->ops->release(mDevice);
+ }
+
+ /**
+ * Dump state of the camera hardware
+ */
+ status_t dump(int fd, const Vector<String16>& args) const
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->dump)
+ return mDevice->ops->dump(mDevice, fd);
+ return OK; // It's fine if the HAL doesn't implement dump()
+ }
+
+private:
+ camera_device_t *mDevice;
+ String8 mName;
+
+ static void __notify_cb(int32_t msg_type, int32_t ext1,
+ int32_t ext2, void *user)
+ {
+ LOGV("%s", __FUNCTION__);
+ CameraHardwareInterface *__this =
+ static_cast<CameraHardwareInterface *>(user);
+ __this->mNotifyCb(msg_type, ext1, ext2, __this->mCbUser);
+ }
+
+ static void __data_cb(int32_t msg_type,
+ const camera_memory_t *data,
+ void *user)
+ {
+ LOGV("%s", __FUNCTION__);
+ CameraHardwareInterface *__this =
+ static_cast<CameraHardwareInterface *>(user);
+ sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
+ __this->mDataCb(msg_type, mem, __this->mCbUser);
+ }
+
+ static void __data_cb_timestamp(nsecs_t timestamp, int32_t msg_type,
+ const camera_memory_t *data,
+ void *user)
+ {
+ LOGV("%s", __FUNCTION__);
+ CameraHardwareInterface *__this =
+ static_cast<CameraHardwareInterface *>(user);
+ // Start refcounting the heap object from here on. When the clients
+ // drop all references, it will be destroyed (as well as the enclosed
+ // MemoryHeapBase.
+ sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
+ __this->mDataCbTimestamp(timestamp, msg_type, mem, __this->mCbUser);
+ }
+
+ // This is a utility class that combines a MemoryHeapBase and a MemoryBase
+ // in one. Since we tend to use them in a one-to-one relationship, this is
+ // handy.
+
+ class CameraHeapMemory : public MemoryBase {
+ public:
+ CameraHeapMemory(size_t size) :
+ MemoryBase(new MemoryHeapBase(size), 0, size)
+ {
+ handle.data = getHeap()->base();
+ handle.size = size;
+ handle.handle = this;
+ }
+
+ camera_memory_t handle;
+ };
+
+ static camera_memory_t* __get_memory(size_t size,
+ void *user __attribute__((unused)))
+ {
+ // We allocate the object here, but we do not assign it to a strong
+ // pointer yet. The HAL will pass it back to us via the data callback
+ // or the data-timestamp callback, and from there on we will wrap it
+ // within a strong pointer.
+
+ CameraHeapMemory *mem = new CameraHeapMemory(size);
+ return &mem->handle;
+ }
+
+ static ANativeWindow *__to_anw(void *user)
+ {
+ CameraHardwareInterface *__this =
+ reinterpret_cast<CameraHardwareInterface *>(user);
+ return __this->mPreviewWindow.get();
+ }
+#define anw(n) __to_anw(((struct camera_preview_window *)n)->user)
+
+ static int __dequeue_buffer(struct preview_stream_ops* w,
+ buffer_handle_t** buffer)
+ {
+ int rc;
+ ANativeWindow *a = anw(w);
+ ANativeWindowBuffer* anb;
+ rc = a->dequeueBuffer(a, &anb);
+ if (!rc) {
+ rc = a->lockBuffer(a, anb);
+ if (!rc)
+ *buffer = &anb->handle;
+ else
+ a->cancelBuffer(a, anb);
+ }
+ return rc;
+ }
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type *) 0)->member) *__mptr = (ptr); \
+ (type *) ((char *) __mptr - (char *)(&((type *)0)->member)); })
+#endif
+
+ static int __enqueue_buffer(struct preview_stream_ops* w,
+ buffer_handle_t* buffer)
+ {
+ ANativeWindow *a = anw(w);
+ return a->queueBuffer(a,
+ container_of(buffer, ANativeWindowBuffer, handle));
+ }
+
+ static int __cancel_buffer(struct preview_stream_ops* w,
+ buffer_handle_t* buffer)
+ {
+ ANativeWindow *a = anw(w);
+ return a->cancelBuffer(a,
+ container_of(buffer, ANativeWindowBuffer, handle));
+ }
+
+ static int __set_buffer_count(struct preview_stream_ops* w, int count)
+ {
+ ANativeWindow *a = anw(w);
+ return native_window_set_buffer_count(a, count);
+ }
+
+ static int __set_buffers_geometry(struct preview_stream_ops* w,
+ int width, int height, int format)
+ {
+ ANativeWindow *a = anw(w);
+ return native_window_set_buffers_geometry(a,
+ width, height, format);
+ }
+
+ static int __set_crop(struct preview_stream_ops *w,
+ int left, int top, int right, int bottom)
+ {
+ ANativeWindow *a = anw(w);
+ android_native_rect_t crop;
+ crop.left = left;
+ crop.top = top;
+ crop.right = right;
+ crop.bottom = bottom;
+ return native_window_set_crop(a, &crop);
+ }
+
+ static int __set_usage(struct preview_stream_ops* w, int usage)
+ {
+ ANativeWindow *a = anw(w);
+ return native_window_set_usage(a, usage);
+ }
+
+ static int __set_swap_interval(struct preview_stream_ops *w, int interval)
+ {
+ ANativeWindow *a = anw(w);
+ return a->setSwapInterval(a, interval);
+ }
+
+ static int __get_min_undequeued_buffer_count(
+ const struct preview_stream_ops *w,
+ int *count)
+ {
+ ANativeWindow *a = anw(w);
+ return a->query(a, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, count);
+ }
+
+ void initHalPreviewWindow()
+ {
+ mHalPreviewWindow.nw.cancel_buffer = __cancel_buffer;
+ mHalPreviewWindow.nw.dequeue_buffer = __dequeue_buffer;
+ mHalPreviewWindow.nw.enqueue_buffer = __enqueue_buffer;
+ mHalPreviewWindow.nw.set_buffer_count = __set_buffer_count;
+ mHalPreviewWindow.nw.set_buffers_geometry = __set_buffers_geometry;
+ mHalPreviewWindow.nw.set_crop = __set_crop;
+ mHalPreviewWindow.nw.set_usage = __set_usage;
+ mHalPreviewWindow.nw.set_swap_interval = __set_swap_interval;
+
+ mHalPreviewWindow.nw.get_min_undequeued_buffer_count =
+ __get_min_undequeued_buffer_count;
+ }
+
+ sp<ANativeWindow> mPreviewWindow;
+
+ struct camera_preview_window {
+ struct preview_stream_ops nw;
+ void *user;
+ };
+
+ struct camera_preview_window mHalPreviewWindow;
+
+ notify_callback mNotifyCb;
+ data_callback mDataCb;
+ data_callback_timestamp mDataCbTimestamp;
+ void *mCbUser;
+};
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 3d8ca7a..1e8c30b 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -16,6 +16,7 @@
*/
#define LOG_TAG "CameraService"
+//#define LOG_NDEBUG 0
#include <stdio.h>
#include <sys/types.h>
@@ -37,6 +38,7 @@
#include <utils/String16.h>
#include "CameraService.h"
+#include "CameraHardwareInterface.h"
namespace android {
@@ -69,24 +71,34 @@
static CameraService *gCameraService;
CameraService::CameraService()
-:mSoundRef(0)
+:mSoundRef(0), mModule(0)
{
LOGI("CameraService started (pid=%d)", getpid());
-
- mNumberOfCameras = HAL_getNumberOfCameras();
- if (mNumberOfCameras > MAX_CAMERAS) {
- LOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
- mNumberOfCameras, MAX_CAMERAS);
- mNumberOfCameras = MAX_CAMERAS;
- }
-
- for (int i = 0; i < mNumberOfCameras; i++) {
- setCameraFree(i);
- }
-
gCameraService = this;
}
+void CameraService::onFirstRef()
+{
+ BnCameraService::onFirstRef();
+
+ if (hw_get_module(CAMERA_HARDWARE_MODULE_ID,
+ (const hw_module_t **)&mModule) < 0) {
+ LOGE("Could not load camera HAL module");
+ mNumberOfCameras = 0;
+ }
+ else {
+ mNumberOfCameras = mModule->get_number_of_cameras();
+ if (mNumberOfCameras > MAX_CAMERAS) {
+ LOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
+ mNumberOfCameras, MAX_CAMERAS);
+ mNumberOfCameras = MAX_CAMERAS;
+ }
+ for (int i = 0; i < mNumberOfCameras; i++) {
+ setCameraFree(i);
+ }
+ }
+}
+
CameraService::~CameraService() {
for (int i = 0; i < mNumberOfCameras; i++) {
if (mBusy[i]) {
@@ -103,12 +115,19 @@
status_t CameraService::getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo) {
+ if (!mModule) {
+ return NO_INIT;
+ }
+
if (cameraId < 0 || cameraId >= mNumberOfCameras) {
return BAD_VALUE;
}
- HAL_getCameraInfo(cameraId, cameraInfo);
- return OK;
+ struct camera_info info;
+ status_t rc = mModule->get_camera_info(cameraId, &info);
+ cameraInfo->facing = info.facing;
+ cameraInfo->orientation = info.orientation;
+ return rc;
}
sp<ICamera> CameraService::connect(
@@ -116,6 +135,11 @@
int callingPid = getCallingPid();
LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId);
+ if (!mModule) {
+ LOGE("Camera HAL module not loaded");
+ return NULL;
+ }
+
sp<Client> client;
if (cameraId < 0 || cameraId >= mNumberOfCameras) {
LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",
@@ -146,15 +170,19 @@
return NULL;
}
- sp<CameraHardwareInterface> hardware = HAL_openCameraHardware(cameraId);
- if (hardware == NULL) {
- LOGE("Fail to open camera hardware (id=%d)", cameraId);
+ struct camera_info info;
+ if (mModule->get_camera_info(cameraId, &info) != OK) {
+ LOGE("Invalid camera id %d", cameraId);
return NULL;
}
- CameraInfo info;
- HAL_getCameraInfo(cameraId, &info);
- client = new Client(this, cameraClient, hardware, cameraId, info.facing,
- callingPid);
+
+ char camera_device_name[10];
+ snprintf(camera_device_name, sizeof(camera_device_name), "%d", cameraId);
+
+ client = new Client(this, cameraClient,
+ new CameraHardwareInterface(&mModule->common,
+ camera_device_name),
+ cameraId, info.facing, callingPid);
mClient[cameraId] = client;
LOG1("CameraService::connect X");
return client;
@@ -244,7 +272,7 @@
static MediaPlayer* newMediaPlayer(const char *file) {
MediaPlayer* mp = new MediaPlayer();
if (mp->setDataSource(file, NULL) == NO_ERROR) {
- mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE);
+ mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE);
mp->prepare();
} else {
LOGE("Failed to load CameraService sounds: %s", file);
@@ -283,7 +311,7 @@
// do not play the sound if stream volume is 0
// (typically because ringer mode is silent).
int index;
- AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index);
+ AudioSystem::getStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, &index);
if (index != 0) {
player->seekTo(0);
player->start();
@@ -320,7 +348,7 @@
CAMERA_MSG_FOCUS);
// Callback is disabled by default
- mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
+ mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);
mPlayShutterSound = true;
cameraService->setCameraBusy(cameraId);
@@ -410,7 +438,7 @@
return NO_ERROR;
}
- mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
+ mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
mClientPid = callingPid;
mCameraClient = client;
@@ -472,15 +500,15 @@
result = NO_ERROR;
// return if no change in surface.
- // asBinder() is safe on NULL (returns NULL)
- if (getISurface(surface)->asBinder() == mSurface) {
+ sp<IBinder> binder(surface != 0 ? surface->asBinder() : 0);
+ if (binder == mSurface) {
return result;
}
if (mSurface != 0) {
LOG1("clearing old preview surface %p", mSurface.get());
}
- mSurface = getISurface(surface)->asBinder();
+ mSurface = binder;
mPreviewWindow = surface;
// If preview has been already started, register preview
@@ -543,7 +571,7 @@
if (checkPidAndHardware() != NO_ERROR) return;
mPreviewCallbackFlag = callback_flag;
- if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK) {
+ if (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) {
enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
} else {
disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
@@ -666,20 +694,6 @@
mHardware->releaseRecordingFrame(mem);
}
-int32_t CameraService::Client::getNumberOfVideoBuffers() const {
- LOG1("getNumberOfVideoBuffers");
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return 0;
- return mHardware->getNumberOfVideoBuffers();
-}
-
-sp<IMemory> CameraService::Client::getVideoBuffer(int32_t index) const {
- LOG1("getVideoBuffer: %d", index);
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return 0;
- return mHardware->getVideoBuffer(index);
-}
-
status_t CameraService::Client::storeMetaDataInBuffers(bool enabled)
{
LOG1("storeMetaDataInBuffers: %s", enabled? "true": "false");
@@ -727,17 +741,30 @@
}
// take a picture - image is returned in callback
-status_t CameraService::Client::takePicture() {
- LOG1("takePicture (pid %d)", getCallingPid());
+status_t CameraService::Client::takePicture(int msgType) {
+ LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType);
Mutex::Autolock lock(mLock);
status_t result = checkPidAndHardware();
if (result != NO_ERROR) return result;
- enableMsgType(CAMERA_MSG_SHUTTER |
- CAMERA_MSG_POSTVIEW_FRAME |
- CAMERA_MSG_RAW_IMAGE |
- CAMERA_MSG_COMPRESSED_IMAGE);
+ if ((msgType & CAMERA_MSG_RAW_IMAGE) &&
+ (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
+ LOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
+ " cannot be both enabled");
+ return BAD_VALUE;
+ }
+
+ // We only accept picture related message types
+ // and ignore other types of messages for takePicture().
+ int picMsgType = msgType
+ & (CAMERA_MSG_SHUTTER |
+ CAMERA_MSG_POSTVIEW_FRAME |
+ CAMERA_MSG_RAW_IMAGE |
+ CAMERA_MSG_RAW_IMAGE_NOTIFY |
+ CAMERA_MSG_COMPRESSED_IMAGE);
+
+ enableMsgType(picMsgType);
return mHardware->takePicture();
}
@@ -925,7 +952,7 @@
switch (msgType) {
case CAMERA_MSG_SHUTTER:
// ext1 is the dimension of the yuv picture.
- client->handleShutter((image_rect_type *)ext1);
+ client->handleShutter();
break;
default:
client->handleGenericNotify(msgType, ext1, ext2);
@@ -984,9 +1011,7 @@
}
// snapshot taken callback
-// "size" is the width and height of yuv picture for registerBuffer.
-// If it is NULL, use the picture size from parameters.
-void CameraService::Client::handleShutter(image_rect_type *size) {
+void CameraService::Client::handleShutter(void) {
if (mPlayShutterSound) {
mCameraService->playSound(SOUND_SHUTTER);
}
@@ -1012,7 +1037,7 @@
int flags = mPreviewCallbackFlag;
// is callback enabled?
- if (!(flags & FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
+ if (!(flags & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
// If the enable bit is off, the copy-out and one-shot bits are ignored
LOG2("frame callback is disabled");
mLock.unlock();
@@ -1023,17 +1048,17 @@
sp<ICameraClient> c = mCameraClient;
// clear callback flags if no client or one-shot mode
- if (c == 0 || (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
+ if (c == 0 || (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
LOG2("Disable preview callback");
- mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
- FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
- FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ mPreviewCallbackFlag &= ~(CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
+ CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
+ CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);
disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
}
if (c != 0) {
// Is the received frame copied out or not?
- if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
+ if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
LOG2("frame is copied");
copyFrameAndPostCopiedFrame(c, heap, offset, size);
} else {
@@ -1244,12 +1269,4 @@
return NO_ERROR;
}
-sp<ISurface> CameraService::getISurface(const sp<Surface>& surface) {
- if (surface != 0) {
- return surface->getISurface();
- } else {
- return sp<ISurface>(0);
- }
-}
-
}; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index ccb9cf7..5e2d571 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -19,9 +19,8 @@
#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
#include <binder/BinderService.h>
-
#include <camera/ICameraService.h>
-#include <camera/CameraHardwareInterface.h>
+#include <hardware/camera.h>
/* This needs to be increased if we can have more cameras */
#define MAX_CAMERAS 2
@@ -30,6 +29,7 @@
class MemoryHeapBase;
class MediaPlayer;
+class CameraHardwareInterface;
class CameraService :
public BinderService<CameraService>,
@@ -53,6 +53,7 @@
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
+ virtual void onFirstRef();
enum sound_kind {
SOUND_SHUTTER = 0,
@@ -79,12 +80,6 @@
sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS];
int mSoundRef; // reference count (release all MediaPlayer when 0)
- // Used by Client objects to extract the ISurface from a Surface object.
- // This is used because making Client a friend class of Surface would
- // require including this header in Surface.h since Client is a nested
- // class.
- static sp<ISurface> getISurface(const sp<Surface>& surface);
-
class Client : public BnCamera
{
public:
@@ -99,8 +94,6 @@
virtual status_t startPreview();
virtual void stopPreview();
virtual bool previewEnabled();
- virtual int32_t getNumberOfVideoBuffers() const;
- virtual sp<IMemory> getVideoBuffer(int32_t index) const;
virtual status_t storeMetaDataInBuffers(bool enabled);
virtual status_t startRecording();
virtual void stopRecording();
@@ -108,7 +101,7 @@
virtual void releaseRecordingFrame(const sp<IMemory>& mem);
virtual status_t autoFocus();
virtual status_t cancelAutoFocus();
- virtual status_t takePicture();
+ virtual status_t takePicture(int msgType);
virtual status_t setParameters(const String8& params);
virtual String8 getParameters() const;
virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
@@ -152,7 +145,7 @@
// convert client from cookie
static sp<Client> getClientFromCookie(void* user);
// handlers for messages
- void handleShutter(image_rect_type *size);
+ void handleShutter(void);
void handlePreviewData(const sp<IMemory>& mem);
void handlePostview(const sp<IMemory>& mem);
void handleRawPicture(const sp<IMemory>& mem);
@@ -207,6 +200,8 @@
// is found to be disabled. It returns true if mLock is grabbed.
bool lockIfMessageWanted(int32_t msgType);
};
+
+ camera_module_t *mModule;
};
} // namespace android
diff --git a/services/camera/libcameraservice/CannedJpeg.h b/services/camera/libcameraservice/CannedJpeg.h
index b6266fb..6dd99c1 100644
--- a/services/camera/libcameraservice/CannedJpeg.h
+++ b/services/camera/libcameraservice/CannedJpeg.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
const int kCannedJpegWidth = 320;
const int kCannedJpegHeight = 240;
const int kCannedJpegSize = 8733;
diff --git a/services/camera/tests/CameraServiceTest/Android.mk b/services/camera/tests/CameraServiceTest/Android.mk
index cf4e42f..cf7302a 100644
--- a/services/camera/tests/CameraServiceTest/Android.mk
+++ b/services/camera/tests/CameraServiceTest/Android.mk
@@ -19,7 +19,7 @@
libutils \
libui \
libcamera_client \
- libsurfaceflinger_client
+ libgui
# Disable it because the ISurface interface may change, and before we have a
# chance to fix this test, we don't want to break normal builds.
diff --git a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
index 3c8d553..f86ca47 100644
--- a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
+++ b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 "CameraServiceTest"
#include <stdio.h>
@@ -814,10 +830,10 @@
ASSERT(c->previewEnabled() == true);
sleep(2);
c->stopPreview();
- if ((v & FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) {
+ if ((v & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) {
cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 0);
} else {
- if ((v & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) == 0) {
+ if ((v & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) == 0) {
cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 10);
} else {
cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 1);
@@ -833,7 +849,7 @@
ASSERT(c->recordingEnabled() == false);
sp<MSurface> surface = new MSurface();
ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
- c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ c->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);
cc->setReleaser(c.get());
c->startRecording();
ASSERT(c->recordingEnabled() == true);
@@ -854,7 +870,7 @@
CameraParameters param(c->getParameters());
param.setPreviewSize(w, h);
- c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ c->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);
c->setParameters(param.flatten());
c->startPreview();