Merge "Simplify AudioTrack stream end and fix race"
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index f447c5b..6b726e0 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -25,6 +25,9 @@
namespace android {
+#define ALIGN_TO(val, alignment) \
+ (((uintptr_t)(val) + ((alignment) - 1)) & ~((alignment) - 1))
+
typedef Parcel::WritableBlob WritableBlob;
typedef Parcel::ReadableBlob ReadableBlob;
@@ -133,11 +136,19 @@
}
status_t CameraMetadata::append(const CameraMetadata &other) {
+ return append(other.mBuffer);
+}
+
+status_t CameraMetadata::append(const camera_metadata_t* other) {
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
}
- return append_camera_metadata(mBuffer, other.mBuffer);
+ size_t extraEntries = get_camera_metadata_entry_count(other);
+ size_t extraData = get_camera_metadata_data_count(other);
+ resizeIfNeeded(extraEntries, extraData);
+
+ return append_camera_metadata(mBuffer, other);
}
size_t CameraMetadata::entryCount() const {
@@ -423,40 +434,70 @@
*out = NULL;
}
- // arg0 = metadataSize (int32)
- int32_t metadataSizeTmp = -1;
- if ((err = data.readInt32(&metadataSizeTmp)) != OK) {
+ // See CameraMetadata::writeToParcel for parcel data layout diagram and explanation.
+ // arg0 = blobSize (int32)
+ int32_t blobSizeTmp = -1;
+ if ((err = data.readInt32(&blobSizeTmp)) != OK) {
ALOGE("%s: Failed to read metadata size (error %d %s)",
__FUNCTION__, err, strerror(-err));
return err;
}
- const size_t metadataSize = static_cast<size_t>(metadataSizeTmp);
+ const size_t blobSize = static_cast<size_t>(blobSizeTmp);
+ const size_t alignment = get_camera_metadata_alignment();
- if (metadataSize == 0) {
+ // Special case: zero blob size means zero sized (NULL) metadata.
+ if (blobSize == 0) {
ALOGV("%s: Read 0-sized metadata", __FUNCTION__);
return OK;
}
- // NOTE: this doesn't make sense to me. shouldnt the blob
+ if (blobSize <= alignment) {
+ ALOGE("%s: metadata blob is malformed, blobSize(%zu) should be larger than alignment(%zu)",
+ __FUNCTION__, blobSize, alignment);
+ return BAD_VALUE;
+ }
+
+ const size_t metadataSize = blobSize - alignment;
+
+ // NOTE: this doesn't make sense to me. shouldn't the blob
// know how big it is? why do we have to specify the size
// to Parcel::readBlob ?
-
ReadableBlob blob;
// arg1 = metadata (blob)
do {
- if ((err = data.readBlob(metadataSize, &blob)) != OK) {
- ALOGE("%s: Failed to read metadata blob (sized %d). Possible "
+ if ((err = data.readBlob(blobSize, &blob)) != OK) {
+ ALOGE("%s: Failed to read metadata blob (sized %zu). Possible "
" serialization bug. Error %d %s",
- __FUNCTION__, metadataSize, err, strerror(-err));
+ __FUNCTION__, blobSize, err, strerror(-err));
break;
}
- const camera_metadata_t* tmp =
- reinterpret_cast<const camera_metadata_t*>(blob.data());
+ // arg2 = offset (blob)
+ // Must be after blob since we don't know offset until after writeBlob.
+ int32_t offsetTmp;
+ if ((err = data.readInt32(&offsetTmp)) != OK) {
+ ALOGE("%s: Failed to read metadata offsetTmp (error %d %s)",
+ __FUNCTION__, err, strerror(-err));
+ break;
+ }
+ const size_t offset = static_cast<size_t>(offsetTmp);
+ if (offset >= alignment) {
+ ALOGE("%s: metadata offset(%zu) should be less than alignment(%zu)",
+ __FUNCTION__, blobSize, alignment);
+ err = BAD_VALUE;
+ break;
+ }
+
+ const uintptr_t metadataStart = reinterpret_cast<uintptr_t>(blob.data()) + offset;
+ const camera_metadata_t* tmp =
+ reinterpret_cast<const camera_metadata_t*>(metadataStart);
+ ALOGV("%s: alignment is: %zu, metadata start: %p, offset: %zu",
+ __FUNCTION__, alignment, tmp, offset);
metadata = allocate_copy_camera_metadata_checked(tmp, metadataSize);
if (metadata == NULL) {
// We consider that allocation only fails if the validation
// also failed, therefore the readFromParcel was a failure.
+ ALOGE("%s: metadata allocation and copy failed", __FUNCTION__);
err = BAD_VALUE;
}
} while(0);
@@ -477,38 +518,79 @@
const camera_metadata_t* metadata) {
status_t res = OK;
- // arg0 = metadataSize (int32)
+ /**
+ * Below is the camera metadata parcel layout:
+ *
+ * |--------------------------------------------|
+ * | arg0: blobSize |
+ * | (length = 4) |
+ * |--------------------------------------------|<--Skip the rest if blobSize == 0.
+ * | |
+ * | |
+ * | arg1: blob |
+ * | (length = variable, see arg1 layout below) |
+ * | |
+ * | |
+ * |--------------------------------------------|
+ * | arg2: offset |
+ * | (length = 4) |
+ * |--------------------------------------------|
+ */
+ // arg0 = blobSize (int32)
if (metadata == NULL) {
+ // Write zero blobSize for null metadata.
return data.writeInt32(0);
}
+ /**
+ * Always make the blob size sufficiently larger, as we need put alignment
+ * padding and metadata into the blob. Since we don't know the alignment
+ * offset before writeBlob. Then write the metadata to aligned offset.
+ */
const size_t metadataSize = get_camera_metadata_compact_size(metadata);
- res = data.writeInt32(static_cast<int32_t>(metadataSize));
+ const size_t alignment = get_camera_metadata_alignment();
+ const size_t blobSize = metadataSize + alignment;
+ res = data.writeInt32(static_cast<int32_t>(blobSize));
if (res != OK) {
return res;
}
- // arg1 = metadata (blob)
+ size_t offset = 0;
+ /**
+ * arg1 = metadata (blob).
+ *
+ * The blob size is the sum of front padding size, metadata size and back padding
+ * size, which is equal to metadataSize + alignment.
+ *
+ * The blob layout is:
+ * |------------------------------------|<----Start address of the blob (unaligned).
+ * | front padding |
+ * | (size = offset) |
+ * |------------------------------------|<----Aligned start address of metadata.
+ * | |
+ * | |
+ * | metadata |
+ * | (size = metadataSize) |
+ * | |
+ * | |
+ * |------------------------------------|
+ * | back padding |
+ * | (size = alignment - offset) |
+ * |------------------------------------|<----End address of blob.
+ * (Blob start address + blob size).
+ */
WritableBlob blob;
do {
- res = data.writeBlob(metadataSize, &blob);
+ res = data.writeBlob(blobSize, &blob);
if (res != OK) {
break;
}
- copy_camera_metadata(blob.data(), metadataSize, metadata);
-
- IF_ALOGV() {
- if (validate_camera_metadata_structure(
- (const camera_metadata_t*)blob.data(),
- &metadataSize) != OK) {
- ALOGV("%s: Failed to validate metadata %p after writing blob",
- __FUNCTION__, blob.data());
- } else {
- ALOGV("%s: Metadata written to blob. Validation success",
- __FUNCTION__);
- }
- }
+ const uintptr_t metadataStart = ALIGN_TO(blob.data(), alignment);
+ offset = metadataStart - reinterpret_cast<uintptr_t>(blob.data());
+ ALOGV("%s: alignment is: %zu, metadata start: %p, offset: %zu",
+ __FUNCTION__, alignment, metadataStart, offset);
+ copy_camera_metadata(reinterpret_cast<void*>(metadataStart), metadataSize, metadata);
// Not too big of a problem since receiving side does hard validation
// Don't check the size since the compact size could be larger
@@ -520,6 +602,9 @@
} while(false);
blob.release();
+ // arg2 = offset (int32)
+ res = data.writeInt32(static_cast<int32_t>(offset));
+
return res;
}
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index c51f265..af091f4 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -470,7 +470,7 @@
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
- snprintf(buffer, 255, "CameraParameters::dump: mMap.size = %d\n", mMap.size());
+ snprintf(buffer, 255, "CameraParameters::dump: mMap.size = %zu\n", mMap.size());
result.append(buffer);
for (size_t i = 0; i < mMap.size(); i++) {
String8 k, v;
diff --git a/camera/IProCameraCallbacks.cpp b/camera/IProCameraCallbacks.cpp
index 0fdb85a..bd3d420 100644
--- a/camera/IProCameraCallbacks.cpp
+++ b/camera/IProCameraCallbacks.cpp
@@ -67,11 +67,11 @@
IBinder::FLAG_ONEWAY);
}
- void onResultReceived(int32_t frameId, camera_metadata* result) {
+ void onResultReceived(int32_t requestId, camera_metadata* result) {
ALOGV("onResultReceived");
Parcel data, reply;
data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
- data.writeInt32(frameId);
+ data.writeInt32(requestId);
CameraMetadata::writeToParcel(data, result);
remote()->transact(RESULT_RECEIVED, data, &reply, IBinder::FLAG_ONEWAY);
}
@@ -107,10 +107,10 @@
case RESULT_RECEIVED: {
ALOGV("RESULT_RECEIVED");
CHECK_INTERFACE(IProCameraCallbacks, data, reply);
- int32_t frameId = data.readInt32();
+ int32_t requestId = data.readInt32();
camera_metadata_t *result = NULL;
CameraMetadata::readFromParcel(data, &result);
- onResultReceived(frameId, result);
+ onResultReceived(requestId, result);
return NO_ERROR;
break;
}
diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp
index 577c760..ba5a48c 100644
--- a/camera/ProCamera.cpp
+++ b/camera/ProCamera.cpp
@@ -90,8 +90,8 @@
}
}
-void ProCamera::onResultReceived(int32_t frameId, camera_metadata* result) {
- ALOGV("%s: frameId = %d, result = %p", __FUNCTION__, frameId, result);
+void ProCamera::onResultReceived(int32_t requestId, camera_metadata* result) {
+ ALOGV("%s: requestId = %d, result = %p", __FUNCTION__, requestId, result);
sp<ProCameraListener> listener;
{
@@ -112,7 +112,7 @@
result = tmp.release();
if (listener != NULL) {
- listener->onResultReceived(frameId, result);
+ listener->onResultReceived(requestId, result);
} else {
free_camera_metadata(result);
}
diff --git a/camera/camera2/ICameraDeviceCallbacks.cpp b/camera/camera2/ICameraDeviceCallbacks.cpp
index 3cec1f4..613358a 100644
--- a/camera/camera2/ICameraDeviceCallbacks.cpp
+++ b/camera/camera2/ICameraDeviceCallbacks.cpp
@@ -32,7 +32,9 @@
namespace android {
enum {
- NOTIFY_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
+ CAMERA_ERROR = IBinder::FIRST_CALL_TRANSACTION,
+ CAMERA_IDLE,
+ CAPTURE_STARTED,
RESULT_RECEIVED,
};
@@ -44,19 +46,37 @@
{
}
- // generic callback from camera service to app
- void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
+ void onDeviceError(CameraErrorCode errorCode)
{
- ALOGV("notifyCallback");
+ ALOGV("onDeviceError");
Parcel data, reply;
data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
- data.writeInt32(msgType);
- data.writeInt32(ext1);
- data.writeInt32(ext2);
- remote()->transact(NOTIFY_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY);
+ data.writeInt32(static_cast<int32_t>(errorCode));
+ remote()->transact(CAMERA_ERROR, data, &reply, IBinder::FLAG_ONEWAY);
data.writeNoException();
}
+ void onDeviceIdle()
+ {
+ ALOGV("onDeviceIdle");
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
+ remote()->transact(CAMERA_IDLE, data, &reply, IBinder::FLAG_ONEWAY);
+ data.writeNoException();
+ }
+
+ void onCaptureStarted(int32_t requestId, int64_t timestamp)
+ {
+ ALOGV("onCaptureStarted");
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
+ data.writeInt32(requestId);
+ data.writeInt64(timestamp);
+ remote()->transact(CAPTURE_STARTED, data, &reply, IBinder::FLAG_ONEWAY);
+ data.writeNoException();
+ }
+
+
void onResultReceived(int32_t requestId, const CameraMetadata& result) {
ALOGV("onResultReceived");
Parcel data, reply;
@@ -79,18 +99,33 @@
{
ALOGV("onTransact - code = %d", code);
switch(code) {
- case NOTIFY_CALLBACK: {
- ALOGV("NOTIFY_CALLBACK");
+ case CAMERA_ERROR: {
+ ALOGV("onDeviceError");
CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
- int32_t msgType = data.readInt32();
- int32_t ext1 = data.readInt32();
- int32_t ext2 = data.readInt32();
- notifyCallback(msgType, ext1, ext2);
+ CameraErrorCode errorCode =
+ static_cast<CameraErrorCode>(data.readInt32());
+ onDeviceError(errorCode);
+ data.readExceptionCode();
+ return NO_ERROR;
+ } break;
+ case CAMERA_IDLE: {
+ ALOGV("onDeviceIdle");
+ CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
+ onDeviceIdle();
+ data.readExceptionCode();
+ return NO_ERROR;
+ } break;
+ case CAPTURE_STARTED: {
+ ALOGV("onCaptureStarted");
+ CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
+ int32_t requestId = data.readInt32();
+ int64_t timestamp = data.readInt64();
+ onCaptureStarted(requestId, timestamp);
data.readExceptionCode();
return NO_ERROR;
} break;
case RESULT_RECEIVED: {
- ALOGV("RESULT_RECEIVED");
+ ALOGV("onResultReceived");
CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
int32_t requestId = data.readInt32();
CameraMetadata result;
@@ -102,8 +137,7 @@
onResultReceived(requestId, result);
data.readExceptionCode();
return NO_ERROR;
- break;
- }
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
index e9aa99d..1f5867a 100644
--- a/camera/tests/ProCameraTests.cpp
+++ b/camera/tests/ProCameraTests.cpp
@@ -284,9 +284,9 @@
}
}
- virtual void onResultReceived(int32_t frameId,
+ virtual void onResultReceived(int32_t requestId,
camera_metadata* request) {
- dout << "Result received frameId = " << frameId
+ dout << "Result received requestId = " << requestId
<< ", requestPtr = " << (void*)request << std::endl;
QueueEvent(RESULT_RECEIVED);
free_camera_metadata(request);
@@ -1276,4 +1276,3 @@
}
}
}
-
diff --git a/cmds/screenrecord/Android.mk b/cmds/screenrecord/Android.mk
index b4a5947..6747e60 100644
--- a/cmds/screenrecord/Android.mk
+++ b/cmds/screenrecord/Android.mk
@@ -18,10 +18,15 @@
LOCAL_SRC_FILES := \
screenrecord.cpp \
+ EglWindow.cpp \
+ FrameOutput.cpp \
+ TextRenderer.cpp \
+ Overlay.cpp \
+ Program.cpp
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia libutils libbinder libstagefright_foundation \
- libjpeg libgui libcutils liblog
+ libjpeg libgui libcutils liblog libEGL libGLESv2
LOCAL_C_INCLUDES := \
frameworks/av/media/libstagefright \
@@ -30,6 +35,7 @@
external/jpeg
LOCAL_CFLAGS += -Wno-multichar
+#LOCAL_CFLAGS += -UNDEBUG
LOCAL_MODULE_TAGS := optional
diff --git a/cmds/screenrecord/EglWindow.cpp b/cmds/screenrecord/EglWindow.cpp
new file mode 100644
index 0000000..c16f2ad
--- /dev/null
+++ b/cmds/screenrecord/EglWindow.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2013 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 "ScreenRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <gui/BufferQueue.h>
+#include <gui/GraphicBufferAlloc.h>
+#include <gui/Surface.h>
+
+#include "EglWindow.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <assert.h>
+
+using namespace android;
+
+
+status_t EglWindow::createWindow(const sp<IGraphicBufferProducer>& surface) {
+ if (mEglSurface != EGL_NO_SURFACE) {
+ ALOGE("surface already created");
+ return UNKNOWN_ERROR;
+ }
+ status_t err = eglSetupContext(false);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ // Cache the current dimensions. We're not expecting these to change.
+ surface->query(NATIVE_WINDOW_WIDTH, &mWidth);
+ surface->query(NATIVE_WINDOW_HEIGHT, &mHeight);
+
+ // Output side (EGL surface to draw on).
+ sp<ANativeWindow> anw = new Surface(surface);
+ mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, anw.get(),
+ NULL);
+ if (mEglSurface == EGL_NO_SURFACE) {
+ ALOGE("eglCreateWindowSurface error: %#x", eglGetError());
+ eglRelease();
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+status_t EglWindow::createPbuffer(int width, int height) {
+ if (mEglSurface != EGL_NO_SURFACE) {
+ ALOGE("surface already created");
+ return UNKNOWN_ERROR;
+ }
+ status_t err = eglSetupContext(true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ mWidth = width;
+ mHeight = height;
+
+ EGLint pbufferAttribs[] = {
+ EGL_WIDTH, width,
+ EGL_HEIGHT, height,
+ EGL_NONE
+ };
+ mEglSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, pbufferAttribs);
+ if (mEglSurface == EGL_NO_SURFACE) {
+ ALOGE("eglCreatePbufferSurface error: %#x", eglGetError());
+ eglRelease();
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+status_t EglWindow::makeCurrent() const {
+ if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ ALOGE("eglMakeCurrent failed: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ return NO_ERROR;
+}
+
+status_t EglWindow::eglSetupContext(bool forPbuffer) {
+ EGLBoolean result;
+
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ ALOGE("eglGetDisplay failed: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ EGLint majorVersion, minorVersion;
+ result = eglInitialize(mEglDisplay, &majorVersion, &minorVersion);
+ if (result != EGL_TRUE) {
+ ALOGE("eglInitialize failed: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ ALOGV("Initialized EGL v%d.%d", majorVersion, minorVersion);
+
+ EGLint numConfigs = 0;
+ EGLint windowConfigAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RECORDABLE_ANDROID, 1,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ // no alpha
+ EGL_NONE
+ };
+ EGLint pbufferConfigAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_NONE
+ };
+ result = eglChooseConfig(mEglDisplay,
+ forPbuffer ? pbufferConfigAttribs : windowConfigAttribs,
+ &mEglConfig, 1, &numConfigs);
+ if (result != EGL_TRUE) {
+ ALOGE("eglChooseConfig error: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ EGLint contextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+ mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT,
+ contextAttribs);
+ if (mEglContext == EGL_NO_CONTEXT) {
+ ALOGE("eglCreateContext error: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+void EglWindow::eglRelease() {
+ ALOGV("EglWindow::eglRelease");
+ if (mEglDisplay != EGL_NO_DISPLAY) {
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+
+ if (mEglContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEglDisplay, mEglContext);
+ }
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ }
+ }
+
+ mEglDisplay = EGL_NO_DISPLAY;
+ mEglContext = EGL_NO_CONTEXT;
+ mEglSurface = EGL_NO_SURFACE;
+ mEglConfig = NULL;
+
+ eglReleaseThread();
+}
+
+// Sets the presentation time on the current EGL buffer.
+void EglWindow::presentationTime(nsecs_t whenNsec) const {
+ eglPresentationTimeANDROID(mEglDisplay, mEglSurface, whenNsec);
+}
+
+// Swaps the EGL buffer.
+void EglWindow::swapBuffers() const {
+ eglSwapBuffers(mEglDisplay, mEglSurface);
+}
diff --git a/cmds/screenrecord/EglWindow.h b/cmds/screenrecord/EglWindow.h
new file mode 100644
index 0000000..69d0c31
--- /dev/null
+++ b/cmds/screenrecord/EglWindow.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 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 SCREENRECORD_EGL_WINDOW_H
+#define SCREENRECORD_EGL_WINDOW_H
+
+#include <gui/BufferQueue.h>
+#include <utils/Errors.h>
+
+#include <EGL/egl.h>
+
+namespace android {
+
+/*
+ * Wraps EGL display, context, surface, config for a window surface.
+ *
+ * Not thread safe.
+ */
+class EglWindow {
+public:
+ EglWindow() :
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+ mEglSurface(EGL_NO_SURFACE),
+ mEglConfig(NULL),
+ mWidth(0),
+ mHeight(0)
+ {}
+ ~EglWindow() { eglRelease(); }
+
+ // Creates an EGL window for the supplied surface.
+ status_t createWindow(const sp<IGraphicBufferProducer>& surface);
+
+ // Creates an EGL pbuffer surface.
+ status_t createPbuffer(int width, int height);
+
+ // Return width and height values (obtained from IGBP).
+ int getWidth() const { return mWidth; }
+ int getHeight() const { return mHeight; }
+
+ // Release anything we created.
+ void release() { eglRelease(); }
+
+ // Make this context current.
+ status_t makeCurrent() const;
+
+ // Sets the presentation time on the current EGL buffer.
+ void presentationTime(nsecs_t whenNsec) const;
+
+ // Swaps the EGL buffer.
+ void swapBuffers() const;
+
+private:
+ EglWindow(const EglWindow&);
+ EglWindow& operator=(const EglWindow&);
+
+ // Init display, create config and context.
+ status_t eglSetupContext(bool forPbuffer);
+ void eglRelease();
+
+ // Basic EGL goodies.
+ EGLDisplay mEglDisplay;
+ EGLContext mEglContext;
+ EGLSurface mEglSurface;
+ EGLConfig mEglConfig;
+
+ // Surface dimensions.
+ int mWidth;
+ int mHeight;
+};
+
+}; // namespace android
+
+#endif /*SCREENRECORD_EGL_WINDOW_H*/
diff --git a/cmds/screenrecord/FontBitmap.h b/cmds/screenrecord/FontBitmap.h
new file mode 100644
index 0000000..2b94f35
--- /dev/null
+++ b/cmds/screenrecord/FontBitmap.h
@@ -0,0 +1,6571 @@
+// auto-generated from Android default system font at 24pts
+class FontBitmap {
+public:
+ static const uint32_t width = 256;
+ static const uint32_t height = 204;
+ static const uint32_t numGlyphs = 95;
+ static const uint32_t firstGlyphChar = 32;
+ static const uint32_t maxGlyphHeight = 34;
+ static const uint32_t outlineWidth = 1;
+ static const uint8_t pixels[];
+ static const uint16_t yoffset[];
+ static const uint16_t glyphWidth[];
+};
+const uint8_t FontBitmap::pixels[] = {
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0xca, 0xd2,
+ 0xb4, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xe, 0x40, 0x68, 0x62, 0x4a,
+ 0x4a, 0x6a, 0x5c, 0x30, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xb4, 0x9b, 0xd7,
+ 0x4b, 0x48, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xe, 0x40, 0x68, 0x66, 0x3e, 0xa, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x2, 0x66, 0x9a, 0x98,
+ 0x56, 0x0, 0x1c, 0xa0, 0xa8, 0x9c, 0x16, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4, 0xe, 0x1a, 0x20, 0x16, 0xc,
+ 0x0, 0x0, 0x0, 0x3e, 0x27, 0x67, 0x67, 0x13,
+ 0x39, 0x67, 0x61, 0xde, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x6, 0x12, 0x16, 0x10, 0x6,
+ 0xc, 0x16, 0x14, 0xa, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x14, 0xe4, 0xbb, 0xff,
+ 0x5b, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x10, 0x3e, 0x5a, 0x54, 0x2c,
+ 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x1a, 0x4a, 0x6c, 0x62, 0x38, 0xa,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x3e, 0x27, 0x67, 0x67, 0x21, 0x32, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x6, 0x9c, 0xd, 0x9d, 0x8d,
+ 0xcc, 0x0, 0x74, 0x43, 0xc7, 0x39, 0xdc, 0x24,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x8, 0x14, 0x18, 0x10, 0x4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xa, 0x14, 0x1a, 0x12, 0x6, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1c, 0x4a, 0x6c, 0x68, 0x40,
+ 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x4, 0x2a, 0x58, 0x76,
+ 0x68, 0x3e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x54, 0x11, 0x23, 0x23, 0x23, 0xfe,
+ 0x0, 0x0, 0x0, 0x64, 0x63, 0xff, 0xff, 0x35,
+ 0x8d, 0xff, 0xf1, 0xf8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x9c, 0x19, 0x23, 0x15, 0x7e,
+ 0xb, 0x23, 0x21, 0xb8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x2e, 0xba, 0xfe, 0x35, 0xcb, 0xff,
+ 0x77, 0x5, 0xd8, 0x4a, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x2, 0x78, 0xfc, 0x2f, 0x5f, 0x51, 0x19,
+ 0xe0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x20, 0xba, 0xfe, 0x41, 0x67, 0x5f, 0x21, 0xf2,
+ 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x64, 0x63, 0xff, 0xff, 0x57, 0x4e, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x88, 0x15, 0xcb, 0xff, 0xdb,
+ 0xd4, 0x0, 0x7a, 0x9b, 0xff, 0xef, 0x4f, 0xd8,
+ 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x9c, 0x1d, 0x23, 0x13, 0x56, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12,
+ 0xfa, 0x21, 0x23, 0x17, 0x5a, 0x0, 0x0, 0x0,
+ 0x0, 0x3e, 0xd0, 0xfe, 0x43, 0x67, 0x61, 0x33,
+ 0xfe, 0xae, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x16, 0x4e, 0x98, 0xd4, 0xf8,
+ 0x5, 0xde, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x6, 0x70, 0xf0, 0x11, 0x53, 0x69,
+ 0x5f, 0x2f, 0xfe, 0xb2, 0x26, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x8c, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x7a, 0x63, 0xff, 0xff, 0x27,
+ 0x8d, 0xff, 0xeb, 0xfc, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xec, 0xcb, 0xff, 0x89, 0xbe,
+ 0x71, 0xff, 0xdf, 0xdc, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x38, 0xf6, 0x5d, 0xd9, 0xff, 0xff, 0xff,
+ 0xff, 0xe9, 0x7f, 0xfe, 0x5e, 0x0, 0x0, 0x0,
+ 0x0, 0x58, 0xb, 0xaf, 0xff, 0xff, 0xff, 0xf1,
+ 0x71, 0xec, 0x16, 0x4, 0x38, 0x38, 0x36, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12,
+ 0xde, 0x49, 0xdb, 0xff, 0xff, 0xff, 0xf9, 0x97,
+ 0xfe, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7a, 0x63, 0xff, 0xff, 0x4d, 0x4c, 0x0, 0x0,
+ 0x0, 0x0, 0x42, 0xfe, 0xbb, 0xff, 0xfb, 0x5f,
+ 0xc0, 0x0, 0x6a, 0x25, 0xdf, 0xff, 0xed, 0x33,
+ 0x9e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xd2, 0xcb, 0xff, 0x89, 0x84, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62,
+ 0x31, 0xff, 0xff, 0x8d, 0x66, 0x0, 0x0, 0x0,
+ 0x4e, 0xfe, 0x71, 0xe5, 0xff, 0xff, 0xff, 0xff,
+ 0xd1, 0x4b, 0xe8, 0x24, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x78, 0xe8, 0xfe, 0x37, 0x73, 0xa3, 0xcf,
+ 0xf5, 0x15, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x4, 0xa6, 0x15, 0xa5, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xd3, 0x53, 0xf2, 0x2e, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x7a, 0x63, 0xff, 0xf5, 0xfe,
+ 0x8d, 0xff, 0xd1, 0xf6, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xe, 0xfe, 0xef, 0xff, 0x5b, 0xe2,
+ 0x9b, 0xff, 0xbb, 0xe8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xd4, 0x6b, 0xfd, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x95, 0xf6, 0x16, 0x0, 0x0,
+ 0x0, 0xc4, 0x8f, 0xff, 0xff, 0xbf, 0xdb, 0xff,
+ 0xf9, 0x39, 0x5e, 0x68, 0x13, 0x67, 0xfe, 0x6c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x72,
+ 0x31, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x89, 0xda, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7a, 0x63, 0xff, 0xff, 0x19, 0x2a, 0x0, 0x0,
+ 0x0, 0x4, 0xda, 0x77, 0xff, 0xff, 0x89, 0xf4,
+ 0x30, 0x0, 0xc, 0xc0, 0x41, 0xf9, 0xff, 0xc9,
+ 0xfe, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x2e, 0x2e,
+ 0x2a, 0xe8, 0xbf, 0xff, 0x7b, 0x9e, 0x1a, 0x1a,
+ 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
+ 0x1a, 0x32, 0x46, 0x34, 0x1c, 0x4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc8,
+ 0x8d, 0xff, 0xff, 0x31, 0x62, 0x0, 0x0, 0x12,
+ 0xf0, 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf7, 0x4f, 0xbc, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xd6, 0xaf, 0xef, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x64, 0x11, 0xd1, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xfb, 0x5d, 0xc8, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x7a, 0x63, 0xff, 0xdb, 0xfe,
+ 0x8d, 0xff, 0xb5, 0xe2, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x3c, 0x29, 0xff, 0xff, 0x23, 0xf2,
+ 0xc3, 0xff, 0x93, 0xc2, 0x0, 0x0, 0x0, 0x0,
+ 0x22, 0x9, 0xe9, 0xff, 0xff, 0xef, 0x93, 0x89,
+ 0xe5, 0xff, 0xff, 0xfd, 0x39, 0x68, 0x0, 0x0,
+ 0x0, 0xf2, 0xd5, 0xff, 0xa7, 0xfe, 0xf, 0xeb,
+ 0xff, 0x87, 0xac, 0xf4, 0x9b, 0xff, 0xa3, 0x7a,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc8,
+ 0x9f, 0xff, 0xff, 0xed, 0x85, 0x9d, 0xfd, 0xff,
+ 0xeb, 0xfe, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7a, 0x63, 0xff, 0xe7, 0xfe, 0x6, 0x0, 0x0,
+ 0x0, 0x4c, 0x11, 0xed, 0xff, 0xdf, 0x9, 0x52,
+ 0x0, 0x0, 0x0, 0x1a, 0xf4, 0xa3, 0xff, 0xff,
+ 0x6f, 0xc0, 0x0, 0x0, 0x0, 0x42, 0x17, 0x57,
+ 0xfe, 0xf8, 0xb5, 0xff, 0x6d, 0xec, 0x5, 0x41,
+ 0xd0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12,
+ 0xb, 0x3f, 0x3f, 0x3f, 0xf, 0x1c, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0xfe,
+ 0xd5, 0xff, 0xd1, 0xfc, 0x12, 0x0, 0x0, 0x68,
+ 0x31, 0xf9, 0xff, 0xff, 0xdf, 0x89, 0x95, 0xf3,
+ 0xff, 0xff, 0xdd, 0xfe, 0x2a, 0x0, 0x0, 0x0,
+ 0x0, 0xe4, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xe0, 0x9b, 0xff, 0xff, 0xff, 0xbd, 0x7f,
+ 0x9f, 0xf9, 0xff, 0xff, 0xe3, 0xfe, 0x20, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x7a, 0x63, 0xff, 0xbf, 0xf8,
+ 0x8d, 0xff, 0x95, 0xc2, 0x0, 0x0, 0x0, 0x14,
+ 0x34, 0x50, 0xaa, 0x5f, 0xff, 0xeb, 0xfe, 0xfe,
+ 0xe5, 0xff, 0x69, 0xb0, 0x36, 0x16, 0x0, 0x0,
+ 0x4e, 0x4d, 0xff, 0xff, 0xff, 0x73, 0xf2, 0xe6,
+ 0x3b, 0xfd, 0xff, 0xff, 0x93, 0xb4, 0x0, 0x0,
+ 0x0, 0xfa, 0xeb, 0xff, 0x83, 0xbe, 0xf8, 0xcd,
+ 0xff, 0xa1, 0xea, 0x43, 0xfb, 0xf9, 0x3d, 0x7a,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe8,
+ 0xcd, 0xff, 0xff, 0x83, 0xf4, 0xfe, 0xc1, 0xff,
+ 0xff, 0x15, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7a, 0x63, 0xff, 0xc7, 0xf0, 0x0, 0x0, 0x0,
+ 0x0, 0xbc, 0x7f, 0xff, 0xff, 0x7f, 0xcc, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x74, 0x2d, 0xfd, 0xff,
+ 0xd9, 0xfe, 0x22, 0x0, 0x0, 0x82, 0x71, 0xff,
+ 0xdb, 0x85, 0xb1, 0xff, 0x71, 0x8f, 0xe5, 0xeb,
+ 0x5, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20,
+ 0x2f, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0x37,
+ 0xff, 0xff, 0x89, 0xc4, 0x0, 0x0, 0x0, 0xbe,
+ 0x93, 0xff, 0xff, 0xf5, 0x27, 0xe0, 0xf2, 0x61,
+ 0xff, 0xff, 0xff, 0x57, 0x76, 0x0, 0x0, 0x0,
+ 0x0, 0xcc, 0x8f, 0xa7, 0xa7, 0xc9, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x14, 0xfe, 0xed, 0xff, 0xff, 0xbf, 0xfe, 0xcc,
+ 0xfa, 0x89, 0xff, 0xff, 0xff, 0x47, 0x4e, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x60, 0x63, 0xff, 0xa1, 0xe4,
+ 0x8d, 0xff, 0x75, 0x8a, 0x0, 0x0, 0x0, 0x7e,
+ 0x33, 0x4d, 0x4d, 0xa1, 0xff, 0xd5, 0x4d, 0x55,
+ 0xff, 0xff, 0x6b, 0x4d, 0x37, 0x8e, 0x0, 0x0,
+ 0x64, 0x63, 0xff, 0xff, 0xff, 0x49, 0x8a, 0x18,
+ 0xfc, 0xe1, 0xff, 0xff, 0xb7, 0xb6, 0x0, 0x0,
+ 0x0, 0xf8, 0xdf, 0xff, 0x91, 0xf4, 0xfe, 0xdb,
+ 0xff, 0x93, 0xfe, 0xcf, 0xff, 0x97, 0xf2, 0x1a,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xea,
+ 0xcf, 0xff, 0xff, 0x7d, 0xe0, 0xfe, 0xd3, 0xff,
+ 0xf5, 0xfe, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x60, 0x63, 0xff, 0xa3, 0xc4, 0x0, 0x0, 0x0,
+ 0x8, 0xfa, 0xcf, 0xff, 0xfb, 0x21, 0x50, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x10, 0xfc, 0xcb, 0xff,
+ 0xff, 0x4d, 0x78, 0x0, 0x0, 0x80, 0x8f, 0xf9,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb,
+ 0x43, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e,
+ 0x2f, 0xff, 0xff, 0xff, 0x3f, 0x46, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x91,
+ 0xff, 0xff, 0x2f, 0x5e, 0x0, 0x0, 0x0, 0xec,
+ 0xc7, 0xff, 0xff, 0xc3, 0xf8, 0xc, 0x30, 0xfe,
+ 0xef, 0xff, 0xff, 0x93, 0xb6, 0x0, 0x0, 0x0,
+ 0x0, 0x58, 0xa0, 0xc8, 0xee, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1a, 0x33, 0xff, 0xff, 0xff, 0x77, 0xac, 0x0,
+ 0x80, 0x43, 0xff, 0xff, 0xff, 0x67, 0x68, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x3a, 0x1f, 0x51, 0x2d, 0xa8,
+ 0x2d, 0x51, 0x1d, 0x46, 0x0, 0x0, 0x0, 0xac,
+ 0xa9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xbb, 0xbc, 0x0, 0x0,
+ 0x4c, 0x49, 0xff, 0xff, 0xff, 0x85, 0xf6, 0x60,
+ 0xc2, 0x47, 0x57, 0x57, 0x41, 0x8e, 0x0, 0x0,
+ 0x0, 0xdc, 0xb1, 0xff, 0xeb, 0x7b, 0x99, 0xff,
+ 0xff, 0x5d, 0x7d, 0xff, 0xdf, 0xf, 0x60, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xce,
+ 0xa7, 0xff, 0xff, 0xcb, 0xb, 0x9b, 0xff, 0xff,
+ 0xaf, 0xee, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x3a, 0x1d, 0x4d, 0x29, 0x74, 0x0, 0x0, 0x0,
+ 0x32, 0x1f, 0xfb, 0xff, 0xd5, 0xfc, 0xa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x95, 0xff,
+ 0xff, 0x9b, 0xc6, 0x0, 0x0, 0x58, 0xee, 0x13,
+ 0x67, 0xbd, 0xff, 0xff, 0xf7, 0xaf, 0x6b, 0x19,
+ 0xee, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e,
+ 0x2f, 0xff, 0xff, 0xff, 0x3f, 0x46, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0xfe, 0xd9,
+ 0xff, 0xcf, 0xfc, 0x10, 0x0, 0x0, 0x0, 0xf8,
+ 0xdf, 0xff, 0xff, 0xa9, 0xda, 0x0, 0x0, 0xfa,
+ 0xd7, 0xff, 0xff, 0xaf, 0xd6, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1a, 0x1b, 0x63, 0x63, 0x63, 0x27, 0x4a, 0x0,
+ 0xa2, 0x5b, 0xff, 0xff, 0xff, 0x51, 0x58, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x8, 0x2e, 0x3a, 0x34, 0x1e,
+ 0x34, 0x3a, 0x2e, 0x8, 0x0, 0x0, 0x0, 0xa4,
+ 0x93, 0xdd, 0xdd, 0xfb, 0xff, 0xe7, 0xdd, 0xed,
+ 0xff, 0xf5, 0xdd, 0xdd, 0xa1, 0xb6, 0x0, 0x0,
+ 0x20, 0x9, 0xe9, 0xff, 0xff, 0xf9, 0x93, 0x1b,
+ 0xee, 0x9a, 0x6c, 0x5e, 0x40, 0x1a, 0x0, 0x0,
+ 0x0, 0x82, 0x2d, 0xe7, 0xff, 0xff, 0xff, 0xff,
+ 0xb5, 0x27, 0xf1, 0xff, 0x59, 0xc4, 0x4, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7c,
+ 0x3d, 0xfb, 0xff, 0xff, 0xe9, 0xff, 0xff, 0xdf,
+ 0x21, 0x7c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x8, 0x2a, 0x36, 0x30, 0xe, 0x0, 0x0, 0x0,
+ 0x6e, 0x59, 0xff, 0xff, 0xaf, 0xe2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x65, 0xff,
+ 0xff, 0xc9, 0xf0, 0x0, 0x0, 0x0, 0x4, 0x8e,
+ 0xd, 0xd1, 0xff, 0xfb, 0xfd, 0x5b, 0xee, 0x48,
+ 0x6, 0x0, 0x0, 0x20, 0x7a, 0xb0, 0xc8, 0xd4,
+ 0x2f, 0xff, 0xff, 0xff, 0x3f, 0xd8, 0xc8, 0xb0,
+ 0x7c, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x6c, 0x3b, 0xff,
+ 0xff, 0x87, 0xc2, 0x0, 0x0, 0x0, 0x0, 0xfc,
+ 0xe9, 0xff, 0xff, 0xa3, 0xd0, 0x0, 0x0, 0xf4,
+ 0xd1, 0xff, 0xff, 0xbb, 0xe0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x8, 0x38, 0x60, 0x7a, 0x62, 0x3c, 0xc, 0x30,
+ 0xfc, 0xb7, 0xff, 0xff, 0xf3, 0x17, 0x2c, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a,
+ 0xce, 0xf0, 0x5, 0xf9, 0xff, 0x4f, 0xfe, 0xa9,
+ 0xff, 0xb1, 0xfe, 0xf2, 0xd2, 0x68, 0x0, 0x0,
+ 0x0, 0xd2, 0x67, 0xfb, 0xff, 0xff, 0xff, 0xf3,
+ 0xa5, 0x2f, 0xf0, 0x5a, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x10, 0xc8, 0x19, 0x85, 0xab, 0xa1, 0x69,
+ 0xfe, 0xb7, 0xff, 0xb1, 0xfc, 0x2c, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e,
+ 0xfc, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xc5, 0x23,
+ 0xbc, 0x46, 0x6a, 0x7a, 0x52, 0x1c, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xa0, 0x81, 0xff, 0xff, 0x95, 0xc2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x54, 0x43, 0xff,
+ 0xff, 0xe9, 0xfe, 0x0, 0x0, 0x0, 0x38, 0xfe,
+ 0xa7, 0xff, 0xd9, 0x7d, 0xff, 0xeb, 0x29, 0x9e,
+ 0x0, 0x0, 0x0, 0x5a, 0x49, 0x9f, 0x9f, 0x9f,
+ 0xa9, 0xff, 0xff, 0xff, 0xad, 0x9f, 0x9f, 0x9f,
+ 0x4d, 0x5e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x50, 0xa6, 0xd0,
+ 0xdc, 0xdc, 0xdc, 0xda, 0xb8, 0x74, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xd0, 0x93, 0xff,
+ 0xfd, 0x2d, 0x5a, 0x0, 0x0, 0x0, 0x0, 0xfc,
+ 0xeb, 0xff, 0xff, 0xa3, 0xce, 0x0, 0x0, 0xf2,
+ 0xd1, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0xe0,
+ 0x5f, 0xff, 0xff, 0xff, 0x99, 0xe8, 0x6, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x4e, 0x3d, 0xff, 0xfd, 0x15, 0xf8, 0xcf,
+ 0xff, 0x89, 0xb4, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0xf2, 0x53, 0xdb, 0xff, 0xff, 0xff,
+ 0xff, 0xf9, 0x93, 0x5, 0x84, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x6, 0x52, 0xa0, 0xc6, 0xc0, 0xe6,
+ 0x5d, 0xff, 0xef, 0x25, 0xd6, 0xd4, 0xd4, 0xaa,
+ 0x54, 0x4, 0x0, 0x0, 0x0, 0x0, 0x1c, 0xda,
+ 0x3b, 0xdb, 0xff, 0xff, 0xff, 0xfb, 0x47, 0xec,
+ 0x36, 0xf, 0x6f, 0x6f, 0x43, 0x6e, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xbc, 0x95, 0xff, 0xff, 0x83, 0xac, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x27, 0xff,
+ 0xff, 0xfb, 0xfe, 0x6, 0x0, 0x0, 0x40, 0x6b,
+ 0xff, 0xff, 0x51, 0xfe, 0xd1, 0xff, 0xcb, 0xb4,
+ 0x0, 0x0, 0x0, 0x86, 0x77, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x7b, 0x8a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xb0, 0x87, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xab, 0xec, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x1a, 0xfe, 0xdb, 0xff,
+ 0xcd, 0xfc, 0x10, 0x0, 0x0, 0x0, 0x0, 0xfc,
+ 0xeb, 0xff, 0xff, 0xa3, 0xce, 0x0, 0x0, 0xf2,
+ 0xd1, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xc6, 0x37,
+ 0xf1, 0xff, 0xff, 0xd5, 0xf, 0x64, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0xc0,
+ 0xea, 0xfa, 0x6f, 0xff, 0xe3, 0xfe, 0xfe, 0xf1,
+ 0xff, 0x5b, 0xf6, 0xd2, 0x78, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x26, 0xbc, 0x5, 0x6d, 0xc9, 0xff,
+ 0xff, 0xff, 0xff, 0xb9, 0xfe, 0x34, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0x11,
+ 0xe3, 0xff, 0x79, 0x13, 0x8b, 0xb7, 0xb7, 0x89,
+ 0x11, 0xb0, 0x4, 0x0, 0x0, 0x0, 0xb6, 0x41,
+ 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x2d,
+ 0xca, 0x43, 0xff, 0xff, 0x93, 0x96, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xc8, 0xa1, 0xff, 0xff, 0x7b, 0xa0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x17, 0xff,
+ 0xff, 0xff, 0x15, 0xe, 0x0, 0x0, 0x40, 0x21,
+ 0xb7, 0xad, 0xfa, 0xbc, 0x4b, 0xf1, 0x71, 0xb0,
+ 0x0, 0x0, 0x0, 0x82, 0x77, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x7b, 0x88, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc, 0xc1, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf5, 0xfc, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x6e, 0x3f, 0xff, 0xff,
+ 0x85, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfc,
+ 0xeb, 0xff, 0xff, 0xa3, 0xce, 0x0, 0x0, 0xf2,
+ 0xd1, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa, 0xb6, 0x2b, 0xe3,
+ 0xff, 0xff, 0xeb, 0x31, 0xb0, 0x4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x8e, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x82, 0x77,
+ 0xd7, 0xd7, 0xe9, 0xff, 0xf5, 0xd7, 0xd7, 0xff,
+ 0xff, 0xdb, 0xd7, 0xaf, 0xce, 0x0, 0x0, 0x0,
+ 0x3a, 0x6a, 0x90, 0x80, 0x7c, 0xb8, 0xfc, 0x47,
+ 0xd3, 0xff, 0xff, 0xff, 0x65, 0x8e, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0xf4, 0x9b,
+ 0xff, 0xcb, 0x17, 0xdd, 0xff, 0xff, 0xff, 0xff,
+ 0xd9, 0x11, 0x56, 0x0, 0x0, 0x1c, 0xfe, 0xd9,
+ 0xff, 0xff, 0xd3, 0x4f, 0xf3, 0xff, 0xff, 0xd7,
+ 0x15, 0x77, 0xff, 0xff, 0x7b, 0x96, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xca, 0xa3, 0xff, 0xff, 0x79, 0x9e, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0xf, 0xff,
+ 0xff, 0xff, 0x1d, 0xe, 0x0, 0x0, 0xa, 0x86,
+ 0xbe, 0xd, 0x76, 0x22, 0xda, 0x1f, 0xe0, 0x3e,
+ 0x0, 0x0, 0x0, 0x56, 0x41, 0x8d, 0x8d, 0x8d,
+ 0x99, 0xff, 0xff, 0xff, 0x9f, 0x8d, 0x8d, 0x8d,
+ 0x43, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xc1, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf5, 0xfa, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xd4, 0x97, 0xff, 0xfd,
+ 0x29, 0x58, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfc,
+ 0xe9, 0xff, 0xff, 0xa3, 0xd0, 0x0, 0x0, 0xf4,
+ 0xd1, 0xff, 0xff, 0xbb, 0xe0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x6, 0xa8, 0x21, 0xdb, 0xff,
+ 0xff, 0xf3, 0x49, 0xd4, 0x12, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x58, 0x1b, 0x35, 0x35, 0x35, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x8d,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xd1, 0xd2, 0x0, 0x0, 0x0,
+ 0xd4, 0x6d, 0x73, 0x73, 0x47, 0x88, 0x1e, 0xb2,
+ 0x21, 0xf3, 0xff, 0xff, 0xa7, 0xc8, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xa8, 0x41, 0xfb,
+ 0xf9, 0x3d, 0x93, 0xff, 0xf3, 0x73, 0x79, 0xf7,
+ 0xff, 0x8b, 0xae, 0x0, 0x0, 0x48, 0x43, 0xff,
+ 0xff, 0xff, 0x67, 0xf8, 0x61, 0xfb, 0xff, 0xff,
+ 0xbf, 0xcf, 0xff, 0xff, 0x41, 0x5a, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xc2, 0x9b, 0xff, 0xff, 0x7f, 0xa6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x21, 0xff,
+ 0xff, 0xff, 0x5, 0x8, 0x0, 0x0, 0x0, 0x0,
+ 0x2, 0x2, 0x2, 0x0, 0x8, 0x8, 0x8, 0x0,
+ 0x0, 0x0, 0x0, 0x1c, 0x68, 0x9c, 0xb6, 0xc2,
+ 0x2f, 0xff, 0xff, 0xff, 0x3f, 0xc8, 0xb6, 0x9c,
+ 0x6a, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x9, 0xd,
+ 0xd, 0xd, 0xd, 0xd, 0xb, 0xde, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1c, 0xfe, 0xdd, 0xff, 0xcb,
+ 0xfc, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf8,
+ 0xdf, 0xff, 0xff, 0xa9, 0xdc, 0x0, 0x0, 0xfa,
+ 0xd7, 0xff, 0xff, 0xaf, 0xd4, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4, 0x9a, 0x17, 0xd1, 0xff, 0xff,
+ 0xf9, 0x5b, 0xe4, 0x20, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x8, 0x1a, 0x2c, 0x36, 0x26, 0x14,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x2b,
+ 0x4d, 0x4d, 0xe9, 0xff, 0x8b, 0x4d, 0xa7, 0xff,
+ 0xd3, 0x4d, 0x4d, 0x3f, 0xaa, 0x0, 0x0, 0x0,
+ 0xee, 0xe9, 0xff, 0xff, 0xaf, 0xe6, 0x6, 0x14,
+ 0xfe, 0xd9, 0xff, 0xff, 0xb9, 0xd8, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x48, 0xfe, 0xcf, 0xff,
+ 0x97, 0xfe, 0xc1, 0xff, 0xb1, 0xfa, 0xfa, 0xb9,
+ 0xff, 0xbb, 0xdc, 0x0, 0x0, 0x5c, 0x5f, 0xff,
+ 0xff, 0xff, 0x4f, 0xa8, 0xf6, 0x7f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xd9, 0xfe, 0x1c, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xac, 0x8b, 0xff, 0xff, 0x8b, 0xb8, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x35, 0xff,
+ 0xff, 0xf3, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e,
+ 0x2f, 0xff, 0xff, 0xff, 0x3f, 0x46, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x4,
+ 0x6, 0x6, 0x6, 0x6, 0x4, 0x2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x74, 0x43, 0xff, 0xff, 0x81,
+ 0xba, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xea,
+ 0xc5, 0xff, 0xff, 0xc5, 0xf8, 0x10, 0x32, 0xfe,
+ 0xf1, 0xff, 0xff, 0x91, 0xb4, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x88, 0xf, 0xc7, 0xff, 0xff, 0xfd,
+ 0x6b, 0xf0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x52, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0x30,
+ 0x68, 0x13, 0xfd, 0xff, 0x3f, 0xf2, 0xb3, 0xff,
+ 0xa7, 0xe4, 0x54, 0x3a, 0x1a, 0x0, 0x0, 0x0,
+ 0xee, 0xc9, 0xff, 0xff, 0xeb, 0x19, 0xe2, 0xe0,
+ 0x21, 0xf3, 0xff, 0xff, 0xa7, 0xc6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x8, 0xe0, 0x7d, 0xff, 0xdf,
+ 0xf, 0xf0, 0xcb, 0xff, 0xa7, 0xe4, 0xe6, 0xad,
+ 0xff, 0xc5, 0xe2, 0x0, 0x0, 0x48, 0x43, 0xff,
+ 0xff, 0xff, 0x99, 0xfc, 0xd8, 0xfe, 0x9f, 0xff,
+ 0xff, 0xff, 0xff, 0x63, 0xca, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x84, 0x69, 0xff, 0xff, 0xa5, 0xd4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x72, 0x57, 0xff,
+ 0xff, 0xd5, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e,
+ 0x2f, 0xff, 0xff, 0xff, 0x3f, 0x46, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x3c, 0xcc, 0xf2, 0xf6,
+ 0xdc, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x3c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x14,
+ 0x0, 0x0, 0x0, 0xd8, 0x9b, 0xff, 0xfb, 0x27,
+ 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xba,
+ 0x8f, 0xff, 0xff, 0xf7, 0x2d, 0xe8, 0xf4, 0x63,
+ 0xff, 0xff, 0xff, 0x53, 0x72, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x74, 0x9, 0xbd, 0xff, 0xff, 0xff, 0x7d,
+ 0xfe, 0xe6, 0xe0, 0xe0, 0xda, 0xb4, 0x66, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x8a, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x64, 0x4d, 0xff, 0xf9, 0x9, 0xfa, 0xd7, 0xff,
+ 0x7d, 0xa6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xb2, 0x77, 0xff, 0xff, 0xff, 0xe1, 0x95, 0x91,
+ 0xdd, 0xff, 0xff, 0xff, 0x5f, 0x8a, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x8, 0x1f, 0xf1, 0xff, 0x59,
+ 0xc4, 0xd4, 0xb1, 0xff, 0xd1, 0x5, 0xfe, 0xcd,
+ 0xff, 0xab, 0xce, 0x0, 0x0, 0x1c, 0xfe, 0xdf,
+ 0xff, 0xff, 0xff, 0xb1, 0x87, 0x9f, 0xe3, 0xff,
+ 0xff, 0xff, 0xfd, 0x63, 0xea, 0x1e, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x48, 0x39, 0xff, 0xff, 0xc5, 0xf4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xb0, 0x7f, 0xff,
+ 0xff, 0xb1, 0xdc, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22,
+ 0x2f, 0xff, 0xff, 0xff, 0x3f, 0x36, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x74, 0x71, 0xe3, 0xe3,
+ 0xaf, 0xc4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x68, 0x6f, 0xff, 0xff, 0xff, 0x35, 0x26,
+ 0x0, 0x0, 0x20, 0xfe, 0xe1, 0xff, 0xc9, 0xfa,
+ 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60,
+ 0x2b, 0xf7, 0xff, 0xff, 0xe5, 0x93, 0x9d, 0xf5,
+ 0xff, 0xff, 0xd9, 0xfe, 0x26, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xda, 0xab, 0xff, 0xff, 0xff, 0xed, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0x9d, 0xd0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xa4, 0x7b, 0xff, 0xd9, 0xfc, 0x5, 0xf7, 0xff,
+ 0x4f, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x42, 0x5, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xb5, 0xfe, 0x30, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x8, 0xdc, 0x55, 0x8d, 0xfc,
+ 0x2c, 0x94, 0x5f, 0xff, 0xff, 0xdb, 0xd5, 0xff,
+ 0xff, 0x59, 0x8a, 0x0, 0x0, 0x0, 0xc2, 0x55,
+ 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf5, 0x45, 0xce, 0x10, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x16, 0xfe, 0xe5, 0xff, 0xef, 0x5, 0x2a, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x2, 0xee, 0xb3, 0xff,
+ 0xff, 0x6d, 0x9a, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14,
+ 0x11, 0x67, 0x67, 0x67, 0x19, 0x20, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x9c, 0x7f, 0xff, 0xff,
+ 0xc5, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x8c, 0x6f, 0xff, 0xff, 0xff, 0x35, 0x36,
+ 0x0, 0x0, 0x78, 0x47, 0xff, 0xff, 0x7f, 0xb8,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe,
+ 0xea, 0x79, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf3, 0x47, 0xb6, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa2, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xf6, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xf0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x8a, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x94, 0xa5, 0xff, 0xb5, 0xda, 0x3d, 0xff, 0xff,
+ 0x15, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x84, 0xfe, 0x7f, 0xe1, 0xff, 0xff, 0xff,
+ 0xff, 0xdf, 0x7b, 0xfe, 0x80, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x28, 0x72, 0x72, 0x56,
+ 0x0, 0x30, 0xfe, 0x83, 0xf5, 0xff, 0xff, 0xf5,
+ 0x81, 0xfc, 0x2a, 0x0, 0x0, 0x0, 0x28, 0xec,
+ 0x47, 0xc9, 0xfd, 0xff, 0xff, 0xff, 0xf3, 0xa7,
+ 0x41, 0xe3, 0xff, 0xff, 0xe9, 0x35, 0x10, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xde, 0xa5, 0xff, 0xff, 0x59, 0x98, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x44, 0xb, 0xef, 0xff,
+ 0xf3, 0x17, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
+ 0x38, 0x62, 0x80, 0x64, 0x3a, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xa6, 0x7f, 0xff, 0xff,
+ 0xc1, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x68, 0x6f, 0xff, 0xff, 0xff, 0x35, 0x26,
+ 0x0, 0x0, 0xdc, 0x9d, 0xff, 0xfb, 0x23, 0x50,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x44, 0xf8, 0x63, 0xd9, 0xff, 0xff, 0xff, 0xfd,
+ 0xc7, 0x41, 0xe2, 0x20, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x7c, 0x7f, 0xff, 0xff,
+ 0xff, 0x15, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xee, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xe8, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x52, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x6c, 0xfe, 0xfe, 0xfe, 0x8c, 0xfe, 0xfe, 0xfe,
+ 0xf6, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4a, 0xcc, 0xfe, 0x2f, 0xed, 0xff,
+ 0x27, 0xfe, 0xca, 0x46, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4c, 0xe8, 0xf, 0x43, 0x43, 0xf,
+ 0xe8, 0x4c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20,
+ 0xa4, 0xfa, 0x1f, 0x4f, 0x59, 0x41, 0x9, 0xec,
+ 0xde, 0xfe, 0xfe, 0xfe, 0xfe, 0xc8, 0x10, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x7a, 0x39, 0xfd, 0xff, 0xbb, 0xfa, 0x24,
+ 0x0, 0x0, 0x0, 0x4, 0xce, 0x77, 0xff, 0xff,
+ 0x9f, 0xea, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xbe, 0x89, 0xff, 0xff,
+ 0xa7, 0xca, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x3c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x14,
+ 0x0, 0x8, 0xe6, 0xe3, 0xff, 0xc7, 0xf8, 0xc,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x32, 0xbe, 0xfe, 0x31, 0x55, 0x51, 0x23,
+ 0xfc, 0xa0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x48, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xbc, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xb2, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x10, 0xf8, 0xe9, 0xff,
+ 0xfe, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x4, 0x20, 0x38, 0x38, 0x20,
+ 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x8, 0x2c, 0x50, 0x5e, 0x42, 0x1c, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x16, 0xf8, 0xad, 0xff, 0xfd, 0x4d, 0xc2,
+ 0x8, 0x0, 0x0, 0x74, 0x11, 0xe3, 0xff, 0xef,
+ 0x25, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x6, 0xf0, 0xaf, 0xff, 0xff,
+ 0x5d, 0x88, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x8, 0x21, 0x95, 0x95, 0x55, 0xa4, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x12, 0x38, 0x54, 0x52, 0x30,
+ 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xda, 0x91, 0x9f,
+ 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x7c, 0x21, 0xe9, 0xff, 0xdf, 0x1d,
+ 0x9e, 0x0, 0x52, 0xfe, 0xa9, 0xff, 0xff, 0x71,
+ 0xde, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x18, 0x17, 0xf1, 0xff, 0xd1,
+ 0xfe, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x8, 0x62, 0x9c, 0xa8, 0x76, 0x28, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x58, 0x9c, 0x9c,
+ 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xa, 0xd0, 0x4b, 0xf5, 0xff, 0xcb,
+ 0xce, 0x0, 0x78, 0x89, 0xff, 0xff, 0x9b, 0xfe,
+ 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x18, 0x37, 0xd9, 0xf1, 0x41,
+ 0xae, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x22, 0xe6, 0x4b, 0xe7, 0xb5,
+ 0xd0, 0x0, 0x78, 0x6b, 0xfb, 0x8d, 0xfe, 0x64,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x14, 0xb4, 0x5, 0x31, 0xde,
+ 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x22, 0xcc, 0x17, 0x39,
+ 0x88, 0x0, 0x3a, 0xb, 0x3b, 0xf2, 0x56, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x10,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x18, 0x18,
+ 0x14, 0x0, 0x2, 0x18, 0x18, 0x16, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x8, 0x30, 0x5c, 0x76,
+ 0x66, 0x3c, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x2, 0xe, 0x18, 0x20, 0x18, 0xc, 0x2, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x4, 0xe, 0x1a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x16, 0xc,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x1c, 0x48, 0x6c, 0x6a, 0x46, 0x1c, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x2, 0xe, 0x18, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x1e, 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x2, 0x22, 0x52, 0x74, 0x6e, 0x46, 0x18,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x22, 0x50, 0x70, 0x60, 0x36,
+ 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x28, 0x56,
+ 0x76, 0x6e, 0x46, 0x18, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc,
+ 0x16, 0x20, 0x1c, 0x10, 0x6, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xe, 0x86, 0xf6, 0x1b, 0x57, 0x6b,
+ 0x5f, 0x2f, 0xfe, 0xba, 0x30, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7e, 0xd, 0x23, 0x23, 0x23, 0xb, 0x24, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x62, 0x11, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x5,
+ 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52,
+ 0xda, 0xfe, 0x41, 0x63, 0x61, 0x41, 0x5, 0xd4,
+ 0x2c, 0x0, 0x0, 0x0, 0x3a, 0xd, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x1d, 0xa8, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5a, 0xe4, 0xb, 0x4b, 0x69, 0x63, 0x3d, 0xfe,
+ 0xc8, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x44, 0xd8, 0x5, 0x4b, 0x69, 0x5b, 0x21,
+ 0xf8, 0x8c, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x6, 0x70, 0xec, 0x11, 0x51,
+ 0x69, 0x63, 0x3d, 0xfe, 0xcc, 0x3e, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xa, 0x46, 0x8c, 0xbe, 0xd2, 0xd2, 0xc4, 0x9a,
+ 0x56, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0xfe,
+ 0x23, 0x23, 0x23, 0x15, 0x94, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x12, 0xca, 0x29, 0xb5, 0xfb, 0xff, 0xff,
+ 0xff, 0xff, 0xd7, 0x5f, 0xfa, 0x40, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c,
+ 0xfc, 0xb3, 0xff, 0xff, 0xff, 0x4d, 0x42, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xac, 0x97, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2f,
+ 0x20, 0x0, 0x0, 0x0, 0x0, 0x4, 0x98, 0x9,
+ 0x89, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xeb, 0x5b,
+ 0x44, 0x0, 0x0, 0x0, 0x66, 0x6b, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xd3, 0xe0, 0x0, 0x0, 0x0, 0x0, 0x78,
+ 0x9, 0x93, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xe1,
+ 0x6b, 0xfc, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0xfe, 0x79, 0xeb, 0xff, 0xff, 0xff, 0xfd,
+ 0xbb, 0x2b, 0xc8, 0xe, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xa4, 0x19, 0xa5, 0xf5, 0xff,
+ 0xff, 0xff, 0xff, 0xe3, 0x71, 0xfe, 0x4c, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x92,
+ 0xf8, 0x21, 0x73, 0x99, 0xab, 0xaf, 0x9d, 0x7f,
+ 0x35, 0xfe, 0xac, 0x26, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x82, 0x4f,
+ 0xff, 0xff, 0xff, 0xc5, 0xf8, 0xa, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x8a, 0x31, 0xe7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x75, 0xe0, 0x4, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xc6,
+ 0x5b, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x5c, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xda, 0xaf, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2f,
+ 0x2a, 0x0, 0x0, 0x0, 0x0, 0x7c, 0x13, 0xc7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x43,
+ 0x44, 0x0, 0x0, 0x0, 0x76, 0x6b, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xd3, 0xf2, 0x0, 0x0, 0x0, 0x20, 0xfe,
+ 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x79, 0xe0, 0x4, 0x0, 0x0, 0x0, 0x26,
+ 0xfa, 0x93, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xe5, 0x2b, 0x8e, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x42, 0x9, 0xd1, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xe8, 0x8,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0xf2, 0x3d,
+ 0xbb, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xcf, 0x51, 0xf8, 0x48, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe2, 0xa5,
+ 0xff, 0xff, 0xff, 0xfb, 0x21, 0x50, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xf0, 0xb7, 0xff, 0xff, 0xff, 0xb7, 0x7d,
+ 0x99, 0xf3, 0xff, 0xff, 0xf1, 0x13, 0x32, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0xf,
+ 0xe1, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x5c, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xec, 0xc3, 0xff, 0xff,
+ 0xd7, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0x23,
+ 0x1c, 0x0, 0x0, 0x0, 0x1e, 0xfc, 0xb1, 0xff,
+ 0xff, 0xff, 0xd1, 0x8d, 0x89, 0xa7, 0xcf, 0xfe,
+ 0x1c, 0x0, 0x0, 0x0, 0x50, 0x47, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xe9, 0xff,
+ 0xff, 0xc5, 0xdc, 0x0, 0x0, 0x0, 0x6c, 0x49,
+ 0xff, 0xff, 0xff, 0xe1, 0x85, 0x93, 0xf3, 0xff,
+ 0xff, 0xf1, 0xf, 0x2a, 0x0, 0x0, 0x0, 0x9a,
+ 0x53, 0xff, 0xff, 0xff, 0xdb, 0x85, 0xa5, 0xfd,
+ 0xff, 0xff, 0xbd, 0xfa, 0x14, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xa0, 0x77, 0xff, 0xff, 0xff, 0xd7,
+ 0x8b, 0x9b, 0xf3, 0xff, 0xff, 0xf5, 0x1f, 0x3e,
+ 0x0, 0x0, 0x0, 0x0, 0x4a, 0xfc, 0x77, 0xfb,
+ 0xff, 0xf3, 0xb5, 0x7b, 0x5f, 0x5b, 0x73, 0xa3,
+ 0xe5, 0xff, 0xfd, 0x7f, 0xfc, 0x3c, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x5, 0xe9,
+ 0xff, 0xff, 0xff, 0xff, 0x7f, 0xba, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xf4, 0xf7, 0xff, 0xff, 0xb5, 0xfe, 0xc8,
+ 0xf4, 0x73, 0xff, 0xff, 0xff, 0x5d, 0x64, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0xf2, 0x97,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x5c, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xf8, 0xd9, 0xff, 0xff,
+ 0x65, 0xf2, 0xe8, 0xe8, 0xe8, 0xe8, 0xce, 0x92,
+ 0xc, 0x0, 0x0, 0x0, 0x78, 0x45, 0xff, 0xff,
+ 0xff, 0xaf, 0xfe, 0xd6, 0xbe, 0xda, 0xc8, 0x9e,
+ 0x0, 0x0, 0x0, 0x0, 0x20, 0x86, 0xbe, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xda, 0xfa, 0x67, 0xff, 0xff,
+ 0xef, 0x35, 0x9a, 0x0, 0x0, 0x0, 0xa4, 0x8d,
+ 0xff, 0xff, 0xff, 0x39, 0xe2, 0xf4, 0x7b, 0xff,
+ 0xff, 0xff, 0x55, 0x56, 0x0, 0x0, 0x0, 0xec,
+ 0xbd, 0xff, 0xff, 0xef, 0x21, 0xe0, 0xfa, 0x8d,
+ 0xff, 0xff, 0xfd, 0x33, 0x52, 0x0, 0x0, 0x0,
+ 0x3c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x14, 0x0,
+ 0x0, 0x0, 0x3c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x6, 0x52, 0x9e, 0x9c, 0x72,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x60, 0x98, 0x9c, 0x62,
+ 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb2, 0xb3, 0xff, 0xff, 0xef, 0x19,
+ 0xdc, 0xf2, 0x67, 0xff, 0xff, 0xff, 0x6b, 0x78,
+ 0x0, 0x0, 0x0, 0x20, 0xf2, 0x7f, 0xff, 0xff,
+ 0xb7, 0x27, 0xf2, 0xb2, 0x82, 0x7e, 0xa4, 0xe4,
+ 0xd, 0x91, 0xff, 0xff, 0x6f, 0xdc, 0xa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x59, 0xff,
+ 0xff, 0xf9, 0xff, 0xff, 0xcb, 0xfa, 0xe, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x9, 0x7b, 0x7b, 0x7b, 0x41, 0x8c, 0x0,
+ 0x6a, 0x31, 0xff, 0xff, 0xff, 0x73, 0x78, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xa6, 0x3d, 0xf9,
+ 0xff, 0xeb, 0xff, 0xff, 0xff, 0x4d, 0x5c, 0x0,
+ 0x0, 0x0, 0x0, 0x2, 0xfe, 0xed, 0xff, 0xff,
+ 0x43, 0x6a, 0x3a, 0x34, 0x16, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xc8, 0x9d, 0xff, 0xff,
+ 0xf5, 0x19, 0xb0, 0x98, 0x9c, 0x74, 0x34, 0x2,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x92, 0x2d, 0xf1, 0xff, 0xff,
+ 0x63, 0xde, 0x14, 0x0, 0x0, 0x0, 0xb6, 0x9d,
+ 0xff, 0xff, 0xf1, 0xfe, 0x1e, 0x78, 0x41, 0xff,
+ 0xff, 0xff, 0x67, 0x68, 0x0, 0x0, 0x2, 0xfe,
+ 0xef, 0xff, 0xff, 0xa7, 0xee, 0xa, 0x5c, 0x21,
+ 0xff, 0xff, 0xff, 0x79, 0x94, 0x0, 0x0, 0x0,
+ 0x68, 0x6f, 0xff, 0xff, 0xff, 0x35, 0x26, 0x0,
+ 0x0, 0x0, 0x68, 0x6f, 0xff, 0xff, 0xff, 0x35,
+ 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x18, 0x7a, 0xec, 0x19, 0x83, 0xa9, 0xc8,
+ 0x0, 0x0, 0x0, 0x26, 0x7e, 0xb2, 0xc8, 0xc8,
+ 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xa8, 0x6c,
+ 0x8, 0x0, 0x0, 0x0, 0xae, 0x97, 0x91, 0x27,
+ 0xf6, 0x90, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x98, 0x67, 0x87, 0x87, 0x6f, 0xde,
+ 0x6, 0x4e, 0x19, 0xff, 0xff, 0xff, 0x83, 0x92,
+ 0x0, 0x0, 0x0, 0xac, 0x49, 0xf9, 0xff, 0xa5,
+ 0xfe, 0x88, 0x2c, 0x64, 0x92, 0x9c, 0x74, 0x38,
+ 0x5e, 0xfc, 0x89, 0xff, 0xef, 0x21, 0x5a, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xea, 0xad, 0xff,
+ 0xff, 0x9d, 0xf9, 0xff, 0xfd, 0x2b, 0x5a, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x46, 0x78, 0x9c, 0x86, 0x58, 0x1a, 0x1c,
+ 0xc2, 0x57, 0xff, 0xff, 0xff, 0x4d, 0x5a, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x46, 0xfe, 0xcd, 0xff,
+ 0xf9, 0x79, 0xff, 0xff, 0xff, 0x4d, 0x5c, 0x0,
+ 0x0, 0x0, 0x0, 0x14, 0xb, 0xfd, 0xff, 0xff,
+ 0x19, 0x1b, 0x49, 0x39, 0xfe, 0xde, 0x52, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xec, 0xcb, 0xff, 0xff,
+ 0xc7, 0xfe, 0x53, 0x85, 0x89, 0x63, 0xd, 0xc4,
+ 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x2e, 0xfe, 0xbf, 0xff, 0xff, 0xab,
+ 0xfc, 0x32, 0x0, 0x0, 0x0, 0x0, 0x94, 0x7f,
+ 0xff, 0xff, 0xfd, 0x1f, 0xa4, 0xd0, 0x63, 0xff,
+ 0xff, 0xff, 0x41, 0x4c, 0x0, 0x0, 0x2, 0xb,
+ 0xff, 0xff, 0xff, 0x87, 0xbe, 0x0, 0xa, 0xfe,
+ 0xf1, 0xff, 0xff, 0x9b, 0xbe, 0x0, 0x0, 0x0,
+ 0x8c, 0x6f, 0xff, 0xff, 0xff, 0x35, 0x36, 0x0,
+ 0x0, 0x0, 0x8c, 0x6f, 0xff, 0xff, 0xff, 0x35,
+ 0x36, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32,
+ 0xa6, 0xfc, 0x3b, 0xa5, 0xf3, 0xff, 0xcd, 0xea,
+ 0x0, 0x0, 0x0, 0x6a, 0x53, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x1d,
+ 0x18, 0x0, 0x0, 0x0, 0xd6, 0xb3, 0xff, 0xf9,
+ 0xb5, 0x4f, 0xfe, 0xbe, 0x46, 0x4, 0x0, 0x0,
+ 0x0, 0x0, 0x34, 0x74, 0xa0, 0xa2, 0x78, 0x3c,
+ 0x0, 0x88, 0x3d, 0xff, 0xff, 0xff, 0x69, 0x7a,
+ 0x0, 0x0, 0x28, 0xfe, 0xcf, 0xff, 0xd3, 0x9,
+ 0x70, 0x86, 0xfe, 0x49, 0x83, 0x83, 0x63, 0x13,
+ 0xe0, 0x84, 0x5, 0xd5, 0xff, 0x8b, 0xca, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x32, 0x9, 0xef, 0xff,
+ 0xff, 0x45, 0xc3, 0xff, 0xff, 0x87, 0xc2, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x48, 0xf8, 0xfe, 0x9,
+ 0x43, 0xd5, 0xff, 0xff, 0xcd, 0xfe, 0x24, 0x0,
+ 0x0, 0x0, 0x0, 0xc, 0xe0, 0x7b, 0xff, 0xff,
+ 0x95, 0x57, 0xff, 0xff, 0xff, 0x4d, 0x5c, 0x0,
+ 0x0, 0x0, 0x0, 0x38, 0x31, 0xff, 0xff, 0xf9,
+ 0xc3, 0xff, 0xff, 0xff, 0xed, 0x89, 0xfe, 0x64,
+ 0x0, 0x0, 0x0, 0x0, 0xf6, 0xdb, 0xff, 0xff,
+ 0xcb, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x47,
+ 0xd4, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xb2, 0x5d, 0xff, 0xff, 0xef, 0x21,
+ 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4e, 0x1f,
+ 0xf1, 0xff, 0xff, 0xbb, 0x3d, 0x4d, 0xd9, 0xff,
+ 0xff, 0xcd, 0xfe, 0x1c, 0x0, 0x0, 0x2, 0xfe,
+ 0xfd, 0xff, 0xff, 0x8f, 0xc8, 0x0, 0x0, 0xfc,
+ 0xe5, 0xff, 0xff, 0xa7, 0xce, 0x0, 0x0, 0x0,
+ 0x68, 0x6f, 0xff, 0xff, 0xff, 0x35, 0x26, 0x0,
+ 0x0, 0x0, 0x68, 0x6f, 0xff, 0xff, 0xff, 0x35,
+ 0x26, 0x0, 0x0, 0x0, 0x8, 0x58, 0xd0, 0xfe,
+ 0x63, 0xc5, 0xff, 0xff, 0xff, 0xff, 0xcd, 0xe0,
+ 0x0, 0x0, 0x0, 0x98, 0x87, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2f,
+ 0x26, 0x0, 0x0, 0x0, 0xc6, 0xb3, 0xff, 0xff,
+ 0xff, 0xff, 0xd5, 0x79, 0x11, 0xe6, 0x72, 0x14,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x52, 0xfe, 0xaf, 0xff, 0xff, 0xf5, 0x21, 0x40,
+ 0x0, 0x0, 0x86, 0x55, 0xff, 0xff, 0x53, 0xb2,
+ 0x82, 0x11, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0x81, 0xc8, 0xc4, 0x7d, 0xff, 0xd9, 0xfa, 0x4,
+ 0x0, 0x0, 0x0, 0x0, 0x96, 0x63, 0xff, 0xff,
+ 0xe5, 0xfe, 0x7d, 0xff, 0xff, 0xd1, 0xfc, 0x12,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x7c, 0x7d, 0xfd, 0xff,
+ 0xff, 0xff, 0xff, 0xd1, 0x25, 0xa0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x84, 0x25, 0xef, 0xff, 0xdf,
+ 0xd, 0x57, 0xff, 0xff, 0xff, 0x4d, 0x5c, 0x0,
+ 0x0, 0x0, 0x0, 0x64, 0x53, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9d, 0xf8,
+ 0x16, 0x0, 0x0, 0x0, 0xfa, 0xe3, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb,
+ 0x21, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x22, 0xfe, 0xd1, 0xff, 0xff, 0x8f, 0xe4,
+ 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xdc,
+ 0x53, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xd7, 0x2b, 0xa2, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xe7, 0xff, 0xff, 0xbb, 0xfa, 0x32, 0x5e, 0xfe,
+ 0xe5, 0xff, 0xff, 0xa9, 0xd4, 0x0, 0x0, 0x0,
+ 0x3c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x14, 0x0,
+ 0x0, 0x0, 0x3c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x14, 0x0, 0x0, 0x30, 0xe6, 0x1d, 0x89, 0xe1,
+ 0xff, 0xff, 0xff, 0xff, 0xfd, 0xc7, 0x69, 0xb2,
+ 0x0, 0x0, 0x0, 0x8c, 0x87, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2f,
+ 0x22, 0x0, 0x0, 0x0, 0x90, 0x53, 0xb7, 0xf5,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0x9d, 0x33, 0xf6,
+ 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6c,
+ 0xfe, 0x89, 0xff, 0xff, 0xff, 0x8f, 0xea, 0xa,
+ 0x0, 0x0, 0xdc, 0xa7, 0xff, 0xd7, 0xfe, 0x4c,
+ 0xfe, 0xb7, 0xff, 0xff, 0xcd, 0xb1, 0xe9, 0xff,
+ 0xdd, 0xea, 0x52, 0x29, 0xff, 0xfb, 0x11, 0x24,
+ 0x0, 0x0, 0x0, 0x4, 0xee, 0xb3, 0xff, 0xff,
+ 0xa5, 0xea, 0x27, 0xfd, 0xff, 0xff, 0x31, 0x62,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x90, 0x7f, 0xff, 0xff,
+ 0xff, 0xff, 0xf7, 0x79, 0xfe, 0x7c, 0x0, 0x0,
+ 0x0, 0x0, 0x2e, 0xfc, 0xb3, 0xff, 0xff, 0x55,
+ 0xda, 0x57, 0xff, 0xff, 0xff, 0x4d, 0x5c, 0x0,
+ 0x0, 0x0, 0x0, 0x6e, 0x6f, 0xff, 0xff, 0xff,
+ 0xb5, 0x75, 0x8b, 0xed, 0xff, 0xff, 0xfd, 0x39,
+ 0x66, 0x0, 0x0, 0x0, 0xfa, 0xe3, 0xff, 0xff,
+ 0xfd, 0xa1, 0x55, 0x53, 0xb5, 0xff, 0xff, 0xff,
+ 0x9b, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x7c, 0x4d, 0xff, 0xff, 0xf9, 0x21, 0x5e,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xb0,
+ 0x11, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb,
+ 0x77, 0xfe, 0x74, 0x0, 0x0, 0x0, 0x0, 0xe2,
+ 0xb1, 0xff, 0xff, 0xf9, 0x5f, 0xfe, 0xd, 0x95,
+ 0xff, 0xff, 0xff, 0xa9, 0xd4, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x68, 0x5f, 0xf5, 0xff, 0xff,
+ 0xff, 0xff, 0xd7, 0x8b, 0x2b, 0xfa, 0xac, 0x38,
+ 0x0, 0x0, 0x0, 0x58, 0x25, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0xd,
+ 0x12, 0x0, 0x0, 0x0, 0x26, 0x94, 0xf2, 0x15,
+ 0x75, 0xc3, 0xfb, 0xff, 0xff, 0xff, 0xfd, 0x93,
+ 0xac, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x5,
+ 0xa5, 0xff, 0xff, 0xff, 0xad, 0xfe, 0x58, 0x0,
+ 0x0, 0x4, 0xfe, 0xdf, 0xff, 0x9d, 0xd6, 0x9c,
+ 0x5b, 0xff, 0xff, 0x97, 0xfe, 0xfc, 0xbf, 0xff,
+ 0xcb, 0xf0, 0xe, 0xfe, 0xf1, 0xff, 0x4b, 0x4a,
+ 0x0, 0x0, 0x0, 0x3a, 0xf, 0xf3, 0xff, 0xff,
+ 0x59, 0x8a, 0xfc, 0xcf, 0xff, 0xff, 0x8d, 0xca,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x66, 0x55, 0xad, 0xaf,
+ 0xcb, 0xff, 0xff, 0xff, 0xad, 0xfe, 0x2c, 0x0,
+ 0x0, 0x4, 0xc6, 0x5b, 0xff, 0xff, 0xad, 0xfe,
+ 0xce, 0x57, 0xff, 0xff, 0xff, 0x4d, 0xaa, 0x4a,
+ 0x8, 0x0, 0x0, 0x54, 0x49, 0x9b, 0xab, 0x99,
+ 0xfe, 0xbe, 0xea, 0x4f, 0xff, 0xff, 0xff, 0x8f,
+ 0xb2, 0x0, 0x0, 0x0, 0xfa, 0xe3, 0xff, 0xff,
+ 0xb5, 0xfe, 0x94, 0xa4, 0xfe, 0xdb, 0xff, 0xff,
+ 0xdb, 0xfa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xdc, 0xa1, 0xff, 0xff, 0xc3, 0xf8, 0xa,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0x17,
+ 0xcf, 0xff, 0xff, 0xf1, 0xb7, 0xc1, 0xfb, 0xff,
+ 0xff, 0xa3, 0xfe, 0x2c, 0x0, 0x0, 0x0, 0x8a,
+ 0x43, 0xfb, 0xff, 0xff, 0xff, 0xeb, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xa9, 0xd4, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x90, 0x7b, 0xff, 0xff, 0xff,
+ 0xaf, 0x41, 0xfe, 0xd8, 0x62, 0xe, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x2c, 0x88, 0xba, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xae, 0x72,
+ 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x46,
+ 0xbe, 0xfc, 0x23, 0x8d, 0xf7, 0xff, 0xff, 0xb3,
+ 0xd4, 0x0, 0x0, 0x0, 0x0, 0x6, 0xf0, 0x9f,
+ 0xff, 0xff, 0xfd, 0x89, 0xfe, 0x76, 0x0, 0x0,
+ 0x0, 0x1a, 0xf, 0xff, 0xff, 0x67, 0x94, 0xf0,
+ 0xbb, 0xff, 0xf3, 0x13, 0x62, 0xf4, 0xd1, 0xff,
+ 0xb9, 0xe2, 0x0, 0xfc, 0xe3, 0xff, 0x5b, 0x6c,
+ 0x0, 0x0, 0x0, 0xa0, 0x6b, 0xff, 0xff, 0xef,
+ 0x9, 0xac, 0xea, 0x8d, 0xff, 0xff, 0xd7, 0xfe,
+ 0x16, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x28, 0x8c, 0xc2, 0xe2,
+ 0xfe, 0x6d, 0xff, 0xff, 0xff, 0x5b, 0x84, 0x0,
+ 0x0, 0x3a, 0x11, 0xe1, 0xff, 0xfd, 0x89, 0x7b,
+ 0x7b, 0x9b, 0xff, 0xff, 0xff, 0x97, 0x7b, 0x1b,
+ 0x1e, 0x0, 0x0, 0x20, 0x78, 0xb4, 0xca, 0xa8,
+ 0x60, 0x0, 0x24, 0xfe, 0xe5, 0xff, 0xff, 0xb5,
+ 0xd8, 0x0, 0x0, 0x0, 0xf8, 0xdf, 0xff, 0xff,
+ 0xb1, 0xe2, 0x0, 0x0, 0xe0, 0x9d, 0xff, 0xff,
+ 0xf9, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1c, 0xfe, 0xe3, 0xff, 0xff, 0x85, 0xba, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd2, 0x99,
+ 0xff, 0xff, 0xf1, 0x2f, 0xf6, 0xfc, 0x5f, 0xff,
+ 0xff, 0xff, 0x5b, 0x8e, 0x0, 0x0, 0x0, 0x1c,
+ 0xf2, 0x73, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xd5,
+ 0xed, 0xff, 0xff, 0xa7, 0xce, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x7c, 0x7b, 0xff, 0xff, 0xff,
+ 0xfd, 0xc9, 0x7b, 0x1b, 0xf6, 0xa2, 0x3a, 0x4,
+ 0x0, 0x0, 0x0, 0x68, 0x4d, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x19,
+ 0x16, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x8c, 0xec,
+ 0xd, 0x6b, 0xbb, 0xf9, 0xff, 0xff, 0xff, 0xb3,
+ 0xc2, 0x0, 0x0, 0x0, 0x0, 0x22, 0x17, 0xf9,
+ 0xff, 0xff, 0x9f, 0xfc, 0x52, 0x0, 0x0, 0x0,
+ 0x0, 0x3c, 0x39, 0xff, 0xff, 0x47, 0x66, 0x5,
+ 0xf3, 0xff, 0xc3, 0xf6, 0x4, 0xfa, 0xe3, 0xff,
+ 0xa7, 0xd2, 0x0, 0xfc, 0xe1, 0xff, 0x63, 0x72,
+ 0x0, 0x0, 0x6, 0xf4, 0xb9, 0xff, 0xff, 0xd7,
+ 0x7b, 0x7b, 0x7b, 0x95, 0xff, 0xff, 0xff, 0x39,
+ 0x6c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1c, 0x92, 0xca, 0xe2, 0xca, 0x90, 0x1a, 0x0,
+ 0x3a, 0xfe, 0xed, 0xff, 0xff, 0xa1, 0xbe, 0x0,
+ 0x0, 0x5c, 0x69, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x39,
+ 0x30, 0x0, 0x0, 0x60, 0xba, 0xe4, 0xe6, 0xc6,
+ 0x6a, 0x0, 0x0, 0xfa, 0xd3, 0xff, 0xff, 0xbf,
+ 0xe0, 0x0, 0x0, 0x0, 0xf2, 0xd3, 0xff, 0xff,
+ 0xc3, 0xf0, 0x0, 0x0, 0xc8, 0x8f, 0xff, 0xff,
+ 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5a, 0x43, 0xff, 0xff, 0xff, 0x47, 0x64, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf6, 0xdd,
+ 0xff, 0xff, 0xaf, 0xf4, 0x10, 0x30, 0xfe, 0xdd,
+ 0xff, 0xff, 0xad, 0xcc, 0x0, 0x0, 0x0, 0x0,
+ 0x40, 0xec, 0x31, 0x93, 0xb7, 0xb7, 0x83, 0xf,
+ 0xf7, 0xff, 0xff, 0x99, 0xbe, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x4a, 0x19, 0x85, 0xdf, 0xff,
+ 0xff, 0xff, 0xff, 0xf9, 0xbb, 0x6b, 0xf, 0x96,
+ 0x0, 0x0, 0x0, 0x96, 0x87, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2f,
+ 0x26, 0x0, 0x0, 0x0, 0x74, 0xfe, 0x57, 0xad,
+ 0xf1, 0xff, 0xff, 0xff, 0xff, 0xed, 0x9b, 0x2f,
+ 0x86, 0x0, 0x0, 0x0, 0x0, 0x26, 0x41, 0xff,
+ 0xff, 0xff, 0x5d, 0x84, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x54, 0x51, 0xff, 0xff, 0x2d, 0x66, 0x35,
+ 0xff, 0xff, 0x9f, 0xd2, 0x4, 0xfe, 0xf3, 0xff,
+ 0x93, 0xbc, 0xa, 0xfe, 0xef, 0xff, 0x57, 0x5e,
+ 0x0, 0x0, 0x44, 0x17, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x95,
+ 0xd2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x30, 0x41, 0xbb, 0xbb, 0xbb, 0x3d, 0x74, 0x0,
+ 0x20, 0xfe, 0xe5, 0xff, 0xff, 0xaf, 0xcc, 0x0,
+ 0x0, 0x5e, 0x5b, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x39,
+ 0x30, 0x0, 0x0, 0xb4, 0x99, 0xc3, 0xcf, 0xa1,
+ 0xe2, 0x8, 0x24, 0xfe, 0xe3, 0xff, 0xff, 0xb1,
+ 0xd6, 0x0, 0x0, 0x0, 0xdc, 0xb1, 0xff, 0xff,
+ 0xe7, 0x5, 0x3c, 0xc, 0xf0, 0xad, 0xff, 0xff,
+ 0xed, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xa0, 0x7b, 0xff, 0xff, 0xf9, 0xb, 0x20, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfa, 0xef,
+ 0xff, 0xff, 0xa9, 0xec, 0x4, 0x16, 0xfc, 0xd7,
+ 0xff, 0xff, 0xc1, 0xde, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x12, 0x66, 0xb4, 0xd6, 0xd4, 0xdc, 0x41,
+ 0xff, 0xff, 0xff, 0x75, 0x90, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x6, 0x52, 0xcc, 0xfe, 0x5f,
+ 0xc1, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xd6,
+ 0x0, 0x0, 0x0, 0x8e, 0x87, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2f,
+ 0x22, 0x0, 0x0, 0x0, 0xb8, 0xab, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xd3, 0x77, 0xf, 0xe4, 0x6e,
+ 0x10, 0x0, 0x0, 0x0, 0x0, 0x20, 0x13, 0x3f,
+ 0x3f, 0x3f, 0x13, 0x32, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x5a, 0x4f, 0xff, 0xff, 0x2f, 0x7c, 0x57,
+ 0xff, 0xff, 0x8d, 0xc0, 0x1a, 0xf, 0xff, 0xff,
+ 0x7d, 0xa4, 0x54, 0x21, 0xff, 0xff, 0x29, 0x36,
+ 0x0, 0x0, 0xaa, 0x73, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdd,
+ 0xfe, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x30, 0x3b, 0xff, 0xff, 0xff, 0x9d, 0xfe, 0xce,
+ 0xf0, 0x49, 0xff, 0xff, 0xff, 0x91, 0xae, 0x0,
+ 0x0, 0x34, 0x1f, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b,
+ 0x6b, 0x91, 0xff, 0xff, 0xff, 0x8b, 0x6b, 0x17,
+ 0x1c, 0x0, 0x0, 0xc4, 0xbb, 0xff, 0xff, 0xed,
+ 0x1b, 0xe0, 0xee, 0x4f, 0xff, 0xff, 0xff, 0x83,
+ 0xa6, 0x0, 0x0, 0x0, 0x9a, 0x71, 0xff, 0xff,
+ 0xff, 0x6f, 0xf8, 0xe4, 0x25, 0xf1, 0xff, 0xff,
+ 0xbf, 0xec, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xd2, 0xa3, 0xff, 0xff, 0xdb, 0xfc, 0x2, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf6, 0xdb,
+ 0xff, 0xff, 0xe3, 0xf, 0xdc, 0xec, 0x39, 0xf9,
+ 0xff, 0xff, 0xab, 0xcc, 0x0, 0x0, 0x0, 0x0,
+ 0x1c, 0xb0, 0xd0, 0xdc, 0xbc, 0xd8, 0x9, 0xbf,
+ 0xff, 0xff, 0xf9, 0x27, 0x4a, 0x0, 0x0, 0x0,
+ 0x3c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x14, 0x0,
+ 0x0, 0x0, 0x3c, 0xcc, 0xf2, 0xf6, 0xdc, 0x78,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0xa2,
+ 0xfa, 0x35, 0xa1, 0xf1, 0xff, 0xff, 0xcd, 0xee,
+ 0x0, 0x0, 0x0, 0x5a, 0x2d, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0xf,
+ 0x14, 0x0, 0x0, 0x0, 0xda, 0xb3, 0xff, 0xff,
+ 0xf9, 0xb1, 0x4b, 0xfe, 0xbc, 0x44, 0x4, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0xe4, 0xfa,
+ 0xfe, 0xfc, 0xe6, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x42, 0x45, 0xff, 0xff, 0x3f, 0x90, 0x59,
+ 0xff, 0xff, 0x95, 0xda, 0x9e, 0x39, 0xff, 0xff,
+ 0x6b, 0x96, 0xd8, 0x85, 0xff, 0xd9, 0xfe, 0xe,
+ 0x0, 0x8, 0xf6, 0xc1, 0xff, 0xff, 0xcd, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x7f, 0xff, 0xff, 0xff,
+ 0x43, 0x76, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x18, 0xfe, 0xd9, 0xff, 0xff, 0xff, 0xbb, 0x89,
+ 0x9f, 0xf1, 0xff, 0xff, 0xf9, 0x39, 0x68, 0x0,
+ 0x0, 0x8, 0x3e, 0x6a, 0x86, 0x86, 0x86, 0x86,
+ 0xb8, 0x57, 0xff, 0xff, 0xff, 0x4d, 0x9e, 0x3e,
+ 0x6, 0x0, 0x0, 0xa0, 0x6d, 0xff, 0xff, 0xff,
+ 0xdd, 0x8f, 0x99, 0xef, 0xff, 0xff, 0xf3, 0x21,
+ 0x54, 0x0, 0x0, 0x0, 0x3e, 0xb, 0xdf, 0xff,
+ 0xff, 0xf9, 0xa1, 0x8f, 0xdf, 0xff, 0xff, 0xff,
+ 0x57, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xf0, 0xc9, 0xff, 0xff, 0xc1, 0xec, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd0, 0x99,
+ 0xff, 0xff, 0xff, 0xd9, 0x8f, 0x97, 0xeb, 0xff,
+ 0xff, 0xff, 0x5f, 0x8e, 0x0, 0x0, 0x0, 0x0,
+ 0x52, 0x43, 0xd5, 0xa1, 0x89, 0x91, 0xd5, 0xff,
+ 0xff, 0xff, 0xa1, 0xf2, 0xe, 0x0, 0x0, 0x0,
+ 0x68, 0x6f, 0xff, 0xff, 0xff, 0x35, 0x26, 0x0,
+ 0x0, 0x0, 0x74, 0x71, 0xe3, 0xe3, 0xaf, 0xc4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x14, 0x76, 0xe8, 0x15, 0x7f, 0xdb, 0xcd, 0xdc,
+ 0x0, 0x0, 0x0, 0x10, 0x36, 0x58, 0x6a, 0x6a,
+ 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x4e, 0x2c,
+ 0x4, 0x0, 0x0, 0x0, 0xc2, 0xb3, 0xe5, 0x8f,
+ 0x23, 0xf4, 0x8c, 0x22, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x4a, 0x51, 0xf5,
+ 0xf5, 0xf5, 0x57, 0x4e, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x24, 0x1b, 0xff, 0xff, 0x6b, 0xac, 0x2d,
+ 0xff, 0xff, 0xd5, 0xb, 0x1b, 0xc3, 0xff, 0xff,
+ 0x6d, 0xfa, 0x27, 0xe7, 0xff, 0x7b, 0xc4, 0x0,
+ 0x0, 0x4c, 0x1f, 0xf9, 0xff, 0xff, 0x7b, 0xd2,
+ 0x7a, 0x7a, 0x7a, 0x8a, 0xfe, 0xe9, 0xff, 0xff,
+ 0x9b, 0xda, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xb6, 0x49, 0xf1, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0x16, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x6a, 0x57, 0xff, 0xff, 0xff, 0x4d, 0x5c, 0x0,
+ 0x0, 0x0, 0x0, 0x3a, 0xfe, 0xb3, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x71, 0xe4,
+ 0xa, 0x0, 0x0, 0x0, 0x2, 0xc0, 0x47, 0xef,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x97,
+ 0xfc, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xfc, 0xe5, 0xff, 0xff, 0xa7, 0xd6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0x19,
+ 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xb1, 0xfe, 0x30, 0x0, 0x0, 0x0, 0x0,
+ 0x76, 0x71, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xc5, 0xd, 0x6c, 0x0, 0x0, 0x0, 0x0,
+ 0x8c, 0x6f, 0xff, 0xff, 0xff, 0x35, 0x36, 0x0,
+ 0x0, 0x0, 0x9c, 0x7f, 0xff, 0xff, 0xc5, 0xe4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x6, 0x4e, 0xc8, 0xec, 0x3f, 0xa4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x88, 0x3f, 0x5, 0xd6,
+ 0x5e, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x57, 0xff,
+ 0xff, 0xff, 0x5b, 0x6e, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x8, 0xfe, 0xe3, 0xff, 0xa1, 0xe6, 0xfe,
+ 0xcf, 0xff, 0xff, 0xf3, 0xf9, 0xe9, 0xe1, 0xff,
+ 0xdb, 0xa7, 0xeb, 0xff, 0xc3, 0xfe, 0x44, 0x0,
+ 0x0, 0xb4, 0x7b, 0xff, 0xff, 0xfd, 0x27, 0x50,
+ 0x0, 0x0, 0x0, 0x0, 0xe6, 0xad, 0xff, 0xff,
+ 0xe3, 0xfe, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x20, 0xdc, 0x31, 0xb3, 0xf7, 0xff, 0xff,
+ 0xff, 0xfd, 0xcb, 0x57, 0xfa, 0x4a, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x4c, 0x57, 0xff, 0xff, 0xff, 0x4d, 0x42, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x7c, 0xfe, 0x7f, 0xe1,
+ 0xff, 0xff, 0xff, 0xff, 0xd9, 0x61, 0xf8, 0x3e,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0xda, 0x37,
+ 0xbb, 0xfb, 0xff, 0xff, 0xff, 0xe3, 0x75, 0xfe,
+ 0x5e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xf8, 0xf7, 0xff, 0xff, 0x93, 0xa4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xae,
+ 0x17, 0x9d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xe3,
+ 0x83, 0x5, 0x7a, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x64, 0x65, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xe5,
+ 0x87, 0x5, 0x92, 0x2, 0x0, 0x0, 0x0, 0x0,
+ 0x68, 0x6f, 0xff, 0xff, 0xff, 0x35, 0x26, 0x0,
+ 0x0, 0x0, 0xa6, 0x7f, 0xff, 0xff, 0xc1, 0xe4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x16, 0x16,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x16, 0x16, 0x16, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x4c, 0x57, 0xff,
+ 0xff, 0xff, 0x5b, 0x52, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xde, 0xa7, 0xff, 0xe9, 0xd, 0xc6,
+ 0x39, 0xdf, 0xff, 0xff, 0xe5, 0x4b, 0x5b, 0xf3,
+ 0xff, 0xff, 0xf5, 0xa3, 0x13, 0x92, 0x0, 0x0,
+ 0x0, 0xb2, 0xc7, 0xff, 0xff, 0xcf, 0xfc, 0xc,
+ 0x0, 0x0, 0x0, 0x0, 0x90, 0x63, 0xff, 0xff,
+ 0xff, 0x4d, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x12, 0x86, 0xf2, 0xd, 0x47, 0x59,
+ 0x4d, 0x1b, 0xfc, 0xaa, 0x2a, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x2a, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x24, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x48, 0xcc, 0xfe,
+ 0x31, 0x57, 0x55, 0x2d, 0xfe, 0xbc, 0x30, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x90,
+ 0xf6, 0x19, 0x4d, 0x57, 0x39, 0xfe, 0xce, 0x40,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xe4, 0xfe, 0xfe, 0xfe, 0xfe, 0x5c, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6,
+ 0x68, 0xe4, 0xfe, 0x3d, 0x57, 0x55, 0x31, 0xfe,
+ 0xd0, 0x4c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x34, 0xca, 0xfe, 0x2f, 0x4f, 0x55, 0x35, 0xfe,
+ 0xd4, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x3c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x14, 0x0,
+ 0x0, 0x0, 0xbe, 0x89, 0xff, 0xff, 0xa7, 0xca,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0x2c, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x82, 0x47, 0xff, 0xff, 0x91, 0xf4,
+ 0xca, 0x5, 0x45, 0x43, 0x9, 0xd2, 0xe6, 0x1f,
+ 0x55, 0x49, 0xf, 0xec, 0x6e, 0x4, 0x0, 0x0,
+ 0x0, 0x94, 0xfe, 0xfe, 0xfe, 0xfe, 0xa0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x32, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x2, 0x22, 0x48, 0x5e,
+ 0x4e, 0x2a, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12,
+ 0x38, 0x58, 0x56, 0x34, 0x10, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x6, 0x2a, 0x4c, 0x58, 0x3c, 0x16, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x18, 0x3e, 0x5c, 0x58, 0x38, 0x12,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x10, 0x32, 0x54, 0x54, 0x38, 0x14,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x6, 0xf0, 0xaf, 0xff, 0xff, 0x5d, 0x88,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x1e, 0xfe, 0xaf, 0xff, 0xfb, 0x6b,
+ 0xfa, 0x8c, 0x46, 0x36, 0x1c, 0x0, 0x2c, 0x58,
+ 0x6e, 0x52, 0x22, 0x4, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x18, 0x17, 0xf1, 0xff, 0xd1, 0xfe, 0x2e,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x7c, 0x1b, 0xd9, 0xff, 0xfd,
+ 0xab, 0x35, 0xfe, 0xf2, 0xee, 0xf8, 0xfe, 0x4f,
+ 0x2f, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x18, 0x37, 0xd9, 0xf1, 0x41, 0xae, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x8, 0xb4, 0x1f, 0xc7, 0xff,
+ 0xff, 0xff, 0xdf, 0xc7, 0xbf, 0xd3, 0xef, 0xff,
+ 0x87, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x14, 0xb4, 0x5, 0x31, 0xde, 0x1c, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x8, 0x98, 0xfe, 0x67,
+ 0xc7, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3,
+ 0x5d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x10, 0x10, 0x10, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0xac,
+ 0xf6, 0x13, 0x49, 0x5f, 0x6b, 0x57, 0x23, 0xfc,
+ 0xb6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x4, 0x24, 0x4c, 0x72, 0x78, 0x60, 0x34, 0xc,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x4, 0xe, 0x1a, 0x20, 0x20, 0x20, 0x1e,
+ 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x20, 0x4e, 0x72, 0x72, 0x4e, 0x20, 0x2, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xe, 0x1a,
+ 0x20, 0x20, 0x20, 0x1c, 0x10, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x4, 0xe, 0x1a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x18, 0xe, 0x2, 0x0,
+ 0x0, 0x4, 0xe, 0x1a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x18, 0xe, 0x2,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e,
+ 0x4a, 0x70, 0x74, 0x54, 0x28, 0x4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xe, 0x1a,
+ 0x20, 0x16, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xc, 0x16, 0x20, 0x1a, 0xe, 0x4, 0x0, 0x0,
+ 0x0, 0x2, 0xc, 0x18, 0x20, 0x18, 0xc, 0x2,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x2, 0xe, 0x18, 0x20, 0x16, 0xc,
+ 0x0, 0x0, 0x0, 0x0, 0x4, 0xe, 0x1a, 0x20,
+ 0x16, 0xc, 0x0, 0x0, 0x0, 0x0, 0xa, 0x16,
+ 0x20, 0x20, 0x18, 0xc, 0x2, 0x0, 0x0, 0x4,
+ 0xe, 0x1a, 0x20, 0x16, 0xc, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
+ 0xe, 0x1a, 0x20, 0x20, 0x16, 0xc, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x16, 0x20,
+ 0x20, 0x1a, 0xe, 0x4, 0x0, 0x0, 0x0, 0x4,
+ 0xe, 0x1a, 0x20, 0x16, 0xc, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xc, 0x16, 0x20, 0x1a, 0xe, 0x4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x14, 0x3e, 0x66, 0x72, 0x56, 0x28, 0x4, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x52, 0x11, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x1f, 0x9, 0xfe, 0xea, 0x9e, 0x30, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x60, 0xe2,
+ 0x5, 0x49, 0x65, 0x65, 0x49, 0xb, 0xec, 0x76,
+ 0x8, 0x0, 0x0, 0x0, 0x0, 0x52, 0x11, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x15, 0xfe, 0xf8, 0xc2,
+ 0x5c, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x52, 0x11, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0xd, 0x32, 0x0,
+ 0x0, 0x52, 0x11, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0xd, 0x34,
+ 0x0, 0x0, 0x0, 0x0, 0x4, 0x5c, 0xe0, 0x5,
+ 0x45, 0x65, 0x67, 0x4f, 0x13, 0xf6, 0x8e, 0x14,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x11, 0x23,
+ 0x23, 0x23, 0xfe, 0x2, 0x0, 0x0, 0x0, 0x2,
+ 0xfe, 0x23, 0x23, 0x23, 0x11, 0x4e, 0x0, 0x0,
+ 0x0, 0x24, 0xb, 0x23, 0x23, 0x23, 0xb, 0x2e,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x32, 0xd, 0x23, 0x23, 0x23, 0x9,
+ 0x1c, 0x0, 0x0, 0x0, 0x52, 0x11, 0x23, 0x23,
+ 0x23, 0xfe, 0x2, 0x0, 0x0, 0x40, 0xfe, 0x21,
+ 0x23, 0x23, 0x23, 0xf, 0x4, 0x0, 0x0, 0x52,
+ 0x11, 0x23, 0x23, 0x23, 0xfe, 0x2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52,
+ 0x11, 0x23, 0x23, 0x23, 0x23, 0x5, 0x30, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x40, 0x9, 0x23, 0x23,
+ 0x23, 0x23, 0xf, 0x44, 0x0, 0x0, 0x0, 0x52,
+ 0x11, 0x23, 0x23, 0x23, 0xfe, 0x42, 0x0, 0x0,
+ 0x0, 0x2, 0xfe, 0x23, 0x23, 0x23, 0x11, 0x4e,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0xc8,
+ 0xfe, 0x35, 0x5d, 0x67, 0x51, 0x11, 0xf2, 0x8a,
+ 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x88, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xeb, 0xbd, 0x5f, 0xfe, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x8, 0xa6, 0x11, 0x97,
+ 0xef, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xa7, 0x1f,
+ 0xc2, 0xe, 0x0, 0x0, 0x0, 0x88, 0x87, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xd1, 0x8d,
+ 0x1b, 0xd6, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x88, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x5f, 0x58, 0x0,
+ 0x0, 0x88, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x63, 0x5c,
+ 0x0, 0x0, 0x0, 0x6, 0xa4, 0xf, 0x91, 0xed,
+ 0xff, 0xff, 0xff, 0xff, 0xfb, 0xbb, 0x35, 0xdc,
+ 0x1c, 0x0, 0x0, 0x0, 0x0, 0x88, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x4, 0x0, 0x0, 0x0, 0x4,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0x82, 0x0, 0x0,
+ 0x0, 0x42, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x52,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x58, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x36, 0x0, 0x0, 0x0, 0x88, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0x4, 0x0, 0xe, 0xe0, 0x75, 0xff,
+ 0xff, 0xff, 0xd9, 0x15, 0x4, 0x0, 0x0, 0x88,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88,
+ 0x87, 0xff, 0xff, 0xff, 0xff, 0x5f, 0x94, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xac, 0x75, 0xff, 0xff,
+ 0xff, 0xff, 0x77, 0x76, 0x0, 0x0, 0x0, 0x88,
+ 0x87, 0xff, 0xff, 0xff, 0x77, 0xda, 0x8, 0x0,
+ 0x0, 0x4, 0xd, 0xff, 0xff, 0xff, 0x83, 0x82,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x7a, 0xfe, 0x75,
+ 0xdf, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xb5, 0x35,
+ 0xde, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0xfe, 0x36,
+ 0x0, 0x0, 0x0, 0x0, 0x86, 0x1b, 0xd1, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3,
+ 0x29, 0x94, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xed, 0x59, 0xee, 0x26, 0x0, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x5f, 0x68, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x63, 0x6e,
+ 0x0, 0x0, 0x0, 0x86, 0x19, 0xcf, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x43,
+ 0xb8, 0x0, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0x6,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x4c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0x6, 0x0, 0x8a, 0x27, 0xef, 0xff,
+ 0xff, 0xf9, 0x43, 0xb8, 0x4, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xff, 0xb1, 0xee, 0x4,
+ 0x0, 0x0, 0x0, 0xa, 0xf8, 0xc3, 0xff, 0xff,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xeb, 0x1b, 0x6e, 0x0,
+ 0x0, 0x6, 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0x0, 0x60, 0x5, 0xb1, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1,
+ 0x57, 0xde, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xad, 0xad,
+ 0xb1, 0xc7, 0xfb, 0xff, 0xff, 0xff, 0x67, 0x9a,
+ 0x0, 0x0, 0x0, 0x20, 0xfe, 0xb7, 0xff, 0xff,
+ 0xff, 0xbf, 0x7d, 0x89, 0xbd, 0xff, 0xff, 0xff,
+ 0xc3, 0xfe, 0x1c, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xad, 0xad, 0xb7, 0xd7, 0xff, 0xff,
+ 0xff, 0xf9, 0x51, 0xc6, 0x2, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0x41, 0x44, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0x43, 0x4a,
+ 0x0, 0x0, 0x24, 0xfe, 0xb9, 0xff, 0xff, 0xff,
+ 0xcd, 0x85, 0x81, 0xb1, 0xfd, 0xff, 0xff, 0xdb,
+ 0x9, 0x32, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0x6,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x4c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0x6, 0x36, 0xfe, 0xb9, 0xff, 0xff,
+ 0xff, 0x87, 0xf0, 0x1c, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf, 0x3a,
+ 0x0, 0x0, 0x0, 0x50, 0x21, 0xfb, 0xff, 0xff,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xff, 0xa1, 0xf6, 0x1a,
+ 0x0, 0x6, 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0x12, 0xf4, 0x97, 0xff, 0xff,
+ 0xff, 0xd9, 0x91, 0x83, 0xad, 0xf9, 0xff, 0xff,
+ 0xf1, 0x35, 0x8e, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xd8,
+ 0xe0, 0xfa, 0x43, 0xf7, 0xff, 0xff, 0xb5, 0xd6,
+ 0x0, 0x0, 0x0, 0x7c, 0x49, 0xff, 0xff, 0xff,
+ 0xa3, 0xfe, 0xc6, 0xc6, 0xfe, 0x95, 0xff, 0xff,
+ 0xff, 0x43, 0x6a, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0xda, 0xe8, 0xfe, 0x53, 0xed,
+ 0xff, 0xff, 0xe3, 0xb, 0x40, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xd8, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd6, 0xbc, 0x84, 0x1a, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xd8,
+ 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xbc, 0x84, 0x1c,
+ 0x0, 0x0, 0x80, 0x4f, 0xff, 0xff, 0xff, 0xb1,
+ 0xfe, 0xce, 0xbe, 0xfa, 0x77, 0xff, 0xff, 0xff,
+ 0x63, 0x8c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0x6,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x4c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0xe, 0xd6, 0x69, 0xff, 0xff, 0xff,
+ 0xc7, 0xfe, 0x50, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xa0,
+ 0x0, 0x0, 0x0, 0xba, 0x7f, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x41, 0xa2,
+ 0x0, 0x6, 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0x68, 0x31, 0xfb, 0xff, 0xff,
+ 0xc1, 0x9, 0xda, 0xc0, 0xf8, 0x51, 0xf9, 0xff,
+ 0xff, 0xb9, 0xf8, 0x8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x1c, 0xfc, 0xcf, 0xff, 0xff, 0xcb, 0xe6,
+ 0x0, 0x0, 0x0, 0xca, 0xa1, 0xff, 0xff, 0xf7,
+ 0x21, 0x72, 0x0, 0x0, 0x60, 0x13, 0xfb, 0xff,
+ 0xff, 0x91, 0x98, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x26, 0xde, 0x67,
+ 0xff, 0xff, 0xff, 0x73, 0x9c, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xce, 0xa3, 0xff, 0xff, 0xf7, 0x27,
+ 0x80, 0x0, 0x0, 0x42, 0xfe, 0xe3, 0xff, 0xff,
+ 0xa7, 0x9a, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0x6,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x4c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0x7e, 0x1f, 0xeb, 0xff, 0xff, 0xef,
+ 0x2d, 0x9a, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xeb, 0xff, 0xff, 0xbb, 0xf4,
+ 0x6, 0x0, 0x10, 0xfc, 0xcb, 0xff, 0xff, 0xeb,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0xfe,
+ 0x3a, 0x6, 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0xbe, 0x93, 0xff, 0xff, 0xfb,
+ 0x37, 0x96, 0x0, 0x0, 0x26, 0xf6, 0xa9, 0xff,
+ 0xff, 0xfd, 0x1f, 0x3a, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x6, 0x5e, 0xfe, 0xd9, 0xff, 0xff, 0xb5, 0xd4,
+ 0x0, 0x0, 0x0, 0xec, 0xcb, 0xff, 0xff, 0xcb,
+ 0xf8, 0xa, 0x0, 0x0, 0x4, 0xf4, 0xa3, 0xbd,
+ 0xbd, 0x83, 0x8a, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x34, 0xfe,
+ 0xe7, 0xff, 0xff, 0xb1, 0xd8, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x4c, 0x46,
+ 0x46, 0x46, 0x46, 0x30, 0x1a, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xee, 0xcd, 0xff, 0xff, 0xcd, 0xfa,
+ 0xc, 0x0, 0x0, 0x0, 0xc4, 0x49, 0x63, 0x63,
+ 0x49, 0x82, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0x6,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x4c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0xfc, 0xaf, 0xff, 0xff, 0xff, 0x6b,
+ 0xde, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xcb, 0xe3, 0xff, 0xf7, 0x17,
+ 0x44, 0x0, 0x5e, 0x2d, 0xfd, 0xff, 0xd3, 0xdb,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0x6d,
+ 0xd0, 0xa, 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0xe8, 0xc5, 0xff, 0xff, 0xd3,
+ 0xfc, 0x16, 0x0, 0x0, 0x0, 0x8e, 0x53, 0xff,
+ 0xff, 0xff, 0x65, 0x72, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xfe,
+ 0xfe, 0x17, 0x91, 0xff, 0xff, 0xff, 0x5b, 0x96,
+ 0x0, 0x0, 0x0, 0xf6, 0xdb, 0xff, 0xff, 0xb3,
+ 0xe2, 0x0, 0x0, 0x0, 0x0, 0x6c, 0xba, 0xde,
+ 0xd8, 0xac, 0x4c, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0xf2,
+ 0xc3, 0xff, 0xff, 0xcf, 0xf0, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0x43, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x9, 0xe, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xda, 0xa0, 0xa, 0x0,
+ 0x0, 0x0, 0xf6, 0xdb, 0xff, 0xff, 0xb3, 0xe4,
+ 0x0, 0x0, 0x0, 0x0, 0x20, 0x4c, 0x70, 0x70,
+ 0x4c, 0x20, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x4c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0x33, 0x69, 0xff, 0xff, 0xff, 0xb1, 0xfe,
+ 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xd5, 0x99, 0xff, 0xff, 0x75,
+ 0xac, 0x0, 0xc6, 0x89, 0xff, 0xff, 0x85, 0xe3,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0x5f, 0xff, 0xff, 0xe5,
+ 0x13, 0x66, 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0xf4, 0xd9, 0xff, 0xff, 0xb7,
+ 0xe6, 0x0, 0x0, 0x0, 0x0, 0x32, 0x27, 0xff,
+ 0xff, 0xff, 0x7d, 0x9a, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xf1, 0xf1,
+ 0xf3, 0xff, 0xff, 0xff, 0xf5, 0x81, 0xfc, 0x2c,
+ 0x0, 0x0, 0x0, 0xf8, 0xdf, 0xff, 0xff, 0xad,
+ 0xd8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0xe4,
+ 0xb7, 0xff, 0xff, 0xdb, 0xf6, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x29, 0x1c, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xcf, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0x21, 0x18, 0x0,
+ 0x0, 0x0, 0xf8, 0xdf, 0xff, 0xff, 0xad, 0xd8,
+ 0x0, 0x68, 0xc0, 0xe4, 0xec, 0xec, 0xec, 0xe4,
+ 0xbe, 0x62, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xcf, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcf, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x4c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5, 0x19, 0x82,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xdd, 0x3b, 0xff, 0xff, 0xc3,
+ 0xf8, 0x20, 0xfe, 0xd5, 0xff, 0xfb, 0x25, 0xeb,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0xbb, 0xff, 0xff,
+ 0x99, 0xf0, 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0xf8, 0xdf, 0xff, 0xff, 0xad,
+ 0xda, 0x0, 0x0, 0x0, 0x0, 0x10, 0xd, 0xff,
+ 0xff, 0xff, 0x87, 0xa8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xe3, 0x81, 0xfe, 0x6a,
+ 0x0, 0x0, 0x0, 0xfa, 0xdf, 0xff, 0xff, 0xad,
+ 0xd6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0xe0,
+ 0xb7, 0xff, 0xff, 0xdd, 0xf8, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x29, 0x22, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x29, 0x22, 0x0,
+ 0x0, 0x0, 0xfa, 0xdf, 0xff, 0xff, 0xad, 0xd6,
+ 0x0, 0xc4, 0x9f, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0x9b, 0xbe, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x4c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x27, 0x90,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xe5, 0xfe, 0xd3, 0xff, 0xfb,
+ 0x21, 0x98, 0x39, 0xff, 0xff, 0xc3, 0xfe, 0xf3,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x31, 0xf7, 0xff,
+ 0xf9, 0x37, 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0xfa, 0xdf, 0xff, 0xff, 0xad,
+ 0xd6, 0x0, 0x0, 0x0, 0x0, 0x6, 0xd, 0xff,
+ 0xff, 0xff, 0x87, 0xac, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0x93, 0x91,
+ 0x91, 0x95, 0xbb, 0xff, 0xff, 0xff, 0xa1, 0xf6,
+ 0x12, 0x0, 0x0, 0xf8, 0xdf, 0xff, 0xff, 0xad,
+ 0xda, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0xe4,
+ 0xb7, 0xff, 0xff, 0xdb, 0xf6, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0x9d, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x19, 0x14, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0x27, 0x1a, 0x0,
+ 0x0, 0x0, 0xf8, 0xdf, 0xff, 0xff, 0xad, 0xda,
+ 0x0, 0xda, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xc7, 0xe4, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef,
+ 0xef, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x4c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xe9, 0xe9, 0xff, 0xff, 0xff, 0xbb, 0xfe,
+ 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xef, 0xfe, 0x85, 0xff, 0xff,
+ 0x7d, 0xf2, 0x93, 0xff, 0xff, 0x71, 0xfe, 0xfb,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0xec, 0x93, 0xff,
+ 0xff, 0xbf, 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0xf8, 0xdf, 0xff, 0xff, 0xad,
+ 0xda, 0x0, 0x0, 0x0, 0x0, 0x12, 0xd, 0xff,
+ 0xff, 0xff, 0x87, 0xa8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xba,
+ 0xba, 0xcc, 0xfe, 0x8b, 0xff, 0xff, 0xfb, 0x31,
+ 0x46, 0x0, 0x0, 0xf6, 0xdb, 0xff, 0xff, 0xb5,
+ 0xe4, 0x0, 0x0, 0x0, 0x0, 0x6c, 0xb8, 0xdc,
+ 0xd6, 0xa8, 0x4c, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0xf4,
+ 0xc5, 0xff, 0xff, 0xcd, 0xee, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xc8, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xa4, 0x68, 0x6, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xf8, 0xd4, 0xc, 0x0,
+ 0x0, 0x0, 0xf6, 0xdb, 0xff, 0xff, 0xb5, 0xe6,
+ 0x0, 0xc2, 0x9b, 0xc1, 0xc1, 0xef, 0xff, 0xff,
+ 0xc7, 0xec, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x4c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0xfe, 0xc1, 0xff, 0xff, 0xff, 0x71,
+ 0xde, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xf7, 0xfe, 0x25, 0xfb, 0xff,
+ 0xcb, 0xfe, 0xdd, 0xff, 0xf3, 0x11, 0xf, 0xff,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x60, 0xf, 0xe3,
+ 0xff, 0xff, 0x67, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0xf4, 0xd9, 0xff, 0xff, 0xbb,
+ 0xe8, 0x0, 0x0, 0x0, 0x0, 0x36, 0x2b, 0xff,
+ 0xff, 0xff, 0x7b, 0x98, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x70, 0x35, 0xff, 0xff, 0xff, 0x6b,
+ 0x74, 0x0, 0x0, 0xec, 0xcb, 0xff, 0xff, 0xcd,
+ 0xfa, 0xc, 0x0, 0x0, 0x6, 0xf6, 0xa3, 0xbb,
+ 0xbb, 0x81, 0x8e, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x3c, 0x5,
+ 0xeb, 0xff, 0xff, 0xaf, 0xd6, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xec, 0xcb, 0xff, 0xff, 0xcf, 0xfa,
+ 0x12, 0x62, 0xb8, 0xe0, 0xfe, 0xcb, 0xff, 0xff,
+ 0xc7, 0xec, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0x6,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x34, 0xe8, 0xfc, 0xfe, 0xfc, 0xe6,
+ 0x1a, 0x0, 0x80, 0x5f, 0xff, 0xff, 0xff, 0x43,
+ 0x44, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0x98, 0x31, 0xf3, 0xff, 0xff, 0xef,
+ 0x27, 0x8e, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0x5, 0xfa, 0xc1, 0xff,
+ 0xfd, 0x5d, 0xff, 0xff, 0xb1, 0xf0, 0x21, 0xff,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0xa, 0xca, 0x65,
+ 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0xe8, 0xc3, 0xff, 0xff, 0xd5,
+ 0xfe, 0x1a, 0x0, 0x0, 0x0, 0x92, 0x55, 0xff,
+ 0xff, 0xff, 0x61, 0x6e, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x8c, 0x3b, 0xff, 0xff, 0xff, 0x75,
+ 0x7e, 0x0, 0x0, 0xca, 0xa1, 0xff, 0xff, 0xf7,
+ 0x23, 0x72, 0x0, 0x0, 0x6a, 0x17, 0xfb, 0xff,
+ 0xff, 0x95, 0x9c, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x32, 0xe6, 0x71,
+ 0xff, 0xff, 0xff, 0x6d, 0x98, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xc8, 0x9d, 0xff, 0xff, 0xf9, 0x31,
+ 0x92, 0x0, 0x0, 0x0, 0xf0, 0xcb, 0xff, 0xff,
+ 0xc7, 0xec, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0x6,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x4c, 0x67, 0xf7, 0xf7, 0xf7, 0x3f,
+ 0x5e, 0x0, 0xb2, 0x75, 0xff, 0xff, 0xff, 0x31,
+ 0x2c, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0x18, 0xe8, 0x83, 0xff, 0xff, 0xff,
+ 0xbd, 0xfe, 0x3e, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0xac, 0x71, 0xff,
+ 0xff, 0xe9, 0xff, 0xff, 0x5b, 0xa0, 0x29, 0xff,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x34, 0xfe,
+ 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0xba, 0x8f, 0xff, 0xff, 0xfd,
+ 0x3f, 0xa2, 0x4, 0x0, 0x2a, 0xf8, 0xad, 0xff,
+ 0xff, 0xfb, 0x1b, 0x36, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xe0,
+ 0xe2, 0xf0, 0x9, 0xad, 0xff, 0xff, 0xff, 0x4d,
+ 0x5a, 0x0, 0x0, 0x7c, 0x49, 0xff, 0xff, 0xff,
+ 0xa5, 0xfe, 0xd0, 0xd0, 0xfe, 0x9f, 0xff, 0xff,
+ 0xff, 0x4b, 0x72, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0xe4, 0xf0, 0xfe, 0x63, 0xf1,
+ 0xff, 0xff, 0xdf, 0x9, 0x3a, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xc8, 0x92, 0x20, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x74, 0x41, 0xfd, 0xff, 0xff, 0xbf,
+ 0x9, 0xdc, 0xb6, 0xd6, 0xfe, 0xd1, 0xff, 0xff,
+ 0xc7, 0xec, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0x6,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x4c, 0x47, 0xff, 0xff, 0xff, 0x81,
+ 0xf6, 0xd0, 0xfe, 0xb7, 0xff, 0xff, 0xf1, 0x9,
+ 0x12, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0x6, 0x4c, 0x5, 0xcf, 0xff, 0xff,
+ 0xff, 0x73, 0xe0, 0xe, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xd2, 0xa6, 0x4a, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x44, 0x11, 0xf3,
+ 0xff, 0xff, 0xff, 0xe7, 0x5, 0x4e, 0x29, 0xff,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x98,
+ 0x39, 0xf9, 0xff, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0x62, 0x2d, 0xf9, 0xff, 0xff,
+ 0xc9, 0x11, 0xe6, 0xce, 0xfa, 0x57, 0xfb, 0xff,
+ 0xff, 0xb5, 0xf6, 0x8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xb7, 0xb7,
+ 0xb7, 0xbf, 0xe3, 0xff, 0xff, 0xff, 0xdb, 0xfe,
+ 0x22, 0x0, 0x0, 0x20, 0xfe, 0xb9, 0xff, 0xff,
+ 0xff, 0xc7, 0x89, 0x93, 0xc9, 0xff, 0xff, 0xff,
+ 0xc5, 0xfe, 0x22, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xb7, 0xb7, 0xbf, 0xe1, 0xff, 0xff,
+ 0xff, 0xf7, 0x49, 0xc0, 0x0, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0x49, 0x50, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x1c, 0xfa, 0xa7, 0xff, 0xff, 0xff,
+ 0xdb, 0x93, 0x83, 0x95, 0xd1, 0xff, 0xff, 0xff,
+ 0xc3, 0xd4, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0x6,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x1e, 0xfe, 0xe3, 0xff, 0xff, 0xf7,
+ 0xa5, 0x89, 0xbb, 0xff, 0xff, 0xff, 0xa7, 0xea,
+ 0x0, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0x6, 0x0, 0xaa, 0x3f, 0xf9, 0xff,
+ 0xff, 0xef, 0x29, 0x90, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0x7f, 0xa2, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0xa, 0xee, 0xb1,
+ 0xff, 0xff, 0xff, 0x9f, 0xde, 0x2a, 0x29, 0xff,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x16,
+ 0xf2, 0x9b, 0xff, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0x10, 0xee, 0x8f, 0xff, 0xff,
+ 0xff, 0xe3, 0x9f, 0x91, 0xb7, 0xfb, 0xff, 0xff,
+ 0xef, 0x31, 0x88, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xed, 0x45, 0xba,
+ 0x0, 0x0, 0x0, 0x0, 0x88, 0x1d, 0xcf, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf,
+ 0x27, 0x98, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xe5, 0x4d, 0xe8, 0x20, 0x0, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0x74, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x70, 0xb, 0xb7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf,
+ 0x37, 0x98, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x6, 0x0, 0x0, 0x0, 0x6,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0,
+ 0x0, 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70,
+ 0x0, 0x0, 0x0, 0xc8, 0x5d, 0xfb, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0x1d, 0x74,
+ 0x0, 0x0, 0x0, 0x0, 0xac, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0x6, 0x0, 0x1a, 0xf2, 0x93, 0xff,
+ 0xff, 0xff, 0xbf, 0xfe, 0x40, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xb3, 0xd0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x90, 0x59,
+ 0xff, 0xff, 0xff, 0x41, 0x76, 0x2a, 0x29, 0xff,
+ 0xff, 0xff, 0x77, 0x98, 0x0, 0x0, 0x0, 0xac,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0, 0x0,
+ 0x66, 0x15, 0xe9, 0xff, 0xff, 0xff, 0x83, 0xa6,
+ 0x0, 0x0, 0x0, 0x0, 0x58, 0xfe, 0xa7, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0x51, 0xda, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x86, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfd, 0xf3, 0xd3, 0x95, 0x21, 0xd4, 0x1e,
+ 0x0, 0x0, 0x0, 0x0, 0x8, 0xa4, 0x11, 0x91,
+ 0xe9, 0xff, 0xff, 0xff, 0xff, 0xeb, 0x9b, 0x17,
+ 0xba, 0xe, 0x0, 0x0, 0x0, 0x86, 0x87, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf9, 0xeb, 0xc3, 0x7f,
+ 0xf, 0xca, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x86, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0x60, 0x0,
+ 0x0, 0x86, 0x87, 0xff, 0xff, 0xff, 0xd, 0x4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x2, 0x82, 0xfe, 0x73, 0xd7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x89, 0xd,
+ 0xbc, 0x16, 0x0, 0x0, 0x0, 0x86, 0x87, 0xff,
+ 0xff, 0xff, 0xd, 0x4, 0x0, 0x0, 0x0, 0x4,
+ 0xd, 0xff, 0xff, 0xff, 0x83, 0x82, 0x0, 0x0,
+ 0x0, 0x42, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x52,
+ 0x0, 0x0, 0x0, 0x2e, 0xf0, 0x4d, 0xc9, 0xfd,
+ 0xff, 0xff, 0xff, 0xf3, 0xa5, 0x19, 0xb2, 0x8,
+ 0x0, 0x0, 0x0, 0x0, 0x86, 0x87, 0xff, 0xff,
+ 0xff, 0xd, 0x4, 0x0, 0x0, 0x5e, 0xd, 0xdb,
+ 0xff, 0xff, 0xff, 0x75, 0x40, 0x0, 0x0, 0x86,
+ 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xb3, 0xbc, 0x0, 0x0, 0x86,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x4, 0x2c, 0xfe,
+ 0xe7, 0xff, 0xd9, 0xfe, 0x1c, 0x1c, 0x29, 0xff,
+ 0xff, 0xff, 0x77, 0x74, 0x0, 0x0, 0x0, 0x86,
+ 0x87, 0xff, 0xff, 0xff, 0xd, 0x4, 0x0, 0x0,
+ 0x6, 0xd4, 0x6f, 0xff, 0xff, 0xff, 0x83, 0x82,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x6e, 0xfe, 0x69,
+ 0xd3, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xab, 0x2d,
+ 0xd8, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x50, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xf6, 0xc8, 0x64, 0xa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x5a, 0xda,
+ 0xfe, 0x39, 0x55, 0x55, 0x35, 0xfe, 0xe0, 0x68,
+ 0x6, 0x0, 0x0, 0x0, 0x0, 0x50, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xee, 0xb0,
+ 0x4a, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x50, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x36, 0x0,
+ 0x0, 0x50, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x2,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0xbe, 0xfe,
+ 0x25, 0x4f, 0x57, 0x43, 0x21, 0xfc, 0xc4, 0x54,
+ 0x2, 0x0, 0x0, 0x0, 0x0, 0x50, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0x2, 0x0, 0x0, 0x0, 0x2,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x4c, 0x0, 0x0,
+ 0x0, 0x24, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x2c,
+ 0x0, 0x0, 0x0, 0x0, 0x24, 0xa6, 0xfa, 0x1b,
+ 0x4d, 0x59, 0x47, 0xb, 0xea, 0x70, 0x6, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x50, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0x2, 0x0, 0x0, 0x2, 0xb0, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0x40, 0x0, 0x0, 0x50,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0x7e, 0x0, 0x0, 0x50,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x2, 0x0, 0xc2,
+ 0xfe, 0xfe, 0xfe, 0xac, 0x0, 0xe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0x42, 0x0, 0x0, 0x0, 0x50,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x2, 0x0, 0x0,
+ 0x0, 0x3c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x4c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0xb8,
+ 0xfc, 0x23, 0x4d, 0x57, 0x41, 0x9, 0xea, 0x7e,
+ 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x16, 0x3c, 0x5a, 0x58, 0x38, 0x14, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc,
+ 0x2e, 0x50, 0x5c, 0x48, 0x24, 0xa, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x2a,
+ 0x4e, 0x5e, 0x46, 0x20, 0x2, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xc, 0x2e, 0x50, 0x5a, 0x40, 0x1a, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x62, 0xd0, 0xf0, 0xf8, 0xf8, 0xee, 0xca,
+ 0x4c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90,
+ 0xde, 0xf4, 0xf8, 0xf8, 0xea, 0xbc, 0x24, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xb0, 0x9b, 0xdd, 0xdd, 0xdd, 0xdd, 0x83,
+ 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe2,
+ 0xc3, 0xdd, 0xdd, 0xdd, 0xdd, 0x4d, 0x4a, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x4, 0xe, 0x1a, 0x20, 0x20, 0x20, 0x20,
+ 0x1e, 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x14, 0x3e, 0x66, 0x72, 0x56, 0x28, 0x4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x4, 0xe, 0x1a, 0x20, 0x20, 0x20, 0x20, 0x1e,
+ 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa,
+ 0x32, 0x5e, 0x78, 0x6c, 0x44, 0x18, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x4, 0x10, 0x1a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x1e, 0x12, 0x8, 0x0, 0x0, 0x6,
+ 0x12, 0x1c, 0x1e, 0x14, 0x8, 0x0, 0x0, 0x0,
+ 0x0, 0x4, 0xe, 0x1a, 0x20, 0x16, 0xc, 0x0,
+ 0x0, 0x8, 0x14, 0x1e, 0x1e, 0x14, 0x8, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x4, 0xe, 0x1a, 0x20,
+ 0x18, 0xc, 0x2, 0x2, 0xe, 0x18, 0x20, 0x16,
+ 0xc, 0x0, 0x0, 0x0, 0x8, 0x14, 0x1a, 0x12,
+ 0x8, 0x0, 0x0, 0x0, 0xc, 0x16, 0x20, 0x18,
+ 0xc, 0x2, 0x0, 0x6, 0x12, 0x1c, 0x20, 0x1a,
+ 0xe, 0x4, 0x0, 0x0, 0x0, 0x2, 0xe, 0x18,
+ 0x20, 0x1c, 0x12, 0x6, 0x0, 0xa, 0x14, 0x20,
+ 0x1e, 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x6, 0x12, 0x1c, 0x20, 0x16, 0xc, 0x0, 0x0,
+ 0xc, 0x16, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x18, 0xc, 0x2, 0x0,
+ 0x0, 0xd6, 0xb3, 0xff, 0xff, 0xff, 0xff, 0x99,
+ 0xae, 0x0, 0xa, 0x16, 0x20, 0x1a, 0xe, 0x4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0,
+ 0xe3, 0xff, 0xff, 0xff, 0xff, 0x5b, 0x6a, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x16, 0x1a,
+ 0x10, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x52, 0x11, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x1d, 0xfe, 0xfc, 0xd2, 0x6a, 0xc, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40,
+ 0xc8, 0xfe, 0x35, 0x5d, 0x67, 0x51, 0x11, 0xf2,
+ 0x8a, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x52, 0x11, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x1d, 0xfe, 0xfc, 0xd4, 0x6a, 0xa, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xa8, 0xfc,
+ 0x21, 0x57, 0x69, 0x61, 0x3b, 0xfe, 0xd4, 0x4e,
+ 0x0, 0x0, 0x0, 0x0, 0x60, 0x13, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x1b, 0x98, 0x0, 0x0, 0x7c,
+ 0x17, 0x23, 0x23, 0x1f, 0xbe, 0x0, 0x0, 0x0,
+ 0x0, 0x44, 0xf, 0x23, 0x23, 0x23, 0x5, 0x10,
+ 0x0, 0x92, 0x1f, 0x23, 0x23, 0x1d, 0xd2, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x6c, 0xf, 0x23, 0x23,
+ 0x23, 0xb, 0xe, 0x1c, 0xd, 0x23, 0x23, 0x23,
+ 0xfe, 0x1c, 0x0, 0x0, 0xd6, 0x1d, 0x23, 0x1b,
+ 0xbe, 0x0, 0x0, 0x26, 0x5, 0x23, 0x23, 0x23,
+ 0xb, 0x10, 0x0, 0x36, 0x17, 0x23, 0x23, 0x23,
+ 0x11, 0x8c, 0x0, 0x0, 0x0, 0x76, 0xd, 0x23,
+ 0x23, 0x23, 0x19, 0x4c, 0x0, 0x86, 0x21, 0x23,
+ 0x23, 0x1f, 0xf0, 0x14, 0x0, 0x0, 0x0, 0x4,
+ 0xcc, 0x19, 0x23, 0x23, 0x23, 0x7, 0x0, 0xe,
+ 0x5, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0xb, 0x24, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xf7, 0xcb, 0x79,
+ 0x8a, 0x0, 0xa6, 0x21, 0x23, 0x23, 0x11, 0x82,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc,
+ 0xb3, 0xd5, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x28, 0xfe, 0x21, 0x23,
+ 0x15, 0x9e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x88, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xfd, 0xdd, 0x9b, 0x25, 0xd4, 0x20,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7a, 0xfe,
+ 0x75, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xb5,
+ 0x35, 0xde, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x88, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfd, 0xdd, 0x9b, 0x21, 0xce, 0x16, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x4a, 0xfa, 0x55, 0xcb,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5, 0x85, 0x5,
+ 0x8c, 0x2, 0x0, 0x0, 0x9a, 0x95, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xc7, 0xd4, 0x0, 0x0, 0xba,
+ 0xb1, 0xff, 0xff, 0xe3, 0xee, 0x0, 0x0, 0x0,
+ 0x0, 0x76, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x20,
+ 0x0, 0xae, 0xc3, 0xff, 0xff, 0xef, 0x5, 0x26,
+ 0x0, 0x0, 0x0, 0x0, 0xd4, 0xa1, 0xff, 0xff,
+ 0xfd, 0x29, 0xe, 0x1c, 0x41, 0xff, 0xff, 0xff,
+ 0x41, 0x54, 0x0, 0x20, 0x5, 0xf1, 0xff, 0xe3,
+ 0xfe, 0x12, 0x0, 0x66, 0x51, 0xff, 0xff, 0xff,
+ 0x2b, 0x10, 0x0, 0x36, 0x63, 0xff, 0xff, 0xff,
+ 0xbf, 0xfc, 0x24, 0x0, 0x18, 0xf8, 0xad, 0xff,
+ 0xff, 0xff, 0x7b, 0x4c, 0x0, 0x90, 0xb7, 0xff,
+ 0xff, 0xfb, 0x35, 0x7e, 0x0, 0x0, 0x0, 0x4c,
+ 0xf, 0xeb, 0xff, 0xff, 0xdb, 0x7, 0x0, 0x1c,
+ 0x29, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x3c, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfe, 0xb4,
+ 0x44, 0x0, 0xbe, 0xd1, 0xff, 0xff, 0xb7, 0xf2,
+ 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7c,
+ 0xdc, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x96, 0x55, 0xff, 0xff,
+ 0xcd, 0xfc, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0x49, 0xce,
+ 0x6, 0x0, 0x0, 0x0, 0x0, 0x60, 0x5, 0xb1,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf1, 0x57, 0xde, 0x14, 0x0, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xe9, 0x37, 0x9e, 0x0,
+ 0x0, 0x0, 0x0, 0x1a, 0xf4, 0x7f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf,
+ 0xd, 0x5e, 0x0, 0x0, 0xac, 0x95, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xc7, 0xe0, 0x0, 0x0, 0xda,
+ 0xb1, 0xff, 0xff, 0xe3, 0xfa, 0x0, 0x0, 0x0,
+ 0x0, 0x98, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x2e,
+ 0x0, 0xae, 0x77, 0xff, 0xff, 0xff, 0x53, 0x7c,
+ 0x0, 0x0, 0x0, 0x16, 0xfe, 0xdf, 0xff, 0xff,
+ 0xc9, 0xfc, 0xe, 0x1a, 0xfe, 0xf3, 0xff, 0xff,
+ 0x75, 0x9a, 0x0, 0x6a, 0x49, 0xff, 0xff, 0xff,
+ 0x31, 0x52, 0x0, 0xac, 0x81, 0xff, 0xff, 0xe7,
+ 0xfe, 0xe, 0x0, 0x32, 0xfe, 0xbf, 0xff, 0xff,
+ 0xff, 0x4f, 0xa4, 0x0, 0x8e, 0x3b, 0xfb, 0xff,
+ 0xff, 0xd3, 0xfe, 0x46, 0x0, 0x8c, 0x39, 0xfb,
+ 0xff, 0xff, 0xab, 0xf2, 0xe, 0x0, 0x0, 0xd0,
+ 0x81, 0xff, 0xff, 0xff, 0x67, 0xc0, 0x0, 0x22,
+ 0x29, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x41, 0x3a, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0xbe, 0x7d, 0xff, 0xff, 0xf7, 0x1f,
+ 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0xc, 0xf6, 0xb7, 0xff, 0xff,
+ 0xff, 0x41, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xad, 0xad,
+ 0xad, 0xb3, 0xd5, 0xff, 0xff, 0xff, 0xe9, 0x17,
+ 0x50, 0x0, 0x0, 0x0, 0x12, 0xf4, 0x97, 0xff,
+ 0xff, 0xff, 0xd9, 0x91, 0x83, 0xad, 0xf9, 0xff,
+ 0xff, 0xf1, 0x35, 0x8e, 0x0, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xad, 0xad, 0xad,
+ 0xb9, 0xe5, 0xff, 0xff, 0xff, 0xc7, 0xfc, 0xc,
+ 0x0, 0x0, 0x0, 0x70, 0x3f, 0xfb, 0xff, 0xff,
+ 0xef, 0x99, 0x75, 0x87, 0xd5, 0xff, 0xff, 0xff,
+ 0x95, 0xde, 0x0, 0x0, 0x7e, 0x65, 0xad, 0xad,
+ 0xad, 0xad, 0xb7, 0xff, 0xff, 0xff, 0xc7, 0xad,
+ 0xad, 0xad, 0xad, 0x87, 0xb6, 0x0, 0x0, 0xda,
+ 0xb1, 0xff, 0xff, 0xe3, 0xfa, 0x0, 0x0, 0x0,
+ 0x0, 0x98, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x2e,
+ 0x0, 0x48, 0x1b, 0xf9, 0xff, 0xff, 0x9b, 0xd4,
+ 0x0, 0x0, 0x0, 0x62, 0x39, 0xff, 0xff, 0xff,
+ 0x7f, 0xb8, 0x0, 0x0, 0xf6, 0xc9, 0xff, 0xff,
+ 0xa1, 0xd0, 0x0, 0xbe, 0x8b, 0xff, 0xff, 0xff,
+ 0x79, 0xa6, 0x0, 0xdc, 0xad, 0xff, 0xff, 0xbd,
+ 0xec, 0x0, 0x0, 0x0, 0x92, 0x33, 0xf7, 0xff,
+ 0xff, 0xc9, 0xfe, 0x46, 0xfc, 0xb9, 0xff, 0xff,
+ 0xfd, 0x4b, 0xb0, 0x0, 0x0, 0x16, 0xf6, 0xab,
+ 0xff, 0xff, 0xf9, 0x29, 0x70, 0x0, 0x40, 0x9,
+ 0xe3, 0xff, 0xff, 0xd1, 0xfe, 0x36, 0x0, 0x16,
+ 0x1b, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xb7, 0xff, 0xff, 0xff, 0xb9, 0xfe, 0x1a, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x4c, 0x19, 0xf5, 0xff, 0xff, 0x83,
+ 0xc4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x60, 0x27, 0xf9, 0xff, 0xff,
+ 0xff, 0xa7, 0xec, 0x6, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xd8,
+ 0xd8, 0xe6, 0xfe, 0x87, 0xff, 0xff, 0xff, 0x81,
+ 0xa8, 0x0, 0x0, 0x0, 0x68, 0x31, 0xfb, 0xff,
+ 0xff, 0xc1, 0x9, 0xda, 0xc0, 0xf8, 0x51, 0xf9,
+ 0xff, 0xff, 0xb9, 0xf8, 0x8, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xd8, 0xda,
+ 0xee, 0xd, 0xc3, 0xff, 0xff, 0xff, 0x25, 0x2e,
+ 0x0, 0x0, 0x0, 0xb2, 0x97, 0xff, 0xff, 0xfb,
+ 0x3d, 0xea, 0xae, 0xd0, 0x5, 0xcf, 0xff, 0xff,
+ 0xeb, 0x5, 0xc, 0x0, 0x34, 0x92, 0xc4, 0xd6,
+ 0xd6, 0xe0, 0x35, 0xff, 0xff, 0xff, 0x6f, 0xec,
+ 0xd6, 0xd6, 0xcc, 0xa0, 0x50, 0x0, 0x0, 0xda,
+ 0xb1, 0xff, 0xff, 0xe3, 0xfa, 0x0, 0x0, 0x0,
+ 0x0, 0x98, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x2e,
+ 0x0, 0x8, 0xf6, 0xbf, 0xff, 0xff, 0xd9, 0xfe,
+ 0x10, 0x0, 0x0, 0xbe, 0x89, 0xff, 0xff, 0xfb,
+ 0x23, 0x50, 0x0, 0x0, 0xce, 0x9d, 0xff, 0xff,
+ 0xc7, 0xf2, 0x0, 0xf2, 0xc3, 0xff, 0xff, 0xff,
+ 0xb3, 0xe8, 0x0, 0xf8, 0xd3, 0xff, 0xff, 0x8d,
+ 0xbc, 0x0, 0x0, 0x0, 0x14, 0xee, 0x93, 0xff,
+ 0xff, 0xff, 0x59, 0xe2, 0x47, 0xff, 0xff, 0xff,
+ 0xab, 0xfa, 0x22, 0x0, 0x0, 0x0, 0x7c, 0x2d,
+ 0xf7, 0xff, 0xff, 0x9f, 0xec, 0x8, 0xc4, 0x75,
+ 0xff, 0xff, 0xff, 0x59, 0xb2, 0x0, 0x0, 0x8,
+ 0x7a, 0xb6, 0xd6, 0xd6, 0xd6, 0xd6, 0xde, 0xfe,
+ 0xad, 0xff, 0xff, 0xf1, 0x29, 0x8a, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x6, 0xf0, 0xb1, 0xff, 0xff, 0xd5,
+ 0xfe, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0xd8, 0x91, 0xff, 0xff, 0xd5,
+ 0xff, 0xf3, 0x17, 0x4e, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x50, 0xfe, 0xe5, 0xff, 0xff, 0xb5,
+ 0xd6, 0x0, 0x0, 0x0, 0xbe, 0x93, 0xff, 0xff,
+ 0xfb, 0x37, 0x96, 0x0, 0x0, 0x26, 0xf6, 0xa9,
+ 0xff, 0xff, 0xfd, 0x1f, 0x3a, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0,
+ 0x2, 0xb6, 0x63, 0xff, 0xff, 0xff, 0x4f, 0x44,
+ 0x0, 0x0, 0x0, 0xc8, 0xad, 0xff, 0xff, 0xe7,
+ 0xfe, 0x46, 0x0, 0x0, 0xb8, 0x7b, 0xf5, 0xf5,
+ 0xf5, 0x27, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xda,
+ 0xb1, 0xff, 0xff, 0xe3, 0xfa, 0x0, 0x0, 0x0,
+ 0x0, 0x98, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x2e,
+ 0x0, 0x0, 0xa8, 0x71, 0xff, 0xff, 0xff, 0x2f,
+ 0x56, 0x0, 0x6, 0xf8, 0xc9, 0xff, 0xff, 0xc5,
+ 0xfa, 0xc, 0x0, 0x0, 0x8c, 0x69, 0xff, 0xff,
+ 0xeb, 0xfe, 0x30, 0x5, 0xf3, 0xff, 0xff, 0xff,
+ 0xe7, 0xfe, 0x2c, 0x5, 0xf5, 0xff, 0xff, 0x57,
+ 0x76, 0x0, 0x0, 0x0, 0x0, 0x5e, 0xf, 0xe3,
+ 0xff, 0xff, 0xd1, 0xfe, 0xc3, 0xff, 0xff, 0xef,
+ 0x21, 0x7a, 0x0, 0x0, 0x0, 0x0, 0x10, 0xf0,
+ 0x9d, 0xff, 0xff, 0xf3, 0x1f, 0x82, 0xfe, 0xdb,
+ 0xff, 0xff, 0xc5, 0xfe, 0x2a, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xc4, 0x57,
+ 0xff, 0xff, 0xff, 0x7b, 0xe2, 0xe, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x90, 0x55, 0xff, 0xff, 0xff,
+ 0x43, 0x7e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x34, 0x5, 0xe5, 0xff, 0xd5, 0x65,
+ 0xff, 0xff, 0x81, 0xc6, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x4, 0xfa, 0xcf, 0xff, 0xff, 0xc1,
+ 0xe0, 0x0, 0x0, 0x0, 0xe8, 0xc5, 0xff, 0xff,
+ 0xd3, 0xfc, 0x16, 0x0, 0x0, 0x0, 0x8e, 0x53,
+ 0xff, 0xff, 0xff, 0x65, 0x72, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0,
+ 0x0, 0xa2, 0x5b, 0xff, 0xff, 0xff, 0x41, 0x3e,
+ 0x0, 0x0, 0x0, 0xb2, 0x97, 0xff, 0xff, 0xff,
+ 0x63, 0xfc, 0x9a, 0x2e, 0x44, 0xe6, 0xfc, 0xfe,
+ 0xfa, 0xde, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xda,
+ 0xb1, 0xff, 0xff, 0xe3, 0xfa, 0x0, 0x0, 0x0,
+ 0x0, 0x98, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x2e,
+ 0x0, 0x0, 0x42, 0x15, 0xf7, 0xff, 0xff, 0x81,
+ 0xb4, 0x0, 0x40, 0x19, 0xf9, 0xff, 0xff, 0x7b,
+ 0xb2, 0x0, 0x0, 0x0, 0x42, 0x29, 0xff, 0xff,
+ 0xff, 0x29, 0x90, 0x4d, 0xff, 0xff, 0xfb, 0xff,
+ 0xff, 0x37, 0x8c, 0x3d, 0xff, 0xff, 0xfb, 0x15,
+ 0x2e, 0x0, 0x0, 0x0, 0x0, 0x4, 0xca, 0x63,
+ 0xff, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0x7d,
+ 0xe0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6c,
+ 0x21, 0xf1, 0xff, 0xff, 0x93, 0xf6, 0x67, 0xff,
+ 0xff, 0xff, 0x49, 0xa0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0x11, 0xe1,
+ 0xff, 0xff, 0xc9, 0xfe, 0x44, 0x0, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x28, 0xfe, 0xdf, 0xff, 0xff,
+ 0xa5, 0xe8, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0xa6, 0x65, 0xff, 0xff, 0x7f, 0x5,
+ 0xe7, 0xff, 0xd9, 0xfe, 0x26, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x6, 0x82, 0x11, 0xef, 0xff, 0xff, 0xad,
+ 0xd0, 0x0, 0x0, 0x0, 0xf4, 0xd9, 0xff, 0xff,
+ 0xb7, 0xe6, 0x0, 0x0, 0x0, 0x0, 0x32, 0x27,
+ 0xff, 0xff, 0xff, 0x7d, 0x9a, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xba, 0xbe,
+ 0xda, 0xfe, 0xab, 0xff, 0xff, 0xe7, 0xd, 0x1e,
+ 0x0, 0x0, 0x0, 0x6e, 0x3d, 0xfb, 0xff, 0xff,
+ 0xfd, 0xbb, 0x5d, 0x5, 0xe8, 0x88, 0x22, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xda,
+ 0xb1, 0xff, 0xff, 0xe3, 0xfa, 0x0, 0x0, 0x0,
+ 0x0, 0x98, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x2e,
+ 0x0, 0x0, 0x6, 0xf4, 0xbb, 0xff, 0xff, 0xc3,
+ 0xf6, 0x4, 0x9c, 0x6d, 0xff, 0xff, 0xf9, 0x1f,
+ 0x4a, 0x0, 0x0, 0x0, 0xe, 0xfe, 0xe5, 0xff,
+ 0xff, 0x63, 0xde, 0x8f, 0xff, 0xff, 0xad, 0xff,
+ 0xff, 0x7f, 0xdc, 0x71, 0xff, 0xff, 0xd9, 0xfc,
+ 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0xfe,
+ 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0x5,
+ 0x48, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa,
+ 0xe6, 0x91, 0xff, 0xff, 0xef, 0x15, 0xd1, 0xff,
+ 0xff, 0xbb, 0xfc, 0x20, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x1e, 0xf6, 0x9b, 0xff,
+ 0xff, 0xf7, 0x39, 0xa0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xd2, 0x8f, 0xff, 0xff,
+ 0xef, 0xd, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x14, 0xfc, 0xc3, 0xff, 0xf5, 0x1b, 0xe6,
+ 0x97, 0xff, 0xff, 0x53, 0x94, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xfa,
+ 0xfa, 0xfe, 0x19, 0xb3, 0xff, 0xff, 0xff, 0x6d,
+ 0x96, 0x0, 0x0, 0x0, 0xf8, 0xdf, 0xff, 0xff,
+ 0xad, 0xda, 0x0, 0x0, 0x0, 0x0, 0x10, 0xd,
+ 0xff, 0xff, 0xff, 0x87, 0xa8, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0x93, 0x91, 0x91,
+ 0x9f, 0xcf, 0xff, 0xff, 0xf7, 0x5f, 0xce, 0x2,
+ 0x0, 0x0, 0x0, 0x1a, 0xf4, 0x7b, 0xfb, 0xff,
+ 0xff, 0xff, 0xff, 0xed, 0xad, 0x4b, 0xfe, 0x96,
+ 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xda,
+ 0xb1, 0xff, 0xff, 0xe3, 0xfa, 0x0, 0x0, 0x0,
+ 0x0, 0x98, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x2e,
+ 0x0, 0x0, 0x0, 0xa2, 0x6d, 0xff, 0xff, 0xf7,
+ 0x11, 0x36, 0xea, 0xb1, 0xff, 0xff, 0xc1, 0xf8,
+ 0x8, 0x0, 0x0, 0x0, 0x0, 0xec, 0xbb, 0xff,
+ 0xff, 0x91, 0xfc, 0xc5, 0xff, 0xf9, 0x31, 0xff,
+ 0xff, 0xb7, 0xfa, 0x9d, 0xff, 0xff, 0xad, 0xde,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x94,
+ 0x35, 0xf7, 0xff, 0xff, 0xff, 0xfd, 0x4d, 0xb2,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x15, 0xeb, 0xff, 0xff, 0xb9, 0xff, 0xff,
+ 0xfb, 0x3d, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xb2, 0x47, 0xfb, 0xff,
+ 0xff, 0x8d, 0xee, 0x16, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x60, 0x2b, 0xfb, 0xff,
+ 0xff, 0x6f, 0xac, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x32, 0x35, 0xfd, 0xff, 0xaf, 0xf0, 0x6e,
+ 0x31, 0xfd, 0xff, 0xb7, 0xb6, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xdf, 0xdf,
+ 0xdf, 0xe5, 0xfb, 0xff, 0xff, 0xff, 0xd1, 0x5,
+ 0x3a, 0x0, 0x0, 0x0, 0xfa, 0xdf, 0xff, 0xff,
+ 0xad, 0xd6, 0x0, 0x0, 0x0, 0x0, 0x6, 0xd,
+ 0xff, 0xff, 0xff, 0x87, 0xac, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xd1, 0x3d, 0xf4, 0x32, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x46, 0xf4, 0x49, 0xc7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x31,
+ 0xd4, 0x16, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xda,
+ 0xb1, 0xff, 0xff, 0xe3, 0xfa, 0x0, 0x0, 0x0,
+ 0x0, 0x98, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x2e,
+ 0x0, 0x0, 0x0, 0x3c, 0x11, 0xf3, 0xff, 0xff,
+ 0x63, 0xa0, 0xfe, 0xed, 0xff, 0xff, 0x75, 0xac,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xba, 0x8b, 0xff,
+ 0xff, 0xb9, 0x9, 0xf3, 0xff, 0xcb, 0xfe, 0xdb,
+ 0xff, 0xeb, 0xfe, 0xc5, 0xff, 0xff, 0x7b, 0xa6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36,
+ 0xfe, 0xc3, 0xff, 0xff, 0xff, 0xd9, 0xfe, 0x58,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x6, 0xda, 0x83, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xad, 0xf8, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x54, 0x9, 0xd5, 0xff, 0xff,
+ 0xd9, 0x9, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xe, 0xfa, 0xc1, 0xff,
+ 0xff, 0xc5, 0xfa, 0x10, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x32, 0x4b, 0x91, 0x91, 0x3d, 0x86, 0x12,
+ 0xf6, 0x7b, 0x91, 0x89, 0xa8, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xcb, 0x25, 0xa6,
+ 0x0, 0x0, 0x0, 0x0, 0xf8, 0xdf, 0xff, 0xff,
+ 0xad, 0xda, 0x0, 0x0, 0x0, 0x0, 0x12, 0xd,
+ 0xff, 0xff, 0xff, 0x87, 0xa6, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xb9, 0xf, 0x6e, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0xa2, 0xfc,
+ 0x41, 0x9d, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xed,
+ 0x39, 0x9a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xda,
+ 0xb1, 0xff, 0xff, 0xe3, 0xfa, 0x0, 0x0, 0x0,
+ 0x0, 0x98, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x2e,
+ 0x0, 0x0, 0x0, 0x4, 0xf0, 0xb5, 0xff, 0xff,
+ 0xa9, 0xf0, 0x4f, 0xff, 0xff, 0xf7, 0x19, 0x46,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x72, 0x55, 0xff,
+ 0xff, 0xdf, 0x51, 0xff, 0xff, 0x95, 0xf8, 0xa7,
+ 0xff, 0xff, 0x3d, 0xeb, 0xff, 0xff, 0x43, 0x5a,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xba,
+ 0x53, 0xff, 0xff, 0xff, 0xff, 0xff, 0x73, 0xda,
+ 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x4e, 0xb, 0xe3, 0xff, 0xff, 0xff, 0xf9,
+ 0x2f, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x16, 0xee, 0x8b, 0xff, 0xff, 0xfb,
+ 0x4b, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xa6, 0x6b, 0xff,
+ 0xff, 0xfd, 0x2f, 0x66, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x20, 0x6e, 0xa0, 0x9e, 0x6a, 0x1a, 0x0,
+ 0x46, 0x86, 0xac, 0x8e, 0x52, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xdd, 0xdd,
+ 0xdd, 0xdb, 0xcf, 0xab, 0x5f, 0xfe, 0x9e, 0xc,
+ 0x0, 0x0, 0x0, 0x0, 0xf4, 0xd9, 0xff, 0xff,
+ 0xbb, 0xe8, 0x0, 0x0, 0x0, 0x0, 0x36, 0x2b,
+ 0xff, 0xff, 0xff, 0x7b, 0x96, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0x4d, 0x49, 0x49,
+ 0x5b, 0xa5, 0xff, 0xff, 0xff, 0xa3, 0xe4, 0x0,
+ 0x0, 0x0, 0x6, 0x3a, 0x62, 0x80, 0x6a, 0x56,
+ 0x7e, 0xd8, 0xfe, 0x47, 0xb7, 0xff, 0xff, 0xff,
+ 0xc3, 0xf8, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd8,
+ 0xb1, 0xff, 0xff, 0xe3, 0xfc, 0x0, 0x0, 0x0,
+ 0x0, 0x9c, 0x77, 0xff, 0xff, 0xff, 0x2f, 0x2a,
+ 0x0, 0x0, 0x0, 0x0, 0x9a, 0x67, 0xff, 0xff,
+ 0xe5, 0xfe, 0x9b, 0xff, 0xff, 0xbd, 0xf6, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x13, 0xfb,
+ 0xff, 0xfb, 0x97, 0xff, 0xff, 0x55, 0xc4, 0x6b,
+ 0xff, 0xff, 0x93, 0xff, 0xff, 0xf3, 0x5, 0x1c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50, 0x9,
+ 0xd9, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xeb, 0x1f,
+ 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x2, 0xd2, 0x75, 0xff, 0xff, 0xff, 0xa1,
+ 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x9e, 0x37, 0xf7, 0xff, 0xff, 0xa1,
+ 0xf8, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0xb, 0xed,
+ 0xff, 0xff, 0x93, 0xd6, 0x0, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0xf8,
+ 0xf8, 0xf6, 0xee, 0xd4, 0x8e, 0x30, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xe8, 0xc3, 0xff, 0xff,
+ 0xd5, 0xfe, 0x1a, 0x0, 0x0, 0x0, 0x92, 0x55,
+ 0xff, 0xff, 0xff, 0x5f, 0x6a, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x58, 0x5e,
+ 0x94, 0xfe, 0xb7, 0xff, 0xff, 0xef, 0xfe, 0x6,
+ 0x0, 0x0, 0xe, 0x15, 0x67, 0x67, 0x67, 0x2d,
+ 0x5e, 0x0, 0x1e, 0x90, 0xfe, 0xb3, 0xff, 0xff,
+ 0xfb, 0x13, 0x16, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd0,
+ 0xab, 0xff, 0xff, 0xeb, 0xfe, 0x8, 0x0, 0x0,
+ 0x0, 0xb2, 0x81, 0xff, 0xff, 0xff, 0x25, 0x1c,
+ 0x0, 0x0, 0x0, 0x0, 0x36, 0xd, 0xf1, 0xff,
+ 0xff, 0x41, 0xd9, 0xff, 0xff, 0x71, 0xa6, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfc, 0xd9,
+ 0xff, 0xff, 0xeb, 0xff, 0xf5, 0xb, 0x5c, 0x1f,
+ 0xfd, 0xff, 0xeb, 0xff, 0xff, 0xcb, 0xf6, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe6, 0x87,
+ 0xff, 0xff, 0xff, 0x75, 0xf9, 0xff, 0xff, 0xab,
+ 0xfa, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x68, 0x43, 0xff, 0xff, 0xff, 0x63,
+ 0x9e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x44, 0xfe, 0xc7, 0xff, 0xff, 0xe3, 0x15,
+ 0x6c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xe4, 0xa1,
+ 0xff, 0xff, 0xe3, 0xfe, 0x2c, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xba, 0x8f, 0xff, 0xff,
+ 0xfd, 0x3f, 0xa2, 0x4, 0x0, 0x2a, 0xf8, 0xad,
+ 0xff, 0xff, 0xf7, 0x15, 0x34, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0,
+ 0x0, 0xbc, 0x81, 0xff, 0xff, 0xff, 0x19, 0x14,
+ 0x0, 0x0, 0xe, 0x1d, 0xff, 0xff, 0xff, 0x8b,
+ 0xd0, 0x6, 0x0, 0x0, 0xcc, 0x77, 0xff, 0xff,
+ 0xff, 0x31, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb6,
+ 0x97, 0xff, 0xff, 0xff, 0x1d, 0x74, 0x0, 0x0,
+ 0x16, 0xf2, 0xa5, 0xff, 0xff, 0xf9, 0x5, 0xc,
+ 0x0, 0x0, 0x0, 0x0, 0x2, 0xee, 0xb1, 0xff,
+ 0xff, 0x9f, 0xff, 0xff, 0xf7, 0x15, 0x40, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xde, 0xad,
+ 0xff, 0xff, 0xff, 0xff, 0xc5, 0xf6, 0xa, 0xfc,
+ 0xd5, 0xff, 0xff, 0xff, 0xff, 0x9d, 0xce, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x84, 0x29, 0xf3,
+ 0xff, 0xff, 0xc3, 0xfe, 0xa5, 0xff, 0xff, 0xfd,
+ 0x51, 0xba, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4c, 0x43, 0xff, 0xff, 0xff, 0x63,
+ 0x7a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xe, 0xe2, 0x79, 0xff, 0xff, 0xff, 0x5d, 0xca,
+ 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78, 0x41,
+ 0xff, 0xff, 0xff, 0x5b, 0x96, 0x0, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x62, 0x2d, 0xf9, 0xff,
+ 0xff, 0xc9, 0x11, 0xe6, 0xce, 0xfa, 0x57, 0xfb,
+ 0xff, 0xff, 0xad, 0xf2, 0x6, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0,
+ 0x0, 0x94, 0x73, 0xff, 0xff, 0xff, 0x29, 0x2a,
+ 0x0, 0x0, 0x8, 0xfe, 0xdf, 0xff, 0xff, 0xdd,
+ 0x15, 0xe8, 0xb4, 0xc8, 0xfe, 0xb1, 0xff, 0xff,
+ 0xfb, 0x11, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7a,
+ 0x59, 0xff, 0xff, 0xff, 0xa7, 0xfe, 0xd8, 0xc4,
+ 0xf4, 0x37, 0xf5, 0xff, 0xff, 0xcb, 0xf8, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x94, 0x5f, 0xff,
+ 0xff, 0xf3, 0xff, 0xff, 0xb9, 0xf4, 0x6, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa4, 0x7b,
+ 0xff, 0xff, 0xff, 0xff, 0x8d, 0xbe, 0x0, 0xd4,
+ 0x9f, 0xff, 0xff, 0xff, 0xff, 0x6b, 0x8e, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x2a, 0xfc, 0xb3, 0xff,
+ 0xff, 0xfd, 0x47, 0xca, 0x27, 0xf5, 0xff, 0xff,
+ 0xd9, 0x9, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4c, 0x43, 0xff, 0xff, 0xff, 0x63,
+ 0x7a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x8a, 0x27, 0xf1, 0xff, 0xff, 0xb3, 0xfe, 0xe4,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xcc, 0x98, 0x2e, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0xfe,
+ 0xd1, 0xff, 0xff, 0xb5, 0xf4, 0x8, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x10, 0xee, 0x8f, 0xff,
+ 0xff, 0xff, 0xe3, 0x9f, 0x91, 0xb7, 0xfb, 0xff,
+ 0xff, 0xeb, 0x27, 0x7e, 0x0, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0,
+ 0x0, 0x7e, 0x69, 0xff, 0xff, 0xff, 0x39, 0x52,
+ 0x0, 0x0, 0x0, 0xc8, 0x75, 0xff, 0xff, 0xff,
+ 0xe9, 0x9f, 0x81, 0x89, 0xc3, 0xff, 0xff, 0xff,
+ 0xbf, 0xf8, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a,
+ 0xfe, 0xd1, 0xff, 0xff, 0xff, 0xd1, 0x97, 0x89,
+ 0xab, 0xf7, 0xff, 0xff, 0xff, 0x5f, 0xac, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0x9, 0xef,
+ 0xff, 0xff, 0xff, 0xff, 0x6b, 0xa0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x58, 0x41,
+ 0xff, 0xff, 0xff, 0xff, 0x49, 0x6a, 0x0, 0x86,
+ 0x61, 0xff, 0xff, 0xff, 0xff, 0x2b, 0x42, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xbc, 0x57, 0xff, 0xff,
+ 0xff, 0xb5, 0xfa, 0x2a, 0xec, 0x95, 0xff, 0xff,
+ 0xff, 0x8b, 0xec, 0x14, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4c, 0x43, 0xff, 0xff, 0xff, 0x63,
+ 0x7a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe,
+ 0xfe, 0xb9, 0xff, 0xff, 0xff, 0xc3, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0x5d, 0x6c, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbe,
+ 0x7d, 0xff, 0xff, 0xf7, 0x1d, 0x52, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x58, 0xfe, 0xa7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x4f, 0xf2, 0x20, 0x0, 0x0, 0x0, 0x0,
+ 0xac, 0x87, 0xff, 0xff, 0xff, 0xd, 0x6, 0x0,
+ 0x0, 0x58, 0x51, 0xff, 0xff, 0xff, 0x67, 0xba,
+ 0x0, 0x0, 0x0, 0x40, 0xfe, 0x91, 0xfd, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5,
+ 0x31, 0x92, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x36, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x8c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xaa, 0x31, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xfd, 0x8b, 0xfe, 0x30, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xea, 0xad,
+ 0xff, 0xff, 0xff, 0xf3, 0x11, 0x3c, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0xfe,
+ 0xf3, 0xff, 0xff, 0xef, 0x5, 0x20, 0x0, 0x34,
+ 0x15, 0xf9, 0xff, 0xff, 0xe7, 0xfe, 0xe, 0x0,
+ 0x0, 0x0, 0x0, 0x52, 0x9, 0xdb, 0xff, 0xff,
+ 0xfb, 0x35, 0x8a, 0x0, 0x62, 0x19, 0xed, 0xff,
+ 0xff, 0xf7, 0x33, 0x94, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4c, 0x43, 0xff, 0xff, 0xff, 0x63,
+ 0x7a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e,
+ 0x29, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0x98, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4e,
+ 0x19, 0xf5, 0xff, 0xff, 0x81, 0xc4, 0x0, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x86, 0x87, 0xff, 0xff, 0xff, 0xd, 0x4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6e, 0xfe,
+ 0x69, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xdb, 0x33, 0xd6, 0x22, 0x0, 0x0, 0x0,
+ 0x86, 0x87, 0xff, 0xff, 0xff, 0xd, 0x4, 0x0,
+ 0x0, 0x28, 0xf, 0xe9, 0xff, 0xff, 0xcf, 0xb4,
+ 0x0, 0x0, 0x0, 0x0, 0x5a, 0xf8, 0x49, 0xbb,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xed, 0xa1, 0x21,
+ 0xc6, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x26, 0x35, 0xff, 0xff, 0xff, 0x6f, 0x68,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x12, 0xc0, 0x17, 0x97, 0xe7, 0xff, 0xff, 0xff,
+ 0xff, 0xf9, 0xc3, 0x55, 0xf8, 0x54, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8e, 0x5b,
+ 0xff, 0xff, 0xff, 0xb7, 0xee, 0x4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0,
+ 0xc9, 0xff, 0xff, 0xbf, 0xea, 0x0, 0x0, 0x6,
+ 0xf6, 0xcf, 0xff, 0xff, 0xbd, 0xe2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x52, 0x89, 0xff, 0xff, 0xff,
+ 0xa7, 0xf4, 0x14, 0x0, 0x6, 0xde, 0x87, 0xff,
+ 0xff, 0xff, 0xc1, 0x94, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x36, 0x43, 0xff, 0xff, 0xff, 0x63,
+ 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e,
+ 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0x82, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfa, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6,
+ 0xf2, 0xb1, 0xff, 0xff, 0xd5, 0xfe, 0x1c, 0x0,
+ 0x5c, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x50, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x2,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36,
+ 0xb8, 0xfc, 0x23, 0x4d, 0x59, 0x3d, 0x63, 0xf5,
+ 0xff, 0xff, 0xeb, 0x4d, 0x50, 0x0, 0x0, 0x0,
+ 0x50, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x2, 0x0,
+ 0x0, 0x4, 0xc8, 0xfe, 0xfe, 0xfe, 0xfe, 0xa0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x94, 0xf2,
+ 0x9, 0x43, 0x59, 0x55, 0x35, 0xfe, 0xe4, 0x70,
+ 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x14, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x3c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x6, 0x64, 0xda, 0xfe, 0x31, 0x53, 0x59,
+ 0x47, 0xf, 0xf6, 0xa0, 0x28, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0x80, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x98,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0x8a, 0x0, 0x0, 0x0,
+ 0x9e, 0xfe, 0xfe, 0xfe, 0xfe, 0x88, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x52, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0x6e, 0x0, 0x0, 0x0, 0x50, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0x8c, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x1c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x32, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x4c, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xdf, 0xfe, 0x96,
+ 0x36, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x90, 0x57, 0xff, 0xff, 0xff, 0x43, 0x3e, 0x64,
+ 0xca, 0x4d, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xc, 0x2e, 0x50, 0x5a, 0x66, 0xea, 0x4b,
+ 0xed, 0xff, 0xf5, 0x67, 0x50, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1c, 0x42, 0x60, 0x5a, 0x3a, 0x14, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x12, 0x36, 0x58, 0x60,
+ 0x48, 0x22, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xdc, 0xb3, 0xff, 0xff, 0xf3, 0xb1, 0x69,
+ 0x82, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x2a, 0xfe, 0x8d, 0x95, 0x95, 0x51, 0x3e, 0xd6,
+ 0x9b, 0xbf, 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0xd8,
+ 0x35, 0xc9, 0x49, 0xe8, 0x34, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xda, 0xb3, 0xff, 0xff, 0xff, 0xff, 0x99,
+ 0xae, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x56, 0x94, 0xbc, 0xa8, 0x76, 0x26, 0xf2,
+ 0xe3, 0xff, 0xff, 0xff, 0xff, 0x5b, 0x6e, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14,
+ 0xa0, 0xac, 0xa4, 0x20, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xb8, 0xab, 0xf5, 0xf5, 0xf5, 0xf5, 0x91,
+ 0x98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xea,
+ 0xd9, 0xf5, 0xf5, 0xf5, 0xf5, 0x57, 0x4e, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x74, 0xec, 0xfc, 0xfe, 0xfe, 0xfc, 0xe8,
+ 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac,
+ 0xf4, 0xfe, 0xfe, 0xfe, 0xfc, 0xe2, 0x2a, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x24, 0x52, 0x76, 0x76, 0x52, 0x26,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xa, 0x3e, 0x66, 0x80, 0x66, 0x3e, 0xa,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x54,
+ 0x98, 0xba, 0xb2, 0x80, 0x34, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x28, 0x56, 0x78, 0x74, 0x50, 0x22, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x1a, 0x4a, 0x70, 0x7e, 0x5c, 0x30, 0x0,
+ 0x0, 0x0, 0x1a, 0x4a, 0x70, 0x7e, 0x5c, 0x32,
+ 0x0, 0x0, 0x0, 0x24, 0x52, 0x76, 0x78, 0x54,
+ 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1a, 0x4a, 0x70, 0x7e, 0x5c,
+ 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3c,
+ 0x6e, 0x90, 0x86, 0x5e, 0x2a, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xa0, 0x4f, 0x67, 0x67, 0x51, 0xa8,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x32, 0x21, 0x67, 0x67, 0x67, 0x21, 0x32,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x4e, 0xf2, 0x2d,
+ 0x7f, 0x9b, 0x99, 0x67, 0xaa, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xb4, 0x55, 0x67, 0x67, 0x4d, 0x98, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x78, 0x41, 0x67, 0x67, 0x5f, 0xda, 0x0,
+ 0x0, 0x0, 0x74, 0x3f, 0x67, 0x67, 0x63, 0xe4,
+ 0x0, 0x0, 0x0, 0xa0, 0x4f, 0x67, 0x67, 0x51,
+ 0xac, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x78, 0x41, 0x67, 0x67, 0x5f,
+ 0xda, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x17,
+ 0x71, 0x73, 0x73, 0x57, 0xf0, 0x22, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xd4, 0xc3, 0xff, 0xff, 0xcb, 0xdc,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x54, 0x57, 0xff, 0xff, 0xff, 0x57, 0x54,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x28, 0xfc, 0x85, 0xfb,
+ 0xff, 0xff, 0xff, 0xc7, 0xd2, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xe4, 0xd3, 0xff, 0xff, 0xbd, 0xce, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xae, 0x9f, 0xff, 0xff, 0xef, 0xf8, 0x0,
+ 0x0, 0x0, 0xaa, 0x9b, 0xff, 0xff, 0xf5, 0xfa,
+ 0x0, 0x0, 0x0, 0xd4, 0xc3, 0xff, 0xff, 0xcd,
+ 0xde, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xae, 0x9f, 0xff, 0xff, 0xef,
+ 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x6c,
+ 0x77, 0xfb, 0xff, 0xfb, 0x4d, 0xc8, 0x8, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x6a, 0x57, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x8e, 0x55, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xb1, 0xcc, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xae, 0x9f, 0xff, 0xff, 0xef, 0xf8, 0x0,
+ 0x0, 0x0, 0xaa, 0x9b, 0xff, 0xff, 0xf5, 0xfc,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40,
+ 0xf6, 0x6d, 0xfb, 0xff, 0xe3, 0x1d, 0x5e, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x6a, 0x57, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xd2, 0xaf, 0xff, 0xff,
+ 0xfb, 0x7b, 0x45, 0x37, 0x86, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x78, 0x41, 0x67, 0x67, 0x5f, 0xda, 0x0,
+ 0x0, 0x0, 0x74, 0x3d, 0x63, 0x63, 0x5d, 0xe4,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x3a, 0xf2, 0x65, 0xd9, 0xd9, 0x95, 0x5e, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x6a, 0x57, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xec, 0xcd, 0xff, 0xff,
+ 0xc9, 0xfe, 0x6a, 0x30, 0x16, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x1a, 0x4a, 0x70, 0x7e, 0x5c, 0x30, 0x0,
+ 0x0, 0x0, 0x18, 0x46, 0x6a, 0x78, 0x58, 0x2e,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x34, 0xbe, 0xea, 0xee, 0xca, 0x58, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x3c, 0xa6, 0xe8, 0xfa,
+ 0xfc, 0xf8, 0xe4, 0x98, 0x2a, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xf8,
+ 0xea, 0xf8, 0xf8, 0xe2, 0x8e, 0x1c, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x8e, 0xde,
+ 0xf8, 0xfc, 0xf8, 0xe4, 0x98, 0x2a, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x48, 0xbc, 0xf0, 0xfa,
+ 0xf4, 0xe2, 0x57, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x74, 0xd4,
+ 0xf6, 0xfa, 0xf8, 0xe6, 0x9e, 0x2e, 0x0, 0x0,
+ 0x0, 0x0, 0x56, 0xb6, 0xfe, 0xd3, 0xff, 0xff,
+ 0xbd, 0xfc, 0xd4, 0x9a, 0x18, 0x0, 0x0, 0x0,
+ 0x0, 0x46, 0xba, 0xf0, 0xfa, 0xf6, 0xd2, 0xa4,
+ 0xc0, 0xe2, 0xd4, 0x9e, 0x1e, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xf2, 0xe0, 0xf8,
+ 0xf8, 0xea, 0xa2, 0x28, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x44, 0xae, 0xdc, 0xe6, 0xc8, 0x80, 0x0,
+ 0x0, 0x0, 0x38, 0xa8, 0xd8, 0xea, 0xce, 0x90,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xf0, 0x0, 0x5c, 0xb8, 0xe0, 0xea, 0xd4, 0x9e,
+ 0x20, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0x5e, 0xb8, 0xe0, 0xdc,
+ 0xb2, 0x9c, 0xda, 0xf6, 0xf8, 0xee, 0xae, 0x34,
+ 0x60, 0xce, 0xf4, 0xfa, 0xf2, 0xc0, 0x48, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x68, 0xbe, 0xe2, 0xda,
+ 0xae, 0x92, 0xd6, 0xf6, 0xfa, 0xf0, 0xb6, 0x3c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14,
+ 0x78, 0xd4, 0xf6, 0xfa, 0xfa, 0xec, 0xb0, 0x44,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4, 0x8e, 0xfe, 0x6f, 0xbf, 0xe3,
+ 0xef, 0xe3, 0xb9, 0x57, 0xfc, 0x5c, 0x0, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0x49,
+ 0xc3, 0xeb, 0xe5, 0xb7, 0x41, 0xea, 0x26, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x52, 0xfa, 0x4d, 0xb3,
+ 0xdf, 0xed, 0xe3, 0xb9, 0x57, 0xfc, 0x5c, 0x0,
+ 0x0, 0x0, 0x0, 0x7a, 0xfe, 0x7d, 0xd1, 0xeb,
+ 0xdd, 0x8f, 0x59, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0x0, 0x2a, 0xe2, 0x2f, 0xa3,
+ 0xdb, 0xed, 0xe3, 0xbd, 0x5d, 0xfe, 0x60, 0x0,
+ 0x0, 0x0, 0xae, 0x8d, 0xc3, 0xf3, 0xff, 0xff,
+ 0xeb, 0xc3, 0xc3, 0x3b, 0x38, 0x0, 0x0, 0x0,
+ 0x76, 0xfe, 0x7b, 0xcf, 0xeb, 0xdf, 0x97, 0xd,
+ 0xa5, 0xc3, 0xc3, 0x45, 0x44, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0x2d, 0xb1, 0xe5,
+ 0xe9, 0xc3, 0x55, 0xf2, 0x2e, 0x0, 0x0, 0x0,
+ 0x0, 0x90, 0x79, 0xc3, 0xc3, 0xb7, 0xe8, 0x0,
+ 0x0, 0x0, 0x78, 0x69, 0xc3, 0xc3, 0xc3, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xf0, 0x24, 0xfa, 0x93, 0xc3, 0xc3, 0xc3, 0x53,
+ 0x20, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xb8, 0x95, 0xc3, 0xc3,
+ 0x79, 0x27, 0xa9, 0xe1, 0xeb, 0xcb, 0x61, 0xfc,
+ 0x11, 0x95, 0xdb, 0xed, 0xd5, 0x7d, 0xfe, 0x5c,
+ 0x0, 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xc3, 0xc3,
+ 0x71, 0x1d, 0xa1, 0xdf, 0xeb, 0xcf, 0x71, 0xfe,
+ 0x4c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0xec,
+ 0x35, 0xa5, 0xd9, 0xeb, 0xe5, 0xc5, 0x79, 0x5,
+ 0x9e, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x62, 0x11, 0xc1, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x93, 0xfa, 0x22, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xe5, 0xf5,
+ 0xff, 0xff, 0xff, 0xff, 0xf7, 0x53, 0xc4, 0x2,
+ 0x0, 0x0, 0x0, 0x2c, 0xfa, 0x89, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x93, 0xfe, 0x2e,
+ 0x0, 0x0, 0x38, 0xfe, 0xb1, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xd9, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0xe, 0xdc, 0x57, 0xf3, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x97, 0xfc, 0x2e,
+ 0x0, 0x0, 0xca, 0xbb, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x4d, 0x4a, 0x0, 0x0, 0x36,
+ 0xfe, 0xad, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1,
+ 0xf1, 0xff, 0xff, 0x5b, 0x66, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xd1, 0xeb, 0xff, 0xff,
+ 0xff, 0xff, 0xfb, 0x5d, 0xbc, 0x0, 0x0, 0x0,
+ 0x0, 0xbc, 0x9f, 0xff, 0xff, 0xef, 0xfa, 0x0,
+ 0x0, 0x0, 0xa4, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xf0, 0xb4, 0x4f, 0xff, 0xff, 0xff, 0xbf, 0xd4,
+ 0x20, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xe0, 0xc3, 0xff, 0xff,
+ 0xbf, 0xe5, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x61,
+ 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0x93, 0xee,
+ 0x8, 0x0, 0x0, 0x0, 0xea, 0xd1, 0xff, 0xff,
+ 0xb1, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83,
+ 0xe0, 0x2, 0x0, 0x0, 0x0, 0x1c, 0xec, 0x6b,
+ 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcd,
+ 0x1d, 0x88, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xd2, 0x99, 0xff, 0xff, 0xff, 0xcb,
+ 0xb7, 0xed, 0xff, 0xff, 0xff, 0x4b, 0x76, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xff, 0xff,
+ 0xdb, 0xe1, 0xff, 0xff, 0xff, 0xe1, 0xb, 0x34,
+ 0x0, 0x0, 0x0, 0xac, 0x59, 0xff, 0xff, 0xff,
+ 0xef, 0xd1, 0xf3, 0xff, 0xff, 0xff, 0x5d, 0x9c,
+ 0x0, 0x0, 0xb0, 0x6b, 0xff, 0xff, 0xff, 0xf9,
+ 0xd5, 0xef, 0xff, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0x70, 0x29, 0xef, 0xff, 0xff,
+ 0xf9, 0xd5, 0xed, 0xff, 0xff, 0xff, 0x5d, 0xa2,
+ 0x0, 0x0, 0xb0, 0x93, 0xcb, 0xf3, 0xff, 0xff,
+ 0xef, 0xcb, 0xcb, 0x3d, 0x38, 0x0, 0x0, 0xae,
+ 0x67, 0xff, 0xff, 0xff, 0xf9, 0xd5, 0xed, 0xff,
+ 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xff, 0xf9, 0xd7, 0xeb,
+ 0xff, 0xff, 0xff, 0xd9, 0xfe, 0x10, 0x0, 0x0,
+ 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef, 0xfe, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xf4, 0x5, 0xd5, 0xff, 0xff, 0xed, 0x27, 0x8e,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff,
+ 0xff, 0xfb, 0xd7, 0xeb, 0xff, 0xff, 0xff, 0xf9,
+ 0xff, 0xe5, 0xdb, 0xff, 0xff, 0xff, 0xf9, 0x1f,
+ 0x3c, 0x0, 0x0, 0x0, 0xf2, 0xd1, 0xff, 0xff,
+ 0xff, 0xfb, 0xd9, 0xe7, 0xff, 0xff, 0xff, 0xef,
+ 0xb, 0x22, 0x0, 0x0, 0x0, 0x92, 0x41, 0xf9,
+ 0xff, 0xff, 0xf9, 0xd1, 0xe3, 0xff, 0xff, 0xff,
+ 0xb9, 0xfe, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xd4, 0xdf, 0xff, 0xff, 0xbd, 0xfe,
+ 0xf6, 0x31, 0xf9, 0xff, 0xff, 0x9b, 0xb8, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xf7, 0x4f,
+ 0xfe, 0xfe, 0x8f, 0xff, 0xff, 0xff, 0x63, 0x88,
+ 0x0, 0x0, 0x8, 0xfa, 0xcb, 0xff, 0xff, 0xdb,
+ 0x19, 0xfc, 0x23, 0xdf, 0xff, 0xff, 0xbb, 0xe2,
+ 0x0, 0x4, 0xf8, 0xcb, 0xff, 0xff, 0xf1, 0x39,
+ 0xfe, 0x11, 0xbb, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0xda, 0xa1, 0xff, 0xff, 0xf9,
+ 0x49, 0xfe, 0x15, 0xd5, 0xff, 0xff, 0xc3, 0xf0,
+ 0x0, 0x0, 0x5a, 0xbc, 0xfe, 0xd3, 0xff, 0xff,
+ 0xbd, 0xfe, 0xda, 0xa2, 0x18, 0x0, 0x2, 0xf8,
+ 0xcb, 0xff, 0xff, 0xf1, 0x39, 0xfe, 0xb, 0xad,
+ 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xe3, 0x35, 0xfe, 0x9,
+ 0xbb, 0xff, 0xff, 0xff, 0x2d, 0x36, 0x0, 0x0,
+ 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef, 0xfe, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xfe, 0x81, 0xff, 0xff, 0xff, 0x67, 0xda, 0xc,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff,
+ 0xed, 0x43, 0xfe, 0xd, 0xcf, 0xff, 0xff, 0xff,
+ 0x8f, 0xfe, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0x67,
+ 0x78, 0x0, 0x0, 0x0, 0xf2, 0xd1, 0xff, 0xff,
+ 0xeb, 0x43, 0xfe, 0xfe, 0xa7, 0xff, 0xff, 0xff,
+ 0x49, 0x50, 0x0, 0x0, 0x2, 0xf2, 0xbb, 0xff,
+ 0xff, 0xf1, 0x39, 0xfc, 0xfe, 0xa1, 0xff, 0xff,
+ 0xff, 0x49, 0x76, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xbc, 0x2b, 0x2f, 0x2f, 0x1d, 0xda,
+ 0xc0, 0xfe, 0xdf, 0xff, 0xff, 0xb1, 0xd4, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xfa,
+ 0x24, 0x5a, 0x11, 0xf7, 0xff, 0xff, 0xa1, 0xca,
+ 0x0, 0x0, 0x2a, 0x1b, 0xfd, 0xff, 0xff, 0x87,
+ 0xd4, 0x12, 0xd0, 0x89, 0xff, 0xff, 0xdd, 0xdc,
+ 0x0, 0x1e, 0xf, 0xfb, 0xff, 0xff, 0xa7, 0xee,
+ 0x1a, 0xaa, 0x57, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x4, 0xfe, 0xe3, 0xff, 0xff, 0xb5,
+ 0xfe, 0xdc, 0xf8, 0x87, 0xff, 0xff, 0xf1, 0xfe,
+ 0x2, 0x0, 0x0, 0x0, 0xf4, 0xd3, 0xff, 0xff,
+ 0xbd, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x1c, 0xd,
+ 0xfb, 0xff, 0xff, 0xa7, 0xee, 0x18, 0x9c, 0x51,
+ 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xf2, 0x14, 0xa4,
+ 0x5d, 0xff, 0xff, 0xff, 0x57, 0x60, 0x0, 0x0,
+ 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef, 0xfe, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0x25, 0xf1, 0xff, 0xff, 0xaf, 0xfe, 0x36, 0x0,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff,
+ 0xcb, 0xf8, 0x1e, 0xce, 0x87, 0xff, 0xff, 0xff,
+ 0x25, 0x68, 0x4e, 0x1b, 0xff, 0xff, 0xff, 0x87,
+ 0xa4, 0x0, 0x0, 0x0, 0xf2, 0xd1, 0xff, 0xff,
+ 0xc1, 0xf6, 0x1c, 0x8e, 0x4f, 0xff, 0xff, 0xff,
+ 0x67, 0x78, 0x0, 0x0, 0x1a, 0xb, 0xf7, 0xff,
+ 0xff, 0xa3, 0xee, 0x16, 0x6e, 0x1d, 0xf7, 0xff,
+ 0xff, 0x9b, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x14, 0x90, 0xfa, 0x2d, 0x6f, 0x8d,
+ 0x97, 0x99, 0xef, 0xff, 0xff, 0xb3, 0xdc, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee,
+ 0x0, 0x4, 0xfa, 0xd1, 0xff, 0xff, 0xc1, 0xe4,
+ 0x0, 0x0, 0x52, 0x4f, 0xff, 0xff, 0xff, 0x59,
+ 0x80, 0x0, 0x52, 0xfe, 0xfe, 0xfe, 0xfe, 0xb4,
+ 0x0, 0x40, 0x41, 0xff, 0xff, 0xff, 0x6f, 0xa2,
+ 0x0, 0x6a, 0x57, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x12, 0x11, 0xff, 0xff, 0xff, 0xd3,
+ 0xad, 0xad, 0xad, 0xc9, 0xff, 0xff, 0xff, 0xb,
+ 0x6, 0x0, 0x0, 0x0, 0xf4, 0xd3, 0xff, 0xff,
+ 0xbd, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x41,
+ 0xff, 0xff, 0xff, 0x6f, 0xa2, 0x0, 0x64, 0x51,
+ 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x56,
+ 0x41, 0xff, 0xff, 0xff, 0x65, 0x78, 0x0, 0x0,
+ 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef, 0xfe, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xfb,
+ 0xf3, 0xff, 0xff, 0xe5, 0x19, 0x7a, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff,
+ 0xcb, 0xee, 0x0, 0x9c, 0x75, 0xff, 0xff, 0xff,
+ 0x29, 0x26, 0x8, 0xfe, 0xfb, 0xff, 0xff, 0x91,
+ 0xb4, 0x0, 0x0, 0x0, 0xf2, 0xd1, 0xff, 0xff,
+ 0xc1, 0xe8, 0x0, 0x48, 0x39, 0xff, 0xff, 0xff,
+ 0x6f, 0x88, 0x0, 0x0, 0x3e, 0x3f, 0xff, 0xff,
+ 0xff, 0x6d, 0xa0, 0x0, 0x8, 0xfa, 0xd5, 0xff,
+ 0xff, 0xc1, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x8c, 0x19, 0xb5, 0xfd, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xb3, 0xdc, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee,
+ 0x0, 0x0, 0xee, 0xc3, 0xff, 0xff, 0xcd, 0xec,
+ 0x0, 0x0, 0x6a, 0x5f, 0xff, 0xff, 0xff, 0x47,
+ 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x58, 0x51, 0xff, 0xff, 0xff, 0x59, 0x7a,
+ 0x0, 0x6a, 0x57, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x1c, 0x2b, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15,
+ 0xa, 0x0, 0x0, 0x0, 0xf4, 0xd3, 0xff, 0xff,
+ 0xbd, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x58, 0x51,
+ 0xff, 0xff, 0xff, 0x59, 0x7a, 0x0, 0x64, 0x51,
+ 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x46,
+ 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0, 0x0,
+ 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef, 0xfe, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xd3, 0x5, 0x54, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff,
+ 0xcb, 0xee, 0x0, 0x94, 0x73, 0xff, 0xff, 0xff,
+ 0x29, 0x2a, 0x0, 0xfe, 0xfb, 0xff, 0xff, 0x91,
+ 0xb8, 0x0, 0x0, 0x0, 0xf2, 0xd1, 0xff, 0xff,
+ 0xc1, 0xe8, 0x0, 0x3e, 0x39, 0xff, 0xff, 0xff,
+ 0x6f, 0x8c, 0x0, 0x0, 0x54, 0x53, 0xff, 0xff,
+ 0xff, 0x57, 0x7a, 0x0, 0x0, 0xf0, 0xc3, 0xff,
+ 0xff, 0xcd, 0xec, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xc, 0xfc, 0xbd, 0xff, 0xff, 0xfb, 0xb5,
+ 0x91, 0x8d, 0xed, 0xff, 0xff, 0xb3, 0xdc, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee,
+ 0x0, 0x0, 0xf0, 0xc5, 0xff, 0xff, 0xcb, 0xec,
+ 0x0, 0x0, 0x5e, 0x57, 0xff, 0xff, 0xff, 0x51,
+ 0x6e, 0x0, 0x18, 0x54, 0x80, 0x92, 0x6c, 0x38,
+ 0x0, 0x52, 0x4f, 0xff, 0xff, 0xff, 0x5d, 0x82,
+ 0x0, 0x6a, 0x57, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x18, 0x21, 0xff, 0xff, 0xff, 0xdb,
+ 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xf,
+ 0x8, 0x0, 0x0, 0x0, 0xf4, 0xd3, 0xff, 0xff,
+ 0xbd, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x52, 0x4f,
+ 0xff, 0xff, 0xff, 0x5d, 0x82, 0x0, 0x64, 0x51,
+ 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x46,
+ 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0, 0x0,
+ 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef, 0xfe, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xf1,
+ 0xcd, 0xff, 0xff, 0xff, 0x87, 0xea, 0x12, 0x0,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff,
+ 0xcb, 0xee, 0x0, 0x94, 0x73, 0xff, 0xff, 0xff,
+ 0x29, 0x2a, 0x0, 0xfe, 0xfb, 0xff, 0xff, 0x91,
+ 0xb8, 0x0, 0x0, 0x0, 0xf2, 0xd1, 0xff, 0xff,
+ 0xc1, 0xe8, 0x0, 0x3e, 0x39, 0xff, 0xff, 0xff,
+ 0x6f, 0x8c, 0x0, 0x0, 0x48, 0x49, 0xff, 0xff,
+ 0xff, 0x65, 0x8a, 0x0, 0x0, 0xf6, 0xcf, 0xff,
+ 0xff, 0xc7, 0xe8, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x2a, 0x27, 0xff, 0xff, 0xff, 0x91, 0xfc,
+ 0xc8, 0xfe, 0xdd, 0xff, 0xff, 0xb3, 0xdc, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xf4,
+ 0x4, 0x1c, 0xfe, 0xdb, 0xff, 0xff, 0xb9, 0xde,
+ 0x0, 0x0, 0x3a, 0x33, 0xff, 0xff, 0xff, 0x71,
+ 0xb2, 0x0, 0x82, 0x3b, 0x77, 0x77, 0x69, 0xb6,
+ 0x0, 0x34, 0x33, 0xff, 0xff, 0xff, 0x7d, 0xc4,
+ 0x0, 0x7c, 0x57, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0xa, 0xfe, 0xf5, 0xff, 0xff, 0xab,
+ 0xfe, 0xea, 0xe8, 0xe8, 0xea, 0xea, 0xd0, 0x8e,
+ 0x4, 0x0, 0x0, 0x0, 0xf4, 0xd3, 0xff, 0xff,
+ 0xbd, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x34, 0x33,
+ 0xff, 0xff, 0xff, 0x7d, 0xc4, 0x0, 0x74, 0x51,
+ 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x46,
+ 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0, 0x0,
+ 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef, 0xfe, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xfe, 0xd1, 0xff, 0xff, 0xf5, 0x31, 0x96, 0x0,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff,
+ 0xcb, 0xee, 0x0, 0x94, 0x73, 0xff, 0xff, 0xff,
+ 0x29, 0x2a, 0x0, 0xfe, 0xfb, 0xff, 0xff, 0x91,
+ 0xb8, 0x0, 0x0, 0x0, 0xf2, 0xd1, 0xff, 0xff,
+ 0xc1, 0xe8, 0x0, 0x3e, 0x39, 0xff, 0xff, 0xff,
+ 0x6f, 0x8c, 0x0, 0x0, 0x26, 0x1f, 0xff, 0xff,
+ 0xff, 0x89, 0xd0, 0x0, 0x36, 0x5, 0xeb, 0xff,
+ 0xff, 0xab, 0xd2, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x32, 0x47, 0xff, 0xff, 0xff, 0x65, 0xe8,
+ 0xd4, 0x13, 0xe9, 0xff, 0xff, 0xb3, 0xe2, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xe1, 0xf,
+ 0xe2, 0xee, 0x43, 0xff, 0xff, 0xff, 0x8f, 0xb4,
+ 0x0, 0x0, 0x14, 0xfe, 0xe5, 0xff, 0xff, 0xbd,
+ 0xfe, 0xd8, 0xfe, 0xad, 0xff, 0xff, 0xd1, 0xce,
+ 0x0, 0x14, 0xfe, 0xef, 0xff, 0xff, 0xcd, 0x5,
+ 0xde, 0xfa, 0x7b, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0xf2, 0xc1, 0xff, 0xff, 0xf5,
+ 0x39, 0xf0, 0xbe, 0xd4, 0xfc, 0x3b, 0xf4, 0x10,
+ 0x0, 0x0, 0x0, 0x0, 0xf4, 0xd3, 0xff, 0xff,
+ 0xbd, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x14, 0xfe,
+ 0xed, 0xff, 0xff, 0xcd, 0x5, 0xdc, 0xf8, 0x71,
+ 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x46,
+ 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0, 0x0,
+ 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef, 0xfe, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xfa, 0x47, 0xfb, 0xff, 0xff, 0xc3, 0xfe, 0x3e,
+ 0x0, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff,
+ 0xcb, 0xee, 0x0, 0x94, 0x73, 0xff, 0xff, 0xff,
+ 0x29, 0x2a, 0x0, 0xfe, 0xfb, 0xff, 0xff, 0x91,
+ 0xb8, 0x0, 0x0, 0x0, 0xf2, 0xd1, 0xff, 0xff,
+ 0xc1, 0xe8, 0x0, 0x3e, 0x39, 0xff, 0xff, 0xff,
+ 0x6f, 0x8c, 0x0, 0x0, 0x8, 0xfc, 0xd5, 0xff,
+ 0xff, 0xd7, 0x5, 0xd6, 0xf2, 0x69, 0xff, 0xff,
+ 0xff, 0x6d, 0x94, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x28, 0x21, 0xff, 0xff, 0xff, 0xd9, 0x73,
+ 0x81, 0xd9, 0xff, 0xff, 0xff, 0xbf, 0xec, 0x0,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xff, 0xd9,
+ 0x95, 0xa1, 0xed, 0xff, 0xff, 0xfb, 0x35, 0x64,
+ 0x0, 0x0, 0x0, 0xd4, 0x8b, 0xff, 0xff, 0xff,
+ 0xbf, 0x8d, 0xb5, 0xff, 0xff, 0xff, 0x8d, 0xc2,
+ 0x0, 0x0, 0xe6, 0xa7, 0xff, 0xff, 0xff, 0xcd,
+ 0x93, 0xaf, 0xfb, 0xff, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0x9e, 0x55, 0xff, 0xff, 0xff,
+ 0xf1, 0xa1, 0x87, 0x97, 0xcd, 0xfd, 0x2f, 0x5c,
+ 0x0, 0x0, 0x0, 0x0, 0xf4, 0xd3, 0xff, 0xff,
+ 0xbd, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe4,
+ 0xa5, 0xff, 0xff, 0xff, 0xcd, 0x93, 0xab, 0xf9,
+ 0xff, 0xff, 0xff, 0x5b, 0x70, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x46,
+ 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0, 0x0,
+ 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef, 0xfe, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xf2, 0xf8, 0xa3, 0xff, 0xff, 0xff, 0x71, 0xdc,
+ 0xa, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff,
+ 0xcb, 0xee, 0x0, 0x94, 0x73, 0xff, 0xff, 0xff,
+ 0x29, 0x2a, 0x0, 0xfe, 0xfb, 0xff, 0xff, 0x91,
+ 0xb8, 0x0, 0x0, 0x0, 0xf2, 0xd1, 0xff, 0xff,
+ 0xc1, 0xe8, 0x0, 0x3e, 0x39, 0xff, 0xff, 0xff,
+ 0x6f, 0x8c, 0x0, 0x0, 0x0, 0xba, 0x6f, 0xff,
+ 0xff, 0xff, 0xcd, 0x89, 0x9d, 0xf3, 0xff, 0xff,
+ 0xe1, 0x9, 0x3a, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xa, 0xfc, 0xbb, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfb, 0xe5, 0xff, 0xff, 0xd7, 0xfc, 0x8,
+ 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xe3, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xf6, 0x14,
+ 0x0, 0x0, 0x0, 0x54, 0xb, 0xcb, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xf, 0x58,
+ 0x0, 0x0, 0x76, 0x27, 0xeb, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xe9, 0xfd, 0xff, 0xff, 0x57, 0x6a,
+ 0x0, 0x0, 0x0, 0x28, 0xfa, 0x91, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0x64,
+ 0x0, 0x0, 0x0, 0x0, 0xf4, 0xd3, 0xff, 0xff,
+ 0xbd, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x72,
+ 0x23, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb,
+ 0xff, 0xff, 0xff, 0x5b, 0x6e, 0x0, 0x0, 0x0,
+ 0xf4, 0xd3, 0xff, 0xff, 0xbd, 0xe4, 0x0, 0x46,
+ 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0, 0x0,
+ 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef, 0xfe, 0x0,
+ 0x0, 0x0, 0xb0, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcd,
+ 0xf0, 0x70, 0x19, 0xe9, 0xff, 0xff, 0xed, 0x21,
+ 0x80, 0x0, 0x0, 0xc8, 0x9f, 0xff, 0xff, 0xef,
+ 0xfe, 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff,
+ 0xcb, 0xee, 0x0, 0x94, 0x73, 0xff, 0xff, 0xff,
+ 0x29, 0x2a, 0x0, 0xfe, 0xfb, 0xff, 0xff, 0x91,
+ 0xb8, 0x0, 0x0, 0x0, 0xf2, 0xd1, 0xff, 0xff,
+ 0xc1, 0xe8, 0x0, 0x3e, 0x39, 0xff, 0xff, 0xff,
+ 0x6f, 0x8c, 0x0, 0x0, 0x0, 0x3c, 0xfe, 0xad,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3,
+ 0x4b, 0xc2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xf4, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0x8c, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x8a, 0x1d, 0xb9, 0xff, 0xff, 0xff,
+ 0xe9, 0x67, 0x8b, 0xff, 0xff, 0xfd, 0x1f, 0x8,
+ 0x0, 0x0, 0xce, 0xc3, 0xff, 0xff, 0x77, 0x97,
+ 0xf9, 0xff, 0xff, 0xf5, 0x99, 0x5, 0x66, 0x0,
+ 0x0, 0x0, 0x0, 0x2, 0x9a, 0x11, 0x9d, 0xf1,
+ 0xff, 0xff, 0xff, 0xf3, 0x9d, 0x13, 0xa2, 0x4,
+ 0x0, 0x0, 0xc, 0xd0, 0x3d, 0xcd, 0xff, 0xff,
+ 0xff, 0xd9, 0x3f, 0xe3, 0xff, 0xff, 0x57, 0x4c,
+ 0x0, 0x0, 0x0, 0x0, 0x58, 0xfe, 0x6d, 0xdb,
+ 0xff, 0xff, 0xff, 0xff, 0xeb, 0xa1, 0x23, 0x58,
+ 0x0, 0x0, 0x0, 0x0, 0xe0, 0xd3, 0xff, 0xff,
+ 0xbd, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc,
+ 0xce, 0x39, 0xcb, 0xff, 0xff, 0xff, 0xdf, 0x81,
+ 0xff, 0xff, 0xff, 0x59, 0x64, 0x0, 0x0, 0x0,
+ 0xe0, 0xd3, 0xff, 0xff, 0xbd, 0xc8, 0x0, 0x30,
+ 0x3f, 0xff, 0xff, 0xff, 0x67, 0x60, 0x0, 0x0,
+ 0x0, 0xa4, 0x9f, 0xff, 0xff, 0xef, 0xf6, 0x0,
+ 0x0, 0x0, 0xb2, 0x89, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0xce, 0xc3, 0xff, 0xff, 0xcd,
+ 0xd8, 0x6, 0xd4, 0x6d, 0xff, 0xff, 0xff, 0xb1,
+ 0x80, 0x0, 0x0, 0xa4, 0x9f, 0xff, 0xff, 0xef,
+ 0xf6, 0x0, 0x0, 0x0, 0xce, 0xc3, 0xff, 0xff,
+ 0xcb, 0xd6, 0x0, 0x70, 0x73, 0xff, 0xff, 0xff,
+ 0x29, 0x1c, 0x0, 0xfe, 0xfb, 0xff, 0xff, 0x91,
+ 0x94, 0x0, 0x0, 0x0, 0xdc, 0xd1, 0xff, 0xff,
+ 0xc1, 0xcc, 0x0, 0x2a, 0x39, 0xff, 0xff, 0xff,
+ 0x6f, 0x68, 0x0, 0x0, 0x0, 0x0, 0x76, 0xfe,
+ 0x83, 0xe5, 0xff, 0xff, 0xff, 0xfb, 0xc1, 0x3f,
+ 0xe0, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xc1, 0xcc, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x8, 0x88, 0xfa, 0x25, 0x53, 0x45,
+ 0x9, 0xe0, 0xfe, 0xfe, 0xfe, 0xfe, 0xf2, 0x8,
+ 0x0, 0x0, 0x90, 0xfe, 0xfe, 0xfe, 0xfe, 0xf4,
+ 0x1d, 0x4f, 0x49, 0x11, 0xea, 0x60, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x4, 0x66, 0xe4, 0x5,
+ 0x3f, 0x55, 0x45, 0xb, 0xe8, 0x68, 0x4, 0x0,
+ 0x0, 0x0, 0x0, 0x18, 0xa4, 0xfc, 0x2d, 0x53,
+ 0x3d, 0xfe, 0xec, 0xfe, 0xfe, 0xfe, 0xfe, 0x2a,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0xc2, 0xfe,
+ 0x2d, 0x51, 0x51, 0x31, 0xfe, 0xe2, 0x70, 0xc,
+ 0x0, 0x0, 0x0, 0x0, 0xa6, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0x88, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1e, 0xa6, 0xfc, 0x2d, 0x53, 0x41, 0xfe, 0x73,
+ 0xff, 0xff, 0xff, 0x43, 0x42, 0x0, 0x0, 0x0,
+ 0xa6, 0xfe, 0xfe, 0xfe, 0xfe, 0x88, 0x0, 0x1a,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x36, 0x0, 0x0,
+ 0x0, 0x68, 0xfe, 0xfe, 0xfe, 0xfe, 0xd2, 0x0,
+ 0x0, 0x0, 0xc2, 0x8d, 0xff, 0xff, 0xff, 0xfe,
+ 0x0, 0x0, 0x0, 0x90, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x9c, 0x0, 0x3a, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x7c, 0x0, 0x0, 0x68, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xd2, 0x0, 0x0, 0x0, 0x90, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0x98, 0x0, 0x40, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xe, 0x0, 0xec, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x5a, 0x0, 0x0, 0x0, 0xa2, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0x8c, 0x0, 0x16, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4c,
+ 0xd4, 0xfe, 0x35, 0x53, 0x49, 0x1b, 0xf8, 0x98,
+ 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xc1, 0xda, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xc, 0x30, 0x48, 0x40,
+ 0x1e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8,
+ 0x2c, 0x46, 0x42, 0x24, 0x4, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a,
+ 0x3e, 0x54, 0x42, 0x1e, 0x2, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x34, 0x48,
+ 0x3c, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10,
+ 0x32, 0x50, 0x52, 0x36, 0x12, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5a, 0x21, 0x15, 0xf6, 0xde, 0xe8, 0xb, 0xcf,
+ 0xff, 0xff, 0xf3, 0xb, 0x1e, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7a, 0xbe, 0xfe, 0xb1, 0xff, 0xff, 0xef, 0xfe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x14, 0x38, 0x50, 0x48, 0x26, 0x8, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xf8, 0xa7, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0x7f, 0xac, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xba, 0x8d, 0xf3, 0xb9, 0x9b, 0x9d, 0xdb, 0xff,
+ 0xff, 0xff, 0xa1, 0xea, 0x2, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc,
+ 0x5, 0xb1, 0xb9, 0xfd, 0xff, 0xff, 0xbf, 0xee,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x70, 0xb0, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xc8, 0x9a, 0x48, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xc2, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xcd, 0x13, 0x6c, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20,
+ 0x23, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x4d, 0x9a,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xa4, 0x55, 0xb1, 0xef, 0xff, 0xff, 0xfd, 0xdb,
+ 0x89, 0xb, 0x9e, 0x4, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20,
+ 0x37, 0xff, 0xff, 0xff, 0xd5, 0x57, 0xf0, 0x22,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x28, 0x90, 0xea, 0xfe, 0x21, 0x2d, 0x11, 0xfc,
+ 0xc8, 0x52, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16,
+ 0xfa, 0x1b, 0x35, 0x1b, 0xfc, 0xb4, 0x2a, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xa, 0x1a, 0x1c, 0x14, 0x4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x8, 0x1a, 0x22, 0x1a, 0x8, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26,
+ 0x86, 0x86, 0x72, 0x2, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x76,
+ 0x84, 0x80, 0x1e, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x7a, 0xfe,
+ 0x51, 0xa9, 0xd, 0x3a, 0x0, 0x0, 0x0, 0xa,
+ 0x16, 0x16, 0xc, 0x0, 0x0, 0x0, 0x4e, 0x1f,
+ 0xa7, 0x45, 0xfa, 0x68, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0xb, 0xb1,
+ 0xff, 0xff, 0x69, 0x4e, 0x0, 0x0, 0x0, 0xec,
+ 0x21, 0x23, 0x5, 0x10, 0x0, 0x0, 0x64, 0x7d,
+ 0xff, 0xff, 0x9f, 0xfe, 0x4c, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xaa,
+ 0xe2, 0xf6, 0xe8, 0xbc, 0x3a, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x6, 0xe6, 0x99, 0xff,
+ 0xff, 0xd9, 0x3f, 0x4c, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x20, 0x0, 0x0, 0x60, 0x49,
+ 0xe1, 0xff, 0xff, 0x83, 0xd4, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xb,
+ 0xd7, 0xd7, 0xd7, 0x6d, 0x76, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x2c, 0x17, 0xf1, 0xff,
+ 0xff, 0x37, 0xba, 0x1a, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x20, 0xca,
+ 0x4f, 0xff, 0xff, 0xe7, 0x9, 0x16, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xd,
+ 0xff, 0xff, 0xff, 0x83, 0x9e, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x60, 0x53, 0xff, 0xff,
+ 0xdd, 0xfe, 0x16, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x24,
+ 0xfe, 0xed, 0xff, 0xff, 0x39, 0x40, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x5e, 0xb8, 0xe0, 0xdc, 0xb6, 0xb6, 0xea,
+ 0xf8, 0xf8, 0xe0, 0x88, 0x16, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x48, 0xbc, 0xf0, 0xfa,
+ 0xf6, 0xd0, 0xa6, 0xc4, 0xe4, 0xd2, 0x9a, 0x16,
+ 0x0, 0x0, 0x0, 0x5e, 0xb8, 0xe0, 0xdc, 0xb8,
+ 0xbc, 0xec, 0xf0, 0xda, 0x46, 0x0, 0x0, 0x0,
+ 0x4, 0x54, 0xbe, 0xf0, 0xfa, 0xfc, 0xf6, 0xd6,
+ 0x80, 0x1a, 0x0, 0x0, 0x0, 0x64, 0xbc, 0xd,
+ 0xff, 0xff, 0xff, 0x83, 0xf4, 0xb2, 0x4e, 0x0,
+ 0x0, 0x6a, 0xbe, 0xe2, 0xde, 0xb6, 0x56, 0x0,
+ 0x10, 0x98, 0xd2, 0xea, 0xd6, 0xa0, 0x24, 0x0,
+ 0x0, 0x5e, 0xba, 0xe0, 0xe6, 0xc4, 0x78, 0x0,
+ 0x0, 0x4e, 0xb2, 0xdc, 0xe8, 0xca, 0x86, 0x0,
+ 0x4c, 0xb2, 0xdc, 0xde, 0xb4, 0x52, 0x0, 0x38,
+ 0xa8, 0xd8, 0xd0, 0x92, 0x6, 0x2, 0x8e, 0xce,
+ 0xea, 0xd4, 0x9a, 0x18, 0x0, 0x3a, 0xaa, 0xda,
+ 0xea, 0xd8, 0xa4, 0x2e, 0xa, 0x92, 0xd0, 0xea,
+ 0xe2, 0xbe, 0x68, 0x0, 0x72, 0xc2, 0xe4, 0xe4,
+ 0xc2, 0x74, 0x0, 0x0, 0x4e, 0xb2, 0xde, 0xea,
+ 0xd0, 0x94, 0xa, 0x0, 0x86, 0xca, 0xe8, 0xea,
+ 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xd4, 0x9c,
+ 0x1c, 0x0, 0x0, 0x0, 0x88, 0x73, 0xff, 0xff,
+ 0xcb, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0xfa, 0xdb, 0xff, 0xff, 0x5d, 0x68, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xb8, 0x95, 0xc3, 0xc3, 0x75, 0x49, 0xc3,
+ 0xeb, 0xe3, 0xb3, 0x39, 0xe2, 0x20, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x7a, 0xfe, 0x7d, 0xd1, 0xeb,
+ 0xdf, 0x93, 0xb, 0xad, 0xc3, 0xc3, 0x37, 0x32,
+ 0x0, 0x0, 0x0, 0xb8, 0x95, 0xc3, 0xc3, 0x79,
+ 0x43, 0xc9, 0xeb, 0x7b, 0x74, 0x0, 0x0, 0xc,
+ 0xb2, 0x11, 0x89, 0xcf, 0xe9, 0xed, 0xd9, 0xa9,
+ 0x3f, 0xf2, 0x40, 0x0, 0x0, 0xc4, 0x9d, 0xc5,
+ 0xff, 0xff, 0xff, 0xdb, 0xc3, 0x83, 0xa0, 0x0,
+ 0x0, 0xca, 0xa1, 0xc3, 0xc3, 0x8d, 0xae, 0x0,
+ 0x28, 0x2f, 0xc3, 0xc3, 0xc3, 0x4f, 0x52, 0x0,
+ 0x0, 0x90, 0x97, 0xc3, 0xc3, 0xaf, 0xfc, 0xe,
+ 0x0, 0xc6, 0x83, 0xc3, 0xc3, 0xbb, 0xb, 0x0,
+ 0x7c, 0x83, 0xc3, 0xc3, 0x89, 0xc6, 0x0, 0xa6,
+ 0x6b, 0xc3, 0xc3, 0x17, 0x3c, 0x22, 0xb, 0xc1,
+ 0xc3, 0xc3, 0x3f, 0x1c, 0x0, 0x40, 0x73, 0xc3,
+ 0xc3, 0xc3, 0x5b, 0xb0, 0x64, 0x21, 0xc1, 0xc3,
+ 0xc3, 0xa1, 0x84, 0x0, 0xaa, 0xa9, 0xc3, 0xc3,
+ 0xab, 0xfc, 0x10, 0x0, 0xd0, 0x85, 0xc3, 0xc3,
+ 0xc3, 0x27, 0xa, 0x0, 0xee, 0xbb, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x41,
+ 0x40, 0x0, 0x0, 0x0, 0x98, 0x7b, 0xff, 0xff,
+ 0xc7, 0xee, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0xf6, 0xd7, 0xff, 0xff, 0x67, 0x7c, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xe0, 0xc3, 0xff, 0xff, 0xcf, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xf3, 0x49, 0xba, 0x0, 0x0,
+ 0x0, 0x0, 0x38, 0xfe, 0xb1, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xbb, 0xfb, 0xff, 0xff, 0x49, 0x4c,
+ 0x0, 0x0, 0x0, 0xe0, 0xc3, 0xff, 0xff, 0xb9,
+ 0xeb, 0xff, 0xff, 0x75, 0x8c, 0x0, 0x0, 0x7c,
+ 0x23, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfb, 0x73, 0xec, 0x10, 0x0, 0xdc, 0xcd, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xbe, 0x0,
+ 0x0, 0xec, 0xd3, 0xff, 0xff, 0xbb, 0xd8, 0x0,
+ 0x3e, 0x3f, 0xff, 0xff, 0xff, 0x67, 0x76, 0x0,
+ 0x0, 0x98, 0x87, 0xff, 0xff, 0xff, 0x2b, 0x4e,
+ 0xc, 0xfc, 0xd9, 0xff, 0xff, 0xc1, 0xd6, 0x0,
+ 0x86, 0x7b, 0xff, 0xff, 0xd7, 0xfa, 0x6, 0xf4,
+ 0xc1, 0xff, 0xff, 0x69, 0x96, 0x64, 0x4b, 0xff,
+ 0xff, 0xf7, 0xf, 0x1c, 0x0, 0x40, 0x1d, 0xeb,
+ 0xff, 0xff, 0xcf, 0xfe, 0xe8, 0x97, 0xff, 0xff,
+ 0xff, 0x5d, 0x84, 0x0, 0xb4, 0x9f, 0xff, 0xff,
+ 0xff, 0x2d, 0x54, 0x1a, 0xfe, 0xe3, 0xff, 0xff,
+ 0xd7, 0xe6, 0xa, 0x0, 0xfc, 0xf5, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x57,
+ 0x4c, 0x0, 0x0, 0x0, 0xa2, 0x7b, 0xff, 0xff,
+ 0xc7, 0xea, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0xf4, 0xd7, 0xff, 0xff, 0x67, 0x86, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xff, 0xfd, 0xd9,
+ 0xe5, 0xff, 0xff, 0xff, 0xdb, 0x5, 0x2e, 0x0,
+ 0x0, 0x0, 0xb0, 0x6b, 0xff, 0xff, 0xff, 0xf7,
+ 0xcf, 0xe9, 0xff, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x57, 0x64, 0x0, 0x0, 0xe2,
+ 0xad, 0xff, 0xff, 0xf7, 0xa7, 0x9b, 0xe5, 0xff,
+ 0xff, 0xf7, 0x2f, 0x54, 0x0, 0xc6, 0xa1, 0xcb,
+ 0xff, 0xff, 0xff, 0xdf, 0xcb, 0x89, 0xa2, 0x0,
+ 0x0, 0xf4, 0xd3, 0xff, 0xff, 0xbb, 0xe2, 0x0,
+ 0x46, 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0,
+ 0x0, 0x5a, 0x2d, 0xfd, 0xff, 0xff, 0x79, 0xa6,
+ 0x48, 0x25, 0xff, 0xff, 0xff, 0x75, 0xaa, 0x0,
+ 0x52, 0x31, 0xff, 0xff, 0xf9, 0xf, 0x50, 0xd,
+ 0xf5, 0xff, 0xff, 0xad, 0xe6, 0xaa, 0x81, 0xff,
+ 0xff, 0xcb, 0xf8, 0x4, 0x0, 0x8, 0xd8, 0x73,
+ 0xff, 0xff, 0xff, 0x55, 0x19, 0xf1, 0xff, 0xff,
+ 0xb9, 0xfe, 0x2e, 0x0, 0x78, 0x45, 0xff, 0xff,
+ 0xff, 0x7d, 0xb0, 0x68, 0x3f, 0xff, 0xff, 0xff,
+ 0x8b, 0xca, 0x0, 0x0, 0xf8, 0xef, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xf3, 0x2d,
+ 0x38, 0x0, 0x0, 0x0, 0xc2, 0x87, 0xff, 0xff,
+ 0xbd, 0xe0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0xec, 0xcd, 0xff, 0xff, 0x73, 0xaa, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x48, 0xa6, 0xd0, 0xd0,
+ 0x9e, 0x42, 0x2, 0x0, 0x2, 0x18, 0x1a, 0x18,
+ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xf3, 0x49, 0xfe,
+ 0xfe, 0xa5, 0xff, 0xff, 0xff, 0x5d, 0x82, 0x0,
+ 0x0, 0x4, 0xf8, 0xcb, 0xff, 0xff, 0xef, 0x33,
+ 0xfc, 0x9, 0xaf, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xff,
+ 0xbb, 0x77, 0x75, 0x1d, 0x30, 0x0, 0x0, 0xf8,
+ 0xe9, 0xff, 0xff, 0x8f, 0xfc, 0xea, 0x39, 0xff,
+ 0xff, 0xff, 0x7d, 0x5c, 0x0, 0x6a, 0xc4, 0xd,
+ 0xff, 0xff, 0xff, 0x83, 0xf4, 0xb8, 0x52, 0x0,
+ 0x0, 0xf4, 0xd3, 0xff, 0xff, 0xbb, 0xe2, 0x0,
+ 0x46, 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0,
+ 0x0, 0x10, 0xfc, 0xcd, 0xff, 0xff, 0xb5, 0xea,
+ 0x9c, 0x73, 0xff, 0xff, 0xf7, 0x19, 0x44, 0x0,
+ 0x12, 0xfe, 0xe3, 0xff, 0xff, 0x4d, 0xb8, 0x5f,
+ 0xff, 0xff, 0xff, 0xe7, 0xfe, 0xe2, 0xad, 0xff,
+ 0xff, 0x93, 0xc8, 0x0, 0x0, 0x0, 0x3e, 0xfe,
+ 0xc9, 0xff, 0xff, 0xc3, 0x8d, 0xff, 0xff, 0xf3,
+ 0x2d, 0x8a, 0x0, 0x0, 0x1e, 0xfe, 0xdb, 0xff,
+ 0xff, 0xbf, 0xf4, 0xc6, 0x8f, 0xff, 0xff, 0xfd,
+ 0x2d, 0x60, 0x0, 0x0, 0xd2, 0xfc, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0x97, 0xff, 0xff, 0xff, 0x6d, 0xe4,
+ 0x10, 0x0, 0x26, 0x98, 0xfe, 0xbb, 0xff, 0xff,
+ 0x95, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0xce, 0xa7, 0xff, 0xff, 0xa9, 0xfc, 0x8c, 0x1e,
+ 0x0, 0x0, 0x0, 0x86, 0x9, 0x7d, 0xb7, 0xb1,
+ 0x77, 0xb, 0xbc, 0x1c, 0x26, 0xd, 0x39, 0xb,
+ 0xf2, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xfa, 0x20,
+ 0x74, 0x23, 0xfb, 0xff, 0xff, 0x9f, 0xc6, 0x0,
+ 0x0, 0x1e, 0xf, 0xfb, 0xff, 0xff, 0xa5, 0xec,
+ 0x14, 0xa6, 0x5f, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xd9,
+ 0xfe, 0xba, 0x74, 0x46, 0x8, 0x0, 0x0, 0xf8,
+ 0xeb, 0xff, 0xff, 0xb3, 0x5, 0xea, 0xfe, 0x41,
+ 0x43, 0x43, 0x25, 0x50, 0x0, 0x0, 0x6, 0xd,
+ 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0, 0x0,
+ 0x0, 0xf4, 0xd3, 0xff, 0xff, 0xbb, 0xe2, 0x0,
+ 0x46, 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0,
+ 0x0, 0x0, 0xbc, 0x83, 0xff, 0xff, 0xe9, 0xfe,
+ 0xe6, 0xaf, 0xff, 0xff, 0xbd, 0xf6, 0x6, 0x0,
+ 0x0, 0xe6, 0xb1, 0xff, 0xff, 0x83, 0xf4, 0xa5,
+ 0xff, 0xff, 0xff, 0xff, 0x41, 0xfc, 0xd7, 0xff,
+ 0xff, 0x55, 0x76, 0x0, 0x0, 0x0, 0x0, 0xa2,
+ 0x3d, 0xf9, 0xff, 0xff, 0xf1, 0xff, 0xff, 0x89,
+ 0xe8, 0x10, 0x0, 0x0, 0x0, 0xd0, 0x91, 0xff,
+ 0xff, 0xf5, 0xd, 0xfc, 0xcf, 0xff, 0xff, 0xcb,
+ 0xfc, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10,
+ 0xda, 0x5d, 0xfd, 0xff, 0xff, 0xa7, 0xfe, 0x3a,
+ 0x0, 0x0, 0x70, 0x51, 0xbb, 0xff, 0xff, 0xef,
+ 0x2d, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0x7e, 0x3d, 0xf5, 0xff, 0xff, 0xb3, 0x45, 0x5a,
+ 0x0, 0x0, 0x30, 0xfe, 0xbb, 0xff, 0xff, 0xff,
+ 0xff, 0xdd, 0x43, 0xee, 0xca, 0x4f, 0xff, 0xfb,
+ 0x5b, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee, 0x0,
+ 0xc, 0xfc, 0xd7, 0xff, 0xff, 0xbf, 0xe4, 0x0,
+ 0x0, 0x40, 0x41, 0xff, 0xff, 0xff, 0x6f, 0xa2,
+ 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb,
+ 0xf2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe2,
+ 0xad, 0xff, 0xff, 0xff, 0xe7, 0xb1, 0x7b, 0x2d,
+ 0xfc, 0xac, 0x38, 0xc, 0x0, 0x0, 0x6, 0xd,
+ 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0, 0x0,
+ 0x0, 0xf4, 0xd3, 0xff, 0xff, 0xbb, 0xe2, 0x0,
+ 0x46, 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0,
+ 0x0, 0x0, 0x56, 0x27, 0xfb, 0xff, 0xff, 0x41,
+ 0xfe, 0xe3, 0xff, 0xff, 0x6f, 0xa4, 0x0, 0x0,
+ 0x0, 0xa2, 0x77, 0xff, 0xff, 0xb1, 0xfe, 0xdf,
+ 0xff, 0xd1, 0xff, 0xff, 0x8d, 0xf, 0xf9, 0xff,
+ 0xf5, 0xb, 0x28, 0x0, 0x0, 0x0, 0x0, 0x1a,
+ 0xf4, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xd9, 0x9,
+ 0x52, 0x0, 0x0, 0x0, 0x0, 0x64, 0x31, 0xff,
+ 0xff, 0xff, 0x5f, 0x21, 0xfb, 0xff, 0xff, 0x7f,
+ 0xba, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa2,
+ 0x2d, 0xed, 0xff, 0xff, 0xd5, 0xf, 0x72, 0x0,
+ 0x0, 0x0, 0x9e, 0x8d, 0xff, 0xff, 0xeb, 0x43,
+ 0xea, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0x1a, 0xf2, 0x4d, 0xed, 0xff, 0xff, 0x7b, 0x84,
+ 0x0, 0x0, 0x92, 0x5f, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf5, 0x85, 0x4d, 0xcb, 0xff, 0xff,
+ 0x39, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee, 0x0,
+ 0x0, 0xf0, 0xc5, 0xff, 0xff, 0xcd, 0xec, 0x0,
+ 0x0, 0x58, 0x51, 0xff, 0xff, 0xff, 0x59, 0x7a,
+ 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb,
+ 0xee, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78,
+ 0x19, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd,
+ 0xbf, 0x33, 0xcc, 0x8, 0x0, 0x0, 0x6, 0xd,
+ 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0, 0x0,
+ 0x0, 0xf4, 0xd3, 0xff, 0xff, 0xbb, 0xe2, 0x0,
+ 0x46, 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0,
+ 0x0, 0x0, 0xc, 0xfa, 0xc9, 0xff, 0xff, 0x89,
+ 0x35, 0xff, 0xff, 0xf5, 0x13, 0x3e, 0x0, 0x0,
+ 0x0, 0x4e, 0x2d, 0xff, 0xff, 0xd9, 0x35, 0xff,
+ 0xff, 0x7d, 0xd5, 0xff, 0xcb, 0x4f, 0xff, 0xff,
+ 0xc7, 0xf6, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x94, 0x17, 0xf7, 0xff, 0xff, 0xff, 0x6b, 0xe4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0xfc, 0xcf,
+ 0xff, 0xff, 0xa5, 0x75, 0xff, 0xff, 0xf9, 0x21,
+ 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0x9,
+ 0xcd, 0xff, 0xff, 0xf3, 0x37, 0xb0, 0x4, 0x0,
+ 0x0, 0x0, 0x8c, 0x89, 0xff, 0xff, 0xff, 0xa7,
+ 0xfe, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0x46, 0x5, 0xb5, 0xff, 0xff, 0xff, 0x77, 0x74,
+ 0x0, 0x0, 0xc4, 0xaf, 0xff, 0xf9, 0x5d, 0x41,
+ 0xb3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3,
+ 0xfe, 0x16, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee, 0x0,
+ 0x0, 0xf4, 0xcb, 0xff, 0xff, 0xcb, 0xec, 0x0,
+ 0x0, 0x52, 0x4f, 0xff, 0xff, 0xff, 0x5d, 0x82,
+ 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb,
+ 0xee, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc,
+ 0x92, 0xfe, 0x4d, 0xa3, 0xd9, 0xff, 0xff, 0xff,
+ 0xff, 0xe9, 0x1f, 0x4c, 0x0, 0x0, 0x6, 0xd,
+ 0xff, 0xff, 0xff, 0x83, 0xa6, 0x0, 0x0, 0x0,
+ 0x0, 0xf2, 0xd3, 0xff, 0xff, 0xbb, 0xe6, 0x0,
+ 0x46, 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0,
+ 0x0, 0x0, 0x0, 0xb6, 0x7d, 0xff, 0xff, 0xc3,
+ 0x7f, 0xff, 0xff, 0xb9, 0xf2, 0x4, 0x0, 0x0,
+ 0x0, 0x10, 0xfe, 0xe1, 0xff, 0xfb, 0x8b, 0xff,
+ 0xff, 0x29, 0x95, 0xff, 0xfb, 0x8d, 0xff, 0xff,
+ 0x8f, 0xc2, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe,
+ 0xe6, 0x83, 0xff, 0xff, 0xff, 0xff, 0xc5, 0xfe,
+ 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbe, 0x81,
+ 0xff, 0xff, 0xe1, 0xb9, 0xff, 0xff, 0xbf, 0xf8,
+ 0xa, 0x0, 0x0, 0x0, 0x0, 0x30, 0xfa, 0x9b,
+ 0xff, 0xff, 0xff, 0x6d, 0xe4, 0x16, 0x0, 0x0,
+ 0x0, 0x0, 0x52, 0xfe, 0x43, 0xeb, 0xff, 0xff,
+ 0x67, 0x94, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0xac, 0x7b, 0xff, 0xff, 0xdf, 0x37, 0xfe, 0x42,
+ 0x0, 0x0, 0xb6, 0xab, 0xef, 0xc1, 0xf8, 0x9e,
+ 0xfe, 0x7d, 0xf7, 0xff, 0xff, 0xff, 0xef, 0x43,
+ 0xae, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xf2, 0x0,
+ 0x2a, 0xfe, 0xe3, 0xff, 0xff, 0xb7, 0xde, 0x0,
+ 0x0, 0x34, 0x33, 0xff, 0xff, 0xff, 0x7d, 0xc4,
+ 0x0, 0x82, 0x5f, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb,
+ 0xee, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x9,
+ 0x15, 0x15, 0x13, 0xfc, 0xfe, 0x27, 0x9f, 0xff,
+ 0xff, 0xff, 0x7b, 0x8c, 0x0, 0x0, 0x4, 0xd,
+ 0xff, 0xff, 0xff, 0x83, 0xac, 0x0, 0x0, 0x0,
+ 0x0, 0xee, 0xcf, 0xff, 0xff, 0xc3, 0xf2, 0x0,
+ 0x56, 0x3f, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0,
+ 0x0, 0x0, 0x0, 0x50, 0x21, 0xfb, 0xff, 0xf3,
+ 0xb7, 0xff, 0xff, 0x6b, 0xa0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xe2, 0xad, 0xff, 0xff, 0xe9, 0xff,
+ 0xd3, 0xfe, 0x47, 0xff, 0xff, 0xe9, 0xff, 0xff,
+ 0x4f, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88,
+ 0x29, 0xf3, 0xff, 0xff, 0xfb, 0xff, 0xff, 0x71,
+ 0xd8, 0x8, 0x0, 0x0, 0x0, 0x0, 0x54, 0x23,
+ 0xfb, 0xff, 0xff, 0xf3, 0xff, 0xff, 0x71, 0xa8,
+ 0x0, 0x0, 0x0, 0x0, 0x10, 0xdc, 0x61, 0xff,
+ 0xff, 0xff, 0xa7, 0xfc, 0x3a, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1c, 0xea, 0x99, 0xff, 0xff,
+ 0xb1, 0xd2, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0xe2, 0xc1, 0xff, 0xff, 0x85, 0xdc, 0x16, 0x0,
+ 0x0, 0x0, 0x74, 0xe6, 0xf4, 0x9, 0x8c, 0x0,
+ 0x48, 0xea, 0x2b, 0x87, 0xa3, 0x8d, 0x29, 0xda,
+ 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xdb, 0x5, 0xd8,
+ 0xf2, 0x59, 0xff, 0xff, 0xff, 0x8b, 0xb0, 0x0,
+ 0x0, 0x14, 0xfe, 0xef, 0xff, 0xff, 0xcb, 0x5,
+ 0xd6, 0xf8, 0x77, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb,
+ 0xee, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x63,
+ 0xff, 0xff, 0xfb, 0x17, 0xae, 0xb4, 0xfe, 0xeb,
+ 0xff, 0xff, 0x91, 0xa0, 0x0, 0x0, 0x2, 0x5,
+ 0xff, 0xff, 0xff, 0x8d, 0xf2, 0x80, 0x30, 0x0,
+ 0x0, 0xe0, 0xbb, 0xff, 0xff, 0xe9, 0x9, 0xda,
+ 0xf6, 0x5b, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0,
+ 0x0, 0x0, 0x0, 0xa, 0xfa, 0xc5, 0xff, 0xff,
+ 0xf3, 0xff, 0xf3, 0xf, 0x3a, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x9c, 0x73, 0xff, 0xff, 0xff, 0xff,
+ 0x93, 0xd2, 0xfe, 0xe7, 0xff, 0xff, 0xff, 0xf3,
+ 0x9, 0x24, 0x0, 0x0, 0x0, 0x0, 0x2e, 0xfe,
+ 0xb7, 0xff, 0xff, 0xdd, 0x99, 0xff, 0xff, 0xeb,
+ 0x1d, 0x74, 0x0, 0x0, 0x0, 0x0, 0xc, 0xf8,
+ 0xc1, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x13, 0x40,
+ 0x0, 0x0, 0x0, 0x2, 0xa6, 0x2f, 0xef, 0xff,
+ 0xff, 0xd5, 0xf, 0xee, 0xe0, 0xe0, 0xcc, 0x9a,
+ 0x32, 0x0, 0x0, 0x0, 0xac, 0x7d, 0xff, 0xff,
+ 0xc5, 0xe6, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0xf2, 0xd5, 0xff, 0xff, 0x69, 0x92, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xe, 0x5a, 0x9e, 0xba, 0xa2, 0x5e, 0xe,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xff, 0xcf, 0x8b,
+ 0x9f, 0xf1, 0xff, 0xff, 0xfb, 0x2f, 0x5e, 0x0,
+ 0x0, 0x0, 0xe6, 0xa7, 0xff, 0xff, 0xff, 0xc7,
+ 0x8d, 0xa5, 0xf7, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb,
+ 0xee, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x19,
+ 0xf3, 0xff, 0xff, 0xc1, 0x51, 0x3f, 0x8b, 0xff,
+ 0xff, 0xff, 0x67, 0x7c, 0x0, 0x0, 0x0, 0xfe,
+ 0xeb, 0xff, 0xff, 0xe9, 0x99, 0x5f, 0x8c, 0x0,
+ 0x0, 0xb2, 0x89, 0xff, 0xff, 0xff, 0xcb, 0x91,
+ 0xa9, 0xf5, 0xff, 0xff, 0xff, 0x67, 0x80, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xb2, 0x79, 0xff, 0xff,
+ 0xff, 0xff, 0xb5, 0xf0, 0x4, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4a, 0x29, 0xff, 0xff, 0xff, 0xff,
+ 0x45, 0x70, 0xe4, 0xab, 0xff, 0xff, 0xff, 0xc3,
+ 0xf4, 0x0, 0x0, 0x0, 0x0, 0x4, 0xc6, 0x5d,
+ 0xff, 0xff, 0xff, 0x73, 0x21, 0xf3, 0xff, 0xff,
+ 0xa7, 0xf8, 0x22, 0x0, 0x0, 0x0, 0x0, 0xaa,
+ 0x71, 0xff, 0xff, 0xff, 0xff, 0xb5, 0xf0, 0x4,
+ 0x0, 0x0, 0x0, 0x18, 0xb, 0xcf, 0xff, 0xff,
+ 0xff, 0xcd, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0x63,
+ 0x74, 0x0, 0x0, 0x0, 0x9e, 0x7b, 0xff, 0xff,
+ 0xc7, 0xec, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0xf6, 0xd7, 0xff, 0xff, 0x67, 0x82, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xfb, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x95, 0xf4, 0x10, 0x0,
+ 0x0, 0x0, 0x76, 0x27, 0xeb, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb,
+ 0xee, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xe4,
+ 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xc7, 0x5, 0x34, 0x0, 0x0, 0x0, 0xe4,
+ 0xad, 0xff, 0xff, 0xff, 0xff, 0xa9, 0xc8, 0x0,
+ 0x0, 0x5a, 0x25, 0xef, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xe7, 0xf7, 0xff, 0xff, 0x67, 0x80, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x4a, 0x1d, 0xf9, 0xff,
+ 0xff, 0xff, 0x65, 0x9a, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xe, 0xfe, 0xdd, 0xff, 0xff, 0xe5,
+ 0xfe, 0x1e, 0x90, 0x63, 0xff, 0xff, 0xff, 0x8b,
+ 0xbc, 0x0, 0x0, 0x0, 0x0, 0x60, 0xf, 0xe1,
+ 0xff, 0xff, 0xdf, 0x5, 0xf0, 0x9d, 0xff, 0xff,
+ 0xfd, 0x4b, 0xb2, 0x0, 0x0, 0x0, 0x0, 0x40,
+ 0x13, 0xf5, 0xff, 0xff, 0xff, 0x63, 0x98, 0x0,
+ 0x0, 0x0, 0x0, 0x2c, 0x39, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x89,
+ 0xa0, 0x0, 0x0, 0x0, 0x92, 0x7b, 0xff, 0xff,
+ 0xc7, 0xee, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x0,
+ 0xf8, 0xd7, 0xff, 0xff, 0x65, 0x74, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0x9f, 0xfb,
+ 0xff, 0xff, 0xf3, 0x8f, 0xfe, 0x5e, 0x0, 0x0,
+ 0x0, 0x0, 0xc, 0xd0, 0x3d, 0xcd, 0xff, 0xff,
+ 0xff, 0xdd, 0x87, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0xce, 0xc3, 0xff, 0xff, 0xcb,
+ 0xd6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40,
+ 0xfa, 0x5d, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xed,
+ 0x99, 0x11, 0x96, 0x0, 0x0, 0x0, 0x0, 0x7a,
+ 0x21, 0xbd, 0xff, 0xff, 0xff, 0xb9, 0xba, 0x0,
+ 0x0, 0xc, 0xd8, 0x4b, 0xdb, 0xff, 0xff, 0xff,
+ 0xcf, 0x39, 0xdb, 0xff, 0xff, 0x67, 0x60, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x8, 0xf4, 0xc1, 0xff,
+ 0xff, 0xf1, 0xd, 0x36, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xd6, 0xa9, 0xff, 0xff, 0xa7,
+ 0xdc, 0x0, 0x36, 0xf, 0xf5, 0xff, 0xff, 0x49,
+ 0x68, 0x0, 0x0, 0x0, 0x0, 0x60, 0x95, 0xff,
+ 0xff, 0xff, 0x77, 0xc8, 0x6c, 0x25, 0xf5, 0xff,
+ 0xff, 0xd5, 0x7, 0x0, 0x0, 0x0, 0x0, 0x4,
+ 0xf6, 0xb3, 0xff, 0xff, 0xed, 0x9, 0x32, 0x0,
+ 0x0, 0x0, 0x0, 0x2a, 0x39, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x89,
+ 0x8a, 0x0, 0x0, 0x0, 0x78, 0x65, 0xff, 0xff,
+ 0xd1, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x8,
+ 0xfc, 0xe3, 0xff, 0xff, 0x4d, 0x56, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xfe, 0x21,
+ 0x51, 0x49, 0xf, 0xe8, 0x58, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x18, 0xa4, 0xfc, 0x2d, 0x53,
+ 0x3f, 0xfe, 0x5f, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0x90, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x2e, 0xb6, 0xfe, 0x25, 0x4f, 0x55, 0x39, 0xfe,
+ 0xe2, 0x64, 0x4, 0x0, 0x0, 0x0, 0x0, 0xa,
+ 0x8e, 0xfe, 0x25, 0x51, 0x43, 0x5, 0x84, 0x0,
+ 0x0, 0x0, 0x22, 0xbc, 0xfe, 0x39, 0x55, 0x33,
+ 0xfe, 0xe4, 0xfe, 0xfe, 0xfe, 0xfe, 0x36, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xd6, 0x2, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x72, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x70, 0x0, 0x4, 0xde, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x20, 0x0, 0x0, 0x0, 0x0, 0x5e, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0x42, 0xc, 0xe0, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xa8, 0x0, 0x0, 0x0, 0x0, 0x1a,
+ 0xfc, 0xbb, 0xff, 0xff, 0xa7, 0xe8, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x16, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0x52, 0x0, 0x0, 0x0, 0x46, 0x39, 0xff, 0xff,
+ 0xf1, 0x9, 0x4e, 0x0, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x0, 0x66,
+ 0x1b, 0xfb, 0xff, 0xfd, 0x1f, 0x2a, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee, 0x2e,
+ 0x48, 0x44, 0x22, 0x4, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x34, 0x48,
+ 0x3c, 0x84, 0x5f, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xc, 0x2e, 0x4e, 0x56, 0x3a, 0x16,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xc, 0x30, 0x46, 0x3e, 0x1c, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x16, 0x3a, 0x48, 0x38,
+ 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0xa0, 0xee,
+ 0x3d, 0xfb, 0xff, 0xff, 0x51, 0x86, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x16, 0xfe, 0xcd, 0xff,
+ 0xff, 0x83, 0xfc, 0x40, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x2e, 0x0, 0x0, 0x50, 0xfe,
+ 0x99, 0xff, 0xff, 0xbb, 0xfa, 0x8, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xea, 0xc3, 0xff, 0xff, 0xcb, 0xee, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x76, 0x5f, 0xff, 0xff, 0xff, 0x49, 0x54,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xa8, 0x73, 0xb5,
+ 0xef, 0xff, 0xff, 0xd5, 0xfe, 0x26, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x4d, 0xf7,
+ 0xff, 0xff, 0x73, 0x50, 0x0, 0x0, 0x0, 0xfe,
+ 0xfb, 0xff, 0x2f, 0x22, 0x0, 0x0, 0x68, 0x87,
+ 0xff, 0xff, 0xef, 0x3b, 0x90, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xe6, 0xc3, 0xff, 0xff, 0xcb, 0xea, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x72, 0x5f, 0xff, 0xff, 0xff, 0x49, 0x50,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xe2, 0xc1, 0xff,
+ 0xff, 0xff, 0xfb, 0x55, 0xb6, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0xe8, 0x41,
+ 0xc7, 0xfd, 0x37, 0x50, 0x0, 0x0, 0x0, 0xee,
+ 0x47, 0x49, 0xd, 0x12, 0x0, 0x0, 0x68, 0x4f,
+ 0xfd, 0xbd, 0x35, 0xda, 0x18, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xc6, 0xb1, 0xe9, 0xe9, 0xb7, 0xcc, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x52, 0x57, 0xe9, 0xe9, 0xe9, 0x41, 0x38,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xd8, 0xd7, 0xff,
+ 0xff, 0xdf, 0x63, 0xf2, 0x28, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x9e,
+ 0xfa, 0x29, 0xf6, 0x16, 0x0, 0x0, 0x0, 0x1e,
+ 0x3a, 0x3c, 0x22, 0x2, 0x0, 0x0, 0x24, 0xf4,
+ 0x29, 0xfa, 0x92, 0x14, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x7c, 0xe2, 0xf8, 0xf8, 0xe4, 0x82, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x2a, 0xd0, 0xf4, 0xfc, 0xf4, 0xcc, 0x1c,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x9, 0x2f,
+ 0x17, 0xfe, 0xc6, 0x32, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xe, 0xe, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe,
+ 0xe, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x16,
+ 0x16, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+};
+const uint16_t FontBitmap::glyphWidth[] = {
+ 8, 9, 10, 16, 16, 20, 18, 6,
+ 10, 10, 13, 15, 8, 11, 9, 11,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 9, 9, 14, 16, 14, 14,
+ 23, 17, 17, 17, 18, 16, 16, 18,
+ 19, 9, 16, 18, 15, 23, 19, 19,
+ 18, 19, 18, 17, 17, 18, 17, 23,
+ 17, 17, 16, 9, 12, 9, 13, 13,
+ 10, 15, 16, 14, 16, 15, 11, 16,
+ 16, 8, 8, 15, 8, 23, 16, 16,
+ 16, 16, 11, 14, 10, 16, 14, 20,
+ 14, 14, 14, 10, 8, 10, 18,
+};
+const uint16_t FontBitmap::yoffset[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 136,
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170,
+};
diff --git a/cmds/screenrecord/FrameOutput.cpp b/cmds/screenrecord/FrameOutput.cpp
new file mode 100644
index 0000000..b5cf2f9
--- /dev/null
+++ b/cmds/screenrecord/FrameOutput.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ScreenRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include "FrameOutput.h"
+
+using namespace android;
+
+static const bool kShowTiming = false; // set to "true" for debugging
+static const int kGlBytesPerPixel = 4; // GL_RGBA
+static const int kOutBytesPerPixel = 3; // RGB only
+
+inline void FrameOutput::setValueLE(uint8_t* buf, uint32_t value) {
+ // Since we're running on an Android device, we're (almost) guaranteed
+ // to be little-endian, and (almost) guaranteed that unaligned 32-bit
+ // writes will work without any performance penalty... but do it
+ // byte-by-byte anyway.
+ buf[0] = (uint8_t) value;
+ buf[1] = (uint8_t) (value >> 8);
+ buf[2] = (uint8_t) (value >> 16);
+ buf[3] = (uint8_t) (value >> 24);
+}
+
+status_t FrameOutput::createInputSurface(int width, int height,
+ sp<IGraphicBufferProducer>* pBufferProducer) {
+ status_t err;
+
+ err = mEglWindow.createPbuffer(width, height);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ mEglWindow.makeCurrent();
+
+ glViewport(0, 0, width, height);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ // Shader for rendering the external texture.
+ err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ // Input side (buffers from virtual display).
+ glGenTextures(1, &mExtTextureName);
+ if (mExtTextureName == 0) {
+ ALOGE("glGenTextures failed: %#x", glGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ mBufferQueue = new BufferQueue(/*new GraphicBufferAlloc()*/);
+ mGlConsumer = new GLConsumer(mBufferQueue, mExtTextureName,
+ GL_TEXTURE_EXTERNAL_OES);
+ mGlConsumer->setName(String8("virtual display"));
+ mGlConsumer->setDefaultBufferSize(width, height);
+ mGlConsumer->setDefaultMaxBufferCount(5);
+ mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
+
+ mGlConsumer->setFrameAvailableListener(this);
+
+ mPixelBuf = new uint8_t[width * height * kGlBytesPerPixel];
+
+ *pBufferProducer = mBufferQueue;
+
+ ALOGD("FrameOutput::createInputSurface OK");
+ return NO_ERROR;
+}
+
+status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec) {
+ Mutex::Autolock _l(mMutex);
+ ALOGV("copyFrame %ld\n", timeoutUsec);
+
+ if (!mFrameAvailable) {
+ nsecs_t timeoutNsec = (nsecs_t)timeoutUsec * 1000;
+ int cc = mEventCond.waitRelative(mMutex, timeoutNsec);
+ if (cc == -ETIMEDOUT) {
+ ALOGV("cond wait timed out");
+ return ETIMEDOUT;
+ } else if (cc != 0) {
+ ALOGW("cond wait returned error %d", cc);
+ return cc;
+ }
+ }
+ if (!mFrameAvailable) {
+ // This happens when Ctrl-C is hit. Apparently POSIX says that the
+ // pthread wait call doesn't return EINTR, treating this instead as
+ // an instance of a "spurious wakeup". We didn't get a frame, so
+ // we just treat it as a timeout.
+ return ETIMEDOUT;
+ }
+
+ // A frame is available. Clear the flag for the next round.
+ mFrameAvailable = false;
+
+ float texMatrix[16];
+ mGlConsumer->updateTexImage();
+ mGlConsumer->getTransformMatrix(texMatrix);
+
+ // The data is in an external texture, so we need to render it to the
+ // pbuffer to get access to RGB pixel data. We also want to flip it
+ // upside-down for easy conversion to a bitmap.
+ int width = mEglWindow.getWidth();
+ int height = mEglWindow.getHeight();
+ status_t err = mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0,
+ width, height, true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ // GLES only guarantees that glReadPixels() will work with GL_RGBA, so we
+ // need to get 4 bytes/pixel and reduce it. Depending on the size of the
+ // screen and the device capabilities, this can take a while.
+ int64_t startWhenNsec, pixWhenNsec, endWhenNsec;
+ if (kShowTiming) {
+ startWhenNsec = systemTime(CLOCK_MONOTONIC);
+ }
+ GLenum glErr;
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, mPixelBuf);
+ if ((glErr = glGetError()) != GL_NO_ERROR) {
+ ALOGE("glReadPixels failed: %#x", glErr);
+ return UNKNOWN_ERROR;
+ }
+ if (kShowTiming) {
+ pixWhenNsec = systemTime(CLOCK_MONOTONIC);
+ }
+ reduceRgbaToRgb(mPixelBuf, width * height);
+ if (kShowTiming) {
+ endWhenNsec = systemTime(CLOCK_MONOTONIC);
+ ALOGD("got pixels (get=%.3f ms, reduce=%.3fms)",
+ (pixWhenNsec - startWhenNsec) / 1000000.0,
+ (endWhenNsec - pixWhenNsec) / 1000000.0);
+ }
+
+ // Fill out the header.
+ size_t headerLen = sizeof(uint32_t) * 5;
+ size_t rgbDataLen = width * height * kOutBytesPerPixel;
+ size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen;
+ uint8_t header[headerLen];
+ setValueLE(&header[0], packetLen);
+ setValueLE(&header[4], width);
+ setValueLE(&header[8], height);
+ setValueLE(&header[12], width * kOutBytesPerPixel);
+ setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888);
+
+ // Currently using buffered I/O rather than writev(). Not expecting it
+ // to make much of a difference, but it might be worth a test for larger
+ // frame sizes.
+ if (kShowTiming) {
+ startWhenNsec = systemTime(CLOCK_MONOTONIC);
+ }
+ fwrite(header, 1, headerLen, fp);
+ fwrite(mPixelBuf, 1, rgbDataLen, fp);
+ fflush(fp);
+ if (kShowTiming) {
+ endWhenNsec = systemTime(CLOCK_MONOTONIC);
+ ALOGD("wrote pixels (%.3f ms)",
+ (endWhenNsec - startWhenNsec) / 1000000.0);
+ }
+
+ if (ferror(fp)) {
+ // errno may not be useful; log it anyway
+ ALOGE("write failed (errno=%d)", errno);
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+void FrameOutput::reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount) {
+ // Convert RGBA to RGB.
+ //
+ // Unaligned 32-bit accesses are allowed on ARM, so we could do this
+ // with 32-bit copies advancing at different rates (taking care at the
+ // end to not go one byte over).
+ const uint8_t* readPtr = buf;
+ for (unsigned int i = 0; i < pixelCount; i++) {
+ *buf++ = *readPtr++;
+ *buf++ = *readPtr++;
+ *buf++ = *readPtr++;
+ readPtr++;
+ }
+}
+
+// Callback; executes on arbitrary thread.
+void FrameOutput::onFrameAvailable() {
+ Mutex::Autolock _l(mMutex);
+ mFrameAvailable = true;
+ mEventCond.signal();
+}
diff --git a/cmds/screenrecord/FrameOutput.h b/cmds/screenrecord/FrameOutput.h
new file mode 100644
index 0000000..bb66e05
--- /dev/null
+++ b/cmds/screenrecord/FrameOutput.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SCREENRECORD_FRAMEOUTPUT_H
+#define SCREENRECORD_FRAMEOUTPUT_H
+
+#include "Program.h"
+#include "EglWindow.h"
+
+#include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
+
+namespace android {
+
+/*
+ * Support for "frames" output format.
+ */
+class FrameOutput : public GLConsumer::FrameAvailableListener {
+public:
+ FrameOutput() : mFrameAvailable(false),
+ mExtTextureName(0),
+ mPixelBuf(NULL)
+ {}
+
+ // Create an "input surface", similar in purpose to a MediaCodec input
+ // surface, that the virtual display can send buffers to. Also configures
+ // EGL with a pbuffer surface on the current thread.
+ status_t createInputSurface(int width, int height,
+ sp<IGraphicBufferProducer>* pBufferProducer);
+
+ // Copy one from input to output. If no frame is available, this will wait up to the
+ // specified number of microseconds.
+ //
+ // Returns ETIMEDOUT if the timeout expired before we found a frame.
+ status_t copyFrame(FILE* fp, long timeoutUsec);
+
+ // Prepare to copy frames. Makes the EGL context used by this object current.
+ void prepareToCopy() {
+ mEglWindow.makeCurrent();
+ }
+
+private:
+ FrameOutput(const FrameOutput&);
+ FrameOutput& operator=(const FrameOutput&);
+
+ // Destruction via RefBase.
+ virtual ~FrameOutput() {
+ delete[] mPixelBuf;
+ }
+
+ // (overrides GLConsumer::FrameAvailableListener method)
+ virtual void onFrameAvailable();
+
+ // Reduces RGBA to RGB, in place.
+ static void reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount);
+
+ // Put a 32-bit value into a buffer, in little-endian byte order.
+ static void setValueLE(uint8_t* buf, uint32_t value);
+
+ // Used to wait for the FrameAvailableListener callback.
+ Mutex mMutex;
+ Condition mEventCond;
+
+ // Set by the FrameAvailableListener callback.
+ bool mFrameAvailable;
+
+ // Our queue. The producer side is passed to the virtual display, the
+ // consumer side feeds into our GLConsumer.
+ sp<BufferQueue> mBufferQueue;
+
+ // This receives frames from the virtual display and makes them available
+ // as an external texture.
+ sp<GLConsumer> mGlConsumer;
+
+ // EGL display / context / surface.
+ EglWindow mEglWindow;
+
+ // GL rendering support.
+ Program mExtTexProgram;
+
+ // External texture, updated by GLConsumer.
+ GLuint mExtTextureName;
+
+ // Pixel data buffer.
+ uint8_t* mPixelBuf;
+};
+
+}; // namespace android
+
+#endif /*SCREENRECORD_FRAMEOUTPUT_H*/
diff --git a/cmds/screenrecord/Overlay.cpp b/cmds/screenrecord/Overlay.cpp
new file mode 100644
index 0000000..2e98874
--- /dev/null
+++ b/cmds/screenrecord/Overlay.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2013 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 "ScreenRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <gui/BufferQueue.h>
+#include <gui/GraphicBufferAlloc.h>
+#include <gui/Surface.h>
+#include <cutils/properties.h>
+#include <utils/misc.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "screenrecord.h"
+#include "Overlay.h"
+#include "TextRenderer.h"
+
+using namespace android;
+
+// System properties to look up and display on the info screen.
+const char* Overlay::kPropertyNames[] = {
+ "ro.build.description",
+ // includes ro.build.id, ro.build.product, ro.build.tags, ro.build.type,
+ // and ro.build.version.release
+ "ro.product.manufacturer",
+ "ro.product.model",
+ "ro.board.platform",
+ "ro.revision",
+ "dalvik.vm.heapgrowthlimit",
+ "dalvik.vm.heapsize",
+ "persist.sys.dalvik.vm.lib.1",
+ //"ro.product.cpu.abi",
+ //"ro.bootloader",
+ //"this-never-appears!",
+};
+
+
+status_t Overlay::start(const sp<IGraphicBufferProducer>& outputSurface,
+ sp<IGraphicBufferProducer>* pBufferProducer) {
+ ALOGV("Overlay::start");
+ mOutputSurface = outputSurface;
+
+ // Grab the current monotonic time and the current wall-clock time so we
+ // can map one to the other. This allows the overlay counter to advance
+ // by the exact delay between frames, but if the wall clock gets adjusted
+ // we won't track it, which means we'll gradually go out of sync with the
+ // times in logcat.
+ mStartMonotonicNsecs = systemTime(CLOCK_MONOTONIC);
+ mStartRealtimeNsecs = systemTime(CLOCK_REALTIME);
+
+ Mutex::Autolock _l(mMutex);
+
+ // Start the thread. Traffic begins immediately.
+ run("overlay");
+
+ mState = INIT;
+ while (mState == INIT) {
+ mStartCond.wait(mMutex);
+ }
+
+ if (mThreadResult != NO_ERROR) {
+ ALOGE("Failed to start overlay thread: err=%d", mThreadResult);
+ return mThreadResult;
+ }
+ assert(mState == RUNNING);
+
+ ALOGV("Overlay::start successful");
+ *pBufferProducer = mBufferQueue;
+ return NO_ERROR;
+}
+
+status_t Overlay::stop() {
+ ALOGV("Overlay::stop");
+ Mutex::Autolock _l(mMutex);
+ mState = STOPPING;
+ mEventCond.signal();
+ return NO_ERROR;
+}
+
+bool Overlay::threadLoop() {
+ Mutex::Autolock _l(mMutex);
+
+ mThreadResult = setup_l();
+
+ if (mThreadResult != NO_ERROR) {
+ ALOGW("Aborting overlay thread");
+ mState = STOPPED;
+ release_l();
+ mStartCond.broadcast();
+ return false;
+ }
+
+ ALOGV("Overlay thread running");
+ mState = RUNNING;
+ mStartCond.broadcast();
+
+ while (mState == RUNNING) {
+ mEventCond.wait(mMutex);
+ if (mFrameAvailable) {
+ ALOGV("Awake, frame available");
+ processFrame_l();
+ mFrameAvailable = false;
+ } else {
+ ALOGV("Awake, frame not available");
+ }
+ }
+
+ ALOGV("Overlay thread stopping");
+ release_l();
+ mState = STOPPED;
+ return false; // stop
+}
+
+status_t Overlay::setup_l() {
+ status_t err;
+
+ err = mEglWindow.createWindow(mOutputSurface);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ mEglWindow.makeCurrent();
+
+ int width = mEglWindow.getWidth();
+ int height = mEglWindow.getHeight();
+
+ glViewport(0, 0, width, height);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ // Shaders for rendering from different types of textures.
+ err = mTexProgram.setup(Program::PROGRAM_TEXTURE_2D);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = mTextRenderer.loadIntoTexture();
+ if (err != NO_ERROR) {
+ return err;
+ }
+ mTextRenderer.setScreenSize(width, height);
+
+ // Input side (buffers from virtual display).
+ glGenTextures(1, &mExtTextureName);
+ if (mExtTextureName == 0) {
+ ALOGE("glGenTextures failed: %#x", glGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ mBufferQueue = new BufferQueue(/*new GraphicBufferAlloc()*/);
+ mGlConsumer = new GLConsumer(mBufferQueue, mExtTextureName,
+ GL_TEXTURE_EXTERNAL_OES);
+ mGlConsumer->setName(String8("virtual display"));
+ mGlConsumer->setDefaultBufferSize(width, height);
+ mGlConsumer->setDefaultMaxBufferCount(5);
+ mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
+
+ mGlConsumer->setFrameAvailableListener(this);
+
+ return NO_ERROR;
+}
+
+
+void Overlay::release_l() {
+ ALOGV("Overlay::release_l");
+ mOutputSurface.clear();
+ mGlConsumer.clear();
+ mBufferQueue.clear();
+
+ mTexProgram.release();
+ mExtTexProgram.release();
+ mEglWindow.release();
+}
+
+void Overlay::processFrame_l() {
+ float texMatrix[16];
+
+ mGlConsumer->updateTexImage();
+ mGlConsumer->getTransformMatrix(texMatrix);
+ nsecs_t monotonicNsec = mGlConsumer->getTimestamp();
+ nsecs_t frameNumber = mGlConsumer->getFrameNumber();
+ int64_t droppedFrames = 0;
+
+ if (mLastFrameNumber > 0) {
+ mTotalDroppedFrames += size_t(frameNumber - mLastFrameNumber) - 1;
+ }
+ mLastFrameNumber = frameNumber;
+
+ mTextRenderer.setProportionalScale(35);
+
+ if (false) { // DEBUG - full blue background
+ glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ }
+
+ int width = mEglWindow.getWidth();
+ int height = mEglWindow.getHeight();
+ if (false) { // DEBUG - draw inset
+ mExtTexProgram.blit(mExtTextureName, texMatrix,
+ 100, 100, width-200, height-200);
+ } else {
+ mExtTexProgram.blit(mExtTextureName, texMatrix,
+ 0, 0, width, height);
+ }
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ if (false) { // DEBUG - show entire font bitmap
+ mTexProgram.blit(mTextRenderer.getTextureName(), Program::kIdentity,
+ 100, 100, width-200, height-200);
+ }
+
+ char textBuf[64];
+ getTimeString_l(monotonicNsec, textBuf, sizeof(textBuf));
+ String8 timeStr(String8::format("%s f=%lld (%zd)",
+ textBuf, frameNumber, mTotalDroppedFrames));
+ mTextRenderer.drawString(mTexProgram, Program::kIdentity, 0, 0, timeStr);
+
+ glDisable(GL_BLEND);
+
+ if (false) { // DEBUG - add red rectangle in lower-left corner
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(0, 0, 200, 200);
+ glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDisable(GL_SCISSOR_TEST);
+ }
+
+ mEglWindow.presentationTime(monotonicNsec);
+ mEglWindow.swapBuffers();
+}
+
+void Overlay::getTimeString_l(nsecs_t monotonicNsec, char* buf, size_t bufLen) {
+ //const char* format = "%m-%d %T"; // matches log output
+ const char* format = "%T";
+ struct tm tm;
+
+ // localtime/strftime is not the fastest way to do this, but a trivial
+ // benchmark suggests that the cost is negligible.
+ int64_t realTime = mStartRealtimeNsecs +
+ (monotonicNsec - mStartMonotonicNsecs);
+ time_t secs = (time_t) (realTime / 1000000000);
+ localtime_r(&secs, &tm);
+ strftime(buf, bufLen, format, &tm);
+
+ int32_t msec = (int32_t) ((realTime % 1000000000) / 1000000);
+ char tmpBuf[5];
+ snprintf(tmpBuf, sizeof(tmpBuf), ".%03d", msec);
+ strlcat(buf, tmpBuf, bufLen);
+}
+
+// Callback; executes on arbitrary thread.
+void Overlay::onFrameAvailable() {
+ ALOGV("Overlay::onFrameAvailable");
+ Mutex::Autolock _l(mMutex);
+ mFrameAvailable = true;
+ mEventCond.signal();
+}
+
+
+/*static*/ status_t Overlay::drawInfoPage(
+ const sp<IGraphicBufferProducer>& outputSurface) {
+ status_t err;
+
+ EglWindow window;
+ err = window.createWindow(outputSurface);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ window.makeCurrent();
+
+ int width = window.getWidth();
+ int height = window.getHeight();
+ glViewport(0, 0, width, height);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ // Shaders for rendering.
+ Program texProgram;
+ err = texProgram.setup(Program::PROGRAM_TEXTURE_2D);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ TextRenderer textRenderer;
+ err = textRenderer.loadIntoTexture();
+ if (err != NO_ERROR) {
+ return err;
+ }
+ textRenderer.setScreenSize(width, height);
+
+ doDrawInfoPage(window, texProgram, textRenderer);
+
+ // Destroy the surface. This causes a disconnect.
+ texProgram.release();
+ window.release();
+
+ return NO_ERROR;
+}
+
+/*static*/ void Overlay::doDrawInfoPage(const EglWindow& window,
+ const Program& texProgram, TextRenderer& textRenderer) {
+ const nsecs_t holdTime = 250000000LL;
+
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ int width = window.getWidth();
+ int height = window.getHeight();
+
+ // Draw a thin border around the screen. Some players, e.g. browser
+ // plugins, make it hard to see where the edges are when the device
+ // is using a black background, so this gives the viewer a frame of
+ // reference.
+ //
+ // This is a clumsy way to do it, but we're only doing it for one frame,
+ // and it's easier than actually drawing lines.
+ const int lineWidth = 4;
+ glEnable(GL_SCISSOR_TEST);
+ glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
+ glScissor(0, 0, width, lineWidth);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glScissor(0, height - lineWidth, width, lineWidth);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glScissor(0, 0, lineWidth, height);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glScissor(width - lineWidth, 0, lineWidth, height);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDisable(GL_SCISSOR_TEST);
+
+ //glEnable(GL_BLEND);
+ //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ textRenderer.setProportionalScale(30);
+
+ float xpos = 0;
+ float ypos = 0;
+ ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos,
+ String8::format("Android screenrecord v%d.%d",
+ kVersionMajor, kVersionMinor));
+
+ // Show date/time
+ time_t now = time(0);
+ struct tm tm;
+ localtime_r(&now, &tm);
+ char timeBuf[64];
+ strftime(timeBuf, sizeof(timeBuf), "%a, %d %b %Y %T %z", &tm);
+ String8 header("Started ");
+ header += timeBuf;
+ ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, header);
+ ypos += 8 * textRenderer.getScale(); // slight padding
+
+ // Show selected system property values
+ for (int i = 0; i < NELEM(kPropertyNames); i++) {
+ char valueBuf[PROPERTY_VALUE_MAX];
+
+ property_get(kPropertyNames[i], valueBuf, "");
+ if (valueBuf[0] == '\0') {
+ continue;
+ }
+ String8 str(String8::format("%s: [%s]", kPropertyNames[i], valueBuf));
+ ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, str);
+ }
+ ypos += 8 * textRenderer.getScale(); // slight padding
+
+ // Show GL info
+ String8 glStr("OpenGL: ");
+ glStr += (char*) glGetString(GL_VENDOR);
+ glStr += " / ";
+ glStr += (char*) glGetString(GL_RENDERER);
+ glStr += ", ";
+ glStr += (char*) glGetString(GL_VERSION);
+ ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, glStr);
+
+ //glDisable(GL_BLEND);
+
+ // Set a presentation time slightly in the past. This will cause the
+ // player to hold the frame on screen.
+ window.presentationTime(systemTime(CLOCK_MONOTONIC) - holdTime);
+ window.swapBuffers();
+}
diff --git a/cmds/screenrecord/Overlay.h b/cmds/screenrecord/Overlay.h
new file mode 100644
index 0000000..48e48e0
--- /dev/null
+++ b/cmds/screenrecord/Overlay.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2013 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 SCREENRECORD_OVERLAY_H
+#define SCREENRECORD_OVERLAY_H
+
+#include "Program.h"
+#include "TextRenderer.h"
+#include "EglWindow.h"
+
+#include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
+#include <utils/Thread.h>
+
+#include <EGL/egl.h>
+
+namespace android {
+
+/*
+ * Overlay "filter". This sits between the virtual display and the video
+ * encoder.
+ *
+ * Most functions run on a thread created by start().
+ */
+class Overlay : public GLConsumer::FrameAvailableListener, Thread {
+public:
+ Overlay() : Thread(false),
+ mThreadResult(UNKNOWN_ERROR),
+ mState(UNINITIALIZED),
+ mFrameAvailable(false),
+ mExtTextureName(0),
+ mStartMonotonicNsecs(0),
+ mStartRealtimeNsecs(0),
+ mLastFrameNumber(-1),
+ mTotalDroppedFrames(0)
+ {}
+
+ // Creates a thread that performs the overlay. Pass in the surface that
+ // output will be sent to.
+ //
+ // This creates a dedicated thread for processing frames.
+ //
+ // Returns a reference to the producer side of a new BufferQueue that will
+ // be used by the virtual display.
+ status_t start(const sp<IGraphicBufferProducer>& outputSurface,
+ sp<IGraphicBufferProducer>* pBufferProducer);
+
+ // Stops the thread and releases resources. It's okay to call this even
+ // if start() was never called.
+ status_t stop();
+
+ // This creates an EGL context and window surface, draws some informative
+ // text on it, swaps the buffer, and then tears the whole thing down.
+ static status_t drawInfoPage(const sp<IGraphicBufferProducer>& outputSurface);
+
+private:
+ Overlay(const Overlay&);
+ Overlay& operator=(const Overlay&);
+
+ // Destruction via RefBase.
+ virtual ~Overlay() { assert(mState == UNINITIALIZED || mState == STOPPED); }
+
+ // Draw the initial info screen.
+ static void doDrawInfoPage(const EglWindow& window,
+ const Program& texRender, TextRenderer& textRenderer);
+
+ // (overrides GLConsumer::FrameAvailableListener method)
+ virtual void onFrameAvailable();
+
+ // (overrides Thread method)
+ virtual bool threadLoop();
+
+ // One-time setup (essentially object construction on the overlay thread).
+ status_t setup_l();
+
+ // Release all resources held.
+ void release_l();
+
+ // Release EGL display, context, surface.
+ void eglRelease_l();
+
+ // Process a frame received from the virtual display.
+ void processFrame_l();
+
+ // Convert a monotonic time stamp into a string with the current time.
+ void getTimeString_l(nsecs_t monotonicNsec, char* buf, size_t bufLen);
+
+ // Guards all fields below.
+ Mutex mMutex;
+
+ // Initialization gate.
+ Condition mStartCond;
+
+ // Thread status, mostly useful during startup.
+ status_t mThreadResult;
+
+ // Overlay thread state. States advance from left to right; object may
+ // not be restarted.
+ enum { UNINITIALIZED, INIT, RUNNING, STOPPING, STOPPED } mState;
+
+ // Event notification. Overlay thread sleeps on this until a frame
+ // arrives or it's time to shut down.
+ Condition mEventCond;
+
+ // Set by the FrameAvailableListener callback.
+ bool mFrameAvailable;
+
+ // The surface we send our output to, i.e. the video encoder's input
+ // surface.
+ sp<IGraphicBufferProducer> mOutputSurface;
+
+ // Our queue. The producer side is passed to the virtual display, the
+ // consumer side feeds into our GLConsumer.
+ sp<BufferQueue> mBufferQueue;
+
+ // This receives frames from the virtual display and makes them available
+ // as an external texture.
+ sp<GLConsumer> mGlConsumer;
+
+ // EGL display / context / surface.
+ EglWindow mEglWindow;
+
+ // GL rendering support.
+ Program mExtTexProgram;
+ Program mTexProgram;
+
+ // Text rendering.
+ TextRenderer mTextRenderer;
+
+ // External texture, updated by GLConsumer.
+ GLuint mExtTextureName;
+
+ // Start time, used to map monotonic to wall-clock time.
+ nsecs_t mStartMonotonicNsecs;
+ nsecs_t mStartRealtimeNsecs;
+
+ // Used for tracking dropped frames.
+ nsecs_t mLastFrameNumber;
+ size_t mTotalDroppedFrames;
+
+ static const char* kPropertyNames[];
+};
+
+}; // namespace android
+
+#endif /*SCREENRECORD_OVERLAY_H*/
diff --git a/cmds/screenrecord/Program.cpp b/cmds/screenrecord/Program.cpp
new file mode 100644
index 0000000..73cae6e
--- /dev/null
+++ b/cmds/screenrecord/Program.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2013 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 "ScreenRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "Program.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <assert.h>
+
+using namespace android;
+
+// 4x4 identity matrix
+const float Program::kIdentity[] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+};
+
+// Simple vertex shader. Texture coord calc includes matrix for GLConsumer
+// transform.
+static const char* kVertexShader =
+ "uniform mat4 uMVPMatrix;\n"
+ "uniform mat4 uGLCMatrix;\n"
+ "attribute vec4 aPosition;\n"
+ "attribute vec4 aTextureCoord;\n"
+ "varying vec2 vTextureCoord;\n"
+ "void main() {\n"
+ " gl_Position = uMVPMatrix * aPosition;\n"
+ " vTextureCoord = (uGLCMatrix * aTextureCoord).xy;\n"
+ "}\n";
+
+// Trivial fragment shader for external texture.
+static const char* kExtFragmentShader =
+ "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "varying vec2 vTextureCoord;\n"
+ "uniform samplerExternalOES uTexture;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(uTexture, vTextureCoord);\n"
+ "}\n";
+
+// Trivial fragment shader for mundane texture.
+static const char* kFragmentShader =
+ "precision mediump float;\n"
+ "varying vec2 vTextureCoord;\n"
+ "uniform sampler2D uTexture;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(uTexture, vTextureCoord);\n"
+ //" gl_FragColor = vec4(0.2, 1.0, 0.2, 1.0);\n"
+ "}\n";
+
+status_t Program::setup(ProgramType type) {
+ ALOGV("Program::setup type=%d", type);
+ status_t err;
+
+ mProgramType = type;
+
+ GLuint program;
+ if (type == PROGRAM_TEXTURE_2D) {
+ err = createProgram(&program, kVertexShader, kFragmentShader);
+ } else {
+ err = createProgram(&program, kVertexShader, kExtFragmentShader);
+ }
+ if (err != NO_ERROR) {
+ return err;
+ }
+ assert(program != 0);
+
+ maPositionLoc = glGetAttribLocation(program, "aPosition");
+ maTextureCoordLoc = glGetAttribLocation(program, "aTextureCoord");
+ muMVPMatrixLoc = glGetUniformLocation(program, "uMVPMatrix");
+ muGLCMatrixLoc = glGetUniformLocation(program, "uGLCMatrix");
+ muTextureLoc = glGetUniformLocation(program, "uTexture");
+ if ((maPositionLoc | maTextureCoordLoc | muMVPMatrixLoc |
+ muGLCMatrixLoc | muTextureLoc) == -1) {
+ ALOGE("Attrib/uniform lookup failed: %#x", glGetError());
+ glDeleteProgram(program);
+ return UNKNOWN_ERROR;
+ }
+
+ mProgram = program;
+ return NO_ERROR;
+}
+
+void Program::release() {
+ ALOGV("Program::release");
+ if (mProgram != 0) {
+ glDeleteProgram(mProgram);
+ mProgram = 0;
+ }
+}
+
+status_t Program::createProgram(GLuint* outPgm, const char* vertexShader,
+ const char* fragmentShader) {
+ GLuint vs, fs;
+ status_t err;
+
+ err = compileShader(GL_VERTEX_SHADER, vertexShader, &vs);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = compileShader(GL_FRAGMENT_SHADER, fragmentShader, &fs);
+ if (err != NO_ERROR) {
+ glDeleteShader(vs);
+ return err;
+ }
+
+ GLuint program;
+ err = linkShaderProgram(vs, fs, &program);
+ glDeleteShader(vs);
+ glDeleteShader(fs);
+ if (err == NO_ERROR) {
+ *outPgm = program;
+ }
+ return err;
+}
+
+status_t Program::compileShader(GLenum shaderType, const char* src,
+ GLuint* outShader) {
+ GLuint shader = glCreateShader(shaderType);
+ if (shader == 0) {
+ ALOGE("glCreateShader error: %#x", glGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ glShaderSource(shader, 1, &src, NULL);
+ glCompileShader(shader);
+
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ ALOGE("Compile of shader type %d failed", shaderType);
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+ if (infoLen) {
+ char* buf = new char[infoLen];
+ if (buf) {
+ glGetShaderInfoLog(shader, infoLen, NULL, buf);
+ ALOGE("Compile log: %s", buf);
+ delete[] buf;
+ }
+ }
+ glDeleteShader(shader);
+ return UNKNOWN_ERROR;
+ }
+ *outShader = shader;
+ return NO_ERROR;
+}
+
+status_t Program::linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm) {
+ GLuint program = glCreateProgram();
+ if (program == 0) {
+ ALOGE("glCreateProgram error: %#x", glGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ glAttachShader(program, vs);
+ glAttachShader(program, fs);
+ glLinkProgram(program);
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus != GL_TRUE) {
+ ALOGE("glLinkProgram failed");
+ GLint bufLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+ if (bufLength) {
+ char* buf = new char[bufLength];
+ if (buf) {
+ glGetProgramInfoLog(program, bufLength, NULL, buf);
+ ALOGE("Link log: %s", buf);
+ delete[] buf;
+ }
+ }
+ glDeleteProgram(program);
+ return UNKNOWN_ERROR;
+ }
+
+ *outPgm = program;
+ return NO_ERROR;
+}
+
+
+
+status_t Program::blit(GLuint texName, const float* texMatrix,
+ int32_t x, int32_t y, int32_t w, int32_t h, bool invert) const {
+ ALOGV("Program::blit %d xy=%d,%d wh=%d,%d", texName, x, y, w, h);
+
+ const float pos[] = {
+ float(x), float(y+h),
+ float(x+w), float(y+h),
+ float(x), float(y),
+ float(x+w), float(y),
+ };
+ const float uv[] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ };
+ status_t err;
+
+ err = beforeDraw(texName, texMatrix, pos, uv, invert);
+ if (err == NO_ERROR) {
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ err = afterDraw();
+ }
+ return err;
+}
+
+status_t Program::drawTriangles(GLuint texName, const float* texMatrix,
+ const float* vertices, const float* texes, size_t count) const {
+ ALOGV("Program::drawTriangles texName=%d", texName);
+
+ status_t err;
+
+ err = beforeDraw(texName, texMatrix, vertices, texes, false);
+ if (err == NO_ERROR) {
+ glDrawArrays(GL_TRIANGLES, 0, count);
+ err = afterDraw();
+ }
+ return err;
+}
+
+status_t Program::beforeDraw(GLuint texName, const float* texMatrix,
+ const float* vertices, const float* texes, bool invert) const {
+ // Create an orthographic projection matrix based on viewport size.
+ GLint vp[4];
+ glGetIntegerv(GL_VIEWPORT, vp);
+ float screenToNdc[16] = {
+ 2.0f/float(vp[2]), 0.0f, 0.0f, 0.0f,
+ 0.0f, -2.0f/float(vp[3]), 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ -1.0f, 1.0f, 0.0f, 1.0f,
+ };
+ if (invert) {
+ screenToNdc[5] = -screenToNdc[5];
+ screenToNdc[13] = -screenToNdc[13];
+ }
+
+ glUseProgram(mProgram);
+
+ glVertexAttribPointer(maPositionLoc, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+ glVertexAttribPointer(maTextureCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, texes);
+ glEnableVertexAttribArray(maPositionLoc);
+ glEnableVertexAttribArray(maTextureCoordLoc);
+
+ glUniformMatrix4fv(muMVPMatrixLoc, 1, GL_FALSE, screenToNdc);
+ glUniformMatrix4fv(muGLCMatrixLoc, 1, GL_FALSE, texMatrix);
+
+ glActiveTexture(GL_TEXTURE0);
+
+ switch (mProgramType) {
+ case PROGRAM_EXTERNAL_TEXTURE:
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, texName);
+ break;
+ case PROGRAM_TEXTURE_2D:
+ glBindTexture(GL_TEXTURE_2D, texName);
+ break;
+ default:
+ ALOGE("unexpected program type %d", mProgramType);
+ return UNKNOWN_ERROR;
+ }
+
+ glUniform1i(muTextureLoc, 0);
+
+ GLenum glErr;
+ if ((glErr = glGetError()) != GL_NO_ERROR) {
+ ALOGE("GL error before draw: %#x", glErr);
+ glDisableVertexAttribArray(maPositionLoc);
+ glDisableVertexAttribArray(maTextureCoordLoc);
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+status_t Program::afterDraw() const {
+ glDisableVertexAttribArray(maPositionLoc);
+ glDisableVertexAttribArray(maTextureCoordLoc);
+
+ GLenum glErr;
+ if ((glErr = glGetError()) != GL_NO_ERROR) {
+ ALOGE("GL error after draw: %#x", glErr);
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
diff --git a/cmds/screenrecord/Program.h b/cmds/screenrecord/Program.h
new file mode 100644
index 0000000..558be8d
--- /dev/null
+++ b/cmds/screenrecord/Program.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2013 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 SCREENRECORD_PROGRAM_H
+#define SCREENRECORD_PROGRAM_H
+
+#include <utils/Errors.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+namespace android {
+
+/*
+ * Utility class for GLES rendering.
+ *
+ * Not thread-safe.
+ */
+class Program {
+public:
+ enum ProgramType { PROGRAM_UNKNOWN=0, PROGRAM_EXTERNAL_TEXTURE,
+ PROGRAM_TEXTURE_2D };
+
+ Program() :
+ mProgramType(PROGRAM_UNKNOWN),
+ mProgram(0),
+ maPositionLoc(0),
+ maTextureCoordLoc(0),
+ muMVPMatrixLoc(0),
+ muGLCMatrixLoc(0),
+ muTextureLoc(0)
+ {}
+ ~Program() { release(); }
+
+ // Initialize the program for use with the specified texture type.
+ status_t setup(ProgramType type);
+
+ // Release the program and associated resources.
+ void release();
+
+ // Blit the specified texture to { x, y, x+w, y+h }. Inverts the
+ // content if "invert" is set.
+ status_t blit(GLuint texName, const float* texMatrix,
+ int32_t x, int32_t y, int32_t w, int32_t h,
+ bool invert = false) const;
+
+ // Draw a number of triangles.
+ status_t drawTriangles(GLuint texName, const float* texMatrix,
+ const float* vertices, const float* texes, size_t count) const;
+
+ static const float kIdentity[];
+
+private:
+ Program(const Program&);
+ Program& operator=(const Program&);
+
+ // Common code for draw functions.
+ status_t beforeDraw(GLuint texName, const float* texMatrix,
+ const float* vertices, const float* texes, bool invert) const;
+ status_t afterDraw() const;
+
+ // GLES 2 shader utilities.
+ status_t createProgram(GLuint* outPgm, const char* vertexShader,
+ const char* fragmentShader);
+ static status_t compileShader(GLenum shaderType, const char* src,
+ GLuint* outShader);
+ static status_t linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm);
+
+ ProgramType mProgramType;
+ GLuint mProgram;
+
+ GLint maPositionLoc;
+ GLint maTextureCoordLoc;
+ GLint muMVPMatrixLoc;
+ GLint muGLCMatrixLoc;
+ GLint muTextureLoc;
+};
+
+}; // namespace android
+
+#endif /*SCREENRECORD_PROGRAM_H*/
diff --git a/cmds/screenrecord/TextRenderer.cpp b/cmds/screenrecord/TextRenderer.cpp
new file mode 100644
index 0000000..784055c
--- /dev/null
+++ b/cmds/screenrecord/TextRenderer.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2013 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 "ScreenRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "TextRenderer.h"
+
+#include <assert.h>
+
+namespace android {
+#include "FontBitmap.h"
+};
+
+using namespace android;
+
+const char TextRenderer::kWhitespace[] = " \t\n\r";
+
+bool TextRenderer::mInitialized = false;
+uint32_t TextRenderer::mXOffset[FontBitmap::numGlyphs];
+
+void TextRenderer::initOnce() {
+ if (!mInitialized) {
+ initXOffset();
+ mInitialized = true;
+ }
+}
+
+void TextRenderer::initXOffset() {
+ // Generate a table of X offsets. They start at zero and reset whenever
+ // we move down a line (i.e. the Y offset changes). The offset increases
+ // by one pixel more than the width because the generator left a gap to
+ // avoid reading pixels from adjacent glyphs in the texture filter.
+ uint16_t offset = 0;
+ uint16_t prevYOffset = (int16_t) -1;
+ for (unsigned int i = 0; i < FontBitmap::numGlyphs; i++) {
+ if (prevYOffset != FontBitmap::yoffset[i]) {
+ prevYOffset = FontBitmap::yoffset[i];
+ offset = 0;
+ }
+ mXOffset[i] = offset;
+ offset += FontBitmap::glyphWidth[i] + 1;
+ }
+}
+
+static bool isPowerOfTwo(uint32_t val) {
+ // a/k/a "is exactly one bit set"; note returns true for 0
+ return (val & (val -1)) == 0;
+}
+
+static uint32_t powerOfTwoCeil(uint32_t val) {
+ // drop it, smear the bits across, pop it
+ val--;
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ val++;
+
+ return val;
+}
+
+float TextRenderer::getGlyphHeight() const {
+ return FontBitmap::maxGlyphHeight;
+}
+
+status_t TextRenderer::loadIntoTexture() {
+ ALOGV("Font::loadIntoTexture");
+
+ glGenTextures(1, &mTextureName);
+ if (mTextureName == 0) {
+ ALOGE("glGenTextures failed: %#x", glGetError());
+ return UNKNOWN_ERROR;
+ }
+ glBindTexture(GL_TEXTURE_2D, mTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ // The pixel data is stored as combined color+alpha, 8 bits per pixel.
+ // It's guaranteed to be a power-of-two wide, but we cut off the height
+ // where the data ends. We want to expand it to a power-of-two bitmap
+ // with ARGB data and hand that to glTexImage2D.
+
+ if (!isPowerOfTwo(FontBitmap::width)) {
+ ALOGE("npot glyph bitmap width %u", FontBitmap::width);
+ return UNKNOWN_ERROR;
+ }
+
+ uint32_t potHeight = powerOfTwoCeil(FontBitmap::height);
+ uint8_t* rgbaPixels = new uint8_t[FontBitmap::width * potHeight * 4];
+ memset(rgbaPixels, 0, FontBitmap::width * potHeight * 4);
+ uint8_t* pix = rgbaPixels;
+
+ for (unsigned int i = 0; i < FontBitmap::width * FontBitmap::height; i++) {
+ uint8_t alpha, color;
+ if ((FontBitmap::pixels[i] & 1) == 0) {
+ // black pixel with varying alpha
+ color = 0x00;
+ alpha = FontBitmap::pixels[i] & ~1;
+ } else {
+ // opaque grey pixel
+ color = FontBitmap::pixels[i] & ~1;
+ alpha = 0xff;
+ }
+ *pix++ = color;
+ *pix++ = color;
+ *pix++ = color;
+ *pix++ = alpha;
+ }
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, FontBitmap::width, potHeight, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, rgbaPixels);
+ delete[] rgbaPixels;
+ GLint glErr = glGetError();
+ if (glErr != 0) {
+ ALOGE("glTexImage2D failed: %#x", glErr);
+ return UNKNOWN_ERROR;
+ }
+ return NO_ERROR;
+}
+
+void TextRenderer::setProportionalScale(float linesPerScreen) {
+ if (mScreenWidth == 0 || mScreenHeight == 0) {
+ ALOGW("setFontScale: can't set scale for width=%d height=%d",
+ mScreenWidth, mScreenHeight);
+ return;
+ }
+ float tallest = mScreenWidth > mScreenHeight ? mScreenWidth : mScreenHeight;
+ setScale(tallest / (linesPerScreen * getGlyphHeight()));
+}
+
+float TextRenderer::computeScaledStringWidth(const String8& str8) const {
+ // String8.length() isn't documented, but I'm assuming it will return
+ // the number of characters rather than the number of bytes. Since
+ // we can only display ASCII we want to ignore anything else, so we
+ // just convert to char* -- but String8 doesn't document what it does
+ // with values outside 0-255. So just convert to char* and use strlen()
+ // to see what we get.
+ const char* str = str8.string();
+ return computeScaledStringWidth(str, strlen(str));
+}
+
+size_t TextRenderer::glyphIndex(char ch) const {
+ size_t chi = ch - FontBitmap::firstGlyphChar;
+ if (chi >= FontBitmap::numGlyphs) {
+ chi = '?' - FontBitmap::firstGlyphChar;
+ }
+ assert(chi < FontBitmap::numGlyphs);
+ return chi;
+}
+
+float TextRenderer::computeScaledStringWidth(const char* str,
+ size_t len) const {
+ float width = 0.0f;
+ for (size_t i = 0; i < len; i++) {
+ size_t chi = glyphIndex(str[i]);
+ float glyphWidth = FontBitmap::glyphWidth[chi];
+ width += (glyphWidth - 1 - FontBitmap::outlineWidth) * mScale;
+ }
+
+ return width;
+}
+
+void TextRenderer::drawString(const Program& program, const float* texMatrix,
+ float x, float y, const String8& str8) const {
+ ALOGV("drawString %.3f,%.3f '%s' (scale=%.3f)", x, y, str8.string(),mScale);
+ initOnce();
+
+ // We want to draw the entire string with a single GLES call. We
+ // generate two arrays, one with screen coordinates, one with texture
+ // coordinates. Need two triangles per character.
+ const char* str = str8.string();
+ size_t len = strlen(str); // again, unsure about String8 handling
+
+ const size_t quadCoords =
+ 2 /*triangles*/ * 3 /*vertex/tri*/ * 2 /*coord/vertex*/;
+ float vertices[len * quadCoords];
+ float texes[len * quadCoords];
+
+ float fullTexWidth = FontBitmap::width;
+ float fullTexHeight = powerOfTwoCeil(FontBitmap::height);
+ for (size_t i = 0; i < len; i++) {
+ size_t chi = glyphIndex(str[i]);
+ float glyphWidth = FontBitmap::glyphWidth[chi];
+ float glyphHeight = FontBitmap::maxGlyphHeight;
+
+ float vertLeft = x;
+ float vertRight = x + glyphWidth * mScale;
+ float vertTop = y;
+ float vertBottom = y + glyphHeight * mScale;
+
+ // Lowest-numbered glyph is in top-left of bitmap, which puts it at
+ // the bottom-left in texture coordinates.
+ float texLeft = mXOffset[chi] / fullTexWidth;
+ float texRight = (mXOffset[chi] + glyphWidth) / fullTexWidth;
+ float texTop = FontBitmap::yoffset[chi] / fullTexHeight;
+ float texBottom = (FontBitmap::yoffset[chi] + glyphHeight) /
+ fullTexHeight;
+
+ size_t off = i * quadCoords;
+ vertices[off + 0] = vertLeft;
+ vertices[off + 1] = vertBottom;
+ vertices[off + 2] = vertRight;
+ vertices[off + 3] = vertBottom;
+ vertices[off + 4] = vertLeft;
+ vertices[off + 5] = vertTop;
+ vertices[off + 6] = vertLeft;
+ vertices[off + 7] = vertTop;
+ vertices[off + 8] = vertRight;
+ vertices[off + 9] = vertBottom;
+ vertices[off + 10] = vertRight;
+ vertices[off + 11] = vertTop;
+ texes[off + 0] = texLeft;
+ texes[off + 1] = texBottom;
+ texes[off + 2] = texRight;
+ texes[off + 3] = texBottom;
+ texes[off + 4] = texLeft;
+ texes[off + 5] = texTop;
+ texes[off + 6] = texLeft;
+ texes[off + 7] = texTop;
+ texes[off + 8] = texRight;
+ texes[off + 9] = texBottom;
+ texes[off + 10] = texRight;
+ texes[off + 11] = texTop;
+
+ // We added 1-pixel padding in the texture, so we want to advance by
+ // one less. Also, each glyph is surrounded by a black outline, which
+ // we want to merge.
+ x += (glyphWidth - 1 - FontBitmap::outlineWidth) * mScale;
+ }
+
+ program.drawTriangles(mTextureName, texMatrix, vertices, texes,
+ len * quadCoords / 2);
+}
+
+float TextRenderer::drawWrappedString(const Program& texRender,
+ float xpos, float ypos, const String8& str) {
+ ALOGV("drawWrappedString %.3f,%.3f '%s'", xpos, ypos, str.string());
+ initOnce();
+
+ if (mScreenWidth == 0 || mScreenHeight == 0) {
+ ALOGW("drawWrappedString: can't wrap with width=%d height=%d",
+ mScreenWidth, mScreenHeight);
+ return ypos;
+ }
+
+ const float indentWidth = mIndentMult * getScale();
+ if (xpos < mBorderWidth) {
+ xpos = mBorderWidth;
+ }
+ if (ypos < mBorderWidth) {
+ ypos = mBorderWidth;
+ }
+
+ const size_t maxWidth = (mScreenWidth - mBorderWidth) - xpos;
+ if (maxWidth < 1) {
+ ALOGE("Unable to render text: xpos=%.3f border=%.3f width=%u",
+ xpos, mBorderWidth, mScreenWidth);
+ return ypos;
+ }
+ float stringWidth = computeScaledStringWidth(str);
+ if (stringWidth <= maxWidth) {
+ // Trivial case.
+ drawString(texRender, Program::kIdentity, xpos, ypos, str);
+ ypos += getScaledGlyphHeight();
+ } else {
+ // We need to break the string into pieces, ideally at whitespace
+ // boundaries.
+ char* mangle = strdup(str.string());
+ char* start = mangle;
+ while (start != NULL) {
+ float xposAdj = (start == mangle) ? xpos : xpos + indentWidth;
+ char* brk = breakString(start,
+ (float) (mScreenWidth - mBorderWidth - xposAdj));
+ if (brk == NULL) {
+ // draw full string
+ drawString(texRender, Program::kIdentity, xposAdj, ypos,
+ String8(start));
+ start = NULL;
+ } else {
+ // draw partial string
+ char ch = *brk;
+ *brk = '\0';
+ drawString(texRender, Program::kIdentity, xposAdj, ypos,
+ String8(start));
+ *brk = ch;
+ start = brk;
+ if (strchr(kWhitespace, ch) != NULL) {
+ // if we broke on whitespace, skip past it
+ start++;
+ }
+ }
+ ypos += getScaledGlyphHeight();
+ }
+ free(mangle);
+ }
+
+ return ypos;
+}
+
+char* TextRenderer::breakString(const char* str, float maxWidth) const {
+ // Ideally we'd do clever things like binary search. Not bothering.
+ ALOGV("breakString '%s' %.3f", str, maxWidth);
+
+ size_t len = strlen(str);
+ if (len == 0) {
+ // Caller should detect this and not advance ypos.
+ return NULL;
+ }
+
+ float stringWidth = computeScaledStringWidth(str, len);
+ if (stringWidth <= maxWidth) {
+ return NULL; // trivial -- use full string
+ }
+
+ // Find the longest string that will fit.
+ size_t goodPos = 0;
+ for (size_t i = 0; i < len; i++) {
+ stringWidth = computeScaledStringWidth(str, i);
+ if (stringWidth < maxWidth) {
+ goodPos = i;
+ } else {
+ break; // too big
+ }
+ }
+ if (goodPos == 0) {
+ // space is too small to hold any glyph; output a single char
+ ALOGW("Couldn't find a nonzero prefix that fit from '%s'", str);
+ goodPos = 1;
+ }
+
+ // Scan back for whitespace. If we can't find any we'll just have
+ // an ugly mid-word break.
+ for (size_t i = goodPos; i > 0; i--) {
+ if (strchr(kWhitespace, str[i]) != NULL) {
+ goodPos = i;
+ break;
+ }
+ }
+
+ ALOGV("goodPos=%d for str='%s'", goodPos, str);
+ return const_cast<char*>(str + goodPos);
+}
diff --git a/cmds/screenrecord/TextRenderer.h b/cmds/screenrecord/TextRenderer.h
new file mode 100644
index 0000000..03dd2fb
--- /dev/null
+++ b/cmds/screenrecord/TextRenderer.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2013 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 SCREENRECORD_TEXT_RENDER_H
+#define SCREENRECORD_TEXT_RENDER_H
+
+#include "Program.h"
+
+#include <utils/String8.h>
+#include <utils/Errors.h>
+
+#include <GLES2/gl2.h>
+
+
+namespace android {
+
+/*
+ * Simple font representation.
+ *
+ * Not thread-safe.
+ */
+class TextRenderer {
+public:
+ TextRenderer() :
+ mTextureName(0),
+ mScale(1.0f),
+ mBorderWidth(10.0f),
+ mIndentMult(30.0f),
+ mScreenWidth(0),
+ mScreenHeight(0)
+ {}
+ ~TextRenderer() {}
+
+ // Load the glyph bitmap into a 2D texture in the current context.
+ status_t loadIntoTexture();
+
+ // Set the screen dimensions, used for scaling and line wrap.
+ void setScreenSize(uint32_t width, uint32_t height) {
+ mScreenWidth = width;
+ mScreenHeight = height;
+ }
+
+ // Get/set the font scaling.
+ float getScale() const { return mScale; }
+ void setScale(float scale) { mScale = scale; }
+
+ // Set the font scaling based on the desired number of lines per screen.
+ // The display's tallest axis is used, so if the device is in landscape
+ // the screen will fit fewer lines.
+ void setProportionalScale(float linesPerScreen);
+
+ // Render the text string at the specified coordinates. Pass in the
+ // upper-left corner in non-GL-flipped coordinates, i.e. to print text
+ // at the top left of the screen use (0,0).
+ //
+ // Set blend func (1, 1-srcAlpha) before calling if drawing onto
+ // something other than black.
+ void drawString(const Program& program, const float* texMatrix,
+ float x, float y, const String8& str) const;
+
+ // Draw a string, possibly wrapping it at the screen boundary. Top-left
+ // is at (0,0).
+ //
+ // Returns the updated Y position.
+ float drawWrappedString(const Program& texRender, float xpos, float ypos,
+ const String8& str);
+
+ // Returns the name of the texture the font was loaded into.
+ GLuint getTextureName() const { return mTextureName; }
+
+private:
+ TextRenderer(const TextRenderer&);
+ TextRenderer& operator=(const TextRenderer&);
+
+ // Perform one-time initialization.
+ static void initOnce();
+
+ // Populate the mXOffset array.
+ static void initXOffset();
+
+ // Find a good place to break the string. Returns NULL if the entire
+ // string will fit.
+ char* breakString(const char* str, float maxWidth) const;
+
+ // Computes the width of the string, in pixels.
+ float computeScaledStringWidth(const String8& str8) const;
+
+ // Computes the width of first N characters in the string.
+ float computeScaledStringWidth(const char* str, size_t len) const;
+
+ // Returns the font's glyph height. This is the full pixel height of the
+ // tallest glyph, both above and below the baseline, NOT adjusted by the
+ // current scale factor.
+ float getGlyphHeight() const;
+
+ // Like getGlyphHeight(), but result is scaled.
+ float getScaledGlyphHeight() const { return getGlyphHeight() * mScale; }
+
+ // Convert an ASCII character to a glyph index. Returns the glyph for
+ // '?' if we have no glyph for the specified character.
+ size_t glyphIndex(char ch) const;
+
+ GLuint mTextureName;
+ float mScale;
+
+ // Number of pixels preserved at the left/right edges of the screen by
+ // drawWrappedString(). Not scaled.
+ float mBorderWidth;
+
+ // Distance to indent a broken line. Used by drawWrappedString().
+ // Value will be adjusted by the current scale factor.
+ float mIndentMult;
+
+ // Screen dimensions.
+ uint32_t mScreenWidth;
+ uint32_t mScreenHeight;
+
+ // Static font info.
+ static bool mInitialized;
+ static uint32_t mXOffset[];
+
+ static const char kWhitespace[];
+};
+
+}; // namespace android
+
+#endif /*SCREENRECORD_TEXT_RENDER_H*/
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 49999b5..a17fc51 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -15,13 +15,14 @@
*/
#define LOG_TAG "ScreenRecord"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <utils/Errors.h>
-#include <utils/Thread.h>
#include <utils/Timers.h>
+#include <utils/Trace.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -29,7 +30,6 @@
#include <ui/DisplayInfo.h>
#include <media/openmax/OMX_IVCommon.h>
#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaErrors.h>
@@ -40,30 +40,43 @@
#include <unistd.h>
#include <string.h>
#include <stdio.h>
+#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <getopt.h>
#include <sys/wait.h>
+#include <termios.h>
+#include <assert.h>
+
+#include "screenrecord.h"
+#include "Overlay.h"
+#include "FrameOutput.h"
using namespace android;
static const uint32_t kMinBitRate = 100000; // 0.1Mbps
-static const uint32_t kMaxBitRate = 100 * 1000000; // 100Mbps
+static const uint32_t kMaxBitRate = 200 * 1000000; // 200Mbps
static const uint32_t kMaxTimeLimitSec = 180; // 3 minutes
static const uint32_t kFallbackWidth = 1280; // 720p
static const uint32_t kFallbackHeight = 720;
+static const char* kMimeTypeAvc = "video/avc";
// Command-line parameters.
-static bool gVerbose = false; // chatty on stdout
-static bool gRotate = false; // rotate 90 degrees
-static bool gSizeSpecified = false; // was size explicitly requested?
-static uint32_t gVideoWidth = 0; // default width+height
+static bool gVerbose = false; // chatty on stdout
+static bool gRotate = false; // rotate 90 degrees
+static enum {
+ FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES
+} gOutputFormat = FORMAT_MP4; // data format for output
+static bool gSizeSpecified = false; // was size explicitly requested?
+static bool gWantInfoScreen = false; // do we want initial info screen?
+static bool gWantFrameTime = false; // do we want times on each frame?
+static uint32_t gVideoWidth = 0; // default width+height
static uint32_t gVideoHeight = 0;
-static uint32_t gBitRate = 4000000; // 4Mbps
+static uint32_t gBitRate = 4000000; // 4Mbps
static uint32_t gTimeLimitSec = kMaxTimeLimitSec;
// Set by signal handler to stop recording.
-static bool gStopRequested;
+static volatile bool gStopRequested;
// Previous signal handler state, restored after first hit.
static struct sigaction gOrigSigactionINT;
@@ -97,8 +110,7 @@
* when Ctrl-C is hit. If we're run from the host, the local adb process
* gets the signal, and we get a SIGHUP when the terminal disconnects.
*/
-static status_t configureSignals()
-{
+static status_t configureSignals() {
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = signalCatcher;
@@ -134,14 +146,14 @@
status_t err;
if (gVerbose) {
- printf("Configuring recorder for %dx%d video at %.2fMbps\n",
- gVideoWidth, gVideoHeight, gBitRate / 1000000.0);
+ printf("Configuring recorder for %dx%d %s at %.2fMbps\n",
+ gVideoWidth, gVideoHeight, kMimeTypeAvc, gBitRate / 1000000.0);
}
sp<AMessage> format = new AMessage;
format->setInt32("width", gVideoWidth);
format->setInt32("height", gVideoHeight);
- format->setString("mime", "video/avc");
+ format->setString("mime", kMimeTypeAvc);
format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
format->setInt32("bitrate", gBitRate);
format->setFloat("frame-rate", displayFps);
@@ -151,40 +163,37 @@
looper->setName("screenrecord_looper");
looper->start();
ALOGV("Creating codec");
- sp<MediaCodec> codec = MediaCodec::CreateByType(looper, "video/avc", true);
+ sp<MediaCodec> codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
if (codec == NULL) {
- fprintf(stderr, "ERROR: unable to create video/avc codec instance\n");
+ fprintf(stderr, "ERROR: unable to create %s codec instance\n",
+ kMimeTypeAvc);
return UNKNOWN_ERROR;
}
+
err = codec->configure(format, NULL, NULL,
MediaCodec::CONFIGURE_FLAG_ENCODE);
if (err != NO_ERROR) {
+ fprintf(stderr, "ERROR: unable to configure %s codec at %dx%d (err=%d)\n",
+ kMimeTypeAvc, gVideoWidth, gVideoHeight, err);
codec->release();
- codec.clear();
-
- fprintf(stderr, "ERROR: unable to configure codec (err=%d)\n", err);
return err;
}
- ALOGV("Creating buffer producer");
+ ALOGV("Creating encoder input surface");
sp<IGraphicBufferProducer> bufferProducer;
err = codec->createInputSurface(&bufferProducer);
if (err != NO_ERROR) {
- codec->release();
- codec.clear();
-
fprintf(stderr,
"ERROR: unable to create encoder input surface (err=%d)\n", err);
+ codec->release();
return err;
}
ALOGV("Starting codec");
err = codec->start();
if (err != NO_ERROR) {
- codec->release();
- codec.clear();
-
fprintf(stderr, "ERROR: unable to start codec (err=%d)\n", err);
+ codec->release();
return err;
}
@@ -195,12 +204,11 @@
}
/*
- * Configures the virtual display. When this completes, virtual display
- * frames will start being sent to the encoder's surface.
+ * Sets the display projection, based on the display dimensions, video size,
+ * and device orientation.
*/
-static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo,
- const sp<IGraphicBufferProducer>& bufferProducer,
- sp<IBinder>* pDisplayHandle) {
+static status_t setDisplayProjection(const sp<IBinder>& dpy,
+ const DisplayInfo& mainDpyInfo) {
status_t err;
// Set the region of the layer stack we're interested in, which in our
@@ -266,15 +274,25 @@
}
}
-
- sp<IBinder> dpy = SurfaceComposerClient::createDisplay(
- String8("ScreenRecorder"), false /* secure */);
-
- SurfaceComposerClient::openGlobalTransaction();
- SurfaceComposerClient::setDisplaySurface(dpy, bufferProducer);
SurfaceComposerClient::setDisplayProjection(dpy,
gRotate ? DISPLAY_ORIENTATION_90 : DISPLAY_ORIENTATION_0,
layerStackRect, displayRect);
+ return NO_ERROR;
+}
+
+/*
+ * Configures the virtual display. When this completes, virtual display
+ * frames will start arriving from the buffer producer.
+ */
+static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo,
+ const sp<IGraphicBufferProducer>& bufferProducer,
+ sp<IBinder>* pDisplayHandle) {
+ sp<IBinder> dpy = SurfaceComposerClient::createDisplay(
+ String8("ScreenRecorder"), false /*secure*/);
+
+ SurfaceComposerClient::openGlobalTransaction();
+ SurfaceComposerClient::setDisplaySurface(dpy, bufferProducer);
+ setDisplayProjection(dpy, mainDpyInfo);
SurfaceComposerClient::setDisplayLayerStack(dpy, 0); // default stack
SurfaceComposerClient::closeGlobalTransaction();
@@ -288,16 +306,22 @@
* input frames are coming from the virtual display as fast as SurfaceFlinger
* wants to send them.
*
+ * Exactly one of muxer or rawFp must be non-null.
+ *
* The muxer must *not* have been started before calling.
*/
static status_t runEncoder(const sp<MediaCodec>& encoder,
- const sp<MediaMuxer>& muxer) {
+ const sp<MediaMuxer>& muxer, FILE* rawFp, const sp<IBinder>& mainDpy,
+ const sp<IBinder>& virtualDpy, uint8_t orientation) {
static int kTimeout = 250000; // be responsive on signal
status_t err;
ssize_t trackIdx = -1;
uint32_t debugNumFrames = 0;
int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC);
int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec);
+ DisplayInfo mainDpyInfo;
+
+ assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL));
Vector<sp<ABuffer> > buffers;
err = encoder->getOutputBuffers(&buffers);
@@ -330,31 +354,68 @@
case NO_ERROR:
// got a buffer
if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) {
- // ignore this -- we passed the CSD into MediaMuxer when
- // we got the format change notification
- ALOGV("Got codec config buffer (%u bytes); ignoring", size);
- size = 0;
+ ALOGV("Got codec config buffer (%u bytes)", size);
+ if (muxer != NULL) {
+ // ignore this -- we passed the CSD into MediaMuxer when
+ // we got the format change notification
+ size = 0;
+ }
}
if (size != 0) {
ALOGV("Got data in buffer %d, size=%d, pts=%lld",
bufIndex, size, ptsUsec);
- CHECK(trackIdx != -1);
+
+ { // scope
+ ATRACE_NAME("orientation");
+ // Check orientation, update if it has changed.
+ //
+ // Polling for changes is inefficient and wrong, but the
+ // useful stuff is hard to get at without a Dalvik VM.
+ err = SurfaceComposerClient::getDisplayInfo(mainDpy,
+ &mainDpyInfo);
+ if (err != NO_ERROR) {
+ ALOGW("getDisplayInfo(main) failed: %d", err);
+ } else if (orientation != mainDpyInfo.orientation) {
+ ALOGD("orientation changed, now %d", mainDpyInfo.orientation);
+ SurfaceComposerClient::openGlobalTransaction();
+ setDisplayProjection(virtualDpy, mainDpyInfo);
+ SurfaceComposerClient::closeGlobalTransaction();
+ orientation = mainDpyInfo.orientation;
+ }
+ }
// If the virtual display isn't providing us with timestamps,
- // use the current time.
+ // use the current time. This isn't great -- we could get
+ // decoded data in clusters -- but we're not expecting
+ // to hit this anyway.
if (ptsUsec == 0) {
ptsUsec = systemTime(SYSTEM_TIME_MONOTONIC) / 1000;
}
- // The MediaMuxer docs are unclear, but it appears that we
- // need to pass either the full set of BufferInfo flags, or
- // (flags & BUFFER_FLAG_SYNCFRAME).
- err = muxer->writeSampleData(buffers[bufIndex], trackIdx,
- ptsUsec, flags);
- if (err != NO_ERROR) {
- fprintf(stderr, "Failed writing data to muxer (err=%d)\n",
- err);
- return err;
+ if (muxer == NULL) {
+ fwrite(buffers[bufIndex]->data(), 1, size, rawFp);
+ // Flush the data immediately in case we're streaming.
+ // We don't want to do this if all we've written is
+ // the SPS/PPS data because mplayer gets confused.
+ if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) == 0) {
+ fflush(rawFp);
+ }
+ } else {
+ // The MediaMuxer docs are unclear, but it appears that we
+ // need to pass either the full set of BufferInfo flags, or
+ // (flags & BUFFER_FLAG_SYNCFRAME).
+ //
+ // If this blocks for too long we could drop frames. We may
+ // want to queue these up and do them on a different thread.
+ ATRACE_NAME("write sample");
+ assert(trackIdx != -1);
+ err = muxer->writeSampleData(buffers[bufIndex], trackIdx,
+ ptsUsec, flags);
+ if (err != NO_ERROR) {
+ fprintf(stderr,
+ "Failed writing data to muxer (err=%d)\n", err);
+ return err;
+ }
}
debugNumFrames++;
}
@@ -366,8 +427,8 @@
}
if ((flags & MediaCodec::BUFFER_FLAG_EOS) != 0) {
// Not expecting EOS from SurfaceFlinger. Go with it.
- ALOGD("Received end-of-stream");
- gStopRequested = false;
+ ALOGI("Received end-of-stream");
+ gStopRequested = true;
}
break;
case -EAGAIN: // INFO_TRY_AGAIN_LATER
@@ -375,21 +436,23 @@
break;
case INFO_FORMAT_CHANGED: // INFO_OUTPUT_FORMAT_CHANGED
{
- // format includes CSD, which we must provide to muxer
+ // Format includes CSD, which we must provide to muxer.
ALOGV("Encoder format changed");
sp<AMessage> newFormat;
encoder->getOutputFormat(&newFormat);
- trackIdx = muxer->addTrack(newFormat);
- ALOGV("Starting muxer");
- err = muxer->start();
- if (err != NO_ERROR) {
- fprintf(stderr, "Unable to start muxer (err=%d)\n", err);
- return err;
+ if (muxer != NULL) {
+ trackIdx = muxer->addTrack(newFormat);
+ ALOGV("Starting muxer");
+ err = muxer->start();
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Unable to start muxer (err=%d)\n", err);
+ return err;
+ }
}
}
break;
case INFO_OUTPUT_BUFFERS_CHANGED: // INFO_OUTPUT_BUFFERS_CHANGED
- // not expected for an encoder; handle it anyway
+ // Not expected for an encoder; handle it anyway.
ALOGV("Encoder buffers changed");
err = encoder->getOutputBuffers(&buffers);
if (err != NO_ERROR) {
@@ -399,7 +462,7 @@
}
break;
case INVALID_OPERATION:
- fprintf(stderr, "Request for encoder buffer failed\n");
+ ALOGW("dequeueOutputBuffer returned INVALID_OPERATION");
return err;
default:
fprintf(stderr,
@@ -411,14 +474,52 @@
ALOGV("Encoder stopping (req=%d)", gStopRequested);
if (gVerbose) {
printf("Encoder stopping; recorded %u frames in %lld seconds\n",
- debugNumFrames,
- nanoseconds_to_seconds(systemTime(CLOCK_MONOTONIC) - startWhenNsec));
+ debugNumFrames, nanoseconds_to_seconds(
+ systemTime(CLOCK_MONOTONIC) - startWhenNsec));
}
return NO_ERROR;
}
/*
- * Main "do work" method.
+ * Raw H.264 byte stream output requested. Send the output to stdout
+ * if desired. If the output is a tty, reconfigure it to avoid the
+ * CRLF line termination that we see with "adb shell" commands.
+ */
+static FILE* prepareRawOutput(const char* fileName) {
+ FILE* rawFp = NULL;
+
+ if (strcmp(fileName, "-") == 0) {
+ if (gVerbose) {
+ fprintf(stderr, "ERROR: verbose output and '-' not compatible");
+ return NULL;
+ }
+ rawFp = stdout;
+ } else {
+ rawFp = fopen(fileName, "w");
+ if (rawFp == NULL) {
+ fprintf(stderr, "fopen raw failed: %s\n", strerror(errno));
+ return NULL;
+ }
+ }
+
+ int fd = fileno(rawFp);
+ if (isatty(fd)) {
+ // best effort -- reconfigure tty for "raw"
+ ALOGD("raw video output to tty (fd=%d)", fd);
+ struct termios term;
+ if (tcgetattr(fd, &term) == 0) {
+ cfmakeraw(&term);
+ if (tcsetattr(fd, TCSANOW, &term) == 0) {
+ ALOGD("tty successfully configured for raw");
+ }
+ }
+ }
+
+ return rawFp;
+}
+
+/*
+ * Main "do work" start point.
*
* Configures codec, muxer, and virtual display, then starts moving bits
* around.
@@ -460,66 +561,158 @@
// Configure and start the encoder.
sp<MediaCodec> encoder;
- sp<IGraphicBufferProducer> bufferProducer;
- err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);
+ sp<FrameOutput> frameOutput;
+ sp<IGraphicBufferProducer> encoderInputSurface;
+ if (gOutputFormat != FORMAT_FRAMES) {
+ err = prepareEncoder(mainDpyInfo.fps, &encoder, &encoderInputSurface);
- if (err != NO_ERROR && !gSizeSpecified) {
- // fallback is defined for landscape; swap if we're in portrait
- bool needSwap = gVideoWidth < gVideoHeight;
- uint32_t newWidth = needSwap ? kFallbackHeight : kFallbackWidth;
- uint32_t newHeight = needSwap ? kFallbackWidth : kFallbackHeight;
- if (gVideoWidth != newWidth && gVideoHeight != newHeight) {
- ALOGV("Retrying with 720p");
- fprintf(stderr, "WARNING: failed at %dx%d, retrying at %dx%d\n",
- gVideoWidth, gVideoHeight, newWidth, newHeight);
- gVideoWidth = newWidth;
- gVideoHeight = newHeight;
- err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);
+ if (err != NO_ERROR && !gSizeSpecified) {
+ // fallback is defined for landscape; swap if we're in portrait
+ bool needSwap = gVideoWidth < gVideoHeight;
+ uint32_t newWidth = needSwap ? kFallbackHeight : kFallbackWidth;
+ uint32_t newHeight = needSwap ? kFallbackWidth : kFallbackHeight;
+ if (gVideoWidth != newWidth && gVideoHeight != newHeight) {
+ ALOGV("Retrying with 720p");
+ fprintf(stderr, "WARNING: failed at %dx%d, retrying at %dx%d\n",
+ gVideoWidth, gVideoHeight, newWidth, newHeight);
+ gVideoWidth = newWidth;
+ gVideoHeight = newHeight;
+ err = prepareEncoder(mainDpyInfo.fps, &encoder,
+ &encoderInputSurface);
+ }
+ }
+ if (err != NO_ERROR) return err;
+
+ // From here on, we must explicitly release() the encoder before it goes
+ // out of scope, or we will get an assertion failure from stagefright
+ // later on in a different thread.
+ } else {
+ // We're not using an encoder at all. The "encoder input surface" we hand to
+ // SurfaceFlinger will just feed directly to us.
+ frameOutput = new FrameOutput();
+ err = frameOutput->createInputSurface(gVideoWidth, gVideoHeight, &encoderInputSurface);
+ if (err != NO_ERROR) {
+ return err;
}
}
- if (err != NO_ERROR) {
- return err;
+
+ // Draw the "info" page by rendering a frame with GLES and sending
+ // it directly to the encoder.
+ // TODO: consider displaying this as a regular layer to avoid b/11697754
+ if (gWantInfoScreen) {
+ Overlay::drawInfoPage(encoderInputSurface);
+ }
+
+ // Configure optional overlay.
+ sp<IGraphicBufferProducer> bufferProducer;
+ sp<Overlay> overlay;
+ if (gWantFrameTime) {
+ // Send virtual display frames to an external texture.
+ overlay = new Overlay();
+ err = overlay->start(encoderInputSurface, &bufferProducer);
+ if (err != NO_ERROR) {
+ if (encoder != NULL) encoder->release();
+ return err;
+ }
+ if (gVerbose) {
+ printf("Bugreport overlay created\n");
+ }
+ } else {
+ // Use the encoder's input surface as the virtual display surface.
+ bufferProducer = encoderInputSurface;
}
// Configure virtual display.
sp<IBinder> dpy;
err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy);
if (err != NO_ERROR) {
- encoder->release();
- encoder.clear();
-
+ if (encoder != NULL) encoder->release();
return err;
}
- // Configure, but do not start, muxer.
- sp<MediaMuxer> muxer = new MediaMuxer(fileName,
- MediaMuxer::OUTPUT_FORMAT_MPEG_4);
- if (gRotate) {
- muxer->setOrientationHint(90);
+ sp<MediaMuxer> muxer = NULL;
+ FILE* rawFp = NULL;
+ switch (gOutputFormat) {
+ case FORMAT_MP4: {
+ // Configure muxer. We have to wait for the CSD blob from the encoder
+ // before we can start it.
+ muxer = new MediaMuxer(fileName, MediaMuxer::OUTPUT_FORMAT_MPEG_4);
+ if (gRotate) {
+ muxer->setOrientationHint(90); // TODO: does this do anything?
+ }
+ break;
+ }
+ case FORMAT_H264:
+ case FORMAT_FRAMES: {
+ rawFp = prepareRawOutput(fileName);
+ if (rawFp == NULL) {
+ if (encoder != NULL) encoder->release();
+ return -1;
+ }
+ break;
+ }
+ default:
+ fprintf(stderr, "ERROR: unknown format %d\n", gOutputFormat);
+ abort();
}
- // Main encoder loop.
- err = runEncoder(encoder, muxer);
- if (err != NO_ERROR) {
- encoder->release();
- encoder.clear();
+ if (gOutputFormat == FORMAT_FRAMES) {
+ // TODO: if we want to make this a proper feature, we should output
+ // an outer header with version info. Right now we never change
+ // the frame size or format, so we could conceivably just send
+ // the current frame header once and then follow it with an
+ // unbroken stream of data.
- return err;
- }
+ // Make the EGL context current again. This gets unhooked if we're
+ // using "--bugreport" mode.
+ // TODO: figure out if we can eliminate this
+ frameOutput->prepareToCopy();
- if (gVerbose) {
- printf("Stopping encoder and muxer\n");
+ while (!gStopRequested) {
+ // Poll for frames, the same way we do for MediaCodec. We do
+ // all of the work on the main thread.
+ //
+ // Ideally we'd sleep indefinitely and wake when the
+ // stop was requested, but this will do for now. (It almost
+ // works because wait() wakes when a signal hits, but we
+ // need to handle the edge cases.)
+ err = frameOutput->copyFrame(rawFp, 250000);
+ if (err == ETIMEDOUT) {
+ err = NO_ERROR;
+ } else if (err != NO_ERROR) {
+ ALOGE("Got error %d from copyFrame()", err);
+ break;
+ }
+ }
+ } else {
+ // Main encoder loop.
+ err = runEncoder(encoder, muxer, rawFp, mainDpy, dpy,
+ mainDpyInfo.orientation);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Encoder failed (err=%d)\n", err);
+ // fall through to cleanup
+ }
+
+ if (gVerbose) {
+ printf("Stopping encoder and muxer\n");
+ }
}
// Shut everything down, starting with the producer side.
- bufferProducer = NULL;
+ encoderInputSurface = NULL;
SurfaceComposerClient::destroyDisplay(dpy);
+ if (overlay != NULL) overlay->stop();
+ if (encoder != NULL) encoder->stop();
+ if (muxer != NULL) {
+ // If we don't stop muxer explicitly, i.e. let the destructor run,
+ // it may hang (b/11050628).
+ muxer->stop();
+ } else if (rawFp != stdout) {
+ fclose(rawFp);
+ }
+ if (encoder != NULL) encoder->release();
- encoder->stop();
- muxer->stop();
- encoder->release();
-
- return 0;
+ return err;
}
/*
@@ -528,6 +721,28 @@
* This is optional, but nice to have.
*/
static status_t notifyMediaScanner(const char* fileName) {
+ // need to do allocations before the fork()
+ String8 fileUrl("file://");
+ fileUrl.append(fileName);
+
+ const char* kCommand = "/system/bin/am";
+ const char* const argv[] = {
+ kCommand,
+ "broadcast",
+ "-a",
+ "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
+ "-d",
+ fileUrl.string(),
+ NULL
+ };
+ if (gVerbose) {
+ printf("Executing:");
+ for (int i = 0; argv[i] != NULL; i++) {
+ printf(" %s", argv[i]);
+ }
+ putchar('\n');
+ }
+
pid_t pid = fork();
if (pid < 0) {
int err = errno;
@@ -539,34 +754,14 @@
int status;
pid_t actualPid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (actualPid != pid) {
- ALOGW("waitpid() returned %d (errno=%d)", actualPid, errno);
+ ALOGW("waitpid(%d) returned %d (errno=%d)", pid, actualPid, errno);
} else if (status != 0) {
ALOGW("'am broadcast' exited with status=%d", status);
} else {
ALOGV("'am broadcast' exited successfully");
}
} else {
- const char* kCommand = "/system/bin/am";
-
- // child; we're single-threaded, so okay to alloc
- String8 fileUrl("file://");
- fileUrl.append(fileName);
- const char* const argv[] = {
- kCommand,
- "broadcast",
- "-a",
- "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
- "-d",
- fileUrl.string(),
- NULL
- };
- if (gVerbose) {
- printf("Executing:");
- for (int i = 0; argv[i] != NULL; i++) {
- printf(" %s", argv[i]);
- }
- putchar('\n');
- } else {
+ if (!gVerbose) {
// non-verbose, suppress 'am' output
ALOGV("closing stdout/stderr in child");
int fd = open("/dev/null", O_WRONLY);
@@ -611,13 +806,37 @@
}
/*
+ * Accepts a string with a bare number ("4000000") or with a single-character
+ * unit ("4m").
+ *
+ * Returns an error if parsing fails.
+ */
+static status_t parseValueWithUnit(const char* str, uint32_t* pValue) {
+ long value;
+ char* endptr;
+
+ value = strtol(str, &endptr, 10);
+ if (*endptr == '\0') {
+ // bare number
+ *pValue = value;
+ return NO_ERROR;
+ } else if (toupper(*endptr) == 'M' && *(endptr+1) == '\0') {
+ *pValue = value * 1000000; // check for overflow?
+ return NO_ERROR;
+ } else {
+ fprintf(stderr, "Unrecognized value: %s\n", str);
+ return UNKNOWN_ERROR;
+ }
+}
+
+/*
* Dumps usage on stderr.
*/
static void usage() {
fprintf(stderr,
"Usage: screenrecord [options] <filename>\n"
"\n"
- "Records the device's display to a .mp4 file.\n"
+ "Android screenrecord v%d.%d. Records the device's display to a .mp4 file.\n"
"\n"
"Options:\n"
"--size WIDTHxHEIGHT\n"
@@ -625,11 +844,13 @@
" display resolution (if supported), 1280x720 if not. For best results,\n"
" use a size supported by the AVC encoder.\n"
"--bit-rate RATE\n"
- " Set the video bit rate, in megabits per second. Default %dMbps.\n"
+ " Set the video bit rate, in bits per second. Value may be specified as\n"
+ " bits or megabits, e.g. '4000000' is equivalent to '4M'. Default %dMbps.\n"
+ "--bugreport\n"
+ " Add additional information, such as a timestamp overlay, that is helpful\n"
+ " in videos captured to illustrate bugs.\n"
"--time-limit TIME\n"
" Set the maximum recording time, in seconds. Default / maximum is %d.\n"
- "--rotate\n"
- " Rotate the output 90 degrees.\n"
"--verbose\n"
" Display interesting information on stdout.\n"
"--help\n"
@@ -637,7 +858,7 @@
"\n"
"Recording continues until Ctrl-C is hit or the time limit is reached.\n"
"\n",
- gBitRate / 1000000, gTimeLimitSec
+ kVersionMajor, kVersionMinor, gBitRate / 1000000, gTimeLimitSec
);
}
@@ -646,13 +867,18 @@
*/
int main(int argc, char* const argv[]) {
static const struct option longOptions[] = {
- { "help", no_argument, NULL, 'h' },
- { "verbose", no_argument, NULL, 'v' },
- { "size", required_argument, NULL, 's' },
- { "bit-rate", required_argument, NULL, 'b' },
- { "time-limit", required_argument, NULL, 't' },
- { "rotate", no_argument, NULL, 'r' },
- { NULL, 0, NULL, 0 }
+ { "help", no_argument, NULL, 'h' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "size", required_argument, NULL, 's' },
+ { "bit-rate", required_argument, NULL, 'b' },
+ { "time-limit", required_argument, NULL, 't' },
+ { "bugreport", no_argument, NULL, 'u' },
+ // "unofficial" options
+ { "show-device-info", no_argument, NULL, 'i' },
+ { "show-frame-time", no_argument, NULL, 'f' },
+ { "rotate", no_argument, NULL, 'r' },
+ { "output-format", required_argument, NULL, 'o' },
+ { NULL, 0, NULL, 0 }
};
while (true) {
@@ -684,7 +910,9 @@
gSizeSpecified = true;
break;
case 'b':
- gBitRate = atoi(optarg);
+ if (parseValueWithUnit(optarg, &gBitRate) != NO_ERROR) {
+ return 2;
+ }
if (gBitRate < kMinBitRate || gBitRate > kMaxBitRate) {
fprintf(stderr,
"Bit rate %dbps outside acceptable range [%d,%d]\n",
@@ -701,9 +929,32 @@
return 2;
}
break;
+ case 'u':
+ gWantInfoScreen = true;
+ gWantFrameTime = true;
+ break;
+ case 'i':
+ gWantInfoScreen = true;
+ break;
+ case 'f':
+ gWantFrameTime = true;
+ break;
case 'r':
+ // experimental feature
gRotate = true;
break;
+ case 'o':
+ if (strcmp(optarg, "mp4") == 0) {
+ gOutputFormat = FORMAT_MP4;
+ } else if (strcmp(optarg, "h264") == 0) {
+ gOutputFormat = FORMAT_H264;
+ } else if (strcmp(optarg, "frames") == 0) {
+ gOutputFormat = FORMAT_FRAMES;
+ } else {
+ fprintf(stderr, "Unknown format '%s'\n", optarg);
+ return 2;
+ }
+ break;
default:
if (ic != '?') {
fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);
@@ -717,17 +968,19 @@
return 2;
}
- // MediaMuxer tries to create the file in the constructor, but we don't
- // learn about the failure until muxer.start(), which returns a generic
- // error code without logging anything. We attempt to create the file
- // now for better diagnostics.
const char* fileName = argv[optind];
- int fd = open(fileName, O_CREAT | O_RDWR, 0644);
- if (fd < 0) {
- fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno));
- return 1;
+ if (gOutputFormat == FORMAT_MP4) {
+ // MediaMuxer tries to create the file in the constructor, but we don't
+ // learn about the failure until muxer.start(), which returns a generic
+ // error code without logging anything. We attempt to create the file
+ // now for better diagnostics.
+ int fd = open(fileName, O_CREAT | O_RDWR, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno));
+ return 1;
+ }
+ close(fd);
}
- close(fd);
status_t err = recordScreen(fileName);
if (err == NO_ERROR) {
diff --git a/cmds/screenrecord/screenrecord.h b/cmds/screenrecord/screenrecord.h
new file mode 100644
index 0000000..9b058c2
--- /dev/null
+++ b/cmds/screenrecord/screenrecord.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 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 SCREENRECORD_SCREENRECORD_H
+#define SCREENRECORD_SCREENRECORD_H
+
+#define kVersionMajor 1
+#define kVersionMinor 2
+
+#endif /*SCREENRECORD_SCREENRECORD_H*/
diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp
index 5d2d721..1b2f792 100644
--- a/cmds/stagefright/SimplePlayer.cpp
+++ b/cmds/stagefright/SimplePlayer.cpp
@@ -23,6 +23,7 @@
#include <gui/Surface.h>
#include <media/AudioTrack.h>
#include <media/ICrypto.h>
+#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -275,7 +276,8 @@
mExtractor = new NuMediaExtractor;
- status_t err = mExtractor->setDataSource(mPath.c_str());
+ status_t err = mExtractor->setDataSource(
+ NULL /* httpService */, mPath.c_str());
if (err != OK) {
mExtractor.clear();
diff --git a/cmds/stagefright/SineSource.cpp b/cmds/stagefright/SineSource.cpp
index 14b4306..587077a 100644
--- a/cmds/stagefright/SineSource.cpp
+++ b/cmds/stagefright/SineSource.cpp
@@ -24,7 +24,7 @@
}
}
-status_t SineSource::start(MetaData *params) {
+status_t SineSource::start(MetaData * /* params */) {
CHECK(!mStarted);
mGroup = new MediaBufferGroup;
@@ -58,7 +58,7 @@
}
status_t SineSource::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBuffer **out, const ReadOptions * /* options */) {
*out = NULL;
MediaBuffer *buffer;
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index fdfefdf..fd02bcc 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -16,6 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "codec"
+#include <inttypes.h>
#include <utils/Log.h>
#include "SimplePlayer.h"
@@ -23,6 +24,7 @@
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <media/ICrypto.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -75,7 +77,7 @@
static int64_t kTimeout = 500ll;
sp<NuMediaExtractor> extractor = new NuMediaExtractor;
- if (extractor->setDataSource(path) != OK) {
+ if (extractor->setDataSource(NULL /* httpService */, path) != OK) {
fprintf(stderr, "unable to instantiate extractor.\n");
return 1;
}
@@ -291,13 +293,13 @@
CHECK_EQ((status_t)OK, state->mCodec->release());
if (state->mIsAudio) {
- printf("track %d: %lld bytes received. %.2f KB/sec\n",
+ printf("track %zu: %" PRId64 " bytes received. %.2f KB/sec\n",
i,
state->mNumBytesDecoded,
state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs);
} else {
- printf("track %d: %lld frames decoded, %.2f fps. %lld bytes "
- "received. %.2f KB/sec\n",
+ printf("track %zu: %" PRId64 " frames decoded, %.2f fps. %" PRId64
+ " bytes received. %.2f KB/sec\n",
i,
state->mNumBuffersDecoded,
state->mNumBuffersDecoded * 1E6 / elapsedTimeUs,
diff --git a/cmds/stagefright/muxer.cpp b/cmds/stagefright/muxer.cpp
index cca33e0..f4a33e8 100644
--- a/cmds/stagefright/muxer.cpp
+++ b/cmds/stagefright/muxer.cpp
@@ -16,9 +16,11 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "muxer"
+#include <inttypes.h>
#include <utils/Log.h>
#include <binder/ProcessState.h>
+#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
@@ -58,7 +60,7 @@
int trimEndTimeMs,
int rotationDegrees) {
sp<NuMediaExtractor> extractor = new NuMediaExtractor;
- if (extractor->setDataSource(path) != OK) {
+ if (extractor->setDataSource(NULL /* httpService */, path) != OK) {
fprintf(stderr, "unable to instantiate extractor. %s\n", path);
return 1;
}
@@ -198,7 +200,7 @@
trackIndexMap.clear();
int64_t elapsedTimeUs = ALooper::GetNowUs() - muxerStartTimeUs;
- fprintf(stderr, "SUCCESS: muxer generate the video in %lld ms\n",
+ fprintf(stderr, "SUCCESS: muxer generate the video in %" PRId64 " ms\n",
elapsedTimeUs / 1000);
return 0;
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index b7a40c2..fdc352e 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -296,7 +296,7 @@
}
#else
-int main(int argc, char **argv) {
+int main(int /* argc */, char ** /* argv */) {
android::ProcessState::self()->startThreadPool();
OMXClient client;
diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp
index c30c122..1d267f9 100644
--- a/cmds/stagefright/recordvideo.cpp
+++ b/cmds/stagefright/recordvideo.cpp
@@ -16,6 +16,7 @@
#include "SineSource.h"
+#include <inttypes.h>
#include <binder/ProcessState.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/AudioPlayer.h>
@@ -312,7 +313,7 @@
fprintf(stderr, "record failed: %d\n", err);
return 1;
}
- fprintf(stderr, "encoding %d frames in %lld us\n", nFrames, (end-start)/1000);
+ fprintf(stderr, "encoding %d frames in %" PRId64 " us\n", nFrames, (end-start)/1000);
fprintf(stderr, "encoding speed is: %.2f fps\n", (nFrames * 1E9) / (end-start));
return 0;
}
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index c817443..3c0c7ec 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -16,10 +16,15 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "sf2"
+#include <inttypes.h>
#include <utils/Log.h>
+#include <signal.h>
+
#include <binder/ProcessState.h>
+#include <media/IMediaHTTPService.h>
+
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -42,6 +47,18 @@
using namespace android;
+volatile static bool ctrlc = false;
+
+static sighandler_t oldhandler = NULL;
+
+static void mysighandler(int signum) {
+ if (signum == SIGINT) {
+ ctrlc = true;
+ return;
+ }
+ oldhandler(signum);
+}
+
struct Controller : public AHandler {
Controller(const char *uri, bool decodeAudio,
const sp<Surface> &surface, bool renderToSurface)
@@ -62,7 +79,30 @@
virtual ~Controller() {
}
+ virtual void printStatistics() {
+ int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
+
+ if (mDecodeAudio) {
+ printf("%" PRId64 " bytes received. %.2f KB/sec\n",
+ mTotalBytesReceived,
+ mTotalBytesReceived * 1E6 / 1024 / delayUs);
+ } else {
+ printf("%d frames decoded, %.2f fps. %" PRId64 " bytes "
+ "received. %.2f KB/sec\n",
+ mNumOutputBuffersReceived,
+ mNumOutputBuffersReceived * 1E6 / delayUs,
+ mTotalBytesReceived,
+ mTotalBytesReceived * 1E6 / 1024 / delayUs);
+ }
+ }
+
virtual void onMessageReceived(const sp<AMessage> &msg) {
+ if (ctrlc) {
+ printf("\n");
+ printStatistics();
+ (new AMessage(kWhatStop, id()))->post();
+ ctrlc = false;
+ }
switch (msg->what()) {
case kWhatStart:
{
@@ -75,7 +115,8 @@
#endif
sp<DataSource> dataSource =
- DataSource::CreateFromURI(mURI.c_str());
+ DataSource::CreateFromURI(
+ NULL /* httpService */, mURI.c_str());
sp<MediaExtractor> extractor =
MediaExtractor::Create(dataSource);
@@ -98,7 +139,10 @@
break;
}
}
- CHECK(mSource != NULL);
+ if (mSource == NULL) {
+ printf("no %s track found\n", mDecodeAudio ? "audio" : "video");
+ exit (1);
+ }
CHECK_EQ(mSource->start(), (status_t)OK);
@@ -180,21 +224,7 @@
|| what == ACodec::kWhatError) {
printf((what == ACodec::kWhatEOS) ? "$\n" : "E\n");
- int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
-
- if (mDecodeAudio) {
- printf("%lld bytes received. %.2f KB/sec\n",
- mTotalBytesReceived,
- mTotalBytesReceived * 1E6 / 1024 / delayUs);
- } else {
- printf("%d frames decoded, %.2f fps. %lld bytes "
- "received. %.2f KB/sec\n",
- mNumOutputBuffersReceived,
- mNumOutputBuffersReceived * 1E6 / delayUs,
- mTotalBytesReceived,
- mTotalBytesReceived * 1E6 / 1024 / delayUs);
- }
-
+ printStatistics();
(new AMessage(kWhatStop, id()))->post();
} else if (what == ACodec::kWhatFlushCompleted) {
mSeekState = SEEK_FLUSH_COMPLETED;
@@ -638,6 +668,8 @@
looper->registerHandler(controller);
+ signal(SIGINT, mysighandler);
+
controller->startAsync();
CHECK_EQ(looper->start(true /* runOnCallingThread */), (status_t)OK);
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 030bf1b..daaea27 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -22,12 +22,14 @@
#include <stdlib.h>
#include <string.h>
+#include <inttypes.h>
#include "jpeg.h"
#include "SineSource.h"
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ALooper.h>
#include "include/NuCachedSource2.h"
@@ -89,8 +91,8 @@
int64_t minUs = decodeTimesUs->itemAt(0);
int64_t maxUs = decodeTimesUs->itemAt(n - 1);
- printf("min decode time %lld us (%.2f secs)\n", minUs, minUs / 1E6);
- printf("max decode time %lld us (%.2f secs)\n", maxUs, maxUs / 1E6);
+ printf("min decode time %" PRId64 " us (%.2f secs)\n", minUs, minUs / 1E6);
+ printf("max decode time %" PRId64 " us (%.2f secs)\n", maxUs, maxUs / 1E6);
size_t counts[100];
for (size_t i = 0; i < 100; ++i) {
@@ -110,7 +112,7 @@
int64_t slotUs = minUs + (i * (maxUs - minUs) / 100);
double fps = 1E6 / slotUs;
- printf("[%.2f fps]: %d\n", fps, counts[i]);
+ printf("[%.2f fps]: %zu\n", fps, counts[i]);
}
}
@@ -262,7 +264,7 @@
}
}
- printf("buffer has timestamp %lld us (%.2f secs)\n",
+ printf("buffer has timestamp %" PRId64 " us (%.2f secs)\n",
timestampUs, timestampUs / 1E6);
buffer->release();
@@ -285,7 +287,7 @@
seekTimeUs = (rand() * (float)durationUs) / RAND_MAX;
options.setSeekTo(seekTimeUs);
- printf("seeking to %lld us (%.2f secs)\n",
+ printf("seeking to %" PRId64 " us (%.2f secs)\n",
seekTimeUs, seekTimeUs / 1E6);
}
}
@@ -388,7 +390,7 @@
// sizes may be different across decoders.
printf("avg. %.2f KB/sec\n", totalBytes / 1024 * 1E6 / delay);
- printf("decoded a total of %lld bytes\n", totalBytes);
+ printf("decoded a total of %" PRId64 " bytes\n", totalBytes);
}
}
@@ -574,7 +576,8 @@
int64_t timeUs;
CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
- printf("%lld\t%lld\t%lld\n", seekTimeUs, timeUs, seekTimeUs - timeUs);
+ printf("%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
+ seekTimeUs, timeUs, seekTimeUs - timeUs);
buffer->release();
buffer = NULL;
@@ -958,7 +961,8 @@
const char *filename = argv[k];
- sp<DataSource> dataSource = DataSource::CreateFromURI(filename);
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(NULL /* httpService */, filename);
if (strncasecmp(filename, "sine:", 5) && dataSource == NULL) {
fprintf(stderr, "Unable to create data source.\n");
@@ -1071,7 +1075,7 @@
int64_t thumbTimeUs;
if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
- printf("thumbnailTime: %lld us (%.2f secs)\n",
+ printf("thumbnailTime: %" PRId64 " us (%.2f secs)\n",
thumbTimeUs, thumbTimeUs / 1E6);
}
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index dba67a9..b2abc0f 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -21,6 +21,7 @@
#include <binder/ProcessState.h>
#include <cutils/properties.h> // for property_get
+#include <media/IMediaHTTPService.h>
#include <media/IStreamSource.h>
#include <media/mediaplayer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -159,7 +160,9 @@
MyConvertingStreamSource::MyConvertingStreamSource(const char *filename)
: mCurrentBufferIndex(-1),
mCurrentBufferOffset(0) {
- sp<DataSource> dataSource = DataSource::CreateFromURI(filename);
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(NULL /* httpService */, filename);
+
CHECK(dataSource != NULL);
sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
index e251f82..48b0afe 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
@@ -61,7 +61,7 @@
$(LOCAL_PATH)/include \
external/openssl/include
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm
+LOCAL_MODULE_RELATIVE_PATH := drm
LOCAL_MODULE_TAGS := optional
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
index 234aef2..f400732 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -316,6 +316,7 @@
if (-1 < fileDesc) {
if (FwdLockFile_attach(fileDesc) < 0) {
+ close(fileDesc);
return mimeString;
}
const char* pMimeType = FwdLockFile_GetContentType(fileDesc);
diff --git a/drm/mediadrm/plugins/mock/Android.mk b/drm/mediadrm/plugins/mock/Android.mk
index ada23a2..26c245b 100644
--- a/drm/mediadrm/plugins/mock/Android.mk
+++ b/drm/mediadrm/plugins/mock/Android.mk
@@ -21,7 +21,8 @@
LOCAL_MODULE := libmockdrmcryptoplugin
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/mediadrm
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := mediadrm
LOCAL_SHARED_LIBRARIES := \
libutils liblog
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index f2cadf7..69fa7a0fc 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -695,7 +695,7 @@
{
String8 result;
for (size_t i = 0; i < numSubSamples; i++) {
- result.appendFormat("[%d] {clear:%d, encrypted:%d} ", i,
+ result.appendFormat("[%zu] {clear:%zu, encrypted:%zu} ", i,
subSamples[i].mNumBytesOfClearData,
subSamples[i].mNumBytesOfEncryptedData);
}
diff --git a/include/camera/CameraMetadata.h b/include/camera/CameraMetadata.h
index fe2bd19..1254d3c 100644
--- a/include/camera/CameraMetadata.h
+++ b/include/camera/CameraMetadata.h
@@ -99,6 +99,11 @@
status_t append(const CameraMetadata &other);
/**
+ * Append metadata from a raw camera_metadata buffer
+ */
+ status_t append(const camera_metadata* other);
+
+ /**
* Number of metadata entries.
*/
size_t entryCount() const;
diff --git a/include/camera/IProCameraCallbacks.h b/include/camera/IProCameraCallbacks.h
index c774698..e8abb89 100644
--- a/include/camera/IProCameraCallbacks.h
+++ b/include/camera/IProCameraCallbacks.h
@@ -51,7 +51,7 @@
/** Missing by design: implementation is client-side in ProCamera.cpp **/
// virtual void onBufferReceived(int streamId,
// const CpuConsumer::LockedBufer& buf);
- virtual void onResultReceived(int32_t frameId,
+ virtual void onResultReceived(int32_t requestId,
camera_metadata* result) = 0;
};
diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h
index d9ee662..83a3028 100644
--- a/include/camera/ProCamera.h
+++ b/include/camera/ProCamera.h
@@ -252,7 +252,7 @@
virtual void onLockStatusChanged(
IProCameraCallbacks::LockStatus newLockStatus);
- virtual void onResultReceived(int32_t frameId,
+ virtual void onResultReceived(int32_t requestId,
camera_metadata* result);
private:
ProCamera(int cameraId);
diff --git a/include/camera/camera2/ICameraDeviceCallbacks.h b/include/camera/camera2/ICameraDeviceCallbacks.h
index 041fa65..8dac4f2 100644
--- a/include/camera/camera2/ICameraDeviceCallbacks.h
+++ b/include/camera/camera2/ICameraDeviceCallbacks.h
@@ -35,13 +35,27 @@
public:
DECLARE_META_INTERFACE(CameraDeviceCallbacks);
- // One way
- virtual void notifyCallback(int32_t msgType,
- int32_t ext1,
- int32_t ext2) = 0;
+ /**
+ * Error codes for CAMERA_MSG_ERROR
+ */
+ enum CameraErrorCode {
+ ERROR_CAMERA_DISCONNECTED = 0,
+ ERROR_CAMERA_DEVICE = 1,
+ ERROR_CAMERA_SERVICE = 2
+ };
// One way
- virtual void onResultReceived(int32_t frameId,
+ virtual void onDeviceError(CameraErrorCode errorCode) = 0;
+
+ // One way
+ virtual void onDeviceIdle() = 0;
+
+ // One way
+ virtual void onCaptureStarted(int32_t requestId,
+ int64_t timestamp) = 0;
+
+ // One way
+ virtual void onResultReceived(int32_t requestId,
const CameraMetadata& result) = 0;
};
diff --git a/include/media/AudioBufferProvider.h b/include/media/AudioBufferProvider.h
index ef392f0..7be449c 100644
--- a/include/media/AudioBufferProvider.h
+++ b/include/media/AudioBufferProvider.h
@@ -61,6 +61,17 @@
// buffer->frameCount 0
virtual status_t getNextBuffer(Buffer* buffer, int64_t pts = kInvalidPTS) = 0;
+ // Release (a portion of) the buffer previously obtained by getNextBuffer().
+ // It is permissible to call releaseBuffer() multiple times per getNextBuffer().
+ // On entry:
+ // buffer->frameCount number of frames to release, must be <= number of frames
+ // obtained but not yet released
+ // buffer->raw unused
+ // On return:
+ // buffer->frameCount 0; implementation MUST set to zero
+ // buffer->raw undefined; implementation is PERMITTED to set to any value,
+ // so if caller needs to continue using this buffer it must
+ // keep track of the pointer itself
virtual void releaseBuffer(Buffer* buffer) = 0;
};
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 05d834d..f3024b7 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -36,7 +36,7 @@
// ----------------------------------------------------------------------------
-class effect_param_cblk_t;
+struct effect_param_cblk_t;
// ----------------------------------------------------------------------------
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index fb47448..b3c44a8 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -26,7 +26,7 @@
// ----------------------------------------------------------------------------
-class audio_track_cblk_t;
+struct audio_track_cblk_t;
class AudioRecordClientProxy;
// ----------------------------------------------------------------------------
@@ -39,8 +39,12 @@
* Keep in sync with frameworks/base/media/java/android/media/AudioRecord.java NATIVE_EVENT_*.
*/
enum event_type {
- EVENT_MORE_DATA = 0, // Request to read more data from PCM buffer.
- EVENT_OVERRUN = 1, // PCM buffer overrun occurred.
+ EVENT_MORE_DATA = 0, // Request to read available data from buffer.
+ // If this event is delivered but the callback handler
+ // does not want to read the available data, the handler must
+ // explicitly
+ // ignore the event by setting frameCount to zero.
+ EVENT_OVERRUN = 1, // Buffer overrun occurred.
EVENT_MARKER = 2, // Record head is at the specified marker position
// (See setMarkerPosition()).
EVENT_NEW_POS = 3, // Record head is at a new position
@@ -63,6 +67,7 @@
// (currently ignored but will make the primary field in future)
size_t size; // input/output in bytes == frameCount * frameSize
+ // on output is the number of bytes actually drained
// FIXME this is redundant with respect to frameCount,
// and TRANSFER_OBTAIN mode is broken for 8-bit data
// since we don't define the frame format
@@ -76,7 +81,7 @@
/* As a convenience, if a callback is supplied, a handler thread
* is automatically created with the appropriate priority. This thread
- * invokes the callback when a new buffer becomes ready or various conditions occur.
+ * invokes the callback when a new buffer becomes available or various conditions occur.
* Parameters:
*
* event: type of event notified (see enum AudioRecord::event_type).
@@ -99,6 +104,8 @@
* - NO_ERROR: successful operation
* - NO_INIT: audio server or audio hardware not initialized
* - BAD_VALUE: unsupported configuration
+ * frameCount is guaranteed to be non-zero if status is NO_ERROR,
+ * and is undefined otherwise.
*/
static status_t getMinFrameCount(size_t* frameCount,
@@ -109,7 +116,7 @@
/* How data is transferred from AudioRecord
*/
enum transfer_type {
- TRANSFER_DEFAULT, // not specified explicitly; determine from other parameters
+ TRANSFER_DEFAULT, // not specified explicitly; determine from the other parameters
TRANSFER_CALLBACK, // callback EVENT_MORE_DATA
TRANSFER_OBTAIN, // FIXME deprecated: call obtainBuffer() and releaseBuffer()
TRANSFER_SYNC, // synchronous read()
@@ -137,7 +144,7 @@
* be larger if the requested size is not compatible with current audio HAL
* latency. Zero means to use a default value.
* cbf: Callback function. If not null, this function is called periodically
- * to consume new PCM data and inform of marker, position updates, etc.
+ * to consume new data and inform of marker, position updates, etc.
* user: Context for use by the callback receiver.
* notificationFrames: The callback function is called each time notificationFrames PCM
* frames are ready in record track output buffer.
@@ -151,11 +158,11 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount = 0,
+ size_t frameCount = 0,
callback_t cbf = NULL,
void* user = NULL,
- int notificationFrames = 0,
- int sessionId = 0,
+ uint32_t notificationFrames = 0,
+ int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE);
@@ -171,9 +178,10 @@
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful intialization
* - INVALID_OPERATION: AudioRecord is already initialized or record device is already in use
- * - BAD_VALUE: invalid parameter (channels, format, sampleRate...)
+ * - BAD_VALUE: invalid parameter (channelMask, format, sampleRate...)
* - NO_INIT: audio server or audio hardware not initialized
* - PERMISSION_DENIED: recording is not allowed for the requesting process
+ * If status is not equal to NO_ERROR, don't call any other APIs on this AudioRecord.
*
* Parameters not listed in the AudioRecord constructors above:
*
@@ -183,16 +191,16 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount = 0,
+ size_t frameCount = 0,
callback_t cbf = NULL,
void* user = NULL,
- int notificationFrames = 0,
+ uint32_t notificationFrames = 0,
bool threadCanCallJava = false,
- int sessionId = 0,
+ int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE);
- /* Result of constructing the AudioRecord. This must be checked
+ /* Result of constructing the AudioRecord. This must be checked for successful initialization
* before using any AudioRecord API (except for set()), because using
* an uninitialized AudioRecord produces undefined results.
* See set() method above for possible return codes.
@@ -221,7 +229,7 @@
status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
int triggerSession = 0);
- /* Stop a track. If set, the callback will cease being called. Note that obtainBuffer() still
+ /* Stop a track. The callback will cease being called. Note that obtainBuffer() still
* works and will drain buffers until the pool is exhausted, and then will return WOULD_BLOCK.
*/
void stop();
@@ -236,7 +244,7 @@
* a callback with event type EVENT_MARKER is called. Calling setMarkerPosition
* with marker == 0 cancels marker notification callback.
* To set a marker at a position which would compute as 0,
- * a workaround is to the set the marker at a nearby position such as ~0 or 1.
+ * a workaround is to set the marker at a nearby position such as ~0 or 1.
* If the AudioRecord has been opened with no callback function associated,
* the operation will fail.
*
@@ -378,8 +386,10 @@
* returning the current value by this function call. Such loss typically occurs when the
* user space process is blocked longer than the capacity of audio driver buffers.
* Units: the number of input audio frames.
+ * FIXME The side-effect of resetting the counter may be incompatible with multi-client.
+ * Consider making it more like AudioTrack::getUnderrunFrames which doesn't have side effects.
*/
- unsigned int getInputFramesLost() const;
+ uint32_t getInputFramesLost() const;
private:
/* copying audio record objects is not allowed */
@@ -412,6 +422,7 @@
bool mPaused; // whether thread is requested to pause at next loop entry
bool mPausedInt; // whether thread internally requests pause
nsecs_t mPausedNs; // if mPausedInt then associated timeout, otherwise ignored
+ bool mIgnoreNextPausedInt; // whether to ignore next mPausedInt request
};
// body of AudioRecordThread::threadLoop()
@@ -422,9 +433,10 @@
// NS_INACTIVE inactive so don't run again until re-started
// NS_NEVER never again
static const nsecs_t NS_WHENEVER = -1, NS_INACTIVE = -2, NS_NEVER = -3;
- nsecs_t processAudioBuffer(const sp<AudioRecordThread>& thread);
+ nsecs_t processAudioBuffer();
// caller must hold lock on mLock for all _l methods
+
status_t openRecord_l(size_t epoch);
// FIXME enum is faster than strcmp() for parameter 'from'
@@ -452,7 +464,7 @@
// These are private to processAudioBuffer(), and are not protected by a lock
uint32_t mRemainingFrames; // number of frames to request in obtainBuffer()
bool mRetryOnPartialBuffer; // sleep and retry after partial obtainBuffer()
- int mObservedSequence; // last observed value of mSequence
+ uint32_t mObservedSequence; // last observed value of mSequence
uint32_t mMarkerPosition; // in wrapping (overflow) frame units
bool mMarkerReached;
@@ -461,9 +473,13 @@
status_t mStatus;
+ size_t mFrameCount; // corresponds to current IAudioRecord, value is
+ // reported back by AudioFlinger to the client
+ size_t mReqFrameCount; // frame count to request the first or next time
+ // a new IAudioRecord is needed, non-decreasing
+
// constant after constructor or set()
uint32_t mSampleRate;
- size_t mFrameCount;
audio_format_t mFormat;
uint32_t mChannelCount;
size_t mFrameSize; // app-level frame size == AudioFlinger frame size
@@ -474,12 +490,11 @@
int mSessionId;
transfer_type mTransfer;
- audio_io_handle_t mInput; // returned by AudioSystem::getInput()
-
- // may be changed if IAudioRecord object is re-created
+ // Next 4 fields may be changed if IAudioRecord is re-created, but always != 0
sp<IAudioRecord> mAudioRecord;
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk; // re-load after mLock.unlock()
+ audio_io_handle_t mInput; // returned by AudioSystem::getInput()
int mPreviousPriority; // before start()
SchedPolicy mPreviousSchedulingGroup;
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index b96b8a1..28fdfd4 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -67,20 +67,24 @@
// returns true in *state if tracks are active on the specified stream or have been active
// in the past inPastMs milliseconds
- static status_t isStreamActive(audio_stream_type_t stream, bool *state, uint32_t inPastMs = 0);
+ static status_t isStreamActive(audio_stream_type_t stream, bool *state, uint32_t inPastMs);
// returns true in *state if tracks are active for what qualifies as remote playback
// on the specified stream or have been active in the past inPastMs milliseconds. Remote
// playback isn't mutually exclusive with local playback.
static status_t isStreamActiveRemotely(audio_stream_type_t stream, bool *state,
- uint32_t inPastMs = 0);
+ uint32_t inPastMs);
// returns true in *state if a recorder is currently recording with the specified source
static status_t isSourceActive(audio_source_t source, bool *state);
// set/get audio hardware parameters. The function accepts a list of parameters
// key value pairs in the form: key1=value1;key2=value2;...
// Some keys are reserved for standard parameters (See AudioParameter class).
+ // The versions with audio_io_handle_t are intended for internal media framework use only.
static status_t setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs);
static String8 getParameters(audio_io_handle_t ioHandle, const String8& keys);
+ // The versions without audio_io_handle_t are intended for JNI.
+ static status_t setParameters(const String8& keyValuePairs);
+ static String8 getParameters(const String8& keys);
static void setErrorCallback(audio_error_callback cb);
@@ -90,12 +94,14 @@
static float linearToLog(int volume);
static int logToLinear(float volume);
+ // Returned samplingRate and frameCount output values are guaranteed
+ // to be non-zero if status == NO_ERROR
static status_t getOutputSamplingRate(uint32_t* samplingRate,
- audio_stream_type_t stream = AUDIO_STREAM_DEFAULT);
+ audio_stream_type_t stream);
static status_t getOutputFrameCount(size_t* frameCount,
- audio_stream_type_t stream = AUDIO_STREAM_DEFAULT);
+ audio_stream_type_t stream);
static status_t getOutputLatency(uint32_t* latency,
- audio_stream_type_t stream = AUDIO_STREAM_DEFAULT);
+ audio_stream_type_t stream);
static status_t getSamplingRate(audio_io_handle_t output,
audio_stream_type_t streamType,
uint32_t* samplingRate);
@@ -112,6 +118,7 @@
static bool routedToA2dpOutput(audio_stream_type_t streamType);
+ // return status NO_ERROR implies *buffSize > 0
static status_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask, size_t* buffSize);
@@ -127,16 +134,16 @@
// 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(audio_io_handle_t output,
- size_t *halFrames,
- size_t *dspFrames,
+ uint32_t *halFrames,
+ uint32_t *dspFrames,
audio_stream_type_t stream = AUDIO_STREAM_DEFAULT);
// return the number of input frames lost by HAL implementation, or 0 if the handle is invalid
- static size_t getInputFramesLost(audio_io_handle_t ioHandle);
+ static uint32_t getInputFramesLost(audio_io_handle_t ioHandle);
static int newAudioSessionId();
- static void acquireAudioSessionId(int audioSession);
- static void releaseAudioSessionId(int audioSession);
+ static void acquireAudioSessionId(int audioSession, pid_t pid);
+ static void releaseAudioSessionId(int audioSession, pid_t pid);
// types of io configuration change events received with ioConfigChanged()
enum io_config_event {
@@ -194,24 +201,32 @@
static status_t setPhoneState(audio_mode_t state);
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);
+
+ // Client must successfully hand off the handle reference to AudioFlinger via createTrack(),
+ // or release it with releaseOutput().
static audio_io_handle_t getOutput(audio_stream_type_t stream,
uint32_t samplingRate = 0,
audio_format_t format = AUDIO_FORMAT_DEFAULT,
audio_channel_mask_t channelMask = AUDIO_CHANNEL_OUT_STEREO,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t *offloadInfo = NULL);
+
static status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
- int session = 0);
+ int session);
static status_t stopOutput(audio_io_handle_t output,
audio_stream_type_t stream,
- int session = 0);
+ int session);
static void releaseOutput(audio_io_handle_t output);
+
+ // Client must successfully hand off the handle reference to AudioFlinger via openRecord(),
+ // or release it with releaseInput().
static audio_io_handle_t getInput(audio_source_t inputSource,
- uint32_t samplingRate = 0,
- audio_format_t format = AUDIO_FORMAT_DEFAULT,
- audio_channel_mask_t channelMask = AUDIO_CHANNEL_IN_MONO,
- int sessionId = 0);
+ uint32_t samplingRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ int sessionId);
+
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);
diff --git a/include/media/AudioTimestamp.h b/include/media/AudioTimestamp.h
index c29c7e5..99e9c3e 100644
--- a/include/media/AudioTimestamp.h
+++ b/include/media/AudioTimestamp.h
@@ -19,6 +19,8 @@
#include <time.h>
+namespace android {
+
class AudioTimestamp {
public:
AudioTimestamp() : mPosition(0) {
@@ -30,4 +32,6 @@
struct timespec mTime; // corresponding CLOCK_MONOTONIC when frame is expected to present
};
+} // namespace
+
#endif // ANDROID_AUDIO_TIMESTAMP_H
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index f863e35..7d23d02 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -27,7 +27,7 @@
// ----------------------------------------------------------------------------
-class audio_track_cblk_t;
+struct audio_track_cblk_t;
class AudioTrackClientProxy;
class StaticAudioTrackClientProxy;
@@ -123,6 +123,8 @@
* - NO_ERROR: successful operation
* - NO_INIT: audio server or audio hardware not initialized
* - BAD_VALUE: unsupported configuration
+ * frameCount is guaranteed to be non-zero if status is NO_ERROR,
+ * and is undefined otherwise.
*/
static status_t getMinFrameCount(size_t* frameCount,
@@ -158,7 +160,7 @@
* sampleRate: Data source sampling rate in Hz.
* format: Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
* 16 bits per sample).
- * channelMask: Channel mask.
+ * channelMask: Channel mask, such that audio_is_output_channel(channelMask) is true.
* frameCount: Minimum size of track PCM buffer in frames. This defines the
* application's contribution to the
* latency of the track. The actual size selected by the AudioTrack could be
@@ -180,14 +182,16 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t,
- int frameCount = 0,
+ size_t frameCount = 0,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
- int notificationFrames = 0,
- int sessionId = 0,
+ uint32_t notificationFrames = 0,
+ int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
- const audio_offload_info_t *offloadInfo = NULL);
+ const audio_offload_info_t *offloadInfo = NULL,
+ int uid = -1,
+ pid_t pid = -1);
/* Creates an audio track and registers it with AudioFlinger.
* With this constructor, the track is configured for static buffer mode.
@@ -208,10 +212,12 @@
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
- int notificationFrames = 0,
- int sessionId = 0,
+ uint32_t notificationFrames = 0,
+ int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
- const audio_offload_info_t *offloadInfo = NULL);
+ const audio_offload_info_t *offloadInfo = NULL,
+ int uid = -1,
+ pid_t pid = -1);
/* Terminates the AudioTrack and unregisters it from AudioFlinger.
* Also destroys all resources associated with the AudioTrack.
@@ -239,16 +245,18 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount = 0,
+ size_t frameCount = 0,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
- int notificationFrames = 0,
+ uint32_t notificationFrames = 0,
const sp<IMemory>& sharedBuffer = 0,
bool threadCanCallJava = false,
- int sessionId = 0,
+ int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
- const audio_offload_info_t *offloadInfo = NULL);
+ const audio_offload_info_t *offloadInfo = NULL,
+ int uid = -1,
+ pid_t pid = -1);
/* Result of constructing the AudioTrack. This must be checked for successful initialization
* before using any AudioTrack API (except for set()), because using
@@ -276,7 +284,7 @@
size_t frameSize() const { return mFrameSize; }
uint32_t channelCount() const { return mChannelCount; }
- uint32_t frameCount() const { return mFrameCount; }
+ size_t frameCount() const { return mFrameCount; }
/* Return the static buffer specified in constructor or set(), or 0 for streaming mode */
sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
@@ -333,7 +341,7 @@
*/
status_t setSampleRate(uint32_t sampleRate);
- /* Return current source sample rate in Hz, or 0 if unknown */
+ /* Return current source sample rate in Hz */
uint32_t getSampleRate() const;
/* Enables looping and sets the start and end points of looping.
@@ -358,7 +366,7 @@
/* Sets marker position. When playback reaches the number of frames specified, a callback with
* event type EVENT_MARKER is called. Calling setMarkerPosition with marker == 0 cancels marker
* notification callback. To set a marker at a position which would compute as 0,
- * a workaround is to the set the marker at a nearby position such as ~0 or 1.
+ * a workaround is to set the marker at a nearby position such as ~0 or 1.
* If the AudioTrack has been opened with no callback function associated, the operation will
* fail.
*
@@ -449,7 +457,7 @@
* Returned value:
* handle on audio hardware output
*/
- audio_io_handle_t getOutput();
+ audio_io_handle_t getOutput() const;
/* Returns the unique session ID associated with this track.
*
@@ -563,7 +571,7 @@
uint32_t getUnderrunFrames() const;
/* Get the flags */
- audio_output_flags_t getFlags() const { return mFlags; }
+ audio_output_flags_t getFlags() const { AutoMutex _l(mLock); return mFlags; }
/* Set parameters - only possible when using direct output */
status_t setParameters(const String8& keyValuePairs);
@@ -612,6 +620,7 @@
bool mPaused; // whether thread is requested to pause at next loop entry
bool mPausedInt; // whether thread internally requests pause
nsecs_t mPausedNs; // if mPausedInt then associated timeout, otherwise ignored
+ bool mIgnoreNextPausedInt; // whether to ignore next mPausedInt request
};
// body of AudioTrackThread::threadLoop()
@@ -622,52 +631,50 @@
// NS_INACTIVE inactive so don't run again until re-started
// NS_NEVER never again
static const nsecs_t NS_WHENEVER = -1, NS_INACTIVE = -2, NS_NEVER = -3;
- nsecs_t processAudioBuffer(const sp<AudioTrackThread>& thread);
- status_t processStreamEnd(int32_t waitCount);
+ nsecs_t processAudioBuffer();
+ bool isOffloaded() const;
// caller must hold lock on mLock for all _l methods
- status_t createTrack_l(audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- size_t frameCount,
- audio_output_flags_t flags,
- const sp<IMemory>& sharedBuffer,
- audio_io_handle_t output,
- size_t epoch);
+ status_t createTrack_l(size_t epoch);
// can only be called when mState != STATE_ACTIVE
void flush_l();
void setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount);
- audio_io_handle_t getOutput_l();
// FIXME enum is faster than strcmp() for parameter 'from'
status_t restoreTrack_l(const char *from);
- bool isOffloaded() const
+ bool isOffloaded_l() const
{ return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }
- // Next 3 fields may be changed if IAudioTrack is re-created, but always != 0
+ // Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk; // re-load after mLock.unlock()
+ audio_io_handle_t mOutput; // returned by AudioSystem::getOutput()
sp<AudioTrackThread> mAudioTrackThread;
+
float mVolume[2];
float mSendLevel;
- uint32_t mSampleRate;
- size_t mFrameCount; // corresponds to current IAudioTrack
- size_t mReqFrameCount; // frame count to request the next time a new
- // IAudioTrack is needed
+ mutable uint32_t mSampleRate; // mutable because getSampleRate() can update it.
+ size_t mFrameCount; // corresponds to current IAudioTrack, value is
+ // reported back by AudioFlinger to the client
+ size_t mReqFrameCount; // frame count to request the first or next time
+ // a new IAudioTrack is needed, non-decreasing
// constant after constructor or set()
audio_format_t mFormat; // as requested by client, not forced to 16-bit
audio_stream_type_t mStreamType;
uint32_t mChannelCount;
audio_channel_mask_t mChannelMask;
+ sp<IMemory> mSharedBuffer;
transfer_type mTransfer;
+ audio_offload_info_t mOffloadInfoCopy;
+ const audio_offload_info_t* mOffloadInfo;
// mFrameSize is equal to mFrameSizeAF for non-PCM or 16-bit PCM data. For 8-bit PCM data, it's
// twice as large as mFrameSize because data is expanded to 16-bit before it's stored in buffer.
@@ -708,14 +715,17 @@
bool mRetryOnPartialBuffer; // sleep and retry after partial obtainBuffer()
uint32_t mObservedSequence; // last observed value of mSequence
- sp<IMemory> mSharedBuffer;
uint32_t mLoopPeriod; // in frames, zero means looping is disabled
+
uint32_t mMarkerPosition; // in wrapping (overflow) frame units
bool mMarkerReached;
uint32_t mNewPosition; // in frames
uint32_t mUpdatePeriod; // in frames, zero means no EVENT_NEW_POS
audio_output_flags_t mFlags;
+ // const after set(), except for bits AUDIO_OUTPUT_FLAG_FAST and AUDIO_OUTPUT_FLAG_OFFLOAD.
+ // mLock must be held to read or write those bits reliably.
+
int mSessionId;
int mAuxEffectId;
@@ -736,6 +746,7 @@
bool mInUnderrun; // whether track is currently in underrun state
String8 mName; // server's name for this IAudioTrack
+ uint32_t mPausedPosition;
private:
class DeathNotifier : public IBinder::DeathRecipient {
@@ -749,7 +760,8 @@
sp<DeathNotifier> mDeathNotifier;
uint32_t mSequence; // incremented for each new IAudioTrack attempt
- audio_io_handle_t mOutput; // cached output io handle
+ int mClientUid;
+ pid_t mClientPid;
};
class TimedAudioTrack : public AudioTrack
diff --git a/include/media/EffectsFactoryApi.h b/include/media/EffectsFactoryApi.h
index b1143b9..b1ed7b0 100644
--- a/include/media/EffectsFactoryApi.h
+++ b/include/media/EffectsFactoryApi.h
@@ -171,30 +171,6 @@
////////////////////////////////////////////////////////////////////////////////
int EffectIsNullUuid(const effect_uuid_t *pEffectUuid);
-////////////////////////////////////////////////////////////////////////////////
-//
-// Function: EffectGetSubEffects
-//
-// Description: Returns the descriptors of the sub effects of the effect
-// whose uuid is pointed to by first argument.
-//
-// Input:
-// pEffectUuid: pointer to the effect uuid.
-// size: size of the buffer pointed by pDescriptor.
-//
-// Input/Output:
-// pDescriptor: address where to return the sub effect descriptors.
-//
-// Output:
-// returned value: 0 successful operation.
-// -ENODEV factory failed to initialize
-// -EINVAL invalid pEffectUuid or pDescriptor
-// -ENOENT no effect with this uuid found
-// *pDescriptor: updated with the sub effect descriptors.
-//
-////////////////////////////////////////////////////////////////////////////////
-int EffectGetSubEffects(const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptors, size_t size);
-
#if __cplusplus
} // extern "C"
#endif
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 49f921b..7c5f33a 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -53,6 +53,9 @@
};
typedef uint32_t track_flags_t;
+ // invariant on exit for all APIs that return an sp<>:
+ // (return value != 0) == (*status == NO_ERROR)
+
/* create an audio track and registers it with AudioFlinger.
* return null if the track cannot be created.
*/
@@ -61,9 +64,12 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
track_flags_t *flags,
const sp<IMemory>& sharedBuffer,
+ // On successful return, AudioFlinger takes over the handle
+ // reference and will release it when the track is destroyed.
+ // However on failure, the client is responsible for release.
audio_io_handle_t output,
pid_t tid, // -1 means unused, otherwise must be valid non-0
int *sessionId,
@@ -71,14 +77,18 @@
// output: server's description of IAudioTrack for display in logs.
// Don't attempt to parse, as the format could change.
String8& name,
+ int clientUid,
status_t *status) = 0;
virtual sp<IAudioRecord> openRecord(
+ // On successful return, AudioFlinger takes over the handle
+ // reference and will release it when the track is destroyed.
+ // However on failure, the client is responsible for release.
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
track_flags_t *flags,
pid_t tid, // -1 means unused, otherwise must be valid non-0
int *sessionId,
@@ -159,19 +169,19 @@
audio_channel_mask_t *pChannelMask) = 0;
virtual status_t closeInput(audio_io_handle_t input) = 0;
- virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output) = 0;
+ virtual status_t invalidateStream(audio_stream_type_t stream) = 0;
virtual status_t setVoiceVolume(float volume) = 0;
- virtual status_t getRenderPosition(size_t *halFrames, size_t *dspFrames,
+ virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
audio_io_handle_t output) const = 0;
- virtual size_t getInputFramesLost(audio_io_handle_t ioHandle) const = 0;
+ virtual uint32_t getInputFramesLost(audio_io_handle_t ioHandle) const = 0;
virtual int newAudioSessionId() = 0;
- virtual void acquireAudioSessionId(int audioSession) = 0;
- virtual void releaseAudioSessionId(int audioSession) = 0;
+ virtual void acquireAudioSessionId(int audioSession, pid_t pid) = 0;
+ virtual void releaseAudioSessionId(int audioSession, pid_t pid) = 0;
virtual status_t queryNumberEffects(uint32_t *numEffects) const = 0;
@@ -184,6 +194,7 @@
effect_descriptor_t *pDesc,
const sp<IEffectClient>& client,
int32_t priority,
+ // AudioFlinger doesn't take over handle reference from client
audio_io_handle_t output,
int sessionId,
status_t *status,
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index afac4ae..5c8a484 100644
--- a/include/media/IAudioTrack.h
+++ b/include/media/IAudioTrack.h
@@ -90,6 +90,9 @@
/* Return NO_ERROR if timestamp is valid */
virtual status_t getTimestamp(AudioTimestamp& timestamp) = 0;
+
+ /* Signal the playback thread for a change in control block */
+ virtual void signal() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/IMediaHTTPConnection.h b/include/media/IMediaHTTPConnection.h
new file mode 100644
index 0000000..2a63eb7
--- /dev/null
+++ b/include/media/IMediaHTTPConnection.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 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 I_MEDIA_HTTP_CONNECTION_H_
+
+#define I_MEDIA_HTTP_CONNECTION_H_
+
+#include <binder/IInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+struct IMediaHTTPConnection;
+
+/** MUST stay in sync with IMediaHTTPConnection.aidl */
+
+struct IMediaHTTPConnection : public IInterface {
+ DECLARE_META_INTERFACE(MediaHTTPConnection);
+
+ virtual bool connect(
+ const char *uri, const KeyedVector<String8, String8> *headers) = 0;
+
+ virtual void disconnect() = 0;
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size) = 0;
+ virtual off64_t getSize() = 0;
+ virtual status_t getMIMEType(String8 *mimeType) = 0;
+ virtual status_t getUri(String8 *uri) = 0;
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IMediaHTTPConnection);
+};
+
+} // namespace android
+
+#endif // I_MEDIA_HTTP_CONNECTION_H_
diff --git a/include/media/IMediaHTTPService.h b/include/media/IMediaHTTPService.h
new file mode 100644
index 0000000..f66d6c8
--- /dev/null
+++ b/include/media/IMediaHTTPService.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 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 I_MEDIA_HTTP_SERVICE_H_
+
+#define I_MEDIA_HTTP_SERVICE_H_
+
+#include <binder/IInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct IMediaHTTPConnection;
+
+/** MUST stay in sync with IMediaHTTPService.aidl */
+
+struct IMediaHTTPService : public IInterface {
+ DECLARE_META_INTERFACE(MediaHTTPService);
+
+ virtual sp<IMediaHTTPConnection> makeHTTPConnection() = 0;
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IMediaHTTPService);
+};
+
+} // namespace android
+
+#endif // I_MEDIA_HTTP_SERVICE_H_
diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h
index 6dbb2d7..2529800 100644
--- a/include/media/IMediaMetadataRetriever.h
+++ b/include/media/IMediaMetadataRetriever.h
@@ -26,6 +26,8 @@
namespace android {
+struct IMediaHTTPService;
+
class IMediaMetadataRetriever: public IInterface
{
public:
@@ -33,6 +35,7 @@
virtual void disconnect() = 0;
virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *srcUrl,
const KeyedVector<String8, String8> *headers = NULL) = 0;
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index 0cbd269..db62cd5 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -33,6 +33,7 @@
class Surface;
class IStreamSource;
class IGraphicBufferProducer;
+struct IMediaHTTPService;
class IMediaPlayer: public IInterface
{
@@ -41,8 +42,11 @@
virtual void disconnect() = 0;
- virtual status_t setDataSource(const char *url,
- const KeyedVector<String8, String8>* headers) = 0;
+ virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8>* headers) = 0;
+
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
virtual status_t setDataSource(const sp<IStreamSource>& source) = 0;
virtual status_t setVideoSurfaceTexture(
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index 2998b37..5b45376 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -34,6 +34,7 @@
struct ICrypto;
struct IDrm;
struct IHDCP;
+struct IMediaHTTPService;
class IMediaRecorder;
class IOMX;
class IRemoteDisplay;
@@ -49,9 +50,14 @@
virtual sp<IMediaMetadataRetriever> createMetadataRetriever() = 0;
virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId = 0) = 0;
- virtual status_t decode(const char* url, uint32_t *pSampleRate, int* pNumChannels,
- audio_format_t* pFormat,
- const sp<IMemoryHeap>& heap, size_t *pSize) = 0;
+ virtual status_t decode(
+ const sp<IMediaHTTPService> &httpService,
+ const char* url,
+ uint32_t *pSampleRate,
+ int* pNumChannels,
+ audio_format_t* pFormat,
+ const sp<IMemoryHeap>& heap, size_t *pSize) = 0;
+
virtual status_t decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate,
int* pNumChannels, audio_format_t* pFormat,
const sp<IMemoryHeap>& heap, size_t *pSize) = 0;
@@ -93,9 +99,6 @@
virtual void addBatteryData(uint32_t params) = 0;
virtual status_t pullBatteryData(Parcel* reply) = 0;
-
- virtual status_t updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index db9093a..f6f9e7a 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -83,6 +83,10 @@
virtual status_t storeMetaDataInBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable) = 0;
+ virtual status_t prepareForAdaptivePlayback(
+ node_id node, OMX_U32 portIndex, OMX_BOOL enable,
+ OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) = 0;
+
virtual status_t enableGraphicBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable) = 0;
@@ -138,6 +142,9 @@
enum InternalOptionType {
INTERNAL_OPTION_SUSPEND, // data is a bool
INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY, // data is an int64_t
+ INTERNAL_OPTION_MAX_TIMESTAMP_GAP, // data is int64_t
+ INTERNAL_OPTION_START_TIME, // data is an int64_t
+ INTERNAL_OPTION_TIME_LAPSE, // data is an int64_t[2]
};
virtual status_t setInternalOption(
node_id node,
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index ecc3b65..bb6b97b 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -24,6 +24,8 @@
namespace android {
+struct IMediaHTTPService;
+
// Abstract base class
class MediaMetadataRetrieverBase : public RefBase
{
@@ -32,6 +34,7 @@
virtual ~MediaMetadataRetrieverBase() {}
virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers = NULL) = 0;
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 3b151ef..87717da 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -99,6 +99,8 @@
virtual status_t getPosition(uint32_t *position) const = 0;
virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
virtual int getSessionId() const = 0;
+ virtual audio_stream_type_t getAudioStreamType() const = 0;
+ virtual uint32_t getSampleRate() const = 0;
// If no callback is specified, use the "write" API below to submit
// audio data.
@@ -135,6 +137,7 @@
}
virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers = NULL) = 0;
@@ -211,11 +214,6 @@
return INVALID_OPERATION;
}
- virtual status_t updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
- return INVALID_OPERATION;
- }
-
private:
friend class MediaPlayerService;
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index 0df77c1..b35cf32 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -25,6 +25,7 @@
namespace android {
+struct IMediaHTTPService;
class IMediaPlayerService;
class IMediaMetadataRetriever;
@@ -68,6 +69,7 @@
void disconnect();
status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *dataSourceUrl,
const KeyedVector<String8, String8> *headers = NULL);
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 2177c4c..f8e4e3b 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -45,6 +45,7 @@
MEDIA_STARTED = 6,
MEDIA_PAUSED = 7,
MEDIA_STOPPED = 8,
+ MEDIA_SKIPPED = 9,
MEDIA_TIMED_TEXT = 99,
MEDIA_ERROR = 100,
MEDIA_INFO = 200,
@@ -188,6 +189,8 @@
virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
};
+struct IMediaHTTPService;
+
class MediaPlayer : public BnMediaPlayerClient,
public virtual IMediaDeathNotifier
{
@@ -198,6 +201,7 @@
void disconnect();
status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers);
@@ -223,9 +227,14 @@
bool isLooping();
status_t setVolume(float leftVolume, float rightVolume);
void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
- static status_t decode(const char* url, uint32_t *pSampleRate, int* pNumChannels,
- audio_format_t* pFormat,
- const sp<IMemoryHeap>& heap, size_t *pSize);
+ static status_t decode(
+ const sp<IMediaHTTPService> &httpService,
+ const char* url,
+ uint32_t *pSampleRate,
+ int* pNumChannels,
+ audio_format_t* pFormat,
+ const sp<IMemoryHeap>& heap,
+ size_t *pSize);
static status_t decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate,
int* pNumChannels, audio_format_t* pFormat,
const sp<IMemoryHeap>& heap, size_t *pSize);
@@ -241,9 +250,6 @@
status_t setRetransmitEndpoint(const char* addrString, uint16_t port);
status_t setNextMediaPlayer(const sp<MediaPlayer>& player);
- status_t updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList);
-
private:
void clear_l();
status_t seekTo_l(int msec);
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 88a42a0..142cb90 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -39,7 +39,7 @@
enum video_source {
VIDEO_SOURCE_DEFAULT = 0,
VIDEO_SOURCE_CAMERA = 1,
- VIDEO_SOURCE_GRALLOC_BUFFER = 2,
+ VIDEO_SOURCE_SURFACE = 2,
VIDEO_SOURCE_LIST_END // must be last - used to validate audio source type
};
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h
index a73403b..4537679 100644
--- a/include/media/mediascanner.h
+++ b/include/media/mediascanner.h
@@ -21,6 +21,7 @@
#include <utils/threads.h>
#include <utils/List.h>
#include <utils/Errors.h>
+#include <utils/String8.h>
#include <pthread.h>
struct dirent;
@@ -29,6 +30,7 @@
class MediaScannerClient;
class StringArray;
+class CharacterEncodingDetector;
enum MediaScanResult {
// This file or directory was scanned successfully.
@@ -94,15 +96,9 @@
virtual status_t setMimeType(const char* mimeType) = 0;
protected:
- void convertValues(uint32_t encoding);
-
-protected:
- // cached name and value strings, for native encoding support.
- StringArray* mNames;
- StringArray* mValues;
-
- // default encoding based on MediaScanner::mLocale string
- uint32_t mLocaleEncoding;
+ // default encoding from MediaScanner::mLocale
+ String8 mLocale;
+ CharacterEncodingDetector *mEncodingDetector;
};
}; // namespace android
diff --git a/include/media/nbaio/AudioBufferProviderSource.h b/include/media/nbaio/AudioBufferProviderSource.h
index 2c4aaff..b16e20a 100644
--- a/include/media/nbaio/AudioBufferProviderSource.h
+++ b/include/media/nbaio/AudioBufferProviderSource.h
@@ -27,7 +27,7 @@
class AudioBufferProviderSource : public NBAIO_Source {
public:
- AudioBufferProviderSource(AudioBufferProvider *provider, NBAIO_Format format);
+ AudioBufferProviderSource(AudioBufferProvider *provider, const NBAIO_Format& format);
virtual ~AudioBufferProviderSource();
// NBAIO_Port interface
diff --git a/include/media/nbaio/AudioStreamInSource.h b/include/media/nbaio/AudioStreamInSource.h
index 07d8c89..eaea63c 100644
--- a/include/media/nbaio/AudioStreamInSource.h
+++ b/include/media/nbaio/AudioStreamInSource.h
@@ -43,7 +43,7 @@
// This is an over-estimate, and could dupe the caller into making a blocking read()
// FIXME Use an audio HAL API to query the buffer filling status when it's available.
- virtual ssize_t availableToRead() { return mStreamBufferSizeBytes >> mBitShift; }
+ virtual ssize_t availableToRead() { return mStreamBufferSizeBytes / mFrameSize; }
virtual ssize_t read(void *buffer, size_t count);
diff --git a/include/media/nbaio/AudioStreamOutSink.h b/include/media/nbaio/AudioStreamOutSink.h
index 7948d40..9949b88 100644
--- a/include/media/nbaio/AudioStreamOutSink.h
+++ b/include/media/nbaio/AudioStreamOutSink.h
@@ -43,7 +43,7 @@
// This is an over-estimate, and could dupe the caller into making a blocking write()
// FIXME Use an audio HAL API to query the buffer emptying status when it's available.
- virtual ssize_t availableToWrite() const { return mStreamBufferSizeBytes >> mBitShift; }
+ virtual ssize_t availableToWrite() const { return mStreamBufferSizeBytes / mFrameSize; }
virtual ssize_t write(const void *buffer, size_t count);
diff --git a/include/media/nbaio/MonoPipe.h b/include/media/nbaio/MonoPipe.h
index d3802fe..b09b35f 100644
--- a/include/media/nbaio/MonoPipe.h
+++ b/include/media/nbaio/MonoPipe.h
@@ -41,7 +41,7 @@
// Note: whatever shares this object with another thread needs to do so in an SMP-safe way (like
// creating it the object before creating the other thread, or storing the object with a
// release_store). Otherwise the other thread could see a partially-constructed object.
- MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock = false);
+ MonoPipe(size_t reqFrames, const NBAIO_Format& format, bool writeCanBlock = false);
virtual ~MonoPipe();
// NBAIO_Port interface
diff --git a/include/media/nbaio/NBAIO.h b/include/media/nbaio/NBAIO.h
index 1da0c73..ab9a4a5 100644
--- a/include/media/nbaio/NBAIO.h
+++ b/include/media/nbaio/NBAIO.h
@@ -29,6 +29,7 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <media/AudioTimestamp.h>
+#include <system/audio.h>
namespace android {
@@ -52,31 +53,41 @@
// the combinations that are actually needed within AudioFlinger. If the list of combinations grows
// too large, then this decision should be re-visited.
// Sample rate and channel count are explicit, PCM interleaved 16-bit is assumed.
-typedef unsigned NBAIO_Format;
-enum {
- Format_Invalid
+struct NBAIO_Format {
+//private:
+ unsigned mSampleRate;
+ unsigned mChannelCount;
+ audio_format_t mFormat;
+ ssize_t mFrameSize;
};
-// Return the frame size of an NBAIO_Format in bytes
-size_t Format_frameSize(NBAIO_Format format);
+extern const NBAIO_Format Format_Invalid;
-// Return the frame size of an NBAIO_Format as a bit shift
-size_t Format_frameBitShift(NBAIO_Format format);
+// Return the frame size of an NBAIO_Format in bytes
+size_t Format_frameSize(const NBAIO_Format& format);
// Convert a sample rate in Hz and channel count to an NBAIO_Format
-NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount);
+// FIXME Remove the default value of AUDIO_FORMAT_PCM_16_BIT, and rename
+NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount,
+ audio_format_t format = AUDIO_FORMAT_PCM_16_BIT);
// Return the sample rate in Hz of an NBAIO_Format
-unsigned Format_sampleRate(NBAIO_Format format);
+unsigned Format_sampleRate(const NBAIO_Format& format);
// Return the channel count of an NBAIO_Format
-unsigned Format_channelCount(NBAIO_Format format);
+unsigned Format_channelCount(const NBAIO_Format& format);
// Callbacks used by NBAIO_Sink::writeVia() and NBAIO_Source::readVia() below.
typedef ssize_t (*writeVia_t)(void *user, void *buffer, size_t count);
typedef ssize_t (*readVia_t)(void *user, const void *buffer,
size_t count, int64_t readPTS);
+// Check whether an NBAIO_Format is valid
+bool Format_isValid(const NBAIO_Format& format);
+
+// Compare two NBAIO_Format values
+bool Format_isEqual(const NBAIO_Format& format1, const NBAIO_Format& format2);
+
// Abstract class (interface) representing a data port.
class NBAIO_Port : public RefBase {
@@ -115,15 +126,15 @@
virtual NBAIO_Format format() const { return mNegotiated ? mFormat : Format_Invalid; }
protected:
- NBAIO_Port(NBAIO_Format format) : mNegotiated(false), mFormat(format),
- mBitShift(Format_frameBitShift(format)) { }
+ NBAIO_Port(const NBAIO_Format& format) : mNegotiated(false), mFormat(format),
+ mFrameSize(Format_frameSize(format)) { }
virtual ~NBAIO_Port() { }
// Implementations are free to ignore these if they don't need them
bool mNegotiated; // mNegotiated implies (mFormat != Format_Invalid)
NBAIO_Format mFormat; // (mFormat != Format_Invalid) does not imply mNegotiated
- size_t mBitShift; // assign in parallel with any assignment to mFormat
+ size_t mFrameSize; // assign in parallel with any assignment to mFormat
};
// Abstract class (interface) representing a non-blocking data sink, for use by a data provider.
@@ -220,7 +231,7 @@
virtual status_t getTimestamp(AudioTimestamp& timestamp) { return INVALID_OPERATION; }
protected:
- NBAIO_Sink(NBAIO_Format format = Format_Invalid) : NBAIO_Port(format), mFramesWritten(0) { }
+ NBAIO_Sink(const NBAIO_Format& format = Format_Invalid) : NBAIO_Port(format), mFramesWritten(0) { }
virtual ~NBAIO_Sink() { }
// Implementations are free to ignore these if they don't need them
@@ -311,7 +322,7 @@
virtual void onTimestamp(const AudioTimestamp& timestamp) { }
protected:
- NBAIO_Source(NBAIO_Format format = Format_Invalid) : NBAIO_Port(format), mFramesRead(0) { }
+ NBAIO_Source(const NBAIO_Format& format = Format_Invalid) : NBAIO_Port(format), mFramesRead(0) { }
virtual ~NBAIO_Source() { }
// Implementations are free to ignore these if they don't need them
diff --git a/include/media/nbaio/NBLog.h b/include/media/nbaio/NBLog.h
index 6d59ea7..bcbbc04 100644
--- a/include/media/nbaio/NBLog.h
+++ b/include/media/nbaio/NBLog.h
@@ -25,6 +25,8 @@
namespace android {
+class String8;
+
class NBLog {
public:
@@ -187,6 +189,10 @@
const Shared* const mShared; // raw pointer to shared memory
const sp<IMemory> mIMemory; // ref-counted version
int32_t mFront; // index of oldest acknowledged Entry
+ int mFd; // file descriptor
+ int mIndent; // indentation level
+
+ void dumpLine(const String8& timestamp, String8& body);
static const size_t kSquashTimestamp = 5; // squash this many or more adjacent timestamps
};
diff --git a/include/media/nbaio/Pipe.h b/include/media/nbaio/Pipe.h
index 79a4eee..c784129 100644
--- a/include/media/nbaio/Pipe.h
+++ b/include/media/nbaio/Pipe.h
@@ -30,7 +30,7 @@
public:
// maxFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2.
- Pipe(size_t maxFrames, NBAIO_Format format);
+ Pipe(size_t maxFrames, const NBAIO_Format& format);
virtual ~Pipe();
// NBAIO_Port interface
diff --git a/include/media/nbaio/SourceAudioBufferProvider.h b/include/media/nbaio/SourceAudioBufferProvider.h
index cdfb6fe..daf6bc3 100644
--- a/include/media/nbaio/SourceAudioBufferProvider.h
+++ b/include/media/nbaio/SourceAudioBufferProvider.h
@@ -41,7 +41,7 @@
private:
const sp<NBAIO_Source> mSource; // the wrapped source
- /*const*/ size_t mFrameBitShift; // log2(frame size in bytes)
+ /*const*/ size_t mFrameSize; // frame size in bytes
void* mAllocated; // pointer to base of allocated memory
size_t mSize; // size of mAllocated in frames
size_t mOffset; // frame offset within mAllocated of valid data
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index a8ffd4a..36f2a67 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -67,6 +67,8 @@
void signalRequestIDRFrame();
+ bool isConfiguredForAdaptivePlayback() { return mIsConfiguredForAdaptivePlayback; }
+
struct PortDescription : public RefBase {
size_t countBuffers();
IOMX::buffer_id bufferIDAt(size_t index) const;
@@ -116,6 +118,7 @@
kWhatStart = 'star',
kWhatRequestIDRFrame = 'ridr',
kWhatSetParameters = 'setP',
+ kWhatSubmitOutputMetaDataBufferIfEOS = 'subm',
};
enum {
@@ -186,6 +189,7 @@
bool mIsEncoder;
bool mUseMetadataOnEncoderOutput;
bool mShutdownInProgress;
+ bool mIsConfiguredForAdaptivePlayback;
// If "mKeepComponentAllocated" we only transition back to Loaded state
// and do not release the component instance.
@@ -201,6 +205,12 @@
int32_t mMetaDataBuffersToSubmit;
int64_t mRepeatFrameDelayUs;
+ int64_t mMaxPtsGapUs;
+
+ int64_t mTimePerFrameUs;
+ int64_t mTimePerCaptureUs;
+
+ bool mCreateInputBuffersSuspended;
status_t setCyclicIntraMacroblockRefresh(const sp<AMessage> &msg, int32_t mode);
status_t allocateBuffersOnPort(OMX_U32 portIndex);
@@ -212,6 +222,7 @@
OMX_U32 *nMinUndequeuedBuffers);
status_t allocateOutputMetaDataBuffers();
status_t submitOutputMetaDataBuffer();
+ void signalSubmitOutputMetaDataBufferIfEOS_workaround();
status_t allocateOutputBuffersFromNativeWindow();
status_t cancelBufferToNativeWindow(BufferInfo *info);
status_t freeOutputBuffersNotOwnedByComponent();
@@ -247,6 +258,8 @@
int32_t numChannels, int32_t sampleRate, int32_t bitRate,
int32_t aacProfile, bool isADTS);
+ status_t setupAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate);
+
status_t selectAudioPortFormat(
OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat);
@@ -264,6 +277,7 @@
status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg);
status_t setupH263EncoderParameters(const sp<AMessage> &msg);
status_t setupAVCEncoderParameters(const sp<AMessage> &msg);
+ status_t setupVPXEncoderParameters(const sp<AMessage> &msg);
status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 912a43c..14afb85 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -129,7 +129,7 @@
void reset();
uint32_t getNumFramesPendingPlayout() const;
- int64_t getOutputPlayPositionUs_l() const;
+ int64_t getOutputPlayPositionUs_l();
bool allowDeepBuffering() const { return (mCreateFlags & ALLOW_DEEP_BUFFERING) != 0; }
bool useOffload() const { return (mCreateFlags & USE_OFFLOAD) != 0; }
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index a829916..dd0a106 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -172,7 +172,7 @@
const sp<IGraphicBufferProducer>& surface,
bool storeMetaDataInVideoBuffers);
- virtual void startCameraRecording();
+ virtual status_t startCameraRecording();
virtual void releaseRecordingFrame(const sp<IMemory>& frame);
// Returns true if need to skip the current frame.
@@ -185,6 +185,8 @@
virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
const sp<IMemory> &data);
+ void releaseCamera();
+
private:
friend class CameraSourceListener;
@@ -233,7 +235,6 @@
int32_t frameRate);
void stopCameraRecording();
- void releaseCamera();
status_t reset();
CameraSource(const CameraSource &);
diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h
index 6b7a63c..34213be 100644
--- a/include/media/stagefright/CameraSourceTimeLapse.h
+++ b/include/media/stagefright/CameraSourceTimeLapse.h
@@ -41,7 +41,8 @@
Size videoSize,
int32_t videoFrameRate,
const sp<IGraphicBufferProducer>& surface,
- int64_t timeBetweenTimeLapseFrameCaptureUs);
+ int64_t timeBetweenTimeLapseFrameCaptureUs,
+ bool storeMetaDataInVideoBuffers = true);
virtual ~CameraSourceTimeLapse();
@@ -116,7 +117,8 @@
Size videoSize,
int32_t videoFrameRate,
const sp<IGraphicBufferProducer>& surface,
- int64_t timeBetweenTimeLapseFrameCaptureUs);
+ int64_t timeBetweenTimeLapseFrameCaptureUs,
+ bool storeMetaDataInVideoBuffers = true);
// Wrapper over CameraSource::signalBufferReturned() to implement quick stop.
// It only handles the case when mLastReadBufferCopy is signalled. Otherwise
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 742bc0e..f8787dd 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -31,6 +31,7 @@
namespace android {
struct AMessage;
+struct IMediaHTTPService;
class String8;
class DataSource : public RefBase {
@@ -43,6 +44,7 @@
};
static sp<DataSource> CreateFromURI(
+ const sp<IMediaHTTPService> &httpService,
const char *uri,
const KeyedVector<String8, String8> *headers = NULL);
@@ -80,7 +82,6 @@
const sp<DataSource> &source, String8 *mimeType,
float *confidence, sp<AMessage> *meta);
- static void RegisterSniffer(SnifferFunc func);
static void RegisterDefaultSniffers();
// for DRM
@@ -101,6 +102,9 @@
private:
static Mutex gSnifferMutex;
static List<SnifferFunc> gSniffers;
+ static bool gSniffersRegistered;
+
+ static void RegisterSniffer_l(SnifferFunc func);
DataSource(const DataSource &);
DataSource &operator=(const DataSource &);
diff --git a/include/media/stagefright/DataURISource.h b/include/media/stagefright/DataURISource.h
new file mode 100644
index 0000000..693562e
--- /dev/null
+++ b/include/media/stagefright/DataURISource.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DATA_URI_SOURCE_H_
+
+#define DATA_URI_SOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct DataURISource : public DataSource {
+ static sp<DataURISource> Create(const char *uri);
+
+ virtual status_t initCheck() const;
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+ virtual status_t getSize(off64_t *size);
+
+protected:
+ virtual ~DataURISource();
+
+private:
+ sp<ABuffer> mBuffer;
+
+ DataURISource(const sp<ABuffer> &buffer);
+
+ DISALLOW_EVIL_CONSTRUCTORS(DataURISource);
+};
+
+} // namespace android
+
+#endif // DATA_URI_SOURCE_H_
+
diff --git a/include/media/stagefright/MediaCodecSource.h b/include/media/stagefright/MediaCodecSource.h
new file mode 100644
index 0000000..4b18a0b
--- /dev/null
+++ b/include/media/stagefright/MediaCodecSource.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MediaCodecSource_H_
+#define MediaCodecSource_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+class ALooper;
+class AMessage;
+class IGraphicBufferProducer;
+class MediaCodec;
+class MetaData;
+
+struct MediaCodecSource : public MediaSource,
+ public MediaBufferObserver {
+ enum FlagBits {
+ FLAG_USE_SURFACE_INPUT = 1,
+ FLAG_USE_METADATA_INPUT = 2,
+ };
+
+ static sp<MediaCodecSource> Create(
+ const sp<ALooper> &looper,
+ const sp<AMessage> &format,
+ const sp<MediaSource> &source,
+ uint32_t flags = 0);
+
+ bool isVideo() const { return mIsVideo; }
+ sp<IGraphicBufferProducer> getGraphicBufferProducer();
+
+ // MediaSource
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual status_t pause();
+ virtual sp<MetaData> getFormat() { return mMeta; }
+ virtual status_t read(
+ MediaBuffer **buffer,
+ const ReadOptions *options = NULL);
+
+ // MediaBufferObserver
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+ // for AHandlerReflector
+ void onMessageReceived(const sp<AMessage> &msg);
+
+protected:
+ virtual ~MediaCodecSource();
+
+private:
+ struct Puller;
+
+ enum {
+ kWhatPullerNotify,
+ kWhatEncoderActivity,
+ kWhatStart,
+ kWhatStop,
+ kWhatPause,
+ };
+
+ MediaCodecSource(
+ const sp<ALooper> &looper,
+ const sp<AMessage> &outputFormat,
+ const sp<MediaSource> &source,
+ uint32_t flags = 0);
+
+ status_t onStart(MetaData *params);
+ status_t init();
+ status_t initEncoder();
+ void releaseEncoder();
+ status_t feedEncoderInputBuffers();
+ void scheduleDoMoreWork();
+ status_t doMoreWork();
+ void suspend();
+ void resume(int64_t skipFramesBeforeUs = -1ll);
+ void signalEOS(status_t err = ERROR_END_OF_STREAM);
+ bool reachedEOS();
+ status_t postSynchronouslyAndReturnError(const sp<AMessage> &msg);
+
+ sp<ALooper> mLooper;
+ sp<ALooper> mCodecLooper;
+ sp<AHandlerReflector<MediaCodecSource> > mReflector;
+ sp<AMessage> mOutputFormat;
+ sp<MetaData> mMeta;
+ sp<Puller> mPuller;
+ sp<MediaCodec> mEncoder;
+ uint32_t mFlags;
+ List<uint32_t> mStopReplyIDQueue;
+ bool mIsVideo;
+ bool mStarted;
+ bool mStopping;
+ bool mDoMoreWorkPending;
+ bool mPullerReachedEOS;
+ sp<AMessage> mEncoderActivityNotify;
+ sp<IGraphicBufferProducer> mGraphicBufferProducer;
+ Vector<sp<ABuffer> > mEncoderInputBuffers;
+ Vector<sp<ABuffer> > mEncoderOutputBuffers;
+ List<MediaBuffer *> mInputBufferQueue;
+ List<size_t> mAvailEncoderInputIndices;
+ List<int64_t> mDecodingTimeQueue; // decoding time (us) for video
+
+ // audio drift time
+ int64_t mFirstSampleTimeUs;
+ List<int64_t> mDriftTimeQueue;
+
+ // following variables are protected by mOutputBufferLock
+ Mutex mOutputBufferLock;
+ Condition mOutputBufferCond;
+ List<MediaBuffer*> mOutputBufferQueue;
+ bool mEncodedReachedEOS;
+ status_t mErrorCode;
+
+ DISALLOW_EVIL_CONSTRUCTORS(MediaCodecSource);
+};
+
+} // namespace android
+
+#endif /* MediaCodecSource_H_ */
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 85693d4..678d642 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -38,12 +38,14 @@
extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
extern const char *MEDIA_MIMETYPE_AUDIO_QCELP;
extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS;
+extern const char *MEDIA_MIMETYPE_AUDIO_OPUS;
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_AUDIO_MSGSM;
+extern const char *MEDIA_MIMETYPE_AUDIO_AC3;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
diff --git a/include/media/stagefright/MediaHTTP.h b/include/media/stagefright/MediaHTTP.h
new file mode 100644
index 0000000..006d8d8
--- /dev/null
+++ b/include/media/stagefright/MediaHTTP.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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_HTTP_H_
+
+#define MEDIA_HTTP_H_
+
+#include <media/stagefright/foundation/AString.h>
+
+#include "include/HTTPBase.h"
+
+namespace android {
+
+struct IMediaHTTPConnection;
+
+struct MediaHTTP : public HTTPBase {
+ MediaHTTP(const sp<IMediaHTTPConnection> &conn);
+
+ virtual status_t connect(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers,
+ off64_t offset);
+
+ 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 status_t reconnectAtOffset(off64_t offset);
+
+protected:
+ virtual ~MediaHTTP();
+
+ virtual sp<DecryptHandle> DrmInitialization(const char* mime);
+ virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
+ virtual String8 getUri();
+ virtual String8 getMIMEType() const;
+
+private:
+ status_t mInitCheck;
+ sp<IMediaHTTPConnection> mHTTPConnection;
+
+ KeyedVector<String8, String8> mLastHeaders;
+ AString mLastURI;
+
+ bool mCachedSizeValid;
+ off64_t mCachedSize;
+
+ sp<DecryptHandle> mDecryptHandle;
+ DrmManagerClient *mDrmManagerClient;
+
+ void clearDRMState_l();
+
+ DISALLOW_EVIL_CONSTRUCTORS(MediaHTTP);
+};
+
+} // namespace android
+
+#endif // MEDIA_HTTP_H_
diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
index 3818e63..204d1c6 100644
--- a/include/media/stagefright/MediaSource.h
+++ b/include/media/stagefright/MediaSource.h
@@ -105,7 +105,7 @@
// This will be called after a successful start() and before the
// first read() call.
// Callee assumes ownership of the buffers if no error is returned.
- virtual status_t setBuffers(const Vector<MediaBuffer *> &buffers) {
+ virtual status_t setBuffers(const Vector<MediaBuffer *> & /* buffers */) {
return ERROR_UNSUPPORTED;
}
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index de3fc36..e862ec3 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -56,6 +56,9 @@
kKeyD263 = 'd263', // raw data
kKeyVorbisInfo = 'vinf', // raw data
kKeyVorbisBooks = 'vboo', // raw data
+ kKeyOpusHeader = 'ohdr', // raw data
+ kKeyOpusCodecDelay = 'ocod', // uint64_t (codec delay in ns)
+ kKeyOpusSeekPreRoll = 'ospr', // uint64_t (seek preroll in ns)
kKeyWantsNALFragments = 'NALf',
kKeyIsSyncFrame = 'sync', // int32_t (bool)
kKeyIsCodecConfig = 'conf', // int32_t (bool)
@@ -134,6 +137,7 @@
kKeyRequiresSecureBuffers = 'secu', // bool (int32_t)
kKeyIsADTS = 'adts', // bool (int32_t)
+ kKeyAACAOT = 'aaot', // int32_t
// If a MediaBuffer's data represents (at least partially) encrypted
// data, the following fields aid in decryption.
@@ -214,6 +218,8 @@
bool findData(uint32_t key, uint32_t *type,
const void **data, size_t *size) const;
+ bool hasData(uint32_t key) const;
+
void dumpToLog() const;
protected:
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 5ae6f6b..402e7f8 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -31,6 +31,7 @@
struct ABuffer;
struct AMessage;
struct DataSource;
+struct IMediaHTTPService;
struct MediaBuffer;
struct MediaExtractor;
struct MediaSource;
@@ -45,6 +46,7 @@
NuMediaExtractor();
status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *path,
const KeyedVector<String8, String8> *headers = NULL);
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index daaf20f..5121c17 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -248,6 +248,8 @@
int32_t numChannels, int32_t sampleRate, int32_t bitRate,
int32_t aacProfile, bool isADTS);
+ status_t setAC3Format(int32_t numChannels, int32_t sampleRate);
+
void setG711Format(int32_t numChannels);
status_t setVideoPortFormatType(
diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h
index c24f612..bbad271 100644
--- a/include/media/stagefright/Utils.h
+++ b/include/media/stagefright/Utils.h
@@ -57,7 +57,8 @@
status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink, const sp<MetaData>& meta);
// Check whether the stream defined by meta can be offloaded to hardware
-bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming);
+bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo,
+ bool isStreaming, audio_stream_type_t streamType);
} // namespace android
diff --git a/include/media/stagefright/timedtext/TimedTextDriver.h b/include/media/stagefright/timedtext/TimedTextDriver.h
index f23c337..37ef674 100644
--- a/include/media/stagefright/timedtext/TimedTextDriver.h
+++ b/include/media/stagefright/timedtext/TimedTextDriver.h
@@ -25,6 +25,7 @@
namespace android {
class ALooper;
+struct IMediaHTTPService;
class MediaPlayerBase;
class MediaSource;
class Parcel;
@@ -34,7 +35,9 @@
class TimedTextDriver {
public:
- TimedTextDriver(const wp<MediaPlayerBase> &listener);
+ TimedTextDriver(
+ const wp<MediaPlayerBase> &listener,
+ const sp<IMediaHTTPService> &httpService);
~TimedTextDriver();
@@ -77,6 +80,7 @@
sp<ALooper> mLooper;
sp<TimedTextPlayer> mPlayer;
wp<MediaPlayerBase> mListener;
+ sp<IMediaHTTPService> mHTTPService;
// Variables to be guarded by mLock.
State mState;
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 4b13887..3901e79 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -65,7 +65,9 @@
struct AudioTrackSharedStatic {
StaticAudioTrackSingleStateQueue::Shared
mSingleStateQueue;
- size_t mBufferPosition; // updated asynchronously by server,
+ // This field should be a size_t, but since it is located in shared memory we
+ // force to 32-bit. The client and server may have different typedefs for size_t.
+ uint32_t mBufferPosition; // updated asynchronously by server,
// "for entertainment purposes only"
};
@@ -96,11 +98,7 @@
// The value should be used "for entertainment purposes only",
// which means don't make important decisions based on it.
- size_t frameCount_; // used during creation to pass actual track buffer size
- // from AudioFlinger to client, and not referenced again
- // FIXME remove here and replace by createTrack() in/out
- // parameter
- // renamed to "_" to detect incorrect use
+ uint32_t mPad1; // unused
volatile int32_t mFutex; // event flag: down (P) by client,
// up (V) by server or binderDied() or interrupt()
@@ -108,7 +106,9 @@
private:
- size_t mMinimum; // server wakes up client if available >= mMinimum
+ // This field should be a size_t, but since it is located in shared memory we
+ // force to 32-bit. The client and server may have different typedefs for size_t.
+ uint32_t mMinimum; // server wakes up client if available >= mMinimum
// Channel volumes are fixed point U4.12, so 0x1000 means 1.0.
// Left channel is in [0:15], right channel is in [16:31].
@@ -245,7 +245,11 @@
}
void setMinimum(size_t minimum) {
- mCblk->mMinimum = minimum;
+ // This can only happen on a 64-bit client
+ if (minimum > UINT32_MAX) {
+ minimum = UINT32_MAX;
+ }
+ mCblk->mMinimum = (uint32_t) minimum;
}
// Return the number of frames that would need to be obtained and released
@@ -256,6 +260,8 @@
return mEpoch;
}
+ size_t getFramesFilled();
+
private:
size_t mEpoch;
};
@@ -358,6 +364,7 @@
// which must be > 0.
// buffer->mNonContig is unused.
// buffer->mRaw is unused.
+ // ackFlush is true iff being called from Track::start to acknowledge a pending flush.
// On exit:
// buffer->mFrameCount has the actual number of contiguous available frames,
// which is always 0 when the return status != NO_ERROR.
@@ -368,7 +375,7 @@
// NO_ERROR Success, buffer->mFrameCount > 0.
// WOULD_BLOCK No frames are available.
// NO_INIT Shared memory is corrupt.
- virtual status_t obtainBuffer(Buffer* buffer);
+ virtual status_t obtainBuffer(Buffer* buffer, bool ackFlush = false);
// Release (some of) the frames last obtained.
// On entry, buffer->mFrameCount should have the number of frames to release,
@@ -435,7 +442,7 @@
public:
virtual size_t framesReady();
virtual void framesReadyIsCalledByMultipleThreads();
- virtual status_t obtainBuffer(Buffer* buffer);
+ virtual status_t obtainBuffer(Buffer* buffer, bool ackFlush);
virtual void releaseBuffer(Buffer* buffer);
virtual void tallyUnderrunFrames(uint32_t frameCount);
virtual uint32_t getUnderrunFrames() const { return 0; }
diff --git a/include/private/media/StaticAudioTrackState.h b/include/private/media/StaticAudioTrackState.h
index 46a5946..d483061 100644
--- a/include/private/media/StaticAudioTrackState.h
+++ b/include/private/media/StaticAudioTrackState.h
@@ -25,9 +25,13 @@
// state is wrapped by a SingleStateQueue.
struct StaticAudioTrackState {
// do not define constructors, destructors, or virtual methods
- size_t mLoopStart;
- size_t mLoopEnd;
- int mLoopCount;
+
+ // These fields should both be size_t, but since they are located in shared memory we
+ // force to 32-bit. The client and server may have different typedefs for size_t.
+ uint32_t mLoopStart;
+ uint32_t mLoopEnd;
+
+ int mLoopCount;
};
} // namespace android
diff --git a/libvideoeditor/lvpp/Android.mk b/libvideoeditor/lvpp/Android.mk
index 2286827..860d351 100755
--- a/libvideoeditor/lvpp/Android.mk
+++ b/libvideoeditor/lvpp/Android.mk
@@ -71,7 +71,6 @@
$(TOP)/frameworks/av/media/libstagefright \
$(TOP)/frameworks/av/media/libstagefright/include \
$(TOP)/frameworks/av/media/libstagefright/rtsp \
- $(call include-path-for, corecg graphics) \
$(TOP)/frameworks/av/libvideoeditor/osal/inc \
$(TOP)/frameworks/av/libvideoeditor/vss/common/inc \
$(TOP)/frameworks/av/libvideoeditor/vss/mcs/inc \
diff --git a/libvideoeditor/lvpp/PreviewPlayer.cpp b/libvideoeditor/lvpp/PreviewPlayer.cpp
index 2bd9f84..b36fe0a 100755
--- a/libvideoeditor/lvpp/PreviewPlayer.cpp
+++ b/libvideoeditor/lvpp/PreviewPlayer.cpp
@@ -21,6 +21,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBuffer.h>
@@ -1160,7 +1161,8 @@
sp<DataSource> dataSource;
sp<MediaExtractor> extractor;
- dataSource = DataSource::CreateFromURI(mUri.string(), NULL);
+ dataSource = DataSource::CreateFromURI(
+ NULL /* httpService */, mUri.string(), NULL);
if (dataSource == NULL) {
return UNKNOWN_ERROR;
diff --git a/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp b/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp
index 176f8e9..cb4b23e 100755
--- a/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp
+++ b/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp
@@ -671,8 +671,9 @@
M4OSA_Void* ptr;
- ptr = (M4OSA_Void*)((unsigned int)mInputBuffer->data() +
- mInputBuffer->range_offset());
+ ptr = reinterpret_cast<M4OSA_Void*>(
+ reinterpret_cast<uintptr_t>(mInputBuffer->data()) +
+ mInputBuffer->range_offset());
M4OSA_UInt32 len = mInputBuffer->range_length();
M4OSA_Context fp = M4OSA_NULL;
@@ -700,8 +701,8 @@
mBGAudioPCMFileOriginalSeekPoint <=
(mBGAudioPCMFileTrimmedLength - len)) {
- ALOGV("Checking mBGAudioPCMFileHandle %d",
- (unsigned int)mBGAudioPCMFileHandle);
+ ALOGV("Checking mBGAudioPCMFileHandle %p",
+ mBGAudioPCMFileHandle);
if (mBGAudioPCMFileHandle != M4OSA_NULL) {
ALOGV("fillBuffer seeking file to %lld",
diff --git a/libvideoeditor/lvpp/VideoEditorPlayer.cpp b/libvideoeditor/lvpp/VideoEditorPlayer.cpp
index 5aeba4f..f9c3879 100755
--- a/libvideoeditor/lvpp/VideoEditorPlayer.cpp
+++ b/libvideoeditor/lvpp/VideoEditorPlayer.cpp
@@ -57,6 +57,7 @@
status_t VideoEditorPlayer::setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url, const KeyedVector<String8, String8> *headers) {
ALOGI("setDataSource('%s')", url);
if (headers != NULL) {
@@ -585,4 +586,11 @@
return mSessionId;
}
+uint32_t VideoEditorPlayer::VeAudioOutput::getSampleRate() const {
+ if (mMsecsPerFrame == 0) {
+ return 0;
+ }
+ return (uint32_t)(1.e3 / mMsecsPerFrame);
+}
+
} // namespace android
diff --git a/libvideoeditor/lvpp/VideoEditorPlayer.h b/libvideoeditor/lvpp/VideoEditorPlayer.h
index ab6d731..781e4bc 100755
--- a/libvideoeditor/lvpp/VideoEditorPlayer.h
+++ b/libvideoeditor/lvpp/VideoEditorPlayer.h
@@ -48,6 +48,7 @@
virtual status_t getPosition(uint32_t *position) const;
virtual status_t getFramesWritten(uint32_t*) const;
virtual int getSessionId() const;
+ virtual uint32_t getSampleRate() const;
virtual status_t open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
@@ -62,6 +63,7 @@
virtual void pause();
virtual void close();
void setAudioStreamType(audio_stream_type_t streamType) { mStreamType = streamType; }
+ virtual audio_stream_type_t getAudioStreamType() const { return mStreamType; }
void setVolume(float left, float right);
virtual status_t dump(int fd,const Vector<String16>& args) const;
@@ -96,6 +98,7 @@
virtual status_t initCheck();
virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url, const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
diff --git a/libvideoeditor/lvpp/VideoEditorPreviewController.cpp b/libvideoeditor/lvpp/VideoEditorPreviewController.cpp
index 149c4ea..953f35a 100755
--- a/libvideoeditor/lvpp/VideoEditorPreviewController.cpp
+++ b/libvideoeditor/lvpp/VideoEditorPreviewController.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include <gui/Surface.h>
+#include <media/IMediaHTTPService.h>
#include "VideoEditorAudioPlayer.h"
#include "PreviewRenderer.h"
@@ -967,7 +968,8 @@
ALOGV("preparePlayer: instance %d file %d", playerInstance, index);
const char* fileName = (const char*) pController->mClipList[index]->pFile;
- pController->mVePlayer[playerInstance]->setDataSource(fileName, NULL);
+ pController->mVePlayer[playerInstance]->setDataSource(
+ NULL /* httpService */, fileName, NULL);
ALOGV("preparePlayer: setDataSource instance %s",
(const char *)pController->mClipList[index]->pFile);
@@ -1248,7 +1250,7 @@
case MEDIA_SET_VIDEO_SIZE:
ALOGV("MEDIA_SET_VIDEO_SIZE; New video size %d x %d", ext1, ext2);
break;
- case 0xAAAAAAAA:
+ case static_cast<int>(0xAAAAAAAA):
ALOGV("VIDEO PLAYBACK ALMOST over, prepare next player");
// Select next player and prepare it
// If there is a clip after this one
@@ -1268,7 +1270,7 @@
}
}
break;
- case 0xBBBBBBBB:
+ case static_cast<int>(0xBBBBBBBB):
{
ALOGV("VIDEO PLAYBACK, Update Overlay");
int overlayIndex = ext2;
diff --git a/libvideoeditor/lvpp/VideoEditorSRC.cpp b/libvideoeditor/lvpp/VideoEditorSRC.cpp
index 36d0812..6beabfa 100755
--- a/libvideoeditor/lvpp/VideoEditorSRC.cpp
+++ b/libvideoeditor/lvpp/VideoEditorSRC.cpp
@@ -284,7 +284,7 @@
void VideoEditorSRC::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
- ALOGV("releaseBuffer: %p", pBuffers);
+ ALOGV("releaseBuffer: %p", pBuffer);
free(pBuffer->raw);
pBuffer->raw = NULL;
pBuffer->frameCount = 0;
diff --git a/libvideoeditor/osal/inc/M4OSA_Clock.h b/libvideoeditor/osal/inc/M4OSA_Clock.h
index db753a5..52ea696 100755
--- a/libvideoeditor/osal/inc/M4OSA_Clock.h
+++ b/libvideoeditor/osal/inc/M4OSA_Clock.h
@@ -21,7 +21,7 @@
************************************************************************
*/
-#ifndef M4OSA_CLOCH_H
+#ifndef M4OSA_CLOCK_H
#define M4OSA_CLOCK_H
#include "M4OSA_Types.h"
diff --git a/libvideoeditor/osal/inc/M4OSA_Error.h b/libvideoeditor/osal/inc/M4OSA_Error.h
index 4d59529..75c3177 100755
--- a/libvideoeditor/osal/inc/M4OSA_Error.h
+++ b/libvideoeditor/osal/inc/M4OSA_Error.h
@@ -57,7 +57,7 @@
* @arg coreID: (IN) [M4OSA_UInt32] CoreID to put in the error code
* @arg errorID: (IN) [M4OSA_UInt32] ErrorID to put in the error code*/
#define M4OSA_ERR_CREATE(severity, coreID, errorID)\
- (M4OSA_Int32)((((M4OSA_UInt32)severity)<<30)+((((M4OSA_UInt32)coreID)&0x003FFF)<<16)+(((M4OSA_UInt32)errorID)&0x00FFFF))
+ (M4OSA_UInt32)((((M4OSA_UInt32)severity)<<30)+((((M4OSA_UInt32)coreID)&0x003FFF)<<16)+(((M4OSA_UInt32)errorID)&0x00FFFF))
/** This macro extracts the 3 fields from the error:
* @arg error: (IN) [M4OSA_ERR] Error code
diff --git a/libvideoeditor/vss/src/M4DECODER_Null.c b/libvideoeditor/vss/src/M4DECODER_Null.c
index a0dad30..ce260e5 100755
--- a/libvideoeditor/vss/src/M4DECODER_Null.c
+++ b/libvideoeditor/vss/src/M4DECODER_Null.c
@@ -210,7 +210,7 @@
break;
case M4DECODER_kOptionID_EnableYuvWithEffect:
- pStreamContext->bYuvWithEffectSet = (M4OSA_Bool)pValue;
+ pStreamContext->bYuvWithEffectSet = (M4OSA_Bool)(intptr_t)pValue;
break;
case M4DECODER_kOptionID_YuvWithEffectNonContiguous:
diff --git a/libvideoeditor/vss/stagefrightshells/src/Android.mk b/libvideoeditor/vss/stagefrightshells/src/Android.mk
index e30b85d..9188942 100755
--- a/libvideoeditor/vss/stagefrightshells/src/Android.mk
+++ b/libvideoeditor/vss/stagefrightshells/src/Android.mk
@@ -33,7 +33,6 @@
$(TOP)/frameworks/av/media/libstagefright \
$(TOP)/frameworks/av/media/libstagefright/include \
$(TOP)/frameworks/av/media/libstagefright/rtsp \
- $(call include-path-for, corecg graphics) \
$(TOP)/frameworks/av/libvideoeditor/lvpp \
$(TOP)/frameworks/av/libvideoeditor/osal/inc \
$(TOP)/frameworks/av/libvideoeditor/vss/inc \
diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp
index 3c8915a..99cf9ec 100755
--- a/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp
+++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp
@@ -776,16 +776,16 @@
case M4READER_kOptionID_SetOsaFileReaderFctsPtr:
break;
- case M4READER_3GP_kOptionID_AudioOnly:
+ case static_cast<M4OSA_OptionID>(M4READER_3GP_kOptionID_AudioOnly):
break;
- case M4READER_3GP_kOptionID_VideoOnly:
+ case static_cast<M4OSA_OptionID>(M4READER_3GP_kOptionID_VideoOnly):
break;
- case M4READER_3GP_kOptionID_FastOpenMode:
+ case static_cast<M4OSA_OptionID>(M4READER_3GP_kOptionID_FastOpenMode):
break;
- case M4READER_kOptionID_MaxMetadataSize:
+ case static_cast<M4OSA_OptionID>(M4READER_kOptionID_MaxMetadataSize):
break;
default:
diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorAudioDecoder.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorAudioDecoder.cpp
index 9b35d07..e4c7ea1 100755
--- a/libvideoeditor/vss/stagefrightshells/src/VideoEditorAudioDecoder.cpp
+++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorAudioDecoder.cpp
@@ -809,7 +809,7 @@
pDecoderContext = (VideoEditorAudioDecoder_Context*)pContext;
switch( optionID ) {
- case M4AD_kOptionID_UserParam:
+ case static_cast<M4OSA_UInt32>(M4AD_kOptionID_UserParam):
ALOGV("VideoEditorAudioDecodersetOption UserParam is not supported");
err = M4ERR_NOT_IMPLEMENTED;
break;
diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorMp3Reader.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorMp3Reader.cpp
index af53c54..2e0d05d 100755
--- a/libvideoeditor/vss/stagefrightshells/src/VideoEditorMp3Reader.cpp
+++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorMp3Reader.cpp
@@ -334,8 +334,8 @@
(VideoEditorMp3Reader_Context*)context;
M4OSA_ERR err = M4NO_ERROR;
- ALOGV("VideoEditorMp3Reader_Context begin: optionId: %d Value: %d ",
- (int)optionId,(int)pValue);
+ ALOGV("VideoEditorMp3Reader_Context begin: optionId: %u Value: %p ",
+ optionId, pValue);
M4OSA_DEBUG_IF1((M4OSA_NULL == pReaderContext), M4ERR_PARAMETER,
"invalid context pointer");
diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp
index 5a7237d..d264a2e 100755
--- a/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp
+++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp
@@ -84,17 +84,17 @@
LOG1("displayMetaData kKeyBitRate %d", int32Data);
}
if (meta->findData(kKeyESDS, &type, &data, &size)) {
- LOG1("displayMetaData kKeyESDS type=%d size=%d", type, size);
+ LOG1("displayMetaData kKeyESDS type=%d size=%zu", type, size);
}
if (meta->findData(kKeyAVCC, &type, &data, &size)) {
- LOG1("displayMetaData kKeyAVCC data=0x%X type=%d size=%d",
+ LOG1("displayMetaData kKeyAVCC data=0x%X type=%d size=%zu",
*((unsigned int*)data), type, size);
}
if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
- LOG1("displayMetaData kKeyVorbisInfo type=%d size=%d", type, size);
+ LOG1("displayMetaData kKeyVorbisInfo type=%d size=%zu", type, size);
}
if (meta->findData(kKeyVorbisBooks, &type, &data, &size)) {
- LOG1("displayMetaData kKeyVorbisBooks type=%d size=%d", type, size);
+ LOG1("displayMetaData kKeyVorbisBooks type=%d size=%zu", type, size);
}
if (meta->findInt32(kKeyWantsNALFragments, &int32Data)) {
LOG1("displayMetaData kKeyWantsNALFragments %d", int32Data);
@@ -115,7 +115,7 @@
LOG1("displayMetaData kKeyColorFormat %d", int32Data);
}
if (meta->findPointer(kKeyPlatformPrivate, &ptr)) {
- LOG1("displayMetaData kKeyPlatformPrivate pointer=0x%x", (int32_t) ptr);
+ LOG1("displayMetaData kKeyPlatformPrivate pointer=%p", ptr);
}
if (meta->findCString(kKeyDecoderComponent, &charData)) {
LOG1("displayMetaData kKeyDecoderComponent %s", charData);
@@ -151,7 +151,7 @@
LOG1("displayMetaData kKeyYear %s", charData);
}
if (meta->findData(kKeyAlbumArt, &type, &data, &size)) {
- LOG1("displayMetaData kKeyAlbumArt type=%d size=%d", type, size);
+ LOG1("displayMetaData kKeyAlbumArt type=%d size=%zu", type, size);
}
if (meta->findCString(kKeyAlbumArtMIME, &charData)) {
LOG1("displayMetaData kKeyAlbumArtMIME %s", charData);
@@ -277,7 +277,7 @@
}
if (size < 4) {
- ALOGE("Codec specific data length too short: %d", size);
+ ALOGE("Codec specific data length too short: %zu", size);
return ERROR_MALFORMED;
}
@@ -286,7 +286,7 @@
// 2 bytes for each of the parameter set length field
// plus the 7 bytes for the header
if (size < 4 + 7) {
- ALOGE("Codec specific data length too short: %d", size);
+ ALOGE("Codec specific data length too short: %zu", size);
return ERROR_MALFORMED;
}
@@ -355,7 +355,7 @@
}
if (nSeqParamSets > 0x1F) {
- ALOGE("Too many seq parameter sets (%d) found", nSeqParamSets);
+ ALOGE("Too many seq parameter sets (%zu) found", nSeqParamSets);
return ERROR_MALFORMED;
}
}
@@ -368,7 +368,7 @@
return ERROR_MALFORMED;
}
if (nPicParamSets > 0xFF) {
- ALOGE("Too many pic parameter sets (%d) found", nPicParamSets);
+ ALOGE("Too many pic parameter sets (%zu) found", nPicParamSets);
return ERROR_MALFORMED;
}
}
diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk
index 5d0a87c..2bb6dbe 100644
--- a/media/libeffects/downmix/Android.mk
+++ b/media/libeffects/downmix/Android.mk
@@ -13,7 +13,7 @@
LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
LOCAL_LDLIBS += -ldl
diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c
index f17a6e8..d25dc9b 100644
--- a/media/libeffects/downmix/EffectDownmix.c
+++ b/media/libeffects/downmix/EffectDownmix.c
@@ -61,13 +61,13 @@
// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
- tag : AUDIO_EFFECT_LIBRARY_TAG,
- version : EFFECT_LIBRARY_API_VERSION,
- name : "Downmix Library",
- implementor : "The Android Open Source Project",
- create_effect : DownmixLib_Create,
- release_effect : DownmixLib_Release,
- get_descriptor : DownmixLib_GetDescriptor,
+ .tag = AUDIO_EFFECT_LIBRARY_TAG,
+ .version = EFFECT_LIBRARY_API_VERSION,
+ .name = "Downmix Library",
+ .implementor = "The Android Open Source Project",
+ .create_effect = DownmixLib_Create,
+ .release_effect = DownmixLib_Release,
+ .get_descriptor = DownmixLib_GetDescriptor,
};
@@ -629,7 +629,9 @@
return -EINVAL;
}
- memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
+ if (&pDwmModule->config != pConfig) {
+ memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
+ }
if (init) {
pDownmixer->type = DOWNMIX_TYPE_FOLD;
@@ -707,7 +709,7 @@
case DOWNMIX_PARAM_TYPE:
if (size != sizeof(downmix_type_t)) {
- ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %d, should be %d",
+ ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %zu, should be %zu",
size, sizeof(downmix_type_t));
return -EINVAL;
}
@@ -760,7 +762,7 @@
case DOWNMIX_PARAM_TYPE:
if (*pSize < sizeof(int16_t)) {
- ALOGE("Downmix_getParameter invalid parameter size %d for DOWNMIX_PARAM_TYPE", *pSize);
+ ALOGE("Downmix_getParameter invalid parameter size %zu for DOWNMIX_PARAM_TYPE", *pSize);
return -EINVAL;
}
pValue16 = (int16_t *)pValue;
diff --git a/media/libeffects/factory/Android.mk b/media/libeffects/factory/Android.mk
index 60a6ce5..a932af7 100644
--- a/media/libeffects/factory/Android.mk
+++ b/media/libeffects/factory/Android.mk
@@ -9,7 +9,6 @@
LOCAL_SHARED_LIBRARIES := \
libcutils liblog
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)
LOCAL_MODULE:= libeffects
LOCAL_SHARED_LIBRARIES += libdl
diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c
index f8d6041..6d30d64 100644
--- a/media/libeffects/factory/EffectsFactory.c
+++ b/media/libeffects/factory/EffectsFactory.c
@@ -368,27 +368,21 @@
}
if (e1 == NULL) {
ret = -ENOENT;
- pthread_mutex_unlock(&gLibLock);
goto exit;
}
// release effect in library
if (fx->lib == NULL) {
ALOGW("EffectRelease() fx %p library already unloaded", handle);
- pthread_mutex_unlock(&gLibLock);
} else {
pthread_mutex_lock(&fx->lib->lock);
- // Releasing the gLibLock here as the list access is over as the
- // effect is removed from the list.
- // If the gLibLock is not released, we will have a deadlock situation
- // since we call the sub effect release inside the EffectRelease of Proxy
- pthread_mutex_unlock(&gLibLock);
fx->lib->desc->release_effect(fx->subItfe);
pthread_mutex_unlock(&fx->lib->lock);
}
free(fx);
exit:
+ pthread_mutex_unlock(&gLibLock);
return ret;
}
@@ -404,8 +398,8 @@
// is pointed by the first argument. It searches the gSubEffectList for the
// matching uuid and then copies the corresponding sub effect descriptors
// to the inout param
-int EffectGetSubEffects(const effect_uuid_t *uuid,
- effect_descriptor_t *pDescriptors, size_t size)
+int EffectGetSubEffects(const effect_uuid_t *uuid, sub_effect_entry_t **pSube,
+ size_t size)
{
ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X"
"%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
@@ -413,8 +407,7 @@
uuid->node[3],uuid->node[4],uuid->node[5]);
// Check if the size of the desc buffer is large enough for 2 subeffects
- if ((uuid == NULL) || (pDescriptors == NULL) ||
- (size < 2*sizeof(effect_descriptor_t))) {
+ if ((uuid == NULL) || (pSube == NULL) || (size < 2)) {
ALOGW("NULL pointer or insufficient memory. Cannot query subeffects");
return -EINVAL;
}
@@ -432,11 +425,10 @@
list_elem_t *subefx = e->sub_elem;
while (subefx != NULL) {
subeffect = (sub_effect_entry_t*)subefx->object;
- d = (effect_descriptor_t*)(subeffect->object);
- pDescriptors[count++] = *d;
+ pSube[count++] = subeffect;
subefx = subefx->next;
}
- ALOGV("EffectGetSubEffects end - copied the sub effect descriptors");
+ ALOGV("EffectGetSubEffects end - copied the sub effect structures");
return count;
}
e = e->next;
diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h
index 147ff18..560b485 100644
--- a/media/libeffects/factory/EffectsFactory.h
+++ b/media/libeffects/factory/EffectsFactory.h
@@ -20,7 +20,7 @@
#include <cutils/log.h>
#include <pthread.h>
#include <dirent.h>
-#include <media/EffectsFactoryApi.h>
+#include <hardware/audio_effect.h>
#if __cplusplus
extern "C" {
@@ -66,6 +66,32 @@
void *object;
} sub_effect_entry_t;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Function: EffectGetSubEffects
+//
+// Description: Returns the descriptors of the sub effects of the effect
+// whose uuid is pointed to by first argument.
+//
+// Input:
+// pEffectUuid: pointer to the effect uuid.
+// size: max number of sub_effect_entry_t * in pSube.
+//
+// Input/Output:
+// pSube: address where to return the sub effect structures.
+// Output:
+// returned value: 0 successful operation.
+// -ENODEV factory failed to initialize
+// -EINVAL invalid pEffectUuid or pDescriptor
+// -ENOENT no effect with this uuid found
+// *pDescriptor: updated with the sub effect descriptors.
+//
+////////////////////////////////////////////////////////////////////////////////
+int EffectGetSubEffects(const effect_uuid_t *pEffectUuid,
+ sub_effect_entry_t **pSube,
+ size_t size);
+
#if __cplusplus
} // extern "C"
#endif
diff --git a/media/libeffects/loudness/Android.mk b/media/libeffects/loudness/Android.mk
index dcb7b27..edf964e 100644
--- a/media/libeffects/loudness/Android.mk
+++ b/media/libeffects/loudness/Android.mk
@@ -14,7 +14,7 @@
liblog \
libstlport
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE:= libldnhncr
LOCAL_C_INCLUDES := \
diff --git a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp
index dfc25db..3c2b320 100644
--- a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp
+++ b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp
@@ -56,8 +56,7 @@
int32_t mTargetGainmB;// target gain in mB
// in this implementation, there is no coupling between the compression on the left and right
// channels
- le_fx::AdaptiveDynamicRangeCompression* mCompressorL;
- le_fx::AdaptiveDynamicRangeCompression* mCompressorR;
+ le_fx::AdaptiveDynamicRangeCompression* mCompressor;
};
//
@@ -68,11 +67,10 @@
{
ALOGV(" > LE_reset(%p)", pContext);
- if ((pContext->mCompressorL != NULL) && (pContext->mCompressorR != NULL)) {
+ if (pContext->mCompressor != NULL) {
float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification
ALOGV("LE_reset(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp);
- pContext->mCompressorL->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
- pContext->mCompressorR->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
+ pContext->mCompressor->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
} else {
ALOGE("LE_reset(%p): null compressors, can't apply target gain", pContext);
}
@@ -176,13 +174,9 @@
float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification
ALOGV("LE_init(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp);
- if (pContext->mCompressorL == NULL) {
- pContext->mCompressorL = new le_fx::AdaptiveDynamicRangeCompression();
- pContext->mCompressorL->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
- }
- if (pContext->mCompressorR == NULL) {
- pContext->mCompressorR = new le_fx::AdaptiveDynamicRangeCompression();
- pContext->mCompressorR->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
+ if (pContext->mCompressor == NULL) {
+ pContext->mCompressor = new le_fx::AdaptiveDynamicRangeCompression();
+ pContext->mCompressor->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
}
LE_setConfig(pContext, &pContext->mConfig);
@@ -215,8 +209,7 @@
pContext->mItfe = &gLEInterface;
pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED;
- pContext->mCompressorL = NULL;
- pContext->mCompressorR = NULL;
+ pContext->mCompressor = NULL;
ret = LE_init(pContext);
if (ret < 0) {
ALOGW("LELib_Create() init failed");
@@ -242,13 +235,9 @@
return -EINVAL;
}
pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED;
- if (pContext->mCompressorL != NULL) {
- delete pContext->mCompressorL;
- pContext->mCompressorL = NULL;
- }
- if (pContext->mCompressorR != NULL) {
- delete pContext->mCompressorR;
- pContext->mCompressorR = NULL;
+ if (pContext->mCompressor != NULL) {
+ delete pContext->mCompressor;
+ pContext->mCompressor = NULL;
}
delete pContext;
@@ -293,11 +282,14 @@
//ALOGV("LE about to process %d samples", inBuffer->frameCount);
uint16_t inIdx;
float inputAmp = pow(10, pContext->mTargetGainmB/2000.0f);
+ float leftSample, rightSample;
for (inIdx = 0 ; inIdx < inBuffer->frameCount ; inIdx++) {
- inBuffer->s16[2*inIdx] = pContext->mCompressorL->Compress(
- inputAmp * (float)inBuffer->s16[2*inIdx]);
- inBuffer->s16[2*inIdx +1] = pContext->mCompressorR->Compress(
- inputAmp * (float)inBuffer->s16[2*inIdx +1]);
+ // makeup gain is applied on the input of the compressor
+ leftSample = inputAmp * (float)inBuffer->s16[2*inIdx];
+ rightSample = inputAmp * (float)inBuffer->s16[2*inIdx +1];
+ pContext->mCompressor->Compress(&leftSample, &rightSample);
+ inBuffer->s16[2*inIdx] = (int16_t) leftSample;
+ inBuffer->s16[2*inIdx +1] = (int16_t) rightSample;
}
if (inBuffer->raw != outBuffer->raw) {
@@ -461,13 +453,13 @@
// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
- tag : AUDIO_EFFECT_LIBRARY_TAG,
- version : EFFECT_LIBRARY_API_VERSION,
- name : "Loudness Enhancer Library",
- implementor : "The Android Open Source Project",
- create_effect : LELib_Create,
- release_effect : LELib_Release,
- get_descriptor : LELib_GetDescriptor,
+ .tag = AUDIO_EFFECT_LIBRARY_TAG,
+ .version = EFFECT_LIBRARY_API_VERSION,
+ .name = "Loudness Enhancer Library",
+ .implementor = "The Android Open Source Project",
+ .create_effect = LELib_Create,
+ .release_effect = LELib_Release,
+ .get_descriptor = LELib_GetDescriptor,
};
}; // extern "C"
diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h b/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h
index fed8c2a..da75ceb 100644
--- a/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h
+++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h
@@ -35,7 +35,7 @@
float target_gain) {
const float decibel = target_gain_to_knee_threshold_.Interpolate(
target_gain);
- ALOGE("set_knee_threshold_via_target_gain: decibel =%.3f", decibel);
+ ALOGV("set_knee_threshold_via_target_gain: decibel =%.3fdB", decibel);
set_knee_threshold(decibel);
}
diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
index 2bbd043..7bd068e 100644
--- a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
+++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
@@ -102,5 +102,40 @@
return x;
}
+void AdaptiveDynamicRangeCompression::Compress(float *x1, float *x2) {
+ // Taking the maximum amplitude of both channels
+ const float max_abs_x = std::max(std::fabs(*x1),
+ std::max(std::fabs(*x2), kMinLogAbsValue));
+ const float max_abs_x_dB = math::fast_log(max_abs_x);
+ // Subtract Threshold from log-encoded input to get the amount of overshoot
+ const float overshoot = max_abs_x_dB - knee_threshold_;
+ // Hard half-wave rectifier
+ const float rect = std::max(overshoot, 0.0f);
+ // Multiply rectified overshoot with slope
+ const float cv = rect * slope_;
+ const float prev_state = state_;
+ if (cv <= state_) {
+ state_ = alpha_attack_ * state_ + (1.0f - alpha_attack_) * cv;
+ } else {
+ state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv;
+ }
+ compressor_gain_ *=
+ math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state);
+ *x1 *= compressor_gain_;
+ if (*x1 > kFixedPointLimit) {
+ *x1 = kFixedPointLimit;
+ }
+ if (*x1 < -kFixedPointLimit) {
+ *x1 = -kFixedPointLimit;
+ }
+ *x2 *= compressor_gain_;
+ if (*x2 > kFixedPointLimit) {
+ *x2 = kFixedPointLimit;
+ }
+ if (*x2 < -kFixedPointLimit) {
+ *x2 = -kFixedPointLimit;
+ }
+}
+
} // namespace le_fx
diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression.h b/media/libeffects/loudness/dsp/core/dynamic_range_compression.h
index 4c015df..2821a78 100644
--- a/media/libeffects/loudness/dsp/core/dynamic_range_compression.h
+++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression.h
@@ -55,6 +55,9 @@
// log(.) and exp(.).
float Compress(float x);
+ // Stereo channel version of the compressor
+ void Compress(float *x1, float *x2);
+
// This version is slower than Compress(.) but faster than CompressSlow(.)
float CompressNormalSpeed(float x);
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c
index c4767a8..e01c1c5 100644
--- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c
@@ -25,6 +25,7 @@
#include "LVEQNB.h"
#include "LVEQNB_Private.h"
#include "InstAlloc.h"
+#include <string.h> /* For memset */
/****************************************************************************************/
/* */
diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk
index f1af389..68ba34c 100644
--- a/media/libeffects/lvm/wrapper/Android.mk
+++ b/media/libeffects/lvm/wrapper/Android.mk
@@ -13,7 +13,7 @@
LOCAL_MODULE:= libbundlewrapper
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_STATIC_LIBRARIES += libmusicbundle
@@ -42,7 +42,7 @@
LOCAL_MODULE:= libreverbwrapper
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_STATIC_LIBRARIES += libreverb
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 85232e7..58d7767 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -138,22 +138,22 @@
int BassBoost_setParameter (EffectContext *pContext, void *pParam, void *pValue);
int BassBoost_getParameter (EffectContext *pContext,
void *pParam,
- size_t *pValueSize,
+ uint32_t *pValueSize,
void *pValue);
int Virtualizer_setParameter (EffectContext *pContext, void *pParam, void *pValue);
int Virtualizer_getParameter (EffectContext *pContext,
void *pParam,
- size_t *pValueSize,
+ uint32_t *pValueSize,
void *pValue);
int Equalizer_setParameter (EffectContext *pContext, void *pParam, void *pValue);
int Equalizer_getParameter (EffectContext *pContext,
void *pParam,
- size_t *pValueSize,
+ uint32_t *pValueSize,
void *pValue);
int Volume_setParameter (EffectContext *pContext, void *pParam, void *pValue);
int Volume_getParameter (EffectContext *pContext,
void *pParam,
- size_t *pValueSize,
+ uint32_t *pValueSize,
void *pValue);
int Effect_setEnabled(EffectContext *pContext, bool enabled);
@@ -1758,7 +1758,7 @@
int BassBoost_getParameter(EffectContext *pContext,
void *pParam,
- size_t *pValueSize,
+ uint32_t *pValueSize,
void *pValue){
int status = 0;
int32_t *pParamTemp = (int32_t *)pParam;
@@ -1876,7 +1876,7 @@
int Virtualizer_getParameter(EffectContext *pContext,
void *pParam,
- size_t *pValueSize,
+ uint32_t *pValueSize,
void *pValue){
int status = 0;
int32_t *pParamTemp = (int32_t *)pParam;
@@ -1994,7 +1994,7 @@
//----------------------------------------------------------------------------
int Equalizer_getParameter(EffectContext *pContext,
void *pParam,
- size_t *pValueSize,
+ uint32_t *pValueSize,
void *pValue){
int status = 0;
int bMute = 0;
@@ -2252,7 +2252,7 @@
int Volume_getParameter(EffectContext *pContext,
void *pParam,
- size_t *pValueSize,
+ uint32_t *pValueSize,
void *pValue){
int status = 0;
int bMute = 0;
@@ -2830,7 +2830,7 @@
p->status = android::BassBoost_getParameter(pContext,
p->data,
- (size_t *)&p->vsize,
+ &p->vsize,
p->data + voffset);
*replySize = sizeof(effect_param_t) + voffset + p->vsize;
@@ -2860,8 +2860,8 @@
int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
p->status = android::Virtualizer_getParameter(pContext,
- (void *)p->data,
- (size_t *)&p->vsize,
+ (void *)p->data,
+ &p->vsize,
p->data + voffset);
*replySize = sizeof(effect_param_t) + voffset + p->vsize;
@@ -2925,7 +2925,7 @@
p->status = android::Volume_getParameter(pContext,
(void *)p->data,
- (size_t *)&p->vsize,
+ &p->vsize,
p->data + voffset);
*replySize = sizeof(effect_param_t) + voffset + p->vsize;
@@ -3267,13 +3267,13 @@
// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
- tag : AUDIO_EFFECT_LIBRARY_TAG,
- version : EFFECT_LIBRARY_API_VERSION,
- name : "Effect Bundle Library",
- implementor : "NXP Software Ltd.",
- create_effect : android::EffectCreate,
- release_effect : android::EffectRelease,
- get_descriptor : android::EffectGetDescriptor,
+ .tag = AUDIO_EFFECT_LIBRARY_TAG,
+ .version = EFFECT_LIBRARY_API_VERSION,
+ .name = "Effect Bundle Library",
+ .implementor = "NXP Software Ltd.",
+ .create_effect = android::EffectCreate,
+ .release_effect = android::EffectRelease,
+ .get_descriptor = android::EffectGetDescriptor,
};
}
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 8a96212..0367302 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -2149,13 +2149,13 @@
// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
- tag : AUDIO_EFFECT_LIBRARY_TAG,
- version : EFFECT_LIBRARY_API_VERSION,
- name : "Reverb Library",
- implementor : "NXP Software Ltd.",
- create_effect : android::EffectCreate,
- release_effect : android::EffectRelease,
- get_descriptor : android::EffectGetDescriptor,
+ .tag = AUDIO_EFFECT_LIBRARY_TAG,
+ .version = EFFECT_LIBRARY_API_VERSION,
+ .name = "Reverb Library",
+ .implementor = "NXP Software Ltd.",
+ .create_effect = android::EffectCreate,
+ .release_effect = android::EffectRelease,
+ .get_descriptor = android::EffectGetDescriptor,
};
}
diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk
index c344352..9e8cb83 100644
--- a/media/libeffects/preprocessing/Android.mk
+++ b/media/libeffects/preprocessing/Android.mk
@@ -5,7 +5,7 @@
LOCAL_MODULE:= libaudiopreprocessing
LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_SRC_FILES:= \
PreProcessing.cpp
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index 25586e8..c56ff72 100644
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -1233,8 +1233,8 @@
if (session->framesIn < session->frameCount) {
return 0;
}
- size_t frIn = session->framesIn;
- size_t frOut = session->apmFrameCount;
+ spx_uint32_t frIn = session->framesIn;
+ spx_uint32_t frOut = session->apmFrameCount;
if (session->inChannelCount == 1) {
speex_resampler_process_int(session->inResampler,
0,
@@ -1290,8 +1290,8 @@
}
if (session->outResampler != NULL) {
- size_t frIn = session->apmFrameCount;
- size_t frOut = session->frameCount;
+ spx_uint32_t frIn = session->apmFrameCount;
+ spx_uint32_t frOut = session->frameCount;
if (session->inChannelCount == 1) {
speex_resampler_process_int(session->outResampler,
0,
@@ -1754,8 +1754,8 @@
if (session->framesRev < session->frameCount) {
return 0;
}
- size_t frIn = session->framesRev;
- size_t frOut = session->apmFrameCount;
+ spx_uint32_t frIn = session->framesRev;
+ spx_uint32_t frOut = session->apmFrameCount;
if (session->inChannelCount == 1) {
speex_resampler_process_int(session->revResampler,
0,
@@ -1892,13 +1892,13 @@
// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
- tag : AUDIO_EFFECT_LIBRARY_TAG,
- version : EFFECT_LIBRARY_API_VERSION,
- name : "Audio Preprocessing Library",
- implementor : "The Android Open Source Project",
- create_effect : PreProcessingLib_Create,
- release_effect : PreProcessingLib_Release,
- get_descriptor : PreProcessingLib_GetDescriptor
+ .tag = AUDIO_EFFECT_LIBRARY_TAG,
+ .version = EFFECT_LIBRARY_API_VERSION,
+ .name = "Audio Preprocessing Library",
+ .implementor = "The Android Open Source Project",
+ .create_effect = PreProcessingLib_Create,
+ .release_effect = PreProcessingLib_Release,
+ .get_descriptor = PreProcessingLib_GetDescriptor
};
}; // extern "C"
diff --git a/media/libeffects/proxy/Android.mk b/media/libeffects/proxy/Android.mk
index 01b3be1..b438796 100644
--- a/media/libeffects/proxy/Android.mk
+++ b/media/libeffects/proxy/Android.mk
@@ -15,7 +15,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= libeffectproxy
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE_TAGS := optional
@@ -28,7 +28,8 @@
LOCAL_C_INCLUDES := \
system/media/audio_effects/include \
- bionic/libc/include
+ bionic/libc/include \
+ frameworks/av/media/libeffects/factory
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp
index b3304b7..62d3fd3 100644
--- a/media/libeffects/proxy/EffectProxy.cpp
+++ b/media/libeffects/proxy/EffectProxy.cpp
@@ -30,9 +30,10 @@
// This is a dummy proxy descriptor just to return to Factory during the initial
// GetDescriptor call. Later in the factory, it is replaced with the
// SW sub effect descriptor
+// proxy UUID af8da7e0-2ca1-11e3-b71d-0002a5d5c51b
const effect_descriptor_t gProxyDescriptor = {
EFFECT_UUID_INITIALIZER, // type
- EFFECT_UUID_INITIALIZER, // uuid
+ {0xaf8da7e0, 0x2ca1, 0x11e3, 0xb71d, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }}, // uuid
EFFECT_CONTROL_API_VERSION, //version of effect control API
(EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST |
EFFECT_FLAG_VOLUME_CTRL), // effect capability flags
@@ -55,6 +56,8 @@
effect_handle_t *pHandle) {
effect_descriptor_t* desc;
+ audio_effect_library_t** aeli;
+ sub_effect_entry_t** sube;
EffectContext* pContext;
if (pHandle == NULL || uuid == NULL) {
ALOGE("EffectProxyCreate() called with NULL pointer");
@@ -73,31 +76,52 @@
// Get the HW and SW sub effect descriptors from the effects factory
desc = new effect_descriptor_t[SUB_FX_COUNT];
+ aeli = new audio_effect_library_t*[SUB_FX_COUNT];
+ sube = new sub_effect_entry_t*[SUB_FX_COUNT];
+ pContext->sube = new sub_effect_entry_t*[SUB_FX_COUNT];
pContext->desc = new effect_descriptor_t[SUB_FX_COUNT];
- int retValue = EffectGetSubEffects(uuid, desc,
- sizeof(effect_descriptor_t) * SUB_FX_COUNT);
+ pContext->aeli = new audio_effect_library_t*[SUB_FX_COUNT];
+ int retValue = EffectGetSubEffects(uuid, sube, SUB_FX_COUNT);
// EffectGetSubEffects returns the number of sub-effects copied.
if (retValue != SUB_FX_COUNT) {
ALOGE("EffectCreate() could not get the sub effects");
- delete desc;
- delete pContext->desc;
+ delete[] sube;
+ delete[] desc;
+ delete[] aeli;
+ delete[] pContext->sube;
+ delete[] pContext->desc;
+ delete[] pContext->aeli;
return -EINVAL;
}
// Check which is the HW descriptor and copy the descriptors
// to the Context desc array
// Also check if there is only one HW and one SW descriptor.
// HW descriptor alone has the HW_TUNNEL flag.
+ desc[0] = *(effect_descriptor_t*)(sube[0])->object;
+ desc[1] = *(effect_descriptor_t*)(sube[1])->object;
+ aeli[0] = sube[0]->lib->desc;
+ aeli[1] = sube[1]->lib->desc;
if ((desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL) &&
!(desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) {
+ pContext->sube[SUB_FX_OFFLOAD] = sube[0];
pContext->desc[SUB_FX_OFFLOAD] = desc[0];
+ pContext->aeli[SUB_FX_OFFLOAD] = aeli[0];
+ pContext->sube[SUB_FX_HOST] = sube[1];
pContext->desc[SUB_FX_HOST] = desc[1];
+ pContext->aeli[SUB_FX_HOST] = aeli[1];
}
else if ((desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL) &&
!(desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) {
+ pContext->sube[SUB_FX_HOST] = sube[0];
pContext->desc[SUB_FX_HOST] = desc[0];
+ pContext->aeli[SUB_FX_HOST] = aeli[0];
+ pContext->sube[SUB_FX_OFFLOAD] = sube[1];
pContext->desc[SUB_FX_OFFLOAD] = desc[1];
+ pContext->aeli[SUB_FX_OFFLOAD] = aeli[1];
}
- delete desc;
+ delete[] desc;
+ delete[] aeli;
+ delete[] sube;
#if (LOG_NDEBUG == 0)
effect_uuid_t uuid_print = pContext->desc[SUB_FX_HOST].uuid;
ALOGV("EffectCreate() UUID of HOST: %08X-%04X-%04X-%04X-%02X%02X%02X%02X"
@@ -127,13 +151,15 @@
return -EINVAL;
}
ALOGV("EffectRelease");
- delete pContext->desc;
+ delete[] pContext->desc;
free(pContext->replyData);
if (pContext->eHandle[SUB_FX_HOST])
- EffectRelease(pContext->eHandle[SUB_FX_HOST]);
+ pContext->aeli[SUB_FX_HOST]->release_effect(pContext->eHandle[SUB_FX_HOST]);
if (pContext->eHandle[SUB_FX_OFFLOAD])
- EffectRelease(pContext->eHandle[SUB_FX_OFFLOAD]);
+ pContext->aeli[SUB_FX_OFFLOAD]->release_effect(pContext->eHandle[SUB_FX_OFFLOAD]);
+ delete[] pContext->aeli;
+ delete[] pContext->sube;
delete pContext;
pContext = NULL;
return 0;
@@ -186,7 +212,8 @@
}
if (pContext->eHandle[SUB_FX_HOST] == NULL) {
ALOGV("Effect_command() Calling HOST EffectCreate");
- status = EffectCreate(&pContext->desc[SUB_FX_HOST].uuid,
+ status = pContext->aeli[SUB_FX_HOST]->create_effect(
+ &pContext->desc[SUB_FX_HOST].uuid,
pContext->sessionId, pContext->ioId,
&(pContext->eHandle[SUB_FX_HOST]));
if (status != NO_ERROR || (pContext->eHandle[SUB_FX_HOST] == NULL)) {
@@ -196,11 +223,13 @@
}
if (pContext->eHandle[SUB_FX_OFFLOAD] == NULL) {
ALOGV("Effect_command() Calling OFFLOAD EffectCreate");
- status = EffectCreate(&pContext->desc[SUB_FX_OFFLOAD].uuid,
+ status = pContext->aeli[SUB_FX_OFFLOAD]->create_effect(
+ &pContext->desc[SUB_FX_OFFLOAD].uuid,
pContext->sessionId, pContext->ioId,
&(pContext->eHandle[SUB_FX_OFFLOAD]));
if (status != NO_ERROR || (pContext->eHandle[SUB_FX_OFFLOAD] == NULL)) {
ALOGV("Effect_command() Error creating HW effect");
+ pContext->eHandle[SUB_FX_OFFLOAD] = NULL;
// Do not return error here as SW effect is created
// Return error if the CMD_OFFLOAD sends the index as OFFLOAD
}
@@ -232,11 +261,17 @@
// Update the DSP wrapper with the new ioHandle.
// Pass the OFFLOAD command to the wrapper.
// The DSP wrapper needs to handle this CMD
- if (pContext->eHandle[SUB_FX_OFFLOAD])
- status = (*pContext->eHandle[SUB_FX_OFFLOAD])->command(
- pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize,
- pCmdData, replySize, pReplyData);
- return status;
+ if (pContext->eHandle[SUB_FX_OFFLOAD]) {
+ ALOGV("Effect_command: Calling OFFLOAD command");
+ return (*pContext->eHandle[SUB_FX_OFFLOAD])->command(
+ pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize,
+ pCmdData, replySize, pReplyData);
+ }
+ *(int*)pReplyData = NO_ERROR;
+ ALOGV("Effect_command OFFLOAD return 0, replyData %d",
+ *(int*)pReplyData);
+
+ return NO_ERROR;
}
int index = pContext->index;
@@ -328,11 +363,11 @@
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
- tag : AUDIO_EFFECT_LIBRARY_TAG,
- version : EFFECT_LIBRARY_API_VERSION,
- name : "Effect Proxy",
- implementor : "AOSP",
- create_effect : android::EffectProxyCreate,
- release_effect : android::EffectProxyRelease,
- get_descriptor : android::EffectProxyGetDescriptor,
+ .tag = AUDIO_EFFECT_LIBRARY_TAG,
+ .version = EFFECT_LIBRARY_API_VERSION,
+ .name = "Effect Proxy",
+ .implementor = "AOSP",
+ .create_effect = android::EffectProxyCreate,
+ .release_effect = android::EffectProxyRelease,
+ .get_descriptor = android::EffectProxyGetDescriptor,
};
diff --git a/media/libeffects/proxy/EffectProxy.h b/media/libeffects/proxy/EffectProxy.h
index acbe17e..046b93e 100644
--- a/media/libeffects/proxy/EffectProxy.h
+++ b/media/libeffects/proxy/EffectProxy.h
@@ -16,6 +16,8 @@
#include <hardware/audio.h>
#include <hardware/audio_effect.h>
+#include "EffectsFactory.h"
+
namespace android {
enum {
SUB_FX_HOST, // Index of HOST in the descriptor and handle arrays
@@ -62,7 +64,9 @@
struct EffectContext {
const struct effect_interface_s *common_itfe; // Holds the itfe of the Proxy
+ sub_effect_entry_t** sube; // Points to the sub effects
effect_descriptor_t* desc; // Points to the sub effect descriptors
+ audio_effect_library_t** aeli; // Points to the sub effect aeli
effect_handle_t eHandle[SUB_FX_COUNT]; // The effect handles of the sub effects
int index; // The index that is currently active - HOST or OFFLOAD
int32_t sessionId; // The sessiond in which the effect is created.
diff --git a/media/libeffects/testlibs/Android.mk_ b/media/libeffects/testlibs/Android.mk_
index 2954908..672ebba 100644
--- a/media/libeffects/testlibs/Android.mk_
+++ b/media/libeffects/testlibs/Android.mk_
@@ -11,7 +11,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE:= libreverbtest
ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
@@ -47,7 +47,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE:= libequalizertest
ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
index e196eb2..c92c543 100644
--- a/media/libeffects/visualizer/Android.mk
+++ b/media/libeffects/visualizer/Android.mk
@@ -13,11 +13,10 @@
liblog \
libdl
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE:= libvisualizer
LOCAL_C_INCLUDES := \
- $(call include-path-for, graphics corecg) \
$(call include-path-for, audio-effects)
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index dc403ab..5bdaa03 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -544,56 +544,57 @@
break;
- case VISUALIZER_CMD_CAPTURE:
- if (pReplyData == NULL || *replySize != pContext->mCaptureSize) {
- ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
- *replySize, pContext->mCaptureSize);
+ case VISUALIZER_CMD_CAPTURE: {
+ int32_t captureSize = pContext->mCaptureSize;
+ if (pReplyData == NULL || *replySize != captureSize) {
+ ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d captureSize %d",
+ *replySize, captureSize);
return -EINVAL;
}
if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
- int32_t latencyMs = pContext->mLatency;
const uint32_t deltaMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext);
- latencyMs -= deltaMs;
- if (latencyMs < 0) {
- latencyMs = 0;
- }
- const uint32_t deltaSmpl = pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;
-
- int32_t capturePoint = pContext->mCaptureIdx - pContext->mCaptureSize - deltaSmpl;
- int32_t captureSize = pContext->mCaptureSize;
- if (capturePoint < 0) {
- int32_t size = -capturePoint;
- if (size > captureSize) {
- size = captureSize;
- }
- memcpy(pReplyData,
- pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint,
- size);
- pReplyData = (char *)pReplyData + size;
- captureSize -= size;
- capturePoint = 0;
- }
- memcpy(pReplyData,
- pContext->mCaptureBuf + capturePoint,
- captureSize);
-
// if audio framework has stopped playing audio although the effect is still
// active we must clear the capture buffer to return silence
if ((pContext->mLastCaptureIdx == pContext->mCaptureIdx) &&
- (pContext->mBufferUpdateTime.tv_sec != 0)) {
- if (deltaMs > MAX_STALL_TIME_MS) {
+ (pContext->mBufferUpdateTime.tv_sec != 0) &&
+ (deltaMs > MAX_STALL_TIME_MS)) {
ALOGV("capture going to idle");
pContext->mBufferUpdateTime.tv_sec = 0;
- memset(pReplyData, 0x80, pContext->mCaptureSize);
+ memset(pReplyData, 0x80, captureSize);
+ } else {
+ int32_t latencyMs = pContext->mLatency;
+ latencyMs -= deltaMs;
+ if (latencyMs < 0) {
+ latencyMs = 0;
}
+ const uint32_t deltaSmpl =
+ pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;
+ int32_t capturePoint = pContext->mCaptureIdx - captureSize - deltaSmpl;
+
+ if (capturePoint < 0) {
+ int32_t size = -capturePoint;
+ if (size > captureSize) {
+ size = captureSize;
+ }
+ memcpy(pReplyData,
+ pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint,
+ size);
+ pReplyData = (char *)pReplyData + size;
+ captureSize -= size;
+ capturePoint = 0;
+ }
+ memcpy(pReplyData,
+ pContext->mCaptureBuf + capturePoint,
+ captureSize);
}
+
pContext->mLastCaptureIdx = pContext->mCaptureIdx;
} else {
- memset(pReplyData, 0x80, pContext->mCaptureSize);
+ memset(pReplyData, 0x80, captureSize);
}
- break;
+ } break;
case VISUALIZER_CMD_MEASURE: {
uint16_t peakU16 = 0;
@@ -678,13 +679,13 @@
// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
- tag : AUDIO_EFFECT_LIBRARY_TAG,
- version : EFFECT_LIBRARY_API_VERSION,
- name : "Visualizer Library",
- implementor : "The Android Open Source Project",
- create_effect : VisualizerLib_Create,
- release_effect : VisualizerLib_Release,
- get_descriptor : VisualizerLib_GetDescriptor,
+ .tag = AUDIO_EFFECT_LIBRARY_TAG,
+ .version = EFFECT_LIBRARY_API_VERSION,
+ .name = "Visualizer Library",
+ .implementor = "The Android Open Source Project",
+ .create_effect = VisualizerLib_Create,
+ .release_effect = VisualizerLib_Release,
+ .get_descriptor = VisualizerLib_GetDescriptor,
};
}; // extern "C"
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 56e7787..f3770e4 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -25,6 +25,8 @@
AudioRecord.cpp \
AudioSystem.cpp \
mediaplayer.cpp \
+ IMediaHTTPConnection.cpp \
+ IMediaHTTPService.cpp \
IMediaLogService.cpp \
IMediaPlayerService.cpp \
IMediaPlayerClient.cpp \
@@ -44,7 +46,7 @@
IAudioPolicyService.cpp \
MediaScanner.cpp \
MediaScannerClient.cpp \
- autodetect.cpp \
+ CharacterEncodingDetector.cpp \
IMediaDeathNotifier.cpp \
MediaProfiles.cpp \
IEffect.cpp \
@@ -58,26 +60,34 @@
LOCAL_SRC_FILES += ../libnbaio/roundup.c
-# for <cutils/atomic-inline.h>
-LOCAL_CFLAGS += -DANDROID_SMP=$(if $(findstring true,$(TARGET_CPU_SMP)),1,0)
-LOCAL_SRC_FILES += SingleStateQueue.cpp
-LOCAL_CFLAGS += -DSINGLE_STATE_QUEUE_INSTANTIATIONS='"SingleStateQueueInstantiations.cpp"'
-# Consider a separate a library for SingleStateQueueInstantiations.
-
LOCAL_SHARED_LIBRARIES := \
- libui liblog libcutils libutils libbinder libsonivox libicuuc libexpat \
+ libui liblog libcutils libutils libbinder libsonivox libicuuc libicui18n libexpat \
libcamera_client libstagefright_foundation \
- libgui libdl libaudioutils
+ libgui libdl libaudioutils libnbaio
+
+LOCAL_STATIC_LIBRARIES += libinstantssq
LOCAL_WHOLE_STATIC_LIBRARY := libmedia_helper
LOCAL_MODULE:= libmedia
LOCAL_C_INCLUDES := \
- $(call include-path-for, graphics corecg) \
$(TOP)/frameworks/native/include/media/openmax \
external/icu4c/common \
+ external/icu4c/i18n \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils)
include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+# for <cutils/atomic-inline.h>
+LOCAL_CFLAGS += -DANDROID_SMP=$(if $(findstring true,$(TARGET_CPU_SMP)),1,0)
+LOCAL_SRC_FILES += SingleStateQueue.cpp
+LOCAL_CFLAGS += -DSINGLE_STATE_QUEUE_INSTANTIATIONS='"SingleStateQueueInstantiations.cpp"'
+
+LOCAL_MODULE := libinstantssq
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 8dfffb3..35f6557 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -380,9 +380,9 @@
}
void AudioEffect::commandExecuted(uint32_t cmdCode,
- uint32_t cmdSize,
+ uint32_t cmdSize __unused,
void *cmdData,
- uint32_t replySize,
+ uint32_t replySize __unused,
void *replyData)
{
if (cmdData == NULL || replyData == NULL) {
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index c5a7777..961b0a2 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -41,37 +41,29 @@
return BAD_VALUE;
}
- // default to 0 in case of error
- *frameCount = 0;
-
- size_t size = 0;
+ size_t size;
status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size);
if (status != NO_ERROR) {
- ALOGE("AudioSystem could not query the input buffer size; status %d", status);
- return NO_INIT;
+ ALOGE("AudioSystem could not query the input buffer size for sampleRate %u, format %#x, "
+ "channelMask %#x; status %d", sampleRate, format, channelMask, status);
+ return status;
}
- if (size == 0) {
- ALOGE("Unsupported configuration: sampleRate %u, format %d, channelMask %#x",
+ // We double the size of input buffer for ping pong use of record buffer.
+ // Assumes audio_is_linear_pcm(format)
+ if ((*frameCount = (size * 2) / (popcount(channelMask) * audio_bytes_per_sample(format))) == 0) {
+ ALOGE("Unsupported configuration: sampleRate %u, format %#x, channelMask %#x",
sampleRate, format, channelMask);
return BAD_VALUE;
}
- // We double the size of input buffer for ping pong use of record buffer.
- size <<= 1;
-
- // Assumes audio_is_linear_pcm(format)
- uint32_t channelCount = popcount(channelMask);
- size /= channelCount * audio_bytes_per_sample(format);
-
- *frameCount = size;
return NO_ERROR;
}
// ---------------------------------------------------------------------------
AudioRecord::AudioRecord()
- : mStatus(NO_INIT), mSessionId(0),
+ : mStatus(NO_INIT), mSessionId(AUDIO_SESSION_ALLOCATE),
mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
{
}
@@ -81,14 +73,14 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount,
+ size_t frameCount,
callback_t cbf,
void* user,
- int notificationFrames,
+ uint32_t notificationFrames,
int sessionId,
transfer_type transferType,
- audio_input_flags_t flags)
- : mStatus(NO_INIT), mSessionId(0),
+ audio_input_flags_t flags __unused)
+ : mStatus(NO_INIT), mSessionId(AUDIO_SESSION_ALLOCATE),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
mProxy(NULL)
@@ -110,12 +102,10 @@
mAudioRecordThread->requestExitAndWait();
mAudioRecordThread.clear();
}
- if (mAudioRecord != 0) {
- mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
- mAudioRecord.clear();
- }
+ mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
+ mAudioRecord.clear();
IPCThreadState::self()->flushCommands();
- AudioSystem::releaseAudioSessionId(mSessionId);
+ AudioSystem::releaseAudioSessionId(mSessionId, -1);
}
}
@@ -124,15 +114,20 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCountInt,
+ size_t frameCount,
callback_t cbf,
void* user,
- int notificationFrames,
+ uint32_t notificationFrames,
bool threadCanCallJava,
int sessionId,
transfer_type transferType,
audio_input_flags_t flags)
{
+ ALOGV("set(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
+ "notificationFrames %u, sessionId %d, transferType %d, flags %#x",
+ inputSource, sampleRate, format, channelMask, frameCount, notificationFrames,
+ sessionId, transferType, flags);
+
switch (transferType) {
case TRANSFER_DEFAULT:
if (cbf == NULL || threadCanCallJava) {
@@ -156,23 +151,15 @@
}
mTransfer = transferType;
- // FIXME "int" here is legacy and will be replaced by size_t later
- if (frameCountInt < 0) {
- ALOGE("Invalid frame count %d", frameCountInt);
- return BAD_VALUE;
- }
- size_t frameCount = frameCountInt;
-
- ALOGV("set(): sampleRate %u, channelMask %#x, frameCount %u", sampleRate, channelMask,
- frameCount);
-
AutoMutex lock(mLock);
+ // invariant that mAudioRecord != 0 is true only after set() returns successfully
if (mAudioRecord != 0) {
ALOGE("Track already in use");
return INVALID_OPERATION;
}
+ // handle default values first.
if (inputSource == AUDIO_SOURCE_DEFAULT) {
inputSource = AUDIO_SOURCE_MIC;
}
@@ -191,12 +178,12 @@
// validate parameters
if (!audio_is_valid_format(format)) {
- ALOGE("Invalid format %d", format);
+ ALOGE("Invalid format %#x", format);
return BAD_VALUE;
}
// Temporary restriction: AudioFlinger currently supports 16-bit PCM only
if (format != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("Format %d is not supported", format);
+ ALOGE("Format %#x is not supported", format);
return BAD_VALUE;
}
mFormat = format;
@@ -209,15 +196,19 @@
uint32_t channelCount = popcount(channelMask);
mChannelCount = channelCount;
- // Assumes audio_is_linear_pcm(format), else sizeof(uint8_t)
- mFrameSize = channelCount * audio_bytes_per_sample(format);
+ if (audio_is_linear_pcm(format)) {
+ mFrameSize = channelCount * audio_bytes_per_sample(format);
+ } else {
+ mFrameSize = sizeof(uint8_t);
+ }
// validate framecount
- size_t minFrameCount = 0;
+ size_t minFrameCount;
status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
sampleRate, format, channelMask);
if (status != NO_ERROR) {
- ALOGE("getMinFrameCount() failed; status %d", status);
+ ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; status %d",
+ sampleRate, format, channelMask, status);
return status;
}
ALOGV("AudioRecord::set() minFrameCount = %d", minFrameCount);
@@ -228,12 +219,13 @@
ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
return BAD_VALUE;
}
- mFrameCount = frameCount;
+ // mFrameCount is initialized in openRecord_l
+ mReqFrameCount = frameCount;
mNotificationFramesReq = notificationFrames;
mNotificationFramesAct = 0;
- if (sessionId == 0 ) {
+ if (sessionId == AUDIO_SESSION_ALLOCATE) {
mSessionId = AudioSystem::newAudioSessionId();
} else {
mSessionId = sessionId;
@@ -241,26 +233,27 @@
ALOGV("set(): mSessionId %d", mSessionId);
mFlags = flags;
-
- // create the IAudioRecord
- status = openRecord_l(0 /*epoch*/);
- if (status) {
- return status;
- }
+ mCbf = cbf;
if (cbf != NULL) {
mAudioRecordThread = new AudioRecordThread(*this, threadCanCallJava);
mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO);
}
+ // create the IAudioRecord
+ status = openRecord_l(0 /*epoch*/);
+
+ if (status != NO_ERROR) {
+ if (mAudioRecordThread != 0) {
+ mAudioRecordThread->requestExit(); // see comment in AudioRecord.h
+ mAudioRecordThread->requestExitAndWait();
+ mAudioRecordThread.clear();
+ }
+ return status;
+ }
+
mStatus = NO_ERROR;
-
- // Update buffer size in case it has been limited by AudioFlinger during track creation
- mFrameCount = mCblk->frameCount_;
-
mActive = false;
- mCbf = cbf;
- mRefreshRemaining = true;
mUserData = user;
// TODO: add audio hardware input latency here
mLatency = (1000*mFrameCount) / sampleRate;
@@ -268,7 +261,7 @@
mMarkerReached = false;
mNewPosition = 0;
mUpdatePeriod = 0;
- AudioSystem::acquireAudioSessionId(mSessionId);
+ AudioSystem::acquireAudioSessionId(mSessionId, -1);
mSequence = 1;
mObservedSequence = mSequence;
mInOverrun = false;
@@ -289,6 +282,9 @@
// reset current position as seen by client to 0
mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
+ // force refresh of remaining frames by processAudioBuffer() as last
+ // read before stop could be partial.
+ mRefreshRemaining = true;
mNewPosition = mProxy->getPosition() + mUpdatePeriod;
int32_t flags = android_atomic_acquire_load(&mCblk->mFlags);
@@ -352,6 +348,7 @@
status_t AudioRecord::setMarkerPosition(uint32_t marker)
{
+ // The only purpose of setting marker position is to get a callback
if (mCbf == NULL) {
return INVALID_OPERATION;
}
@@ -377,6 +374,7 @@
status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
{
+ // The only purpose of setting position update period is to get a callback
if (mCbf == NULL) {
return INVALID_OPERATION;
}
@@ -412,7 +410,7 @@
return NO_ERROR;
}
-unsigned int AudioRecord::getInputFramesLost() const
+uint32_t AudioRecord::getInputFramesLost() const
{
// no need to check mActive, because if inactive this will return 0, which is what we want
return AudioSystem::getInputFramesLost(getInput());
@@ -430,92 +428,142 @@
return NO_INIT;
}
- IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
- pid_t tid = -1;
+ // Fast tracks must be at the primary _output_ [sic] sampling rate,
+ // because there is currently no concept of a primary input sampling rate
+ uint32_t afSampleRate = AudioSystem::getPrimaryOutputSamplingRate();
+ if (afSampleRate == 0) {
+ ALOGW("getPrimaryOutputSamplingRate failed");
+ }
// Client can only express a preference for FAST. Server will perform additional tests.
- // The only supported use case for FAST is callback transfer mode.
+ if ((mFlags & AUDIO_INPUT_FLAG_FAST) && !(
+ // use case: callback transfer mode
+ (mTransfer == TRANSFER_CALLBACK) &&
+ // matching sample rate
+ (mSampleRate == afSampleRate))) {
+ ALOGW("AUDIO_INPUT_FLAG_FAST denied by client");
+ // once denied, do not request again if IAudioRecord is re-created
+ mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
+ }
+
+ IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
+
+ pid_t tid = -1;
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
- if ((mTransfer != TRANSFER_CALLBACK) || (mAudioRecordThread == 0)) {
- ALOGW("AUDIO_INPUT_FLAG_FAST denied by client");
- // once denied, do not request again if IAudioRecord is re-created
- mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
- } else {
- trackFlags |= IAudioFlinger::TRACK_FAST;
+ trackFlags |= IAudioFlinger::TRACK_FAST;
+ if (mAudioRecordThread != 0) {
tid = mAudioRecordThread->getTid();
}
}
+ // FIXME Assume double buffering, because we don't know the true HAL sample rate
+ const uint32_t nBuffering = 2;
+
mNotificationFramesAct = mNotificationFramesReq;
+ size_t frameCount = mReqFrameCount;
if (!(mFlags & AUDIO_INPUT_FLAG_FAST)) {
// Make sure that application is notified with sufficient margin before overrun
- if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount/2) {
- mNotificationFramesAct = mFrameCount/2;
+ if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) {
+ mNotificationFramesAct = frameCount/2;
}
}
audio_io_handle_t input = AudioSystem::getInput(mInputSource, mSampleRate, mFormat,
mChannelMask, mSessionId);
if (input == 0) {
- ALOGE("Could not get audio input for record source %d", mInputSource);
+ ALOGE("Could not get audio input for record source %d, sample rate %u, format %#x, "
+ "channel mask %#x, session %d",
+ mInputSource, mSampleRate, mFormat, mChannelMask, mSessionId);
return BAD_VALUE;
}
+ {
+ // Now that we have a reference to an I/O handle and have not yet handed it off to AudioFlinger,
+ // we must release it ourselves if anything goes wrong.
+ size_t temp = frameCount; // temp may be replaced by a revised value of frameCount,
+ // but we will still need the original value also
int originalSessionId = mSessionId;
sp<IAudioRecord> record = audioFlinger->openRecord(input,
mSampleRate, mFormat,
mChannelMask,
- mFrameCount,
+ &temp,
&trackFlags,
tid,
&mSessionId,
&status);
- ALOGE_IF(originalSessionId != 0 && mSessionId != originalSessionId,
+ ALOGE_IF(originalSessionId != AUDIO_SESSION_ALLOCATE && mSessionId != originalSessionId,
"session ID changed from %d to %d", originalSessionId, mSessionId);
- if (record == 0) {
+ if (status != NO_ERROR) {
ALOGE("AudioFlinger could not create record track, status: %d", status);
- AudioSystem::releaseInput(input);
- return status;
+ goto release;
}
+ ALOG_ASSERT(record != 0);
+
+ // AudioFlinger now owns the reference to the I/O handle,
+ // so we are no longer responsible for releasing it.
+
sp<IMemory> iMem = record->getCblk();
if (iMem == 0) {
ALOGE("Could not get control block");
return NO_INIT;
}
+ void *iMemPointer = iMem->pointer();
+ if (iMemPointer == NULL) {
+ ALOGE("Could not get control block pointer");
+ return NO_INIT;
+ }
+ // invariant that mAudioRecord != 0 is true only after set() returns successfully
if (mAudioRecord != 0) {
mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
mDeathNotifier.clear();
}
- mInput = input;
mAudioRecord = record;
+
mCblkMemory = iMem;
- audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer());
+ audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
mCblk = cblk;
- // FIXME missing fast track frameCount logic
+ // note that temp is the (possibly revised) value of frameCount
+ if (temp < frameCount || (frameCount == 0 && temp == 0)) {
+ ALOGW("Requested frameCount %u but received frameCount %u", frameCount, temp);
+ }
+ frameCount = temp;
+
mAwaitBoost = false;
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
if (trackFlags & IAudioFlinger::TRACK_FAST) {
- ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", mFrameCount);
+ ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", frameCount);
mAwaitBoost = true;
- // double-buffering is not required for fast tracks, due to tighter scheduling
- if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount) {
- mNotificationFramesAct = mFrameCount;
- }
} else {
- ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %u", mFrameCount);
+ ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %u", frameCount);
// once denied, do not request again if IAudioRecord is re-created
mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
- if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount/2) {
- mNotificationFramesAct = mFrameCount/2;
- }
+ }
+ // Theoretically double-buffering is not required for fast tracks,
+ // due to tighter scheduling. But in practice, to accomodate kernels with
+ // scheduling jitter, and apps with computation jitter, we use double-buffering.
+ if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) {
+ mNotificationFramesAct = frameCount/nBuffering;
}
}
- // starting address of buffers in shared memory
+ // We retain a copy of the I/O handle, but don't own the reference
+ mInput = input;
+ mRefreshRemaining = true;
+
+ // Starting address of buffers in shared memory, immediately after the control block. This
+ // address is for the mapping within client address space. AudioFlinger::TrackBase::mBuffer
+ // is for the server address space.
void *buffers = (char*)cblk + sizeof(audio_track_cblk_t);
+ mFrameCount = frameCount;
+ // If IAudioRecord is re-created, don't let the requested frameCount
+ // decrease. This can confuse clients that cache frameCount().
+ if (frameCount > mReqFrameCount) {
+ mReqFrameCount = frameCount;
+ }
+
// update proxy
mProxy = new AudioRecordClientProxy(cblk, buffers, mFrameCount, mFrameSize);
mProxy->setEpoch(epoch);
@@ -525,6 +573,14 @@
mAudioRecord->asBinder()->linkToDeath(mDeathNotifier, this);
return NO_ERROR;
+ }
+
+release:
+ AudioSystem::releaseInput(input);
+ if (status == NO_ERROR) {
+ status = NO_INIT;
+ }
+ return status;
}
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
@@ -540,13 +596,13 @@
}
const struct timespec *requested;
+ struct timespec timeout;
if (waitCount == -1) {
requested = &ClientProxy::kForever;
} else if (waitCount == 0) {
requested = &ClientProxy::kNonBlocking;
} else if (waitCount > 0) {
long long ms = WAIT_PERIOD_MS * (long long) waitCount;
- struct timespec timeout;
timeout.tv_sec = ms / 1000;
timeout.tv_nsec = (int) (ms % 1000) * 1000000;
requested = &timeout;
@@ -586,6 +642,9 @@
if (newSequence == oldSequence) {
status = restoreRecord_l("obtainBuffer");
if (status != NO_ERROR) {
+ buffer.mFrameCount = 0;
+ buffer.mRaw = NULL;
+ buffer.mNonContig = 0;
break;
}
}
@@ -687,7 +746,7 @@
// -------------------------------------------------------------------------
-nsecs_t AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread)
+nsecs_t AudioRecord::processAudioBuffer()
{
mLock.lock();
if (mAwaitBoost) {
@@ -755,17 +814,17 @@
}
// Cache other fields that will be needed soon
- size_t notificationFrames = mNotificationFramesAct;
+ uint32_t notificationFrames = mNotificationFramesAct;
if (mRefreshRemaining) {
mRefreshRemaining = false;
mRemainingFrames = notificationFrames;
mRetryOnPartialBuffer = false;
}
size_t misalignment = mProxy->getMisalignment();
- int32_t sequence = mSequence;
+ uint32_t sequence = mSequence;
// These fields don't need to be cached, because they are assigned only by set():
- // mTransfer, mCbf, mUserData, mSampleRate
+ // mTransfer, mCbf, mUserData, mSampleRate, mFrameSize
mLock.unlock();
@@ -839,8 +898,8 @@
"obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount);
requested = &ClientProxy::kNonBlocking;
size_t avail = audioBuffer.frameCount + nonContig;
- ALOGV("obtainBuffer(%u) returned %u = %u + %u",
- mRemainingFrames, avail, audioBuffer.frameCount, nonContig);
+ ALOGV("obtainBuffer(%u) returned %u = %u + %u err %d",
+ mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err);
if (err != NO_ERROR) {
if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) {
break;
@@ -949,7 +1008,7 @@
// =========================================================================
-void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who)
+void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
{
sp<AudioRecord> audioRecord = mAudioRecord.promote();
if (audioRecord != 0) {
@@ -961,7 +1020,8 @@
// =========================================================================
AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver, bool bCanCallJava)
- : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL)
+ : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL),
+ mIgnoreNextPausedInt(false)
{
}
@@ -978,6 +1038,10 @@
// caller will check for exitPending()
return true;
}
+ if (mIgnoreNextPausedInt) {
+ mIgnoreNextPausedInt = false;
+ mPausedInt = false;
+ }
if (mPausedInt) {
if (mPausedNs > 0) {
(void) mMyCond.waitRelative(mMyLock, mPausedNs);
@@ -988,7 +1052,7 @@
return true;
}
}
- nsecs_t ns = mReceiver.processAudioBuffer(this);
+ nsecs_t ns = mReceiver.processAudioBuffer();
switch (ns) {
case 0:
return true;
@@ -1012,12 +1076,7 @@
{
// must be in this order to avoid a race condition
Thread::requestExit();
- AutoMutex _l(mMyLock);
- if (mPaused || mPausedInt) {
- mPaused = false;
- mPausedInt = false;
- mMyCond.signal();
- }
+ resume();
}
void AudioRecord::AudioRecordThread::pause()
@@ -1029,6 +1088,7 @@
void AudioRecord::AudioRecordThread::resume()
{
AutoMutex _l(mMyLock);
+ mIgnoreNextPausedInt = true;
if (mPaused || mPausedInt) {
mPaused = false;
mPausedInt = false;
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 8033c2c..140fb66 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -40,10 +40,10 @@
DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSystem::gOutputs(0);
// Cached values for recording queries, all protected by gLock
-uint32_t AudioSystem::gPrevInSamplingRate = 16000;
-audio_format_t AudioSystem::gPrevInFormat = AUDIO_FORMAT_PCM_16_BIT;
-audio_channel_mask_t AudioSystem::gPrevInChannelMask = AUDIO_CHANNEL_IN_MONO;
-size_t AudioSystem::gInBuffSize = 0;
+uint32_t AudioSystem::gPrevInSamplingRate;
+audio_format_t AudioSystem::gPrevInFormat;
+audio_channel_mask_t AudioSystem::gPrevInChannelMask;
+size_t AudioSystem::gInBuffSize = 0; // zero indicates cache is invalid
// establish binder interface to AudioFlinger service
@@ -84,13 +84,15 @@
return DEAD_OBJECT;
}
-status_t AudioSystem::muteMicrophone(bool state) {
+status_t AudioSystem::muteMicrophone(bool state)
+{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setMicMute(state);
}
-status_t AudioSystem::isMicrophoneMuted(bool* state) {
+status_t AudioSystem::isMicrophoneMuted(bool* state)
+{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
*state = af->getMicMute();
@@ -175,13 +177,15 @@
return af->setMode(mode);
}
-status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs) {
+status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
+{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setParameters(ioHandle, keyValuePairs);
}
-String8 AudioSystem::getParameters(audio_io_handle_t ioHandle, const String8& keys) {
+String8 AudioSystem::getParameters(audio_io_handle_t ioHandle, const String8& keys)
+{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
String8 result = String8("");
if (af == 0) return result;
@@ -190,6 +194,16 @@
return result;
}
+status_t AudioSystem::setParameters(const String8& keyValuePairs)
+{
+ return setParameters((audio_io_handle_t) 0, keyValuePairs);
+}
+
+String8 AudioSystem::getParameters(const String8& keys)
+{
+ return getParameters((audio_io_handle_t) 0, keys);
+}
+
// convert volume steps to natural log scale
// change this value to change volume scaling
@@ -249,6 +263,11 @@
*samplingRate = outputDesc->samplingRate;
gLock.unlock();
}
+ if (*samplingRate == 0) {
+ ALOGE("AudioSystem::getSamplingRate failed for output %d stream type %d",
+ output, streamType);
+ return BAD_VALUE;
+ }
ALOGV("getSamplingRate() streamType %d, output %d, sampling rate %u", streamType, output,
*samplingRate);
@@ -289,6 +308,11 @@
*frameCount = outputDesc->frameCount;
gLock.unlock();
}
+ if (*frameCount == 0) {
+ ALOGE("AudioSystem::getFrameCount failed for output %d stream type %d",
+ output, streamType);
+ return BAD_VALUE;
+ }
ALOGV("getFrameCount() streamType %d, output %d, frameCount %d", streamType, output,
*frameCount);
@@ -313,7 +337,7 @@
}
status_t AudioSystem::getLatency(audio_io_handle_t output,
- audio_stream_type_t streamType,
+ audio_stream_type_t streamType __unused,
uint32_t* latency)
{
OutputDescriptor *outputDesc;
@@ -349,6 +373,12 @@
return PERMISSION_DENIED;
}
inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);
+ if (inBuffSize == 0) {
+ ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x",
+ sampleRate, format, channelMask);
+ return BAD_VALUE;
+ }
+ // A benign race is possible here: we could overwrite a fresher cache entry
gLock.lock();
// save the request params
gPrevInSamplingRate = sampleRate;
@@ -370,8 +400,8 @@
return af->setVoiceVolume(value);
}
-status_t AudioSystem::getRenderPosition(audio_io_handle_t output, size_t *halFrames,
- size_t *dspFrames, audio_stream_type_t stream)
+status_t AudioSystem::getRenderPosition(audio_io_handle_t output, uint32_t *halFrames,
+ uint32_t *dspFrames, audio_stream_type_t stream)
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
@@ -387,9 +417,10 @@
return af->getRenderPosition(halFrames, dspFrames, output);
}
-size_t AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle) {
+uint32_t AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle)
+{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
- unsigned int result = 0;
+ uint32_t result = 0;
if (af == 0) return result;
if (ioHandle == 0) return result;
@@ -397,29 +428,33 @@
return result;
}
-int AudioSystem::newAudioSessionId() {
+int AudioSystem::newAudioSessionId()
+{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return 0;
return af->newAudioSessionId();
}
-void AudioSystem::acquireAudioSessionId(int audioSession) {
+void AudioSystem::acquireAudioSessionId(int audioSession, pid_t pid)
+{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af != 0) {
- af->acquireAudioSessionId(audioSession);
+ af->acquireAudioSessionId(audioSession, pid);
}
}
-void AudioSystem::releaseAudioSessionId(int audioSession) {
+void AudioSystem::releaseAudioSessionId(int audioSession, pid_t pid)
+{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af != 0) {
- af->releaseAudioSessionId(audioSession);
+ af->releaseAudioSessionId(audioSession, pid);
}
}
// ---------------------------------------------------------------------------
-void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
+void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused)
+{
Mutex::Autolock _l(AudioSystem::gLock);
AudioSystem::gAudioFlinger.clear();
@@ -455,7 +490,7 @@
OutputDescriptor *outputDesc = new OutputDescriptor(*desc);
gOutputs.add(ioHandle, outputDesc);
- ALOGV("ioConfigChanged() new output samplingRate %u, format %d channel mask %#x frameCount %u "
+ ALOGV("ioConfigChanged() new output samplingRate %u, format %#x channel mask %#x frameCount %u "
"latency %d",
outputDesc->samplingRate, outputDesc->format, outputDesc->channelMask,
outputDesc->frameCount, outputDesc->latency);
@@ -479,7 +514,7 @@
if (param2 == NULL) break;
desc = (const OutputDescriptor *)param2;
- ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %d channel mask %#x "
+ ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %#x channel mask %#x "
"frameCount %d latency %d",
ioHandle, desc->samplingRate, desc->format,
desc->channelMask, desc->frameCount, desc->latency);
@@ -496,12 +531,14 @@
}
}
-void AudioSystem::setErrorCallback(audio_error_callback cb) {
+void AudioSystem::setErrorCallback(audio_error_callback cb)
+{
Mutex::Autolock _l(gLock);
gAudioErrorCallback = cb;
}
-bool AudioSystem::routedToA2dpOutput(audio_stream_type_t streamType) {
+bool AudioSystem::routedToA2dpOutput(audio_stream_type_t streamType)
+{
switch (streamType) {
case AUDIO_STREAM_MUSIC:
case AUDIO_STREAM_VOICE_CALL:
@@ -709,7 +746,8 @@
audio_io_handle_t AudioSystem::getOutputForEffect(const effect_descriptor_t *desc)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- if (aps == 0) return PERMISSION_DENIED;
+ // FIXME change return type to status_t, and return PERMISSION_DENIED here
+ if (aps == 0) return 0;
return aps->getOutputForEffect(desc);
}
@@ -804,7 +842,8 @@
// ---------------------------------------------------------------------------
-void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who) {
+void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused)
+{
Mutex::Autolock _l(AudioSystem::gLock);
AudioSystem::gAudioPolicyService.clear();
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 5e805c9..3217171 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -44,9 +44,6 @@
return BAD_VALUE;
}
- // default to 0 in case of error
- *frameCount = 0;
-
// FIXME merge with similar code in createTrack_l(), except we're missing
// some information here that is available in createTrack_l():
// audio_io_handle_t output
@@ -54,16 +51,26 @@
// audio_channel_mask_t channelMask
// audio_output_flags_t flags
uint32_t afSampleRate;
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
- return NO_INIT;
+ status_t status;
+ status = AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);
+ if (status != NO_ERROR) {
+ ALOGE("Unable to query output sample rate for stream type %d; status %d",
+ streamType, status);
+ return status;
}
size_t afFrameCount;
- if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
- return NO_INIT;
+ status = AudioSystem::getOutputFrameCount(&afFrameCount, streamType);
+ if (status != NO_ERROR) {
+ ALOGE("Unable to query output frame count for stream type %d; status %d",
+ streamType, status);
+ return status;
}
uint32_t afLatency;
- if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
- return NO_INIT;
+ status = AudioSystem::getOutputLatency(&afLatency, streamType);
+ if (status != NO_ERROR) {
+ ALOGE("Unable to query output latency for stream type %d; status %d",
+ streamType, status);
+ return status;
}
// Ensure that buffer depth covers at least audio hardware latency
@@ -74,6 +81,13 @@
*frameCount = (sampleRate == 0) ? afFrameCount * minBufCount :
afFrameCount * minBufCount * sampleRate / afSampleRate;
+ // The formula above should always produce a non-zero value, but return an error
+ // in the unlikely event that it does not, as that's part of the API contract.
+ if (*frameCount == 0) {
+ ALOGE("AudioTrack::getMinFrameCount failed for streamType %d, sampleRate %d",
+ streamType, sampleRate);
+ return BAD_VALUE;
+ }
ALOGV("getMinFrameCount=%d: afFrameCount=%d, minBufCount=%d, afSampleRate=%d, afLatency=%d",
*frameCount, afFrameCount, minBufCount, afSampleRate, afLatency);
return NO_ERROR;
@@ -85,7 +99,8 @@
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mPausedPosition(0)
{
}
@@ -94,22 +109,26 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount,
+ size_t frameCount,
audio_output_flags_t flags,
callback_t cbf,
void* user,
- int notificationFrames,
+ uint32_t notificationFrames,
int sessionId,
transfer_type transferType,
- const audio_offload_info_t *offloadInfo)
+ const audio_offload_info_t *offloadInfo,
+ int uid,
+ pid_t pid)
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mPausedPosition(0)
{
mStatus = set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
- 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo);
+ 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType,
+ offloadInfo, uid, pid);
}
AudioTrack::AudioTrack(
@@ -121,18 +140,22 @@
audio_output_flags_t flags,
callback_t cbf,
void* user,
- int notificationFrames,
+ uint32_t notificationFrames,
int sessionId,
transfer_type transferType,
- const audio_offload_info_t *offloadInfo)
+ const audio_offload_info_t *offloadInfo,
+ int uid,
+ pid_t pid)
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mPausedPosition(0)
{
mStatus = set(streamType, sampleRate, format, channelMask,
0 /*frameCount*/, flags, cbf, user, notificationFrames,
- sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo);
+ sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo,
+ uid, pid);
}
AudioTrack::~AudioTrack()
@@ -151,7 +174,9 @@
mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
mAudioTrack.clear();
IPCThreadState::self()->flushCommands();
- AudioSystem::releaseAudioSessionId(mSessionId);
+ ALOGV("~AudioTrack, releasing session id from %d on behalf of %d",
+ IPCThreadState::self()->getCallingPid(), mClientPid);
+ AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
}
}
@@ -160,17 +185,24 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCountInt,
+ size_t frameCount,
audio_output_flags_t flags,
callback_t cbf,
void* user,
- int notificationFrames,
+ uint32_t notificationFrames,
const sp<IMemory>& sharedBuffer,
bool threadCanCallJava,
int sessionId,
transfer_type transferType,
- const audio_offload_info_t *offloadInfo)
+ const audio_offload_info_t *offloadInfo,
+ int uid,
+ pid_t pid)
{
+ ALOGV("set(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
+ "flags #%x, notificationFrames %u, sessionId %d, transferType %d",
+ streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
+ sessionId, transferType);
+
switch (transferType) {
case TRANSFER_DEFAULT:
if (sharedBuffer != 0) {
@@ -204,15 +236,9 @@
ALOGE("Invalid transfer type %d", transferType);
return BAD_VALUE;
}
+ mSharedBuffer = sharedBuffer;
mTransfer = transferType;
- // FIXME "int" here is legacy and will be replaced by size_t later
- if (frameCountInt < 0) {
- ALOGE("Invalid frame count %d", frameCountInt);
- return BAD_VALUE;
- }
- size_t frameCount = frameCountInt;
-
ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(),
sharedBuffer->size());
@@ -226,19 +252,24 @@
return INVALID_OPERATION;
}
- mOutput = 0;
-
// handle default values first.
if (streamType == AUDIO_STREAM_DEFAULT) {
streamType = AUDIO_STREAM_MUSIC;
}
+ if (uint32_t(streamType) >= AUDIO_STREAM_CNT) {
+ ALOGE("Invalid stream type %d", streamType);
+ return BAD_VALUE;
+ }
+ mStreamType = streamType;
+ status_t status;
if (sampleRate == 0) {
- uint32_t afSampleRate;
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
- return NO_INIT;
+ status = AudioSystem::getOutputSamplingRate(&sampleRate, streamType);
+ if (status != NO_ERROR) {
+ ALOGE("Could not get output sample rate for stream type %d; status %d",
+ streamType, status);
+ return status;
}
- sampleRate = afSampleRate;
}
mSampleRate = sampleRate;
@@ -246,15 +277,21 @@
if (format == AUDIO_FORMAT_DEFAULT) {
format = AUDIO_FORMAT_PCM_16_BIT;
}
- if (channelMask == 0) {
- channelMask = AUDIO_CHANNEL_OUT_STEREO;
- }
// validate parameters
if (!audio_is_valid_format(format)) {
- ALOGE("Invalid format %d", format);
+ ALOGE("Invalid format %#x", format);
return BAD_VALUE;
}
+ mFormat = format;
+
+ if (!audio_is_output_channel(channelMask)) {
+ ALOGE("Invalid channel mask %#x", channelMask);
+ return BAD_VALUE;
+ }
+ mChannelMask = channelMask;
+ uint32_t channelCount = popcount(channelMask);
+ mChannelCount = channelCount;
// AudioFlinger does not currently support 8-bit data in shared memory
if (format == AUDIO_FORMAT_PCM_8_BIT && sharedBuffer != 0) {
@@ -278,14 +315,6 @@
flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
}
- if (!audio_is_output_channel(channelMask)) {
- ALOGE("Invalid channel mask %#x", channelMask);
- return BAD_VALUE;
- }
- mChannelMask = channelMask;
- uint32_t channelCount = popcount(channelMask);
- mChannelCount = channelCount;
-
if (audio_is_linear_pcm(format)) {
mFrameSize = channelCount * audio_bytes_per_sample(format);
mFrameSizeAF = channelCount * sizeof(int16_t);
@@ -294,25 +323,36 @@
mFrameSizeAF = sizeof(uint8_t);
}
- audio_io_handle_t output = AudioSystem::getOutput(
- streamType,
- sampleRate, format, channelMask,
- flags,
- offloadInfo);
-
- if (output == 0) {
- ALOGE("Could not get audio output for stream type %d", streamType);
- return BAD_VALUE;
+ // Make copy of input parameter offloadInfo so that in the future:
+ // (a) createTrack_l doesn't need it as an input parameter
+ // (b) we can support re-creation of offloaded tracks
+ if (offloadInfo != NULL) {
+ mOffloadInfoCopy = *offloadInfo;
+ mOffloadInfo = &mOffloadInfoCopy;
+ } else {
+ mOffloadInfo = NULL;
}
mVolume[LEFT] = 1.0f;
mVolume[RIGHT] = 1.0f;
mSendLevel = 0.0f;
- mFrameCount = frameCount;
+ // mFrameCount is initialized in createTrack_l
mReqFrameCount = frameCount;
mNotificationFramesReq = notificationFrames;
mNotificationFramesAct = 0;
mSessionId = sessionId;
+ int callingpid = IPCThreadState::self()->getCallingPid();
+ int mypid = getpid();
+ if (uid == -1 || (callingpid != mypid)) {
+ mClientUid = IPCThreadState::self()->getCallingUid();
+ } else {
+ mClientUid = uid;
+ }
+ if (pid == -1 || (callingpid != mypid)) {
+ mClientPid = callingpid;
+ } else {
+ mClientPid = pid;
+ }
mAuxEffectId = 0;
mFlags = flags;
mCbf = cbf;
@@ -323,14 +363,7 @@
}
// create the IAudioTrack
- status_t status = createTrack_l(streamType,
- sampleRate,
- format,
- frameCount,
- flags,
- sharedBuffer,
- output,
- 0 /*epoch*/);
+ status = createTrack_l(0 /*epoch*/);
if (status != NO_ERROR) {
if (mAudioTrackThread != 0) {
@@ -338,17 +371,20 @@
mAudioTrackThread->requestExitAndWait();
mAudioTrackThread.clear();
}
+ // Use of direct and offloaded output streams is ref counted by audio policy manager.
+#if 0 // FIXME This should no longer be needed
//Use of direct and offloaded output streams is ref counted by audio policy manager.
// As getOutput was called above and resulted in an output stream to be opened,
// we need to release it.
- AudioSystem::releaseOutput(output);
+ if (mOutput != 0) {
+ AudioSystem::releaseOutput(mOutput);
+ mOutput = 0;
+ }
+#endif
return status;
}
mStatus = NO_ERROR;
- mStreamType = streamType;
- mFormat = format;
- mSharedBuffer = sharedBuffer;
mState = STATE_STOPPED;
mUserData = user;
mLoopPeriod = 0;
@@ -356,11 +392,10 @@
mMarkerReached = false;
mNewPosition = 0;
mUpdatePeriod = 0;
- AudioSystem::acquireAudioSessionId(mSessionId);
+ AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
mSequence = 1;
mObservedSequence = mSequence;
mInUnderrun = false;
- mOutput = output;
return NO_ERROR;
}
@@ -436,12 +471,11 @@
void AudioTrack::stop()
{
AutoMutex lock(mLock);
- // FIXME pause then stop should not be a nop
- if (mState != STATE_ACTIVE) {
+ if (mState != STATE_ACTIVE && mState != STATE_PAUSED) {
return;
}
- if (isOffloaded()) {
+ if (isOffloaded_l()) {
mState = STATE_STOPPING;
} else {
mState = STATE_STOPPED;
@@ -463,7 +497,7 @@
sp<AudioTrackThread> t = mAudioTrackThread;
if (t != 0) {
- if (!isOffloaded()) {
+ if (!isOffloaded_l()) {
t->pause();
}
} else {
@@ -501,7 +535,7 @@
mRefreshRemaining = true;
mState = STATE_FLUSHED;
- if (isOffloaded()) {
+ if (isOffloaded_l()) {
mProxy->interrupt();
}
mProxy->flush();
@@ -520,6 +554,16 @@
}
mProxy->interrupt();
mAudioTrack->pause();
+
+ if (isOffloaded()) {
+ if (mOutput != 0) {
+ uint32_t halFrames;
+ // OffloadThread sends HAL pause in its threadLoop.. time saved
+ // here can be slightly off
+ AudioSystem::getRenderPosition(mOutput, &halFrames, &mPausedPosition);
+ ALOGV("AudioTrack::pause for offload, cache current position %u", mPausedPosition);
+ }
+ }
}
status_t AudioTrack::setVolume(float left, float right)
@@ -534,6 +578,9 @@
mProxy->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
+ if (isOffloaded_l()) {
+ mAudioTrack->signal();
+ }
return NO_ERROR;
}
@@ -591,6 +638,19 @@
}
AutoMutex lock(mLock);
+
+ // sample rate can be updated during playback by the offloaded decoder so we need to
+ // query the HAL and update if needed.
+// FIXME use Proxy return channel to update the rate from server and avoid polling here
+ if (isOffloaded_l()) {
+ if (mOutput != 0) {
+ uint32_t sampleRate = 0;
+ status_t status = AudioSystem::getSamplingRate(mOutput, mStreamType, &sampleRate);
+ if (status == NO_ERROR) {
+ mSampleRate = sampleRate;
+ }
+ }
+ }
return mSampleRate;
}
@@ -666,6 +726,7 @@
AutoMutex lock(mLock);
mNewPosition = mProxy->getPosition() + updatePeriod;
mUpdatePeriod = updatePeriod;
+
return NO_ERROR;
}
@@ -719,9 +780,15 @@
}
AutoMutex lock(mLock);
- if (isOffloaded()) {
+ if (isOffloaded_l()) {
uint32_t dspFrames = 0;
+ if ((mState == STATE_PAUSED) || (mState == STATE_PAUSED_STOPPING)) {
+ ALOGV("getPosition called in paused state, return cached position %u", mPausedPosition);
+ *position = mPausedPosition;
+ return NO_ERROR;
+ }
+
if (mOutput != 0) {
uint32_t halFrames;
AudioSystem::getRenderPosition(mOutput, &halFrames, &dspFrames);
@@ -735,7 +802,7 @@
return NO_ERROR;
}
-status_t AudioTrack::getBufferPosition(size_t *position)
+status_t AudioTrack::getBufferPosition(uint32_t *position)
{
if (mSharedBuffer == 0 || mIsTimed) {
return INVALID_OPERATION;
@@ -768,23 +835,12 @@
return NO_ERROR;
}
-audio_io_handle_t AudioTrack::getOutput()
+audio_io_handle_t AudioTrack::getOutput() const
{
AutoMutex lock(mLock);
return mOutput;
}
-// must be called with mLock held
-audio_io_handle_t AudioTrack::getOutput_l()
-{
- if (mOutput) {
- return mOutput;
- } else {
- return AudioSystem::getOutput(mStreamType,
- mSampleRate, mFormat, mChannelMask, mFlags);
- }
-}
-
status_t AudioTrack::attachAuxEffect(int effectId)
{
AutoMutex lock(mLock);
@@ -798,15 +854,7 @@
// -------------------------------------------------------------------------
// must be called with mLock held
-status_t AudioTrack::createTrack_l(
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- size_t frameCount,
- audio_output_flags_t flags,
- const sp<IMemory>& sharedBuffer,
- audio_io_handle_t output,
- size_t epoch)
+status_t AudioTrack::createTrack_l(size_t epoch)
{
status_t status;
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
@@ -815,87 +863,103 @@
return NO_INIT;
}
+ audio_io_handle_t output = AudioSystem::getOutput(mStreamType, mSampleRate, mFormat,
+ mChannelMask, mFlags, mOffloadInfo);
+ if (output == 0) {
+ ALOGE("Could not get audio output for stream type %d, sample rate %u, format %#x, "
+ "channel mask %#x, flags %#x",
+ mStreamType, mSampleRate, mFormat, mChannelMask, mFlags);
+ return BAD_VALUE;
+ }
+ {
+ // Now that we have a reference to an I/O handle and have not yet handed it off to AudioFlinger,
+ // we must release it ourselves if anything goes wrong.
+
// Not all of these values are needed under all conditions, but it is easier to get them all
uint32_t afLatency;
- status = AudioSystem::getLatency(output, streamType, &afLatency);
+ status = AudioSystem::getLatency(output, mStreamType, &afLatency);
if (status != NO_ERROR) {
ALOGE("getLatency(%d) failed status %d", output, status);
- return NO_INIT;
+ goto release;
}
size_t afFrameCount;
- status = AudioSystem::getFrameCount(output, streamType, &afFrameCount);
+ status = AudioSystem::getFrameCount(output, mStreamType, &afFrameCount);
if (status != NO_ERROR) {
- ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType, status);
- return NO_INIT;
+ ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, mStreamType, status);
+ goto release;
}
uint32_t afSampleRate;
- status = AudioSystem::getSamplingRate(output, streamType, &afSampleRate);
+ status = AudioSystem::getSamplingRate(output, mStreamType, &afSampleRate);
if (status != NO_ERROR) {
- ALOGE("getSamplingRate(output=%d, streamType=%d) status %d", output, streamType, status);
- return NO_INIT;
+ ALOGE("getSamplingRate(output=%d, streamType=%d) status %d", output, mStreamType, status);
+ goto release;
}
// Client decides whether the track is TIMED (see below), but can only express a preference
// for FAST. Server will perform additional tests.
- if ((flags & AUDIO_OUTPUT_FLAG_FAST) && !(
+ if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) && !((
// either of these use cases:
// use case 1: shared buffer
- (sharedBuffer != 0) ||
- // use case 2: callback handler
- (mCbf != NULL))) {
+ (mSharedBuffer != 0) ||
+ // use case 2: callback transfer mode
+ (mTransfer == TRANSFER_CALLBACK)) &&
+ // matching sample rate
+ (mSampleRate == afSampleRate))) {
ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client");
// once denied, do not request again if IAudioTrack is re-created
- flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST);
- mFlags = flags;
+ mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST);
}
ALOGV("createTrack_l() output %d afLatency %d", output, afLatency);
// The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where
- // n = 1 fast track; nBuffering is ignored
+ // n = 1 fast track with single buffering; nBuffering is ignored
+ // n = 2 fast track with double buffering
// n = 2 normal track, no sample rate conversion
// n = 3 normal track, with sample rate conversion
// (pessimistic; some non-1:1 conversion ratios don't actually need triple-buffering)
// n > 3 very high latency or very small notification interval; nBuffering is ignored
- const uint32_t nBuffering = (sampleRate == afSampleRate) ? 2 : 3;
+ const uint32_t nBuffering = (mSampleRate == afSampleRate) ? 2 : 3;
mNotificationFramesAct = mNotificationFramesReq;
- if (!audio_is_linear_pcm(format)) {
+ size_t frameCount = mReqFrameCount;
+ if (!audio_is_linear_pcm(mFormat)) {
- if (sharedBuffer != 0) {
+ if (mSharedBuffer != 0) {
// Same comment as below about ignoring frameCount parameter for set()
- frameCount = sharedBuffer->size();
+ frameCount = mSharedBuffer->size();
} else if (frameCount == 0) {
frameCount = afFrameCount;
}
if (mNotificationFramesAct != frameCount) {
mNotificationFramesAct = frameCount;
}
- } else if (sharedBuffer != 0) {
+ } else if (mSharedBuffer != 0) {
// Ensure that buffer alignment matches channel count
// 8-bit data in shared memory is not currently supported by AudioFlinger
- size_t alignment = /* format == AUDIO_FORMAT_PCM_8_BIT ? 1 : */ 2;
+ size_t alignment = /* mFormat == AUDIO_FORMAT_PCM_8_BIT ? 1 : */ 2;
if (mChannelCount > 1) {
// More than 2 channels does not require stronger alignment than stereo
alignment <<= 1;
}
- if (((size_t)sharedBuffer->pointer() & (alignment - 1)) != 0) {
+ if (((uintptr_t)mSharedBuffer->pointer() & (alignment - 1)) != 0) {
ALOGE("Invalid buffer alignment: address %p, channel count %u",
- sharedBuffer->pointer(), mChannelCount);
- return BAD_VALUE;
+ mSharedBuffer->pointer(), mChannelCount);
+ status = BAD_VALUE;
+ goto release;
}
// When initializing a shared buffer AudioTrack via constructors,
// there's no frameCount parameter.
// But when initializing a shared buffer AudioTrack via set(),
// there _is_ a frameCount parameter. We silently ignore it.
- frameCount = sharedBuffer->size()/mChannelCount/sizeof(int16_t);
+ frameCount = mSharedBuffer->size()/mChannelCount/sizeof(int16_t);
- } else if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) {
+ } else if (!(mFlags & AUDIO_OUTPUT_FLAG_FAST)) {
// FIXME move these calculations and associated checks to server
@@ -907,10 +971,10 @@
minBufCount = nBuffering;
}
- size_t minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate;
+ size_t minFrameCount = (afFrameCount*mSampleRate*minBufCount)/afSampleRate;
ALOGV("minFrameCount: %u, afFrameCount=%d, minBufCount=%d, sampleRate=%u, afSampleRate=%u"
", afLatency=%d",
- minFrameCount, afFrameCount, minBufCount, sampleRate, afSampleRate, afLatency);
+ minFrameCount, afFrameCount, minBufCount, mSampleRate, afSampleRate, afLatency);
if (frameCount == 0) {
frameCount = minFrameCount;
@@ -935,51 +999,65 @@
}
pid_t tid = -1;
- if (flags & AUDIO_OUTPUT_FLAG_FAST) {
+ if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
trackFlags |= IAudioFlinger::TRACK_FAST;
if (mAudioTrackThread != 0) {
tid = mAudioTrackThread->getTid();
}
}
- if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ if (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
trackFlags |= IAudioFlinger::TRACK_OFFLOAD;
}
- sp<IAudioTrack> track = audioFlinger->createTrack(streamType,
- sampleRate,
+ size_t temp = frameCount; // temp may be replaced by a revised value of frameCount,
+ // but we will still need the original value also
+ sp<IAudioTrack> track = audioFlinger->createTrack(mStreamType,
+ mSampleRate,
// AudioFlinger only sees 16-bit PCM
- format == AUDIO_FORMAT_PCM_8_BIT ?
- AUDIO_FORMAT_PCM_16_BIT : format,
+ mFormat == AUDIO_FORMAT_PCM_8_BIT ?
+ AUDIO_FORMAT_PCM_16_BIT : mFormat,
mChannelMask,
- frameCount,
+ &temp,
&trackFlags,
- sharedBuffer,
+ mSharedBuffer,
output,
tid,
&mSessionId,
mName,
+ mClientUid,
&status);
- if (track == 0) {
+ if (status != NO_ERROR) {
ALOGE("AudioFlinger could not create track, status: %d", status);
- return status;
+ goto release;
}
+ ALOG_ASSERT(track != 0);
+
+ // AudioFlinger now owns the reference to the I/O handle,
+ // so we are no longer responsible for releasing it.
+
sp<IMemory> iMem = track->getCblk();
if (iMem == 0) {
ALOGE("Could not get control block");
return NO_INIT;
}
+ void *iMemPointer = iMem->pointer();
+ if (iMemPointer == NULL) {
+ ALOGE("Could not get control block pointer");
+ return NO_INIT;
+ }
// invariant that mAudioTrack != 0 is true only after set() returns successfully
if (mAudioTrack != 0) {
mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
mDeathNotifier.clear();
}
mAudioTrack = track;
+
mCblkMemory = iMem;
- audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer());
+ audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
mCblk = cblk;
- size_t temp = cblk->frameCount_;
+ // note that temp is the (possibly revised) value of frameCount
if (temp < frameCount || (frameCount == 0 && temp == 0)) {
// In current design, AudioTrack client checks and ensures frame count validity before
// passing it to AudioFlinger so AudioFlinger should not return a different value except
@@ -987,40 +1065,44 @@
ALOGW("Requested frameCount %u but received frameCount %u", frameCount, temp);
}
frameCount = temp;
+
mAwaitBoost = false;
- if (flags & AUDIO_OUTPUT_FLAG_FAST) {
+ if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
if (trackFlags & IAudioFlinger::TRACK_FAST) {
ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", frameCount);
mAwaitBoost = true;
- if (sharedBuffer == 0) {
- // double-buffering is not required for fast tracks, due to tighter scheduling
- if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount) {
- mNotificationFramesAct = frameCount;
+ if (mSharedBuffer == 0) {
+ // Theoretically double-buffering is not required for fast tracks,
+ // due to tighter scheduling. But in practice, to accommodate kernels with
+ // scheduling jitter, and apps with computation jitter, we use double-buffering.
+ if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) {
+ mNotificationFramesAct = frameCount/nBuffering;
}
}
} else {
ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", frameCount);
// once denied, do not request again if IAudioTrack is re-created
- flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST);
- mFlags = flags;
- if (sharedBuffer == 0) {
+ mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST);
+ if (mSharedBuffer == 0) {
if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) {
mNotificationFramesAct = frameCount/nBuffering;
}
}
}
}
- if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ if (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
if (trackFlags & IAudioFlinger::TRACK_OFFLOAD) {
ALOGV("AUDIO_OUTPUT_FLAG_OFFLOAD successful");
} else {
ALOGW("AUDIO_OUTPUT_FLAG_OFFLOAD denied by server");
- flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
- mFlags = flags;
- return NO_INIT;
+ mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
+ // FIXME This is a warning, not an error, so don't return error status
+ //return NO_INIT;
}
}
+ // We retain a copy of the I/O handle, but don't own the reference
+ mOutput = output;
mRefreshRemaining = true;
// Starting address of buffers in shared memory. If there is a shared buffer, buffers
@@ -1028,15 +1110,16 @@
// immediately after the control block. This address is for the mapping within client
// address space. AudioFlinger::TrackBase::mBuffer is for the server address space.
void* buffers;
- if (sharedBuffer == 0) {
+ if (mSharedBuffer == 0) {
buffers = (char*)cblk + sizeof(audio_track_cblk_t);
} else {
- buffers = sharedBuffer->pointer();
+ buffers = mSharedBuffer->pointer();
}
mAudioTrack->attachAuxEffect(mAuxEffectId);
// FIXME don't believe this lie
- mLatency = afLatency + (1000*frameCount) / sampleRate;
+ mLatency = afLatency + (1000*frameCount) / mSampleRate;
+
mFrameCount = frameCount;
// If IAudioTrack is re-created, don't let the requested frameCount
// decrease. This can confuse clients that cache frameCount().
@@ -1045,7 +1128,7 @@
}
// update proxy
- if (sharedBuffer == 0) {
+ if (mSharedBuffer == 0) {
mStaticProxy.clear();
mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
} else {
@@ -1063,6 +1146,14 @@
mAudioTrack->asBinder()->linkToDeath(mDeathNotifier, this);
return NO_ERROR;
+ }
+
+release:
+ AudioSystem::releaseOutput(output);
+ if (status == NO_ERROR) {
+ status = NO_INIT;
+ }
+ return status;
}
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
@@ -1078,13 +1169,13 @@
}
const struct timespec *requested;
+ struct timespec timeout;
if (waitCount == -1) {
requested = &ClientProxy::kForever;
} else if (waitCount == 0) {
requested = &ClientProxy::kNonBlocking;
} else if (waitCount > 0) {
long long ms = WAIT_PERIOD_MS * (long long) waitCount;
- struct timespec timeout;
timeout.tv_sec = ms / 1000;
timeout.tv_nsec = (int) (ms % 1000) * 1000000;
requested = &timeout;
@@ -1209,7 +1300,7 @@
if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
// Sanity-check: user is most-likely passing an error code, and it would
// make the return value ambiguous (actualSize vs error).
- ALOGE("AudioTrack::write(buffer=%p, size=%u (%d)", buffer, userSize, userSize);
+ ALOGE("AudioTrack::write(buffer=%p, size=%zu (%zd)", buffer, userSize, userSize);
return BAD_VALUE;
}
@@ -1315,7 +1406,7 @@
// -------------------------------------------------------------------------
-nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
+nsecs_t AudioTrack::processAudioBuffer()
{
// Currently the AudioTrack thread is not created if there are no callbacks.
// Would it ever make sense to run the thread, even without callbacks?
@@ -1353,7 +1444,7 @@
// for offloaded tracks restoreTrack_l() will just update the sequence and clear
// AudioSystem cache. We should not exit here but after calling the callback so
// that the upper layers can recreate the track
- if (!isOffloaded() || (mSequence == mObservedSequence)) {
+ if (!isOffloaded_l() || (mSequence == mObservedSequence)) {
status_t status = restoreTrack_l("processAudioBuffer");
mLock.unlock();
// Run again immediately, but with a new IAudioTrack
@@ -1408,7 +1499,7 @@
// Cache other fields that will be needed soon
uint32_t loopPeriod = mLoopPeriod;
uint32_t sampleRate = mSampleRate;
- size_t notificationFrames = mNotificationFramesAct;
+ uint32_t notificationFrames = mNotificationFramesAct;
if (mRefreshRemaining) {
mRefreshRemaining = false;
mRemainingFrames = notificationFrames;
@@ -1572,7 +1663,6 @@
size_t reqSize = audioBuffer.size;
mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
size_t writtenSize = audioBuffer.size;
- size_t writtenFrames = writtenSize / mFrameSize;
// Sanity check on returned size
if (ssize_t(writtenSize) < 0 || writtenSize > reqSize) {
@@ -1638,36 +1728,27 @@
status_t AudioTrack::restoreTrack_l(const char *from)
{
ALOGW("dead IAudioTrack, %s, creating a new one from %s()",
- isOffloaded() ? "Offloaded" : "PCM", from);
+ isOffloaded_l() ? "Offloaded" : "PCM", from);
++mSequence;
status_t result;
// refresh the audio configuration cache in this process to make sure we get new
- // output parameters in getOutput_l() and createTrack_l()
+ // output parameters in createTrack_l()
AudioSystem::clearAudioConfigCache();
- if (isOffloaded()) {
+ if (isOffloaded_l()) {
+ // FIXME re-creation of offloaded tracks is not yet implemented
return DEAD_OBJECT;
}
- // force new output query from audio policy manager;
- mOutput = 0;
- audio_io_handle_t output = getOutput_l();
-
// 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
- size_t position = mProxy->getPosition();
- mNewPosition = position + mUpdatePeriod;
+
+ // take the frames that will be lost by track recreation into account in saved position
+ size_t position = mProxy->getPosition() + mProxy->getFramesFilled();
size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0;
- result = createTrack_l(mStreamType,
- mSampleRate,
- mFormat,
- mReqFrameCount, // so that frame count never goes down
- mFlags,
- mSharedBuffer,
- output,
- position /*epoch*/);
+ result = createTrack_l(position /*epoch*/);
if (result == NO_ERROR) {
// continue playback from last known position, but
@@ -1695,10 +1776,16 @@
}
}
if (result != NO_ERROR) {
+ // Use of direct and offloaded output streams is ref counted by audio policy manager.
+#if 0 // FIXME This should no longer be needed
//Use of direct and offloaded output streams is ref counted by audio policy manager.
// As getOutput was called above and resulted in an output stream to be opened,
// we need to release it.
- AudioSystem::releaseOutput(output);
+ if (mOutput != 0) {
+ AudioSystem::releaseOutput(mOutput);
+ mOutput = 0;
+ }
+#endif
ALOGW("restoreTrack_l() failed status %d", result);
mState = STATE_STOPPED;
}
@@ -1731,14 +1818,21 @@
String8 AudioTrack::getParameters(const String8& keys)
{
- if (mOutput) {
- return AudioSystem::getParameters(mOutput, keys);
+ audio_io_handle_t output = getOutput();
+ if (output != 0) {
+ return AudioSystem::getParameters(output, keys);
} else {
return String8::empty();
}
}
-status_t AudioTrack::dump(int fd, const Vector<String16>& args) const
+bool AudioTrack::isOffloaded() const
+{
+ AutoMutex lock(mLock);
+ return isOffloaded_l();
+}
+
+status_t AudioTrack::dump(int fd, const Vector<String16>& args __unused) const
{
const size_t SIZE = 256;
@@ -1749,7 +1843,7 @@
snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType,
mVolume[0], mVolume[1]);
result.append(buffer);
- snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat,
+ snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%zu)\n", mFormat,
mChannelCount, mFrameCount);
result.append(buffer);
snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus);
@@ -1768,7 +1862,7 @@
// =========================================================================
-void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who)
+void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
{
sp<AudioTrack> audioTrack = mAudioTrack.promote();
if (audioTrack != 0) {
@@ -1780,7 +1874,8 @@
// =========================================================================
AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava)
- : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL)
+ : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL),
+ mIgnoreNextPausedInt(false)
{
}
@@ -1797,6 +1892,10 @@
// caller will check for exitPending()
return true;
}
+ if (mIgnoreNextPausedInt) {
+ mIgnoreNextPausedInt = false;
+ mPausedInt = false;
+ }
if (mPausedInt) {
if (mPausedNs > 0) {
(void) mMyCond.waitRelative(mMyLock, mPausedNs);
@@ -1807,7 +1906,7 @@
return true;
}
}
- nsecs_t ns = mReceiver.processAudioBuffer(this);
+ nsecs_t ns = mReceiver.processAudioBuffer();
switch (ns) {
case 0:
return true;
@@ -1831,12 +1930,7 @@
{
// must be in this order to avoid a race condition
Thread::requestExit();
- AutoMutex _l(mMyLock);
- if (mPaused || mPausedInt) {
- mPaused = false;
- mPausedInt = false;
- mMyCond.signal();
- }
+ resume();
}
void AudioTrack::AudioTrackThread::pause()
@@ -1848,6 +1942,7 @@
void AudioTrack::AudioTrackThread::resume()
{
AutoMutex _l(mMyLock);
+ mIgnoreNextPausedInt = true;
if (mPaused || mPausedInt) {
mPaused = false;
mPausedInt = false;
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 4fd92b2..fdd1a12 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -26,7 +26,7 @@
namespace android {
audio_track_cblk_t::audio_track_cblk_t()
- : mServer(0), frameCount_(0), mFutex(0), mMinimum(0),
+ : mServer(0), mFutex(0), mMinimum(0),
mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mFlags(0)
{
memset(&u, 0, sizeof(u));
@@ -316,6 +316,27 @@
(mFrameCountP2 - 1);
}
+size_t ClientProxy::getFramesFilled() {
+ audio_track_cblk_t* cblk = mCblk;
+ int32_t front;
+ int32_t rear;
+
+ if (mIsOut) {
+ front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
+ rear = cblk->u.mStreaming.mRear;
+ } else {
+ rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
+ front = cblk->u.mStreaming.mFront;
+ }
+ ssize_t filled = rear - front;
+ // pipe should not be overfull
+ if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
+ ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
+ return 0;
+ }
+ return (size_t)filled;
+}
+
// ---------------------------------------------------------------------------
void AudioTrackClientProxy::flush()
@@ -454,9 +475,14 @@
void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int loopCount)
{
+ // This can only happen on a 64-bit client
+ if (loopStart > UINT32_MAX || loopEnd > UINT32_MAX) {
+ // FIXME Should return an error status
+ return;
+ }
StaticAudioTrackState newState;
- newState.mLoopStart = loopStart;
- newState.mLoopEnd = loopEnd;
+ newState.mLoopStart = (uint32_t) loopStart;
+ newState.mLoopEnd = (uint32_t) loopEnd;
newState.mLoopCount = loopCount;
mBufferPosition = loopStart;
(void) mMutator.push(newState);
@@ -466,7 +492,7 @@
{
size_t bufferPosition;
if (mMutator.ack()) {
- bufferPosition = mCblk->u.mStatic.mBufferPosition;
+ bufferPosition = (size_t) mCblk->u.mStatic.mBufferPosition;
if (bufferPosition > mFrameCount) {
bufferPosition = mFrameCount;
}
@@ -485,7 +511,7 @@
{
}
-status_t ServerProxy::obtainBuffer(Buffer* buffer)
+status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
{
LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0);
if (mIsShutdown) {
@@ -558,7 +584,11 @@
buffer->mRaw = part1 > 0 ?
&((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL;
buffer->mNonContig = availToServer - part1;
- mUnreleased = part1;
+ // After flush(), allow releaseBuffer() on a previously obtained buffer;
+ // see "Acknowledge any pending flush()" in audioflinger/Tracks.cpp.
+ if (!ackFlush) {
+ mUnreleased = part1;
+ }
return part1 > 0 ? NO_ERROR : WOULD_BLOCK;
}
no_init:
@@ -597,7 +627,7 @@
if (half == 0) {
half = 1;
}
- size_t minimum = cblk->mMinimum;
+ size_t minimum = (size_t) cblk->mMinimum;
if (minimum == 0) {
minimum = mIsOut ? half : 1;
} else if (minimum > half) {
@@ -735,12 +765,13 @@
mIsShutdown = true;
return (ssize_t) NO_INIT;
}
- mCblk->u.mStatic.mBufferPosition = position;
+ // This may overflow, but client is not supposed to rely on it
+ mCblk->u.mStatic.mBufferPosition = (uint32_t) position;
}
return (ssize_t) position;
}
-status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer)
+status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush __unused)
{
if (mIsShutdown) {
buffer->mFrameCount = 0;
@@ -811,7 +842,8 @@
mPosition = newPosition;
cblk->mServer += stepCount;
- cblk->u.mStatic.mBufferPosition = newPosition;
+ // This may overflow, but client is not supposed to rely on it
+ cblk->u.mStatic.mBufferPosition = (uint32_t) newPosition;
if (setFlags != 0) {
(void) android_atomic_or(setFlags, &cblk->mFlags);
// this would be a good place to wake a futex
@@ -822,7 +854,7 @@
buffer->mNonContig = 0;
}
-void StaticAudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
+void StaticAudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount __unused)
{
// Unlike AudioTrackServerProxy::tallyUnderrunFrames() used for streaming tracks,
// we don't have a location to count underrun frames. The underrun frame counter
diff --git a/media/libmedia/CharacterEncodingDetector.cpp b/media/libmedia/CharacterEncodingDetector.cpp
new file mode 100644
index 0000000..eb091ac
--- /dev/null
+++ b/media/libmedia/CharacterEncodingDetector.cpp
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2013 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 "CharacterEncodingDector"
+#include <utils/Log.h>
+
+#include "CharacterEncodingDetector.h"
+#include "CharacterEncodingDetectorTables.h"
+
+#include "utils/Vector.h"
+#include "StringArray.h"
+
+#include "unicode/ucnv.h"
+#include "unicode/ucsdet.h"
+#include "unicode/ustring.h"
+
+namespace android {
+
+CharacterEncodingDetector::CharacterEncodingDetector() {
+
+ UErrorCode status = U_ZERO_ERROR;
+ mUtf8Conv = ucnv_open("UTF-8", &status);
+ if (U_FAILURE(status)) {
+ ALOGE("could not create UConverter for UTF-8");
+ mUtf8Conv = NULL;
+ }
+}
+
+CharacterEncodingDetector::~CharacterEncodingDetector() {
+ ucnv_close(mUtf8Conv);
+}
+
+void CharacterEncodingDetector::addTag(const char *name, const char *value) {
+ mNames.push_back(name);
+ mValues.push_back(value);
+}
+
+size_t CharacterEncodingDetector::size() {
+ return mNames.size();
+}
+
+status_t CharacterEncodingDetector::getTag(int index, const char **name, const char**value) {
+ if (index >= mNames.size()) {
+ return BAD_VALUE;
+ }
+
+ *name = mNames.getEntry(index);
+ *value = mValues.getEntry(index);
+ return OK;
+}
+
+static bool isPrintableAscii(const char *value, size_t len) {
+ for (size_t i = 0; i < len; i++) {
+ if ((value[i] & 0x80) || value[i] < 0x20 || value[i] == 0x7f) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void CharacterEncodingDetector::detectAndConvert() {
+
+ int size = mNames.size();
+ ALOGV("%d tags before conversion", size);
+ for (int i = 0; i < size; i++) {
+ ALOGV("%s: %s", mNames.getEntry(i), mValues.getEntry(i));
+ }
+
+ if (size && mUtf8Conv) {
+
+ UErrorCode status = U_ZERO_ERROR;
+ UCharsetDetector *csd = ucsdet_open(&status);
+ const UCharsetMatch *ucm;
+
+ // try combined detection of artist/album/title etc.
+ char buf[1024];
+ buf[0] = 0;
+ int idx;
+ for (int i = 0; i < size; i++) {
+ const char *name = mNames.getEntry(i);
+ const char *value = mValues.getEntry(i);
+ if (!isPrintableAscii(value, strlen(value)) && (
+ !strcmp(name, "artist") ||
+ !strcmp(name, "albumartist") ||
+ !strcmp(name, "composer") ||
+ !strcmp(name, "genre") ||
+ !strcmp(name, "album") ||
+ !strcmp(name, "title"))) {
+ strlcat(buf, value, sizeof(buf));
+ // separate tags by space so ICU's ngram detector can do its job
+ strlcat(buf, " ", sizeof(buf));
+ }
+ }
+ ucsdet_setText(csd, buf, strlen(buf), &status);
+
+ int32_t matches;
+ const UCharsetMatch** ucma = ucsdet_detectAll(csd, &matches, &status);
+ const char *combinedenc = "???";
+
+ const UCharsetMatch* bestCombinedMatch = getPreferred(buf, strlen(buf), ucma, matches);
+
+ if (bestCombinedMatch != NULL) {
+ combinedenc = ucsdet_getName(bestCombinedMatch, &status);
+ }
+
+ for (int i = 0; i < size; i++) {
+ const char *name = mNames.getEntry(i);
+ uint8_t* src = (uint8_t *)mValues.getEntry(i);
+ int len = strlen((char *)src);
+ uint8_t* dest = src;
+
+ ALOGV("@@@ checking %s", name);
+ const char *s = mValues.getEntry(i);
+ int32_t inputLength = strlen(s);
+ const char *enc;
+
+ if (!strcmp(name, "artist") ||
+ !strcmp(name, "albumartist") ||
+ !strcmp(name, "composer") ||
+ !strcmp(name, "genre") ||
+ !strcmp(name, "album") ||
+ !strcmp(name, "title")) {
+ // use encoding determined from the combination of artist/album/title etc.
+ enc = combinedenc;
+ } else {
+ ucsdet_setText(csd, s, inputLength, &status);
+ ucm = ucsdet_detect(csd, &status);
+ if (!ucm) {
+ mValues.setEntry(i, "???");
+ continue;
+ }
+ enc = ucsdet_getName(ucm, &status);
+ ALOGV("@@@@ recognized charset: %s for %s confidence %d",
+ enc, mNames.getEntry(i), ucsdet_getConfidence(ucm, &status));
+ }
+
+ if (strcmp(enc,"UTF-8") != 0) {
+ // only convert if the source encoding isn't already UTF-8
+ ALOGV("@@@ using converter %s for %s", enc, mNames.getEntry(i));
+ UConverter *conv = ucnv_open(enc, &status);
+ if (U_FAILURE(status)) {
+ ALOGE("could not create UConverter for %s", enc);
+ continue;
+ }
+
+ // convert from native encoding to UTF-8
+ const char* source = mValues.getEntry(i);
+ int targetLength = len * 3 + 1;
+ char* buffer = new char[targetLength];
+ // don't normally check for NULL, but in this case targetLength may be large
+ if (!buffer)
+ break;
+ char* target = buffer;
+
+ ucnv_convertEx(mUtf8Conv, conv, &target, target + targetLength,
+ &source, source + strlen(source),
+ NULL, NULL, NULL, NULL, TRUE, TRUE, &status);
+
+ if (U_FAILURE(status)) {
+ ALOGE("ucnv_convertEx failed: %d", status);
+ mValues.setEntry(i, "???");
+ } else {
+ // zero terminate
+ *target = 0;
+ mValues.setEntry(i, buffer);
+ }
+
+ delete[] buffer;
+
+ ucnv_close(conv);
+ }
+ }
+
+ for (int i = size - 1; i >= 0; --i) {
+ if (strlen(mValues.getEntry(i)) == 0) {
+ ALOGV("erasing %s because entry is empty", mNames.getEntry(i));
+ mNames.erase(i);
+ mValues.erase(i);
+ }
+ }
+
+ ucsdet_close(csd);
+ }
+}
+
+/*
+ * When ICU detects multiple encoding matches, apply additional heuristics to determine
+ * which one is the best match, since ICU can't always be trusted to make the right choice.
+ *
+ * What this method does is:
+ * - decode the input using each of the matches found
+ * - recalculate the starting confidence level for multibyte encodings using a different
+ * algorithm and larger frequent character lists than ICU
+ * - devalue encoding where the conversion contains unlikely characters (symbols, reserved, etc)
+ * - pick the highest match
+ */
+const UCharsetMatch *CharacterEncodingDetector::getPreferred(
+ const char *input, size_t len, const UCharsetMatch** ucma, size_t nummatches) {
+
+ Vector<const UCharsetMatch*> matches;
+ UErrorCode status = U_ZERO_ERROR;
+
+ ALOGV("%d matches", nummatches);
+ for (size_t i = 0; i < nummatches; i++) {
+ const char *encname = ucsdet_getName(ucma[i], &status);
+ int confidence = ucsdet_getConfidence(ucma[i], &status);
+ ALOGV("%d: %s %d", i, encname, confidence);
+ matches.push_back(ucma[i]);
+ }
+
+ size_t num = matches.size();
+ if (num == 0) {
+ return NULL;
+ }
+ if (num == 1) {
+ return matches[0];
+ }
+
+ ALOGV("considering %d matches", num);
+
+ // keep track of how many "special" characters result when converting the input using each
+ // encoding
+ Vector<int> newconfidence;
+ for (size_t i = 0; i < num; i++) {
+ const uint16_t *freqdata = NULL;
+ float freqcoverage = 0;
+ status = U_ZERO_ERROR;
+ const char *encname = ucsdet_getName(matches[i], &status);
+ int confidence = ucsdet_getConfidence(matches[i], &status);
+ if (!strcmp("GB18030", encname)) {
+ freqdata = frequent_zhCN;
+ freqcoverage = frequent_zhCN_coverage;
+ } else if (!strcmp("Big5", encname)) {
+ freqdata = frequent_zhTW;
+ freqcoverage = frequent_zhTW_coverage;
+ } else if (!strcmp("EUC-KR", encname)) {
+ freqdata = frequent_ko;
+ freqcoverage = frequent_ko_coverage;
+ } else if (!strcmp("EUC-JP", encname)) {
+ freqdata = frequent_ja;
+ freqcoverage = frequent_ja_coverage;
+ } else if (!strcmp("Shift_JIS", encname)) {
+ freqdata = frequent_ja;
+ freqcoverage = frequent_ja_coverage;
+ }
+
+ ALOGV("%d: %s %d", i, encname, confidence);
+ UConverter *conv = ucnv_open(encname, &status);
+ const char *source = input;
+ const char *sourceLimit = input + len;
+ status = U_ZERO_ERROR;
+ int demerit = 0;
+ int frequentchars = 0;
+ int totalchars = 0;
+ while (true) {
+ // demerit the current encoding for each "special" character found after conversion.
+ // The amount of demerit is somewhat arbitrarily chosen.
+ int inchar;
+ if (source != sourceLimit) {
+ inchar = (source[0] << 8) + source[1];
+ }
+ UChar32 c = ucnv_getNextUChar(conv, &source, sourceLimit, &status);
+ if (!U_SUCCESS(status)) {
+ break;
+ }
+ if (c < 0x20 || (c >= 0x7f && c <= 0x009f)) {
+ ALOGV("control character %x", c);
+ demerit += 100;
+ } else if ((c >= 0xa0 && c <= 0xbe) // symbols, superscripts
+ || (c == 0xd7) || (c == 0xf7) // multiplication and division signs
+ || (c >= 0x2000 && c <= 0x209f)) { // punctuation, superscripts
+ ALOGV("unlikely character %x", c);
+ demerit += 10;
+ } else if (c >= 0xe000 && c <= 0xf8ff) {
+ ALOGV("private use character %x", c);
+ demerit += 30;
+ } else if (c >= 0x2190 && c <= 0x2bff) {
+ // this range comprises various symbol ranges that are unlikely to appear in
+ // music file metadata.
+ ALOGV("symbol %x", c);
+ demerit += 10;
+ } else if (c == 0xfffd) {
+ ALOGV("replacement character");
+ demerit += 50;
+ } else if (c >= 0xfff0 && c <= 0xfffc) {
+ ALOGV("unicode special %x", c);
+ demerit += 50;
+ } else if (freqdata != NULL) {
+ totalchars++;
+ if (isFrequent(freqdata, c)) {
+ frequentchars++;
+ }
+ }
+ }
+ if (freqdata != NULL && totalchars != 0) {
+ int myconfidence = 10 + float((100 * frequentchars) / totalchars) / freqcoverage;
+ ALOGV("ICU confidence: %d, my confidence: %d (%d %d)", confidence, myconfidence,
+ totalchars, frequentchars);
+ if (myconfidence > 100) myconfidence = 100;
+ if (myconfidence < 0) myconfidence = 0;
+ confidence = myconfidence;
+ }
+ ALOGV("%d-%d=%d", confidence, demerit, confidence - demerit);
+ newconfidence.push_back(confidence - demerit);
+ ucnv_close(conv);
+ if (i == 0 && (confidence - demerit) == 100) {
+ // no need to check any further, we'll end up using this match anyway
+ break;
+ }
+ }
+
+ // find match with highest confidence after adjusting for unlikely characters
+ int highest = newconfidence[0];
+ size_t highestidx = 0;
+ num = newconfidence.size();
+ for (size_t i = 1; i < num; i++) {
+ if (newconfidence[i] > highest) {
+ highest = newconfidence[i];
+ highestidx = i;
+ }
+ }
+ status = U_ZERO_ERROR;
+ ALOGV("selecting '%s' w/ %d confidence", ucsdet_getName(matches[highestidx], &status), highest);
+ return matches[highestidx];
+}
+
+
+bool CharacterEncodingDetector::isFrequent(const uint16_t *values, uint32_t c) {
+
+ int start = 0;
+ int end = 511; // All the tables have 512 entries
+ int mid = (start+end)/2;
+
+ while(start <= end) {
+ if(c == values[mid]) {
+ return true;
+ } else if (c > values[mid]) {
+ start = mid + 1;
+ } else {
+ end = mid - 1;
+ }
+
+ mid = (start + end) / 2;
+ }
+
+ return false;
+}
+
+
+} // namespace android
diff --git a/media/libmedia/CharacterEncodingDetector.h b/media/libmedia/CharacterEncodingDetector.h
new file mode 100644
index 0000000..3655a91
--- /dev/null
+++ b/media/libmedia/CharacterEncodingDetector.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 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 _CHARACTER_ENCODING_DETECTOR_H
+#define _CHARACTER_ENCODING_DETECTOR_H
+
+#include <media/mediascanner.h>
+
+#include "StringArray.h"
+
+#include "unicode/ucnv.h"
+#include "unicode/ucsdet.h"
+#include "unicode/ustring.h"
+
+namespace android {
+
+class CharacterEncodingDetector {
+
+ public:
+ CharacterEncodingDetector();
+ ~CharacterEncodingDetector();
+
+ void addTag(const char *name, const char *value);
+ size_t size();
+
+ void detectAndConvert();
+ status_t getTag(int index, const char **name, const char**value);
+
+ private:
+ const UCharsetMatch *getPreferred(
+ const char *input, size_t len, const UCharsetMatch** ucma, size_t matches);
+
+ bool isFrequent(const uint16_t *values, uint32_t c);
+
+ // cached name and value strings, for native encoding support.
+ // TODO: replace these with byte blob arrays that don't require the data to be
+ // singlenullbyte-terminated
+ StringArray mNames;
+ StringArray mValues;
+
+ UConverter* mUtf8Conv;
+};
+
+
+
+}; // namespace android
+
+#endif
diff --git a/media/libmedia/CharacterEncodingDetectorTables.h b/media/libmedia/CharacterEncodingDetectorTables.h
new file mode 100644
index 0000000..1fe1137
--- /dev/null
+++ b/media/libmedia/CharacterEncodingDetectorTables.h
@@ -0,0 +1,2092 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// The 512 most frequently occuring characters for the zhCN language in a sample of the Internet.
+// Ordered by codepoint, comment shows character and ranking by frequency
+const uint16_t frequent_zhCN[] = {
+ 0x4E00, // 一, #2
+ 0x4E07, // 万, #306
+ 0x4E09, // 三, #138
+ 0x4E0A, // 上, #16
+ 0x4E0B, // 下, #25
+ 0x4E0D, // 不, #7
+ 0x4E0E, // 与, #133
+ 0x4E13, // 专, #151
+ 0x4E16, // 世, #346
+ 0x4E1A, // 业, #39
+ 0x4E1C, // 东, #197
+ 0x4E24, // 两, #376
+ 0x4E2A, // 个, #23
+ 0x4E2D, // 中, #4
+ 0x4E3A, // 为, #31
+ 0x4E3B, // 主, #95
+ 0x4E3E, // 举, #418
+ 0x4E48, // 么, #93
+ 0x4E4B, // 之, #131
+ 0x4E50, // 乐, #130
+ 0x4E5F, // 也, #145
+ 0x4E66, // 书, #283
+ 0x4E70, // 买, #483
+ 0x4E86, // 了, #13
+ 0x4E8B, // 事, #168
+ 0x4E8C, // 二, #218
+ 0x4E8E, // 于, #64
+ 0x4E94, // 五, #430
+ 0x4E9A, // 亚, #468
+ 0x4E9B, // 些, #366
+ 0x4EA4, // 交, #243
+ 0x4EA7, // 产, #86
+ 0x4EAB, // 享, #345
+ 0x4EAC, // 京, #206
+ 0x4EBA, // 人, #3
+ 0x4EC0, // 什, #287
+ 0x4ECB, // 介, #478
+ 0x4ECE, // 从, #381
+ 0x4ED6, // 他, #129
+ 0x4EE3, // 代, #241
+ 0x4EE5, // 以, #51
+ 0x4EEC, // 们, #83
+ 0x4EF6, // 件, #141
+ 0x4EF7, // 价, #140
+ 0x4EFB, // 任, #383
+ 0x4F01, // 企, #439
+ 0x4F18, // 优, #374
+ 0x4F1A, // 会, #29
+ 0x4F20, // 传, #222
+ 0x4F46, // 但, #451
+ 0x4F4D, // 位, #208
+ 0x4F53, // 体, #98
+ 0x4F55, // 何, #339
+ 0x4F5C, // 作, #44
+ 0x4F60, // 你, #76
+ 0x4F7F, // 使, #272
+ 0x4F9B, // 供, #375
+ 0x4FDD, // 保, #180
+ 0x4FE1, // 信, #84
+ 0x4FEE, // 修, #437
+ 0x503C, // 值, #450
+ 0x505A, // 做, #368
+ 0x5065, // 健, #484
+ 0x50CF, // 像, #487
+ 0x513F, // 儿, #326
+ 0x5143, // 元, #202
+ 0x5148, // 先, #485
+ 0x5149, // 光, #254
+ 0x514B, // 克, #503
+ 0x514D, // 免, #349
+ 0x5165, // 入, #156
+ 0x5168, // 全, #47
+ 0x516C, // 公, #35
+ 0x5171, // 共, #448
+ 0x5173, // 关, #49
+ 0x5176, // 其, #195
+ 0x5177, // 具, #329
+ 0x5185, // 内, #109
+ 0x518C, // 册, #225
+ 0x519B, // 军, #466
+ 0x51FA, // 出, #53
+ 0x51FB, // 击, #359
+ 0x5206, // 分, #22
+ 0x5217, // 列, #410
+ 0x521B, // 创, #399
+ 0x5229, // 利, #296
+ 0x522B, // 别, #372
+ 0x5230, // 到, #33
+ 0x5236, // 制, #192
+ 0x524D, // 前, #117
+ 0x529B, // 力, #173
+ 0x529E, // 办, #436
+ 0x529F, // 功, #455
+ 0x52A0, // 加, #97
+ 0x52A1, // 务, #100
+ 0x52A8, // 动, #46
+ 0x52A9, // 助, #365
+ 0x5305, // 包, #331
+ 0x5316, // 化, #155
+ 0x5317, // 北, #194
+ 0x533A, // 区, #105
+ 0x533B, // 医, #234
+ 0x5341, // 十, #294
+ 0x534E, // 华, #205
+ 0x5355, // 单, #259
+ 0x5357, // 南, #182
+ 0x535A, // 博, #153
+ 0x5361, // 卡, #332
+ 0x539F, // 原, #271
+ 0x53BB, // 去, #282
+ 0x53C2, // 参, #500
+ 0x53CA, // 及, #255
+ 0x53CB, // 友, #186
+ 0x53CD, // 反, #422
+ 0x53D1, // 发, #15
+ 0x53D7, // 受, #507
+ 0x53D8, // 变, #395
+ 0x53E3, // 口, #293
+ 0x53EA, // 只, #340
+ 0x53EF, // 可, #45
+ 0x53F0, // 台, #267
+ 0x53F7, // 号, #121
+ 0x53F8, // 司, #150
+ 0x5404, // 各, #491
+ 0x5408, // 合, #115
+ 0x540C, // 同, #189
+ 0x540D, // 名, #127
+ 0x540E, // 后, #75
+ 0x5411, // 向, #459
+ 0x5427, // 吧, #353
+ 0x544A, // 告, #318
+ 0x5458, // 员, #232
+ 0x5468, // 周, #347
+ 0x548C, // 和, #43
+ 0x54C1, // 品, #36
+ 0x5546, // 商, #148
+ 0x5668, // 器, #228
+ 0x56DB, // 四, #352
+ 0x56DE, // 回, #38
+ 0x56E0, // 因, #355
+ 0x56E2, // 团, #412
+ 0x56ED, // 园, #470
+ 0x56FD, // 国, #12
+ 0x56FE, // 图, #32
+ 0x5728, // 在, #10
+ 0x5730, // 地, #30
+ 0x573A, // 场, #177
+ 0x575B, // 坛, #364
+ 0x578B, // 型, #274
+ 0x57CE, // 城, #172
+ 0x57FA, // 基, #315
+ 0x58EB, // 士, #434
+ 0x58F0, // 声, #397
+ 0x5904, // 处, #416
+ 0x5907, // 备, #270
+ 0x590D, // 复, #122
+ 0x5916, // 外, #190
+ 0x591A, // 多, #40
+ 0x5927, // 大, #8
+ 0x5929, // 天, #52
+ 0x592A, // 太, #456
+ 0x5934, // 头, #258
+ 0x5973, // 女, #65
+ 0x597D, // 好, #62
+ 0x5982, // 如, #135
+ 0x5A31, // 娱, #452
+ 0x5B50, // 子, #37
+ 0x5B57, // 字, #285
+ 0x5B66, // 学, #19
+ 0x5B89, // 安, #144
+ 0x5B8C, // 完, #469
+ 0x5B9A, // 定, #179
+ 0x5B9D, // 宝, #188
+ 0x5B9E, // 实, #154
+ 0x5BA2, // 客, #174
+ 0x5BB6, // 家, #26
+ 0x5BB9, // 容, #307
+ 0x5BC6, // 密, #471
+ 0x5BF9, // 对, #90
+ 0x5BFC, // 导, #348
+ 0x5C06, // 将, #265
+ 0x5C0F, // 小, #28
+ 0x5C11, // 少, #379
+ 0x5C14, // 尔, #490
+ 0x5C31, // 就, #101
+ 0x5C55, // 展, #291
+ 0x5C71, // 山, #239
+ 0x5DDE, // 州, #227
+ 0x5DE5, // 工, #73
+ 0x5DF1, // 己, #480
+ 0x5DF2, // 已, #310
+ 0x5E02, // 市, #78
+ 0x5E03, // 布, #350
+ 0x5E08, // 师, #277
+ 0x5E16, // 帖, #396
+ 0x5E26, // 带, #449
+ 0x5E2E, // 帮, #461
+ 0x5E38, // 常, #319
+ 0x5E73, // 平, #217
+ 0x5E74, // 年, #20
+ 0x5E76, // 并, #440
+ 0x5E7F, // 广, #166
+ 0x5E93, // 库, #446
+ 0x5E94, // 应, #187
+ 0x5E97, // 店, #320
+ 0x5EA6, // 度, #114
+ 0x5EB7, // 康, #499
+ 0x5EFA, // 建, #211
+ 0x5F00, // 开, #72
+ 0x5F0F, // 式, #207
+ 0x5F15, // 引, #495
+ 0x5F20, // 张, #385
+ 0x5F3A, // 强, #404
+ 0x5F53, // 当, #233
+ 0x5F55, // 录, #146
+ 0x5F62, // 形, #494
+ 0x5F69, // 彩, #356
+ 0x5F71, // 影, #214
+ 0x5F88, // 很, #300
+ 0x5F97, // 得, #193
+ 0x5FAE, // 微, #245
+ 0x5FC3, // 心, #70
+ 0x5FEB, // 快, #324
+ 0x6001, // 态, #508
+ 0x600E, // 怎, #370
+ 0x6027, // 性, #99
+ 0x603B, // 总, #398
+ 0x606F, // 息, #176
+ 0x60A8, // 您, #251
+ 0x60C5, // 情, #87
+ 0x60F3, // 想, #290
+ 0x610F, // 意, #184
+ 0x611F, // 感, #253
+ 0x620F, // 戏, #237
+ 0x6210, // 成, #71
+ 0x6211, // 我, #11
+ 0x6216, // 或, #321
+ 0x6218, // 战, #369
+ 0x6237, // 户, #215
+ 0x623F, // 房, #236
+ 0x6240, // 所, #147
+ 0x624B, // 手, #55
+ 0x624D, // 才, #407
+ 0x6253, // 打, #281
+ 0x6280, // 技, #203
+ 0x6295, // 投, #408
+ 0x62A4, // 护, #502
+ 0x62A5, // 报, #113
+ 0x62DB, // 招, #363
+ 0x6301, // 持, #403
+ 0x6307, // 指, #414
+ 0x636E, // 据, #409
+ 0x6392, // 排, #377
+ 0x63A5, // 接, #266
+ 0x63A8, // 推, #244
+ 0x63D0, // 提, #181
+ 0x641C, // 搜, #301
+ 0x64AD, // 播, #401
+ 0x652F, // 支, #400
+ 0x6536, // 收, #158
+ 0x653E, // 放, #317
+ 0x653F, // 政, #380
+ 0x6548, // 效, #496
+ 0x6559, // 教, #170
+ 0x6570, // 数, #136
+ 0x6587, // 文, #21
+ 0x6599, // 料, #295
+ 0x65AF, // 斯, #473
+ 0x65B0, // 新, #14
+ 0x65B9, // 方, #68
+ 0x65C5, // 旅, #457
+ 0x65E0, // 无, #164
+ 0x65E5, // 日, #50
+ 0x65F6, // 时, #18
+ 0x660E, // 明, #132
+ 0x6613, // 易, #428
+ 0x661F, // 星, #240
+ 0x662F, // 是, #6
+ 0x663E, // 显, #486
+ 0x66F4, // 更, #103
+ 0x6700, // 最, #61
+ 0x6708, // 月, #80
+ 0x6709, // 有, #5
+ 0x670D, // 服, #94
+ 0x671F, // 期, #139
+ 0x672C, // 本, #56
+ 0x672F, // 术, #216
+ 0x673A, // 机, #27
+ 0x6743, // 权, #250
+ 0x6761, // 条, #309
+ 0x6765, // 来, #42
+ 0x677F, // 板, #505
+ 0x6797, // 林, #475
+ 0x679C, // 果, #212
+ 0x67E5, // 查, #165
+ 0x6807, // 标, #269
+ 0x6821, // 校, #462
+ 0x6837, // 样, #314
+ 0x683C, // 格, #238
+ 0x6848, // 案, #378
+ 0x697C, // 楼, #342
+ 0x6A21, // 模, #413
+ 0x6B21, // 次, #263
+ 0x6B22, // 欢, #443
+ 0x6B3E, // 款, #358
+ 0x6B63, // 正, #219
+ 0x6B64, // 此, #362
+ 0x6BD4, // 比, #298
+ 0x6C11, // 民, #279
+ 0x6C14, // 气, #303
+ 0x6C34, // 水, #163
+ 0x6C42, // 求, #373
+ 0x6C5F, // 江, #336
+ 0x6CA1, // 没, #229
+ 0x6CBB, // 治, #425
+ 0x6CD5, // 法, #85
+ 0x6CE8, // 注, #119
+ 0x6D3B, // 活, #231
+ 0x6D41, // 流, #280
+ 0x6D4B, // 测, #460
+ 0x6D77, // 海, #124
+ 0x6D88, // 消, #415
+ 0x6DF1, // 深, #477
+ 0x6E05, // 清, #311
+ 0x6E38, // 游, #81
+ 0x6E90, // 源, #325
+ 0x706B, // 火, #498
+ 0x70B9, // 点, #58
+ 0x70ED, // 热, #183
+ 0x7136, // 然, #308
+ 0x7167, // 照, #431
+ 0x7231, // 爱, #223
+ 0x7247, // 片, #128
+ 0x7248, // 版, #91
+ 0x724C, // 牌, #429
+ 0x7269, // 物, #169
+ 0x7279, // 特, #224
+ 0x738B, // 王, #351
+ 0x73A9, // 玩, #476
+ 0x73B0, // 现, #125
+ 0x7403, // 球, #367
+ 0x7406, // 理, #69
+ 0x751F, // 生, #24
+ 0x7528, // 用, #17
+ 0x7531, // 由, #441
+ 0x7535, // 电, #34
+ 0x7537, // 男, #275
+ 0x754C, // 界, #419
+ 0x75C5, // 病, #371
+ 0x767B, // 登, #204
+ 0x767D, // 白, #338
+ 0x767E, // 百, #157
+ 0x7684, // 的, #1
+ 0x76D8, // 盘, #493
+ 0x76EE, // 目, #261
+ 0x76F4, // 直, #391
+ 0x76F8, // 相, #143
+ 0x7701, // 省, #464
+ 0x770B, // 看, #54
+ 0x771F, // 真, #249
+ 0x7740, // 着, #302
+ 0x77E5, // 知, #142
+ 0x7801, // 码, #257
+ 0x7814, // 研, #387
+ 0x793A, // 示, #334
+ 0x793E, // 社, #343
+ 0x795E, // 神, #330
+ 0x798F, // 福, #509
+ 0x79BB, // 离, #454
+ 0x79CD, // 种, #278
+ 0x79D1, // 科, #126
+ 0x79EF, // 积, #390
+ 0x7A0B, // 程, #209
+ 0x7A76, // 究, #504
+ 0x7A7A, // 空, #312
+ 0x7ACB, // 立, #393
+ 0x7AD9, // 站, #107
+ 0x7AE0, // 章, #304
+ 0x7B2C, // 第, #96
+ 0x7B49, // 等, #210
+ 0x7B54, // 答, #256
+ 0x7B80, // 简, #474
+ 0x7BA1, // 管, #221
+ 0x7C7B, // 类, #246
+ 0x7CBE, // 精, #226
+ 0x7CFB, // 系, #89
+ 0x7D22, // 索, #354
+ 0x7EA2, // 红, #417
+ 0x7EA7, // 级, #178
+ 0x7EBF, // 线, #108
+ 0x7EC4, // 组, #389
+ 0x7EC6, // 细, #442
+ 0x7ECF, // 经, #74
+ 0x7ED3, // 结, #333
+ 0x7ED9, // 给, #384
+ 0x7EDC, // 络, #472
+ 0x7EDF, // 统, #344
+ 0x7F16, // 编, #424
+ 0x7F51, // 网, #9
+ 0x7F6E, // 置, #411
+ 0x7F8E, // 美, #60
+ 0x8001, // 老, #292
+ 0x8003, // 考, #288
+ 0x8005, // 者, #106
+ 0x800C, // 而, #297
+ 0x8054, // 联, #159
+ 0x80B2, // 育, #327
+ 0x80FD, // 能, #59
+ 0x81EA, // 自, #77
+ 0x8272, // 色, #198
+ 0x8282, // 节, #361
+ 0x82B1, // 花, #299
+ 0x82F1, // 英, #316
+ 0x8350, // 荐, #402
+ 0x836F, // 药, #481
+ 0x8425, // 营, #394
+ 0x85CF, // 藏, #337
+ 0x884C, // 行, #41
+ 0x8868, // 表, #104
+ 0x88AB, // 被, #289
+ 0x88C5, // 装, #161
+ 0x897F, // 西, #199
+ 0x8981, // 要, #48
+ 0x89C1, // 见, #360
+ 0x89C2, // 观, #423
+ 0x89C4, // 规, #453
+ 0x89C6, // 视, #120
+ 0x89E3, // 解, #264
+ 0x8A00, // 言, #433
+ 0x8BA1, // 计, #191
+ 0x8BA4, // 认, #482
+ 0x8BA9, // 让, #421
+ 0x8BAE, // 议, #427
+ 0x8BAF, // 讯, #388
+ 0x8BB0, // 记, #273
+ 0x8BBA, // 论, #66
+ 0x8BBE, // 设, #162
+ 0x8BC1, // 证, #201
+ 0x8BC4, // 评, #111
+ 0x8BC6, // 识, #463
+ 0x8BD5, // 试, #323
+ 0x8BDD, // 话, #247
+ 0x8BE2, // 询, #432
+ 0x8BE5, // 该, #447
+ 0x8BE6, // 详, #497
+ 0x8BED, // 语, #268
+ 0x8BF4, // 说, #112
+ 0x8BF7, // 请, #213
+ 0x8BFB, // 读, #341
+ 0x8C03, // 调, #438
+ 0x8D22, // 财, #488
+ 0x8D28, // 质, #386
+ 0x8D2D, // 购, #260
+ 0x8D34, // 贴, #510
+ 0x8D39, // 费, #242
+ 0x8D44, // 资, #116
+ 0x8D77, // 起, #220
+ 0x8D85, // 超, #406
+ 0x8DEF, // 路, #235
+ 0x8EAB, // 身, #262
+ 0x8F66, // 车, #82
+ 0x8F6C, // 转, #322
+ 0x8F7D, // 载, #175
+ 0x8FBE, // 达, #435
+ 0x8FC7, // 过, #118
+ 0x8FD0, // 运, #357
+ 0x8FD1, // 近, #492
+ 0x8FD8, // 还, #171
+ 0x8FD9, // 这, #57
+ 0x8FDB, // 进, #160
+ 0x8FDE, // 连, #489
+ 0x9009, // 选, #328
+ 0x901A, // 通, #137
+ 0x901F, // 速, #458
+ 0x9020, // 造, #511
+ 0x9053, // 道, #79
+ 0x90A3, // 那, #305
+ 0x90E8, // 部, #102
+ 0x90FD, // 都, #167
+ 0x914D, // 配, #479
+ 0x9152, // 酒, #444
+ 0x91CC, // 里, #196
+ 0x91CD, // 重, #230
+ 0x91CF, // 量, #248
+ 0x91D1, // 金, #134
+ 0x9500, // 销, #465
+ 0x957F, // 长, #152
+ 0x95E8, // 门, #185
+ 0x95EE, // 问, #92
+ 0x95F4, // 间, #88
+ 0x95FB, // 闻, #313
+ 0x9605, // 阅, #467
+ 0x9633, // 阳, #420
+ 0x9645, // 际, #501
+ 0x9650, // 限, #286
+ 0x9662, // 院, #276
+ 0x96C6, // 集, #284
+ 0x9700, // 需, #405
+ 0x9762, // 面, #123
+ 0x97F3, // 音, #335
+ 0x9875, // 页, #63
+ 0x9879, // 项, #506
+ 0x9891, // 频, #200
+ 0x9898, // 题, #110
+ 0x98CE, // 风, #252
+ 0x98DF, // 食, #445
+ 0x9996, // 首, #149
+ 0x9999, // 香, #512
+ 0x9A6C, // 马, #392
+ 0x9A8C, // 验, #382
+ 0x9AD8, // 高, #67
+ 0x9F99, // 龙, #426
+};
+// the percentage of the sample covered by the above characters
+static const float frequent_zhCN_coverage=0.718950369339973;
+
+// The 512 most frequently occuring characters for the zhTW language in a sample of the Internet.
+// Ordered by codepoint, comment shows character and ranking by frequency
+const uint16_t frequent_zhTW[] = {
+ 0x4E00, // 一, #2
+ 0x4E09, // 三, #131
+ 0x4E0A, // 上, #12
+ 0x4E0B, // 下, #37
+ 0x4E0D, // 不, #6
+ 0x4E16, // 世, #312
+ 0x4E26, // 並, #434
+ 0x4E2D, // 中, #9
+ 0x4E3B, // 主, #97
+ 0x4E4B, // 之, #55
+ 0x4E5F, // 也, #95
+ 0x4E86, // 了, #19
+ 0x4E8B, // 事, #128
+ 0x4E8C, // 二, #187
+ 0x4E94, // 五, #339
+ 0x4E9B, // 些, #435
+ 0x4E9E, // 亞, #432
+ 0x4EA4, // 交, #264
+ 0x4EAB, // 享, #160
+ 0x4EBA, // 人, #3
+ 0x4EC0, // 什, #483
+ 0x4ECA, // 今, #380
+ 0x4ECB, // 介, #468
+ 0x4ED6, // 他, #65
+ 0x4EE3, // 代, #284
+ 0x4EE5, // 以, #26
+ 0x4EF6, // 件, #234
+ 0x4EFB, // 任, #381
+ 0x4EFD, // 份, #447
+ 0x4F46, // 但, #281
+ 0x4F4D, // 位, #202
+ 0x4F4F, // 住, #471
+ 0x4F55, // 何, #334
+ 0x4F5C, // 作, #56
+ 0x4F60, // 你, #64
+ 0x4F7F, // 使, #236
+ 0x4F86, // 來, #38
+ 0x4F9B, // 供, #397
+ 0x4FBF, // 便, #440
+ 0x4FC2, // 係, #506
+ 0x4FDD, // 保, #161
+ 0x4FE1, // 信, #268
+ 0x4FEE, // 修, #473
+ 0x500B, // 個, #27
+ 0x5011, // 們, #109
+ 0x505A, // 做, #383
+ 0x5065, // 健, #415
+ 0x5099, // 備, #461
+ 0x50B3, // 傳, #277
+ 0x50CF, // 像, #403
+ 0x50F9, // 價, #93
+ 0x512A, // 優, #396
+ 0x5143, // 元, #158
+ 0x5148, // 先, #382
+ 0x5149, // 光, #216
+ 0x514D, // 免, #321
+ 0x5152, // 兒, #374
+ 0x5165, // 入, #58
+ 0x5167, // 內, #106
+ 0x5168, // 全, #67
+ 0x5169, // 兩, #322
+ 0x516C, // 公, #53
+ 0x516D, // 六, #493
+ 0x5171, // 共, #456
+ 0x5176, // 其, #148
+ 0x5177, // 具, #328
+ 0x518A, // 冊, #360
+ 0x518D, // 再, #311
+ 0x51FA, // 出, #44
+ 0x5206, // 分, #15
+ 0x5217, // 列, #259
+ 0x5225, // 別, #361
+ 0x5229, // 利, #251
+ 0x5230, // 到, #29
+ 0x5247, // 則, #511
+ 0x524D, // 前, #82
+ 0x5275, // 創, #409
+ 0x529B, // 力, #176
+ 0x529F, // 功, #430
+ 0x52A0, // 加, #87
+ 0x52A9, // 助, #465
+ 0x52D5, // 動, #48
+ 0x52D9, // 務, #102
+ 0x5305, // 包, #248
+ 0x5316, // 化, #223
+ 0x5317, // 北, #145
+ 0x5340, // 區, #60
+ 0x5341, // 十, #242
+ 0x5357, // 南, #261
+ 0x535A, // 博, #484
+ 0x5361, // 卡, #327
+ 0x5370, // 印, #498
+ 0x5373, // 即, #351
+ 0x539F, // 原, #237
+ 0x53BB, // 去, #190
+ 0x53C3, // 參, #444
+ 0x53C8, // 又, #426
+ 0x53CA, // 及, #136
+ 0x53CB, // 友, #142
+ 0x53D6, // 取, #422
+ 0x53D7, // 受, #410
+ 0x53E3, // 口, #357
+ 0x53EA, // 只, #250
+ 0x53EF, // 可, #35
+ 0x53F0, // 台, #34
+ 0x53F8, // 司, #226
+ 0x5403, // 吃, #362
+ 0x5404, // 各, #454
+ 0x5408, // 合, #147
+ 0x540C, // 同, #173
+ 0x540D, // 名, #108
+ 0x544A, // 告, #186
+ 0x548C, // 和, #130
+ 0x54C1, // 品, #23
+ 0x54E1, // 員, #150
+ 0x5546, // 商, #75
+ 0x554F, // 問, #120
+ 0x559C, // 喜, #502
+ 0x55AE, // 單, #210
+ 0x55CE, // 嗎, #443
+ 0x5668, // 器, #305
+ 0x56DB, // 四, #318
+ 0x56DE, // 回, #59
+ 0x56E0, // 因, #253
+ 0x570B, // 國, #21
+ 0x5712, // 園, #345
+ 0x5716, // 圖, #73
+ 0x5718, // 團, #338
+ 0x5728, // 在, #11
+ 0x5730, // 地, #50
+ 0x578B, // 型, #270
+ 0x57CE, // 城, #466
+ 0x57FA, // 基, #349
+ 0x5831, // 報, #127
+ 0x5834, // 場, #165
+ 0x58EB, // 士, #372
+ 0x5916, // 外, #152
+ 0x591A, // 多, #54
+ 0x5927, // 大, #8
+ 0x5929, // 天, #43
+ 0x592A, // 太, #343
+ 0x5947, // 奇, #325
+ 0x5973, // 女, #85
+ 0x5979, // 她, #420
+ 0x597D, // 好, #22
+ 0x5982, // 如, #144
+ 0x5B50, // 子, #46
+ 0x5B57, // 字, #275
+ 0x5B78, // 學, #49
+ 0x5B89, // 安, #239
+ 0x5B8C, // 完, #320
+ 0x5B9A, // 定, #159
+ 0x5BA2, // 客, #188
+ 0x5BB6, // 家, #31
+ 0x5BB9, // 容, #244
+ 0x5BE6, // 實, #198
+ 0x5BF6, // 寶, #367
+ 0x5C07, // 將, #232
+ 0x5C08, // 專, #133
+ 0x5C0B, // 尋, #352
+ 0x5C0D, // 對, #126
+ 0x5C0E, // 導, #418
+ 0x5C0F, // 小, #20
+ 0x5C11, // 少, #368
+ 0x5C31, // 就, #63
+ 0x5C55, // 展, #341
+ 0x5C71, // 山, #273
+ 0x5DE5, // 工, #121
+ 0x5DF1, // 己, #402
+ 0x5DF2, // 已, #299
+ 0x5E02, // 市, #81
+ 0x5E2B, // 師, #262
+ 0x5E36, // 帶, #470
+ 0x5E38, // 常, #303
+ 0x5E73, // 平, #297
+ 0x5E74, // 年, #30
+ 0x5E97, // 店, #171
+ 0x5EA6, // 度, #220
+ 0x5EB7, // 康, #441
+ 0x5EE3, // 廣, #279
+ 0x5EFA, // 建, #254
+ 0x5F0F, // 式, #155
+ 0x5F15, // 引, #346
+ 0x5F35, // 張, #366
+ 0x5F37, // 強, #437
+ 0x5F71, // 影, #94
+ 0x5F88, // 很, #177
+ 0x5F8C, // 後, #66
+ 0x5F97, // 得, #113
+ 0x5F9E, // 從, #436
+ 0x5FC3, // 心, #57
+ 0x5FEB, // 快, #292
+ 0x6027, // 性, #175
+ 0x606F, // 息, #378
+ 0x60A8, // 您, #252
+ 0x60C5, // 情, #123
+ 0x60F3, // 想, #178
+ 0x610F, // 意, #168
+ 0x611B, // 愛, #125
+ 0x611F, // 感, #211
+ 0x61C9, // 應, #164
+ 0x6210, // 成, #86
+ 0x6211, // 我, #7
+ 0x6216, // 或, #199
+ 0x6230, // 戰, #438
+ 0x6232, // 戲, #309
+ 0x6236, // 戶, #497
+ 0x623F, // 房, #274
+ 0x6240, // 所, #79
+ 0x624B, // 手, #68
+ 0x624D, // 才, #400
+ 0x6253, // 打, #278
+ 0x627E, // 找, #449
+ 0x6280, // 技, #332
+ 0x6295, // 投, #425
+ 0x62C9, // 拉, #500
+ 0x62CD, // 拍, #398
+ 0x6307, // 指, #407
+ 0x6392, // 排, #458
+ 0x63A5, // 接, #326
+ 0x63A8, // 推, #153
+ 0x63D0, // 提, #235
+ 0x641C, // 搜, #314
+ 0x6469, // 摩, #472
+ 0x6536, // 收, #249
+ 0x6539, // 改, #508
+ 0x653E, // 放, #331
+ 0x653F, // 政, #295
+ 0x6559, // 教, #184
+ 0x6574, // 整, #394
+ 0x6578, // 數, #134
+ 0x6587, // 文, #16
+ 0x6599, // 料, #167
+ 0x65AF, // 斯, #476
+ 0x65B0, // 新, #10
+ 0x65B9, // 方, #96
+ 0x65BC, // 於, #70
+ 0x65C5, // 旅, #289
+ 0x65E5, // 日, #18
+ 0x660E, // 明, #118
+ 0x6613, // 易, #482
+ 0x661F, // 星, #205
+ 0x662F, // 是, #5
+ 0x6642, // 時, #13
+ 0x66F4, // 更, #149
+ 0x66F8, // 書, #209
+ 0x6700, // 最, #51
+ 0x6703, // 會, #14
+ 0x6708, // 月, #25
+ 0x6709, // 有, #4
+ 0x670D, // 服, #99
+ 0x671F, // 期, #139
+ 0x672A, // 未, #404
+ 0x672C, // 本, #45
+ 0x6771, // 東, #221
+ 0x677F, // 板, #364
+ 0x6797, // 林, #330
+ 0x679C, // 果, #179
+ 0x67E5, // 查, #283
+ 0x683C, // 格, #157
+ 0x6848, // 案, #392
+ 0x689D, // 條, #406
+ 0x696D, // 業, #103
+ 0x6A02, // 樂, #116
+ 0x6A13, // 樓, #411
+ 0x6A19, // 標, #384
+ 0x6A23, // 樣, #306
+ 0x6A5F, // 機, #40
+ 0x6AA2, // 檢, #359
+ 0x6B0A, // 權, #228
+ 0x6B21, // 次, #227
+ 0x6B3E, // 款, #276
+ 0x6B4C, // 歌, #496
+ 0x6B61, // 歡, #427
+ 0x6B63, // 正, #206
+ 0x6B64, // 此, #247
+ 0x6BCF, // 每, #391
+ 0x6BD4, // 比, #257
+ 0x6C11, // 民, #230
+ 0x6C23, // 氣, #200
+ 0x6C34, // 水, #140
+ 0x6C42, // 求, #501
+ 0x6C92, // 沒, #162
+ 0x6CD5, // 法, #89
+ 0x6D3B, // 活, #124
+ 0x6D41, // 流, #315
+ 0x6D77, // 海, #258
+ 0x6D88, // 消, #342
+ 0x6E05, // 清, #329
+ 0x6E2F, // 港, #293
+ 0x6F14, // 演, #491
+ 0x7063, // 灣, #195
+ 0x70BA, // 為, #39
+ 0x7121, // 無, #107
+ 0x7136, // 然, #215
+ 0x7167, // 照, #376
+ 0x71B1, // 熱, #245
+ 0x7247, // 片, #90
+ 0x7248, // 版, #112
+ 0x724C, // 牌, #467
+ 0x7269, // 物, #110
+ 0x7279, // 特, #183
+ 0x738B, // 王, #287
+ 0x73A9, // 玩, #354
+ 0x73FE, // 現, #143
+ 0x7403, // 球, #350
+ 0x7406, // 理, #105
+ 0x751F, // 生, #24
+ 0x7522, // 產, #201
+ 0x7528, // 用, #17
+ 0x7531, // 由, #288
+ 0x7537, // 男, #298
+ 0x754C, // 界, #399
+ 0x7559, // 留, #218
+ 0x756B, // 畫, #412
+ 0x7576, // 當, #185
+ 0x767B, // 登, #138
+ 0x767C, // 發, #28
+ 0x767D, // 白, #377
+ 0x767E, // 百, #393
+ 0x7684, // 的, #1
+ 0x76EE, // 目, #271
+ 0x76F4, // 直, #379
+ 0x76F8, // 相, #98
+ 0x770B, // 看, #52
+ 0x771F, // 真, #180
+ 0x773C, // 眼, #433
+ 0x77E5, // 知, #170
+ 0x78BC, // 碼, #481
+ 0x793A, // 示, #353
+ 0x793E, // 社, #333
+ 0x795E, // 神, #304
+ 0x7968, // 票, #477
+ 0x798F, // 福, #494
+ 0x79C1, // 私, #507
+ 0x79D1, // 科, #280
+ 0x7A0B, // 程, #272
+ 0x7A2E, // 種, #337
+ 0x7A4D, // 積, #385
+ 0x7A7A, // 空, #324
+ 0x7ACB, // 立, #286
+ 0x7AD9, // 站, #117
+ 0x7AE0, // 章, #141
+ 0x7B2C, // 第, #135
+ 0x7B49, // 等, #240
+ 0x7BA1, // 管, #340
+ 0x7BC0, // 節, #431
+ 0x7BC7, // 篇, #479
+ 0x7C21, // 簡, #499
+ 0x7CBE, // 精, #213
+ 0x7CFB, // 系, #212
+ 0x7D04, // 約, #462
+ 0x7D05, // 紅, #452
+ 0x7D1A, // 級, #267
+ 0x7D30, // 細, #486
+ 0x7D44, // 組, #335
+ 0x7D50, // 結, #243
+ 0x7D66, // 給, #355
+ 0x7D71, // 統, #375
+ 0x7D93, // 經, #111
+ 0x7DB2, // 網, #32
+ 0x7DDA, // 線, #151
+ 0x7E23, // 縣, #439
+ 0x7E3D, // 總, #370
+ 0x7F8E, // 美, #41
+ 0x7FA9, // 義, #504
+ 0x8001, // 老, #290
+ 0x8003, // 考, #428
+ 0x8005, // 者, #92
+ 0x800C, // 而, #217
+ 0x805E, // 聞, #181
+ 0x806F, // 聯, #310
+ 0x8072, // 聲, #413
+ 0x80A1, // 股, #390
+ 0x80B2, // 育, #453
+ 0x80FD, // 能, #71
+ 0x8166, // 腦, #408
+ 0x81EA, // 自, #61
+ 0x81F3, // 至, #344
+ 0x8207, // 與, #84
+ 0x8209, // 舉, #463
+ 0x8272, // 色, #192
+ 0x82B1, // 花, #255
+ 0x82F1, // 英, #348
+ 0x83EF, // 華, #196
+ 0x842C, // 萬, #316
+ 0x843D, // 落, #308
+ 0x8457, // 著, #233
+ 0x85A6, // 薦, #401
+ 0x85CF, // 藏, #503
+ 0x85DD, // 藝, #488
+ 0x8655, // 處, #419
+ 0x865F, // 號, #191
+ 0x884C, // 行, #47
+ 0x8853, // 術, #395
+ 0x8868, // 表, #77
+ 0x88AB, // 被, #291
+ 0x88DD, // 裝, #256
+ 0x88E1, // 裡, #369
+ 0x88FD, // 製, #510
+ 0x897F, // 西, #300
+ 0x8981, // 要, #36
+ 0x898B, // 見, #307
+ 0x8996, // 視, #204
+ 0x89BA, // 覺, #450
+ 0x89BD, // 覽, #387
+ 0x89C0, // 觀, #365
+ 0x89E3, // 解, #323
+ 0x8A00, // 言, #169
+ 0x8A02, // 訂, #423
+ 0x8A08, // 計, #225
+ 0x8A0A, // 訊, #156
+ 0x8A0E, // 討, #373
+ 0x8A18, // 記, #222
+ 0x8A2D, // 設, #174
+ 0x8A3B, // 註, #356
+ 0x8A55, // 評, #246
+ 0x8A66, // 試, #448
+ 0x8A71, // 話, #229
+ 0x8A72, // 該, #446
+ 0x8A8D, // 認, #464
+ 0x8A9E, // 語, #371
+ 0x8AAA, // 說, #91
+ 0x8ABF, // 調, #509
+ 0x8ACB, // 請, #119
+ 0x8AD6, // 論, #114
+ 0x8B1D, // 謝, #389
+ 0x8B49, // 證, #429
+ 0x8B58, // 識, #416
+ 0x8B70, // 議, #485
+ 0x8B77, // 護, #475
+ 0x8B80, // 讀, #386
+ 0x8B8A, // 變, #388
+ 0x8B93, // 讓, #336
+ 0x8CA8, // 貨, #313
+ 0x8CB7, // 買, #260
+ 0x8CBB, // 費, #203
+ 0x8CC7, // 資, #62
+ 0x8CE3, // 賣, #294
+ 0x8CEA, // 質, #457
+ 0x8CFC, // 購, #189
+ 0x8D77, // 起, #214
+ 0x8D85, // 超, #296
+ 0x8DDF, // 跟, #489
+ 0x8DEF, // 路, #137
+ 0x8EAB, // 身, #197
+ 0x8ECA, // 車, #76
+ 0x8F09, // 載, #301
+ 0x8F49, // 轉, #282
+ 0x8FD1, // 近, #414
+ 0x9001, // 送, #363
+ 0x9019, // 這, #42
+ 0x901A, // 通, #207
+ 0x901F, // 速, #495
+ 0x9020, // 造, #455
+ 0x9023, // 連, #285
+ 0x9032, // 進, #231
+ 0x904A, // 遊, #132
+ 0x904B, // 運, #219
+ 0x904E, // 過, #101
+ 0x9053, // 道, #146
+ 0x9054, // 達, #417
+ 0x9078, // 選, #182
+ 0x9084, // 還, #154
+ 0x908A, // 邊, #487
+ 0x90A3, // 那, #269
+ 0x90E8, // 部, #78
+ 0x90FD, // 都, #104
+ 0x914D, // 配, #421
+ 0x9152, // 酒, #512
+ 0x91AB, // 醫, #358
+ 0x91CD, // 重, #224
+ 0x91CF, // 量, #319
+ 0x91D1, // 金, #115
+ 0x9304, // 錄, #302
+ 0x9577, // 長, #172
+ 0x9580, // 門, #193
+ 0x958B, // 開, #72
+ 0x9593, // 間, #80
+ 0x95B1, // 閱, #405
+ 0x95DC, // 關, #74
+ 0x963F, // 阿, #460
+ 0x9650, // 限, #265
+ 0x9662, // 院, #474
+ 0x9664, // 除, #478
+ 0x969B, // 際, #459
+ 0x96C6, // 集, #347
+ 0x96E2, // 離, #442
+ 0x96FB, // 電, #33
+ 0x9700, // 需, #445
+ 0x975E, // 非, #451
+ 0x9762, // 面, #129
+ 0x97F3, // 音, #194
+ 0x9801, // 頁, #83
+ 0x982D, // 頭, #238
+ 0x984C, // 題, #122
+ 0x985E, // 類, #163
+ 0x98A8, // 風, #266
+ 0x98DF, // 食, #208
+ 0x9910, // 餐, #469
+ 0x9928, // 館, #424
+ 0x9996, // 首, #166
+ 0x9999, // 香, #263
+ 0x99AC, // 馬, #317
+ 0x9A57, // 驗, #492
+ 0x9AD4, // 體, #100
+ 0x9AD8, // 高, #88
+ 0x9EBC, // 麼, #241
+ 0x9EC3, // 黃, #480
+ 0x9ED1, // 黑, #490
+ 0x9EDE, // 點, #69
+ 0x9F8D, // 龍, #505
+};
+// the percentage of the sample covered by the above characters
+static const float frequent_zhTW_coverage=0.704841200026877;
+
+// The 512 most frequently occuring characters for the ja language in a sample of the Internet.
+// Ordered by codepoint, comment shows character and ranking by frequency
+const uint16_t frequent_ja[] = {
+ 0x3005, // 々, #352
+ 0x3041, // ぁ, #486
+ 0x3042, // あ, #50
+ 0x3044, // い, #2
+ 0x3046, // う, #33
+ 0x3048, // え, #83
+ 0x304A, // お, #37
+ 0x304B, // か, #21
+ 0x304C, // が, #17
+ 0x304D, // き, #51
+ 0x304E, // ぎ, #324
+ 0x304F, // く, #38
+ 0x3050, // ぐ, #334
+ 0x3051, // け, #60
+ 0x3052, // げ, #296
+ 0x3053, // こ, #34
+ 0x3054, // ご, #100
+ 0x3055, // さ, #31
+ 0x3056, // ざ, #378
+ 0x3057, // し, #4
+ 0x3058, // じ, #121
+ 0x3059, // す, #12
+ 0x305A, // ず, #215
+ 0x305B, // せ, #86
+ 0x305D, // そ, #68
+ 0x305F, // た, #11
+ 0x3060, // だ, #42
+ 0x3061, // ち, #67
+ 0x3063, // っ, #23
+ 0x3064, // つ, #73
+ 0x3066, // て, #7
+ 0x3067, // で, #6
+ 0x3068, // と, #14
+ 0x3069, // ど, #75
+ 0x306A, // な, #8
+ 0x306B, // に, #5
+ 0x306D, // ね, #123
+ 0x306E, // の, #1
+ 0x306F, // は, #16
+ 0x3070, // ば, #150
+ 0x3071, // ぱ, #259
+ 0x3072, // ひ, #364
+ 0x3073, // び, #266
+ 0x3075, // ふ, #484
+ 0x3076, // ぶ, #330
+ 0x3078, // へ, #146
+ 0x3079, // べ, #207
+ 0x307B, // ほ, #254
+ 0x307E, // ま, #18
+ 0x307F, // み, #74
+ 0x3080, // む, #285
+ 0x3081, // め, #78
+ 0x3082, // も, #32
+ 0x3083, // ゃ, #111
+ 0x3084, // や, #85
+ 0x3086, // ゆ, #392
+ 0x3087, // ょ, #224
+ 0x3088, // よ, #63
+ 0x3089, // ら, #29
+ 0x308A, // り, #28
+ 0x308B, // る, #9
+ 0x308C, // れ, #35
+ 0x308D, // ろ, #127
+ 0x308F, // わ, #88
+ 0x3092, // を, #19
+ 0x3093, // ん, #22
+ 0x30A1, // ァ, #193
+ 0x30A2, // ア, #27
+ 0x30A3, // ィ, #70
+ 0x30A4, // イ, #15
+ 0x30A6, // ウ, #89
+ 0x30A7, // ェ, #134
+ 0x30A8, // エ, #81
+ 0x30A9, // ォ, #225
+ 0x30AA, // オ, #76
+ 0x30AB, // カ, #52
+ 0x30AC, // ガ, #147
+ 0x30AD, // キ, #66
+ 0x30AE, // ギ, #246
+ 0x30AF, // ク, #25
+ 0x30B0, // グ, #39
+ 0x30B1, // ケ, #137
+ 0x30B2, // ゲ, #200
+ 0x30B3, // コ, #46
+ 0x30B4, // ゴ, #183
+ 0x30B5, // サ, #64
+ 0x30B6, // ザ, #221
+ 0x30B7, // シ, #48
+ 0x30B8, // ジ, #55
+ 0x30B9, // ス, #13
+ 0x30BA, // ズ, #103
+ 0x30BB, // セ, #109
+ 0x30BC, // ゼ, #499
+ 0x30BD, // ソ, #175
+ 0x30BF, // タ, #45
+ 0x30C0, // ダ, #104
+ 0x30C1, // チ, #71
+ 0x30C3, // ッ, #20
+ 0x30C4, // ツ, #119
+ 0x30C6, // テ, #59
+ 0x30C7, // デ, #82
+ 0x30C8, // ト, #10
+ 0x30C9, // ド, #44
+ 0x30CA, // ナ, #102
+ 0x30CB, // ニ, #72
+ 0x30CD, // ネ, #117
+ 0x30CE, // ノ, #192
+ 0x30CF, // ハ, #164
+ 0x30D0, // バ, #62
+ 0x30D1, // パ, #90
+ 0x30D2, // ヒ, #398
+ 0x30D3, // ビ, #77
+ 0x30D4, // ピ, #135
+ 0x30D5, // フ, #47
+ 0x30D6, // ブ, #56
+ 0x30D7, // プ, #43
+ 0x30D8, // ヘ, #268
+ 0x30D9, // ベ, #157
+ 0x30DA, // ペ, #125
+ 0x30DB, // ホ, #155
+ 0x30DC, // ボ, #168
+ 0x30DD, // ポ, #114
+ 0x30DE, // マ, #57
+ 0x30DF, // ミ, #97
+ 0x30E0, // ム, #69
+ 0x30E1, // メ, #53
+ 0x30E2, // モ, #142
+ 0x30E3, // ャ, #93
+ 0x30E4, // ヤ, #258
+ 0x30E5, // ュ, #79
+ 0x30E6, // ユ, #405
+ 0x30E7, // ョ, #98
+ 0x30E9, // ラ, #26
+ 0x30EA, // リ, #30
+ 0x30EB, // ル, #24
+ 0x30EC, // レ, #41
+ 0x30ED, // ロ, #40
+ 0x30EF, // ワ, #144
+ 0x30F3, // ン, #3
+ 0x30F4, // ヴ, #483
+ 0x30FD, // ヽ, #501
+ 0x4E00, // 一, #84
+ 0x4E07, // 万, #337
+ 0x4E09, // 三, #323
+ 0x4E0A, // 上, #133
+ 0x4E0B, // 下, #180
+ 0x4E0D, // 不, #277
+ 0x4E16, // 世, #385
+ 0x4E2D, // 中, #87
+ 0x4E3B, // 主, #432
+ 0x4E88, // 予, #326
+ 0x4E8B, // 事, #95
+ 0x4E8C, // 二, #394
+ 0x4E95, // 井, #468
+ 0x4EA4, // 交, #410
+ 0x4EAC, // 京, #260
+ 0x4EBA, // 人, #61
+ 0x4ECA, // 今, #184
+ 0x4ECB, // 介, #358
+ 0x4ED5, // 仕, #391
+ 0x4ED6, // 他, #256
+ 0x4ED8, // 付, #243
+ 0x4EE3, // 代, #280
+ 0x4EE5, // 以, #216
+ 0x4EF6, // 件, #190
+ 0x4F1A, // 会, #105
+ 0x4F4D, // 位, #177
+ 0x4F4F, // 住, #376
+ 0x4F53, // 体, #223
+ 0x4F55, // 何, #294
+ 0x4F5C, // 作, #154
+ 0x4F7F, // 使, #233
+ 0x4F9B, // 供, #503
+ 0x4FA1, // 価, #217
+ 0x4FBF, // 便, #511
+ 0x4FDD, // 保, #279
+ 0x4FE1, // 信, #271
+ 0x500B, // 個, #415
+ 0x50CF, // 像, #178
+ 0x512A, // 優, #403
+ 0x5143, // 元, #384
+ 0x5148, // 先, #311
+ 0x5149, // 光, #488
+ 0x5165, // 入, #115
+ 0x5168, // 全, #173
+ 0x516C, // 公, #287
+ 0x5177, // 具, #447
+ 0x5185, // 内, #169
+ 0x5186, // 円, #131
+ 0x5199, // 写, #275
+ 0x51FA, // 出, #110
+ 0x5206, // 分, #130
+ 0x5207, // 切, #401
+ 0x521D, // 初, #319
+ 0x5225, // 別, #290
+ 0x5229, // 利, #226
+ 0x5236, // 制, #507
+ 0x524D, // 前, #124
+ 0x529B, // 力, #272
+ 0x52A0, // 加, #249
+ 0x52D5, // 動, #120
+ 0x52D9, // 務, #421
+ 0x52DF, // 募, #476
+ 0x5316, // 化, #308
+ 0x5317, // 北, #341
+ 0x533A, // 区, #348
+ 0x539F, // 原, #321
+ 0x53C2, // 参, #452
+ 0x53CB, // 友, #451
+ 0x53D6, // 取, #237
+ 0x53D7, // 受, #354
+ 0x53E3, // 口, #289
+ 0x53E4, // 古, #339
+ 0x53EF, // 可, #298
+ 0x53F0, // 台, #439
+ 0x53F7, // 号, #361
+ 0x5408, // 合, #118
+ 0x540C, // 同, #263
+ 0x540D, // 名, #65
+ 0x5411, // 向, #434
+ 0x544A, // 告, #386
+ 0x5468, // 周, #393
+ 0x5473, // 味, #299
+ 0x548C, // 和, #350
+ 0x54C1, // 品, #96
+ 0x54E1, // 員, #293
+ 0x5546, // 商, #198
+ 0x554F, // 問, #158
+ 0x55B6, // 営, #438
+ 0x5668, // 器, #366
+ 0x56DE, // 回, #143
+ 0x56F3, // 図, #444
+ 0x56FD, // 国, #153
+ 0x5712, // 園, #435
+ 0x571F, // 土, #239
+ 0x5728, // 在, #351
+ 0x5730, // 地, #163
+ 0x578B, // 型, #430
+ 0x5831, // 報, #112
+ 0x5834, // 場, #139
+ 0x58F2, // 売, #232
+ 0x5909, // 変, #306
+ 0x5916, // 外, #222
+ 0x591A, // 多, #336
+ 0x5927, // 大, #80
+ 0x5929, // 天, #278
+ 0x5973, // 女, #161
+ 0x597D, // 好, #349
+ 0x5A5A, // 婚, #479
+ 0x5B50, // 子, #113
+ 0x5B57, // 字, #492
+ 0x5B66, // 学, #132
+ 0x5B89, // 安, #295
+ 0x5B9A, // 定, #145
+ 0x5B9F, // 実, #220
+ 0x5BA4, // 室, #482
+ 0x5BAE, // 宮, #487
+ 0x5BB6, // 家, #211
+ 0x5BB9, // 容, #333
+ 0x5BFE, // 対, #252
+ 0x5C02, // 専, #474
+ 0x5C0F, // 小, #212
+ 0x5C11, // 少, #377
+ 0x5C4B, // 屋, #284
+ 0x5C71, // 山, #206
+ 0x5CA1, // 岡, #429
+ 0x5CF6, // 島, #297
+ 0x5DDD, // 川, #253
+ 0x5DE5, // 工, #374
+ 0x5E02, // 市, #159
+ 0x5E2F, // 帯, #416
+ 0x5E38, // 常, #437
+ 0x5E73, // 平, #390
+ 0x5E74, // 年, #54
+ 0x5E83, // 広, #367
+ 0x5E97, // 店, #149
+ 0x5EA6, // 度, #269
+ 0x5EAB, // 庫, #380
+ 0x5F0F, // 式, #265
+ 0x5F15, // 引, #345
+ 0x5F37, // 強, #446
+ 0x5F53, // 当, #240
+ 0x5F62, // 形, #502
+ 0x5F8C, // 後, #230
+ 0x5F97, // 得, #490
+ 0x5FC3, // 心, #307
+ 0x5FC5, // 必, #422
+ 0x5FDC, // 応, #356
+ 0x601D, // 思, #189
+ 0x6027, // 性, #201
+ 0x6075, // 恵, #400
+ 0x60C5, // 情, #140
+ 0x60F3, // 想, #477
+ 0x610F, // 意, #305
+ 0x611B, // 愛, #273
+ 0x611F, // 感, #257
+ 0x6210, // 成, #262
+ 0x6226, // 戦, #365
+ 0x6240, // 所, #236
+ 0x624B, // 手, #160
+ 0x6295, // 投, #129
+ 0x6301, // 持, #355
+ 0x6307, // 指, #425
+ 0x63A2, // 探, #369
+ 0x63B2, // 掲, #399
+ 0x643A, // 携, #459
+ 0x652F, // 支, #512
+ 0x653E, // 放, #469
+ 0x6559, // 教, #270
+ 0x6570, // 数, #181
+ 0x6587, // 文, #202
+ 0x6599, // 料, #106
+ 0x65B0, // 新, #99
+ 0x65B9, // 方, #126
+ 0x65C5, // 旅, #445
+ 0x65E5, // 日, #36
+ 0x660E, // 明, #300
+ 0x6620, // 映, #418
+ 0x6642, // 時, #107
+ 0x66F4, // 更, #359
+ 0x66F8, // 書, #174
+ 0x6700, // 最, #152
+ 0x6708, // 月, #49
+ 0x6709, // 有, #302
+ 0x671F, // 期, #332
+ 0x6728, // 木, #203
+ 0x672C, // 本, #92
+ 0x6750, // 材, #489
+ 0x6751, // 村, #466
+ 0x6765, // 来, #267
+ 0x6771, // 東, #191
+ 0x677F, // 板, #411
+ 0x679C, // 果, #441
+ 0x6821, // 校, #327
+ 0x682A, // 株, #412
+ 0x683C, // 格, #228
+ 0x691C, // 検, #179
+ 0x696D, // 業, #166
+ 0x697D, // 楽, #172
+ 0x69D8, // 様, #255
+ 0x6A5F, // 機, #235
+ 0x6B21, // 次, #318
+ 0x6B62, // 止, #475
+ 0x6B63, // 正, #312
+ 0x6C17, // 気, #116
+ 0x6C34, // 水, #165
+ 0x6C42, // 求, #465
+ 0x6C7A, // 決, #370
+ 0x6CBB, // 治, #505
+ 0x6CC1, // 況, #462
+ 0x6CD5, // 法, #227
+ 0x6CE8, // 注, #372
+ 0x6D3B, // 活, #303
+ 0x6D41, // 流, #480
+ 0x6D77, // 海, #274
+ 0x6E08, // 済, #417
+ 0x6F14, // 演, #504
+ 0x706B, // 火, #264
+ 0x70B9, // 点, #331
+ 0x7121, // 無, #58
+ 0x7248, // 版, #409
+ 0x7269, // 物, #170
+ 0x7279, // 特, #242
+ 0x72B6, // 状, #458
+ 0x73FE, // 現, #322
+ 0x7406, // 理, #162
+ 0x751F, // 生, #122
+ 0x7523, // 産, #320
+ 0x7528, // 用, #94
+ 0x7530, // 田, #195
+ 0x7537, // 男, #373
+ 0x753A, // 町, #314
+ 0x753B, // 画, #91
+ 0x754C, // 界, #436
+ 0x756A, // 番, #261
+ 0x75C5, // 病, #428
+ 0x767A, // 発, #194
+ 0x767B, // 登, #231
+ 0x767D, // 白, #419
+ 0x7684, // 的, #251
+ 0x76EE, // 目, #197
+ 0x76F4, // 直, #497
+ 0x76F8, // 相, #286
+ 0x770C, // 県, #199
+ 0x771F, // 真, #219
+ 0x7740, // 着, #283
+ 0x77E5, // 知, #185
+ 0x77F3, // 石, #500
+ 0x78BA, // 確, #383
+ 0x793A, // 示, #241
+ 0x793E, // 社, #167
+ 0x795E, // 神, #315
+ 0x798F, // 福, #423
+ 0x79C1, // 私, #347
+ 0x79D1, // 科, #420
+ 0x7A0E, // 税, #368
+ 0x7A2E, // 種, #455
+ 0x7A3F, // 稿, #148
+ 0x7A7A, // 空, #427
+ 0x7ACB, // 立, #309
+ 0x7B11, // 笑, #454
+ 0x7B2C, // 第, #317
+ 0x7B49, // 等, #457
+ 0x7B54, // 答, #426
+ 0x7BA1, // 管, #481
+ 0x7CFB, // 系, #408
+ 0x7D04, // 約, #276
+ 0x7D20, // 素, #407
+ 0x7D22, // 索, #214
+ 0x7D30, // 細, #381
+ 0x7D39, // 紹, #471
+ 0x7D42, // 終, #456
+ 0x7D44, // 組, #424
+ 0x7D4C, // 経, #360
+ 0x7D50, // 結, #291
+ 0x7D9A, // 続, #357
+ 0x7DCF, // 総, #467
+ 0x7DDA, // 線, #338
+ 0x7DE8, // 編, #453
+ 0x7F8E, // 美, #204
+ 0x8003, // 考, #387
+ 0x8005, // 者, #151
+ 0x805E, // 聞, #463
+ 0x8077, // 職, #363
+ 0x80B2, // 育, #433
+ 0x80FD, // 能, #250
+ 0x8179, // 腹, #396
+ 0x81EA, // 自, #156
+ 0x826F, // 良, #329
+ 0x8272, // 色, #402
+ 0x82B1, // 花, #440
+ 0x82B8, // 芸, #413
+ 0x82F1, // 英, #485
+ 0x8449, // 葉, #472
+ 0x884C, // 行, #128
+ 0x8853, // 術, #460
+ 0x8868, // 表, #209
+ 0x88FD, // 製, #431
+ 0x897F, // 西, #406
+ 0x8981, // 要, #313
+ 0x898B, // 見, #101
+ 0x898F, // 規, #375
+ 0x89A7, // 覧, #171
+ 0x89E3, // 解, #388
+ 0x8A00, // 言, #210
+ 0x8A08, // 計, #343
+ 0x8A18, // 記, #136
+ 0x8A2D, // 設, #292
+ 0x8A71, // 話, #213
+ 0x8A73, // 詳, #371
+ 0x8A8D, // 認, #404
+ 0x8A9E, // 語, #234
+ 0x8AAC, // 説, #494
+ 0x8AAD, // 読, #301
+ 0x8ABF, // 調, #443
+ 0x8AC7, // 談, #448
+ 0x8B77, // 護, #509
+ 0x8C37, // 谷, #506
+ 0x8CA9, // 販, #362
+ 0x8CB7, // 買, #346
+ 0x8CC7, // 資, #473
+ 0x8CEA, // 質, #281
+ 0x8CFC, // 購, #495
+ 0x8EAB, // 身, #470
+ 0x8ECA, // 車, #205
+ 0x8EE2, // 転, #335
+ 0x8F09, // 載, #342
+ 0x8FBC, // 込, #229
+ 0x8FD1, // 近, #304
+ 0x8FD4, // 返, #461
+ 0x8FFD, // 追, #379
+ 0x9001, // 送, #186
+ 0x901A, // 通, #182
+ 0x901F, // 速, #340
+ 0x9023, // 連, #244
+ 0x904B, // 運, #382
+ 0x904E, // 過, #498
+ 0x9053, // 道, #282
+ 0x9054, // 達, #450
+ 0x9055, // 違, #414
+ 0x9078, // 選, #288
+ 0x90E8, // 部, #208
+ 0x90FD, // 都, #344
+ 0x914D, // 配, #389
+ 0x91CD, // 重, #478
+ 0x91CE, // 野, #245
+ 0x91D1, // 金, #138
+ 0x9332, // 録, #238
+ 0x9577, // 長, #247
+ 0x9580, // 門, #508
+ 0x958B, // 開, #248
+ 0x9593, // 間, #141
+ 0x95A2, // 関, #188
+ 0x962A, // 阪, #496
+ 0x9650, // 限, #395
+ 0x9662, // 院, #449
+ 0x9664, // 除, #510
+ 0x969B, // 際, #493
+ 0x96C6, // 集, #196
+ 0x96D1, // 雑, #442
+ 0x96FB, // 電, #187
+ 0x9762, // 面, #328
+ 0x97F3, // 音, #325
+ 0x984C, // 題, #310
+ 0x985E, // 類, #491
+ 0x98A8, // 風, #353
+ 0x98DF, // 食, #218
+ 0x9928, // 館, #464
+ 0x99C5, // 駅, #316
+ 0x9A13, // 験, #397
+ 0x9AD8, // 高, #176
+ 0xFF57, // w, #108
+};
+// the percentage of the sample covered by the above characters
+static const float frequent_ja_coverage=0.880569589120162;
+
+// The 512 most frequently occuring characters for the ko language in a sample of the Internet.
+// Ordered by codepoint, comment shows character and ranking by frequency
+const uint16_t frequent_ko[] = {
+ 0x314B, // ㅋ, #148
+ 0x314E, // ㅎ, #390
+ 0x3160, // ㅠ, #354
+ 0x318D, // ㆍ, #439
+ 0xAC00, // 가, #6
+ 0xAC01, // 각, #231
+ 0xAC04, // 간, #106
+ 0xAC08, // 갈, #362
+ 0xAC10, // 감, #122
+ 0xAC11, // 갑, #493
+ 0xAC15, // 강, #155
+ 0xAC19, // 같, #264
+ 0xAC1C, // 개, #87
+ 0xAC1D, // 객, #198
+ 0xAC24, // 갤, #457
+ 0xAC70, // 거, #91
+ 0xAC74, // 건, #161
+ 0xAC78, // 걸, #338
+ 0xAC80, // 검, #184
+ 0xAC83, // 것, #116
+ 0xAC8C, // 게, #36
+ 0xACA0, // 겠, #233
+ 0xACA8, // 겨, #341
+ 0xACA9, // 격, #245
+ 0xACAC, // 견, #413
+ 0xACB0, // 결, #202
+ 0xACBD, // 경, #62
+ 0xACC4, // 계, #142
+ 0xACE0, // 고, #12
+ 0xACE1, // 곡, #444
+ 0xACE8, // 골, #379
+ 0xACF3, // 곳, #388
+ 0xACF5, // 공, #59
+ 0xACFC, // 과, #69
+ 0xAD00, // 관, #95
+ 0xAD11, // 광, #235
+ 0xAD50, // 교, #128
+ 0xAD6C, // 구, #52
+ 0xAD6D, // 국, #85
+ 0xAD70, // 군, #293
+ 0xAD74, // 굴, #487
+ 0xAD81, // 궁, #441
+ 0xAD8C, // 권, #192
+ 0xADC0, // 귀, #386
+ 0xADDC, // 규, #367
+ 0xADF8, // 그, #30
+ 0xADF9, // 극, #424
+ 0xADFC, // 근, #241
+ 0xAE00, // 글, #61
+ 0xAE08, // 금, #138
+ 0xAE09, // 급, #269
+ 0xAE30, // 기, #3
+ 0xAE34, // 긴, #465
+ 0xAE38, // 길, #297
+ 0xAE40, // 김, #205
+ 0xAE4C, // 까, #171
+ 0xAED8, // 께, #273
+ 0xAF43, // 꽃, #475
+ 0xB05D, // 끝, #505
+ 0xB07C, // 끼, #490
+ 0xB098, // 나, #39
+ 0xB09C, // 난, #274
+ 0xB0A0, // 날, #292
+ 0xB0A8, // 남, #139
+ 0xB0B4, // 내, #56
+ 0xB108, // 너, #272
+ 0xB110, // 널, #476
+ 0xB118, // 넘, #492
+ 0xB124, // 네, #100
+ 0xB137, // 넷, #329
+ 0xB140, // 녀, #288
+ 0xB144, // 년, #151
+ 0xB178, // 노, #149
+ 0xB17C, // 논, #491
+ 0xB180, // 놀, #464
+ 0xB18D, // 농, #442
+ 0xB204, // 누, #319
+ 0xB208, // 눈, #383
+ 0xB274, // 뉴, #173
+ 0xB290, // 느, #368
+ 0xB294, // 는, #5
+ 0xB298, // 늘, #322
+ 0xB2A5, // 능, #190
+ 0xB2C8, // 니, #16
+ 0xB2D8, // 님, #153
+ 0xB2E4, // 다, #2
+ 0xB2E8, // 단, #134
+ 0xB2EB, // 닫, #195
+ 0xB2EC, // 달, #243
+ 0xB2F4, // 담, #254
+ 0xB2F5, // 답, #287
+ 0xB2F9, // 당, #159
+ 0xB300, // 대, #33
+ 0xB313, // 댓, #303
+ 0xB354, // 더, #140
+ 0xB358, // 던, #252
+ 0xB367, // 덧, #463
+ 0xB370, // 데, #104
+ 0xB378, // 델, #429
+ 0xB3C4, // 도, #25
+ 0xB3C5, // 독, #301
+ 0xB3CC, // 돌, #309
+ 0xB3D9, // 동, #58
+ 0xB418, // 되, #82
+ 0xB41C, // 된, #189
+ 0xB420, // 될, #408
+ 0xB429, // 됩, #332
+ 0xB450, // 두, #199
+ 0xB4A4, // 뒤, #496
+ 0xB4DC, // 드, #40
+ 0xB4E0, // 든, #283
+ 0xB4E4, // 들, #54
+ 0xB4EF, // 듯, #478
+ 0xB4F1, // 등, #90
+ 0xB514, // 디, #133
+ 0xB529, // 딩, #462
+ 0xB530, // 따, #333
+ 0xB54C, // 때, #240
+ 0xB610, // 또, #313
+ 0xB77C, // 라, #42
+ 0xB77D, // 락, #355
+ 0xB780, // 란, #290
+ 0xB78C, // 람, #246
+ 0xB78D, // 랍, #420
+ 0xB791, // 랑, #270
+ 0xB798, // 래, #174
+ 0xB799, // 랙, #381
+ 0xB79C, // 랜, #357
+ 0xB7A8, // 램, #359
+ 0xB7A9, // 랩, #402
+ 0xB7C9, // 량, #346
+ 0xB7EC, // 러, #130
+ 0xB7F0, // 런, #312
+ 0xB7FC, // 럼, #327
+ 0xB7FD, // 럽, #447
+ 0xB807, // 렇, #412
+ 0xB808, // 레, #114
+ 0xB80C, // 렌, #395
+ 0xB824, // 려, #158
+ 0xB825, // 력, #194
+ 0xB828, // 련, #326
+ 0xB839, // 령, #389
+ 0xB85C, // 로, #4
+ 0xB85D, // 록, #84
+ 0xB860, // 론, #366
+ 0xB8CC, // 료, #154
+ 0xB8E8, // 루, #236
+ 0xB958, // 류, #265
+ 0xB974, // 르, #212
+ 0xB978, // 른, #250
+ 0xB97C, // 를, #35
+ 0xB984, // 름, #276
+ 0xB9AC, // 리, #19
+ 0xB9AD, // 릭, #394
+ 0xB9B0, // 린, #259
+ 0xB9B4, // 릴, #485
+ 0xB9BC, // 림, #305
+ 0xB9BD, // 립, #217
+ 0xB9C1, // 링, #351
+ 0xB9C8, // 마, #67
+ 0xB9C9, // 막, #310
+ 0xB9CC, // 만, #65
+ 0xB9CE, // 많, #257
+ 0xB9D0, // 말, #188
+ 0xB9DB, // 맛, #397
+ 0xB9DD, // 망, #370
+ 0xB9DE, // 맞, #399
+ 0xB9E4, // 매, #125
+ 0xB9E8, // 맨, #422
+ 0xBA38, // 머, #311
+ 0xBA39, // 먹, #377
+ 0xBA3C, // 먼, #469
+ 0xBA54, // 메, #147
+ 0xBA70, // 며, #191
+ 0xBA74, // 면, #72
+ 0xBA85, // 명, #131
+ 0xBAA8, // 모, #73
+ 0xBAA9, // 목, #157
+ 0xBAB0, // 몰, #401
+ 0xBAB8, // 몸, #437
+ 0xBABB, // 못, #336
+ 0xBB34, // 무, #80
+ 0xBB38, // 문, #57
+ 0xBB3C, // 물, #94
+ 0xBBA4, // 뮤, #431
+ 0xBBF8, // 미, #76
+ 0xBBFC, // 민, #200
+ 0xBC00, // 밀, #308
+ 0xBC0F, // 및, #249
+ 0xBC14, // 바, #89
+ 0xBC15, // 박, #226
+ 0xBC18, // 반, #175
+ 0xBC1B, // 받, #248
+ 0xBC1C, // 발, #164
+ 0xBC29, // 방, #92
+ 0xBC30, // 배, #162
+ 0xBC31, // 백, #256
+ 0xBC84, // 버, #111
+ 0xBC88, // 번, #167
+ 0xBC8C, // 벌, #423
+ 0xBC94, // 범, #427
+ 0xBC95, // 법, #207
+ 0xBCA0, // 베, #281
+ 0xBCA4, // 벤, #378
+ 0xBCA8, // 벨, #387
+ 0xBCC0, // 변, #253
+ 0xBCC4, // 별, #262
+ 0xBCD1, // 병, #340
+ 0xBCF4, // 보, #20
+ 0xBCF5, // 복, #204
+ 0xBCF8, // 본, #182
+ 0xBCFC, // 볼, #385
+ 0xBD09, // 봉, #405
+ 0xBD80, // 부, #46
+ 0xBD81, // 북, #261
+ 0xBD84, // 분, #105
+ 0xBD88, // 불, #225
+ 0xBDF0, // 뷰, #350
+ 0xBE0C, // 브, #214
+ 0xBE14, // 블, #99
+ 0xBE44, // 비, #55
+ 0xBE4C, // 빌, #510
+ 0xBE60, // 빠, #398
+ 0xC0AC, // 사, #14
+ 0xC0AD, // 삭, #342
+ 0xC0B0, // 산, #121
+ 0xC0B4, // 살, #279
+ 0xC0BC, // 삼, #348
+ 0xC0C1, // 상, #41
+ 0xC0C8, // 새, #282
+ 0xC0C9, // 색, #181
+ 0xC0DD, // 생, #109
+ 0xC11C, // 서, #21
+ 0xC11D, // 석, #234
+ 0xC120, // 선, #107
+ 0xC124, // 설, #170
+ 0xC131, // 성, #50
+ 0xC138, // 세, #60
+ 0xC139, // 섹, #456
+ 0xC13C, // 센, #267
+ 0xC154, // 셔, #455
+ 0xC158, // 션, #237
+ 0xC15C, // 셜, #448
+ 0xC168, // 셨, #421
+ 0xC18C, // 소, #51
+ 0xC18D, // 속, #219
+ 0xC190, // 손, #323
+ 0xC1A1, // 송, #203
+ 0xC1C4, // 쇄, #501
+ 0xC1FC, // 쇼, #364
+ 0xC218, // 수, #27
+ 0xC219, // 숙, #467
+ 0xC21C, // 순, #258
+ 0xC220, // 술, #302
+ 0xC26C, // 쉬, #511
+ 0xC288, // 슈, #384
+ 0xC2A4, // 스, #11
+ 0xC2AC, // 슬, #438
+ 0xC2B4, // 슴, #504
+ 0xC2B5, // 습, #77
+ 0xC2B9, // 승, #299
+ 0xC2DC, // 시, #13
+ 0xC2DD, // 식, #137
+ 0xC2E0, // 신, #47
+ 0xC2E4, // 실, #132
+ 0xC2EC, // 심, #196
+ 0xC2ED, // 십, #482
+ 0xC2F6, // 싶, #352
+ 0xC2F8, // 싸, #419
+ 0xC4F0, // 쓰, #278
+ 0xC528, // 씨, #360
+ 0xC544, // 아, #23
+ 0xC545, // 악, #296
+ 0xC548, // 안, #71
+ 0xC54A, // 않, #209
+ 0xC54C, // 알, #222
+ 0xC554, // 암, #460
+ 0xC558, // 았, #349
+ 0xC559, // 앙, #473
+ 0xC55E, // 앞, #434
+ 0xC560, // 애, #271
+ 0xC561, // 액, #415
+ 0xC571, // 앱, #477
+ 0xC57C, // 야, #124
+ 0xC57D, // 약, #229
+ 0xC591, // 양, #177
+ 0xC5B4, // 어, #24
+ 0xC5B5, // 억, #407
+ 0xC5B8, // 언, #294
+ 0xC5BC, // 얼, #356
+ 0xC5C4, // 엄, #426
+ 0xC5C5, // 업, #118
+ 0xC5C6, // 없, #178
+ 0xC5C8, // 었, #165
+ 0xC5D0, // 에, #9
+ 0xC5D4, // 엔, #375
+ 0xC5D8, // 엘, #506
+ 0xC5EC, // 여, #66
+ 0xC5ED, // 역, #186
+ 0xC5EE, // 엮, #488
+ 0xC5F0, // 연, #96
+ 0xC5F4, // 열, #266
+ 0xC5FC, // 염, #449
+ 0xC600, // 였, #374
+ 0xC601, // 영, #83
+ 0xC608, // 예, #168
+ 0xC624, // 오, #75
+ 0xC628, // 온, #300
+ 0xC62C, // 올, #306
+ 0xC640, // 와, #119
+ 0xC644, // 완, #361
+ 0xC654, // 왔, #489
+ 0xC655, // 왕, #418
+ 0xC678, // 외, #218
+ 0xC694, // 요, #43
+ 0xC695, // 욕, #479
+ 0xC6A9, // 용, #48
+ 0xC6B0, // 우, #64
+ 0xC6B1, // 욱, #503
+ 0xC6B4, // 운, #108
+ 0xC6B8, // 울, #223
+ 0xC6C0, // 움, #317
+ 0xC6C3, // 웃, #404
+ 0xC6CC, // 워, #280
+ 0xC6D0, // 원, #45
+ 0xC6D4, // 월, #150
+ 0xC6E8, // 웨, #446
+ 0xC6F9, // 웹, #500
+ 0xC704, // 위, #78
+ 0xC720, // 유, #81
+ 0xC721, // 육, #321
+ 0xC724, // 윤, #416
+ 0xC73C, // 으, #49
+ 0xC740, // 은, #31
+ 0xC744, // 을, #17
+ 0xC74C, // 음, #112
+ 0xC751, // 응, #461
+ 0xC758, // 의, #8
+ 0xC774, // 이, #1
+ 0xC775, // 익, #403
+ 0xC778, // 인, #18
+ 0xC77C, // 일, #28
+ 0xC784, // 임, #160
+ 0xC785, // 입, #93
+ 0xC788, // 있, #44
+ 0xC790, // 자, #22
+ 0xC791, // 작, #88
+ 0xC798, // 잘, #347
+ 0xC7A1, // 잡, #372
+ 0xC7A5, // 장, #53
+ 0xC7AC, // 재, #120
+ 0xC7C1, // 쟁, #483
+ 0xC800, // 저, #98
+ 0xC801, // 적, #97
+ 0xC804, // 전, #34
+ 0xC808, // 절, #320
+ 0xC810, // 점, #201
+ 0xC811, // 접, #331
+ 0xC815, // 정, #26
+ 0xC81C, // 제, #29
+ 0xC838, // 져, #414
+ 0xC870, // 조, #86
+ 0xC871, // 족, #373
+ 0xC874, // 존, #432
+ 0xC880, // 좀, #470
+ 0xC885, // 종, #208
+ 0xC88B, // 좋, #239
+ 0xC8E0, // 죠, #451
+ 0xC8FC, // 주, #38
+ 0xC8FD, // 죽, #471
+ 0xC900, // 준, #286
+ 0xC904, // 줄, #392
+ 0xC911, // 중, #103
+ 0xC988, // 즈, #255
+ 0xC98C, // 즌, #507
+ 0xC990, // 즐, #371
+ 0xC99D, // 증, #260
+ 0xC9C0, // 지, #10
+ 0xC9C1, // 직, #216
+ 0xC9C4, // 진, #79
+ 0xC9C8, // 질, #238
+ 0xC9D1, // 집, #206
+ 0xC9DC, // 짜, #411
+ 0xC9F8, // 째, #494
+ 0xCABD, // 쪽, #435
+ 0xCC28, // 차, #146
+ 0xCC29, // 착, #443
+ 0xCC2C, // 찬, #481
+ 0xCC30, // 찰, #440
+ 0xCC38, // 참, #343
+ 0xCC3D, // 창, #304
+ 0xCC3E, // 찾, #335
+ 0xCC44, // 채, #284
+ 0xCC45, // 책, #298
+ 0xCC98, // 처, #242
+ 0xCC9C, // 천, #143
+ 0xCCA0, // 철, #380
+ 0xCCA8, // 첨, #452
+ 0xCCAB, // 첫, #484
+ 0xCCAD, // 청, #197
+ 0xCCB4, // 체, #126
+ 0xCCD0, // 쳐, #472
+ 0xCD08, // 초, #220
+ 0xCD1D, // 총, #406
+ 0xCD5C, // 최, #179
+ 0xCD94, // 추, #136
+ 0xCD95, // 축, #337
+ 0xCD9C, // 출, #166
+ 0xCDA9, // 충, #369
+ 0xCDE8, // 취, #210
+ 0xCE20, // 츠, #215
+ 0xCE21, // 측, #468
+ 0xCE35, // 층, #512
+ 0xCE58, // 치, #102
+ 0xCE5C, // 친, #325
+ 0xCE68, // 침, #263
+ 0xCE74, // 카, #115
+ 0xCE7C, // 칼, #466
+ 0xCE90, // 캐, #454
+ 0xCEE4, // 커, #285
+ 0xCEE8, // 컨, #328
+ 0xCEF4, // 컴, #417
+ 0xCF00, // 케, #339
+ 0xCF13, // 켓, #509
+ 0xCF1C, // 켜, #508
+ 0xCF54, // 코, #193
+ 0xCF58, // 콘, #391
+ 0xCFE0, // 쿠, #393
+ 0xD035, // 퀵, #453
+ 0xD06C, // 크, #101
+ 0xD070, // 큰, #495
+ 0xD074, // 클, #289
+ 0xD0A4, // 키, #230
+ 0xD0C0, // 타, #127
+ 0xD0C1, // 탁, #314
+ 0xD0C4, // 탄, #450
+ 0xD0C8, // 탈, #436
+ 0xD0DC, // 태, #221
+ 0xD0DD, // 택, #275
+ 0xD130, // 터, #70
+ 0xD14C, // 테, #213
+ 0xD150, // 텐, #324
+ 0xD154, // 텔, #430
+ 0xD15C, // 템, #382
+ 0xD1A0, // 토, #145
+ 0xD1B5, // 통, #156
+ 0xD22C, // 투, #227
+ 0xD2B8, // 트, #37
+ 0xD2B9, // 특, #247
+ 0xD2F0, // 티, #187
+ 0xD305, // 팅, #410
+ 0xD30C, // 파, #141
+ 0xD310, // 판, #163
+ 0xD314, // 팔, #499
+ 0xD328, // 패, #307
+ 0xD32C, // 팬, #459
+ 0xD338, // 팸, #433
+ 0xD37C, // 퍼, #344
+ 0xD398, // 페, #172
+ 0xD3B8, // 편, #251
+ 0xD3C9, // 평, #291
+ 0xD3EC, // 포, #68
+ 0xD3ED, // 폭, #445
+ 0xD3F0, // 폰, #318
+ 0xD45C, // 표, #232
+ 0xD480, // 풀, #497
+ 0xD488, // 품, #113
+ 0xD48D, // 풍, #425
+ 0xD504, // 프, #110
+ 0xD508, // 픈, #498
+ 0xD50C, // 플, #211
+ 0xD53C, // 피, #169
+ 0xD544, // 필, #295
+ 0xD551, // 핑, #376
+ 0xD558, // 하, #7
+ 0xD559, // 학, #129
+ 0xD55C, // 한, #15
+ 0xD560, // 할, #144
+ 0xD568, // 함, #152
+ 0xD569, // 합, #123
+ 0xD56D, // 항, #268
+ 0xD574, // 해, #32
+ 0xD588, // 했, #180
+ 0xD589, // 행, #135
+ 0xD5A5, // 향, #345
+ 0xD5C8, // 허, #396
+ 0xD5D8, // 험, #316
+ 0xD5E4, // 헤, #474
+ 0xD604, // 현, #185
+ 0xD611, // 협, #315
+ 0xD615, // 형, #244
+ 0xD61C, // 혜, #428
+ 0xD638, // 호, #117
+ 0xD63C, // 혼, #358
+ 0xD648, // 홈, #330
+ 0xD64D, // 홍, #363
+ 0xD654, // 화, #63
+ 0xD655, // 확, #183
+ 0xD658, // 환, #224
+ 0xD65C, // 활, #277
+ 0xD669, // 황, #353
+ 0xD68C, // 회, #74
+ 0xD68D, // 획, #458
+ 0xD69F, // 횟, #409
+ 0xD6A8, // 효, #400
+ 0xD6C4, // 후, #176
+ 0xD6C8, // 훈, #486
+ 0xD734, // 휴, #365
+ 0xD754, // 흔, #480
+ 0xD76C, // 희, #334
+ 0xD788, // 히, #228
+ 0xD798, // 힘, #502
+};
+// the percentage of the sample covered by the above characters
+static const float frequent_ko_coverage=0.948157021464184;
+
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 5d81e5d..a9a9f1a 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -58,7 +58,7 @@
RESTORE_OUTPUT,
OPEN_INPUT,
CLOSE_INPUT,
- SET_STREAM_OUTPUT,
+ INVALIDATE_STREAM,
SET_VOICE_VOLUME,
GET_RENDER_POSITION,
GET_INPUT_FRAMES_LOST,
@@ -89,13 +89,14 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
track_flags_t *flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
pid_t tid,
int *sessionId,
String8& name,
+ int clientUid,
status_t *status)
{
Parcel data, reply;
@@ -105,6 +106,7 @@
data.writeInt32(sampleRate);
data.writeInt32(format);
data.writeInt32(channelMask);
+ size_t frameCount = pFrameCount != NULL ? *pFrameCount : 0;
data.writeInt32(frameCount);
track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT;
data.writeInt32(lFlags);
@@ -116,15 +118,20 @@
}
data.writeInt32((int32_t) output);
data.writeInt32((int32_t) tid);
- int lSessionId = 0;
+ int lSessionId = AUDIO_SESSION_ALLOCATE;
if (sessionId != NULL) {
lSessionId = *sessionId;
}
data.writeInt32(lSessionId);
+ data.writeInt32(clientUid);
status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply);
if (lStatus != NO_ERROR) {
ALOGE("createTrack error: %s", strerror(-lStatus));
} else {
+ frameCount = reply.readInt32();
+ if (pFrameCount != NULL) {
+ *pFrameCount = frameCount;
+ }
lFlags = reply.readInt32();
if (flags != NULL) {
*flags = lFlags;
@@ -136,6 +143,17 @@
name = reply.readString8();
lStatus = reply.readInt32();
track = interface_cast<IAudioTrack>(reply.readStrongBinder());
+ if (lStatus == NO_ERROR) {
+ if (track == 0) {
+ ALOGE("createTrack should have returned an IAudioTrack");
+ lStatus = UNKNOWN_ERROR;
+ }
+ } else {
+ if (track != 0) {
+ ALOGE("createTrack returned an IAudioTrack but with status %d", lStatus);
+ track.clear();
+ }
+ }
}
if (status != NULL) {
*status = lStatus;
@@ -148,7 +166,7 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
track_flags_t *flags,
pid_t tid,
int *sessionId,
@@ -161,11 +179,12 @@
data.writeInt32(sampleRate);
data.writeInt32(format);
data.writeInt32(channelMask);
+ size_t frameCount = pFrameCount != NULL ? *pFrameCount : 0;
data.writeInt32(frameCount);
track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT;
data.writeInt32(lFlags);
data.writeInt32((int32_t) tid);
- int lSessionId = 0;
+ int lSessionId = AUDIO_SESSION_ALLOCATE;
if (sessionId != NULL) {
lSessionId = *sessionId;
}
@@ -174,6 +193,10 @@
if (lStatus != NO_ERROR) {
ALOGE("openRecord error: %s", strerror(-lStatus));
} else {
+ frameCount = reply.readInt32();
+ if (pFrameCount != NULL) {
+ *pFrameCount = frameCount;
+ }
lFlags = reply.readInt32();
if (flags != NULL) {
*flags = lFlags;
@@ -184,6 +207,17 @@
}
lStatus = reply.readInt32();
record = interface_cast<IAudioRecord>(reply.readStrongBinder());
+ if (lStatus == NO_ERROR) {
+ if (record == 0) {
+ ALOGE("openRecord should have returned an IAudioRecord");
+ lStatus = UNKNOWN_ERROR;
+ }
+ } else {
+ if (record != 0) {
+ ALOGE("openRecord returned an IAudioRecord but with status %d", lStatus);
+ record.clear();
+ }
+ }
}
if (status != NULL) {
*status = lStatus;
@@ -511,13 +545,12 @@
return reply.readInt32();
}
- virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output)
+ virtual status_t invalidateStream(audio_stream_type_t stream)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32((int32_t) stream);
- data.writeInt32((int32_t) output);
- remote()->transact(SET_STREAM_OUTPUT, data, &reply);
+ remote()->transact(INVALIDATE_STREAM, data, &reply);
return reply.readInt32();
}
@@ -530,7 +563,7 @@
return reply.readInt32();
}
- virtual status_t getRenderPosition(size_t *halFrames, size_t *dspFrames,
+ virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
audio_io_handle_t output) const
{
Parcel data, reply;
@@ -551,13 +584,16 @@
return status;
}
- virtual size_t getInputFramesLost(audio_io_handle_t ioHandle) const
+ virtual uint32_t getInputFramesLost(audio_io_handle_t ioHandle) const
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32((int32_t) ioHandle);
- remote()->transact(GET_INPUT_FRAMES_LOST, data, &reply);
- return reply.readInt32();
+ status_t status = remote()->transact(GET_INPUT_FRAMES_LOST, data, &reply);
+ if (status != NO_ERROR) {
+ return 0;
+ }
+ return (uint32_t) reply.readInt32();
}
virtual int newAudioSessionId()
@@ -572,19 +608,21 @@
return id;
}
- virtual void acquireAudioSessionId(int audioSession)
+ virtual void acquireAudioSessionId(int audioSession, int pid)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32(audioSession);
+ data.writeInt32(pid);
remote()->transact(ACQUIRE_AUDIO_SESSION_ID, data, &reply);
}
- virtual void releaseAudioSessionId(int audioSession)
+ virtual void releaseAudioSessionId(int audioSession, int pid)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32(audioSession);
+ data.writeInt32(pid);
remote()->transact(RELEASE_AUDIO_SESSION_ID, data, &reply);
}
@@ -769,6 +807,7 @@
audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
pid_t tid = (pid_t) data.readInt32();
int sessionId = data.readInt32();
+ int clientUid = data.readInt32();
String8 name;
status_t status;
sp<IAudioTrack> track;
@@ -779,9 +818,11 @@
} else {
track = createTrack(
(audio_stream_type_t) streamType, sampleRate, format,
- channelMask, frameCount, &flags, buffer, output, tid,
- &sessionId, name, &status);
+ channelMask, &frameCount, &flags, buffer, output, tid,
+ &sessionId, name, clientUid, &status);
+ LOG_ALWAYS_FATAL_IF((track != 0) != (status == NO_ERROR));
}
+ reply->writeInt32(frameCount);
reply->writeInt32(flags);
reply->writeInt32(sessionId);
reply->writeString8(name);
@@ -801,7 +842,9 @@
int sessionId = data.readInt32();
status_t status;
sp<IAudioRecord> record = openRecord(input,
- sampleRate, format, channelMask, frameCount, &flags, tid, &sessionId, &status);
+ sampleRate, format, channelMask, &frameCount, &flags, tid, &sessionId, &status);
+ LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR));
+ reply->writeInt32(frameCount);
reply->writeInt32(flags);
reply->writeInt32(sessionId);
reply->writeInt32(status);
@@ -944,7 +987,7 @@
&latency,
flags,
hasOffloadInfo ? &offloadInfo : NULL);
- ALOGV("OPEN_OUTPUT output, %p", output);
+ ALOGV("OPEN_OUTPUT output, %d", output);
reply->writeInt32((int32_t) output);
reply->writeInt32(devices);
reply->writeInt32(samplingRate);
@@ -1000,11 +1043,10 @@
reply->writeInt32(closeInput((audio_io_handle_t) data.readInt32()));
return NO_ERROR;
} break;
- case SET_STREAM_OUTPUT: {
+ case INVALIDATE_STREAM: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- uint32_t stream = data.readInt32();
- audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
- reply->writeInt32(setStreamOutput((audio_stream_type_t) stream, output));
+ audio_stream_type_t stream = (audio_stream_type_t) data.readInt32();
+ reply->writeInt32(invalidateStream(stream));
return NO_ERROR;
} break;
case SET_VOICE_VOLUME: {
@@ -1016,8 +1058,8 @@
case GET_RENDER_POSITION: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
- size_t halFrames;
- size_t dspFrames;
+ uint32_t halFrames;
+ uint32_t dspFrames;
status_t status = getRenderPosition(&halFrames, &dspFrames, output);
reply->writeInt32(status);
if (status == NO_ERROR) {
@@ -1029,7 +1071,7 @@
case GET_INPUT_FRAMES_LOST: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
- reply->writeInt32(getInputFramesLost(ioHandle));
+ reply->writeInt32((int32_t) getInputFramesLost(ioHandle));
return NO_ERROR;
} break;
case NEW_AUDIO_SESSION_ID: {
@@ -1040,13 +1082,15 @@
case ACQUIRE_AUDIO_SESSION_ID: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
int audioSession = data.readInt32();
- acquireAudioSessionId(audioSession);
+ int pid = data.readInt32();
+ acquireAudioSessionId(audioSession, pid);
return NO_ERROR;
} break;
case RELEASE_AUDIO_SESSION_ID: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
int audioSession = data.readInt32();
- releaseAudioSessionId(audioSession);
+ int pid = data.readInt32();
+ releaseAudioSessionId(audioSession, pid);
return NO_ERROR;
} break;
case QUERY_NUM_EFFECTS: {
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 4be3c09..1a027a6 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -476,10 +476,11 @@
case START_OUTPUT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
- uint32_t stream = data.readInt32();
+ audio_stream_type_t stream =
+ static_cast <audio_stream_type_t>(data.readInt32());
int session = data.readInt32();
reply->writeInt32(static_cast <uint32_t>(startOutput(output,
- (audio_stream_type_t)stream,
+ stream,
session)));
return NO_ERROR;
} break;
@@ -487,10 +488,11 @@
case STOP_OUTPUT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
- uint32_t stream = data.readInt32();
+ audio_stream_type_t stream =
+ static_cast <audio_stream_type_t>(data.readInt32());
int session = data.readInt32();
reply->writeInt32(static_cast <uint32_t>(stopOutput(output,
- (audio_stream_type_t)stream,
+ stream,
session)));
return NO_ERROR;
} break;
@@ -633,7 +635,7 @@
CHECK_INTERFACE(IAudioPolicyService, data, reply);
audio_stream_type_t stream = (audio_stream_type_t) data.readInt32();
uint32_t inPastMs = (uint32_t)data.readInt32();
- reply->writeInt32( isStreamActive((audio_stream_type_t) stream, inPastMs) );
+ reply->writeInt32( isStreamActive(stream, inPastMs) );
return NO_ERROR;
} break;
@@ -641,7 +643,7 @@
CHECK_INTERFACE(IAudioPolicyService, data, reply);
audio_stream_type_t stream = (audio_stream_type_t) data.readInt32();
uint32_t inPastMs = (uint32_t)data.readInt32();
- reply->writeInt32( isStreamActiveRemotely((audio_stream_type_t) stream, inPastMs) );
+ reply->writeInt32( isStreamActiveRemotely(stream, inPastMs) );
return NO_ERROR;
} break;
diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp
index 4a7de65..9866d70 100644
--- a/media/libmedia/IAudioRecord.cpp
+++ b/media/libmedia/IAudioRecord.cpp
@@ -50,6 +50,9 @@
status_t status = remote()->transact(GET_CBLK, data, &reply);
if (status == NO_ERROR) {
cblk = interface_cast<IMemory>(reply.readStrongBinder());
+ if (cblk != 0 && cblk->pointer() == NULL) {
+ cblk.clear();
+ }
}
return cblk;
}
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index f0d75ba..ffc21fc 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -41,6 +41,7 @@
SET_MEDIA_TIME_TRANSFORM,
SET_PARAMETERS,
GET_TIMESTAMP,
+ SIGNAL,
};
class BpAudioTrack : public BpInterface<IAudioTrack>
@@ -59,6 +60,9 @@
status_t status = remote()->transact(GET_CBLK, data, &reply);
if (status == NO_ERROR) {
cblk = interface_cast<IMemory>(reply.readStrongBinder());
+ if (cblk != 0 && cblk->pointer() == NULL) {
+ cblk.clear();
+ }
}
return cblk;
}
@@ -121,6 +125,9 @@
status = reply.readInt32();
if (status == NO_ERROR) {
*buffer = interface_cast<IMemory>(reply.readStrongBinder());
+ if (*buffer != 0 && (*buffer)->pointer() == NULL) {
+ (*buffer).clear();
+ }
}
}
return status;
@@ -182,6 +189,12 @@
}
return status;
}
+
+ virtual void signal() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+ remote()->transact(SIGNAL, data, &reply);
+ }
};
IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack");
@@ -269,6 +282,11 @@
}
return NO_ERROR;
} break;
+ case SIGNAL: {
+ CHECK_INTERFACE(IAudioTrack, data, reply);
+ signal();
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IEffect.cpp b/media/libmedia/IEffect.cpp
index a303a8f..b94012a 100644
--- a/media/libmedia/IEffect.cpp
+++ b/media/libmedia/IEffect.cpp
@@ -117,6 +117,9 @@
status_t status = remote()->transact(GET_CBLK, data, &reply);
if (status == NO_ERROR) {
cblk = interface_cast<IMemory>(reply.readStrongBinder());
+ if (cblk != 0 && cblk->pointer() == NULL) {
+ cblk.clear();
+ }
}
return cblk;
}
diff --git a/media/libmedia/IMediaDeathNotifier.cpp b/media/libmedia/IMediaDeathNotifier.cpp
index 9db5b1b..10b4934 100644
--- a/media/libmedia/IMediaDeathNotifier.cpp
+++ b/media/libmedia/IMediaDeathNotifier.cpp
@@ -75,7 +75,7 @@
}
void
-IMediaDeathNotifier::DeathNotifier::binderDied(const wp<IBinder>& who) {
+IMediaDeathNotifier::DeathNotifier::binderDied(const wp<IBinder>& who __unused) {
ALOGW("media server died");
// Need to do this with the lock held
diff --git a/media/libmedia/IMediaHTTPConnection.cpp b/media/libmedia/IMediaHTTPConnection.cpp
new file mode 100644
index 0000000..7e26ee6
--- /dev/null
+++ b/media/libmedia/IMediaHTTPConnection.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2013 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 "IMediaHTTPConnection"
+#include <utils/Log.h>
+
+#include <media/IMediaHTTPConnection.h>
+
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+enum {
+ CONNECT = IBinder::FIRST_CALL_TRANSACTION,
+ DISCONNECT,
+ READ_AT,
+ GET_SIZE,
+ GET_MIME_TYPE,
+ GET_URI
+};
+
+struct BpMediaHTTPConnection : public BpInterface<IMediaHTTPConnection> {
+ BpMediaHTTPConnection(const sp<IBinder> &impl)
+ : BpInterface<IMediaHTTPConnection>(impl) {
+ }
+
+ virtual bool connect(
+ const char *uri, const KeyedVector<String8, String8> *headers) {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IMediaHTTPConnection::getInterfaceDescriptor());
+
+ String16 tmp(uri);
+ data.writeString16(tmp);
+
+ tmp = String16("");
+ if (headers != NULL) {
+ for (size_t i = 0; i < headers->size(); ++i) {
+ String16 key(headers->keyAt(i).string());
+ String16 val(headers->valueAt(i).string());
+
+ tmp.append(key);
+ tmp.append(String16(": "));
+ tmp.append(val);
+ tmp.append(String16("\r\n"));
+ }
+ }
+ data.writeString16(tmp);
+
+ remote()->transact(CONNECT, data, &reply);
+
+ int32_t exceptionCode = reply.readExceptionCode();
+
+ if (exceptionCode) {
+ return UNKNOWN_ERROR;
+ }
+
+ sp<IBinder> binder = reply.readStrongBinder();
+ mMemory = interface_cast<IMemory>(binder);
+
+ return mMemory != NULL;
+ }
+
+ virtual void disconnect() {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IMediaHTTPConnection::getInterfaceDescriptor());
+
+ remote()->transact(DISCONNECT, data, &reply);
+ }
+
+ virtual ssize_t readAt(off64_t offset, void *buffer, size_t size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IMediaHTTPConnection::getInterfaceDescriptor());
+
+ data.writeInt64(offset);
+ data.writeInt32(size);
+
+ status_t err = remote()->transact(READ_AT, data, &reply);
+ if (err != OK) {
+ ALOGE("remote readAt failed");
+ return UNKNOWN_ERROR;
+ }
+
+ int32_t exceptionCode = reply.readExceptionCode();
+
+ if (exceptionCode) {
+ return UNKNOWN_ERROR;
+ }
+
+ int32_t len = reply.readInt32();
+
+ if (len > 0) {
+ memcpy(buffer, mMemory->pointer(), len);
+ }
+
+ return len;
+ }
+
+ virtual off64_t getSize() {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IMediaHTTPConnection::getInterfaceDescriptor());
+
+ remote()->transact(GET_SIZE, data, &reply);
+
+ int32_t exceptionCode = reply.readExceptionCode();
+
+ if (exceptionCode) {
+ return UNKNOWN_ERROR;
+ }
+
+ return reply.readInt64();
+ }
+
+ virtual status_t getMIMEType(String8 *mimeType) {
+ *mimeType = String8("");
+
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IMediaHTTPConnection::getInterfaceDescriptor());
+
+ remote()->transact(GET_MIME_TYPE, data, &reply);
+
+ int32_t exceptionCode = reply.readExceptionCode();
+
+ if (exceptionCode) {
+ return UNKNOWN_ERROR;
+ }
+
+ *mimeType = String8(reply.readString16());
+
+ return OK;
+ }
+
+ virtual status_t getUri(String8 *uri) {
+ *uri = String8("");
+
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IMediaHTTPConnection::getInterfaceDescriptor());
+
+ remote()->transact(GET_URI, data, &reply);
+
+ int32_t exceptionCode = reply.readExceptionCode();
+
+ if (exceptionCode) {
+ return UNKNOWN_ERROR;
+ }
+
+ *uri = String8(reply.readString16());
+
+ return OK;
+ }
+
+private:
+ sp<IMemory> mMemory;
+};
+
+IMPLEMENT_META_INTERFACE(
+ MediaHTTPConnection, "android.media.IMediaHTTPConnection");
+
+} // namespace android
+
diff --git a/media/libmedia/IMediaHTTPService.cpp b/media/libmedia/IMediaHTTPService.cpp
new file mode 100644
index 0000000..1260582
--- /dev/null
+++ b/media/libmedia/IMediaHTTPService.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 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 "IMediaHTTPService"
+#include <utils/Log.h>
+
+#include <media/IMediaHTTPService.h>
+
+#include <binder/Parcel.h>
+#include <media/IMediaHTTPConnection.h>
+
+namespace android {
+
+enum {
+ MAKE_HTTP = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+struct BpMediaHTTPService : public BpInterface<IMediaHTTPService> {
+ BpMediaHTTPService(const sp<IBinder> &impl)
+ : BpInterface<IMediaHTTPService>(impl) {
+ }
+
+ virtual sp<IMediaHTTPConnection> makeHTTPConnection() {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IMediaHTTPService::getInterfaceDescriptor());
+
+ remote()->transact(MAKE_HTTP, data, &reply);
+
+ status_t err = reply.readInt32();
+
+ if (err != OK) {
+ return NULL;
+ }
+
+ return interface_cast<IMediaHTTPConnection>(reply.readStrongBinder());
+ }
+};
+
+IMPLEMENT_META_INTERFACE(
+ MediaHTTPService, "android.media.IMediaHTTPService");
+
+} // namespace android
+
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index bb066a0..c7d9d51 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -18,6 +18,7 @@
#include <stdint.h>
#include <sys/types.h>
#include <binder/Parcel.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaMetadataRetriever.h>
#include <utils/String8.h>
#include <utils/KeyedVector.h>
@@ -84,10 +85,16 @@
}
status_t setDataSource(
- const char *srcUrl, const KeyedVector<String8, String8> *headers)
+ const sp<IMediaHTTPService> &httpService,
+ const char *srcUrl,
+ const KeyedVector<String8, String8> *headers)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
+ data.writeInt32(httpService != NULL);
+ if (httpService != NULL) {
+ data.writeStrongBinder(httpService->asBinder());
+ }
data.writeCString(srcUrl);
if (headers == NULL) {
@@ -195,6 +202,13 @@
} break;
case SET_DATA_SOURCE_URL: {
CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
+
+ sp<IMediaHTTPService> httpService;
+ if (data.readInt32()) {
+ httpService =
+ interface_cast<IMediaHTTPService>(data.readStrongBinder());
+ }
+
const char* srcUrl = data.readCString();
KeyedVector<String8, String8> headers;
@@ -206,7 +220,8 @@
}
reply->writeInt32(
- setDataSource(srcUrl, numHeaders > 0 ? &headers : NULL));
+ setDataSource(
+ httpService, srcUrl, numHeaders > 0 ? &headers : NULL));
return NO_ERROR;
} break;
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index e79bcd2..d778d05 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -21,6 +21,7 @@
#include <binder/Parcel.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayer.h>
#include <media/IStreamSource.h>
@@ -75,11 +76,17 @@
remote()->transact(DISCONNECT, data, &reply);
}
- status_t setDataSource(const char* url,
+ status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
+ const char* url,
const KeyedVector<String8, String8>* headers)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ data.writeInt32(httpService != NULL);
+ if (httpService != NULL) {
+ data.writeStrongBinder(httpService->asBinder());
+ }
data.writeCString(url);
if (headers == NULL) {
data.writeInt32(0);
@@ -355,6 +362,13 @@
} break;
case SET_DATA_SOURCE_URL: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
+
+ sp<IMediaHTTPService> httpService;
+ if (data.readInt32()) {
+ httpService =
+ interface_cast<IMediaHTTPService>(data.readStrongBinder());
+ }
+
const char* url = data.readCString();
KeyedVector<String8, String8> headers;
int32_t numHeaders = data.readInt32();
@@ -363,7 +377,8 @@
String8 value = data.readString8();
headers.add(key, value);
}
- reply->writeInt32(setDataSource(url, numHeaders > 0 ? &headers : NULL));
+ reply->writeInt32(setDataSource(
+ httpService, url, numHeaders > 0 ? &headers : NULL));
return NO_ERROR;
} break;
case SET_DATA_SOURCE_FD: {
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 3c22b4c..d116b14 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -23,6 +23,7 @@
#include <media/ICrypto.h>
#include <media/IDrm.h>
#include <media/IHDCP.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <media/IMediaRecorder.h>
#include <media/IOMX.h>
@@ -48,7 +49,6 @@
ADD_BATTERY_DATA,
PULL_BATTERY_DATA,
LISTEN_FOR_REMOTE_DISPLAY,
- UPDATE_PROXY_CONFIG,
};
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
@@ -86,12 +86,21 @@
return interface_cast<IMediaRecorder>(reply.readStrongBinder());
}
- virtual status_t decode(const char* url, uint32_t *pSampleRate, int* pNumChannels,
- audio_format_t* pFormat,
- const sp<IMemoryHeap>& heap, size_t *pSize)
+ virtual status_t decode(
+ const sp<IMediaHTTPService> &httpService,
+ const char* url,
+ uint32_t *pSampleRate,
+ int* pNumChannels,
+ audio_format_t* pFormat,
+ const sp<IMemoryHeap>& heap,
+ size_t *pSize)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ data.writeInt32(httpService != NULL);
+ if (httpService != NULL) {
+ data.writeStrongBinder(httpService->asBinder());
+ }
data.writeCString(url);
data.writeStrongBinder(heap->asBinder());
status_t status = remote()->transact(DECODE_URL, data, &reply);
@@ -182,25 +191,6 @@
remote()->transact(LISTEN_FOR_REMOTE_DISPLAY, data, &reply);
return interface_cast<IRemoteDisplay>(reply.readStrongBinder());
}
-
- virtual status_t updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
- Parcel data, reply;
-
- data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
- if (host == NULL) {
- data.writeInt32(0);
- } else {
- data.writeInt32(1);
- data.writeCString(host);
- data.writeInt32(port);
- data.writeCString(exclusionList);
- }
-
- remote()->transact(UPDATE_PROXY_CONFIG, data, &reply);
-
- return reply.readInt32();
- }
};
IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService");
@@ -222,13 +212,25 @@
} break;
case DECODE_URL: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
+ sp<IMediaHTTPService> httpService;
+ if (data.readInt32()) {
+ httpService =
+ interface_cast<IMediaHTTPService>(data.readStrongBinder());
+ }
const char* url = data.readCString();
sp<IMemoryHeap> heap = interface_cast<IMemoryHeap>(data.readStrongBinder());
uint32_t sampleRate;
int numChannels;
audio_format_t format;
size_t size;
- status_t status = decode(url, &sampleRate, &numChannels, &format, heap, &size);
+ status_t status =
+ decode(httpService,
+ url,
+ &sampleRate,
+ &numChannels,
+ &format,
+ heap,
+ &size);
reply->writeInt32(status);
if (status == NO_ERROR) {
reply->writeInt32(sampleRate);
@@ -316,24 +318,6 @@
reply->writeStrongBinder(display->asBinder());
return NO_ERROR;
} break;
- case UPDATE_PROXY_CONFIG:
- {
- CHECK_INTERFACE(IMediaPlayerService, data, reply);
-
- const char *host = NULL;
- int32_t port = 0;
- const char *exclusionList = NULL;
-
- if (data.readInt32()) {
- host = data.readCString();
- port = data.readInt32();
- exclusionList = data.readCString();
- }
-
- reply->writeInt32(updateProxyConfig(host, port, exclusionList));
-
- return OK;
- }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index ef99f4f..71ce320 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -43,6 +43,7 @@
CREATE_INPUT_SURFACE,
SIGNAL_END_OF_INPUT_STREAM,
STORE_META_DATA_IN_BUFFERS,
+ PREPARE_FOR_ADAPTIVE_PLAYBACK,
ALLOC_BUFFER,
ALLOC_BUFFER_WITH_BACKUP,
FREE_BUFFER,
@@ -351,6 +352,22 @@
return err;
}
+ virtual status_t prepareForAdaptivePlayback(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable,
+ OMX_U32 max_width, OMX_U32 max_height) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(port_index);
+ data.writeInt32((int32_t)enable);
+ data.writeInt32(max_width);
+ data.writeInt32(max_height);
+ remote()->transact(PREPARE_FOR_ADAPTIVE_PLAYBACK, data, &reply);
+
+ status_t err = reply.readInt32();
+ return err;
+ }
+
virtual status_t allocateBuffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer, void **buffer_data) {
@@ -770,6 +787,23 @@
return NO_ERROR;
}
+ case PREPARE_FOR_ADAPTIVE_PLAYBACK:
+ {
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_U32 port_index = data.readInt32();
+ OMX_BOOL enable = (OMX_BOOL)data.readInt32();
+ OMX_U32 max_width = data.readInt32();
+ OMX_U32 max_height = data.readInt32();
+
+ status_t err = prepareForAdaptivePlayback(
+ node, port_index, enable, max_width, max_height);
+ reply->writeInt32(err);
+
+ return NO_ERROR;
+ }
+
case ALLOC_BUFFER:
{
CHECK_OMX_INTERFACE(IOMX, data, reply);
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index e914b34..f0f1832 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -90,7 +90,7 @@
pLibConfig->sampleRate,
AUDIO_FORMAT_PCM_16_BIT,
audio_channel_out_mask_from_count(pLibConfig->numChannels),
- mTrackBufferSize,
+ (size_t) mTrackBufferSize,
AUDIO_OUTPUT_FLAG_NONE);
// create render and playback thread
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 8319cd7..1074da9 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -81,8 +81,14 @@
{"timelapseqvga", CAMCORDER_QUALITY_TIME_LAPSE_QVGA},
};
+#if LOG_NDEBUG
+#define UNUSED __unused
+#else
+#define UNUSED
+#endif
+
/*static*/ void
-MediaProfiles::logVideoCodec(const MediaProfiles::VideoCodec& codec)
+MediaProfiles::logVideoCodec(const MediaProfiles::VideoCodec& codec UNUSED)
{
ALOGV("video codec:");
ALOGV("codec = %d", codec.mCodec);
@@ -93,7 +99,7 @@
}
/*static*/ void
-MediaProfiles::logAudioCodec(const MediaProfiles::AudioCodec& codec)
+MediaProfiles::logAudioCodec(const MediaProfiles::AudioCodec& codec UNUSED)
{
ALOGV("audio codec:");
ALOGV("codec = %d", codec.mCodec);
@@ -103,7 +109,7 @@
}
/*static*/ void
-MediaProfiles::logVideoEncoderCap(const MediaProfiles::VideoEncoderCap& cap)
+MediaProfiles::logVideoEncoderCap(const MediaProfiles::VideoEncoderCap& cap UNUSED)
{
ALOGV("video encoder cap:");
ALOGV("codec = %d", cap.mCodec);
@@ -114,7 +120,7 @@
}
/*static*/ void
-MediaProfiles::logAudioEncoderCap(const MediaProfiles::AudioEncoderCap& cap)
+MediaProfiles::logAudioEncoderCap(const MediaProfiles::AudioEncoderCap& cap UNUSED)
{
ALOGV("audio encoder cap:");
ALOGV("codec = %d", cap.mCodec);
@@ -124,21 +130,21 @@
}
/*static*/ void
-MediaProfiles::logVideoDecoderCap(const MediaProfiles::VideoDecoderCap& cap)
+MediaProfiles::logVideoDecoderCap(const MediaProfiles::VideoDecoderCap& cap UNUSED)
{
ALOGV("video decoder cap:");
ALOGV("codec = %d", cap.mCodec);
}
/*static*/ void
-MediaProfiles::logAudioDecoderCap(const MediaProfiles::AudioDecoderCap& cap)
+MediaProfiles::logAudioDecoderCap(const MediaProfiles::AudioDecoderCap& cap UNUSED)
{
ALOGV("audio codec cap:");
ALOGV("codec = %d", cap.mCodec);
}
/*static*/ void
-MediaProfiles::logVideoEditorCap(const MediaProfiles::VideoEditorCap& cap)
+MediaProfiles::logVideoEditorCap(const MediaProfiles::VideoEditorCap& cap UNUSED)
{
ALOGV("videoeditor cap:");
ALOGV("mMaxInputFrameWidth = %d", cap.mMaxInputFrameWidth);
diff --git a/media/libmedia/MediaScannerClient.cpp b/media/libmedia/MediaScannerClient.cpp
index 93a4a4c..1661f04 100644
--- a/media/libmedia/MediaScannerClient.cpp
+++ b/media/libmedia/MediaScannerClient.cpp
@@ -14,217 +14,57 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaScannerClient"
+#include <utils/Log.h>
+
#include <media/mediascanner.h>
+#include "CharacterEncodingDetector.h"
#include "StringArray.h"
-#include "autodetect.h"
-#include "unicode/ucnv.h"
-#include "unicode/ustring.h"
-
namespace android {
MediaScannerClient::MediaScannerClient()
- : mNames(NULL),
- mValues(NULL),
- mLocaleEncoding(kEncodingNone)
+ : mEncodingDetector(NULL)
{
}
MediaScannerClient::~MediaScannerClient()
{
- delete mNames;
- delete mValues;
+ delete mEncodingDetector;
}
void MediaScannerClient::setLocale(const char* locale)
{
- if (!locale) return;
-
- if (!strncmp(locale, "ja", 2))
- mLocaleEncoding = kEncodingShiftJIS;
- else if (!strncmp(locale, "ko", 2))
- mLocaleEncoding = kEncodingEUCKR;
- else if (!strncmp(locale, "zh", 2)) {
- if (!strcmp(locale, "zh_CN")) {
- // simplified chinese for mainland China
- mLocaleEncoding = kEncodingGBK;
- } else {
- // assume traditional for non-mainland Chinese locales (Taiwan, Hong Kong, Singapore)
- mLocaleEncoding = kEncodingBig5;
- }
- }
+ mLocale = locale; // not currently used
}
void MediaScannerClient::beginFile()
{
- mNames = new StringArray;
- mValues = new StringArray;
+ delete mEncodingDetector;
+ mEncodingDetector = new CharacterEncodingDetector();
}
status_t MediaScannerClient::addStringTag(const char* name, const char* value)
{
- if (mLocaleEncoding != kEncodingNone) {
- // don't bother caching strings that are all ASCII.
- // call handleStringTag directly instead.
- // check to see if value (which should be utf8) has any non-ASCII characters
- bool nonAscii = false;
- const char* chp = value;
- char ch;
- while ((ch = *chp++)) {
- if (ch & 0x80) {
- nonAscii = true;
- break;
- }
- }
-
- if (nonAscii) {
- // save the strings for later so they can be used for native encoding detection
- mNames->push_back(name);
- mValues->push_back(value);
- return OK;
- }
- // else fall through
- }
-
- // autodetection is not necessary, so no need to cache the values
- // pass directly to the client instead
- return handleStringTag(name, value);
-}
-
-static uint32_t possibleEncodings(const char* s)
-{
- uint32_t result = kEncodingAll;
- // if s contains a native encoding, then it was mistakenly encoded in utf8 as if it were latin-1
- // so we need to reverse the latin-1 -> utf8 conversion to get the native chars back
- uint8_t ch1, ch2;
- uint8_t* chp = (uint8_t *)s;
-
- while ((ch1 = *chp++)) {
- if (ch1 & 0x80) {
- ch2 = *chp++;
- ch1 = ((ch1 << 6) & 0xC0) | (ch2 & 0x3F);
- // ch1 is now the first byte of the potential native char
-
- ch2 = *chp++;
- if (ch2 & 0x80)
- ch2 = ((ch2 << 6) & 0xC0) | (*chp++ & 0x3F);
- // ch2 is now the second byte of the potential native char
- int ch = (int)ch1 << 8 | (int)ch2;
- result &= findPossibleEncodings(ch);
- }
- // else ASCII character, which could be anything
- }
-
- return result;
-}
-
-void MediaScannerClient::convertValues(uint32_t encoding)
-{
- const char* enc = NULL;
- switch (encoding) {
- case kEncodingShiftJIS:
- enc = "shift-jis";
- break;
- case kEncodingGBK:
- enc = "gbk";
- break;
- case kEncodingBig5:
- enc = "Big5";
- break;
- case kEncodingEUCKR:
- enc = "EUC-KR";
- break;
- }
-
- if (enc) {
- UErrorCode status = U_ZERO_ERROR;
-
- UConverter *conv = ucnv_open(enc, &status);
- if (U_FAILURE(status)) {
- ALOGE("could not create UConverter for %s", enc);
- return;
- }
- UConverter *utf8Conv = ucnv_open("UTF-8", &status);
- if (U_FAILURE(status)) {
- ALOGE("could not create UConverter for UTF-8");
- ucnv_close(conv);
- return;
- }
-
- // for each value string, convert from native encoding to UTF-8
- for (int i = 0; i < mNames->size(); i++) {
- // first we need to untangle the utf8 and convert it back to the original bytes
- // since we are reducing the length of the string, we can do this in place
- uint8_t* src = (uint8_t *)mValues->getEntry(i);
- int len = strlen((char *)src);
- uint8_t* dest = src;
-
- uint8_t uch;
- while ((uch = *src++)) {
- if (uch & 0x80)
- *dest++ = ((uch << 6) & 0xC0) | (*src++ & 0x3F);
- else
- *dest++ = uch;
- }
- *dest = 0;
-
- // now convert from native encoding to UTF-8
- const char* source = mValues->getEntry(i);
- int targetLength = len * 3 + 1;
- char* buffer = new char[targetLength];
- // don't normally check for NULL, but in this case targetLength may be large
- if (!buffer)
- break;
- char* target = buffer;
-
- ucnv_convertEx(utf8Conv, conv, &target, target + targetLength,
- &source, (const char *)dest, NULL, NULL, NULL, NULL, TRUE, TRUE, &status);
- if (U_FAILURE(status)) {
- ALOGE("ucnv_convertEx failed: %d", status);
- mValues->setEntry(i, "???");
- } else {
- // zero terminate
- *target = 0;
- mValues->setEntry(i, buffer);
- }
-
- delete[] buffer;
- }
-
- ucnv_close(conv);
- ucnv_close(utf8Conv);
- }
+ mEncodingDetector->addTag(name, value);
+ return OK;
}
void MediaScannerClient::endFile()
{
- if (mLocaleEncoding != kEncodingNone) {
- int size = mNames->size();
- uint32_t encoding = kEncodingAll;
+ mEncodingDetector->detectAndConvert();
- // compute a bit mask containing all possible encodings
- for (int i = 0; i < mNames->size(); i++)
- encoding &= possibleEncodings(mValues->getEntry(i));
-
- // if the locale encoding matches, then assume we have a native encoding.
- if (encoding & mLocaleEncoding)
- convertValues(mLocaleEncoding);
-
- // finally, push all name/value pairs to the client
- for (int i = 0; i < mNames->size(); i++) {
- status_t status = handleStringTag(mNames->getEntry(i), mValues->getEntry(i));
- if (status) {
- break;
- }
+ int size = mEncodingDetector->size();
+ if (size) {
+ for (int i = 0; i < size; i++) {
+ const char *name;
+ const char *value;
+ mEncodingDetector->getTag(i, &name, &value);
+ handleStringTag(name, value);
}
}
- // else addStringTag() has done all the work so we have nothing to do
-
- delete mNames;
- delete mValues;
- mNames = NULL;
- mValues = NULL;
}
} // namespace android
diff --git a/media/libmedia/MemoryLeakTrackUtil.cpp b/media/libmedia/MemoryLeakTrackUtil.cpp
index 6a108ae..f004ca4 100644
--- a/media/libmedia/MemoryLeakTrackUtil.cpp
+++ b/media/libmedia/MemoryLeakTrackUtil.cpp
@@ -49,7 +49,7 @@
}
void append(const char *s) {
- strcat(mPtr, s);
+ strncat(mPtr, s, MAX_SIZE - size() - 1);
}
const char *string() const {
@@ -60,6 +60,10 @@
return strlen(mPtr);
}
+ void clear() {
+ *mPtr = '\0';
+ }
+
private:
char *mPtr;
@@ -139,6 +143,9 @@
}
} while (moved);
+ write(fd, result.string(), result.size());
+ result.clear();
+
for (size_t i = 0; i < count; i++) {
AllocEntry *e = &entries[i];
@@ -152,13 +159,14 @@
result.append(buffer);
}
result.append("\n");
+
+ write(fd, result.string(), result.size());
+ result.clear();
}
delete[] entries;
free_malloc_leak_info(info);
}
-
- write(fd, result.string(), result.size());
}
#else
diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp
index 8434d43..a55e09c 100644
--- a/media/libmedia/SoundPool.cpp
+++ b/media/libmedia/SoundPool.cpp
@@ -21,6 +21,7 @@
#define USE_SHARED_MEM_BUFFER
#include <media/AudioTrack.h>
+#include <media/IMediaHTTPService.h>
#include <media/mediaplayer.h>
#include <media/SoundPool.h>
#include "SoundPoolThread.h"
@@ -199,7 +200,7 @@
return NULL;
}
-int SoundPool::load(const char* path, int priority)
+int SoundPool::load(const char* path, int priority __unused)
{
ALOGV("load: path=%s, priority=%d", path, priority);
Mutex::Autolock lock(&mLock);
@@ -209,7 +210,7 @@
return sample->sampleID();
}
-int SoundPool::load(int fd, int64_t offset, int64_t length, int priority)
+int SoundPool::load(int fd, int64_t offset, int64_t length, int priority __unused)
{
ALOGV("load: fd=%d, offset=%lld, length=%lld, priority=%d",
fd, offset, length, priority);
@@ -496,7 +497,14 @@
ALOGV("Start decode");
if (mUrl) {
- status = MediaPlayer::decode(mUrl, &sampleRate, &numChannels, &format, mHeap, &mSize);
+ status = MediaPlayer::decode(
+ NULL /* httpService */,
+ mUrl,
+ &sampleRate,
+ &numChannels,
+ &format,
+ mHeap,
+ &mSize);
} else {
status = MediaPlayer::decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format,
mHeap, &mSize);
@@ -579,7 +587,7 @@
uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5);
uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate;
uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount;
- uint32_t frameCount = 0;
+ size_t frameCount = 0;
if (loop) {
frameCount = sample->size()/numChannels/
@@ -600,16 +608,15 @@
// wrong audio audio buffer size (mAudioBufferSize)
unsigned long toggle = mToggle ^ 1;
void *userData = (void *)((unsigned long)this | toggle);
- uint32_t channels = (numChannels == 2) ?
- AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO;
+ audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(numChannels);
// do not create a new audio track if current track is compatible with sample parameters
#ifdef USE_SHARED_MEM_BUFFER
newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
- channels, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData);
+ channelMask, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData);
#else
newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
- channels, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData,
+ channelMask, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData,
bufferFrames);
#endif
oldTrack = mAudioTrack;
@@ -730,7 +737,8 @@
count = b->size;
}
memcpy(q, p, count);
-// ALOGV("fill: q=%p, p=%p, mPos=%u, b->size=%u, count=%d", q, p, mPos, b->size, count);
+// ALOGV("fill: q=%p, p=%p, mPos=%u, b->size=%u, count=%d", q, p, mPos, b->size,
+// count);
} else if (mPos < mAudioBufferSize) {
count = mAudioBufferSize - mPos;
if (count > b->size) {
@@ -744,11 +752,16 @@
b->size = count;
//ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]);
}
- } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END) {
- ALOGV("process %p channel %d EVENT_UNDERRUN or EVENT_BUFFER_END", this, mChannelID);
+ } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END ||
+ event == AudioTrack::EVENT_NEW_IAUDIOTRACK) {
+ ALOGV("process %p channel %d event %s",
+ this, mChannelID, (event == AudioTrack::EVENT_UNDERRUN) ? "UNDERRUN" :
+ (event == AudioTrack::EVENT_BUFFER_END) ? "BUFFER_END" : "NEW_IAUDIOTRACK");
mSoundPool->addToStopList(this);
} else if (event == AudioTrack::EVENT_LOOP_END) {
ALOGV("End loop %p channel %d", this, mChannelID);
+ } else {
+ ALOGW("SoundChannel::process unexpected event %d", event);
}
}
diff --git a/media/libmedia/autodetect.cpp b/media/libmedia/autodetect.cpp
deleted file mode 100644
index be5c3b2..0000000
--- a/media/libmedia/autodetect.cpp
+++ /dev/null
@@ -1,885 +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 "autodetect.h"
-
-struct CharRange {
- uint16_t first;
- uint16_t last;
-};
-
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
-
-// generated from http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT
-static const CharRange kShiftJISRanges[] = {
- { 0x8140, 0x817E },
- { 0x8180, 0x81AC },
- { 0x81B8, 0x81BF },
- { 0x81C8, 0x81CE },
- { 0x81DA, 0x81E8 },
- { 0x81F0, 0x81F7 },
- { 0x81FC, 0x81FC },
- { 0x824F, 0x8258 },
- { 0x8260, 0x8279 },
- { 0x8281, 0x829A },
- { 0x829F, 0x82F1 },
- { 0x8340, 0x837E },
- { 0x8380, 0x8396 },
- { 0x839F, 0x83B6 },
- { 0x83BF, 0x83D6 },
- { 0x8440, 0x8460 },
- { 0x8470, 0x847E },
- { 0x8480, 0x8491 },
- { 0x849F, 0x84BE },
- { 0x8740, 0x875D },
- { 0x875F, 0x8775 },
- { 0x877E, 0x877E },
- { 0x8780, 0x879C },
- { 0x889F, 0x88FC },
- { 0x8940, 0x897E },
- { 0x8980, 0x89FC },
- { 0x8A40, 0x8A7E },
- { 0x8A80, 0x8AFC },
- { 0x8B40, 0x8B7E },
- { 0x8B80, 0x8BFC },
- { 0x8C40, 0x8C7E },
- { 0x8C80, 0x8CFC },
- { 0x8D40, 0x8D7E },
- { 0x8D80, 0x8DFC },
- { 0x8E40, 0x8E7E },
- { 0x8E80, 0x8EFC },
- { 0x8F40, 0x8F7E },
- { 0x8F80, 0x8FFC },
- { 0x9040, 0x907E },
- { 0x9080, 0x90FC },
- { 0x9140, 0x917E },
- { 0x9180, 0x91FC },
- { 0x9240, 0x927E },
- { 0x9280, 0x92FC },
- { 0x9340, 0x937E },
- { 0x9380, 0x93FC },
- { 0x9440, 0x947E },
- { 0x9480, 0x94FC },
- { 0x9540, 0x957E },
- { 0x9580, 0x95FC },
- { 0x9640, 0x967E },
- { 0x9680, 0x96FC },
- { 0x9740, 0x977E },
- { 0x9780, 0x97FC },
- { 0x9840, 0x9872 },
- { 0x989F, 0x98FC },
- { 0x9940, 0x997E },
- { 0x9980, 0x99FC },
- { 0x9A40, 0x9A7E },
- { 0x9A80, 0x9AFC },
- { 0x9B40, 0x9B7E },
- { 0x9B80, 0x9BFC },
- { 0x9C40, 0x9C7E },
- { 0x9C80, 0x9CFC },
- { 0x9D40, 0x9D7E },
- { 0x9D80, 0x9DFC },
- { 0x9E40, 0x9E7E },
- { 0x9E80, 0x9EFC },
- { 0x9F40, 0x9F7E },
- { 0x9F80, 0x9FFC },
- { 0xE040, 0xE07E },
- { 0xE080, 0xE0FC },
- { 0xE140, 0xE17E },
- { 0xE180, 0xE1FC },
- { 0xE240, 0xE27E },
- { 0xE280, 0xE2FC },
- { 0xE340, 0xE37E },
- { 0xE380, 0xE3FC },
- { 0xE440, 0xE47E },
- { 0xE480, 0xE4FC },
- { 0xE540, 0xE57E },
- { 0xE580, 0xE5FC },
- { 0xE640, 0xE67E },
- { 0xE680, 0xE6FC },
- { 0xE740, 0xE77E },
- { 0xE780, 0xE7FC },
- { 0xE840, 0xE87E },
- { 0xE880, 0xE8FC },
- { 0xE940, 0xE97E },
- { 0xE980, 0xE9FC },
- { 0xEA40, 0xEA7E },
- { 0xEA80, 0xEAA4 },
- { 0xED40, 0xED7E },
- { 0xED80, 0xEDFC },
- { 0xEE40, 0xEE7E },
- { 0xEE80, 0xEEEC },
- { 0xEEEF, 0xEEFC },
- { 0xFA40, 0xFA7E },
- { 0xFA80, 0xFAFC },
- { 0xFB40, 0xFB7E },
- { 0xFB80, 0xFBFC },
- { 0xFC40, 0xFC4B },
-};
-
-// generated from http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT
-static const CharRange kGBKRanges[] = {
- { 0x8140, 0x817E },
- { 0x8180, 0x81FE },
- { 0x8240, 0x827E },
- { 0x8280, 0x82FE },
- { 0x8340, 0x837E },
- { 0x8380, 0x83FE },
- { 0x8440, 0x847E },
- { 0x8480, 0x84FE },
- { 0x8540, 0x857E },
- { 0x8580, 0x85FE },
- { 0x8640, 0x867E },
- { 0x8680, 0x86FE },
- { 0x8740, 0x877E },
- { 0x8780, 0x87FE },
- { 0x8840, 0x887E },
- { 0x8880, 0x88FE },
- { 0x8940, 0x897E },
- { 0x8980, 0x89FE },
- { 0x8A40, 0x8A7E },
- { 0x8A80, 0x8AFE },
- { 0x8B40, 0x8B7E },
- { 0x8B80, 0x8BFE },
- { 0x8C40, 0x8C7E },
- { 0x8C80, 0x8CFE },
- { 0x8D40, 0x8D7E },
- { 0x8D80, 0x8DFE },
- { 0x8E40, 0x8E7E },
- { 0x8E80, 0x8EFE },
- { 0x8F40, 0x8F7E },
- { 0x8F80, 0x8FFE },
- { 0x9040, 0x907E },
- { 0x9080, 0x90FE },
- { 0x9140, 0x917E },
- { 0x9180, 0x91FE },
- { 0x9240, 0x927E },
- { 0x9280, 0x92FE },
- { 0x9340, 0x937E },
- { 0x9380, 0x93FE },
- { 0x9440, 0x947E },
- { 0x9480, 0x94FE },
- { 0x9540, 0x957E },
- { 0x9580, 0x95FE },
- { 0x9640, 0x967E },
- { 0x9680, 0x96FE },
- { 0x9740, 0x977E },
- { 0x9780, 0x97FE },
- { 0x9840, 0x987E },
- { 0x9880, 0x98FE },
- { 0x9940, 0x997E },
- { 0x9980, 0x99FE },
- { 0x9A40, 0x9A7E },
- { 0x9A80, 0x9AFE },
- { 0x9B40, 0x9B7E },
- { 0x9B80, 0x9BFE },
- { 0x9C40, 0x9C7E },
- { 0x9C80, 0x9CFE },
- { 0x9D40, 0x9D7E },
- { 0x9D80, 0x9DFE },
- { 0x9E40, 0x9E7E },
- { 0x9E80, 0x9EFE },
- { 0x9F40, 0x9F7E },
- { 0x9F80, 0x9FFE },
- { 0xA040, 0xA07E },
- { 0xA080, 0xA0FE },
- { 0xA1A1, 0xA1FE },
- { 0xA2A1, 0xA2AA },
- { 0xA2B1, 0xA2E2 },
- { 0xA2E5, 0xA2EE },
- { 0xA2F1, 0xA2FC },
- { 0xA3A1, 0xA3FE },
- { 0xA4A1, 0xA4F3 },
- { 0xA5A1, 0xA5F6 },
- { 0xA6A1, 0xA6B8 },
- { 0xA6C1, 0xA6D8 },
- { 0xA6E0, 0xA6EB },
- { 0xA6EE, 0xA6F2 },
- { 0xA6F4, 0xA6F5 },
- { 0xA7A1, 0xA7C1 },
- { 0xA7D1, 0xA7F1 },
- { 0xA840, 0xA87E },
- { 0xA880, 0xA895 },
- { 0xA8A1, 0xA8BB },
- { 0xA8BD, 0xA8BE },
- { 0xA8C0, 0xA8C0 },
- { 0xA8C5, 0xA8E9 },
- { 0xA940, 0xA957 },
- { 0xA959, 0xA95A },
- { 0xA95C, 0xA95C },
- { 0xA960, 0xA97E },
- { 0xA980, 0xA988 },
- { 0xA996, 0xA996 },
- { 0xA9A4, 0xA9EF },
- { 0xAA40, 0xAA7E },
- { 0xAA80, 0xAAA0 },
- { 0xAB40, 0xAB7E },
- { 0xAB80, 0xABA0 },
- { 0xAC40, 0xAC7E },
- { 0xAC80, 0xACA0 },
- { 0xAD40, 0xAD7E },
- { 0xAD80, 0xADA0 },
- { 0xAE40, 0xAE7E },
- { 0xAE80, 0xAEA0 },
- { 0xAF40, 0xAF7E },
- { 0xAF80, 0xAFA0 },
- { 0xB040, 0xB07E },
- { 0xB080, 0xB0FE },
- { 0xB140, 0xB17E },
- { 0xB180, 0xB1FE },
- { 0xB240, 0xB27E },
- { 0xB280, 0xB2FE },
- { 0xB340, 0xB37E },
- { 0xB380, 0xB3FE },
- { 0xB440, 0xB47E },
- { 0xB480, 0xB4FE },
- { 0xB540, 0xB57E },
- { 0xB580, 0xB5FE },
- { 0xB640, 0xB67E },
- { 0xB680, 0xB6FE },
- { 0xB740, 0xB77E },
- { 0xB780, 0xB7FE },
- { 0xB840, 0xB87E },
- { 0xB880, 0xB8FE },
- { 0xB940, 0xB97E },
- { 0xB980, 0xB9FE },
- { 0xBA40, 0xBA7E },
- { 0xBA80, 0xBAFE },
- { 0xBB40, 0xBB7E },
- { 0xBB80, 0xBBFE },
- { 0xBC40, 0xBC7E },
- { 0xBC80, 0xBCFE },
- { 0xBD40, 0xBD7E },
- { 0xBD80, 0xBDFE },
- { 0xBE40, 0xBE7E },
- { 0xBE80, 0xBEFE },
- { 0xBF40, 0xBF7E },
- { 0xBF80, 0xBFFE },
- { 0xC040, 0xC07E },
- { 0xC080, 0xC0FE },
- { 0xC140, 0xC17E },
- { 0xC180, 0xC1FE },
- { 0xC240, 0xC27E },
- { 0xC280, 0xC2FE },
- { 0xC340, 0xC37E },
- { 0xC380, 0xC3FE },
- { 0xC440, 0xC47E },
- { 0xC480, 0xC4FE },
- { 0xC540, 0xC57E },
- { 0xC580, 0xC5FE },
- { 0xC640, 0xC67E },
- { 0xC680, 0xC6FE },
- { 0xC740, 0xC77E },
- { 0xC780, 0xC7FE },
- { 0xC840, 0xC87E },
- { 0xC880, 0xC8FE },
- { 0xC940, 0xC97E },
- { 0xC980, 0xC9FE },
- { 0xCA40, 0xCA7E },
- { 0xCA80, 0xCAFE },
- { 0xCB40, 0xCB7E },
- { 0xCB80, 0xCBFE },
- { 0xCC40, 0xCC7E },
- { 0xCC80, 0xCCFE },
- { 0xCD40, 0xCD7E },
- { 0xCD80, 0xCDFE },
- { 0xCE40, 0xCE7E },
- { 0xCE80, 0xCEFE },
- { 0xCF40, 0xCF7E },
- { 0xCF80, 0xCFFE },
- { 0xD040, 0xD07E },
- { 0xD080, 0xD0FE },
- { 0xD140, 0xD17E },
- { 0xD180, 0xD1FE },
- { 0xD240, 0xD27E },
- { 0xD280, 0xD2FE },
- { 0xD340, 0xD37E },
- { 0xD380, 0xD3FE },
- { 0xD440, 0xD47E },
- { 0xD480, 0xD4FE },
- { 0xD540, 0xD57E },
- { 0xD580, 0xD5FE },
- { 0xD640, 0xD67E },
- { 0xD680, 0xD6FE },
- { 0xD740, 0xD77E },
- { 0xD780, 0xD7F9 },
- { 0xD840, 0xD87E },
- { 0xD880, 0xD8FE },
- { 0xD940, 0xD97E },
- { 0xD980, 0xD9FE },
- { 0xDA40, 0xDA7E },
- { 0xDA80, 0xDAFE },
- { 0xDB40, 0xDB7E },
- { 0xDB80, 0xDBFE },
- { 0xDC40, 0xDC7E },
- { 0xDC80, 0xDCFE },
- { 0xDD40, 0xDD7E },
- { 0xDD80, 0xDDFE },
- { 0xDE40, 0xDE7E },
- { 0xDE80, 0xDEFE },
- { 0xDF40, 0xDF7E },
- { 0xDF80, 0xDFFE },
- { 0xE040, 0xE07E },
- { 0xE080, 0xE0FE },
- { 0xE140, 0xE17E },
- { 0xE180, 0xE1FE },
- { 0xE240, 0xE27E },
- { 0xE280, 0xE2FE },
- { 0xE340, 0xE37E },
- { 0xE380, 0xE3FE },
- { 0xE440, 0xE47E },
- { 0xE480, 0xE4FE },
- { 0xE540, 0xE57E },
- { 0xE580, 0xE5FE },
- { 0xE640, 0xE67E },
- { 0xE680, 0xE6FE },
- { 0xE740, 0xE77E },
- { 0xE780, 0xE7FE },
- { 0xE840, 0xE87E },
- { 0xE880, 0xE8FE },
- { 0xE940, 0xE97E },
- { 0xE980, 0xE9FE },
- { 0xEA40, 0xEA7E },
- { 0xEA80, 0xEAFE },
- { 0xEB40, 0xEB7E },
- { 0xEB80, 0xEBFE },
- { 0xEC40, 0xEC7E },
- { 0xEC80, 0xECFE },
- { 0xED40, 0xED7E },
- { 0xED80, 0xEDFE },
- { 0xEE40, 0xEE7E },
- { 0xEE80, 0xEEFE },
- { 0xEF40, 0xEF7E },
- { 0xEF80, 0xEFFE },
- { 0xF040, 0xF07E },
- { 0xF080, 0xF0FE },
- { 0xF140, 0xF17E },
- { 0xF180, 0xF1FE },
- { 0xF240, 0xF27E },
- { 0xF280, 0xF2FE },
- { 0xF340, 0xF37E },
- { 0xF380, 0xF3FE },
- { 0xF440, 0xF47E },
- { 0xF480, 0xF4FE },
- { 0xF540, 0xF57E },
- { 0xF580, 0xF5FE },
- { 0xF640, 0xF67E },
- { 0xF680, 0xF6FE },
- { 0xF740, 0xF77E },
- { 0xF780, 0xF7FE },
- { 0xF840, 0xF87E },
- { 0xF880, 0xF8A0 },
- { 0xF940, 0xF97E },
- { 0xF980, 0xF9A0 },
- { 0xFA40, 0xFA7E },
- { 0xFA80, 0xFAA0 },
- { 0xFB40, 0xFB7E },
- { 0xFB80, 0xFBA0 },
- { 0xFC40, 0xFC7E },
- { 0xFC80, 0xFCA0 },
- { 0xFD40, 0xFD7E },
- { 0xFD80, 0xFDA0 },
- { 0xFE40, 0xFE4F },
-};
-
-// generated from http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP949.TXT
-static const CharRange kEUCKRRanges[] = {
- { 0x8141, 0x815A },
- { 0x8161, 0x817A },
- { 0x8181, 0x81FE },
- { 0x8241, 0x825A },
- { 0x8261, 0x827A },
- { 0x8281, 0x82FE },
- { 0x8341, 0x835A },
- { 0x8361, 0x837A },
- { 0x8381, 0x83FE },
- { 0x8441, 0x845A },
- { 0x8461, 0x847A },
- { 0x8481, 0x84FE },
- { 0x8541, 0x855A },
- { 0x8561, 0x857A },
- { 0x8581, 0x85FE },
- { 0x8641, 0x865A },
- { 0x8661, 0x867A },
- { 0x8681, 0x86FE },
- { 0x8741, 0x875A },
- { 0x8761, 0x877A },
- { 0x8781, 0x87FE },
- { 0x8841, 0x885A },
- { 0x8861, 0x887A },
- { 0x8881, 0x88FE },
- { 0x8941, 0x895A },
- { 0x8961, 0x897A },
- { 0x8981, 0x89FE },
- { 0x8A41, 0x8A5A },
- { 0x8A61, 0x8A7A },
- { 0x8A81, 0x8AFE },
- { 0x8B41, 0x8B5A },
- { 0x8B61, 0x8B7A },
- { 0x8B81, 0x8BFE },
- { 0x8C41, 0x8C5A },
- { 0x8C61, 0x8C7A },
- { 0x8C81, 0x8CFE },
- { 0x8D41, 0x8D5A },
- { 0x8D61, 0x8D7A },
- { 0x8D81, 0x8DFE },
- { 0x8E41, 0x8E5A },
- { 0x8E61, 0x8E7A },
- { 0x8E81, 0x8EFE },
- { 0x8F41, 0x8F5A },
- { 0x8F61, 0x8F7A },
- { 0x8F81, 0x8FFE },
- { 0x9041, 0x905A },
- { 0x9061, 0x907A },
- { 0x9081, 0x90FE },
- { 0x9141, 0x915A },
- { 0x9161, 0x917A },
- { 0x9181, 0x91FE },
- { 0x9241, 0x925A },
- { 0x9261, 0x927A },
- { 0x9281, 0x92FE },
- { 0x9341, 0x935A },
- { 0x9361, 0x937A },
- { 0x9381, 0x93FE },
- { 0x9441, 0x945A },
- { 0x9461, 0x947A },
- { 0x9481, 0x94FE },
- { 0x9541, 0x955A },
- { 0x9561, 0x957A },
- { 0x9581, 0x95FE },
- { 0x9641, 0x965A },
- { 0x9661, 0x967A },
- { 0x9681, 0x96FE },
- { 0x9741, 0x975A },
- { 0x9761, 0x977A },
- { 0x9781, 0x97FE },
- { 0x9841, 0x985A },
- { 0x9861, 0x987A },
- { 0x9881, 0x98FE },
- { 0x9941, 0x995A },
- { 0x9961, 0x997A },
- { 0x9981, 0x99FE },
- { 0x9A41, 0x9A5A },
- { 0x9A61, 0x9A7A },
- { 0x9A81, 0x9AFE },
- { 0x9B41, 0x9B5A },
- { 0x9B61, 0x9B7A },
- { 0x9B81, 0x9BFE },
- { 0x9C41, 0x9C5A },
- { 0x9C61, 0x9C7A },
- { 0x9C81, 0x9CFE },
- { 0x9D41, 0x9D5A },
- { 0x9D61, 0x9D7A },
- { 0x9D81, 0x9DFE },
- { 0x9E41, 0x9E5A },
- { 0x9E61, 0x9E7A },
- { 0x9E81, 0x9EFE },
- { 0x9F41, 0x9F5A },
- { 0x9F61, 0x9F7A },
- { 0x9F81, 0x9FFE },
- { 0xA041, 0xA05A },
- { 0xA061, 0xA07A },
- { 0xA081, 0xA0FE },
- { 0xA141, 0xA15A },
- { 0xA161, 0xA17A },
- { 0xA181, 0xA1FE },
- { 0xA241, 0xA25A },
- { 0xA261, 0xA27A },
- { 0xA281, 0xA2E7 },
- { 0xA341, 0xA35A },
- { 0xA361, 0xA37A },
- { 0xA381, 0xA3FE },
- { 0xA441, 0xA45A },
- { 0xA461, 0xA47A },
- { 0xA481, 0xA4FE },
- { 0xA541, 0xA55A },
- { 0xA561, 0xA57A },
- { 0xA581, 0xA5AA },
- { 0xA5B0, 0xA5B9 },
- { 0xA5C1, 0xA5D8 },
- { 0xA5E1, 0xA5F8 },
- { 0xA641, 0xA65A },
- { 0xA661, 0xA67A },
- { 0xA681, 0xA6E4 },
- { 0xA741, 0xA75A },
- { 0xA761, 0xA77A },
- { 0xA781, 0xA7EF },
- { 0xA841, 0xA85A },
- { 0xA861, 0xA87A },
- { 0xA881, 0xA8A4 },
- { 0xA8A6, 0xA8A6 },
- { 0xA8A8, 0xA8AF },
- { 0xA8B1, 0xA8FE },
- { 0xA941, 0xA95A },
- { 0xA961, 0xA97A },
- { 0xA981, 0xA9FE },
- { 0xAA41, 0xAA5A },
- { 0xAA61, 0xAA7A },
- { 0xAA81, 0xAAF3 },
- { 0xAB41, 0xAB5A },
- { 0xAB61, 0xAB7A },
- { 0xAB81, 0xABF6 },
- { 0xAC41, 0xAC5A },
- { 0xAC61, 0xAC7A },
- { 0xAC81, 0xACC1 },
- { 0xACD1, 0xACF1 },
- { 0xAD41, 0xAD5A },
- { 0xAD61, 0xAD7A },
- { 0xAD81, 0xADA0 },
- { 0xAE41, 0xAE5A },
- { 0xAE61, 0xAE7A },
- { 0xAE81, 0xAEA0 },
- { 0xAF41, 0xAF5A },
- { 0xAF61, 0xAF7A },
- { 0xAF81, 0xAFA0 },
- { 0xB041, 0xB05A },
- { 0xB061, 0xB07A },
- { 0xB081, 0xB0FE },
- { 0xB141, 0xB15A },
- { 0xB161, 0xB17A },
- { 0xB181, 0xB1FE },
- { 0xB241, 0xB25A },
- { 0xB261, 0xB27A },
- { 0xB281, 0xB2FE },
- { 0xB341, 0xB35A },
- { 0xB361, 0xB37A },
- { 0xB381, 0xB3FE },
- { 0xB441, 0xB45A },
- { 0xB461, 0xB47A },
- { 0xB481, 0xB4FE },
- { 0xB541, 0xB55A },
- { 0xB561, 0xB57A },
- { 0xB581, 0xB5FE },
- { 0xB641, 0xB65A },
- { 0xB661, 0xB67A },
- { 0xB681, 0xB6FE },
- { 0xB741, 0xB75A },
- { 0xB761, 0xB77A },
- { 0xB781, 0xB7FE },
- { 0xB841, 0xB85A },
- { 0xB861, 0xB87A },
- { 0xB881, 0xB8FE },
- { 0xB941, 0xB95A },
- { 0xB961, 0xB97A },
- { 0xB981, 0xB9FE },
- { 0xBA41, 0xBA5A },
- { 0xBA61, 0xBA7A },
- { 0xBA81, 0xBAFE },
- { 0xBB41, 0xBB5A },
- { 0xBB61, 0xBB7A },
- { 0xBB81, 0xBBFE },
- { 0xBC41, 0xBC5A },
- { 0xBC61, 0xBC7A },
- { 0xBC81, 0xBCFE },
- { 0xBD41, 0xBD5A },
- { 0xBD61, 0xBD7A },
- { 0xBD81, 0xBDFE },
- { 0xBE41, 0xBE5A },
- { 0xBE61, 0xBE7A },
- { 0xBE81, 0xBEFE },
- { 0xBF41, 0xBF5A },
- { 0xBF61, 0xBF7A },
- { 0xBF81, 0xBFFE },
- { 0xC041, 0xC05A },
- { 0xC061, 0xC07A },
- { 0xC081, 0xC0FE },
- { 0xC141, 0xC15A },
- { 0xC161, 0xC17A },
- { 0xC181, 0xC1FE },
- { 0xC241, 0xC25A },
- { 0xC261, 0xC27A },
- { 0xC281, 0xC2FE },
- { 0xC341, 0xC35A },
- { 0xC361, 0xC37A },
- { 0xC381, 0xC3FE },
- { 0xC441, 0xC45A },
- { 0xC461, 0xC47A },
- { 0xC481, 0xC4FE },
- { 0xC541, 0xC55A },
- { 0xC561, 0xC57A },
- { 0xC581, 0xC5FE },
- { 0xC641, 0xC652 },
- { 0xC6A1, 0xC6FE },
- { 0xC7A1, 0xC7FE },
- { 0xC8A1, 0xC8FE },
- { 0xCAA1, 0xCAFE },
- { 0xCBA1, 0xCBFE },
- { 0xCCA1, 0xCCFE },
- { 0xCDA1, 0xCDFE },
- { 0xCEA1, 0xCEFE },
- { 0xCFA1, 0xCFFE },
- { 0xD0A1, 0xD0FE },
- { 0xD1A1, 0xD1FE },
- { 0xD2A1, 0xD2FE },
- { 0xD3A1, 0xD3FE },
- { 0xD4A1, 0xD4FE },
- { 0xD5A1, 0xD5FE },
- { 0xD6A1, 0xD6FE },
- { 0xD7A1, 0xD7FE },
- { 0xD8A1, 0xD8FE },
- { 0xD9A1, 0xD9FE },
- { 0xDAA1, 0xDAFE },
- { 0xDBA1, 0xDBFE },
- { 0xDCA1, 0xDCFE },
- { 0xDDA1, 0xDDFE },
- { 0xDEA1, 0xDEFE },
- { 0xDFA1, 0xDFFE },
- { 0xE0A1, 0xE0FE },
- { 0xE1A1, 0xE1FE },
- { 0xE2A1, 0xE2FE },
- { 0xE3A1, 0xE3FE },
- { 0xE4A1, 0xE4FE },
- { 0xE5A1, 0xE5FE },
- { 0xE6A1, 0xE6FE },
- { 0xE7A1, 0xE7FE },
- { 0xE8A1, 0xE8FE },
- { 0xE9A1, 0xE9FE },
- { 0xEAA1, 0xEAFE },
- { 0xEBA1, 0xEBFE },
- { 0xECA1, 0xECFE },
- { 0xEDA1, 0xEDFE },
- { 0xEEA1, 0xEEFE },
- { 0xEFA1, 0xEFFE },
- { 0xF0A1, 0xF0FE },
- { 0xF1A1, 0xF1FE },
- { 0xF2A1, 0xF2FE },
- { 0xF3A1, 0xF3FE },
- { 0xF4A1, 0xF4FE },
- { 0xF5A1, 0xF5FE },
- { 0xF6A1, 0xF6FE },
- { 0xF7A1, 0xF7FE },
- { 0xF8A1, 0xF8FE },
- { 0xF9A1, 0xF9FE },
- { 0xFAA1, 0xFAFE },
- { 0xFBA1, 0xFBFE },
- { 0xFCA1, 0xFCFE },
- { 0xFDA1, 0xFDFE },
-};
-
-// generated from http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP950.TXT
-static const CharRange kBig5Ranges[] = {
- { 0xA140, 0xA17E },
- { 0xA1A1, 0xA1FE },
- { 0xA240, 0xA27E },
- { 0xA2A1, 0xA2FE },
- { 0xA340, 0xA37E },
- { 0xA3A1, 0xA3BF },
- { 0xA3E1, 0xA3E1 },
- { 0xA440, 0xA47E },
- { 0xA4A1, 0xA4FE },
- { 0xA540, 0xA57E },
- { 0xA5A1, 0xA5FE },
- { 0xA640, 0xA67E },
- { 0xA6A1, 0xA6FE },
- { 0xA740, 0xA77E },
- { 0xA7A1, 0xA7FE },
- { 0xA840, 0xA87E },
- { 0xA8A1, 0xA8FE },
- { 0xA940, 0xA97E },
- { 0xA9A1, 0xA9FE },
- { 0xAA40, 0xAA7E },
- { 0xAAA1, 0xAAFE },
- { 0xAB40, 0xAB7E },
- { 0xABA1, 0xABFE },
- { 0xAC40, 0xAC7E },
- { 0xACA1, 0xACFE },
- { 0xAD40, 0xAD7E },
- { 0xADA1, 0xADFE },
- { 0xAE40, 0xAE7E },
- { 0xAEA1, 0xAEFE },
- { 0xAF40, 0xAF7E },
- { 0xAFA1, 0xAFFE },
- { 0xB040, 0xB07E },
- { 0xB0A1, 0xB0FE },
- { 0xB140, 0xB17E },
- { 0xB1A1, 0xB1FE },
- { 0xB240, 0xB27E },
- { 0xB2A1, 0xB2FE },
- { 0xB340, 0xB37E },
- { 0xB3A1, 0xB3FE },
- { 0xB440, 0xB47E },
- { 0xB4A1, 0xB4FE },
- { 0xB540, 0xB57E },
- { 0xB5A1, 0xB5FE },
- { 0xB640, 0xB67E },
- { 0xB6A1, 0xB6FE },
- { 0xB740, 0xB77E },
- { 0xB7A1, 0xB7FE },
- { 0xB840, 0xB87E },
- { 0xB8A1, 0xB8FE },
- { 0xB940, 0xB97E },
- { 0xB9A1, 0xB9FE },
- { 0xBA40, 0xBA7E },
- { 0xBAA1, 0xBAFE },
- { 0xBB40, 0xBB7E },
- { 0xBBA1, 0xBBFE },
- { 0xBC40, 0xBC7E },
- { 0xBCA1, 0xBCFE },
- { 0xBD40, 0xBD7E },
- { 0xBDA1, 0xBDFE },
- { 0xBE40, 0xBE7E },
- { 0xBEA1, 0xBEFE },
- { 0xBF40, 0xBF7E },
- { 0xBFA1, 0xBFFE },
- { 0xC040, 0xC07E },
- { 0xC0A1, 0xC0FE },
- { 0xC140, 0xC17E },
- { 0xC1A1, 0xC1FE },
- { 0xC240, 0xC27E },
- { 0xC2A1, 0xC2FE },
- { 0xC340, 0xC37E },
- { 0xC3A1, 0xC3FE },
- { 0xC440, 0xC47E },
- { 0xC4A1, 0xC4FE },
- { 0xC540, 0xC57E },
- { 0xC5A1, 0xC5FE },
- { 0xC640, 0xC67E },
- { 0xC940, 0xC97E },
- { 0xC9A1, 0xC9FE },
- { 0xCA40, 0xCA7E },
- { 0xCAA1, 0xCAFE },
- { 0xCB40, 0xCB7E },
- { 0xCBA1, 0xCBFE },
- { 0xCC40, 0xCC7E },
- { 0xCCA1, 0xCCFE },
- { 0xCD40, 0xCD7E },
- { 0xCDA1, 0xCDFE },
- { 0xCE40, 0xCE7E },
- { 0xCEA1, 0xCEFE },
- { 0xCF40, 0xCF7E },
- { 0xCFA1, 0xCFFE },
- { 0xD040, 0xD07E },
- { 0xD0A1, 0xD0FE },
- { 0xD140, 0xD17E },
- { 0xD1A1, 0xD1FE },
- { 0xD240, 0xD27E },
- { 0xD2A1, 0xD2FE },
- { 0xD340, 0xD37E },
- { 0xD3A1, 0xD3FE },
- { 0xD440, 0xD47E },
- { 0xD4A1, 0xD4FE },
- { 0xD540, 0xD57E },
- { 0xD5A1, 0xD5FE },
- { 0xD640, 0xD67E },
- { 0xD6A1, 0xD6FE },
- { 0xD740, 0xD77E },
- { 0xD7A1, 0xD7FE },
- { 0xD840, 0xD87E },
- { 0xD8A1, 0xD8FE },
- { 0xD940, 0xD97E },
- { 0xD9A1, 0xD9FE },
- { 0xDA40, 0xDA7E },
- { 0xDAA1, 0xDAFE },
- { 0xDB40, 0xDB7E },
- { 0xDBA1, 0xDBFE },
- { 0xDC40, 0xDC7E },
- { 0xDCA1, 0xDCFE },
- { 0xDD40, 0xDD7E },
- { 0xDDA1, 0xDDFE },
- { 0xDE40, 0xDE7E },
- { 0xDEA1, 0xDEFE },
- { 0xDF40, 0xDF7E },
- { 0xDFA1, 0xDFFE },
- { 0xE040, 0xE07E },
- { 0xE0A1, 0xE0FE },
- { 0xE140, 0xE17E },
- { 0xE1A1, 0xE1FE },
- { 0xE240, 0xE27E },
- { 0xE2A1, 0xE2FE },
- { 0xE340, 0xE37E },
- { 0xE3A1, 0xE3FE },
- { 0xE440, 0xE47E },
- { 0xE4A1, 0xE4FE },
- { 0xE540, 0xE57E },
- { 0xE5A1, 0xE5FE },
- { 0xE640, 0xE67E },
- { 0xE6A1, 0xE6FE },
- { 0xE740, 0xE77E },
- { 0xE7A1, 0xE7FE },
- { 0xE840, 0xE87E },
- { 0xE8A1, 0xE8FE },
- { 0xE940, 0xE97E },
- { 0xE9A1, 0xE9FE },
- { 0xEA40, 0xEA7E },
- { 0xEAA1, 0xEAFE },
- { 0xEB40, 0xEB7E },
- { 0xEBA1, 0xEBFE },
- { 0xEC40, 0xEC7E },
- { 0xECA1, 0xECFE },
- { 0xED40, 0xED7E },
- { 0xEDA1, 0xEDFE },
- { 0xEE40, 0xEE7E },
- { 0xEEA1, 0xEEFE },
- { 0xEF40, 0xEF7E },
- { 0xEFA1, 0xEFFE },
- { 0xF040, 0xF07E },
- { 0xF0A1, 0xF0FE },
- { 0xF140, 0xF17E },
- { 0xF1A1, 0xF1FE },
- { 0xF240, 0xF27E },
- { 0xF2A1, 0xF2FE },
- { 0xF340, 0xF37E },
- { 0xF3A1, 0xF3FE },
- { 0xF440, 0xF47E },
- { 0xF4A1, 0xF4FE },
- { 0xF540, 0xF57E },
- { 0xF5A1, 0xF5FE },
- { 0xF640, 0xF67E },
- { 0xF6A1, 0xF6FE },
- { 0xF740, 0xF77E },
- { 0xF7A1, 0xF7FE },
- { 0xF840, 0xF87E },
- { 0xF8A1, 0xF8FE },
- { 0xF940, 0xF97E },
- { 0xF9A1, 0xF9FE },
-};
-
-static bool charMatchesEncoding(int ch, const CharRange* encodingRanges, int rangeCount) {
- // Use binary search to see if the character is contained in the encoding
- int low = 0;
- int high = rangeCount;
-
- while (low < high) {
- int i = (low + high) / 2;
- const CharRange* range = &encodingRanges[i];
- if (ch >= range->first && ch <= range->last)
- return true;
- if (ch > range->last)
- low = i + 1;
- else
- high = i;
- }
-
- return false;
-}
-
-extern uint32_t findPossibleEncodings(int ch)
-{
- // ASCII matches everything
- if (ch < 256) return kEncodingAll;
-
- int result = kEncodingNone;
-
- if (charMatchesEncoding(ch, kShiftJISRanges, ARRAY_SIZE(kShiftJISRanges)))
- result |= kEncodingShiftJIS;
- if (charMatchesEncoding(ch, kGBKRanges, ARRAY_SIZE(kGBKRanges)))
- result |= kEncodingGBK;
- if (charMatchesEncoding(ch, kBig5Ranges, ARRAY_SIZE(kBig5Ranges)))
- result |= kEncodingBig5;
- if (charMatchesEncoding(ch, kEUCKRRanges, ARRAY_SIZE(kEUCKRRanges)))
- result |= kEncodingEUCKR;
-
- return result;
-}
diff --git a/media/libmedia/autodetect.h b/media/libmedia/autodetect.h
deleted file mode 100644
index 9675db3..0000000
--- a/media/libmedia/autodetect.h
+++ /dev/null
@@ -1,37 +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 AUTODETECT_H
-#define AUTODETECT_H
-
-#include <inttypes.h>
-
-// flags used for native encoding detection
-enum {
- kEncodingNone = 0,
- kEncodingShiftJIS = (1 << 0),
- kEncodingGBK = (1 << 1),
- kEncodingBig5 = (1 << 2),
- kEncodingEUCKR = (1 << 3),
-
- kEncodingAll = (kEncodingShiftJIS | kEncodingGBK | kEncodingBig5 | kEncodingEUCKR),
-};
-
-
-// returns a bitfield containing the possible native encodings for the given character
-extern uint32_t findPossibleEncodings(int ch);
-
-#endif // AUTODETECT_H
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index 110b94c..1d6bb6f 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -21,6 +21,7 @@
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <media/mediametadataretriever.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <utils/Log.h>
#include <dlfcn.h>
@@ -93,7 +94,9 @@
}
status_t MediaMetadataRetriever::setDataSource(
- const char *srcUrl, const KeyedVector<String8, String8> *headers)
+ const sp<IMediaHTTPService> &httpService,
+ const char *srcUrl,
+ const KeyedVector<String8, String8> *headers)
{
ALOGV("setDataSource");
Mutex::Autolock _l(mLock);
@@ -106,7 +109,7 @@
return UNKNOWN_ERROR;
}
ALOGV("data source (%s)", srcUrl);
- return mRetriever->setDataSource(srcUrl, headers);
+ return mRetriever->setDataSource(httpService, srcUrl, headers);
}
status_t MediaMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
@@ -157,7 +160,7 @@
return mRetriever->extractAlbumArt();
}
-void MediaMetadataRetriever::DeathNotifier::binderDied(const wp<IBinder>& who) {
+void MediaMetadataRetriever::DeathNotifier::binderDied(const wp<IBinder>& who __unused) {
Mutex::Autolock lock(MediaMetadataRetriever::sServiceLock);
MediaMetadataRetriever::sService.clear();
ALOGW("MediaMetadataRetriever server died!");
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 0f6d897..d94c7c5 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -58,7 +58,7 @@
mVideoWidth = mVideoHeight = 0;
mLockThreadId = 0;
mAudioSessionId = AudioSystem::newAudioSessionId();
- AudioSystem::acquireAudioSessionId(mAudioSessionId);
+ AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
mSendLevel = 0;
mRetransmitEndpointValid = false;
}
@@ -66,7 +66,7 @@
MediaPlayer::~MediaPlayer()
{
ALOGV("destructor");
- AudioSystem::releaseAudioSessionId(mAudioSessionId);
+ AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
disconnect();
IPCThreadState::self()->flushCommands();
}
@@ -136,6 +136,7 @@
}
status_t MediaPlayer::setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url, const KeyedVector<String8, String8> *headers)
{
ALOGV("setDataSource(%s)", url);
@@ -145,7 +146,7 @@
if (service != 0) {
sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
- (NO_ERROR != player->setDataSource(url, headers))) {
+ (NO_ERROR != player->setDataSource(httpService, url, headers))) {
player.clear();
}
err = attachNewPlayer(player);
@@ -575,8 +576,8 @@
return BAD_VALUE;
}
if (sessionId != mAudioSessionId) {
- AudioSystem::acquireAudioSessionId(sessionId);
- AudioSystem::releaseAudioSessionId(mAudioSessionId);
+ AudioSystem::acquireAudioSessionId(sessionId, -1);
+ AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
mAudioSessionId = sessionId;
}
return NO_ERROR;
@@ -654,7 +655,7 @@
return BAD_VALUE;
}
- memset(&mRetransmitEndpoint, 0, sizeof(&mRetransmitEndpoint));
+ memset(&mRetransmitEndpoint, 0, sizeof(mRetransmitEndpoint));
mRetransmitEndpoint.sin_family = AF_INET;
mRetransmitEndpoint.sin_addr = saddr;
mRetransmitEndpoint.sin_port = htons(port);
@@ -776,15 +777,20 @@
}
}
-/*static*/ status_t MediaPlayer::decode(const char* url, uint32_t *pSampleRate,
- int* pNumChannels, audio_format_t* pFormat,
- const sp<IMemoryHeap>& heap, size_t *pSize)
+/*static*/ status_t MediaPlayer::decode(
+ const sp<IMediaHTTPService> &httpService,
+ const char* url,
+ uint32_t *pSampleRate,
+ int* pNumChannels,
+ audio_format_t* pFormat,
+ const sp<IMemoryHeap>& heap,
+ size_t *pSize)
{
ALOGV("decode(%s)", url);
status_t status;
const sp<IMediaPlayerService>& service = getMediaPlayerService();
if (service != 0) {
- status = service->decode(url, pSampleRate, pNumChannels, pFormat, heap, pSize);
+ status = service->decode(httpService, url, pSampleRate, pNumChannels, pFormat, heap, pSize);
} else {
ALOGE("Unable to locate media service");
status = DEAD_OBJECT;
@@ -832,15 +838,4 @@
return mPlayer->setNextPlayer(next == NULL ? NULL : next->mPlayer);
}
-status_t MediaPlayer::updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
- const sp<IMediaPlayerService>& service = getMediaPlayerService();
-
- if (service != NULL) {
- return service->updateProxyConfig(host, port, exclusionList);
- }
-
- return INVALID_OPERATION;
-}
-
}; // namespace android
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 8f21632..4189a5e 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -45,7 +45,6 @@
libstagefright_rtsp \
LOCAL_C_INCLUDES := \
- $(call include-path-for, graphics corecg) \
$(TOP)/frameworks/av/media/libstagefright/include \
$(TOP)/frameworks/av/media/libstagefright/rtsp \
$(TOP)/frameworks/av/media/libstagefright/wifi-display \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 9553458..200c561 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -44,6 +44,7 @@
#include <utils/SystemClock.h>
#include <utils/Vector.h>
+#include <media/IMediaHTTPService.h>
#include <media/IRemoteDisplay.h>
#include <media/IRemoteDisplayClient.h>
#include <media/MediaPlayerInterface.h>
@@ -306,11 +307,6 @@
return new RemoteDisplay(client, iface.string());
}
-status_t MediaPlayerService::updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
- return HTTPBase::UpdateProxyConfig(host, port, exclusionList);
-}
-
status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
{
const size_t SIZE = 256;
@@ -319,7 +315,7 @@
result.append(" AudioCache\n");
if (mHeap != 0) {
- snprintf(buffer, 255, " heap base(%p), size(%d), flags(%d)\n",
+ snprintf(buffer, 255, " heap base(%p), size(%zu), flags(%d)\n",
mHeap->getBase(), mHeap->getSize(), mHeap->getFlags());
result.append(buffer);
}
@@ -590,7 +586,8 @@
}
if (!p->hardwareOutput()) {
- mAudioOutput = new AudioOutput(mAudioSessionId);
+ mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
+ mPid);
static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
}
@@ -622,7 +619,9 @@
}
status_t MediaPlayerService::Client::setDataSource(
- const char *url, const KeyedVector<String8, String8> *headers)
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers)
{
ALOGV("setDataSource(%s)", url);
if (url == NULL)
@@ -657,7 +656,7 @@
return NO_INIT;
}
- setDataSource_post(p, p->setDataSource(url, headers));
+ setDataSource_post(p, p->setDataSource(httpService, url, headers));
return mStatus;
}
}
@@ -1176,9 +1175,14 @@
}
#endif
-status_t MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels,
- audio_format_t* pFormat,
- const sp<IMemoryHeap>& heap, size_t *pSize)
+status_t MediaPlayerService::decode(
+ const sp<IMediaHTTPService> &httpService,
+ const char* url,
+ uint32_t *pSampleRate,
+ int* pNumChannels,
+ audio_format_t* pFormat,
+ const sp<IMemoryHeap>& heap,
+ size_t *pSize)
{
ALOGV("decode(%s)", url);
sp<MediaPlayerBase> player;
@@ -1206,7 +1210,7 @@
static_cast<MediaPlayerInterface*>(player.get())->setAudioSink(cache);
// set data source
- if (player->setDataSource(url) != NO_ERROR) goto Exit;
+ if (player->setDataSource(httpService, url) != NO_ERROR) goto Exit;
ALOGV("prepare");
player->prepareAsync();
@@ -1296,12 +1300,14 @@
#undef LOG_TAG
#define LOG_TAG "AudioSink"
-MediaPlayerService::AudioOutput::AudioOutput(int sessionId)
+MediaPlayerService::AudioOutput::AudioOutput(int sessionId, int uid, int pid)
: mCallback(NULL),
mCallbackCookie(NULL),
mCallbackData(NULL),
mBytesWritten(0),
mSessionId(sessionId),
+ mUid(uid),
+ mPid(pid),
mFlags(AUDIO_OUTPUT_FLAG_NONE) {
ALOGV("AudioOutput(%d)", sessionId);
mStreamType = AUDIO_STREAM_MUSIC;
@@ -1449,7 +1455,7 @@
format, bufferCount, mSessionId, flags);
uint32_t afSampleRate;
size_t afFrameCount;
- uint32_t frameCount;
+ size_t frameCount;
// offloading is only supported in callback mode for now.
// offloadInfo must be present if offload flag is set
@@ -1549,7 +1555,9 @@
0, // notification frames
mSessionId,
AudioTrack::TRANSFER_CALLBACK,
- offloadInfo);
+ offloadInfo,
+ mUid,
+ mPid);
} else {
t = new AudioTrack(
mStreamType,
@@ -1558,10 +1566,14 @@
channelMask,
frameCount,
flags,
- NULL,
- NULL,
- 0,
- mSessionId);
+ NULL, // callback
+ NULL, // user data
+ 0, // notification frames
+ mSessionId,
+ AudioTrack::TRANSFER_DEFAULT,
+ NULL, // offload info
+ mUid,
+ mPid);
}
if ((t == 0) || (t->initCheck() != NO_ERROR)) {
@@ -1808,6 +1820,12 @@
return mSessionId;
}
+uint32_t MediaPlayerService::AudioOutput::getSampleRate() const
+{
+ if (mTrack == 0) return 0;
+ return mTrack->getSampleRate();
+}
+
#undef LOG_TAG
#define LOG_TAG "AudioCache"
MediaPlayerService::AudioCache::AudioCache(const sp<IMemoryHeap>& heap) :
@@ -2010,6 +2028,14 @@
return 0;
}
+uint32_t MediaPlayerService::AudioCache::getSampleRate() const
+{
+ if (mMsecsPerFrame == 0) {
+ return 0;
+ }
+ return (uint32_t)(1.e3 / mMsecsPerFrame);
+}
+
void MediaPlayerService::addBatteryData(uint32_t params)
{
Mutex::Autolock lock(mLock);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 21f4117..448f27a 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -72,7 +72,7 @@
class CallbackData;
public:
- AudioOutput(int sessionId);
+ AudioOutput(int sessionId, int uid, int pid);
virtual ~AudioOutput();
virtual bool ready() const { return mTrack != 0; }
@@ -86,6 +86,7 @@
virtual status_t getPosition(uint32_t *position) const;
virtual status_t getFramesWritten(uint32_t *frameswritten) const;
virtual int getSessionId() const;
+ virtual uint32_t getSampleRate() const;
virtual status_t open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
@@ -100,7 +101,10 @@
virtual void flush();
virtual void pause();
virtual void close();
- void setAudioStreamType(audio_stream_type_t streamType) { mStreamType = streamType; }
+ void setAudioStreamType(audio_stream_type_t streamType) {
+ mStreamType = streamType; }
+ virtual audio_stream_type_t getAudioStreamType() const { return mStreamType; }
+
void setVolume(float left, float right);
virtual status_t setPlaybackRatePermille(int32_t ratePermille);
status_t setAuxEffectSendLevel(float level);
@@ -135,6 +139,8 @@
uint32_t mSampleRateHz; // sample rate of the content, as set in open()
float mMsecsPerFrame;
int mSessionId;
+ int mUid;
+ int mPid;
float mSendLevel;
int mAuxEffectId;
static bool mIsOnEmulator;
@@ -191,6 +197,7 @@
virtual status_t getPosition(uint32_t *position) const;
virtual status_t getFramesWritten(uint32_t *frameswritten) const;
virtual int getSessionId() const;
+ virtual uint32_t getSampleRate() const;
virtual status_t open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
@@ -205,9 +212,12 @@
virtual void flush() {}
virtual void pause() {}
virtual void close() {}
- void setAudioStreamType(audio_stream_type_t streamType) {}
- void setVolume(float left, float right) {}
- virtual status_t setPlaybackRatePermille(int32_t ratePermille) { return INVALID_OPERATION; }
+ void setAudioStreamType(audio_stream_type_t streamType __unused) {}
+ // stream type is not used for AudioCache
+ virtual audio_stream_type_t getAudioStreamType() const { return AUDIO_STREAM_DEFAULT; }
+
+ void setVolume(float left __unused, float right __unused) {}
+ virtual status_t setPlaybackRatePermille(int32_t ratePermille __unused) { return INVALID_OPERATION; }
uint32_t sampleRate() const { return mSampleRate; }
audio_format_t format() const { return mFormat; }
size_t size() const { return mSize; }
@@ -247,9 +257,15 @@
virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId);
- virtual status_t decode(const char* url, uint32_t *pSampleRate, int* pNumChannels,
- audio_format_t* pFormat,
- const sp<IMemoryHeap>& heap, size_t *pSize);
+ virtual status_t decode(
+ const sp<IMediaHTTPService> &httpService,
+ const char* url,
+ uint32_t *pSampleRate,
+ int* pNumChannels,
+ audio_format_t* pFormat,
+ const sp<IMemoryHeap>& heap,
+ size_t *pSize);
+
virtual status_t decode(int fd, int64_t offset, int64_t length,
uint32_t *pSampleRate, int* pNumChannels,
audio_format_t* pFormat,
@@ -263,9 +279,6 @@
const String8& iface);
virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList);
-
void removeClient(wp<Client> client);
// For battery usage tracking purpose
@@ -347,6 +360,7 @@
sp<MediaPlayerBase> createPlayer(player_type playerType);
virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers);
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 348957f..c61cf89 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -31,6 +31,7 @@
#include <binder/MemoryHeapBase.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <media/IMediaHTTPService.h>
#include <media/MediaMetadataRetrieverInterface.h>
#include <media/MediaPlayerInterface.h>
#include <private/media/VideoFrame.h>
@@ -106,7 +107,9 @@
}
status_t MetadataRetrieverClient::setDataSource(
- const char *url, const KeyedVector<String8, String8> *headers)
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers)
{
ALOGV("setDataSource(%s)", url);
Mutex::Autolock lock(mLock);
@@ -127,7 +130,7 @@
ALOGV("player type = %d", playerType);
sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
if (p == NULL) return NO_INIT;
- status_t ret = p->setDataSource(url, headers);
+ status_t ret = p->setDataSource(httpService, url, headers);
if (ret == NO_ERROR) mRetriever = p;
return ret;
}
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index f08f933..9d3fbe9 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -30,6 +30,7 @@
namespace android {
+struct IMediaHTTPService;
class IMediaPlayerService;
class MemoryDealer;
@@ -43,7 +44,9 @@
virtual void disconnect();
virtual status_t setDataSource(
- const char *url, const KeyedVector<String8, String8> *headers);
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual sp<IMemory> getFrameAtTime(int64_t timeUs, int option);
diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp
index 0a6aa90..deeddd1 100644
--- a/media/libmediaplayerservice/MidiFile.cpp
+++ b/media/libmediaplayerservice/MidiFile.cpp
@@ -114,7 +114,9 @@
}
status_t MidiFile::setDataSource(
- const char* path, const KeyedVector<String8, String8> *) {
+ const sp<IMediaHTTPService> &httpService,
+ const char* path,
+ const KeyedVector<String8, String8> *) {
ALOGV("MidiFile::setDataSource url=%s", path);
Mutex::Autolock lock(mMutex);
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index 24d59b4..12802ba 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -32,7 +32,9 @@
virtual status_t initCheck();
virtual status_t setDataSource(
- const char* path, const KeyedVector<String8, String8> *headers);
+ const sp<IMediaHTTPService> &httpService,
+ const char* path,
+ const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual status_t setVideoSurfaceTexture(
diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.cpp b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
index 465209f..f3cf6ef 100644
--- a/media/libmediaplayerservice/MidiMetadataRetriever.cpp
+++ b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
@@ -22,6 +22,8 @@
#include "MidiMetadataRetriever.h"
#include <media/mediametadataretriever.h>
+#include <media/IMediaHTTPService.h>
+
namespace android {
static status_t ERROR_NOT_OPEN = -1;
@@ -36,7 +38,9 @@
}
status_t MidiMetadataRetriever::setDataSource(
- const char *url, const KeyedVector<String8, String8> *headers)
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers)
{
ALOGV("setDataSource: %s", url? url: "NULL pointer");
Mutex::Autolock lock(mLock);
@@ -44,7 +48,7 @@
if (mMidiPlayer == 0) {
mMidiPlayer = new MidiFile();
}
- return mMidiPlayer->setDataSource(url, headers);
+ return mMidiPlayer->setDataSource(httpService, 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 4cee42d..b8214ee 100644
--- a/media/libmediaplayerservice/MidiMetadataRetriever.h
+++ b/media/libmediaplayerservice/MidiMetadataRetriever.h
@@ -32,7 +32,9 @@
~MidiMetadataRetriever() {}
virtual status_t setDataSource(
- const char *url, const KeyedVector<String8, String8> *headers);
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual const char* extractMetadata(int keyCode);
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index de61d9b..b37aee3 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -54,8 +54,10 @@
}
status_t StagefrightPlayer::setDataSource(
- const char *url, const KeyedVector<String8, String8> *headers) {
- return mPlayer->setDataSource(url, headers);
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers) {
+ return mPlayer->setDataSource(httpService, url, headers);
}
// Warning: The filedescriptor passed into this method will only be valid until
@@ -187,7 +189,7 @@
}
status_t StagefrightPlayer::getMetadata(
- const media::Metadata::Filter& ids, Parcel *records) {
+ const media::Metadata::Filter& /* ids */, Parcel *records) {
using media::Metadata;
uint32_t flags = mPlayer->flags();
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index 600945e..e6c30ff 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -34,7 +34,9 @@
virtual status_t setUID(uid_t uid);
virtual status_t setDataSource(
- const char *url, const KeyedVector<String8, String8> *headers);
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 095d5ca..5b7a236 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -16,6 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "StagefrightRecorder"
+#include <inttypes.h>
#include <utils/Log.h>
#include "StagefrightRecorder.h"
@@ -24,8 +25,10 @@
#include <binder/IServiceManager.h>
#include <media/IMediaPlayerService.h>
-#include <media/openmax/OMX_Audio.h>
+#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/AudioSource.h>
#include <media/stagefright/AMRWriter.h>
#include <media/stagefright/AACWriter.h>
@@ -35,13 +38,12 @@
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaCodecSource.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
-#include <media/stagefright/SurfaceMediaSource.h>
#include <media/MediaProfiles.h>
#include <camera/ICamera.h>
#include <camera/CameraParameters.h>
-#include <gui/Surface.h>
#include <utils/Errors.h>
#include <sys/types.h>
@@ -70,8 +72,8 @@
mOutputFd(-1),
mAudioSource(AUDIO_SOURCE_CNT),
mVideoSource(VIDEO_SOURCE_LIST_END),
- mStarted(false), mSurfaceMediaSource(NULL),
- mCaptureTimeLapse(false) {
+ mCaptureTimeLapse(false),
+ mStarted(false) {
ALOGV("Constructor");
reset();
@@ -80,10 +82,19 @@
StagefrightRecorder::~StagefrightRecorder() {
ALOGV("Destructor");
stop();
+
+ if (mLooper != NULL) {
+ mLooper->stop();
+ }
}
status_t StagefrightRecorder::init() {
ALOGV("init");
+
+ mLooper = new ALooper;
+ mLooper->setName("recorder_looper");
+ mLooper->start();
+
return OK;
}
@@ -92,7 +103,7 @@
// while encoding GL Frames
sp<IGraphicBufferProducer> StagefrightRecorder::querySurfaceMediaSource() const {
ALOGV("Get SurfaceMediaSource");
- return mSurfaceMediaSource->getBufferQueue();
+ return mGraphicBufferProducer;
}
status_t StagefrightRecorder::setAudioSource(audio_source_t as) {
@@ -232,7 +243,7 @@
return OK;
}
-status_t StagefrightRecorder::setOutputFile(const char *path) {
+status_t StagefrightRecorder::setOutputFile(const char * /* path */) {
ALOGE("setOutputFile(const char*) must not be called");
// We don't actually support this at all, as the media_server process
// no longer has permissions to create files.
@@ -679,10 +690,10 @@
return setParamTimeLapseEnable(timeLapseEnable);
}
} else if (key == "time-between-time-lapse-frame-capture") {
- int64_t timeBetweenTimeLapseFrameCaptureMs;
- if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureMs)) {
+ int64_t timeBetweenTimeLapseFrameCaptureUs;
+ if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureUs)) {
return setParamTimeBetweenTimeLapseFrameCapture(
- 1000LL * timeBetweenTimeLapseFrameCaptureMs);
+ timeBetweenTimeLapseFrameCaptureUs);
}
} else {
ALOGE("setParameter: failed to find key %s", key.string());
@@ -737,19 +748,15 @@
return OK;
}
-status_t StagefrightRecorder::prepare() {
- return OK;
-}
-
-status_t StagefrightRecorder::start() {
- CHECK_GE(mOutputFd, 0);
+status_t StagefrightRecorder::prepareInternal() {
+ ALOGV("prepare");
+ if (mOutputFd < 0) {
+ ALOGE("Output file descriptor is invalid");
+ return INVALID_OPERATION;
+ }
// Get UID here for permission checking
mClientUid = IPCThreadState::self()->getCallingUid();
- if (mWriter != NULL) {
- ALOGE("File writer is not avaialble");
- return UNKNOWN_ERROR;
- }
status_t status = OK;
@@ -757,25 +764,25 @@
case OUTPUT_FORMAT_DEFAULT:
case OUTPUT_FORMAT_THREE_GPP:
case OUTPUT_FORMAT_MPEG_4:
- status = startMPEG4Recording();
+ status = setupMPEG4Recording();
break;
case OUTPUT_FORMAT_AMR_NB:
case OUTPUT_FORMAT_AMR_WB:
- status = startAMRRecording();
+ status = setupAMRRecording();
break;
case OUTPUT_FORMAT_AAC_ADIF:
case OUTPUT_FORMAT_AAC_ADTS:
- status = startAACRecording();
+ status = setupAACRecording();
break;
case OUTPUT_FORMAT_RTP_AVP:
- status = startRTPRecording();
+ status = setupRTPRecording();
break;
case OUTPUT_FORMAT_MPEG2TS:
- status = startMPEG2TSRecording();
+ status = setupMPEG2TSRecording();
break;
default:
@@ -784,6 +791,72 @@
break;
}
+ return status;
+}
+
+status_t StagefrightRecorder::prepare() {
+ if (mVideoSource == VIDEO_SOURCE_SURFACE) {
+ return prepareInternal();
+ }
+ return OK;
+}
+
+status_t StagefrightRecorder::start() {
+ ALOGV("start");
+ if (mOutputFd < 0) {
+ ALOGE("Output file descriptor is invalid");
+ return INVALID_OPERATION;
+ }
+
+ status_t status = OK;
+
+ if (mVideoSource != VIDEO_SOURCE_SURFACE) {
+ status = prepareInternal();
+ if (status != OK) {
+ return status;
+ }
+ }
+
+ if (mWriter == NULL) {
+ ALOGE("File writer is not avaialble");
+ return UNKNOWN_ERROR;
+ }
+
+ switch (mOutputFormat) {
+ case OUTPUT_FORMAT_DEFAULT:
+ case OUTPUT_FORMAT_THREE_GPP:
+ case OUTPUT_FORMAT_MPEG_4:
+ {
+ sp<MetaData> meta = new MetaData;
+ setupMPEG4MetaData(&meta);
+ status = mWriter->start(meta.get());
+ break;
+ }
+
+ case OUTPUT_FORMAT_AMR_NB:
+ case OUTPUT_FORMAT_AMR_WB:
+ case OUTPUT_FORMAT_AAC_ADIF:
+ case OUTPUT_FORMAT_AAC_ADTS:
+ case OUTPUT_FORMAT_RTP_AVP:
+ case OUTPUT_FORMAT_MPEG2TS:
+ {
+ status = mWriter->start();
+ break;
+ }
+
+ default:
+ {
+ ALOGE("Unsupported output file format: %d", mOutputFormat);
+ status = UNKNOWN_ERROR;
+ break;
+ }
+ }
+
+ if (status != OK) {
+ mWriter.clear();
+ mWriter = NULL;
+ }
+
if ((status == OK) && (!mStarted)) {
mStarted = true;
@@ -815,58 +888,54 @@
return NULL;
}
- sp<MetaData> encMeta = new MetaData;
+ sp<AMessage> format = new AMessage;
const char *mime;
switch (mAudioEncoder) {
case AUDIO_ENCODER_AMR_NB:
case AUDIO_ENCODER_DEFAULT:
- mime = MEDIA_MIMETYPE_AUDIO_AMR_NB;
+ format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
break;
case AUDIO_ENCODER_AMR_WB:
- mime = MEDIA_MIMETYPE_AUDIO_AMR_WB;
+ format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_WB);
break;
case AUDIO_ENCODER_AAC:
- mime = MEDIA_MIMETYPE_AUDIO_AAC;
- encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectLC);
+ format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
+ format->setInt32("aac-profile", OMX_AUDIO_AACObjectLC);
break;
case AUDIO_ENCODER_HE_AAC:
- mime = MEDIA_MIMETYPE_AUDIO_AAC;
- encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectHE);
+ format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
+ format->setInt32("aac-profile", OMX_AUDIO_AACObjectHE);
break;
case AUDIO_ENCODER_AAC_ELD:
- mime = MEDIA_MIMETYPE_AUDIO_AAC;
- encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectELD);
+ format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
+ format->setInt32("aac-profile", OMX_AUDIO_AACObjectELD);
break;
default:
ALOGE("Unknown audio encoder: %d", mAudioEncoder);
return NULL;
}
- encMeta->setCString(kKeyMIMEType, mime);
int32_t maxInputSize;
CHECK(audioSource->getFormat()->findInt32(
kKeyMaxInputSize, &maxInputSize));
- encMeta->setInt32(kKeyMaxInputSize, maxInputSize);
- encMeta->setInt32(kKeyChannelCount, mAudioChannels);
- encMeta->setInt32(kKeySampleRate, mSampleRate);
- encMeta->setInt32(kKeyBitRate, mAudioBitRate);
+ format->setInt32("max-input-size", maxInputSize);
+ format->setInt32("channel-count", mAudioChannels);
+ format->setInt32("sample-rate", mSampleRate);
+ format->setInt32("bitrate", mAudioBitRate);
if (mAudioTimeScale > 0) {
- encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+ format->setInt32("time-scale", mAudioTimeScale);
}
- OMXClient client;
- CHECK_EQ(client.connect(), (status_t)OK);
sp<MediaSource> audioEncoder =
- OMXCodec::Create(client.interface(), encMeta,
- true /* createEncoder */, audioSource);
+ MediaCodecSource::Create(mLooper, format, audioSource);
mAudioSourceNode = audioSource;
return audioEncoder;
}
-status_t StagefrightRecorder::startAACRecording() {
+status_t StagefrightRecorder::setupAACRecording() {
// FIXME:
// Add support for OUTPUT_FORMAT_AAC_ADIF
CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_AAC_ADTS);
@@ -877,16 +946,10 @@
CHECK(mAudioSource != AUDIO_SOURCE_CNT);
mWriter = new AACWriter(mOutputFd);
- status_t status = startRawAudioRecording();
- if (status != OK) {
- mWriter.clear();
- mWriter = NULL;
- }
-
- return status;
+ return setupRawAudioRecording();
}
-status_t StagefrightRecorder::startAMRRecording() {
+status_t StagefrightRecorder::setupAMRRecording() {
CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB ||
mOutputFormat == OUTPUT_FORMAT_AMR_WB);
@@ -906,15 +969,10 @@
}
mWriter = new AMRWriter(mOutputFd);
- status_t status = startRawAudioRecording();
- if (status != OK) {
- mWriter.clear();
- mWriter = NULL;
- }
- return status;
+ return setupRawAudioRecording();
}
-status_t StagefrightRecorder::startRawAudioRecording() {
+status_t StagefrightRecorder::setupRawAudioRecording() {
if (mAudioSource >= AUDIO_SOURCE_CNT) {
ALOGE("Invalid audio source: %d", mAudioSource);
return BAD_VALUE;
@@ -940,12 +998,11 @@
mWriter->setMaxFileSize(mMaxFileSizeBytes);
}
mWriter->setListener(mListener);
- mWriter->start();
return OK;
}
-status_t StagefrightRecorder::startRTPRecording() {
+status_t StagefrightRecorder::setupRTPRecording() {
CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_RTP_AVP);
if ((mAudioSource != AUDIO_SOURCE_CNT
@@ -972,7 +1029,7 @@
return err;
}
- err = setupVideoEncoder(mediaSource, mVideoBitRate, &source);
+ err = setupVideoEncoder(mediaSource, &source);
if (err != OK) {
return err;
}
@@ -982,10 +1039,10 @@
mWriter->addSource(source);
mWriter->setListener(mListener);
- return mWriter->start();
+ return OK;
}
-status_t StagefrightRecorder::startMPEG2TSRecording() {
+status_t StagefrightRecorder::setupMPEG2TSRecording() {
CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS);
sp<MediaWriter> writer = new MPEG2TSWriter(mOutputFd);
@@ -1016,7 +1073,7 @@
}
sp<MediaSource> encoder;
- err = setupVideoEncoder(mediaSource, mVideoBitRate, &encoder);
+ err = setupVideoEncoder(mediaSource, &encoder);
if (err != OK) {
return err;
@@ -1035,7 +1092,7 @@
mWriter = writer;
- return mWriter->start();
+ return OK;
}
void StagefrightRecorder::clipVideoFrameRate() {
@@ -1089,7 +1146,22 @@
}
}
-status_t StagefrightRecorder::checkVideoEncoderCapabilities() {
+status_t StagefrightRecorder::checkVideoEncoderCapabilities(
+ bool *supportsCameraSourceMetaDataMode) {
+ /* hardware codecs must support camera source meta data mode */
+ Vector<CodecCapabilities> codecs;
+ OMXClient client;
+ CHECK_EQ(client.connect(), (status_t)OK);
+ QueryCodecs(
+ client.interface(),
+ (mVideoEncoder == VIDEO_ENCODER_H263 ? MEDIA_MIMETYPE_VIDEO_H263 :
+ mVideoEncoder == VIDEO_ENCODER_MPEG_4_SP ? MEDIA_MIMETYPE_VIDEO_MPEG4 :
+ mVideoEncoder == VIDEO_ENCODER_H264 ? MEDIA_MIMETYPE_VIDEO_AVC : ""),
+ false /* decoder */, true /* hwCodec */, &codecs);
+ *supportsCameraSourceMetaDataMode = codecs.size() > 0;
+ ALOGV("encoder %s camera source meta-data mode",
+ *supportsCameraSourceMetaDataMode ? "supports" : "DOES NOT SUPPORT");
+
if (!mCaptureTimeLapse) {
// Dont clip for time lapse capture as encoder will have enough
// time to encode because of slow capture rate of time lapse.
@@ -1261,53 +1333,20 @@
return err;
}
*mediaSource = cameraSource;
- } else if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) {
- // If using GRAlloc buffers, setup surfacemediasource.
- // Later a handle to that will be passed
- // to the client side when queried
- status_t err = setupSurfaceMediaSource();
- if (err != OK) {
- return err;
- }
- *mediaSource = mSurfaceMediaSource;
+ } else if (mVideoSource == VIDEO_SOURCE_SURFACE) {
+ *mediaSource = NULL;
} else {
return INVALID_OPERATION;
}
return OK;
}
-// setupSurfaceMediaSource creates a source with the given
-// width and height and framerate.
-// TODO: This could go in a static function inside SurfaceMediaSource
-// similar to that in CameraSource
-status_t StagefrightRecorder::setupSurfaceMediaSource() {
- status_t err = OK;
- mSurfaceMediaSource = new SurfaceMediaSource(mVideoWidth, mVideoHeight);
- if (mSurfaceMediaSource == NULL) {
- return NO_INIT;
- }
-
- if (mFrameRate == -1) {
- int32_t frameRate = 0;
- CHECK (mSurfaceMediaSource->getFormat()->findInt32(
- kKeyFrameRate, &frameRate));
- ALOGI("Frame rate is not explicitly set. Use the current frame "
- "rate (%d fps)", frameRate);
- mFrameRate = frameRate;
- } else {
- err = mSurfaceMediaSource->setFrameRate(mFrameRate);
- }
- CHECK(mFrameRate != -1);
-
- mIsMetaDataStoredInVideoBuffers =
- mSurfaceMediaSource->isMetaDataStoredInVideoBuffers();
- return err;
-}
-
status_t StagefrightRecorder::setupCameraSource(
sp<CameraSource> *cameraSource) {
status_t err = OK;
- if ((err = checkVideoEncoderCapabilities()) != OK) {
+ bool encoderSupportsCameraSourceMetaDataMode;
+ if ((err = checkVideoEncoderCapabilities(
+ &encoderSupportsCameraSourceMetaDataMode)) != OK) {
return err;
}
Size videoSize;
@@ -1323,13 +1362,14 @@
mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(
mCamera, mCameraProxy, mCameraId, mClientName, mClientUid,
videoSize, mFrameRate, mPreviewSurface,
- mTimeBetweenTimeLapseFrameCaptureUs);
+ mTimeBetweenTimeLapseFrameCaptureUs,
+ encoderSupportsCameraSourceMetaDataMode);
*cameraSource = mCameraSourceTimeLapse;
} else {
*cameraSource = CameraSource::CreateFromCamera(
mCamera, mCameraProxy, mCameraId, mClientName, mClientUid,
videoSize, mFrameRate,
- mPreviewSurface, true /*storeMetaDataInVideoBuffers*/);
+ mPreviewSurface, encoderSupportsCameraSourceMetaDataMode);
}
mCamera.clear();
mCameraProxy.clear();
@@ -1364,25 +1404,22 @@
status_t StagefrightRecorder::setupVideoEncoder(
sp<MediaSource> cameraSource,
- int32_t videoBitRate,
sp<MediaSource> *source) {
source->clear();
- sp<MetaData> enc_meta = new MetaData;
- enc_meta->setInt32(kKeyBitRate, videoBitRate);
- enc_meta->setInt32(kKeyFrameRate, mFrameRate);
+ sp<AMessage> format = new AMessage();
switch (mVideoEncoder) {
case VIDEO_ENCODER_H263:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+ format->setString("mime", MEDIA_MIMETYPE_VIDEO_H263);
break;
case VIDEO_ENCODER_MPEG_4_SP:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ format->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4);
break;
case VIDEO_ENCODER_H264:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
break;
default:
@@ -1390,59 +1427,80 @@
break;
}
- sp<MetaData> meta = cameraSource->getFormat();
+ if (cameraSource != NULL) {
+ sp<MetaData> meta = cameraSource->getFormat();
- int32_t width, height, stride, sliceHeight, colorFormat;
- CHECK(meta->findInt32(kKeyWidth, &width));
- CHECK(meta->findInt32(kKeyHeight, &height));
- CHECK(meta->findInt32(kKeyStride, &stride));
- CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
- CHECK(meta->findInt32(kKeyColorFormat, &colorFormat));
+ int32_t width, height, stride, sliceHeight, colorFormat;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+ CHECK(meta->findInt32(kKeyStride, &stride));
+ CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
+ CHECK(meta->findInt32(kKeyColorFormat, &colorFormat));
- enc_meta->setInt32(kKeyWidth, width);
- enc_meta->setInt32(kKeyHeight, height);
- enc_meta->setInt32(kKeyIFramesInterval, mIFramesIntervalSec);
- enc_meta->setInt32(kKeyStride, stride);
- enc_meta->setInt32(kKeySliceHeight, sliceHeight);
- enc_meta->setInt32(kKeyColorFormat, colorFormat);
+ format->setInt32("width", width);
+ format->setInt32("height", height);
+ format->setInt32("stride", stride);
+ format->setInt32("slice-height", sliceHeight);
+ format->setInt32("color-format", colorFormat);
+ } else {
+ format->setInt32("width", mVideoWidth);
+ format->setInt32("height", mVideoHeight);
+ format->setInt32("stride", mVideoWidth);
+ format->setInt32("slice-height", mVideoWidth);
+ format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
+
+ // set up time lapse/slow motion for surface source
+ if (mCaptureTimeLapse) {
+ if (mTimeBetweenTimeLapseFrameCaptureUs <= 0) {
+ ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld",
+ mTimeBetweenTimeLapseFrameCaptureUs);
+ return BAD_VALUE;
+ }
+ format->setInt64("time-lapse",
+ mTimeBetweenTimeLapseFrameCaptureUs);
+ }
+ }
+
+ format->setInt32("bitrate", mVideoBitRate);
+ format->setInt32("frame-rate", mFrameRate);
+ format->setInt32("i-frame-interval", mIFramesIntervalSec);
+
if (mVideoTimeScale > 0) {
- enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+ format->setInt32("time-scale", mVideoTimeScale);
}
if (mVideoEncoderProfile != -1) {
- enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
+ format->setInt32("profile", mVideoEncoderProfile);
}
if (mVideoEncoderLevel != -1) {
- enc_meta->setInt32(kKeyVideoLevel, mVideoEncoderLevel);
+ format->setInt32("level", mVideoEncoderLevel);
}
- OMXClient client;
- CHECK_EQ(client.connect(), (status_t)OK);
-
- uint32_t encoder_flags = 0;
+ uint32_t flags = 0;
if (mIsMetaDataStoredInVideoBuffers) {
- encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;
+ flags |= MediaCodecSource::FLAG_USE_METADATA_INPUT;
}
- // Do not wait for all the input buffers to become available.
- // This give timelapse video recording faster response in
- // receiving output from video encoder component.
- if (mCaptureTimeLapse) {
- encoder_flags |= OMXCodec::kOnlySubmitOneInputBufferAtOneTime;
+ if (cameraSource == NULL) {
+ flags |= MediaCodecSource::FLAG_USE_SURFACE_INPUT;
}
- sp<MediaSource> encoder = OMXCodec::Create(
- client.interface(), enc_meta,
- true /* createEncoder */, cameraSource,
- NULL, encoder_flags);
+ sp<MediaCodecSource> encoder =
+ MediaCodecSource::Create(mLooper, format, cameraSource, flags);
if (encoder == NULL) {
ALOGW("Failed to create the encoder");
// When the encoder fails to be created, we need
// release the camera source due to the camera's lock
// and unlock mechanism.
- cameraSource->stop();
+ if (cameraSource != NULL) {
+ cameraSource->stop();
+ }
return UNKNOWN_ERROR;
}
+ if (cameraSource == NULL) {
+ mGraphicBufferProducer = encoder->getGraphicBufferProducer();
+ }
+
*source = encoder;
return OK;
@@ -1476,16 +1534,12 @@
return OK;
}
-status_t StagefrightRecorder::setupMPEG4Recording(
- int outputFd,
- int32_t videoWidth, int32_t videoHeight,
- int32_t videoBitRate,
- int32_t *totalBitRate,
- sp<MediaWriter> *mediaWriter) {
- mediaWriter->clear();
- *totalBitRate = 0;
+status_t StagefrightRecorder::setupMPEG4Recording() {
+ mWriter.clear();
+ mTotalBitRate = 0;
+
status_t err = OK;
- sp<MediaWriter> writer = new MPEG4Writer(outputFd);
+ sp<MediaWriter> writer = new MPEG4Writer(mOutputFd);
if (mVideoSource < VIDEO_SOURCE_LIST_END) {
@@ -1496,13 +1550,13 @@
}
sp<MediaSource> encoder;
- err = setupVideoEncoder(mediaSource, videoBitRate, &encoder);
+ err = setupVideoEncoder(mediaSource, &encoder);
if (err != OK) {
return err;
}
writer->addSource(encoder);
- *totalBitRate += videoBitRate;
+ mTotalBitRate += mVideoBitRate;
}
// Audio source is added at the end if it exists.
@@ -1511,7 +1565,7 @@
if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_CNT)) {
err = setupAudioEncoder(writer);
if (err != OK) return err;
- *totalBitRate += mAudioBitRate;
+ mTotalBitRate += mAudioBitRate;
}
if (mInterleaveDurationUs > 0) {
@@ -1529,22 +1583,28 @@
writer->setMaxFileSize(mMaxFileSizeBytes);
}
- mStartTimeOffsetMs = mEncoderProfiles->getStartTimeOffsetMs(mCameraId);
+ if (mVideoSource == VIDEO_SOURCE_DEFAULT
+ || mVideoSource == VIDEO_SOURCE_CAMERA) {
+ mStartTimeOffsetMs = mEncoderProfiles->getStartTimeOffsetMs(mCameraId);
+ } else if (mVideoSource == VIDEO_SOURCE_SURFACE) {
+ // surface source doesn't need large initial delay
+ mStartTimeOffsetMs = 200;
+ }
if (mStartTimeOffsetMs > 0) {
reinterpret_cast<MPEG4Writer *>(writer.get())->
setStartTimeOffsetMs(mStartTimeOffsetMs);
}
writer->setListener(mListener);
- *mediaWriter = writer;
+ mWriter = writer;
return OK;
}
-void StagefrightRecorder::setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate,
- sp<MetaData> *meta) {
+void StagefrightRecorder::setupMPEG4MetaData(sp<MetaData> *meta) {
+ int64_t startTimeUs = systemTime() / 1000;
(*meta)->setInt64(kKeyTime, startTimeUs);
(*meta)->setInt32(kKeyFileType, mOutputFormat);
- (*meta)->setInt32(kKeyBitRate, totalBitRate);
+ (*meta)->setInt32(kKeyBitRate, mTotalBitRate);
(*meta)->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
if (mMovieTimeScale > 0) {
(*meta)->setInt32(kKeyTimeScale, mMovieTimeScale);
@@ -1557,27 +1617,6 @@
}
}
-status_t StagefrightRecorder::startMPEG4Recording() {
- int32_t totalBitRate;
- status_t err = setupMPEG4Recording(
- mOutputFd, mVideoWidth, mVideoHeight,
- mVideoBitRate, &totalBitRate, &mWriter);
- if (err != OK) {
- return err;
- }
-
- int64_t startTimeUs = systemTime() / 1000;
- sp<MetaData> meta = new MetaData;
- setupMPEG4MetaData(startTimeUs, totalBitRate, &meta);
-
- err = mWriter->start(meta.get());
- if (err != OK) {
- return err;
- }
-
- return OK;
-}
-
status_t StagefrightRecorder::pause() {
ALOGV("pause");
if (mWriter == NULL) {
@@ -1617,6 +1656,8 @@
mWriter.clear();
}
+ mGraphicBufferProducer.clear();
+
if (mOutputFd >= 0) {
::close(mOutputFd);
mOutputFd = -1;
@@ -1636,7 +1677,6 @@
addBatteryData(params);
}
-
return err;
}
@@ -1688,6 +1728,7 @@
mRotationDegrees = 0;
mLatitudex10000 = -3600000;
mLongitudex10000 = -3600000;
+ mTotalBitRate = 0;
mOutputFd = -1;
@@ -1728,15 +1769,15 @@
result.append(buffer);
snprintf(buffer, SIZE, " File format: %d\n", mOutputFormat);
result.append(buffer);
- snprintf(buffer, SIZE, " Max file size (bytes): %lld\n", mMaxFileSizeBytes);
+ snprintf(buffer, SIZE, " Max file size (bytes): %" PRId64 "\n", mMaxFileSizeBytes);
result.append(buffer);
- snprintf(buffer, SIZE, " Max file duration (us): %lld\n", mMaxFileDurationUs);
+ snprintf(buffer, SIZE, " Max file duration (us): %" PRId64 "\n", mMaxFileDurationUs);
result.append(buffer);
snprintf(buffer, SIZE, " File offset length (bits): %d\n", mUse64BitFileOffset? 64: 32);
result.append(buffer);
snprintf(buffer, SIZE, " Interleave duration (us): %d\n", mInterleaveDurationUs);
result.append(buffer);
- snprintf(buffer, SIZE, " Progress notification: %lld us\n", mTrackEveryTimeDurationUs);
+ snprintf(buffer, SIZE, " Progress notification: %" PRId64 " us\n", mTrackEveryTimeDurationUs);
result.append(buffer);
snprintf(buffer, SIZE, " Audio\n");
result.append(buffer);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index c864207..377d168 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -37,6 +37,7 @@
class MediaProfiles;
class IGraphicBufferProducer;
class SurfaceMediaSource;
+class ALooper;
struct StagefrightRecorder : public MediaRecorderBase {
StagefrightRecorder();
@@ -106,6 +107,7 @@
int32_t mLatitudex10000;
int32_t mLongitudex10000;
int32_t mStartTimeOffsetMs;
+ int32_t mTotalBitRate;
bool mCaptureTimeLapse;
int64_t mTimeBetweenTimeLapseFrameCaptureUs;
@@ -122,38 +124,28 @@
// An <IGraphicBufferProducer> pointer
// will be sent to the client side using which the
// frame buffers will be queued and dequeued
- sp<SurfaceMediaSource> mSurfaceMediaSource;
+ sp<IGraphicBufferProducer> mGraphicBufferProducer;
+ sp<ALooper> mLooper;
- status_t setupMPEG4Recording(
- int outputFd,
- int32_t videoWidth, int32_t videoHeight,
- int32_t videoBitRate,
- int32_t *totalBitRate,
- sp<MediaWriter> *mediaWriter);
- void setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate,
- sp<MetaData> *meta);
- status_t startMPEG4Recording();
- status_t startAMRRecording();
- status_t startAACRecording();
- status_t startRawAudioRecording();
- status_t startRTPRecording();
- status_t startMPEG2TSRecording();
+ status_t prepareInternal();
+ status_t setupMPEG4Recording();
+ void setupMPEG4MetaData(sp<MetaData> *meta);
+ status_t setupAMRRecording();
+ status_t setupAACRecording();
+ status_t setupRawAudioRecording();
+ status_t setupRTPRecording();
+ status_t setupMPEG2TSRecording();
sp<MediaSource> createAudioSource();
- status_t checkVideoEncoderCapabilities();
+ status_t checkVideoEncoderCapabilities(
+ bool *supportsCameraSourceMetaDataMode);
status_t checkAudioEncoderCapabilities();
// Generic MediaSource set-up. Returns the appropriate
// source (CameraSource or SurfaceMediaSource)
// depending on the videosource type
status_t setupMediaSource(sp<MediaSource> *mediaSource);
status_t setupCameraSource(sp<CameraSource> *cameraSource);
- // setup the surfacemediasource for the encoder
- status_t setupSurfaceMediaSource();
-
status_t setupAudioEncoder(const sp<MediaWriter>& writer);
- status_t setupVideoEncoder(
- sp<MediaSource> cameraSource,
- int32_t videoBitRate,
- sp<MediaSource> *source);
+ status_t setupVideoEncoder(sp<MediaSource> cameraSource, sp<MediaSource> *source);
// Encoding parameter handling utilities
status_t setParameter(const String8 &key, const String8 &value);
diff --git a/media/libmediaplayerservice/TestPlayerStub.cpp b/media/libmediaplayerservice/TestPlayerStub.cpp
index 5d9728a..5795773 100644
--- a/media/libmediaplayerservice/TestPlayerStub.cpp
+++ b/media/libmediaplayerservice/TestPlayerStub.cpp
@@ -113,7 +113,9 @@
// Create the test player.
// Call setDataSource on the test player with the url in param.
status_t TestPlayerStub::setDataSource(
- const char *url, const KeyedVector<String8, String8> *headers) {
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers) {
if (!isTestUrl(url) || NULL != mHandle) {
return INVALID_OPERATION;
}
@@ -162,7 +164,7 @@
}
mPlayer = (*mNewPlayer)();
- return mPlayer->setDataSource(mContentUrl, headers);
+ return mPlayer->setDataSource(httpService, mContentUrl, headers);
}
// Internal cleanup.
diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h
index a3802eb..55bf2c8 100644
--- a/media/libmediaplayerservice/TestPlayerStub.h
+++ b/media/libmediaplayerservice/TestPlayerStub.h
@@ -66,7 +66,9 @@
// @param url Should be a test url. See class comment.
virtual status_t setDataSource(
- const char* url, const KeyedVector<String8, String8> *headers);
+ const sp<IMediaHTTPService> &httpService,
+ const char* url,
+ const KeyedVector<String8, String8> *headers);
// Test player for a file descriptor source is not supported.
virtual status_t setDataSource(int, int64_t, int64_t) {
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index f946c1c..f97ba57 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -11,7 +11,6 @@
NuPlayerStreamListener.cpp \
RTSPSource.cpp \
StreamingSource.cpp \
- mp4/MP4Source.cpp \
LOCAL_C_INCLUDES := \
$(TOP)/frameworks/av/media/libstagefright/httplive \
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index b04e7a6..06aac33 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -33,17 +33,16 @@
NuPlayer::GenericSource::GenericSource(
const sp<AMessage> ¬ify,
+ const sp<IMediaHTTPService> &httpService,
const char *url,
- const KeyedVector<String8, String8> *headers,
- bool uidValid,
- uid_t uid)
+ const KeyedVector<String8, String8> *headers)
: Source(notify),
mDurationUs(0ll),
mAudioIsVorbis(false) {
DataSource::RegisterDefaultSniffers();
sp<DataSource> dataSource =
- DataSource::CreateFromURI(url, headers);
+ DataSource::CreateFromURI(httpService, url, headers);
CHECK(dataSource != NULL);
initFromDataSource(dataSource);
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 2da680c..20d597e 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -33,10 +33,9 @@
struct NuPlayer::GenericSource : public NuPlayer::Source {
GenericSource(
const sp<AMessage> ¬ify,
+ const sp<IMediaHTTPService> &httpService,
const char *url,
- const KeyedVector<String8, String8> *headers,
- bool uidValid = false,
- uid_t uid = 0);
+ const KeyedVector<String8, String8> *headers);
GenericSource(
const sp<AMessage> ¬ify,
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index d8b35d7..cbedf5c 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -24,6 +24,7 @@
#include "LiveDataSource.h"
#include "LiveSession.h"
+#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -34,13 +35,12 @@
NuPlayer::HTTPLiveSource::HTTPLiveSource(
const sp<AMessage> ¬ify,
+ const sp<IMediaHTTPService> &httpService,
const char *url,
- const KeyedVector<String8, String8> *headers,
- bool uidValid, uid_t uid)
+ const KeyedVector<String8, String8> *headers)
: Source(notify),
+ mHTTPService(httpService),
mURL(url),
- mUIDValid(uidValid),
- mUID(uid),
mFlags(0),
mFinalResult(OK),
mOffset(0),
@@ -79,8 +79,7 @@
mLiveSession = new LiveSession(
notify,
(mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0,
- mUIDValid,
- mUID);
+ mHTTPService);
mLiveLooper->registerHandler(mLiveSession);
@@ -140,7 +139,7 @@
// LiveSession::selectTrack returns BAD_VALUE when selecting the currently
// selected track, or unselecting a non-selected track. In this case it's an
// no-op so we return OK.
- return (err == OK || err == BAD_VALUE) ? OK : err;
+ return (err == OK || err == BAD_VALUE) ? (status_t)OK : err;
}
status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) {
@@ -201,7 +200,16 @@
switch (what) {
case LiveSession::kWhatPrepared:
{
- notifyVideoSizeChanged(0, 0);
+ // notify the current size here if we have it, otherwise report an initial size of (0,0)
+ sp<AMessage> format = getFormat(false /* audio */);
+ int32_t width;
+ int32_t height;
+ if (format != NULL &&
+ format->findInt32("width", &width) && format->findInt32("height", &height)) {
+ notifyVideoSizeChanged(width, height);
+ } else {
+ notifyVideoSizeChanged(0, 0);
+ }
uint32_t flags = FLAG_CAN_PAUSE;
if (mLiveSession->isSeekable()) {
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index bcc3f8b..4d7251f 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -28,10 +28,9 @@
struct NuPlayer::HTTPLiveSource : public NuPlayer::Source {
HTTPLiveSource(
const sp<AMessage> ¬ify,
+ const sp<IMediaHTTPService> &httpService,
const char *url,
- const KeyedVector<String8, String8> *headers,
- bool uidValid = false,
- uid_t uid = 0);
+ const KeyedVector<String8, String8> *headers);
virtual void prepareAsync();
virtual void start();
@@ -61,10 +60,9 @@
kWhatFetchSubtitleData,
};
+ sp<IMediaHTTPService> mHTTPService;
AString mURL;
KeyedVector<String8, String8> mExtraHeaders;
- bool mUIDValid;
- uid_t mUID;
uint32_t mFlags;
status_t mFinalResult;
off64_t mOffset;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 750287f..a750ad0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -28,11 +28,11 @@
#include "RTSPSource.h"
#include "StreamingSource.h"
#include "GenericSource.h"
-#include "mp4/MP4Source.h"
#include "ATSParser.h"
-#include <cutils/properties.h> // for property_get
+#include "SoftwareRenderer.h"
+
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -146,6 +146,7 @@
: mUIDValid(false),
mSourceFlags(0),
mVideoIsAVC(false),
+ mNeedsSwRenderer(false),
mAudioEOS(false),
mVideoEOS(false),
mScanSourcesPending(false),
@@ -180,14 +181,7 @@
sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
- char prop[PROPERTY_VALUE_MAX];
- if (property_get("media.stagefright.use-mp4source", prop, NULL)
- && (!strcmp(prop, "1") || !strcasecmp(prop, "true"))) {
- msg->setObject("source", new MP4Source(notify, source));
- } else {
- msg->setObject("source", new StreamingSource(notify, source));
- }
-
+ msg->setObject("source", new StreamingSource(notify, source));
msg->post();
}
@@ -209,7 +203,9 @@
}
void NuPlayer::setDataSourceAsync(
- const char *url, const KeyedVector<String8, String8> *headers) {
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers) {
sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
size_t len = strlen(url);
@@ -217,16 +213,18 @@
sp<Source> source;
if (IsHTTPLiveURL(url)) {
- source = new HTTPLiveSource(notify, url, headers, mUIDValid, mUID);
+ source = new HTTPLiveSource(notify, httpService, url, headers);
} else if (!strncasecmp(url, "rtsp://", 7)) {
- source = new RTSPSource(notify, url, headers, mUIDValid, mUID);
+ source = new RTSPSource(
+ notify, httpService, url, headers, mUIDValid, mUID);
} else if ((!strncasecmp(url, "http://", 7)
|| !strncasecmp(url, "https://", 8))
&& ((len >= 4 && !strcasecmp(".sdp", &url[len - 4]))
|| strstr(url, ".sdp?"))) {
- source = new RTSPSource(notify, url, headers, mUIDValid, mUID, true);
+ source = new RTSPSource(
+ notify, httpService, url, headers, mUIDValid, mUID, true);
} else {
- source = new GenericSource(notify, url, headers, mUIDValid, mUID);
+ source = new GenericSource(notify, httpService, url, headers);
}
msg->setObject("source", source);
@@ -444,6 +442,7 @@
ALOGV("kWhatStart");
mVideoIsAVC = false;
+ mNeedsSwRenderer = false;
mAudioEOS = false;
mVideoEOS = false;
mSkipRenderingAudioUntilMediaTimeUs = -1;
@@ -680,6 +679,20 @@
notifyListener(
MEDIA_SET_VIDEO_SIZE, displayWidth, displayHeight);
+
+ if (mNeedsSwRenderer && mNativeWindow != NULL) {
+ int32_t colorFormat;
+ CHECK(codecRequest->findInt32("color-format", &colorFormat));
+
+ sp<MetaData> meta = new MetaData;
+ meta->setInt32(kKeyWidth, width);
+ meta->setInt32(kKeyHeight, height);
+ meta->setRect(kKeyCropRect, cropLeft, cropTop, cropRight, cropBottom);
+ meta->setInt32(kKeyColorFormat, colorFormat);
+
+ mRenderer->setSoftRenderer(
+ new SoftwareRenderer(mNativeWindow->getNativeWindow(), meta));
+ }
}
} else if (what == ACodec::kWhatShutdownCompleted) {
ALOGV("%s shutdown completed", audio ? "audio" : "video");
@@ -703,8 +716,13 @@
mRenderer->queueEOS(audio, UNKNOWN_ERROR);
} else if (what == ACodec::kWhatDrainThisBuffer) {
renderBuffer(audio, codecRequest);
- } else if (what != ACodec::kWhatComponentAllocated
- && what != ACodec::kWhatComponentConfigured
+ } else if (what == ACodec::kWhatComponentAllocated) {
+ if (!audio) {
+ AString name;
+ CHECK(codecRequest->findString("componentName", &name));
+ mNeedsSwRenderer = name.startsWith("OMX.google.");
+ }
+ } else if (what != ACodec::kWhatComponentConfigured
&& what != ACodec::kWhatBuffersAllocated) {
ALOGV("Unhandled codec notification %d '%c%c%c%c'.",
what,
@@ -988,7 +1006,14 @@
&NuPlayer::performScanSources));
}
- flushDecoder(audio, formatChange);
+ sp<AMessage> newFormat = mSource->getFormat(audio);
+ sp<Decoder> &decoder = audio ? mAudioDecoder : mVideoDecoder;
+ if (formatChange && !decoder->supportsSeamlessFormatChange(newFormat)) {
+ flushDecoder(audio, /* needShutdown = */ true);
+ } else {
+ flushDecoder(audio, /* needShutdown = */ false);
+ err = OK;
+ }
} else {
// This stream is unaffected by the discontinuity
@@ -1501,7 +1526,7 @@
notify->post();
}
-void NuPlayer::Source::onMessageReceived(const sp<AMessage> &msg) {
+void NuPlayer::Source::onMessageReceived(const sp<AMessage> & /* msg */) {
TRESPASS();
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 13350f3..9dfe4a0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -38,7 +38,9 @@
void setDataSourceAsync(const sp<IStreamSource> &source);
void setDataSourceAsync(
- const char *url, const KeyedVector<String8, String8> *headers);
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers);
void setDataSourceAsync(int fd, int64_t offset, int64_t length);
@@ -116,6 +118,7 @@
sp<MediaPlayerBase::AudioSink> mAudioSink;
sp<Decoder> mVideoDecoder;
bool mVideoIsAVC;
+ bool mNeedsSwRenderer;
sp<Decoder> mAudioDecoder;
sp<Renderer> mRenderer;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 22f699e..2423fd5 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -67,6 +67,7 @@
// queue.
bool needDedicatedLooper = !strncasecmp(mime.c_str(), "video/", 6);
+ mFormat = format;
mCodec = new ACodec;
if (needDedicatedLooper && mCodecLooper == NULL) {
@@ -147,5 +148,65 @@
}
}
+bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const {
+ if (targetFormat == NULL) {
+ return true;
+ }
+
+ AString mime;
+ if (!targetFormat->findString("mime", &mime)) {
+ return false;
+ }
+
+ if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
+ // field-by-field comparison
+ const char * keys[] = { "channel-count", "sample-rate", "is-adts" };
+ for (unsigned int i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
+ int32_t oldVal, newVal;
+ if (!mFormat->findInt32(keys[i], &oldVal) || !targetFormat->findInt32(keys[i], &newVal)
+ || oldVal != newVal) {
+ return false;
+ }
+ }
+
+ sp<ABuffer> oldBuf, newBuf;
+ if (mFormat->findBuffer("csd-0", &oldBuf) && targetFormat->findBuffer("csd-0", &newBuf)) {
+ if (oldBuf->size() != newBuf->size()) {
+ return false;
+ }
+ return !memcmp(oldBuf->data(), newBuf->data(), oldBuf->size());
+ }
+ }
+ return false;
+}
+
+bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetFormat) const {
+ if (mFormat == NULL) {
+ return false;
+ }
+
+ if (targetFormat == NULL) {
+ return true;
+ }
+
+ AString oldMime, newMime;
+ if (!mFormat->findString("mime", &oldMime)
+ || !targetFormat->findString("mime", &newMime)
+ || !(oldMime == newMime)) {
+ return false;
+ }
+
+ bool audio = !strncasecmp(oldMime.c_str(), "audio/", strlen("audio/"));
+ bool seamless;
+ if (audio) {
+ seamless = supportsSeamlessAudioFormatChange(targetFormat);
+ } else {
+ seamless = mCodec != NULL && mCodec->isConfiguredForAdaptivePlayback();
+ }
+
+ ALOGV("%s seamless support for %s", seamless ? "yes" : "no", oldMime.c_str());
+ return seamless;
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index a876148..78ea74a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -36,6 +36,8 @@
void signalResume();
void initiateShutdown();
+ bool supportsSeamlessFormatChange(const sp<AMessage> &to) const;
+
protected:
virtual ~Decoder();
@@ -49,6 +51,7 @@
sp<AMessage> mNotify;
sp<NativeWindowWrapper> mNativeWindow;
+ sp<AMessage> mFormat;
sp<ACodec> mCodec;
sp<ALooper> mCodecLooper;
@@ -59,6 +62,8 @@
void onFillThisBuffer(const sp<AMessage> &msg);
+ bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const;
+
DISALLOW_EVIL_CONSTRUCTORS(Decoder);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 47834fd..e4850f0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -16,6 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "NuPlayerDriver"
+#include <inttypes.h>
#include <utils/Log.h>
#include "NuPlayerDriver.h"
@@ -70,7 +71,9 @@
}
status_t NuPlayerDriver::setDataSource(
- const char *url, const KeyedVector<String8, String8> *headers) {
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers) {
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
@@ -79,7 +82,7 @@
mState = STATE_SET_DATASOURCE_PENDING;
- mPlayer->setDataSourceAsync(url, headers);
+ mPlayer->setDataSourceAsync(httpService, url, headers);
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock);
@@ -364,7 +367,7 @@
return OK;
}
-status_t NuPlayerDriver::setLooping(int loop) {
+status_t NuPlayerDriver::setLooping(int /* loop */) {
return INVALID_OPERATION;
}
@@ -420,16 +423,17 @@
mPlayer->setAudioSink(audioSink);
}
-status_t NuPlayerDriver::setParameter(int key, const Parcel &request) {
+status_t NuPlayerDriver::setParameter(
+ int /* key */, const Parcel & /* request */) {
return INVALID_OPERATION;
}
-status_t NuPlayerDriver::getParameter(int key, Parcel *reply) {
+status_t NuPlayerDriver::getParameter(int /* key */, Parcel * /* reply */) {
return INVALID_OPERATION;
}
status_t NuPlayerDriver::getMetadata(
- const media::Metadata::Filter& ids, Parcel *records) {
+ const media::Metadata::Filter& /* ids */, Parcel *records) {
Mutex::Autolock autoLock(mLock);
using media::Metadata;
@@ -493,13 +497,14 @@
mNumFramesDropped = numFramesDropped;
}
-status_t NuPlayerDriver::dump(int fd, const Vector<String16> &args) const {
+status_t NuPlayerDriver::dump(
+ int fd, const Vector<String16> & /* args */) const {
Mutex::Autolock autoLock(mLock);
FILE *out = fdopen(dup(fd), "w");
fprintf(out, " NuPlayer\n");
- fprintf(out, " numFramesTotal(%lld), numFramesDropped(%lld), "
+ fprintf(out, " numFramesTotal(%" PRId64 "), numFramesDropped(%" PRId64 "), "
"percentageDropped(%.2f)\n",
mNumFramesTotal,
mNumFramesDropped,
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 99f72a6..0148fb1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -31,7 +31,9 @@
virtual status_t setUID(uid_t uid);
virtual status_t setDataSource(
- const char *url, const KeyedVector<String8, String8> *headers);
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 3b2784b..bf5271e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -20,6 +20,8 @@
#include "NuPlayerRenderer.h"
+#include "SoftwareRenderer.h"
+
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -34,6 +36,7 @@
const sp<AMessage> ¬ify,
uint32_t flags)
: mAudioSink(sink),
+ mSoftRenderer(NULL),
mNotify(notify),
mFlags(flags),
mNumFramesWritten(0),
@@ -57,6 +60,12 @@
}
NuPlayer::Renderer::~Renderer() {
+ delete mSoftRenderer;
+}
+
+void NuPlayer::Renderer::setSoftRenderer(SoftwareRenderer *softRenderer) {
+ delete mSoftRenderer;
+ mSoftRenderer = softRenderer;
}
void NuPlayer::Renderer::queueBuffer(
@@ -413,7 +422,12 @@
ALOGV("video late by %lld us (%.2f secs)",
mVideoLateByUs, mVideoLateByUs / 1E6);
} else {
- ALOGV("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
+ ALOGV("rendering video at media time %.2f secs",
+ (mFlags & FLAG_REAL_TIME ? realTimeUs :
+ (realTimeUs + mAnchorTimeMediaUs - mAnchorTimeRealUs)) / 1E6);
+ if (mSoftRenderer != NULL) {
+ mSoftRenderer->render(entry->mBuffer->data(), entry->mBuffer->size(), NULL);
+ }
}
entry->mNotifyConsumed->setInt32("render", !tooLate);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 94a05ea..9124e03 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -23,6 +23,7 @@
namespace android {
struct ABuffer;
+class SoftwareRenderer;
struct NuPlayer::Renderer : public AHandler {
enum Flags {
@@ -56,6 +57,8 @@
kWhatMediaRenderingStart = 'mdrd',
};
+ void setSoftRenderer(SoftwareRenderer *softRenderer);
+
protected:
virtual ~Renderer();
@@ -83,6 +86,7 @@
static const int64_t kMinPositionUpdateDelayUs;
sp<MediaPlayerBase::AudioSink> mAudioSink;
+ SoftwareRenderer *mSoftRenderer;
sp<AMessage> mNotify;
uint32_t mFlags;
List<QueueEntry> mAudioQueue;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index e50533a..11279fc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -68,19 +68,19 @@
virtual status_t dequeueAccessUnit(
bool audio, sp<ABuffer> *accessUnit) = 0;
- virtual status_t getDuration(int64_t *durationUs) {
+ virtual status_t getDuration(int64_t * /* durationUs */) {
return INVALID_OPERATION;
}
- virtual status_t getTrackInfo(Parcel* reply) const {
+ virtual status_t getTrackInfo(Parcel* /* reply */) const {
return INVALID_OPERATION;
}
- virtual status_t selectTrack(size_t trackIndex, bool select) {
+ virtual status_t selectTrack(size_t /* trackIndex */, bool /* select */) {
return INVALID_OPERATION;
}
- virtual status_t seekTo(int64_t seekTimeUs) {
+ virtual status_t seekTo(int64_t /* seekTimeUs */) {
return INVALID_OPERATION;
}
@@ -93,7 +93,7 @@
virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual sp<MetaData> getFormatMeta(bool audio) { return NULL; }
+ virtual sp<MetaData> getFormatMeta(bool /* audio */) { return NULL; }
sp<AMessage> dupNotify() const { return mNotify->dup(); }
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 18cf6d1..94800ba 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -24,6 +24,7 @@
#include "MyHandler.h"
#include "SDPLoader.h"
+#include <media/IMediaHTTPService.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
@@ -33,12 +34,14 @@
NuPlayer::RTSPSource::RTSPSource(
const sp<AMessage> ¬ify,
+ const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers,
bool uidValid,
uid_t uid,
bool isSDP)
: Source(notify),
+ mHTTPService(httpService),
mURL(url),
mUIDValid(uidValid),
mUID(uid),
@@ -92,7 +95,7 @@
if (mIsSDP) {
mSDPLoader = new SDPLoader(notify,
(mFlags & kFlagIncognito) ? SDPLoader::kFlagIncognito : 0,
- mUIDValid, mUID);
+ mHTTPService);
mSDPLoader->load(
mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
index 8cf34a0..3718bf9 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.h
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -34,6 +34,7 @@
struct NuPlayer::RTSPSource : public NuPlayer::Source {
RTSPSource(
const sp<AMessage> ¬ify,
+ const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers,
bool uidValid = false,
@@ -88,6 +89,7 @@
bool mNPTMappingValid;
};
+ sp<IMediaHTTPService> mHTTPService;
AString mURL;
KeyedVector<String8, String8> mExtraHeaders;
bool mUIDValid;
diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp
deleted file mode 100644
index d31d947..0000000
--- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MP4Source.h"
-
-#include "FragmentedMP4Parser.h"
-#include "../NuPlayerStreamListener.h"
-
-#include <media/IStreamSource.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MetaData.h>
-
-namespace android {
-
-struct StreamSource : public FragmentedMP4Parser::Source {
- StreamSource(const sp<IStreamSource> &source)
- : mListener(new NuPlayer::NuPlayerStreamListener(source, 0)),
- mPosition(0) {
- mListener->start();
- }
-
- virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
- if (offset < mPosition) {
- return -EPIPE;
- }
-
- while (offset > mPosition) {
- char buffer[1024];
- off64_t skipBytes = offset - mPosition;
- if (skipBytes > sizeof(buffer)) {
- skipBytes = sizeof(buffer);
- }
-
- sp<AMessage> extra;
- ssize_t n;
- for (;;) {
- n = mListener->read(buffer, skipBytes, &extra);
-
- if (n == -EWOULDBLOCK) {
- usleep(10000);
- continue;
- }
-
- break;
- }
-
- ALOGV("skipped %ld bytes at offset %lld", n, mPosition);
-
- if (n < 0) {
- return n;
- }
-
- mPosition += n;
- }
-
- sp<AMessage> extra;
- size_t total = 0;
- while (total < size) {
- ssize_t n = mListener->read(
- (uint8_t *)data + total, size - total, &extra);
-
- if (n == -EWOULDBLOCK) {
- usleep(10000);
- continue;
- } else if (n == 0) {
- break;
- } else if (n < 0) {
- mPosition += total;
- return n;
- }
-
- total += n;
- }
-
- ALOGV("read %ld bytes at offset %lld", n, mPosition);
-
- mPosition += total;
-
- return total;
- }
-
- bool isSeekable() {
- return false;
- }
-
-private:
- sp<NuPlayer::NuPlayerStreamListener> mListener;
- off64_t mPosition;
-
- DISALLOW_EVIL_CONSTRUCTORS(StreamSource);
-};
-
-MP4Source::MP4Source(
- const sp<AMessage> ¬ify, const sp<IStreamSource> &source)
- : Source(notify),
- mSource(source),
- mLooper(new ALooper),
- mParser(new FragmentedMP4Parser),
- mEOS(false) {
- mLooper->registerHandler(mParser);
-}
-
-MP4Source::~MP4Source() {
-}
-
-void MP4Source::prepareAsync() {
- notifyVideoSizeChanged(0, 0);
- notifyFlagsChanged(0);
- notifyPrepared();
-}
-
-void MP4Source::start() {
- mLooper->start(false /* runOnCallingThread */);
- mParser->start(new StreamSource(mSource));
-}
-
-status_t MP4Source::feedMoreTSData() {
- return mEOS ? ERROR_END_OF_STREAM : (status_t)OK;
-}
-
-sp<AMessage> MP4Source::getFormat(bool audio) {
- return mParser->getFormat(audio);
-}
-
-status_t MP4Source::dequeueAccessUnit(
- bool audio, sp<ABuffer> *accessUnit) {
- return mParser->dequeueAccessUnit(audio, accessUnit);
-}
-
-} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h
deleted file mode 100644
index a6ef622..0000000
--- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MP4_SOURCE_H
-#define MP4_SOURCE_H
-
-#include "NuPlayerSource.h"
-
-namespace android {
-
-struct FragmentedMP4Parser;
-
-struct MP4Source : public NuPlayer::Source {
- MP4Source(const sp<AMessage> ¬ify, const sp<IStreamSource> &source);
-
- virtual void prepareAsync();
- virtual void start();
-
- virtual status_t feedMoreTSData();
-
- virtual sp<AMessage> getFormat(bool audio);
-
- virtual status_t dequeueAccessUnit(
- bool audio, sp<ABuffer> *accessUnit);
-
-protected:
- virtual ~MP4Source();
-
-private:
- sp<IStreamSource> mSource;
- sp<ALooper> mLooper;
- sp<FragmentedMP4Parser> mParser;
- bool mEOS;
-
- DISALLOW_EVIL_CONSTRUCTORS(MP4Source);
-};
-
-} // namespace android
-
-#endif // MP4_SOURCE_H
diff --git a/media/libnbaio/Android.mk b/media/libnbaio/Android.mk
index 69c75b8..9707c4a 100644
--- a/media/libnbaio/Android.mk
+++ b/media/libnbaio/Android.mk
@@ -31,9 +31,8 @@
libcommon_time_client \
libcutils \
libutils \
- liblog \
- libmedia
-# This dependency on libmedia is for SingleStateQueueInstantiations.
-# Consider a separate a library for SingleStateQueueInstantiations.
+ liblog
+
+LOCAL_STATIC_LIBRARIES += libinstantssq
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libnbaio/AudioBufferProviderSource.cpp b/media/libnbaio/AudioBufferProviderSource.cpp
index 74a6fdb..551f516 100644
--- a/media/libnbaio/AudioBufferProviderSource.cpp
+++ b/media/libnbaio/AudioBufferProviderSource.cpp
@@ -24,11 +24,11 @@
namespace android {
AudioBufferProviderSource::AudioBufferProviderSource(AudioBufferProvider *provider,
- NBAIO_Format format) :
+ const NBAIO_Format& format) :
NBAIO_Source(format), mProvider(provider), mConsumed(0)
{
ALOG_ASSERT(provider != NULL);
- ALOG_ASSERT(format != Format_Invalid);
+ ALOG_ASSERT(Format_isValid(format));
}
AudioBufferProviderSource::~AudioBufferProviderSource()
@@ -68,7 +68,7 @@
}
// count could be zero, either because count was zero on entry or
// available is zero, but both are unlikely so don't check for that
- memcpy(buffer, (char *) mBuffer.raw + (mConsumed << mBitShift), count << mBitShift);
+ memcpy(buffer, (char *) mBuffer.raw + (mConsumed * mFrameSize), count * mFrameSize);
if (CC_UNLIKELY((mConsumed += count) >= mBuffer.frameCount)) {
mProvider->releaseBuffer(&mBuffer);
mBuffer.raw = NULL;
@@ -120,7 +120,7 @@
count = available;
}
if (CC_LIKELY(count > 0)) {
- char* readTgt = (char *) mBuffer.raw + (mConsumed << mBitShift);
+ char* readTgt = (char *) mBuffer.raw + (mConsumed * mFrameSize);
ssize_t ret = via(user, readTgt, count, readPTS);
if (CC_UNLIKELY(ret <= 0)) {
if (CC_LIKELY(accumulator > 0)) {
diff --git a/media/libnbaio/AudioStreamInSource.cpp b/media/libnbaio/AudioStreamInSource.cpp
index 05273f6..efbc1a7 100644
--- a/media/libnbaio/AudioStreamInSource.cpp
+++ b/media/libnbaio/AudioStreamInSource.cpp
@@ -40,7 +40,7 @@
ssize_t AudioStreamInSource::negotiate(const NBAIO_Format offers[], size_t numOffers,
NBAIO_Format counterOffers[], size_t& numCounterOffers)
{
- if (mFormat == Format_Invalid) {
+ if (!Format_isValid(mFormat)) {
mStreamBufferSizeBytes = mStream->common.get_buffer_size(&mStream->common);
audio_format_t streamFormat = mStream->common.get_format(&mStream->common);
if (streamFormat == AUDIO_FORMAT_PCM_16_BIT) {
@@ -48,7 +48,7 @@
audio_channel_mask_t channelMask =
(audio_channel_mask_t) mStream->common.get_channels(&mStream->common);
mFormat = Format_from_SR_C(sampleRate, popcount(channelMask));
- mBitShift = Format_frameBitShift(mFormat);
+ mFrameSize = Format_frameSize(mFormat);
}
}
return NBAIO_Source::negotiate(offers, numOffers, counterOffers, numCounterOffers);
@@ -67,12 +67,12 @@
ssize_t AudioStreamInSource::read(void *buffer, size_t count)
{
- if (CC_UNLIKELY(mFormat == Format_Invalid)) {
+ if (CC_UNLIKELY(!Format_isValid(mFormat))) {
return NEGOTIATE;
}
- ssize_t bytesRead = mStream->read(mStream, buffer, count << mBitShift);
+ ssize_t bytesRead = mStream->read(mStream, buffer, count * mFrameSize);
if (bytesRead > 0) {
- size_t framesRead = bytesRead >> mBitShift;
+ size_t framesRead = bytesRead / mFrameSize;
mFramesRead += framesRead;
return framesRead;
} else {
diff --git a/media/libnbaio/AudioStreamOutSink.cpp b/media/libnbaio/AudioStreamOutSink.cpp
index e4341d7..f7f764e 100644
--- a/media/libnbaio/AudioStreamOutSink.cpp
+++ b/media/libnbaio/AudioStreamOutSink.cpp
@@ -37,7 +37,7 @@
ssize_t AudioStreamOutSink::negotiate(const NBAIO_Format offers[], size_t numOffers,
NBAIO_Format counterOffers[], size_t& numCounterOffers)
{
- if (mFormat == Format_Invalid) {
+ if (!Format_isValid(mFormat)) {
mStreamBufferSizeBytes = mStream->common.get_buffer_size(&mStream->common);
audio_format_t streamFormat = mStream->common.get_format(&mStream->common);
if (streamFormat == AUDIO_FORMAT_PCM_16_BIT) {
@@ -45,7 +45,7 @@
audio_channel_mask_t channelMask =
(audio_channel_mask_t) mStream->common.get_channels(&mStream->common);
mFormat = Format_from_SR_C(sampleRate, popcount(channelMask));
- mBitShift = Format_frameBitShift(mFormat);
+ mFrameSize = Format_frameSize(mFormat);
}
}
return NBAIO_Sink::negotiate(offers, numOffers, counterOffers, numCounterOffers);
@@ -56,10 +56,10 @@
if (!mNegotiated) {
return NEGOTIATE;
}
- ALOG_ASSERT(mFormat != Format_Invalid);
- ssize_t ret = mStream->write(mStream, buffer, count << mBitShift);
+ ALOG_ASSERT(Format_isValid(mFormat));
+ ssize_t ret = mStream->write(mStream, buffer, count * mFrameSize);
if (ret > 0) {
- ret >>= mBitShift;
+ ret /= mFrameSize;
mFramesWritten += ret;
} else {
// FIXME verify HAL implementations are returning the correct error codes e.g. WOULD_BLOCK
diff --git a/media/libnbaio/MonoPipe.cpp b/media/libnbaio/MonoPipe.cpp
index de0ad28..9c8461c 100644
--- a/media/libnbaio/MonoPipe.cpp
+++ b/media/libnbaio/MonoPipe.cpp
@@ -30,7 +30,7 @@
namespace android {
-MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) :
+MonoPipe::MonoPipe(size_t reqFrames, const NBAIO_Format& format, bool writeCanBlock) :
NBAIO_Sink(format),
mUpdateSeq(0),
mReqFrames(reqFrames),
@@ -115,11 +115,11 @@
part1 = written;
}
if (CC_LIKELY(part1 > 0)) {
- memcpy((char *) mBuffer + (rear << mBitShift), buffer, part1 << mBitShift);
+ memcpy((char *) mBuffer + (rear * mFrameSize), buffer, part1 * mFrameSize);
if (CC_UNLIKELY(rear + part1 == mMaxFrames)) {
size_t part2 = written - part1;
if (CC_LIKELY(part2 > 0)) {
- memcpy(mBuffer, (char *) buffer + (part1 << mBitShift), part2 << mBitShift);
+ memcpy(mBuffer, (char *) buffer + (part1 * mFrameSize), part2 * mFrameSize);
}
}
android_atomic_release_store(written + mRear, &mRear);
@@ -129,7 +129,7 @@
break;
}
count -= written;
- buffer = (char *) buffer + (written << mBitShift);
+ buffer = (char *) buffer + (written * mFrameSize);
// Simulate blocking I/O by sleeping at different rates, depending on a throttle.
// The throttle tries to keep the mean pipe depth near the setpoint, with a slight jitter.
uint32_t ns;
@@ -183,7 +183,7 @@
}
}
if (ns > 0) {
- const struct timespec req = {0, ns};
+ const struct timespec req = {0, static_cast<long>(ns)};
nanosleep(&req, NULL);
}
// record the time that this write() completed
diff --git a/media/libnbaio/MonoPipeReader.cpp b/media/libnbaio/MonoPipeReader.cpp
index 851341a..de82229 100644
--- a/media/libnbaio/MonoPipeReader.cpp
+++ b/media/libnbaio/MonoPipeReader.cpp
@@ -73,11 +73,11 @@
part1 = red;
}
if (CC_LIKELY(part1 > 0)) {
- memcpy(buffer, (char *) mPipe->mBuffer + (front << mBitShift), part1 << mBitShift);
+ memcpy(buffer, (char *) mPipe->mBuffer + (front * mFrameSize), part1 * mFrameSize);
if (CC_UNLIKELY(front + part1 == mPipe->mMaxFrames)) {
size_t part2 = red - part1;
if (CC_LIKELY(part2 > 0)) {
- memcpy((char *) buffer + (part1 << mBitShift), mPipe->mBuffer, part2 << mBitShift);
+ memcpy((char *) buffer + (part1 * mFrameSize), mPipe->mBuffer, part2 * mFrameSize);
}
}
mPipe->updateFrontAndNRPTS(red + mPipe->mFront, nextReadPTS);
diff --git a/media/libnbaio/NBAIO.cpp b/media/libnbaio/NBAIO.cpp
index e0d2c21..4f6591d 100644
--- a/media/libnbaio/NBAIO.cpp
+++ b/media/libnbaio/NBAIO.cpp
@@ -22,16 +22,22 @@
namespace android {
-size_t Format_frameSize(NBAIO_Format format)
+size_t Format_frameSize(const NBAIO_Format& format)
{
- return Format_channelCount(format) * sizeof(short);
+ return format.mFrameSize;
}
-size_t Format_frameBitShift(NBAIO_Format format)
+#if 0
+int Format_frameBitShift(const NBAIO_Format& format)
{
+ // FIXME The sample format is hard-coded to AUDIO_FORMAT_PCM_16_BIT
// sizeof(short) == 2, so frame size == 1 << channels
return Format_channelCount(format);
+ // FIXME must return -1 for non-power of 2
}
+#endif
+
+const NBAIO_Format Format_Invalid = { 0, 0, AUDIO_FORMAT_INVALID, 0 };
enum {
Format_SR_8000,
@@ -51,90 +57,35 @@
Format_C_Mask = 0x18
};
-unsigned Format_sampleRate(NBAIO_Format format)
+unsigned Format_sampleRate(const NBAIO_Format& format)
{
- if (format == Format_Invalid) {
+ if (!Format_isValid(format)) {
return 0;
}
- switch (format & Format_SR_Mask) {
- case Format_SR_8000:
- return 8000;
- case Format_SR_11025:
- return 11025;
- case Format_SR_16000:
- return 16000;
- case Format_SR_22050:
- return 22050;
- case Format_SR_24000:
- return 24000;
- case Format_SR_32000:
- return 32000;
- case Format_SR_44100:
- return 44100;
- case Format_SR_48000:
- return 48000;
- default:
- return 0;
- }
+ return format.mSampleRate;
}
-unsigned Format_channelCount(NBAIO_Format format)
+unsigned Format_channelCount(const NBAIO_Format& format)
{
- if (format == Format_Invalid) {
+ if (!Format_isValid(format)) {
return 0;
}
- switch (format & Format_C_Mask) {
- case Format_C_1:
- return 1;
- case Format_C_2:
- return 2;
- default:
- return 0;
- }
+ return format.mChannelCount;
}
-NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount)
+NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount,
+ audio_format_t format)
{
- NBAIO_Format format;
- switch (sampleRate) {
- case 8000:
- format = Format_SR_8000;
- break;
- case 11025:
- format = Format_SR_11025;
- break;
- case 16000:
- format = Format_SR_16000;
- break;
- case 22050:
- format = Format_SR_22050;
- break;
- case 24000:
- format = Format_SR_24000;
- break;
- case 32000:
- format = Format_SR_32000;
- break;
- case 44100:
- format = Format_SR_44100;
- break;
- case 48000:
- format = Format_SR_48000;
- break;
- default:
+ if (sampleRate == 0 || channelCount == 0 || !audio_is_valid_format(format)) {
return Format_Invalid;
}
- switch (channelCount) {
- case 1:
- format |= Format_C_1;
- break;
- case 2:
- format |= Format_C_2;
- break;
- default:
- return Format_Invalid;
- }
- return format;
+ NBAIO_Format ret;
+ ret.mSampleRate = sampleRate;
+ ret.mChannelCount = channelCount;
+ ret.mFormat = format;
+ ret.mFrameSize = audio_is_linear_pcm(format) ?
+ channelCount * audio_bytes_per_sample(format) : sizeof(uint8_t);
+ return ret;
}
// This is a default implementation; it is expected that subclasses will optimize this.
@@ -216,9 +167,9 @@
{
ALOGV("negotiate offers=%p numOffers=%u countersOffers=%p numCounterOffers=%u",
offers, numOffers, counterOffers, numCounterOffers);
- if (mFormat != Format_Invalid) {
+ if (Format_isValid(mFormat)) {
for (size_t i = 0; i < numOffers; ++i) {
- if (offers[i] == mFormat) {
+ if (Format_isEqual(offers[i], mFormat)) {
mNegotiated = true;
return i;
}
@@ -233,4 +184,17 @@
return (ssize_t) NEGOTIATE;
}
+bool Format_isValid(const NBAIO_Format& format)
+{
+ return format.mSampleRate != 0 && format.mChannelCount != 0 &&
+ format.mFormat != AUDIO_FORMAT_INVALID && format.mFrameSize != 0;
+}
+
+bool Format_isEqual(const NBAIO_Format& format1, const NBAIO_Format& format2)
+{
+ return format1.mSampleRate == format2.mSampleRate &&
+ format1.mChannelCount == format2.mChannelCount && format1.mFormat == format2.mFormat &&
+ format1.mFrameSize == format2.mFrameSize;
+}
+
} // namespace android
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index 045bf64..96738a7 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -26,6 +26,7 @@
#include <cutils/atomic.h>
#include <media/nbaio/NBLog.h>
#include <utils/Log.h>
+#include <utils/String8.h>
namespace android {
@@ -337,25 +338,25 @@
}
i -= length + 3;
}
- if (i > 0) {
- lost += i;
- if (fd >= 0) {
- fdprintf(fd, "%*swarning: lost %u bytes worth of events\n", indent, "", lost);
- } else {
- ALOGI("%*swarning: lost %u bytes worth of events\n", indent, "", lost);
- }
+ mFd = fd;
+ mIndent = indent;
+ String8 timestamp, body;
+ lost += i;
+ if (lost > 0) {
+ body.appendFormat("warning: lost %u bytes worth of events", lost);
+ // TODO timestamp empty here, only other choice to wait for the first timestamp event in the
+ // log to push it out. Consider keeping the timestamp/body between calls to readAt().
+ dumpLine(timestamp, body);
}
size_t width = 1;
while (maxSec >= 10) {
++width;
maxSec /= 10;
}
- char prefix[32];
if (maxSec >= 0) {
- snprintf(prefix, sizeof(prefix), "[%*s] ", width + 4, "");
- } else {
- prefix[0] = '\0';
+ timestamp.appendFormat("[%*s]", width + 4, "");
}
+ bool deferredTimestamp = false;
while (i < avail) {
event = (Event) copy[i];
length = copy[i + 1];
@@ -363,11 +364,8 @@
size_t advance = length + 3;
switch (event) {
case EVENT_STRING:
- if (fd >= 0) {
- fdprintf(fd, "%*s%s%.*s\n", indent, "", prefix, length, (const char *) data);
- } else {
- ALOGI("%*s%s%.*s", indent, "", prefix, length, (const char *) data);
- } break;
+ body.appendFormat("%.*s", length, (const char *) data);
+ break;
case EVENT_TIMESTAMP: {
// already checked that length == sizeof(struct timespec);
memcpy(&ts, data, sizeof(struct timespec));
@@ -400,48 +398,56 @@
prevNsec = tsNext.tv_nsec;
}
size_t n = (j - i) / (sizeof(struct timespec) + 3);
+ if (deferredTimestamp) {
+ dumpLine(timestamp, body);
+ deferredTimestamp = false;
+ }
+ timestamp.clear();
if (n >= kSquashTimestamp) {
- if (fd >= 0) {
- fdprintf(fd, "%*s[%d.%03d to .%.03d by .%.03d to .%.03d]\n", indent, "",
- (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000),
- (int) ((ts.tv_nsec + deltaTotal) / 1000000),
- (int) (deltaMin / 1000000), (int) (deltaMax / 1000000));
- } else {
- ALOGI("%*s[%d.%03d to .%.03d by .%.03d to .%.03d]\n", indent, "",
- (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000),
- (int) ((ts.tv_nsec + deltaTotal) / 1000000),
- (int) (deltaMin / 1000000), (int) (deltaMax / 1000000));
- }
+ timestamp.appendFormat("[%d.%03d to .%.03d by .%.03d to .%.03d]",
+ (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000),
+ (int) ((ts.tv_nsec + deltaTotal) / 1000000),
+ (int) (deltaMin / 1000000), (int) (deltaMax / 1000000));
i = j;
advance = 0;
break;
}
- if (fd >= 0) {
- fdprintf(fd, "%*s[%d.%03d]\n", indent, "", (int) ts.tv_sec,
- (int) (ts.tv_nsec / 1000000));
- } else {
- ALOGI("%*s[%d.%03d]", indent, "", (int) ts.tv_sec,
- (int) (ts.tv_nsec / 1000000));
- }
+ timestamp.appendFormat("[%d.%03d]", (int) ts.tv_sec,
+ (int) (ts.tv_nsec / 1000000));
+ deferredTimestamp = true;
} break;
case EVENT_RESERVED:
default:
- if (fd >= 0) {
- fdprintf(fd, "%*s%swarning: unknown event %d\n", indent, "", prefix, event);
- } else {
- ALOGI("%*s%swarning: unknown event %d", indent, "", prefix, event);
- }
+ body.appendFormat("warning: unknown event %d", event);
break;
}
i += advance;
+
+ if (!body.isEmpty()) {
+ dumpLine(timestamp, body);
+ deferredTimestamp = false;
+ }
+ }
+ if (deferredTimestamp) {
+ dumpLine(timestamp, body);
}
// FIXME it would be more efficient to put a char mCopy[256] as a member variable of the dumper
delete[] copy;
}
+void NBLog::Reader::dumpLine(const String8& timestamp, String8& body)
+{
+ if (mFd >= 0) {
+ fdprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
+ } else {
+ ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string());
+ }
+ body.clear();
+}
+
bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
{
- return iMemory.get() == mIMemory.get();
+ return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
}
} // namespace android
diff --git a/media/libnbaio/Pipe.cpp b/media/libnbaio/Pipe.cpp
index 1c21f9c..28a034c 100644
--- a/media/libnbaio/Pipe.cpp
+++ b/media/libnbaio/Pipe.cpp
@@ -25,7 +25,7 @@
namespace android {
-Pipe::Pipe(size_t maxFrames, NBAIO_Format format) :
+Pipe::Pipe(size_t maxFrames, const NBAIO_Format& format) :
NBAIO_Sink(format),
mMaxFrames(roundup(maxFrames)),
mBuffer(malloc(mMaxFrames * Format_frameSize(format))),
@@ -52,13 +52,13 @@
if (CC_LIKELY(written > count)) {
written = count;
}
- memcpy((char *) mBuffer + (rear << mBitShift), buffer, written << mBitShift);
+ memcpy((char *) mBuffer + (rear * mFrameSize), buffer, written * mFrameSize);
if (CC_UNLIKELY(rear + written == mMaxFrames)) {
if (CC_UNLIKELY((count -= written) > rear)) {
count = rear;
}
if (CC_LIKELY(count > 0)) {
- memcpy(mBuffer, (char *) buffer + (written << mBitShift), count << mBitShift);
+ memcpy(mBuffer, (char *) buffer + (written * mFrameSize), count * mFrameSize);
written += count;
}
}
diff --git a/media/libnbaio/PipeReader.cpp b/media/libnbaio/PipeReader.cpp
index d786b84..c8e4953 100644
--- a/media/libnbaio/PipeReader.cpp
+++ b/media/libnbaio/PipeReader.cpp
@@ -59,7 +59,7 @@
return avail;
}
-ssize_t PipeReader::read(void *buffer, size_t count, int64_t readPTS)
+ssize_t PipeReader::read(void *buffer, size_t count, int64_t readPTS __unused)
{
ssize_t avail = availableToRead();
if (CC_UNLIKELY(avail <= 0)) {
@@ -76,14 +76,14 @@
red = count;
}
// In particular, an overrun during the memcpy will result in reading corrupt data
- memcpy(buffer, (char *) mPipe.mBuffer + (front << mBitShift), red << mBitShift);
+ memcpy(buffer, (char *) mPipe.mBuffer + (front * mFrameSize), red * mFrameSize);
// We could re-read the rear pointer here to detect the corruption, but why bother?
if (CC_UNLIKELY(front + red == mPipe.mMaxFrames)) {
if (CC_UNLIKELY((count -= red) > front)) {
count = front;
}
if (CC_LIKELY(count > 0)) {
- memcpy((char *) buffer + (red << mBitShift), mPipe.mBuffer, count << mBitShift);
+ memcpy((char *) buffer + (red * mFrameSize), mPipe.mBuffer, count * mFrameSize);
red += count;
}
}
diff --git a/media/libnbaio/SourceAudioBufferProvider.cpp b/media/libnbaio/SourceAudioBufferProvider.cpp
index 062fa0f..e21ef48 100644
--- a/media/libnbaio/SourceAudioBufferProvider.cpp
+++ b/media/libnbaio/SourceAudioBufferProvider.cpp
@@ -24,7 +24,7 @@
SourceAudioBufferProvider::SourceAudioBufferProvider(const sp<NBAIO_Source>& source) :
mSource(source),
- // mFrameBitShiftFormat below
+ // mFrameSize below
mAllocated(NULL), mSize(0), mOffset(0), mRemaining(0), mGetCount(0), mFramesReleased(0)
{
ALOG_ASSERT(source != 0);
@@ -37,7 +37,7 @@
numCounterOffers = 0;
index = source->negotiate(counterOffers, 1, NULL, numCounterOffers);
ALOG_ASSERT(index == 0);
- mFrameBitShift = Format_frameBitShift(source->format());
+ mFrameSize = Format_frameSize(source->format());
}
SourceAudioBufferProvider::~SourceAudioBufferProvider()
@@ -54,14 +54,14 @@
if (mRemaining < buffer->frameCount) {
buffer->frameCount = mRemaining;
}
- buffer->raw = (char *) mAllocated + (mOffset << mFrameBitShift);
+ buffer->raw = (char *) mAllocated + (mOffset * mFrameSize);
mGetCount = buffer->frameCount;
return OK;
}
// do we need to reallocate?
if (buffer->frameCount > mSize) {
free(mAllocated);
- mAllocated = malloc(buffer->frameCount << mFrameBitShift);
+ mAllocated = malloc(buffer->frameCount * mFrameSize);
mSize = buffer->frameCount;
}
// read from source
@@ -84,7 +84,7 @@
void SourceAudioBufferProvider::releaseBuffer(Buffer *buffer)
{
ALOG_ASSERT((buffer != NULL) &&
- (buffer->raw == (char *) mAllocated + (mOffset << mFrameBitShift)) &&
+ (buffer->raw == (char *) mAllocated + (mOffset * mFrameSize)) &&
(buffer->frameCount <= mGetCount) &&
(mGetCount <= mRemaining) &&
(mOffset + mRemaining <= mSize));
diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp
index 4d1072f..196f6ee 100644
--- a/media/libstagefright/AACExtractor.cpp
+++ b/media/libstagefright/AACExtractor.cpp
@@ -219,7 +219,7 @@
return new AACSource(mDataSource, mMeta, mOffsetVector, mFrameDurationUs);
}
-sp<MetaData> AACExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+sp<MetaData> AACExtractor::getTrackMetaData(size_t index, uint32_t /* flags */) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
@@ -252,7 +252,7 @@
}
}
-status_t AACSource::start(MetaData *params) {
+status_t AACSource::start(MetaData * /* params */) {
CHECK(!mStarted);
if (mOffsetVector.empty()) {
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
index a6f7cfb..deee8e7 100644
--- a/media/libstagefright/AACWriter.cpp
+++ b/media/libstagefright/AACWriter.cpp
@@ -111,7 +111,7 @@
return OK;
}
-status_t AACWriter::start(MetaData *params) {
+status_t AACWriter::start(MetaData * /* params */) {
if (mInitCheck != OK) {
return mInitCheck;
}
@@ -171,7 +171,7 @@
void *dummy;
pthread_join(mThread, &dummy);
- status_t err = (status_t) dummy;
+ status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
{
status_t status = mSource->stop();
if (err == OK &&
@@ -200,7 +200,7 @@
// static
void *AACWriter::ThreadWrapper(void *me) {
- return (void *) static_cast<AACWriter *>(me)->threadFunc();
+ return (void *)(uintptr_t)static_cast<AACWriter *>(me)->threadFunc();
}
/*
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index bfb730c..9c48587 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -35,7 +35,9 @@
#include <media/hardware/HardwareAPI.h>
+#include <OMX_AudioExt.h>
#include <OMX_Component.h>
+#include <OMX_IndexExt.h>
#include "include/avc_utils.h"
@@ -363,6 +365,7 @@
mIsEncoder(false),
mUseMetadataOnEncoderOutput(false),
mShutdownInProgress(false),
+ mIsConfiguredForAdaptivePlayback(false),
mEncoderDelay(0),
mEncoderPadding(0),
mChannelMaskPresent(false),
@@ -370,7 +373,11 @@
mDequeueCounter(0),
mStoreMetaDataInOutputBuffers(false),
mMetaDataBuffersToSubmit(0),
- mRepeatFrameDelayUs(-1ll) {
+ mRepeatFrameDelayUs(-1ll),
+ mMaxPtsGapUs(-1ll),
+ mTimePerCaptureUs(-1ll),
+ mTimePerFrameUs(-1ll),
+ mCreateInputBuffersSuspended(false) {
mUninitializedState = new UninitializedState(this);
mLoadedState = new LoadedState(this);
mLoadedToIdleState = new LoadedToIdleState(this);
@@ -452,6 +459,18 @@
(new AMessage(kWhatRequestIDRFrame, id()))->post();
}
+// *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED ***
+// Some codecs may return input buffers before having them processed.
+// This causes a halt if we already signaled an EOS on the input
+// port. For now keep submitting an output buffer if there was an
+// EOS on the input port, but not yet on the output port.
+void ACodec::signalSubmitOutputMetaDataBufferIfEOS_workaround() {
+ if (mPortEOS[kPortIndexInput] && !mPortEOS[kPortIndexOutput] &&
+ mMetaDataBuffersToSubmit > 0) {
+ (new AMessage(kWhatSubmitOutputMetaDataBufferIfEOS, id()))->post();
+ }
+}
+
status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
@@ -945,6 +964,8 @@
"audio_decoder.aac", "audio_encoder.aac" },
{ MEDIA_MIMETYPE_AUDIO_VORBIS,
"audio_decoder.vorbis", "audio_encoder.vorbis" },
+ { MEDIA_MIMETYPE_AUDIO_OPUS,
+ "audio_decoder.opus", "audio_encoder.opus" },
{ MEDIA_MIMETYPE_AUDIO_G711_MLAW,
"audio_decoder.g711mlaw", "audio_encoder.g711mlaw" },
{ MEDIA_MIMETYPE_AUDIO_G711_ALAW,
@@ -965,6 +986,10 @@
"audio_decoder.flac", "audio_encoder.flac" },
{ MEDIA_MIMETYPE_AUDIO_MSGSM,
"audio_decoder.gsm", "audio_encoder.gsm" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG2,
+ "video_decoder.mpeg2", "video_encoder.mpeg2" },
+ { MEDIA_MIMETYPE_AUDIO_AC3,
+ "audio_decoder.ac3", "audio_encoder.ac3" },
};
static const size_t kNumMimeToRole =
@@ -1096,6 +1121,20 @@
&mRepeatFrameDelayUs)) {
mRepeatFrameDelayUs = -1ll;
}
+
+ if (!msg->findInt64("max-pts-gap-to-encoder", &mMaxPtsGapUs)) {
+ mMaxPtsGapUs = -1ll;
+ }
+
+ if (!msg->findInt64("time-lapse", &mTimePerCaptureUs)) {
+ mTimePerCaptureUs = -1ll;
+ }
+
+ if (!msg->findInt32(
+ "create-input-buffers-suspended",
+ (int32_t*)&mCreateInputBuffersSuspended)) {
+ mCreateInputBuffersSuspended = false;
+ }
}
// Always try to enable dynamic output buffers on native surface
@@ -1103,16 +1142,59 @@
int32_t haveNativeWindow = msg->findObject("native-window", &obj) &&
obj != NULL;
mStoreMetaDataInOutputBuffers = false;
+ mIsConfiguredForAdaptivePlayback = false;
if (!encoder && video && haveNativeWindow) {
err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexOutput, OMX_TRUE);
if (err != OK) {
- // allow failure
ALOGE("[%s] storeMetaDataInBuffers failed w/ err %d",
mComponentName.c_str(), err);
+
+ // if adaptive playback has been requested, try JB fallback
+ // NOTE: THIS FALLBACK MECHANISM WILL BE REMOVED DUE TO ITS
+ // LARGE MEMORY REQUIREMENT
+
+ // we will not do adaptive playback on software accessed
+ // surfaces as they never had to respond to changes in the
+ // crop window, and we don't trust that they will be able to.
+ int usageBits = 0;
+ bool canDoAdaptivePlayback;
+
+ sp<NativeWindowWrapper> windowWrapper(
+ static_cast<NativeWindowWrapper *>(obj.get()));
+ sp<ANativeWindow> nativeWindow = windowWrapper->getNativeWindow();
+
+ if (nativeWindow->query(
+ nativeWindow.get(),
+ NATIVE_WINDOW_CONSUMER_USAGE_BITS,
+ &usageBits) != OK) {
+ canDoAdaptivePlayback = false;
+ } else {
+ canDoAdaptivePlayback =
+ (usageBits &
+ (GRALLOC_USAGE_SW_READ_MASK |
+ GRALLOC_USAGE_SW_WRITE_MASK)) == 0;
+ }
+
+ int32_t maxWidth = 0, maxHeight = 0;
+ if (canDoAdaptivePlayback &&
+ msg->findInt32("max-width", &maxWidth) &&
+ msg->findInt32("max-height", &maxHeight)) {
+ ALOGV("[%s] prepareForAdaptivePlayback(%dx%d)",
+ mComponentName.c_str(), maxWidth, maxHeight);
+
+ err = mOMX->prepareForAdaptivePlayback(
+ mNode, kPortIndexOutput, OMX_TRUE, maxWidth, maxHeight);
+ ALOGW_IF(err != OK,
+ "[%s] prepareForAdaptivePlayback failed w/ err %d",
+ mComponentName.c_str(), err);
+ mIsConfiguredForAdaptivePlayback = (err == OK);
+ }
+ // allow failure
err = OK;
} else {
ALOGV("[%s] storeMetaDataInBuffers succeeded", mComponentName.c_str());
mStoreMetaDataInOutputBuffers = true;
+ mIsConfiguredForAdaptivePlayback = true;
}
int32_t push;
@@ -1216,6 +1298,15 @@
} else {
err = setupRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
}
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC3)) {
+ int32_t numChannels;
+ int32_t sampleRate;
+ if (!msg->findInt32("channel-count", &numChannels)
+ || !msg->findInt32("sample-rate", &sampleRate)) {
+ err = INVALID_OPERATION;
+ } else {
+ err = setupAC3Codec(encoder, numChannels, sampleRate);
+ }
}
if (err != OK) {
@@ -1412,6 +1503,44 @@
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
}
+status_t ACodec::setupAC3Codec(
+ bool encoder, int32_t numChannels, int32_t sampleRate) {
+ status_t err = setupRawAudioFormat(
+ encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, numChannels);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (encoder) {
+ ALOGW("AC3 encoding is not supported.");
+ return INVALID_OPERATION;
+ }
+
+ OMX_AUDIO_PARAM_ANDROID_AC3TYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexInput;
+
+ err = mOMX->getParameter(
+ mNode,
+ (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+ &def,
+ sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ def.nChannels = numChannels;
+ def.nSampleRate = sampleRate;
+
+ return mOMX->setParameter(
+ mNode,
+ (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+ &def,
+ sizeof(def));
+}
+
static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate(
bool isAMRWB, int32_t bps) {
if (isAMRWB) {
@@ -1795,6 +1924,7 @@
return INVALID_OPERATION;
}
frameRate = (float)tmp;
+ mTimePerFrameUs = (int64_t) (1000000.0f / frameRate);
}
video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f);
@@ -1869,6 +1999,11 @@
err = setupAVCEncoderParameters(msg);
break;
+ case OMX_VIDEO_CodingVP8:
+ case OMX_VIDEO_CodingVP9:
+ err = setupVPXEncoderParameters(msg);
+ break;
+
default:
break;
}
@@ -2200,6 +2335,17 @@
return configureBitrate(bitrate, bitrateMode);
}
+status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg) {
+ int32_t bitrate;
+ if (!msg->findInt32("bitrate", &bitrate)) {
+ return INVALID_OPERATION;
+ }
+
+ OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
+
+ return configureBitrate(bitrate, bitrateMode);
+}
+
status_t ACodec::verifySupportForProfileAndLevel(
int32_t profile, int32_t level) {
OMX_VIDEO_PARAM_PROFILELEVELTYPE params;
@@ -2490,7 +2636,7 @@
{
OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio;
- switch (audioDef->eEncoding) {
+ switch ((int)audioDef->eEncoding) {
case OMX_AUDIO_CodingPCM:
{
OMX_AUDIO_PARAM_PCMMODETYPE params;
@@ -2596,6 +2742,24 @@
break;
}
+ case OMX_AUDIO_CodingAndroidAC3:
+ {
+ OMX_AUDIO_PARAM_ANDROID_AC3TYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = kPortIndexOutput;
+
+ CHECK_EQ((status_t)OK, mOMX->getParameter(
+ mNode,
+ (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+ ¶ms,
+ sizeof(params)));
+
+ notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AC3);
+ notify->setInt32("channel-count", params.nChannels);
+ notify->setInt32("sample-rate", params.nSampleRate);
+ break;
+ }
+
default:
TRESPASS();
}
@@ -2812,7 +2976,8 @@
mCodec(codec) {
}
-ACodec::BaseState::PortMode ACodec::BaseState::getPortMode(OMX_U32 portIndex) {
+ACodec::BaseState::PortMode ACodec::BaseState::getPortMode(
+ OMX_U32 /* portIndex */) {
return KEEP_BUFFERS;
}
@@ -3026,16 +3191,22 @@
sp<ABuffer> buffer;
int32_t err = OK;
bool eos = false;
+ PortMode mode = getPortMode(kPortIndexInput);
if (!msg->findBuffer("buffer", &buffer)) {
+ /* these are unfilled buffers returned by client */
CHECK(msg->findInt32("err", &err));
- ALOGV("[%s] saw error %d instead of an input buffer",
- mCodec->mComponentName.c_str(), err);
+ if (err == OK) {
+ /* buffers with no errors are returned on MediaCodec.flush */
+ mode = KEEP_BUFFERS;
+ } else {
+ ALOGV("[%s] saw error %d instead of an input buffer",
+ mCodec->mComponentName.c_str(), err);
+ eos = true;
+ }
buffer.clear();
-
- eos = true;
}
int32_t tmp;
@@ -3049,8 +3220,6 @@
info->mStatus = BufferInfo::OWNED_BY_US;
- PortMode mode = getPortMode(kPortIndexInput);
-
switch (mode) {
case KEEP_BUFFERS:
{
@@ -3175,11 +3344,11 @@
mCodec->mInputEOSResult = err;
}
break;
-
- default:
- CHECK_EQ((int)mode, (int)FREE_BUFFERS);
- break;
}
+
+ default:
+ CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+ break;
}
}
@@ -3217,8 +3386,8 @@
size_t rangeOffset, size_t rangeLength,
OMX_U32 flags,
int64_t timeUs,
- void *platformPrivate,
- void *dataPtr) {
+ void * /* platformPrivate */,
+ void * /* dataPtr */) {
ALOGV("[%s] onOMXFillBufferDone %p time %lld us, flags = 0x%08lx",
mCodec->mComponentName.c_str(), bufferID, timeUs, flags);
@@ -3270,7 +3439,7 @@
sp<AMessage> reply =
new AMessage(kWhatOutputBufferDrained, mCodec->id());
- if (!mCodec->mSentFormat) {
+ if (!mCodec->mSentFormat && rangeLength > 0) {
mCodec->sendFormatChange(reply);
}
@@ -3355,7 +3524,7 @@
int32_t render;
if (mCodec->mNativeWindow != NULL
&& msg->findInt32("render", &render) && render != 0
- && (info->mData == NULL || info->mData->size() != 0)) {
+ && info->mData != NULL && info->mData->size() != 0) {
// The client wants this buffer to be rendered.
status_t err;
@@ -3630,6 +3799,7 @@
mCodec->mDequeueCounter = 0;
mCodec->mMetaDataBuffersToSubmit = 0;
mCodec->mRepeatFrameDelayUs = -1ll;
+ mCodec->mIsConfiguredForAdaptivePlayback = false;
if (mCodec->mShutdownInProgress) {
bool keepComponentAllocated = mCodec->mKeepComponentAllocated;
@@ -3750,7 +3920,7 @@
}
void ACodec::LoadedState::onCreateInputSurface(
- const sp<AMessage> &msg) {
+ const sp<AMessage> & /* msg */) {
ALOGV("onCreateInputSurface");
sp<AMessage> notify = mCodec->mNotify->dup();
@@ -3778,6 +3948,56 @@
}
}
+ if (err == OK && mCodec->mMaxPtsGapUs > 0ll) {
+ err = mCodec->mOMX->setInternalOption(
+ mCodec->mNode,
+ kPortIndexInput,
+ IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP,
+ &mCodec->mMaxPtsGapUs,
+ sizeof(mCodec->mMaxPtsGapUs));
+
+ if (err != OK) {
+ ALOGE("[%s] Unable to configure max timestamp gap (err %d)",
+ mCodec->mComponentName.c_str(),
+ err);
+ }
+ }
+
+ if (err == OK && mCodec->mTimePerCaptureUs > 0ll
+ && mCodec->mTimePerFrameUs > 0ll) {
+ int64_t timeLapse[2];
+ timeLapse[0] = mCodec->mTimePerFrameUs;
+ timeLapse[1] = mCodec->mTimePerCaptureUs;
+ err = mCodec->mOMX->setInternalOption(
+ mCodec->mNode,
+ kPortIndexInput,
+ IOMX::INTERNAL_OPTION_TIME_LAPSE,
+ &timeLapse[0],
+ sizeof(timeLapse));
+
+ if (err != OK) {
+ ALOGE("[%s] Unable to configure time lapse (err %d)",
+ mCodec->mComponentName.c_str(),
+ err);
+ }
+ }
+
+ if (err == OK && mCodec->mCreateInputBuffersSuspended) {
+ bool suspend = true;
+ err = mCodec->mOMX->setInternalOption(
+ mCodec->mNode,
+ kPortIndexInput,
+ IOMX::INTERNAL_OPTION_SUSPEND,
+ &suspend,
+ sizeof(suspend));
+
+ if (err != OK) {
+ ALOGE("[%s] Unable to configure option to suspend (err %d)",
+ mCodec->mComponentName.c_str(),
+ err);
+ }
+ }
+
if (err == OK) {
notify->setObject("input-surface",
new BufferProducerWrapper(bufferProducer));
@@ -3835,6 +4055,7 @@
bool ACodec::LoadedToIdleState::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
+ case kWhatSetParameters:
case kWhatShutdown:
{
mCodec->deferMessage(msg);
@@ -3901,6 +4122,7 @@
bool ACodec::IdleToExecutingState::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
+ case kWhatSetParameters:
case kWhatShutdown:
{
mCodec->deferMessage(msg);
@@ -3961,7 +4183,7 @@
}
ACodec::BaseState::PortMode ACodec::ExecutingState::getPortMode(
- OMX_U32 portIndex) {
+ OMX_U32 /* portIndex */) {
return RESUBMIT_BUFFERS;
}
@@ -3976,6 +4198,9 @@
break;
}
}
+
+ // *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED ***
+ mCodec->signalSubmitOutputMetaDataBufferIfEOS_workaround();
}
void ACodec::ExecutingState::submitRegularOutputBuffers() {
@@ -4124,6 +4349,19 @@
break;
}
+ // *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED ***
+ case kWhatSubmitOutputMetaDataBufferIfEOS:
+ {
+ if (mCodec->mPortEOS[kPortIndexInput] &&
+ !mCodec->mPortEOS[kPortIndexOutput]) {
+ status_t err = mCodec->submitOutputMetaDataBuffer();
+ if (err == OK) {
+ mCodec->signalSubmitOutputMetaDataBufferIfEOS_workaround();
+ }
+ }
+ return true;
+ }
+
default:
handled = BaseState::onMessageReceived(msg);
break;
@@ -4154,6 +4392,22 @@
}
}
+ int64_t skipFramesBeforeUs;
+ if (params->findInt64("skip-frames-before", &skipFramesBeforeUs)) {
+ status_t err =
+ mOMX->setInternalOption(
+ mNode,
+ kPortIndexInput,
+ IOMX::INTERNAL_OPTION_START_TIME,
+ &skipFramesBeforeUs,
+ sizeof(skipFramesBeforeUs));
+
+ if (err != OK) {
+ ALOGE("Failed to set parameter 'skip-frames-before' (err %d)", err);
+ return err;
+ }
+ }
+
int32_t dropInputFrames;
if (params->findInt32("drop-input-frames", &dropInputFrames)) {
bool suspend = dropInputFrames != 0;
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 03dcbf9..3f592ed 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -189,7 +189,7 @@
mOffsetTable, mOffsetTableLength);
}
-sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t /* flags */) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
@@ -221,7 +221,7 @@
}
}
-status_t AMRSource::start(MetaData *params) {
+status_t AMRSource::start(MetaData * /* params */) {
CHECK(!mStarted);
mOffset = mIsWide ? 9 : 6;
@@ -258,7 +258,7 @@
int64_t seekFrame = seekTimeUs / 20000ll; // 20ms per frame.
mCurrentTimeUs = seekFrame * 20000ll;
- int index = seekFrame / 50;
+ size_t index = seekFrame < 0 ? 0 : seekFrame / 50;
if (index >= mOffsetTableLength) {
index = mOffsetTableLength - 1;
}
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 8d5eec8..653ca36 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -105,7 +105,7 @@
return OK;
}
-status_t AMRWriter::start(MetaData *params) {
+status_t AMRWriter::start(MetaData * /* params */) {
if (mInitCheck != OK) {
return mInitCheck;
}
@@ -162,7 +162,7 @@
void *dummy;
pthread_join(mThread, &dummy);
- status_t err = (status_t) dummy;
+ status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
{
status_t status = mSource->stop();
if (err == OK &&
@@ -191,7 +191,7 @@
// static
void *AMRWriter::ThreadWrapper(void *me) {
- return (void *) static_cast<AMRWriter *>(me)->threadFunc();
+ return (void *)(uintptr_t) static_cast<AMRWriter *>(me)->threadFunc();
}
status_t AMRWriter::threadFunc() {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 1f68b51..0fd1e69 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -15,6 +15,7 @@
CameraSource.cpp \
CameraSourceTimeLapse.cpp \
DataSource.cpp \
+ DataURISource.cpp \
DRMExtractor.cpp \
ESDS.cpp \
FileSource.cpp \
@@ -30,8 +31,10 @@
MediaBufferGroup.cpp \
MediaCodec.cpp \
MediaCodecList.cpp \
+ MediaCodecSource.cpp \
MediaDefs.cpp \
MediaExtractor.cpp \
+ http/MediaHTTP.cpp \
MediaMuxer.cpp \
MediaSource.cpp \
MetaData.cpp \
@@ -55,8 +58,6 @@
WVMExtractor.cpp \
XINGSeeker.cpp \
avc_utils.cpp \
- mp4/FragmentedMP4Parser.cpp \
- mp4/TrackFragment.cpp \
LOCAL_C_INCLUDES:= \
$(TOP)/frameworks/av/include/media/stagefright/timedtext \
@@ -80,6 +81,7 @@
libicuuc \
liblog \
libmedia \
+ libopus \
libsonivox \
libssl \
libstagefright_omx \
@@ -89,6 +91,7 @@
libutils \
libvorbisidec \
libz \
+ libpowermanager
LOCAL_STATIC_LIBRARIES := \
libstagefright_color_conversion \
@@ -102,13 +105,6 @@
libFLAC \
libmedia_helper
-LOCAL_SRC_FILES += \
- chromium_http_stub.cpp
-LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1
-
-LOCAL_SHARED_LIBRARIES += libstlport
-include external/stlport/libstlport.mk
-
LOCAL_SHARED_LIBRARIES += \
libstagefright_enc_common \
libstagefright_avc_common \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index e38e261..8623100 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -363,6 +363,7 @@
mPositionTimeMediaUs = -1;
mPositionTimeRealUs = -1;
mSeeking = false;
+ mSeekTimeUs = 0;
mReachedEOS = false;
mFinalStatus = OK;
mStarted = false;
@@ -409,7 +410,7 @@
// static
size_t AudioPlayer::AudioSinkCallback(
- MediaPlayerBase::AudioSink *audioSink,
+ MediaPlayerBase::AudioSink * /* audioSink */,
void *buffer, size_t size, void *cookie,
MediaPlayerBase::AudioSink::cb_event_t event) {
AudioPlayer *me = (AudioPlayer *)cookie;
@@ -602,15 +603,24 @@
// need to adjust the mStartPosUs for offload decoding since parser
// might not be able to get the exact seek time requested.
- if (refreshSeekTime && useOffload()) {
- if (postSeekComplete) {
- ALOGV("fillBuffer is going to post SEEK_COMPLETE");
- mObserver->postAudioSeekComplete();
- postSeekComplete = false;
- }
+ if (refreshSeekTime) {
+ if (useOffload()) {
+ if (postSeekComplete) {
+ ALOGV("fillBuffer is going to post SEEK_COMPLETE");
+ mObserver->postAudioSeekComplete();
+ postSeekComplete = false;
+ }
- mStartPosUs = mPositionTimeMediaUs;
- ALOGV("adjust seek time to: %.2f", mStartPosUs/ 1E6);
+ mStartPosUs = mPositionTimeMediaUs;
+ ALOGV("adjust seek time to: %.2f", mStartPosUs/ 1E6);
+ }
+ // clear seek time with mLock locked and once we have valid mPositionTimeMediaUs
+ // and mPositionTimeRealUs
+ // before clearing mSeekTimeUs check if a new seek request has been received while
+ // we were reading from the source with mLock released.
+ if (!mSeeking) {
+ mSeekTimeUs = 0;
+ }
}
if (!useOffload()) {
@@ -711,16 +721,27 @@
return result + diffUs;
}
-int64_t AudioPlayer::getOutputPlayPositionUs_l() const
+int64_t AudioPlayer::getOutputPlayPositionUs_l()
{
uint32_t playedSamples = 0;
+ uint32_t sampleRate;
if (mAudioSink != NULL) {
mAudioSink->getPosition(&playedSamples);
+ sampleRate = mAudioSink->getSampleRate();
} else {
mAudioTrack->getPosition(&playedSamples);
+ sampleRate = mAudioTrack->getSampleRate();
+ }
+ if (sampleRate != 0) {
+ mSampleRate = sampleRate;
}
- const int64_t playedUs = (static_cast<int64_t>(playedSamples) * 1000000 ) / mSampleRate;
+ int64_t playedUs;
+ if (mSampleRate != 0) {
+ playedUs = (static_cast<int64_t>(playedSamples) * 1000000 ) / mSampleRate;
+ } else {
+ playedUs = 0;
+ }
// HAL position is relative to the first buffer we sent at mStartPosUs
const int64_t renderedDuration = mStartPosUs + playedUs;
@@ -741,12 +762,10 @@
return mPositionTimeRealUs;
}
- if (mPositionTimeMediaUs < 0 || mPositionTimeRealUs < 0) {
- if (mSeeking) {
- return mSeekTimeUs;
- }
- return 0;
+ if (mPositionTimeMediaUs < 0 || mPositionTimeRealUs < 0) {
+ // mSeekTimeUs is either seek time while seeking or 0 if playback did not start.
+ return mSeekTimeUs;
}
int64_t realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs;
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index bdd842f..d0e0e8e 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -65,10 +65,10 @@
if (status == OK) {
// make sure that the AudioRecord callback never returns more than the maximum
// buffer size
- int frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
+ uint32_t frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
// make sure that the AudioRecord total buffer size is large enough
- int bufCount = 2;
+ size_t bufCount = 2;
while ((bufCount * frameCount) < minFrameCount) {
bufCount++;
}
@@ -76,10 +76,10 @@
mRecord = new AudioRecord(
inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
audio_channel_in_mask_from_count(channelCount),
- bufCount * frameCount,
+ (size_t) (bufCount * frameCount),
AudioRecordCallbackFunction,
this,
- frameCount);
+ frameCount /*notificationFrames*/);
mInitCheck = mRecord->initCheck();
} else {
mInitCheck = status;
@@ -208,7 +208,7 @@
}
status_t AudioSource::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBuffer **out, const ReadOptions * /* options */) {
Mutex::Autolock autoLock(mLock);
*out = NULL;
@@ -236,10 +236,10 @@
memset((uint8_t *) buffer->data(), 0, buffer->range_length());
} else if (elapsedTimeUs < kAutoRampStartUs + kAutoRampDurationUs) {
int32_t autoRampDurationFrames =
- (kAutoRampDurationUs * mSampleRate + 500000LL) / 1000000LL;
+ ((int64_t)kAutoRampDurationUs * mSampleRate + 500000LL) / 1000000LL; //Need type casting
int32_t autoRampStartFrames =
- (kAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL;
+ ((int64_t)kAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL; //Need type casting
int32_t nFrames = mNumFramesReceived - autoRampStartFrames;
rampVolume(nFrames, autoRampDurationFrames,
@@ -278,7 +278,7 @@
// Drop retrieved and previously lost audio data.
if (mNumFramesReceived == 0 && timeUs < mStartTimeUs) {
- mRecord->getInputFramesLost();
+ (void) mRecord->getInputFramesLost();
ALOGV("Drop audio data at %lld/%lld us", timeUs, mStartTimeUs);
return OK;
}
@@ -308,7 +308,7 @@
if (numLostBytes > 0) {
// Loss of audio frames should happen rarely; thus the LOGW should
// not cause a logging spam
- ALOGW("Lost audio record data: %d bytes", numLostBytes);
+ ALOGW("Lost audio record data: %zu bytes", numLostBytes);
}
while (numLostBytes > 0) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 5fbee7e..4bad14b 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -19,6 +19,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "AwesomePlayer"
#define ATRACE_TAG ATRACE_TAG_VIDEO
+#include <inttypes.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -34,6 +35,8 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <media/IMediaHTTPConnection.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -44,6 +47,7 @@
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaHTTP.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXCodec.h>
@@ -82,7 +86,7 @@
protected:
virtual ~AwesomeEvent() {}
- virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+ virtual void fire(TimedEventQueue * /* queue */, int64_t /* now_us */) {
(mPlayer->*mMethod)();
}
@@ -259,6 +263,7 @@
mQueue.cancelEvent(mBufferingEvent->eventID());
mBufferingEventPending = false;
+ mAudioTearDown = false;
}
}
@@ -275,15 +280,20 @@
}
status_t AwesomePlayer::setDataSource(
- const char *uri, const KeyedVector<String8, String8> *headers) {
+ const sp<IMediaHTTPService> &httpService,
+ const char *uri,
+ const KeyedVector<String8, String8> *headers) {
Mutex::Autolock autoLock(mLock);
- return setDataSource_l(uri, headers);
+ return setDataSource_l(httpService, uri, headers);
}
status_t AwesomePlayer::setDataSource_l(
- const char *uri, const KeyedVector<String8, String8> *headers) {
+ const sp<IMediaHTTPService> &httpService,
+ const char *uri,
+ const KeyedVector<String8, String8> *headers) {
reset_l();
+ mHTTPService = httpService;
mUri = uri;
if (headers) {
@@ -580,6 +590,7 @@
mSeekNotificationSent = true;
mSeekTimeUs = 0;
+ mHTTPService.clear();
mUri.setTo("");
mUriHeaders.clear();
@@ -605,6 +616,9 @@
mWatchForAudioSeekComplete = false;
mWatchForAudioEOS = false;
+
+ mMediaRenderingStartGeneration = 0;
+ mStartGeneration = 0;
}
void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
@@ -721,7 +735,7 @@
if ((mFlags & PLAYING) && !eos
&& (cachedDataRemaining < kLowWaterMarkBytes)) {
- ALOGI("cache is running low (< %d) , pausing.",
+ ALOGI("cache is running low (< %zu) , pausing.",
kLowWaterMarkBytes);
modifyFlags(CACHE_UNDERRUN, SET);
pause_l();
@@ -730,12 +744,12 @@
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
} else if (eos || cachedDataRemaining > kHighWaterMarkBytes) {
if (mFlags & CACHE_UNDERRUN) {
- ALOGI("cache has filled up (> %d), resuming.",
+ ALOGI("cache has filled up (> %zu), resuming.",
kHighWaterMarkBytes);
modifyFlags(CACHE_UNDERRUN, CLEAR);
play_l();
} else if (mFlags & PREPARING) {
- ALOGV("cache has filled up (> %d), prepare is done",
+ ALOGV("cache has filled up (> %zu), prepare is done",
kHighWaterMarkBytes);
finishAsyncPrepare_l();
}
@@ -797,7 +811,9 @@
}
}
- postBufferingEvent_l();
+ if (mFlags & (PLAYING | PREPARING | CACHE_UNDERRUN)) {
+ postBufferingEvent_l();
+ }
}
void AwesomePlayer::sendCacheStats() {
@@ -892,6 +908,8 @@
return OK;
}
+ mMediaRenderingStartGeneration = ++mStartGeneration;
+
if (!(mFlags & PREPARED)) {
status_t err = prepare_l();
@@ -927,6 +945,9 @@
if ((err != OK) && mOffloadAudio) {
ALOGI("play_l() cannot create offload output, fallback to sw decode");
+ int64_t curTimeUs;
+ getPosition(&curTimeUs);
+
delete mAudioPlayer;
mAudioPlayer = NULL;
// if the player was started it will take care of stopping the source when destroyed
@@ -942,6 +963,10 @@
if (err != OK) {
mAudioSource.clear();
} else {
+ mSeekNotificationSent = true;
+ if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
+ seekTo_l(curTimeUs);
+ }
createAudioPlayer_l();
err = startAudioPlayer_l(false);
}
@@ -993,6 +1018,10 @@
}
addBatteryData(params);
+ if (isStreamingHTTP()) {
+ postBufferingEvent_l();
+ }
+
return OK;
}
@@ -1183,8 +1212,7 @@
setVideoScalingMode_l(mVideoScalingMode);
if (USE_SURFACE_ALLOC
&& !strncmp(component, "OMX.", 4)
- && strncmp(component, "OMX.google.", 11)
- && strcmp(component, "OMX.Nvidia.mpeg2v.decode")) {
+ && 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.
@@ -1211,6 +1239,12 @@
status_t AwesomePlayer::pause_l(bool at_eos) {
if (!(mFlags & PLAYING)) {
+ if (mAudioTearDown && mAudioTearDownWasPlaying) {
+ ALOGV("pause_l() during teardown and finishSetDataSource_l() mFlags %x" , mFlags);
+ mAudioTearDownWasPlaying = false;
+ notifyListener_l(MEDIA_PAUSED);
+ mMediaRenderingStartGeneration = ++mStartGeneration;
+ }
return OK;
}
@@ -1405,8 +1439,10 @@
mSeekTimeUs = timeUs;
modifyFlags((AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS), CLEAR);
- notifyListener_l(MEDIA_PAUSED);
- mMediaRenderingStartGeneration = ++mStartGeneration;
+ if (mFlags & PLAYING) {
+ notifyListener_l(MEDIA_PAUSED);
+ mMediaRenderingStartGeneration = ++mStartGeneration;
+ }
seekAudioIfNecessary_l();
@@ -1456,7 +1492,7 @@
CHECK(source != NULL);
if (mTextDriver == NULL) {
- mTextDriver = new TimedTextDriver(mListener);
+ mTextDriver = new TimedTextDriver(mListener, mHTTPService);
}
mTextDriver->addInBandTextSource(trackIndex, source);
@@ -1473,7 +1509,13 @@
// This doesn't guarantee that the hardware has a free stream
// but it avoids us attempting to open (and re-open) an offload
// stream to hardware that doesn't have the necessary codec
- mOffloadAudio = canOffloadStream(meta, (mVideoSource != NULL), isStreamingHTTP());
+ audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
+ if (mAudioSink != NULL) {
+ streamType = mAudioSink->getAudioStreamType();
+ }
+
+ mOffloadAudio = canOffloadStream(meta, (mVideoSource != NULL),
+ isStreamingHTTP(), streamType);
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
ALOGV("createAudioPlayer: bypass OMX (raw)");
@@ -1651,6 +1693,16 @@
return;
}
+ // If we paused, then seeked, then resumed, it is possible that we have
+ // signaled SEEK_COMPLETE at a copmletely different media time than where
+ // we are now resuming. Signal new position to media time provider.
+ // Cannot signal another SEEK_COMPLETE, as existing clients may not expect
+ // multiple SEEK_COMPLETE responses to a single seek() request.
+ if (mSeekNotificationSent && abs(mSeekTimeUs - videoTimeUs) > 10000) {
+ // notify if we are resuming more than 10ms away from desired seek time
+ notifyListener_l(MEDIA_SKIPPED);
+ }
+
if (mAudioPlayer != NULL) {
ALOGV("seeking audio to %lld us (%.2f secs).", videoTimeUs, videoTimeUs / 1E6);
@@ -1895,7 +1947,7 @@
++mStats.mNumVideoFramesDropped;
}
- postVideoEvent_l();
+ postVideoEvent_l(0);
return;
}
}
@@ -1922,7 +1974,9 @@
notifyListener_l(MEDIA_INFO, MEDIA_INFO_RENDERING_START);
}
- notifyIfMediaStarted_l();
+ if (mFlags & PLAYING) {
+ notifyIfMediaStarted_l();
+ }
}
mVideoBuffer->release();
@@ -1933,6 +1987,41 @@
return;
}
+ /* get next frame time */
+ if (wasSeeking == NO_SEEK) {
+ MediaSource::ReadOptions options;
+ for (;;) {
+ status_t err = mVideoSource->read(&mVideoBuffer, &options);
+ if (err != OK) {
+ // deal with any errors next time
+ CHECK(mVideoBuffer == NULL);
+ postVideoEvent_l(0);
+ return;
+ }
+
+ if (mVideoBuffer->range_length() != 0) {
+ break;
+ }
+
+ // Some decoders, notably the PV AVC software decoder
+ // return spurious empty buffers that we just want to ignore.
+
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
+ }
+
+ {
+ Mutex::Autolock autoLock(mStatsLock);
+ ++mStats.mNumVideoFramesDecoded;
+ }
+
+ int64_t nextTimeUs;
+ CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &nextTimeUs));
+ int64_t delayUs = nextTimeUs - ts->getRealTimeUs() + mTimeSourceDeltaUs;
+ postVideoEvent_l(delayUs > 10000 ? 10000 : delayUs < 0 ? 0 : delayUs);
+ return;
+ }
+
postVideoEvent_l();
}
@@ -2017,7 +2106,10 @@
mSeekNotificationSent = true;
}
- mSeeking = NO_SEEK;
+ if (mVideoSource == NULL) {
+ // For video the mSeeking flag is always reset in finishSeekIfNecessary
+ mSeeking = NO_SEEK;
+ }
notifyIfMediaStarted_l();
}
@@ -2110,15 +2202,14 @@
if (!strncasecmp("http://", mUri.string(), 7)
|| !strncasecmp("https://", mUri.string(), 8)
|| isWidevineStreaming) {
- mConnectingDataSource = HTTPBase::Create(
- (mFlags & INCOGNITO)
- ? HTTPBase::kFlagIncognito
- : 0);
-
- if (mUIDValid) {
- mConnectingDataSource->setUID(mUID);
+ if (mHTTPService == NULL) {
+ ALOGE("Attempt to play media from http URI without HTTP service.");
+ return UNKNOWN_ERROR;
}
+ sp<IMediaHTTPConnection> conn = mHTTPService->makeHTTPConnection();
+ mConnectingDataSource = new MediaHTTP(conn);
+
String8 cacheConfig;
bool disconnectAtHighwatermark;
NuCachedSource2::RemoveCacheSpecificHeaders(
@@ -2126,6 +2217,10 @@
mLock.unlock();
status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders);
+ // force connection at this point, to avoid a race condition between getMIMEType and the
+ // caching datasource constructed below, which could result in multiple requests to the
+ // server, and/or failed connections.
+ String8 contentType = mConnectingDataSource->getMIMEType();
mLock.lock();
if (err != OK) {
@@ -2156,8 +2251,6 @@
mConnectingDataSource.clear();
- String8 contentType = dataSource->getMIMEType();
-
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
@@ -2213,8 +2306,8 @@
sniffedMIME = tmp.string();
if (meta == NULL
- || !meta->findInt64(
- "meta-data-size", &metaDataSize)) {
+ || !meta->findInt64("meta-data-size",
+ reinterpret_cast<int64_t*>(&metaDataSize))) {
metaDataSize = kHighWaterMarkBytes;
}
@@ -2234,7 +2327,8 @@
}
}
} else {
- dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders);
+ dataSource = DataSource::CreateFromURI(
+ mHTTPService, mUri.string(), &mUriHeaders);
}
if (dataSource == NULL) {
@@ -2301,6 +2395,7 @@
modifyFlags((PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED), CLEAR);
mAsyncPrepareEvent = NULL;
mPreparedCondition.broadcast();
+ mAudioTearDown = false;
}
// static
@@ -2374,6 +2469,20 @@
modifyFlags(PREPARED, SET);
mAsyncPrepareEvent = NULL;
mPreparedCondition.broadcast();
+
+ if (mAudioTearDown) {
+ if (mPrepareResult == OK) {
+ if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
+ seekTo_l(mAudioTearDownPosition);
+ }
+
+ if (mAudioTearDownWasPlaying) {
+ modifyFlags(CACHE_UNDERRUN, CLEAR);
+ play_l();
+ }
+ }
+ mAudioTearDown = false;
+ }
}
uint32_t AwesomePlayer::flags() const {
@@ -2486,12 +2595,12 @@
status_t AwesomePlayer::selectAudioTrack_l(
const sp<MediaSource>& source, size_t trackIndex) {
- ALOGI("selectAudioTrack_l: trackIndex=%d, mFlags=0x%x", trackIndex, mFlags);
+ ALOGI("selectAudioTrack_l: trackIndex=%zu, mFlags=0x%x", trackIndex, mFlags);
{
Mutex::Autolock autoLock(mStatsLock);
if ((ssize_t)trackIndex == mActiveAudioTrackIndex) {
- ALOGI("Track %d is active. Does nothing.", trackIndex);
+ ALOGI("Track %zu is active. Does nothing.", trackIndex);
return OK;
}
//mStats.mFlags = mFlags;
@@ -2564,7 +2673,7 @@
trackCount += mTextDriver->countExternalTracks();
}
if (trackIndex >= trackCount) {
- ALOGE("Track index (%d) is out of range [0, %d)", trackIndex, trackCount);
+ ALOGE("Track index (%zu) is out of range [0, %zu)", trackIndex, trackCount);
return ERROR_OUT_OF_RANGE;
}
@@ -2576,14 +2685,14 @@
isAudioTrack = !strncasecmp(mime, "audio/", 6);
if (!isAudioTrack && strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) != 0) {
- ALOGE("Track %d is not either audio or timed text", trackIndex);
+ ALOGE("Track %zu is not either audio or timed text", trackIndex);
return ERROR_UNSUPPORTED;
}
}
if (isAudioTrack) {
if (!select) {
- ALOGE("Deselect an audio track (%d) is not supported", trackIndex);
+ ALOGE("Deselect an audio track (%zu) is not supported", trackIndex);
return ERROR_UNSUPPORTED;
}
return selectAudioTrack_l(mExtractor->getTrack(trackIndex), trackIndex);
@@ -2661,7 +2770,7 @@
{
Mutex::Autolock autoLock(mLock);
if (mTextDriver == NULL) {
- mTextDriver = new TimedTextDriver(mListener);
+ mTextDriver = new TimedTextDriver(mListener, mHTTPService);
}
// String values written in Parcel are UTF-16 values.
String8 uri(request.readString16());
@@ -2673,7 +2782,7 @@
{
Mutex::Autolock autoLock(mLock);
if (mTextDriver == NULL) {
- mTextDriver = new TimedTextDriver(mListener);
+ mTextDriver = new TimedTextDriver(mListener, mHTTPService);
}
int fd = request.readFileDescriptor();
off64_t offset = request.readInt64();
@@ -2706,7 +2815,8 @@
return mCachedSource != NULL || mWVMExtractor != NULL;
}
-status_t AwesomePlayer::dump(int fd, const Vector<String16> &args) const {
+status_t AwesomePlayer::dump(
+ int fd, const Vector<String16> & /* args */) const {
Mutex::Autolock autoLock(mStatsLock);
FILE *out = fdopen(dup(fd), "w");
@@ -2721,7 +2831,7 @@
fprintf(out, ", flags(0x%08x)", mStats.mFlags);
if (mStats.mBitrate >= 0) {
- fprintf(out, ", bitrate(%lld bps)", mStats.mBitrate);
+ fprintf(out, ", bitrate(%" PRId64 " bps)", mStats.mBitrate);
}
fprintf(out, "\n");
@@ -2729,7 +2839,7 @@
for (size_t i = 0; i < mStats.mTracks.size(); ++i) {
const TrackStat &stat = mStats.mTracks.itemAt(i);
- fprintf(out, " Track %d\n", i + 1);
+ fprintf(out, " Track %zu\n", i + 1);
fprintf(out, " MIME(%s)", stat.mMIME.string());
if (!stat.mDecoderName.isEmpty()) {
@@ -2741,8 +2851,8 @@
if ((ssize_t)i == mStats.mVideoTrackIndex) {
fprintf(out,
" videoDimensions(%d x %d), "
- "numVideoFramesDecoded(%lld), "
- "numVideoFramesDropped(%lld)\n",
+ "numVideoFramesDecoded(%" PRId64 "), "
+ "numVideoFramesDropped(%" PRId64 ")\n",
mStats.mVideoWidth,
mStats.mVideoHeight,
mStats.mNumVideoFramesDecoded,
@@ -2791,7 +2901,7 @@
ALOGV("onAudioTearDownEvent");
// stream info is cleared by reset_l() so copy what we need
- const bool wasPlaying = (mFlags & PLAYING);
+ mAudioTearDownWasPlaying = (mFlags & PLAYING);
KeyedVector<String8, String8> uriHeaders(mUriHeaders);
sp<DataSource> fileSource(mFileSource);
@@ -2800,8 +2910,9 @@
mStatsLock.unlock();
// get current position so we can start recreated stream from here
- int64_t position = 0;
- getPosition(&position);
+ getPosition(&mAudioTearDownPosition);
+
+ sp<IMediaHTTPService> savedHTTPService = mHTTPService;
// Reset and recreate
reset_l();
@@ -2812,7 +2923,7 @@
mFileSource = fileSource;
err = setDataSource_l(fileSource);
} else {
- err = setDataSource_l(uri, &uriHeaders);
+ err = setDataSource_l(savedHTTPService, uri, &uriHeaders);
}
mFlags |= PREPARING;
@@ -2825,21 +2936,8 @@
mAudioTearDown = true;
mIsAsyncPrepare = true;
- // Call parepare for the host decoding
+ // Call prepare for the host decoding
beginPrepareAsync_l();
-
- if (mPrepareResult == OK) {
- if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
- seekTo_l(position);
- }
-
- if (wasPlaying) {
- modifyFlags(CACHE_UNDERRUN, CLEAR);
- play_l();
- }
- }
-
- mAudioTearDown = false;
}
} // namespace android
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 3017fe7..b31e9e8 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -31,6 +31,12 @@
#include <utils/String8.h>
#include <cutils/properties.h>
+#if LOG_NDEBUG
+#define UNUSED_UNLESS_VERBOSE(x) (void)(x)
+#else
+#define UNUSED_UNLESS_VERBOSE(x)
+#endif
+
namespace android {
static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL;
@@ -63,11 +69,14 @@
}
void CameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) {
+ UNUSED_UNLESS_VERBOSE(msgType);
+ UNUSED_UNLESS_VERBOSE(ext1);
+ UNUSED_UNLESS_VERBOSE(ext2);
ALOGV("notify(%d, %d, %d)", msgType, ext1, ext2);
}
void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr,
- camera_frame_metadata_t *metadata) {
+ camera_frame_metadata_t * /* metadata */) {
ALOGV("postData(%d, ptr:%p, size:%d)",
msgType, dataPtr->pointer(), dataPtr->size());
@@ -577,14 +586,15 @@
}
}
-void CameraSource::startCameraRecording() {
+status_t CameraSource::startCameraRecording() {
ALOGV("startCameraRecording");
// Reset the identity to the current thread because media server owns the
// camera and recording is started by the applications. The applications
// will connect to the camera in ICameraRecordingProxy::startRecording.
int64_t token = IPCThreadState::self()->clearCallingIdentity();
+ status_t err;
if (mNumInputBuffers > 0) {
- status_t err = mCamera->sendCommand(
+ err = mCamera->sendCommand(
CAMERA_CMD_SET_VIDEO_BUFFER_COUNT, mNumInputBuffers, 0);
// This could happen for CameraHAL1 clients; thus the failure is
@@ -595,17 +605,25 @@
}
}
+ err = OK;
if (mCameraFlags & FLAGS_HOT_CAMERA) {
mCamera->unlock();
mCamera.clear();
- CHECK_EQ((status_t)OK,
- mCameraRecordingProxy->startRecording(new ProxyListener(this)));
+ if ((err = mCameraRecordingProxy->startRecording(
+ new ProxyListener(this))) != OK) {
+ ALOGE("Failed to start recording, received error: %s (%d)",
+ strerror(-err), err);
+ }
} else {
mCamera->setListener(new CameraSourceListener(this));
mCamera->startRecording();
- CHECK(mCamera->recordingEnabled());
+ if (!mCamera->recordingEnabled()) {
+ err = -EINVAL;
+ ALOGE("Failed to start recording");
+ }
}
IPCThreadState::self()->restoreCallingIdentity(token);
+ return err;
}
status_t CameraSource::start(MetaData *meta) {
@@ -637,10 +655,12 @@
}
}
- startCameraRecording();
+ status_t err;
+ if ((err = startCameraRecording()) == OK) {
+ mStarted = true;
+ }
- mStarted = true;
- return OK;
+ return err;
}
void CameraSource::stopCameraRecording() {
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 20214e8..60cdf66 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -41,13 +41,15 @@
Size videoSize,
int32_t videoFrameRate,
const sp<IGraphicBufferProducer>& surface,
- int64_t timeBetweenFrameCaptureUs) {
+ int64_t timeBetweenFrameCaptureUs,
+ bool storeMetaDataInVideoBuffers) {
CameraSourceTimeLapse *source = new
CameraSourceTimeLapse(camera, proxy, cameraId,
clientName, clientUid,
videoSize, videoFrameRate, surface,
- timeBetweenFrameCaptureUs);
+ timeBetweenFrameCaptureUs,
+ storeMetaDataInVideoBuffers);
if (source != NULL) {
if (source->initCheck() != OK) {
@@ -67,9 +69,11 @@
Size videoSize,
int32_t videoFrameRate,
const sp<IGraphicBufferProducer>& surface,
- int64_t timeBetweenFrameCaptureUs)
+ int64_t timeBetweenFrameCaptureUs,
+ bool storeMetaDataInVideoBuffers)
: CameraSource(camera, proxy, cameraId, clientName, clientUid,
- videoSize, videoFrameRate, surface, true),
+ videoSize, videoFrameRate, surface,
+ storeMetaDataInVideoBuffers),
mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate),
mLastTimeLapseFrameRealTimestampUs(0),
mSkipCurrentFrame(false) {
@@ -81,7 +85,8 @@
mVideoWidth = videoSize.width;
mVideoHeight = videoSize.height;
- if (!trySettingVideoSize(videoSize.width, videoSize.height)) {
+ if (OK == mInitCheck && !trySettingVideoSize(videoSize.width, videoSize.height)) {
+ releaseCamera();
mInitCheck = NO_INIT;
}
@@ -227,7 +232,7 @@
return newMemory;
}
-bool CameraSourceTimeLapse::skipCurrentFrame(int64_t timestampUs) {
+bool CameraSourceTimeLapse::skipCurrentFrame(int64_t /* timestampUs */) {
ALOGV("skipCurrentFrame");
if (mSkipCurrentFrame) {
mSkipCurrentFrame = false;
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index fc6fd9c..6e0f37a 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -16,10 +16,6 @@
#include "include/AMRExtractor.h"
-#if CHROMIUM_AVAILABLE
-#include "include/chromium_http_stub.h"
-#endif
-
#include "include/AACExtractor.h"
#include "include/DRMExtractor.h"
#include "include/FLACExtractor.h"
@@ -35,10 +31,14 @@
#include "matroska/MatroskaExtractor.h"
+#include <media/IMediaHTTPConnection.h>
+#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
+#include <media/stagefright/DataURISource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaHTTP.h>
#include <utils/String8.h>
#include <cutils/properties.h>
@@ -107,6 +107,7 @@
Mutex DataSource::gSnifferMutex;
List<DataSource::SnifferFunc> DataSource::gSniffers;
+bool DataSource::gSniffersRegistered = false;
bool DataSource::sniff(
String8 *mimeType, float *confidence, sp<AMessage> *meta) {
@@ -114,7 +115,13 @@
*confidence = 0.0f;
meta->clear();
- Mutex::Autolock autoLock(gSnifferMutex);
+ {
+ Mutex::Autolock autoLock(gSnifferMutex);
+ if (!gSniffersRegistered) {
+ return false;
+ }
+ }
+
for (List<SnifferFunc>::iterator it = gSniffers.begin();
it != gSniffers.end(); ++it) {
String8 newMimeType;
@@ -133,9 +140,7 @@
}
// static
-void DataSource::RegisterSniffer(SnifferFunc func) {
- Mutex::Autolock autoLock(gSnifferMutex);
-
+void DataSource::RegisterSniffer_l(SnifferFunc func) {
for (List<SnifferFunc>::iterator it = gSniffers.begin();
it != gSniffers.end(); ++it) {
if (*it == func) {
@@ -148,28 +153,36 @@
// static
void DataSource::RegisterDefaultSniffers() {
- RegisterSniffer(SniffMPEG4);
- RegisterSniffer(SniffMatroska);
- RegisterSniffer(SniffOgg);
- RegisterSniffer(SniffWAV);
- RegisterSniffer(SniffFLAC);
- RegisterSniffer(SniffAMR);
- RegisterSniffer(SniffMPEG2TS);
- RegisterSniffer(SniffMP3);
- RegisterSniffer(SniffAAC);
- RegisterSniffer(SniffMPEG2PS);
- RegisterSniffer(SniffWVM);
+ Mutex::Autolock autoLock(gSnifferMutex);
+ if (gSniffersRegistered) {
+ return;
+ }
+
+ RegisterSniffer_l(SniffMPEG4);
+ RegisterSniffer_l(SniffMatroska);
+ RegisterSniffer_l(SniffOgg);
+ RegisterSniffer_l(SniffWAV);
+ RegisterSniffer_l(SniffFLAC);
+ RegisterSniffer_l(SniffAMR);
+ RegisterSniffer_l(SniffMPEG2TS);
+ RegisterSniffer_l(SniffMP3);
+ RegisterSniffer_l(SniffAAC);
+ RegisterSniffer_l(SniffMPEG2PS);
+ RegisterSniffer_l(SniffWVM);
char value[PROPERTY_VALUE_MAX];
if (property_get("drm.service.enabled", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
- RegisterSniffer(SniffDRM);
+ RegisterSniffer_l(SniffDRM);
}
+ gSniffersRegistered = true;
}
// static
sp<DataSource> DataSource::CreateFromURI(
- const char *uri, const KeyedVector<String8, String8> *headers) {
+ const sp<IMediaHTTPService> &httpService,
+ const char *uri,
+ const KeyedVector<String8, String8> *headers) {
bool isWidevine = !strncasecmp("widevine://", uri, 11);
sp<DataSource> source;
@@ -178,7 +191,7 @@
} else if (!strncasecmp("http://", uri, 7)
|| !strncasecmp("https://", uri, 8)
|| isWidevine) {
- sp<HTTPBase> httpSource = HTTPBase::Create();
+ sp<HTTPBase> httpSource = new MediaHTTP(httpService->makeHTTPConnection());
String8 tmp;
if (isWidevine) {
@@ -209,11 +222,8 @@
// in the widevine:// case.
source = httpSource;
}
-
-# if CHROMIUM_AVAILABLE
} else if (!strncasecmp("data:", uri, 5)) {
- source = createDataUriSource(uri);
-#endif
+ source = DataURISource::Create(uri);
} else {
// Assume it's a filename.
source = new FileSource(uri);
diff --git a/media/libstagefright/DataURISource.cpp b/media/libstagefright/DataURISource.cpp
new file mode 100644
index 0000000..377bc85
--- /dev/null
+++ b/media/libstagefright/DataURISource.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/DataURISource.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+
+namespace android {
+
+// static
+sp<DataURISource> DataURISource::Create(const char *uri) {
+ if (strncasecmp("data:", uri, 5)) {
+ return NULL;
+ }
+
+ char *commaPos = strrchr(uri, ',');
+
+ if (commaPos == NULL) {
+ return NULL;
+ }
+
+ sp<ABuffer> buffer;
+
+ AString tmp(&uri[5], commaPos - &uri[5]);
+
+ if (tmp.endsWith(";base64")) {
+ AString encoded(commaPos + 1);
+
+ // Strip CR and LF...
+ for (size_t i = encoded.size(); i-- > 0;) {
+ if (encoded.c_str()[i] == '\r' || encoded.c_str()[i] == '\n') {
+ encoded.erase(i, 1);
+ }
+ }
+
+ buffer = decodeBase64(encoded);
+
+ if (buffer == NULL) {
+ ALOGE("Malformed base64 encoded content found.");
+ return NULL;
+ }
+ } else {
+#if 0
+ size_t dataLen = strlen(uri) - tmp.size() - 6;
+ buffer = new ABuffer(dataLen);
+ memcpy(buffer->data(), commaPos + 1, dataLen);
+
+ // unescape
+#else
+ // MediaPlayer doesn't care for this right now as we don't
+ // play any text-based media.
+ return NULL;
+#endif
+ }
+
+ // We don't really care about charset or mime type.
+
+ return new DataURISource(buffer);
+}
+
+DataURISource::DataURISource(const sp<ABuffer> &buffer)
+ : mBuffer(buffer) {
+}
+
+DataURISource::~DataURISource() {
+}
+
+status_t DataURISource::initCheck() const {
+ return OK;
+}
+
+ssize_t DataURISource::readAt(off64_t offset, void *data, size_t size) {
+ if (offset >= mBuffer->size()) {
+ return 0;
+ }
+
+ size_t copy = mBuffer->size() - offset;
+ if (copy > size) {
+ copy = size;
+ }
+
+ memcpy(data, mBuffer->data() + offset, copy);
+
+ return copy;
+}
+
+status_t DataURISource::getSize(off64_t *size) {
+ *size = mBuffer->size();
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp
index 098fcf9..fa7251c 100644
--- a/media/libstagefright/FLACExtractor.cpp
+++ b/media/libstagefright/FLACExtractor.cpp
@@ -208,55 +208,55 @@
// with the same parameter list, but discard redundant information.
FLAC__StreamDecoderReadStatus FLACParser::read_callback(
- const FLAC__StreamDecoder *decoder, FLAC__byte buffer[],
+ 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,
+ 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,
+ 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,
+ 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)
+ 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__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__StreamDecoder * /* decoder */,
const FLAC__StreamMetadata *metadata, void *client_data)
{
((FLACParser *) client_data)->metadataCallback(metadata);
}
void FLACParser::error_callback(
- const FLAC__StreamDecoder *decoder,
+ const FLAC__StreamDecoder * /* decoder */,
FLAC__StreamDecoderErrorStatus status, void *client_data)
{
((FLACParser *) client_data)->errorCallback(status);
@@ -380,15 +380,21 @@
// 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, unsigned nChannels)
-{
+static void copyMono8(
+ short *dst,
+ const int *const *src,
+ unsigned nSamples,
+ unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i] << 8;
}
}
-static void copyStereo8(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
-{
+static void copyStereo8(
+ short *dst,
+ const int *const *src,
+ unsigned nSamples,
+ unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i] << 8;
*dst++ = src[1][i] << 8;
@@ -404,15 +410,21 @@
}
}
-static void copyMono16(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
-{
+static void copyMono16(
+ short *dst,
+ const int *const *src,
+ unsigned nSamples,
+ unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i];
}
}
-static void copyStereo16(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
-{
+static void copyStereo16(
+ short *dst,
+ const int *const *src,
+ unsigned nSamples,
+ unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i];
*dst++ = src[1][i];
@@ -430,15 +442,21 @@
// 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger
-static void copyMono24(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
-{
+static void copyMono24(
+ short *dst,
+ const int *const *src,
+ unsigned nSamples,
+ unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i] >> 8;
}
}
-static void copyStereo24(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
-{
+static void copyStereo24(
+ short *dst,
+ const int *const *src,
+ unsigned nSamples,
+ unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i] >> 8;
*dst++ = src[1][i] >> 8;
@@ -454,8 +472,11 @@
}
}
-static void copyTrespass(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
-{
+static void copyTrespass(
+ short * /* dst */,
+ const int *const * /* src */,
+ unsigned /* nSamples */,
+ unsigned /* nChannels */) {
TRESPASS();
}
@@ -700,7 +721,7 @@
}
}
-status_t FLACSource::start(MetaData *params)
+status_t FLACSource::start(MetaData * /* params */)
{
ALOGV("FLACSource::start");
@@ -792,8 +813,7 @@
}
sp<MetaData> FLACExtractor::getTrackMetaData(
- size_t index, uint32_t flags)
-{
+ size_t index, uint32_t /* flags */) {
if (mInitCheck != OK || index > 0) {
return NULL;
}
diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp
index 5fa4b6f..ca68c3d 100644
--- a/media/libstagefright/HTTPBase.cpp
+++ b/media/libstagefright/HTTPBase.cpp
@@ -20,10 +20,6 @@
#include "include/HTTPBase.h"
-#if CHROMIUM_AVAILABLE
-#include "include/chromium_http_stub.h"
-#endif
-
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
@@ -40,34 +36,7 @@
mTotalTransferBytes(0),
mPrevBandwidthMeasureTimeUs(0),
mPrevEstimatedBandWidthKbps(0),
- mBandWidthCollectFreqMs(5000),
- mUIDValid(false),
- mUID(0) {
-}
-
-// static
-sp<HTTPBase> HTTPBase::Create(uint32_t flags) {
-#if CHROMIUM_AVAILABLE
- HTTPBase *dataSource = createChromiumHTTPDataSource(flags);
- if (dataSource) {
- return dataSource;
- }
-#endif
- {
- TRESPASS();
-
- return NULL;
- }
-}
-
-// static
-status_t HTTPBase::UpdateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
-#if CHROMIUM_AVAILABLE
- return UpdateChromiumHTTPDataSourceProxyConfig(host, port, exclusionList);
-#else
- return INVALID_OPERATION;
-#endif
+ mBandWidthCollectFreqMs(5000) {
}
void HTTPBase::addBandwidthMeasurement(
@@ -135,21 +104,6 @@
return OK;
}
-void HTTPBase::setUID(uid_t uid) {
- mUIDValid = true;
- mUID = uid;
-}
-
-bool HTTPBase::getUID(uid_t *uid) const {
- if (!mUIDValid) {
- return false;
- }
-
- *uid = mUID;
-
- return true;
-}
-
// static
void HTTPBase::RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag) {
int res = qtaguid_tagSocket(sockfd, kTag, uid);
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 380dab4..4a63152 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -398,7 +398,8 @@
mSeeker);
}
-sp<MetaData> MP3Extractor::getTrackMetaData(size_t index, uint32_t flags) {
+sp<MetaData> MP3Extractor::getTrackMetaData(
+ size_t index, uint32_t /* flags */) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index c9ed5bb..78c12e1 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -555,7 +555,7 @@
return OK;
}
-status_t MPEG2TSWriter::start(MetaData *param) {
+status_t MPEG2TSWriter::start(MetaData * /* param */) {
CHECK(!mStarted);
mStarted = true;
@@ -596,7 +596,8 @@
return !mStarted || (mNumSourcesDone == mSources.size() ? true : false);
}
-status_t MPEG2TSWriter::dump(int fd, const Vector<String16> &args) {
+status_t MPEG2TSWriter::dump(
+ int /* fd */, const Vector<String16> & /* args */) {
return OK;
}
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index cbc169b..2a3fa04 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -39,6 +39,7 @@
#include <utils/String8.h>
#include <byteswap.h>
+#include "include/ID3.h"
namespace android {
@@ -259,7 +260,7 @@
const uint8_t *data = (const uint8_t *)_data;
size_t offset = 0;
while (offset < size) {
- printf("0x%04x ", offset);
+ printf("0x%04zx ", offset);
size_t n = size - offset;
if (n > 16) {
@@ -487,12 +488,12 @@
break;
}
uint32_t chunk_type = ntohl(hdr[1]);
- if (chunk_type == FOURCC('s', 'i', 'd', 'x')) {
- // parse the sidx box too
- continue;
- } else if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
+ if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
// store the offset of the first segment
mMoofOffset = offset;
+ } else if (chunk_type != FOURCC('m', 'd', 'a', 't')) {
+ // keep parsing until we get to the data
+ continue;
}
break;
}
@@ -570,7 +571,8 @@
return size;
}
-status_t MPEG4Extractor::parseDrmSINF(off64_t *offset, off64_t data_offset) {
+status_t MPEG4Extractor::parseDrmSINF(
+ off64_t * /* offset */, off64_t data_offset) {
uint8_t updateIdTag;
if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
return ERROR_IO;
@@ -682,8 +684,9 @@
}
sinf->len = dataLen - 3;
sinf->IPMPData = new char[sinf->len];
+ data_offset += 2;
- if (mDataSource->readAt(data_offset + 2, sinf->IPMPData, sinf->len) < sinf->len) {
+ if (mDataSource->readAt(data_offset, sinf->IPMPData, sinf->len) < sinf->len) {
return ERROR_IO;
}
data_offset += sinf->len;
@@ -910,6 +913,8 @@
case FOURCC('e', 'l', 's', 't'):
{
+ *offset += chunk_size;
+
// See 14496-12 8.6.6
uint8_t version;
if (mDataSource->readAt(data_offset, &version, 1) < 1) {
@@ -962,16 +967,23 @@
mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
int64_t paddingus = duration - (segment_duration + media_time);
+ if (paddingus < 0) {
+ // track duration from media header (which is what kKeyDuration is) might
+ // be slightly shorter than the segment duration, which would make the
+ // padding negative. Clamp to zero.
+ paddingus = 0;
+ }
int64_t paddingsamples = (paddingus * samplerate + 500000) / 1000000;
mLastTrack->meta->setInt32(kKeyEncoderPadding, paddingsamples);
}
}
- *offset += chunk_size;
break;
}
case FOURCC('f', 'r', 'm', 'a'):
{
+ *offset += chunk_size;
+
uint32_t original_fourcc;
if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) {
return ERROR_IO;
@@ -985,12 +997,13 @@
mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
}
- *offset += chunk_size;
break;
}
case FOURCC('t', 'e', 'n', 'c'):
{
+ *offset += chunk_size;
+
if (chunk_size < 32) {
return ERROR_MALFORMED;
}
@@ -1035,23 +1048,25 @@
mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
- *offset += chunk_size;
break;
}
case FOURCC('t', 'k', 'h', 'd'):
{
+ *offset += chunk_size;
+
status_t err;
if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) {
return err;
}
- *offset += chunk_size;
break;
}
case FOURCC('p', 's', 's', 'h'):
{
+ *offset += chunk_size;
+
PsshInfo pssh;
if (mDataSource->readAt(data_offset + 4, &pssh.uuid, 16) < 16) {
@@ -1077,12 +1092,13 @@
}
mPssh.push_back(pssh);
- *offset += chunk_size;
break;
}
case FOURCC('m', 'd', 'h', 'd'):
{
+ *offset += chunk_size;
+
if (chunk_data_size < 4) {
return ERROR_MALFORMED;
}
@@ -1163,7 +1179,6 @@
mLastTrack->meta->setCString(
kKeyMediaLanguage, lang_code);
- *offset += chunk_size;
break;
}
@@ -1330,11 +1345,12 @@
mLastTrack->sampleTable->setChunkOffsetParams(
chunk_type, data_offset, chunk_data_size);
+ *offset += chunk_size;
+
if (err != OK) {
return err;
}
- *offset += chunk_size;
break;
}
@@ -1344,11 +1360,12 @@
mLastTrack->sampleTable->setSampleToChunkParams(
data_offset, chunk_data_size);
+ *offset += chunk_size;
+
if (err != OK) {
return err;
}
- *offset += chunk_size;
break;
}
@@ -1359,6 +1376,8 @@
mLastTrack->sampleTable->setSampleSizeParams(
chunk_type, data_offset, chunk_data_size);
+ *offset += chunk_size;
+
if (err != OK) {
return err;
}
@@ -1379,19 +1398,32 @@
} else {
// No size was specified. Pick a conservatively large size.
int32_t width, height;
- if (mLastTrack->meta->findInt32(kKeyWidth, &width) &&
- mLastTrack->meta->findInt32(kKeyHeight, &height)) {
- mLastTrack->meta->setInt32(kKeyMaxInputSize, width * height * 3 / 2);
- } else {
+ if (!mLastTrack->meta->findInt32(kKeyWidth, &width) ||
+ !mLastTrack->meta->findInt32(kKeyHeight, &height)) {
ALOGE("No width or height, assuming worst case 1080p");
- mLastTrack->meta->setInt32(kKeyMaxInputSize, 3110400);
+ width = 1920;
+ height = 1080;
}
- }
- *offset += chunk_size;
- // Calculate average frame rate.
+ const char *mime;
+ CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
+ if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+ // AVC requires compression ratio of at least 2, and uses
+ // macroblocks
+ max_size = ((width + 15) / 16) * ((height + 15) / 16) * 192;
+ } else {
+ // For all other formats there is no minimum compression
+ // ratio. Use compression ratio of 1.
+ max_size = width * height * 3 / 2;
+ }
+ mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size);
+ }
+
+ // NOTE: setting another piece of metadata invalidates any pointers (such as the
+ // mimetype) previously obtained, so don't cache them.
const char *mime;
CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
+ // Calculate average frame rate.
if (!strncasecmp("video/", mime, 6)) {
size_t nSamples = mLastTrack->sampleTable->countSamples();
int64_t durationUs;
@@ -1409,6 +1441,8 @@
case FOURCC('s', 't', 't', 's'):
{
+ *offset += chunk_size;
+
status_t err =
mLastTrack->sampleTable->setTimeToSampleParams(
data_offset, chunk_data_size);
@@ -1417,12 +1451,13 @@
return err;
}
- *offset += chunk_size;
break;
}
case FOURCC('c', 't', 't', 's'):
{
+ *offset += chunk_size;
+
status_t err =
mLastTrack->sampleTable->setCompositionTimeToSampleParams(
data_offset, chunk_data_size);
@@ -1431,12 +1466,13 @@
return err;
}
- *offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 's'):
{
+ *offset += chunk_size;
+
status_t err =
mLastTrack->sampleTable->setSyncSampleParams(
data_offset, chunk_data_size);
@@ -1445,13 +1481,14 @@
return err;
}
- *offset += chunk_size;
break;
}
// @xyz
case FOURCC('\xA9', 'x', 'y', 'z'):
{
+ *offset += chunk_size;
+
// Best case the total data length inside "@xyz" box
// would be 8, for instance "@xyz" + "\x00\x04\x15\xc7" + "0+0/",
// where "\x00\x04" is the text string length with value = 4,
@@ -1480,12 +1517,13 @@
buffer[location_length] = '\0';
mFileMetaData->setCString(kKeyLocation, buffer);
- *offset += chunk_size;
break;
}
case FOURCC('e', 's', 'd', 's'):
{
+ *offset += chunk_size;
+
if (chunk_data_size < 4) {
return ERROR_MALFORMED;
}
@@ -1523,12 +1561,13 @@
}
}
- *offset += chunk_size;
break;
}
case FOURCC('a', 'v', 'c', 'C'):
{
+ *offset += chunk_size;
+
sp<ABuffer> buffer = new ABuffer(chunk_data_size);
if (mDataSource->readAt(
@@ -1539,12 +1578,12 @@
mLastTrack->meta->setData(
kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);
- *offset += chunk_size;
break;
}
case FOURCC('d', '2', '6', '3'):
{
+ *offset += chunk_size;
/*
* d263 contains a fixed 7 bytes part:
* vendor - 4 bytes
@@ -1570,7 +1609,6 @@
mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
- *offset += chunk_size;
break;
}
@@ -1578,11 +1616,13 @@
{
uint8_t buffer[4];
if (chunk_data_size < (off64_t)sizeof(buffer)) {
+ *offset += chunk_size;
return ERROR_MALFORMED;
}
if (mDataSource->readAt(
data_offset, buffer, 4) < 4) {
+ *offset += chunk_size;
return ERROR_IO;
}
@@ -1616,6 +1656,8 @@
case FOURCC('n', 'a', 'm', 'e'):
case FOURCC('d', 'a', 't', 'a'):
{
+ *offset += chunk_size;
+
if (mPath.size() == 6 && underMetaDataPath(mPath)) {
status_t err = parseITunesMetaData(data_offset, chunk_data_size);
@@ -1624,12 +1666,13 @@
}
}
- *offset += chunk_size;
break;
}
case FOURCC('m', 'v', 'h', 'd'):
{
+ *offset += chunk_size;
+
if (chunk_data_size < 24) {
return ERROR_MALFORMED;
}
@@ -1657,7 +1700,6 @@
mFileMetaData->setCString(kKeyDate, s.string());
- *offset += chunk_size;
break;
}
@@ -1678,6 +1720,8 @@
case FOURCC('h', 'd', 'l', 'r'):
{
+ *offset += chunk_size;
+
uint32_t buffer;
if (mDataSource->readAt(
data_offset + 8, &buffer, 4) < 4) {
@@ -1692,7 +1736,6 @@
mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
}
- *offset += chunk_size;
break;
}
@@ -1717,6 +1760,8 @@
delete[] buffer;
buffer = NULL;
+ // advance read pointer so we don't end up reading this again
+ *offset += chunk_size;
return ERROR_IO;
}
@@ -1731,6 +1776,8 @@
case FOURCC('c', 'o', 'v', 'r'):
{
+ *offset += chunk_size;
+
if (mFileMetaData != NULL) {
ALOGV("chunk_data_size = %lld and data_offset = %lld",
chunk_data_size, data_offset);
@@ -1745,7 +1792,6 @@
buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
}
- *offset += chunk_size;
break;
}
@@ -1756,13 +1802,27 @@
case FOURCC('a', 'l', 'b', 'm'):
case FOURCC('y', 'r', 'r', 'c'):
{
+ *offset += chunk_size;
+
status_t err = parse3GPPMetaData(data_offset, chunk_data_size, depth);
if (err != OK) {
return err;
}
+ break;
+ }
+
+ case FOURCC('I', 'D', '3', '2'):
+ {
*offset += chunk_size;
+
+ if (chunk_data_size < 6) {
+ return ERROR_MALFORMED;
+ }
+
+ parseID3v2MetaData(data_offset + 6);
+
break;
}
@@ -1886,9 +1946,10 @@
ALOGW("sub-sidx boxes not supported yet");
}
bool sap = d3 & 0x80000000;
- bool saptype = d3 >> 28;
- if (!sap || saptype > 2) {
- ALOGW("not a stream access point, or unsupported type");
+ uint32_t saptype = (d3 >> 28) & 7;
+ if (!sap || (saptype != 1 && saptype != 2)) {
+ // type 1 and 2 are sync samples
+ ALOGW("not a stream access point, or unsupported type: %08x", d3);
}
total_duration += d2;
offset += 12;
@@ -2146,7 +2207,7 @@
break;
}
- if (size >= 8 && metadataKey) {
+ if (size >= 8 && metadataKey && !mFileMetaData->hasData(metadataKey)) {
if (metadataKey == kKeyAlbumArt) {
mFileMetaData->setData(
kKeyAlbumArt, MetaData::TYPE_NONE,
@@ -2295,6 +2356,62 @@
return OK;
}
+void MPEG4Extractor::parseID3v2MetaData(off64_t offset) {
+ ID3 id3(mDataSource, true /* ignorev1 */, offset);
+
+ if (id3.isValid()) {
+ struct Map {
+ int key;
+ const char *tag1;
+ const char *tag2;
+ };
+ static const Map kMap[] = {
+ { kKeyAlbum, "TALB", "TAL" },
+ { kKeyArtist, "TPE1", "TP1" },
+ { kKeyAlbumArtist, "TPE2", "TP2" },
+ { kKeyComposer, "TCOM", "TCM" },
+ { kKeyGenre, "TCON", "TCO" },
+ { kKeyTitle, "TIT2", "TT2" },
+ { kKeyYear, "TYE", "TYER" },
+ { kKeyAuthor, "TXT", "TEXT" },
+ { kKeyCDTrackNumber, "TRK", "TRCK" },
+ { kKeyDiscNumber, "TPA", "TPOS" },
+ { kKeyCompilation, "TCP", "TCMP" },
+ };
+ static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
+
+ for (size_t i = 0; i < kNumMapEntries; ++i) {
+ if (!mFileMetaData->hasData(kMap[i].key)) {
+ ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1);
+ if (it->done()) {
+ delete it;
+ it = new ID3::Iterator(id3, kMap[i].tag2);
+ }
+
+ if (it->done()) {
+ delete it;
+ continue;
+ }
+
+ String8 s;
+ it->getString(&s);
+ delete it;
+
+ mFileMetaData->setCString(kMap[i].key, s);
+ }
+ }
+
+ size_t dataSize;
+ String8 mime;
+ const void *data = id3.getAlbumArt(&dataSize, &mime);
+
+ if (data) {
+ mFileMetaData->setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
+ mFileMetaData->setCString(kKeyAlbumArtMIME, mime.string());
+ }
+ }
+}
+
sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
status_t err;
if ((err = readMetaData()) != OK) {
@@ -2351,6 +2468,58 @@
return OK;
}
+typedef enum {
+ //AOT_NONE = -1,
+ //AOT_NULL_OBJECT = 0,
+ //AOT_AAC_MAIN = 1, /**< Main profile */
+ AOT_AAC_LC = 2, /**< Low Complexity object */
+ //AOT_AAC_SSR = 3,
+ //AOT_AAC_LTP = 4,
+ AOT_SBR = 5,
+ //AOT_AAC_SCAL = 6,
+ //AOT_TWIN_VQ = 7,
+ //AOT_CELP = 8,
+ //AOT_HVXC = 9,
+ //AOT_RSVD_10 = 10, /**< (reserved) */
+ //AOT_RSVD_11 = 11, /**< (reserved) */
+ //AOT_TTSI = 12, /**< TTSI Object */
+ //AOT_MAIN_SYNTH = 13, /**< Main Synthetic object */
+ //AOT_WAV_TAB_SYNTH = 14, /**< Wavetable Synthesis object */
+ //AOT_GEN_MIDI = 15, /**< General MIDI object */
+ //AOT_ALG_SYNTH_AUD_FX = 16, /**< Algorithmic Synthesis and Audio FX object */
+ AOT_ER_AAC_LC = 17, /**< Error Resilient(ER) AAC Low Complexity */
+ //AOT_RSVD_18 = 18, /**< (reserved) */
+ //AOT_ER_AAC_LTP = 19, /**< Error Resilient(ER) AAC LTP object */
+ AOT_ER_AAC_SCAL = 20, /**< Error Resilient(ER) AAC Scalable object */
+ //AOT_ER_TWIN_VQ = 21, /**< Error Resilient(ER) TwinVQ object */
+ AOT_ER_BSAC = 22, /**< Error Resilient(ER) BSAC object */
+ AOT_ER_AAC_LD = 23, /**< Error Resilient(ER) AAC LowDelay object */
+ //AOT_ER_CELP = 24, /**< Error Resilient(ER) CELP object */
+ //AOT_ER_HVXC = 25, /**< Error Resilient(ER) HVXC object */
+ //AOT_ER_HILN = 26, /**< Error Resilient(ER) HILN object */
+ //AOT_ER_PARA = 27, /**< Error Resilient(ER) Parametric object */
+ //AOT_RSVD_28 = 28, /**< might become SSC */
+ AOT_PS = 29, /**< PS, Parametric Stereo (includes SBR) */
+ //AOT_MPEGS = 30, /**< MPEG Surround */
+
+ AOT_ESCAPE = 31, /**< Signal AOT uses more than 5 bits */
+
+ //AOT_MP3ONMP4_L1 = 32, /**< MPEG-Layer1 in mp4 */
+ //AOT_MP3ONMP4_L2 = 33, /**< MPEG-Layer2 in mp4 */
+ //AOT_MP3ONMP4_L3 = 34, /**< MPEG-Layer3 in mp4 */
+ //AOT_RSVD_35 = 35, /**< might become DST */
+ //AOT_RSVD_36 = 36, /**< might become ALS */
+ //AOT_AAC_SLS = 37, /**< AAC + SLS */
+ //AOT_SLS = 38, /**< SLS */
+ //AOT_ER_AAC_ELD = 39, /**< AAC Enhanced Low Delay */
+
+ //AOT_USAC = 42, /**< USAC */
+ //AOT_SAOC = 43, /**< SAOC */
+ //AOT_LD_MPEGS = 44, /**< Low Delay MPEG Surround */
+
+ //AOT_RSVD50 = 50, /**< Interim AOT for Rsvd50 */
+} AUDIO_OBJECT_TYPE;
+
status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
const void *esds_data, size_t esds_size) {
ESDS esds(esds_data, esds_size);
@@ -2398,6 +2567,11 @@
return ERROR_MALFORMED;
}
+ static uint32_t kSamplingRate[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+ 16000, 12000, 11025, 8000, 7350
+ };
+
ABitReader br(csd, csd_size);
uint32_t objectType = br.getBits(5);
@@ -2405,6 +2579,9 @@
objectType = 32 + br.getBits(6);
}
+ //keep AOT type
+ mLastTrack->meta->setInt32(kKeyAACAOT, objectType);
+
uint32_t freqIndex = br.getBits(4);
int32_t sampleRate = 0;
@@ -2417,28 +2594,134 @@
numChannels = br.getBits(4);
} else {
numChannels = br.getBits(4);
- if (objectType == 5) {
- // SBR specific config per 14496-3 table 1.13
- freqIndex = br.getBits(4);
- if (freqIndex == 15) {
- if (csd_size < 8) {
- return ERROR_MALFORMED;
- }
- sampleRate = br.getBits(24);
- }
+
+ if (freqIndex == 13 || freqIndex == 14) {
+ return ERROR_MALFORMED;
}
- if (sampleRate == 0) {
- static uint32_t kSamplingRate[] = {
- 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
- 16000, 12000, 11025, 8000, 7350
- };
+ sampleRate = kSamplingRate[freqIndex];
+ }
- if (freqIndex == 13 || freqIndex == 14) {
+ if (objectType == AOT_SBR || objectType == AOT_PS) {//SBR specific config per 14496-3 table 1.13
+ uint32_t extFreqIndex = br.getBits(4);
+ int32_t extSampleRate;
+ if (extFreqIndex == 15) {
+ if (csd_size < 8) {
return ERROR_MALFORMED;
}
+ extSampleRate = br.getBits(24);
+ } else {
+ if (extFreqIndex == 13 || extFreqIndex == 14) {
+ return ERROR_MALFORMED;
+ }
+ extSampleRate = kSamplingRate[extFreqIndex];
+ }
+ //TODO: save the extension sampling rate value in meta data =>
+ // mLastTrack->meta->setInt32(kKeyExtSampleRate, extSampleRate);
+ }
- sampleRate = kSamplingRate[freqIndex];
+ switch (numChannels) {
+ // values defined in 14496-3_2009 amendment-4 Table 1.19 - Channel Configuration
+ case 0:
+ case 1:// FC
+ case 2:// FL FR
+ case 3:// FC, FL FR
+ case 4:// FC, FL FR, RC
+ case 5:// FC, FL FR, SL SR
+ case 6:// FC, FL FR, SL SR, LFE
+ //numChannels already contains the right value
+ break;
+ case 11:// FC, FL FR, SL SR, RC, LFE
+ numChannels = 7;
+ break;
+ case 7: // FC, FCL FCR, FL FR, SL SR, LFE
+ case 12:// FC, FL FR, SL SR, RL RR, LFE
+ case 14:// FC, FL FR, SL SR, LFE, FHL FHR
+ numChannels = 8;
+ break;
+ default:
+ return ERROR_UNSUPPORTED;
+ }
+
+ {
+ if (objectType == AOT_SBR || objectType == AOT_PS) {
+ const int32_t extensionSamplingFrequency = br.getBits(4);
+ objectType = br.getBits(5);
+
+ if (objectType == AOT_ESCAPE) {
+ objectType = 32 + br.getBits(6);
+ }
+ }
+ if (objectType == AOT_AAC_LC || objectType == AOT_ER_AAC_LC ||
+ objectType == AOT_ER_AAC_LD || objectType == AOT_ER_AAC_SCAL ||
+ objectType == AOT_ER_BSAC) {
+ const int32_t frameLengthFlag = br.getBits(1);
+
+ const int32_t dependsOnCoreCoder = br.getBits(1);
+
+ if (dependsOnCoreCoder ) {
+ const int32_t coreCoderDelay = br.getBits(14);
+ }
+
+ const int32_t extensionFlag = br.getBits(1);
+
+ if (numChannels == 0 ) {
+ int32_t channelsEffectiveNum = 0;
+ int32_t channelsNum = 0;
+ const int32_t ElementInstanceTag = br.getBits(4);
+ const int32_t Profile = br.getBits(2);
+ const int32_t SamplingFrequencyIndex = br.getBits(4);
+ const int32_t NumFrontChannelElements = br.getBits(4);
+ const int32_t NumSideChannelElements = br.getBits(4);
+ const int32_t NumBackChannelElements = br.getBits(4);
+ const int32_t NumLfeChannelElements = br.getBits(2);
+ const int32_t NumAssocDataElements = br.getBits(3);
+ const int32_t NumValidCcElements = br.getBits(4);
+
+ const int32_t MonoMixdownPresent = br.getBits(1);
+ if (MonoMixdownPresent != 0) {
+ const int32_t MonoMixdownElementNumber = br.getBits(4);
+ }
+
+ const int32_t StereoMixdownPresent = br.getBits(1);
+ if (StereoMixdownPresent != 0) {
+ const int32_t StereoMixdownElementNumber = br.getBits(4);
+ }
+
+ const int32_t MatrixMixdownIndexPresent = br.getBits(1);
+ if (MatrixMixdownIndexPresent != 0) {
+ const int32_t MatrixMixdownIndex = br.getBits(2);
+ const int32_t PseudoSurroundEnable = br.getBits(1);
+ }
+
+ int i;
+ for (i=0; i < NumFrontChannelElements; i++) {
+ const int32_t FrontElementIsCpe = br.getBits(1);
+ const int32_t FrontElementTagSelect = br.getBits(4);
+ channelsNum += FrontElementIsCpe ? 2 : 1;
+ }
+
+ for (i=0; i < NumSideChannelElements; i++) {
+ const int32_t SideElementIsCpe = br.getBits(1);
+ const int32_t SideElementTagSelect = br.getBits(4);
+ channelsNum += SideElementIsCpe ? 2 : 1;
+ }
+
+ for (i=0; i < NumBackChannelElements; i++) {
+ const int32_t BackElementIsCpe = br.getBits(1);
+ const int32_t BackElementTagSelect = br.getBits(4);
+ channelsNum += BackElementIsCpe ? 2 : 1;
+ }
+ channelsEffectiveNum = channelsNum;
+
+ for (i=0; i < NumLfeChannelElements; i++) {
+ const int32_t LfeElementTagSelect = br.getBits(4);
+ channelsNum += 1;
+ }
+ ALOGV("mpeg4 audio channelsNum = %d", channelsNum);
+ ALOGV("mpeg4 audio channelsEffectiveNum = %d", channelsEffectiveNum);
+ numChannels = channelsNum;
+ }
}
}
@@ -2642,9 +2925,20 @@
}
}
if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
- // *offset points to the mdat box following this moof
- parseChunk(offset); // doesn't actually parse it, just updates offset
- mNextMoofOffset = *offset;
+ // *offset points to the box following this moof. Find the next moof from there.
+
+ while (true) {
+ if (mDataSource->readAt(*offset, hdr, 8) < 8) {
+ return ERROR_END_OF_STREAM;
+ }
+ chunk_size = ntohl(hdr[0]);
+ chunk_type = ntohl(hdr[1]);
+ if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
+ mNextMoofOffset = *offset;
+ break;
+ }
+ *offset += chunk_size;
+ }
}
break;
}
@@ -2703,7 +2997,8 @@
return OK;
}
-status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size) {
+status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(
+ off64_t offset, off64_t /* size */) {
ALOGV("parseSampleAuxiliaryInformationSizes");
// 14496-12 8.7.12
uint8_t version;
@@ -2765,7 +3060,8 @@
return OK;
}
-status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size) {
+status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(
+ off64_t offset, off64_t /* size */) {
ALOGV("parseSampleAuxiliaryInformationOffsets");
// 14496-12 8.7.13
uint8_t version;
@@ -3447,7 +3743,7 @@
const SidxEntry *se = &mSegments[i];
if (totalTime + se->mDurationUs > seekTimeUs) {
// The requested time is somewhere in this segment
- if ((mode == ReadOptions::SEEK_NEXT_SYNC) ||
+ if ((mode == ReadOptions::SEEK_NEXT_SYNC && seekTimeUs > totalTime) ||
(mode == ReadOptions::SEEK_CLOSEST_SYNC &&
(seekTimeUs - totalTime) > (totalTime + se->mDurationUs - seekTimeUs))) {
// requested next sync, or closest sync and it was closer to the end of
@@ -3460,11 +3756,19 @@
totalTime += se->mDurationUs;
totalOffset += se->mSize;
}
- mCurrentMoofOffset = totalOffset;
- mCurrentSamples.clear();
- mCurrentSampleIndex = 0;
- parseChunk(&totalOffset);
- mCurrentTime = totalTime * mTimescale / 1000000ll;
+ mCurrentMoofOffset = totalOffset;
+ mCurrentSamples.clear();
+ mCurrentSampleIndex = 0;
+ parseChunk(&totalOffset);
+ mCurrentTime = totalTime * mTimescale / 1000000ll;
+ } else {
+ // without sidx boxes, we can only seek to 0
+ mCurrentMoofOffset = mFirstMoofOffset;
+ mCurrentSamples.clear();
+ mCurrentSampleIndex = 0;
+ off64_t tmp = mCurrentMoofOffset;
+ parseChunk(&tmp);
+ mCurrentTime = 0;
}
if (mBuffer != NULL) {
@@ -3476,7 +3780,7 @@
}
off64_t offset = 0;
- size_t size;
+ size_t size = 0;
uint32_t cts = 0;
bool isSyncSample = false;
bool newBuffer = false;
@@ -3484,16 +3788,18 @@
newBuffer = true;
if (mCurrentSampleIndex >= mCurrentSamples.size()) {
- // move to next fragment
- Sample lastSample = mCurrentSamples[mCurrentSamples.size() - 1];
- off64_t nextMoof = mNextMoofOffset; // lastSample.offset + lastSample.size;
+ // move to next fragment if there is one
+ if (mNextMoofOffset <= mCurrentMoofOffset) {
+ return ERROR_END_OF_STREAM;
+ }
+ off64_t nextMoof = mNextMoofOffset;
mCurrentMoofOffset = nextMoof;
mCurrentSamples.clear();
mCurrentSampleIndex = 0;
parseChunk(&nextMoof);
- if (mCurrentSampleIndex >= mCurrentSamples.size()) {
- return ERROR_END_OF_STREAM;
- }
+ if (mCurrentSampleIndex >= mCurrentSamples.size()) {
+ return ERROR_END_OF_STREAM;
+ }
}
const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index a0f17b5..c839560 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -16,6 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MPEG4Writer"
+#include <inttypes.h>
#include <utils/Log.h>
#include <arpa/inet.h>
@@ -43,7 +44,9 @@
namespace android {
static const int64_t kMinStreamableFileSizeInBytes = 5 * 1024 * 1024;
-static const int64_t kMax32BitFileSize = 0x007fffffffLL;
+static const int64_t kMax32BitFileSize = 0x00ffffffffLL; // 2^32-1 : max FAT32
+ // filesystem file size
+ // used by most SD cards
static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
static const uint8_t kNalUnitTypePicParamSet = 0x08;
static const int64_t kInitialDelayTimeUs = 700000LL;
@@ -406,7 +409,7 @@
}
status_t MPEG4Writer::Track::dump(
- int fd, const Vector<String16>& args) const {
+ int fd, const Vector<String16>& /* args */) const {
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
@@ -417,7 +420,7 @@
result.append(buffer);
snprintf(buffer, SIZE, " frames encoded : %d\n", mStszTableEntries->count());
result.append(buffer);
- snprintf(buffer, SIZE, " duration encoded : %lld us\n", mTrackDurationUs);
+ snprintf(buffer, SIZE, " duration encoded : %" PRId64 " us\n", mTrackDurationUs);
result.append(buffer);
::write(fd, result.string(), result.size());
return OK;
@@ -860,11 +863,11 @@
// Fix up the size of the 'mdat' chunk.
if (mUse32BitOffset) {
lseek64(mFd, mMdatOffset, SEEK_SET);
- int32_t size = htonl(static_cast<int32_t>(mOffset - mMdatOffset));
+ uint32_t size = htonl(static_cast<uint32_t>(mOffset - mMdatOffset));
::write(mFd, &size, 4);
} else {
lseek64(mFd, mMdatOffset + 8, SEEK_SET);
- int64_t size = mOffset - mMdatOffset;
+ uint64_t size = mOffset - mMdatOffset;
size = hton64(size);
::write(mFd, &size, 8);
}
@@ -972,13 +975,16 @@
if (param && param->findInt32(kKeyFileType, &fileType) &&
fileType != OUTPUT_FORMAT_MPEG_4) {
writeFourcc("3gp4");
- } else {
+ writeInt32(0);
writeFourcc("isom");
+ writeFourcc("3gp4");
+ } else {
+ writeFourcc("mp42");
+ writeInt32(0);
+ writeFourcc("isom");
+ writeFourcc("mp42");
}
- writeInt32(0);
- writeFourcc("isom");
- writeFourcc("3gp4");
endBox();
}
@@ -1404,7 +1410,7 @@
size_t sampleCount, int32_t duration) {
if (duration == 0) {
- ALOGW("0-duration samples found: %d", sampleCount);
+ ALOGW("0-duration samples found: %zu", sampleCount);
}
mSttsTableEntries->add(htonl(sampleCount));
mSttsTableEntries->add(htonl(duration));
@@ -1584,7 +1590,7 @@
sendSessionSummary();
mChunkInfos.clear();
- ALOGD("%d chunks are written in the last batch", outstandingChunks);
+ ALOGD("%zu chunks are written in the last batch", outstandingChunks);
}
bool MPEG4Writer::findChunkToWrite(Chunk *chunk) {
@@ -1760,7 +1766,7 @@
}
status_t MPEG4Writer::Track::stop() {
- ALOGD("Stopping %s track", mIsAudio? "Audio": "Video");
+ ALOGD("%s track stopping", mIsAudio? "Audio": "Video");
if (!mStarted) {
ALOGE("Stop() called but track is not started");
return ERROR_END_OF_STREAM;
@@ -1771,18 +1777,13 @@
}
mDone = true;
+ ALOGD("%s track source stopping", mIsAudio? "Audio": "Video");
+ mSource->stop();
+ ALOGD("%s track source stopped", mIsAudio? "Audio": "Video");
+
void *dummy;
pthread_join(mThread, &dummy);
-
- status_t err = (status_t) dummy;
-
- ALOGD("Stopping %s track source", mIsAudio? "Audio": "Video");
- {
- status_t status = mSource->stop();
- if (err == OK && status != OK && status != ERROR_END_OF_STREAM) {
- err = status;
- }
- }
+ status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
ALOGD("%s track stopped", mIsAudio? "Audio": "Video");
return err;
@@ -1797,7 +1798,7 @@
Track *track = static_cast<Track *>(me);
status_t err = track->threadEntry();
- return (void *) err;
+ return (void *)(uintptr_t)err;
}
static void getNalUnitType(uint8_t byte, uint8_t* type) {
@@ -1869,7 +1870,7 @@
// 2 bytes for each of the parameter set length field
// plus the 7 bytes for the header
if (size < 4 + 7) {
- ALOGE("Codec specific data length too short: %d", size);
+ ALOGE("Codec specific data length too short: %zu", size);
return ERROR_MALFORMED;
}
@@ -1938,7 +1939,7 @@
}
if (nSeqParamSets > 0x1F) {
- ALOGE("Too many seq parameter sets (%d) found", nSeqParamSets);
+ ALOGE("Too many seq parameter sets (%zu) found", nSeqParamSets);
return ERROR_MALFORMED;
}
}
@@ -1951,7 +1952,7 @@
return ERROR_MALFORMED;
}
if (nPicParamSets > 0xFF) {
- ALOGE("Too many pic parameter sets (%d) found", nPicParamSets);
+ ALOGE("Too many pic parameter sets (%zd) found", nPicParamSets);
return ERROR_MALFORMED;
}
}
@@ -1981,7 +1982,7 @@
}
if (size < 4) {
- ALOGE("Codec specific data length too short: %d", size);
+ ALOGE("Codec specific data length too short: %zu", size);
return ERROR_MALFORMED;
}
@@ -2279,6 +2280,22 @@
return UNKNOWN_ERROR;
}
+ // if the duration is different for this sample, see if it is close enough to the previous
+ // duration that we can fudge it and use the same value, to avoid filling the stts table
+ // with lots of near-identical entries.
+ // "close enough" here means that the current duration needs to be adjusted by less
+ // than 0.1 milliseconds
+ if (lastDurationTicks && (currDurationTicks != lastDurationTicks)) {
+ int64_t deltaUs = ((lastDurationTicks - currDurationTicks) * 1000000LL
+ + (mTimeScale / 2)) / mTimeScale;
+ if (deltaUs > -100 && deltaUs < 100) {
+ // use previous ticks, and adjust timestamp as if it was actually that number
+ // of ticks
+ currDurationTicks = lastDurationTicks;
+ timestampUs += deltaUs;
+ }
+ }
+
mStszTableEntries->add(htonl(sampleSize));
if (mStszTableEntries->count() > 2) {
diff --git a/media/libstagefright/MediaAdapter.cpp b/media/libstagefright/MediaAdapter.cpp
index 2484212..d680e0c 100644
--- a/media/libstagefright/MediaAdapter.cpp
+++ b/media/libstagefright/MediaAdapter.cpp
@@ -36,7 +36,7 @@
CHECK(mCurrentMediaBuffer == NULL);
}
-status_t MediaAdapter::start(MetaData *params) {
+status_t MediaAdapter::start(MetaData * /* params */) {
Mutex::Autolock autoLock(mAdapterLock);
if (!mStarted) {
mStarted = true;
@@ -75,7 +75,7 @@
}
status_t MediaAdapter::read(
- MediaBuffer **buffer, const ReadOptions *options) {
+ MediaBuffer **buffer, const ReadOptions * /* options */) {
Mutex::Autolock autoLock(mAdapterLock);
if (!mStarted) {
ALOGV("Read before even started!");
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index e299caf..fe21296 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -115,7 +115,7 @@
if (codecIdx >= 0) {
Vector<AString> types;
if (mcl->getSupportedTypes(codecIdx, &types) == OK) {
- for (int i = 0; i < types.size(); i++) {
+ for (size_t i = 0; i < types.size(); i++) {
if (types[i].startsWith("video/")) {
needDedicatedLooper = true;
break;
@@ -1506,7 +1506,8 @@
info->mOwnedByClient = false;
if (portIndex == kPortIndexInput) {
- msg->setInt32("err", ERROR_END_OF_STREAM);
+ /* no error, just returning buffers */
+ msg->setInt32("err", OK);
}
msg->post();
}
@@ -1679,7 +1680,7 @@
return -EACCES;
}
- if (render && (info->mData == NULL || info->mData->size() != 0)) {
+ if (render && info->mData != NULL && info->mData->size() != 0) {
info->mNotify->setInt32("render", true);
if (mSoftRenderer != NULL) {
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
new file mode 100644
index 0000000..924173c
--- /dev/null
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -0,0 +1,881 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodecSource"
+#define DEBUG_DRIFT_TIME 0
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <media/ICrypto.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MediaCodecSource.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static void ReleaseMediaBufferReference(const sp<ABuffer> &accessUnit) {
+ void *mbuf;
+ if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
+ && mbuf != NULL) {
+ ALOGV("releasing mbuf %p", mbuf);
+
+ accessUnit->meta()->setPointer("mediaBuffer", NULL);
+
+ static_cast<MediaBuffer *>(mbuf)->release();
+ mbuf = NULL;
+ }
+}
+
+struct MediaCodecSource::Puller : public AHandler {
+ Puller(const sp<MediaSource> &source);
+
+ status_t start(const sp<MetaData> &meta, const sp<AMessage> ¬ify);
+ void stopAsync();
+
+ void pause();
+ void resume();
+
+protected:
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+ virtual ~Puller();
+
+private:
+ enum {
+ kWhatStart = 'msta',
+ kWhatStop,
+ kWhatPull,
+ kWhatPause,
+ kWhatResume,
+ };
+
+ sp<MediaSource> mSource;
+ sp<AMessage> mNotify;
+ sp<ALooper> mLooper;
+ int32_t mPullGeneration;
+ bool mIsAudio;
+ bool mPaused;
+ bool mReachedEOS;
+
+ status_t postSynchronouslyAndReturnError(const sp<AMessage> &msg);
+ void schedulePull();
+ void handleEOS();
+
+ DISALLOW_EVIL_CONSTRUCTORS(Puller);
+};
+
+MediaCodecSource::Puller::Puller(const sp<MediaSource> &source)
+ : mSource(source),
+ mLooper(new ALooper()),
+ mPullGeneration(0),
+ mIsAudio(false),
+ mPaused(false),
+ mReachedEOS(false) {
+ sp<MetaData> meta = source->getFormat();
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ mIsAudio = !strncasecmp(mime, "audio/", 6);
+
+ mLooper->setName("pull_looper");
+}
+
+MediaCodecSource::Puller::~Puller() {
+ mLooper->unregisterHandler(id());
+ mLooper->stop();
+}
+
+status_t MediaCodecSource::Puller::postSynchronouslyAndReturnError(
+ const sp<AMessage> &msg) {
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (!response->findInt32("err", &err)) {
+ err = OK;
+ }
+
+ return err;
+}
+
+status_t MediaCodecSource::Puller::start(const sp<MetaData> &meta,
+ const sp<AMessage> ¬ify) {
+ ALOGV("puller (%s) start", mIsAudio ? "audio" : "video");
+ mLooper->start(
+ false /* runOnCallingThread */,
+ false /* canCallJava */,
+ PRIORITY_AUDIO);
+ mLooper->registerHandler(this);
+ mNotify = notify;
+
+ sp<AMessage> msg = new AMessage(kWhatStart, id());
+ msg->setObject("meta", meta);
+ return postSynchronouslyAndReturnError(msg);
+}
+
+void MediaCodecSource::Puller::stopAsync() {
+ ALOGV("puller (%s) stopAsync", mIsAudio ? "audio" : "video");
+ (new AMessage(kWhatStop, id()))->post();
+}
+
+void MediaCodecSource::Puller::pause() {
+ (new AMessage(kWhatPause, id()))->post();
+}
+
+void MediaCodecSource::Puller::resume() {
+ (new AMessage(kWhatResume, id()))->post();
+}
+
+void MediaCodecSource::Puller::schedulePull() {
+ sp<AMessage> msg = new AMessage(kWhatPull, id());
+ msg->setInt32("generation", mPullGeneration);
+ msg->post();
+}
+
+void MediaCodecSource::Puller::handleEOS() {
+ if (!mReachedEOS) {
+ ALOGV("puller (%s) posting EOS", mIsAudio ? "audio" : "video");
+ mReachedEOS = true;
+ sp<AMessage> notify = mNotify->dup();
+ notify->setPointer("accessUnit", NULL);
+ notify->post();
+ }
+}
+
+void MediaCodecSource::Puller::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatStart:
+ {
+ sp<RefBase> obj;
+ CHECK(msg->findObject("meta", &obj));
+
+ mReachedEOS = false;
+
+ status_t err = mSource->start(static_cast<MetaData *>(obj.get()));
+
+ if (err == OK) {
+ schedulePull();
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatStop:
+ {
+ ALOGV("source (%s) stopping", mIsAudio ? "audio" : "video");
+ mSource->stop();
+ ALOGV("source (%s) stopped", mIsAudio ? "audio" : "video");
+ ++mPullGeneration;
+
+ handleEOS();
+ break;
+ }
+
+ case kWhatPull:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mPullGeneration) {
+ break;
+ }
+
+ MediaBuffer *mbuf;
+ status_t err = mSource->read(&mbuf);
+
+ if (mPaused) {
+ if (err == OK) {
+ mbuf->release();
+ mbuf = NULL;
+ }
+
+ msg->post();
+ break;
+ }
+
+ if (err != OK) {
+ if (err == ERROR_END_OF_STREAM) {
+ ALOGV("stream ended, mbuf %p", mbuf);
+ } else {
+ ALOGE("error %d reading stream.", err);
+ }
+ handleEOS();
+ } else {
+ sp<AMessage> notify = mNotify->dup();
+
+ notify->setPointer("accessUnit", mbuf);
+ notify->post();
+
+ msg->post();
+ }
+ break;
+ }
+
+ case kWhatPause:
+ {
+ mPaused = true;
+ break;
+ }
+
+ case kWhatResume:
+ {
+ mPaused = false;
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+// static
+sp<MediaCodecSource> MediaCodecSource::Create(
+ const sp<ALooper> &looper,
+ const sp<AMessage> &format,
+ const sp<MediaSource> &source,
+ uint32_t flags) {
+ sp<MediaCodecSource> mediaSource =
+ new MediaCodecSource(looper, format, source, flags);
+
+ if (mediaSource->init() == OK) {
+ return mediaSource;
+ }
+ return NULL;
+}
+
+status_t MediaCodecSource::start(MetaData* params) {
+ sp<AMessage> msg = new AMessage(kWhatStart, mReflector->id());
+ msg->setObject("meta", params);
+ return postSynchronouslyAndReturnError(msg);
+}
+
+status_t MediaCodecSource::stop() {
+ sp<AMessage> msg = new AMessage(kWhatStop, mReflector->id());
+ return postSynchronouslyAndReturnError(msg);
+}
+
+status_t MediaCodecSource::pause() {
+ (new AMessage(kWhatPause, mReflector->id()))->post();
+ return OK;
+}
+
+sp<IGraphicBufferProducer> MediaCodecSource::getGraphicBufferProducer() {
+ CHECK(mFlags & FLAG_USE_SURFACE_INPUT);
+ return mGraphicBufferProducer;
+}
+
+status_t MediaCodecSource::read(
+ MediaBuffer** buffer, const ReadOptions* /* options */) {
+ Mutex::Autolock autolock(mOutputBufferLock);
+
+ *buffer = NULL;
+ while (mOutputBufferQueue.size() == 0 && !mEncodedReachedEOS) {
+ mOutputBufferCond.wait(mOutputBufferLock);
+ }
+ if (!mEncodedReachedEOS) {
+ *buffer = *mOutputBufferQueue.begin();
+ mOutputBufferQueue.erase(mOutputBufferQueue.begin());
+ return OK;
+ }
+ return mErrorCode;
+}
+
+void MediaCodecSource::signalBufferReturned(MediaBuffer *buffer) {
+ buffer->setObserver(0);
+ buffer->release();
+}
+
+MediaCodecSource::MediaCodecSource(
+ const sp<ALooper> &looper,
+ const sp<AMessage> &outputFormat,
+ const sp<MediaSource> &source,
+ uint32_t flags)
+ : mLooper(looper),
+ mOutputFormat(outputFormat),
+ mMeta(new MetaData),
+ mFlags(flags),
+ mIsVideo(false),
+ mStarted(false),
+ mStopping(false),
+ mDoMoreWorkPending(false),
+ mPullerReachedEOS(false),
+ mFirstSampleTimeUs(-1ll),
+ mEncodedReachedEOS(false),
+ mErrorCode(OK) {
+ CHECK(mLooper != NULL);
+
+ AString mime;
+ CHECK(mOutputFormat->findString("mime", &mime));
+
+ if (!strncasecmp("video/", mime.c_str(), 6)) {
+ mIsVideo = true;
+ }
+
+ if (!(mFlags & FLAG_USE_SURFACE_INPUT)) {
+ mPuller = new Puller(source);
+ }
+}
+
+MediaCodecSource::~MediaCodecSource() {
+ releaseEncoder();
+
+ mCodecLooper->stop();
+ mLooper->unregisterHandler(mReflector->id());
+}
+
+status_t MediaCodecSource::init() {
+ status_t err = initEncoder();
+
+ if (err != OK) {
+ releaseEncoder();
+ }
+
+ return err;
+}
+
+status_t MediaCodecSource::initEncoder() {
+ mReflector = new AHandlerReflector<MediaCodecSource>(this);
+ mLooper->registerHandler(mReflector);
+
+ mCodecLooper = new ALooper;
+ mCodecLooper->setName("codec_looper");
+ mCodecLooper->start();
+
+ if (mFlags & FLAG_USE_METADATA_INPUT) {
+ mOutputFormat->setInt32("store-metadata-in-buffers", 1);
+ }
+
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
+ mOutputFormat->setInt32("create-input-buffers-suspended", 1);
+ }
+
+ AString outputMIME;
+ CHECK(mOutputFormat->findString("mime", &outputMIME));
+
+ mEncoder = MediaCodec::CreateByType(
+ mCodecLooper, outputMIME.c_str(), true /* encoder */);
+
+ if (mEncoder == NULL) {
+ return NO_INIT;
+ }
+
+ ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
+
+ status_t err = mEncoder->configure(
+ mOutputFormat,
+ NULL /* nativeWindow */,
+ NULL /* crypto */,
+ MediaCodec::CONFIGURE_FLAG_ENCODE);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mEncoder->getOutputFormat(&mOutputFormat);
+ convertMessageToMetaData(mOutputFormat, mMeta);
+
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
+ CHECK(mIsVideo);
+
+ err = mEncoder->createInputSurface(&mGraphicBufferProducer);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ err = mEncoder->start();
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = mEncoder->getInputBuffers(&mEncoderInputBuffers);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mEncodedReachedEOS = false;
+ mErrorCode = OK;
+
+ return OK;
+}
+
+void MediaCodecSource::releaseEncoder() {
+ if (mEncoder == NULL) {
+ return;
+ }
+
+ mEncoder->release();
+ mEncoder.clear();
+
+ while (!mInputBufferQueue.empty()) {
+ MediaBuffer *mbuf = *mInputBufferQueue.begin();
+ mInputBufferQueue.erase(mInputBufferQueue.begin());
+ if (mbuf != NULL) {
+ mbuf->release();
+ }
+ }
+
+ for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) {
+ sp<ABuffer> accessUnit = mEncoderInputBuffers.itemAt(i);
+ ReleaseMediaBufferReference(accessUnit);
+ }
+
+ mEncoderInputBuffers.clear();
+ mEncoderOutputBuffers.clear();
+}
+
+bool MediaCodecSource::reachedEOS() {
+ return mEncodedReachedEOS && ((mPuller == NULL) || mPullerReachedEOS);
+}
+
+status_t MediaCodecSource::postSynchronouslyAndReturnError(
+ const sp<AMessage> &msg) {
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (!response->findInt32("err", &err)) {
+ err = OK;
+ }
+
+ return err;
+}
+
+void MediaCodecSource::signalEOS(status_t err) {
+ if (!mEncodedReachedEOS) {
+ ALOGI("encoder (%s) reached EOS", mIsVideo ? "video" : "audio");
+ {
+ Mutex::Autolock autoLock(mOutputBufferLock);
+ // release all unread media buffers
+ for (List<MediaBuffer*>::iterator it = mOutputBufferQueue.begin();
+ it != mOutputBufferQueue.end(); it++) {
+ (*it)->release();
+ }
+ mOutputBufferQueue.clear();
+ mEncodedReachedEOS = true;
+ mErrorCode = err;
+ mOutputBufferCond.signal();
+ }
+
+ releaseEncoder();
+ }
+ if (mStopping && reachedEOS()) {
+ ALOGI("MediaCodecSource (%s) fully stopped",
+ mIsVideo ? "video" : "audio");
+ // posting reply to everyone that's waiting
+ List<uint32_t>::iterator it;
+ for (it = mStopReplyIDQueue.begin();
+ it != mStopReplyIDQueue.end(); it++) {
+ (new AMessage)->postReply(*it);
+ }
+ mStopReplyIDQueue.clear();
+ mStopping = false;
+ }
+}
+
+void MediaCodecSource::suspend() {
+ CHECK(mFlags & FLAG_USE_SURFACE_INPUT);
+ if (mEncoder != NULL) {
+ sp<AMessage> params = new AMessage;
+ params->setInt32("drop-input-frames", true);
+ mEncoder->setParameters(params);
+ }
+}
+
+void MediaCodecSource::resume(int64_t skipFramesBeforeUs) {
+ CHECK(mFlags & FLAG_USE_SURFACE_INPUT);
+ if (mEncoder != NULL) {
+ sp<AMessage> params = new AMessage;
+ params->setInt32("drop-input-frames", false);
+ if (skipFramesBeforeUs > 0) {
+ params->setInt64("skip-frames-before", skipFramesBeforeUs);
+ }
+ mEncoder->setParameters(params);
+ }
+}
+
+void MediaCodecSource::scheduleDoMoreWork() {
+ if (mDoMoreWorkPending) {
+ return;
+ }
+
+ mDoMoreWorkPending = true;
+
+ if (mEncoderActivityNotify == NULL) {
+ mEncoderActivityNotify = new AMessage(
+ kWhatEncoderActivity, mReflector->id());
+ }
+ mEncoder->requestActivityNotification(mEncoderActivityNotify);
+}
+
+status_t MediaCodecSource::feedEncoderInputBuffers() {
+ while (!mInputBufferQueue.empty()
+ && !mAvailEncoderInputIndices.empty()) {
+ MediaBuffer* mbuf = *mInputBufferQueue.begin();
+ mInputBufferQueue.erase(mInputBufferQueue.begin());
+
+ size_t bufferIndex = *mAvailEncoderInputIndices.begin();
+ mAvailEncoderInputIndices.erase(mAvailEncoderInputIndices.begin());
+
+ int64_t timeUs = 0ll;
+ uint32_t flags = 0;
+ size_t size = 0;
+
+ if (mbuf != NULL) {
+ CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
+
+ // push decoding time for video, or drift time for audio
+ if (mIsVideo) {
+ mDecodingTimeQueue.push_back(timeUs);
+ } else {
+#if DEBUG_DRIFT_TIME
+ if (mFirstSampleTimeUs < 0ll) {
+ mFirstSampleTimeUs = timeUs;
+ }
+
+ int64_t driftTimeUs = 0;
+ if (mbuf->meta_data()->findInt64(kKeyDriftTime, &driftTimeUs)
+ && driftTimeUs) {
+ driftTimeUs = timeUs - mFirstSampleTimeUs - driftTimeUs;
+ }
+ mDriftTimeQueue.push_back(driftTimeUs);
+#endif // DEBUG_DRIFT_TIME
+ }
+
+ size = mbuf->size();
+
+ memcpy(mEncoderInputBuffers.itemAt(bufferIndex)->data(),
+ mbuf->data(), size);
+
+ if (mIsVideo) {
+ // video encoder will release MediaBuffer when done
+ // with underlying data.
+ mEncoderInputBuffers.itemAt(bufferIndex)->meta()
+ ->setPointer("mediaBuffer", mbuf);
+ } else {
+ mbuf->release();
+ }
+ } else {
+ flags = MediaCodec::BUFFER_FLAG_EOS;
+ }
+
+ status_t err = mEncoder->queueInputBuffer(
+ bufferIndex, 0, size, timeUs, flags);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t MediaCodecSource::doMoreWork() {
+ status_t err;
+
+ if (!(mFlags & FLAG_USE_SURFACE_INPUT)) {
+ for (;;) {
+ size_t bufferIndex;
+ err = mEncoder->dequeueInputBuffer(&bufferIndex);
+
+ if (err != OK) {
+ break;
+ }
+
+ mAvailEncoderInputIndices.push_back(bufferIndex);
+ }
+
+ feedEncoderInputBuffers();
+ }
+
+ for (;;) {
+ size_t bufferIndex;
+ size_t offset;
+ size_t size;
+ int64_t timeUs;
+ uint32_t flags;
+ native_handle_t* handle = NULL;
+ err = mEncoder->dequeueOutputBuffer(
+ &bufferIndex, &offset, &size, &timeUs, &flags);
+
+ if (err != OK) {
+ if (err == INFO_FORMAT_CHANGED) {
+ continue;
+ } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
+ mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
+ continue;
+ }
+
+ if (err == -EAGAIN) {
+ err = OK;
+ }
+ break;
+ }
+ if (!(flags & MediaCodec::BUFFER_FLAG_EOS)) {
+ sp<ABuffer> outbuf = mEncoderOutputBuffers.itemAt(bufferIndex);
+
+ MediaBuffer *mbuf = new MediaBuffer(outbuf->size());
+ memcpy(mbuf->data(), outbuf->data(), outbuf->size());
+
+ if (!(flags & MediaCodec::BUFFER_FLAG_CODECCONFIG)) {
+ if (mIsVideo) {
+ int64_t decodingTimeUs;
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
+ // GraphicBufferSource is supposed to discard samples
+ // queued before start, and offset timeUs by start time
+ CHECK_GE(timeUs, 0ll);
+ // TODO:
+ // Decoding time for surface source is unavailable,
+ // use presentation time for now. May need to move
+ // this logic into MediaCodec.
+ decodingTimeUs = timeUs;
+ } else {
+ CHECK(!mDecodingTimeQueue.empty());
+ decodingTimeUs = *(mDecodingTimeQueue.begin());
+ mDecodingTimeQueue.erase(mDecodingTimeQueue.begin());
+ }
+ mbuf->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs);
+
+ ALOGV("[video] time %lld us (%.2f secs), dts/pts diff %lld",
+ timeUs, timeUs / 1E6, decodingTimeUs - timeUs);
+ } else {
+ int64_t driftTimeUs = 0;
+#if DEBUG_DRIFT_TIME
+ CHECK(!mDriftTimeQueue.empty());
+ driftTimeUs = *(mDriftTimeQueue.begin());
+ mDriftTimeQueue.erase(mDriftTimeQueue.begin());
+ mbuf->meta_data()->setInt64(kKeyDriftTime, driftTimeUs);
+#endif // DEBUG_DRIFT_TIME
+ ALOGV("[audio] time %lld us (%.2f secs), drift %lld",
+ timeUs, timeUs / 1E6, driftTimeUs);
+ }
+ mbuf->meta_data()->setInt64(kKeyTime, timeUs);
+ } else {
+ mbuf->meta_data()->setInt32(kKeyIsCodecConfig, true);
+ }
+ if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) {
+ mbuf->meta_data()->setInt32(kKeyIsSyncFrame, true);
+ }
+ mbuf->setObserver(this);
+ mbuf->add_ref();
+
+ {
+ Mutex::Autolock autoLock(mOutputBufferLock);
+ mOutputBufferQueue.push_back(mbuf);
+ mOutputBufferCond.signal();
+ }
+ }
+
+ mEncoder->releaseOutputBuffer(bufferIndex);
+
+ if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+ err = ERROR_END_OF_STREAM;
+ break;
+ }
+ }
+
+ return err;
+}
+
+status_t MediaCodecSource::onStart(MetaData *params) {
+ if (mStopping) {
+ ALOGE("Failed to start while we're stopping");
+ return INVALID_OPERATION;
+ }
+
+ if (mStarted) {
+ ALOGI("MediaCodecSource (%s) resuming", mIsVideo ? "video" : "audio");
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
+ resume();
+ } else {
+ CHECK(mPuller != NULL);
+ mPuller->resume();
+ }
+ return OK;
+ }
+
+ ALOGI("MediaCodecSource (%s) starting", mIsVideo ? "video" : "audio");
+
+ status_t err = OK;
+
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
+ int64_t startTimeUs;
+ if (!params || !params->findInt64(kKeyTime, &startTimeUs)) {
+ startTimeUs = -1ll;
+ }
+ resume(startTimeUs);
+ scheduleDoMoreWork();
+ } else {
+ CHECK(mPuller != NULL);
+ sp<AMessage> notify = new AMessage(
+ kWhatPullerNotify, mReflector->id());
+ err = mPuller->start(params, notify);
+ if (err != OK) {
+ mPullerReachedEOS = true;
+ return err;
+ }
+ }
+
+ ALOGI("MediaCodecSource (%s) started", mIsVideo ? "video" : "audio");
+
+ mStarted = true;
+ return OK;
+}
+
+void MediaCodecSource::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatPullerNotify:
+ {
+ MediaBuffer *mbuf;
+ CHECK(msg->findPointer("accessUnit", (void**)&mbuf));
+
+ if (mbuf == NULL) {
+ ALOGI("puller (%s) reached EOS",
+ mIsVideo ? "video" : "audio");
+ mPullerReachedEOS = true;
+ }
+
+ if (mEncoder == NULL) {
+ ALOGV("got msg '%s' after encoder shutdown.",
+ msg->debugString().c_str());
+
+ if (mbuf != NULL) {
+ mbuf->release();
+ } else {
+ signalEOS();
+ }
+ break;
+ }
+
+ mInputBufferQueue.push_back(mbuf);
+
+ feedEncoderInputBuffers();
+ scheduleDoMoreWork();
+
+ break;
+ }
+ case kWhatEncoderActivity:
+ {
+ mDoMoreWorkPending = false;
+
+ if (mEncoder == NULL) {
+ break;
+ }
+
+ status_t err = doMoreWork();
+
+ if (err == OK) {
+ scheduleDoMoreWork();
+ } else {
+ // reached EOS, or error
+ signalEOS(err);
+ }
+
+ break;
+ }
+ case kWhatStart:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("meta", &obj));
+ MetaData *params = static_cast<MetaData *>(obj.get());
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", onStart(params));
+ response->postReply(replyID);
+ break;
+ }
+ case kWhatStop:
+ {
+ ALOGI("MediaCodecSource (%s) stopping", mIsVideo ? "video" : "audio");
+
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (reachedEOS()) {
+ // if we already reached EOS, reply and return now
+ ALOGI("MediaCodecSource (%s) already stopped",
+ mIsVideo ? "video" : "audio");
+ (new AMessage)->postReply(replyID);
+ break;
+ }
+
+ mStopReplyIDQueue.push_back(replyID);
+ if (mStopping) {
+ // nothing to do if we're already stopping, reply will be posted
+ // to all when we're stopped.
+ break;
+ }
+
+ mStopping = true;
+
+ // if using surface, signal source EOS and wait for EOS to come back.
+ // otherwise, release encoder and post EOS if haven't done already
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
+ mEncoder->signalEndOfInputStream();
+ } else {
+ CHECK(mPuller != NULL);
+ mPuller->stopAsync();
+ signalEOS();
+ }
+ break;
+ }
+ case kWhatPause:
+ {
+ if (mFlags && FLAG_USE_SURFACE_INPUT) {
+ suspend();
+ } else {
+ CHECK(mPuller != NULL);
+ mPuller->pause();
+ }
+ break;
+ }
+ default:
+ TRESPASS();
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index b5d4e44..c670bb4 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -36,12 +36,14 @@
const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp";
const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+const char *MEDIA_MIMETYPE_AUDIO_OPUS = "audio/opus";
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_AUDIO_MSGSM = "audio/gsm";
+const char *MEDIA_MIMETYPE_AUDIO_AC3 = "audio/ac3";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4";
const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav";
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
index ae6ae2d..74234a6 100644
--- a/media/libstagefright/MetaData.cpp
+++ b/media/libstagefright/MetaData.cpp
@@ -16,6 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MetaData"
+#include <inttypes.h>
#include <utils/Log.h>
#include <stdlib.h>
@@ -89,6 +90,9 @@
return setData(key, TYPE_RECT, &r, sizeof(r));
}
+/**
+ * Note that the returned pointer becomes invalid when additional metadata is set.
+ */
bool MetaData::findCString(uint32_t key, const char **value) {
uint32_t type;
const void *data;
@@ -218,6 +222,16 @@
return true;
}
+bool MetaData::hasData(uint32_t key) const {
+ ssize_t i = mItems.indexOfKey(key);
+
+ if (i < 0) {
+ return false;
+ }
+
+ return true;
+}
+
MetaData::typed_data::typed_data()
: mType(0),
mSize(0) {
@@ -294,7 +308,7 @@
const void *data = storage();
switch(mType) {
case TYPE_NONE:
- out = String8::format("no type, size %d)", mSize);
+ out = String8::format("no type, size %zu)", mSize);
break;
case TYPE_C_STRING:
out = String8::format("(char*) %s", (const char *)data);
@@ -303,7 +317,7 @@
out = String8::format("(int32_t) %d", *(int32_t *)data);
break;
case TYPE_INT64:
- out = String8::format("(int64_t) %lld", *(int64_t *)data);
+ out = String8::format("(int64_t) %" PRId64, *(int64_t *)data);
break;
case TYPE_FLOAT:
out = String8::format("(float) %f", *(float *)data);
@@ -320,7 +334,7 @@
}
default:
- out = String8::format("(unknown type %d, size %d)", mType, mSize);
+ out = String8::format("(unknown type %d, size %zu)", mType, mSize);
if (mSize <= 48) { // if it's less than three lines of hex data, dump it
AString foo;
hexdump(data, mSize, 0, &foo);
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 05e599b..72ea32d 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -213,7 +213,14 @@
mLooper->setName("NuCachedSource2");
mLooper->registerHandler(mReflector);
- mLooper->start();
+
+ // Since it may not be obvious why our looper thread needs to be
+ // able to call into java since it doesn't appear to do so at all...
+ // IMediaHTTPConnection may be (and most likely is) implemented in JAVA
+ // and a local JAVA IBinder will call directly into JNI methods.
+ // So whenever we call DataSource::readAt it may end up in a call to
+ // IMediaHTTPConnection::readAt and therefore call back into JAVA.
+ mLooper->start(false /* runOnCallingThread */, true /* canCallJava */);
Mutex::Autolock autoLock(mLock);
(new AMessage(kWhatFetchMore, mReflector->id()))->post();
@@ -326,7 +333,7 @@
mNumRetriesLeft = 0;
}
- ALOGE("source returned error %ld, %d retries left", n, mNumRetriesLeft);
+ ALOGE("source returned error %d, %d retries left", n, mNumRetriesLeft);
mCache->releasePage(page);
} else if (n == 0) {
ALOGI("ERROR_END_OF_STREAM");
@@ -641,7 +648,7 @@
ssize_t lowwaterMarkKb, highwaterMarkKb;
int keepAliveSecs;
- if (sscanf(s, "%ld/%ld/%d",
+ if (sscanf(s, "%d/%d/%d",
&lowwaterMarkKb, &highwaterMarkKb, &keepAliveSecs) != 3) {
ALOGE("Failed to parse cache parameters from '%s'.", s);
return;
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 7bc7da2..64f56e9 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -58,7 +58,9 @@
}
status_t NuMediaExtractor::setDataSource(
- const char *path, const KeyedVector<String8, String8> *headers) {
+ const sp<IMediaHTTPService> &httpService,
+ const char *path,
+ const KeyedVector<String8, String8> *headers) {
Mutex::Autolock autoLock(mLock);
if (mImpl != NULL) {
@@ -66,7 +68,7 @@
}
sp<DataSource> dataSource =
- DataSource::CreateFromURI(path, headers);
+ DataSource::CreateFromURI(httpService, path, headers);
if (dataSource == NULL) {
return -ENOENT;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 9820ef5..9f9352d 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -69,6 +69,10 @@
virtual status_t storeMetaDataInBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable);
+ virtual status_t prepareForAdaptivePlayback(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable,
+ OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight);
+
virtual status_t enableGraphicBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable);
@@ -268,6 +272,13 @@
return getOMX(node)->storeMetaDataInBuffers(node, port_index, enable);
}
+status_t MuxOMX::prepareForAdaptivePlayback(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable,
+ OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) {
+ return getOMX(node)->prepareForAdaptivePlayback(
+ node, port_index, enable, maxFrameWidth, maxFrameHeight);
+}
+
status_t MuxOMX::enableGraphicBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable) {
return getOMX(node)->enableGraphicBuffers(node, port_index, enable);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 7b37365..4d3b5bd 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -40,7 +40,9 @@
#include <utils/Vector.h>
#include <OMX_Audio.h>
+#include <OMX_AudioExt.h>
#include <OMX_Component.h>
+#include <OMX_IndexExt.h>
#include "include/avc_utils.h"
@@ -359,12 +361,7 @@
observer->setCodec(codec);
err = codec->configureCodec(meta);
-
if (err == OK) {
- if (!strcmp("OMX.Nvidia.mpeg2v.decode", componentName)) {
- codec->mFlags |= kOnlySubmitOneInputBufferAtOneTime;
- }
-
return codec;
}
@@ -492,6 +489,13 @@
CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size));
addCodecSpecificData(data, size);
+ } else if (meta->findData(kKeyOpusHeader, &type, &data, &size)) {
+ addCodecSpecificData(data, size);
+
+ CHECK(meta->findData(kKeyOpusCodecDelay, &type, &data, &size));
+ addCodecSpecificData(data, size);
+ CHECK(meta->findData(kKeyOpusSeekPreRoll, &type, &data, &size));
+ addCodecSpecificData(data, size);
}
}
@@ -533,6 +537,17 @@
sampleRate,
numChannels);
}
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AC3, mMIME)) {
+ int32_t numChannels;
+ int32_t sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+ status_t err = setAC3Format(numChannels, sampleRate);
+ if (err != OK) {
+ CODEC_LOGE("setAC3Format() failed (err = %d)", err);
+ return err;
+ }
} else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_ALAW, mMIME)
|| !strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_MLAW, mMIME)) {
// These are PCM-like formats with a fixed sample rate but
@@ -1346,8 +1361,7 @@
mLeftOverBuffer(NULL),
mPaused(false),
mNativeWindow(
- (!strncmp(componentName, "OMX.google.", 11)
- || !strcmp(componentName, "OMX.Nvidia.mpeg2v.decode"))
+ (!strncmp(componentName, "OMX.google.", 11))
? NULL : nativeWindow) {
mPortStatus[kPortIndexInput] = ENABLED;
mPortStatus[kPortIndexOutput] = ENABLED;
@@ -1380,6 +1394,8 @@
"audio_decoder.aac", "audio_encoder.aac" },
{ MEDIA_MIMETYPE_AUDIO_VORBIS,
"audio_decoder.vorbis", "audio_encoder.vorbis" },
+ { MEDIA_MIMETYPE_AUDIO_OPUS,
+ "audio_decoder.opus", "audio_encoder.opus" },
{ MEDIA_MIMETYPE_AUDIO_G711_MLAW,
"audio_decoder.g711mlaw", "audio_encoder.g711mlaw" },
{ MEDIA_MIMETYPE_AUDIO_G711_ALAW,
@@ -1400,6 +1416,10 @@
"audio_decoder.flac", "audio_encoder.flac" },
{ MEDIA_MIMETYPE_AUDIO_MSGSM,
"audio_decoder.gsm", "audio_encoder.gsm" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG2,
+ "video_decoder.mpeg2", "video_encoder.mpeg2" },
+ { MEDIA_MIMETYPE_AUDIO_AC3,
+ "audio_decoder.ac3", "audio_encoder.ac3" },
};
static const size_t kNumMimeToRole =
@@ -3495,6 +3515,31 @@
return OK;
}
+status_t OMXCodec::setAC3Format(int32_t numChannels, int32_t sampleRate) {
+ OMX_AUDIO_PARAM_ANDROID_AC3TYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexInput;
+
+ status_t err = mOMX->getParameter(
+ mNode,
+ (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+ &def,
+ sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ def.nChannels = numChannels;
+ def.nSampleRate = sampleRate;
+
+ return mOMX->setParameter(
+ mNode,
+ (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+ &def,
+ sizeof(def));
+}
+
void OMXCodec::setG711Format(int32_t numChannels) {
CHECK(!mIsEncoder);
setRawAudioFormat(kPortIndexInput, 8000, numChannels);
@@ -4089,6 +4134,7 @@
"OMX_AUDIO_CodingMP3",
"OMX_AUDIO_CodingSBC",
"OMX_AUDIO_CodingVORBIS",
+ "OMX_AUDIO_CodingOPUS",
"OMX_AUDIO_CodingWMA",
"OMX_AUDIO_CodingRA",
"OMX_AUDIO_CodingMIDI",
@@ -4428,6 +4474,17 @@
mOutputFormat->setInt32(kKeyChannelCount, numChannels);
mOutputFormat->setInt32(kKeySampleRate, sampleRate);
mOutputFormat->setInt32(kKeyBitRate, bitRate);
+ } else if (audio_def->eEncoding ==
+ (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidAC3) {
+ mOutputFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
+ int32_t numChannels, sampleRate, bitRate;
+ inputFormat->findInt32(kKeyChannelCount, &numChannels);
+ inputFormat->findInt32(kKeySampleRate, &sampleRate);
+ inputFormat->findInt32(kKeyBitRate, &bitRate);
+ mOutputFormat->setInt32(kKeyChannelCount, numChannels);
+ mOutputFormat->setInt32(kKeySampleRate, sampleRate);
+ mOutputFormat->setInt32(kKeyBitRate, bitRate);
} else {
CHECK(!"Should not be here. Unknown audio encoding.");
}
@@ -4585,12 +4642,6 @@
caps->mFlags = 0;
caps->mComponentName = componentName;
- if (!isEncoder && !strncmp(mime, "video/", 6) &&
- omx->storeMetaDataInBuffers(
- node, 1 /* port index */, OMX_TRUE) == OK) {
- caps->mFlags |= CodecCapabilities::kFlagSupportsAdaptivePlayback;
- }
-
OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
InitOMXParams(¶m);
@@ -4626,6 +4677,16 @@
caps->mColorFormats.push(portFormat.eColorFormat);
}
+ if (!isEncoder && !strncmp(mime, "video/", 6)) {
+ if (omx->storeMetaDataInBuffers(
+ node, 1 /* port index */, OMX_TRUE) == OK ||
+ omx->prepareForAdaptivePlayback(
+ node, 1 /* port index */, OMX_TRUE,
+ 1280 /* width */, 720 /* height */) == OK) {
+ caps->mFlags |= CodecCapabilities::kFlagSupportsAdaptivePlayback;
+ }
+ }
+
CHECK_EQ(omx->freeNode(node), (status_t)OK);
return OK;
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 5e79e78..f3eeb03 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -151,7 +151,7 @@
return mExtractor->mImpl->getFormat();
}
-status_t OggSource::start(MetaData *params) {
+status_t OggSource::start(MetaData * /* params */) {
if (mStarted) {
return INVALID_OPERATION;
}
@@ -381,7 +381,7 @@
ssize_t n;
if ((n = mSource->readAt(offset, header, sizeof(header)))
< (ssize_t)sizeof(header)) {
- ALOGV("failed to read %d bytes at offset 0x%016llx, got %ld bytes",
+ ALOGV("failed to read %zu bytes at offset 0x%016llx, got %d bytes",
sizeof(header), offset, n);
if (n < 0) {
@@ -505,7 +505,7 @@
packetSize);
if (n < (ssize_t)packetSize) {
- ALOGV("failed to read %d bytes at 0x%016llx, got %ld bytes",
+ ALOGV("failed to read %zu bytes at 0x%016llx, got %d bytes",
packetSize, dataOffset, n);
return ERROR_IO;
}
@@ -546,7 +546,7 @@
buffer = NULL;
}
- ALOGV("readPage returned %ld", n);
+ ALOGV("readPage returned %d", n);
return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
}
@@ -998,7 +998,7 @@
}
sp<MetaData> OggExtractor::getTrackMetaData(
- size_t index, uint32_t flags) {
+ size_t index, uint32_t /* flags */) {
if (index >= 1) {
return NULL;
}
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index af8186c..fe20835 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -24,6 +24,7 @@
#include <media/stagefright/StagefrightMediaScanner.h>
+#include <media/IMediaHTTPService.h>
#include <media/mediametadataretriever.h>
#include <private/media/VideoFrame.h>
@@ -117,7 +118,7 @@
}
MediaScanResult StagefrightMediaScanner::processFileInternal(
- const char *path, const char *mimeType,
+ const char *path, const char * /* mimeType */,
MediaScannerClient &client) {
const char *extension = strrchr(path, '.');
@@ -147,7 +148,7 @@
status_t status;
if (fd < 0) {
// couldn't open it locally, maybe the media server can?
- status = mRetriever->setDataSource(path);
+ status = mRetriever->setDataSource(NULL /* httpService */, path);
} else {
status = mRetriever->setDataSource(fd, 0, 0x7ffffffffffffffL);
close(fd);
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 19af4fb..9475d05 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -16,10 +16,12 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "StagefrightMetadataRetriever"
+#include <inttypes.h>
#include <utils/Log.h>
#include "include/StagefrightMetadataRetriever.h"
+#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/ColorConverter.h>
#include <media/stagefright/DataSource.h>
@@ -50,7 +52,9 @@
}
status_t StagefrightMetadataRetriever::setDataSource(
- const char *uri, const KeyedVector<String8, String8> *headers) {
+ const sp<IMediaHTTPService> &httpService,
+ const char *uri,
+ const KeyedVector<String8, String8> *headers) {
ALOGV("setDataSource(%s)", uri);
mParsedMetaData = false;
@@ -58,7 +62,7 @@
delete mAlbumArt;
mAlbumArt = NULL;
- mSource = DataSource::CreateFromURI(uri, headers);
+ mSource = DataSource::CreateFromURI(httpService, uri, headers);
if (mSource == NULL) {
ALOGE("Unable to create data source for '%s'.", uri);
@@ -488,7 +492,7 @@
size_t numTracks = mExtractor->countTracks();
char tmp[32];
- sprintf(tmp, "%d", numTracks);
+ sprintf(tmp, "%zu", numTracks);
mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp));
@@ -545,7 +549,7 @@
}
// The duration value is a string representing the duration in ms.
- sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000);
+ sprintf(tmp, "%" PRId64, (maxDurationUs + 500) / 1000);
mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
if (hasAudio) {
@@ -573,7 +577,7 @@
if (mSource->getSize(&sourceSize) == OK) {
int64_t avgBitRate = (int64_t)(sourceSize * 8E6 / maxDurationUs);
- sprintf(tmp, "%lld", avgBitRate);
+ sprintf(tmp, "%" PRId64, avgBitRate);
mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
}
}
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 6b934d4..10c00f4 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -99,8 +99,11 @@
dump(result, "", buffer, 1024);
}
-void SurfaceMediaSource::dump(String8& result, const char* prefix,
- char* buffer, size_t SIZE) const
+void SurfaceMediaSource::dump(
+ String8& result,
+ const char* /* prefix */,
+ char* buffer,
+ size_t /* SIZE */) const
{
Mutex::Autolock lock(mMutex);
@@ -202,6 +205,9 @@
return OK;
}
+ mStarted = false;
+ mFrameAvailableCondition.signal();
+
while (mNumPendingBuffers > 0) {
ALOGI("Still waiting for %d buffers to be returned.",
mNumPendingBuffers);
@@ -215,8 +221,6 @@
mMediaBuffersAvailableCondition.wait(mMutex);
}
- mStarted = false;
- mFrameAvailableCondition.signal();
mMediaBuffersAvailableCondition.signal();
return mBufferQueue->consumerDisconnect();
@@ -269,9 +273,8 @@
bufferHandle, (*buffer)->range_length(), (*buffer)->range_offset());
}
-status_t SurfaceMediaSource::read( MediaBuffer **buffer,
- const ReadOptions *options)
-{
+status_t SurfaceMediaSource::read(
+ MediaBuffer **buffer, const ReadOptions * /* options */) {
ALOGV("read");
Mutex::Autolock lock(mMutex);
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index 7e9c4bf..3d2eb1f 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -31,17 +31,29 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
+#include <binder/IServiceManager.h>
+#include <powermanager/PowerManager.h>
+#include <binder/IPCThreadState.h>
+#include <utils/CallStack.h>
namespace android {
+static int64_t kWakelockMinDelay = 100000ll; // 100ms
+
TimedEventQueue::TimedEventQueue()
: mNextEventID(1),
mRunning(false),
- mStopped(false) {
+ mStopped(false),
+ mDeathRecipient(new PMDeathRecipient(this)),
+ mWakeLockCount(0) {
}
TimedEventQueue::~TimedEventQueue() {
stop();
+ if (mPowerManager != 0) {
+ sp<IBinder> binder = mPowerManager->asBinder();
+ binder->unlinkToDeath(mDeathRecipient);
+ }
}
void TimedEventQueue::start() {
@@ -76,6 +88,9 @@
void *dummy;
pthread_join(mThread, &dummy);
+ // some events may be left in the queue if we did not flush and the wake lock
+ // must be released.
+ releaseWakeLock_l(true /*force*/);
mQueue.clear();
mRunning = false;
@@ -112,11 +127,16 @@
QueueItem item;
item.event = event;
item.realtime_us = realtime_us;
+ item.has_wakelock = false;
if (it == mQueue.begin()) {
mQueueHeadChangedCondition.signal();
}
+ if (realtime_us > ALooper::GetNowUs() + kWakelockMinDelay) {
+ acquireWakeLock_l();
+ item.has_wakelock = true;
+ }
mQueue.insert(it, item);
mQueueNotEmptyCondition.signal();
@@ -171,8 +191,10 @@
ALOGV("cancelling event %d", (*it).event->eventID());
(*it).event->setEventID(0);
+ if ((*it).has_wakelock) {
+ releaseWakeLock_l();
+ }
it = mQueue.erase(it);
-
if (stopAfterFirstMatch) {
return;
}
@@ -195,6 +217,7 @@
for (;;) {
int64_t now_us = 0;
sp<Event> event;
+ bool wakeLocked = false;
{
Mutex::Autolock autoLock(mLock);
@@ -261,26 +284,29 @@
// removeEventFromQueue_l will return NULL.
// Otherwise, the QueueItem will be removed
// from the queue and the referenced event returned.
- event = removeEventFromQueue_l(eventID);
+ event = removeEventFromQueue_l(eventID, &wakeLocked);
}
if (event != NULL) {
// Fire event with the lock NOT held.
event->fire(this, now_us);
+ if (wakeLocked) {
+ Mutex::Autolock autoLock(mLock);
+ releaseWakeLock_l();
+ }
}
}
}
sp<TimedEventQueue::Event> TimedEventQueue::removeEventFromQueue_l(
- event_id id) {
+ event_id id, bool *wakeLocked) {
for (List<QueueItem>::iterator it = mQueue.begin();
it != mQueue.end(); ++it) {
if ((*it).event->eventID() == id) {
sp<Event> event = (*it).event;
event->setEventID(0);
-
+ *wakeLocked = (*it).has_wakelock;
mQueue.erase(it);
-
return event;
}
}
@@ -290,5 +316,70 @@
return NULL;
}
+void TimedEventQueue::acquireWakeLock_l()
+{
+ if (mWakeLockCount == 0) {
+ CHECK(mWakeLockToken == 0);
+ if (mPowerManager == 0) {
+ // use checkService() to avoid blocking if power service is not up yet
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16("power"));
+ if (binder == 0) {
+ ALOGW("cannot connect to the power manager service");
+ } else {
+ mPowerManager = interface_cast<IPowerManager>(binder);
+ binder->linkToDeath(mDeathRecipient);
+ }
+ }
+ if (mPowerManager != 0) {
+ sp<IBinder> binder = new BBinder();
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
+ status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ binder,
+ String16("TimedEventQueue"),
+ String16("media"));
+ IPCThreadState::self()->restoreCallingIdentity(token);
+ if (status == NO_ERROR) {
+ mWakeLockToken = binder;
+ mWakeLockCount++;
+ }
+ }
+ } else {
+ mWakeLockCount++;
+ }
+}
+
+void TimedEventQueue::releaseWakeLock_l(bool force)
+{
+ if (mWakeLockCount == 0) {
+ return;
+ }
+ if (force) {
+ // Force wakelock release below by setting reference count to 1.
+ mWakeLockCount = 1;
+ }
+ if (--mWakeLockCount == 0) {
+ CHECK(mWakeLockToken != 0);
+ if (mPowerManager != 0) {
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
+ mPowerManager->releaseWakeLock(mWakeLockToken, 0);
+ IPCThreadState::self()->restoreCallingIdentity(token);
+ }
+ mWakeLockToken.clear();
+ }
+}
+
+void TimedEventQueue::clearPowerManager()
+{
+ Mutex::Autolock _l(mLock);
+ releaseWakeLock_l(true /*force*/);
+ mPowerManager.clear();
+}
+
+void TimedEventQueue::PMDeathRecipient::binderDied(
+ const wp<IBinder>& /* who */) {
+ mQueue->clearPowerManager();
+}
+
} // namespace android
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 4db8e80..4ff805f 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -251,6 +251,13 @@
buffer->meta()->setInt32("csd", true);
buffer->meta()->setInt64("timeUs", 0);
msg->setBuffer("csd-1", buffer);
+ } else if (meta->findData(kKeyOpusHeader, &type, &data, &size)) {
+ sp<ABuffer> buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-0", buffer);
}
*format = msg;
@@ -452,6 +459,11 @@
}
}
+ int32_t timeScale;
+ if (msg->findInt32("time-scale", &timeScale)) {
+ meta->setInt32(kKeyTimeScale, timeScale);
+ }
+
// XXX TODO add whatever other keys there are
#if 0
@@ -523,6 +535,7 @@
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, AUDIO_FORMAT_AMR_WB },
{ MEDIA_MIMETYPE_AUDIO_AAC, AUDIO_FORMAT_AAC },
{ MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS },
+ { MEDIA_MIMETYPE_AUDIO_OPUS, AUDIO_FORMAT_OPUS},
{ 0, AUDIO_FORMAT_INVALID }
};
@@ -540,7 +553,8 @@
return BAD_VALUE;
}
-bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming)
+bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo,
+ bool isStreaming, audio_stream_type_t streamType)
{
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
@@ -561,6 +575,17 @@
return false;
}
+ // check whether it is ELD/LD content -> no offloading
+ // FIXME: this should depend on audio DSP capabilities. mapMimeToAudioFormat() should use the
+ // metadata to refine the AAC format and the audio HAL should only list supported profiles.
+ int32_t aacaot = -1;
+ if (meta->findInt32(kKeyAACAOT, &aacaot)) {
+ if (aacaot == 23 || aacaot == 39 ) {
+ ALOGV("track of type '%s' is ELD/LD content", mime);
+ return false;
+ }
+ }
+
int32_t srate = -1;
if (!meta->findInt32(kKeySampleRate, &srate)) {
ALOGV("track of type '%s' does not publish sample rate", mime);
@@ -594,7 +619,7 @@
info.bit_rate = brate;
- info.stream_type = AUDIO_STREAM_MUSIC;
+ info.stream_type = streamType;
info.has_video = hasVideo;
info.is_streaming = isStreaming;
diff --git a/media/libstagefright/VBRISeeker.cpp b/media/libstagefright/VBRISeeker.cpp
index a245f2c..af858b9 100644
--- a/media/libstagefright/VBRISeeker.cpp
+++ b/media/libstagefright/VBRISeeker.cpp
@@ -119,7 +119,7 @@
seeker->mSegments.push(numBytes);
- ALOGV("entry #%d: %d offset 0x%08lx", i, numBytes, offset);
+ ALOGV("entry #%d: %u offset 0x%016llx", i, numBytes, offset);
offset += numBytes;
}
@@ -160,7 +160,7 @@
*pos += mSegments.itemAt(segmentIndex++);
}
- ALOGV("getOffsetForTime %lld us => 0x%08lx", *timeUs, *pos);
+ ALOGV("getOffsetForTime %lld us => 0x%016llx", *timeUs, *pos);
*timeUs = nowUs;
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 22af6fb..fe9058b 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -127,7 +127,7 @@
}
sp<MetaData> WAVExtractor::getTrackMetaData(
- size_t index, uint32_t flags) {
+ size_t index, uint32_t /* flags */) {
if (mInitCheck != OK || index > 0) {
return NULL;
}
@@ -358,7 +358,7 @@
}
}
-status_t WAVSource::start(MetaData *params) {
+status_t WAVSource::start(MetaData * /* params */) {
ALOGV("WAVSource::start");
CHECK(!mStarted);
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index 5ae80cc..bc48272 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -76,7 +76,7 @@
{
gVendorLibHandle = dlopen("libwvm.so", RTLD_NOW);
if (gVendorLibHandle == NULL) {
- ALOGE("Failed to open libwvm.so");
+ ALOGE("Failed to open libwvm.so: %s", dlerror());
}
}
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index b822868..38a1f6b 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -40,6 +40,25 @@
return x + (1u << numZeroes) - 1;
}
+signed parseSE(ABitReader *br) {
+ unsigned codeNum = parseUE(br);
+
+ return (codeNum & 1) ? (codeNum + 1) / 2 : -(codeNum / 2);
+}
+
+static void skipScalingList(ABitReader *br, size_t sizeOfScalingList) {
+ size_t lastScale = 8;
+ size_t nextScale = 8;
+ for (size_t j = 0; j < sizeOfScalingList; ++j) {
+ if (nextScale != 0) {
+ signed delta_scale = parseSE(br);
+ nextScale = (lastScale + delta_scale + 256) % 256;
+ }
+
+ lastScale = (nextScale == 0) ? lastScale : nextScale;
+ }
+}
+
// Determine video dimensions from the sequence parameterset.
void FindAVCDimensions(
const sp<ABuffer> &seqParamSet,
@@ -63,7 +82,24 @@
parseUE(&br); // bit_depth_luma_minus8
parseUE(&br); // bit_depth_chroma_minus8
br.skipBits(1); // qpprime_y_zero_transform_bypass_flag
- CHECK_EQ(br.getBits(1), 0u); // seq_scaling_matrix_present_flag
+
+ if (br.getBits(1)) { // seq_scaling_matrix_present_flag
+ for (size_t i = 0; i < 8; ++i) {
+ if (br.getBits(1)) { // seq_scaling_list_present_flag[i]
+
+ // WARNING: the code below has not ever been exercised...
+ // need a real-world example.
+
+ if (i < 6) {
+ // ScalingList4x4[i],16,...
+ skipScalingList(&br, 16);
+ } else {
+ // ScalingList8x8[i-6],64,...
+ skipScalingList(&br, 64);
+ }
+ }
+ }
+ }
}
parseUE(&br); // log2_max_frame_num_minus4
@@ -251,9 +287,7 @@
return OK;
}
-static sp<ABuffer> FindNAL(
- const uint8_t *data, size_t size, unsigned nalType,
- size_t *stopOffset) {
+static sp<ABuffer> FindNAL(const uint8_t *data, size_t size, unsigned nalType) {
const uint8_t *nalStart;
size_t nalSize;
while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
@@ -293,7 +327,7 @@
const uint8_t *data = accessUnit->data();
size_t size = accessUnit->size();
- sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
+ sp<ABuffer> seqParamSet = FindNAL(data, size, 7);
if (seqParamSet == NULL) {
return NULL;
}
@@ -303,8 +337,7 @@
FindAVCDimensions(
seqParamSet, &width, &height, &sarWidth, &sarHeight);
- size_t stopOffset;
- sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
+ sp<ABuffer> picParamSet = FindNAL(data, size, 8);
CHECK(picParamSet != NULL);
size_t csdSize =
diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk
deleted file mode 100644
index f26f386..0000000
--- a/media/libstagefright/chromium_http/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-ifneq ($(TARGET_BUILD_PDK), true)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- DataUriSource.cpp \
- ChromiumHTTPDataSource.cpp \
- support.cpp \
- chromium_http_stub.cpp
-
-LOCAL_C_INCLUDES:= \
- $(TOP)/frameworks/av/media/libstagefright \
- $(TOP)/frameworks/native/include/media/openmax \
- external/chromium \
- external/chromium/android
-
-LOCAL_CFLAGS += -Wno-multichar
-
-LOCAL_SHARED_LIBRARIES += \
- libstlport \
- libchromium_net \
- libutils \
- libcutils \
- liblog \
- libstagefright_foundation \
- libstagefright \
- libdrmframework
-
-include external/stlport/libstlport.mk
-
-LOCAL_MODULE:= libstagefright_chromium_http
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
-endif
diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
deleted file mode 100644
index a862d8b..0000000
--- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * 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"
-
-#include <cutils/properties.h> // for property_get
-
-namespace android {
-
-ChromiumHTTPDataSource::ChromiumHTTPDataSource(uint32_t flags)
- : mFlags(flags),
- mState(DISCONNECTED),
- mDelegate(new SfDelegate),
- mCurrentOffset(0),
- mIOResult(OK),
- mContentSize(-1),
- mDecryptHandle(NULL),
- mDrmManagerClient(NULL) {
- mDelegate->setOwner(this);
-}
-
-ChromiumHTTPDataSource::~ChromiumHTTPDataSource() {
- disconnect();
-
- delete mDelegate;
- mDelegate = NULL;
-
- clearDRMState_l();
-
- 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);
-
- uid_t uid;
- if (getUID(&uid)) {
- mDelegate->setUID(uid);
- }
-
-#if defined(LOG_NDEBUG) && !LOG_NDEBUG
- LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG, "connect on behalf of uid %d", uid);
-#endif
-
- 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 defined(LOG_NDEBUG) && !LOG_NDEBUG
- LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG,
- "connect to <URL suppressed> @%lld", offset);
-#endif
-
- 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 || mState == DISCONNECTING) {
- mCondition.wait(mLock);
- }
-
- return mState == CONNECTED ? OK : mIOResult;
-}
-
-void ChromiumHTTPDataSource::onConnectionEstablished(
- int64_t contentSize, const char *contentType) {
- Mutex::Autolock autoLock(mLock);
-
- if (mState != CONNECTING) {
- // We may have initiated disconnection.
- CHECK_EQ(mState, DISCONNECTING);
- return;
- }
-
- 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;
-}
-
-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 INVALID_OPERATION;
- }
-
-#if 0
- char value[PROPERTY_VALUE_MAX];
- if (property_get("media.stagefright.disable-net", value, 0)
- && (!strcasecmp(value, "true") || !strcmp(value, "1"))) {
- LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Simulating that the network is down.");
- disconnect_l();
- return ERROR_IO;
- }
-#endif
-
- 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(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 | kIsHTTPBasedSource;
-}
-
-// 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();
- mIOResult = -ENOTCONN;
-
- mCondition.broadcast();
-}
-
-sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization(const char* mime) {
- 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()), mime);
- }
-
- 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;
- }
-}
-
-status_t ChromiumHTTPDataSource::reconnectAtOffset(off64_t offset) {
- Mutex::Autolock autoLock(mLock);
-
- if (mURI.empty()) {
- return INVALID_OPERATION;
- }
-
- LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Reconnecting...");
- status_t err = connect_l(mURI.c_str(), &mHeaders, offset);
- if (err != OK) {
- LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Reconnect failed w/ err 0x%08x", err);
- }
-
- return err;
-}
-
-// static
-status_t ChromiumHTTPDataSource::UpdateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
- return SfDelegate::UpdateProxyConfig(host, port, exclusionList);
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/chromium_http/DataUriSource.cpp b/media/libstagefright/chromium_http/DataUriSource.cpp
deleted file mode 100644
index ecf3fa1..0000000
--- a/media/libstagefright/chromium_http/DataUriSource.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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/DataUriSource.h>
-
-#include <net/base/data_url.h>
-#include <googleurl/src/gurl.h>
-
-
-namespace android {
-
-DataUriSource::DataUriSource(const char *uri) :
- mDataUri(uri),
- mInited(NO_INIT) {
-
- // Copy1: const char *uri -> String8 mDataUri.
- std::string mimeTypeStr, unusedCharsetStr, dataStr;
- // Copy2: String8 mDataUri -> std::string
- const bool ret = net::DataURL::Parse(
- GURL(std::string(mDataUri.string())),
- &mimeTypeStr, &unusedCharsetStr, &dataStr);
- // Copy3: std::string dataStr -> AString mData
- mData.setTo(dataStr.data(), dataStr.length());
- mInited = ret ? OK : UNKNOWN_ERROR;
-
- // The chromium data url implementation defaults to using "text/plain"
- // if no mime type is specified. We prefer to leave this unspecified
- // instead, since the mime type is sniffed in most cases.
- if (mimeTypeStr != "text/plain") {
- mMimeType = mimeTypeStr.c_str();
- }
-}
-
-ssize_t DataUriSource::readAt(off64_t offset, void *out, size_t size) {
- if (mInited != OK) {
- return mInited;
- }
-
- const off64_t length = mData.size();
- if (offset >= length) {
- return UNKNOWN_ERROR;
- }
-
- const char *dataBuf = mData.c_str();
- const size_t bytesToCopy =
- offset + size >= length ? (length - offset) : size;
-
- if (bytesToCopy > 0) {
- memcpy(out, dataBuf + offset, bytesToCopy);
- }
-
- return bytesToCopy;
-}
-
-} // namespace android
diff --git a/media/libstagefright/chromium_http/chromium_http_stub.cpp b/media/libstagefright/chromium_http/chromium_http_stub.cpp
deleted file mode 100644
index 289f6de..0000000
--- a/media/libstagefright/chromium_http/chromium_http_stub.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dlfcn.h>
-
-#include <include/chromium_http_stub.h>
-#include <include/ChromiumHTTPDataSource.h>
-#include <include/DataUriSource.h>
-
-namespace android {
-
-HTTPBase *createChromiumHTTPDataSource(uint32_t flags) {
- return new ChromiumHTTPDataSource(flags);
-}
-
-status_t UpdateChromiumHTTPDataSourceProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
- return ChromiumHTTPDataSource::UpdateProxyConfig(host, port, exclusionList);
-}
-
-DataSource *createDataUriSource(const char *uri) {
- return new DataUriSource(uri);
-}
-
-}
diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp
deleted file mode 100644
index 0a8e3e3..0000000
--- a/media/libstagefright/chromium_http/support.cpp
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- * 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/logging.h"
-#include "base/threading/thread.h"
-#include "net/base/cert_verifier.h"
-#include "net/base/cookie_monster.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/log.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-#include <string>
-
-namespace android {
-
-static Mutex gNetworkThreadLock;
-static base::Thread *gNetworkThread = NULL;
-static scoped_refptr<SfRequestContext> gReqContext;
-static scoped_ptr<net::NetworkChangeNotifier> gNetworkChangeNotifier;
-
-bool logMessageHandler(
- int severity,
- const char* file,
- int line,
- size_t message_start,
- const std::string& str) {
- int androidSeverity = ANDROID_LOG_VERBOSE;
- switch(severity) {
- case logging::LOG_FATAL:
- androidSeverity = ANDROID_LOG_FATAL;
- break;
- case logging::LOG_ERROR_REPORT:
- case logging::LOG_ERROR:
- androidSeverity = ANDROID_LOG_ERROR;
- break;
- case logging::LOG_WARNING:
- androidSeverity = ANDROID_LOG_WARN;
- break;
- default:
- androidSeverity = ANDROID_LOG_VERBOSE;
- break;
- }
- android_printLog(androidSeverity, "chromium-libstagefright",
- "%s:%d: %s", file, line, str.c_str());
- return false;
-}
-
-struct AutoPrioritySaver {
- AutoPrioritySaver()
- : mTID(androidGetTid()),
- mPrevPriority(androidGetThreadPriority(mTID)) {
- androidSetThreadPriority(mTID, ANDROID_PRIORITY_NORMAL);
- }
-
- ~AutoPrioritySaver() {
- androidSetThreadPriority(mTID, mPrevPriority);
- }
-
-private:
- pid_t mTID;
- int mPrevPriority;
-
- DISALLOW_EVIL_CONSTRUCTORS(AutoPrioritySaver);
-};
-
-static void InitializeNetworkThreadIfNecessary() {
- Mutex::Autolock autoLock(gNetworkThreadLock);
-
- if (gNetworkThread == NULL) {
- // Make sure any threads spawned by the chromium framework are
- // running at normal priority instead of inheriting this thread's.
- AutoPrioritySaver saver;
-
- gNetworkThread = new base::Thread("network");
- base::Thread::Options options;
- options.message_loop_type = MessageLoop::TYPE_IO;
- CHECK(gNetworkThread->StartWithOptions(options));
-
- gReqContext = new SfRequestContext;
-
- gNetworkChangeNotifier.reset(net::NetworkChangeNotifier::Create());
-
- net::AndroidNetworkLibrary::RegisterSharedInstance(
- new SfNetworkLibrary);
- logging::SetLogMessageHandler(logMessageHandler);
- }
-}
-
-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_BASIC;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-SfRequestContext::SfRequestContext() {
- mUserAgent = MakeUserAgent().c_str();
-
- set_net_log(new SfNetLog());
-
- set_host_resolver(
- net::CreateSystemHostResolver(
- net::HostResolver::kDefaultParallelism,
- NULL /* resolver_proc */,
- net_log()));
-
- set_ssl_config_service(
- net::SSLConfigService::CreateSystemSSLConfigService());
-
- mProxyConfigService = new net::ProxyConfigServiceAndroid;
-
- set_proxy_service(net::ProxyService::CreateWithoutProxyResolver(
- mProxyConfigService, net_log()));
-
- set_http_transaction_factory(new net::HttpCache(
- host_resolver(),
- new net::CertVerifier(),
- dnsrr_resolver(),
- dns_cert_checker(),
- proxy_service(),
- ssl_config_service(),
- net::HttpAuthHandlerFactory::CreateDefault(host_resolver()),
- network_delegate(),
- net_log(),
- NULL)); // backend_factory
-
- set_cookie_store(new net::CookieMonster(NULL, NULL));
-}
-
-const std::string &SfRequestContext::GetUserAgent(const GURL &url) const {
- return mUserAgent;
-}
-
-status_t SfRequestContext::updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
- Mutex::Autolock autoLock(mProxyConfigLock);
-
- if (host == NULL || *host == '\0') {
- MY_LOGV("updateProxyConfig NULL");
-
- std::string proxy;
- std::string exList;
- mProxyConfigService->UpdateProxySettings(proxy, exList);
- } else {
-#if !defined(LOG_NDEBUG) || LOG_NDEBUG == 0
- LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG,
- "updateProxyConfig %s:%d, exclude '%s'",
- host, port, exclusionList);
-#endif
-
- std::string proxy = StringPrintf("%s:%d", host, port).c_str();
- std::string exList = exclusionList;
- mProxyConfigService->UpdateProxySettings(proxy, exList);
- }
-
- return OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-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);
-}
-
-// static
-status_t SfDelegate::UpdateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
- InitializeNetworkThreadIfNecessary();
-
- return gReqContext->updateProxyConfig(host, port, exclusionList);
-}
-
-void SfDelegate::setOwner(ChromiumHTTPDataSource *owner) {
- mOwner = owner;
-}
-
-void SfDelegate::setUID(uid_t uid) {
- gReqContext->setUID(uid);
-}
-
-bool SfDelegate::getUID(uid_t *uid) const {
- return gReqContext->getUID(uid);
-}
-
-void SfDelegate::OnReceivedRedirect(
- net::URLRequest *request, const GURL &new_url, bool *defer_redirect) {
- MY_LOGV("OnReceivedRedirect");
-}
-
-void SfDelegate::OnAuthRequired(
- net::URLRequest *request, net::AuthChallengeInfo *auth_info) {
- MY_LOGV("OnAuthRequired");
-
- inherited::OnAuthRequired(request, auth_info);
-}
-
-void SfDelegate::OnCertificateRequested(
- net::URLRequest *request, net::SSLCertRequestInfo *cert_request_info) {
- MY_LOGV("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_LOGV("OnGetCookies");
-}
-
-void SfDelegate::OnSetCookie(
- net::URLRequest *request,
- const std::string &cookie_line,
- const net::CookieOptions &options,
- bool blocked_by_policy) {
- MY_LOGV("OnSetCookie");
-}
-
-void SfDelegate::OnResponseStarted(net::URLRequest *request) {
- if (request->status().status() != net::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() != net::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() {
- if (mURLRequest == NULL) {
- return;
- }
-
- 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
deleted file mode 100644
index 975a1d3..0000000
--- a/media/libstagefright/chromium_http/support.h
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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/Mutex.h>
-#include <utils/String8.h>
-
-namespace net {
- struct ProxyConfigServiceAndroid;
-};
-
-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 net::URLRequestContext {
- SfRequestContext();
-
- virtual const std::string &GetUserAgent(const GURL &url) const;
-
- status_t updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList);
-
-private:
- Mutex mProxyConfigLock;
-
- std::string mUserAgent;
- net::ProxyConfigServiceAndroid *mProxyConfigService;
-
- 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);
-
- // Gets the UID of the calling process
- bool getUID(uid_t *uid) const;
-
- void setUID(uid_t uid);
-
- 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);
-
- static status_t UpdateProxyConfig(
- const char *host, int32_t port, const char *exclusionList);
-
-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/chromium_http_stub.cpp b/media/libstagefright/chromium_http_stub.cpp
deleted file mode 100644
index ed8a878..0000000
--- a/media/libstagefright/chromium_http_stub.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dlfcn.h>
-
-#include <media/stagefright/DataSource.h>
-
-#include "include/chromium_http_stub.h"
-#include "include/HTTPBase.h"
-
-namespace android {
-
-static bool gFirst = true;
-static void *gHandle;
-static Mutex gLibMutex;
-
-HTTPBase *(*gLib_createChromiumHTTPDataSource)(uint32_t flags);
-DataSource *(*gLib_createDataUriSource)(const char *uri);
-
-status_t (*gLib_UpdateChromiumHTTPDataSourceProxyConfig)(
- const char *host, int32_t port, const char *exclusionList);
-
-static bool load_libstagefright_chromium_http() {
- Mutex::Autolock autoLock(gLibMutex);
- void *sym;
-
- if (!gFirst) {
- return (gHandle != NULL);
- }
-
- gFirst = false;
-
- gHandle = dlopen("libstagefright_chromium_http.so", RTLD_NOW);
- if (gHandle == NULL) {
- return false;
- }
-
- sym = dlsym(gHandle, "createChromiumHTTPDataSource");
- if (sym == NULL) {
- gHandle = NULL;
- return false;
- }
- gLib_createChromiumHTTPDataSource = (HTTPBase *(*)(uint32_t))sym;
-
- sym = dlsym(gHandle, "createDataUriSource");
- if (sym == NULL) {
- gHandle = NULL;
- return false;
- }
- gLib_createDataUriSource = (DataSource *(*)(const char *))sym;
-
- sym = dlsym(gHandle, "UpdateChromiumHTTPDataSourceProxyConfig");
- if (sym == NULL) {
- gHandle = NULL;
- return false;
- }
- gLib_UpdateChromiumHTTPDataSourceProxyConfig =
- (status_t (*)(const char *, int32_t, const char *))sym;
-
- return true;
-}
-
-HTTPBase *createChromiumHTTPDataSource(uint32_t flags) {
- if (!load_libstagefright_chromium_http()) {
- return NULL;
- }
-
- return gLib_createChromiumHTTPDataSource(flags);
-}
-
-status_t UpdateChromiumHTTPDataSourceProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
- if (!load_libstagefright_chromium_http()) {
- return INVALID_OPERATION;
- }
-
- return gLib_UpdateChromiumHTTPDataSourceProxyConfig(
- host, port, exclusionList);
-}
-
-DataSource *createDataUriSource(const char *uri) {
- if (!load_libstagefright_chromium_http()) {
- return NULL;
- }
-
- return gLib_createDataUriSource(uri);
-}
-
-}
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index ffa64f9..49ff238 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -17,6 +17,8 @@
LOCAL_CFLAGS :=
+LOCAL_CFLAGS += -Werror
+
LOCAL_STATIC_LIBRARIES := libFraunhoferAAC
LOCAL_SHARED_LIBRARIES := \
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index f842e27..4ac8999 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -30,7 +30,7 @@
#define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */
-#define MAX_CHANNEL_COUNT 6 /* maximum number of audio channels that can be decoded */
+#define MAX_CHANNEL_COUNT 8 /* maximum number of audio channels that can be decoded */
// names of properties that can be used to override the default DRC settings
#define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level"
#define PROP_DRC_OVERRIDE_CUT "aac_drc_cut"
@@ -296,8 +296,11 @@
if (!(property_get("media.aac_51_output_enabled", value, NULL) &&
(!strcmp(value, "1") || !strcasecmp(value, "true")))) {
ALOGI("Downmixing multichannel AAC to stereo");
- aacDecoder_SetParam(mAACDecoder, AAC_PCM_OUTPUT_CHANNELS, 2);
+ aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
mStreamInfo->numChannels = 2;
+ // By default, the decoder creates a 5.1 channel downmix signal
+ // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output
+ // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1)
}
}
}
@@ -374,7 +377,7 @@
mNumSamplesOutput = 0;
}
- if (mIsADTS) {
+ if (mIsADTS && inHeader->nFilledLen) {
size_t adtsHeaderSize = 0;
// skip 30 bits, aac_frame_length follows.
// ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk
index 057c69b..58ec3ba 100644
--- a/media/libstagefright/codecs/aacenc/Android.mk
+++ b/media/libstagefright/codecs/aacenc/Android.mk
@@ -82,6 +82,8 @@
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV7
endif
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_STATIC_LIBRARY)
################################################################################
@@ -106,6 +108,8 @@
LOCAL_CFLAGS :=
+ LOCAL_CFLAGS += -Werror
+
LOCAL_STATIC_LIBRARIES := libFraunhoferAAC
LOCAL_SHARED_LIBRARIES := \
@@ -128,6 +132,8 @@
LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+ LOCAL_CFLAGS += -Werror
+
LOCAL_STATIC_LIBRARIES := \
libstagefright_aacenc
diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
index ff2b503..9a91579 100644
--- a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
+++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
@@ -338,7 +338,7 @@
return OK;
}
-void SoftAACEncoder2::onQueueFilled(OMX_U32 portIndex) {
+void SoftAACEncoder2::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError) {
return;
}
diff --git a/media/libstagefright/codecs/aacenc/basic_op/oper_32b.c b/media/libstagefright/codecs/aacenc/basic_op/oper_32b.c
index cc01927..1d029fc 100644
--- a/media/libstagefright/codecs/aacenc/basic_op/oper_32b.c
+++ b/media/libstagefright/codecs/aacenc/basic_op/oper_32b.c
@@ -24,6 +24,8 @@
#include "basic_op.h"
#include "oper_32b.h"
+#define UNUSED(x) (void)(x)
+
/*****************************************************************************
* *
* Function L_Extract() *
@@ -243,6 +245,8 @@
Word32 rsqrt(Word32 value, /*!< Operand to square root (0.0 ... 1) */
Word32 accuracy) /*!< Number of valid bits that will be calculated */
{
+ UNUSED(accuracy);
+
Word32 root = 0;
Word32 scale;
diff --git a/media/libstagefright/codecs/aacenc/src/aacenc.c b/media/libstagefright/codecs/aacenc/src/aacenc.c
index d1c8621..40db92c 100644
--- a/media/libstagefright/codecs/aacenc/src/aacenc.c
+++ b/media/libstagefright/codecs/aacenc/src/aacenc.c
@@ -27,6 +27,8 @@
#include "cmnMemory.h"
#include "memalign.h"
+#define UNUSED(x) (void)(x)
+
/**
* Init the audio codec module and return codec handle
* \param phCodec [OUT] Return the video codec handle
@@ -46,6 +48,8 @@
VO_MEM_OPERATOR *pMemOP;
int interMem;
+ UNUSED(vType);
+
interMem = 0;
error = 0;
@@ -471,6 +475,10 @@
*/
VO_U32 VO_API voAACEncGetParam(VO_HANDLE hCodec, VO_S32 uParamID, VO_PTR pData)
{
+ UNUSED(hCodec);
+ UNUSED(uParamID);
+ UNUSED(pData);
+
return VO_ERR_NONE;
}
diff --git a/media/libstagefright/codecs/aacenc/src/adj_thr.c b/media/libstagefright/codecs/aacenc/src/adj_thr.c
index ccfe883..471631c 100644
--- a/media/libstagefright/codecs/aacenc/src/adj_thr.c
+++ b/media/libstagefright/codecs/aacenc/src/adj_thr.c
@@ -72,7 +72,7 @@
const Word16 nChannels)
{
Word16 ch, sfb, sfbGrp;
- Word32 *pthrExp, *psfbThre;
+ Word32 *pthrExp = NULL, *psfbThre;
for (ch=0; ch<nChannels; ch++) {
PSY_OUT_CHANNEL *psyOutChan = &psyOutChannel[ch];
for(sfbGrp = 0; sfbGrp < psyOutChan->sfbCnt; sfbGrp+= psyOutChan->sfbPerGroup)
diff --git a/media/libstagefright/codecs/aacenc/src/bitenc.c b/media/libstagefright/codecs/aacenc/src/bitenc.c
index fcc12dd..d1fd647 100644
--- a/media/libstagefright/codecs/aacenc/src/bitenc.c
+++ b/media/libstagefright/codecs/aacenc/src/bitenc.c
@@ -26,6 +26,7 @@
#include "qc_data.h"
#include "interface.h"
+#define UNUSED(x) (void)(x)
static const Word16 globalGainOffset = 100;
static const Word16 icsReservedBit = 0;
@@ -585,6 +586,8 @@
Word16 elementUsedBits;
Word16 frameBits=0;
+ UNUSED(ancBytes);
+
/* struct bitbuffer bsWriteCopy; */
bitMarkUp = GetBitsAvail(hBitStream);
if(qcOut->qcElement.adtsUsed) /* write adts header*/
diff --git a/media/libstagefright/codecs/aacenc/src/dyn_bits.c b/media/libstagefright/codecs/aacenc/src/dyn_bits.c
index 7769188..4d763d0 100644
--- a/media/libstagefright/codecs/aacenc/src/dyn_bits.c
+++ b/media/libstagefright/codecs/aacenc/src/dyn_bits.c
@@ -25,7 +25,6 @@
#include "bit_cnt.h"
#include "psy_const.h"
-
/*****************************************************************************
*
* function name: buildBitLookUp
@@ -226,7 +225,7 @@
}
while (TRUE) {
- Word16 maxMergeGain, maxNdx, maxNdxNext, maxNdxLast;
+ Word16 maxMergeGain, maxNdx = 0, maxNdxNext, maxNdxLast;
maxMergeGain = findMaxMerge(mergeGainLookUp, sectionInfo, maxSfb, &maxNdx);
diff --git a/media/libstagefright/codecs/aacenc/src/psy_main.c b/media/libstagefright/codecs/aacenc/src/psy_main.c
index 4e9218c..6f0679c 100644
--- a/media/libstagefright/codecs/aacenc/src/psy_main.c
+++ b/media/libstagefright/codecs/aacenc/src/psy_main.c
@@ -38,6 +38,8 @@
#include "tns_func.h"
#include "memalign.h"
+#define UNUSED(x) (void)(x)
+
/* long start short stop */
static Word16 blockType2windowShape[] = {KBD_WINDOW,SINE_WINDOW,SINE_WINDOW,KBD_WINDOW};
@@ -170,7 +172,9 @@
*****************************************************************************/
Word16 PsyOutDelete(PSY_OUT *hPsyOut, VO_MEM_OPERATOR *pMemOP)
{
- hPsyOut=NULL;
+ UNUSED(hPsyOut);
+ UNUSED(pMemOP);
+
return 0;
}
diff --git a/media/libstagefright/codecs/aacenc/src/qc_main.c b/media/libstagefright/codecs/aacenc/src/qc_main.c
index 48ff300..e5d78aa 100644
--- a/media/libstagefright/codecs/aacenc/src/qc_main.c
+++ b/media/libstagefright/codecs/aacenc/src/qc_main.c
@@ -33,6 +33,7 @@
#include "channel_map.h"
#include "memalign.h"
+#define UNUSED(x) (void)(x)
typedef enum{
FRAME_LEN_BYTES_MODULO = 1,
@@ -204,11 +205,8 @@
**********************************************************************************/
void QCDelete(QC_STATE *hQC, VO_MEM_OPERATOR *pMemOP)
{
-
- /*
- nothing to do
- */
- hQC=NULL;
+ UNUSED(hQC);
+ UNUSED(pMemOP);
}
/*********************************************************************************
diff --git a/media/libstagefright/codecs/aacenc/src/tns.c b/media/libstagefright/codecs/aacenc/src/tns.c
index 455a864..5172612 100644
--- a/media/libstagefright/codecs/aacenc/src/tns.c
+++ b/media/libstagefright/codecs/aacenc/src/tns.c
@@ -30,6 +30,8 @@
#include "psy_configuration.h"
#include "tns_func.h"
+#define UNUSED(x) (void)(x)
+
#define TNS_MODIFY_BEGIN 2600 /* Hz */
#define RATIO_PATCH_LOWER_BORDER 380 /* Hz */
#define TNS_GAIN_THRESH 141 /* 1.41*100 */
@@ -643,6 +645,8 @@
Word32 i;
Word32 tnsOrderPlus1 = tnsOrder + 1;
+ UNUSED(window);
+
assert(tnsOrder <= TNS_MAX_ORDER); /* remove asserts later? (btg) */
for(i=0;i<tnsOrder;i++) {
diff --git a/media/libstagefright/codecs/amrnb/common/Android.mk b/media/libstagefright/codecs/amrnb/common/Android.mk
index 30ce29c..a2b3c8f 100644
--- a/media/libstagefright/codecs/amrnb/common/Android.mk
+++ b/media/libstagefright/codecs/amrnb/common/Android.mk
@@ -69,6 +69,8 @@
LOCAL_CFLAGS := \
-DOSCL_UNUSED_ARG= -DOSCL_IMPORT_REF= -DOSCL_EXPORT_REF=
+LOCAL_CFLAGS += -Werror
+
LOCAL_MODULE := libstagefright_amrnb_common
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk
index 8d6c6f8..b067456 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.mk
+++ b/media/libstagefright/codecs/amrnb/dec/Android.mk
@@ -47,6 +47,8 @@
LOCAL_CFLAGS := \
-DOSCL_UNUSED_ARG= -DOSCL_IMPORT_REF=
+LOCAL_CFLAGS += -Werror
+
LOCAL_MODULE := libstagefright_amrnbdec
include $(BUILD_STATIC_LIBRARY)
@@ -68,6 +70,8 @@
LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+LOCAL_CFLAGS += -Werror
+
LOCAL_STATIC_LIBRARIES := \
libstagefright_amrnbdec libstagefright_amrwbdec
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
index 3320688..d1b0f76 100644
--- a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
@@ -274,7 +274,7 @@
return frameSize;
}
-void SoftAMR::onQueueFilled(OMX_U32 portIndex) {
+void SoftAMR::onQueueFilled(OMX_U32 /* portIndex */) {
List<BufferInfo *> &inQueue = getPortQueue(0);
List<BufferInfo *> &outQueue = getPortQueue(1);
@@ -428,7 +428,7 @@
}
}
-void SoftAMR::onPortFlushCompleted(OMX_U32 portIndex) {
+void SoftAMR::onPortFlushCompleted(OMX_U32 /* portIndex */) {
}
void SoftAMR::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.mk b/media/libstagefright/codecs/amrnb/enc/Android.mk
index f4e467a..afc0b89 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.mk
+++ b/media/libstagefright/codecs/amrnb/enc/Android.mk
@@ -69,6 +69,8 @@
LOCAL_CFLAGS := \
-DOSCL_UNUSED_ARG=
+LOCAL_CFLAGS += -Werror
+
LOCAL_MODULE := libstagefright_amrnbenc
include $(BUILD_STATIC_LIBRARY)
@@ -88,6 +90,8 @@
$(LOCAL_PATH)/../common/include \
$(LOCAL_PATH)/../common
+LOCAL_CFLAGS += -Werror
+
LOCAL_STATIC_LIBRARIES := \
libstagefright_amrnbenc
diff --git a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
index 50b739c..9489457 100644
--- a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
@@ -270,7 +270,7 @@
}
}
-void SoftAMRNBEncoder::onQueueFilled(OMX_U32 portIndex) {
+void SoftAMRNBEncoder::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError) {
return;
}
diff --git a/media/libstagefright/codecs/amrwb/Android.mk b/media/libstagefright/codecs/amrwb/Android.mk
index 677107f..efdf988 100644
--- a/media/libstagefright/codecs/amrwb/Android.mk
+++ b/media/libstagefright/codecs/amrwb/Android.mk
@@ -50,6 +50,8 @@
LOCAL_CFLAGS := \
-DOSCL_UNUSED_ARG= -DOSCL_IMPORT_REF=
+LOCAL_CFLAGS += -Werror
+
LOCAL_MODULE := libstagefright_amrwbdec
include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/codecs/amrwbenc/Android.mk b/media/libstagefright/codecs/amrwbenc/Android.mk
index c5b8e0c..64fe8d1 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.mk
+++ b/media/libstagefright/codecs/amrwbenc/Android.mk
@@ -112,6 +112,8 @@
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV7
endif
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_STATIC_LIBRARY)
################################################################################
@@ -126,6 +128,8 @@
frameworks/av/media/libstagefright/codecs/common/include \
frameworks/native/include/media/openmax
+LOCAL_CFLAGS += -Werror
+
LOCAL_STATIC_LIBRARIES := \
libstagefright_amrwbenc
diff --git a/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp
index 9ccb49c..91a512d 100644
--- a/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp
+++ b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp
@@ -317,7 +317,7 @@
}
}
-void SoftAMRWBEncoder::onQueueFilled(OMX_U32 portIndex) {
+void SoftAMRWBEncoder::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError) {
return;
}
diff --git a/media/libstagefright/codecs/amrwbenc/src/autocorr.c b/media/libstagefright/codecs/amrwbenc/src/autocorr.c
index 8c477ca..0b2ea89 100644
--- a/media/libstagefright/codecs/amrwbenc/src/autocorr.c
+++ b/media/libstagefright/codecs/amrwbenc/src/autocorr.c
@@ -28,6 +28,8 @@
#include "acelp.h"
#include "ham_wind.tab"
+#define UNUSED(x) (void)(x)
+
void Autocorr(
Word16 x[], /* (i) : Input signal */
Word16 m, /* (i) : LPC order */
@@ -40,6 +42,8 @@
Word32 L_sum, L_sum1, L_tmp, F_LEN;
Word16 *p1,*p2,*p3;
const Word16 *p4;
+ UNUSED(m);
+
/* Windowing of signal */
p1 = x;
p4 = vo_window;
diff --git a/media/libstagefright/codecs/amrwbenc/src/convolve.c b/media/libstagefright/codecs/amrwbenc/src/convolve.c
index acba532..4c1f7d4 100644
--- a/media/libstagefright/codecs/amrwbenc/src/convolve.c
+++ b/media/libstagefright/codecs/amrwbenc/src/convolve.c
@@ -25,6 +25,8 @@
#include "typedef.h"
#include "basic_op.h"
+#define UNUSED(x) (void)(x)
+
void Convolve (
Word16 x[], /* (i) : input vector */
Word16 h[], /* (i) : impulse response */
@@ -35,6 +37,8 @@
Word32 i, n;
Word16 *tmpH,*tmpX;
Word32 s;
+ UNUSED(L);
+
for (n = 0; n < 64;)
{
tmpH = h+n;
diff --git a/media/libstagefright/codecs/amrwbenc/src/pitch_f4.c b/media/libstagefright/codecs/amrwbenc/src/pitch_f4.c
index 0d66c31..b66b55e 100644
--- a/media/libstagefright/codecs/amrwbenc/src/pitch_f4.c
+++ b/media/libstagefright/codecs/amrwbenc/src/pitch_f4.c
@@ -31,6 +31,8 @@
#define UP_SAMP 4
#define L_INTERPOL1 4
+#define UNUSED(x) (void)(x)
+
/* Local functions */
#ifdef ASM_OPT
@@ -171,6 +173,7 @@
Word32 corr, exp_corr, norm, exp, scale;
Word16 exp_norm, excf[L_SUBFR], tmp;
Word32 L_tmp, L_tmp1, L_tmp2;
+ UNUSED(L_subfr);
/* compute the filtered excitation for the first delay t_min */
k = -t_min;
diff --git a/media/libstagefright/codecs/amrwbenc/src/syn_filt.c b/media/libstagefright/codecs/amrwbenc/src/syn_filt.c
index 1bda05a..961aadc 100644
--- a/media/libstagefright/codecs/amrwbenc/src/syn_filt.c
+++ b/media/libstagefright/codecs/amrwbenc/src/syn_filt.c
@@ -26,6 +26,8 @@
#include "math_op.h"
#include "cnst.h"
+#define UNUSED(x) (void)(x)
+
void Syn_filt(
Word16 a[], /* (i) Q12 : a[m+1] prediction coefficients */
Word16 x[], /* (i) : input signal */
@@ -95,6 +97,8 @@
Word32 i,a0;
Word32 L_tmp, L_tmp1;
Word16 *p1, *p2, *p3;
+ UNUSED(m);
+
a0 = a[0] >> (4 + Qnew); /* input / 16 and >>Qnew */
/* Do the filtering. */
for (i = 0; i < lg; i++)
diff --git a/media/libstagefright/codecs/amrwbenc/src/voAMRWBEnc.c b/media/libstagefright/codecs/amrwbenc/src/voAMRWBEnc.c
index ea9da52..df7b9b3 100644
--- a/media/libstagefright/codecs/amrwbenc/src/voAMRWBEnc.c
+++ b/media/libstagefright/codecs/amrwbenc/src/voAMRWBEnc.c
@@ -39,6 +39,8 @@
#include "mem_align.h"
#include "cmnMemory.h"
+#define UNUSED(x) (void)(x)
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -1602,6 +1604,8 @@
VO_MEM_OPERATOR voMemoprator;
#endif
VO_MEM_OPERATOR *pMemOP;
+ UNUSED(vType);
+
int interMem = 0;
if(pUserData == NULL || pUserData->memflag != VO_IMF_USERMEMOPERATOR || pUserData->memData == NULL )
diff --git a/media/libstagefright/codecs/avc/common/Android.mk b/media/libstagefright/codecs/avc/common/Android.mk
index 22dee15..844ef0a 100644
--- a/media/libstagefright/codecs/avc/common/Android.mk
+++ b/media/libstagefright/codecs/avc/common/Android.mk
@@ -16,4 +16,6 @@
$(LOCAL_PATH)/src \
$(LOCAL_PATH)/include
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/avc/enc/Android.mk b/media/libstagefright/codecs/avc/enc/Android.mk
index 7d17c2a..537ba42 100644
--- a/media/libstagefright/codecs/avc/enc/Android.mk
+++ b/media/libstagefright/codecs/avc/enc/Android.mk
@@ -30,6 +30,8 @@
LOCAL_CFLAGS := \
-DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF=
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_STATIC_LIBRARY)
################################################################################
@@ -69,4 +71,6 @@
LOCAL_MODULE := libstagefright_soft_h264enc
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp
index 1d66120..a15b040 100644
--- a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp
@@ -34,6 +34,12 @@
#include "SoftAVCEncoder.h"
+#if LOG_NDEBUG
+#define UNUSED_UNLESS_VERBOSE(x) (void)(x)
+#else
+#define UNUSED_UNLESS_VERBOSE(x)
+#endif
+
namespace android {
template<class T>
@@ -136,14 +142,14 @@
}
static void* MallocWrapper(
- void *userData, int32_t size, int32_t attrs) {
+ void * /* userData */, int32_t size, int32_t /* attrs */) {
void *ptr = malloc(size);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
-static void FreeWrapper(void *userData, void* ptr) {
+static void FreeWrapper(void * /* userData */, void* ptr) {
free(ptr);
}
@@ -217,7 +223,7 @@
mHandle->CBAVC_Free = FreeWrapper;
CHECK(mEncParams != NULL);
- memset(mEncParams, 0, sizeof(mEncParams));
+ memset(mEncParams, 0, sizeof(*mEncParams));
mEncParams->rate_control = AVC_ON;
mEncParams->initQP = 0;
mEncParams->init_CBP_removal_delay = 1600;
@@ -593,6 +599,17 @@
mVideoHeight = def->format.video.nFrameHeight;
mVideoFrameRate = def->format.video.xFramerate >> 16;
mVideoColorFormat = def->format.video.eColorFormat;
+
+ OMX_PARAM_PORTDEFINITIONTYPE *portDef =
+ &editPortInfo(0)->mDef;
+ portDef->format.video.nFrameWidth = mVideoWidth;
+ portDef->format.video.nFrameHeight = mVideoHeight;
+ portDef->format.video.xFramerate = def->format.video.xFramerate;
+ portDef->format.video.eColorFormat =
+ (OMX_COLOR_FORMATTYPE) mVideoColorFormat;
+ portDef = &editPortInfo(1)->mDef;
+ portDef->format.video.nFrameWidth = mVideoWidth;
+ portDef->format.video.nFrameHeight = mVideoHeight;
} else {
mVideoBitRate = def->format.video.nBitrate;
}
@@ -711,7 +728,7 @@
}
}
-void SoftAVCEncoder::onQueueFilled(OMX_U32 portIndex) {
+void SoftAVCEncoder::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError || mSawInputEOS) {
return;
}
@@ -784,7 +801,7 @@
}
}
- buffer_handle_t srcBuffer; // for MetaDataMode only
+ buffer_handle_t srcBuffer = NULL; // for MetaDataMode only
// Get next input video frame
if (mReadyForNextFrame) {
@@ -871,7 +888,13 @@
CHECK(encoderStatus == AVCENC_SUCCESS || encoderStatus == AVCENC_NEW_IDR);
dataLength = outHeader->nAllocLen; // Reset the output buffer length
if (inHeader->nFilledLen > 0) {
+ if (outHeader->nAllocLen >= 4) {
+ memcpy(outPtr, "\x00\x00\x00\x01", 4);
+ outPtr += 4;
+ dataLength -= 4;
+ }
encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
+ dataLength = outPtr + dataLength - outHeader->pBuffer;
if (encoderStatus == AVCENC_SUCCESS) {
CHECK(NULL == PVAVCEncGetOverrunBuffer(mHandle));
} else if (encoderStatus == AVCENC_PICTURE_READY) {
@@ -947,6 +970,7 @@
}
void SoftAVCEncoder::signalBufferReturned(MediaBuffer *buffer) {
+ UNUSED_UNLESS_VERBOSE(buffer);
ALOGV("signalBufferReturned: %p", buffer);
}
diff --git a/media/libstagefright/codecs/avc/enc/src/bitstream_io.cpp b/media/libstagefright/codecs/avc/enc/src/bitstream_io.cpp
index 0e3037f..d71c327 100644
--- a/media/libstagefright/codecs/avc/enc/src/bitstream_io.cpp
+++ b/media/libstagefright/codecs/avc/enc/src/bitstream_io.cpp
@@ -103,6 +103,15 @@
{
num_bits -= 8;
byte = (current_word >> num_bits) & 0xFF;
+ if (stream->count_zeros == 2)
+ { /* for num_bits = 32, this can add 2 more bytes extra for EPBS */
+ if (byte <= 3)
+ {
+ *write_pnt++ = 0x3;
+ stream->write_pos++;
+ stream->count_zeros = 0;
+ }
+ }
if (byte != 0)
{
*write_pnt++ = byte;
@@ -114,12 +123,6 @@
stream->count_zeros++;
*write_pnt++ = byte;
stream->write_pos++;
- if (stream->count_zeros == 2)
- { /* for num_bits = 32, this can add 2 more bytes extra for EPBS */
- *write_pnt++ = 0x3;
- stream->write_pos++;
- stream->count_zeros = 0;
- }
}
}
diff --git a/media/libstagefright/codecs/common/Android.mk b/media/libstagefright/codecs/common/Android.mk
index a33cb92..b0010ff 100644
--- a/media/libstagefright/codecs/common/Android.mk
+++ b/media/libstagefright/codecs/common/Android.mk
@@ -14,6 +14,8 @@
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/common/cmnMemory.c b/media/libstagefright/codecs/common/cmnMemory.c
index aa52bd9..5bb6cc4 100644
--- a/media/libstagefright/codecs/common/cmnMemory.c
+++ b/media/libstagefright/codecs/common/cmnMemory.c
@@ -26,8 +26,12 @@
//VO_MEM_OPERATOR g_memOP;
+#define UNUSED(x) (void)(x)
+
VO_U32 cmnMemAlloc (VO_S32 uID, VO_MEM_INFO * pMemInfo)
{
+ UNUSED(uID);
+
if (!pMemInfo)
return VO_ERR_INVALID_ARG;
@@ -37,34 +41,48 @@
VO_U32 cmnMemFree (VO_S32 uID, VO_PTR pMem)
{
+ UNUSED(uID);
+
free (pMem);
return 0;
}
VO_U32 cmnMemSet (VO_S32 uID, VO_PTR pBuff, VO_U8 uValue, VO_U32 uSize)
{
+ UNUSED(uID);
+
memset (pBuff, uValue, uSize);
return 0;
}
VO_U32 cmnMemCopy (VO_S32 uID, VO_PTR pDest, VO_PTR pSource, VO_U32 uSize)
{
+ UNUSED(uID);
+
memcpy (pDest, pSource, uSize);
return 0;
}
VO_U32 cmnMemCheck (VO_S32 uID, VO_PTR pBuffer, VO_U32 uSize)
{
+ UNUSED(uID);
+ UNUSED(pBuffer);
+ UNUSED(uSize);
+
return 0;
}
VO_S32 cmnMemCompare (VO_S32 uID, VO_PTR pBuffer1, VO_PTR pBuffer2, VO_U32 uSize)
{
+ UNUSED(uID);
+
return memcmp(pBuffer1, pBuffer2, uSize);
}
VO_U32 cmnMemMove (VO_S32 uID, VO_PTR pDest, VO_PTR pSource, VO_U32 uSize)
{
+ UNUSED(uID);
+
memmove (pDest, pSource, uSize);
return 0;
}
diff --git a/media/libstagefright/codecs/flac/enc/Android.mk b/media/libstagefright/codecs/flac/enc/Android.mk
index f01d605..59a11de 100644
--- a/media/libstagefright/codecs/flac/enc/Android.mk
+++ b/media/libstagefright/codecs/flac/enc/Android.mk
@@ -9,6 +9,8 @@
frameworks/native/include/media/openmax \
external/flac/include
+LOCAL_CFLAGS += -Werror
+
LOCAL_SHARED_LIBRARIES := \
libstagefright libstagefright_omx libstagefright_foundation libutils liblog
diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
index e64fe72..40661e7 100644
--- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
+++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
@@ -27,6 +27,12 @@
#define FLAC_COMPRESSION_LEVEL_DEFAULT 5
#define FLAC_COMPRESSION_LEVEL_MAX 8
+#if LOG_NDEBUG
+#define UNUSED_UNLESS_VERBOSE(x) (void)(x)
+#else
+#define UNUSED_UNLESS_VERBOSE(x)
+#endif
+
namespace android {
template<class T>
@@ -257,7 +263,7 @@
}
void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) {
-
+ UNUSED_UNLESS_VERBOSE(portIndex);
ALOGV("SoftFlacEncoder::onQueueFilled(portIndex=%ld)", portIndex);
if (mSignalledError) {
@@ -343,10 +349,11 @@
}
}
-
FLAC__StreamEncoderWriteStatus SoftFlacEncoder::onEncodedFlacAvailable(
const FLAC__byte buffer[],
- size_t bytes, unsigned samples, unsigned current_frame) {
+ size_t bytes, unsigned samples,
+ unsigned current_frame) {
+ UNUSED_UNLESS_VERBOSE(current_frame);
ALOGV("SoftFlacEncoder::onEncodedFlacAvailable(bytes=%d, samples=%d, curr_frame=%d)",
bytes, samples, current_frame);
@@ -444,8 +451,12 @@
// static
FLAC__StreamEncoderWriteStatus SoftFlacEncoder::flacEncoderWriteCallback(
- const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[],
- size_t bytes, unsigned samples, unsigned current_frame, void *client_data) {
+ const FLAC__StreamEncoder * /* encoder */,
+ const FLAC__byte buffer[],
+ size_t bytes,
+ unsigned samples,
+ unsigned current_frame,
+ void *client_data) {
return ((SoftFlacEncoder*) client_data)->onEncodedFlacAvailable(
buffer, bytes, samples, current_frame);
}
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
index 4c80da6..a0112e1 100644
--- a/media/libstagefright/codecs/g711/dec/Android.mk
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -14,4 +14,6 @@
LOCAL_MODULE := libstagefright_soft_g711dec
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.cpp b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
index bcdd3c7..160ada0 100644
--- a/media/libstagefright/codecs/g711/dec/SoftG711.cpp
+++ b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
@@ -182,7 +182,7 @@
}
}
-void SoftG711::onQueueFilled(OMX_U32 portIndex) {
+void SoftG711::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError) {
return;
}
diff --git a/media/libstagefright/codecs/gsm/dec/Android.mk b/media/libstagefright/codecs/gsm/dec/Android.mk
index 71613d2..30868d5 100644
--- a/media/libstagefright/codecs/gsm/dec/Android.mk
+++ b/media/libstagefright/codecs/gsm/dec/Android.mk
@@ -9,6 +9,8 @@
frameworks/native/include/media/openmax \
external/libgsm/inc
+LOCAL_CFLAGS += -Werror
+
LOCAL_SHARED_LIBRARIES := \
libstagefright libstagefright_omx libstagefright_foundation libutils liblog
diff --git a/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp b/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp
index 00e0c85..18f7d29 100644
--- a/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp
+++ b/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp
@@ -172,7 +172,7 @@
}
}
-void SoftGSM::onQueueFilled(OMX_U32 portIndex) {
+void SoftGSM::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError) {
return;
}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
index a3d5779..1d232c6 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
@@ -46,6 +46,8 @@
LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF=
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_STATIC_LIBRARY)
################################################################################
@@ -72,4 +74,6 @@
LOCAL_MODULE := libstagefright_soft_mpeg4dec
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index fb2a430..0d1ab71 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -91,7 +91,7 @@
return OK;
}
-void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) {
+void SoftMPEG4::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError || mOutputPortSettingsChange != NONE) {
return;
}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/get_pred_adv_b_add.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/get_pred_adv_b_add.cpp
index e23f23d..fe9e7dc 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/get_pred_adv_b_add.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/get_pred_adv_b_add.cpp
@@ -96,7 +96,7 @@
offset = width - B_SIZE; /* offset for prev */
offset2 = (pred_width_rnd >> 1) - 4; /* offset for pred_block */
- tmp = (uint32)prev & 0x3;
+ tmp = (uintptr_t)prev & 0x3;
pred_block -= offset2; /* preset */
if (tmp == 0) /* word-aligned */
@@ -203,7 +203,7 @@
/* Branch based on pixel location (half-pel or full-pel) for x and y */
pred_block -= offset2; /* preset */
- tmp = (uint32)prev & 3;
+ tmp = (uintptr_t)prev & 3;
mask = 254;
mask |= (mask << 8);
mask |= (mask << 16); /* 0xFEFEFEFE */
@@ -532,7 +532,7 @@
/* Branch based on pixel location (half-pel or full-pel) for x and y */
pred_block -= offset2; /* preset */
- tmp = (uint32)prev & 3;
+ tmp = (uintptr_t)prev & 3;
mask = 254;
mask |= (mask << 8);
mask |= (mask << 16); /* 0xFEFEFEFE */
@@ -884,7 +884,7 @@
mask |= (mask << 8);
mask |= (mask << 16); /* 0x3f3f3f3f */
- tmp = (uint32)prev & 3;
+ tmp = (uintptr_t)prev & 3;
pred_block -= 4; /* preset */
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.mk b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
index 83a2dd2..c9006d9 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
@@ -33,6 +33,8 @@
$(TOP)/frameworks/av/media/libstagefright/include \
$(TOP)/frameworks/native/include/media/openmax
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_STATIC_LIBRARY)
################################################################################
@@ -72,4 +74,6 @@
LOCAL_MODULE := libstagefright_soft_mpeg4enc
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
index e02af90..2c73e57 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -620,7 +620,7 @@
}
}
-void SoftMPEG4Encoder::onQueueFilled(OMX_U32 portIndex) {
+void SoftMPEG4Encoder::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError || mSawInputEOS) {
return;
}
@@ -677,7 +677,7 @@
mSawInputEOS = true;
}
- buffer_handle_t srcBuffer; // for MetaDataMode only
+ buffer_handle_t srcBuffer = NULL; // for MetaDataMode only
if (inHeader->nFilledLen > 0) {
uint8_t *inputData = NULL;
if (mStoreMetaDataInBuffers) {
diff --git a/media/libstagefright/codecs/m4v_h263/enc/src/dct.cpp b/media/libstagefright/codecs/m4v_h263/enc/src/dct.cpp
index fa50eeb..fa4ae23 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/src/dct.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/src/dct.cpp
@@ -250,7 +250,7 @@
out[40] = k4 ; /* row 5 */
out++;
}
- while ((UInt)out < (UInt)dst) ;
+ while ((uintptr_t)out < (uintptr_t)dst) ;
return ;
}
@@ -455,7 +455,7 @@
out[8] = k5 ; /* row 1 */
out++;
}
- while ((UInt)out < (UInt)dst) ;
+ while ((uintptr_t)out < (uintptr_t)dst) ;
return ;
}
@@ -635,7 +635,7 @@
out[8] = k5 ; /* row 1 */
out++;
}
- while ((UInt)out < (UInt)dst) ;
+ while ((uintptr_t)out < (uintptr_t)dst) ;
return ;
}
@@ -846,7 +846,7 @@
out[40] = k4 ; /* row 5 */
out++;
}
- while ((UInt)out < (UInt)dst) ;
+ while ((uintptr_t)out < (uintptr_t)dst) ;
return ;
}
@@ -1033,7 +1033,7 @@
out[8] = k5 ; /* row 1 */
out++;
}
- while ((UInt)out < (UInt)dst) ;
+ while ((uintptr_t)out < (uintptr_t)dst) ;
return ;
}
@@ -1195,7 +1195,7 @@
out[8] = k5 ; /* row 1 */
out++;
}
- while ((UInt)out < (UInt)dst) ;
+ while ((uintptr_t)out < (uintptr_t)dst) ;
return ;
}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/src/fastcodemb.cpp b/media/libstagefright/codecs/m4v_h263/enc/src/fastcodemb.cpp
index 6fd41c3..0ad39a6 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/src/fastcodemb.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/src/fastcodemb.cpp
@@ -572,7 +572,7 @@
cur2 = cur2 & (mask << 8); /* mask first and third bytes */
sum2 = sum2 + ((UInt)cur2 >> 8);
}
- while ((UInt)curInt < (UInt)end);
+ while ((uintptr_t)curInt < (uintptr_t)end);
cur1 = sum4 - (sum2 << 8); /* get even-sum */
cur1 = cur1 + sum2; /* add 16 bit even-sum and odd-sum*/
@@ -611,7 +611,7 @@
load2 = load2 & (mask << 8); /* even bytes */
sum2 += ((UInt)load2 >> 8); /* sum even bytes, 16 bit */
}
- while ((UInt)curInt < (UInt)end);
+ while ((uintptr_t)curInt < (uintptr_t)end);
load1 = sum4 - (sum2 << 8); /* get even-sum */
load1 = load1 + sum2; /* add 16 bit even-sum and odd-sum*/
load1 = load1 + (load1 << 16); /* add upper and lower 16 bit sum */
diff --git a/media/libstagefright/codecs/m4v_h263/enc/src/motion_comp.cpp b/media/libstagefright/codecs/m4v_h263/enc/src/motion_comp.cpp
index b81d278..06e8926 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/src/motion_comp.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/src/motion_comp.cpp
@@ -1959,7 +1959,7 @@
dst += offset;
src += offset;
}
- while ((UInt)src < (UInt)end);
+ while ((uintptr_t)src < (uintptr_t)end);
return ;
}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/src/sad_inline.h b/media/libstagefright/codecs/m4v_h263/enc/src/sad_inline.h
index ba77dfd..b865f23 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/src/sad_inline.h
+++ b/media/libstagefright/codecs/m4v_h263/enc/src/sad_inline.h
@@ -85,7 +85,7 @@
x9 = 0x80808080; /* const. */
- x8 = (uint32)ref & 0x3;
+ x8 = (uintptr_t)ref & 0x3;
if (x8 == 3)
goto SadMBOffset3;
if (x8 == 2)
diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk
index 135c715..8284490 100644
--- a/media/libstagefright/codecs/mp3dec/Android.mk
+++ b/media/libstagefright/codecs/mp3dec/Android.mk
@@ -50,6 +50,8 @@
LOCAL_CFLAGS := \
-DOSCL_UNUSED_ARG=
+LOCAL_CFLAGS += -Werror
+
LOCAL_MODULE := libstagefright_mp3dec
LOCAL_ARM_MODE := arm
@@ -69,6 +71,8 @@
$(LOCAL_PATH)/src \
$(LOCAL_PATH)/include
+LOCAL_CFLAGS += -Werror
+
LOCAL_SHARED_LIBRARIES := \
libstagefright libstagefright_omx libstagefright_foundation libutils liblog
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
index 877e3cb..a09ab7c 100644
--- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
@@ -188,7 +188,7 @@
}
}
-void SoftMP3::onQueueFilled(OMX_U32 portIndex) {
+void SoftMP3::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError || mOutputPortSettingsChange != NONE) {
return;
}
@@ -335,6 +335,9 @@
// depend on fragments from the last one decoded.
pvmp3_InitDecoder(mConfig, mDecoderBuf);
mIsFirst = true;
+ mSignalledError = false;
+ mSawInputEos = false;
+ mSignalledOutputEos = false;
}
}
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_mpeg2_get_scale_data.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_mpeg2_get_scale_data.cpp
index ee42dc5..499672b 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_mpeg2_get_scale_data.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_mpeg2_get_scale_data.cpp
@@ -139,7 +139,7 @@
int16 blocknumber = 0;
granuleInfo *gr_info = &(si->ch[ch].gran[gr]);
- uint32 scalefac_comp, int_scalefac_comp, new_slen[4];
+ uint32 scalefac_comp, int_scalefac_comp, new_slen[4] = { 0,0,0,0 };
scalefac_comp = gr_info->scalefac_compress;
diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk
index 7f2c46d..93ff64c 100644
--- a/media/libstagefright/codecs/on2/dec/Android.mk
+++ b/media/libstagefright/codecs/on2/dec/Android.mk
@@ -20,4 +20,6 @@
LOCAL_MODULE := libstagefright_soft_vpxdec
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
index 476e986..423a057 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -85,7 +85,7 @@
return OK;
}
-void SoftVPX::onQueueFilled(OMX_U32 portIndex) {
+void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) {
if (mOutputPortSettingsChange != NONE) {
return;
}
diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
index 5f2b5c8..5efe022 100644
--- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
+++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
@@ -141,6 +141,7 @@
mWidth(176),
mHeight(144),
mBitrate(192000), // in bps
+ mBitrateUpdated(false),
mBitrateControlMode(VPX_VBR), // variable bitrate
mFrameDurationUs(33333), // Defaults to 30 fps
mDCTPartitions(0),
@@ -536,6 +537,22 @@
return OMX_ErrorNone;
}
+ case OMX_IndexConfigVideoBitrate:
+ {
+ OMX_VIDEO_CONFIG_BITRATETYPE *params =
+ (OMX_VIDEO_CONFIG_BITRATETYPE *)_params;
+
+ if (params->nPortIndex != kOutputPortIndex) {
+ return OMX_ErrorBadPortIndex;
+ }
+
+ if (mBitrate != params->nEncodeBitrate) {
+ mBitrate = params->nEncodeBitrate;
+ mBitrateUpdated = true;
+ }
+ return OMX_ErrorNone;
+ }
+
default:
return SimpleSoftOMXComponent::setConfig(index, _params);
}
@@ -660,6 +677,9 @@
def->format.video.nFrameHeight = mHeight;
def->format.video.xFramerate = port->format.video.xFramerate;
def->format.video.eColorFormat = mColorFormat;
+ def = &editPortInfo(kOutputPortIndex)->mDef;
+ def->format.video.nFrameWidth = mWidth;
+ def->format.video.nFrameHeight = mHeight;
return OMX_ErrorNone;
} else if (port->nPortIndex == kOutputPortIndex) {
@@ -779,6 +799,21 @@
mKeyFrameRequested = false;
}
+ if (mBitrateUpdated) {
+ mCodecConfiguration->rc_target_bitrate = mBitrate/1000;
+ vpx_codec_err_t res = vpx_codec_enc_config_set(mCodecContext,
+ mCodecConfiguration);
+ if (res != VPX_CODEC_OK) {
+ ALOGE("vp8 encoder failed to update bitrate: %s",
+ vpx_codec_err_to_string(res));
+ notify(OMX_EventError,
+ OMX_ErrorUndefined,
+ 0, // Extra notification data
+ NULL); // Notification data pointer
+ }
+ mBitrateUpdated = false;
+ }
+
codec_return = vpx_codec_encode(
mCodecContext,
&raw_frame,
@@ -803,6 +838,8 @@
if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) {
outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts;
outputBufferHeader->nFlags = 0;
+ if (encoded_packet->data.frame.flags & VPX_FRAME_IS_KEY)
+ outputBufferHeader->nFlags |= OMX_BUFFERFLAG_SYNCFRAME;
outputBufferHeader->nOffset = 0;
outputBufferHeader->nFilledLen = encoded_packet->data.frame.sz;
memcpy(outputBufferHeader->pBuffer,
diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h
index 4ee5e51..076830f 100644
--- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h
+++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h
@@ -128,7 +128,10 @@
int32_t mHeight;
// Target bitrate set for the encoder, in bits per second.
- int32_t mBitrate;
+ uint32_t mBitrate;
+
+ // If a request for a change it bitrate has been received.
+ bool mBitrateUpdated;
// Bitrate control mode, either constant or variable
vpx_rc_mode mBitrateControlMode;
diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
index 7ddb13c..a7bde97 100644
--- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
+++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
@@ -98,7 +98,7 @@
return UNKNOWN_ERROR;
}
-void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
+void SoftAVC::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError || mOutputPortSettingsChange != NONE) {
return;
}
diff --git a/media/libstagefright/codecs/on2/h264dec/source/H264SwDecApi.c b/media/libstagefright/codecs/on2/h264dec/source/H264SwDecApi.c
index 2bb4c4d..524a3f0 100644
--- a/media/libstagefright/codecs/on2/h264dec/source/H264SwDecApi.c
+++ b/media/libstagefright/codecs/on2/h264dec/source/H264SwDecApi.c
@@ -42,6 +42,8 @@
#include "h264bsd_decoder.h"
#include "h264bsd_util.h"
+#define UNUSED(x) (void)(x)
+
/*------------------------------------------------------------------------------
Version Information
------------------------------------------------------------------------------*/
@@ -73,6 +75,7 @@
#endif
void H264SwDecTrace(char *string) {
+ UNUSED(string);
}
void* H264SwDecMalloc(u32 size) {
diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.c
index 493fb9e..7a262ed 100755
--- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.c
+++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.c
@@ -267,7 +267,7 @@
i32 firstPhase[16];
i32 *pTmp;
/* neighbours above, below, left and right */
- i32 a[4], b[4], l[4], r[4];
+ i32 a[4] = { 0,0,0,0 }, b[4], l[4] = { 0,0,0,0 }, r[4];
u32 A, B, L, R;
#ifdef H264DEC_OMXDL
u8 fillBuff[32*21 + 15 + 32];
diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c
index 15eabfb..52c85e5 100755
--- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c
+++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c
@@ -1110,7 +1110,7 @@
/* Variables */
- u32 i, j;
+ i32 i, j;
i32 a, b, c;
i32 tmp;
@@ -1123,20 +1123,20 @@
a = 16 * (above[15] + left[15]);
for (i = 0, b = 0; i < 8; i++)
- b += ((i32)i + 1) * (above[8+i] - above[6-i]);
+ b += (i + 1) * (above[8+i] - above[6-i]);
b = (5 * b + 32) >> 6;
for (i = 0, c = 0; i < 7; i++)
- c += ((i32)i + 1) * (left[8+i] - left[6-i]);
+ c += (i + 1) * (left[8+i] - left[6-i]);
/* p[-1,-1] has to be accessed through above pointer */
- c += ((i32)i + 1) * (left[8+i] - above[-1]);
+ c += (i + 1) * (left[8+i] - above[-1]);
c = (5 * c + 32) >> 6;
for (i = 0; i < 16; i++)
{
for (j = 0; j < 16; j++)
{
- tmp = (a + b * ((i32)j - 7) + c * ((i32)i - 7) + 16) >> 5;
+ tmp = (a + b * (j - 7) + c * (i - 7) + 16) >> 5;
data[i*16+j] = (u8)CLIP1(tmp);
}
}
diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.c
index c948776..b409a06 100755
--- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.c
+++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.c
@@ -42,6 +42,8 @@
#include "armVC.h"
#endif /* H264DEC_OMXDL */
+#define UNUSED(x) (void)(x)
+
/*------------------------------------------------------------------------------
2. External compiler flags
--------------------------------------------------------------------------------
@@ -2136,7 +2138,8 @@
i32 center,
i32 right)
{
-
+ UNUSED(left);
+ UNUSED(right);
ASSERT(ref);
ASSERT(fill);
diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_header.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_header.c
index a7c6f64..23401c6 100755
--- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_header.c
+++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_header.c
@@ -47,6 +47,8 @@
#include "h264bsd_nal_unit.h"
#include "h264bsd_dpb.h"
+#define UNUSED(x) (void)(x)
+
/*------------------------------------------------------------------------------
2. External compiler flags
--------------------------------------------------------------------------------
@@ -1407,6 +1409,7 @@
u32 tmp, value, i;
i32 ivalue;
strmData_t tmpStrmData[1];
+ UNUSED(nalUnitType);
/* Code */
diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c
index cc838fd..fb97a28 100755
--- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c
+++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c
@@ -186,7 +186,7 @@
return(HANTRO_FALSE);
if ( (bits > 8) ||
- ((h264bsdShowBits32(pStrmData)>>(32-bits)) != (1 << (bits-1))) )
+ ((h264bsdShowBits32(pStrmData)>>(32-bits)) != (1ul << (bits-1))) )
return(HANTRO_TRUE);
else
return(HANTRO_FALSE);
diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.h
index cb3adda..216ad04 100755
--- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.h
+++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.h
@@ -42,6 +42,7 @@
#include <stdio.h>
#endif
+#include <stdint.h>
#include "basetype.h"
#include "h264bsd_stream.h"
#include "h264bsd_image.h"
@@ -150,7 +151,7 @@
}
#define ALIGN(ptr, bytePos) \
- (ptr + ( ((bytePos - (int)ptr) & (bytePos - 1)) / sizeof(*ptr) ))
+ (ptr + ( ((bytePos - (uintptr_t)ptr) & (bytePos - 1)) / sizeof(*ptr) ))
extern const u32 h264bsdQpC[52];
diff --git a/media/libstagefright/codecs/opus/Android.mk b/media/libstagefright/codecs/opus/Android.mk
new file mode 100644
index 0000000..365b179
--- /dev/null
+++ b/media/libstagefright/codecs/opus/Android.mk
@@ -0,0 +1,4 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/media/libstagefright/codecs/opus/dec/Android.mk b/media/libstagefright/codecs/opus/dec/Android.mk
new file mode 100644
index 0000000..2379c5f
--- /dev/null
+++ b/media/libstagefright/codecs/opus/dec/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftOpus.cpp
+
+LOCAL_C_INCLUDES := \
+ external/libopus/include \
+ frameworks/av/media/libstagefright/include \
+ frameworks/native/include/media/openmax \
+
+LOCAL_SHARED_LIBRARIES := \
+ libopus libstagefright libstagefright_omx \
+ libstagefright_foundation libutils liblog
+
+LOCAL_MODULE := libstagefright_soft_opusdec
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp
new file mode 100644
index 0000000..b8084ae
--- /dev/null
+++ b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoftOpus"
+#include <utils/Log.h>
+
+#include "SoftOpus.h"
+#include <OMX_AudioExt.h>
+#include <OMX_IndexExt.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+extern "C" {
+ #include <opus.h>
+ #include <opus_multistream.h>
+}
+
+namespace android {
+
+static const int kRate = 48000;
+
+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;
+}
+
+SoftOpus::SoftOpus(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mInputBufferCount(0),
+ mDecoder(NULL),
+ mHeader(NULL),
+ mCodecDelay(0),
+ mSeekPreRoll(0),
+ mAnchorTimeUs(0),
+ mNumFramesOutput(0),
+ mOutputPortSettingsChange(NONE) {
+ initPorts();
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftOpus::~SoftOpus() {
+ if (mDecoder != NULL) {
+ opus_multistream_decoder_destroy(mDecoder);
+ mDecoder = NULL;
+ }
+ if (mHeader != NULL) {
+ delete mHeader;
+ mHeader = NULL;
+ }
+}
+
+void SoftOpus::initPorts() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 960 * 6;
+ 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_OPUS);
+
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding =
+ (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidOPUS;
+
+ 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 SoftOpus::initDecoder() {
+ return OK;
+}
+
+OMX_ERRORTYPE SoftOpus::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ switch ((int)index) {
+ case OMX_IndexParamAudioAndroidOpus:
+ {
+ OMX_AUDIO_PARAM_ANDROID_OPUSTYPE *opusParams =
+ (OMX_AUDIO_PARAM_ANDROID_OPUSTYPE *)params;
+
+ if (opusParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ opusParams->nAudioBandWidth = 0;
+ opusParams->nSampleRate = kRate;
+ opusParams->nBitRate = 0;
+
+ if (!isConfigured()) {
+ opusParams->nChannels = 1;
+ } else {
+ opusParams->nChannels = mHeader->channels;
+ }
+
+ 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;
+ pcmParams->nSamplingRate = kRate;
+
+ if (!isConfigured()) {
+ pcmParams->nChannels = 1;
+ } else {
+ pcmParams->nChannels = mHeader->channels;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftOpus::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ switch ((int)index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.opus",
+ OMX_MAX_STRINGNAME_SIZE - 1)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioAndroidOpus:
+ {
+ const OMX_AUDIO_PARAM_ANDROID_OPUSTYPE *opusParams =
+ (const OMX_AUDIO_PARAM_ANDROID_OPUSTYPE *)params;
+
+ if (opusParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+bool SoftOpus::isConfigured() const {
+ return mInputBufferCount >= 1;
+}
+
+static uint16_t ReadLE16(const uint8_t *data, size_t data_size,
+ uint32_t read_offset) {
+ if (read_offset + 1 > data_size)
+ return 0;
+ uint16_t val;
+ val = data[read_offset];
+ val |= data[read_offset + 1] << 8;
+ return val;
+}
+
+// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
+// mappings for up to 8 channels. This information is part of the Vorbis I
+// Specification:
+// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
+static const int kMaxChannels = 8;
+
+// Maximum packet size used in Xiph's opusdec.
+static const int kMaxOpusOutputPacketSizeSamples = 960 * 6;
+
+// Default audio output channel layout. Used to initialize |stream_map| in
+// OpusHeader, and passed to opus_multistream_decoder_create() when the header
+// does not contain mapping information. The values are valid only for mono and
+// stereo output: Opus streams with more than 2 channels require a stream map.
+static const int kMaxChannelsWithDefaultLayout = 2;
+static const uint8_t kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = { 0, 1 };
+
+// Parses Opus Header. Header spec: http://wiki.xiph.org/OggOpus#ID_Header
+static bool ParseOpusHeader(const uint8_t *data, size_t data_size,
+ OpusHeader* header) {
+ // Size of the Opus header excluding optional mapping information.
+ const size_t kOpusHeaderSize = 19;
+
+ // Offset to the channel count byte in the Opus header.
+ const size_t kOpusHeaderChannelsOffset = 9;
+
+ // Offset to the pre-skip value in the Opus header.
+ const size_t kOpusHeaderSkipSamplesOffset = 10;
+
+ // Offset to the gain value in the Opus header.
+ const size_t kOpusHeaderGainOffset = 16;
+
+ // Offset to the channel mapping byte in the Opus header.
+ const size_t kOpusHeaderChannelMappingOffset = 18;
+
+ // Opus Header contains a stream map. The mapping values are in the header
+ // beyond the always present |kOpusHeaderSize| bytes of data. The mapping
+ // data contains stream count, coupling information, and per channel mapping
+ // values:
+ // - Byte 0: Number of streams.
+ // - Byte 1: Number coupled.
+ // - Byte 2: Starting at byte 2 are |header->channels| uint8 mapping
+ // values.
+ const size_t kOpusHeaderNumStreamsOffset = kOpusHeaderSize;
+ const size_t kOpusHeaderNumCoupledOffset = kOpusHeaderNumStreamsOffset + 1;
+ const size_t kOpusHeaderStreamMapOffset = kOpusHeaderNumStreamsOffset + 2;
+
+ if (data_size < kOpusHeaderSize) {
+ ALOGV("Header size is too small.");
+ return false;
+ }
+ header->channels = *(data + kOpusHeaderChannelsOffset);
+
+ if (header->channels <= 0 || header->channels > kMaxChannels) {
+ ALOGV("Invalid Header, wrong channel count: %d", header->channels);
+ return false;
+ }
+ header->skip_samples = ReadLE16(data, data_size,
+ kOpusHeaderSkipSamplesOffset);
+ header->gain_db = static_cast<int16_t>(
+ ReadLE16(data, data_size,
+ kOpusHeaderGainOffset));
+ header->channel_mapping = *(data + kOpusHeaderChannelMappingOffset);
+ if (!header->channel_mapping) {
+ if (header->channels > kMaxChannelsWithDefaultLayout) {
+ ALOGV("Invalid Header, missing stream map.");
+ return false;
+ }
+ header->num_streams = 1;
+ header->num_coupled = header->channels > 1;
+ header->stream_map[0] = 0;
+ header->stream_map[1] = 1;
+ return true;
+ }
+ if (data_size < kOpusHeaderStreamMapOffset + header->channels) {
+ ALOGV("Invalid stream map; insufficient data for current channel "
+ "count: %d", header->channels);
+ return false;
+ }
+ header->num_streams = *(data + kOpusHeaderNumStreamsOffset);
+ header->num_coupled = *(data + kOpusHeaderNumCoupledOffset);
+ if (header->num_streams + header->num_coupled != header->channels) {
+ ALOGV("Inconsistent channel mapping.");
+ return false;
+ }
+ for (int i = 0; i < header->channels; ++i)
+ header->stream_map[i] = *(data + kOpusHeaderStreamMapOffset + i);
+ return true;
+}
+
+// Convert nanoseconds to number of samples.
+static uint64_t ns_to_samples(uint64_t ns, int kRate) {
+ return static_cast<double>(ns) * kRate / 1000000000;
+}
+
+void SoftOpus::onQueueFilled(OMX_U32 portIndex) {
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ if (mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ if (portIndex == 0 && mInputBufferCount < 3) {
+ BufferInfo *info = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *header = info->mHeader;
+
+ const uint8_t *data = header->pBuffer + header->nOffset;
+ size_t size = header->nFilledLen;
+
+ if (mInputBufferCount == 0) {
+ CHECK(mHeader == NULL);
+ mHeader = new OpusHeader();
+ memset(mHeader, 0, sizeof(*mHeader));
+ if (!ParseOpusHeader(data, size, mHeader)) {
+ ALOGV("Parsing Opus Header failed.");
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ uint8_t channel_mapping[kMaxChannels] = {0};
+ memcpy(&channel_mapping,
+ kDefaultOpusChannelLayout,
+ kMaxChannelsWithDefaultLayout);
+
+ int status = OPUS_INVALID_STATE;
+ mDecoder = opus_multistream_decoder_create(kRate,
+ mHeader->channels,
+ mHeader->num_streams,
+ mHeader->num_coupled,
+ channel_mapping,
+ &status);
+ if (!mDecoder || status != OPUS_OK) {
+ ALOGV("opus_multistream_decoder_create failed status=%s",
+ opus_strerror(status));
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+ status =
+ opus_multistream_decoder_ctl(mDecoder,
+ OPUS_SET_GAIN(mHeader->gain_db));
+ if (status != OPUS_OK) {
+ ALOGV("Failed to set OPUS header gain; status=%s",
+ opus_strerror(status));
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+ } else if (mInputBufferCount == 1) {
+ mCodecDelay = ns_to_samples(
+ *(reinterpret_cast<int64_t*>(header->pBuffer +
+ header->nOffset)),
+ kRate);
+ mSamplesToDiscard = mCodecDelay;
+ } else {
+ mSeekPreRoll = ns_to_samples(
+ *(reinterpret_cast<int64_t*>(header->pBuffer +
+ header->nOffset)),
+ kRate);
+ 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;
+ }
+
+ if (inHeader->nOffset == 0) {
+ mAnchorTimeUs = inHeader->nTimeStamp;
+ mNumFramesOutput = 0;
+ }
+
+ // When seeking to zero, |mCodecDelay| samples has to be discarded
+ // instead of |mSeekPreRoll| samples (as we would when seeking to any
+ // other timestamp).
+ if (inHeader->nTimeStamp == 0) {
+ mSamplesToDiscard = mCodecDelay;
+ }
+
+ const uint8_t *data = inHeader->pBuffer + inHeader->nOffset;
+ const uint32_t size = inHeader->nFilledLen;
+
+ int numFrames = opus_multistream_decode(mDecoder,
+ data,
+ size,
+ (int16_t *)outHeader->pBuffer,
+ kMaxOpusOutputPacketSizeSamples,
+ 0);
+ if (numFrames < 0) {
+ ALOGE("opus_multistream_decode returned %d", numFrames);
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ outHeader->nOffset = 0;
+ if (mSamplesToDiscard > 0) {
+ if (mSamplesToDiscard > numFrames) {
+ mSamplesToDiscard -= numFrames;
+ numFrames = 0;
+ } else {
+ numFrames -= mSamplesToDiscard;
+ outHeader->nOffset = mSamplesToDiscard * sizeof(int16_t) *
+ mHeader->channels;
+ mSamplesToDiscard = 0;
+ }
+ }
+
+ outHeader->nFilledLen = numFrames * sizeof(int16_t) * mHeader->channels;
+ outHeader->nFlags = 0;
+
+ outHeader->nTimeStamp = mAnchorTimeUs +
+ (mNumFramesOutput * 1000000ll) /
+ kRate;
+
+ 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 SoftOpus::onPortFlushCompleted(OMX_U32 portIndex) {
+ if (portIndex == 0 && mDecoder != NULL) {
+ // Make sure that the next buffer output does not still
+ // depend on fragments from the last one decoded.
+ mNumFramesOutput = 0;
+ opus_multistream_decoder_ctl(mDecoder, OPUS_RESET_STATE);
+ mAnchorTimeUs = 0;
+ mSamplesToDiscard = mSeekPreRoll;
+ }
+}
+
+void SoftOpus::onReset() {
+ mInputBufferCount = 0;
+ mNumFramesOutput = 0;
+ if (mDecoder != NULL) {
+ opus_multistream_decoder_destroy(mDecoder);
+ mDecoder = NULL;
+ }
+ if (mHeader != NULL) {
+ delete mHeader;
+ mHeader = NULL;
+ }
+
+ mOutputPortSettingsChange = NONE;
+}
+
+void SoftOpus::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::SoftOpus(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.h b/media/libstagefright/codecs/opus/dec/SoftOpus.h
new file mode 100644
index 0000000..97f6561
--- /dev/null
+++ b/media/libstagefright/codecs/opus/dec/SoftOpus.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * The Opus specification is part of IETF RFC 6716:
+ * http://tools.ietf.org/html/rfc6716
+ */
+
+#ifndef SOFT_OPUS_H_
+
+#define SOFT_OPUS_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct OpusMSDecoder;
+
+namespace android {
+
+struct OpusHeader {
+ int channels;
+ int skip_samples;
+ int channel_mapping;
+ int num_streams;
+ int num_coupled;
+ int16_t gain_db;
+ uint8_t stream_map[8];
+};
+
+struct SoftOpus : public SimpleSoftOMXComponent {
+ SoftOpus(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftOpus();
+
+ 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);
+ virtual void onReset();
+
+private:
+ enum {
+ kNumBuffers = 4,
+ kMaxNumSamplesPerBuffer = 960 * 6
+ };
+
+ size_t mInputBufferCount;
+
+ OpusMSDecoder *mDecoder;
+ OpusHeader *mHeader;
+
+ int64_t mCodecDelay;
+ int64_t mSeekPreRoll;
+ int64_t mSamplesToDiscard;
+ int64_t mAnchorTimeUs;
+ int64_t mNumFramesOutput;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ status_t initDecoder();
+ bool isConfigured() const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftOpus);
+};
+
+} // namespace android
+
+#endif // SOFT_OPUS_H_
diff --git a/media/libstagefright/codecs/raw/Android.mk b/media/libstagefright/codecs/raw/Android.mk
index fe90a03..87080e7 100644
--- a/media/libstagefright/codecs/raw/Android.mk
+++ b/media/libstagefright/codecs/raw/Android.mk
@@ -8,6 +8,8 @@
frameworks/av/media/libstagefright/include \
frameworks/native/include/media/openmax
+LOCAL_CFLAGS += -Werror
+
LOCAL_SHARED_LIBRARIES := \
libstagefright_omx libstagefright_foundation libutils liblog
diff --git a/media/libstagefright/codecs/raw/SoftRaw.cpp b/media/libstagefright/codecs/raw/SoftRaw.cpp
index 19d6f13..9d514a6 100644
--- a/media/libstagefright/codecs/raw/SoftRaw.cpp
+++ b/media/libstagefright/codecs/raw/SoftRaw.cpp
@@ -163,7 +163,7 @@
}
}
-void SoftRaw::onQueueFilled(OMX_U32 portIndex) {
+void SoftRaw::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError) {
return;
}
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk
index 2232353..217a6d2 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.mk
+++ b/media/libstagefright/codecs/vorbis/dec/Android.mk
@@ -16,4 +16,6 @@
LOCAL_MODULE := libstagefright_soft_vorbisdec
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/foundation/ANetworkSession.cpp b/media/libstagefright/foundation/ANetworkSession.cpp
index e629588..08c4a87 100644
--- a/media/libstagefright/foundation/ANetworkSession.cpp
+++ b/media/libstagefright/foundation/ANetworkSession.cpp
@@ -521,7 +521,7 @@
return err;
}
-void ANetworkSession::Session::dumpFragmentStats(const Fragment &frag) {
+void ANetworkSession::Session::dumpFragmentStats(const Fragment & /* frag */) {
#if 0
int64_t nowUs = ALooper::GetNowUs();
int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll;
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index ad2dab5..90a6a23 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -24,7 +24,7 @@
libutils \
liblog
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror
LOCAL_MODULE:= libstagefright_foundation
diff --git a/media/libstagefright/foundation/base64.cpp b/media/libstagefright/foundation/base64.cpp
index d5fb4e0..dcf5bef 100644
--- a/media/libstagefright/foundation/base64.cpp
+++ b/media/libstagefright/foundation/base64.cpp
@@ -33,6 +33,10 @@
if (n >= 2 && s.c_str()[n - 2] == '=') {
padding = 2;
+
+ if (n >= 3 && s.c_str()[n - 3] == '=') {
+ padding = 3;
+ }
}
}
@@ -71,7 +75,7 @@
if (((i + 1) % 4) == 0) {
out[j++] = (accum >> 16);
- if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
+ if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
if (j < outLen) { out[j++] = accum & 0xff; }
accum = 0;
diff --git a/media/libstagefright/http/Android.mk b/media/libstagefright/http/Android.mk
new file mode 100644
index 0000000..7f3307d
--- /dev/null
+++ b/media/libstagefright/http/Android.mk
@@ -0,0 +1,28 @@
+LOCAL_PATH:= $(call my-dir)
+
+ifneq ($(TARGET_BUILD_PDK), true)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ HTTPHelper.cpp \
+
+LOCAL_C_INCLUDES:= \
+ $(TOP)/frameworks/av/media/libstagefright \
+ $(TOP)/frameworks/native/include/media/openmax \
+ $(TOP)/frameworks/base/core/jni \
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright liblog libutils libbinder libstagefright_foundation \
+ libandroid_runtime \
+ libmedia
+
+LOCAL_MODULE:= libstagefright_http_support
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_CFLAGS += -Werror
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/media/libstagefright/http/HTTPHelper.cpp b/media/libstagefright/http/HTTPHelper.cpp
new file mode 100644
index 0000000..77845e2
--- /dev/null
+++ b/media/libstagefright/http/HTTPHelper.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "HTTPHelper"
+#include <utils/Log.h>
+
+#include "HTTPHelper.h"
+
+#include "android_runtime/AndroidRuntime.h"
+#include "android_util_Binder.h"
+#include <media/IMediaHTTPService.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include "jni.h"
+
+namespace android {
+
+sp<IMediaHTTPService> CreateHTTPServiceInCurrentJavaContext() {
+ if (AndroidRuntime::getJavaVM() == NULL) {
+ ALOGE("CreateHTTPServiceInCurrentJavaContext called outside "
+ "JAVA environment.");
+ return NULL;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ ScopedLocalRef<jclass> clazz(
+ env, env->FindClass("android/media/MediaHTTPService"));
+ CHECK(clazz.get() != NULL);
+
+ jmethodID constructID = env->GetMethodID(clazz.get(), "<init>", "()V");
+ CHECK(constructID != NULL);
+
+ ScopedLocalRef<jobject> httpServiceObj(
+ env, env->NewObject(clazz.get(), constructID));
+
+ sp<IMediaHTTPService> httpService;
+ if (httpServiceObj.get() != NULL) {
+ jmethodID asBinderID =
+ env->GetMethodID(clazz.get(), "asBinder", "()Landroid/os/IBinder;");
+ CHECK(asBinderID != NULL);
+
+ ScopedLocalRef<jobject> httpServiceBinderObj(
+ env, env->CallObjectMethod(httpServiceObj.get(), asBinderID));
+ CHECK(httpServiceBinderObj.get() != NULL);
+
+ sp<IBinder> binder =
+ ibinderForJavaObject(env, httpServiceBinderObj.get());
+
+ httpService = interface_cast<IMediaHTTPService>(binder);
+ }
+
+ return httpService;
+}
+
+} // namespace android
diff --git a/media/libstagefright/http/HTTPHelper.h b/media/libstagefright/http/HTTPHelper.h
new file mode 100644
index 0000000..8aef115
--- /dev/null
+++ b/media/libstagefright/http/HTTPHelper.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HTTP_HELPER_H_
+
+#define HTTP_HELPER_H_
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct IMediaHTTPService;
+
+sp<IMediaHTTPService> CreateHTTPServiceInCurrentJavaContext();
+
+} // namespace android
+
+#endif // HTTP_HELPER_H_
diff --git a/media/libstagefright/http/MediaHTTP.cpp b/media/libstagefright/http/MediaHTTP.cpp
new file mode 100644
index 0000000..2d29913
--- /dev/null
+++ b/media/libstagefright/http/MediaHTTP.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2013 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 "MediaHTTP"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaHTTP.h>
+
+#include <binder/IServiceManager.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/Utils.h>
+
+#include <media/IMediaHTTPConnection.h>
+
+namespace android {
+
+MediaHTTP::MediaHTTP(const sp<IMediaHTTPConnection> &conn)
+ : mInitCheck(NO_INIT),
+ mHTTPConnection(conn),
+ mCachedSizeValid(false),
+ mCachedSize(0ll),
+ mDrmManagerClient(NULL) {
+ mInitCheck = OK;
+}
+
+MediaHTTP::~MediaHTTP() {
+ clearDRMState_l();
+}
+
+status_t MediaHTTP::connect(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers,
+ off64_t /* offset */) {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ KeyedVector<String8, String8> extHeaders;
+ if (headers != NULL) {
+ extHeaders = *headers;
+ }
+ extHeaders.add(String8("User-Agent"), String8(MakeUserAgent().c_str()));
+
+ bool success = mHTTPConnection->connect(uri, &extHeaders);
+
+ mLastHeaders = extHeaders;
+ mLastURI = uri;
+
+ mCachedSizeValid = false;
+
+ return success ? OK : UNKNOWN_ERROR;
+}
+
+void MediaHTTP::disconnect() {
+ if (mInitCheck != OK) {
+ return;
+ }
+
+ mHTTPConnection->disconnect();
+}
+
+status_t MediaHTTP::initCheck() const {
+ return mInitCheck;
+}
+
+ssize_t MediaHTTP::readAt(off64_t offset, void *data, size_t size) {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ int64_t startTimeUs = ALooper::GetNowUs();
+
+ size_t numBytesRead = 0;
+ while (numBytesRead < size) {
+ size_t copy = size - numBytesRead;
+
+ if (copy > 64 * 1024) {
+ // limit the buffer sizes transferred across binder boundaries
+ // to avoid spurious transaction failures.
+ copy = 64 * 1024;
+ }
+
+ ssize_t n = mHTTPConnection->readAt(
+ offset + numBytesRead, (uint8_t *)data + numBytesRead, copy);
+
+ if (n < 0) {
+ return n;
+ } else if (n == 0) {
+ break;
+ }
+
+ numBytesRead += n;
+ }
+
+ int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
+
+ addBandwidthMeasurement(numBytesRead, delayUs);
+
+ return numBytesRead;
+}
+
+status_t MediaHTTP::getSize(off64_t *size) {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ // Caching the returned size so that it stays valid even after a
+ // disconnect. NuCachedSource2 relies on this.
+
+ if (!mCachedSizeValid) {
+ mCachedSize = mHTTPConnection->getSize();
+ mCachedSizeValid = true;
+ }
+
+ *size = mCachedSize;
+
+ return *size < 0 ? *size : OK;
+}
+
+uint32_t MediaHTTP::flags() {
+ return kWantsPrefetching | kIsHTTPBasedSource;
+}
+
+status_t MediaHTTP::reconnectAtOffset(off64_t offset) {
+ return connect(mLastURI.c_str(), &mLastHeaders, offset);
+}
+
+// DRM...
+
+sp<DecryptHandle> MediaHTTP::DrmInitialization(const char* mime) {
+ if (mDrmManagerClient == NULL) {
+ mDrmManagerClient = new DrmManagerClient();
+ }
+
+ if (mDrmManagerClient == NULL) {
+ return NULL;
+ }
+
+ if (mDecryptHandle == NULL) {
+ mDecryptHandle = mDrmManagerClient->openDecryptSession(
+ String8(mLastURI.c_str()), mime);
+ }
+
+ if (mDecryptHandle == NULL) {
+ delete mDrmManagerClient;
+ mDrmManagerClient = NULL;
+ }
+
+ return mDecryptHandle;
+}
+
+void MediaHTTP::getDrmInfo(
+ sp<DecryptHandle> &handle, DrmManagerClient **client) {
+ handle = mDecryptHandle;
+ *client = mDrmManagerClient;
+}
+
+String8 MediaHTTP::getUri() {
+ String8 uri;
+ if (OK == mHTTPConnection->getUri(&uri)) {
+ return uri;
+ }
+ return String8(mLastURI.c_str());
+}
+
+String8 MediaHTTP::getMIMEType() const {
+ if (mInitCheck != OK) {
+ return String8("application/octet-stream");
+ }
+
+ String8 mimeType;
+ status_t err = mHTTPConnection->getMIMEType(&mimeType);
+
+ if (err != OK) {
+ return String8("application/octet-stream");
+ }
+
+ return mimeType;
+}
+
+void MediaHTTP::clearDRMState_l() {
+ if (mDecryptHandle != NULL) {
+ // To release mDecryptHandle
+ CHECK(mDrmManagerClient);
+ mDrmManagerClient->closeDecryptSession(mDecryptHandle);
+ mDecryptHandle = NULL;
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk
index f3529f9..e8d558c 100644
--- a/media/libstagefright/httplive/Android.mk
+++ b/media/libstagefright/httplive/Android.mk
@@ -13,6 +13,8 @@
$(TOP)/frameworks/native/include/media/openmax \
$(TOP)/external/openssl/include
+LOCAL_CFLAGS += -Werror
+
LOCAL_SHARED_LIBRARIES := \
libbinder \
libcrypto \
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index bd12ddc..2af4682 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -27,6 +27,8 @@
#include "mpeg2ts/AnotherPacketSource.h"
#include <cutils/properties.h>
+#include <media/IMediaHTTPConnection.h>
+#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -34,9 +36,12 @@
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaHTTP.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
+#include <utils/Mutex.h>
+
#include <ctype.h>
#include <openssl/aes.h>
#include <openssl/md5.h>
@@ -44,45 +49,62 @@
namespace android {
LiveSession::LiveSession(
- const sp<AMessage> ¬ify, uint32_t flags, bool uidValid, uid_t uid)
+ const sp<AMessage> ¬ify, uint32_t flags,
+ const sp<IMediaHTTPService> &httpService)
: mNotify(notify),
mFlags(flags),
- mUIDValid(uidValid),
- mUID(uid),
+ mHTTPService(httpService),
mInPreparationPhase(true),
- mHTTPDataSource(
- HTTPBase::Create(
- (mFlags & kFlagIncognito)
- ? HTTPBase::kFlagIncognito
- : 0)),
+ mHTTPDataSource(new MediaHTTP(mHTTPService->makeHTTPConnection())),
mPrevBandwidthIndex(-1),
mStreamMask(0),
+ mNewStreamMask(0),
+ mSwapMask(0),
mCheckBandwidthGeneration(0),
+ mSwitchGeneration(0),
mLastDequeuedTimeUs(0ll),
mRealTimeBaseUs(0ll),
mReconfigurationInProgress(false),
+ mSwitchInProgress(false),
mDisconnectReplyID(0) {
- if (mUIDValid) {
- mHTTPDataSource->setUID(mUID);
+
+ mStreams[kAudioIndex] = StreamItem("audio");
+ mStreams[kVideoIndex] = StreamItem("video");
+ mStreams[kSubtitleIndex] = StreamItem("subtitle");
+
+ for (size_t i = 0; i < kMaxStreams; ++i) {
+ mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
+ mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
}
-
- mPacketSources.add(
- STREAMTYPE_AUDIO, new AnotherPacketSource(NULL /* meta */));
-
- mPacketSources.add(
- STREAMTYPE_VIDEO, new AnotherPacketSource(NULL /* meta */));
-
- mPacketSources.add(
- STREAMTYPE_SUBTITLES, new AnotherPacketSource(NULL /* meta */));
}
LiveSession::~LiveSession() {
}
+sp<ABuffer> LiveSession::createFormatChangeBuffer(bool swap) {
+ ABuffer *discontinuity = new ABuffer(0);
+ discontinuity->meta()->setInt32("discontinuity", ATSParser::DISCONTINUITY_FORMATCHANGE);
+ discontinuity->meta()->setInt32("swapPacketSource", swap);
+ discontinuity->meta()->setInt32("switchGeneration", mSwitchGeneration);
+ discontinuity->meta()->setInt64("timeUs", -1);
+ return discontinuity;
+}
+
+void LiveSession::swapPacketSource(StreamType stream) {
+ sp<AnotherPacketSource> &aps = mPacketSources.editValueFor(stream);
+ sp<AnotherPacketSource> &aps2 = mPacketSources2.editValueFor(stream);
+ sp<AnotherPacketSource> tmp = aps;
+ aps = aps2;
+ aps2 = tmp;
+ aps2->clear();
+}
+
status_t LiveSession::dequeueAccessUnit(
StreamType stream, sp<ABuffer> *accessUnit) {
if (!(mStreamMask & stream)) {
- return UNKNOWN_ERROR;
+ // return -EWOULDBLOCK to avoid halting the decoder
+ // when switching between audio/video and audio only.
+ return -EWOULDBLOCK;
}
sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
@@ -122,6 +144,25 @@
streamStr,
type,
extra == NULL ? "NULL" : extra->debugString().c_str());
+
+ int32_t swap;
+ if (type == ATSParser::DISCONTINUITY_FORMATCHANGE
+ && (*accessUnit)->meta()->findInt32("swapPacketSource", &swap)
+ && swap) {
+
+ int32_t switchGeneration;
+ CHECK((*accessUnit)->meta()->findInt32("switchGeneration", &switchGeneration));
+ {
+ Mutex::Autolock lock(mSwapMutex);
+ if (switchGeneration == mSwitchGeneration) {
+ swapPacketSource(stream);
+ sp<AMessage> msg = new AMessage(kWhatSwapped, id());
+ msg->setInt32("stream", stream);
+ msg->setInt32("switchGeneration", switchGeneration);
+ msg->post();
+ }
+ }
+ }
} else if (err == OK) {
if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) {
int64_t timeUs;
@@ -143,6 +184,7 @@
}
status_t LiveSession::getStreamFormat(StreamType stream, sp<AMessage> *format) {
+ // No swapPacketSource race condition; called from the same thread as dequeueAccessUnit.
if (!(mStreamMask & stream)) {
return UNKNOWN_ERROR;
}
@@ -239,7 +281,12 @@
if (what == PlaylistFetcher::kWhatStopped) {
AString uri;
CHECK(msg->findString("uri", &uri));
- mFetcherInfos.removeItem(uri);
+ if (mFetcherInfos.removeItem(uri) < 0) {
+ // ignore duplicated kWhatStopped messages.
+ break;
+ }
+
+ tryToFinishBandwidthSwitch();
}
if (mContinuation != NULL) {
@@ -275,6 +322,8 @@
postPrepared(err);
}
+ cancelBandwidthSwitch();
+
mPacketSources.valueFor(STREAMTYPE_AUDIO)->signalEOS(err);
mPacketSources.valueFor(STREAMTYPE_VIDEO)->signalEOS(err);
@@ -313,6 +362,27 @@
break;
}
+ case PlaylistFetcher::kWhatStartedAt:
+ {
+ int32_t switchGeneration;
+ CHECK(msg->findInt32("switchGeneration", &switchGeneration));
+
+ if (switchGeneration != mSwitchGeneration) {
+ break;
+ }
+
+ // Resume fetcher for the original variant; the resumed fetcher should
+ // continue until the timestamps found in msg, which is stored by the
+ // new fetcher to indicate where the new variant has started buffering.
+ for (size_t i = 0; i < mFetcherInfos.size(); i++) {
+ const FetcherInfo info = mFetcherInfos.valueAt(i);
+ if (info.mToBeRemoved) {
+ info.mFetcher->resumeUntilAsync(msg);
+ }
+ }
+ break;
+ }
+
default:
TRESPASS();
}
@@ -357,6 +427,11 @@
break;
}
+ case kWhatSwapped:
+ {
+ onSwapped(msg);
+ break;
+ }
default:
TRESPASS();
break;
@@ -374,6 +449,12 @@
return 1;
}
+// static
+LiveSession::StreamType LiveSession::indexToType(int idx) {
+ CHECK(idx >= 0 && idx < kMaxStreams);
+ return (StreamType)(1 << idx);
+}
+
void LiveSession::onConnect(const sp<AMessage> &msg) {
AString url;
CHECK(msg->findString("url", &url));
@@ -461,6 +542,10 @@
// during disconnection either.
cancelCheckBandwidthEvent();
+ // Protect mPacketSources from a swapPacketSource race condition through disconnect.
+ // (finishDisconnect, onFinishDisconnect2)
+ cancelBandwidthSwitch();
+
for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
mFetcherInfos.valueAt(i).mFetcher->stopAsync();
}
@@ -500,11 +585,13 @@
sp<AMessage> notify = new AMessage(kWhatFetcherNotify, id());
notify->setString("uri", uri);
+ notify->setInt32("switchGeneration", mSwitchGeneration);
FetcherInfo info;
info.mFetcher = new PlaylistFetcher(notify, this, uri);
info.mDurationUs = -1ll;
info.mIsPrepared = false;
+ info.mToBeRemoved = false;
looper()->registerHandler(info.mFetcher);
mFetcherInfos.add(uri, info);
@@ -512,53 +599,81 @@
return info.mFetcher;
}
+/*
+ * Illustration of parameters:
+ *
+ * 0 `range_offset`
+ * +------------+-------------------------------------------------------+--+--+
+ * | | | next block to fetch | | |
+ * | | `source` handle => `out` buffer | | | |
+ * | `url` file |<--------- buffer size --------->|<--- `block_size` -->| | |
+ * | |<----------- `range_length` / buffer capacity ----------->| |
+ * |<------------------------------ file_size ------------------------------->|
+ *
+ * Special parameter values:
+ * - range_length == -1 means entire file
+ * - block_size == 0 means entire range
+ *
+ */
status_t LiveSession::fetchFile(
const char *url, sp<ABuffer> *out,
- int64_t range_offset, int64_t range_length) {
- *out = NULL;
-
- sp<DataSource> source;
-
- if (!strncasecmp(url, "file://", 7)) {
- source = new FileSource(url + 7);
- } else if (strncasecmp(url, "http://", 7)
- && strncasecmp(url, "https://", 8)) {
- return ERROR_UNSUPPORTED;
- } else {
- KeyedVector<String8, String8> headers = mExtraHeaders;
- if (range_offset > 0 || range_length >= 0) {
- headers.add(
- String8("Range"),
- String8(
- StringPrintf(
- "bytes=%lld-%s",
- range_offset,
- range_length < 0
- ? "" : StringPrintf("%lld", range_offset + range_length - 1).c_str()).c_str()));
- }
- status_t err = mHTTPDataSource->connect(url, &headers);
-
- if (err != OK) {
- return err;
- }
-
- source = mHTTPDataSource;
+ int64_t range_offset, int64_t range_length,
+ uint32_t block_size, /* download block size */
+ sp<DataSource> *source, /* to return and reuse source */
+ String8 *actualUrl) {
+ off64_t size;
+ sp<DataSource> temp_source;
+ if (source == NULL) {
+ source = &temp_source;
}
- off64_t size;
- status_t err = source->getSize(&size);
+ if (*source == NULL) {
+ if (!strncasecmp(url, "file://", 7)) {
+ *source = new FileSource(url + 7);
+ } else if (strncasecmp(url, "http://", 7)
+ && strncasecmp(url, "https://", 8)) {
+ return ERROR_UNSUPPORTED;
+ } else {
+ KeyedVector<String8, String8> headers = mExtraHeaders;
+ if (range_offset > 0 || range_length >= 0) {
+ headers.add(
+ String8("Range"),
+ String8(
+ StringPrintf(
+ "bytes=%lld-%s",
+ range_offset,
+ range_length < 0
+ ? "" : StringPrintf("%lld",
+ range_offset + range_length - 1).c_str()).c_str()));
+ }
+ status_t err = mHTTPDataSource->connect(url, &headers);
- if (err != OK) {
+ if (err != OK) {
+ return err;
+ }
+
+ *source = mHTTPDataSource;
+ }
+ }
+
+ status_t getSizeErr = (*source)->getSize(&size);
+ if (getSizeErr != OK) {
size = 65536;
}
- sp<ABuffer> buffer = new ABuffer(size);
- buffer->setRange(0, 0);
+ sp<ABuffer> buffer = *out != NULL ? *out : new ABuffer(size);
+ if (*out == NULL) {
+ buffer->setRange(0, 0);
+ }
+ // adjust range_length if only reading partial block
+ if (block_size > 0 && (range_length == -1 || buffer->size() + block_size < range_length)) {
+ range_length = buffer->size() + block_size;
+ }
for (;;) {
+ // Only resize when we don't know the size.
size_t bufferRemaining = buffer->capacity() - buffer->size();
-
- if (bufferRemaining == 0) {
+ if (bufferRemaining == 0 && getSizeErr != OK) {
bufferRemaining = 32768;
ALOGV("increasing download buffer to %d bytes",
@@ -583,7 +698,9 @@
}
}
- ssize_t n = source->readAt(
+ // The DataSource is responsible for informing us of error (n < 0) or eof (n == 0)
+ // to help us break out of the loop.
+ ssize_t n = (*source)->readAt(
buffer->size(), buffer->data() + buffer->size(),
maxBytesToRead);
@@ -599,6 +716,12 @@
}
*out = buffer;
+ if (actualUrl != NULL) {
+ *actualUrl = (*source)->getUri();
+ if (actualUrl->isEmpty()) {
+ *actualUrl = url;
+ }
+ }
return OK;
}
@@ -610,7 +733,8 @@
*unchanged = false;
sp<ABuffer> buffer;
- status_t err = fetchFile(url, &buffer);
+ String8 actualUrl;
+ status_t err = fetchFile(url, &buffer, 0, -1, 0, NULL, &actualUrl);
if (err != OK) {
return NULL;
@@ -632,9 +756,6 @@
// playlist unchanged
*unchanged = true;
- ALOGV("Playlist unchanged, refresh state is now %d",
- (int)mRefreshState);
-
return NULL;
}
@@ -644,7 +765,7 @@
#endif
sp<M3UParser> playlist =
- new M3UParser(url, buffer->data(), buffer->size());
+ new M3UParser(actualUrl.string(), buffer->data(), buffer->size());
if (playlist->initCheck() != OK) {
ALOGE("failed to parse .m3u8 playlist");
@@ -810,8 +931,25 @@
return err;
}
+bool LiveSession::canSwitchUp() {
+ // Allow upwards bandwidth switch when a stream has buffered at least 10 seconds.
+ status_t err = OK;
+ for (size_t i = 0; i < mPacketSources.size(); ++i) {
+ sp<AnotherPacketSource> source = mPacketSources.valueAt(i);
+ int64_t dur = source->getBufferedDurationUs(&err);
+ if (err == OK && dur > 10000000) {
+ return true;
+ }
+ }
+ return false;
+}
+
void LiveSession::changeConfiguration(
int64_t timeUs, size_t bandwidthIndex, bool pickTrack) {
+ // Protect mPacketSources from a swapPacketSource race condition through reconfiguration.
+ // (changeConfiguration, onChangeConfiguration2, onChangeConfiguration3).
+ cancelBandwidthSwitch();
+
CHECK(!mReconfigurationInProgress);
mReconfigurationInProgress = true;
@@ -827,21 +965,14 @@
CHECK_LT(bandwidthIndex, mBandwidthItems.size());
const BandwidthItem &item = mBandwidthItems.itemAt(bandwidthIndex);
- uint32_t streamMask = 0;
+ uint32_t streamMask = 0; // streams that should be fetched by the new fetcher
+ uint32_t resumeMask = 0; // streams that should be fetched by the original fetcher
- AString audioURI;
- if (mPlaylist->getAudioURI(item.mPlaylistIndex, &audioURI)) {
- streamMask |= STREAMTYPE_AUDIO;
- }
-
- AString videoURI;
- if (mPlaylist->getVideoURI(item.mPlaylistIndex, &videoURI)) {
- streamMask |= STREAMTYPE_VIDEO;
- }
-
- AString subtitleURI;
- if (mPlaylist->getSubtitleURI(item.mPlaylistIndex, &subtitleURI)) {
- streamMask |= STREAMTYPE_SUBTITLES;
+ AString URIs[kMaxStreams];
+ for (size_t i = 0; i < kMaxStreams; ++i) {
+ if (mPlaylist->getTypeURI(item.mPlaylistIndex, mStreams[i].mType, &URIs[i])) {
+ streamMask |= indexToType(i);
+ }
}
// Step 1, stop and discard fetchers that are no longer needed.
@@ -853,10 +984,15 @@
// If we're seeking all current fetchers are discarded.
if (timeUs < 0ll) {
- if (((streamMask & STREAMTYPE_AUDIO) && uri == audioURI)
- || ((streamMask & STREAMTYPE_VIDEO) && uri == videoURI)
- || ((streamMask & STREAMTYPE_SUBTITLES) && uri == subtitleURI)) {
- discardFetcher = false;
+ // delay fetcher removal
+ discardFetcher = false;
+
+ for (size_t j = 0; j < kMaxStreams; ++j) {
+ StreamType type = indexToType(j);
+ if ((streamMask & type) && uri == URIs[j]) {
+ resumeMask |= type;
+ streamMask &= ~type;
+ }
}
}
@@ -867,17 +1003,20 @@
}
}
- sp<AMessage> msg = new AMessage(kWhatChangeConfiguration2, id());
+ sp<AMessage> msg;
+ if (timeUs < 0ll) {
+ // skip onChangeConfiguration2 (decoder destruction) if switching.
+ msg = new AMessage(kWhatChangeConfiguration3, id());
+ } else {
+ msg = new AMessage(kWhatChangeConfiguration2, id());
+ }
msg->setInt32("streamMask", streamMask);
+ msg->setInt32("resumeMask", resumeMask);
msg->setInt64("timeUs", timeUs);
- if (streamMask & STREAMTYPE_AUDIO) {
- msg->setString("audioURI", audioURI.c_str());
- }
- if (streamMask & STREAMTYPE_VIDEO) {
- msg->setString("videoURI", videoURI.c_str());
- }
- if (streamMask & STREAMTYPE_SUBTITLES) {
- msg->setString("subtitleURI", subtitleURI.c_str());
+ for (size_t i = 0; i < kMaxStreams; ++i) {
+ if (streamMask & indexToType(i)) {
+ msg->setString(mStreams[i].uriKey().c_str(), URIs[i].c_str());
+ }
}
// Every time a fetcher acknowledges the stopAsync or pauseAsync request
@@ -908,18 +1047,13 @@
uint32_t streamMask;
CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask));
- AString audioURI, videoURI, subtitleURI;
- if (streamMask & STREAMTYPE_AUDIO) {
- CHECK(msg->findString("audioURI", &audioURI));
- ALOGV("audioURI = '%s'", audioURI.c_str());
- }
- if (streamMask & STREAMTYPE_VIDEO) {
- CHECK(msg->findString("videoURI", &videoURI));
- ALOGV("videoURI = '%s'", videoURI.c_str());
- }
- if (streamMask & STREAMTYPE_SUBTITLES) {
- CHECK(msg->findString("subtitleURI", &subtitleURI));
- ALOGV("subtitleURI = '%s'", subtitleURI.c_str());
+ AString URIs[kMaxStreams];
+ for (size_t i = 0; i < kMaxStreams; ++i) {
+ if (streamMask & indexToType(i)) {
+ const AString &uriKey = mStreams[i].uriKey();
+ CHECK(msg->findString(uriKey.c_str(), &URIs[i]));
+ ALOGV("%s = '%s'", uriKey.c_str(), URIs[i].c_str());
+ }
}
// Determine which decoders to shutdown on the player side,
@@ -929,15 +1063,12 @@
// 2) its streamtype was already active and still is but the URI
// has changed.
uint32_t changedMask = 0;
- if (((mStreamMask & streamMask & STREAMTYPE_AUDIO)
- && !(audioURI == mAudioURI))
- || (mStreamMask & ~streamMask & STREAMTYPE_AUDIO)) {
- changedMask |= STREAMTYPE_AUDIO;
- }
- if (((mStreamMask & streamMask & STREAMTYPE_VIDEO)
- && !(videoURI == mVideoURI))
- || (mStreamMask & ~streamMask & STREAMTYPE_VIDEO)) {
- changedMask |= STREAMTYPE_VIDEO;
+ for (size_t i = 0; i < kMaxStreams && i != kSubtitleIndex; ++i) {
+ if (((mStreamMask & streamMask & indexToType(i))
+ && !(URIs[i] == mStreams[i].mUri))
+ || (mStreamMask & ~streamMask & indexToType(i))) {
+ changedMask |= indexToType(i);
+ }
}
if (changedMask == 0) {
@@ -963,68 +1094,54 @@
}
void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) {
+ mContinuation.clear();
// All remaining fetchers are still suspended, the player has shutdown
// any decoders that needed it.
- uint32_t streamMask;
+ uint32_t streamMask, resumeMask;
CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask));
+ CHECK(msg->findInt32("resumeMask", (int32_t *)&resumeMask));
- AString audioURI, videoURI, subtitleURI;
- if (streamMask & STREAMTYPE_AUDIO) {
- CHECK(msg->findString("audioURI", &audioURI));
- }
- if (streamMask & STREAMTYPE_VIDEO) {
- CHECK(msg->findString("videoURI", &videoURI));
- }
- if (streamMask & STREAMTYPE_SUBTITLES) {
- CHECK(msg->findString("subtitleURI", &subtitleURI));
+ for (size_t i = 0; i < kMaxStreams; ++i) {
+ if (streamMask & indexToType(i)) {
+ CHECK(msg->findString(mStreams[i].uriKey().c_str(), &mStreams[i].mUri));
+ }
}
int64_t timeUs;
+ bool switching = false;
CHECK(msg->findInt64("timeUs", &timeUs));
if (timeUs < 0ll) {
timeUs = mLastDequeuedTimeUs;
+ switching = true;
}
mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
- mStreamMask = streamMask;
- mAudioURI = audioURI;
- mVideoURI = videoURI;
- mSubtitleURI = subtitleURI;
+ mNewStreamMask = streamMask;
- // Resume all existing fetchers and assign them packet sources.
+ // Of all existing fetchers:
+ // * Resume fetchers that are still needed and assign them original packet sources.
+ // * Mark otherwise unneeded fetchers for removal.
+ ALOGV("resuming fetchers for mask 0x%08x", resumeMask);
for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
const AString &uri = mFetcherInfos.keyAt(i);
- uint32_t resumeMask = 0;
-
- sp<AnotherPacketSource> audioSource;
- if ((streamMask & STREAMTYPE_AUDIO) && uri == audioURI) {
- audioSource = mPacketSources.valueFor(STREAMTYPE_AUDIO);
- resumeMask |= STREAMTYPE_AUDIO;
+ sp<AnotherPacketSource> sources[kMaxStreams];
+ for (size_t j = 0; j < kMaxStreams; ++j) {
+ if ((resumeMask & indexToType(j)) && uri == mStreams[j].mUri) {
+ sources[j] = mPacketSources.valueFor(indexToType(j));
+ }
}
- sp<AnotherPacketSource> videoSource;
- if ((streamMask & STREAMTYPE_VIDEO) && uri == videoURI) {
- videoSource = mPacketSources.valueFor(STREAMTYPE_VIDEO);
- resumeMask |= STREAMTYPE_VIDEO;
+ FetcherInfo &info = mFetcherInfos.editValueAt(i);
+ if (sources[kAudioIndex] != NULL || sources[kVideoIndex] != NULL
+ || sources[kSubtitleIndex] != NULL) {
+ info.mFetcher->startAsync(
+ sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex]);
+ } else {
+ info.mToBeRemoved = true;
}
-
- sp<AnotherPacketSource> subtitleSource;
- if ((streamMask & STREAMTYPE_SUBTITLES) && uri == subtitleURI) {
- subtitleSource = mPacketSources.valueFor(STREAMTYPE_SUBTITLES);
- resumeMask |= STREAMTYPE_SUBTITLES;
- }
-
- CHECK_NE(resumeMask, 0u);
-
- ALOGV("resuming fetchers for mask 0x%08x", resumeMask);
-
- streamMask &= ~resumeMask;
-
- mFetcherInfos.valueAt(i).mFetcher->startAsync(
- audioSource, videoSource, subtitleSource);
}
// streamMask now only contains the types that need a new fetcher created.
@@ -1033,52 +1150,65 @@
ALOGV("creating new fetchers for mask 0x%08x", streamMask);
}
- while (streamMask != 0) {
- StreamType streamType = (StreamType)(streamMask & ~(streamMask - 1));
+ // Find out when the original fetchers have buffered up to and start the new fetchers
+ // at a later timestamp.
+ for (size_t i = 0; i < kMaxStreams; i++) {
+ if (!(indexToType(i) & streamMask)) {
+ continue;
+ }
AString uri;
- switch (streamType) {
- case STREAMTYPE_AUDIO:
- uri = audioURI;
- break;
- case STREAMTYPE_VIDEO:
- uri = videoURI;
- break;
- case STREAMTYPE_SUBTITLES:
- uri = subtitleURI;
- break;
- default:
- TRESPASS();
- }
+ uri = mStreams[i].mUri;
sp<PlaylistFetcher> fetcher = addFetcher(uri.c_str());
CHECK(fetcher != NULL);
- sp<AnotherPacketSource> audioSource;
- if ((streamMask & STREAMTYPE_AUDIO) && uri == audioURI) {
- audioSource = mPacketSources.valueFor(STREAMTYPE_AUDIO);
- audioSource->clear();
+ int32_t latestSeq = -1;
+ int64_t latestTimeUs = 0ll;
+ sp<AnotherPacketSource> sources[kMaxStreams];
- streamMask &= ~STREAMTYPE_AUDIO;
+ // TRICKY: looping from i as earlier streams are already removed from streamMask
+ for (size_t j = i; j < kMaxStreams; ++j) {
+ if ((streamMask & indexToType(j)) && uri == mStreams[j].mUri) {
+ sources[j] = mPacketSources.valueFor(indexToType(j));
+
+ if (!switching) {
+ sources[j]->clear();
+ } else {
+ int32_t type, seq;
+ int64_t srcTimeUs;
+ sp<AMessage> meta = sources[j]->getLatestMeta();
+
+ if (meta != NULL && !meta->findInt32("discontinuity", &type)) {
+ CHECK(meta->findInt32("seq", &seq));
+ if (seq > latestSeq) {
+ latestSeq = seq;
+ }
+ CHECK(meta->findInt64("timeUs", &srcTimeUs));
+ if (srcTimeUs > latestTimeUs) {
+ latestTimeUs = srcTimeUs;
+ }
+ }
+
+ sources[j] = mPacketSources2.valueFor(indexToType(j));
+ sources[j]->clear();
+ uint32_t extraStreams = mNewStreamMask & (~mStreamMask);
+ if (extraStreams & indexToType(j)) {
+ sources[j]->queueAccessUnit(createFormatChangeBuffer(/* swap = */ false));
+ }
+ }
+
+ streamMask &= ~indexToType(j);
+ }
}
- sp<AnotherPacketSource> videoSource;
- if ((streamMask & STREAMTYPE_VIDEO) && uri == videoURI) {
- videoSource = mPacketSources.valueFor(STREAMTYPE_VIDEO);
- videoSource->clear();
-
- streamMask &= ~STREAMTYPE_VIDEO;
- }
-
- sp<AnotherPacketSource> subtitleSource;
- if ((streamMask & STREAMTYPE_SUBTITLES) && uri == subtitleURI) {
- subtitleSource = mPacketSources.valueFor(STREAMTYPE_SUBTITLES);
- subtitleSource->clear();
-
- streamMask &= ~STREAMTYPE_SUBTITLES;
- }
-
- fetcher->startAsync(audioSource, videoSource, subtitleSource, timeUs);
+ fetcher->startAsync(
+ sources[kAudioIndex],
+ sources[kVideoIndex],
+ sources[kSubtitleIndex],
+ timeUs,
+ latestTimeUs /* min start time(us) */,
+ latestSeq >= 0 ? latestSeq + 1 : -1 /* starting sequence number hint */ );
}
// All fetchers have now been started, the configuration change
@@ -1087,14 +1217,61 @@
scheduleCheckBandwidthEvent();
ALOGV("XXX configuration change completed.");
-
mReconfigurationInProgress = false;
+ if (switching) {
+ mSwitchInProgress = true;
+ } else {
+ mStreamMask = mNewStreamMask;
+ }
if (mDisconnectReplyID != 0) {
finishDisconnect();
}
}
+void LiveSession::onSwapped(const sp<AMessage> &msg) {
+ int32_t switchGeneration;
+ CHECK(msg->findInt32("switchGeneration", &switchGeneration));
+ if (switchGeneration != mSwitchGeneration) {
+ return;
+ }
+
+ int32_t stream;
+ CHECK(msg->findInt32("stream", &stream));
+ mSwapMask |= stream;
+ if (mSwapMask != mStreamMask) {
+ return;
+ }
+
+ // Check if new variant contains extra streams.
+ uint32_t extraStreams = mNewStreamMask & (~mStreamMask);
+ while (extraStreams) {
+ StreamType extraStream = (StreamType) (extraStreams & ~(extraStreams - 1));
+ swapPacketSource(extraStream);
+ extraStreams &= ~extraStream;
+ }
+
+ tryToFinishBandwidthSwitch();
+}
+
+// Mark switch done when:
+// 1. all old buffers are swapped out, AND
+// 2. all old fetchers are removed.
+void LiveSession::tryToFinishBandwidthSwitch() {
+ bool needToRemoveFetchers = false;
+ for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
+ if (mFetcherInfos.valueAt(i).mToBeRemoved) {
+ needToRemoveFetchers = true;
+ break;
+ }
+ }
+ if (!needToRemoveFetchers && mSwapMask == mStreamMask) {
+ mStreamMask = mNewStreamMask;
+ mSwitchInProgress = false;
+ mSwapMask = 0;
+ }
+}
+
void LiveSession::scheduleCheckBandwidthEvent() {
sp<AMessage> msg = new AMessage(kWhatCheckBandwidth, id());
msg->setInt32("generation", mCheckBandwidthGeneration);
@@ -1105,16 +1282,37 @@
++mCheckBandwidthGeneration;
}
-void LiveSession::onCheckBandwidth() {
- if (mReconfigurationInProgress) {
- scheduleCheckBandwidthEvent();
- return;
+void LiveSession::cancelBandwidthSwitch() {
+ Mutex::Autolock lock(mSwapMutex);
+ mSwitchGeneration++;
+ mSwitchInProgress = false;
+ mSwapMask = 0;
+}
+
+bool LiveSession::canSwitchBandwidthTo(size_t bandwidthIndex) {
+ if (mReconfigurationInProgress || mSwitchInProgress) {
+ return false;
}
+ if (mPrevBandwidthIndex < 0) {
+ return true;
+ }
+
+ if (bandwidthIndex == (size_t)mPrevBandwidthIndex) {
+ return false;
+ } else if (bandwidthIndex > (size_t)mPrevBandwidthIndex) {
+ return canSwitchUp();
+ } else {
+ return true;
+ }
+}
+
+void LiveSession::onCheckBandwidth() {
size_t bandwidthIndex = getBandwidthIndex();
- if (mPrevBandwidthIndex < 0
- || bandwidthIndex != (size_t)mPrevBandwidthIndex) {
+ if (canSwitchBandwidthTo(bandwidthIndex)) {
changeConfiguration(-1ll /* timeUs */, bandwidthIndex);
+ } else {
+ scheduleCheckBandwidthEvent();
}
// Handling the kWhatCheckBandwidth even here does _not_ automatically
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 99b480a8..91bbcea 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -28,6 +28,7 @@
struct AnotherPacketSource;
struct DataSource;
struct HTTPBase;
+struct IMediaHTTPService;
struct LiveDataSource;
struct M3UParser;
struct PlaylistFetcher;
@@ -40,12 +41,20 @@
};
LiveSession(
const sp<AMessage> ¬ify,
- uint32_t flags = 0, bool uidValid = false, uid_t uid = 0);
+ uint32_t flags,
+ const sp<IMediaHTTPService> &httpService);
+
+ enum StreamIndex {
+ kAudioIndex = 0,
+ kVideoIndex = 1,
+ kSubtitleIndex = 2,
+ kMaxStreams = 3,
+ };
enum StreamType {
- STREAMTYPE_AUDIO = 1,
- STREAMTYPE_VIDEO = 2,
- STREAMTYPE_SUBTITLES = 4,
+ STREAMTYPE_AUDIO = 1 << kAudioIndex,
+ STREAMTYPE_VIDEO = 1 << kVideoIndex,
+ STREAMTYPE_SUBTITLES = 1 << kSubtitleIndex,
};
status_t dequeueAccessUnit(StreamType stream, sp<ABuffer> *accessUnit);
@@ -74,6 +83,11 @@
kWhatPreparationFailed,
};
+ // create a format-change discontinuity
+ //
+ // swap:
+ // whether is format-change discontinuity should trigger a buffer swap
+ sp<ABuffer> createFormatChangeBuffer(bool swap = true);
protected:
virtual ~LiveSession();
@@ -92,6 +106,7 @@
kWhatChangeConfiguration2 = 'chC2',
kWhatChangeConfiguration3 = 'chC3',
kWhatFinishDisconnect2 = 'fin2',
+ kWhatSwapped = 'swap',
};
struct BandwidthItem {
@@ -103,12 +118,25 @@
sp<PlaylistFetcher> mFetcher;
int64_t mDurationUs;
bool mIsPrepared;
+ bool mToBeRemoved;
};
+ struct StreamItem {
+ const char *mType;
+ AString mUri;
+ StreamItem() : mType("") {}
+ StreamItem(const char *type) : mType(type) {}
+ AString uriKey() {
+ AString key(mType);
+ key.append("URI");
+ return key;
+ }
+ };
+ StreamItem mStreams[kMaxStreams];
+
sp<AMessage> mNotify;
uint32_t mFlags;
- bool mUIDValid;
- uid_t mUID;
+ sp<IMediaHTTPService> mHTTPService;
bool mInPreparationPhase;
@@ -123,12 +151,28 @@
sp<M3UParser> mPlaylist;
KeyedVector<AString, FetcherInfo> mFetcherInfos;
- AString mAudioURI, mVideoURI, mSubtitleURI;
uint32_t mStreamMask;
+ // Masks used during reconfiguration:
+ // mNewStreamMask: streams in the variant playlist we're switching to;
+ // we don't want to immediately overwrite the original value.
+ uint32_t mNewStreamMask;
+
+ // mSwapMask: streams that have started to playback content in the new variant playlist;
+ // we use this to track reconfiguration progress.
+ uint32_t mSwapMask;
+
KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources;
+ // A second set of packet sources that buffer content for the variant we're switching to.
+ KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources2;
+
+ // A mutex used to serialize two sets of events:
+ // * the swapping of packet sources in dequeueAccessUnit on the player thread, AND
+ // * a forced bandwidth switch termination in cancelSwitch on the live looper.
+ Mutex mSwapMutex;
int32_t mCheckBandwidthGeneration;
+ int32_t mSwitchGeneration;
size_t mContinuationCounter;
sp<AMessage> mContinuation;
@@ -137,6 +181,7 @@
int64_t mRealTimeBaseUs;
bool mReconfigurationInProgress;
+ bool mSwitchInProgress;
uint32_t mDisconnectReplyID;
sp<PlaylistFetcher> addFetcher(const char *uri);
@@ -145,9 +190,26 @@
status_t onSeek(const sp<AMessage> &msg);
void onFinishDisconnect2();
+ // If given a non-zero block_size (default 0), it is used to cap the number of
+ // bytes read in from the DataSource. If given a non-NULL buffer, new content
+ // is read into the end.
+ //
+ // The DataSource we read from is responsible for signaling error or EOF to help us
+ // break out of the read loop. The DataSource can be returned to the caller, so
+ // that the caller can reuse it for subsequent fetches (within the initially
+ // requested range).
+ //
+ // For reused HTTP sources, the caller must download a file sequentially without
+ // any overlaps or gaps to prevent reconnection.
status_t fetchFile(
const char *url, sp<ABuffer> *out,
- int64_t range_offset = 0, int64_t range_length = -1);
+ /* request/open a file starting at range_offset for range_length bytes */
+ int64_t range_offset = 0, int64_t range_length = -1,
+ /* download block size */
+ uint32_t block_size = 0,
+ /* reuse DataSource if doing partial fetch */
+ sp<DataSource> *source = NULL,
+ String8 *actualUrl = NULL);
sp<M3UParser> fetchPlaylist(
const char *url, uint8_t *curPlaylistHash, bool *unchanged);
@@ -155,22 +217,34 @@
size_t getBandwidthIndex();
static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
+ static StreamType indexToType(int idx);
void changeConfiguration(
int64_t timeUs, size_t bandwidthIndex, bool pickTrack = false);
void onChangeConfiguration(const sp<AMessage> &msg);
void onChangeConfiguration2(const sp<AMessage> &msg);
void onChangeConfiguration3(const sp<AMessage> &msg);
+ void onSwapped(const sp<AMessage> &msg);
+ void tryToFinishBandwidthSwitch();
void scheduleCheckBandwidthEvent();
void cancelCheckBandwidthEvent();
+ // cancelBandwidthSwitch is atomic wrt swapPacketSource; call it to prevent packet sources
+ // from being swapped out on stale discontinuities while manipulating
+ // mPacketSources/mPacketSources2.
+ void cancelBandwidthSwitch();
+
+ bool canSwitchBandwidthTo(size_t bandwidthIndex);
void onCheckBandwidth();
void finishDisconnect();
void postPrepared(status_t err);
+ void swapPacketSource(StreamType stream);
+ bool canSwitchUp();
+
DISALLOW_EVIL_CONSTRUCTORS(LiveSession);
};
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 243888c..587a6d5 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -24,6 +24,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
#include <media/mediaplayer.h>
namespace android {
@@ -125,7 +126,7 @@
mSelectedIndex = strtoul(value, &end, 10);
CHECK(end > value && *end == '\0');
- if (mSelectedIndex >= mMediaItems.size()) {
+ if (mSelectedIndex >= (ssize_t)mMediaItems.size()) {
mSelectedIndex = mMediaItems.size() - 1;
}
} else {
@@ -165,14 +166,14 @@
ALOGE("track %d does not exist", index);
return INVALID_OPERATION;
}
- if (mSelectedIndex == index) {
+ if (mSelectedIndex == (ssize_t)index) {
ALOGE("track %d already selected", index);
return BAD_VALUE;
}
ALOGV("selected track %d", index);
mSelectedIndex = index;
} else {
- if (mSelectedIndex != index) {
+ if (mSelectedIndex != (ssize_t)index) {
ALOGE("track %d is not selected", index);
return BAD_VALUE;
}
@@ -352,9 +353,27 @@
if (!meta->findString(key, &groupID)) {
*uri = mItems.itemAt(index).mURI;
- // Assume media without any more specific attribute contains
- // audio and video, but no subtitles.
- return !strcmp("audio", key) || !strcmp("video", key);
+ AString codecs;
+ if (!meta->findString("codecs", &codecs)) {
+ // Assume media without any more specific attribute contains
+ // audio and video, but no subtitles.
+ return !strcmp("audio", key) || !strcmp("video", key);
+ } else {
+ // Split the comma separated list of codecs.
+ size_t offset = 0;
+ ssize_t commaPos = -1;
+ codecs.append(',');
+ while ((commaPos = codecs.find(",", offset)) >= 0) {
+ AString codec(codecs, offset, commaPos - offset);
+ // return true only if a codec of type `key` ("audio"/"video")
+ // is found.
+ if (codecIsType(codec, key)) {
+ return true;
+ }
+ offset = commaPos + 1;
+ }
+ return false;
+ }
}
sp<MediaGroup> group = mMediaGroups.valueFor(groupID);
@@ -369,18 +388,6 @@
return true;
}
-bool M3UParser::getAudioURI(size_t index, AString *uri) const {
- return getTypeURI(index, "audio", uri);
-}
-
-bool M3UParser::getVideoURI(size_t index, AString *uri) const {
- return getTypeURI(index, "video", uri);
-}
-
-bool M3UParser::getSubtitleURI(size_t index, AString *uri) const {
- return getTypeURI(index, "subtitles", uri);
-}
-
static bool MakeURL(const char *baseURL, const char *url, AString *out) {
out->clear();
@@ -416,22 +423,32 @@
} else {
// URL is a relative path
- size_t n = strlen(baseURL);
- if (baseURL[n - 1] == '/') {
- out->setTo(baseURL);
- out->append(url);
+ // Check for a possible query string
+ const char *qsPos = strchr(baseURL, '?');
+ size_t end;
+ if (qsPos != NULL) {
+ end = qsPos - baseURL;
} else {
- const char *slashPos = strrchr(baseURL, '/');
-
- if (slashPos > &baseURL[6]) {
- out->setTo(baseURL, slashPos - baseURL);
- } else {
- out->setTo(baseURL);
- }
-
- out->append("/");
- out->append(url);
+ end = strlen(baseURL);
}
+ // Check for the last slash before a potential query string
+ for (ssize_t pos = end - 1; pos >= 0; pos--) {
+ if (baseURL[pos] == '/') {
+ end = pos;
+ break;
+ }
+ }
+
+ // Check whether the found slash actually is part of the path
+ // and not part of the "http://".
+ if (end > 6) {
+ out->setTo(baseURL, end);
+ } else {
+ out->setTo(baseURL);
+ }
+
+ out->append("/");
+ out->append(url);
}
ALOGV("base:'%s', url:'%s' => '%s'", baseURL, url, out->c_str());
@@ -608,7 +625,7 @@
if (meta->get() == NULL) {
*meta = new AMessage;
}
- (*meta)->setInt64(key, (int64_t)x * 1E6);
+ (*meta)->setInt64(key, (int64_t)(x * 1E6));
return OK;
}
@@ -684,12 +701,22 @@
*meta = new AMessage;
}
(*meta)->setInt32("bandwidth", x);
+ } else if (!strcasecmp("codecs", key.c_str())) {
+ if (!isQuotedString(val)) {
+ ALOGE("Expected quoted string for %s attribute, "
+ "got '%s' instead.",
+ key.c_str(), val.c_str());;
+
+ return ERROR_MALFORMED;
+ }
+
+ key.tolower();
+ const AString &codecs = unquoteString(val);
+ (*meta)->setString(key.c_str(), codecs.c_str());
} else if (!strcasecmp("audio", key.c_str())
|| !strcasecmp("video", key.c_str())
|| !strcasecmp("subtitles", key.c_str())) {
- if (val.size() < 2
- || val.c_str()[0] != '"'
- || val.c_str()[val.size() - 1] != '"') {
+ if (!isQuotedString(val)) {
ALOGE("Expected quoted string for %s attribute, "
"got '%s' instead.",
key.c_str(), val.c_str());
@@ -697,7 +724,7 @@
return ERROR_MALFORMED;
}
- AString groupID(val, 1, val.size() - 2);
+ const AString &groupID = unquoteString(val);
ssize_t groupIndex = mMediaGroups.indexOfKey(groupID);
if (groupIndex < 0) {
@@ -1086,4 +1113,121 @@
return OK;
}
+// static
+bool M3UParser::isQuotedString(const AString &str) {
+ if (str.size() < 2
+ || str.c_str()[0] != '"'
+ || str.c_str()[str.size() - 1] != '"') {
+ return false;
+ }
+ return true;
+}
+
+// static
+AString M3UParser::unquoteString(const AString &str) {
+ if (!isQuotedString(str)) {
+ return str;
+ }
+ return AString(str, 1, str.size() - 2);
+}
+
+// static
+bool M3UParser::codecIsType(const AString &codec, const char *type) {
+ if (codec.size() < 4) {
+ return false;
+ }
+ const char *c = codec.c_str();
+ switch (FOURCC(c[0], c[1], c[2], c[3])) {
+ // List extracted from http://www.mp4ra.org/codecs.html
+ case 'ac-3':
+ case 'alac':
+ case 'dra1':
+ case 'dtsc':
+ case 'dtse':
+ case 'dtsh':
+ case 'dtsl':
+ case 'ec-3':
+ case 'enca':
+ case 'g719':
+ case 'g726':
+ case 'm4ae':
+ case 'mlpa':
+ case 'mp4a':
+ case 'raw ':
+ case 'samr':
+ case 'sawb':
+ case 'sawp':
+ case 'sevc':
+ case 'sqcp':
+ case 'ssmv':
+ case 'twos':
+ case 'agsm':
+ case 'alaw':
+ case 'dvi ':
+ case 'fl32':
+ case 'fl64':
+ case 'ima4':
+ case 'in24':
+ case 'in32':
+ case 'lpcm':
+ case 'Qclp':
+ case 'QDM2':
+ case 'QDMC':
+ case 'ulaw':
+ case 'vdva':
+ return !strcmp("audio", type);
+
+ case 'avc1':
+ case 'avc2':
+ case 'avcp':
+ case 'drac':
+ case 'encv':
+ case 'mjp2':
+ case 'mp4v':
+ case 'mvc1':
+ case 'mvc2':
+ case 'resv':
+ case 's263':
+ case 'svc1':
+ case 'vc-1':
+ case 'CFHD':
+ case 'civd':
+ case 'DV10':
+ case 'dvh5':
+ case 'dvh6':
+ case 'dvhp':
+ case 'DVOO':
+ case 'DVOR':
+ case 'DVTV':
+ case 'DVVT':
+ case 'flic':
+ case 'gif ':
+ case 'h261':
+ case 'h263':
+ case 'HD10':
+ case 'jpeg':
+ case 'M105':
+ case 'mjpa':
+ case 'mjpb':
+ case 'png ':
+ case 'PNTG':
+ case 'rle ':
+ case 'rpza':
+ case 'Shr0':
+ case 'Shr1':
+ case 'Shr2':
+ case 'Shr3':
+ case 'Shr4':
+ case 'SVQ1':
+ case 'SVQ3':
+ case 'tga ':
+ case 'tiff':
+ case 'WRLE':
+ return !strcmp("video", type);
+
+ default:
+ return false;
+ }
+}
+
} // namespace android
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index 5248004..ccd6556 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -45,9 +45,7 @@
status_t getTrackInfo(Parcel* reply) const;
ssize_t getSelectedIndex() const;
- bool getAudioURI(size_t index, AString *uri) const;
- bool getVideoURI(size_t index, AString *uri) const;
- bool getSubtitleURI(size_t index, AString *uri) const;
+ bool getTypeURI(size_t index, const char *key, AString *uri) const;
protected:
virtual ~M3UParser();
@@ -95,11 +93,13 @@
status_t parseMedia(const AString &line);
- bool getTypeURI(size_t index, const char *key, AString *uri) const;
-
static status_t ParseInt32(const char *s, int32_t *x);
static status_t ParseDouble(const char *s, double *x);
+ static bool isQuotedString(const AString &str);
+ static AString unquoteString(const AString &str);
+ static bool codecIsType(const AString &codec, const char *type);
+
DISALLOW_EVIL_CONSTRUCTORS(M3UParser);
};
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 973b779..2649705 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -47,26 +47,34 @@
// static
const int64_t PlaylistFetcher::kMinBufferedDurationUs = 10000000ll;
+const int64_t PlaylistFetcher::kMaxMonitorDelayUs = 3000000ll;
+const int32_t PlaylistFetcher::kNumSkipFrames = 10;
PlaylistFetcher::PlaylistFetcher(
const sp<AMessage> ¬ify,
const sp<LiveSession> &session,
const char *uri)
: mNotify(notify),
+ mStartTimeUsNotify(notify->dup()),
mSession(session),
mURI(uri),
mStreamTypeMask(0),
mStartTimeUs(-1ll),
+ mMinStartTimeUs(0ll),
+ mStopParams(NULL),
mLastPlaylistFetchTimeUs(-1ll),
mSeqNumber(-1),
mNumRetries(0),
mStartup(true),
+ mPrepared(false),
mNextPTSTimeUs(-1ll),
mMonitorQueueGeneration(0),
mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY),
mFirstPTSValid(false),
mAbsoluteTimeAnchorUs(0ll) {
memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
+ mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
+ mStartTimeUsNotify->setInt32("streamMask", 0);
}
PlaylistFetcher::~PlaylistFetcher() {
@@ -103,10 +111,16 @@
return segmentStartUs;
}
-bool PlaylistFetcher::timeToRefreshPlaylist(int64_t nowUs) const {
- if (mPlaylist == NULL) {
+int64_t PlaylistFetcher::delayUsToRefreshPlaylist() const {
+ int64_t nowUs = ALooper::GetNowUs();
+
+ if (mPlaylist == NULL || mLastPlaylistFetchTimeUs < 0ll) {
CHECK_EQ((int)mRefreshState, (int)INITIAL_MINIMUM_RELOAD_DELAY);
- return true;
+ return 0ll;
+ }
+
+ if (mPlaylist->isComplete()) {
+ return (~0llu >> 1);
}
int32_t targetDurationSecs;
@@ -157,11 +171,13 @@
break;
}
- return mLastPlaylistFetchTimeUs + minPlaylistAgeUs <= nowUs;
+ int64_t delayUs = mLastPlaylistFetchTimeUs + minPlaylistAgeUs - nowUs;
+ return delayUs > 0ll ? delayUs : 0ll;
}
status_t PlaylistFetcher::decryptBuffer(
- size_t playlistIndex, const sp<ABuffer> &buffer) {
+ size_t playlistIndex, const sp<ABuffer> &buffer,
+ bool first) {
sp<AMessage> itemMeta;
bool found = false;
AString method;
@@ -179,6 +195,7 @@
if (!found) {
method = "NONE";
}
+ buffer->meta()->setString("cipher-method", method.c_str());
if (method == "NONE") {
return OK;
@@ -218,63 +235,89 @@
return UNKNOWN_ERROR;
}
- unsigned char aes_ivec[16];
+ size_t n = buffer->size();
+ if (!n) {
+ return OK;
+ }
+ CHECK(n % 16 == 0);
- AString iv;
- if (itemMeta->findString("cipher-iv", &iv)) {
- if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
- || iv.size() != 16 * 2 + 2) {
- ALOGE("malformed cipher IV '%s'.", iv.c_str());
- return ERROR_MALFORMED;
- }
+ if (first) {
+ // If decrypting the first block in a file, read the iv from the manifest
+ // or derive the iv from the file's sequence number.
- memset(aes_ivec, 0, sizeof(aes_ivec));
- for (size_t i = 0; i < 16; ++i) {
- char c1 = tolower(iv.c_str()[2 + 2 * i]);
- char c2 = tolower(iv.c_str()[3 + 2 * i]);
- if (!isxdigit(c1) || !isxdigit(c2)) {
+ AString iv;
+ if (itemMeta->findString("cipher-iv", &iv)) {
+ if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
+ || iv.size() != 16 * 2 + 2) {
ALOGE("malformed cipher IV '%s'.", iv.c_str());
return ERROR_MALFORMED;
}
- uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
- uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
- aes_ivec[i] = nibble1 << 4 | nibble2;
+ memset(mAESInitVec, 0, sizeof(mAESInitVec));
+ for (size_t i = 0; i < 16; ++i) {
+ char c1 = tolower(iv.c_str()[2 + 2 * i]);
+ char c2 = tolower(iv.c_str()[3 + 2 * i]);
+ if (!isxdigit(c1) || !isxdigit(c2)) {
+ ALOGE("malformed cipher IV '%s'.", iv.c_str());
+ return ERROR_MALFORMED;
+ }
+ uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
+ uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
+
+ mAESInitVec[i] = nibble1 << 4 | nibble2;
+ }
+ } else {
+ memset(mAESInitVec, 0, sizeof(mAESInitVec));
+ mAESInitVec[15] = mSeqNumber & 0xff;
+ mAESInitVec[14] = (mSeqNumber >> 8) & 0xff;
+ mAESInitVec[13] = (mSeqNumber >> 16) & 0xff;
+ mAESInitVec[12] = (mSeqNumber >> 24) & 0xff;
}
- } else {
- memset(aes_ivec, 0, sizeof(aes_ivec));
- aes_ivec[15] = mSeqNumber & 0xff;
- aes_ivec[14] = (mSeqNumber >> 8) & 0xff;
- aes_ivec[13] = (mSeqNumber >> 16) & 0xff;
- aes_ivec[12] = (mSeqNumber >> 24) & 0xff;
}
AES_cbc_encrypt(
buffer->data(), buffer->data(), buffer->size(),
- &aes_key, aes_ivec, AES_DECRYPT);
-
- // hexdump(buffer->data(), buffer->size());
-
- size_t n = buffer->size();
- CHECK_GT(n, 0u);
-
- size_t pad = buffer->data()[n - 1];
-
- CHECK_GT(pad, 0u);
- CHECK_LE(pad, 16u);
- CHECK_GE((size_t)n, pad);
- for (size_t i = 0; i < pad; ++i) {
- CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad);
- }
-
- n -= pad;
-
- buffer->setRange(buffer->offset(), n);
+ &aes_key, mAESInitVec, AES_DECRYPT);
return OK;
}
-void PlaylistFetcher::postMonitorQueue(int64_t delayUs) {
+status_t PlaylistFetcher::checkDecryptPadding(const sp<ABuffer> &buffer) {
+ status_t err;
+ AString method;
+ CHECK(buffer->meta()->findString("cipher-method", &method));
+ if (method == "NONE") {
+ return OK;
+ }
+
+ uint8_t padding = 0;
+ if (buffer->size() > 0) {
+ padding = buffer->data()[buffer->size() - 1];
+ }
+
+ if (padding > 16) {
+ return ERROR_MALFORMED;
+ }
+
+ for (size_t i = buffer->size() - padding; i < padding; i++) {
+ if (buffer->data()[i] != padding) {
+ return ERROR_MALFORMED;
+ }
+ }
+
+ buffer->setRange(buffer->offset(), buffer->size() - padding);
+ return OK;
+}
+
+void PlaylistFetcher::postMonitorQueue(int64_t delayUs, int64_t minDelayUs) {
+ int64_t maxDelayUs = delayUsToRefreshPlaylist();
+ if (maxDelayUs < minDelayUs) {
+ maxDelayUs = minDelayUs;
+ }
+ if (delayUs > maxDelayUs) {
+ ALOGV("Need to refresh playlist in %lld", maxDelayUs);
+ delayUs = maxDelayUs;
+ }
sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id());
msg->setInt32("generation", mMonitorQueueGeneration);
msg->post(delayUs);
@@ -288,7 +331,9 @@
const sp<AnotherPacketSource> &audioSource,
const sp<AnotherPacketSource> &videoSource,
const sp<AnotherPacketSource> &subtitleSource,
- int64_t startTimeUs) {
+ int64_t startTimeUs,
+ int64_t minStartTimeUs,
+ int32_t startSeqNumberHint) {
sp<AMessage> msg = new AMessage(kWhatStart, id());
uint32_t streamTypeMask = 0ul;
@@ -310,6 +355,8 @@
msg->setInt32("streamTypeMask", streamTypeMask);
msg->setInt64("startTimeUs", startTimeUs);
+ msg->setInt64("minStartTimeUs", minStartTimeUs);
+ msg->setInt32("startSeqNumberHint", startSeqNumberHint);
msg->post();
}
@@ -321,6 +368,12 @@
(new AMessage(kWhatStop, id()))->post();
}
+void PlaylistFetcher::resumeUntilAsync(const sp<AMessage> ¶ms) {
+ AMessage* msg = new AMessage(kWhatResumeUntil, id());
+ msg->setMessage("params", params);
+ msg->post();
+}
+
void PlaylistFetcher::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatStart:
@@ -355,6 +408,7 @@
}
case kWhatMonitorQueue:
+ case kWhatDownloadNext:
{
int32_t generation;
CHECK(msg->findInt32("generation", &generation));
@@ -364,7 +418,17 @@
break;
}
- onMonitorQueue();
+ if (msg->what() == kWhatMonitorQueue) {
+ onMonitorQueue();
+ } else {
+ onDownloadNext();
+ }
+ break;
+ }
+
+ case kWhatResumeUntil:
+ {
+ onResumeUntil(msg);
break;
}
@@ -380,7 +444,10 @@
CHECK(msg->findInt32("streamTypeMask", (int32_t *)&streamTypeMask));
int64_t startTimeUs;
+ int32_t startSeqNumberHint;
CHECK(msg->findInt64("startTimeUs", &startTimeUs));
+ CHECK(msg->findInt64("minStartTimeUs", (int64_t *) &mMinStartTimeUs));
+ CHECK(msg->findInt32("startSeqNumberHint", &startSeqNumberHint));
if (streamTypeMask & LiveSession::STREAMTYPE_AUDIO) {
void *ptr;
@@ -415,6 +482,11 @@
if (mStartTimeUs >= 0ll) {
mSeqNumber = -1;
mStartup = true;
+ mPrepared = false;
+ }
+
+ if (startSeqNumberHint >= 0) {
+ mSeqNumber = startSeqNumberHint;
}
postMonitorQueue();
@@ -424,22 +496,72 @@
void PlaylistFetcher::onPause() {
cancelMonitorQueue();
-
- mPacketSources.clear();
- mStreamTypeMask = 0;
}
void PlaylistFetcher::onStop() {
cancelMonitorQueue();
- for (size_t i = 0; i < mPacketSources.size(); ++i) {
- mPacketSources.valueAt(i)->clear();
- }
-
mPacketSources.clear();
mStreamTypeMask = 0;
}
+// Resume until we have reached the boundary timestamps listed in `msg`; when
+// the remaining time is too short (within a resume threshold) stop immediately
+// instead.
+status_t PlaylistFetcher::onResumeUntil(const sp<AMessage> &msg) {
+ sp<AMessage> params;
+ CHECK(msg->findMessage("params", ¶ms));
+
+ bool stop = false;
+ for (size_t i = 0; i < mPacketSources.size(); i++) {
+ sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
+
+ const char *stopKey;
+ int streamType = mPacketSources.keyAt(i);
+ switch (streamType) {
+ case LiveSession::STREAMTYPE_VIDEO:
+ stopKey = "timeUsVideo";
+ break;
+
+ case LiveSession::STREAMTYPE_AUDIO:
+ stopKey = "timeUsAudio";
+ break;
+
+ case LiveSession::STREAMTYPE_SUBTITLES:
+ stopKey = "timeUsSubtitle";
+ break;
+
+ default:
+ TRESPASS();
+ }
+
+ // Don't resume if we would stop within a resume threshold.
+ int64_t latestTimeUs = 0, stopTimeUs = 0;
+ sp<AMessage> latestMeta = packetSource->getLatestMeta();
+ if (latestMeta != NULL
+ && (latestMeta->findInt64("timeUs", &latestTimeUs)
+ && params->findInt64(stopKey, &stopTimeUs))) {
+ int64_t diffUs = stopTimeUs - latestTimeUs;
+ if (diffUs < resumeThreshold(latestMeta)) {
+ stop = true;
+ }
+ }
+ }
+
+ if (stop) {
+ for (size_t i = 0; i < mPacketSources.size(); i++) {
+ mPacketSources.valueAt(i)->queueAccessUnit(mSession->createFormatChangeBuffer());
+ }
+ stopAsync();
+ return OK;
+ }
+
+ mStopParams = params;
+ postMonitorQueue();
+
+ return OK;
+}
+
void PlaylistFetcher::notifyError(status_t err) {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatError);
@@ -456,41 +578,70 @@
void PlaylistFetcher::onMonitorQueue() {
bool downloadMore = false;
+ refreshPlaylist();
- status_t finalResult;
+ int32_t targetDurationSecs;
+ int64_t targetDurationUs = kMinBufferedDurationUs;
+ if (mPlaylist != NULL) {
+ CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
+ targetDurationUs = targetDurationSecs * 1000000ll;
+ }
+
+ // buffer at least 3 times the target duration, or up to 10 seconds
+ int64_t durationToBufferUs = targetDurationUs * 3;
+ if (durationToBufferUs > kMinBufferedDurationUs) {
+ durationToBufferUs = kMinBufferedDurationUs;
+ }
+
+ int64_t bufferedDurationUs = 0ll;
+ status_t finalResult = NOT_ENOUGH_DATA;
if (mStreamTypeMask == LiveSession::STREAMTYPE_SUBTITLES) {
sp<AnotherPacketSource> packetSource =
mPacketSources.valueFor(LiveSession::STREAMTYPE_SUBTITLES);
- int64_t bufferedDurationUs =
+ bufferedDurationUs =
packetSource->getBufferedDurationUs(&finalResult);
-
- downloadMore = (bufferedDurationUs < kMinBufferedDurationUs);
finalResult = OK;
} else {
- bool first = true;
- int64_t minBufferedDurationUs = 0ll;
-
+ // Use max stream duration to prevent us from waiting on a non-existent stream;
+ // when we cannot make out from the manifest what streams are included in a playlist
+ // we might assume extra streams.
for (size_t i = 0; i < mPacketSources.size(); ++i) {
if ((mStreamTypeMask & mPacketSources.keyAt(i)) == 0) {
continue;
}
- int64_t bufferedDurationUs =
+ int64_t bufferedStreamDurationUs =
mPacketSources.valueAt(i)->getBufferedDurationUs(&finalResult);
-
- if (first || bufferedDurationUs < minBufferedDurationUs) {
- minBufferedDurationUs = bufferedDurationUs;
- first = false;
+ ALOGV("buffered %lld for stream %d",
+ bufferedStreamDurationUs, mPacketSources.keyAt(i));
+ if (bufferedStreamDurationUs > bufferedDurationUs) {
+ bufferedDurationUs = bufferedStreamDurationUs;
}
}
+ }
+ downloadMore = (bufferedDurationUs < durationToBufferUs);
- downloadMore =
- !first && (minBufferedDurationUs < kMinBufferedDurationUs);
+ // signal start if buffered up at least the target size
+ if (!mPrepared && bufferedDurationUs > targetDurationUs && downloadMore) {
+ mPrepared = true;
+
+ ALOGV("prepared, buffered=%lld > %lld",
+ bufferedDurationUs, targetDurationUs);
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatTemporarilyDoneFetching);
+ msg->post();
}
if (finalResult == OK && downloadMore) {
- onDownloadNext();
+ ALOGV("monitoring, buffered=%lld < %lld",
+ bufferedDurationUs, durationToBufferUs);
+ // delay the next download slightly; hopefully this gives other concurrent fetchers
+ // a better chance to run.
+ // onDownloadNext();
+ sp<AMessage> msg = new AMessage(kWhatDownloadNext, id());
+ msg->setInt32("generation", mMonitorQueueGeneration);
+ msg->post(1000l);
} else {
// Nothing to do yet, try again in a second.
@@ -498,15 +649,17 @@
msg->setInt32("what", kWhatTemporarilyDoneFetching);
msg->post();
- postMonitorQueue(1000000ll);
+ int64_t delayUs = mPrepared ? kMaxMonitorDelayUs : targetDurationUs / 2;
+ ALOGV("pausing for %lld, buffered=%lld > %lld",
+ delayUs, bufferedDurationUs, durationToBufferUs);
+ // :TRICKY: need to enforce minimum delay because the delay to
+ // refresh the playlist will become 0
+ postMonitorQueue(delayUs, mPrepared ? targetDurationUs * 2 : 0);
}
}
-void PlaylistFetcher::onDownloadNext() {
- int64_t nowUs = ALooper::GetNowUs();
-
- if (mLastPlaylistFetchTimeUs < 0ll
- || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) {
+status_t PlaylistFetcher::refreshPlaylist() {
+ if (delayUsToRefreshPlaylist() <= 0) {
bool unchanged;
sp<M3UParser> playlist = mSession->fetchPlaylist(
mURI.c_str(), mPlaylistHash, &unchanged);
@@ -522,7 +675,7 @@
} else {
ALOGE("failed to load playlist at url '%s'", mURI.c_str());
notifyError(ERROR_IO);
- return;
+ return ERROR_IO;
}
} else {
mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY;
@@ -535,6 +688,13 @@
mLastPlaylistFetchTimeUs = ALooper::GetNowUs();
}
+ return OK;
+}
+
+void PlaylistFetcher::onDownloadNext() {
+ if (refreshPlaylist() != OK) {
+ return;
+ }
int32_t firstSeqNumberInPlaylist;
if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
@@ -548,17 +708,29 @@
const int32_t lastSeqNumberInPlaylist =
firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+ if (mStartup && mSeqNumber >= 0
+ && (mSeqNumber < firstSeqNumberInPlaylist || mSeqNumber > lastSeqNumberInPlaylist)) {
+ // in case we guessed wrong during reconfiguration, try fetching the latest content.
+ mSeqNumber = lastSeqNumberInPlaylist;
+ }
+
if (mSeqNumber < 0) {
CHECK_GE(mStartTimeUs, 0ll);
if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
mSeqNumber = getSeqNumberForTime(mStartTimeUs);
+ ALOGV("Initial sequence number for time %lld is %d from (%d .. %d)",
+ mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist,
+ lastSeqNumberInPlaylist);
} else {
// If this is a live session, start 3 segments from the end.
mSeqNumber = lastSeqNumberInPlaylist - 3;
if (mSeqNumber < firstSeqNumberInPlaylist) {
mSeqNumber = firstSeqNumberInPlaylist;
}
+ ALOGV("Initial sequence number for live event %d from (%d .. %d)",
+ mSeqNumber, firstSeqNumberInPlaylist,
+ lastSeqNumberInPlaylist);
}
mStartTimeUs = -1ll;
@@ -570,16 +742,35 @@
++mNumRetries;
if (mSeqNumber > lastSeqNumberInPlaylist) {
- mLastPlaylistFetchTimeUs = -1;
- postMonitorQueue(3000000ll);
+ // refresh in increasing fraction (1/2, 1/3, ...) of the
+ // playlist's target duration or 3 seconds, whichever is less
+ int32_t targetDurationSecs;
+ CHECK(mPlaylist->meta()->findInt32(
+ "target-duration", &targetDurationSecs));
+ int64_t delayUs = mPlaylist->size() * targetDurationSecs *
+ 1000000ll / (1 + mNumRetries);
+ if (delayUs > kMaxMonitorDelayUs) {
+ delayUs = kMaxMonitorDelayUs;
+ }
+ ALOGV("sequence number high: %d from (%d .. %d), "
+ "monitor in %lld (retry=%d)",
+ mSeqNumber, firstSeqNumberInPlaylist,
+ lastSeqNumberInPlaylist, delayUs, mNumRetries);
+ postMonitorQueue(delayUs);
return;
}
// we've missed the boat, let's start from the lowest sequence
// number available and signal a discontinuity.
- ALOGI("We've missed the boat, restarting playback.");
- mSeqNumber = lastSeqNumberInPlaylist;
+ ALOGI("We've missed the boat, restarting playback."
+ " mStartup=%d, was looking for %d in %d-%d",
+ mStartup, mSeqNumber, firstSeqNumberInPlaylist,
+ lastSeqNumberInPlaylist);
+ mSeqNumber = lastSeqNumberInPlaylist - 3;
+ if (mSeqNumber < firstSeqNumberInPlaylist) {
+ mSeqNumber = firstSeqNumberInPlaylist;
+ }
explicitDiscontinuity = true;
// fall through
@@ -633,6 +824,9 @@
CHECK(buffer != NULL);
err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer);
+ if (err == OK) {
+ err = checkDecryptPadding(buffer);
+ }
if (err != OK) {
ALOGE("decryptBuffer failed w/ error %d", err);
@@ -665,6 +859,18 @@
err = extractAndQueueAccessUnits(buffer, itemMeta);
+ if (err == -EAGAIN) {
+ // bad starting sequence number hint
+ postMonitorQueue();
+ return;
+ }
+
+ if (err == ERROR_OUT_OF_RANGE) {
+ // reached stopping point
+ stopAsync();
+ return;
+ }
+
if (err != OK) {
notifyError(err);
return;
@@ -721,12 +927,15 @@
}
if (mTSParser == NULL) {
- mTSParser = new ATSParser;
+ // Use TS_TIMESTAMPS_ARE_ABSOLUTE so pts carry over between fetchers.
+ mTSParser = new ATSParser(ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE);
}
if (mNextPTSTimeUs >= 0ll) {
sp<AMessage> extra = new AMessage;
- extra->setInt64(IStreamListener::kKeyMediaTimeUs, mNextPTSTimeUs);
+ // Since we are using absolute timestamps, signal an offset of 0 to prevent
+ // ATSParser from skewing the timestamps of access units.
+ extra->setInt64(IStreamListener::kKeyMediaTimeUs, 0);
mTSParser->signalDiscontinuity(
ATSParser::DISCONTINUITY_SEEK, extra);
@@ -745,17 +954,23 @@
offset += 188;
}
+ status_t err = OK;
for (size_t i = mPacketSources.size(); i-- > 0;) {
sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
+ const char *key;
ATSParser::SourceType type;
- switch (mPacketSources.keyAt(i)) {
+ const LiveSession::StreamType stream = mPacketSources.keyAt(i);
+ switch (stream) {
+
case LiveSession::STREAMTYPE_VIDEO:
type = ATSParser::VIDEO;
+ key = "timeUsVideo";
break;
case LiveSession::STREAMTYPE_AUDIO:
type = ATSParser::AUDIO;
+ key = "timeUsAudio";
break;
case LiveSession::STREAMTYPE_SUBTITLES:
@@ -782,20 +997,89 @@
continue;
}
+ int64_t timeUs;
sp<ABuffer> accessUnit;
status_t finalResult;
while (source->hasBufferAvailable(&finalResult)
&& source->dequeueAccessUnit(&accessUnit) == OK) {
- // Note that we do NOT dequeue any discontinuities.
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+ if (mMinStartTimeUs > 0) {
+ if (timeUs < mMinStartTimeUs) {
+ // TODO untested path
+ // try a later ts
+ int32_t targetDuration;
+ mPlaylist->meta()->findInt32("target-duration", &targetDuration);
+ int32_t incr = (mMinStartTimeUs - timeUs) / 1000000 / targetDuration;
+ if (incr == 0) {
+ // increment mSeqNumber by at least one
+ incr = 1;
+ }
+ mSeqNumber += incr;
+ err = -EAGAIN;
+ break;
+ } else {
+ int64_t startTimeUs;
+ if (mStartTimeUsNotify != NULL
+ && !mStartTimeUsNotify->findInt64(key, &startTimeUs)) {
+ mStartTimeUsNotify->setInt64(key, timeUs);
+
+ uint32_t streamMask = 0;
+ mStartTimeUsNotify->findInt32("streamMask", (int32_t *) &streamMask);
+ streamMask |= mPacketSources.keyAt(i);
+ mStartTimeUsNotify->setInt32("streamMask", streamMask);
+
+ if (streamMask == mStreamTypeMask) {
+ mStartTimeUsNotify->post();
+ mStartTimeUsNotify.clear();
+ }
+ }
+ }
+ }
+
+ if (mStopParams != NULL) {
+ // Queue discontinuity in original stream.
+ int64_t stopTimeUs;
+ if (!mStopParams->findInt64(key, &stopTimeUs) || timeUs >= stopTimeUs) {
+ packetSource->queueAccessUnit(mSession->createFormatChangeBuffer());
+ mStreamTypeMask &= ~stream;
+ mPacketSources.removeItemsAt(i);
+ break;
+ }
+ }
+
+ // Note that we do NOT dequeue any discontinuities except for format change.
+
+ // for simplicity, store a reference to the format in each unit
+ sp<MetaData> format = source->getFormat();
+ if (format != NULL) {
+ accessUnit->meta()->setObject("format", format);
+ }
+
+ // Stash the sequence number so we can hint future fetchers where to start at.
+ accessUnit->meta()->setInt32("seq", mSeqNumber);
packetSource->queueAccessUnit(accessUnit);
}
- if (packetSource->getFormat() == NULL) {
- packetSource->setFormat(source->getFormat());
+ if (err != OK) {
+ break;
}
}
+ if (err != OK) {
+ for (size_t i = mPacketSources.size(); i-- > 0;) {
+ sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
+ packetSource->clear();
+ }
+ return err;
+ }
+
+ if (!mStreamTypeMask) {
+ // Signal gap is filled between original and new stream.
+ ALOGV("ERROR OUT OF RANGE");
+ return ERROR_OUT_OF_RANGE;
+ }
+
return OK;
} else if (buffer->size() >= 7 && !memcmp("WEBVTT\n", buffer->data(), 7)) {
if (mStreamTypeMask != LiveSession::STREAMTYPE_SUBTITLES) {
@@ -810,6 +1094,7 @@
CHECK(itemMeta->findInt64("durationUs", &durationUs));
buffer->meta()->setInt64("timeUs", getSegmentStartTimeUs(mSeqNumber));
buffer->meta()->setInt64("durationUs", durationUs);
+ buffer->meta()->setInt32("seq", mSeqNumber);
packetSource->queueAccessUnit(buffer);
return OK;
@@ -935,6 +1220,18 @@
| (adtsHeader[4] << 3)
| (adtsHeader[5] >> 5);
+ if (aac_frame_length == 0) {
+ const uint8_t *id3Header = adtsHeader;
+ if (!memcmp(id3Header, "ID3", 3)) {
+ ID3 id3(id3Header, buffer->size() - offset, true);
+ if (id3.isValid()) {
+ offset += id3.rawSize();
+ continue;
+ };
+ }
+ return ERROR_MALFORMED;
+ }
+
CHECK_LE(offset + aac_frame_length, buffer->size());
sp<ABuffer> unit = new ABuffer(aac_frame_length);
@@ -946,6 +1243,7 @@
// Each AAC frame encodes 1024 samples.
numSamples += 1024;
+ unit->meta()->setInt32("seq", mSeqNumber);
packetSource->queueAccessUnit(unit);
offset += aac_frame_length;
@@ -973,4 +1271,33 @@
msg->post();
}
+int64_t PlaylistFetcher::resumeThreshold(const sp<AMessage> &msg) {
+ int64_t durationUs, threshold;
+ if (msg->findInt64("durationUs", &durationUs)) {
+ return kNumSkipFrames * durationUs;
+ }
+
+ sp<RefBase> obj;
+ msg->findObject("format", &obj);
+ MetaData *format = static_cast<MetaData *>(obj.get());
+
+ const char *mime;
+ CHECK(format->findCString(kKeyMIMEType, &mime));
+ bool audio = !strncasecmp(mime, "audio/", 6);
+ if (audio) {
+ // Assumes 1000 samples per frame.
+ int32_t sampleRate;
+ CHECK(format->findInt32(kKeySampleRate, &sampleRate));
+ return kNumSkipFrames /* frames */ * 1000 /* samples */
+ * (1000000 / sampleRate) /* sample duration (us) */;
+ } else {
+ int32_t frameRate;
+ if (format->findInt32(kKeyFrameRate, &frameRate) && frameRate > 0) {
+ return kNumSkipFrames * (1000000 / frameRate);
+ }
+ }
+
+ return 500000ll;
+}
+
} // namespace android
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 1648e02..2e0349f 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -43,6 +43,7 @@
kWhatTemporarilyDoneFetching,
kWhatPrepared,
kWhatPreparationFailed,
+ kWhatStartedAt,
};
PlaylistFetcher(
@@ -56,12 +57,16 @@
const sp<AnotherPacketSource> &audioSource,
const sp<AnotherPacketSource> &videoSource,
const sp<AnotherPacketSource> &subtitleSource,
- int64_t startTimeUs = -1ll);
+ int64_t startTimeUs = -1ll,
+ int64_t minStartTimeUs = 0ll /* start after this timestamp */,
+ int32_t startSeqNumberHint = -1 /* try starting at this sequence number */);
void pauseAsync();
void stopAsync();
+ void resumeUntilAsync(const sp<AMessage> ¶ms);
+
protected:
virtual ~PlaylistFetcher();
virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -76,16 +81,25 @@
kWhatPause = 'paus',
kWhatStop = 'stop',
kWhatMonitorQueue = 'moni',
+ kWhatResumeUntil = 'rsme',
+ kWhatDownloadNext = 'dlnx',
};
static const int64_t kMinBufferedDurationUs;
+ static const int64_t kMaxMonitorDelayUs;
+ static const int32_t kNumSkipFrames;
+ // notifications to mSession
sp<AMessage> mNotify;
+ sp<AMessage> mStartTimeUsNotify;
+
sp<LiveSession> mSession;
AString mURI;
uint32_t mStreamTypeMask;
int64_t mStartTimeUs;
+ int64_t mMinStartTimeUs; // start fetching no earlier than this value
+ sp<AMessage> mStopParams; // message containing the latest timestamps we should fetch.
KeyedVector<LiveSession::StreamType, sp<AnotherPacketSource> >
mPacketSources;
@@ -97,6 +111,7 @@
int32_t mSeqNumber;
int32_t mNumRetries;
bool mStartup;
+ bool mPrepared;
int64_t mNextPTSTimeUs;
int32_t mMonitorQueueGeneration;
@@ -117,13 +132,29 @@
uint64_t mFirstPTS;
int64_t mAbsoluteTimeAnchorUs;
- status_t decryptBuffer(
- size_t playlistIndex, const sp<ABuffer> &buffer);
+ // Stores the initialization vector to decrypt the next block of cipher text, which can
+ // either be derived from the sequence number, read from the manifest, or copied from
+ // the last block of cipher text (cipher-block chaining).
+ unsigned char mAESInitVec[16];
- void postMonitorQueue(int64_t delayUs = 0);
+ // Set first to true if decrypting the first segment of a playlist segment. When
+ // first is true, reset the initialization vector based on the available
+ // information in the manifest; otherwise, use the initialization vector as
+ // updated by the last call to AES_cbc_encrypt.
+ //
+ // For the input to decrypt correctly, decryptBuffer must be called on
+ // consecutive byte ranges on block boundaries, e.g. 0..15, 16..47, 48..63,
+ // and so on.
+ status_t decryptBuffer(
+ size_t playlistIndex, const sp<ABuffer> &buffer,
+ bool first = true);
+ status_t checkDecryptPadding(const sp<ABuffer> &buffer);
+
+ void postMonitorQueue(int64_t delayUs = 0, int64_t minDelayUs = 0);
void cancelMonitorQueue();
- bool timeToRefreshPlaylist(int64_t nowUs) const;
+ int64_t delayUsToRefreshPlaylist() const;
+ status_t refreshPlaylist();
// Returns the media time in us of the segment specified by seqNumber.
// This is computed by summing the durations of all segments before it.
@@ -135,6 +166,9 @@
void onMonitorQueue();
void onDownloadNext();
+ // Resume a fetcher to continue until the stopping point stored in msg.
+ status_t onResumeUntil(const sp<AMessage> &msg);
+
status_t extractAndQueueAccessUnits(
const sp<ABuffer> &buffer, const sp<AMessage> &itemMeta);
@@ -147,6 +181,10 @@
void updateDuration();
+ // Before resuming a fetcher in onResume, check the remaining duration is longer than that
+ // returned by resumeThreshold.
+ int64_t resumeThreshold(const sp<AMessage> &msg);
+
DISALLOW_EVIL_CONSTRUCTORS(PlaylistFetcher);
};
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index bf6f7bb..2194c38 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -4,6 +4,8 @@
LOCAL_SRC_FILES := \
ID3.cpp
+LOCAL_CFLAGS += -Werror
+
LOCAL_MODULE := libstagefright_id3
include $(BUILD_STATIC_LIBRARY)
@@ -15,6 +17,8 @@
LOCAL_SRC_FILES := \
testid3.cpp
+LOCAL_CFLAGS += -Werror
+
LOCAL_SHARED_LIBRARIES := \
libstagefright libutils liblog libbinder libstagefright_foundation
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index 34d671a..f0f203c 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -56,14 +56,14 @@
DISALLOW_EVIL_CONSTRUCTORS(MemorySource);
};
-ID3::ID3(const sp<DataSource> &source, bool ignoreV1)
+ID3::ID3(const sp<DataSource> &source, bool ignoreV1, off64_t offset)
: mIsValid(false),
mData(NULL),
mSize(0),
mFirstFrameOffset(0),
mVersion(ID3_UNKNOWN),
mRawSize(0) {
- mIsValid = parseV2(source);
+ mIsValid = parseV2(source, offset);
if (!mIsValid && !ignoreV1) {
mIsValid = parseV1(source);
@@ -79,7 +79,7 @@
mRawSize(0) {
sp<MemorySource> source = new MemorySource(data, size);
- mIsValid = parseV2(source);
+ mIsValid = parseV2(source, 0);
if (!mIsValid && !ignoreV1) {
mIsValid = parseV1(source);
@@ -115,7 +115,7 @@
return true;
}
-bool ID3::parseV2(const sp<DataSource> &source) {
+bool ID3::parseV2(const sp<DataSource> &source, off64_t offset) {
struct id3_header {
char id[3];
uint8_t version_major;
@@ -126,7 +126,7 @@
id3_header header;
if (source->readAt(
- 0, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
+ offset, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
return false;
}
@@ -185,7 +185,7 @@
mSize = size;
mRawSize = mSize + sizeof(header);
- if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
+ if (source->readAt(offset + sizeof(header), mData, mSize) != (ssize_t)mSize) {
free(mData);
mData = NULL;
@@ -468,49 +468,6 @@
}
}
-static void convertISO8859ToString8(
- const uint8_t *data, size_t size,
- String8 *s) {
- size_t utf8len = 0;
- for (size_t i = 0; i < size; ++i) {
- if (data[i] == '\0') {
- size = i;
- break;
- } else if (data[i] < 0x80) {
- ++utf8len;
- } else {
- utf8len += 2;
- }
- }
-
- if (utf8len == size) {
- // Only ASCII characters present.
-
- s->setTo((const char *)data, size);
- return;
- }
-
- char *tmp = new char[utf8len];
- char *ptr = tmp;
- for (size_t i = 0; i < size; ++i) {
- if (data[i] == '\0') {
- break;
- } else if (data[i] < 0x80) {
- *ptr++ = data[i];
- } else if (data[i] < 0xc0) {
- *ptr++ = 0xc2;
- *ptr++ = data[i];
- } else {
- *ptr++ = 0xc3;
- *ptr++ = data[i] - 64;
- }
- }
-
- s->setTo(tmp, utf8len);
-
- delete[] tmp;
- tmp = NULL;
-}
// the 2nd argument is used to get the data following the \0 in a comment field
void ID3::Iterator::getString(String8 *id, String8 *comment) const {
@@ -543,7 +500,9 @@
return;
}
- convertISO8859ToString8(frameData, mFrameSize, id);
+ // this is supposed to be ISO-8859-1, but pass it up as-is to the caller, who will figure
+ // out the real encoding
+ id->setTo((const char*)frameData, mFrameSize);
return;
}
@@ -561,13 +520,13 @@
}
if (encoding == 0x00) {
- // ISO 8859-1
- convertISO8859ToString8(frameData + 1, n, id);
+ // supposedly ISO 8859-1
+ id->setTo((const char*)frameData + 1, n);
} else if (encoding == 0x03) {
- // UTF-8
+ // supposedly UTF-8
id->setTo((const char *)(frameData + 1), n);
} else if (encoding == 0x02) {
- // UTF-16 BE, no byte order mark.
+ // supposedly UTF-16 BE, no byte order mark.
// API wants number of characters, not number of bytes...
int len = n / 2;
const char16_t *framedata = (const char16_t *) (frameData + 1);
@@ -583,7 +542,7 @@
if (framedatacopy != NULL) {
delete[] framedatacopy;
}
- } else {
+ } else if (encoding == 0x01) {
// UCS-2
// API wants number of characters, not number of bytes...
int len = n / 2;
@@ -602,7 +561,27 @@
framedata++;
len--;
}
- id->setTo(framedata, len);
+
+ // check if the resulting data consists entirely of 8-bit values
+ bool eightBit = true;
+ for (int i = 0; i < len; i++) {
+ if (framedata[i] > 0xff) {
+ eightBit = false;
+ break;
+ }
+ }
+ if (eightBit) {
+ // collapse to 8 bit, then let the media scanner client figure out the real encoding
+ char *frame8 = new char[len];
+ for (int i = 0; i < len; i++) {
+ frame8[i] = framedata[i];
+ }
+ id->setTo(frame8, len);
+ delete [] frame8;
+ } else {
+ id->setTo(framedata, len);
+ }
+
if (framedatacopy != NULL) {
delete[] framedatacopy;
}
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
index bc4572c..b2f4188 100644
--- a/media/libstagefright/id3/testid3.cpp
+++ b/media/libstagefright/id3/testid3.cpp
@@ -33,7 +33,7 @@
const uint8_t *data = (const uint8_t *)_data;
size_t offset = 0;
while (offset < size) {
- printf("0x%04x ", offset);
+ printf("0x%04zx ", offset);
size_t n = size - offset;
if (n > 16) {
@@ -101,7 +101,7 @@
const void *data = tag.getAlbumArt(&dataSize, &mime);
if (data) {
- printf("found album art: size=%d mime='%s'\n", dataSize,
+ printf("found album art: size=%zu mime='%s'\n", dataSize,
mime.string());
hexdump(data, dataSize > 128 ? 128 : dataSize);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index b001cf4..a81bbba 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -63,6 +63,7 @@
void setUID(uid_t uid);
status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *uri,
const KeyedVector<String8, String8> *headers = NULL);
@@ -159,6 +160,7 @@
SystemTimeSource mSystemTimeSource;
TimeSource *mTimeSource;
+ sp<IMediaHTTPService> mHTTPService;
String8 mUri;
KeyedVector<String8, String8> mUriHeaders;
@@ -247,6 +249,7 @@
sp<MediaExtractor> mExtractor;
status_t setDataSource_l(
+ const sp<IMediaHTTPService> &httpService,
const char *uri,
const KeyedVector<String8, String8> *headers = NULL);
@@ -342,6 +345,8 @@
bool mOffloadAudio;
bool mAudioTearDown;
+ bool mAudioTearDownWasPlaying;
+ int64_t mAudioTearDownPosition;
status_t setVideoScalingMode(int32_t mode);
status_t setVideoScalingMode_l(int32_t mode);
diff --git a/media/libstagefright/include/ChromiumHTTPDataSource.h b/media/libstagefright/include/ChromiumHTTPDataSource.h
deleted file mode 100644
index 785f939..0000000
--- a/media/libstagefright/include/ChromiumHTTPDataSource.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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 sp<DecryptHandle> DrmInitialization(const char *mime);
-
- virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
-
- virtual String8 getUri();
-
- virtual String8 getMIMEType() const;
-
- virtual status_t reconnectAtOffset(off64_t offset);
-
- static status_t UpdateProxyConfig(
- const char *host, int32_t port, const char *exclusionList);
-
-protected:
- virtual ~ChromiumHTTPDataSource();
-
-private:
- friend struct SfDelegate;
-
- enum State {
- DISCONNECTED,
- CONNECTING,
- CONNECTED,
- READING,
- DISCONNECTING
- };
-
- 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;
-
- 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 clearDRMState_l();
-
- DISALLOW_EVIL_CONSTRUCTORS(ChromiumHTTPDataSource);
-};
-
-} // namespace android
-
-#endif // CHROME_HTTP_DATA_SOURCE_H_
diff --git a/media/libstagefright/include/FragmentedMP4Parser.h b/media/libstagefright/include/FragmentedMP4Parser.h
deleted file mode 100644
index dbe02b8..0000000
--- a/media/libstagefright/include/FragmentedMP4Parser.h
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef PARSER_H_
-
-#define PARSER_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/DataSource.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-struct ABuffer;
-
-struct FragmentedMP4Parser : public AHandler {
- struct Source : public RefBase {
- Source() {}
-
- virtual ssize_t readAt(off64_t offset, void *data, size_t size) = 0;
- virtual bool isSeekable() = 0;
-
- protected:
- virtual ~Source() {}
-
- private:
- DISALLOW_EVIL_CONSTRUCTORS(Source);
- };
-
- FragmentedMP4Parser();
-
- void start(const char *filename);
- void start(const sp<Source> &source);
- void start(sp<DataSource> &source);
-
- sp<AMessage> getFormat(bool audio, bool synchronous = false);
- status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit, bool synchronous = false);
- status_t seekTo(bool audio, int64_t timeUs);
- bool isSeekable() const;
-
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-protected:
- virtual ~FragmentedMP4Parser();
-
-private:
- enum {
- kWhatStart,
- kWhatProceed,
- kWhatReadMore,
- kWhatGetFormat,
- kWhatDequeueAccessUnit,
- kWhatSeekTo,
- };
-
- struct TrackFragment;
- struct DynamicTrackFragment;
- struct StaticTrackFragment;
-
- struct DispatchEntry {
- uint32_t mType;
- uint32_t mParentType;
- status_t (FragmentedMP4Parser::*mHandler)(uint32_t, size_t, uint64_t);
- };
-
- struct Container {
- uint64_t mOffset;
- uint64_t mBytesRemaining;
- uint32_t mType;
- bool mExtendsToEOF;
- };
-
- struct SampleDescription {
- uint32_t mType;
- uint16_t mDataRefIndex;
-
- sp<AMessage> mFormat;
- };
-
- struct SampleInfo {
- off64_t mOffset;
- size_t mSize;
- uint32_t mPresentationTime;
- size_t mSampleDescIndex;
- uint32_t mFlags;
- };
-
- struct MediaDataInfo {
- sp<ABuffer> mBuffer;
- off64_t mOffset;
- };
-
- struct SidxEntry {
- size_t mSize;
- uint32_t mDurationUs;
- };
-
- struct TrackInfo {
- enum Flags {
- kTrackEnabled = 0x01,
- kTrackInMovie = 0x02,
- kTrackInPreview = 0x04,
- };
-
- uint32_t mTrackID;
- uint32_t mFlags;
- uint32_t mDuration; // This is the duration in terms of movie timescale!
- uint64_t mSidxDuration; // usec, from sidx box, which can use a different timescale
-
- uint32_t mMediaTimeScale;
-
- uint32_t mMediaHandlerType;
- Vector<SampleDescription> mSampleDescs;
-
- // from track extends:
- uint32_t mDefaultSampleDescriptionIndex;
- uint32_t mDefaultSampleDuration;
- uint32_t mDefaultSampleSize;
- uint32_t mDefaultSampleFlags;
-
- uint32_t mDecodingTime;
-
- Vector<SidxEntry> mSidx;
- sp<StaticTrackFragment> mStaticFragment;
- List<sp<TrackFragment> > mFragments;
- };
-
- struct TrackFragmentHeaderInfo {
- enum Flags {
- kBaseDataOffsetPresent = 0x01,
- kSampleDescriptionIndexPresent = 0x02,
- kDefaultSampleDurationPresent = 0x08,
- kDefaultSampleSizePresent = 0x10,
- kDefaultSampleFlagsPresent = 0x20,
- kDurationIsEmpty = 0x10000,
- };
-
- uint32_t mTrackID;
- uint32_t mFlags;
- uint64_t mBaseDataOffset;
- uint32_t mSampleDescriptionIndex;
- uint32_t mDefaultSampleDuration;
- uint32_t mDefaultSampleSize;
- uint32_t mDefaultSampleFlags;
-
- uint64_t mDataOffset;
- };
-
- static const DispatchEntry kDispatchTable[];
-
- sp<Source> mSource;
- off_t mBufferPos;
- bool mSuspended;
- bool mDoneWithMoov;
- off_t mFirstMoofOffset; // used as the starting point for offsets calculated from the sidx box
- sp<ABuffer> mBuffer;
- Vector<Container> mStack;
- KeyedVector<uint32_t, TrackInfo> mTracks; // TrackInfo by trackID
- Vector<MediaDataInfo> mMediaData;
-
- uint32_t mCurrentTrackID;
-
- status_t mFinalResult;
-
- TrackFragmentHeaderInfo mTrackFragmentHeaderInfo;
-
- status_t onProceed();
- status_t onDequeueAccessUnit(size_t trackIndex, sp<ABuffer> *accessUnit);
- status_t onSeekTo(bool wantAudio, int64_t position);
-
- void enter(off64_t offset, uint32_t type, uint64_t size);
-
- uint16_t readU16(size_t offset);
- uint32_t readU32(size_t offset);
- uint64_t readU64(size_t offset);
- void skip(off_t distance);
- status_t need(size_t size);
- bool fitsContainer(uint64_t size) const;
-
- status_t parseTrackHeader(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseMediaHeader(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseMediaHandler(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseTrackExtends(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseTrackFragmentHeader(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseTrackFragmentRun(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseVisualSampleEntry(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseAudioSampleEntry(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseSampleSizes(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseCompactSampleSizes(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseSampleToChunk(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseChunkOffsets(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseChunkOffsets64(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseAVCCodecSpecificData(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseESDSCodecSpecificData(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseMediaData(
- uint32_t type, size_t offset, uint64_t size);
-
- status_t parseSegmentIndex(
- uint32_t type, size_t offset, uint64_t size);
-
- TrackInfo *editTrack(uint32_t trackID, bool createIfNecessary = false);
-
- ssize_t findTrack(bool wantAudio) const;
-
- status_t makeAccessUnit(
- TrackInfo *info,
- const SampleInfo &sample,
- const MediaDataInfo &mdatInfo,
- sp<ABuffer> *accessUnit);
-
- status_t getSample(
- TrackInfo *info,
- sp<TrackFragment> *fragment,
- SampleInfo *sampleInfo);
-
- static int CompareSampleLocation(
- const SampleInfo &sample, const MediaDataInfo &mdatInfo);
-
- void resumeIfNecessary();
-
- void copyBuffer(
- sp<ABuffer> *dst,
- size_t offset, uint64_t size) const;
-
- DISALLOW_EVIL_CONSTRUCTORS(FragmentedMP4Parser);
-};
-
-} // namespace android
-
-#endif // PARSER_H_
-
diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h
index d4b7f9f..1c3cd5e 100644
--- a/media/libstagefright/include/HTTPBase.h
+++ b/media/libstagefright/include/HTTPBase.h
@@ -48,14 +48,6 @@
virtual status_t setBandwidthStatCollectFreq(int32_t freqMs);
- static status_t UpdateProxyConfig(
- const char *host, int32_t port, const char *exclusionList);
-
- void setUID(uid_t uid);
- bool getUID(uid_t *uid) const;
-
- static sp<HTTPBase> Create(uint32_t flags = 0);
-
static void RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag);
static void UnRegisterSocketUserTag(int sockfd);
@@ -87,9 +79,6 @@
int32_t mPrevEstimatedBandWidthKbps;
int32_t mBandWidthCollectFreqMs;
- bool mUIDValid;
- uid_t mUID;
-
DISALLOW_EVIL_CONSTRUCTORS(HTTPBase);
};
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
index cca83ab..e83f3ef 100644
--- a/media/libstagefright/include/ID3.h
+++ b/media/libstagefright/include/ID3.h
@@ -35,7 +35,7 @@
ID3_V2_4,
};
- ID3(const sp<DataSource> &source, bool ignoreV1 = false);
+ ID3(const sp<DataSource> &source, bool ignoreV1 = false, off64_t offset = 0);
ID3(const uint8_t *data, size_t size, bool ignoreV1 = false);
~ID3();
@@ -86,7 +86,7 @@
size_t mRawSize;
bool parseV1(const sp<DataSource> &source);
- bool parseV2(const sp<DataSource> &source);
+ bool parseV2(const sp<DataSource> &source, off64_t offset);
void removeUnsynchronization();
bool removeUnsynchronizationV2_4(bool iTunesHack);
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index bd5e4b9..7b4bc6d 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -97,6 +97,7 @@
status_t parseChunk(off64_t *offset, int depth);
status_t parseITunesMetaData(off64_t offset, size_t size);
status_t parse3GPPMetaData(off64_t offset, size_t size, int depth);
+ void parseID3v2MetaData(off64_t offset);
status_t updateAudioTrackInfoFromESDS_MPEG4Audio(
const void *esds_data, size_t esds_size);
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 7e53af3..31a5077 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -71,6 +71,10 @@
virtual status_t storeMetaDataInBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable);
+ virtual status_t prepareForAdaptivePlayback(
+ node_id node, OMX_U32 portIndex, OMX_BOOL enable,
+ OMX_U32 max_frame_width, OMX_U32 max_frame_height);
+
virtual status_t useBuffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index ae498b4..339179e 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -58,6 +58,10 @@
status_t storeMetaDataInBuffers(OMX_U32 portIndex, OMX_BOOL enable);
+ status_t prepareForAdaptivePlayback(
+ OMX_U32 portIndex, OMX_BOOL enable,
+ OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight);
+
status_t useBuffer(
OMX_U32 portIndex, const sp<IMemory> ¶ms,
OMX::buffer_id *buffer);
diff --git a/media/libstagefright/include/SDPLoader.h b/media/libstagefright/include/SDPLoader.h
index ca59dc0..2c4f543 100644
--- a/media/libstagefright/include/SDPLoader.h
+++ b/media/libstagefright/include/SDPLoader.h
@@ -25,6 +25,7 @@
namespace android {
struct HTTPBase;
+struct IMediaHTTPService;
struct SDPLoader : public AHandler {
enum Flags {
@@ -34,7 +35,10 @@
enum {
kWhatSDPLoaded = 'sdpl'
};
- SDPLoader(const sp<AMessage> ¬ify, uint32_t flags = 0, bool uidValid = false, uid_t uid = 0);
+ SDPLoader(
+ const sp<AMessage> ¬ify,
+ uint32_t flags,
+ const sp<IMediaHTTPService> &httpService);
void load(const char* url, const KeyedVector<String8, String8> *headers);
@@ -55,8 +59,6 @@
sp<AMessage> mNotify;
const char* mUrl;
uint32_t mFlags;
- bool mUIDValid;
- uid_t mUID;
sp<ALooper> mNetLooper;
bool mCancelled;
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index b02ed0e..6632c27 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -33,6 +33,7 @@
virtual ~StagefrightMetadataRetriever();
virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers);
diff --git a/media/libstagefright/include/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h
index 11f844c..2963150 100644
--- a/media/libstagefright/include/TimedEventQueue.h
+++ b/media/libstagefright/include/TimedEventQueue.h
@@ -23,6 +23,7 @@
#include <utils/List.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
+#include <powermanager/IPowerManager.h>
namespace android {
@@ -57,6 +58,21 @@
Event &operator=(const Event &);
};
+ class PMDeathRecipient : public IBinder::DeathRecipient {
+ public:
+ PMDeathRecipient(TimedEventQueue *queue) : mQueue(queue) {}
+ virtual ~PMDeathRecipient() {}
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ private:
+ PMDeathRecipient(const PMDeathRecipient&);
+ PMDeathRecipient& operator = (const PMDeathRecipient&);
+
+ TimedEventQueue *mQueue;
+ };
+
TimedEventQueue();
~TimedEventQueue();
@@ -96,14 +112,17 @@
static int64_t getRealTimeUs();
+ void clearPowerManager();
+
private:
struct QueueItem {
sp<Event> event;
int64_t realtime_us;
+ bool has_wakelock;
};
struct StopEvent : public TimedEventQueue::Event {
- virtual void fire(TimedEventQueue *queue, int64_t now_us) {
+ virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
queue->mStopped = true;
}
};
@@ -118,10 +137,18 @@
bool mRunning;
bool mStopped;
+ sp<IPowerManager> mPowerManager;
+ sp<IBinder> mWakeLockToken;
+ const sp<PMDeathRecipient> mDeathRecipient;
+ uint32_t mWakeLockCount;
+
static void *ThreadWrapper(void *me);
void threadEntry();
- sp<Event> removeEventFromQueue_l(event_id id);
+ sp<Event> removeEventFromQueue_l(event_id id, bool *wakeLocked);
+
+ void acquireWakeLock_l();
+ void releaseWakeLock_l(bool force = false);
TimedEventQueue(const TimedEventQueue &);
TimedEventQueue &operator=(const TimedEventQueue &);
diff --git a/media/libstagefright/include/chromium_http_stub.h b/media/libstagefright/include/chromium_http_stub.h
deleted file mode 100644
index e0651a4..0000000
--- a/media/libstagefright/include/chromium_http_stub.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef CHROMIUM_HTTP_STUB_H_
-#define CHROMIUM_HTTP_STUB_H_
-
-#include <include/HTTPBase.h>
-#include <media/stagefright/DataSource.h>
-
-namespace android {
-extern "C" {
-HTTPBase *createChromiumHTTPDataSource(uint32_t flags);
-
-status_t UpdateChromiumHTTPDataSourceProxyConfig(
- const char *host, int32_t port, const char *exclusionList);
-
-DataSource *createDataUriSource(const char *uri);
-}
-}
-
-#endif
diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk
index 2d8c1e1..446ff8c 100644
--- a/media/libstagefright/matroska/Android.mk
+++ b/media/libstagefright/matroska/Android.mk
@@ -8,7 +8,7 @@
$(TOP)/external/libvpx/libwebm \
$(TOP)/frameworks/native/include/media/openmax \
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror
LOCAL_MODULE:= libstagefright_matroska
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index d260d0f..6ec9263 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -193,7 +193,7 @@
clearPendingFrames();
}
-status_t MatroskaSource::start(MetaData *params) {
+status_t MatroskaSource::start(MetaData * /* params */) {
mBlockIter.reset();
return OK;
@@ -313,7 +313,7 @@
*actualFrameTimeUs = -1ll;
- const int64_t seekTimeNs = seekTimeUs * 1000ll;
+ const int64_t seekTimeNs = seekTimeUs * 1000ll - mExtractor->mSeekPreRollNs;
mkvparser::Segment* const pSegment = mExtractor->mSegment;
@@ -410,7 +410,7 @@
// Accept the first key frame
*actualFrameTimeUs = (block()->GetTime(mCluster) + 500LL) / 1000LL;
ALOGV("Requested seek point: %lld actual: %lld",
- seekTimeUs, actualFrameTimeUs);
+ seekTimeUs, *actualFrameTimeUs);
break;
}
}
@@ -628,7 +628,8 @@
mReader(new DataSourceReader(mDataSource)),
mSegment(NULL),
mExtractedThumbnails(false),
- mIsWebm(false) {
+ mIsWebm(false),
+ mSeekPreRollNs(0) {
off64_t size;
mIsLiveStreaming =
(mDataSource->flags()
@@ -716,41 +717,61 @@
return mIsLiveStreaming;
}
+static int bytesForSize(size_t size) {
+ // use at most 28 bits (4 times 7)
+ CHECK(size <= 0xfffffff);
+
+ if (size > 0x1fffff) {
+ return 4;
+ } else if (size > 0x3fff) {
+ return 3;
+ } else if (size > 0x7f) {
+ return 2;
+ }
+ return 1;
+}
+
+static void storeSize(uint8_t *data, size_t &idx, size_t size) {
+ int numBytes = bytesForSize(size);
+ idx += numBytes;
+
+ data += idx;
+ size_t next = 0;
+ while (numBytes--) {
+ *--data = (size & 0x7f) | next;
+ size >>= 7;
+ next = 0x80;
+ }
+}
+
static void addESDSFromCodecPrivate(
const sp<MetaData> &meta,
bool isAudio, const void *priv, size_t privSize) {
- static const uint8_t kStaticESDS[] = {
- 0x03, 22,
- 0x00, 0x00, // ES_ID
- 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
- 0x04, 17,
- 0x40, // ObjectTypeIndication
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
-
- 0x05,
- // CodecSpecificInfo (with size prefix) follows
- };
-
- // Make sure all sizes can be coded in a single byte.
- CHECK(privSize + 22 - 2 < 128);
- size_t esdsSize = sizeof(kStaticESDS) + privSize + 1;
+ int privSizeBytesRequired = bytesForSize(privSize);
+ int esdsSize2 = 14 + privSizeBytesRequired + privSize;
+ int esdsSize2BytesRequired = bytesForSize(esdsSize2);
+ int esdsSize1 = 4 + esdsSize2BytesRequired + esdsSize2;
+ int esdsSize1BytesRequired = bytesForSize(esdsSize1);
+ size_t esdsSize = 1 + esdsSize1BytesRequired + esdsSize1;
uint8_t *esds = new uint8_t[esdsSize];
- memcpy(esds, kStaticESDS, sizeof(kStaticESDS));
- uint8_t *ptr = esds + sizeof(kStaticESDS);
- *ptr++ = privSize;
- memcpy(ptr, priv, privSize);
- // Increment by codecPrivateSize less 2 bytes that are accounted for
- // already in lengths of 22/17
- esds[1] += privSize - 2;
- esds[6] += privSize - 2;
-
- // Set ObjectTypeIndication.
- esds[7] = isAudio ? 0x40 // Audio ISO/IEC 14496-3
- : 0x20; // Visual ISO/IEC 14496-2
+ size_t idx = 0;
+ esds[idx++] = 0x03;
+ storeSize(esds, idx, esdsSize1);
+ esds[idx++] = 0x00; // ES_ID
+ esds[idx++] = 0x00; // ES_ID
+ esds[idx++] = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag
+ esds[idx++] = 0x04;
+ storeSize(esds, idx, esdsSize2);
+ esds[idx++] = isAudio ? 0x40 // Audio ISO/IEC 14496-3
+ : 0x20; // Visual ISO/IEC 14496-2
+ for (int i = 0; i < 12; i++) {
+ esds[idx++] = 0x00;
+ }
+ esds[idx++] = 0x05;
+ storeSize(esds, idx, privSize);
+ memcpy(esds + idx, priv, privSize);
meta->setData(kKeyESDS, 0, esds, esdsSize);
@@ -899,6 +920,12 @@
err = addVorbisCodecInfo(
meta, codecPrivate, codecPrivateSize);
+ } else if (!strcmp("A_OPUS", codecID)) {
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_OPUS);
+ meta->setData(kKeyOpusHeader, 0, codecPrivate, codecPrivateSize);
+ meta->setInt64(kKeyOpusCodecDelay, track->GetCodecDelay());
+ meta->setInt64(kKeyOpusSeekPreRoll, track->GetSeekPreRoll());
+ mSeekPreRollNs = track->GetSeekPreRoll();
} else if (!strcmp("A_MPEG/L3", codecID)) {
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
} else {
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
index 1294b4f..cf200f3 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.h
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -69,6 +69,7 @@
bool mExtractedThumbnails;
bool mIsLiveStreaming;
bool mIsWebm;
+ int64_t mSeekPreRollNs;
void addTracks();
void findThumbnails();
diff --git a/media/libstagefright/mp4/FragmentedMP4Parser.cpp b/media/libstagefright/mp4/FragmentedMP4Parser.cpp
deleted file mode 100644
index 0102656..0000000
--- a/media/libstagefright/mp4/FragmentedMP4Parser.cpp
+++ /dev/null
@@ -1,1993 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "FragmentedMP4Parser"
-#include <utils/Log.h>
-
-#include "include/avc_utils.h"
-#include "include/ESDS.h"
-#include "include/FragmentedMP4Parser.h"
-#include "TrackFragment.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/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-
-namespace android {
-
-static const char *Fourcc2String(uint32_t fourcc) {
- static char buffer[5];
- buffer[4] = '\0';
- buffer[0] = fourcc >> 24;
- buffer[1] = (fourcc >> 16) & 0xff;
- buffer[2] = (fourcc >> 8) & 0xff;
- buffer[3] = fourcc & 0xff;
-
- return buffer;
-}
-
-static const char *IndentString(size_t n) {
- static const char kSpace[] = " ";
- return kSpace + sizeof(kSpace) - 2 * n - 1;
-}
-
-// static
-const FragmentedMP4Parser::DispatchEntry FragmentedMP4Parser::kDispatchTable[] = {
- { FOURCC('m', 'o', 'o', 'v'), 0, NULL },
- { FOURCC('t', 'r', 'a', 'k'), FOURCC('m', 'o', 'o', 'v'), NULL },
- { FOURCC('u', 'd', 't', 'a'), FOURCC('t', 'r', 'a', 'k'), NULL },
- { FOURCC('u', 'd', 't', 'a'), FOURCC('m', 'o', 'o', 'v'), NULL },
- { FOURCC('m', 'e', 't', 'a'), FOURCC('u', 'd', 't', 'a'), NULL },
- { FOURCC('i', 'l', 's', 't'), FOURCC('m', 'e', 't', 'a'), NULL },
-
- { FOURCC('t', 'k', 'h', 'd'), FOURCC('t', 'r', 'a', 'k'),
- &FragmentedMP4Parser::parseTrackHeader
- },
-
- { FOURCC('m', 'v', 'e', 'x'), FOURCC('m', 'o', 'o', 'v'), NULL },
-
- { FOURCC('t', 'r', 'e', 'x'), FOURCC('m', 'v', 'e', 'x'),
- &FragmentedMP4Parser::parseTrackExtends
- },
-
- { FOURCC('e', 'd', 't', 's'), FOURCC('t', 'r', 'a', 'k'), NULL },
- { FOURCC('m', 'd', 'i', 'a'), FOURCC('t', 'r', 'a', 'k'), NULL },
-
- { FOURCC('m', 'd', 'h', 'd'), FOURCC('m', 'd', 'i', 'a'),
- &FragmentedMP4Parser::parseMediaHeader
- },
-
- { FOURCC('h', 'd', 'l', 'r'), FOURCC('m', 'd', 'i', 'a'),
- &FragmentedMP4Parser::parseMediaHandler
- },
-
- { FOURCC('m', 'i', 'n', 'f'), FOURCC('m', 'd', 'i', 'a'), NULL },
- { FOURCC('d', 'i', 'n', 'f'), FOURCC('m', 'i', 'n', 'f'), NULL },
- { FOURCC('s', 't', 'b', 'l'), FOURCC('m', 'i', 'n', 'f'), NULL },
- { FOURCC('s', 't', 's', 'd'), FOURCC('s', 't', 'b', 'l'), NULL },
-
- { FOURCC('s', 't', 's', 'z'), FOURCC('s', 't', 'b', 'l'),
- &FragmentedMP4Parser::parseSampleSizes },
-
- { FOURCC('s', 't', 'z', '2'), FOURCC('s', 't', 'b', 'l'),
- &FragmentedMP4Parser::parseCompactSampleSizes },
-
- { FOURCC('s', 't', 's', 'c'), FOURCC('s', 't', 'b', 'l'),
- &FragmentedMP4Parser::parseSampleToChunk },
-
- { FOURCC('s', 't', 'c', 'o'), FOURCC('s', 't', 'b', 'l'),
- &FragmentedMP4Parser::parseChunkOffsets },
-
- { FOURCC('c', 'o', '6', '4'), FOURCC('s', 't', 'b', 'l'),
- &FragmentedMP4Parser::parseChunkOffsets64 },
-
- { FOURCC('a', 'v', 'c', 'C'), FOURCC('a', 'v', 'c', '1'),
- &FragmentedMP4Parser::parseAVCCodecSpecificData },
-
- { FOURCC('e', 's', 'd', 's'), FOURCC('m', 'p', '4', 'a'),
- &FragmentedMP4Parser::parseESDSCodecSpecificData },
-
- { FOURCC('e', 's', 'd', 's'), FOURCC('m', 'p', '4', 'v'),
- &FragmentedMP4Parser::parseESDSCodecSpecificData },
-
- { FOURCC('m', 'd', 'a', 't'), 0, &FragmentedMP4Parser::parseMediaData },
-
- { FOURCC('m', 'o', 'o', 'f'), 0, NULL },
- { FOURCC('t', 'r', 'a', 'f'), FOURCC('m', 'o', 'o', 'f'), NULL },
-
- { FOURCC('t', 'f', 'h', 'd'), FOURCC('t', 'r', 'a', 'f'),
- &FragmentedMP4Parser::parseTrackFragmentHeader
- },
- { FOURCC('t', 'r', 'u', 'n'), FOURCC('t', 'r', 'a', 'f'),
- &FragmentedMP4Parser::parseTrackFragmentRun
- },
-
- { FOURCC('m', 'f', 'r', 'a'), 0, NULL },
-
- { FOURCC('s', 'i', 'd', 'x'), 0, &FragmentedMP4Parser::parseSegmentIndex },
-};
-
-struct FileSource : public FragmentedMP4Parser::Source {
- FileSource(const char *filename)
- : mFile(fopen(filename, "rb")) {
- CHECK(mFile != NULL);
- }
-
- virtual ~FileSource() {
- fclose(mFile);
- }
-
- virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
- fseek(mFile, offset, SEEK_SET);
- return fread(data, 1, size, mFile);
- }
-
- virtual bool isSeekable() {
- return true;
- }
-
- private:
- FILE *mFile;
-
- DISALLOW_EVIL_CONSTRUCTORS(FileSource);
-};
-
-struct ReadTracker : public RefBase {
- ReadTracker(off64_t size) {
- allocSize = 1 + size / 8192; // 1 bit per kilobyte
- bitmap = (char*) calloc(1, allocSize);
- }
- virtual ~ReadTracker() {
- dumpToLog();
- free(bitmap);
- }
- void mark(off64_t offset, size_t size) {
- int firstbit = offset / 1024;
- int lastbit = (offset + size - 1) / 1024;
- for (int i = firstbit; i <= lastbit; i++) {
- bitmap[i/8] |= (0x80 >> (i & 7));
- }
- }
-
- private:
- void dumpToLog() {
- // 96 chars per line, each char represents one kilobyte, 1 kb per bit
- int numlines = allocSize / 12;
- char buf[97];
- char *cur = bitmap;
- for (int i = 0; i < numlines; i++ && cur) {
- for (int j = 0; j < 12; j++) {
- for (int k = 0; k < 8; k++) {
- buf[(j * 8) + k] = (*cur & (0x80 >> k)) ? 'X' : '.';
- }
- cur++;
- }
- buf[96] = '\0';
- ALOGI("%5dk: %s", i * 96, buf);
- }
- }
-
- size_t allocSize;
- char *bitmap;
-};
-
-struct DataSourceSource : public FragmentedMP4Parser::Source {
- DataSourceSource(sp<DataSource> &source)
- : mDataSource(source) {
- CHECK(mDataSource != NULL);
-#if 0
- off64_t size;
- if (source->getSize(&size) == OK) {
- mReadTracker = new ReadTracker(size);
- } else {
- ALOGE("couldn't get data source size");
- }
-#endif
- }
-
- virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
- if (mReadTracker != NULL) {
- mReadTracker->mark(offset, size);
- }
- return mDataSource->readAt(offset, data, size);
- }
-
- virtual bool isSeekable() {
- return true;
- }
-
- private:
- sp<DataSource> mDataSource;
- sp<ReadTracker> mReadTracker;
-
- DISALLOW_EVIL_CONSTRUCTORS(DataSourceSource);
-};
-
-FragmentedMP4Parser::FragmentedMP4Parser()
- : mBufferPos(0),
- mSuspended(false),
- mDoneWithMoov(false),
- mFirstMoofOffset(0),
- mFinalResult(OK) {
-}
-
-FragmentedMP4Parser::~FragmentedMP4Parser() {
-}
-
-void FragmentedMP4Parser::start(const char *filename) {
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setObject("source", new FileSource(filename));
- msg->post();
- ALOGV("Parser::start(%s)", filename);
-}
-
-void FragmentedMP4Parser::start(const sp<Source> &source) {
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setObject("source", source);
- msg->post();
- ALOGV("Parser::start(Source)");
-}
-
-void FragmentedMP4Parser::start(sp<DataSource> &source) {
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setObject("source", new DataSourceSource(source));
- msg->post();
- ALOGV("Parser::start(DataSource)");
-}
-
-sp<AMessage> FragmentedMP4Parser::getFormat(bool audio, bool synchronous) {
-
- while (true) {
- bool moovDone = mDoneWithMoov;
- sp<AMessage> msg = new AMessage(kWhatGetFormat, id());
- msg->setInt32("audio", audio);
-
- sp<AMessage> response;
- status_t err = msg->postAndAwaitResponse(&response);
-
- if (err != OK) {
- ALOGV("getFormat post failed: %d", err);
- return NULL;
- }
-
- if (response->findInt32("err", &err) && err != OK) {
- if (synchronous && err == -EWOULDBLOCK && !moovDone) {
- resumeIfNecessary();
- ALOGV("@getFormat parser not ready yet, retrying");
- usleep(10000);
- continue;
- }
- ALOGV("getFormat failed: %d", err);
- return NULL;
- }
-
- sp<AMessage> format;
- CHECK(response->findMessage("format", &format));
-
- ALOGV("returning format %s", format->debugString().c_str());
- return format;
- }
-}
-
-status_t FragmentedMP4Parser::seekTo(bool wantAudio, int64_t timeUs) {
- sp<AMessage> msg = new AMessage(kWhatSeekTo, id());
- msg->setInt32("audio", wantAudio);
- msg->setInt64("position", timeUs);
-
- sp<AMessage> response;
- status_t err = msg->postAndAwaitResponse(&response);
- return err;
-}
-
-bool FragmentedMP4Parser::isSeekable() const {
- while (mFirstMoofOffset == 0 && mFinalResult == OK) {
- usleep(10000);
- }
- bool seekable = mSource->isSeekable();
- for (size_t i = 0; seekable && i < mTracks.size(); i++) {
- const TrackInfo *info = &mTracks.valueAt(i);
- seekable &= !info->mSidx.empty();
- }
- return seekable;
-}
-
-status_t FragmentedMP4Parser::onSeekTo(bool wantAudio, int64_t position) {
- status_t err = -EINVAL;
- ssize_t trackIndex = findTrack(wantAudio);
- if (trackIndex < 0) {
- err = trackIndex;
- } else {
- TrackInfo *info = &mTracks.editValueAt(trackIndex);
-
- int numSidxEntries = info->mSidx.size();
- int64_t totalTime = 0;
- off_t totalOffset = mFirstMoofOffset;
- for (int i = 0; i < numSidxEntries; i++) {
- const SidxEntry *se = &info->mSidx[i];
- if (totalTime + se->mDurationUs > position) {
- mBuffer->setRange(0,0);
- mBufferPos = totalOffset;
- if (mFinalResult == ERROR_END_OF_STREAM) {
- mFinalResult = OK;
- mSuspended = true; // force resume
- resumeIfNecessary();
- }
- info->mFragments.clear();
- info->mDecodingTime = totalTime * info->mMediaTimeScale / 1000000ll;
- return OK;
- }
- totalTime += se->mDurationUs;
- totalOffset += se->mSize;
- }
- }
- ALOGV("seekTo out of range");
- return err;
-}
-
-status_t FragmentedMP4Parser::dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit,
- bool synchronous) {
-
- while (true) {
- sp<AMessage> msg = new AMessage(kWhatDequeueAccessUnit, id());
- msg->setInt32("audio", audio);
-
- sp<AMessage> response;
- status_t err = msg->postAndAwaitResponse(&response);
-
- if (err != OK) {
- ALOGV("dequeue fail 1: %d", err);
- return err;
- }
-
- if (response->findInt32("err", &err) && err != OK) {
- if (synchronous && err == -EWOULDBLOCK) {
- resumeIfNecessary();
- ALOGV("Parser not ready yet, retrying");
- usleep(10000);
- continue;
- }
- ALOGV("dequeue fail 2: %d, %d", err, synchronous);
- return err;
- }
-
- CHECK(response->findBuffer("accessUnit", accessUnit));
-
- return OK;
- }
-}
-
-ssize_t FragmentedMP4Parser::findTrack(bool wantAudio) const {
- for (size_t i = 0; i < mTracks.size(); ++i) {
- const TrackInfo *info = &mTracks.valueAt(i);
-
- bool isAudio =
- info->mMediaHandlerType == FOURCC('s', 'o', 'u', 'n');
-
- bool isVideo =
- info->mMediaHandlerType == FOURCC('v', 'i', 'd', 'e');
-
- if ((wantAudio && isAudio) || (!wantAudio && !isAudio)) {
- if (info->mSampleDescs.empty()) {
- break;
- }
-
- return i;
- }
- }
-
- return -EWOULDBLOCK;
-}
-
-void FragmentedMP4Parser::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatStart:
- {
- sp<RefBase> obj;
- CHECK(msg->findObject("source", &obj));
-
- mSource = static_cast<Source *>(obj.get());
-
- mBuffer = new ABuffer(512 * 1024);
- mBuffer->setRange(0, 0);
-
- enter(0ll, 0, 0);
-
- (new AMessage(kWhatProceed, id()))->post();
- break;
- }
-
- case kWhatProceed:
- {
- CHECK(!mSuspended);
-
- status_t err = onProceed();
-
- if (err == OK) {
- if (!mSuspended) {
- msg->post();
- }
- } else if (err != -EAGAIN) {
- ALOGE("onProceed returned error %d", err);
- }
-
- break;
- }
-
- case kWhatReadMore:
- {
- size_t needed;
- CHECK(msg->findSize("needed", &needed));
-
- memmove(mBuffer->base(), mBuffer->data(), mBuffer->size());
- mBufferPos += mBuffer->offset();
- mBuffer->setRange(0, mBuffer->size());
-
- size_t maxBytesToRead = mBuffer->capacity() - mBuffer->size();
-
- if (maxBytesToRead < needed) {
- ALOGV("resizing buffer.");
-
- sp<ABuffer> newBuffer =
- new ABuffer((mBuffer->size() + needed + 1023) & ~1023);
- memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
- newBuffer->setRange(0, mBuffer->size());
-
- mBuffer = newBuffer;
- maxBytesToRead = mBuffer->capacity() - mBuffer->size();
- }
-
- CHECK_GE(maxBytesToRead, needed);
-
- ssize_t n = mSource->readAt(
- mBufferPos + mBuffer->size(),
- mBuffer->data() + mBuffer->size(), needed);
-
- if (n < (ssize_t)needed) {
- ALOGV("Reached EOF when reading %d @ %d + %d", needed, mBufferPos, mBuffer->size());
- if (n < 0) {
- mFinalResult = n;
- } else if (n == 0) {
- mFinalResult = ERROR_END_OF_STREAM;
- } else {
- mFinalResult = ERROR_IO;
- }
- } else {
- mBuffer->setRange(0, mBuffer->size() + n);
- (new AMessage(kWhatProceed, id()))->post();
- }
-
- break;
- }
-
- case kWhatGetFormat:
- {
- int32_t wantAudio;
- CHECK(msg->findInt32("audio", &wantAudio));
-
- status_t err = -EWOULDBLOCK;
- sp<AMessage> response = new AMessage;
-
- ssize_t trackIndex = findTrack(wantAudio);
-
- if (trackIndex < 0) {
- err = trackIndex;
- } else {
- TrackInfo *info = &mTracks.editValueAt(trackIndex);
-
- sp<AMessage> format = info->mSampleDescs.itemAt(0).mFormat;
- if (info->mSidxDuration) {
- format->setInt64("durationUs", info->mSidxDuration);
- } else {
- // this is probably going to be zero. Oh well...
- format->setInt64("durationUs",
- 1000000ll * info->mDuration / info->mMediaTimeScale);
- }
- response->setMessage(
- "format", format);
-
- err = OK;
- }
-
- response->setInt32("err", err);
-
- uint32_t replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
- response->postReply(replyID);
- break;
- }
-
- case kWhatDequeueAccessUnit:
- {
- int32_t wantAudio;
- CHECK(msg->findInt32("audio", &wantAudio));
-
- status_t err = -EWOULDBLOCK;
- sp<AMessage> response = new AMessage;
-
- ssize_t trackIndex = findTrack(wantAudio);
-
- if (trackIndex < 0) {
- err = trackIndex;
- } else {
- sp<ABuffer> accessUnit;
- err = onDequeueAccessUnit(trackIndex, &accessUnit);
-
- if (err == OK) {
- response->setBuffer("accessUnit", accessUnit);
- }
- }
-
- response->setInt32("err", err);
-
- uint32_t replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
- response->postReply(replyID);
- break;
- }
-
- case kWhatSeekTo:
- {
- ALOGV("kWhatSeekTo");
- int32_t wantAudio;
- CHECK(msg->findInt32("audio", &wantAudio));
- int64_t position;
- CHECK(msg->findInt64("position", &position));
-
- status_t err = -EWOULDBLOCK;
- sp<AMessage> response = new AMessage;
-
- ssize_t trackIndex = findTrack(wantAudio);
-
- if (trackIndex < 0) {
- err = trackIndex;
- } else {
- err = onSeekTo(wantAudio, position);
- }
- response->setInt32("err", err);
- uint32_t replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
- break;
- }
- default:
- TRESPASS();
- }
-}
-
-status_t FragmentedMP4Parser::onProceed() {
- status_t err;
-
- if ((err = need(8)) != OK) {
- return err;
- }
-
- uint64_t size = readU32(0);
- uint32_t type = readU32(4);
-
- size_t offset = 8;
-
- if (size == 1) {
- if ((err = need(16)) != OK) {
- return err;
- }
-
- size = readU64(offset);
- offset += 8;
- }
-
- uint8_t userType[16];
-
- if (type == FOURCC('u', 'u', 'i', 'd')) {
- if ((err = need(offset + 16)) != OK) {
- return err;
- }
-
- memcpy(userType, mBuffer->data() + offset, 16);
- offset += 16;
- }
-
- CHECK(!mStack.isEmpty());
- uint32_t ptype = mStack.itemAt(mStack.size() - 1).mType;
-
- static const size_t kNumDispatchers =
- sizeof(kDispatchTable) / sizeof(kDispatchTable[0]);
-
- size_t i;
- for (i = 0; i < kNumDispatchers; ++i) {
- if (kDispatchTable[i].mType == type
- && kDispatchTable[i].mParentType == ptype) {
- break;
- }
- }
-
- // SampleEntry boxes are container boxes that start with a variable
- // amount of data depending on the media handler type.
- // We don't look inside 'hint' type SampleEntry boxes.
-
- bool isSampleEntryBox =
- (ptype == FOURCC('s', 't', 's', 'd'))
- && editTrack(mCurrentTrackID)->mMediaHandlerType
- != FOURCC('h', 'i', 'n', 't');
-
- if ((i < kNumDispatchers && kDispatchTable[i].mHandler == 0)
- || isSampleEntryBox || ptype == FOURCC('i', 'l', 's', 't')) {
- // This is a container box.
- if (type == FOURCC('m', 'o', 'o', 'f')) {
- if (mFirstMoofOffset == 0) {
- ALOGV("first moof @ %08x", mBufferPos + offset);
- mFirstMoofOffset = mBufferPos + offset - 8; // point at the size
- }
- }
- if (type == FOURCC('m', 'e', 't', 'a')) {
- if ((err = need(offset + 4)) < OK) {
- return err;
- }
-
- if (readU32(offset) != 0) {
- return -EINVAL;
- }
-
- offset += 4;
- } else if (type == FOURCC('s', 't', 's', 'd')) {
- if ((err = need(offset + 8)) < OK) {
- return err;
- }
-
- if (readU32(offset) != 0) {
- return -EINVAL;
- }
-
- if (readU32(offset + 4) == 0) {
- // We need at least some entries.
- return -EINVAL;
- }
-
- offset += 8;
- } else if (isSampleEntryBox) {
- size_t headerSize;
-
- switch (editTrack(mCurrentTrackID)->mMediaHandlerType) {
- case FOURCC('v', 'i', 'd', 'e'):
- {
- // 8 bytes SampleEntry + 70 bytes VisualSampleEntry
- headerSize = 78;
- break;
- }
-
- case FOURCC('s', 'o', 'u', 'n'):
- {
- // 8 bytes SampleEntry + 20 bytes AudioSampleEntry
- headerSize = 28;
- break;
- }
-
- case FOURCC('m', 'e', 't', 'a'):
- {
- headerSize = 8; // 8 bytes SampleEntry
- break;
- }
-
- default:
- TRESPASS();
- }
-
- if (offset + headerSize > size) {
- return -EINVAL;
- }
-
- if ((err = need(offset + headerSize)) != OK) {
- return err;
- }
-
- switch (editTrack(mCurrentTrackID)->mMediaHandlerType) {
- case FOURCC('v', 'i', 'd', 'e'):
- {
- err = parseVisualSampleEntry(
- type, offset, offset + headerSize);
- break;
- }
-
- case FOURCC('s', 'o', 'u', 'n'):
- {
- err = parseAudioSampleEntry(
- type, offset, offset + headerSize);
- break;
- }
-
- case FOURCC('m', 'e', 't', 'a'):
- {
- err = OK;
- break;
- }
-
- default:
- TRESPASS();
- }
-
- if (err != OK) {
- return err;
- }
-
- offset += headerSize;
- }
-
- skip(offset);
-
- ALOGV("%sentering box of type '%s'",
- IndentString(mStack.size()), Fourcc2String(type));
-
- enter(mBufferPos - offset, type, size - offset);
- } else {
- if (!fitsContainer(size)) {
- return -EINVAL;
- }
-
- if (i < kNumDispatchers && kDispatchTable[i].mHandler != 0) {
- // We have a handler for this box type.
-
- if ((err = need(size)) != OK) {
- return err;
- }
-
- ALOGV("%sparsing box of type '%s'",
- IndentString(mStack.size()), Fourcc2String(type));
-
- if ((err = (this->*kDispatchTable[i].mHandler)(
- type, offset, size)) != OK) {
- return err;
- }
- } else {
- // Unknown box type
-
- ALOGV("%sskipping box of type '%s', size %llu",
- IndentString(mStack.size()),
- Fourcc2String(type), size);
-
- }
-
- skip(size);
- }
-
- return OK;
-}
-
-// static
-int FragmentedMP4Parser::CompareSampleLocation(
- const SampleInfo &sample, const MediaDataInfo &mdatInfo) {
- if (sample.mOffset + sample.mSize < mdatInfo.mOffset) {
- return -1;
- }
-
- if (sample.mOffset >= mdatInfo.mOffset + mdatInfo.mBuffer->size()) {
- return 1;
- }
-
- // Otherwise make sure the sample is completely contained within this
- // media data block.
-
- CHECK_GE(sample.mOffset, mdatInfo.mOffset);
-
- CHECK_LE(sample.mOffset + sample.mSize,
- mdatInfo.mOffset + mdatInfo.mBuffer->size());
-
- return 0;
-}
-
-void FragmentedMP4Parser::resumeIfNecessary() {
- if (!mSuspended) {
- return;
- }
-
- ALOGV("resuming.");
-
- mSuspended = false;
- (new AMessage(kWhatProceed, id()))->post();
-}
-
-status_t FragmentedMP4Parser::getSample(
- TrackInfo *info, sp<TrackFragment> *fragment, SampleInfo *sampleInfo) {
- for (;;) {
- if (info->mFragments.empty()) {
- if (mFinalResult != OK) {
- return mFinalResult;
- }
-
- resumeIfNecessary();
- return -EWOULDBLOCK;
- }
-
- *fragment = *info->mFragments.begin();
-
- status_t err = (*fragment)->getSample(sampleInfo);
-
- if (err == OK) {
- return OK;
- } else if (err != ERROR_END_OF_STREAM) {
- return err;
- }
-
- // Really, end of this fragment...
-
- info->mFragments.erase(info->mFragments.begin());
- }
-}
-
-status_t FragmentedMP4Parser::onDequeueAccessUnit(
- size_t trackIndex, sp<ABuffer> *accessUnit) {
- TrackInfo *info = &mTracks.editValueAt(trackIndex);
-
- sp<TrackFragment> fragment;
- SampleInfo sampleInfo;
- status_t err = getSample(info, &fragment, &sampleInfo);
-
- if (err == -EWOULDBLOCK) {
- resumeIfNecessary();
- return err;
- } else if (err != OK) {
- return err;
- }
-
- err = -EWOULDBLOCK;
-
- bool checkDroppable = false;
-
- for (size_t i = 0; i < mMediaData.size(); ++i) {
- const MediaDataInfo &mdatInfo = mMediaData.itemAt(i);
-
- int cmp = CompareSampleLocation(sampleInfo, mdatInfo);
-
- if (cmp < 0 && !mSource->isSeekable()) {
- return -EPIPE;
- } else if (cmp == 0) {
- if (i > 0) {
- checkDroppable = true;
- }
-
- err = makeAccessUnit(info, sampleInfo, mdatInfo, accessUnit);
- break;
- }
- }
-
- if (err != OK) {
- return err;
- }
-
- fragment->advance();
-
- if (!mMediaData.empty() && checkDroppable) {
- size_t numDroppable = 0;
- bool done = false;
-
- // XXX FIXME: if one of the tracks is not advanced (e.g. if you play an audio+video
- // file with sf2), then mMediaData will not be pruned and keeps growing
- for (size_t i = 0; !done && i < mMediaData.size(); ++i) {
- const MediaDataInfo &mdatInfo = mMediaData.itemAt(i);
-
- for (size_t j = 0; j < mTracks.size(); ++j) {
- TrackInfo *info = &mTracks.editValueAt(j);
-
- sp<TrackFragment> fragment;
- SampleInfo sampleInfo;
- err = getSample(info, &fragment, &sampleInfo);
-
- if (err != OK) {
- done = true;
- break;
- }
-
- int cmp = CompareSampleLocation(sampleInfo, mdatInfo);
-
- if (cmp <= 0) {
- done = true;
- break;
- }
- }
-
- if (!done) {
- ++numDroppable;
- }
- }
-
- if (numDroppable > 0) {
- mMediaData.removeItemsAt(0, numDroppable);
-
- if (mMediaData.size() < 5) {
- resumeIfNecessary();
- }
- }
- }
-
- return err;
-}
-
-static size_t parseNALSize(size_t nalLengthSize, const uint8_t *data) {
- switch (nalLengthSize) {
- case 1:
- return *data;
- case 2:
- return U16_AT(data);
- case 3:
- return ((size_t)data[0] << 16) | U16_AT(&data[1]);
- case 4:
- return U32_AT(data);
- }
-
- // This cannot happen, mNALLengthSize springs to life by adding 1 to
- // a 2-bit integer.
- TRESPASS();
-
- return 0;
-}
-
-status_t FragmentedMP4Parser::makeAccessUnit(
- TrackInfo *info,
- const SampleInfo &sample,
- const MediaDataInfo &mdatInfo,
- sp<ABuffer> *accessUnit) {
- if (sample.mSampleDescIndex < 1
- || sample.mSampleDescIndex > info->mSampleDescs.size()) {
- return ERROR_MALFORMED;
- }
-
- int64_t presentationTimeUs =
- 1000000ll * sample.mPresentationTime / info->mMediaTimeScale;
-
- const SampleDescription &sampleDesc =
- info->mSampleDescs.itemAt(sample.mSampleDescIndex - 1);
-
- size_t nalLengthSize;
- if (!sampleDesc.mFormat->findSize("nal-length-size", &nalLengthSize)) {
- *accessUnit = new ABuffer(sample.mSize);
-
- memcpy((*accessUnit)->data(),
- mdatInfo.mBuffer->data() + (sample.mOffset - mdatInfo.mOffset),
- sample.mSize);
-
- (*accessUnit)->meta()->setInt64("timeUs", presentationTimeUs);
- if (IsIDR(*accessUnit)) {
- (*accessUnit)->meta()->setInt32("is-sync-frame", 1);
- }
-
- return OK;
- }
-
- const uint8_t *srcPtr =
- mdatInfo.mBuffer->data() + (sample.mOffset - mdatInfo.mOffset);
-
- for (int i = 0; i < 2 ; ++i) {
- size_t srcOffset = 0;
- size_t dstOffset = 0;
-
- while (srcOffset < sample.mSize) {
- if (srcOffset + nalLengthSize > sample.mSize) {
- return ERROR_MALFORMED;
- }
-
- size_t nalSize = parseNALSize(nalLengthSize, &srcPtr[srcOffset]);
- srcOffset += nalLengthSize;
-
- if (srcOffset + nalSize > sample.mSize) {
- return ERROR_MALFORMED;
- }
-
- if (i == 1) {
- memcpy((*accessUnit)->data() + dstOffset,
- "\x00\x00\x00\x01",
- 4);
-
- memcpy((*accessUnit)->data() + dstOffset + 4,
- srcPtr + srcOffset,
- nalSize);
- }
-
- srcOffset += nalSize;
- dstOffset += nalSize + 4;
- }
-
- if (i == 0) {
- (*accessUnit) = new ABuffer(dstOffset);
- (*accessUnit)->meta()->setInt64(
- "timeUs", presentationTimeUs);
- }
- }
- if (IsIDR(*accessUnit)) {
- (*accessUnit)->meta()->setInt32("is-sync-frame", 1);
- }
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::need(size_t size) {
- if (!fitsContainer(size)) {
- return -EINVAL;
- }
-
- if (size <= mBuffer->size()) {
- return OK;
- }
-
- sp<AMessage> msg = new AMessage(kWhatReadMore, id());
- msg->setSize("needed", size - mBuffer->size());
- msg->post();
-
- // ALOGV("need(%d) returning -EAGAIN, only have %d", size, mBuffer->size());
-
- return -EAGAIN;
-}
-
-void FragmentedMP4Parser::enter(off64_t offset, uint32_t type, uint64_t size) {
- Container container;
- container.mOffset = offset;
- container.mType = type;
- container.mExtendsToEOF = (size == 0);
- container.mBytesRemaining = size;
-
- mStack.push(container);
-}
-
-bool FragmentedMP4Parser::fitsContainer(uint64_t size) const {
- CHECK(!mStack.isEmpty());
- const Container &container = mStack.itemAt(mStack.size() - 1);
-
- return container.mExtendsToEOF || size <= container.mBytesRemaining;
-}
-
-uint16_t FragmentedMP4Parser::readU16(size_t offset) {
- CHECK_LE(offset + 2, mBuffer->size());
-
- const uint8_t *ptr = mBuffer->data() + offset;
- return (ptr[0] << 8) | ptr[1];
-}
-
-uint32_t FragmentedMP4Parser::readU32(size_t offset) {
- CHECK_LE(offset + 4, mBuffer->size());
-
- const uint8_t *ptr = mBuffer->data() + offset;
- return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
-}
-
-uint64_t FragmentedMP4Parser::readU64(size_t offset) {
- return (((uint64_t)readU32(offset)) << 32) | readU32(offset + 4);
-}
-
-void FragmentedMP4Parser::skip(off_t distance) {
- CHECK(!mStack.isEmpty());
- for (size_t i = mStack.size(); i-- > 0;) {
- Container *container = &mStack.editItemAt(i);
- if (!container->mExtendsToEOF) {
- CHECK_LE(distance, (off_t)container->mBytesRemaining);
-
- container->mBytesRemaining -= distance;
-
- if (container->mBytesRemaining == 0) {
- ALOGV("%sleaving box of type '%s'",
- IndentString(mStack.size() - 1),
- Fourcc2String(container->mType));
-
-#if 0
- if (container->mType == FOURCC('s', 't', 's', 'd')) {
- TrackInfo *trackInfo = editTrack(mCurrentTrackID);
- for (size_t i = 0;
- i < trackInfo->mSampleDescs.size(); ++i) {
- ALOGI("format #%d: %s",
- i,
- trackInfo->mSampleDescs.itemAt(i)
- .mFormat->debugString().c_str());
- }
- }
-#endif
-
- if (container->mType == FOURCC('s', 't', 'b', 'l')) {
- TrackInfo *trackInfo = editTrack(mCurrentTrackID);
-
- trackInfo->mStaticFragment->signalCompletion();
-
- CHECK(trackInfo->mFragments.empty());
- trackInfo->mFragments.push_back(trackInfo->mStaticFragment);
- trackInfo->mStaticFragment.clear();
- } else if (container->mType == FOURCC('t', 'r', 'a', 'f')) {
- TrackInfo *trackInfo =
- editTrack(mTrackFragmentHeaderInfo.mTrackID);
-
- const sp<TrackFragment> &fragment =
- *--trackInfo->mFragments.end();
-
- static_cast<DynamicTrackFragment *>(
- fragment.get())->signalCompletion();
- } else if (container->mType == FOURCC('m', 'o', 'o', 'v')) {
- mDoneWithMoov = true;
- }
-
- container = NULL;
- mStack.removeItemsAt(i);
- }
- }
- }
-
- if (distance < (off_t)mBuffer->size()) {
- mBuffer->setRange(mBuffer->offset() + distance, mBuffer->size() - distance);
- mBufferPos += distance;
- return;
- }
-
- mBuffer->setRange(0, 0);
- mBufferPos += distance;
-}
-
-status_t FragmentedMP4Parser::parseTrackHeader(
- uint32_t type, size_t offset, uint64_t size) {
- if (offset + 4 > size) {
- return -EINVAL;
- }
-
- uint32_t flags = readU32(offset);
-
- uint32_t version = flags >> 24;
- flags &= 0xffffff;
-
- uint32_t trackID;
- uint64_t duration;
-
- if (version == 1) {
- if (offset + 36 > size) {
- return -EINVAL;
- }
-
- trackID = readU32(offset + 20);
- duration = readU64(offset + 28);
-
- offset += 36;
- } else if (version == 0) {
- if (offset + 24 > size) {
- return -EINVAL;
- }
-
- trackID = readU32(offset + 12);
- duration = readU32(offset + 20);
-
- offset += 24;
- } else {
- return -EINVAL;
- }
-
- TrackInfo *info = editTrack(trackID, true /* createIfNecessary */);
- info->mFlags = flags;
- info->mDuration = duration;
- if (info->mDuration == 0xffffffff) {
- // ffmpeg sets this to -1, which is incorrect.
- info->mDuration = 0;
- }
-
- info->mStaticFragment = new StaticTrackFragment;
-
- mCurrentTrackID = trackID;
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::parseMediaHeader(
- uint32_t type, size_t offset, uint64_t size) {
- if (offset + 4 > size) {
- return -EINVAL;
- }
-
- uint32_t versionAndFlags = readU32(offset);
-
- if (versionAndFlags & 0xffffff) {
- return ERROR_MALFORMED;
- }
-
- uint32_t version = versionAndFlags >> 24;
-
- TrackInfo *info = editTrack(mCurrentTrackID);
-
- if (version == 1) {
- if (offset + 4 + 32 > size) {
- return -EINVAL;
- }
- info->mMediaTimeScale = U32_AT(mBuffer->data() + offset + 20);
- } else if (version == 0) {
- if (offset + 4 + 20 > size) {
- return -EINVAL;
- }
- info->mMediaTimeScale = U32_AT(mBuffer->data() + offset + 12);
- } else {
- return ERROR_MALFORMED;
- }
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::parseMediaHandler(
- uint32_t type, size_t offset, uint64_t size) {
- if (offset + 12 > size) {
- return -EINVAL;
- }
-
- if (readU32(offset) != 0) {
- return -EINVAL;
- }
-
- uint32_t handlerType = readU32(offset + 8);
-
- switch (handlerType) {
- case FOURCC('v', 'i', 'd', 'e'):
- case FOURCC('s', 'o', 'u', 'n'):
- case FOURCC('h', 'i', 'n', 't'):
- case FOURCC('m', 'e', 't', 'a'):
- break;
-
- default:
- return -EINVAL;
- }
-
- editTrack(mCurrentTrackID)->mMediaHandlerType = handlerType;
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::parseVisualSampleEntry(
- uint32_t type, size_t offset, uint64_t size) {
- if (offset + 78 > size) {
- return -EINVAL;
- }
-
- TrackInfo *trackInfo = editTrack(mCurrentTrackID);
-
- trackInfo->mSampleDescs.push();
- SampleDescription *sampleDesc =
- &trackInfo->mSampleDescs.editItemAt(
- trackInfo->mSampleDescs.size() - 1);
-
- sampleDesc->mType = type;
- sampleDesc->mDataRefIndex = readU16(offset + 6);
-
- sp<AMessage> format = new AMessage;
-
- switch (type) {
- case FOURCC('a', 'v', 'c', '1'):
- format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
- break;
- case FOURCC('m', 'p', '4', 'v'):
- format->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4);
- break;
- case FOURCC('s', '2', '6', '3'):
- case FOURCC('h', '2', '6', '3'):
- case FOURCC('H', '2', '6', '3'):
- format->setString("mime", MEDIA_MIMETYPE_VIDEO_H263);
- break;
- default:
- format->setString("mime", "application/octet-stream");
- break;
- }
-
- format->setInt32("width", readU16(offset + 8 + 16));
- format->setInt32("height", readU16(offset + 8 + 18));
-
- sampleDesc->mFormat = format;
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::parseAudioSampleEntry(
- uint32_t type, size_t offset, uint64_t size) {
- if (offset + 28 > size) {
- return -EINVAL;
- }
-
- TrackInfo *trackInfo = editTrack(mCurrentTrackID);
-
- trackInfo->mSampleDescs.push();
- SampleDescription *sampleDesc =
- &trackInfo->mSampleDescs.editItemAt(
- trackInfo->mSampleDescs.size() - 1);
-
- sampleDesc->mType = type;
- sampleDesc->mDataRefIndex = readU16(offset + 6);
-
- sp<AMessage> format = new AMessage;
-
- format->setInt32("channel-count", readU16(offset + 8 + 8));
- format->setInt32("sample-size", readU16(offset + 8 + 10));
- format->setInt32("sample-rate", readU32(offset + 8 + 16) / 65536.0f);
-
- switch (type) {
- case FOURCC('m', 'p', '4', 'a'):
- format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
- break;
-
- case FOURCC('s', 'a', 'm', 'r'):
- format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
- format->setInt32("channel-count", 1);
- format->setInt32("sample-rate", 8000);
- break;
-
- case FOURCC('s', 'a', 'w', 'b'):
- format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_WB);
- format->setInt32("channel-count", 1);
- format->setInt32("sample-rate", 16000);
- break;
- default:
- format->setString("mime", "application/octet-stream");
- break;
- }
-
- sampleDesc->mFormat = format;
-
- return OK;
-}
-
-static void addCodecSpecificData(
- const sp<AMessage> &format, int32_t index,
- const void *data, size_t size,
- bool insertStartCode = false) {
- sp<ABuffer> csd = new ABuffer(insertStartCode ? size + 4 : size);
-
- memcpy(csd->data() + (insertStartCode ? 4 : 0), data, size);
-
- if (insertStartCode) {
- memcpy(csd->data(), "\x00\x00\x00\x01", 4);
- }
-
- csd->meta()->setInt32("csd", true);
- csd->meta()->setInt64("timeUs", 0ll);
-
- format->setBuffer(StringPrintf("csd-%d", index).c_str(), csd);
-}
-
-status_t FragmentedMP4Parser::parseSampleSizes(
- uint32_t type, size_t offset, uint64_t size) {
- return editTrack(mCurrentTrackID)->mStaticFragment->parseSampleSizes(
- this, type, offset, size);
-}
-
-status_t FragmentedMP4Parser::parseCompactSampleSizes(
- uint32_t type, size_t offset, uint64_t size) {
- return editTrack(mCurrentTrackID)->mStaticFragment->parseCompactSampleSizes(
- this, type, offset, size);
-}
-
-status_t FragmentedMP4Parser::parseSampleToChunk(
- uint32_t type, size_t offset, uint64_t size) {
- return editTrack(mCurrentTrackID)->mStaticFragment->parseSampleToChunk(
- this, type, offset, size);
-}
-
-status_t FragmentedMP4Parser::parseChunkOffsets(
- uint32_t type, size_t offset, uint64_t size) {
- return editTrack(mCurrentTrackID)->mStaticFragment->parseChunkOffsets(
- this, type, offset, size);
-}
-
-status_t FragmentedMP4Parser::parseChunkOffsets64(
- uint32_t type, size_t offset, uint64_t size) {
- return editTrack(mCurrentTrackID)->mStaticFragment->parseChunkOffsets64(
- this, type, offset, size);
-}
-
-status_t FragmentedMP4Parser::parseAVCCodecSpecificData(
- uint32_t type, size_t offset, uint64_t size) {
- TrackInfo *trackInfo = editTrack(mCurrentTrackID);
-
- SampleDescription *sampleDesc =
- &trackInfo->mSampleDescs.editItemAt(
- trackInfo->mSampleDescs.size() - 1);
-
- if (sampleDesc->mType != FOURCC('a', 'v', 'c', '1')) {
- return -EINVAL;
- }
-
- const uint8_t *ptr = mBuffer->data() + offset;
-
- size -= offset;
- offset = 0;
-
- if (size < 7 || ptr[0] != 0x01) {
- return ERROR_MALFORMED;
- }
-
- sampleDesc->mFormat->setSize("nal-length-size", 1 + (ptr[4] & 3));
-
- size_t numSPS = ptr[5] & 31;
-
- ptr += 6;
- size -= 6;
-
- for (size_t i = 0; i < numSPS; ++i) {
- if (size < 2) {
- return ERROR_MALFORMED;
- }
-
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- if (size < length) {
- return ERROR_MALFORMED;
- }
-
- addCodecSpecificData(
- sampleDesc->mFormat, i, ptr, length,
- true /* insertStartCode */);
-
- ptr += length;
- size -= length;
- }
-
- if (size < 1) {
- return ERROR_MALFORMED;
- }
-
- size_t numPPS = *ptr;
- ++ptr;
- --size;
-
- for (size_t i = 0; i < numPPS; ++i) {
- if (size < 2) {
- return ERROR_MALFORMED;
- }
-
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- if (size < length) {
- return ERROR_MALFORMED;
- }
-
- addCodecSpecificData(
- sampleDesc->mFormat, numSPS + i, ptr, length,
- true /* insertStartCode */);
-
- ptr += length;
- size -= length;
- }
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::parseESDSCodecSpecificData(
- uint32_t type, size_t offset, uint64_t size) {
- TrackInfo *trackInfo = editTrack(mCurrentTrackID);
-
- SampleDescription *sampleDesc =
- &trackInfo->mSampleDescs.editItemAt(
- trackInfo->mSampleDescs.size() - 1);
-
- if (sampleDesc->mType != FOURCC('m', 'p', '4', 'a')
- && sampleDesc->mType != FOURCC('m', 'p', '4', 'v')) {
- return -EINVAL;
- }
-
- const uint8_t *ptr = mBuffer->data() + offset;
-
- size -= offset;
- offset = 0;
-
- if (size < 4) {
- return -EINVAL;
- }
-
- if (U32_AT(ptr) != 0) {
- return -EINVAL;
- }
-
- ptr += 4;
- size -=4;
-
- ESDS esds(ptr, size);
-
- uint8_t objectTypeIndication;
- if (esds.getObjectTypeIndication(&objectTypeIndication) != OK) {
- return ERROR_MALFORMED;
- }
-
- const uint8_t *csd;
- size_t csd_size;
- if (esds.getCodecSpecificInfo(
- (const void **)&csd, &csd_size) != OK) {
- return ERROR_MALFORMED;
- }
-
- addCodecSpecificData(sampleDesc->mFormat, 0, csd, csd_size);
-
- if (sampleDesc->mType != FOURCC('m', 'p', '4', 'a')) {
- return OK;
- }
-
- if (csd_size == 0) {
- // There's no further information, i.e. no codec specific data
- // Let's assume that the information provided in the mpeg4 headers
- // is accurate and hope for the best.
-
- return OK;
- }
-
- if (csd_size < 2) {
- return ERROR_MALFORMED;
- }
-
- uint32_t objectType = csd[0] >> 3;
-
- if (objectType == 31) {
- return ERROR_UNSUPPORTED;
- }
-
- uint32_t freqIndex = (csd[0] & 7) << 1 | (csd[1] >> 7);
- int32_t sampleRate = 0;
- int32_t numChannels = 0;
- if (freqIndex == 15) {
- if (csd_size < 5) {
- return ERROR_MALFORMED;
- }
-
- sampleRate = (csd[1] & 0x7f) << 17
- | csd[2] << 9
- | csd[3] << 1
- | (csd[4] >> 7);
-
- numChannels = (csd[4] >> 3) & 15;
- } else {
- static uint32_t kSamplingRate[] = {
- 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
- 16000, 12000, 11025, 8000, 7350
- };
-
- if (freqIndex == 13 || freqIndex == 14) {
- return ERROR_MALFORMED;
- }
-
- sampleRate = kSamplingRate[freqIndex];
- numChannels = (csd[1] >> 3) & 15;
- }
-
- if (numChannels == 0) {
- return ERROR_UNSUPPORTED;
- }
-
- sampleDesc->mFormat->setInt32("sample-rate", sampleRate);
- sampleDesc->mFormat->setInt32("channel-count", numChannels);
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::parseMediaData(
- uint32_t type, size_t offset, uint64_t size) {
- ALOGV("skipping 'mdat' chunk at offsets 0x%08lx-0x%08llx.",
- mBufferPos + offset, mBufferPos + size);
-
- sp<ABuffer> buffer = new ABuffer(size - offset);
- memcpy(buffer->data(), mBuffer->data() + offset, size - offset);
-
- mMediaData.push();
- MediaDataInfo *info = &mMediaData.editItemAt(mMediaData.size() - 1);
- info->mBuffer = buffer;
- info->mOffset = mBufferPos + offset;
-
- if (mMediaData.size() > 10) {
- ALOGV("suspending for now.");
- mSuspended = true;
- }
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::parseSegmentIndex(
- uint32_t type, size_t offset, uint64_t size) {
- ALOGV("sidx box type %d, offset %d, size %d", type, int(offset), int(size));
-// AString sidxstr;
-// hexdump(mBuffer->data() + offset, size, 0 /* indent */, &sidxstr);
-// ALOGV("raw sidx:");
-// ALOGV("%s", sidxstr.c_str());
- if (offset + 12 > size) {
- return -EINVAL;
- }
-
- uint32_t flags = readU32(offset);
-
- uint32_t version = flags >> 24;
- flags &= 0xffffff;
-
- ALOGV("sidx version %d", version);
-
- uint32_t referenceId = readU32(offset + 4);
- uint32_t timeScale = readU32(offset + 8);
- ALOGV("sidx refid/timescale: %d/%d", referenceId, timeScale);
-
- uint64_t earliestPresentationTime;
- uint64_t firstOffset;
-
- offset += 12;
-
- if (version == 0) {
- if (offset + 8 > size) {
- return -EINVAL;
- }
- earliestPresentationTime = readU32(offset);
- firstOffset = readU32(offset + 4);
- offset += 8;
- } else {
- if (offset + 16 > size) {
- return -EINVAL;
- }
- earliestPresentationTime = readU64(offset);
- firstOffset = readU64(offset + 8);
- offset += 16;
- }
- ALOGV("sidx pres/off: %Ld/%Ld", earliestPresentationTime, firstOffset);
-
- if (offset + 4 > size) {
- return -EINVAL;
- }
- if (readU16(offset) != 0) { // reserved
- return -EINVAL;
- }
- int32_t referenceCount = readU16(offset + 2);
- offset += 4;
- ALOGV("refcount: %d", referenceCount);
-
- if (offset + referenceCount * 12 > size) {
- return -EINVAL;
- }
-
- TrackInfo *info = editTrack(mCurrentTrackID);
- uint64_t total_duration = 0;
- for (int i = 0; i < referenceCount; i++) {
- uint32_t d1 = readU32(offset);
- uint32_t d2 = readU32(offset + 4);
- uint32_t d3 = readU32(offset + 8);
-
- if (d1 & 0x80000000) {
- ALOGW("sub-sidx boxes not supported yet");
- }
- bool sap = d3 & 0x80000000;
- bool saptype = d3 >> 28;
- if (!sap || saptype > 2) {
- ALOGW("not a stream access point, or unsupported type");
- }
- total_duration += d2;
- offset += 12;
- ALOGV(" item %d, %08x %08x %08x", i, d1, d2, d3);
- SidxEntry se;
- se.mSize = d1 & 0x7fffffff;
- se.mDurationUs = 1000000LL * d2 / timeScale;
- info->mSidx.add(se);
- }
-
- info->mSidxDuration = total_duration * 1000000 / timeScale;
- ALOGV("duration: %lld", info->mSidxDuration);
- return OK;
-}
-
-status_t FragmentedMP4Parser::parseTrackExtends(
- uint32_t type, size_t offset, uint64_t size) {
- if (offset + 24 > size) {
- return -EINVAL;
- }
-
- if (readU32(offset) != 0) {
- return -EINVAL;
- }
-
- uint32_t trackID = readU32(offset + 4);
-
- TrackInfo *info = editTrack(trackID, true /* createIfNecessary */);
- info->mDefaultSampleDescriptionIndex = readU32(offset + 8);
- info->mDefaultSampleDuration = readU32(offset + 12);
- info->mDefaultSampleSize = readU32(offset + 16);
- info->mDefaultSampleFlags = readU32(offset + 20);
-
- return OK;
-}
-
-FragmentedMP4Parser::TrackInfo *FragmentedMP4Parser::editTrack(
- uint32_t trackID, bool createIfNecessary) {
- ssize_t i = mTracks.indexOfKey(trackID);
-
- if (i >= 0) {
- return &mTracks.editValueAt(i);
- }
-
- if (!createIfNecessary) {
- return NULL;
- }
-
- TrackInfo info;
- info.mTrackID = trackID;
- info.mFlags = 0;
- info.mDuration = 0xffffffff;
- info.mSidxDuration = 0;
- info.mMediaTimeScale = 0;
- info.mMediaHandlerType = 0;
- info.mDefaultSampleDescriptionIndex = 0;
- info.mDefaultSampleDuration = 0;
- info.mDefaultSampleSize = 0;
- info.mDefaultSampleFlags = 0;
-
- info.mDecodingTime = 0;
-
- mTracks.add(trackID, info);
- return &mTracks.editValueAt(mTracks.indexOfKey(trackID));
-}
-
-status_t FragmentedMP4Parser::parseTrackFragmentHeader(
- uint32_t type, size_t offset, uint64_t size) {
- if (offset + 8 > size) {
- return -EINVAL;
- }
-
- uint32_t flags = readU32(offset);
-
- if (flags & 0xff000000) {
- return -EINVAL;
- }
-
- mTrackFragmentHeaderInfo.mFlags = flags;
-
- mTrackFragmentHeaderInfo.mTrackID = readU32(offset + 4);
- offset += 8;
-
- if (flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent) {
- if (offset + 8 > size) {
- return -EINVAL;
- }
-
- mTrackFragmentHeaderInfo.mBaseDataOffset = readU64(offset);
- offset += 8;
- }
-
- if (flags & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent) {
- if (offset + 4 > size) {
- return -EINVAL;
- }
-
- mTrackFragmentHeaderInfo.mSampleDescriptionIndex = readU32(offset);
- offset += 4;
- }
-
- if (flags & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) {
- if (offset + 4 > size) {
- return -EINVAL;
- }
-
- mTrackFragmentHeaderInfo.mDefaultSampleDuration = readU32(offset);
- offset += 4;
- }
-
- if (flags & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
- if (offset + 4 > size) {
- return -EINVAL;
- }
-
- mTrackFragmentHeaderInfo.mDefaultSampleSize = readU32(offset);
- offset += 4;
- }
-
- if (flags & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
- if (offset + 4 > size) {
- return -EINVAL;
- }
-
- mTrackFragmentHeaderInfo.mDefaultSampleFlags = readU32(offset);
- offset += 4;
- }
-
- if (!(flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent)) {
- // This should point to the position of the first byte of the
- // enclosing 'moof' container for the first track and
- // the end of the data of the preceding fragment for subsequent
- // tracks.
-
- CHECK_GE(mStack.size(), 2u);
-
- mTrackFragmentHeaderInfo.mBaseDataOffset =
- mStack.itemAt(mStack.size() - 2).mOffset;
-
- // XXX TODO: This does not do the right thing for the 2nd and
- // subsequent tracks yet.
- }
-
- mTrackFragmentHeaderInfo.mDataOffset =
- mTrackFragmentHeaderInfo.mBaseDataOffset;
-
- TrackInfo *trackInfo = editTrack(mTrackFragmentHeaderInfo.mTrackID);
-
- if (trackInfo->mFragments.empty()
- || (*trackInfo->mFragments.begin())->complete()) {
- trackInfo->mFragments.push_back(new DynamicTrackFragment);
- }
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::parseTrackFragmentRun(
- uint32_t type, size_t offset, uint64_t size) {
- if (offset + 8 > size) {
- return -EINVAL;
- }
-
- enum {
- kDataOffsetPresent = 0x01,
- kFirstSampleFlagsPresent = 0x04,
- kSampleDurationPresent = 0x100,
- kSampleSizePresent = 0x200,
- kSampleFlagsPresent = 0x400,
- kSampleCompositionTimeOffsetPresent = 0x800,
- };
-
- uint32_t flags = readU32(offset);
-
- if (flags & 0xff000000) {
- return -EINVAL;
- }
-
- if ((flags & kFirstSampleFlagsPresent) && (flags & kSampleFlagsPresent)) {
- // These two shall not be used together.
- return -EINVAL;
- }
-
- uint32_t sampleCount = readU32(offset + 4);
- offset += 8;
-
- uint64_t dataOffset = mTrackFragmentHeaderInfo.mDataOffset;
-
- uint32_t firstSampleFlags = 0;
-
- if (flags & kDataOffsetPresent) {
- if (offset + 4 > size) {
- return -EINVAL;
- }
-
- int32_t dataOffsetDelta = (int32_t)readU32(offset);
-
- dataOffset = mTrackFragmentHeaderInfo.mBaseDataOffset + dataOffsetDelta;
-
- offset += 4;
- }
-
- if (flags & kFirstSampleFlagsPresent) {
- if (offset + 4 > size) {
- return -EINVAL;
- }
-
- firstSampleFlags = readU32(offset);
- offset += 4;
- }
-
- TrackInfo *info = editTrack(mTrackFragmentHeaderInfo.mTrackID);
-
- if (info == NULL) {
- return -EINVAL;
- }
-
- uint32_t sampleDuration = 0, sampleSize = 0, sampleFlags = 0,
- sampleCtsOffset = 0;
-
- size_t bytesPerSample = 0;
- if (flags & kSampleDurationPresent) {
- bytesPerSample += 4;
- } else if (mTrackFragmentHeaderInfo.mFlags
- & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) {
- sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration;
- } else {
- sampleDuration = info->mDefaultSampleDuration;
- }
-
- if (flags & kSampleSizePresent) {
- bytesPerSample += 4;
- } else if (mTrackFragmentHeaderInfo.mFlags
- & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
- sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize;
- } else {
- sampleSize = info->mDefaultSampleSize;
- }
-
- if (flags & kSampleFlagsPresent) {
- bytesPerSample += 4;
- } else if (mTrackFragmentHeaderInfo.mFlags
- & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
- sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags;
- } else {
- sampleFlags = info->mDefaultSampleFlags;
- }
-
- if (flags & kSampleCompositionTimeOffsetPresent) {
- bytesPerSample += 4;
- } else {
- sampleCtsOffset = 0;
- }
-
- if (offset + sampleCount * bytesPerSample > size) {
- return -EINVAL;
- }
-
- uint32_t sampleDescIndex =
- (mTrackFragmentHeaderInfo.mFlags
- & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent)
- ? mTrackFragmentHeaderInfo.mSampleDescriptionIndex
- : info->mDefaultSampleDescriptionIndex;
-
- for (uint32_t i = 0; i < sampleCount; ++i) {
- if (flags & kSampleDurationPresent) {
- sampleDuration = readU32(offset);
- offset += 4;
- }
-
- if (flags & kSampleSizePresent) {
- sampleSize = readU32(offset);
- offset += 4;
- }
-
- if (flags & kSampleFlagsPresent) {
- sampleFlags = readU32(offset);
- offset += 4;
- }
-
- if (flags & kSampleCompositionTimeOffsetPresent) {
- sampleCtsOffset = readU32(offset);
- offset += 4;
- }
-
- ALOGV("adding sample at offset 0x%08llx, size %u, duration %u, "
- "sampleDescIndex=%u, flags 0x%08x",
- dataOffset, sampleSize, sampleDuration,
- sampleDescIndex,
- (flags & kFirstSampleFlagsPresent) && i == 0
- ? firstSampleFlags : sampleFlags);
-
- const sp<TrackFragment> &fragment = *--info->mFragments.end();
-
- uint32_t decodingTime = info->mDecodingTime;
- info->mDecodingTime += sampleDuration;
- uint32_t presentationTime = decodingTime + sampleCtsOffset;
-
- static_cast<DynamicTrackFragment *>(
- fragment.get())->addSample(
- dataOffset,
- sampleSize,
- presentationTime,
- sampleDescIndex,
- ((flags & kFirstSampleFlagsPresent) && i == 0)
- ? firstSampleFlags : sampleFlags);
-
- dataOffset += sampleSize;
- }
-
- mTrackFragmentHeaderInfo.mDataOffset = dataOffset;
-
- return OK;
-}
-
-void FragmentedMP4Parser::copyBuffer(
- sp<ABuffer> *dst, size_t offset, uint64_t size) const {
- sp<ABuffer> buf = new ABuffer(size);
- memcpy(buf->data(), mBuffer->data() + offset, size);
-
- *dst = buf;
-}
-
-} // namespace android
diff --git a/media/libstagefright/mp4/TrackFragment.cpp b/media/libstagefright/mp4/TrackFragment.cpp
deleted file mode 100644
index 3699038..0000000
--- a/media/libstagefright/mp4/TrackFragment.cpp
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "TrackFragment"
-#include <utils/Log.h>
-
-#include "TrackFragment.h"
-
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/hexdump.h>
-
-namespace android {
-
-FragmentedMP4Parser::DynamicTrackFragment::DynamicTrackFragment()
- : mComplete(false),
- mSampleIndex(0) {
-}
-
-FragmentedMP4Parser::DynamicTrackFragment::~DynamicTrackFragment() {
-}
-
-status_t FragmentedMP4Parser::DynamicTrackFragment::getSample(SampleInfo *info) {
- if (mSampleIndex >= mSamples.size()) {
- return mComplete ? ERROR_END_OF_STREAM : -EWOULDBLOCK;
- }
-
- *info = mSamples.itemAt(mSampleIndex);
-
- return OK;
-}
-
-void FragmentedMP4Parser::DynamicTrackFragment::advance() {
- ++mSampleIndex;
-}
-
-void FragmentedMP4Parser::DynamicTrackFragment::addSample(
- off64_t dataOffset, size_t sampleSize,
- uint32_t presentationTime,
- size_t sampleDescIndex,
- uint32_t flags) {
- mSamples.push();
- SampleInfo *sampleInfo = &mSamples.editItemAt(mSamples.size() - 1);
-
- sampleInfo->mOffset = dataOffset;
- sampleInfo->mSize = sampleSize;
- sampleInfo->mPresentationTime = presentationTime;
- sampleInfo->mSampleDescIndex = sampleDescIndex;
- sampleInfo->mFlags = flags;
-}
-
-status_t FragmentedMP4Parser::DynamicTrackFragment::signalCompletion() {
- mComplete = true;
-
- return OK;
-}
-
-bool FragmentedMP4Parser::DynamicTrackFragment::complete() const {
- return mComplete;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-FragmentedMP4Parser::StaticTrackFragment::StaticTrackFragment()
- : mSampleIndex(0),
- mSampleCount(0),
- mChunkIndex(0),
- mSampleToChunkIndex(-1),
- mSampleToChunkRemaining(0),
- mPrevChunkIndex(0xffffffff),
- mNextSampleOffset(0) {
-}
-
-FragmentedMP4Parser::StaticTrackFragment::~StaticTrackFragment() {
-}
-
-status_t FragmentedMP4Parser::StaticTrackFragment::getSample(SampleInfo *info) {
- if (mSampleIndex >= mSampleCount) {
- return ERROR_END_OF_STREAM;
- }
-
- *info = mSampleInfo;
-
- ALOGV("returning sample %d at [0x%08llx, 0x%08llx)",
- mSampleIndex,
- info->mOffset, info->mOffset + info->mSize);
-
- return OK;
-}
-
-void FragmentedMP4Parser::StaticTrackFragment::updateSampleInfo() {
- if (mSampleIndex >= mSampleCount) {
- return;
- }
-
- if (mSampleSizes != NULL) {
- uint32_t defaultSampleSize = U32_AT(mSampleSizes->data() + 4);
- if (defaultSampleSize > 0) {
- mSampleInfo.mSize = defaultSampleSize;
- } else {
- mSampleInfo.mSize= U32_AT(mSampleSizes->data() + 12 + 4 * mSampleIndex);
- }
- } else {
- CHECK(mCompactSampleSizes != NULL);
-
- uint32_t fieldSize = U32_AT(mCompactSampleSizes->data() + 4);
-
- switch (fieldSize) {
- case 4:
- {
- unsigned byte = mCompactSampleSizes->data()[12 + mSampleIndex / 2];
- mSampleInfo.mSize = (mSampleIndex & 1) ? byte & 0x0f : byte >> 4;
- break;
- }
-
- case 8:
- {
- mSampleInfo.mSize = mCompactSampleSizes->data()[12 + mSampleIndex];
- break;
- }
-
- default:
- {
- CHECK_EQ(fieldSize, 16);
- mSampleInfo.mSize =
- U16_AT(mCompactSampleSizes->data() + 12 + mSampleIndex * 2);
- break;
- }
- }
- }
-
- CHECK_GT(mSampleToChunkRemaining, 0);
-
- // The sample desc index is 1-based... XXX
- mSampleInfo.mSampleDescIndex =
- U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 8);
-
- if (mChunkIndex != mPrevChunkIndex) {
- mPrevChunkIndex = mChunkIndex;
-
- if (mChunkOffsets != NULL) {
- uint32_t entryCount = U32_AT(mChunkOffsets->data() + 4);
-
- if (mChunkIndex >= entryCount) {
- mSampleIndex = mSampleCount;
- return;
- }
-
- mNextSampleOffset =
- U32_AT(mChunkOffsets->data() + 8 + 4 * mChunkIndex);
- } else {
- CHECK(mChunkOffsets64 != NULL);
-
- uint32_t entryCount = U32_AT(mChunkOffsets64->data() + 4);
-
- if (mChunkIndex >= entryCount) {
- mSampleIndex = mSampleCount;
- return;
- }
-
- mNextSampleOffset =
- U64_AT(mChunkOffsets64->data() + 8 + 8 * mChunkIndex);
- }
- }
-
- mSampleInfo.mOffset = mNextSampleOffset;
-
- mSampleInfo.mPresentationTime = 0;
- mSampleInfo.mFlags = 0;
-}
-
-void FragmentedMP4Parser::StaticTrackFragment::advance() {
- mNextSampleOffset += mSampleInfo.mSize;
-
- ++mSampleIndex;
- if (--mSampleToChunkRemaining == 0) {
- ++mChunkIndex;
-
- uint32_t entryCount = U32_AT(mSampleToChunk->data() + 4);
-
- // If this is the last entry in the sample to chunk table, we will
- // stay on this entry.
- if ((uint32_t)(mSampleToChunkIndex + 1) < entryCount) {
- uint32_t nextChunkIndex =
- U32_AT(mSampleToChunk->data() + 8 + 12 * (mSampleToChunkIndex + 1));
-
- CHECK_GE(nextChunkIndex, 1u);
- --nextChunkIndex;
-
- if (mChunkIndex >= nextChunkIndex) {
- CHECK_EQ(mChunkIndex, nextChunkIndex);
- ++mSampleToChunkIndex;
- }
- }
-
- mSampleToChunkRemaining =
- U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 4);
- }
-
- updateSampleInfo();
-}
-
-static void setU32At(uint8_t *ptr, uint32_t x) {
- ptr[0] = x >> 24;
- ptr[1] = (x >> 16) & 0xff;
- ptr[2] = (x >> 8) & 0xff;
- ptr[3] = x & 0xff;
-}
-
-status_t FragmentedMP4Parser::StaticTrackFragment::signalCompletion() {
- mSampleToChunkIndex = 0;
-
- mSampleToChunkRemaining =
- (mSampleToChunk == NULL)
- ? 0
- : U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 4);
-
- updateSampleInfo();
-
- return OK;
-}
-
-bool FragmentedMP4Parser::StaticTrackFragment::complete() const {
- return true;
-}
-
-status_t FragmentedMP4Parser::StaticTrackFragment::parseSampleSizes(
- FragmentedMP4Parser *parser, uint32_t type, size_t offset, uint64_t size) {
- if (offset + 12 > size) {
- return ERROR_MALFORMED;
- }
-
- if (parser->readU32(offset) != 0) {
- return ERROR_MALFORMED;
- }
-
- uint32_t sampleSize = parser->readU32(offset + 4);
- uint32_t sampleCount = parser->readU32(offset + 8);
-
- if (sampleSize == 0 && offset + 12 + sampleCount * 4 != size) {
- return ERROR_MALFORMED;
- }
-
- parser->copyBuffer(&mSampleSizes, offset, size);
-
- mSampleCount = sampleCount;
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::StaticTrackFragment::parseCompactSampleSizes(
- FragmentedMP4Parser *parser, uint32_t type, size_t offset, uint64_t size) {
- if (offset + 12 > size) {
- return ERROR_MALFORMED;
- }
-
- if (parser->readU32(offset) != 0) {
- return ERROR_MALFORMED;
- }
-
- uint32_t fieldSize = parser->readU32(offset + 4);
-
- if (fieldSize != 4 && fieldSize != 8 && fieldSize != 16) {
- return ERROR_MALFORMED;
- }
-
- uint32_t sampleCount = parser->readU32(offset + 8);
-
- if (offset + 12 + (sampleCount * fieldSize + 4) / 8 != size) {
- return ERROR_MALFORMED;
- }
-
- parser->copyBuffer(&mCompactSampleSizes, offset, size);
-
- mSampleCount = sampleCount;
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::StaticTrackFragment::parseSampleToChunk(
- FragmentedMP4Parser *parser, uint32_t type, size_t offset, uint64_t size) {
- if (offset + 8 > size) {
- return ERROR_MALFORMED;
- }
-
- if (parser->readU32(offset) != 0) {
- return ERROR_MALFORMED;
- }
-
- uint32_t entryCount = parser->readU32(offset + 4);
-
- if (entryCount == 0) {
- return OK;
- }
-
- if (offset + 8 + entryCount * 12 != size) {
- return ERROR_MALFORMED;
- }
-
- parser->copyBuffer(&mSampleToChunk, offset, size);
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::StaticTrackFragment::parseChunkOffsets(
- FragmentedMP4Parser *parser, uint32_t type, size_t offset, uint64_t size) {
- if (offset + 8 > size) {
- return ERROR_MALFORMED;
- }
-
- if (parser->readU32(offset) != 0) {
- return ERROR_MALFORMED;
- }
-
- uint32_t entryCount = parser->readU32(offset + 4);
-
- if (offset + 8 + entryCount * 4 != size) {
- return ERROR_MALFORMED;
- }
-
- parser->copyBuffer(&mChunkOffsets, offset, size);
-
- return OK;
-}
-
-status_t FragmentedMP4Parser::StaticTrackFragment::parseChunkOffsets64(
- FragmentedMP4Parser *parser, uint32_t type, size_t offset, uint64_t size) {
- if (offset + 8 > size) {
- return ERROR_MALFORMED;
- }
-
- if (parser->readU32(offset) != 0) {
- return ERROR_MALFORMED;
- }
-
- uint32_t entryCount = parser->readU32(offset + 4);
-
- if (offset + 8 + entryCount * 8 != size) {
- return ERROR_MALFORMED;
- }
-
- parser->copyBuffer(&mChunkOffsets64, offset, size);
-
- return OK;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/mp4/TrackFragment.h b/media/libstagefright/mp4/TrackFragment.h
deleted file mode 100644
index e1ad46e..0000000
--- a/media/libstagefright/mp4/TrackFragment.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TRACK_FRAGMENT_H_
-
-#define TRACK_FRAGMENT_H_
-
-#include "include/FragmentedMP4Parser.h"
-
-namespace android {
-
-struct FragmentedMP4Parser::TrackFragment : public RefBase {
- TrackFragment() {}
-
- virtual status_t getSample(SampleInfo *info) = 0;
- virtual void advance() = 0;
-
- virtual status_t signalCompletion() = 0;
- virtual bool complete() const = 0;
-
-protected:
- virtual ~TrackFragment() {}
-
-private:
- DISALLOW_EVIL_CONSTRUCTORS(TrackFragment);
-};
-
-struct FragmentedMP4Parser::DynamicTrackFragment : public FragmentedMP4Parser::TrackFragment {
- DynamicTrackFragment();
-
- virtual status_t getSample(SampleInfo *info);
- virtual void advance();
-
- void addSample(
- off64_t dataOffset, size_t sampleSize,
- uint32_t presentationTime,
- size_t sampleDescIndex,
- uint32_t flags);
-
- // No more samples will be added to this fragment.
- virtual status_t signalCompletion();
-
- virtual bool complete() const;
-
-protected:
- virtual ~DynamicTrackFragment();
-
-private:
- bool mComplete;
- size_t mSampleIndex;
- Vector<SampleInfo> mSamples;
-
- DISALLOW_EVIL_CONSTRUCTORS(DynamicTrackFragment);
-};
-
-struct FragmentedMP4Parser::StaticTrackFragment : public FragmentedMP4Parser::TrackFragment {
- StaticTrackFragment();
-
- virtual status_t getSample(SampleInfo *info);
- virtual void advance();
-
- virtual status_t signalCompletion();
- virtual bool complete() const;
-
- status_t parseSampleSizes(
- FragmentedMP4Parser *parser, uint32_t type, size_t offset, uint64_t size);
-
- status_t parseCompactSampleSizes(
- FragmentedMP4Parser *parser, uint32_t type, size_t offset, uint64_t size);
-
- status_t parseSampleToChunk(
- FragmentedMP4Parser *parser, uint32_t type, size_t offset, uint64_t size);
-
- status_t parseChunkOffsets(
- FragmentedMP4Parser *parser, uint32_t type, size_t offset, uint64_t size);
-
- status_t parseChunkOffsets64(
- FragmentedMP4Parser *parser, uint32_t type, size_t offset, uint64_t size);
-
-protected:
- virtual ~StaticTrackFragment();
-
-private:
- size_t mSampleIndex;
- size_t mSampleCount;
- uint32_t mChunkIndex;
-
- SampleInfo mSampleInfo;
-
- sp<ABuffer> mSampleSizes;
- sp<ABuffer> mCompactSampleSizes;
-
- sp<ABuffer> mSampleToChunk;
- ssize_t mSampleToChunkIndex;
- size_t mSampleToChunkRemaining;
-
- sp<ABuffer> mChunkOffsets;
- sp<ABuffer> mChunkOffsets64;
- uint32_t mPrevChunkIndex;
- uint64_t mNextSampleOffset;
-
- void updateSampleInfo();
-
- DISALLOW_EVIL_CONSTRUCTORS(StaticTrackFragment);
-};
-
-} // namespace android
-
-#endif // TRACK_FRAGMENT_H_
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 9850a46..d039f7d 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -506,6 +506,11 @@
ElementaryStreamQueue::PCM_AUDIO);
break;
+ case STREAMTYPE_AC3:
+ mQueue = new ElementaryStreamQueue(
+ ElementaryStreamQueue::AC3);
+ break;
+
default:
break;
}
@@ -614,6 +619,7 @@
case STREAMTYPE_MPEG2_AUDIO:
case STREAMTYPE_MPEG2_AUDIO_ADTS:
case STREAMTYPE_PCM_AUDIO:
+ case STREAMTYPE_AC3:
return true;
default:
@@ -856,7 +862,7 @@
}
void ATSParser::Stream::onPayloadData(
- unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
+ unsigned PTS_DTS_flags, uint64_t PTS, uint64_t /* DTS */,
const uint8_t *data, size_t size) {
#if 0
ALOGI("payload streamType 0x%02x, PTS = 0x%016llx, dPTS = %lld",
@@ -1193,7 +1199,10 @@
unsigned sync_byte = br->getBits(8);
CHECK_EQ(sync_byte, 0x47u);
- MY_LOGV("transport_error_indicator = %u", br->getBits(1));
+ if (br->getBits(1)) { // transport_error_indicator
+ // silently ignore.
+ return OK;
+ }
unsigned payload_unit_start_indicator = br->getBits(1);
ALOGV("payload_unit_start_indicator = %u", payload_unit_start_indicator);
@@ -1258,7 +1267,7 @@
}
void ATSParser::updatePCR(
- unsigned PID, uint64_t PCR, size_t byteOffsetFromStart) {
+ unsigned /* PID */, uint64_t PCR, size_t byteOffsetFromStart) {
ALOGV("PCR 0x%016llx @ %d", PCR, byteOffsetFromStart);
if (mNumPCRs == 2) {
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index a10edc9..d4e30b4 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -88,6 +88,10 @@
STREAMTYPE_MPEG2_AUDIO_ADTS = 0x0f,
STREAMTYPE_MPEG4_VIDEO = 0x10,
STREAMTYPE_H264 = 0x1b,
+
+ // From ATSC A/53 Part 3:2009, 6.7.1
+ STREAMTYPE_AC3 = 0x81,
+
STREAMTYPE_PCM_AUDIO = 0x83,
};
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index c1a7a9d..c17a0b7 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -13,6 +13,8 @@
$(TOP)/frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
+LOCAL_CFLAGS += -Werror
+
LOCAL_MODULE:= libstagefright_mpeg2ts
ifeq ($(TARGET_ARCH),arm)
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 3153c8b..6dfaa94 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -34,7 +34,8 @@
: mIsAudio(false),
mFormat(NULL),
mLastQueuedTimeUs(0),
- mEOSResult(OK) {
+ mEOSResult(OK),
+ mLatestEnqueuedMeta(NULL) {
setFormat(meta);
}
@@ -61,7 +62,7 @@
AnotherPacketSource::~AnotherPacketSource() {
}
-status_t AnotherPacketSource::start(MetaData *params) {
+status_t AnotherPacketSource::start(MetaData * /* params */) {
return OK;
}
@@ -70,7 +71,27 @@
}
sp<MetaData> AnotherPacketSource::getFormat() {
- return mFormat;
+ Mutex::Autolock autoLock(mLock);
+ if (mFormat != NULL) {
+ return mFormat;
+ }
+
+ List<sp<ABuffer> >::iterator it = mBuffers.begin();
+ while (it != mBuffers.end()) {
+ sp<ABuffer> buffer = *it;
+ int32_t discontinuity;
+ if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
+ break;
+ }
+
+ sp<RefBase> object;
+ if (buffer->meta()->findObject("format", &object)) {
+ return static_cast<MetaData*>(object.get());
+ }
+
+ ++it;
+ }
+ return NULL;
}
status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) {
@@ -94,6 +115,11 @@
return INFO_DISCONTINUITY;
}
+ sp<RefBase> object;
+ if ((*buffer)->meta()->findObject("format", &object)) {
+ mFormat = static_cast<MetaData*>(object.get());
+ }
+
return OK;
}
@@ -120,17 +146,22 @@
}
return INFO_DISCONTINUITY;
- } else {
- int64_t timeUs;
- CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
-
- MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
-
- mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
-
- *out = mediaBuffer;
- return OK;
}
+
+ sp<RefBase> object;
+ if (buffer->meta()->findObject("format", &object)) {
+ mFormat = static_cast<MetaData*>(object.get());
+ }
+
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+ MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
+
+ mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+
+ *out = mediaBuffer;
+ return OK;
}
return mEOSResult;
@@ -152,12 +183,24 @@
return;
}
- CHECK(buffer->meta()->findInt64("timeUs", &mLastQueuedTimeUs));
+ int64_t lastQueuedTimeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &lastQueuedTimeUs));
+ mLastQueuedTimeUs = lastQueuedTimeUs;
ALOGV("queueAccessUnit timeUs=%lld us (%.2f secs)", mLastQueuedTimeUs, mLastQueuedTimeUs / 1E6);
Mutex::Autolock autoLock(mLock);
mBuffers.push_back(buffer);
mCondition.signal();
+
+ if (!mLatestEnqueuedMeta.get()) {
+ mLatestEnqueuedMeta = buffer->meta();
+ } else {
+ int64_t latestTimeUs = 0;
+ CHECK(mLatestEnqueuedMeta->findInt64("timeUs", &latestTimeUs));
+ if (lastQueuedTimeUs > latestTimeUs) {
+ mLatestEnqueuedMeta = buffer->meta();
+ }
+ }
}
void AnotherPacketSource::clear() {
@@ -167,6 +210,7 @@
mEOSResult = OK;
mFormat = NULL;
+ mLatestEnqueuedMeta = NULL;
}
void AnotherPacketSource::queueDiscontinuity(
@@ -191,6 +235,7 @@
mEOSResult = OK;
mLastQueuedTimeUs = 0;
+ mLatestEnqueuedMeta = NULL;
sp<ABuffer> buffer = new ABuffer(0);
buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type));
@@ -278,4 +323,9 @@
return (mEOSResult != OK);
}
+sp<AMessage> AnotherPacketSource::getLatestMeta() {
+ Mutex::Autolock autoLock(mLock);
+ return mLatestEnqueuedMeta;
+}
+
} // namespace android
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index e16cf78..9b193a2 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -62,6 +62,8 @@
bool isFinished(int64_t duration) const;
+ sp<AMessage> getLatestMeta();
+
protected:
virtual ~AnotherPacketSource();
@@ -74,6 +76,7 @@
int64_t mLastQueuedTimeUs;
List<sp<ABuffer> > mBuffers;
status_t mEOSResult;
+ sp<AMessage> mLatestEnqueuedMeta;
bool wasFormatChange(int32_t discontinuityType) const;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 8f9c9c8..c0c9717 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -56,6 +56,122 @@
}
}
+// Parse AC3 header assuming the current ptr is start position of syncframe,
+// update metadata only applicable, and return the payload size
+static unsigned parseAC3SyncFrame(
+ const uint8_t *ptr, size_t size, sp<MetaData> *metaData) {
+ static const unsigned channelCountTable[] = {2, 1, 2, 3, 3, 4, 4, 5};
+ static const unsigned samplingRateTable[] = {48000, 44100, 32000};
+ static const unsigned rates[] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256,
+ 320, 384, 448, 512, 576, 640};
+
+ static const unsigned frameSizeTable[19][3] = {
+ { 64, 69, 96 },
+ { 80, 87, 120 },
+ { 96, 104, 144 },
+ { 112, 121, 168 },
+ { 128, 139, 192 },
+ { 160, 174, 240 },
+ { 192, 208, 288 },
+ { 224, 243, 336 },
+ { 256, 278, 384 },
+ { 320, 348, 480 },
+ { 384, 417, 576 },
+ { 448, 487, 672 },
+ { 512, 557, 768 },
+ { 640, 696, 960 },
+ { 768, 835, 1152 },
+ { 896, 975, 1344 },
+ { 1024, 1114, 1536 },
+ { 1152, 1253, 1728 },
+ { 1280, 1393, 1920 },
+ };
+
+ ABitReader bits(ptr, size);
+ unsigned syncStartPos = 0; // in bytes
+ if (bits.numBitsLeft() < 16) {
+ return 0;
+ }
+ if (bits.getBits(16) != 0x0B77) {
+ return 0;
+ }
+
+ if (bits.numBitsLeft() < 16 + 2 + 6 + 5 + 3 + 3) {
+ ALOGV("Not enough bits left for further parsing");
+ return 0;
+ }
+ bits.skipBits(16); // crc1
+
+ unsigned fscod = bits.getBits(2);
+ if (fscod == 3) {
+ ALOGW("Incorrect fscod in AC3 header");
+ return 0;
+ }
+
+ unsigned frmsizecod = bits.getBits(6);
+ if (frmsizecod > 37) {
+ ALOGW("Incorrect frmsizecod in AC3 header");
+ return 0;
+ }
+
+ unsigned bsid = bits.getBits(5);
+ if (bsid > 8) {
+ ALOGW("Incorrect bsid in AC3 header. Possibly E-AC-3?");
+ return 0;
+ }
+
+ unsigned bsmod = bits.getBits(3);
+ unsigned acmod = bits.getBits(3);
+ unsigned cmixlev = 0;
+ unsigned surmixlev = 0;
+ unsigned dsurmod = 0;
+
+ if ((acmod & 1) > 0 && acmod != 1) {
+ if (bits.numBitsLeft() < 2) {
+ return 0;
+ }
+ cmixlev = bits.getBits(2);
+ }
+ if ((acmod & 4) > 0) {
+ if (bits.numBitsLeft() < 2) {
+ return 0;
+ }
+ surmixlev = bits.getBits(2);
+ }
+ if (acmod == 2) {
+ if (bits.numBitsLeft() < 2) {
+ return 0;
+ }
+ dsurmod = bits.getBits(2);
+ }
+
+ if (bits.numBitsLeft() < 1) {
+ return 0;
+ }
+ unsigned lfeon = bits.getBits(1);
+
+ unsigned samplingRate = samplingRateTable[fscod];
+ unsigned payloadSize = frameSizeTable[frmsizecod >> 1][fscod];
+ if (fscod == 1) {
+ payloadSize += frmsizecod & 1;
+ }
+ payloadSize <<= 1; // convert from 16-bit words to bytes
+
+ unsigned channelCount = channelCountTable[acmod] + lfeon;
+
+ if (metaData != NULL) {
+ (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
+ (*metaData)->setInt32(kKeyChannelCount, channelCount);
+ (*metaData)->setInt32(kKeySampleRate, samplingRate);
+ }
+
+ return payloadSize;
+}
+
+static bool IsSeeminglyValidAC3Header(const uint8_t *ptr, size_t size) {
+ return parseAC3SyncFrame(ptr, size, NULL) > 0;
+}
+
static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) {
if (size < 3) {
// Not enough data to verify header.
@@ -224,6 +340,33 @@
break;
}
+ case AC3:
+ {
+ uint8_t *ptr = (uint8_t *)data;
+
+ ssize_t startOffset = -1;
+ for (size_t i = 0; i < size; ++i) {
+ if (IsSeeminglyValidAC3Header(&ptr[i], size - i)) {
+ startOffset = i;
+ break;
+ }
+ }
+
+ if (startOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (startOffset > 0) {
+ ALOGI("found something resembling an AC3 syncword at "
+ "offset %d",
+ startOffset);
+ }
+
+ data = &ptr[startOffset];
+ size -= startOffset;
+ break;
+ }
+
case MPEG_AUDIO:
{
uint8_t *ptr = (uint8_t *)data;
@@ -328,6 +471,8 @@
return dequeueAccessUnitH264();
case AAC:
return dequeueAccessUnitAAC();
+ case AC3:
+ return dequeueAccessUnitAC3();
case MPEG_VIDEO:
return dequeueAccessUnitMPEGVideo();
case MPEG4_VIDEO:
@@ -340,6 +485,51 @@
}
}
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAC3() {
+ unsigned syncStartPos = 0; // in bytes
+ unsigned payloadSize = 0;
+ sp<MetaData> format = new MetaData;
+ while (true) {
+ if (syncStartPos + 2 >= mBuffer->size()) {
+ return NULL;
+ }
+
+ payloadSize = parseAC3SyncFrame(
+ mBuffer->data() + syncStartPos,
+ mBuffer->size() - syncStartPos,
+ &format);
+ if (payloadSize > 0) {
+ break;
+ }
+ ++syncStartPos;
+ }
+
+ if (mBuffer->size() < syncStartPos + payloadSize) {
+ ALOGV("Not enough buffer size for AC3");
+ return NULL;
+ }
+
+ if (mFormat == NULL) {
+ mFormat = format;
+ }
+
+ sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize);
+ memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
+
+ int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize);
+ CHECK_GE(timeUs, 0ll);
+ accessUnit->meta()->setInt64("timeUs", timeUs);
+
+ memmove(
+ mBuffer->data(),
+ mBuffer->data() + syncStartPos + payloadSize,
+ mBuffer->size() - syncStartPos - payloadSize);
+
+ mBuffer->setRange(0, mBuffer->size() - syncStartPos - payloadSize);
+
+ return accessUnit;
+}
+
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitPCMAudio() {
if (mBuffer->size() < 4) {
return NULL;
@@ -604,7 +794,9 @@
dstOffset += pos.nalSize + 4;
}
+#if !LOG_NDEBUG
ALOGV("accessUnit contains nal types %s", out.c_str());
+#endif
const NALPosition &pos = nals.itemAt(nals.size() - 1);
size_t nextScan = pos.nalOffset + pos.nalSize;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 66a8087..a2cca77 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -32,6 +32,7 @@
enum Mode {
H264,
AAC,
+ AC3,
MPEG_AUDIO,
MPEG_VIDEO,
MPEG4_VIDEO,
@@ -67,6 +68,7 @@
sp<ABuffer> dequeueAccessUnitH264();
sp<ABuffer> dequeueAccessUnitAAC();
+ sp<ABuffer> dequeueAccessUnitAC3();
sp<ABuffer> dequeueAccessUnitMPEGAudio();
sp<ABuffer> dequeueAccessUnitMPEGVideo();
sp<ABuffer> dequeueAccessUnitMPEG4Video();
diff --git a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
index dd714c99..bc2a16d 100644
--- a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
@@ -130,7 +130,8 @@
return new WrappedTrack(this, mTracks.valueAt(index));
}
-sp<MetaData> MPEG2PSExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+sp<MetaData> MPEG2PSExtractor::getTrackMetaData(
+ size_t index, uint32_t /* flags */) {
if (index >= mTracks.size()) {
return NULL;
}
@@ -625,7 +626,7 @@
status_t MPEG2PSExtractor::Track::appendPESData(
unsigned PTS_DTS_flags,
- uint64_t PTS, uint64_t DTS,
+ uint64_t PTS, uint64_t /* DTS */,
const uint8_t *data, size_t size) {
if (mQueue == NULL) {
return OK;
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index d449c34..35ca118 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -141,7 +141,7 @@
}
sp<MetaData> MPEG2TSExtractor::getTrackMetaData(
- size_t index, uint32_t flags) {
+ size_t index, uint32_t /* flags */) {
return index < mSourceImpls.size()
? mSourceImpls.editItemAt(index)->getFormat() : NULL;
}
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index cf43e94..b81b116 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -41,12 +41,21 @@
mNumFramesAvailable(0),
mEndOfStream(false),
mEndOfStreamSent(false),
+ mMaxTimestampGapUs(-1ll),
+ mPrevOriginalTimeUs(-1ll),
+ mPrevModifiedTimeUs(-1ll),
+ mSkipFramesBeforeNs(-1ll),
mRepeatAfterUs(-1ll),
mRepeatLastFrameGeneration(0),
+ mRepeatLastFrameTimestamp(-1ll),
mLatestSubmittedBufferId(-1),
mLatestSubmittedBufferFrameNum(0),
mLatestSubmittedBufferUseCount(0),
- mRepeatBufferDeferred(false) {
+ mRepeatBufferDeferred(false),
+ mTimePerCaptureUs(-1ll),
+ mTimePerFrameUs(-1ll),
+ mPrevCaptureUs(-1ll),
+ mPrevFrameUs(-1ll) {
ALOGV("GraphicBufferSource w=%u h=%u c=%u",
bufferWidth, bufferHeight, bufferCount);
@@ -148,6 +157,18 @@
}
}
+void GraphicBufferSource::omxIdle() {
+ ALOGV("omxIdle");
+
+ Mutex::Autolock autoLock(mMutex);
+
+ if (mExecuting) {
+ // We are only interested in the transition from executing->idle,
+ // not loaded->idle.
+ mExecuting = false;
+ }
+}
+
void GraphicBufferSource::omxLoaded(){
Mutex::Autolock autoLock(mMutex);
if (!mExecuting) {
@@ -194,7 +215,9 @@
void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
Mutex::Autolock autoLock(mMutex);
- CHECK(mExecuting); // could this happen if app stop()s early?
+ if (!mExecuting) {
+ return;
+ }
int cbi = findMatchingCodecBuffer_l(header);
if (cbi < 0) {
@@ -213,7 +236,12 @@
// see if the GraphicBuffer reference was null, which should only ever
// happen for EOS.
if (codecBuffer.mGraphicBuffer == NULL) {
- CHECK(mEndOfStream && mEndOfStreamSent);
+ if (!(mEndOfStream && mEndOfStreamSent)) {
+ // This can happen when broken code sends us the same buffer
+ // twice in a row.
+ ALOGE("ERROR: codecBufferEmptied on non-EOS null buffer "
+ "(buffer emptied twice?)");
+ }
// No GraphicBuffer to deal with, no additional input or output is
// expected, so just return.
return;
@@ -280,6 +308,32 @@
return;
}
+void GraphicBufferSource::codecBufferFilled(OMX_BUFFERHEADERTYPE* header) {
+ Mutex::Autolock autoLock(mMutex);
+
+ if (mMaxTimestampGapUs > 0ll
+ && !(header->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
+ ssize_t index = mOriginalTimeUs.indexOfKey(header->nTimeStamp);
+ if (index >= 0) {
+ ALOGV("OUT timestamp: %lld -> %lld",
+ header->nTimeStamp, mOriginalTimeUs[index]);
+ header->nTimeStamp = mOriginalTimeUs[index];
+ mOriginalTimeUs.removeItemsAt(index);
+ } else {
+ // giving up the effort as encoder doesn't appear to preserve pts
+ ALOGW("giving up limiting timestamp gap (pts = %lld)",
+ header->nTimeStamp);
+ mMaxTimestampGapUs = -1ll;
+ }
+ if (mOriginalTimeUs.size() > BufferQueue::NUM_BUFFER_SLOTS) {
+ // something terribly wrong must have happened, giving up...
+ ALOGE("mOriginalTimeUs has too many entries (%d)",
+ mOriginalTimeUs.size());
+ mMaxTimestampGapUs = -1ll;
+ }
+ }
+}
+
void GraphicBufferSource::suspend(bool suspend) {
Mutex::Autolock autoLock(mMutex);
@@ -365,7 +419,18 @@
mBufferSlot[item.mBuf] = item.mGraphicBuffer;
}
- err = submitBuffer_l(item, cbi);
+ err = UNKNOWN_ERROR;
+
+ // only submit sample if start time is unspecified, or sample
+ // is queued after the specified start time
+ if (mSkipFramesBeforeNs < 0ll || item.mTimestamp >= mSkipFramesBeforeNs) {
+ // if start time is set, offset time stamp by start time
+ if (mSkipFramesBeforeNs > 0) {
+ item.mTimestamp -= mSkipFramesBeforeNs;
+ }
+ err = submitBuffer_l(item, cbi);
+ }
+
if (err != OK) {
ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf);
mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber,
@@ -384,6 +449,23 @@
if (mLatestSubmittedBufferId < 0 || mSuspended) {
return false;
}
+ if (mBufferSlot[mLatestSubmittedBufferId] == NULL) {
+ // This can happen if the remote side disconnects, causing
+ // onBuffersReleased() to NULL out our copy of the slots. The
+ // buffer is gone, so we have nothing to show.
+ //
+ // To be on the safe side we try to release the buffer.
+ ALOGD("repeatLatestSubmittedBuffer_l: slot was NULL");
+ mBufferQueue->releaseBuffer(
+ mLatestSubmittedBufferId,
+ mLatestSubmittedBufferFrameNum,
+ EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR,
+ Fence::NO_FENCE);
+ mLatestSubmittedBufferId = -1;
+ mLatestSubmittedBufferFrameNum = 0;
+ return false;
+ }
int cbi = findAvailableCodecBuffer_l();
if (cbi < 0) {
@@ -395,6 +477,7 @@
BufferQueue::BufferItem item;
item.mBuf = mLatestSubmittedBufferId;
item.mFrameNumber = mLatestSubmittedBufferFrameNum;
+ item.mTimestamp = mRepeatLastFrameTimestamp;
status_t err = submitBuffer_l(item, cbi);
@@ -404,6 +487,20 @@
++mLatestSubmittedBufferUseCount;
+ /* repeat last frame up to kRepeatLastFrameCount times.
+ * in case of static scene, a single repeat might not get rid of encoder
+ * ghosting completely, refresh a couple more times to get better quality
+ */
+ if (--mRepeatLastFrameCount > 0) {
+ mRepeatLastFrameTimestamp = item.mTimestamp + mRepeatAfterUs * 1000;
+
+ if (mReflector != NULL) {
+ sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector->id());
+ msg->setInt32("generation", ++mRepeatLastFrameGeneration);
+ msg->post(mRepeatAfterUs);
+ }
+ }
+
return true;
}
@@ -424,8 +521,11 @@
mLatestSubmittedBufferId = item.mBuf;
mLatestSubmittedBufferFrameNum = item.mFrameNumber;
+ mRepeatLastFrameTimestamp = item.mTimestamp + mRepeatAfterUs * 1000;
+
mLatestSubmittedBufferUseCount = 1;
mRepeatBufferDeferred = false;
+ mRepeatLastFrameCount = kRepeatLastFrameCount;
if (mReflector != NULL) {
sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector->id());
@@ -461,9 +561,71 @@
return OK;
}
+int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) {
+ int64_t timeUs = item.mTimestamp / 1000;
+
+ if (mTimePerCaptureUs > 0ll) {
+ // Time lapse or slow motion mode
+ if (mPrevCaptureUs < 0ll) {
+ // first capture
+ mPrevCaptureUs = timeUs;
+ mPrevFrameUs = timeUs;
+ } else {
+ // snap to nearest capture point
+ int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs)
+ / mTimePerCaptureUs;
+ if (nFrames <= 0) {
+ // skip this frame as it's too close to previous capture
+ ALOGV("skipping frame, timeUs %lld", timeUs);
+ return -1;
+ }
+ mPrevCaptureUs = mPrevCaptureUs + nFrames * mTimePerCaptureUs;
+ mPrevFrameUs += mTimePerFrameUs * nFrames;
+ }
+
+ ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
+ timeUs, mPrevCaptureUs, mPrevFrameUs);
+
+ return mPrevFrameUs;
+ } else if (mMaxTimestampGapUs > 0ll) {
+ /* Cap timestamp gap between adjacent frames to specified max
+ *
+ * In the scenario of cast mirroring, encoding could be suspended for
+ * prolonged periods. Limiting the pts gap to workaround the problem
+ * where encoder's rate control logic produces huge frames after a
+ * long period of suspension.
+ */
+
+ int64_t originalTimeUs = timeUs;
+ if (mPrevOriginalTimeUs >= 0ll) {
+ if (originalTimeUs < mPrevOriginalTimeUs) {
+ // Drop the frame if it's going backward in time. Bad timestamp
+ // could disrupt encoder's rate control completely.
+ ALOGW("Dropping frame that's going backward in time");
+ return -1;
+ }
+ int64_t timestampGapUs = originalTimeUs - mPrevOriginalTimeUs;
+ timeUs = (timestampGapUs < mMaxTimestampGapUs ?
+ timestampGapUs : mMaxTimestampGapUs) + mPrevModifiedTimeUs;
+ }
+ mPrevOriginalTimeUs = originalTimeUs;
+ mPrevModifiedTimeUs = timeUs;
+ mOriginalTimeUs.add(timeUs, originalTimeUs);
+ ALOGV("IN timestamp: %lld -> %lld", originalTimeUs, timeUs);
+ }
+
+ return timeUs;
+}
+
status_t GraphicBufferSource::submitBuffer_l(
const BufferQueue::BufferItem &item, int cbi) {
ALOGV("submitBuffer_l cbi=%d", cbi);
+
+ int64_t timeUs = getTimestamp(item);
+ if (timeUs < 0ll) {
+ return UNKNOWN_ERROR;
+ }
+
CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
codecBuffer.mGraphicBuffer = mBufferSlot[item.mBuf];
codecBuffer.mBuf = item.mBuf;
@@ -479,7 +641,7 @@
status_t err = mNodeInstance->emptyDirectBuffer(header, 0,
4 + sizeof(buffer_handle_t), OMX_BUFFERFLAG_ENDOFFRAME,
- item.mTimestamp / 1000);
+ timeUs);
if (err != OK) {
ALOGW("WARNING: emptyDirectBuffer failed: 0x%x", err);
codecBuffer.mGraphicBuffer = NULL;
@@ -573,6 +735,12 @@
BufferQueue::BufferItem item;
status_t err = mBufferQueue->acquireBuffer(&item, 0);
if (err == OK) {
+ // If this is the first time we're seeing this buffer, add it to our
+ // slot table.
+ if (item.mGraphicBuffer != NULL) {
+ ALOGV("onFrameAvailable: setting mBufferSlot %d", item.mBuf);
+ mBufferSlot[item.mBuf] = item.mGraphicBuffer;
+ }
mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
}
@@ -622,6 +790,38 @@
return OK;
}
+status_t GraphicBufferSource::setMaxTimestampGapUs(int64_t maxGapUs) {
+ Mutex::Autolock autoLock(mMutex);
+
+ if (mExecuting || maxGapUs <= 0ll) {
+ return INVALID_OPERATION;
+ }
+
+ mMaxTimestampGapUs = maxGapUs;
+
+ return OK;
+}
+
+void GraphicBufferSource::setSkipFramesBeforeUs(int64_t skipFramesBeforeUs) {
+ Mutex::Autolock autoLock(mMutex);
+
+ mSkipFramesBeforeNs =
+ (skipFramesBeforeUs > 0) ? (skipFramesBeforeUs * 1000) : -1ll;
+}
+
+status_t GraphicBufferSource::setTimeLapseUs(int64_t* data) {
+ Mutex::Autolock autoLock(mMutex);
+
+ if (mExecuting || data[0] <= 0ll || data[1] <= 0ll) {
+ return INVALID_OPERATION;
+ }
+
+ mTimePerFrameUs = data[0];
+ mTimePerCaptureUs = data[1];
+
+ return OK;
+}
+
void GraphicBufferSource::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatRepeatLastFrame:
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 244a843..fba42b7 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -69,6 +69,11 @@
// sitting in the BufferQueue, this will send them to the codec.
void omxExecuting();
+ // This is called when OMX transitions to OMX_StateIdle, indicating that
+ // the codec is meant to return all buffers back to the client for them
+ // to be freed. Do NOT submit any more buffers to the component.
+ void omxIdle();
+
// This is called when OMX transitions to OMX_StateLoaded, indicating that
// we are shutting down.
void omxLoaded();
@@ -82,6 +87,10 @@
// fill it with a new frame of data; otherwise, just mark it as available.
void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header);
+ // Called when omx_message::FILL_BUFFER_DONE is received. (Currently the
+ // buffer source will fix timestamp in the header if needed.)
+ void codecBufferFilled(OMX_BUFFERHEADERTYPE* header);
+
// This is called after the last input frame has been submitted. We
// need to submit an empty buffer with the EOS flag set. If we don't
// have a codec buffer ready, we just set the mEndOfStream flag.
@@ -100,6 +109,26 @@
// state and once this behaviour is specified it cannot be reset.
status_t setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs);
+ // When set, the timestamp fed to the encoder will be modified such that
+ // the gap between two adjacent frames is capped at maxGapUs. Timestamp
+ // will be restored to the original when the encoded frame is returned to
+ // the client.
+ // This is to solve a problem in certain real-time streaming case, where
+ // encoder's rate control logic produces huge frames after a long period
+ // of suspension on input.
+ status_t setMaxTimestampGapUs(int64_t maxGapUs);
+
+ // Sets the time lapse (or slow motion) parameters.
+ // data[0] is the time (us) between two frames for playback
+ // data[1] is the time (us) between two frames for capture
+ // When set, the sample's timestamp will be modified to playback framerate,
+ // and capture timestamp will be modified to capture rate.
+ status_t setTimeLapseUs(int64_t* data);
+
+ // Sets the start time us (in system time), samples before which should
+ // be dropped and not submitted to encoder
+ void setSkipFramesBeforeUs(int64_t startTimeUs);
+
protected:
// BufferQueue::ConsumerListener interface, called when a new frame of
// data is available. If we're executing and a codec buffer is
@@ -160,6 +189,7 @@
void setLatestSubmittedBuffer_l(const BufferQueue::BufferItem &item);
bool repeatLatestSubmittedBuffer_l();
+ int64_t getTimestamp(const BufferQueue::BufferItem &item);
// Lock, covers all member variables.
mutable Mutex mMutex;
@@ -201,13 +231,23 @@
enum {
kWhatRepeatLastFrame,
};
+ enum {
+ kRepeatLastFrameCount = 10,
+ };
- int64_t mRepeatAfterUs;
+ KeyedVector<int64_t, int64_t> mOriginalTimeUs;
+ int64_t mMaxTimestampGapUs;
+ int64_t mPrevOriginalTimeUs;
+ int64_t mPrevModifiedTimeUs;
+ int64_t mSkipFramesBeforeNs;
sp<ALooper> mLooper;
sp<AHandlerReflector<GraphicBufferSource> > mReflector;
+ int64_t mRepeatAfterUs;
int32_t mRepeatLastFrameGeneration;
+ int64_t mRepeatLastFrameTimestamp;
+ int32_t mRepeatLastFrameCount;
int mLatestSubmittedBufferId;
uint64_t mLatestSubmittedBufferFrameNum;
@@ -217,6 +257,12 @@
// no codec buffer was available at the time.
bool mRepeatBufferDeferred;
+ // Time lapse / slow motion configuration
+ int64_t mTimePerCaptureUs;
+ int64_t mTimePerFrameUs;
+ int64_t mPrevCaptureUs;
+ int64_t mPrevFrameUs;
+
void onMessageReceived(const sp<AMessage> &msg);
DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource);
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index aaa9f89..a608479 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -185,7 +185,7 @@
instance->onObserverDied(mMaster);
}
-bool OMX::livesLocally(node_id node, pid_t pid) {
+bool OMX::livesLocally(node_id /* node */, pid_t pid) {
return pid == getpid();
}
@@ -331,6 +331,13 @@
return findInstance(node)->storeMetaDataInBuffers(port_index, enable);
}
+status_t OMX::prepareForAdaptivePlayback(
+ node_id node, OMX_U32 portIndex, OMX_BOOL enable,
+ OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) {
+ return findInstance(node)->prepareForAdaptivePlayback(
+ portIndex, enable, maxFrameWidth, maxFrameHeight);
+}
+
status_t OMX::useBuffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) {
@@ -417,7 +424,7 @@
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
- OMX_IN OMX_PTR pEventData) {
+ OMX_IN OMX_PTR /* pEventData */) {
ALOGV("OnEvent(%d, %ld, %ld)", eEvent, nData1, nData2);
// Forward to OMXNodeInstance.
@@ -472,7 +479,7 @@
OMX::node_id OMX::makeNodeID(OMXNodeInstance *instance) {
// mLock is already held.
- node_id node = (node_id)++mNodeCounter;
+ node_id node = (node_id)(uintptr_t)++mNodeCounter;
mNodeIDToInstance.add(node, instance);
return node;
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index ef683a0..0fb38fa 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -243,13 +243,18 @@
status_t OMXNodeInstance::sendCommand(
OMX_COMMANDTYPE cmd, OMX_S32 param) {
const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource());
- if (bufferSource != NULL
- && cmd == OMX_CommandStateSet
- && param == OMX_StateLoaded) {
- // Initiating transition from Executing -> Loaded
- // Buffers are about to be freed.
- bufferSource->omxLoaded();
- setGraphicBufferSource(NULL);
+ if (bufferSource != NULL && cmd == OMX_CommandStateSet) {
+ if (param == OMX_StateIdle) {
+ // Initiating transition from Executing -> Idle
+ // ACodec is waiting for all buffers to be returned, do NOT
+ // submit any more buffers to the codec.
+ bufferSource->omxIdle();
+ } else if (param == OMX_StateLoaded) {
+ // Initiating transition from Idle/Executing -> Loaded
+ // Buffers are about to be freed.
+ bufferSource->omxLoaded();
+ setGraphicBufferSource(NULL);
+ }
// fall through
}
@@ -261,7 +266,7 @@
}
status_t OMXNodeInstance::getParameter(
- OMX_INDEXTYPE index, void *params, size_t size) {
+ OMX_INDEXTYPE index, void *params, size_t /* size */) {
Mutex::Autolock autoLock(mLock);
OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params);
@@ -270,7 +275,7 @@
}
status_t OMXNodeInstance::setParameter(
- OMX_INDEXTYPE index, const void *params, size_t size) {
+ OMX_INDEXTYPE index, const void *params, size_t /* size */) {
Mutex::Autolock autoLock(mLock);
OMX_ERRORTYPE err = OMX_SetParameter(
@@ -280,7 +285,7 @@
}
status_t OMXNodeInstance::getConfig(
- OMX_INDEXTYPE index, void *params, size_t size) {
+ OMX_INDEXTYPE index, void *params, size_t /* size */) {
Mutex::Autolock autoLock(mLock);
OMX_ERRORTYPE err = OMX_GetConfig(mHandle, index, params);
@@ -288,7 +293,7 @@
}
status_t OMXNodeInstance::setConfig(
- OMX_INDEXTYPE index, const void *params, size_t size) {
+ OMX_INDEXTYPE index, const void *params, size_t /* size */) {
Mutex::Autolock autoLock(mLock);
OMX_ERRORTYPE err = OMX_SetConfig(
@@ -417,6 +422,40 @@
return err;
}
+status_t OMXNodeInstance::prepareForAdaptivePlayback(
+ OMX_U32 portIndex, OMX_BOOL enable, OMX_U32 maxFrameWidth,
+ OMX_U32 maxFrameHeight) {
+ Mutex::Autolock autolock(mLock);
+
+ OMX_INDEXTYPE index;
+ OMX_STRING name = const_cast<OMX_STRING>(
+ "OMX.google.android.index.prepareForAdaptivePlayback");
+
+ OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
+ if (err != OMX_ErrorNone) {
+ ALOGW_IF(enable, "OMX_GetExtensionIndex %s failed", name);
+ return StatusFromOMXError(err);
+ }
+
+ PrepareForAdaptivePlaybackParams params;
+ params.nSize = sizeof(params);
+ params.nVersion.s.nVersionMajor = 1;
+ params.nVersion.s.nVersionMinor = 0;
+ params.nVersion.s.nRevision = 0;
+ params.nVersion.s.nStep = 0;
+
+ params.nPortIndex = portIndex;
+ params.bEnable = enable;
+ params.nMaxFrameWidth = maxFrameWidth;
+ params.nMaxFrameHeight = maxFrameHeight;
+ if ((err = OMX_SetParameter(mHandle, index, ¶ms)) != OMX_ErrorNone) {
+ ALOGW("OMX_SetParameter failed for PrepareForAdaptivePlayback "
+ "with error %d (0x%08x)", err, err);
+ return UNKNOWN_ERROR;
+ }
+ return err;
+}
+
status_t OMXNodeInstance::useBuffer(
OMX_U32 portIndex, const sp<IMemory> ¶ms,
OMX::buffer_id *buffer) {
@@ -571,7 +610,7 @@
}
status_t OMXNodeInstance::updateGraphicBufferInMeta(
- OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
+ OMX_U32 /* portIndex */, const sp<GraphicBuffer>& graphicBuffer,
OMX::buffer_id buffer) {
Mutex::Autolock autoLock(mLock);
@@ -810,6 +849,9 @@
switch (type) {
case IOMX::INTERNAL_OPTION_SUSPEND:
case IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY:
+ case IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP:
+ case IOMX::INTERNAL_OPTION_START_TIME:
+ case IOMX::INTERNAL_OPTION_TIME_LAPSE:
{
const sp<GraphicBufferSource> &bufferSource =
getGraphicBufferSource();
@@ -825,7 +867,8 @@
bool suspend = *(bool *)data;
bufferSource->suspend(suspend);
- } else {
+ } else if (type ==
+ IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY){
if (size != sizeof(int64_t)) {
return INVALID_OPERATION;
}
@@ -833,6 +876,29 @@
int64_t delayUs = *(int64_t *)data;
return bufferSource->setRepeatPreviousFrameDelayUs(delayUs);
+ } else if (type ==
+ IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP){
+ if (size != sizeof(int64_t)) {
+ return INVALID_OPERATION;
+ }
+
+ int64_t maxGapUs = *(int64_t *)data;
+
+ return bufferSource->setMaxTimestampGapUs(maxGapUs);
+ } else if (type == IOMX::INTERNAL_OPTION_START_TIME) {
+ if (size != sizeof(int64_t)) {
+ return INVALID_OPERATION;
+ }
+
+ int64_t skipFramesBeforeUs = *(int64_t *)data;
+
+ bufferSource->setSkipFramesBeforeUs(skipFramesBeforeUs);
+ } else { // IOMX::INTERNAL_OPTION_TIME_LAPSE
+ if (size != sizeof(int64_t) * 2) {
+ return INVALID_OPERATION;
+ }
+
+ bufferSource->setTimeLapseUs((int64_t *)data);
}
return OK;
@@ -844,6 +910,8 @@
}
void OMXNodeInstance::onMessage(const omx_message &msg) {
+ const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource());
+
if (msg.type == omx_message::FILL_BUFFER_DONE) {
OMX_BUFFERHEADERTYPE *buffer =
static_cast<OMX_BUFFERHEADERTYPE *>(
@@ -853,10 +921,18 @@
static_cast<BufferMeta *>(buffer->pAppPrivate);
buffer_meta->CopyFromOMX(buffer);
- } else if (msg.type == omx_message::EMPTY_BUFFER_DONE) {
- const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource());
if (bufferSource != NULL) {
+ // fix up the buffer info (especially timestamp) if needed
+ bufferSource->codecBufferFilled(buffer);
+
+ omx_message newMsg = msg;
+ newMsg.u.extended_buffer_data.timestamp = buffer->nTimeStamp;
+ mObserver->onMessage(newMsg);
+ return;
+ }
+ } else if (msg.type == omx_message::EMPTY_BUFFER_DONE) {
+ if (bufferSource != NULL) {
// This is one of the buffers used exclusively by
// GraphicBufferSource.
// Don't dispatch a message back to ACodec, since it doesn't
@@ -902,7 +978,7 @@
// static
OMX_ERRORTYPE OMXNodeInstance::OnEvent(
- OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_HANDLETYPE /* hComponent */,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
@@ -918,7 +994,7 @@
// static
OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone(
- OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_HANDLETYPE /* hComponent */,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
@@ -930,7 +1006,7 @@
// static
OMX_ERRORTYPE OMXNodeInstance::OnFillBufferDone(
- OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_HANDLETYPE /* hComponent */,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
diff --git a/media/libstagefright/omx/SoftOMXComponent.cpp b/media/libstagefright/omx/SoftOMXComponent.cpp
index b1c34dc..646cd32 100644
--- a/media/libstagefright/omx/SoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftOMXComponent.cpp
@@ -257,69 +257,69 @@
////////////////////////////////////////////////////////////////////////////////
OMX_ERRORTYPE SoftOMXComponent::sendCommand(
- OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) {
+ OMX_COMMANDTYPE /* cmd */, OMX_U32 /* param */, OMX_PTR /* data */) {
return OMX_ErrorUndefined;
}
OMX_ERRORTYPE SoftOMXComponent::getParameter(
- OMX_INDEXTYPE index, OMX_PTR params) {
+ OMX_INDEXTYPE /* index */, OMX_PTR /* params */) {
return OMX_ErrorUndefined;
}
OMX_ERRORTYPE SoftOMXComponent::setParameter(
- OMX_INDEXTYPE index, const OMX_PTR params) {
+ OMX_INDEXTYPE /* index */, const OMX_PTR /* params */) {
return OMX_ErrorUndefined;
}
OMX_ERRORTYPE SoftOMXComponent::getConfig(
- OMX_INDEXTYPE index, OMX_PTR params) {
+ OMX_INDEXTYPE /* index */, OMX_PTR /* params */) {
return OMX_ErrorUndefined;
}
OMX_ERRORTYPE SoftOMXComponent::setConfig(
- OMX_INDEXTYPE index, const OMX_PTR params) {
+ OMX_INDEXTYPE /* index */, const OMX_PTR /* params */) {
return OMX_ErrorUndefined;
}
OMX_ERRORTYPE SoftOMXComponent::getExtensionIndex(
- const char *name, OMX_INDEXTYPE *index) {
+ 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) {
+ 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) {
+ 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) {
+ OMX_U32 /* portIndex */,
+ OMX_BUFFERHEADERTYPE * /* buffer */) {
return OMX_ErrorUndefined;
}
OMX_ERRORTYPE SoftOMXComponent::emptyThisBuffer(
- OMX_BUFFERHEADERTYPE *buffer) {
+ OMX_BUFFERHEADERTYPE * /* buffer */) {
return OMX_ErrorUndefined;
}
OMX_ERRORTYPE SoftOMXComponent::fillThisBuffer(
- OMX_BUFFERHEADERTYPE *buffer) {
+ OMX_BUFFERHEADERTYPE * /* buffer */) {
return OMX_ErrorUndefined;
}
-OMX_ERRORTYPE SoftOMXComponent::getState(OMX_STATETYPE *state) {
+OMX_ERRORTYPE SoftOMXComponent::getState(OMX_STATETYPE * /* state */) {
return OMX_ErrorUndefined;
}
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index d6cde73..65f5404 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -50,6 +50,7 @@
{ "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" },
{ "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },
{ "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },
+ { "OMX.google.opus.decoder", "opusdec", "audio_decoder.opus" },
{ "OMX.google.vp8.decoder", "vpxdec", "video_decoder.vp8" },
{ "OMX.google.vp9.decoder", "vpxdec", "video_decoder.vp9" },
{ "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" },
@@ -154,7 +155,7 @@
OMX_ERRORTYPE SoftOMXPlugin::enumerateComponents(
OMX_STRING name,
- size_t size,
+ size_t /* size */,
OMX_U32 index) {
if (index >= kNumComponents) {
return OMX_ErrorNoMore;
diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk
index 1061c39..8b79af4 100644
--- a/media/libstagefright/omx/tests/Android.mk
+++ b/media/libstagefright/omx/tests/Android.mk
@@ -11,6 +11,8 @@
$(TOP)/frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
+LOCAL_CFLAGS += -Werror
+
LOCAL_MODULE := omx_tests
LOCAL_MODULE_TAGS := tests
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 4bee808..f4dfd6b 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -16,6 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "OMXHarness"
+#include <inttypes.h>
#include <utils/Log.h>
#include "OMXHarness.h"
@@ -25,6 +26,7 @@
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
@@ -241,7 +243,8 @@
};
static sp<MediaExtractor> CreateExtractorFromURI(const char *uri) {
- sp<DataSource> source = DataSource::CreateFromURI(uri);
+ sp<DataSource> source =
+ DataSource::CreateFromURI(NULL /* httpService */, uri);
if (source == NULL) {
return NULL;
@@ -460,6 +463,7 @@
{ "audio_decoder.aac", "audio/mp4a-latm" },
{ "audio_decoder.mp3", "audio/mpeg" },
{ "audio_decoder.vorbis", "audio/vorbis" },
+ { "audio_decoder.opus", "audio/opus" },
{ "audio_decoder.g711alaw", MEDIA_MIMETYPE_AUDIO_G711_ALAW },
{ "audio_decoder.g711mlaw", MEDIA_MIMETYPE_AUDIO_G711_MLAW },
};
@@ -492,6 +496,7 @@
{ "audio/mpeg",
"file:///sdcard/media_api/music/MP3_48KHz_128kbps_s_1_17_CBR.mp3" },
{ "audio/vorbis", NULL },
+ { "audio/opus", NULL },
{ "video/x-vnd.on2.vp8",
"file:///sdcard/media_api/video/big-buck-bunny_trailer.webm" },
{ MEDIA_MIMETYPE_AUDIO_G711_ALAW, "file:///sdcard/M1F1-Alaw-AFsp.wav" },
@@ -711,11 +716,11 @@
int64_t bufferTimeUs;
CHECK(buffer->meta_data()->findInt64(kKeyTime, &bufferTimeUs));
if (!CloseEnough(bufferTimeUs, actualSeekTimeUs)) {
- printf("\n * Attempted seeking to %lld us (%.2f secs)",
+ printf("\n * Attempted seeking to %" PRId64 " us (%.2f secs)",
requestedSeekTimeUs, requestedSeekTimeUs / 1E6);
- printf("\n * Nearest keyframe is at %lld us (%.2f secs)",
+ printf("\n * Nearest keyframe is at %" PRId64 " us (%.2f secs)",
actualSeekTimeUs, actualSeekTimeUs / 1E6);
- printf("\n * Returned buffer was at %lld us (%.2f secs)\n\n",
+ printf("\n * Returned buffer was at %" PRId64 " us (%.2f secs)\n\n",
bufferTimeUs, bufferTimeUs / 1E6);
buffer->release();
diff --git a/media/libstagefright/rtsp/AMPEG2TSAssembler.cpp b/media/libstagefright/rtsp/AMPEG2TSAssembler.cpp
index 4c9bf5b..dca5c89 100644
--- a/media/libstagefright/rtsp/AMPEG2TSAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG2TSAssembler.cpp
@@ -34,7 +34,9 @@
namespace android {
AMPEG2TSAssembler::AMPEG2TSAssembler(
- const sp<AMessage> ¬ify, const char *desc, const AString ¶ms)
+ const sp<AMessage> ¬ify,
+ const char * /* desc */,
+ const AString & /* params */)
: mNotifyMsg(notify),
mNextExpectedSeqNoValid(false),
mNextExpectedSeqNo(0) {
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 462c384..09f52bc 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -23,7 +23,7 @@
#include "ARawAudioAssembler.h"
#include "ASessionDescription.h"
-#include "avc_utils.h"
+#include "include/avc_utils.h"
#include <ctype.h>
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index 0d07043..c46d16f 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -114,7 +114,7 @@
return (mFlags & kFlagEOS) != 0;
}
-status_t ARTPWriter::start(MetaData *params) {
+status_t ARTPWriter::start(MetaData * /* params */) {
Mutex::Autolock autoLock(mLock);
if (mFlags & kFlagStarted) {
return INVALID_OPERATION;
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index efde7a9..4054da6 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -33,7 +33,7 @@
#include <openssl/md5.h>
#include <sys/socket.h>
-#include "HTTPBase.h"
+#include "include/HTTPBase.h"
namespace android {
diff --git a/media/libstagefright/rtsp/ARawAudioAssembler.cpp b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
index 0da5dd2..167f7a4 100644
--- a/media/libstagefright/rtsp/ARawAudioAssembler.cpp
+++ b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
@@ -34,7 +34,9 @@
namespace android {
ARawAudioAssembler::ARawAudioAssembler(
- const sp<AMessage> ¬ify, const char *desc, const AString ¶ms)
+ const sp<AMessage> ¬ify,
+ const char * /* desc */,
+ const AString & /* params */)
: mNotifyMsg(notify),
mNextExpectedSeqNoValid(false),
mNextExpectedSeqNo(0) {
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index e77c69c..39eedc0 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -20,7 +20,7 @@
SDPLoader.cpp \
LOCAL_C_INCLUDES:= \
- $(TOP)/frameworks/av/media/libstagefright/include \
+ $(TOP)/frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax \
$(TOP)/external/openssl/include
@@ -30,6 +30,8 @@
LOCAL_CFLAGS += -Wno-psabi
endif
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_STATIC_LIBRARY)
################################################################################
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index cd77aa0..45470a3 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -19,7 +19,11 @@
#define MY_HANDLER_H_
//#define LOG_NDEBUG 0
+
+#ifndef LOG_TAG
#define LOG_TAG "MyHandler"
+#endif
+
#include <utils/Log.h>
#include "APacketSource.h"
@@ -42,6 +46,12 @@
#include "HTTPBase.h"
+#if LOG_NDEBUG
+#define UNUSED_UNLESS_VERBOSE(x) (void)(x)
+#else
+#define UNUSED_UNLESS_VERBOSE(x)
+#endif
+
// 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 = 10000000ll;
@@ -178,7 +188,7 @@
mConn->connect(mOriginalSessionURL.c_str(), reply);
}
- AString getControlURL(sp<ASessionDescription> desc) {
+ AString getControlURL() {
AString sessionLevelControlURL;
if (mSessionDesc->findAttribute(
0,
@@ -545,7 +555,7 @@
mBaseURL = tmp;
}
- mControlURL = getControlURL(mSessionDesc);
+ mControlURL = getControlURL();
if (mSessionDesc->countTracks() < 2) {
// There's no actual tracks in this session.
@@ -591,7 +601,7 @@
mSeekable = !isLiveStream(mSessionDesc);
- mControlURL = getControlURL(mSessionDesc);
+ mControlURL = getControlURL();
if (mSessionDesc->countTracks() < 2) {
// There's no actual tracks in this session.
@@ -1805,6 +1815,8 @@
bool addMediaTimestamp(
int32_t trackIndex, const TrackInfo *track,
const sp<ABuffer> &accessUnit) {
+ UNUSED_UNLESS_VERBOSE(trackIndex);
+
uint32_t rtpTime;
CHECK(accessUnit->meta()->findInt32(
"rtp-time", (int32_t *)&rtpTime));
diff --git a/media/libstagefright/rtsp/SDPLoader.cpp b/media/libstagefright/rtsp/SDPLoader.cpp
index ed3fa7e..ce1e89d 100644
--- a/media/libstagefright/rtsp/SDPLoader.cpp
+++ b/media/libstagefright/rtsp/SDPLoader.cpp
@@ -18,11 +18,13 @@
#define LOG_TAG "SDPLoader"
#include <utils/Log.h>
-#include "SDPLoader.h"
+#include "include/SDPLoader.h"
#include "ASessionDescription.h"
-#include "HTTPBase.h"
+#include <media/IMediaHTTPConnection.h>
+#include <media/IMediaHTTPService.h>
+#include <media/stagefright/MediaHTTP.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -30,22 +32,15 @@
namespace android {
-SDPLoader::SDPLoader(const sp<AMessage> ¬ify, uint32_t flags, bool uidValid, uid_t uid)
+SDPLoader::SDPLoader(
+ const sp<AMessage> ¬ify,
+ uint32_t flags,
+ const sp<IMediaHTTPService> &httpService)
: mNotify(notify),
mFlags(flags),
- mUIDValid(uidValid),
- mUID(uid),
mNetLooper(new ALooper),
mCancelled(false),
- mHTTPDataSource(
- HTTPBase::Create(
- (mFlags & kFlagIncognito)
- ? HTTPBase::kFlagIncognito
- : 0)) {
- if (mUIDValid) {
- mHTTPDataSource->setUID(mUID);
- }
-
+ mHTTPDataSource(new MediaHTTP(httpService->makeHTTPConnection())) {
mNetLooper->setName("sdp net");
mNetLooper->start(false /* runOnCallingThread */,
false /* canCallJava */,
@@ -130,7 +125,7 @@
ssize_t readSize = mHTTPDataSource->readAt(0, buffer->data(), sdpSize);
if (readSize < 0) {
- ALOGE("Failed to read SDP, error code = %ld", readSize);
+ ALOGE("Failed to read SDP, error code = %d", readSize);
err = UNKNOWN_ERROR;
} else {
desc = new ASessionDescription;
diff --git a/media/libstagefright/tests/DummyRecorder.cpp b/media/libstagefright/tests/DummyRecorder.cpp
index ac37b28..8f17088 100644
--- a/media/libstagefright/tests/DummyRecorder.cpp
+++ b/media/libstagefright/tests/DummyRecorder.cpp
@@ -61,7 +61,7 @@
mSource->stop();
void *dummy;
pthread_join(mThread, &dummy);
- status_t err = (status_t) dummy;
+ status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
ALOGV("Ending the reading thread");
return err;
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index 49ffcd6..a3093d0 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -35,7 +35,6 @@
#include <gui/SurfaceComposerClient.h>
#include <binder/ProcessState.h>
-#include <ui/FramebufferNativeWindow.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBufferGroup.h>
@@ -527,7 +526,8 @@
}
// Dequeuing and queuing the buffer without really filling it in.
-void SurfaceMediaSourceTest::oneBufferPassNoFill(int width, int height ) {
+void SurfaceMediaSourceTest::oneBufferPassNoFill(
+ int /* width */, int /* height */) {
ANativeWindowBuffer* anb;
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
ASSERT_TRUE(anb != NULL);
@@ -746,9 +746,8 @@
CHECK(fd >= 0);
sp<MediaRecorder> mr = SurfaceMediaSourceGLTest::setUpMediaRecorder(fd,
- VIDEO_SOURCE_GRALLOC_BUFFER,
- OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth,
- mYuvTexHeight, 30);
+ VIDEO_SOURCE_SURFACE, OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264,
+ mYuvTexWidth, mYuvTexHeight, 30);
// get the reference to the surfacemediasource living in
// mediaserver that is created by stagefrightrecorder
sp<IGraphicBufferProducer> iST = mr->querySurfaceMediaSourceFromMediaServer();
@@ -880,7 +879,7 @@
}
CHECK(fd >= 0);
- sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_GRALLOC_BUFFER,
+ sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_SURFACE,
OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30);
// get the reference to the surfacemediasource living in
@@ -923,7 +922,7 @@
}
CHECK(fd >= 0);
- sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_GRALLOC_BUFFER,
+ sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_SURFACE,
OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30);
// get the reference to the surfacemediasource living in
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index f099bbd..6a8b9fc 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -9,7 +9,8 @@
TimedTextSRTSource.cpp \
TimedTextPlayer.cpp
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror
+
LOCAL_C_INCLUDES:= \
$(TOP)/frameworks/av/include/media/stagefright/timedtext \
$(TOP)/frameworks/av/media/libstagefright
diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp
index 12fd7f4..71aa21e 100644
--- a/media/libstagefright/timedtext/TimedTextDriver.cpp
+++ b/media/libstagefright/timedtext/TimedTextDriver.cpp
@@ -20,6 +20,7 @@
#include <binder/IPCThreadState.h>
+#include <media/IMediaHTTPService.h>
#include <media/mediaplayer.h>
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/DataSource.h>
@@ -40,9 +41,11 @@
namespace android {
TimedTextDriver::TimedTextDriver(
- const wp<MediaPlayerBase> &listener)
+ const wp<MediaPlayerBase> &listener,
+ const sp<IMediaHTTPService> &httpService)
: mLooper(new ALooper),
mListener(listener),
+ mHTTPService(httpService),
mState(UNINITIALIZED),
mCurrentTrackIndex(UINT_MAX) {
mLooper->setName("TimedTextDriver");
@@ -207,7 +210,7 @@
}
sp<DataSource> dataSource =
- DataSource::CreateFromURI(uri);
+ DataSource::CreateFromURI(mHTTPService, uri);
return createOutOfBandTextSource(trackIndex, mimeType, dataSource);
}
diff --git a/media/libstagefright/timedtext/TimedTextSource.h b/media/libstagefright/timedtext/TimedTextSource.h
index 756cc31..8c1c1cd 100644
--- a/media/libstagefright/timedtext/TimedTextSource.h
+++ b/media/libstagefright/timedtext/TimedTextSource.h
@@ -47,7 +47,7 @@
int64_t *endTimeUs,
Parcel *parcel,
const MediaSource::ReadOptions *options = NULL) = 0;
- virtual status_t extractGlobalDescriptions(Parcel *parcel) {
+ virtual status_t extractGlobalDescriptions(Parcel * /* parcel */) {
return INVALID_OPERATION;
}
virtual sp<MetaData> getFormat();
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index c7d107e..f70454a 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -3,16 +3,9 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- MediaReceiver.cpp \
MediaSender.cpp \
Parameters.cpp \
- rtp/RTPAssembler.cpp \
- rtp/RTPReceiver.cpp \
rtp/RTPSender.cpp \
- sink/DirectRenderer.cpp \
- sink/WifiDisplaySink.cpp \
- SNTPClient.cpp \
- TimeSyncer.cpp \
source/Converter.cpp \
source/MediaPuller.cpp \
source/PlaybackSession.cpp \
@@ -42,87 +35,3 @@
LOCAL_MODULE_TAGS:= optional
include $(BUILD_SHARED_LIBRARY)
-
-################################################################################
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- wfd.cpp \
-
-LOCAL_SHARED_LIBRARIES:= \
- libbinder \
- libgui \
- libmedia \
- libstagefright \
- libstagefright_foundation \
- libstagefright_wfd \
- libutils \
- liblog \
-
-LOCAL_MODULE:= wfd
-
-include $(BUILD_EXECUTABLE)
-
-################################################################################
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- udptest.cpp \
-
-LOCAL_SHARED_LIBRARIES:= \
- libbinder \
- libgui \
- libmedia \
- libstagefright \
- libstagefright_foundation \
- libstagefright_wfd \
- libutils \
- liblog \
-
-LOCAL_MODULE:= udptest
-
-include $(BUILD_EXECUTABLE)
-
-################################################################################
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- rtptest.cpp \
-
-LOCAL_SHARED_LIBRARIES:= \
- libbinder \
- libgui \
- libmedia \
- libstagefright \
- libstagefright_foundation \
- libstagefright_wfd \
- libutils \
- liblog \
-
-LOCAL_MODULE:= rtptest
-
-include $(BUILD_EXECUTABLE)
-
-################################################################################
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- nettest.cpp \
-
-LOCAL_SHARED_LIBRARIES:= \
- libbinder \
- libgui \
- libmedia \
- libstagefright \
- libstagefright_foundation \
- libstagefright_wfd \
- libutils \
- liblog \
-
-LOCAL_MODULE:= nettest
-
-include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp
deleted file mode 100644
index 5524235..0000000
--- a/media/libstagefright/wifi-display/MediaReceiver.cpp
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright 2013, 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 "MediaReceiver"
-#include <utils/Log.h>
-
-#include "MediaReceiver.h"
-
-#include "AnotherPacketSource.h"
-#include "rtp/RTPReceiver.h"
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/Utils.h>
-
-namespace android {
-
-MediaReceiver::MediaReceiver(
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify)
- : mNetSession(netSession),
- mNotify(notify),
- mMode(MODE_UNDEFINED),
- mGeneration(0),
- mInitStatus(OK),
- mInitDoneCount(0) {
-}
-
-MediaReceiver::~MediaReceiver() {
-}
-
-ssize_t MediaReceiver::addTrack(
- RTPReceiver::TransportMode rtpMode,
- RTPReceiver::TransportMode rtcpMode,
- int32_t *localRTPPort) {
- if (mMode != MODE_UNDEFINED) {
- return INVALID_OPERATION;
- }
-
- size_t trackIndex = mTrackInfos.size();
-
- TrackInfo info;
-
- sp<AMessage> notify = new AMessage(kWhatReceiverNotify, id());
- notify->setInt32("generation", mGeneration);
- notify->setSize("trackIndex", trackIndex);
-
- info.mReceiver = new RTPReceiver(mNetSession, notify);
- looper()->registerHandler(info.mReceiver);
-
- info.mReceiver->registerPacketType(
- 33, RTPReceiver::PACKETIZATION_TRANSPORT_STREAM);
-
- info.mReceiver->registerPacketType(
- 96, RTPReceiver::PACKETIZATION_AAC);
-
- info.mReceiver->registerPacketType(
- 97, RTPReceiver::PACKETIZATION_H264);
-
- status_t err = info.mReceiver->initAsync(
- rtpMode,
- rtcpMode,
- localRTPPort);
-
- if (err != OK) {
- looper()->unregisterHandler(info.mReceiver->id());
- info.mReceiver.clear();
-
- return err;
- }
-
- mTrackInfos.push_back(info);
-
- return trackIndex;
-}
-
-status_t MediaReceiver::connectTrack(
- size_t trackIndex,
- const char *remoteHost,
- int32_t remoteRTPPort,
- int32_t remoteRTCPPort) {
- if (trackIndex >= mTrackInfos.size()) {
- return -ERANGE;
- }
-
- TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
- return info->mReceiver->connect(remoteHost, remoteRTPPort, remoteRTCPPort);
-}
-
-status_t MediaReceiver::initAsync(Mode mode) {
- if ((mode == MODE_TRANSPORT_STREAM || mode == MODE_TRANSPORT_STREAM_RAW)
- && mTrackInfos.size() > 1) {
- return INVALID_OPERATION;
- }
-
- sp<AMessage> msg = new AMessage(kWhatInit, id());
- msg->setInt32("mode", mode);
- msg->post();
-
- return OK;
-}
-
-void MediaReceiver::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatInit:
- {
- int32_t mode;
- CHECK(msg->findInt32("mode", &mode));
-
- CHECK_EQ(mMode, MODE_UNDEFINED);
- mMode = (Mode)mode;
-
- if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) {
- notifyInitDone(mInitStatus);
- }
-
- mTSParser = new ATSParser(
- ATSParser::ALIGNED_VIDEO_DATA
- | ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE);
-
- mFormatKnownMask = 0;
- break;
- }
-
- case kWhatReceiverNotify:
- {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
- if (generation != mGeneration) {
- break;
- }
-
- onReceiverNotify(msg);
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void MediaReceiver::onReceiverNotify(const sp<AMessage> &msg) {
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- switch (what) {
- case RTPReceiver::kWhatInitDone:
- {
- ++mInitDoneCount;
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- if (err != OK) {
- mInitStatus = err;
- ++mGeneration;
- }
-
- if (mMode != MODE_UNDEFINED) {
- if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) {
- notifyInitDone(mInitStatus);
- }
- }
- break;
- }
-
- case RTPReceiver::kWhatError:
- {
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- notifyError(err);
- break;
- }
-
- case RTPReceiver::kWhatAccessUnit:
- {
- size_t trackIndex;
- CHECK(msg->findSize("trackIndex", &trackIndex));
-
- sp<ABuffer> accessUnit;
- CHECK(msg->findBuffer("accessUnit", &accessUnit));
-
- int32_t followsDiscontinuity;
- if (!msg->findInt32(
- "followsDiscontinuity", &followsDiscontinuity)) {
- followsDiscontinuity = 0;
- }
-
- if (mMode == MODE_TRANSPORT_STREAM) {
- if (followsDiscontinuity) {
- mTSParser->signalDiscontinuity(
- ATSParser::DISCONTINUITY_TIME, NULL /* extra */);
- }
-
- for (size_t offset = 0;
- offset < accessUnit->size(); offset += 188) {
- status_t err = mTSParser->feedTSPacket(
- accessUnit->data() + offset, 188);
-
- if (err != OK) {
- notifyError(err);
- break;
- }
- }
-
- drainPackets(0 /* trackIndex */, ATSParser::VIDEO);
- drainPackets(1 /* trackIndex */, ATSParser::AUDIO);
- } else {
- postAccessUnit(trackIndex, accessUnit, NULL);
- }
- break;
- }
-
- case RTPReceiver::kWhatPacketLost:
- {
- notifyPacketLost();
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void MediaReceiver::drainPackets(
- size_t trackIndex, ATSParser::SourceType type) {
- sp<AnotherPacketSource> source =
- static_cast<AnotherPacketSource *>(
- mTSParser->getSource(type).get());
-
- if (source == NULL) {
- return;
- }
-
- sp<AMessage> format;
- if (!(mFormatKnownMask & (1ul << trackIndex))) {
- sp<MetaData> meta = source->getFormat();
- CHECK(meta != NULL);
-
- CHECK_EQ((status_t)OK, convertMetaDataToMessage(meta, &format));
-
- mFormatKnownMask |= 1ul << trackIndex;
- }
-
- status_t finalResult;
- while (source->hasBufferAvailable(&finalResult)) {
- sp<ABuffer> accessUnit;
- status_t err = source->dequeueAccessUnit(&accessUnit);
- if (err == OK) {
- postAccessUnit(trackIndex, accessUnit, format);
- format.clear();
- } else if (err != INFO_DISCONTINUITY) {
- notifyError(err);
- }
- }
-
- if (finalResult != OK) {
- notifyError(finalResult);
- }
-}
-
-void MediaReceiver::notifyInitDone(status_t err) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatInitDone);
- notify->setInt32("err", err);
- notify->post();
-}
-
-void MediaReceiver::notifyError(status_t err) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatError);
- notify->setInt32("err", err);
- notify->post();
-}
-
-void MediaReceiver::notifyPacketLost() {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatPacketLost);
- notify->post();
-}
-
-void MediaReceiver::postAccessUnit(
- size_t trackIndex,
- const sp<ABuffer> &accessUnit,
- const sp<AMessage> &format) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatAccessUnit);
- notify->setSize("trackIndex", trackIndex);
- notify->setBuffer("accessUnit", accessUnit);
-
- if (format != NULL) {
- notify->setMessage("format", format);
- }
-
- notify->post();
-}
-
-status_t MediaReceiver::informSender(
- size_t trackIndex, const sp<AMessage> ¶ms) {
- if (trackIndex >= mTrackInfos.size()) {
- return -ERANGE;
- }
-
- TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
- return info->mReceiver->informSender(params);
-}
-
-} // namespace android
-
-
diff --git a/media/libstagefright/wifi-display/MediaReceiver.h b/media/libstagefright/wifi-display/MediaReceiver.h
deleted file mode 100644
index afbb407..0000000
--- a/media/libstagefright/wifi-display/MediaReceiver.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2013, 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/AHandler.h>
-
-#include "ATSParser.h"
-#include "rtp/RTPReceiver.h"
-
-namespace android {
-
-struct ABuffer;
-struct ANetworkSession;
-struct AMessage;
-struct ATSParser;
-
-// This class facilitates receiving of media data for one or more tracks
-// over RTP. Either a 1:1 track to RTP channel mapping is used or a single
-// RTP channel provides the data for a transport stream that is consequently
-// demuxed and its track's data provided to the observer.
-struct MediaReceiver : public AHandler {
- enum {
- kWhatInitDone,
- kWhatError,
- kWhatAccessUnit,
- kWhatPacketLost,
- };
-
- MediaReceiver(
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify);
-
- ssize_t addTrack(
- RTPReceiver::TransportMode rtpMode,
- RTPReceiver::TransportMode rtcpMode,
- int32_t *localRTPPort);
-
- status_t connectTrack(
- size_t trackIndex,
- const char *remoteHost,
- int32_t remoteRTPPort,
- int32_t remoteRTCPPort);
-
- enum Mode {
- MODE_UNDEFINED,
- MODE_TRANSPORT_STREAM,
- MODE_TRANSPORT_STREAM_RAW,
- MODE_ELEMENTARY_STREAMS,
- };
- status_t initAsync(Mode mode);
-
- status_t informSender(size_t trackIndex, const sp<AMessage> ¶ms);
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual ~MediaReceiver();
-
-private:
- enum {
- kWhatInit,
- kWhatReceiverNotify,
- };
-
- struct TrackInfo {
- sp<RTPReceiver> mReceiver;
- };
-
- sp<ANetworkSession> mNetSession;
- sp<AMessage> mNotify;
-
- Mode mMode;
- int32_t mGeneration;
-
- Vector<TrackInfo> mTrackInfos;
-
- status_t mInitStatus;
- size_t mInitDoneCount;
-
- sp<ATSParser> mTSParser;
- uint32_t mFormatKnownMask;
-
- void onReceiverNotify(const sp<AMessage> &msg);
-
- void drainPackets(size_t trackIndex, ATSParser::SourceType type);
-
- void notifyInitDone(status_t err);
- void notifyError(status_t err);
- void notifyPacketLost();
-
- void postAccessUnit(
- size_t trackIndex,
- const sp<ABuffer> &accessUnit,
- const sp<AMessage> &format);
-
- DISALLOW_EVIL_CONSTRUCTORS(MediaReceiver);
-};
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/SNTPClient.cpp b/media/libstagefright/wifi-display/SNTPClient.cpp
deleted file mode 100644
index 5c0af6a..0000000
--- a/media/libstagefright/wifi-display/SNTPClient.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright 2013, 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 "SNTPClient.h"
-
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/Utils.h>
-
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-namespace android {
-
-SNTPClient::SNTPClient() {
-}
-
-status_t SNTPClient::requestTime(const char *host) {
- struct hostent *ent;
- int64_t requestTimeNTP, requestTimeUs;
- ssize_t n;
- int64_t responseTimeUs, responseTimeNTP;
- int64_t originateTimeNTP, receiveTimeNTP, transmitTimeNTP;
- int64_t roundTripTimeNTP, clockOffsetNTP;
-
- status_t err = UNKNOWN_ERROR;
-
- int s = socket(AF_INET, SOCK_DGRAM, 0);
-
- if (s < 0) {
- err = -errno;
-
- goto bail;
- }
-
- ent = gethostbyname(host);
-
- if (ent == NULL) {
- err = -ENOENT;
- goto bail2;
- }
-
- struct sockaddr_in hostAddr;
- memset(hostAddr.sin_zero, 0, sizeof(hostAddr.sin_zero));
- hostAddr.sin_family = AF_INET;
- hostAddr.sin_port = htons(kNTPPort);
- hostAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
-
- uint8_t packet[kNTPPacketSize];
- memset(packet, 0, sizeof(packet));
-
- packet[0] = kNTPModeClient | (kNTPVersion << 3);
-
- requestTimeNTP = getNowNTP();
- requestTimeUs = ALooper::GetNowUs();
- writeTimeStamp(&packet[kNTPTransmitTimeOffset], requestTimeNTP);
-
- n = sendto(
- s, packet, sizeof(packet), 0,
- (const struct sockaddr *)&hostAddr, sizeof(hostAddr));
-
- if (n < 0) {
- err = -errno;
- goto bail2;
- }
-
- memset(packet, 0, sizeof(packet));
-
- do {
- n = recv(s, packet, sizeof(packet), 0);
- } while (n < 0 && errno == EINTR);
-
- if (n < 0) {
- err = -errno;
- goto bail2;
- }
-
- responseTimeUs = ALooper::GetNowUs();
-
- responseTimeNTP = requestTimeNTP + makeNTP(responseTimeUs - requestTimeUs);
-
- originateTimeNTP = readTimeStamp(&packet[kNTPOriginateTimeOffset]);
- receiveTimeNTP = readTimeStamp(&packet[kNTPReceiveTimeOffset]);
- transmitTimeNTP = readTimeStamp(&packet[kNTPTransmitTimeOffset]);
-
- roundTripTimeNTP =
- makeNTP(responseTimeUs - requestTimeUs)
- - (transmitTimeNTP - receiveTimeNTP);
-
- clockOffsetNTP =
- ((receiveTimeNTP - originateTimeNTP)
- + (transmitTimeNTP - responseTimeNTP)) / 2;
-
- mTimeReferenceNTP = responseTimeNTP + clockOffsetNTP;
- mTimeReferenceUs = responseTimeUs;
- mRoundTripTimeNTP = roundTripTimeNTP;
-
- err = OK;
-
-bail2:
- close(s);
- s = -1;
-
-bail:
- return err;
-}
-
-int64_t SNTPClient::adjustTimeUs(int64_t timeUs) const {
- uint64_t nowNTP =
- mTimeReferenceNTP + makeNTP(timeUs - mTimeReferenceUs);
-
- int64_t nowUs =
- (nowNTP >> 32) * 1000000ll
- + ((nowNTP & 0xffffffff) * 1000000ll) / (1ll << 32);
-
- nowUs -= ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
-
- return nowUs;
-}
-
-// static
-void SNTPClient::writeTimeStamp(uint8_t *dst, uint64_t ntpTime) {
- *dst++ = (ntpTime >> 56) & 0xff;
- *dst++ = (ntpTime >> 48) & 0xff;
- *dst++ = (ntpTime >> 40) & 0xff;
- *dst++ = (ntpTime >> 32) & 0xff;
- *dst++ = (ntpTime >> 24) & 0xff;
- *dst++ = (ntpTime >> 16) & 0xff;
- *dst++ = (ntpTime >> 8) & 0xff;
- *dst++ = ntpTime & 0xff;
-}
-
-// static
-uint64_t SNTPClient::readTimeStamp(const uint8_t *dst) {
- return U64_AT(dst);
-}
-
-// static
-uint64_t SNTPClient::getNowNTP() {
- struct timeval tv;
- gettimeofday(&tv, NULL /* time zone */);
-
- uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec;
-
- nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
-
- return makeNTP(nowUs);
-}
-
-// static
-uint64_t SNTPClient::makeNTP(uint64_t deltaUs) {
- uint64_t hi = deltaUs / 1000000ll;
- uint64_t lo = ((1ll << 32) * (deltaUs % 1000000ll)) / 1000000ll;
-
- return (hi << 32) | lo;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/SNTPClient.h b/media/libstagefright/wifi-display/SNTPClient.h
deleted file mode 100644
index 967d1fc..0000000
--- a/media/libstagefright/wifi-display/SNTPClient.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2013, 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 SNTP_CLIENT_H_
-
-#define SNTP_CLIENT_H_
-
-#include <media/stagefright/foundation/ABase.h>
-#include <utils/Errors.h>
-
-namespace android {
-
-// Implementation of the SNTP (Simple Network Time Protocol)
-struct SNTPClient {
- SNTPClient();
-
- status_t requestTime(const char *host);
-
- // given a time obtained from ALooper::GetNowUs()
- // return the number of us elapsed since Jan 1 1970 00:00:00 (UTC).
- int64_t adjustTimeUs(int64_t timeUs) const;
-
-private:
- enum {
- kNTPPort = 123,
- kNTPPacketSize = 48,
- kNTPModeClient = 3,
- kNTPVersion = 3,
- kNTPTransmitTimeOffset = 40,
- kNTPOriginateTimeOffset = 24,
- kNTPReceiveTimeOffset = 32,
- };
-
- uint64_t mTimeReferenceNTP;
- int64_t mTimeReferenceUs;
- int64_t mRoundTripTimeNTP;
-
- static void writeTimeStamp(uint8_t *dst, uint64_t ntpTime);
- static uint64_t readTimeStamp(const uint8_t *dst);
-
- static uint64_t getNowNTP();
- static uint64_t makeNTP(uint64_t deltaUs);
-
- DISALLOW_EVIL_CONSTRUCTORS(SNTPClient);
-};
-
-} // namespace android
-
-#endif // SNTP_CLIENT_H_
diff --git a/media/libstagefright/wifi-display/TimeSyncer.cpp b/media/libstagefright/wifi-display/TimeSyncer.cpp
deleted file mode 100644
index 0f4d93a..0000000
--- a/media/libstagefright/wifi-display/TimeSyncer.cpp
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * Copyright 2013, 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_NEBUG 0
-#define LOG_TAG "TimeSyncer"
-#include <utils/Log.h>
-
-#include "TimeSyncer.h"
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-#include <media/stagefright/Utils.h>
-
-namespace android {
-
-TimeSyncer::TimeSyncer(
- const sp<ANetworkSession> &netSession, const sp<AMessage> ¬ify)
- : mNetSession(netSession),
- mNotify(notify),
- mIsServer(false),
- mConnected(false),
- mUDPSession(0),
- mSeqNo(0),
- mTotalTimeUs(0.0),
- mPendingT1(0ll),
- mTimeoutGeneration(0) {
-}
-
-TimeSyncer::~TimeSyncer() {
-}
-
-void TimeSyncer::startServer(unsigned localPort) {
- sp<AMessage> msg = new AMessage(kWhatStartServer, id());
- msg->setInt32("localPort", localPort);
- msg->post();
-}
-
-void TimeSyncer::startClient(const char *remoteHost, unsigned remotePort) {
- sp<AMessage> msg = new AMessage(kWhatStartClient, id());
- msg->setString("remoteHost", remoteHost);
- msg->setInt32("remotePort", remotePort);
- msg->post();
-}
-
-void TimeSyncer::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatStartClient:
- {
- AString remoteHost;
- CHECK(msg->findString("remoteHost", &remoteHost));
-
- int32_t remotePort;
- CHECK(msg->findInt32("remotePort", &remotePort));
-
- sp<AMessage> notify = new AMessage(kWhatUDPNotify, id());
-
- CHECK_EQ((status_t)OK,
- mNetSession->createUDPSession(
- 0 /* localPort */,
- remoteHost.c_str(),
- remotePort,
- notify,
- &mUDPSession));
-
- postSendPacket();
- break;
- }
-
- case kWhatStartServer:
- {
- mIsServer = true;
-
- int32_t localPort;
- CHECK(msg->findInt32("localPort", &localPort));
-
- sp<AMessage> notify = new AMessage(kWhatUDPNotify, id());
-
- CHECK_EQ((status_t)OK,
- mNetSession->createUDPSession(
- localPort, notify, &mUDPSession));
-
- break;
- }
-
- case kWhatSendPacket:
- {
- if (mHistory.size() == 0) {
- ALOGI("starting batch");
- }
-
- TimeInfo ti;
- memset(&ti, 0, sizeof(ti));
-
- ti.mT1 = ALooper::GetNowUs();
-
- CHECK_EQ((status_t)OK,
- mNetSession->sendRequest(
- mUDPSession, &ti, sizeof(ti)));
-
- mPendingT1 = ti.mT1;
- postTimeout();
- break;
- }
-
- case kWhatTimedOut:
- {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
-
- if (generation != mTimeoutGeneration) {
- break;
- }
-
- ALOGI("timed out, sending another request");
- postSendPacket();
- break;
- }
-
- case kWhatUDPNotify:
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::kWhatError:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- AString detail;
- CHECK(msg->findString("detail", &detail));
-
- ALOGE("An error occurred in session %d (%d, '%s/%s').",
- sessionID,
- err,
- detail.c_str(),
- strerror(-err));
-
- mNetSession->destroySession(sessionID);
-
- cancelTimeout();
-
- notifyError(err);
- break;
- }
-
- case ANetworkSession::kWhatDatagram:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- sp<ABuffer> packet;
- CHECK(msg->findBuffer("data", &packet));
-
- int64_t arrivalTimeUs;
- CHECK(packet->meta()->findInt64(
- "arrivalTimeUs", &arrivalTimeUs));
-
- CHECK_EQ(packet->size(), sizeof(TimeInfo));
-
- TimeInfo *ti = (TimeInfo *)packet->data();
-
- if (mIsServer) {
- if (!mConnected) {
- AString fromAddr;
- CHECK(msg->findString("fromAddr", &fromAddr));
-
- int32_t fromPort;
- CHECK(msg->findInt32("fromPort", &fromPort));
-
- CHECK_EQ((status_t)OK,
- mNetSession->connectUDPSession(
- mUDPSession, fromAddr.c_str(), fromPort));
-
- mConnected = true;
- }
-
- ti->mT2 = arrivalTimeUs;
- ti->mT3 = ALooper::GetNowUs();
-
- CHECK_EQ((status_t)OK,
- mNetSession->sendRequest(
- mUDPSession, ti, sizeof(*ti)));
- } else {
- if (ti->mT1 != mPendingT1) {
- break;
- }
-
- cancelTimeout();
- mPendingT1 = 0;
-
- ti->mT4 = arrivalTimeUs;
-
- // One way delay for a packet to travel from client
- // to server or back (assumed to be the same either way).
- int64_t delay =
- (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2;
-
- // Offset between the client clock (T1, T4) and the
- // server clock (T2, T3) timestamps.
- int64_t offset =
- (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2;
-
- mHistory.push_back(*ti);
-
- ALOGV("delay = %lld us,\toffset %lld us",
- delay,
- offset);
-
- if (mHistory.size() < kNumPacketsPerBatch) {
- postSendPacket(1000000ll / 30);
- } else {
- notifyOffset();
-
- ALOGI("batch done");
-
- mHistory.clear();
- postSendPacket(kBatchDelayUs);
- }
- }
- break;
- }
-
- default:
- TRESPASS();
- }
-
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void TimeSyncer::postSendPacket(int64_t delayUs) {
- (new AMessage(kWhatSendPacket, id()))->post(delayUs);
-}
-
-void TimeSyncer::postTimeout() {
- sp<AMessage> msg = new AMessage(kWhatTimedOut, id());
- msg->setInt32("generation", mTimeoutGeneration);
- msg->post(kTimeoutDelayUs);
-}
-
-void TimeSyncer::cancelTimeout() {
- ++mTimeoutGeneration;
-}
-
-void TimeSyncer::notifyError(status_t err) {
- if (mNotify == NULL) {
- looper()->stop();
- return;
- }
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatError);
- notify->setInt32("err", err);
- notify->post();
-}
-
-// static
-int TimeSyncer::CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2) {
- int64_t rt1 = ti1->mT4 - ti1->mT1;
- int64_t rt2 = ti2->mT4 - ti2->mT1;
-
- if (rt1 < rt2) {
- return -1;
- } else if (rt1 > rt2) {
- return 1;
- }
-
- return 0;
-}
-
-void TimeSyncer::notifyOffset() {
- mHistory.sort(CompareRountripTime);
-
- int64_t sum = 0ll;
- size_t count = 0;
-
- // Only consider the third of the information associated with the best
- // (smallest) roundtrip times.
- for (size_t i = 0; i < mHistory.size() / 3; ++i) {
- const TimeInfo *ti = &mHistory[i];
-
-#if 0
- // One way delay for a packet to travel from client
- // to server or back (assumed to be the same either way).
- int64_t delay =
- (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2;
-#endif
-
- // Offset between the client clock (T1, T4) and the
- // server clock (T2, T3) timestamps.
- int64_t offset =
- (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2;
-
- ALOGV("(%d) RT: %lld us, offset: %lld us",
- i, ti->mT4 - ti->mT1, offset);
-
- sum += offset;
- ++count;
- }
-
- if (mNotify == NULL) {
- ALOGI("avg. offset is %lld", sum / count);
- return;
- }
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatTimeOffset);
- notify->setInt64("offset", sum / count);
- notify->post();
-}
-
-} // namespace android
diff --git a/media/libstagefright/wifi-display/TimeSyncer.h b/media/libstagefright/wifi-display/TimeSyncer.h
deleted file mode 100644
index 4e7571f..0000000
--- a/media/libstagefright/wifi-display/TimeSyncer.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TIME_SYNCER_H_
-
-#define TIME_SYNCER_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-struct ANetworkSession;
-
-/*
- TimeSyncer allows us to synchronize time between a client and a server.
- The client sends a UDP packet containing its send-time to the server,
- the server sends that packet back to the client amended with information
- about when it was received as well as the time the reply was sent back.
- Finally the client receives the reply and has now enough information to
- compute the clock offset between client and server assuming that packet
- exchange is symmetric, i.e. time for a packet client->server and
- server->client is roughly equal.
- This exchange is repeated a number of times and the average offset computed
- over the 30% of packets that had the lowest roundtrip times.
- The offset is determined every 10 secs to account for slight differences in
- clock frequency.
-*/
-struct TimeSyncer : public AHandler {
- enum {
- kWhatError,
- kWhatTimeOffset,
- };
- TimeSyncer(
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify);
-
- void startServer(unsigned localPort);
- void startClient(const char *remoteHost, unsigned remotePort);
-
-protected:
- virtual ~TimeSyncer();
-
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatStartServer,
- kWhatStartClient,
- kWhatUDPNotify,
- kWhatSendPacket,
- kWhatTimedOut,
- };
-
- struct TimeInfo {
- int64_t mT1; // client timestamp at send
- int64_t mT2; // server timestamp at receive
- int64_t mT3; // server timestamp at send
- int64_t mT4; // client timestamp at receive
- };
-
- enum {
- kNumPacketsPerBatch = 30,
- };
- static const int64_t kTimeoutDelayUs = 500000ll;
- static const int64_t kBatchDelayUs = 60000000ll; // every minute
-
- sp<ANetworkSession> mNetSession;
- sp<AMessage> mNotify;
-
- bool mIsServer;
- bool mConnected;
- int32_t mUDPSession;
- uint32_t mSeqNo;
- double mTotalTimeUs;
-
- Vector<TimeInfo> mHistory;
-
- int64_t mPendingT1;
- int32_t mTimeoutGeneration;
-
- void postSendPacket(int64_t delayUs = 0ll);
-
- void postTimeout();
- void cancelTimeout();
-
- void notifyError(status_t err);
- void notifyOffset();
-
- static int CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2);
-
- DISALLOW_EVIL_CONSTRUCTORS(TimeSyncer);
-};
-
-} // namespace android
-
-#endif // TIME_SYNCER_H_
diff --git a/media/libstagefright/wifi-display/nettest.cpp b/media/libstagefright/wifi-display/nettest.cpp
deleted file mode 100644
index 73c0d80..0000000
--- a/media/libstagefright/wifi-display/nettest.cpp
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Copyright 2013, 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_NEBUG 0
-#define LOG_TAG "nettest"
-#include <utils/Log.h>
-
-#include "TimeSyncer.h"
-
-#include <binder/ProcessState.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/NuMediaExtractor.h>
-#include <media/stagefright/Utils.h>
-
-namespace android {
-
-struct TestHandler : public AHandler {
- TestHandler(const sp<ANetworkSession> &netSession);
-
- void listen(int32_t port);
- void connect(const char *host, int32_t port);
-
-protected:
- virtual ~TestHandler();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kTimeSyncerPort = 8123,
- };
-
- enum {
- kWhatListen,
- kWhatConnect,
- kWhatTimeSyncerNotify,
- kWhatNetNotify,
- kWhatSendMore,
- kWhatStop,
- };
-
- sp<ANetworkSession> mNetSession;
- sp<TimeSyncer> mTimeSyncer;
-
- int32_t mServerSessionID;
- int32_t mSessionID;
-
- int64_t mTimeOffsetUs;
- bool mTimeOffsetValid;
-
- int32_t mCounter;
-
- int64_t mMaxDelayMs;
-
- void dumpDelay(int32_t counter, int64_t delayMs);
-
- DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
-};
-
-TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
- : mNetSession(netSession),
- mServerSessionID(0),
- mSessionID(0),
- mTimeOffsetUs(-1ll),
- mTimeOffsetValid(false),
- mCounter(0),
- mMaxDelayMs(-1ll) {
-}
-
-TestHandler::~TestHandler() {
-}
-
-void TestHandler::listen(int32_t port) {
- sp<AMessage> msg = new AMessage(kWhatListen, id());
- msg->setInt32("port", port);
- msg->post();
-}
-
-void TestHandler::connect(const char *host, int32_t port) {
- sp<AMessage> msg = new AMessage(kWhatConnect, id());
- msg->setString("host", host);
- msg->setInt32("port", port);
- msg->post();
-}
-
-void TestHandler::dumpDelay(int32_t counter, int64_t delayMs) {
- static const int64_t kMinDelayMs = 0;
- static const int64_t kMaxDelayMs = 300;
-
- const char *kPattern = "########################################";
- size_t kPatternSize = strlen(kPattern);
-
- int n = (kPatternSize * (delayMs - kMinDelayMs))
- / (kMaxDelayMs - kMinDelayMs);
-
- if (n < 0) {
- n = 0;
- } else if ((size_t)n > kPatternSize) {
- n = kPatternSize;
- }
-
- if (delayMs > mMaxDelayMs) {
- mMaxDelayMs = delayMs;
- }
-
- ALOGI("[%d] (%4lld ms / %4lld ms) %s",
- counter,
- delayMs,
- mMaxDelayMs,
- kPattern + kPatternSize - n);
-}
-
-void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatListen:
- {
- sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
- mTimeSyncer = new TimeSyncer(mNetSession, notify);
- looper()->registerHandler(mTimeSyncer);
-
- notify = new AMessage(kWhatNetNotify, id());
-
- int32_t port;
- CHECK(msg->findInt32("port", &port));
-
- struct in_addr ifaceAddr;
- ifaceAddr.s_addr = INADDR_ANY;
-
- CHECK_EQ((status_t)OK,
- mNetSession->createTCPDatagramSession(
- ifaceAddr,
- port,
- notify,
- &mServerSessionID));
- break;
- }
-
- case kWhatConnect:
- {
- sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
- mTimeSyncer = new TimeSyncer(mNetSession, notify);
- looper()->registerHandler(mTimeSyncer);
- mTimeSyncer->startServer(kTimeSyncerPort);
-
- AString host;
- CHECK(msg->findString("host", &host));
-
- int32_t port;
- CHECK(msg->findInt32("port", &port));
-
- notify = new AMessage(kWhatNetNotify, id());
-
- CHECK_EQ((status_t)OK,
- mNetSession->createTCPDatagramSession(
- 0 /* localPort */,
- host.c_str(),
- port,
- notify,
- &mSessionID));
- break;
- }
-
- case kWhatNetNotify:
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::kWhatConnected:
- {
- ALOGI("kWhatConnected");
-
- (new AMessage(kWhatSendMore, id()))->post();
- break;
- }
-
- case ANetworkSession::kWhatClientConnected:
- {
- ALOGI("kWhatClientConnected");
-
- CHECK_EQ(mSessionID, 0);
- CHECK(msg->findInt32("sessionID", &mSessionID));
-
- AString clientIP;
- CHECK(msg->findString("client-ip", &clientIP));
-
- mTimeSyncer->startClient(clientIP.c_str(), kTimeSyncerPort);
- break;
- }
-
- case ANetworkSession::kWhatDatagram:
- {
- sp<ABuffer> packet;
- CHECK(msg->findBuffer("data", &packet));
-
- CHECK_EQ(packet->size(), 12u);
-
- int32_t counter = U32_AT(packet->data());
- int64_t timeUs = U64_AT(packet->data() + 4);
-
- if (mTimeOffsetValid) {
- timeUs -= mTimeOffsetUs;
- int64_t nowUs = ALooper::GetNowUs();
- int64_t delayMs = (nowUs - timeUs) / 1000ll;
-
- dumpDelay(counter, delayMs);
- } else {
- ALOGI("received %d", counter);
- }
- break;
- }
-
- case ANetworkSession::kWhatError:
- {
- ALOGE("kWhatError");
- break;
- }
-
- default:
- TRESPASS();
- }
- break;
- }
-
- case kWhatTimeSyncerNotify:
- {
- CHECK(msg->findInt64("offset", &mTimeOffsetUs));
- mTimeOffsetValid = true;
- break;
- }
-
- case kWhatSendMore:
- {
- uint8_t buffer[4 + 8];
- buffer[0] = mCounter >> 24;
- buffer[1] = (mCounter >> 16) & 0xff;
- buffer[2] = (mCounter >> 8) & 0xff;
- buffer[3] = mCounter & 0xff;
-
- int64_t nowUs = ALooper::GetNowUs();
-
- buffer[4] = nowUs >> 56;
- buffer[5] = (nowUs >> 48) & 0xff;
- buffer[6] = (nowUs >> 40) & 0xff;
- buffer[7] = (nowUs >> 32) & 0xff;
- buffer[8] = (nowUs >> 24) & 0xff;
- buffer[9] = (nowUs >> 16) & 0xff;
- buffer[10] = (nowUs >> 8) & 0xff;
- buffer[11] = nowUs & 0xff;
-
- ++mCounter;
-
- CHECK_EQ((status_t)OK,
- mNetSession->sendRequest(
- mSessionID,
- buffer,
- sizeof(buffer),
- true /* timeValid */,
- nowUs));
-
- msg->post(100000ll);
- break;
- }
-
- case kWhatStop:
- {
- if (mSessionID != 0) {
- mNetSession->destroySession(mSessionID);
- mSessionID = 0;
- }
-
- if (mServerSessionID != 0) {
- mNetSession->destroySession(mServerSessionID);
- mServerSessionID = 0;
- }
-
- looper()->stop();
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-} // namespace android
-
-static void usage(const char *me) {
- fprintf(stderr,
- "usage: %s -c host:port\tconnect to remote host\n"
- " -l port \tlisten\n",
- me);
-}
-
-int main(int argc, char **argv) {
- using namespace android;
-
- // srand(time(NULL));
-
- ProcessState::self()->startThreadPool();
-
- DataSource::RegisterDefaultSniffers();
-
- int32_t connectToPort = -1;
- AString connectToHost;
-
- int32_t listenOnPort = -1;
-
- int res;
- while ((res = getopt(argc, argv, "hc:l:")) >= 0) {
- switch (res) {
- case 'c':
- {
- const char *colonPos = strrchr(optarg, ':');
-
- if (colonPos == NULL) {
- usage(argv[0]);
- exit(1);
- }
-
- connectToHost.setTo(optarg, colonPos - optarg);
-
- char *end;
- connectToPort = strtol(colonPos + 1, &end, 10);
-
- if (*end != '\0' || end == colonPos + 1
- || connectToPort < 0 || connectToPort > 65535) {
- fprintf(stderr, "Illegal port specified.\n");
- exit(1);
- }
- break;
- }
-
- case 'l':
- {
- char *end;
- listenOnPort = strtol(optarg, &end, 10);
-
- if (*end != '\0' || end == optarg
- || listenOnPort < 0 || listenOnPort > 65535) {
- fprintf(stderr, "Illegal port specified.\n");
- exit(1);
- }
- break;
- }
-
- case '?':
- case 'h':
- usage(argv[0]);
- exit(1);
- }
- }
-
- if ((listenOnPort < 0 && connectToPort < 0)
- || (listenOnPort >= 0 && connectToPort >= 0)) {
- fprintf(stderr,
- "You need to select either client or server mode.\n");
- exit(1);
- }
-
- sp<ANetworkSession> netSession = new ANetworkSession;
- netSession->start();
-
- sp<ALooper> looper = new ALooper;
-
- sp<TestHandler> handler = new TestHandler(netSession);
- looper->registerHandler(handler);
-
- if (listenOnPort) {
- handler->listen(listenOnPort);
- }
-
- if (connectToPort >= 0) {
- handler->connect(connectToHost.c_str(), connectToPort);
- }
-
- looper->start(true /* runOnCallingThread */);
-
- return 0;
-}
diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
deleted file mode 100644
index 7a96081..0000000
--- a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright 2013, 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 "RTPAssembler"
-#include <utils/Log.h>
-
-#include "RTPAssembler.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/MediaErrors.h>
-
-namespace android {
-
-RTPReceiver::Assembler::Assembler(const sp<AMessage> ¬ify)
- : mNotify(notify) {
-}
-
-void RTPReceiver::Assembler::postAccessUnit(
- const sp<ABuffer> &accessUnit, bool followsDiscontinuity) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", RTPReceiver::kWhatAccessUnit);
- notify->setBuffer("accessUnit", accessUnit);
- notify->setInt32("followsDiscontinuity", followsDiscontinuity);
- notify->post();
-}
-////////////////////////////////////////////////////////////////////////////////
-
-RTPReceiver::TSAssembler::TSAssembler(const sp<AMessage> ¬ify)
- : Assembler(notify),
- mSawDiscontinuity(false) {
-}
-
-void RTPReceiver::TSAssembler::signalDiscontinuity() {
- mSawDiscontinuity = true;
-}
-
-status_t RTPReceiver::TSAssembler::processPacket(const sp<ABuffer> &packet) {
- int32_t rtpTime;
- CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
-
- packet->meta()->setInt64("timeUs", (rtpTime * 100ll) / 9);
-
- postAccessUnit(packet, mSawDiscontinuity);
-
- if (mSawDiscontinuity) {
- mSawDiscontinuity = false;
- }
-
- return OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-RTPReceiver::H264Assembler::H264Assembler(const sp<AMessage> ¬ify)
- : Assembler(notify),
- mState(0),
- mIndicator(0),
- mNALType(0),
- mAccessUnitRTPTime(0) {
-}
-
-void RTPReceiver::H264Assembler::signalDiscontinuity() {
- reset();
-}
-
-status_t RTPReceiver::H264Assembler::processPacket(const sp<ABuffer> &packet) {
- status_t err = internalProcessPacket(packet);
-
- if (err != OK) {
- reset();
- }
-
- return err;
-}
-
-status_t RTPReceiver::H264Assembler::internalProcessPacket(
- const sp<ABuffer> &packet) {
- const uint8_t *data = packet->data();
- size_t size = packet->size();
-
- switch (mState) {
- case 0:
- {
- if (size < 1 || (data[0] & 0x80)) {
- ALOGV("Malformed H264 RTP packet (empty or F-bit set)");
- return ERROR_MALFORMED;
- }
-
- unsigned nalType = data[0] & 0x1f;
- if (nalType >= 1 && nalType <= 23) {
- addSingleNALUnit(packet);
- ALOGV("added single NAL packet");
- } else if (nalType == 28) {
- // FU-A
- unsigned indicator = data[0];
- CHECK((indicator & 0x1f) == 28);
-
- if (size < 2) {
- ALOGV("Malformed H264 FU-A packet (single byte)");
- return ERROR_MALFORMED;
- }
-
- if (!(data[1] & 0x80)) {
- ALOGV("Malformed H264 FU-A packet (no start bit)");
- return ERROR_MALFORMED;
- }
-
- mIndicator = data[0];
- mNALType = data[1] & 0x1f;
- uint32_t nri = (data[0] >> 5) & 3;
-
- clearAccumulator();
-
- uint8_t byte = mNALType | (nri << 5);
- appendToAccumulator(&byte, 1);
- appendToAccumulator(data + 2, size - 2);
-
- int32_t rtpTime;
- CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
- mAccumulator->meta()->setInt32("rtp-time", rtpTime);
-
- if (data[1] & 0x40) {
- // Huh? End bit also set on the first buffer.
- addSingleNALUnit(mAccumulator);
- clearAccumulator();
-
- ALOGV("added FU-A");
- break;
- }
-
- mState = 1;
- } else if (nalType == 24) {
- // STAP-A
-
- status_t err = addSingleTimeAggregationPacket(packet);
- if (err != OK) {
- return err;
- }
- } else {
- ALOGV("Malformed H264 packet (unknown type %d)", nalType);
- return ERROR_UNSUPPORTED;
- }
- break;
- }
-
- case 1:
- {
- if (size < 2
- || data[0] != mIndicator
- || (data[1] & 0x1f) != mNALType
- || (data[1] & 0x80)) {
- ALOGV("Malformed H264 FU-A packet (indicator, "
- "type or start bit mismatch)");
-
- return ERROR_MALFORMED;
- }
-
- appendToAccumulator(data + 2, size - 2);
-
- if (data[1] & 0x40) {
- addSingleNALUnit(mAccumulator);
-
- clearAccumulator();
- mState = 0;
-
- ALOGV("added FU-A");
- }
- break;
- }
-
- default:
- TRESPASS();
- }
-
- int32_t marker;
- CHECK(packet->meta()->findInt32("M", &marker));
-
- if (marker) {
- flushAccessUnit();
- }
-
- return OK;
-}
-
-void RTPReceiver::H264Assembler::reset() {
- mNALUnits.clear();
-
- clearAccumulator();
- mState = 0;
-}
-
-void RTPReceiver::H264Assembler::clearAccumulator() {
- if (mAccumulator != NULL) {
- // XXX Too expensive.
- mAccumulator.clear();
- }
-}
-
-void RTPReceiver::H264Assembler::appendToAccumulator(
- const void *data, size_t size) {
- if (mAccumulator == NULL) {
- mAccumulator = new ABuffer(size);
- memcpy(mAccumulator->data(), data, size);
- return;
- }
-
- if (mAccumulator->size() + size > mAccumulator->capacity()) {
- sp<ABuffer> buf = new ABuffer(mAccumulator->size() + size);
- memcpy(buf->data(), mAccumulator->data(), mAccumulator->size());
- buf->setRange(0, mAccumulator->size());
-
- int32_t rtpTime;
- if (mAccumulator->meta()->findInt32("rtp-time", &rtpTime)) {
- buf->meta()->setInt32("rtp-time", rtpTime);
- }
-
- mAccumulator = buf;
- }
-
- memcpy(mAccumulator->data() + mAccumulator->size(), data, size);
- mAccumulator->setRange(0, mAccumulator->size() + size);
-}
-
-void RTPReceiver::H264Assembler::addSingleNALUnit(const sp<ABuffer> &packet) {
- if (mNALUnits.empty()) {
- int32_t rtpTime;
- CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
-
- mAccessUnitRTPTime = rtpTime;
- }
-
- mNALUnits.push_back(packet);
-}
-
-void RTPReceiver::H264Assembler::flushAccessUnit() {
- if (mNALUnits.empty()) {
- return;
- }
-
- size_t totalSize = 0;
- for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
- it != mNALUnits.end(); ++it) {
- totalSize += 4 + (*it)->size();
- }
-
- sp<ABuffer> accessUnit = new ABuffer(totalSize);
- size_t offset = 0;
- for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
- it != mNALUnits.end(); ++it) {
- const sp<ABuffer> nalUnit = *it;
-
- memcpy(accessUnit->data() + offset, "\x00\x00\x00\x01", 4);
-
- memcpy(accessUnit->data() + offset + 4,
- nalUnit->data(),
- nalUnit->size());
-
- offset += 4 + nalUnit->size();
- }
-
- mNALUnits.clear();
-
- accessUnit->meta()->setInt64("timeUs", mAccessUnitRTPTime * 100ll / 9ll);
- postAccessUnit(accessUnit, false /* followsDiscontinuity */);
-}
-
-status_t RTPReceiver::H264Assembler::addSingleTimeAggregationPacket(
- const sp<ABuffer> &packet) {
- const uint8_t *data = packet->data();
- size_t size = packet->size();
-
- if (size < 3) {
- ALOGV("Malformed H264 STAP-A packet (too small)");
- return ERROR_MALFORMED;
- }
-
- int32_t rtpTime;
- CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
-
- ++data;
- --size;
- while (size >= 2) {
- size_t nalSize = (data[0] << 8) | data[1];
-
- if (size < nalSize + 2) {
- ALOGV("Malformed H264 STAP-A packet (incomplete NAL unit)");
- return ERROR_MALFORMED;
- }
-
- sp<ABuffer> unit = new ABuffer(nalSize);
- memcpy(unit->data(), &data[2], nalSize);
-
- unit->meta()->setInt32("rtp-time", rtpTime);
-
- addSingleNALUnit(unit);
-
- data += 2 + nalSize;
- size -= 2 + nalSize;
- }
-
- if (size != 0) {
- ALOGV("Unexpected padding at end of STAP-A packet.");
- }
-
- ALOGV("added STAP-A");
-
- return OK;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.h b/media/libstagefright/wifi-display/rtp/RTPAssembler.h
deleted file mode 100644
index e456d32..0000000
--- a/media/libstagefright/wifi-display/rtp/RTPAssembler.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2013, 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 RTP_ASSEMBLER_H_
-
-#define RTP_ASSEMBLER_H_
-
-#include "RTPReceiver.h"
-
-namespace android {
-
-// A helper class to reassemble the payload of RTP packets into access
-// units depending on the packetization scheme.
-struct RTPReceiver::Assembler : public RefBase {
- Assembler(const sp<AMessage> ¬ify);
-
- virtual void signalDiscontinuity() = 0;
- virtual status_t processPacket(const sp<ABuffer> &packet) = 0;
-
-protected:
- virtual ~Assembler() {}
-
- void postAccessUnit(
- const sp<ABuffer> &accessUnit, bool followsDiscontinuity);
-
-private:
- sp<AMessage> mNotify;
-
- DISALLOW_EVIL_CONSTRUCTORS(Assembler);
-};
-
-struct RTPReceiver::TSAssembler : public RTPReceiver::Assembler {
- TSAssembler(const sp<AMessage> ¬ify);
-
- virtual void signalDiscontinuity();
- virtual status_t processPacket(const sp<ABuffer> &packet);
-
-private:
- bool mSawDiscontinuity;
-
- DISALLOW_EVIL_CONSTRUCTORS(TSAssembler);
-};
-
-struct RTPReceiver::H264Assembler : public RTPReceiver::Assembler {
- H264Assembler(const sp<AMessage> ¬ify);
-
- virtual void signalDiscontinuity();
- virtual status_t processPacket(const sp<ABuffer> &packet);
-
-private:
- int32_t mState;
-
- uint8_t mIndicator;
- uint8_t mNALType;
-
- sp<ABuffer> mAccumulator;
-
- List<sp<ABuffer> > mNALUnits;
- int32_t mAccessUnitRTPTime;
-
- status_t internalProcessPacket(const sp<ABuffer> &packet);
-
- void addSingleNALUnit(const sp<ABuffer> &packet);
- status_t addSingleTimeAggregationPacket(const sp<ABuffer> &packet);
-
- void flushAccessUnit();
-
- void clearAccumulator();
- void appendToAccumulator(const void *data, size_t size);
-
- void reset();
-
- DISALLOW_EVIL_CONSTRUCTORS(H264Assembler);
-};
-
-} // namespace android
-
-#endif // RTP_ASSEMBLER_H_
-
diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
deleted file mode 100644
index 3b3bd63..0000000
--- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
+++ /dev/null
@@ -1,1152 +0,0 @@
-/*
- * Copyright 2013, 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 "RTPReceiver"
-#include <utils/Log.h>
-
-#include "RTPAssembler.h"
-#include "RTPReceiver.h"
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-#define TRACK_PACKET_LOSS 0
-
-namespace android {
-
-////////////////////////////////////////////////////////////////////////////////
-
-struct RTPReceiver::Source : public AHandler {
- Source(RTPReceiver *receiver, uint32_t ssrc);
-
- void onPacketReceived(uint16_t seq, const sp<ABuffer> &buffer);
-
- void addReportBlock(uint32_t ssrc, const sp<ABuffer> &buf);
-
-protected:
- virtual ~Source();
-
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatRetransmit,
- kWhatDeclareLost,
- };
-
- static const uint32_t kMinSequential = 2;
- static const uint32_t kMaxDropout = 3000;
- static const uint32_t kMaxMisorder = 100;
- static const uint32_t kRTPSeqMod = 1u << 16;
- static const int64_t kReportIntervalUs = 10000000ll;
-
- RTPReceiver *mReceiver;
- uint32_t mSSRC;
- bool mFirst;
- uint16_t mMaxSeq;
- uint32_t mCycles;
- uint32_t mBaseSeq;
- uint32_t mReceived;
- uint32_t mExpectedPrior;
- uint32_t mReceivedPrior;
-
- int64_t mFirstArrivalTimeUs;
- int64_t mFirstRTPTimeUs;
-
- // Ordered by extended seq number.
- List<sp<ABuffer> > mPackets;
-
- enum StatusBits {
- STATUS_DECLARED_LOST = 1,
- STATUS_REQUESTED_RETRANSMISSION = 2,
- STATUS_ARRIVED_LATE = 4,
- };
-#if TRACK_PACKET_LOSS
- KeyedVector<int32_t, uint32_t> mLostPackets;
-#endif
-
- void modifyPacketStatus(int32_t extSeqNo, uint32_t mask);
-
- int32_t mAwaitingExtSeqNo;
- bool mRequestedRetransmission;
-
- int32_t mActivePacketType;
- sp<Assembler> mActiveAssembler;
-
- int64_t mNextReportTimeUs;
-
- int32_t mNumDeclaredLost;
- int32_t mNumDeclaredLostPrior;
-
- int32_t mRetransmitGeneration;
- int32_t mDeclareLostGeneration;
- bool mDeclareLostTimerPending;
-
- void queuePacket(const sp<ABuffer> &packet);
- void dequeueMore();
-
- sp<ABuffer> getNextPacket();
- void resync();
-
- void postRetransmitTimer(int64_t delayUs);
- void postDeclareLostTimer(int64_t delayUs);
- void cancelTimers();
-
- DISALLOW_EVIL_CONSTRUCTORS(Source);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-RTPReceiver::Source::Source(RTPReceiver *receiver, uint32_t ssrc)
- : mReceiver(receiver),
- mSSRC(ssrc),
- mFirst(true),
- mMaxSeq(0),
- mCycles(0),
- mBaseSeq(0),
- mReceived(0),
- mExpectedPrior(0),
- mReceivedPrior(0),
- mFirstArrivalTimeUs(-1ll),
- mFirstRTPTimeUs(-1ll),
- mAwaitingExtSeqNo(-1),
- mRequestedRetransmission(false),
- mActivePacketType(-1),
- mNextReportTimeUs(-1ll),
- mNumDeclaredLost(0),
- mNumDeclaredLostPrior(0),
- mRetransmitGeneration(0),
- mDeclareLostGeneration(0),
- mDeclareLostTimerPending(false) {
-}
-
-RTPReceiver::Source::~Source() {
-}
-
-void RTPReceiver::Source::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatRetransmit:
- {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
-
- if (generation != mRetransmitGeneration) {
- break;
- }
-
- mRequestedRetransmission = true;
- mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo);
-
- modifyPacketStatus(
- mAwaitingExtSeqNo, STATUS_REQUESTED_RETRANSMISSION);
- break;
- }
-
- case kWhatDeclareLost:
- {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
-
- if (generation != mDeclareLostGeneration) {
- break;
- }
-
- cancelTimers();
-
- ALOGV("Lost packet extSeqNo %d %s",
- mAwaitingExtSeqNo,
- mRequestedRetransmission ? "*" : "");
-
- mRequestedRetransmission = false;
- if (mActiveAssembler != NULL) {
- mActiveAssembler->signalDiscontinuity();
- }
-
- modifyPacketStatus(mAwaitingExtSeqNo, STATUS_DECLARED_LOST);
-
- // resync();
- ++mAwaitingExtSeqNo;
- ++mNumDeclaredLost;
-
- mReceiver->notifyPacketLost();
-
- dequeueMore();
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void RTPReceiver::Source::onPacketReceived(
- uint16_t seq, const sp<ABuffer> &buffer) {
- if (mFirst) {
- buffer->setInt32Data(mCycles | seq);
- queuePacket(buffer);
-
- mFirst = false;
- mBaseSeq = seq;
- mMaxSeq = seq;
- ++mReceived;
- return;
- }
-
- uint16_t udelta = seq - mMaxSeq;
-
- if (udelta < kMaxDropout) {
- // In order, with permissible gap.
-
- if (seq < mMaxSeq) {
- // Sequence number wrapped - count another 64K cycle
- mCycles += kRTPSeqMod;
- }
-
- mMaxSeq = seq;
-
- ++mReceived;
- } else if (udelta <= kRTPSeqMod - kMaxMisorder) {
- // The sequence number made a very large jump
- return;
- } else {
- // Duplicate or reordered packet.
- }
-
- buffer->setInt32Data(mCycles | seq);
- queuePacket(buffer);
-}
-
-void RTPReceiver::Source::queuePacket(const sp<ABuffer> &packet) {
- int32_t newExtendedSeqNo = packet->int32Data();
-
- if (mFirstArrivalTimeUs < 0ll) {
- mFirstArrivalTimeUs = ALooper::GetNowUs();
-
- uint32_t rtpTime;
- CHECK(packet->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
-
- mFirstRTPTimeUs = (rtpTime * 100ll) / 9ll;
- }
-
- if (mAwaitingExtSeqNo >= 0 && newExtendedSeqNo < mAwaitingExtSeqNo) {
- // We're no longer interested in these. They're old.
- ALOGV("dropping stale extSeqNo %d", newExtendedSeqNo);
-
- modifyPacketStatus(newExtendedSeqNo, STATUS_ARRIVED_LATE);
- return;
- }
-
- if (mPackets.empty()) {
- mPackets.push_back(packet);
- dequeueMore();
- return;
- }
-
- List<sp<ABuffer> >::iterator firstIt = mPackets.begin();
- List<sp<ABuffer> >::iterator it = --mPackets.end();
- for (;;) {
- int32_t extendedSeqNo = (*it)->int32Data();
-
- if (extendedSeqNo == newExtendedSeqNo) {
- // Duplicate packet.
- return;
- }
-
- if (extendedSeqNo < newExtendedSeqNo) {
- // Insert new packet after the one at "it".
- mPackets.insert(++it, packet);
- break;
- }
-
- if (it == firstIt) {
- // Insert new packet before the first existing one.
- mPackets.insert(it, packet);
- break;
- }
-
- --it;
- }
-
- dequeueMore();
-}
-
-void RTPReceiver::Source::dequeueMore() {
- int64_t nowUs = ALooper::GetNowUs();
- if (mNextReportTimeUs < 0ll || nowUs >= mNextReportTimeUs) {
- if (mNextReportTimeUs >= 0ll) {
- uint32_t expected = (mMaxSeq | mCycles) - mBaseSeq + 1;
-
- uint32_t expectedInterval = expected - mExpectedPrior;
- mExpectedPrior = expected;
-
- uint32_t receivedInterval = mReceived - mReceivedPrior;
- mReceivedPrior = mReceived;
-
- int64_t lostInterval =
- (int64_t)expectedInterval - (int64_t)receivedInterval;
-
- int32_t declaredLostInterval =
- mNumDeclaredLost - mNumDeclaredLostPrior;
-
- mNumDeclaredLostPrior = mNumDeclaredLost;
-
- if (declaredLostInterval > 0) {
- ALOGI("lost %lld packets (%.2f %%), declared %d lost\n",
- lostInterval,
- 100.0f * lostInterval / expectedInterval,
- declaredLostInterval);
- }
- }
-
- mNextReportTimeUs = nowUs + kReportIntervalUs;
-
-#if TRACK_PACKET_LOSS
- for (size_t i = 0; i < mLostPackets.size(); ++i) {
- int32_t key = mLostPackets.keyAt(i);
- uint32_t value = mLostPackets.valueAt(i);
-
- AString status;
- if (value & STATUS_REQUESTED_RETRANSMISSION) {
- status.append("retrans ");
- }
- if (value & STATUS_ARRIVED_LATE) {
- status.append("arrived-late ");
- }
- ALOGI("Packet %d declared lost %s", key, status.c_str());
- }
-#endif
- }
-
- sp<ABuffer> packet;
- while ((packet = getNextPacket()) != NULL) {
- if (mDeclareLostTimerPending) {
- cancelTimers();
- }
-
- CHECK_GE(mAwaitingExtSeqNo, 0);
-#if TRACK_PACKET_LOSS
- mLostPackets.removeItem(mAwaitingExtSeqNo);
-#endif
-
- int32_t packetType;
- CHECK(packet->meta()->findInt32("PT", &packetType));
-
- if (packetType != mActivePacketType) {
- mActiveAssembler = mReceiver->makeAssembler(packetType);
- mActivePacketType = packetType;
- }
-
- if (mActiveAssembler != NULL) {
- status_t err = mActiveAssembler->processPacket(packet);
- if (err != OK) {
- ALOGV("assembler returned error %d", err);
- }
- }
-
- ++mAwaitingExtSeqNo;
- }
-
- if (mDeclareLostTimerPending) {
- return;
- }
-
- if (mPackets.empty()) {
- return;
- }
-
- CHECK_GE(mAwaitingExtSeqNo, 0);
-
- const sp<ABuffer> &firstPacket = *mPackets.begin();
-
- uint32_t rtpTime;
- CHECK(firstPacket->meta()->findInt32(
- "rtp-time", (int32_t *)&rtpTime));
-
-
- int64_t rtpUs = (rtpTime * 100ll) / 9ll;
-
- int64_t maxArrivalTimeUs =
- mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs;
-
- nowUs = ALooper::GetNowUs();
-
- CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data());
-
- ALOGV("waiting for %d, comparing against %d, %lld us left",
- mAwaitingExtSeqNo,
- firstPacket->int32Data(),
- maxArrivalTimeUs - nowUs);
-
- postDeclareLostTimer(maxArrivalTimeUs + kPacketLostAfterUs);
-
- if (kRequestRetransmissionAfterUs > 0ll) {
- postRetransmitTimer(
- maxArrivalTimeUs + kRequestRetransmissionAfterUs);
- }
-}
-
-sp<ABuffer> RTPReceiver::Source::getNextPacket() {
- if (mPackets.empty()) {
- return NULL;
- }
-
- int32_t extSeqNo = (*mPackets.begin())->int32Data();
-
- if (mAwaitingExtSeqNo < 0) {
- mAwaitingExtSeqNo = extSeqNo;
- } else if (extSeqNo != mAwaitingExtSeqNo) {
- return NULL;
- }
-
- sp<ABuffer> packet = *mPackets.begin();
- mPackets.erase(mPackets.begin());
-
- return packet;
-}
-
-void RTPReceiver::Source::resync() {
- mAwaitingExtSeqNo = -1;
-}
-
-void RTPReceiver::Source::addReportBlock(
- uint32_t ssrc, const sp<ABuffer> &buf) {
- uint32_t extMaxSeq = mMaxSeq | mCycles;
- uint32_t expected = extMaxSeq - mBaseSeq + 1;
-
- int64_t lost = (int64_t)expected - (int64_t)mReceived;
- if (lost > 0x7fffff) {
- lost = 0x7fffff;
- } else if (lost < -0x800000) {
- lost = -0x800000;
- }
-
- uint32_t expectedInterval = expected - mExpectedPrior;
- mExpectedPrior = expected;
-
- uint32_t receivedInterval = mReceived - mReceivedPrior;
- mReceivedPrior = mReceived;
-
- int64_t lostInterval = expectedInterval - receivedInterval;
-
- uint8_t fractionLost;
- if (expectedInterval == 0 || lostInterval <=0) {
- fractionLost = 0;
- } else {
- fractionLost = (lostInterval << 8) / expectedInterval;
- }
-
- uint8_t *ptr = buf->data() + buf->size();
-
- ptr[0] = ssrc >> 24;
- ptr[1] = (ssrc >> 16) & 0xff;
- ptr[2] = (ssrc >> 8) & 0xff;
- ptr[3] = ssrc & 0xff;
-
- ptr[4] = fractionLost;
-
- ptr[5] = (lost >> 16) & 0xff;
- ptr[6] = (lost >> 8) & 0xff;
- ptr[7] = lost & 0xff;
-
- ptr[8] = extMaxSeq >> 24;
- ptr[9] = (extMaxSeq >> 16) & 0xff;
- ptr[10] = (extMaxSeq >> 8) & 0xff;
- ptr[11] = extMaxSeq & 0xff;
-
- // XXX TODO:
-
- ptr[12] = 0x00; // interarrival jitter
- ptr[13] = 0x00;
- ptr[14] = 0x00;
- ptr[15] = 0x00;
-
- ptr[16] = 0x00; // last SR
- ptr[17] = 0x00;
- ptr[18] = 0x00;
- ptr[19] = 0x00;
-
- ptr[20] = 0x00; // delay since last SR
- ptr[21] = 0x00;
- ptr[22] = 0x00;
- ptr[23] = 0x00;
-
- buf->setRange(buf->offset(), buf->size() + 24);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-RTPReceiver::RTPReceiver(
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify,
- uint32_t flags)
- : mNetSession(netSession),
- mNotify(notify),
- mFlags(flags),
- mRTPMode(TRANSPORT_UNDEFINED),
- mRTCPMode(TRANSPORT_UNDEFINED),
- mRTPSessionID(0),
- mRTCPSessionID(0),
- mRTPConnected(false),
- mRTCPConnected(false),
- mRTPClientSessionID(0),
- mRTCPClientSessionID(0) {
-}
-
-RTPReceiver::~RTPReceiver() {
- if (mRTCPClientSessionID != 0) {
- mNetSession->destroySession(mRTCPClientSessionID);
- mRTCPClientSessionID = 0;
- }
-
- if (mRTPClientSessionID != 0) {
- mNetSession->destroySession(mRTPClientSessionID);
- mRTPClientSessionID = 0;
- }
-
- if (mRTCPSessionID != 0) {
- mNetSession->destroySession(mRTCPSessionID);
- mRTCPSessionID = 0;
- }
-
- if (mRTPSessionID != 0) {
- mNetSession->destroySession(mRTPSessionID);
- mRTPSessionID = 0;
- }
-}
-
-status_t RTPReceiver::initAsync(
- TransportMode rtpMode,
- TransportMode rtcpMode,
- int32_t *outLocalRTPPort) {
- if (mRTPMode != TRANSPORT_UNDEFINED
- || rtpMode == TRANSPORT_UNDEFINED
- || rtpMode == TRANSPORT_NONE
- || rtcpMode == TRANSPORT_UNDEFINED) {
- return INVALID_OPERATION;
- }
-
- CHECK_NE(rtpMode, TRANSPORT_TCP_INTERLEAVED);
- CHECK_NE(rtcpMode, TRANSPORT_TCP_INTERLEAVED);
-
- sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
-
- sp<AMessage> rtcpNotify;
- if (rtcpMode != TRANSPORT_NONE) {
- rtcpNotify = new AMessage(kWhatRTCPNotify, id());
- }
-
- CHECK_EQ(mRTPSessionID, 0);
- CHECK_EQ(mRTCPSessionID, 0);
-
- int32_t localRTPPort;
-
- struct in_addr ifaceAddr;
- ifaceAddr.s_addr = INADDR_ANY;
-
- for (;;) {
- localRTPPort = PickRandomRTPPort();
-
- status_t err;
- if (rtpMode == TRANSPORT_UDP) {
- err = mNetSession->createUDPSession(
- localRTPPort,
- rtpNotify,
- &mRTPSessionID);
- } else {
- CHECK_EQ(rtpMode, TRANSPORT_TCP);
- err = mNetSession->createTCPDatagramSession(
- ifaceAddr,
- localRTPPort,
- rtpNotify,
- &mRTPSessionID);
- }
-
- if (err != OK) {
- continue;
- }
-
- if (rtcpMode == TRANSPORT_NONE) {
- break;
- } else if (rtcpMode == TRANSPORT_UDP) {
- err = mNetSession->createUDPSession(
- localRTPPort + 1,
- rtcpNotify,
- &mRTCPSessionID);
- } else {
- CHECK_EQ(rtpMode, TRANSPORT_TCP);
- err = mNetSession->createTCPDatagramSession(
- ifaceAddr,
- localRTPPort + 1,
- rtcpNotify,
- &mRTCPSessionID);
- }
-
- if (err == OK) {
- break;
- }
-
- mNetSession->destroySession(mRTPSessionID);
- mRTPSessionID = 0;
- }
-
- mRTPMode = rtpMode;
- mRTCPMode = rtcpMode;
- *outLocalRTPPort = localRTPPort;
-
- return OK;
-}
-
-status_t RTPReceiver::connect(
- const char *remoteHost, int32_t remoteRTPPort, int32_t remoteRTCPPort) {
- status_t err;
-
- if (mRTPMode == TRANSPORT_UDP) {
- CHECK(!mRTPConnected);
-
- err = mNetSession->connectUDPSession(
- mRTPSessionID, remoteHost, remoteRTPPort);
-
- if (err != OK) {
- notifyInitDone(err);
- return err;
- }
-
- ALOGI("connectUDPSession RTP successful.");
-
- mRTPConnected = true;
- }
-
- if (mRTCPMode == TRANSPORT_UDP) {
- CHECK(!mRTCPConnected);
-
- err = mNetSession->connectUDPSession(
- mRTCPSessionID, remoteHost, remoteRTCPPort);
-
- if (err != OK) {
- notifyInitDone(err);
- return err;
- }
-
- scheduleSendRR();
-
- ALOGI("connectUDPSession RTCP successful.");
-
- mRTCPConnected = true;
- }
-
- if (mRTPConnected
- && (mRTCPConnected || mRTCPMode == TRANSPORT_NONE)) {
- notifyInitDone(OK);
- }
-
- return OK;
-}
-
-status_t RTPReceiver::informSender(const sp<AMessage> ¶ms) {
- if (!mRTCPConnected) {
- return INVALID_OPERATION;
- }
-
- int64_t avgLatencyUs;
- CHECK(params->findInt64("avgLatencyUs", &avgLatencyUs));
-
- int64_t maxLatencyUs;
- CHECK(params->findInt64("maxLatencyUs", &maxLatencyUs));
-
- sp<ABuffer> buf = new ABuffer(28);
-
- uint8_t *ptr = buf->data();
- ptr[0] = 0x80 | 0;
- ptr[1] = 204; // APP
- ptr[2] = 0;
-
- CHECK((buf->size() % 4) == 0u);
- ptr[3] = (buf->size() / 4) - 1;
-
- ptr[4] = kSourceID >> 24; // SSRC
- ptr[5] = (kSourceID >> 16) & 0xff;
- ptr[6] = (kSourceID >> 8) & 0xff;
- ptr[7] = kSourceID & 0xff;
- ptr[8] = 'l';
- ptr[9] = 'a';
- ptr[10] = 't';
- ptr[11] = 'e';
-
- ptr[12] = avgLatencyUs >> 56;
- ptr[13] = (avgLatencyUs >> 48) & 0xff;
- ptr[14] = (avgLatencyUs >> 40) & 0xff;
- ptr[15] = (avgLatencyUs >> 32) & 0xff;
- ptr[16] = (avgLatencyUs >> 24) & 0xff;
- ptr[17] = (avgLatencyUs >> 16) & 0xff;
- ptr[18] = (avgLatencyUs >> 8) & 0xff;
- ptr[19] = avgLatencyUs & 0xff;
-
- ptr[20] = maxLatencyUs >> 56;
- ptr[21] = (maxLatencyUs >> 48) & 0xff;
- ptr[22] = (maxLatencyUs >> 40) & 0xff;
- ptr[23] = (maxLatencyUs >> 32) & 0xff;
- ptr[24] = (maxLatencyUs >> 24) & 0xff;
- ptr[25] = (maxLatencyUs >> 16) & 0xff;
- ptr[26] = (maxLatencyUs >> 8) & 0xff;
- ptr[27] = maxLatencyUs & 0xff;
-
- mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
-
- return OK;
-}
-
-void RTPReceiver::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatRTPNotify:
- case kWhatRTCPNotify:
- onNetNotify(msg->what() == kWhatRTPNotify, msg);
- break;
-
- case kWhatSendRR:
- {
- onSendRR();
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void RTPReceiver::onNetNotify(bool isRTP, const sp<AMessage> &msg) {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::kWhatError:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- int32_t errorOccuredDuringSend;
- CHECK(msg->findInt32("send", &errorOccuredDuringSend));
-
- AString detail;
- CHECK(msg->findString("detail", &detail));
-
- ALOGE("An error occurred during %s in session %d "
- "(%d, '%s' (%s)).",
- errorOccuredDuringSend ? "send" : "receive",
- sessionID,
- err,
- detail.c_str(),
- strerror(-err));
-
- mNetSession->destroySession(sessionID);
-
- if (sessionID == mRTPSessionID) {
- mRTPSessionID = 0;
- } else if (sessionID == mRTCPSessionID) {
- mRTCPSessionID = 0;
- } else if (sessionID == mRTPClientSessionID) {
- mRTPClientSessionID = 0;
- } else if (sessionID == mRTCPClientSessionID) {
- mRTCPClientSessionID = 0;
- }
-
- if (!mRTPConnected
- || (mRTCPMode != TRANSPORT_NONE && !mRTCPConnected)) {
- notifyInitDone(err);
- break;
- }
-
- notifyError(err);
- break;
- }
-
- case ANetworkSession::kWhatDatagram:
- {
- sp<ABuffer> data;
- CHECK(msg->findBuffer("data", &data));
-
- if (isRTP) {
- if (mFlags & FLAG_AUTO_CONNECT) {
- AString fromAddr;
- CHECK(msg->findString("fromAddr", &fromAddr));
-
- int32_t fromPort;
- CHECK(msg->findInt32("fromPort", &fromPort));
-
- CHECK_EQ((status_t)OK,
- connect(
- fromAddr.c_str(), fromPort, fromPort + 1));
-
- mFlags &= ~FLAG_AUTO_CONNECT;
- }
-
- onRTPData(data);
- } else {
- onRTCPData(data);
- }
- break;
- }
-
- case ANetworkSession::kWhatClientConnected:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- if (isRTP) {
- CHECK_EQ(mRTPMode, TRANSPORT_TCP);
-
- if (mRTPClientSessionID != 0) {
- // We only allow a single client connection.
- mNetSession->destroySession(sessionID);
- sessionID = 0;
- break;
- }
-
- mRTPClientSessionID = sessionID;
- mRTPConnected = true;
- } else {
- CHECK_EQ(mRTCPMode, TRANSPORT_TCP);
-
- if (mRTCPClientSessionID != 0) {
- // We only allow a single client connection.
- mNetSession->destroySession(sessionID);
- sessionID = 0;
- break;
- }
-
- mRTCPClientSessionID = sessionID;
- mRTCPConnected = true;
- }
-
- if (mRTPConnected
- && (mRTCPConnected || mRTCPMode == TRANSPORT_NONE)) {
- notifyInitDone(OK);
- }
- break;
- }
- }
-}
-
-void RTPReceiver::notifyInitDone(status_t err) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatInitDone);
- notify->setInt32("err", err);
- notify->post();
-}
-
-void RTPReceiver::notifyError(status_t err) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatError);
- notify->setInt32("err", err);
- notify->post();
-}
-
-void RTPReceiver::notifyPacketLost() {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatPacketLost);
- notify->post();
-}
-
-status_t RTPReceiver::onRTPData(const sp<ABuffer> &buffer) {
- size_t size = buffer->size();
- if (size < 12) {
- // Too short to be a valid RTP header.
- return ERROR_MALFORMED;
- }
-
- const uint8_t *data = buffer->data();
-
- if ((data[0] >> 6) != 2) {
- // Unsupported version.
- return ERROR_UNSUPPORTED;
- }
-
- if (data[0] & 0x20) {
- // Padding present.
-
- size_t paddingLength = data[size - 1];
-
- if (paddingLength + 12 > size) {
- // If we removed this much padding we'd end up with something
- // that's too short to be a valid RTP header.
- return ERROR_MALFORMED;
- }
-
- size -= paddingLength;
- }
-
- int numCSRCs = data[0] & 0x0f;
-
- size_t payloadOffset = 12 + 4 * numCSRCs;
-
- if (size < payloadOffset) {
- // Not enough data to fit the basic header and all the CSRC entries.
- return ERROR_MALFORMED;
- }
-
- if (data[0] & 0x10) {
- // Header eXtension present.
-
- if (size < payloadOffset + 4) {
- // Not enough data to fit the basic header, all CSRC entries
- // and the first 4 bytes of the extension header.
-
- return ERROR_MALFORMED;
- }
-
- const uint8_t *extensionData = &data[payloadOffset];
-
- size_t extensionLength =
- 4 * (extensionData[2] << 8 | extensionData[3]);
-
- if (size < payloadOffset + 4 + extensionLength) {
- return ERROR_MALFORMED;
- }
-
- payloadOffset += 4 + extensionLength;
- }
-
- uint32_t srcId = U32_AT(&data[8]);
- uint32_t rtpTime = U32_AT(&data[4]);
- uint16_t seqNo = U16_AT(&data[2]);
-
- sp<AMessage> meta = buffer->meta();
- meta->setInt32("ssrc", srcId);
- meta->setInt32("rtp-time", rtpTime);
- meta->setInt32("PT", data[1] & 0x7f);
- meta->setInt32("M", data[1] >> 7);
-
- buffer->setRange(payloadOffset, size - payloadOffset);
-
- ssize_t index = mSources.indexOfKey(srcId);
- sp<Source> source;
- if (index < 0) {
- source = new Source(this, srcId);
- looper()->registerHandler(source);
-
- mSources.add(srcId, source);
- } else {
- source = mSources.valueAt(index);
- }
-
- source->onPacketReceived(seqNo, buffer);
-
- return OK;
-}
-
-status_t RTPReceiver::onRTCPData(const sp<ABuffer> &data) {
- ALOGI("onRTCPData");
- return OK;
-}
-
-void RTPReceiver::addSDES(const sp<ABuffer> &buffer) {
- uint8_t *data = buffer->data() + buffer->size();
- data[0] = 0x80 | 1;
- data[1] = 202; // SDES
- data[4] = kSourceID >> 24; // SSRC
- data[5] = (kSourceID >> 16) & 0xff;
- data[6] = (kSourceID >> 8) & 0xff;
- data[7] = kSourceID & 0xff;
-
- size_t offset = 8;
-
- data[offset++] = 1; // CNAME
-
- AString cname = "stagefright@somewhere";
- data[offset++] = cname.size();
-
- memcpy(&data[offset], cname.c_str(), cname.size());
- offset += cname.size();
-
- data[offset++] = 6; // TOOL
-
- AString tool = "stagefright/1.0";
- data[offset++] = tool.size();
-
- memcpy(&data[offset], tool.c_str(), tool.size());
- offset += tool.size();
-
- data[offset++] = 0;
-
- if ((offset % 4) > 0) {
- size_t count = 4 - (offset % 4);
- switch (count) {
- case 3:
- data[offset++] = 0;
- case 2:
- data[offset++] = 0;
- case 1:
- data[offset++] = 0;
- }
- }
-
- size_t numWords = (offset / 4) - 1;
- data[2] = numWords >> 8;
- data[3] = numWords & 0xff;
-
- buffer->setRange(buffer->offset(), buffer->size() + offset);
-}
-
-void RTPReceiver::scheduleSendRR() {
- (new AMessage(kWhatSendRR, id()))->post(5000000ll);
-}
-
-void RTPReceiver::onSendRR() {
- sp<ABuffer> buf = new ABuffer(kMaxUDPPacketSize);
- buf->setRange(0, 0);
-
- uint8_t *ptr = buf->data();
- ptr[0] = 0x80 | 0;
- ptr[1] = 201; // RR
- ptr[2] = 0;
- ptr[3] = 1;
- ptr[4] = kSourceID >> 24; // SSRC
- ptr[5] = (kSourceID >> 16) & 0xff;
- ptr[6] = (kSourceID >> 8) & 0xff;
- ptr[7] = kSourceID & 0xff;
-
- buf->setRange(0, 8);
-
- size_t numReportBlocks = 0;
- for (size_t i = 0; i < mSources.size(); ++i) {
- uint32_t ssrc = mSources.keyAt(i);
- sp<Source> source = mSources.valueAt(i);
-
- if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) {
- // Cannot fit another report block.
- break;
- }
-
- source->addReportBlock(ssrc, buf);
- ++numReportBlocks;
- }
-
- ptr[0] |= numReportBlocks; // 5 bit
-
- size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks;
- ptr[2] = sizeInWordsMinus1 >> 8;
- ptr[3] = sizeInWordsMinus1 & 0xff;
-
- buf->setRange(0, (sizeInWordsMinus1 + 1) * 4);
-
- addSDES(buf);
-
- mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
-
- scheduleSendRR();
-}
-
-status_t RTPReceiver::registerPacketType(
- uint8_t packetType, PacketizationMode mode) {
- mPacketTypes.add(packetType, mode);
-
- return OK;
-}
-
-sp<RTPReceiver::Assembler> RTPReceiver::makeAssembler(uint8_t packetType) {
- ssize_t index = mPacketTypes.indexOfKey(packetType);
- if (index < 0) {
- return NULL;
- }
-
- PacketizationMode mode = mPacketTypes.valueAt(index);
-
- switch (mode) {
- case PACKETIZATION_NONE:
- case PACKETIZATION_TRANSPORT_STREAM:
- return new TSAssembler(mNotify);
-
- case PACKETIZATION_H264:
- return new H264Assembler(mNotify);
-
- default:
- return NULL;
- }
-}
-
-void RTPReceiver::requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo) {
- int32_t blp = 0;
-
- sp<ABuffer> buf = new ABuffer(16);
- buf->setRange(0, 0);
-
- uint8_t *ptr = buf->data();
- ptr[0] = 0x80 | 1; // generic NACK
- ptr[1] = 205; // TSFB
- ptr[2] = 0;
- ptr[3] = 3;
- ptr[8] = (senderSSRC >> 24) & 0xff;
- ptr[9] = (senderSSRC >> 16) & 0xff;
- ptr[10] = (senderSSRC >> 8) & 0xff;
- ptr[11] = (senderSSRC & 0xff);
- ptr[8] = (kSourceID >> 24) & 0xff;
- ptr[9] = (kSourceID >> 16) & 0xff;
- ptr[10] = (kSourceID >> 8) & 0xff;
- ptr[11] = (kSourceID & 0xff);
- ptr[12] = (extSeqNo >> 8) & 0xff;
- ptr[13] = (extSeqNo & 0xff);
- ptr[14] = (blp >> 8) & 0xff;
- ptr[15] = (blp & 0xff);
-
- buf->setRange(0, 16);
-
- mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
-}
-
-void RTPReceiver::Source::modifyPacketStatus(int32_t extSeqNo, uint32_t mask) {
-#if TRACK_PACKET_LOSS
- ssize_t index = mLostPackets.indexOfKey(extSeqNo);
- if (index < 0) {
- mLostPackets.add(extSeqNo, mask);
- } else {
- mLostPackets.editValueAt(index) |= mask;
- }
-#endif
-}
-
-void RTPReceiver::Source::postRetransmitTimer(int64_t timeUs) {
- int64_t delayUs = timeUs - ALooper::GetNowUs();
- sp<AMessage> msg = new AMessage(kWhatRetransmit, id());
- msg->setInt32("generation", mRetransmitGeneration);
- msg->post(delayUs);
-}
-
-void RTPReceiver::Source::postDeclareLostTimer(int64_t timeUs) {
- CHECK(!mDeclareLostTimerPending);
- mDeclareLostTimerPending = true;
-
- int64_t delayUs = timeUs - ALooper::GetNowUs();
- sp<AMessage> msg = new AMessage(kWhatDeclareLost, id());
- msg->setInt32("generation", mDeclareLostGeneration);
- msg->post(delayUs);
-}
-
-void RTPReceiver::Source::cancelTimers() {
- ++mRetransmitGeneration;
- ++mDeclareLostGeneration;
- mDeclareLostTimerPending = false;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h
deleted file mode 100644
index 240ab2e..0000000
--- a/media/libstagefright/wifi-display/rtp/RTPReceiver.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2013, 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 RTP_RECEIVER_H_
-
-#define RTP_RECEIVER_H_
-
-#include "RTPBase.h"
-
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-struct ABuffer;
-struct ANetworkSession;
-
-// An object of this class facilitates receiving of media data on an RTP
-// channel. The channel is established over a UDP or TCP connection depending
-// on which "TransportMode" was chosen. In addition different RTP packetization
-// schemes are supported such as "Transport Stream Packets over RTP",
-// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)"
-struct RTPReceiver : public RTPBase, public AHandler {
- enum {
- kWhatInitDone,
- kWhatError,
- kWhatAccessUnit,
- kWhatPacketLost,
- };
-
- enum Flags {
- FLAG_AUTO_CONNECT = 1,
- };
- RTPReceiver(
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify,
- uint32_t flags = 0);
-
- status_t registerPacketType(
- uint8_t packetType, PacketizationMode mode);
-
- status_t initAsync(
- TransportMode rtpMode,
- TransportMode rtcpMode,
- int32_t *outLocalRTPPort);
-
- status_t connect(
- const char *remoteHost,
- int32_t remoteRTPPort,
- int32_t remoteRTCPPort);
-
- status_t informSender(const sp<AMessage> ¶ms);
-
-protected:
- virtual ~RTPReceiver();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatRTPNotify,
- kWhatRTCPNotify,
- kWhatSendRR,
- };
-
- enum {
- kSourceID = 0xdeadbeef,
- kPacketLostAfterUs = 100000,
- kRequestRetransmissionAfterUs = -1,
- };
-
- struct Assembler;
- struct H264Assembler;
- struct Source;
- struct TSAssembler;
-
- sp<ANetworkSession> mNetSession;
- sp<AMessage> mNotify;
- uint32_t mFlags;
- TransportMode mRTPMode;
- TransportMode mRTCPMode;
- int32_t mRTPSessionID;
- int32_t mRTCPSessionID;
- bool mRTPConnected;
- bool mRTCPConnected;
-
- int32_t mRTPClientSessionID; // in TRANSPORT_TCP mode.
- int32_t mRTCPClientSessionID; // in TRANSPORT_TCP mode.
-
- KeyedVector<uint8_t, PacketizationMode> mPacketTypes;
- KeyedVector<uint32_t, sp<Source> > mSources;
-
- void onNetNotify(bool isRTP, const sp<AMessage> &msg);
- status_t onRTPData(const sp<ABuffer> &data);
- status_t onRTCPData(const sp<ABuffer> &data);
- void onSendRR();
-
- void scheduleSendRR();
- void addSDES(const sp<ABuffer> &buffer);
-
- void notifyInitDone(status_t err);
- void notifyError(status_t err);
- void notifyPacketLost();
-
- sp<Assembler> makeAssembler(uint8_t packetType);
-
- void requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo);
-
- DISALLOW_EVIL_CONSTRUCTORS(RTPReceiver);
-};
-
-} // namespace android
-
-#endif // RTP_RECEIVER_H_
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
index 1887b8b..e88a3bd 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -685,9 +685,8 @@
return OK;
}
-status_t RTPSender::parseReceiverReport(const uint8_t *data, size_t size) {
- // hexdump(data, size);
-
+status_t RTPSender::parseReceiverReport(
+ const uint8_t *data, size_t /* size */) {
float fractionLost = data[12] / 256.0f;
ALOGI("lost %.2f %% of packets during report interval.",
diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp
deleted file mode 100644
index b902f29..0000000
--- a/media/libstagefright/wifi-display/rtptest.cpp
+++ /dev/null
@@ -1,565 +0,0 @@
-/*
- * Copyright 2013, 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_NEBUG 0
-#define LOG_TAG "rtptest"
-#include <utils/Log.h>
-
-#include "rtp/RTPSender.h"
-#include "rtp/RTPReceiver.h"
-#include "TimeSyncer.h"
-
-#include <binder/ProcessState.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/NuMediaExtractor.h>
-#include <media/stagefright/Utils.h>
-
-#define MEDIA_FILENAME "/sdcard/Frame Counter HD 30FPS_1080p.mp4"
-
-namespace android {
-
-struct PacketSource : public RefBase {
- PacketSource() {}
-
- virtual sp<ABuffer> getNextAccessUnit() = 0;
-
-protected:
- virtual ~PacketSource() {}
-
-private:
- DISALLOW_EVIL_CONSTRUCTORS(PacketSource);
-};
-
-struct MediaPacketSource : public PacketSource {
- MediaPacketSource()
- : mMaxSampleSize(1024 * 1024) {
- mExtractor = new NuMediaExtractor;
- CHECK_EQ((status_t)OK,
- mExtractor->setDataSource(MEDIA_FILENAME));
-
- bool haveVideo = false;
- for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
- sp<AMessage> format;
- CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format));
-
- AString mime;
- CHECK(format->findString("mime", &mime));
-
- if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) {
- mExtractor->selectTrack(i);
- haveVideo = true;
- break;
- }
- }
-
- CHECK(haveVideo);
- }
-
- virtual sp<ABuffer> getNextAccessUnit() {
- int64_t timeUs;
- status_t err = mExtractor->getSampleTime(&timeUs);
-
- if (err != OK) {
- return NULL;
- }
-
- sp<ABuffer> accessUnit = new ABuffer(mMaxSampleSize);
- CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit));
-
- accessUnit->meta()->setInt64("timeUs", timeUs);
-
- CHECK_EQ((status_t)OK, mExtractor->advance());
-
- return accessUnit;
- }
-
-protected:
- virtual ~MediaPacketSource() {
- }
-
-private:
- sp<NuMediaExtractor> mExtractor;
- size_t mMaxSampleSize;
-
- DISALLOW_EVIL_CONSTRUCTORS(MediaPacketSource);
-};
-
-struct SimplePacketSource : public PacketSource {
- SimplePacketSource()
- : mCounter(0) {
- }
-
- virtual sp<ABuffer> getNextAccessUnit() {
- sp<ABuffer> buffer = new ABuffer(4);
- uint8_t *dst = buffer->data();
- dst[0] = mCounter >> 24;
- dst[1] = (mCounter >> 16) & 0xff;
- dst[2] = (mCounter >> 8) & 0xff;
- dst[3] = mCounter & 0xff;
-
- buffer->meta()->setInt64("timeUs", mCounter * 1000000ll / kFrameRate);
-
- ++mCounter;
-
- return buffer;
- }
-
-protected:
- virtual ~SimplePacketSource() {
- }
-
-private:
- enum {
- kFrameRate = 30
- };
-
- uint32_t mCounter;
-
- DISALLOW_EVIL_CONSTRUCTORS(SimplePacketSource);
-};
-
-struct TestHandler : public AHandler {
- TestHandler(const sp<ANetworkSession> &netSession);
-
- void listen();
- void connect(const char *host, int32_t port);
-
-protected:
- virtual ~TestHandler();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatListen,
- kWhatConnect,
- kWhatReceiverNotify,
- kWhatSenderNotify,
- kWhatSendMore,
- kWhatStop,
- kWhatTimeSyncerNotify,
- };
-
-#if 1
- static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_UDP;
- static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_UDP;
-#else
- static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_TCP;
- static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_NONE;
-#endif
-
-#if 1
- static const RTPBase::PacketizationMode kPacketizationMode
- = RTPBase::PACKETIZATION_H264;
-#else
- static const RTPBase::PacketizationMode kPacketizationMode
- = RTPBase::PACKETIZATION_NONE;
-#endif
-
- sp<ANetworkSession> mNetSession;
- sp<PacketSource> mSource;
- sp<RTPSender> mSender;
- sp<RTPReceiver> mReceiver;
-
- sp<TimeSyncer> mTimeSyncer;
- bool mTimeSyncerStarted;
-
- int64_t mFirstTimeRealUs;
- int64_t mFirstTimeMediaUs;
-
- int64_t mTimeOffsetUs;
- bool mTimeOffsetValid;
-
- status_t readMore();
-
- DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
-};
-
-TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
- : mNetSession(netSession),
- mTimeSyncerStarted(false),
- mFirstTimeRealUs(-1ll),
- mFirstTimeMediaUs(-1ll),
- mTimeOffsetUs(-1ll),
- mTimeOffsetValid(false) {
-}
-
-TestHandler::~TestHandler() {
-}
-
-void TestHandler::listen() {
- sp<AMessage> msg = new AMessage(kWhatListen, id());
- msg->post();
-}
-
-void TestHandler::connect(const char *host, int32_t port) {
- sp<AMessage> msg = new AMessage(kWhatConnect, id());
- msg->setString("host", host);
- msg->setInt32("port", port);
- msg->post();
-}
-
-static void dumpDelay(int64_t delayMs) {
- static const int64_t kMinDelayMs = 0;
- static const int64_t kMaxDelayMs = 300;
-
- const char *kPattern = "########################################";
- size_t kPatternSize = strlen(kPattern);
-
- int n = (kPatternSize * (delayMs - kMinDelayMs))
- / (kMaxDelayMs - kMinDelayMs);
-
- if (n < 0) {
- n = 0;
- } else if ((size_t)n > kPatternSize) {
- n = kPatternSize;
- }
-
- ALOGI("(%4lld ms) %s\n",
- delayMs,
- kPattern + kPatternSize - n);
-}
-
-void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatListen:
- {
- sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
- mTimeSyncer = new TimeSyncer(mNetSession, notify);
- looper()->registerHandler(mTimeSyncer);
-
- notify = new AMessage(kWhatReceiverNotify, id());
- mReceiver = new RTPReceiver(
- mNetSession, notify, RTPReceiver::FLAG_AUTO_CONNECT);
- looper()->registerHandler(mReceiver);
-
- CHECK_EQ((status_t)OK,
- mReceiver->registerPacketType(33, kPacketizationMode));
-
- int32_t receiverRTPPort;
- CHECK_EQ((status_t)OK,
- mReceiver->initAsync(
- kRTPMode,
- kRTCPMode,
- &receiverRTPPort));
-
- printf("picked receiverRTPPort %d\n", receiverRTPPort);
-
-#if 0
- CHECK_EQ((status_t)OK,
- mReceiver->connect(
- "127.0.0.1", senderRTPPort, senderRTPPort + 1));
-#endif
- break;
- }
-
- case kWhatConnect:
- {
- AString host;
- CHECK(msg->findString("host", &host));
-
- sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
- mTimeSyncer = new TimeSyncer(mNetSession, notify);
- looper()->registerHandler(mTimeSyncer);
- mTimeSyncer->startServer(8123);
-
- int32_t receiverRTPPort;
- CHECK(msg->findInt32("port", &receiverRTPPort));
-
-#if 1
- mSource = new MediaPacketSource;
-#else
- mSource = new SimplePacketSource;
-#endif
-
- notify = new AMessage(kWhatSenderNotify, id());
- mSender = new RTPSender(mNetSession, notify);
-
- looper()->registerHandler(mSender);
-
- int32_t senderRTPPort;
- CHECK_EQ((status_t)OK,
- mSender->initAsync(
- host.c_str(),
- receiverRTPPort,
- kRTPMode,
- kRTCPMode == RTPBase::TRANSPORT_NONE
- ? -1 : receiverRTPPort + 1,
- kRTCPMode,
- &senderRTPPort));
-
- printf("picked senderRTPPort %d\n", senderRTPPort);
- break;
- }
-
- case kWhatSenderNotify:
- {
- ALOGI("kWhatSenderNotify");
-
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- switch (what) {
- case RTPSender::kWhatInitDone:
- {
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- ALOGI("RTPSender::initAsync completed w/ err %d", err);
-
- if (err == OK) {
- err = readMore();
-
- if (err != OK) {
- (new AMessage(kWhatStop, id()))->post();
- }
- }
- break;
- }
-
- case RTPSender::kWhatError:
- break;
- }
- break;
- }
-
- case kWhatReceiverNotify:
- {
- ALOGV("kWhatReceiverNotify");
-
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- switch (what) {
- case RTPReceiver::kWhatInitDone:
- {
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- ALOGI("RTPReceiver::initAsync completed w/ err %d", err);
- break;
- }
-
- case RTPReceiver::kWhatError:
- break;
-
- case RTPReceiver::kWhatAccessUnit:
- {
-#if 0
- if (!mTimeSyncerStarted) {
- mTimeSyncer->startClient("172.18.41.216", 8123);
- mTimeSyncerStarted = true;
- }
-
- sp<ABuffer> accessUnit;
- CHECK(msg->findBuffer("accessUnit", &accessUnit));
-
- int64_t timeUs;
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-
- if (mTimeOffsetValid) {
- timeUs -= mTimeOffsetUs;
- int64_t nowUs = ALooper::GetNowUs();
- int64_t delayMs = (nowUs - timeUs) / 1000ll;
-
- dumpDelay(delayMs);
- }
-#endif
- break;
- }
-
- case RTPReceiver::kWhatPacketLost:
- ALOGV("kWhatPacketLost");
- break;
-
- default:
- TRESPASS();
- }
- break;
- }
-
- case kWhatSendMore:
- {
- sp<ABuffer> accessUnit;
- CHECK(msg->findBuffer("accessUnit", &accessUnit));
-
- CHECK_EQ((status_t)OK,
- mSender->queueBuffer(
- accessUnit,
- 33,
- kPacketizationMode));
-
- status_t err = readMore();
-
- if (err != OK) {
- (new AMessage(kWhatStop, id()))->post();
- }
- break;
- }
-
- case kWhatStop:
- {
- if (mReceiver != NULL) {
- looper()->unregisterHandler(mReceiver->id());
- mReceiver.clear();
- }
-
- if (mSender != NULL) {
- looper()->unregisterHandler(mSender->id());
- mSender.clear();
- }
-
- mSource.clear();
-
- looper()->stop();
- break;
- }
-
- case kWhatTimeSyncerNotify:
- {
- CHECK(msg->findInt64("offset", &mTimeOffsetUs));
- mTimeOffsetValid = true;
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-status_t TestHandler::readMore() {
- sp<ABuffer> accessUnit = mSource->getNextAccessUnit();
-
- if (accessUnit == NULL) {
- return ERROR_END_OF_STREAM;
- }
-
- int64_t timeUs;
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-
- int64_t nowUs = ALooper::GetNowUs();
- int64_t whenUs;
-
- if (mFirstTimeRealUs < 0ll) {
- mFirstTimeRealUs = whenUs = nowUs;
- mFirstTimeMediaUs = timeUs;
- } else {
- whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs;
- }
-
- accessUnit->meta()->setInt64("timeUs", whenUs);
-
- sp<AMessage> msg = new AMessage(kWhatSendMore, id());
- msg->setBuffer("accessUnit", accessUnit);
- msg->post(whenUs - nowUs);
-
- return OK;
-}
-
-} // namespace android
-
-static void usage(const char *me) {
- fprintf(stderr,
- "usage: %s -c host:port\tconnect to remote host\n"
- " -l \tlisten\n",
- me);
-}
-
-int main(int argc, char **argv) {
- using namespace android;
-
- // srand(time(NULL));
-
- ProcessState::self()->startThreadPool();
-
- DataSource::RegisterDefaultSniffers();
-
- bool listen = false;
- int32_t connectToPort = -1;
- AString connectToHost;
-
- int res;
- while ((res = getopt(argc, argv, "hc:l")) >= 0) {
- switch (res) {
- case 'c':
- {
- const char *colonPos = strrchr(optarg, ':');
-
- if (colonPos == NULL) {
- usage(argv[0]);
- exit(1);
- }
-
- connectToHost.setTo(optarg, colonPos - optarg);
-
- char *end;
- connectToPort = strtol(colonPos + 1, &end, 10);
-
- if (*end != '\0' || end == colonPos + 1
- || connectToPort < 1 || connectToPort > 65535) {
- fprintf(stderr, "Illegal port specified.\n");
- exit(1);
- }
- break;
- }
-
- case 'l':
- {
- listen = true;
- break;
- }
-
- case '?':
- case 'h':
- usage(argv[0]);
- exit(1);
- }
- }
-
- if (!listen && connectToPort < 0) {
- fprintf(stderr,
- "You need to select either client or server mode.\n");
- exit(1);
- }
-
- sp<ANetworkSession> netSession = new ANetworkSession;
- netSession->start();
-
- sp<ALooper> looper = new ALooper;
-
- sp<TestHandler> handler = new TestHandler(netSession);
- looper->registerHandler(handler);
-
- if (listen) {
- handler->listen();
- }
-
- if (connectToPort >= 0) {
- handler->connect(connectToHost.c_str(), connectToPort);
- }
-
- looper->start(true /* runOnCallingThread */);
-
- return 0;
-}
-
diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp
deleted file mode 100644
index cdb2267..0000000
--- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "DirectRenderer"
-#include <utils/Log.h>
-
-#include "DirectRenderer.h"
-
-#include <gui/SurfaceComposerClient.h>
-#include <gui/Surface.h>
-#include <media/AudioTrack.h>
-#include <media/ICrypto.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/MediaCodec.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaErrors.h>
-
-namespace android {
-
-/*
- Drives the decoding process using a MediaCodec instance. Input buffers
- queued by calls to "queueInputBuffer" are fed to the decoder as soon
- as the decoder is ready for them, the client is notified about output
- buffers as the decoder spits them out.
-*/
-struct DirectRenderer::DecoderContext : public AHandler {
- enum {
- kWhatOutputBufferReady,
- };
- DecoderContext(const sp<AMessage> ¬ify);
-
- status_t init(
- const sp<AMessage> &format,
- const sp<IGraphicBufferProducer> &surfaceTex);
-
- void queueInputBuffer(const sp<ABuffer> &accessUnit);
-
- status_t renderOutputBufferAndRelease(size_t index);
- status_t releaseOutputBuffer(size_t index);
-
-protected:
- virtual ~DecoderContext();
-
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatDecoderNotify,
- };
-
- sp<AMessage> mNotify;
- sp<ALooper> mDecoderLooper;
- sp<MediaCodec> mDecoder;
- Vector<sp<ABuffer> > mDecoderInputBuffers;
- Vector<sp<ABuffer> > mDecoderOutputBuffers;
- List<size_t> mDecoderInputBuffersAvailable;
- bool mDecoderNotificationPending;
-
- List<sp<ABuffer> > mAccessUnits;
-
- void onDecoderNotify();
- void scheduleDecoderNotification();
- void queueDecoderInputBuffers();
-
- void queueOutputBuffer(
- size_t index, int64_t timeUs, const sp<ABuffer> &buffer);
-
- DISALLOW_EVIL_CONSTRUCTORS(DecoderContext);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-/*
- A "push" audio renderer. The primary function of this renderer is to use
- an AudioTrack in push mode and making sure not to block the event loop
- be ensuring that calls to AudioTrack::write never block. This is done by
- estimating an upper bound of data that can be written to the AudioTrack
- buffer without delay.
-*/
-struct DirectRenderer::AudioRenderer : public AHandler {
- AudioRenderer(const sp<DecoderContext> &decoderContext);
-
- void queueInputBuffer(
- size_t index, int64_t timeUs, const sp<ABuffer> &buffer);
-
-protected:
- virtual ~AudioRenderer();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatPushAudio,
- };
-
- struct BufferInfo {
- size_t mIndex;
- int64_t mTimeUs;
- sp<ABuffer> mBuffer;
- };
-
- sp<DecoderContext> mDecoderContext;
- sp<AudioTrack> mAudioTrack;
-
- List<BufferInfo> mInputBuffers;
- bool mPushPending;
-
- size_t mNumFramesWritten;
-
- void schedulePushIfNecessary();
- void onPushAudio();
-
- ssize_t writeNonBlocking(const uint8_t *data, size_t size);
-
- DISALLOW_EVIL_CONSTRUCTORS(AudioRenderer);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-DirectRenderer::DecoderContext::DecoderContext(const sp<AMessage> ¬ify)
- : mNotify(notify),
- mDecoderNotificationPending(false) {
-}
-
-DirectRenderer::DecoderContext::~DecoderContext() {
- if (mDecoder != NULL) {
- mDecoder->release();
- mDecoder.clear();
-
- mDecoderLooper->stop();
- mDecoderLooper.clear();
- }
-}
-
-status_t DirectRenderer::DecoderContext::init(
- const sp<AMessage> &format,
- const sp<IGraphicBufferProducer> &surfaceTex) {
- CHECK(mDecoder == NULL);
-
- AString mime;
- CHECK(format->findString("mime", &mime));
-
- mDecoderLooper = new ALooper;
- mDecoderLooper->setName("video codec looper");
-
- mDecoderLooper->start(
- false /* runOnCallingThread */,
- false /* canCallJava */,
- PRIORITY_DEFAULT);
-
- mDecoder = MediaCodec::CreateByType(
- mDecoderLooper, mime.c_str(), false /* encoder */);
-
- CHECK(mDecoder != NULL);
-
- status_t err = mDecoder->configure(
- format,
- surfaceTex == NULL
- ? NULL : new Surface(surfaceTex),
- NULL /* crypto */,
- 0 /* flags */);
- CHECK_EQ(err, (status_t)OK);
-
- err = mDecoder->start();
- CHECK_EQ(err, (status_t)OK);
-
- err = mDecoder->getInputBuffers(
- &mDecoderInputBuffers);
- CHECK_EQ(err, (status_t)OK);
-
- err = mDecoder->getOutputBuffers(
- &mDecoderOutputBuffers);
- CHECK_EQ(err, (status_t)OK);
-
- scheduleDecoderNotification();
-
- return OK;
-}
-
-void DirectRenderer::DecoderContext::queueInputBuffer(
- const sp<ABuffer> &accessUnit) {
- CHECK(mDecoder != NULL);
-
- mAccessUnits.push_back(accessUnit);
- queueDecoderInputBuffers();
-}
-
-status_t DirectRenderer::DecoderContext::renderOutputBufferAndRelease(
- size_t index) {
- return mDecoder->renderOutputBufferAndRelease(index);
-}
-
-status_t DirectRenderer::DecoderContext::releaseOutputBuffer(size_t index) {
- return mDecoder->releaseOutputBuffer(index);
-}
-
-void DirectRenderer::DecoderContext::queueDecoderInputBuffers() {
- if (mDecoder == NULL) {
- return;
- }
-
- bool submittedMore = false;
-
- while (!mAccessUnits.empty()
- && !mDecoderInputBuffersAvailable.empty()) {
- size_t index = *mDecoderInputBuffersAvailable.begin();
-
- mDecoderInputBuffersAvailable.erase(
- mDecoderInputBuffersAvailable.begin());
-
- sp<ABuffer> srcBuffer = *mAccessUnits.begin();
- mAccessUnits.erase(mAccessUnits.begin());
-
- const sp<ABuffer> &dstBuffer =
- mDecoderInputBuffers.itemAt(index);
-
- memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size());
-
- int64_t timeUs;
- CHECK(srcBuffer->meta()->findInt64("timeUs", &timeUs));
-
- status_t err = mDecoder->queueInputBuffer(
- index,
- 0 /* offset */,
- srcBuffer->size(),
- timeUs,
- 0 /* flags */);
- CHECK_EQ(err, (status_t)OK);
-
- submittedMore = true;
- }
-
- if (submittedMore) {
- scheduleDecoderNotification();
- }
-}
-
-void DirectRenderer::DecoderContext::onMessageReceived(
- const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatDecoderNotify:
- {
- onDecoderNotify();
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void DirectRenderer::DecoderContext::onDecoderNotify() {
- mDecoderNotificationPending = false;
-
- for (;;) {
- size_t index;
- status_t err = mDecoder->dequeueInputBuffer(&index);
-
- if (err == OK) {
- mDecoderInputBuffersAvailable.push_back(index);
- } else if (err == -EAGAIN) {
- break;
- } else {
- TRESPASS();
- }
- }
-
- queueDecoderInputBuffers();
-
- for (;;) {
- size_t index;
- size_t offset;
- size_t size;
- int64_t timeUs;
- uint32_t flags;
- status_t err = mDecoder->dequeueOutputBuffer(
- &index,
- &offset,
- &size,
- &timeUs,
- &flags);
-
- if (err == OK) {
- queueOutputBuffer(
- index, timeUs, mDecoderOutputBuffers.itemAt(index));
- } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
- err = mDecoder->getOutputBuffers(
- &mDecoderOutputBuffers);
- CHECK_EQ(err, (status_t)OK);
- } else if (err == INFO_FORMAT_CHANGED) {
- // We don't care.
- } else if (err == -EAGAIN) {
- break;
- } else {
- TRESPASS();
- }
- }
-
- scheduleDecoderNotification();
-}
-
-void DirectRenderer::DecoderContext::scheduleDecoderNotification() {
- if (mDecoderNotificationPending) {
- return;
- }
-
- sp<AMessage> notify =
- new AMessage(kWhatDecoderNotify, id());
-
- mDecoder->requestActivityNotification(notify);
- mDecoderNotificationPending = true;
-}
-
-void DirectRenderer::DecoderContext::queueOutputBuffer(
- size_t index, int64_t timeUs, const sp<ABuffer> &buffer) {
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("what", kWhatOutputBufferReady);
- msg->setSize("index", index);
- msg->setInt64("timeUs", timeUs);
- msg->setBuffer("buffer", buffer);
- msg->post();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-DirectRenderer::AudioRenderer::AudioRenderer(
- const sp<DecoderContext> &decoderContext)
- : mDecoderContext(decoderContext),
- mPushPending(false),
- mNumFramesWritten(0) {
- mAudioTrack = new AudioTrack(
- AUDIO_STREAM_DEFAULT,
- 48000.0f,
- AUDIO_FORMAT_PCM,
- AUDIO_CHANNEL_OUT_STEREO,
- (int)0 /* frameCount */);
-
- CHECK_EQ((status_t)OK, mAudioTrack->initCheck());
-
- mAudioTrack->start();
-}
-
-DirectRenderer::AudioRenderer::~AudioRenderer() {
-}
-
-void DirectRenderer::AudioRenderer::queueInputBuffer(
- size_t index, int64_t timeUs, const sp<ABuffer> &buffer) {
- BufferInfo info;
- info.mIndex = index;
- info.mTimeUs = timeUs;
- info.mBuffer = buffer;
-
- mInputBuffers.push_back(info);
- schedulePushIfNecessary();
-}
-
-void DirectRenderer::AudioRenderer::onMessageReceived(
- const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatPushAudio:
- {
- onPushAudio();
- break;
- }
-
- default:
- break;
- }
-}
-
-void DirectRenderer::AudioRenderer::schedulePushIfNecessary() {
- if (mPushPending || mInputBuffers.empty()) {
- return;
- }
-
- mPushPending = true;
-
- uint32_t numFramesPlayed;
- CHECK_EQ(mAudioTrack->getPosition(&numFramesPlayed),
- (status_t)OK);
-
- uint32_t numFramesPendingPlayout = mNumFramesWritten - numFramesPlayed;
-
- // This is how long the audio sink will have data to
- // play back.
- const float msecsPerFrame = 1000.0f / mAudioTrack->getSampleRate();
-
- int64_t delayUs =
- msecsPerFrame * numFramesPendingPlayout * 1000ll;
-
- // Let's give it more data after about half that time
- // has elapsed.
- (new AMessage(kWhatPushAudio, id()))->post(delayUs / 2);
-}
-
-void DirectRenderer::AudioRenderer::onPushAudio() {
- mPushPending = false;
-
- while (!mInputBuffers.empty()) {
- const BufferInfo &info = *mInputBuffers.begin();
-
- ssize_t n = writeNonBlocking(
- info.mBuffer->data(), info.mBuffer->size());
-
- if (n < (ssize_t)info.mBuffer->size()) {
- CHECK_GE(n, 0);
-
- info.mBuffer->setRange(
- info.mBuffer->offset() + n, info.mBuffer->size() - n);
- break;
- }
-
- mDecoderContext->releaseOutputBuffer(info.mIndex);
-
- mInputBuffers.erase(mInputBuffers.begin());
- }
-
- schedulePushIfNecessary();
-}
-
-ssize_t DirectRenderer::AudioRenderer::writeNonBlocking(
- const uint8_t *data, size_t size) {
- uint32_t numFramesPlayed;
- status_t err = mAudioTrack->getPosition(&numFramesPlayed);
- if (err != OK) {
- return err;
- }
-
- ssize_t numFramesAvailableToWrite =
- mAudioTrack->frameCount() - (mNumFramesWritten - numFramesPlayed);
-
- size_t numBytesAvailableToWrite =
- numFramesAvailableToWrite * mAudioTrack->frameSize();
-
- if (size > numBytesAvailableToWrite) {
- size = numBytesAvailableToWrite;
- }
-
- CHECK_EQ(mAudioTrack->write(data, size), (ssize_t)size);
-
- size_t numFramesWritten = size / mAudioTrack->frameSize();
- mNumFramesWritten += numFramesWritten;
-
- return size;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-DirectRenderer::DirectRenderer(
- const sp<IGraphicBufferProducer> &bufferProducer)
- : mSurfaceTex(bufferProducer),
- mVideoRenderPending(false),
- mNumFramesLate(0),
- mNumFrames(0) {
-}
-
-DirectRenderer::~DirectRenderer() {
-}
-
-void DirectRenderer::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatDecoderNotify:
- {
- onDecoderNotify(msg);
- break;
- }
-
- case kWhatRenderVideo:
- {
- onRenderVideo();
- break;
- }
-
- case kWhatQueueAccessUnit:
- onQueueAccessUnit(msg);
- break;
-
- case kWhatSetFormat:
- onSetFormat(msg);
- break;
-
- default:
- TRESPASS();
- }
-}
-
-void DirectRenderer::setFormat(size_t trackIndex, const sp<AMessage> &format) {
- sp<AMessage> msg = new AMessage(kWhatSetFormat, id());
- msg->setSize("trackIndex", trackIndex);
- msg->setMessage("format", format);
- msg->post();
-}
-
-void DirectRenderer::onSetFormat(const sp<AMessage> &msg) {
- size_t trackIndex;
- CHECK(msg->findSize("trackIndex", &trackIndex));
-
- sp<AMessage> format;
- CHECK(msg->findMessage("format", &format));
-
- internalSetFormat(trackIndex, format);
-}
-
-void DirectRenderer::internalSetFormat(
- size_t trackIndex, const sp<AMessage> &format) {
- CHECK_LT(trackIndex, 2u);
-
- CHECK(mDecoderContext[trackIndex] == NULL);
-
- sp<AMessage> notify = new AMessage(kWhatDecoderNotify, id());
- notify->setSize("trackIndex", trackIndex);
-
- mDecoderContext[trackIndex] = new DecoderContext(notify);
- looper()->registerHandler(mDecoderContext[trackIndex]);
-
- CHECK_EQ((status_t)OK,
- mDecoderContext[trackIndex]->init(
- format, trackIndex == 0 ? mSurfaceTex : NULL));
-
- if (trackIndex == 1) {
- // Audio
- mAudioRenderer = new AudioRenderer(mDecoderContext[1]);
- looper()->registerHandler(mAudioRenderer);
- }
-}
-
-void DirectRenderer::queueAccessUnit(
- size_t trackIndex, const sp<ABuffer> &accessUnit) {
- sp<AMessage> msg = new AMessage(kWhatQueueAccessUnit, id());
- msg->setSize("trackIndex", trackIndex);
- msg->setBuffer("accessUnit", accessUnit);
- msg->post();
-}
-
-void DirectRenderer::onQueueAccessUnit(const sp<AMessage> &msg) {
- size_t trackIndex;
- CHECK(msg->findSize("trackIndex", &trackIndex));
-
- sp<ABuffer> accessUnit;
- CHECK(msg->findBuffer("accessUnit", &accessUnit));
-
- CHECK_LT(trackIndex, 2u);
- CHECK(mDecoderContext[trackIndex] != NULL);
-
- mDecoderContext[trackIndex]->queueInputBuffer(accessUnit);
-}
-
-void DirectRenderer::onDecoderNotify(const sp<AMessage> &msg) {
- size_t trackIndex;
- CHECK(msg->findSize("trackIndex", &trackIndex));
-
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- switch (what) {
- case DecoderContext::kWhatOutputBufferReady:
- {
- size_t index;
- CHECK(msg->findSize("index", &index));
-
- int64_t timeUs;
- CHECK(msg->findInt64("timeUs", &timeUs));
-
- sp<ABuffer> buffer;
- CHECK(msg->findBuffer("buffer", &buffer));
-
- queueOutputBuffer(trackIndex, index, timeUs, buffer);
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void DirectRenderer::queueOutputBuffer(
- size_t trackIndex,
- size_t index, int64_t timeUs, const sp<ABuffer> &buffer) {
- if (trackIndex == 1) {
- // Audio
- mAudioRenderer->queueInputBuffer(index, timeUs, buffer);
- return;
- }
-
- OutputInfo info;
- info.mIndex = index;
- info.mTimeUs = timeUs;
- info.mBuffer = buffer;
- mVideoOutputBuffers.push_back(info);
-
- scheduleVideoRenderIfNecessary();
-}
-
-void DirectRenderer::scheduleVideoRenderIfNecessary() {
- if (mVideoRenderPending || mVideoOutputBuffers.empty()) {
- return;
- }
-
- mVideoRenderPending = true;
-
- int64_t timeUs = (*mVideoOutputBuffers.begin()).mTimeUs;
- int64_t nowUs = ALooper::GetNowUs();
-
- int64_t delayUs = timeUs - nowUs;
-
- (new AMessage(kWhatRenderVideo, id()))->post(delayUs);
-}
-
-void DirectRenderer::onRenderVideo() {
- mVideoRenderPending = false;
-
- int64_t nowUs = ALooper::GetNowUs();
-
- while (!mVideoOutputBuffers.empty()) {
- const OutputInfo &info = *mVideoOutputBuffers.begin();
-
- if (info.mTimeUs > nowUs) {
- break;
- }
-
- if (info.mTimeUs + 15000ll < nowUs) {
- ++mNumFramesLate;
- }
- ++mNumFrames;
-
- status_t err =
- mDecoderContext[0]->renderOutputBufferAndRelease(info.mIndex);
- CHECK_EQ(err, (status_t)OK);
-
- mVideoOutputBuffers.erase(mVideoOutputBuffers.begin());
- }
-
- scheduleVideoRenderIfNecessary();
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h
deleted file mode 100644
index 07c2170..0000000
--- a/media/libstagefright/wifi-display/sink/DirectRenderer.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef DIRECT_RENDERER_H_
-
-#define DIRECT_RENDERER_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-struct ABuffer;
-struct IGraphicBufferProducer;
-
-// Renders audio and video data queued by calls to "queueAccessUnit".
-struct DirectRenderer : public AHandler {
- DirectRenderer(const sp<IGraphicBufferProducer> &bufferProducer);
-
- void setFormat(size_t trackIndex, const sp<AMessage> &format);
- void queueAccessUnit(size_t trackIndex, const sp<ABuffer> &accessUnit);
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual ~DirectRenderer();
-
-private:
- struct DecoderContext;
- struct AudioRenderer;
-
- enum {
- kWhatDecoderNotify,
- kWhatRenderVideo,
- kWhatQueueAccessUnit,
- kWhatSetFormat,
- };
-
- struct OutputInfo {
- size_t mIndex;
- int64_t mTimeUs;
- sp<ABuffer> mBuffer;
- };
-
- sp<IGraphicBufferProducer> mSurfaceTex;
-
- sp<DecoderContext> mDecoderContext[2];
- List<OutputInfo> mVideoOutputBuffers;
-
- bool mVideoRenderPending;
-
- sp<AudioRenderer> mAudioRenderer;
-
- int32_t mNumFramesLate;
- int32_t mNumFrames;
-
- void onDecoderNotify(const sp<AMessage> &msg);
-
- void queueOutputBuffer(
- size_t trackIndex,
- size_t index, int64_t timeUs, const sp<ABuffer> &buffer);
-
- void scheduleVideoRenderIfNecessary();
- void onRenderVideo();
-
- void onSetFormat(const sp<AMessage> &msg);
- void onQueueAccessUnit(const sp<AMessage> &msg);
-
- void internalSetFormat(size_t trackIndex, const sp<AMessage> &format);
-
- DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer);
-};
-
-} // namespace android
-
-#endif // DIRECT_RENDERER_H_
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
deleted file mode 100644
index bc88f1e..0000000
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
+++ /dev/null
@@ -1,917 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "WifiDisplaySink"
-#include <utils/Log.h>
-
-#include "WifiDisplaySink.h"
-
-#include "DirectRenderer.h"
-#include "MediaReceiver.h"
-#include "TimeSyncer.h"
-
-#include <cutils/properties.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ParsedMessage.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-namespace android {
-
-// static
-const AString WifiDisplaySink::sUserAgent = MakeUserAgent();
-
-WifiDisplaySink::WifiDisplaySink(
- uint32_t flags,
- const sp<ANetworkSession> &netSession,
- const sp<IGraphicBufferProducer> &bufferProducer,
- const sp<AMessage> ¬ify)
- : mState(UNDEFINED),
- mFlags(flags),
- mNetSession(netSession),
- mSurfaceTex(bufferProducer),
- mNotify(notify),
- mUsingTCPTransport(false),
- mUsingTCPInterleaving(false),
- mSessionID(0),
- mNextCSeq(1),
- mIDRFrameRequestPending(false),
- mTimeOffsetUs(0ll),
- mTimeOffsetValid(false),
- mSetupDeferred(false),
- mLatencyCount(0),
- mLatencySumUs(0ll),
- mLatencyMaxUs(0ll),
- mMaxDelayMs(-1ll) {
- // We support any and all resolutions, but prefer 720p30
- mSinkSupportedVideoFormats.setNativeResolution(
- VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30
-
- mSinkSupportedVideoFormats.enableAll();
-}
-
-WifiDisplaySink::~WifiDisplaySink() {
-}
-
-void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort) {
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setString("sourceHost", sourceHost);
- msg->setInt32("sourcePort", sourcePort);
- msg->post();
-}
-
-void WifiDisplaySink::start(const char *uri) {
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setString("setupURI", uri);
- msg->post();
-}
-
-// static
-bool WifiDisplaySink::ParseURL(
- const char *url, AString *host, int32_t *port, AString *path,
- AString *user, AString *pass) {
- host->clear();
- *port = 0;
- path->clear();
- user->clear();
- pass->clear();
-
- if (strncasecmp("rtsp://", url, 7)) {
- return false;
- }
-
- const char *slashPos = strchr(&url[7], '/');
-
- if (slashPos == NULL) {
- host->setTo(&url[7]);
- path->setTo("/");
- } else {
- host->setTo(&url[7], slashPos - &url[7]);
- path->setTo(slashPos);
- }
-
- ssize_t atPos = host->find("@");
-
- if (atPos >= 0) {
- // Split of user:pass@ from hostname.
-
- AString userPass(*host, 0, atPos);
- host->erase(0, atPos + 1);
-
- ssize_t colonPos = userPass.find(":");
-
- if (colonPos < 0) {
- *user = userPass;
- } else {
- user->setTo(userPass, 0, colonPos);
- pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1);
- }
- }
-
- const char *colonPos = strchr(host->c_str(), ':');
-
- if (colonPos != NULL) {
- char *end;
- unsigned long x = strtoul(colonPos + 1, &end, 10);
-
- if (end == colonPos + 1 || *end != '\0' || x >= 65536) {
- return false;
- }
-
- *port = x;
-
- size_t colonOffset = colonPos - host->c_str();
- size_t trailing = host->size() - colonOffset;
- host->erase(colonOffset, trailing);
- } else {
- *port = 554;
- }
-
- return true;
-}
-
-void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatStart:
- {
- sleep(2); // XXX
-
- int32_t sourcePort;
- CHECK(msg->findString("sourceHost", &mRTSPHost));
- CHECK(msg->findInt32("sourcePort", &sourcePort));
-
- sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id());
-
- status_t err = mNetSession->createRTSPClient(
- mRTSPHost.c_str(), sourcePort, notify, &mSessionID);
- CHECK_EQ(err, (status_t)OK);
-
- mState = CONNECTING;
- break;
- }
-
- case kWhatRTSPNotify:
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::kWhatError:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- AString detail;
- CHECK(msg->findString("detail", &detail));
-
- ALOGE("An error occurred in session %d (%d, '%s/%s').",
- sessionID,
- err,
- detail.c_str(),
- strerror(-err));
-
- if (sessionID == mSessionID) {
- ALOGI("Lost control connection.");
-
- // The control connection is dead now.
- mNetSession->destroySession(mSessionID);
- mSessionID = 0;
-
- if (mNotify == NULL) {
- looper()->stop();
- } else {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatDisconnected);
- notify->post();
- }
- }
- break;
- }
-
- case ANetworkSession::kWhatConnected:
- {
- ALOGI("We're now connected.");
- mState = CONNECTED;
-
- if (mFlags & FLAG_SPECIAL_MODE) {
- sp<AMessage> notify = new AMessage(
- kWhatTimeSyncerNotify, id());
-
- mTimeSyncer = new TimeSyncer(mNetSession, notify);
- looper()->registerHandler(mTimeSyncer);
-
- mTimeSyncer->startClient(mRTSPHost.c_str(), 8123);
- }
- break;
- }
-
- case ANetworkSession::kWhatData:
- {
- onReceiveClientData(msg);
- break;
- }
-
- default:
- TRESPASS();
- }
- break;
- }
-
- case kWhatStop:
- {
- looper()->stop();
- break;
- }
-
- case kWhatMediaReceiverNotify:
- {
- onMediaReceiverNotify(msg);
- break;
- }
-
- case kWhatTimeSyncerNotify:
- {
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- if (what == TimeSyncer::kWhatTimeOffset) {
- CHECK(msg->findInt64("offset", &mTimeOffsetUs));
- mTimeOffsetValid = true;
-
- if (mSetupDeferred) {
- CHECK_EQ((status_t)OK,
- sendSetup(
- mSessionID,
- "rtsp://x.x.x.x:x/wfd1.0/streamid=0"));
-
- mSetupDeferred = false;
- }
- }
- break;
- }
-
- case kWhatReportLateness:
- {
- if (mLatencyCount > 0) {
- int64_t avgLatencyUs = mLatencySumUs / mLatencyCount;
-
- ALOGV("avg. latency = %lld ms (max %lld ms)",
- avgLatencyUs / 1000ll,
- mLatencyMaxUs / 1000ll);
-
- sp<AMessage> params = new AMessage;
- params->setInt64("avgLatencyUs", avgLatencyUs);
- params->setInt64("maxLatencyUs", mLatencyMaxUs);
- mMediaReceiver->informSender(0 /* trackIndex */, params);
- }
-
- mLatencyCount = 0;
- mLatencySumUs = 0ll;
- mLatencyMaxUs = 0ll;
-
- msg->post(kReportLatenessEveryUs);
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void WifiDisplaySink::dumpDelay(size_t trackIndex, int64_t timeUs) {
- int64_t delayMs = (ALooper::GetNowUs() - timeUs) / 1000ll;
-
- if (delayMs > mMaxDelayMs) {
- mMaxDelayMs = delayMs;
- }
-
- static const int64_t kMinDelayMs = 0;
- static const int64_t kMaxDelayMs = 300;
-
- const char *kPattern = "########################################";
- size_t kPatternSize = strlen(kPattern);
-
- int n = (kPatternSize * (delayMs - kMinDelayMs))
- / (kMaxDelayMs - kMinDelayMs);
-
- if (n < 0) {
- n = 0;
- } else if ((size_t)n > kPatternSize) {
- n = kPatternSize;
- }
-
- ALOGI("[%lld]: (%4lld ms / %4lld ms) %s",
- timeUs / 1000,
- delayMs,
- mMaxDelayMs,
- kPattern + kPatternSize - n);
-}
-
-void WifiDisplaySink::onMediaReceiverNotify(const sp<AMessage> &msg) {
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- switch (what) {
- case MediaReceiver::kWhatInitDone:
- {
- status_t err;
- CHECK(msg->findInt32("err", &err));
-
- ALOGI("MediaReceiver initialization completed w/ err %d", err);
- break;
- }
-
- case MediaReceiver::kWhatError:
- {
- status_t err;
- CHECK(msg->findInt32("err", &err));
-
- ALOGE("MediaReceiver signaled error %d", err);
- break;
- }
-
- case MediaReceiver::kWhatAccessUnit:
- {
- if (mRenderer == NULL) {
- mRenderer = new DirectRenderer(mSurfaceTex);
- looper()->registerHandler(mRenderer);
- }
-
- sp<ABuffer> accessUnit;
- CHECK(msg->findBuffer("accessUnit", &accessUnit));
-
- int64_t timeUs;
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-
- if (!mTimeOffsetValid && !(mFlags & FLAG_SPECIAL_MODE)) {
- mTimeOffsetUs = timeUs - ALooper::GetNowUs();
- mTimeOffsetValid = true;
- }
-
- CHECK(mTimeOffsetValid);
-
- // We are the timesync _client_,
- // client time = server time - time offset.
- timeUs -= mTimeOffsetUs;
-
- size_t trackIndex;
- CHECK(msg->findSize("trackIndex", &trackIndex));
-
- int64_t nowUs = ALooper::GetNowUs();
- int64_t delayUs = nowUs - timeUs;
-
- mLatencySumUs += delayUs;
- if (mLatencyCount == 0 || delayUs > mLatencyMaxUs) {
- mLatencyMaxUs = delayUs;
- }
- ++mLatencyCount;
-
- // dumpDelay(trackIndex, timeUs);
-
- timeUs += 220000ll; // Assume 220 ms of latency
- accessUnit->meta()->setInt64("timeUs", timeUs);
-
- sp<AMessage> format;
- if (msg->findMessage("format", &format)) {
- mRenderer->setFormat(trackIndex, format);
- }
-
- mRenderer->queueAccessUnit(trackIndex, accessUnit);
- break;
- }
-
- case MediaReceiver::kWhatPacketLost:
- {
-#if 0
- if (!mIDRFrameRequestPending) {
- ALOGI("requesting IDR frame");
-
- sendIDRFrameRequest(mSessionID);
- }
-#endif
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void WifiDisplaySink::registerResponseHandler(
- int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func) {
- ResponseID id;
- id.mSessionID = sessionID;
- id.mCSeq = cseq;
- mResponseHandlers.add(id, func);
-}
-
-status_t WifiDisplaySink::sendM2(int32_t sessionID) {
- AString request = "OPTIONS * RTSP/1.0\r\n";
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append(
- "Require: org.wfa.wfd1.0\r\n"
- "\r\n");
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceiveM2Response);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySink::onReceiveM2Response(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- return OK;
-}
-
-status_t WifiDisplaySink::onReceiveSetupResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- if (!msg->findString("session", &mPlaybackSessionID)) {
- return ERROR_MALFORMED;
- }
-
- if (!ParsedMessage::GetInt32Attribute(
- mPlaybackSessionID.c_str(),
- "timeout",
- &mPlaybackSessionTimeoutSecs)) {
- mPlaybackSessionTimeoutSecs = -1;
- }
-
- ssize_t colonPos = mPlaybackSessionID.find(";");
- if (colonPos >= 0) {
- // Strip any options from the returned session id.
- mPlaybackSessionID.erase(
- colonPos, mPlaybackSessionID.size() - colonPos);
- }
-
- status_t err = configureTransport(msg);
-
- if (err != OK) {
- return err;
- }
-
- mState = PAUSED;
-
- return sendPlay(
- sessionID,
- "rtsp://x.x.x.x:x/wfd1.0/streamid=0");
-}
-
-status_t WifiDisplaySink::configureTransport(const sp<ParsedMessage> &msg) {
- if (mUsingTCPTransport && !(mFlags & FLAG_SPECIAL_MODE)) {
- // In "special" mode we still use a UDP RTCP back-channel that
- // needs connecting.
- return OK;
- }
-
- AString transport;
- if (!msg->findString("transport", &transport)) {
- ALOGE("Missing 'transport' field in SETUP response.");
- return ERROR_MALFORMED;
- }
-
- AString sourceHost;
- if (!ParsedMessage::GetAttribute(
- transport.c_str(), "source", &sourceHost)) {
- sourceHost = mRTSPHost;
- }
-
- AString serverPortStr;
- if (!ParsedMessage::GetAttribute(
- transport.c_str(), "server_port", &serverPortStr)) {
- ALOGE("Missing 'server_port' in Transport field.");
- return ERROR_MALFORMED;
- }
-
- int rtpPort, rtcpPort;
- if (sscanf(serverPortStr.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
- || rtpPort <= 0 || rtpPort > 65535
- || rtcpPort <=0 || rtcpPort > 65535
- || rtcpPort != rtpPort + 1) {
- ALOGE("Invalid server_port description '%s'.",
- serverPortStr.c_str());
-
- return ERROR_MALFORMED;
- }
-
- if (rtpPort & 1) {
- ALOGW("Server picked an odd numbered RTP port.");
- }
-
- return mMediaReceiver->connectTrack(
- 0 /* trackIndex */, sourceHost.c_str(), rtpPort, rtcpPort);
-}
-
-status_t WifiDisplaySink::onReceivePlayResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- mState = PLAYING;
-
- (new AMessage(kWhatReportLateness, id()))->post(kReportLatenessEveryUs);
-
- return OK;
-}
-
-status_t WifiDisplaySink::onReceiveIDRFrameRequestResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- CHECK(mIDRFrameRequestPending);
- mIDRFrameRequestPending = false;
-
- return OK;
-}
-
-void WifiDisplaySink::onReceiveClientData(const sp<AMessage> &msg) {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- sp<RefBase> obj;
- CHECK(msg->findObject("data", &obj));
-
- sp<ParsedMessage> data =
- static_cast<ParsedMessage *>(obj.get());
-
- ALOGV("session %d received '%s'",
- sessionID, data->debugString().c_str());
-
- AString method;
- AString uri;
- data->getRequestField(0, &method);
-
- int32_t cseq;
- if (!data->findInt32("cseq", &cseq)) {
- sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */);
- return;
- }
-
- if (method.startsWith("RTSP/")) {
- // This is a response.
-
- ResponseID id;
- id.mSessionID = sessionID;
- id.mCSeq = cseq;
-
- ssize_t index = mResponseHandlers.indexOfKey(id);
-
- if (index < 0) {
- ALOGW("Received unsolicited server response, cseq %d", cseq);
- return;
- }
-
- HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index);
- mResponseHandlers.removeItemsAt(index);
-
- status_t err = (this->*func)(sessionID, data);
- CHECK_EQ(err, (status_t)OK);
- } else {
- AString version;
- data->getRequestField(2, &version);
- if (!(version == AString("RTSP/1.0"))) {
- sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq);
- return;
- }
-
- if (method == "OPTIONS") {
- onOptionsRequest(sessionID, cseq, data);
- } else if (method == "GET_PARAMETER") {
- onGetParameterRequest(sessionID, cseq, data);
- } else if (method == "SET_PARAMETER") {
- onSetParameterRequest(sessionID, cseq, data);
- } else {
- sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);
- }
- }
-}
-
-void WifiDisplaySink::onOptionsRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq);
- response.append("Public: org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER\r\n");
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
-
- err = sendM2(sessionID);
- CHECK_EQ(err, (status_t)OK);
-}
-
-void WifiDisplaySink::onGetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- AString body;
-
- if (mState == CONNECTED) {
- mUsingTCPTransport = false;
- mUsingTCPInterleaving = false;
-
- char val[PROPERTY_VALUE_MAX];
- if (property_get("media.wfd-sink.tcp-mode", val, NULL)) {
- if (!strcasecmp("true", val) || !strcmp("1", val)) {
- ALOGI("Using TCP unicast transport.");
- mUsingTCPTransport = true;
- mUsingTCPInterleaving = false;
- } else if (!strcasecmp("interleaved", val)) {
- ALOGI("Using TCP interleaved transport.");
- mUsingTCPTransport = true;
- mUsingTCPInterleaving = true;
- }
- } else if (mFlags & FLAG_SPECIAL_MODE) {
- mUsingTCPTransport = true;
- }
-
- body = "wfd_video_formats: ";
- body.append(mSinkSupportedVideoFormats.getFormatSpec());
-
- body.append(
- "\r\nwfd_audio_codecs: AAC 0000000F 00\r\n"
- "wfd_client_rtp_ports: RTP/AVP/");
-
- if (mUsingTCPTransport) {
- body.append("TCP;");
- if (mUsingTCPInterleaving) {
- body.append("interleaved");
- } else {
- body.append("unicast 19000 0");
- }
- } else {
- body.append("UDP;unicast 19000 0");
- }
-
- body.append(" mode=play\r\n");
- }
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq);
- response.append("Content-Type: text/parameters\r\n");
- response.append(StringPrintf("Content-Length: %d\r\n", body.size()));
- response.append("\r\n");
- response.append(body);
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
-}
-
-status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) {
- sp<AMessage> notify = new AMessage(kWhatMediaReceiverNotify, id());
-
- mMediaReceiverLooper = new ALooper;
- mMediaReceiverLooper->setName("media_receiver");
-
- mMediaReceiverLooper->start(
- false /* runOnCallingThread */,
- false /* canCallJava */,
- PRIORITY_AUDIO);
-
- mMediaReceiver = new MediaReceiver(mNetSession, notify);
- mMediaReceiverLooper->registerHandler(mMediaReceiver);
-
- RTPReceiver::TransportMode rtpMode = RTPReceiver::TRANSPORT_UDP;
- if (mUsingTCPTransport) {
- if (mUsingTCPInterleaving) {
- rtpMode = RTPReceiver::TRANSPORT_TCP_INTERLEAVED;
- } else {
- rtpMode = RTPReceiver::TRANSPORT_TCP;
- }
- }
-
- int32_t localRTPPort;
- status_t err = mMediaReceiver->addTrack(
- rtpMode, RTPReceiver::TRANSPORT_UDP /* rtcpMode */, &localRTPPort);
-
- if (err == OK) {
- err = mMediaReceiver->initAsync(MediaReceiver::MODE_TRANSPORT_STREAM);
- }
-
- if (err != OK) {
- mMediaReceiverLooper->unregisterHandler(mMediaReceiver->id());
- mMediaReceiver.clear();
-
- mMediaReceiverLooper->stop();
- mMediaReceiverLooper.clear();
-
- return err;
- }
-
- AString request = StringPrintf("SETUP %s RTSP/1.0\r\n", uri);
-
- AppendCommonResponse(&request, mNextCSeq);
-
- if (rtpMode == RTPReceiver::TRANSPORT_TCP_INTERLEAVED) {
- request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n");
- } else if (rtpMode == RTPReceiver::TRANSPORT_TCP) {
- if (mFlags & FLAG_SPECIAL_MODE) {
- // This isn't quite true, since the RTP connection is through TCP
- // and the RTCP connection through UDP...
- request.append(
- StringPrintf(
- "Transport: RTP/AVP/TCP;unicast;client_port=%d-%d\r\n",
- localRTPPort, localRTPPort + 1));
- } else {
- request.append(
- StringPrintf(
- "Transport: RTP/AVP/TCP;unicast;client_port=%d\r\n",
- localRTPPort));
- }
- } else {
- request.append(
- StringPrintf(
- "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n",
- localRTPPort,
- localRTPPort + 1));
- }
-
- request.append("\r\n");
-
- ALOGV("request = '%s'", request.c_str());
-
- err = mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceiveSetupResponse);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) {
- AString request = StringPrintf("PLAY %s RTSP/1.0\r\n", uri);
-
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));
- request.append("\r\n");
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceivePlayResponse);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySink::sendIDRFrameRequest(int32_t sessionID) {
- CHECK(!mIDRFrameRequestPending);
-
- AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
-
- AppendCommonResponse(&request, mNextCSeq);
-
- AString content = "wfd_idr_request\r\n";
-
- request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));
- request.append(StringPrintf("Content-Length: %d\r\n", content.size()));
- request.append("\r\n");
- request.append(content);
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID,
- mNextCSeq,
- &WifiDisplaySink::onReceiveIDRFrameRequestResponse);
-
- ++mNextCSeq;
-
- mIDRFrameRequestPending = true;
-
- return OK;
-}
-
-void WifiDisplaySink::onSetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- const char *content = data->getContent();
-
- if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) {
- if ((mFlags & FLAG_SPECIAL_MODE) && !mTimeOffsetValid) {
- mSetupDeferred = true;
- } else {
- status_t err =
- sendSetup(
- sessionID,
- "rtsp://x.x.x.x:x/wfd1.0/streamid=0");
-
- CHECK_EQ(err, (status_t)OK);
- }
- }
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq);
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
-}
-
-void WifiDisplaySink::sendErrorResponse(
- int32_t sessionID,
- const char *errorDetail,
- int32_t cseq) {
- AString response;
- response.append("RTSP/1.0 ");
- response.append(errorDetail);
- response.append("\r\n");
-
- AppendCommonResponse(&response, cseq);
-
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
-}
-
-// static
-void WifiDisplaySink::AppendCommonResponse(AString *response, int32_t cseq) {
- time_t now = time(NULL);
- struct tm *now2 = gmtime(&now);
- char buf[128];
- strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", now2);
-
- response->append("Date: ");
- response->append(buf);
- response->append("\r\n");
-
- response->append(StringPrintf("User-Agent: %s\r\n", sUserAgent.c_str()));
-
- if (cseq >= 0) {
- response->append(StringPrintf("CSeq: %d\r\n", cseq));
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
deleted file mode 100644
index dc1fc32..0000000
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef WIFI_DISPLAY_SINK_H_
-
-#define WIFI_DISPLAY_SINK_H_
-
-#include "VideoFormats.h"
-
-#include <gui/Surface.h>
-#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-
-namespace android {
-
-struct AMessage;
-struct DirectRenderer;
-struct MediaReceiver;
-struct ParsedMessage;
-struct TimeSyncer;
-
-// Represents the RTSP client acting as a wifi display sink.
-// Connects to a wifi display source and renders the incoming
-// transport stream using a MediaPlayer instance.
-struct WifiDisplaySink : public AHandler {
- enum {
- kWhatDisconnected,
- };
-
- enum Flags {
- FLAG_SPECIAL_MODE = 1,
- };
-
- // If no notification message is specified (notify == NULL)
- // the sink will stop its looper() once the session ends,
- // otherwise it will post an appropriate notification but leave
- // the looper() running.
- WifiDisplaySink(
- uint32_t flags,
- const sp<ANetworkSession> &netSession,
- const sp<IGraphicBufferProducer> &bufferProducer = NULL,
- const sp<AMessage> ¬ify = NULL);
-
- void start(const char *sourceHost, int32_t sourcePort);
- void start(const char *uri);
-
-protected:
- virtual ~WifiDisplaySink();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum State {
- UNDEFINED,
- CONNECTING,
- CONNECTED,
- PAUSED,
- PLAYING,
- };
-
- enum {
- kWhatStart,
- kWhatRTSPNotify,
- kWhatStop,
- kWhatMediaReceiverNotify,
- kWhatTimeSyncerNotify,
- kWhatReportLateness,
- };
-
- struct ResponseID {
- int32_t mSessionID;
- int32_t mCSeq;
-
- bool operator<(const ResponseID &other) const {
- return mSessionID < other.mSessionID
- || (mSessionID == other.mSessionID
- && mCSeq < other.mCSeq);
- }
- };
-
- typedef status_t (WifiDisplaySink::*HandleRTSPResponseFunc)(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- static const int64_t kReportLatenessEveryUs = 1000000ll;
-
- static const AString sUserAgent;
-
- State mState;
- uint32_t mFlags;
- VideoFormats mSinkSupportedVideoFormats;
- sp<ANetworkSession> mNetSession;
- sp<IGraphicBufferProducer> mSurfaceTex;
- sp<AMessage> mNotify;
- sp<TimeSyncer> mTimeSyncer;
- bool mUsingTCPTransport;
- bool mUsingTCPInterleaving;
- AString mRTSPHost;
- int32_t mSessionID;
-
- int32_t mNextCSeq;
-
- KeyedVector<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
-
- sp<ALooper> mMediaReceiverLooper;
- sp<MediaReceiver> mMediaReceiver;
- sp<DirectRenderer> mRenderer;
-
- AString mPlaybackSessionID;
- int32_t mPlaybackSessionTimeoutSecs;
-
- bool mIDRFrameRequestPending;
-
- int64_t mTimeOffsetUs;
- bool mTimeOffsetValid;
-
- bool mSetupDeferred;
-
- size_t mLatencyCount;
- int64_t mLatencySumUs;
- int64_t mLatencyMaxUs;
-
- int64_t mMaxDelayMs;
-
- status_t sendM2(int32_t sessionID);
- status_t sendSetup(int32_t sessionID, const char *uri);
- status_t sendPlay(int32_t sessionID, const char *uri);
- status_t sendIDRFrameRequest(int32_t sessionID);
-
- status_t onReceiveM2Response(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- status_t onReceiveSetupResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- status_t configureTransport(const sp<ParsedMessage> &msg);
-
- status_t onReceivePlayResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- status_t onReceiveIDRFrameRequestResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- void registerResponseHandler(
- int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func);
-
- void onReceiveClientData(const sp<AMessage> &msg);
-
- void onOptionsRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- void onGetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- void onSetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- void onMediaReceiverNotify(const sp<AMessage> &msg);
-
- void sendErrorResponse(
- int32_t sessionID,
- const char *errorDetail,
- int32_t cseq);
-
- static void AppendCommonResponse(AString *response, int32_t cseq);
-
- bool ParseURL(
- const char *url, AString *host, int32_t *port, AString *path,
- AString *user, AString *pass);
-
- void dumpDelay(size_t trackIndex, int64_t timeUs);
-
- DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySink);
-};
-
-} // namespace android
-
-#endif // WIFI_DISPLAY_SINK_H_
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index 286ea13..1a5acba 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -29,6 +29,7 @@
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <media/IHDCP.h>
+#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -749,7 +750,8 @@
mExtractor = new NuMediaExtractor;
- status_t err = mExtractor->setDataSource(mMediaPath.c_str());
+ status_t err = mExtractor->setDataSource(
+ NULL /* httpService */, mMediaPath.c_str());
if (err != OK) {
return err;
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.cpp b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
index cc8dee3..59d7e6e 100644
--- a/media/libstagefright/wifi-display/source/RepeaterSource.cpp
+++ b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
@@ -79,6 +79,8 @@
ALOGV("stopping");
+ status_t err = mSource->stop();
+
if (mLooper != NULL) {
mLooper->stop();
mLooper.clear();
@@ -92,7 +94,6 @@
mBuffer = NULL;
}
- status_t err = mSource->stop();
ALOGV("stopped");
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
index c674700..50d317a 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
@@ -216,7 +216,7 @@
uint8_t *ptr = dup->data();
*ptr++ = 0xff;
- *ptr++ = 0xf1; // b11110001, ID=0, layer=0, protection_absent=1
+ *ptr++ = 0xf9; // b11111001, ID=1(MPEG-2), layer=0, protection_absent=1
*ptr++ =
profile << 6
@@ -565,7 +565,7 @@
}
}
- // size_t numPaddingBytes = sizeAvailableForPayload - numBytesOfPayload;
+ size_t numPaddingBytes = sizeAvailableForPayload - numBytesOfPayload;
ALOGV("packet 1 contains %zd padding bytes and %zd bytes of payload",
numPaddingBytes, numBytesOfPayload);
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index d72349d..da405e2 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -22,7 +22,6 @@
#include "PlaybackSession.h"
#include "Parameters.h"
#include "rtp/RTPSender.h"
-#include "TimeSyncer.h"
#include <binder/IServiceManager.h>
#include <gui/IGraphicBufferProducer.h>
@@ -173,15 +172,7 @@
}
}
- if (err == OK) {
- sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
- mTimeSyncer = new TimeSyncer(mNetSession, notify);
- looper()->registerHandler(mTimeSyncer);
-
- mTimeSyncer->startServer(8123);
-
- mState = AWAITING_CLIENT_CONNECTION;
- }
+ mState = AWAITING_CLIENT_CONNECTION;
sp<AMessage> response = new AMessage;
response->setInt32("err", err);
@@ -556,11 +547,6 @@
break;
}
- case kWhatTimeSyncerNotify:
- {
- break;
- }
-
default:
TRESPASS();
}
@@ -760,7 +746,7 @@
}
status_t WifiDisplaySource::onReceiveM1Response(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
+ int32_t /* sessionID */, const sp<ParsedMessage> &msg) {
int32_t statusCode;
if (!msg->getStatusCode(&statusCode)) {
return ERROR_MALFORMED;
@@ -1005,7 +991,7 @@
}
status_t WifiDisplaySource::onReceiveM5Response(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
+ int32_t /* sessionID */, const sp<ParsedMessage> &msg) {
int32_t statusCode;
if (!msg->getStatusCode(&statusCode)) {
return ERROR_MALFORMED;
@@ -1019,7 +1005,7 @@
}
status_t WifiDisplaySource::onReceiveM16Response(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
+ int32_t sessionID, const sp<ParsedMessage> & /* msg */) {
// If only the response was required to include a "Session:" header...
CHECK_EQ(sessionID, mClientSessionID);
@@ -1694,7 +1680,7 @@
}
void WifiDisplaySource::HDCPObserver::notify(
- int msg, int ext1, int ext2, const Parcel *obj) {
+ int msg, int ext1, int ext2, const Parcel * /* obj */) {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("msg", msg);
notify->setInt32("ext1", ext1);
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index 4f11712..750265f 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -30,7 +30,6 @@
struct IHDCP;
struct IRemoteDisplayClient;
struct ParsedMessage;
-struct TimeSyncer;
// Represents the RTSP server acting as a wifi display source.
// Manages incoming connections, sets up Playback sessions as necessary.
@@ -83,7 +82,6 @@
kWhatHDCPNotify,
kWhatFinishStop2,
kWhatTeardownTriggerTimedOut,
- kWhatTimeSyncerNotify,
};
struct ResponseID {
@@ -120,7 +118,6 @@
sp<ANetworkSession> mNetSession;
sp<IRemoteDisplayClient> mClient;
AString mMediaPath;
- sp<TimeSyncer> mTimeSyncer;
struct in_addr mInterfaceAddr;
int32_t mSessionID;
diff --git a/media/libstagefright/wifi-display/udptest.cpp b/media/libstagefright/wifi-display/udptest.cpp
deleted file mode 100644
index 61eb9f9..0000000
--- a/media/libstagefright/wifi-display/udptest.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NEBUG 0
-#define LOG_TAG "udptest"
-#include <utils/Log.h>
-
-#include "TimeSyncer.h"
-
-#include <binder/ProcessState.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-
-namespace android {
-
-} // namespace android
-
-static void usage(const char *me) {
- fprintf(stderr,
- "usage: %s -c host[:port]\tconnect to test server\n"
- " -l \tcreate a test server\n",
- me);
-}
-
-int main(int argc, char **argv) {
- using namespace android;
-
- ProcessState::self()->startThreadPool();
-
- int32_t localPort = -1;
- int32_t connectToPort = -1;
- AString connectToHost;
-
- int res;
- while ((res = getopt(argc, argv, "hc:l:")) >= 0) {
- switch (res) {
- case 'c':
- {
- const char *colonPos = strrchr(optarg, ':');
-
- if (colonPos == NULL) {
- connectToHost = optarg;
- connectToPort = 49152;
- } else {
- connectToHost.setTo(optarg, colonPos - optarg);
-
- char *end;
- connectToPort = strtol(colonPos + 1, &end, 10);
-
- if (*end != '\0' || end == colonPos + 1
- || connectToPort < 1 || connectToPort > 65535) {
- fprintf(stderr, "Illegal port specified.\n");
- exit(1);
- }
- }
- break;
- }
-
- case 'l':
- {
- char *end;
- localPort = strtol(optarg, &end, 10);
-
- if (*end != '\0' || end == optarg
- || localPort < 1 || localPort > 65535) {
- fprintf(stderr, "Illegal port specified.\n");
- exit(1);
- }
- break;
- }
-
- case '?':
- case 'h':
- usage(argv[0]);
- exit(1);
- }
- }
-
- if (localPort < 0 && connectToPort < 0) {
- fprintf(stderr,
- "You need to select either client or server mode.\n");
- exit(1);
- }
-
- sp<ANetworkSession> netSession = new ANetworkSession;
- netSession->start();
-
- sp<ALooper> looper = new ALooper;
-
- sp<TimeSyncer> handler = new TimeSyncer(netSession, NULL /* notify */);
- looper->registerHandler(handler);
-
- if (localPort >= 0) {
- handler->startServer(localPort);
- } else {
- handler->startClient(connectToHost.c_str(), connectToPort);
- }
-
- looper->start(true /* runOnCallingThread */);
-
- return 0;
-}
-
diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp
deleted file mode 100644
index 52e4e26..0000000
--- a/media/libstagefright/wifi-display/wfd.cpp
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "wfd"
-#include <utils/Log.h>
-
-#include "sink/WifiDisplaySink.h"
-#include "source/WifiDisplaySource.h"
-
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <media/AudioSystem.h>
-#include <media/IMediaPlayerService.h>
-#include <media/IRemoteDisplay.h>
-#include <media/IRemoteDisplayClient.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <ui/DisplayInfo.h>
-
-namespace android {
-
-static void usage(const char *me) {
- fprintf(stderr,
- "usage:\n"
- " %s -c host[:port]\tconnect to wifi source\n"
- " -u uri \tconnect to an rtsp uri\n"
- " -l ip[:port] \tlisten on the specified port "
- " -f(ilename) \tstream media "
- "(create a sink)\n"
- " -s(pecial) \trun in 'special' mode\n",
- me);
-}
-
-struct RemoteDisplayClient : public BnRemoteDisplayClient {
- RemoteDisplayClient();
-
- virtual void onDisplayConnected(
- const sp<IGraphicBufferProducer> &bufferProducer,
- uint32_t width,
- uint32_t height,
- uint32_t flags,
- uint32_t session);
-
- virtual void onDisplayDisconnected();
- virtual void onDisplayError(int32_t error);
-
- void waitUntilDone();
-
-protected:
- virtual ~RemoteDisplayClient();
-
-private:
- Mutex mLock;
- Condition mCondition;
-
- bool mDone;
-
- sp<SurfaceComposerClient> mComposerClient;
- sp<IGraphicBufferProducer> mSurfaceTexture;
- sp<IBinder> mDisplayBinder;
-
- DISALLOW_EVIL_CONSTRUCTORS(RemoteDisplayClient);
-};
-
-RemoteDisplayClient::RemoteDisplayClient()
- : mDone(false) {
- mComposerClient = new SurfaceComposerClient;
- CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
-}
-
-RemoteDisplayClient::~RemoteDisplayClient() {
-}
-
-void RemoteDisplayClient::onDisplayConnected(
- const sp<IGraphicBufferProducer> &bufferProducer,
- uint32_t width,
- uint32_t height,
- uint32_t flags,
- uint32_t session) {
- ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x, session = %d",
- width, height, flags, session);
-
- if (bufferProducer != NULL) {
- mSurfaceTexture = bufferProducer;
- mDisplayBinder = mComposerClient->createDisplay(
- String8("foo"), false /* secure */);
-
- SurfaceComposerClient::openGlobalTransaction();
- mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture);
-
- Rect layerStackRect(1280, 720); // XXX fix this.
- Rect displayRect(1280, 720);
-
- mComposerClient->setDisplayProjection(
- mDisplayBinder, 0 /* 0 degree rotation */,
- layerStackRect,
- displayRect);
-
- SurfaceComposerClient::closeGlobalTransaction();
- }
-}
-
-void RemoteDisplayClient::onDisplayDisconnected() {
- ALOGI("onDisplayDisconnected");
-
- Mutex::Autolock autoLock(mLock);
- mDone = true;
- mCondition.broadcast();
-}
-
-void RemoteDisplayClient::onDisplayError(int32_t error) {
- ALOGI("onDisplayError error=%d", error);
-
- Mutex::Autolock autoLock(mLock);
- mDone = true;
- mCondition.broadcast();
-}
-
-void RemoteDisplayClient::waitUntilDone() {
- Mutex::Autolock autoLock(mLock);
- while (!mDone) {
- mCondition.wait(mLock);
- }
-}
-
-static void createSource(const AString &addr, int32_t port) {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.player"));
- sp<IMediaPlayerService> service =
- interface_cast<IMediaPlayerService>(binder);
-
- CHECK(service.get() != NULL);
-
- String8 iface;
- iface.append(addr.c_str());
- iface.append(StringPrintf(":%d", port).c_str());
-
- sp<RemoteDisplayClient> client = new RemoteDisplayClient;
- sp<IRemoteDisplay> display =
- service->listenForRemoteDisplay(client, iface);
-
- client->waitUntilDone();
-
- display->dispose();
- display.clear();
-}
-
-static void createFileSource(
- const AString &addr, int32_t port, const char *path) {
- sp<ANetworkSession> session = new ANetworkSession;
- session->start();
-
- sp<ALooper> looper = new ALooper;
- looper->start();
-
- sp<RemoteDisplayClient> client = new RemoteDisplayClient;
- sp<WifiDisplaySource> source = new WifiDisplaySource(session, client, path);
- looper->registerHandler(source);
-
- AString iface = StringPrintf("%s:%d", addr.c_str(), port);
- CHECK_EQ((status_t)OK, source->start(iface.c_str()));
-
- client->waitUntilDone();
-
- source->stop();
-}
-
-} // namespace android
-
-int main(int argc, char **argv) {
- using namespace android;
-
- ProcessState::self()->startThreadPool();
-
- DataSource::RegisterDefaultSniffers();
-
- AString connectToHost;
- int32_t connectToPort = -1;
- AString uri;
-
- AString listenOnAddr;
- int32_t listenOnPort = -1;
-
- AString path;
-
- bool specialMode = false;
-
- int res;
- while ((res = getopt(argc, argv, "hc:l:u:f:s")) >= 0) {
- switch (res) {
- case 'c':
- {
- const char *colonPos = strrchr(optarg, ':');
-
- if (colonPos == NULL) {
- connectToHost = optarg;
- connectToPort = WifiDisplaySource::kWifiDisplayDefaultPort;
- } else {
- connectToHost.setTo(optarg, colonPos - optarg);
-
- char *end;
- connectToPort = strtol(colonPos + 1, &end, 10);
-
- if (*end != '\0' || end == colonPos + 1
- || connectToPort < 1 || connectToPort > 65535) {
- fprintf(stderr, "Illegal port specified.\n");
- exit(1);
- }
- }
- break;
- }
-
- case 'u':
- {
- uri = optarg;
- break;
- }
-
- case 'f':
- {
- path = optarg;
- break;
- }
-
- case 'l':
- {
- const char *colonPos = strrchr(optarg, ':');
-
- if (colonPos == NULL) {
- listenOnAddr = optarg;
- listenOnPort = WifiDisplaySource::kWifiDisplayDefaultPort;
- } else {
- listenOnAddr.setTo(optarg, colonPos - optarg);
-
- char *end;
- listenOnPort = strtol(colonPos + 1, &end, 10);
-
- if (*end != '\0' || end == colonPos + 1
- || listenOnPort < 1 || listenOnPort > 65535) {
- fprintf(stderr, "Illegal port specified.\n");
- exit(1);
- }
- }
- break;
- }
-
- case 's':
- {
- specialMode = true;
- break;
- }
-
- case '?':
- case 'h':
- default:
- usage(argv[0]);
- exit(1);
- }
- }
-
- if (connectToPort >= 0 && listenOnPort >= 0) {
- fprintf(stderr,
- "You can connect to a source or create one, "
- "but not both at the same time.\n");
- exit(1);
- }
-
- if (listenOnPort >= 0) {
- if (path.empty()) {
- createSource(listenOnAddr, listenOnPort);
- } else {
- createFileSource(listenOnAddr, listenOnPort, path.c_str());
- }
-
- exit(0);
- }
-
- if (connectToPort < 0 && uri.empty()) {
- fprintf(stderr,
- "You need to select either source host or uri.\n");
-
- exit(1);
- }
-
- if (connectToPort >= 0 && !uri.empty()) {
- fprintf(stderr,
- "You need to either connect to a wfd host or an rtsp url, "
- "not both.\n");
- exit(1);
- }
-
- sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
- CHECK_EQ(composerClient->initCheck(), (status_t)OK);
-
- sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
- DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
- ssize_t displayWidth = info.w;
- ssize_t displayHeight = info.h;
-
- ALOGV("display is %d x %d\n", displayWidth, displayHeight);
-
- sp<SurfaceControl> control =
- composerClient->createSurface(
- String8("A Surface"),
- displayWidth,
- displayHeight,
- PIXEL_FORMAT_RGB_565,
- 0);
-
- CHECK(control != NULL);
- CHECK(control->isValid());
-
- SurfaceComposerClient::openGlobalTransaction();
- CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
- CHECK_EQ(control->show(), (status_t)OK);
- SurfaceComposerClient::closeGlobalTransaction();
-
- sp<Surface> surface = control->getSurface();
- CHECK(surface != NULL);
-
- sp<ANetworkSession> session = new ANetworkSession;
- session->start();
-
- sp<ALooper> looper = new ALooper;
-
- sp<WifiDisplaySink> sink = new WifiDisplaySink(
- specialMode ? WifiDisplaySink::FLAG_SPECIAL_MODE : 0 /* flags */,
- session,
- surface->getIGraphicBufferProducer());
-
- looper->registerHandler(sink);
-
- if (connectToPort >= 0) {
- sink->start(connectToHost.c_str(), connectToPort);
- } else {
- sink->start(uri.c_str());
- }
-
- looper->start(true /* runOnCallingThread */);
-
- composerClient->dispose();
-
- return 0;
-}
diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk
index b3f7b1b..bb86dfc 100644
--- a/media/libstagefright/yuv/Android.mk
+++ b/media/libstagefright/yuv/Android.mk
@@ -12,5 +12,7 @@
LOCAL_MODULE:= libstagefright_yuv
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/yuv/YUVImage.cpp b/media/libstagefright/yuv/YUVImage.cpp
index 7b9000b..bb3e2fd 100644
--- a/media/libstagefright/yuv/YUVImage.cpp
+++ b/media/libstagefright/yuv/YUVImage.cpp
@@ -226,8 +226,8 @@
&ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);
int32_t yDestOffsetIncrement;
- int32_t uDestOffsetIncrement;
- int32_t vDestOffsetIncrement;
+ int32_t uDestOffsetIncrement = 0;
+ int32_t vDestOffsetIncrement = 0;
destImage.getOffsetIncrementsPerDataRow(
&yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);
@@ -309,7 +309,7 @@
int32_t yDestOffsetIncrement;
int32_t uDestOffsetIncrement;
- int32_t vDestOffsetIncrement;
+ int32_t vDestOffsetIncrement = 0;
destImage.getOffsetIncrementsPerDataRow(
&yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);
@@ -393,9 +393,9 @@
fprintf(fp, "255\n");
for (int32_t y = 0; y < mHeight; ++y) {
for (int32_t x = 0; x < mWidth; ++x) {
- uint8_t yValue;
- uint8_t uValue;
- uint8_t vValue;
+ uint8_t yValue = 0u;
+ uint8_t uValue = 0u;
+ uint8_t vValue = 0u;
getPixelValue(x, y, &yValue, &uValue, & vValue);
uint8_t rValue;
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index 1ac647a..f848054 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -15,6 +15,7 @@
LOCAL_SHARED_LIBRARIES := \
libaudioflinger \
+ libcamera_metadata\
libcameraservice \
libmedialogservice \
libcutils \
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index d5207d5..a347951 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -37,7 +37,7 @@
using namespace android;
-int main(int argc, char** argv)
+int main(int argc __unused, char** argv)
{
signal(SIGPIPE, SIG_IGN);
char value[PROPERTY_VALUE_MAX];
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 930f0b0..c4f87a0 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -331,7 +331,7 @@
void MtpDataPacket::putString(const uint16_t* string) {
int count = 0;
- for (int i = 0; i < 256; i++) {
+ for (int i = 0; i <= MTP_STRING_MAX_CHARACTER_NUMBER; i++) {
if (string[i])
count++;
else
diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp
index 64dd45b..375ed9a 100644
--- a/media/mtp/MtpProperty.cpp
+++ b/media/mtp/MtpProperty.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "MtpProperty"
+#include <inttypes.h>
#include "MtpDataPacket.h"
#include "MtpDebug.h"
#include "MtpProperty.h"
@@ -385,10 +386,10 @@
buffer.appendFormat("%d", value.u.u32);
break;
case MTP_TYPE_INT64:
- buffer.appendFormat("%lld", value.u.i64);
+ buffer.appendFormat("%" PRId64, value.u.i64);
break;
case MTP_TYPE_UINT64:
- buffer.appendFormat("%lld", value.u.u64);
+ buffer.appendFormat("%" PRIu64, value.u.u64);
break;
case MTP_TYPE_INT128:
buffer.appendFormat("%08X%08X%08X%08X", value.u.i128[0], value.u.i128[1],
diff --git a/media/mtp/MtpStringBuffer.cpp b/media/mtp/MtpStringBuffer.cpp
index fe8cf04..f3420a4 100644
--- a/media/mtp/MtpStringBuffer.cpp
+++ b/media/mtp/MtpStringBuffer.cpp
@@ -56,42 +56,47 @@
}
void MtpStringBuffer::set(const char* src) {
- int length = strlen(src);
- if (length >= sizeof(mBuffer))
- length = sizeof(mBuffer) - 1;
- memcpy(mBuffer, src, length);
-
// count the characters
int count = 0;
char ch;
- while ((ch = *src++) != 0) {
+ char* dest = (char*)mBuffer;
+
+ while ((ch = *src++) != 0 && count < MTP_STRING_MAX_CHARACTER_NUMBER) {
if ((ch & 0x80) == 0) {
// single byte character
+ *dest++ = ch;
} else if ((ch & 0xE0) == 0xC0) {
// two byte character
- if (! *src++) {
+ char ch1 = *src++;
+ if (! ch1) {
// last character was truncated, so ignore last byte
- length--;
break;
}
+
+ *dest++ = ch;
+ *dest++ = ch1;
} else if ((ch & 0xF0) == 0xE0) {
// 3 byte char
- if (! *src++) {
+ char ch1 = *src++;
+ if (! ch1) {
// last character was truncated, so ignore last byte
- length--;
break;
}
- if (! *src++) {
- // last character was truncated, so ignore last two bytes
- length -= 2;
+ char ch2 = *src++;
+ if (! ch2) {
+ // last character was truncated, so ignore last byte
break;
}
+
+ *dest++ = ch;
+ *dest++ = ch1;
+ *dest++ = ch2;
}
count++;
}
- mByteCount = length + 1;
- mBuffer[length] = 0;
+ *dest++ = 0;
+ mByteCount = dest - (char*)mBuffer;
mCharCount = count;
}
@@ -100,7 +105,7 @@
uint16_t ch;
uint8_t* dest = mBuffer;
- while ((ch = *src++) != 0 && count < 255) {
+ while ((ch = *src++) != 0 && count < MTP_STRING_MAX_CHARACTER_NUMBER) {
if (ch >= 0x0800) {
*dest++ = (uint8_t)(0xE0 | (ch >> 12));
*dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F));
diff --git a/media/mtp/MtpStringBuffer.h b/media/mtp/MtpStringBuffer.h
index cbc8307..e5150df 100644
--- a/media/mtp/MtpStringBuffer.h
+++ b/media/mtp/MtpStringBuffer.h
@@ -19,6 +19,9 @@
#include <stdint.h>
+// Max Character number of a MTP String
+#define MTP_STRING_MAX_CHARACTER_NUMBER 255
+
namespace android {
class MtpDataPacket;
@@ -29,7 +32,7 @@
private:
// mBuffer contains string in UTF8 format
// maximum 3 bytes/character, with 1 extra for zero termination
- uint8_t mBuffer[255 * 3 + 1];
+ uint8_t mBuffer[MTP_STRING_MAX_CHARACTER_NUMBER * 3 + 1];
int mCharCount;
int mByteCount;
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 54377f1..4524d3c 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -23,7 +23,8 @@
AudioPolicyService.cpp \
ServiceUtilities.cpp \
AudioResamplerCubic.cpp.arm \
- AudioResamplerSinc.cpp.arm
+ AudioResamplerSinc.cpp.arm \
+ AudioResamplerDyn.cpp.arm
LOCAL_SRC_FILES += StateQueue.cpp
@@ -74,12 +75,20 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- test-resample.cpp \
+ test-resample.cpp \
AudioResampler.cpp.arm \
- AudioResamplerCubic.cpp.arm \
- AudioResamplerSinc.cpp.arm
+ AudioResamplerCubic.cpp.arm \
+ AudioResamplerSinc.cpp.arm \
+ AudioResamplerDyn.cpp.arm
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils)
+
+LOCAL_STATIC_LIBRARIES := \
+ libsndfile
LOCAL_SHARED_LIBRARIES := \
+ libaudioutils \
libdl \
libcutils \
libutils \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 04f0564..92ee30e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -104,6 +104,27 @@
// ----------------------------------------------------------------------------
+const char *formatToString(audio_format_t format) {
+ switch(format) {
+ case AUDIO_FORMAT_PCM_SUB_8_BIT: return "pcm8";
+ case AUDIO_FORMAT_PCM_SUB_16_BIT: return "pcm16";
+ case AUDIO_FORMAT_PCM_SUB_32_BIT: return "pcm32";
+ case AUDIO_FORMAT_PCM_SUB_8_24_BIT: return "pcm8.24";
+ case AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED: return "pcm24";
+ case AUDIO_FORMAT_PCM_SUB_FLOAT: return "pcmfloat";
+ case AUDIO_FORMAT_MP3: return "mp3";
+ case AUDIO_FORMAT_AMR_NB: return "amr-nb";
+ case AUDIO_FORMAT_AMR_WB: return "amr-wb";
+ case AUDIO_FORMAT_AAC: return "aac";
+ case AUDIO_FORMAT_HE_AAC_V1: return "he-aac-v1";
+ case AUDIO_FORMAT_HE_AAC_V2: return "he-aac-v2";
+ case AUDIO_FORMAT_VORBIS: return "vorbis";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
{
const hw_module_t *mod;
@@ -162,6 +183,7 @@
(void) property_get("af.tee", value, "0");
teeEnabled = atoi(value);
}
+ // FIXME symbolic constants here
if (teeEnabled & 1) {
mTeeSinkInputEnabled = true;
}
@@ -213,6 +235,18 @@
audio_hw_device_close(mAudioHwDevs.valueAt(i)->hwDevice());
delete mAudioHwDevs.valueAt(i);
}
+
+ // Tell media.log service about any old writers that still need to be unregistered
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
+ if (binder != 0) {
+ sp<IMediaLogService> mediaLogService(interface_cast<IMediaLogService>(binder));
+ for (size_t count = mUnregisteredWriters.size(); count > 0; count--) {
+ sp<IMemory> iMemory(mUnregisteredWriters.top()->getIMemory());
+ mUnregisteredWriters.pop();
+ mediaLogService->unregisterWriter(iMemory);
+ }
+ }
+
}
static const char * const audio_interfaces[] = {
@@ -252,7 +286,7 @@
return NULL;
}
-void AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
+void AudioFlinger::dumpClients(int fd, const Vector<String16>& args __unused)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -274,17 +308,17 @@
}
result.append("Global session refs:\n");
- result.append(" session pid count\n");
+ result.append(" session pid count\n");
for (size_t i = 0; i < mAudioSessionRefs.size(); i++) {
AudioSessionRef *r = mAudioSessionRefs[i];
- snprintf(buffer, SIZE, " %7d %3d %3d\n", r->mSessionid, r->mPid, r->mCnt);
+ snprintf(buffer, SIZE, " %7d %5d %5d\n", r->mSessionid, r->mPid, r->mCnt);
result.append(buffer);
}
write(fd, result.string(), result.size());
}
-void AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
+void AudioFlinger::dumpInternals(int fd, const Vector<String16>& args __unused)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -299,7 +333,7 @@
write(fd, result.string(), result.size());
}
-void AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args)
+void AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args __unused)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -406,16 +440,44 @@
sp<NBLog::Writer> AudioFlinger::newWriter_l(size_t size, const char *name)
{
+ // If there is no memory allocated for logs, return a dummy writer that does nothing
if (mLogMemoryDealer == 0) {
return new NBLog::Writer();
}
- sp<IMemory> shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size));
- sp<NBLog::Writer> writer = new NBLog::Writer(size, shared);
sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
- if (binder != 0) {
- interface_cast<IMediaLogService>(binder)->registerWriter(shared, size, name);
+ // Similarly if we can't contact the media.log service, also return a dummy writer
+ if (binder == 0) {
+ return new NBLog::Writer();
}
- return writer;
+ sp<IMediaLogService> mediaLogService(interface_cast<IMediaLogService>(binder));
+ sp<IMemory> shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size));
+ // If allocation fails, consult the vector of previously unregistered writers
+ // and garbage-collect one or more them until an allocation succeeds
+ if (shared == 0) {
+ Mutex::Autolock _l(mUnregisteredWritersLock);
+ for (size_t count = mUnregisteredWriters.size(); count > 0; count--) {
+ {
+ // Pick the oldest stale writer to garbage-collect
+ sp<IMemory> iMemory(mUnregisteredWriters[0]->getIMemory());
+ mUnregisteredWriters.removeAt(0);
+ mediaLogService->unregisterWriter(iMemory);
+ // Now the media.log remote reference to IMemory is gone. When our last local
+ // reference to IMemory also drops to zero at end of this block,
+ // the IMemory destructor will deallocate the region from mLogMemoryDealer.
+ }
+ // Re-attempt the allocation
+ shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size));
+ if (shared != 0) {
+ goto success;
+ }
+ }
+ // Even after garbage-collecting all old writers, there is still not enough memory,
+ // so return a dummy writer
+ return new NBLog::Writer();
+ }
+success:
+ mediaLogService->registerWriter(shared, size, name);
+ return new NBLog::Writer(size, shared);
}
void AudioFlinger::unregisterWriter(const sp<NBLog::Writer>& writer)
@@ -427,13 +489,10 @@
if (iMemory == 0) {
return;
}
- sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
- if (binder != 0) {
- interface_cast<IMediaLogService>(binder)->unregisterWriter(iMemory);
- // Now the media.log remote reference to IMemory is gone.
- // When our last local reference to IMemory also drops to zero,
- // the IMemory destructor will deallocate the region from mMemoryDealer.
- }
+ // Rather than removing the writer immediately, append it to a queue of old writers to
+ // be garbage-collected later. This allows us to continue to view old logs for a while.
+ Mutex::Autolock _l(mUnregisteredWritersLock);
+ mUnregisteredWriters.push(writer);
}
// IAudioFlinger interface
@@ -444,13 +503,14 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *frameCount,
IAudioFlinger::track_flags_t *flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
pid_t tid,
int *sessionId,
String8& name,
+ int clientUid,
status_t *status)
{
sp<PlaybackThread::Track> track;
@@ -467,10 +527,31 @@
goto Exit;
}
+ // further sample rate checks are performed by createTrack_l() depending on the thread type
+ if (sampleRate == 0) {
+ ALOGE("createTrack() invalid sample rate %u", sampleRate);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ // further channel mask checks are performed by createTrack_l() depending on the thread type
+ if (!audio_is_output_channel(channelMask)) {
+ ALOGE("createTrack() invalid channel mask %#x", channelMask);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
// client is responsible for conversion of 8-bit PCM to 16-bit PCM,
// and we don't yet support 8.24 or 32-bit PCM
- if (audio_is_linear_pcm(format) && format != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("createTrack() invalid format %d", format);
+ if (!audio_is_valid_format(format) ||
+ (audio_is_linear_pcm(format) && format != AUDIO_FORMAT_PCM_16_BIT)) {
+ ALOGE("createTrack() invalid format %#x", format);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ if (sharedBuffer != 0 && sharedBuffer->pointer() == NULL) {
+ ALOGE("createTrack() sharedBuffer is non-0 but has NULL pointer()");
lStatus = BAD_VALUE;
goto Exit;
}
@@ -486,10 +567,11 @@
}
pid_t pid = IPCThreadState::self()->getCallingPid();
+
client = registerPid_l(pid);
ALOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
- if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) {
+ if (sessionId != NULL && *sessionId != AUDIO_SESSION_ALLOCATE) {
// check if an effect chain with the same session ID is present on another
// output thread and move it here.
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
@@ -513,7 +595,8 @@
ALOGV("createTrack() lSessionId: %d", lSessionId);
track = thread->createTrack_l(client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, &lStatus);
+ channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus);
+ LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0));
// we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless
// move effect chain to this output thread if an effect on same session was waiting
@@ -1013,7 +1096,7 @@
return size;
}
-unsigned int AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const
+uint32_t AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const
{
Mutex::Autolock _l(mLock);
@@ -1045,7 +1128,7 @@
return ret;
}
-status_t AudioFlinger::getRenderPosition(size_t *halFrames, size_t *dspFrames,
+status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
audio_io_handle_t output) const
{
status_t status;
@@ -1213,7 +1296,7 @@
{
}
-void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
+void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who __unused)
{
sp<NotificationClient> keep(this);
mAudioFlinger->removeNotificationClient(mPid);
@@ -1231,7 +1314,7 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *frameCount,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
int *sessionId,
@@ -1247,12 +1330,29 @@
// check calling permissions
if (!recordingAllowed()) {
+ ALOGE("openRecord() permission denied: recording not allowed");
lStatus = PERMISSION_DENIED;
goto Exit;
}
+ // further sample rate checks are performed by createRecordTrack_l()
+ if (sampleRate == 0) {
+ ALOGE("openRecord() invalid sample rate %u", sampleRate);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ // FIXME when we support more formats, add audio_is_valid_format(format)
+ // and any explicit restrictions if audio_is_linear_pcm(format)
if (format != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("openRecord() invalid format %d", format);
+ ALOGE("openRecord() invalid format %#x", format);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ // further channel mask checks are performed by createRecordTrack_l()
+ if (!audio_is_input_channel(channelMask)) {
+ ALOGE("openRecord() invalid channel mask %#x", channelMask);
lStatus = BAD_VALUE;
goto Exit;
}
@@ -1262,12 +1362,14 @@
Mutex::Autolock _l(mLock);
thread = checkRecordThread_l(input);
if (thread == NULL) {
+ ALOGE("openRecord() checkRecordThread_l failed");
lStatus = BAD_VALUE;
goto Exit;
}
if (deviceRequiresCaptureAudioOutputPermission(thread->inDevice())
&& !captureAudioOutputAllowed()) {
+ ALOGE("openRecord() permission denied: capture not allowed");
lStatus = PERMISSION_DENIED;
goto Exit;
}
@@ -1276,7 +1378,7 @@
client = registerPid_l(pid);
// If no audio session id is provided, create one here
- if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) {
+ if (sessionId != NULL && *sessionId != AUDIO_SESSION_ALLOCATE) {
lSessionId = *sessionId;
} else {
lSessionId = nextUniqueId();
@@ -1286,8 +1388,12 @@
}
// create new record track.
// The record track uses one track in mHardwareMixerThread by convention.
+ // TODO: the uid should be passed in as a parameter to openRecord
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
- frameCount, lSessionId, flags, tid, &lStatus);
+ frameCount, lSessionId,
+ IPCThreadState::self()->getCallingUid(),
+ flags, tid, &lStatus);
+ LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
}
if (lStatus != NO_ERROR) {
@@ -1693,7 +1799,7 @@
audio_stream_in_t *inStream = NULL;
status_t status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config,
&inStream);
- ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, "
+ ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %#x, Channels %x, "
"status %d",
inStream,
config.sample_rate,
@@ -1708,9 +1814,11 @@
reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&
(config.sample_rate <= 2 * reqSamplingRate) &&
(popcount(config.channel_mask) <= FCC_2) && (popcount(reqChannelMask) <= FCC_2)) {
+ // FIXME describe the change proposed by HAL (save old values so we can log them here)
ALOGV("openInput() reopening with proposed sampling rate and channel mask");
inStream = NULL;
status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream);
+ // FIXME log this new status; HAL should not propose any further changes
}
if (status == NO_ERROR && inStream != NULL) {
@@ -1728,13 +1836,13 @@
popcount(inStream->common.get_channels(&inStream->common)));
if (!mTeeSinkInputEnabled) {
kind = TEE_SINK_NO;
- } else if (format == Format_Invalid) {
+ } else if (!Format_isValid(format)) {
kind = TEE_SINK_NO;
} else if (mRecordTeeSink == 0) {
kind = TEE_SINK_NEW;
} else if (mRecordTeeSink->getStrongCount() != 1) {
kind = TEE_SINK_NO;
- } else if (format == mRecordTeeSink->format()) {
+ } else if (Format_isEqual(format, mRecordTeeSink->format())) {
kind = TEE_SINK_OLD;
} else {
kind = TEE_SINK_NEW;
@@ -1771,8 +1879,6 @@
// pre processing modules
RecordThread *thread = new RecordThread(this,
input,
- reqSamplingRate,
- reqChannelMask,
id,
primaryOutputDevice_l(),
*pDevices
@@ -1834,10 +1940,10 @@
return NO_ERROR;
}
-status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output)
+status_t AudioFlinger::invalidateStream(audio_stream_type_t stream)
{
Mutex::Autolock _l(mLock);
- ALOGV("setStreamOutput() stream %d to output %d", stream, output);
+ ALOGV("invalidateStream() stream %d", stream);
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
@@ -1853,18 +1959,21 @@
return nextUniqueId();
}
-void AudioFlinger::acquireAudioSessionId(int audioSession)
+void AudioFlinger::acquireAudioSessionId(int audioSession, pid_t pid)
{
Mutex::Autolock _l(mLock);
pid_t caller = IPCThreadState::self()->getCallingPid();
- ALOGV("acquiring %d from %d", audioSession, caller);
+ ALOGV("acquiring %d from %d, for %d", audioSession, caller, pid);
+ if (pid != -1 && (caller == getpid_cached)) {
+ caller = pid;
+ }
// Ignore requests received from processes not known as notification client. The request
// is likely proxied by mediaserver (e.g CameraService) and releaseAudioSessionId() can be
// called from a different pid leaving a stale session reference. Also we don't know how
// to clear this reference if the client process dies.
if (mNotificationClients.indexOfKey(caller) < 0) {
- ALOGV("acquireAudioSessionId() unknown client %d for session %d", caller, audioSession);
+ ALOGW("acquireAudioSessionId() unknown client %d for session %d", caller, audioSession);
return;
}
@@ -1881,11 +1990,14 @@
ALOGV(" added new entry for %d", audioSession);
}
-void AudioFlinger::releaseAudioSessionId(int audioSession)
+void AudioFlinger::releaseAudioSessionId(int audioSession, pid_t pid)
{
Mutex::Autolock _l(mLock);
pid_t caller = IPCThreadState::self()->getCallingPid();
- ALOGV("releasing %d from %d", audioSession, caller);
+ ALOGV("releasing %d from %d for %d", audioSession, caller, pid);
+ if (pid != -1 && (caller == getpid_cached)) {
+ caller = pid;
+ }
size_t num = mAudioSessionRefs.size();
for (size_t i = 0; i< num; i++) {
AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
@@ -2014,7 +2126,7 @@
int triggerSession,
int listenerSession,
sync_event_callback_t callBack,
- void *cookie)
+ wp<RefBase> cookie)
{
Mutex::Autolock _l(mLock);
@@ -2331,6 +2443,7 @@
strategy,
sessionId,
effect->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
}
effect = chain->getEffectFromId_l(0);
}
@@ -2345,6 +2458,7 @@
strategy,
sessionId,
removed[i]->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
}
}
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 7f2f9ec..c2b516b 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -60,8 +60,8 @@
namespace android {
-class audio_track_cblk_t;
-class effect_param_cblk_t;
+struct audio_track_cblk_t;
+struct effect_param_cblk_t;
class AudioMixer;
class AudioBuffer;
class AudioResampler;
@@ -102,13 +102,14 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
IAudioFlinger::track_flags_t *flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
pid_t tid,
int *sessionId,
String8& name,
+ int clientUid,
status_t *status /*non-NULL*/);
virtual sp<IAudioRecord> openRecord(
@@ -116,7 +117,7 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
int *sessionId,
@@ -181,20 +182,20 @@
virtual status_t closeInput(audio_io_handle_t input);
- virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output);
+ virtual status_t invalidateStream(audio_stream_type_t stream);
virtual status_t setVoiceVolume(float volume);
- virtual status_t getRenderPosition(size_t *halFrames, size_t *dspFrames,
+ virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
audio_io_handle_t output) const;
- virtual unsigned int getInputFramesLost(audio_io_handle_t ioHandle) const;
+ virtual uint32_t getInputFramesLost(audio_io_handle_t ioHandle) const;
virtual int newAudioSessionId();
- virtual void acquireAudioSessionId(int audioSession);
+ virtual void acquireAudioSessionId(int audioSession, pid_t pid);
- virtual void releaseAudioSessionId(int audioSession);
+ virtual void releaseAudioSessionId(int audioSession, pid_t pid);
virtual status_t queryNumberEffects(uint32_t *numEffects) const;
@@ -234,8 +235,12 @@
sp<NBLog::Writer> newWriter_l(size_t size, const char *name);
void unregisterWriter(const sp<NBLog::Writer>& writer);
private:
- static const size_t kLogMemorySize = 10 * 1024;
+ static const size_t kLogMemorySize = 40 * 1024;
sp<MemoryDealer> mLogMemoryDealer; // == 0 when NBLog is disabled
+ // When a log writer is unregistered, it is done lazily so that media.log can continue to see it
+ // for as long as possible. The memory is only freed when it is needed for another log writer.
+ Vector< sp<NBLog::Writer> > mUnregisteredWriters;
+ Mutex mUnregisteredWritersLock;
public:
class SyncEvent;
@@ -248,7 +253,7 @@
int triggerSession,
int listenerSession,
sync_event_callback_t callBack,
- void *cookie)
+ wp<RefBase> cookie)
: mType(type), mTriggerSession(triggerSession), mListenerSession(listenerSession),
mCallback(callBack), mCookie(cookie)
{}
@@ -261,14 +266,14 @@
AudioSystem::sync_event_t type() const { return mType; }
int triggerSession() const { return mTriggerSession; }
int listenerSession() const { return mListenerSession; }
- void *cookie() const { return mCookie; }
+ wp<RefBase> cookie() const { return mCookie; }
private:
const AudioSystem::sync_event_t mType;
const int mTriggerSession;
const int mListenerSession;
sync_event_callback_t mCallback;
- void * const mCookie;
+ const wp<RefBase> mCookie;
mutable Mutex mLock;
};
@@ -276,7 +281,7 @@
int triggerSession,
int listenerSession,
sync_event_callback_t callBack,
- void *cookie);
+ wp<RefBase> cookie);
private:
class AudioHwDevice; // fwd declaration for findSuitableHwDev_l
@@ -412,6 +417,7 @@
int target);
virtual status_t setParameters(const String8& keyValuePairs);
virtual status_t getTimestamp(AudioTimestamp& timestamp);
+ virtual void signal(); // signal playback thread for a change in control block
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
@@ -497,7 +503,7 @@
private:
const char * const mModuleName;
audio_hw_device_t * const mHwDevice;
- Flags mFlags;
+ const Flags mFlags;
};
// AudioStreamOut and AudioStreamIn are immutable, so their fields are const.
@@ -507,7 +513,7 @@
struct AudioStreamOut {
AudioHwDevice* const audioHwDev;
audio_stream_out_t* const stream;
- audio_output_flags_t flags;
+ const audio_output_flags_t flags;
audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); }
@@ -632,7 +638,7 @@
// 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
static const size_t kTeeSinkInputFramesDefault = 0x200000;
static const size_t kTeeSinkOutputFramesDefault = 0x200000;
- static const size_t kTeeSinkTrackFramesDefault = 0x1000;
+ static const size_t kTeeSinkTrackFramesDefault = 0x200000;
#endif
// This method reads from a variable without mLock, but the variable is updated under mLock. So
@@ -649,6 +655,8 @@
#undef INCLUDING_FROM_AUDIOFLINGER_H
+const char *formatToString(audio_format_t format);
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 91aedbb..3ac5da9 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -58,7 +58,7 @@
status_t AudioMixer::DownmixerBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
int64_t pts) {
//ALOGV("DownmixerBufferProvider::getNextBuffer()");
- if (this->mTrackBufferProvider != NULL) {
+ if (mTrackBufferProvider != NULL) {
status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
if (res == OK) {
mDownmixConfig.inputCfg.buffer.frameCount = pBuffer->frameCount;
@@ -81,7 +81,7 @@
void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
//ALOGV("DownmixerBufferProvider::releaseBuffer()");
- if (this->mTrackBufferProvider != NULL) {
+ if (mTrackBufferProvider != NULL) {
mTrackBufferProvider->releaseBuffer(pBuffer);
} else {
ALOGE("DownmixerBufferProvider::releaseBuffer() error: NULL track buffer provider");
@@ -90,9 +90,9 @@
// ----------------------------------------------------------------------------
-bool AudioMixer::isMultichannelCapable = false;
+bool AudioMixer::sIsMultichannelCapable = false;
-effect_descriptor_t AudioMixer::dwnmFxDesc;
+effect_descriptor_t AudioMixer::sDwnmFxDesc;
// Ensure mConfiguredNames bitmask is initialized properly on all architectures.
// The value of 1 << x is undefined in C when x >= 32.
@@ -113,8 +113,6 @@
// AudioMixer is not yet capable of multi-channel output beyond stereo
ALOG_ASSERT(2 == MAX_NUM_CHANNELS, "bad MAX_NUM_CHANNELS %d", MAX_NUM_CHANNELS);
- LocalClock lc;
-
pthread_once(&sOnceControl, &sInitRoutine);
mState.enabledTracks= 0;
@@ -136,27 +134,6 @@
t++;
}
- // find multichannel downmix effect if we have to play multichannel content
- uint32_t numEffects = 0;
- int ret = EffectQueryNumberEffects(&numEffects);
- if (ret != 0) {
- ALOGE("AudioMixer() error %d querying number of effects", ret);
- return;
- }
- ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
-
- for (uint32_t i = 0 ; i < numEffects ; i++) {
- if (EffectQueryEffect(i, &dwnmFxDesc) == 0) {
- ALOGV("effect %d is called %s", i, dwnmFxDesc.name);
- if (memcmp(&dwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
- ALOGI("found effect \"%s\" from %s",
- dwnmFxDesc.name, dwnmFxDesc.implementor);
- isMultichannelCapable = true;
- break;
- }
- }
- }
- ALOGE_IF(!isMultichannelCapable, "unable to find downmix effect");
}
AudioMixer::~AudioMixer()
@@ -216,6 +193,7 @@
t->mainBuffer = NULL;
t->auxBuffer = NULL;
t->downmixerBufferProvider = NULL;
+ t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask);
if (status == OK) {
@@ -252,7 +230,7 @@
return status;
}
-void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName) {
+void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName __unused) {
ALOGV("AudioMixer::unprepareTrackForDownmix(%d)", trackName);
if (pTrack->downmixerBufferProvider != NULL) {
@@ -276,13 +254,13 @@
DownmixerBufferProvider* pDbp = new DownmixerBufferProvider();
int32_t status;
- if (!isMultichannelCapable) {
+ if (!sIsMultichannelCapable) {
ALOGE("prepareTrackForDownmix(%d) fails: mixer doesn't support multichannel content",
trackName);
goto noDownmixForActiveTrack;
}
- if (EffectCreate(&dwnmFxDesc.uuid,
+ if (EffectCreate(&sDwnmFxDesc.uuid,
pTrack->sessionId /*sessionId*/, -2 /*ioId not relevant here, using random value*/,
&pDbp->mDownmixHandle/*pHandle*/) != 0) {
ALOGE("prepareTrackForDownmix(%d) fails: error creating downmixer effect", trackName);
@@ -421,15 +399,16 @@
ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
track_t& track = mState.tracks[name];
- int valueInt = (int)value;
- int32_t *valueBuf = (int32_t *)value;
+ int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
+ int32_t *valueBuf = reinterpret_cast<int32_t*>(value);
switch (target) {
case TRACK:
switch (param) {
case CHANNEL_MASK: {
- audio_channel_mask_t mask = (audio_channel_mask_t) value;
+ audio_channel_mask_t mask =
+ static_cast<audio_channel_mask_t>(reinterpret_cast<uintptr_t>(value));
if (track.channelMask != mask) {
uint32_t channelCount = popcount(mask);
ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount);
@@ -462,6 +441,13 @@
// for a specific track? or per mixer?
/* case DOWNMIX_TYPE:
break */
+ case MIXER_FORMAT: {
+ audio_format_t format = static_cast<audio_format_t>(valueInt);
+ if (track.mMixerFormat != format) {
+ track.mMixerFormat = format;
+ ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);
+ }
+ } break;
default:
LOG_FATAL("bad param");
}
@@ -559,14 +545,14 @@
// Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
if (!((value == 44100 && devSampleRate == 48000) ||
(value == 48000 && devSampleRate == 44100))) {
- quality = AudioResampler::LOW_QUALITY;
+ quality = AudioResampler::DYN_LOW_QUALITY;
} else {
quality = AudioResampler::DEFAULT_QUALITY;
}
resampler = AudioResampler::create(
format,
// the resampler sees the number of channels after the downmixer, if any
- downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount,
+ (int) (downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount),
devSampleRate, quality);
resampler->setLocalTimeFreq(sLocalTimeFreq);
}
@@ -667,27 +653,29 @@
countActiveTracks++;
track_t& t = state->tracks[i];
uint32_t n = 0;
+ // FIXME can overflow (mask is only 3 bits)
n |= NEEDS_CHANNEL_1 + t.channelCount - 1;
- n |= NEEDS_FORMAT_16;
- n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED;
+ if (t.doesResample()) {
+ n |= NEEDS_RESAMPLE;
+ }
if (t.auxLevel != 0 && t.auxBuffer != NULL) {
- n |= NEEDS_AUX_ENABLED;
+ n |= NEEDS_AUX;
}
if (t.volumeInc[0]|t.volumeInc[1]) {
volumeRamp = true;
} else if (!t.doesResample() && t.volumeRL == 0) {
- n |= NEEDS_MUTE_ENABLED;
+ n |= NEEDS_MUTE;
}
t.needs = n;
- if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) {
+ if (n & NEEDS_MUTE) {
t.hook = track__nop;
} else {
- if ((n & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+ if (n & NEEDS_AUX) {
all16BitsStereoNoResample = false;
}
- if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
+ if (n & NEEDS_RESAMPLE) {
all16BitsStereoNoResample = false;
resampling = true;
t.hook = track__genericResample;
@@ -753,7 +741,7 @@
en &= ~(1<<i);
track_t& t = state->tracks[i];
if (!t.doesResample() && t.volumeRL == 0) {
- t.needs |= NEEDS_MUTE_ENABLED;
+ t.needs |= NEEDS_MUTE;
t.hook = track__nop;
} else {
allMuted = false;
@@ -804,8 +792,8 @@
}
}
-void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp,
- int32_t* aux)
+void AudioMixer::track__nop(track_t* t __unused, int32_t* out __unused,
+ size_t outFrameCount __unused, int32_t* temp __unused, int32_t* aux __unused)
{
}
@@ -881,8 +869,8 @@
}
}
-void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
- int32_t* aux)
+void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount,
+ int32_t* temp __unused, int32_t* aux)
{
const int16_t *in = static_cast<const int16_t *>(t->in);
@@ -972,8 +960,8 @@
t->in = in;
}
-void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
- int32_t* aux)
+void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount,
+ int32_t* temp __unused, int32_t* aux)
{
const int16_t *in = static_cast<int16_t const *>(t->in);
@@ -1063,7 +1051,7 @@
void AudioMixer::process__nop(state_t* state, int64_t pts)
{
uint32_t e0 = state->enabledTracks;
- size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS;
+ size_t sampleCount = state->frameCount * MAX_NUM_CHANNELS;
while (e0) {
// process by group of tracks with same output buffer to
// avoid multiple memset() on same buffer
@@ -1082,7 +1070,8 @@
}
e0 &= ~(e1);
- memset(t1.mainBuffer, 0, bufSize);
+ memset(t1.mainBuffer, 0, sampleCount
+ * audio_bytes_per_sample(t1.mMixerFormat));
}
while (e1) {
@@ -1121,11 +1110,6 @@
t.bufferProvider->getNextBuffer(&t.buffer, pts);
t.frameCount = t.buffer.frameCount;
t.in = t.buffer.raw;
- // t.in == NULL can happen if the track was flushed just after having
- // been enabled for mixing.
- if (t.in == NULL) {
- enabledTracks &= ~(1<<i);
- }
}
e0 = enabledTracks;
@@ -1157,10 +1141,17 @@
track_t& t = state->tracks[i];
size_t outFrames = BLOCKSIZE;
int32_t *aux = NULL;
- if (CC_UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED)) {
+ if (CC_UNLIKELY(t.needs & NEEDS_AUX)) {
aux = t.auxBuffer + numFrames;
}
while (outFrames) {
+ // t.in == NULL can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (t.in == NULL) {
+ enabledTracks &= ~(1<<i);
+ e1 &= ~(1<<i);
+ break;
+ }
size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
if (inFrames > 0) {
t.hook(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames,
@@ -1188,8 +1179,18 @@
}
}
}
- ditherAndClamp(out, outTemp, BLOCKSIZE);
- out += BLOCKSIZE;
+ switch (t1.mMixerFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ memcpy_to_float_from_q19_12(reinterpret_cast<float *>(out), outTemp, BLOCKSIZE * 2);
+ out += BLOCKSIZE * 2; // output is 2 floats/frame.
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ ditherAndClamp(out, outTemp, BLOCKSIZE);
+ out += BLOCKSIZE; // output is 1 int32_t (2 int16_t samples)/frame
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixer format: %d", t1.mMixerFormat);
+ }
numFrames += BLOCKSIZE;
} while (numFrames < state->frameCount);
}
@@ -1238,14 +1239,14 @@
e1 &= ~(1<<i);
track_t& t = state->tracks[i];
int32_t *aux = NULL;
- if (CC_UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED)) {
+ if (CC_UNLIKELY(t.needs & NEEDS_AUX)) {
aux = t.auxBuffer;
}
// this is a little goofy, on the resampling case we don't
// acquire/release the buffers because it's done by
// the resampler.
- if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
+ if (t.needs & NEEDS_RESAMPLE) {
t.resampler->setPTS(pts);
t.hook(&t, outTemp, numFrames, state->resampleTemp, aux);
} else {
@@ -1271,7 +1272,16 @@
}
}
}
- ditherAndClamp(out, outTemp, numFrames);
+ switch (t1.mMixerFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ memcpy_to_float_from_q19_12(reinterpret_cast<float*>(out), outTemp, numFrames*2);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ ditherAndClamp(out, outTemp, numFrames);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixer format: %d", t1.mMixerFormat);
+ }
}
}
@@ -1312,27 +1322,46 @@
}
size_t outFrames = b.frameCount;
- if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
- // volume is boosted, so we might need to clamp even though
- // we process only one track.
+ switch (t.mMixerFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT: {
+ float *fout = reinterpret_cast<float*>(out);
do {
uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
in += 2;
- int32_t l = mulRL(1, rl, vrl) >> 12;
- int32_t r = mulRL(0, rl, vrl) >> 12;
- // clamping...
- l = clamp16(l);
- r = clamp16(r);
- *out++ = (r<<16) | (l & 0xFFFF);
+ int32_t l = mulRL(1, rl, vrl);
+ int32_t r = mulRL(0, rl, vrl);
+ *fout++ = float_from_q19_12(l);
+ *fout++ = float_from_q19_12(r);
+ // Note: In case of later int16_t sink output,
+ // conversion and clamping is done by memcpy_to_i16_from_float().
} while (--outFrames);
- } else {
- do {
- uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
- in += 2;
- int32_t l = mulRL(1, rl, vrl) >> 12;
- int32_t r = mulRL(0, rl, vrl) >> 12;
- *out++ = (r<<16) | (l & 0xFFFF);
- } while (--outFrames);
+ } break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
+ // volume is boosted, so we might need to clamp even though
+ // we process only one track.
+ do {
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
+ in += 2;
+ int32_t l = mulRL(1, rl, vrl) >> 12;
+ int32_t r = mulRL(0, rl, vrl) >> 12;
+ // clamping...
+ l = clamp16(l);
+ r = clamp16(r);
+ *out++ = (r<<16) | (l & 0xFFFF);
+ } while (--outFrames);
+ } else {
+ do {
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
+ in += 2;
+ int32_t l = mulRL(1, rl, vrl) >> 12;
+ int32_t r = mulRL(0, rl, vrl) >> 12;
+ *out++ = (r<<16) | (l & 0xFFFF);
+ } while (--outFrames);
+ }
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixer format: %d", t.mMixerFormat);
}
numFrames -= b.frameCount;
t.bufferProvider->releaseBuffer(&b);
@@ -1459,6 +1488,28 @@
{
LocalClock lc;
sLocalTimeFreq = lc.getLocalFreq();
+
+ // find multichannel downmix effect if we have to play multichannel content
+ uint32_t numEffects = 0;
+ int ret = EffectQueryNumberEffects(&numEffects);
+ if (ret != 0) {
+ ALOGE("AudioMixer() error %d querying number of effects", ret);
+ return;
+ }
+ ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
+
+ for (uint32_t i = 0 ; i < numEffects ; i++) {
+ if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) {
+ ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
+ if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
+ ALOGI("found effect \"%s\" from %s",
+ sDwnmFxDesc.name, sDwnmFxDesc.implementor);
+ sIsMultichannelCapable = true;
+ break;
+ }
+ }
+ }
+ ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
}
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 43aeb86..e5e120c 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -77,6 +77,7 @@
MAIN_BUFFER = 0x4002,
AUX_BUFFER = 0x4003,
DOWNMIX_TYPE = 0X4004,
+ MIXER_FORMAT = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
// for target RESAMPLE
SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name;
// parameter 'value' is the new sample rate in Hz.
@@ -120,27 +121,19 @@
private:
enum {
+ // FIXME this representation permits up to 8 channels
NEEDS_CHANNEL_COUNT__MASK = 0x00000007,
- NEEDS_FORMAT__MASK = 0x000000F0,
- NEEDS_MUTE__MASK = 0x00000100,
- NEEDS_RESAMPLE__MASK = 0x00001000,
- NEEDS_AUX__MASK = 0x00010000,
};
enum {
- NEEDS_CHANNEL_1 = 0x00000000,
- NEEDS_CHANNEL_2 = 0x00000001,
+ NEEDS_CHANNEL_1 = 0x00000000, // mono
+ NEEDS_CHANNEL_2 = 0x00000001, // stereo
- NEEDS_FORMAT_16 = 0x00000010,
+ // sample format is not explicitly specified, and is assumed to be AUDIO_FORMAT_PCM_16_BIT
- NEEDS_MUTE_DISABLED = 0x00000000,
- NEEDS_MUTE_ENABLED = 0x00000100,
-
- NEEDS_RESAMPLE_DISABLED = 0x00000000,
- NEEDS_RESAMPLE_ENABLED = 0x00001000,
-
- NEEDS_AUX_DISABLED = 0x00000000,
- NEEDS_AUX_ENABLED = 0x00010000,
+ NEEDS_MUTE = 0x00000100,
+ NEEDS_RESAMPLE = 0x00001000,
+ NEEDS_AUX = 0x00010000,
};
struct state_t;
@@ -201,7 +194,9 @@
int32_t sessionId;
- int32_t padding[2];
+ audio_format_t mMixerFormat; // at this time: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+
+ int32_t padding[1];
// 16-byte boundary
@@ -224,7 +219,7 @@
NBLog::Writer* mLog;
int32_t reserved[1];
// FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS
- track_t tracks[MAX_NUM_TRACKS]; __attribute__((aligned(32)));
+ track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
};
// AudioBufferProvider that wraps a track AudioBufferProvider by a call to a downmix effect
@@ -256,9 +251,9 @@
state_t mState __attribute__((aligned(32)));
// effect descriptor for the downmixer used by the mixer
- static effect_descriptor_t dwnmFxDesc;
+ static effect_descriptor_t sDwnmFxDesc;
// indicates whether a downmix effect has been found and is usable by this mixer
- static bool isMultichannelCapable;
+ static bool sIsMultichannelCapable;
// Call after changing either the enabled status of a track, or parameters of an enabled track.
// OK to call more often than that, but unnecessary.
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index c5ad2c0..41bd990 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -60,7 +60,7 @@
// ----------------------------------------------------------------------------
AudioPolicyService::AudioPolicyService()
- : BnAudioPolicyService() , mpAudioPolicyDev(NULL) , mpAudioPolicy(NULL)
+ : BnAudioPolicyService(), mpAudioPolicyDev(NULL), mpAudioPolicy(NULL)
{
char value[PROPERTY_VALUE_MAX];
const struct hw_module_t *module;
@@ -475,8 +475,9 @@
audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc)
{
+ // FIXME change return type to status_t, and return NO_INIT here
if (mpAudioPolicy == NULL) {
- return NO_INIT;
+ return 0;
}
Mutex::Autolock _l(mLock);
return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc);
@@ -612,7 +613,7 @@
return NO_ERROR;
}
-status_t AudioPolicyService::dump(int fd, const Vector<String16>& args)
+status_t AudioPolicyService::dump(int fd, const Vector<String16>& args __unused)
{
if (!dumpAllowed()) {
dumpPermissionDenial(fd);
@@ -1441,6 +1442,14 @@
loadEffects(root, effects);
loadInputSources(root, effects);
+ // delete effects to fix memory leak.
+ // as effects is local var and valgrind would treat this as memory leak
+ // and although it only did in mediaserver init, but free it in case mediaserver reboot
+ size_t i;
+ for (i = 0; i < effects.size(); i++) {
+ delete effects[i];
+ }
+
config_free(root);
free(root);
free(data);
@@ -1452,7 +1461,7 @@
extern "C" {
-static audio_module_handle_t aps_load_hw_module(void *service,
+static audio_module_handle_t aps_load_hw_module(void *service __unused,
const char *name)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
@@ -1465,7 +1474,7 @@
}
// deprecated: replaced by aps_open_output_on_module()
-static audio_io_handle_t aps_open_output(void *service,
+static audio_io_handle_t aps_open_output(void *service __unused,
audio_devices_t *pDevices,
uint32_t *pSamplingRate,
audio_format_t *pFormat,
@@ -1483,7 +1492,7 @@
pLatencyMs, flags);
}
-static audio_io_handle_t aps_open_output_on_module(void *service,
+static audio_io_handle_t aps_open_output_on_module(void *service __unused,
audio_module_handle_t module,
audio_devices_t *pDevices,
uint32_t *pSamplingRate,
@@ -1502,7 +1511,7 @@
pLatencyMs, flags, offloadInfo);
}
-static audio_io_handle_t aps_open_dup_output(void *service,
+static audio_io_handle_t aps_open_dup_output(void *service __unused,
audio_io_handle_t output1,
audio_io_handle_t output2)
{
@@ -1514,7 +1523,7 @@
return af->openDuplicateOutput(output1, output2);
}
-static int aps_close_output(void *service, audio_io_handle_t output)
+static int aps_close_output(void *service __unused, audio_io_handle_t output)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
@@ -1524,7 +1533,7 @@
return af->closeOutput(output);
}
-static int aps_suspend_output(void *service, audio_io_handle_t output)
+static int aps_suspend_output(void *service __unused, audio_io_handle_t output)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
@@ -1535,7 +1544,7 @@
return af->suspendOutput(output);
}
-static int aps_restore_output(void *service, audio_io_handle_t output)
+static int aps_restore_output(void *service __unused, audio_io_handle_t output)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
@@ -1547,12 +1556,12 @@
}
// deprecated: replaced by aps_open_input_on_module(), and acoustics parameter is ignored
-static audio_io_handle_t aps_open_input(void *service,
+static audio_io_handle_t aps_open_input(void *service __unused,
audio_devices_t *pDevices,
uint32_t *pSamplingRate,
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
- audio_in_acoustics_t acoustics)
+ audio_in_acoustics_t acoustics __unused)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
@@ -1563,7 +1572,7 @@
return af->openInput((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask);
}
-static audio_io_handle_t aps_open_input_on_module(void *service,
+static audio_io_handle_t aps_open_input_on_module(void *service __unused,
audio_module_handle_t module,
audio_devices_t *pDevices,
uint32_t *pSamplingRate,
@@ -1579,7 +1588,7 @@
return af->openInput(module, pDevices, pSamplingRate, pFormat, pChannelMask);
}
-static int aps_close_input(void *service, audio_io_handle_t input)
+static int aps_close_input(void *service __unused, audio_io_handle_t input)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
@@ -1589,18 +1598,17 @@
return af->closeInput(input);
}
-static int aps_set_stream_output(void *service, audio_stream_type_t stream,
- audio_io_handle_t output)
+static int aps_invalidate_stream(void *service __unused, audio_stream_type_t stream)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
return PERMISSION_DENIED;
}
- return af->setStreamOutput(stream, output);
+ return af->invalidateStream(stream);
}
-static int aps_move_effects(void *service, int session,
+static int aps_move_effects(void *service __unused, int session,
audio_io_handle_t src_output,
audio_io_handle_t dst_output)
{
@@ -1612,7 +1620,7 @@
return af->moveEffects(session, src_output, dst_output);
}
-static char * aps_get_parameters(void *service, audio_io_handle_t io_handle,
+static char * aps_get_parameters(void *service __unused, audio_io_handle_t io_handle,
const char *keys)
{
String8 result = AudioSystem::getParameters(io_handle, String8(keys));
@@ -1663,24 +1671,24 @@
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,
- load_hw_module : aps_load_hw_module,
- open_output_on_module : aps_open_output_on_module,
- open_input_on_module : aps_open_input_on_module,
+ .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,
+ .invalidate_stream = aps_invalidate_stream,
+ .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,
+ .load_hw_module = aps_load_hw_module,
+ .open_output_on_module = aps_open_output_on_module,
+ .open_input_on_module = aps_open_input_on_module,
};
}; // namespace <unnamed>
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 323f1a4..ca98f16 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -25,6 +25,7 @@
#include "AudioResampler.h"
#include "AudioResamplerSinc.h"
#include "AudioResamplerCubic.h"
+#include "AudioResamplerDyn.h"
#ifdef __arm__
#include <machine/cpu-features.h>
@@ -77,6 +78,9 @@
int mX0R;
};
+/*static*/
+const double AudioResampler::kPhaseMultiplier = 1L << AudioResampler::kNumPhaseBits;
+
bool AudioResampler::qualityIsSupported(src_quality quality)
{
switch (quality) {
@@ -85,6 +89,9 @@
case MED_QUALITY:
case HIGH_QUALITY:
case VERY_HIGH_QUALITY:
+ case DYN_LOW_QUALITY:
+ case DYN_MED_QUALITY:
+ case DYN_HIGH_QUALITY:
return true;
default:
return false;
@@ -105,7 +112,7 @@
if (*endptr == '\0') {
defaultQuality = (src_quality) l;
ALOGD("forcing AudioResampler quality to %d", defaultQuality);
- if (defaultQuality < DEFAULT_QUALITY || defaultQuality > VERY_HIGH_QUALITY) {
+ if (defaultQuality < DEFAULT_QUALITY || defaultQuality > DYN_HIGH_QUALITY) {
defaultQuality = DEFAULT_QUALITY;
}
}
@@ -125,6 +132,12 @@
return 20;
case VERY_HIGH_QUALITY:
return 34;
+ case DYN_LOW_QUALITY:
+ return 4;
+ case DYN_MED_QUALITY:
+ return 6;
+ case DYN_HIGH_QUALITY:
+ return 12;
}
}
@@ -148,6 +161,16 @@
atFinalQuality = true;
}
+ /* if the caller requests DEFAULT_QUALITY and af.resampler.property
+ * has not been set, the target resampler quality is set to DYN_MED_QUALITY,
+ * and allowed to "throttle" down to DYN_LOW_QUALITY if necessary
+ * due to estimated CPU load of having too many active resamplers
+ * (the code below the if).
+ */
+ if (quality == DEFAULT_QUALITY) {
+ quality = DYN_MED_QUALITY;
+ }
+
// naive implementation of CPU load throttling doesn't account for whether resampler is active
pthread_mutex_lock(&mutex);
for (;;) {
@@ -162,7 +185,6 @@
// not enough CPU available for proposed quality level, so try next lowest level
switch (quality) {
default:
- case DEFAULT_QUALITY:
case LOW_QUALITY:
atFinalQuality = true;
break;
@@ -175,6 +197,15 @@
case VERY_HIGH_QUALITY:
quality = HIGH_QUALITY;
break;
+ case DYN_LOW_QUALITY:
+ atFinalQuality = true;
+ break;
+ case DYN_MED_QUALITY:
+ quality = DYN_LOW_QUALITY;
+ break;
+ case DYN_HIGH_QUALITY:
+ quality = DYN_MED_QUALITY;
+ break;
}
}
pthread_mutex_unlock(&mutex);
@@ -183,7 +214,6 @@
switch (quality) {
default:
- case DEFAULT_QUALITY:
case LOW_QUALITY:
ALOGV("Create linear Resampler");
resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate);
@@ -200,6 +230,12 @@
ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality);
resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate, quality);
break;
+ case DYN_LOW_QUALITY:
+ case DYN_MED_QUALITY:
+ case DYN_HIGH_QUALITY:
+ ALOGV("Create dynamic Resampler = %d", quality);
+ resampler = new AudioResamplerDyn(bitDepth, inChannelCount, sampleRate, quality);
+ break;
}
// initialize resampler
@@ -305,7 +341,7 @@
uint32_t phaseIncrement = mPhaseIncrement;
size_t outputIndex = 0;
size_t outputSampleCount = outFrameCount * 2;
- size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+ size_t inFrameCount = getInFrameCountRequired(outFrameCount);
// ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
// outFrameCount, inputIndex, phaseFraction, phaseIncrement);
@@ -403,7 +439,7 @@
uint32_t phaseIncrement = mPhaseIncrement;
size_t outputIndex = 0;
size_t outputSampleCount = outFrameCount * 2;
- size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+ size_t inFrameCount = getInFrameCountRequired(outFrameCount);
// ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
// outFrameCount, inputIndex, phaseFraction, phaseIncrement);
@@ -516,6 +552,16 @@
size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
uint32_t &phaseFraction, uint32_t phaseIncrement)
{
+ (void)maxOutPt; // remove unused parameter warnings
+ (void)maxInIdx;
+ (void)outputIndex;
+ (void)out;
+ (void)inputIndex;
+ (void)vl;
+ (void)vr;
+ (void)phaseFraction;
+ (void)phaseIncrement;
+ (void)in;
#define MO_PARAM5 "36" // offset of parameter 5 (outputIndex)
asm(
@@ -528,7 +574,7 @@
" ldr r8, [sp, #" MO_PARAM5 " + 4]\n" // out
" ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex
" ldr r0, [r0]\n" // outputIndex
- " add r8, r0, asl #2\n" // curOut
+ " add r8, r8, r0, asl #2\n" // curOut
" ldr r9, [sp, #" MO_PARAM5 " + 24]\n" // phaseIncrement
" ldr r10, [sp, #" MO_PARAM5 " + 12]\n" // vl
" ldr r11, [sp, #" MO_PARAM5 " + 16]\n" // vr
@@ -627,6 +673,16 @@
size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
uint32_t &phaseFraction, uint32_t phaseIncrement)
{
+ (void)maxOutPt; // remove unused parameter warnings
+ (void)maxInIdx;
+ (void)outputIndex;
+ (void)out;
+ (void)inputIndex;
+ (void)vl;
+ (void)vr;
+ (void)phaseFraction;
+ (void)phaseIncrement;
+ (void)in;
#define ST_PARAM5 "40" // offset of parameter 5 (outputIndex)
asm(
"stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n"
@@ -638,7 +694,7 @@
" ldr r8, [sp, #" ST_PARAM5 " + 4]\n" // out
" ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex
" ldr r0, [r0]\n" // outputIndex
- " add r8, r0, asl #2\n" // curOut
+ " add r8, r8, r0, asl #2\n" // curOut
" ldr r9, [sp, #" ST_PARAM5 " + 24]\n" // phaseIncrement
" ldr r10, [sp, #" ST_PARAM5 " + 12]\n" // vl
" ldr r11, [sp, #" ST_PARAM5 " + 16]\n" // vr
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 33e64ce..0592855 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -41,6 +41,9 @@
MED_QUALITY=2,
HIGH_QUALITY=3,
VERY_HIGH_QUALITY=4,
+ DYN_LOW_QUALITY=5,
+ DYN_MED_QUALITY=6,
+ DYN_HIGH_QUALITY=7,
};
static AudioResampler* create(int bitDepth, int inChannelCount,
@@ -81,7 +84,7 @@
static const uint32_t kPhaseMask = (1LU<<kNumPhaseBits)-1;
// multiplier to calculate fixed point phase increment
- static const double kPhaseMultiplier = 1L << kNumPhaseBits;
+ static const double kPhaseMultiplier;
AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate, src_quality quality);
@@ -107,6 +110,38 @@
uint64_t mLocalTimeFreq;
int64_t mPTS;
+ // returns the inFrameCount required to generate outFrameCount frames.
+ //
+ // Placed here to be a consistent for all resamplers.
+ //
+ // Right now, we use the upper bound without regards to the current state of the
+ // input buffer using integer arithmetic, as follows:
+ //
+ // (static_cast<uint64_t>(outFrameCount)*mInSampleRate + (mSampleRate - 1))/mSampleRate;
+ //
+ // The double precision equivalent (float may not be precise enough):
+ // ceil(static_cast<double>(outFrameCount) * mInSampleRate / mSampleRate);
+ //
+ // this relies on the fact that the mPhaseIncrement is rounded down from
+ // #phases * mInSampleRate/mSampleRate and the fact that Sum(Floor(x)) <= Floor(Sum(x)).
+ // http://www.proofwiki.org/wiki/Sum_of_Floors_Not_Greater_Than_Floor_of_Sums
+ //
+ // (so long as double precision is computed accurately enough to be considered
+ // greater than or equal to the Floor(x) value in int32_t arithmetic; thus this
+ // will not necessarily hold for floats).
+ //
+ // TODO:
+ // Greater accuracy and a tight bound is obtained by:
+ // 1) subtract and adjust for the current state of the AudioBufferProvider buffer.
+ // 2) using the exact integer formula where (ignoring 64b casting)
+ // inFrameCount = (mPhaseIncrement * (outFrameCount - 1) + mPhaseFraction) / phaseWrapLimit;
+ // phaseWrapLimit is the wraparound (1 << kNumPhaseBits), if not specified explicitly.
+ //
+ inline size_t getInFrameCountRequired(size_t outFrameCount) {
+ return (static_cast<uint64_t>(outFrameCount)*mInSampleRate
+ + (mSampleRate - 1))/mSampleRate;
+ }
+
private:
const src_quality mQuality;
diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp
index 1f9714b..8f14ff9 100644
--- a/services/audioflinger/AudioResamplerCubic.cpp
+++ b/services/audioflinger/AudioResamplerCubic.cpp
@@ -60,7 +60,7 @@
uint32_t phaseIncrement = mPhaseIncrement;
size_t outputIndex = 0;
size_t outputSampleCount = outFrameCount * 2;
- size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+ size_t inFrameCount = getInFrameCountRequired(outFrameCount);
// fetch first buffer
if (mBuffer.frameCount == 0) {
@@ -128,7 +128,7 @@
uint32_t phaseIncrement = mPhaseIncrement;
size_t outputIndex = 0;
size_t outputSampleCount = outFrameCount * 2;
- size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+ size_t inFrameCount = getInFrameCountRequired(outFrameCount);
// fetch first buffer
if (mBuffer.frameCount == 0) {
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
new file mode 100644
index 0000000..7e4ca0c
--- /dev/null
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2013 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 "AudioResamplerDyn"
+//#define LOG_NDEBUG 0
+
+#include <malloc.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <math.h>
+
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <utils/Log.h>
+
+#include "AudioResamplerFirOps.h" // USE_NEON and USE_INLINE_ASSEMBLY defined here
+#include "AudioResamplerFirProcess.h"
+#include "AudioResamplerFirProcessNeon.h"
+#include "AudioResamplerFirGen.h" // requires math.h
+#include "AudioResamplerDyn.h"
+
+//#define DEBUG_RESAMPLER
+
+namespace android {
+
+// generate a unique resample type compile-time constant (constexpr)
+#define RESAMPLETYPE(CHANNELS, LOCKED, STRIDE, COEFTYPE) \
+ ((((CHANNELS)-1)&1) | !!(LOCKED)<<1 | (COEFTYPE)<<2 \
+ | ((STRIDE)==8 ? 1 : (STRIDE)==16 ? 2 : 0)<<3)
+
+/*
+ * InBuffer is a type agnostic input buffer.
+ *
+ * Layout of the state buffer for halfNumCoefs=8.
+ *
+ * [rrrrrrppppppppnnnnnnnnrrrrrrrrrrrrrrrrrrr.... rrrrrrr]
+ * S I R
+ *
+ * S = mState
+ * I = mImpulse
+ * R = mRingFull
+ * p = past samples, convoluted with the (p)ositive side of sinc()
+ * n = future samples, convoluted with the (n)egative side of sinc()
+ * r = extra space for implementing the ring buffer
+ */
+
+template<typename TI>
+AudioResamplerDyn::InBuffer<TI>::InBuffer()
+ : mState(NULL), mImpulse(NULL), mRingFull(NULL), mStateSize(0) {
+}
+
+template<typename TI>
+AudioResamplerDyn::InBuffer<TI>::~InBuffer() {
+ init();
+}
+
+template<typename TI>
+void AudioResamplerDyn::InBuffer<TI>::init() {
+ free(mState);
+ mState = NULL;
+ mImpulse = NULL;
+ mRingFull = NULL;
+ mStateSize = 0;
+}
+
+// resizes the state buffer to accommodate the appropriate filter length
+template<typename TI>
+void AudioResamplerDyn::InBuffer<TI>::resize(int CHANNELS, int halfNumCoefs) {
+ // calculate desired state size
+ int stateSize = halfNumCoefs * CHANNELS * 2
+ * kStateSizeMultipleOfFilterLength;
+
+ // check if buffer needs resizing
+ if (mState
+ && stateSize == mStateSize
+ && mRingFull-mState == mStateSize-halfNumCoefs*CHANNELS) {
+ return;
+ }
+
+ // create new buffer
+ TI* state = (int16_t*)memalign(32, stateSize*sizeof(*state));
+ memset(state, 0, stateSize*sizeof(*state));
+
+ // attempt to preserve state
+ if (mState) {
+ TI* srcLo = mImpulse - halfNumCoefs*CHANNELS;
+ TI* srcHi = mImpulse + halfNumCoefs*CHANNELS;
+ TI* dst = state;
+
+ if (srcLo < mState) {
+ dst += mState-srcLo;
+ srcLo = mState;
+ }
+ if (srcHi > mState + mStateSize) {
+ srcHi = mState + mStateSize;
+ }
+ memcpy(dst, srcLo, (srcHi - srcLo) * sizeof(*srcLo));
+ free(mState);
+ }
+
+ // set class member vars
+ mState = state;
+ mStateSize = stateSize;
+ mImpulse = mState + halfNumCoefs*CHANNELS; // actually one sample greater than needed
+ mRingFull = mState + mStateSize - halfNumCoefs*CHANNELS;
+}
+
+// copy in the input data into the head (impulse+halfNumCoefs) of the buffer.
+template<typename TI>
+template<int CHANNELS>
+void AudioResamplerDyn::InBuffer<TI>::readAgain(TI*& impulse, const int halfNumCoefs,
+ const TI* const in, const size_t inputIndex) {
+ int16_t* head = impulse + halfNumCoefs*CHANNELS;
+ for (size_t i=0 ; i<CHANNELS ; i++) {
+ head[i] = in[inputIndex*CHANNELS + i];
+ }
+}
+
+// advance the impulse pointer, and load in data into the head (impulse+halfNumCoefs)
+template<typename TI>
+template<int CHANNELS>
+void AudioResamplerDyn::InBuffer<TI>::readAdvance(TI*& impulse, const int halfNumCoefs,
+ const TI* const in, const size_t inputIndex) {
+ impulse += CHANNELS;
+
+ if (CC_UNLIKELY(impulse >= mRingFull)) {
+ const size_t shiftDown = mRingFull - mState - halfNumCoefs*CHANNELS;
+ memcpy(mState, mState+shiftDown, halfNumCoefs*CHANNELS*2*sizeof(TI));
+ impulse -= shiftDown;
+ }
+ readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex);
+}
+
+void AudioResamplerDyn::Constants::set(
+ int L, int halfNumCoefs, int inSampleRate, int outSampleRate)
+{
+ int bits = 0;
+ int lscale = inSampleRate/outSampleRate < 2 ? L - 1 :
+ static_cast<int>(static_cast<uint64_t>(L)*inSampleRate/outSampleRate);
+ for (int i=lscale; i; ++bits, i>>=1)
+ ;
+ mL = L;
+ mShift = kNumPhaseBits - bits;
+ mHalfNumCoefs = halfNumCoefs;
+}
+
+AudioResamplerDyn::AudioResamplerDyn(int bitDepth,
+ int inChannelCount, int32_t sampleRate, src_quality quality)
+ : AudioResampler(bitDepth, inChannelCount, sampleRate, quality),
+ mResampleType(0), mFilterSampleRate(0), mFilterQuality(DEFAULT_QUALITY),
+ mCoefBuffer(NULL)
+{
+ mVolumeSimd[0] = mVolumeSimd[1] = 0;
+ // The AudioResampler base class assumes we are always ready for 1:1 resampling.
+ // We reset mInSampleRate to 0, so setSampleRate() will calculate filters for
+ // setSampleRate() for 1:1. (May be removed if precalculated filters are used.)
+ mInSampleRate = 0;
+ mConstants.set(128, 8, mSampleRate, mSampleRate); // TODO: set better
+}
+
+AudioResamplerDyn::~AudioResamplerDyn() {
+ free(mCoefBuffer);
+}
+
+void AudioResamplerDyn::init() {
+ mFilterSampleRate = 0; // always trigger new filter generation
+ mInBuffer.init();
+}
+
+void AudioResamplerDyn::setVolume(int16_t left, int16_t right) {
+ AudioResampler::setVolume(left, right);
+ mVolumeSimd[0] = static_cast<int32_t>(left)<<16;
+ mVolumeSimd[1] = static_cast<int32_t>(right)<<16;
+}
+
+template <typename T> T max(T a, T b) {return a > b ? a : b;}
+
+template <typename T> T absdiff(T a, T b) {return a > b ? a - b : b - a;}
+
+template<typename T>
+void AudioResamplerDyn::createKaiserFir(Constants &c, double stopBandAtten,
+ int inSampleRate, int outSampleRate, double tbwCheat) {
+ T* buf = reinterpret_cast<T*>(memalign(32, (c.mL+1)*c.mHalfNumCoefs*sizeof(T)));
+ static const double atten = 0.9998; // to avoid ripple overflow
+ double fcr;
+ double tbw = firKaiserTbw(c.mHalfNumCoefs, stopBandAtten);
+
+ if (inSampleRate < outSampleRate) { // upsample
+ fcr = max(0.5*tbwCheat - tbw/2, tbw/2);
+ } else { // downsample
+ fcr = max(0.5*tbwCheat*outSampleRate/inSampleRate - tbw/2, tbw/2);
+ }
+ // create and set filter
+ firKaiserGen(buf, c.mL, c.mHalfNumCoefs, stopBandAtten, fcr, atten);
+ c.setBuf(buf);
+ if (mCoefBuffer) {
+ free(mCoefBuffer);
+ }
+ mCoefBuffer = buf;
+#ifdef DEBUG_RESAMPLER
+ // print basic filter stats
+ printf("L:%d hnc:%d stopBandAtten:%lf fcr:%lf atten:%lf tbw:%lf\n",
+ c.mL, c.mHalfNumCoefs, stopBandAtten, fcr, atten, tbw);
+ // test the filter and report results
+ double fp = (fcr - tbw/2)/c.mL;
+ double fs = (fcr + tbw/2)/c.mL;
+ double passMin, passMax, passRipple;
+ double stopMax, stopRipple;
+ testFir(buf, c.mL, c.mHalfNumCoefs, fp, fs, /*passSteps*/ 1000, /*stopSteps*/ 100000,
+ passMin, passMax, passRipple, stopMax, stopRipple);
+ printf("passband(%lf, %lf): %.8lf %.8lf %.8lf\n", 0., fp, passMin, passMax, passRipple);
+ printf("stopband(%lf, %lf): %.8lf %.3lf\n", fs, 0.5, stopMax, stopRipple);
+#endif
+}
+
+// recursive gcd. Using objdump, it appears the tail recursion is converted to a while loop.
+static int gcd(int n, int m) {
+ if (m == 0) {
+ return n;
+ }
+ return gcd(m, n % m);
+}
+
+static bool isClose(int32_t newSampleRate, int32_t prevSampleRate,
+ int32_t filterSampleRate, int32_t outSampleRate) {
+
+ // different upsampling ratios do not need a filter change.
+ if (filterSampleRate != 0
+ && filterSampleRate < outSampleRate
+ && newSampleRate < outSampleRate)
+ return true;
+
+ // check design criteria again if downsampling is detected.
+ int pdiff = absdiff(newSampleRate, prevSampleRate);
+ int adiff = absdiff(newSampleRate, filterSampleRate);
+
+ // allow up to 6% relative change increments.
+ // allow up to 12% absolute change increments (from filter design)
+ return pdiff < prevSampleRate>>4 && adiff < filterSampleRate>>3;
+}
+
+void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) {
+ if (mInSampleRate == inSampleRate) {
+ return;
+ }
+ int32_t oldSampleRate = mInSampleRate;
+ int32_t oldHalfNumCoefs = mConstants.mHalfNumCoefs;
+ uint32_t oldPhaseWrapLimit = mConstants.mL << mConstants.mShift;
+ bool useS32 = false;
+
+ mInSampleRate = inSampleRate;
+
+ // TODO: Add precalculated Equiripple filters
+
+ if (mFilterQuality != getQuality() ||
+ !isClose(inSampleRate, oldSampleRate, mFilterSampleRate, mSampleRate)) {
+ mFilterSampleRate = inSampleRate;
+ mFilterQuality = getQuality();
+
+ // Begin Kaiser Filter computation
+ //
+ // The quantization floor for S16 is about 96db - 10*log_10(#length) + 3dB.
+ // Keep the stop band attenuation no greater than 84-85dB for 32 length S16 filters
+ //
+ // For s32 we keep the stop band attenuation at the same as 16b resolution, about
+ // 96-98dB
+ //
+
+ double stopBandAtten;
+ double tbwCheat = 1.; // how much we "cheat" into aliasing
+ int halfLength;
+ if (mFilterQuality == DYN_HIGH_QUALITY) {
+ // 32b coefficients, 64 length
+ useS32 = true;
+ stopBandAtten = 98.;
+ if (inSampleRate >= mSampleRate * 4) {
+ halfLength = 48;
+ } else if (inSampleRate >= mSampleRate * 2) {
+ halfLength = 40;
+ } else {
+ halfLength = 32;
+ }
+ } else if (mFilterQuality == DYN_LOW_QUALITY) {
+ // 16b coefficients, 16-32 length
+ useS32 = false;
+ stopBandAtten = 80.;
+ if (inSampleRate >= mSampleRate * 4) {
+ halfLength = 24;
+ } else if (inSampleRate >= mSampleRate * 2) {
+ halfLength = 16;
+ } else {
+ halfLength = 8;
+ }
+ if (inSampleRate <= mSampleRate) {
+ tbwCheat = 1.05;
+ } else {
+ tbwCheat = 1.03;
+ }
+ } else { // DYN_MED_QUALITY
+ // 16b coefficients, 32-64 length
+ // note: > 64 length filters with 16b coefs can have quantization noise problems
+ useS32 = false;
+ stopBandAtten = 84.;
+ if (inSampleRate >= mSampleRate * 4) {
+ halfLength = 32;
+ } else if (inSampleRate >= mSampleRate * 2) {
+ halfLength = 24;
+ } else {
+ halfLength = 16;
+ }
+ if (inSampleRate <= mSampleRate) {
+ tbwCheat = 1.03;
+ } else {
+ tbwCheat = 1.01;
+ }
+ }
+
+ // determine the number of polyphases in the filterbank.
+ // for 16b, it is desirable to have 2^(16/2) = 256 phases.
+ // https://ccrma.stanford.edu/~jos/resample/Relation_Interpolation_Error_Quantization.html
+ //
+ // We are a bit more lax on this.
+
+ int phases = mSampleRate / gcd(mSampleRate, inSampleRate);
+
+ // TODO: Once dynamic sample rate change is an option, the code below
+ // should be modified to execute only when dynamic sample rate change is enabled.
+ //
+ // as above, #phases less than 63 is too few phases for accurate linear interpolation.
+ // we increase the phases to compensate, but more phases means more memory per
+ // filter and more time to compute the filter.
+ //
+ // if we know that the filter will be used for dynamic sample rate changes,
+ // that would allow us skip this part for fixed sample rate resamplers.
+ //
+ while (phases<63) {
+ phases *= 2; // this code only needed to support dynamic rate changes
+ }
+
+ if (phases>=256) { // too many phases, always interpolate
+ phases = 127;
+ }
+
+ // create the filter
+ mConstants.set(phases, halfLength, inSampleRate, mSampleRate);
+ if (useS32) {
+ createKaiserFir<int32_t>(mConstants, stopBandAtten,
+ inSampleRate, mSampleRate, tbwCheat);
+ } else {
+ createKaiserFir<int16_t>(mConstants, stopBandAtten,
+ inSampleRate, mSampleRate, tbwCheat);
+ }
+ } // End Kaiser filter
+
+ // update phase and state based on the new filter.
+ const Constants& c(mConstants);
+ mInBuffer.resize(mChannelCount, c.mHalfNumCoefs);
+ const uint32_t phaseWrapLimit = c.mL << c.mShift;
+ // try to preserve as much of the phase fraction as possible for on-the-fly changes
+ mPhaseFraction = static_cast<unsigned long long>(mPhaseFraction)
+ * phaseWrapLimit / oldPhaseWrapLimit;
+ mPhaseFraction %= phaseWrapLimit; // should not do anything, but just in case.
+ mPhaseIncrement = static_cast<uint32_t>(static_cast<double>(phaseWrapLimit)
+ * inSampleRate / mSampleRate);
+
+ // determine which resampler to use
+ // check if locked phase (works only if mPhaseIncrement has no "fractional phase bits")
+ int locked = (mPhaseIncrement << (sizeof(mPhaseIncrement)*8 - c.mShift)) == 0;
+ int stride = (c.mHalfNumCoefs&7)==0 ? 16 : (c.mHalfNumCoefs&3)==0 ? 8 : 2;
+ if (locked) {
+ mPhaseFraction = mPhaseFraction >> c.mShift << c.mShift; // remove fractional phase
+ }
+
+ mResampleType = RESAMPLETYPE(mChannelCount, locked, stride, !!useS32);
+#ifdef DEBUG_RESAMPLER
+ printf("channels:%d %s stride:%d %s coef:%d shift:%d\n",
+ mChannelCount, locked ? "locked" : "interpolated",
+ stride, useS32 ? "S32" : "S16", 2*c.mHalfNumCoefs, c.mShift);
+#endif
+}
+
+void AudioResamplerDyn::resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider)
+{
+ // TODO:
+ // 24 cases - this perhaps can be reduced later, as testing might take too long
+ switch (mResampleType) {
+
+ // stride 16 (falls back to stride 2 for machines that do not support NEON)
+ case RESAMPLETYPE(1, true, 16, 0):
+ return resample<1, true, 16>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(2, true, 16, 0):
+ return resample<2, true, 16>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(1, false, 16, 0):
+ return resample<1, false, 16>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(2, false, 16, 0):
+ return resample<2, false, 16>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(1, true, 16, 1):
+ return resample<1, true, 16>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+ case RESAMPLETYPE(2, true, 16, 1):
+ return resample<2, true, 16>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+ case RESAMPLETYPE(1, false, 16, 1):
+ return resample<1, false, 16>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+ case RESAMPLETYPE(2, false, 16, 1):
+ return resample<2, false, 16>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+#if 0
+ // TODO: Remove these?
+ // stride 8
+ case RESAMPLETYPE(1, true, 8, 0):
+ return resample<1, true, 8>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(2, true, 8, 0):
+ return resample<2, true, 8>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(1, false, 8, 0):
+ return resample<1, false, 8>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(2, false, 8, 0):
+ return resample<2, false, 8>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(1, true, 8, 1):
+ return resample<1, true, 8>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+ case RESAMPLETYPE(2, true, 8, 1):
+ return resample<2, true, 8>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+ case RESAMPLETYPE(1, false, 8, 1):
+ return resample<1, false, 8>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+ case RESAMPLETYPE(2, false, 8, 1):
+ return resample<2, false, 8>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+ // stride 2 (can handle any filter length)
+ case RESAMPLETYPE(1, true, 2, 0):
+ return resample<1, true, 2>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(2, true, 2, 0):
+ return resample<2, true, 2>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(1, false, 2, 0):
+ return resample<1, false, 2>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(2, false, 2, 0):
+ return resample<2, false, 2>(out, outFrameCount, mConstants.mFirCoefsS16, provider);
+ case RESAMPLETYPE(1, true, 2, 1):
+ return resample<1, true, 2>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+ case RESAMPLETYPE(2, true, 2, 1):
+ return resample<2, true, 2>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+ case RESAMPLETYPE(1, false, 2, 1):
+ return resample<1, false, 2>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+ case RESAMPLETYPE(2, false, 2, 1):
+ return resample<2, false, 2>(out, outFrameCount, mConstants.mFirCoefsS32, provider);
+#endif
+ default:
+ ; // error
+ }
+}
+
+template<int CHANNELS, bool LOCKED, int STRIDE, typename TC>
+void AudioResamplerDyn::resample(int32_t* out, size_t outFrameCount,
+ const TC* const coefs, AudioBufferProvider* provider)
+{
+ const Constants& c(mConstants);
+ int16_t* impulse = mInBuffer.getImpulse();
+ size_t inputIndex = mInputIndex;
+ uint32_t phaseFraction = mPhaseFraction;
+ const uint32_t phaseIncrement = mPhaseIncrement;
+ size_t outputIndex = 0;
+ size_t outputSampleCount = outFrameCount * 2; // stereo output
+ size_t inFrameCount = getInFrameCountRequired(outFrameCount);
+ const uint32_t phaseWrapLimit = c.mL << c.mShift;
+
+ // NOTE: be very careful when modifying the code here. register
+ // pressure is very high and a small change might cause the compiler
+ // to generate far less efficient code.
+ // Always sanity check the result with objdump or test-resample.
+
+ // the following logic is a bit convoluted to keep the main processing loop
+ // as tight as possible with register allocation.
+ while (outputIndex < outputSampleCount) {
+ // buffer is empty, fetch a new one
+ while (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = inFrameCount;
+ provider->getNextBuffer(&mBuffer,
+ calculateOutputPTS(outputIndex / 2));
+ if (mBuffer.raw == NULL) {
+ goto resample_exit;
+ }
+ if (phaseFraction >= phaseWrapLimit) { // read in data
+ mInBuffer.readAdvance<CHANNELS>(
+ impulse, c.mHalfNumCoefs, mBuffer.i16, inputIndex);
+ phaseFraction -= phaseWrapLimit;
+ while (phaseFraction >= phaseWrapLimit) {
+ inputIndex++;
+ if (inputIndex >= mBuffer.frameCount) {
+ inputIndex -= mBuffer.frameCount;
+ provider->releaseBuffer(&mBuffer);
+ break;
+ }
+ mInBuffer.readAdvance<CHANNELS>(
+ impulse, c.mHalfNumCoefs, mBuffer.i16, inputIndex);
+ phaseFraction -= phaseWrapLimit;
+ }
+ }
+ }
+ const int16_t* const in = mBuffer.i16;
+ const size_t frameCount = mBuffer.frameCount;
+ const int coefShift = c.mShift;
+ const int halfNumCoefs = c.mHalfNumCoefs;
+ const int32_t* const volumeSimd = mVolumeSimd;
+
+ // reread the last input in.
+ mInBuffer.readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex);
+
+ // main processing loop
+ while (CC_LIKELY(outputIndex < outputSampleCount)) {
+ // caution: fir() is inlined and may be large.
+ // output will be loaded with the appropriate values
+ //
+ // from the input samples in impulse[-halfNumCoefs+1]... impulse[halfNumCoefs]
+ // from the polyphase filter of (phaseFraction / phaseWrapLimit) in coefs.
+ //
+ fir<CHANNELS, LOCKED, STRIDE>(
+ &out[outputIndex],
+ phaseFraction, phaseWrapLimit,
+ coefShift, halfNumCoefs, coefs,
+ impulse, volumeSimd);
+ outputIndex += 2;
+
+ phaseFraction += phaseIncrement;
+ while (phaseFraction >= phaseWrapLimit) {
+ inputIndex++;
+ if (inputIndex >= frameCount) {
+ goto done; // need a new buffer
+ }
+ mInBuffer.readAdvance<CHANNELS>(impulse, halfNumCoefs, in, inputIndex);
+ phaseFraction -= phaseWrapLimit;
+ }
+ }
+done:
+ // often arrives here when input buffer runs out
+ if (inputIndex >= frameCount) {
+ inputIndex -= frameCount;
+ provider->releaseBuffer(&mBuffer);
+ // mBuffer.frameCount MUST be zero here.
+ }
+ }
+
+resample_exit:
+ mInBuffer.setImpulse(impulse);
+ mInputIndex = inputIndex;
+ mPhaseFraction = phaseFraction;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/audioflinger/AudioResamplerDyn.h b/services/audioflinger/AudioResamplerDyn.h
new file mode 100644
index 0000000..df1fdbe
--- /dev/null
+++ b/services/audioflinger/AudioResamplerDyn.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2013 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_RESAMPLER_DYN_H
+#define ANDROID_AUDIO_RESAMPLER_DYN_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+
+#include "AudioResampler.h"
+
+namespace android {
+
+class AudioResamplerDyn: public AudioResampler {
+public:
+ AudioResamplerDyn(int bitDepth, int inChannelCount, int32_t sampleRate,
+ src_quality quality);
+
+ virtual ~AudioResamplerDyn();
+
+ virtual void init();
+
+ virtual void setSampleRate(int32_t inSampleRate);
+
+ virtual void setVolume(int16_t left, int16_t right);
+
+ virtual void resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider);
+
+private:
+
+ class Constants { // stores the filter constants.
+ public:
+ Constants() :
+ mL(0), mShift(0), mHalfNumCoefs(0), mFirCoefsS16(NULL)
+ {}
+ void set(int L, int halfNumCoefs,
+ int inSampleRate, int outSampleRate);
+ inline void setBuf(int16_t* buf) {
+ mFirCoefsS16 = buf;
+ }
+ inline void setBuf(int32_t* buf) {
+ mFirCoefsS32 = buf;
+ }
+
+ int mL; // interpolation phases in the filter.
+ int mShift; // right shift to get polyphase index
+ unsigned int mHalfNumCoefs; // filter half #coefs
+ union { // polyphase filter bank
+ const int16_t* mFirCoefsS16;
+ const int32_t* mFirCoefsS32;
+ };
+ };
+
+ // Input buffer management for a given input type TI, now (int16_t)
+ // Is agnostic of the actual type, can work with int32_t and float.
+ template<typename TI>
+ class InBuffer {
+ public:
+ InBuffer();
+ ~InBuffer();
+ void init();
+ void resize(int CHANNELS, int halfNumCoefs);
+
+ // used for direct management of the mImpulse pointer
+ inline TI* getImpulse() {
+ return mImpulse;
+ }
+ inline void setImpulse(TI *impulse) {
+ mImpulse = impulse;
+ }
+ template<int CHANNELS>
+ inline void readAgain(TI*& impulse, const int halfNumCoefs,
+ const TI* const in, const size_t inputIndex);
+ template<int CHANNELS>
+ inline void readAdvance(TI*& impulse, const int halfNumCoefs,
+ const TI* const in, const size_t inputIndex);
+
+ private:
+ // tuning parameter guidelines: 2 <= multiple <= 8
+ static const int kStateSizeMultipleOfFilterLength = 4;
+
+ TI* mState; // base pointer for the input buffer storage
+ TI* mImpulse; // current location of the impulse response (centered)
+ TI* mRingFull; // mState <= mImpulse < mRingFull
+ // in general, mRingFull = mState + mStateSize - halfNumCoefs*CHANNELS.
+ size_t mStateSize; // in units of TI.
+ };
+
+ template<int CHANNELS, bool LOCKED, int STRIDE, typename TC>
+ void resample(int32_t* out, size_t outFrameCount,
+ const TC* const coefs, AudioBufferProvider* provider);
+
+ template<typename T>
+ void createKaiserFir(Constants &c, double stopBandAtten,
+ int inSampleRate, int outSampleRate, double tbwCheat);
+
+ InBuffer<int16_t> mInBuffer;
+ Constants mConstants; // current set of coefficient parameters
+ int32_t __attribute__ ((aligned (8))) mVolumeSimd[2];
+ int32_t mResampleType; // contains the resample type.
+ int32_t mFilterSampleRate; // designed filter sample rate.
+ src_quality mFilterQuality; // designed filter quality.
+ void* mCoefBuffer; // if a filter is created, this is not null
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif /*ANDROID_AUDIO_RESAMPLER_DYN_H*/
diff --git a/services/audioflinger/AudioResamplerFirGen.h b/services/audioflinger/AudioResamplerFirGen.h
new file mode 100644
index 0000000..fac3001
--- /dev/null
+++ b/services/audioflinger/AudioResamplerFirGen.h
@@ -0,0 +1,684 @@
+/*
+ * Copyright (C) 2013 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_RESAMPLER_FIR_GEN_H
+#define ANDROID_AUDIO_RESAMPLER_FIR_GEN_H
+
+namespace android {
+
+/*
+ * generates a sine wave at equal steps.
+ *
+ * As most of our functions use sine or cosine at equal steps,
+ * it is very efficient to compute them that way (single multiply and subtract),
+ * rather than invoking the math library sin() or cos() each time.
+ *
+ * SineGen uses Goertzel's Algorithm (as a generator not a filter)
+ * to calculate sine(wstart + n * wstep) or cosine(wstart + n * wstep)
+ * by stepping through 0, 1, ... n.
+ *
+ * e^i(wstart+wstep) = 2cos(wstep) * e^i(wstart) - e^i(wstart-wstep)
+ *
+ * or looking at just the imaginary sine term, as the cosine follows identically:
+ *
+ * sin(wstart+wstep) = 2cos(wstep) * sin(wstart) - sin(wstart-wstep)
+ *
+ * Goertzel's algorithm is more efficient than the angle addition formula,
+ * e^i(wstart+wstep) = e^i(wstart) * e^i(wstep), which takes up to
+ * 4 multiplies and 2 adds (or 3* and 3+) and requires both sine and
+ * cosine generation due to the complex * complex multiply (full rotation).
+ *
+ * See: http://en.wikipedia.org/wiki/Goertzel_algorithm
+ *
+ */
+
+class SineGen {
+public:
+ SineGen(double wstart, double wstep, bool cosine = false) {
+ if (cosine) {
+ mCurrent = cos(wstart);
+ mPrevious = cos(wstart - wstep);
+ } else {
+ mCurrent = sin(wstart);
+ mPrevious = sin(wstart - wstep);
+ }
+ mTwoCos = 2.*cos(wstep);
+ }
+ SineGen(double expNow, double expPrev, double twoCosStep) {
+ mCurrent = expNow;
+ mPrevious = expPrev;
+ mTwoCos = twoCosStep;
+ }
+ inline double value() const {
+ return mCurrent;
+ }
+ inline void advance() {
+ double tmp = mCurrent;
+ mCurrent = mCurrent*mTwoCos - mPrevious;
+ mPrevious = tmp;
+ }
+ inline double valueAdvance() {
+ double tmp = mCurrent;
+ mCurrent = mCurrent*mTwoCos - mPrevious;
+ mPrevious = tmp;
+ return tmp;
+ }
+
+private:
+ double mCurrent; // current value of sine/cosine
+ double mPrevious; // previous value of sine/cosine
+ double mTwoCos; // stepping factor
+};
+
+/*
+ * generates a series of sine generators, phase offset by fixed steps.
+ *
+ * This is used to generate polyphase sine generators, one per polyphase
+ * in the filter code below.
+ *
+ * The SineGen returned by value() starts at innerStart = outerStart + n*outerStep;
+ * increments by innerStep.
+ *
+ */
+
+class SineGenGen {
+public:
+ SineGenGen(double outerStart, double outerStep, double innerStep, bool cosine = false)
+ : mSineInnerCur(outerStart, outerStep, cosine),
+ mSineInnerPrev(outerStart-innerStep, outerStep, cosine)
+ {
+ mTwoCos = 2.*cos(innerStep);
+ }
+ inline SineGen value() {
+ return SineGen(mSineInnerCur.value(), mSineInnerPrev.value(), mTwoCos);
+ }
+ inline void advance() {
+ mSineInnerCur.advance();
+ mSineInnerPrev.advance();
+ }
+ inline SineGen valueAdvance() {
+ return SineGen(mSineInnerCur.valueAdvance(), mSineInnerPrev.valueAdvance(), mTwoCos);
+ }
+
+private:
+ SineGen mSineInnerCur; // generate the inner sine values (stepped by outerStep).
+ SineGen mSineInnerPrev; // generate the inner sine previous values
+ // (behind by innerStep, stepped by outerStep).
+ double mTwoCos; // the inner stepping factor for the returned SineGen.
+};
+
+static inline double sqr(double x) {
+ return x * x;
+}
+
+/*
+ * rounds a double to the nearest integer for FIR coefficients.
+ *
+ * One variant uses noise shaping, which must keep error history
+ * to work (the err parameter, initialized to 0).
+ * The other variant is a non-noise shaped version for
+ * S32 coefficients (noise shaping doesn't gain much).
+ *
+ * Caution: No bounds saturation is applied, but isn't needed in this case.
+ *
+ * @param x is the value to round.
+ *
+ * @param maxval is the maximum integer scale factor expressed as an int64 (for headroom).
+ * Typically this may be the maximum positive integer+1 (using the fact that double precision
+ * FIR coefficients generated here are never that close to 1.0 to pose an overflow condition).
+ *
+ * @param err is the previous error (actual - rounded) for the previous rounding op.
+ * For 16b coefficients this can improve stopband dB performance by up to 2dB.
+ *
+ * Many variants exist for the noise shaping: http://en.wikipedia.org/wiki/Noise_shaping
+ *
+ */
+
+static inline int64_t toint(double x, int64_t maxval, double& err) {
+ double val = x * maxval;
+ double ival = floor(val + 0.5 + err*0.2);
+ err = val - ival;
+ return static_cast<int64_t>(ival);
+}
+
+static inline int64_t toint(double x, int64_t maxval) {
+ return static_cast<int64_t>(floor(x * maxval + 0.5));
+}
+
+/*
+ * Modified Bessel function of the first kind
+ * http://en.wikipedia.org/wiki/Bessel_function
+ *
+ * The formulas are taken from Abramowitz and Stegun,
+ * _Handbook of Mathematical Functions_ (links below):
+ *
+ * http://people.math.sfu.ca/~cbm/aands/page_375.htm
+ * http://people.math.sfu.ca/~cbm/aands/page_378.htm
+ *
+ * http://dlmf.nist.gov/10.25
+ * http://dlmf.nist.gov/10.40
+ *
+ * Note we assume x is nonnegative (the function is symmetric,
+ * pass in the absolute value as needed).
+ *
+ * Constants are compile time derived with templates I0Term<> and
+ * I0ATerm<> to the precision of the compiler. The series can be expanded
+ * to any precision needed, but currently set around 24b precision.
+ *
+ * We use a bit of template math here, constexpr would probably be
+ * more appropriate for a C++11 compiler.
+ *
+ * For the intermediate range 3.75 < x < 15, we use minimax polynomial fit.
+ *
+ */
+
+template <int N>
+struct I0Term {
+ static const double value = I0Term<N-1>::value / (4. * N * N);
+};
+
+template <>
+struct I0Term<0> {
+ static const double value = 1.;
+};
+
+template <int N>
+struct I0ATerm {
+ static const double value = I0ATerm<N-1>::value * (2.*N-1.) * (2.*N-1.) / (8. * N);
+};
+
+template <>
+struct I0ATerm<0> { // 1/sqrt(2*PI);
+ static const double value = 0.398942280401432677939946059934381868475858631164934657665925;
+};
+
+#if USE_HORNERS_METHOD
+/* Polynomial evaluation of A + Bx + Cx^2 + Dx^3 + ...
+ * using Horner's Method: http://en.wikipedia.org/wiki/Horner's_method
+ *
+ * This has fewer multiplications than Estrin's method below, but has back to back
+ * floating point dependencies.
+ *
+ * On ARM this appears to work slower, so USE_HORNERS_METHOD is not default enabled.
+ */
+
+inline double Poly2(double A, double B, double x) {
+ return A + x * B;
+}
+
+inline double Poly4(double A, double B, double C, double D, double x) {
+ return A + x * (B + x * (C + x * (D)));
+}
+
+inline double Poly7(double A, double B, double C, double D, double E, double F, double G,
+ double x) {
+ return A + x * (B + x * (C + x * (D + x * (E + x * (F + x * (G))))));
+}
+
+inline double Poly9(double A, double B, double C, double D, double E, double F, double G,
+ double H, double I, double x) {
+ return A + x * (B + x * (C + x * (D + x * (E + x * (F + x * (G + x * (H + x * (I))))))));
+}
+
+#else
+/* Polynomial evaluation of A + Bx + Cx^2 + Dx^3 + ...
+ * using Estrin's Method: http://en.wikipedia.org/wiki/Estrin's_scheme
+ *
+ * This is typically faster, perhaps gains about 5-10% overall on ARM processors
+ * over Horner's method above.
+ */
+
+inline double Poly2(double A, double B, double x) {
+ return A + B * x;
+}
+
+inline double Poly3(double A, double B, double C, double x, double x2) {
+ return Poly2(A, B, x) + C * x2;
+}
+
+inline double Poly3(double A, double B, double C, double x) {
+ return Poly2(A, B, x) + C * x * x;
+}
+
+inline double Poly4(double A, double B, double C, double D, double x, double x2) {
+ return Poly2(A, B, x) + Poly2(C, D, x) * x2; // same as poly2(poly2, poly2, x2);
+}
+
+inline double Poly4(double A, double B, double C, double D, double x) {
+ return Poly4(A, B, C, D, x, x * x);
+}
+
+inline double Poly7(double A, double B, double C, double D, double E, double F, double G,
+ double x) {
+ double x2 = x * x;
+ return Poly4(A, B, C, D, x, x2) + Poly3(E, F, G, x, x2) * (x2 * x2);
+}
+
+inline double Poly8(double A, double B, double C, double D, double E, double F, double G,
+ double H, double x, double x2, double x4) {
+ return Poly4(A, B, C, D, x, x2) + Poly4(E, F, G, H, x, x2) * x4;
+}
+
+inline double Poly9(double A, double B, double C, double D, double E, double F, double G,
+ double H, double I, double x) {
+ double x2 = x * x;
+#if 1
+ // It does not seem faster to explicitly decompose Poly8 into Poly4, but
+ // could depend on compiler floating point scheduling.
+ double x4 = x2 * x2;
+ return Poly8(A, B, C, D, E, F, G, H, x, x2, x4) + I * (x4 * x4);
+#else
+ double val = Poly4(A, B, C, D, x, x2);
+ double x4 = x2 * x2;
+ return val + Poly4(E, F, G, H, x, x2) * x4 + I * (x4 * x4);
+#endif
+}
+#endif
+
+static inline double I0(double x) {
+ if (x < 3.75) {
+ x *= x;
+ return Poly7(I0Term<0>::value, I0Term<1>::value,
+ I0Term<2>::value, I0Term<3>::value,
+ I0Term<4>::value, I0Term<5>::value,
+ I0Term<6>::value, x); // e < 1.6e-7
+ }
+ if (1) {
+ /*
+ * Series expansion coefs are easy to calculate, but are expanded around 0,
+ * so error is unequal over the interval 0 < x < 3.75, the error being
+ * significantly better near 0.
+ *
+ * A better solution is to use precise minimax polynomial fits.
+ *
+ * We use a slightly more complicated solution for 3.75 < x < 15, based on
+ * the tables in Blair and Edwards, "Stable Rational Minimax Approximations
+ * to the Modified Bessel Functions I0(x) and I1(x)", Chalk Hill Nuclear Laboratory,
+ * AECL-4928.
+ *
+ * http://www.iaea.org/inis/collection/NCLCollectionStore/_Public/06/178/6178667.pdf
+ *
+ * See Table 11 for 0 < x < 15; e < 10^(-7.13).
+ *
+ * Note: Beta cannot exceed 15 (hence Stopband cannot exceed 144dB = 24b).
+ *
+ * This speeds up overall computation by about 40% over using the else clause below,
+ * which requires sqrt and exp.
+ *
+ */
+
+ x *= x;
+ double num = Poly9(-0.13544938430e9, -0.33153754512e8,
+ -0.19406631946e7, -0.48058318783e5,
+ -0.63269783360e3, -0.49520779070e1,
+ -0.24970910370e-1, -0.74741159550e-4,
+ -0.18257612460e-6, x);
+ double y = x - 225.; // reflection around 15 (squared)
+ double den = Poly4(-0.34598737196e8, 0.23852643181e6,
+ -0.70699387620e3, 0.10000000000e1, y);
+ return num / den;
+
+#if IO_EXTENDED_BETA
+ /* Table 42 for x > 15; e < 10^(-8.11).
+ * This is used for Beta>15, but is disabled here as
+ * we never use Beta that high.
+ *
+ * NOTE: This should be enabled only for x > 15.
+ */
+
+ double y = 1./x;
+ double z = y - (1./15);
+ double num = Poly2(0.415079861746e1, -0.5149092496e1, z);
+ double den = Poly3(0.103150763823e2, -0.14181687413e2,
+ 0.1000000000e1, z);
+ return exp(x) * sqrt(y) * num / den;
+#endif
+ } else {
+ /*
+ * NOT USED, but reference for large Beta.
+ *
+ * Abramowitz and Stegun asymptotic formula.
+ * works for x > 3.75.
+ */
+ double y = 1./x;
+ return exp(x) * sqrt(y) *
+ // note: reciprocal squareroot may be easier!
+ // http://en.wikipedia.org/wiki/Fast_inverse_square_root
+ Poly9(I0ATerm<0>::value, I0ATerm<1>::value,
+ I0ATerm<2>::value, I0ATerm<3>::value,
+ I0ATerm<4>::value, I0ATerm<5>::value,
+ I0ATerm<6>::value, I0ATerm<7>::value,
+ I0ATerm<8>::value, y); // (... e) < 1.9e-7
+ }
+}
+
+/*
+ * calculates the transition bandwidth for a Kaiser filter
+ *
+ * Formula 3.2.8, Vaidyanathan, _Multirate Systems and Filter Banks_, p. 48
+ * Formula 7.76, Oppenheim and Schafer, _Discrete-time Signal Processing, 3e_, p. 542
+ *
+ * @param halfNumCoef is half the number of coefficients per filter phase.
+ *
+ * @param stopBandAtten is the stop band attenuation desired.
+ *
+ * @return the transition bandwidth in normalized frequency (0 <= f <= 0.5)
+ */
+static inline double firKaiserTbw(int halfNumCoef, double stopBandAtten) {
+ return (stopBandAtten - 7.95)/((2.*14.36)*halfNumCoef);
+}
+
+/*
+ * calculates the fir transfer response of the overall polyphase filter at w.
+ *
+ * Calculates the DTFT transfer coefficient H(w) for 0 <= w <= PI, utilizing the
+ * fact that h[n] is symmetric (cosines only, no complex arithmetic).
+ *
+ * We use Goertzel's algorithm to accelerate the computation to essentially
+ * a single multiply and 2 adds per filter coefficient h[].
+ *
+ * Be careful be careful to consider that h[n] is the overall polyphase filter,
+ * with L phases, so rescaling H(w)/L is probably what you expect for "unity gain",
+ * as you only use one of the polyphases at a time.
+ */
+template <typename T>
+static inline double firTransfer(const T* coef, int L, int halfNumCoef, double w) {
+ double accum = static_cast<double>(coef[0])*0.5; // "center coefficient" from first bank
+ coef += halfNumCoef; // skip first filterbank (picked up by the last filterbank).
+#if SLOW_FIRTRANSFER
+ /* Original code for reference. This is equivalent to the code below, but slower. */
+ for (int i=1 ; i<=L ; ++i) {
+ for (int j=0, ix=i ; j<halfNumCoef ; ++j, ix+=L) {
+ accum += cos(ix*w)*static_cast<double>(*coef++);
+ }
+ }
+#else
+ /*
+ * Our overall filter is stored striped by polyphases, not a contiguous h[n].
+ * We could fetch coefficients in a non-contiguous fashion
+ * but that will not scale to vector processing.
+ *
+ * We apply Goertzel's algorithm directly to each polyphase filter bank instead of
+ * using cosine generation/multiplication, thereby saving one multiply per inner loop.
+ *
+ * See: http://en.wikipedia.org/wiki/Goertzel_algorithm
+ * Also: Oppenheim and Schafer, _Discrete Time Signal Processing, 3e_, p. 720.
+ *
+ * We use the basic recursion to incorporate the cosine steps into real sequence x[n]:
+ * s[n] = x[n] + (2cosw)*s[n-1] + s[n-2]
+ *
+ * y[n] = s[n] - e^(iw)s[n-1]
+ * = sum_{k=-\infty}^{n} x[k]e^(-iw(n-k))
+ * = e^(-iwn) sum_{k=0}^{n} x[k]e^(iwk)
+ *
+ * The summation contains the frequency steps we want multiplied by the source
+ * (similar to a DTFT).
+ *
+ * Using symmetry, and just the real part (be careful, this must happen
+ * after any internal complex multiplications), the polyphase filterbank
+ * transfer function is:
+ *
+ * Hpp[n, w, w_0] = sum_{k=0}^{n} x[k] * cos(wk + w_0)
+ * = Re{ e^(iwn + iw_0) y[n]}
+ * = cos(wn+w_0) * s[n] - cos(w(n+1)+w_0) * s[n-1]
+ *
+ * using the fact that s[n] of real x[n] is real.
+ *
+ */
+ double dcos = 2. * cos(L*w);
+ int start = ((halfNumCoef)*L + 1);
+ SineGen cc((start - L) * w, w, true); // cosine
+ SineGen cp(start * w, w, true); // cosine
+ for (int i=1 ; i<=L ; ++i) {
+ double sc = 0;
+ double sp = 0;
+ for (int j=0 ; j<halfNumCoef ; ++j) {
+ double tmp = sc;
+ sc = static_cast<double>(*coef++) + dcos*sc - sp;
+ sp = tmp;
+ }
+ // If we are awfully clever, we can apply Goertzel's algorithm
+ // again on the sc and sp sequences returned here.
+ accum += cc.valueAdvance() * sc - cp.valueAdvance() * sp;
+ }
+#endif
+ return accum*2.;
+}
+
+/*
+ * evaluates the minimum and maximum |H(f)| bound in a band region.
+ *
+ * This is usually done with equally spaced increments in the target band in question.
+ * The passband is often very small, and sampled that way. The stopband is often much
+ * larger.
+ *
+ * We use the fact that the overall polyphase filter has an additional bank at the end
+ * for interpolation; hence it is overspecified for the H(f) computation. Thus the
+ * first polyphase is never actually checked, excepting its first term.
+ *
+ * In this code we use the firTransfer() evaluator above, which uses Goertzel's
+ * algorithm to calculate the transfer function at each point.
+ *
+ * TODO: An alternative with equal spacing is the FFT/DFT. An alternative with unequal
+ * spacing is a chirp transform.
+ *
+ * @param coef is the designed polyphase filter banks
+ *
+ * @param L is the number of phases (for interpolation)
+ *
+ * @param halfNumCoef should be half the number of coefficients for a single
+ * polyphase.
+ *
+ * @param fstart is the normalized frequency start.
+ *
+ * @param fend is the normalized frequency end.
+ *
+ * @param steps is the number of steps to take (sampling) between frequency start and end
+ *
+ * @param firMin returns the minimum transfer |H(f)| found
+ *
+ * @param firMax returns the maximum transfer |H(f)| found
+ *
+ * 0 <= f <= 0.5.
+ * This is used to test passband and stopband performance.
+ */
+template <typename T>
+static void testFir(const T* coef, int L, int halfNumCoef,
+ double fstart, double fend, int steps, double &firMin, double &firMax) {
+ double wstart = fstart*(2.*M_PI);
+ double wend = fend*(2.*M_PI);
+ double wstep = (wend - wstart)/steps;
+ double fmax, fmin;
+ double trf = firTransfer(coef, L, halfNumCoef, wstart);
+ if (trf<0) {
+ trf = -trf;
+ }
+ fmin = fmax = trf;
+ wstart += wstep;
+ for (int i=1; i<steps; ++i) {
+ trf = firTransfer(coef, L, halfNumCoef, wstart);
+ if (trf<0) {
+ trf = -trf;
+ }
+ if (trf>fmax) {
+ fmax = trf;
+ }
+ else if (trf<fmin) {
+ fmin = trf;
+ }
+ wstart += wstep;
+ }
+ // renormalize - this is only needed for integer filter types
+ double norm = 1./((1ULL<<(sizeof(T)*8-1))*L);
+
+ firMin = fmin * norm;
+ firMax = fmax * norm;
+}
+
+/*
+ * evaluates the |H(f)| lowpass band characteristics.
+ *
+ * This function tests the lowpass characteristics for the overall polyphase filter,
+ * and is used to verify the design. For this case, fp should be set to the
+ * passband normalized frequency from 0 to 0.5 for the overall filter (thus it
+ * is the designed polyphase bank value / L). Likewise for fs.
+ *
+ * @param coef is the designed polyphase filter banks
+ *
+ * @param L is the number of phases (for interpolation)
+ *
+ * @param halfNumCoef should be half the number of coefficients for a single
+ * polyphase.
+ *
+ * @param fp is the passband normalized frequency, 0 < fp < fs < 0.5.
+ *
+ * @param fs is the stopband normalized frequency, 0 < fp < fs < 0.5.
+ *
+ * @param passSteps is the number of passband sampling steps.
+ *
+ * @param stopSteps is the number of stopband sampling steps.
+ *
+ * @param passMin is the minimum value in the passband
+ *
+ * @param passMax is the maximum value in the passband (useful for scaling). This should
+ * be less than 1., to avoid sine wave test overflow.
+ *
+ * @param passRipple is the passband ripple. Typically this should be less than 0.1 for
+ * an audio filter. Generally speaker/headphone device characteristics will dominate
+ * the passband term.
+ *
+ * @param stopMax is the maximum value in the stopband.
+ *
+ * @param stopRipple is the stopband ripple, also known as stopband attenuation.
+ * Typically this should be greater than ~80dB for low quality, and greater than
+ * ~100dB for full 16b quality, otherwise aliasing may become noticeable.
+ *
+ */
+template <typename T>
+static void testFir(const T* coef, int L, int halfNumCoef,
+ double fp, double fs, int passSteps, int stopSteps,
+ double &passMin, double &passMax, double &passRipple,
+ double &stopMax, double &stopRipple) {
+ double fmin, fmax;
+ testFir(coef, L, halfNumCoef, 0., fp, passSteps, fmin, fmax);
+ double d1 = (fmax - fmin)/2.;
+ passMin = fmin;
+ passMax = fmax;
+ passRipple = -20.*log10(1. - d1); // passband ripple
+ testFir(coef, L, halfNumCoef, fs, 0.5, stopSteps, fmin, fmax);
+ // fmin is really not important for the stopband.
+ stopMax = fmax;
+ stopRipple = -20.*log10(fmax); // stopband ripple/attenuation
+}
+
+/*
+ * Calculates the overall polyphase filter based on a windowed sinc function.
+ *
+ * The windowed sinc is an odd length symmetric filter of exactly L*halfNumCoef*2+1
+ * taps for the entire kernel. This is then decomposed into L+1 polyphase filterbanks.
+ * The last filterbank is used for interpolation purposes (and is mostly composed
+ * of the first bank shifted by one sample), and is unnecessary if one does
+ * not do interpolation.
+ *
+ * We use the last filterbank for some transfer function calculation purposes,
+ * so it needs to be generated anyways.
+ *
+ * @param coef is the caller allocated space for coefficients. This should be
+ * exactly (L+1)*halfNumCoef in size.
+ *
+ * @param L is the number of phases (for interpolation)
+ *
+ * @param halfNumCoef should be half the number of coefficients for a single
+ * polyphase.
+ *
+ * @param stopBandAtten is the stopband value, should be >50dB.
+ *
+ * @param fcr is cutoff frequency/sampling rate (<0.5). At this point, the energy
+ * should be 6dB less. (fcr is where the amplitude drops by half). Use the
+ * firKaiserTbw() to calculate the transition bandwidth. fcr is the midpoint
+ * between the stop band and the pass band (fstop+fpass)/2.
+ *
+ * @param atten is the attenuation (generally slightly less than 1).
+ */
+
+template <typename T>
+static inline void firKaiserGen(T* coef, int L, int halfNumCoef,
+ double stopBandAtten, double fcr, double atten) {
+ //
+ // Formula 3.2.5, 3.2.7, Vaidyanathan, _Multirate Systems and Filter Banks_, p. 48
+ // Formula 7.75, Oppenheim and Schafer, _Discrete-time Signal Processing, 3e_, p. 542
+ //
+ // See also: http://melodi.ee.washington.edu/courses/ee518/notes/lec17.pdf
+ //
+ // Kaiser window and beta parameter
+ //
+ // | 0.1102*(A - 8.7) A > 50
+ // beta = | 0.5842*(A - 21)^0.4 + 0.07886*(A - 21) 21 <= A <= 50
+ // | 0. A < 21
+ //
+ // with A is the desired stop-band attenuation in dBFS
+ //
+ // 30 dB 2.210
+ // 40 dB 3.384
+ // 50 dB 4.538
+ // 60 dB 5.658
+ // 70 dB 6.764
+ // 80 dB 7.865
+ // 90 dB 8.960
+ // 100 dB 10.056
+
+ const int N = L * halfNumCoef; // non-negative half
+ const double beta = 0.1102 * (stopBandAtten - 8.7); // >= 50dB always
+ const double xstep = (2. * M_PI) * fcr / L;
+ const double xfrac = 1. / N;
+ const double yscale = atten * L / (I0(beta) * M_PI);
+
+ // We use sine generators, which computes sines on regular step intervals.
+ // This speeds up overall computation about 40% from computing the sine directly.
+
+ SineGenGen sgg(0., xstep, L*xstep); // generates sine generators (one per polyphase)
+
+ for (int i=0 ; i<=L ; ++i) { // generate an extra set of coefs for interpolation
+
+ // computation for a single polyphase of the overall filter.
+ SineGen sg = sgg.valueAdvance(); // current sine generator for "j" inner loop.
+ double err = 0; // for noise shaping on int16_t coefficients (over each polyphase)
+
+ for (int j=0, ix=i ; j<halfNumCoef ; ++j, ix+=L) {
+ double y;
+ if (CC_LIKELY(ix)) {
+ double x = static_cast<double>(ix);
+
+ // sine generator: sg.valueAdvance() returns sin(ix*xstep);
+ y = I0(beta * sqrt(1.0 - sqr(x * xfrac))) * yscale * sg.valueAdvance() / x;
+ } else {
+ y = 2. * atten * fcr; // center of filter, sinc(0) = 1.
+ sg.advance();
+ }
+
+ // (caution!) float version does not need rounding
+ if (is_same<T, int16_t>::value) { // int16_t needs noise shaping
+ *coef++ = static_cast<T>(toint(y, 1ULL<<(sizeof(T)*8-1), err));
+ } else {
+ *coef++ = static_cast<T>(toint(y, 1ULL<<(sizeof(T)*8-1)));
+ }
+ }
+ }
+}
+
+}; // namespace android
+
+#endif /*ANDROID_AUDIO_RESAMPLER_FIR_GEN_H*/
diff --git a/services/audioflinger/AudioResamplerFirOps.h b/services/audioflinger/AudioResamplerFirOps.h
new file mode 100644
index 0000000..bf2163f
--- /dev/null
+++ b/services/audioflinger/AudioResamplerFirOps.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2013 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_RESAMPLER_FIR_OPS_H
+#define ANDROID_AUDIO_RESAMPLER_FIR_OPS_H
+
+namespace android {
+
+#if defined(__arm__) && !defined(__thumb__)
+#define USE_INLINE_ASSEMBLY (true)
+#else
+#define USE_INLINE_ASSEMBLY (false)
+#endif
+
+#if USE_INLINE_ASSEMBLY && defined(__ARM_NEON__)
+#define USE_NEON (true)
+#include <arm_neon.h>
+#else
+#define USE_NEON (false)
+#endif
+
+template<typename T, typename U>
+struct is_same
+{
+ static const bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T> // partial specialization
+{
+ static const bool value = true;
+};
+
+static inline
+int32_t mulRL(int left, int32_t in, uint32_t vRL)
+{
+#if USE_INLINE_ASSEMBLY
+ int32_t out;
+ if (left) {
+ asm( "smultb %[out], %[in], %[vRL] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [vRL]"r"(vRL)
+ : );
+ } else {
+ asm( "smultt %[out], %[in], %[vRL] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [vRL]"r"(vRL)
+ : );
+ }
+ return out;
+#else
+ int16_t v = left ? static_cast<int16_t>(vRL) : static_cast<int16_t>(vRL>>16);
+ return static_cast<int32_t>((static_cast<int64_t>(in) * v) >> 16);
+#endif
+}
+
+static inline
+int32_t mulAdd(int16_t in, int16_t v, int32_t a)
+{
+#if USE_INLINE_ASSEMBLY
+ int32_t out;
+ asm( "smlabb %[out], %[v], %[in], %[a] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [v]"r"(v), [a]"r"(a)
+ : );
+ return out;
+#else
+ return a + v * in;
+#endif
+}
+
+static inline
+int32_t mulAdd(int16_t in, int32_t v, int32_t a)
+{
+#if USE_INLINE_ASSEMBLY
+ int32_t out;
+ asm( "smlawb %[out], %[v], %[in], %[a] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [v]"r"(v), [a]"r"(a)
+ : );
+ return out;
+#else
+ return a + static_cast<int32_t>((static_cast<int64_t>(v) * in) >> 16);
+#endif
+}
+
+static inline
+int32_t mulAdd(int32_t in, int32_t v, int32_t a)
+{
+#if USE_INLINE_ASSEMBLY
+ int32_t out;
+ asm( "smmla %[out], %[v], %[in], %[a] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [v]"r"(v), [a]"r"(a)
+ : );
+ return out;
+#else
+ return a + static_cast<int32_t>((static_cast<int64_t>(v) * in) >> 32);
+#endif
+}
+
+static inline
+int32_t mulAddRL(int left, uint32_t inRL, int16_t v, int32_t a)
+{
+#if USE_INLINE_ASSEMBLY
+ int32_t out;
+ if (left) {
+ asm( "smlabb %[out], %[v], %[inRL], %[a] \n"
+ : [out]"=r"(out)
+ : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
+ : );
+ } else {
+ asm( "smlabt %[out], %[v], %[inRL], %[a] \n"
+ : [out]"=r"(out)
+ : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
+ : );
+ }
+ return out;
+#else
+ int16_t s = left ? static_cast<int16_t>(inRL) : static_cast<int16_t>(inRL>>16);
+ return a + v * s;
+#endif
+}
+
+static inline
+int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
+{
+#if USE_INLINE_ASSEMBLY
+ int32_t out;
+ if (left) {
+ asm( "smlawb %[out], %[v], %[inRL], %[a] \n"
+ : [out]"=r"(out)
+ : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
+ : );
+ } else {
+ asm( "smlawt %[out], %[v], %[inRL], %[a] \n"
+ : [out]"=r"(out)
+ : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
+ : );
+ }
+ return out;
+#else
+ int16_t s = left ? static_cast<int16_t>(inRL) : static_cast<int16_t>(inRL>>16);
+ return a + static_cast<int32_t>((static_cast<int64_t>(v) * s) >> 16);
+#endif
+}
+
+}; // namespace android
+
+#endif /*ANDROID_AUDIO_RESAMPLER_FIR_OPS_H*/
diff --git a/services/audioflinger/AudioResamplerFirProcess.h b/services/audioflinger/AudioResamplerFirProcess.h
new file mode 100644
index 0000000..38e387c
--- /dev/null
+++ b/services/audioflinger/AudioResamplerFirProcess.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2013 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_RESAMPLER_FIR_PROCESS_H
+#define ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_H
+
+namespace android {
+
+// depends on AudioResamplerFirOps.h
+
+template<int CHANNELS, typename TC>
+static inline
+void mac(
+ int32_t& l, int32_t& r,
+ const TC coef,
+ const int16_t* samples)
+{
+ if (CHANNELS == 2) {
+ uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
+ l = mulAddRL(1, rl, coef, l);
+ r = mulAddRL(0, rl, coef, r);
+ } else {
+ r = l = mulAdd(samples[0], coef, l);
+ }
+}
+
+template<int CHANNELS, typename TC>
+static inline
+void interpolate(
+ int32_t& l, int32_t& r,
+ const TC coef_0, const TC coef_1,
+ const int16_t lerp, const int16_t* samples)
+{
+ TC sinc;
+
+ if (is_same<TC, int16_t>::value) {
+ sinc = (lerp * ((coef_1-coef_0)<<1)>>16) + coef_0;
+ } else {
+ sinc = mulAdd(lerp, (coef_1-coef_0)<<1, coef_0);
+ }
+ if (CHANNELS == 2) {
+ uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
+ l = mulAddRL(1, rl, sinc, l);
+ r = mulAddRL(0, rl, sinc, r);
+ } else {
+ r = l = mulAdd(samples[0], sinc, l);
+ }
+}
+
+/*
+ * Calculates a single output sample (two stereo frames).
+ *
+ * This function computes both the positive half FIR dot product and
+ * the negative half FIR dot product, accumulates, and then applies the volume.
+ *
+ * This is a locked phase filter (it does not compute the interpolation).
+ *
+ * Use fir() to compute the proper coefficient pointers for a polyphase
+ * filter bank.
+ */
+
+template <int CHANNELS, int STRIDE, typename TC>
+static inline
+void ProcessL(int32_t* const out,
+ int count,
+ const TC* coefsP,
+ const TC* coefsN,
+ const int16_t* sP,
+ const int16_t* sN,
+ const int32_t* const volumeLR)
+{
+ int32_t l = 0;
+ int32_t r = 0;
+ do {
+ mac<CHANNELS>(l, r, *coefsP++, sP);
+ sP -= CHANNELS;
+ mac<CHANNELS>(l, r, *coefsN++, sN);
+ sN += CHANNELS;
+ } while (--count > 0);
+ out[0] += 2 * mulRL(0, l, volumeLR[0]); // Note: only use top 16b
+ out[1] += 2 * mulRL(0, r, volumeLR[1]); // Note: only use top 16b
+}
+
+/*
+ * Calculates a single output sample (two stereo frames) interpolating phase.
+ *
+ * This function computes both the positive half FIR dot product and
+ * the negative half FIR dot product, accumulates, and then applies the volume.
+ *
+ * This is an interpolated phase filter.
+ *
+ * Use fir() to compute the proper coefficient pointers for a polyphase
+ * filter bank.
+ */
+
+template <int CHANNELS, int STRIDE, typename TC>
+static inline
+void Process(int32_t* const out,
+ int count,
+ const TC* coefsP,
+ const TC* coefsN,
+ const TC* coefsP1,
+ const TC* coefsN1,
+ const int16_t* sP,
+ const int16_t* sN,
+ uint32_t lerpP,
+ const int32_t* const volumeLR)
+{
+ (void) coefsP1; // suppress unused parameter warning
+ (void) coefsN1;
+ if (sizeof(*coefsP)==4) {
+ lerpP >>= 16; // ensure lerpP is 16b
+ }
+ int32_t l = 0;
+ int32_t r = 0;
+ for (size_t i = 0; i < count; ++i) {
+ interpolate<CHANNELS>(l, r, coefsP[0], coefsP[count], lerpP, sP);
+ coefsP++;
+ sP -= CHANNELS;
+ interpolate<CHANNELS>(l, r, coefsN[count], coefsN[0], lerpP, sN);
+ coefsN++;
+ sN += CHANNELS;
+ }
+ out[0] += 2 * mulRL(0, l, volumeLR[0]); // Note: only use top 16b
+ out[1] += 2 * mulRL(0, r, volumeLR[1]); // Note: only use top 16b
+}
+
+/*
+ * Calculates a single output sample (two stereo frames) from input sample pointer.
+ *
+ * This sets up the params for the accelerated Process() and ProcessL()
+ * functions to do the appropriate dot products.
+ *
+ * @param out should point to the output buffer with at least enough space for 2 output frames.
+ *
+ * @param phase is the fractional distance between input samples for interpolation:
+ * phase >= 0 && phase < phaseWrapLimit. It can be thought of as a rational fraction
+ * of phase/phaseWrapLimit.
+ *
+ * @param phaseWrapLimit is #polyphases<<coefShift, where #polyphases is the number of polyphases
+ * in the polyphase filter. Likewise, #polyphases can be obtained as (phaseWrapLimit>>coefShift).
+ *
+ * @param coefShift gives the bit alignment of the polyphase index in the phase parameter.
+ *
+ * @param halfNumCoefs is the half the number of coefficients per polyphase filter. Since the
+ * overall filterbank is odd-length symmetric, only halfNumCoefs need be stored.
+ *
+ * @param coefs is the polyphase filter bank, starting at from polyphase index 0, and ranging to
+ * and including the #polyphases. Each polyphase of the filter has half-length halfNumCoefs
+ * (due to symmetry). The total size of the filter bank in coefficients is
+ * (#polyphases+1)*halfNumCoefs.
+ *
+ * The filter bank coefs should be aligned to a minimum of 16 bytes (preferrably to cache line).
+ *
+ * The coefs should be attenuated (to compensate for passband ripple)
+ * if storing back into the native format.
+ *
+ * @param samples are unaligned input samples. The position is in the "middle" of the
+ * sample array with respect to the FIR filter:
+ * the negative half of the filter is dot product from samples+1 to samples+halfNumCoefs;
+ * the positive half of the filter is dot product from samples to samples-halfNumCoefs+1.
+ *
+ * @param volumeLR is a pointer to an array of two 32 bit volume values, one per stereo channel,
+ * expressed as a S32 integer. A negative value inverts the channel 180 degrees.
+ * The pointer volumeLR should be aligned to a minimum of 8 bytes.
+ * A typical value for volume is 0x1000 to align to a unity gain output of 20.12.
+ *
+ * In between calls to filterCoefficient, the phase is incremented by phaseIncrement, where
+ * phaseIncrement is calculated as inputSampling * phaseWrapLimit / outputSampling.
+ *
+ * The filter polyphase index is given by indexP = phase >> coefShift. Due to
+ * odd length symmetric filter, the polyphase index of the negative half depends on
+ * whether interpolation is used.
+ *
+ * The fractional siting between the polyphase indices is given by the bits below coefShift:
+ *
+ * lerpP = phase << 32 - coefShift >> 1; // for 32 bit unsigned phase multiply
+ * lerpP = phase << 32 - coefShift >> 17; // for 16 bit unsigned phase multiply
+ *
+ * For integer types, this is expressed as:
+ *
+ * lerpP = phase << sizeof(phase)*8 - coefShift
+ * >> (sizeof(phase)-sizeof(*coefs))*8 + 1;
+ *
+ */
+
+template<int CHANNELS, bool LOCKED, int STRIDE, typename TC>
+static inline
+void fir(int32_t* const out,
+ const uint32_t phase, const uint32_t phaseWrapLimit,
+ const int coefShift, const int halfNumCoefs, const TC* const coefs,
+ const int16_t* const samples, const int32_t* const volumeLR)
+{
+ // NOTE: be very careful when modifying the code here. register
+ // pressure is very high and a small change might cause the compiler
+ // to generate far less efficient code.
+ // Always sanity check the result with objdump or test-resample.
+
+ if (LOCKED) {
+ // locked polyphase (no interpolation)
+ // Compute the polyphase filter index on the positive and negative side.
+ uint32_t indexP = phase >> coefShift;
+ uint32_t indexN = (phaseWrapLimit - phase) >> coefShift;
+ const TC* coefsP = coefs + indexP*halfNumCoefs;
+ const TC* coefsN = coefs + indexN*halfNumCoefs;
+ const int16_t* sP = samples;
+ const int16_t* sN = samples + CHANNELS;
+
+ // dot product filter.
+ ProcessL<CHANNELS, STRIDE>(out,
+ halfNumCoefs, coefsP, coefsN, sP, sN, volumeLR);
+ } else {
+ // interpolated polyphase
+ // Compute the polyphase filter index on the positive and negative side.
+ uint32_t indexP = phase >> coefShift;
+ uint32_t indexN = (phaseWrapLimit - phase - 1) >> coefShift; // one's complement.
+ const TC* coefsP = coefs + indexP*halfNumCoefs;
+ const TC* coefsN = coefs + indexN*halfNumCoefs;
+ const TC* coefsP1 = coefsP + halfNumCoefs;
+ const TC* coefsN1 = coefsN + halfNumCoefs;
+ const int16_t* sP = samples;
+ const int16_t* sN = samples + CHANNELS;
+
+ // Interpolation fraction lerpP derived by shifting all the way up and down
+ // to clear the appropriate bits and align to the appropriate level
+ // for the integer multiply. The constants should resolve in compile time.
+ //
+ // The interpolated filter coefficient is derived as follows for the pos/neg half:
+ //
+ // interpolated[P] = index[P]*lerpP + index[P+1]*(1-lerpP)
+ // interpolated[N] = index[N+1]*lerpP + index[N]*(1-lerpP)
+ uint32_t lerpP = phase << (sizeof(phase)*8 - coefShift)
+ >> ((sizeof(phase)-sizeof(*coefs))*8 + 1);
+
+ // on-the-fly interpolated dot product filter
+ Process<CHANNELS, STRIDE>(out,
+ halfNumCoefs, coefsP, coefsN, coefsP1, coefsN1, sP, sN, lerpP, volumeLR);
+ }
+}
+
+}; // namespace android
+
+#endif /*ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_H*/
diff --git a/services/audioflinger/AudioResamplerFirProcessNeon.h b/services/audioflinger/AudioResamplerFirProcessNeon.h
new file mode 100644
index 0000000..f311cef
--- /dev/null
+++ b/services/audioflinger/AudioResamplerFirProcessNeon.h
@@ -0,0 +1,1149 @@
+/*
+ * Copyright (C) 2013 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_RESAMPLER_FIR_PROCESS_NEON_H
+#define ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_NEON_H
+
+namespace android {
+
+// depends on AudioResamplerFirOps.h, AudioResamplerFirProcess.h
+
+#if USE_NEON
+//
+// NEON specializations are enabled for Process() and ProcessL()
+//
+// TODO: Stride 16 and Stride 8 can be combined with one pass stride 8 (if necessary)
+// and looping stride 16 (or vice versa). This has some polyphase coef data alignment
+// issues with S16 coefs. Consider this later.
+
+// Macros to save a mono/stereo accumulator sample in q0 (and q4) as stereo out.
+#define ASSEMBLY_ACCUMULATE_MONO \
+ "vld1.s32 {d2}, [%[vLR]:64] \n"/* (1) load volumes */\
+ "vld1.s32 {d3}, %[out] \n"/* (2) unaligned load the output */\
+ "vpadd.s32 d0, d0, d1 \n"/* (1) add all 4 partial sums */\
+ "vpadd.s32 d0, d0, d0 \n"/* (1+4d) and replicate L/R */\
+ "vqrdmulh.s32 d0, d0, d2 \n"/* (2+3d) apply volume */\
+ "vqadd.s32 d3, d3, d0 \n"/* (1+4d) accumulate result (saturating) */\
+ "vst1.s32 {d3}, %[out] \n"/* (2+2d) store result */
+
+#define ASSEMBLY_ACCUMULATE_STEREO \
+ "vld1.s32 {d2}, [%[vLR]:64] \n"/* (1) load volumes*/\
+ "vld1.s32 {d3}, %[out] \n"/* (2) unaligned load the output*/\
+ "vpadd.s32 d0, d0, d1 \n"/* (1) add all 4 partial sums from q0*/\
+ "vpadd.s32 d8, d8, d9 \n"/* (1) add all 4 partial sums from q4*/\
+ "vpadd.s32 d0, d0, d8 \n"/* (1+4d) combine into L/R*/\
+ "vqrdmulh.s32 d0, d0, d2 \n"/* (2+3d) apply volume*/\
+ "vqadd.s32 d3, d3, d0 \n"/* (1+4d) accumulate result (saturating)*/\
+ "vst1.s32 {d3}, %[out] \n"/* (2+2d)store result*/
+
+template <>
+inline void ProcessL<1, 16>(int32_t* const out,
+ int count,
+ const int16_t* coefsP,
+ const int16_t* coefsN,
+ const int16_t* sP,
+ const int16_t* sN,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 1; // template specialization does not preserve params
+ const int STRIDE = 16;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "veor q0, q0, q0 \n"// (0 - combines+) accumulator = 0
+
+ "1: \n"
+
+ "vld1.16 {q2}, [%[sP]] \n"// (2+0d) load 8 16-bits mono samples
+ "vld1.16 {q3}, [%[sN]]! \n"// (2) load 8 16-bits mono samples
+ "vld1.16 {q8}, [%[coefsP0]:128]! \n"// (1) load 8 16-bits coefs
+ "vld1.16 {q10}, [%[coefsN0]:128]! \n"// (1) load 8 16-bits coefs
+
+ "vrev64.16 q2, q2 \n"// (1) reverse s3, s2, s1, s0, s7, s6, s5, s4
+
+ // reordering the vmal to do d6, d7 before d4, d5 is slower(?)
+ "vmlal.s16 q0, d4, d17 \n"// (1+0d) multiply (reversed)samples by coef
+ "vmlal.s16 q0, d5, d16 \n"// (1) multiply (reversed)samples by coef
+ "vmlal.s16 q0, d6, d20 \n"// (1) multiply neg samples
+ "vmlal.s16 q0, d7, d21 \n"// (1) multiply neg samples
+
+ // moving these ARM instructions before neon above seems to be slower
+ "subs %[count], %[count], #8 \n"// (1) update loop counter
+ "sub %[sP], %[sP], #16 \n"// (0) move pointer to next set of samples
+
+ // sP used after branch (warning)
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_MONO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q8", "q10"
+ );
+}
+
+template <>
+inline void ProcessL<2, 16>(int32_t* const out,
+ int count,
+ const int16_t* coefsP,
+ const int16_t* coefsN,
+ const int16_t* sP,
+ const int16_t* sN,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 2; // template specialization does not preserve params
+ const int STRIDE = 16;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "veor q0, q0, q0 \n"// (1) acc_L = 0
+ "veor q4, q4, q4 \n"// (0 combines+) acc_R = 0
+
+ "1: \n"
+
+ "vld2.16 {q2, q3}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo samples
+ "vld2.16 {q5, q6}, [%[sN]]! \n"// (3) load 8 16-bits stereo samples
+ "vld1.16 {q8}, [%[coefsP0]:128]! \n"// (1) load 8 16-bits coefs
+ "vld1.16 {q10}, [%[coefsN0]:128]! \n"// (1) load 8 16-bits coefs
+
+ "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive
+ "vrev64.16 q3, q3 \n"// (0 combines+) reverse right positive
+
+ "vmlal.s16 q0, d4, d17 \n"// (1) multiply (reversed) samples left
+ "vmlal.s16 q0, d5, d16 \n"// (1) multiply (reversed) samples left
+ "vmlal.s16 q4, d6, d17 \n"// (1) multiply (reversed) samples right
+ "vmlal.s16 q4, d7, d16 \n"// (1) multiply (reversed) samples right
+ "vmlal.s16 q0, d10, d20 \n"// (1) multiply samples left
+ "vmlal.s16 q0, d11, d21 \n"// (1) multiply samples left
+ "vmlal.s16 q4, d12, d20 \n"// (1) multiply samples right
+ "vmlal.s16 q4, d13, d21 \n"// (1) multiply samples right
+
+ // moving these ARM before neon seems to be slower
+ "subs %[count], %[count], #8 \n"// (1) update loop counter
+ "sub %[sP], %[sP], #32 \n"// (0) move pointer to next set of samples
+
+ // sP used after branch (warning)
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_STEREO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q4", "q5", "q6",
+ "q8", "q10"
+ );
+}
+
+template <>
+inline void Process<1, 16>(int32_t* const out,
+ int count,
+ const int16_t* coefsP,
+ const int16_t* coefsN,
+ const int16_t* coefsP1,
+ const int16_t* coefsN1,
+ const int16_t* sP,
+ const int16_t* sN,
+ uint32_t lerpP,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 1; // template specialization does not preserve params
+ const int STRIDE = 16;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n"// load the positive phase S32 Q15
+ "veor q0, q0, q0 \n"// (0 - combines+) accumulator = 0
+
+ "1: \n"
+
+ "vld1.16 {q2}, [%[sP]] \n"// (2+0d) load 8 16-bits mono samples
+ "vld1.16 {q3}, [%[sN]]! \n"// (2) load 8 16-bits mono samples
+ "vld1.16 {q8}, [%[coefsP0]:128]! \n"// (1) load 8 16-bits coefs
+ "vld1.16 {q9}, [%[coefsP1]:128]! \n"// (1) load 8 16-bits coefs for interpolation
+ "vld1.16 {q10}, [%[coefsN1]:128]! \n"// (1) load 8 16-bits coefs
+ "vld1.16 {q11}, [%[coefsN0]:128]! \n"// (1) load 8 16-bits coefs for interpolation
+
+ "vsub.s16 q9, q9, q8 \n"// (1) interpolate (step1) 1st set of coefs
+ "vsub.s16 q11, q11, q10 \n"// (1) interpolate (step1) 2nd set of coets
+
+ "vqrdmulh.s16 q9, q9, d2[0] \n"// (2) interpolate (step2) 1st set of coefs
+ "vqrdmulh.s16 q11, q11, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs
+
+ "vrev64.16 q2, q2 \n"// (1) reverse s3, s2, s1, s0, s7, s6, s5, s4
+
+ "vadd.s16 q8, q8, q9 \n"// (1+2d) interpolate (step3) 1st set
+ "vadd.s16 q10, q10, q11 \n"// (1+1d) interpolate (step3) 2nd set
+
+ // reordering the vmal to do d6, d7 before d4, d5 is slower(?)
+ "vmlal.s16 q0, d4, d17 \n"// (1+0d) multiply reversed samples by coef
+ "vmlal.s16 q0, d5, d16 \n"// (1) multiply reversed samples by coef
+ "vmlal.s16 q0, d6, d20 \n"// (1) multiply neg samples
+ "vmlal.s16 q0, d7, d21 \n"// (1) multiply neg samples
+
+ // moving these ARM instructions before neon above seems to be slower
+ "subs %[count], %[count], #8 \n"// (1) update loop counter
+ "sub %[sP], %[sP], #16 \n"// (0) move pointer to next set of samples
+
+ // sP used after branch (warning)
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_MONO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q8", "q9", "q10", "q11"
+ );
+}
+
+template <>
+inline void Process<2, 16>(int32_t* const out,
+ int count,
+ const int16_t* coefsP,
+ const int16_t* coefsN,
+ const int16_t* coefsP1,
+ const int16_t* coefsN1,
+ const int16_t* sP,
+ const int16_t* sN,
+ uint32_t lerpP,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 2; // template specialization does not preserve params
+ const int STRIDE = 16;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n"// load the positive phase
+ "veor q0, q0, q0 \n"// (1) acc_L = 0
+ "veor q4, q4, q4 \n"// (0 combines+) acc_R = 0
+
+ "1: \n"
+
+ "vld2.16 {q2, q3}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo samples
+ "vld2.16 {q5, q6}, [%[sN]]! \n"// (3) load 8 16-bits stereo samples
+ "vld1.16 {q8}, [%[coefsP0]:128]! \n"// (1) load 8 16-bits coefs
+ "vld1.16 {q9}, [%[coefsP1]:128]! \n"// (1) load 8 16-bits coefs for interpolation
+ "vld1.16 {q10}, [%[coefsN1]:128]! \n"// (1) load 8 16-bits coefs
+ "vld1.16 {q11}, [%[coefsN0]:128]! \n"// (1) load 8 16-bits coefs for interpolation
+
+ "vsub.s16 q9, q9, q8 \n"// (1) interpolate (step1) 1st set of coefs
+ "vsub.s16 q11, q11, q10 \n"// (1) interpolate (step1) 2nd set of coets
+
+ "vqrdmulh.s16 q9, q9, d2[0] \n"// (2) interpolate (step2) 1st set of coefs
+ "vqrdmulh.s16 q11, q11, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs
+
+ "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive
+ "vrev64.16 q3, q3 \n"// (1) reverse 8 frames of the right positive
+
+ "vadd.s16 q8, q8, q9 \n"// (1+1d) interpolate (step3) 1st set
+ "vadd.s16 q10, q10, q11 \n"// (1+1d) interpolate (step3) 2nd set
+
+ "vmlal.s16 q0, d4, d17 \n"// (1) multiply reversed samples left
+ "vmlal.s16 q0, d5, d16 \n"// (1) multiply reversed samples left
+ "vmlal.s16 q4, d6, d17 \n"// (1) multiply reversed samples right
+ "vmlal.s16 q4, d7, d16 \n"// (1) multiply reversed samples right
+ "vmlal.s16 q0, d10, d20 \n"// (1) multiply samples left
+ "vmlal.s16 q0, d11, d21 \n"// (1) multiply samples left
+ "vmlal.s16 q4, d12, d20 \n"// (1) multiply samples right
+ "vmlal.s16 q4, d13, d21 \n"// (1) multiply samples right
+
+ // moving these ARM before neon seems to be slower
+ "subs %[count], %[count], #8 \n"// (1) update loop counter
+ "sub %[sP], %[sP], #32 \n"// (0) move pointer to next set of samples
+
+ // sP used after branch (warning)
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_STEREO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q4", "q5", "q6",
+ "q8", "q9", "q10", "q11"
+ );
+}
+
+template <>
+inline void ProcessL<1, 16>(int32_t* const out,
+ int count,
+ const int32_t* coefsP,
+ const int32_t* coefsN,
+ const int16_t* sP,
+ const int16_t* sN,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 1; // template specialization does not preserve params
+ const int STRIDE = 16;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "veor q0, q0, q0 \n"// result, initialize to 0
+
+ "1: \n"
+
+ "vld1.16 {q2}, [%[sP]] \n"// load 8 16-bits mono samples
+ "vld1.16 {q3}, [%[sN]]! \n"// load 8 16-bits mono samples
+ "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 8 32-bits coefs
+ "vld1.32 {q10, q11}, [%[coefsN0]:128]! \n"// load 8 32-bits coefs
+
+ "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side
+
+ "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits
+
+ "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits
+
+ "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef
+
+ "vadd.s32 q0, q0, q12 \n"// accumulate result
+ "vadd.s32 q13, q13, q14 \n"// accumulate result
+ "vadd.s32 q0, q0, q15 \n"// accumulate result
+ "vadd.s32 q0, q0, q13 \n"// accumulate result
+
+ "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples
+ "subs %[count], %[count], #8 \n"// update loop counter
+
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_MONO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q8", "q9", "q10", "q11",
+ "q12", "q13", "q14", "q15"
+ );
+}
+
+template <>
+inline void ProcessL<2, 16>(int32_t* const out,
+ int count,
+ const int32_t* coefsP,
+ const int32_t* coefsN,
+ const int16_t* sP,
+ const int16_t* sN,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 2; // template specialization does not preserve params
+ const int STRIDE = 16;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "veor q0, q0, q0 \n"// result, initialize to 0
+ "veor q4, q4, q4 \n"// result, initialize to 0
+
+ "1: \n"
+
+ "vld2.16 {q2, q3}, [%[sP]] \n"// load 4 16-bits stereo samples
+ "vld2.16 {q5, q6}, [%[sN]]! \n"// load 4 16-bits stereo samples
+ "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs
+ "vld1.32 {q10, q11}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs
+
+ "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side
+ "vrev64.16 q3, q3 \n"// reverse 8 frames of the positive side
+
+ "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits
+
+ "vshll.s16 q14, d10, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q15, d11, #15 \n"// extend samples to 31 bits
+
+ "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef
+
+ "vadd.s32 q0, q0, q12 \n"// accumulate result
+ "vadd.s32 q13, q13, q14 \n"// accumulate result
+ "vadd.s32 q0, q0, q15 \n"// (+1) accumulate result
+ "vadd.s32 q0, q0, q13 \n"// (+1) accumulate result
+
+ "vshll.s16 q12, d6, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q13, d7, #15 \n"// extend samples to 31 bits
+
+ "vshll.s16 q14, d12, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q15, d13, #15 \n"// extend samples to 31 bits
+
+ "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef
+
+ "vadd.s32 q4, q4, q12 \n"// accumulate result
+ "vadd.s32 q13, q13, q14 \n"// accumulate result
+ "vadd.s32 q4, q4, q15 \n"// (+1) accumulate result
+ "vadd.s32 q4, q4, q13 \n"// (+1) accumulate result
+
+ "subs %[count], %[count], #8 \n"// update loop counter
+ "sub %[sP], %[sP], #32 \n"// move pointer to next set of samples
+
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_STEREO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q4", "q5", "q6",
+ "q8", "q9", "q10", "q11",
+ "q12", "q13", "q14", "q15"
+ );
+}
+
+template <>
+inline void Process<1, 16>(int32_t* const out,
+ int count,
+ const int32_t* coefsP,
+ const int32_t* coefsN,
+ const int32_t* coefsP1,
+ const int32_t* coefsN1,
+ const int16_t* sP,
+ const int16_t* sN,
+ uint32_t lerpP,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 1; // template specialization does not preserve params
+ const int STRIDE = 16;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n"// load the positive phase
+ "veor q0, q0, q0 \n"// result, initialize to 0
+
+ "1: \n"
+
+ "vld1.16 {q2}, [%[sP]] \n"// load 8 16-bits mono samples
+ "vld1.16 {q3}, [%[sN]]! \n"// load 8 16-bits mono samples
+ "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 8 32-bits coefs
+ "vld1.32 {q12, q13}, [%[coefsP1]:128]! \n"// load 8 32-bits coefs
+ "vld1.32 {q10, q11}, [%[coefsN1]:128]! \n"// load 8 32-bits coefs
+ "vld1.32 {q14, q15}, [%[coefsN0]:128]! \n"// load 8 32-bits coefs
+
+ "vsub.s32 q12, q12, q8 \n"// interpolate (step1)
+ "vsub.s32 q13, q13, q9 \n"// interpolate (step1)
+ "vsub.s32 q14, q14, q10 \n"// interpolate (step1)
+ "vsub.s32 q15, q15, q11 \n"// interpolate (step1)
+
+ "vqrdmulh.s32 q12, q12, d2[0] \n"// interpolate (step2)
+ "vqrdmulh.s32 q13, q13, d2[0] \n"// interpolate (step2)
+ "vqrdmulh.s32 q14, q14, d2[0] \n"// interpolate (step2)
+ "vqrdmulh.s32 q15, q15, d2[0] \n"// interpolate (step2)
+
+ "vadd.s32 q8, q8, q12 \n"// interpolate (step3)
+ "vadd.s32 q9, q9, q13 \n"// interpolate (step3)
+ "vadd.s32 q10, q10, q14 \n"// interpolate (step3)
+ "vadd.s32 q11, q11, q15 \n"// interpolate (step3)
+
+ "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side
+
+ "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits
+
+ "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits
+
+ "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef
+
+ "vadd.s32 q0, q0, q12 \n"// accumulate result
+ "vadd.s32 q13, q13, q14 \n"// accumulate result
+ "vadd.s32 q0, q0, q15 \n"// accumulate result
+ "vadd.s32 q0, q0, q13 \n"// accumulate result
+
+ "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples
+ "subs %[count], %[count], #8 \n"// update loop counter
+
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_MONO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q8", "q9", "q10", "q11",
+ "q12", "q13", "q14", "q15"
+ );
+}
+
+template <>
+inline void Process<2, 16>(int32_t* const out,
+ int count,
+ const int32_t* coefsP,
+ const int32_t* coefsN,
+ const int32_t* coefsP1,
+ const int32_t* coefsN1,
+ const int16_t* sP,
+ const int16_t* sN,
+ uint32_t lerpP,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 2; // template specialization does not preserve params
+ const int STRIDE = 16;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n"// load the positive phase
+ "veor q0, q0, q0 \n"// result, initialize to 0
+ "veor q4, q4, q4 \n"// result, initialize to 0
+
+ "1: \n"
+
+ "vld2.16 {q2, q3}, [%[sP]] \n"// load 4 16-bits stereo samples
+ "vld2.16 {q5, q6}, [%[sN]]! \n"// load 4 16-bits stereo samples
+ "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 8 32-bits coefs
+ "vld1.32 {q12, q13}, [%[coefsP1]:128]! \n"// load 8 32-bits coefs
+ "vld1.32 {q10, q11}, [%[coefsN1]:128]! \n"// load 8 32-bits coefs
+ "vld1.32 {q14, q15}, [%[coefsN0]:128]! \n"// load 8 32-bits coefs
+
+ "vsub.s32 q12, q12, q8 \n"// interpolate (step1)
+ "vsub.s32 q13, q13, q9 \n"// interpolate (step1)
+ "vsub.s32 q14, q14, q10 \n"// interpolate (step1)
+ "vsub.s32 q15, q15, q11 \n"// interpolate (step1)
+
+ "vqrdmulh.s32 q12, q12, d2[0] \n"// interpolate (step2)
+ "vqrdmulh.s32 q13, q13, d2[0] \n"// interpolate (step2)
+ "vqrdmulh.s32 q14, q14, d2[0] \n"// interpolate (step2)
+ "vqrdmulh.s32 q15, q15, d2[0] \n"// interpolate (step2)
+
+ "vadd.s32 q8, q8, q12 \n"// interpolate (step3)
+ "vadd.s32 q9, q9, q13 \n"// interpolate (step3)
+ "vadd.s32 q10, q10, q14 \n"// interpolate (step3)
+ "vadd.s32 q11, q11, q15 \n"// interpolate (step3)
+
+ "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side
+ "vrev64.16 q3, q3 \n"// reverse 8 frames of the positive side
+
+ "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits
+
+ "vshll.s16 q14, d10, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q15, d11, #15 \n"// extend samples to 31 bits
+
+ "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef
+
+ "vadd.s32 q0, q0, q12 \n"// accumulate result
+ "vadd.s32 q13, q13, q14 \n"// accumulate result
+ "vadd.s32 q0, q0, q15 \n"// (+1) accumulate result
+ "vadd.s32 q0, q0, q13 \n"// (+1) accumulate result
+
+ "vshll.s16 q12, d6, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q13, d7, #15 \n"// extend samples to 31 bits
+
+ "vshll.s16 q14, d12, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q15, d13, #15 \n"// extend samples to 31 bits
+
+ "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef
+
+ "vadd.s32 q4, q4, q12 \n"// accumulate result
+ "vadd.s32 q13, q13, q14 \n"// accumulate result
+ "vadd.s32 q4, q4, q15 \n"// (+1) accumulate result
+ "vadd.s32 q4, q4, q13 \n"// (+1) accumulate result
+
+ "subs %[count], %[count], #8 \n"// update loop counter
+ "sub %[sP], %[sP], #32 \n"// move pointer to next set of samples
+
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_STEREO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q4", "q5", "q6",
+ "q8", "q9", "q10", "q11",
+ "q12", "q13", "q14", "q15"
+ );
+}
+
+template <>
+inline void ProcessL<1, 8>(int32_t* const out,
+ int count,
+ const int16_t* coefsP,
+ const int16_t* coefsN,
+ const int16_t* sP,
+ const int16_t* sN,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 1; // template specialization does not preserve params
+ const int STRIDE = 8;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "veor q0, q0, q0 \n"// (0 - combines+) accumulator = 0
+
+ "1: \n"
+
+ "vld1.16 {d4}, [%[sP]] \n"// (2+0d) load 4 16-bits mono samples
+ "vld1.16 {d6}, [%[sN]]! \n"// (2) load 4 16-bits mono samples
+ "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 4 16-bits coefs
+ "vld1.16 {d20}, [%[coefsN0]:64]! \n"// (1) load 4 16-bits coefs
+
+ "vrev64.16 d4, d4 \n"// (1) reversed s3, s2, s1, s0, s7, s6, s5, s4
+
+ // reordering the vmal to do d6, d7 before d4, d5 is slower(?)
+ "vmlal.s16 q0, d4, d16 \n"// (1) multiply (reversed)samples by coef
+ "vmlal.s16 q0, d6, d20 \n"// (1) multiply neg samples
+
+ // moving these ARM instructions before neon above seems to be slower
+ "subs %[count], %[count], #4 \n"// (1) update loop counter
+ "sub %[sP], %[sP], #8 \n"// (0) move pointer to next set of samples
+
+ // sP used after branch (warning)
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_MONO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q8", "q10"
+ );
+}
+
+template <>
+inline void ProcessL<2, 8>(int32_t* const out,
+ int count,
+ const int16_t* coefsP,
+ const int16_t* coefsN,
+ const int16_t* sP,
+ const int16_t* sN,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 2; // template specialization does not preserve params
+ const int STRIDE = 8;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "veor q0, q0, q0 \n"// (1) acc_L = 0
+ "veor q4, q4, q4 \n"// (0 combines+) acc_R = 0
+
+ "1: \n"
+
+ "vld2.16 {d4, d5}, [%[sP]] \n"// (2+0d) load 8 16-bits stereo samples
+ "vld2.16 {d6, d7}, [%[sN]]! \n"// (2) load 8 16-bits stereo samples
+ "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 8 16-bits coefs
+ "vld1.16 {d20}, [%[coefsN0]:64]! \n"// (1) load 8 16-bits coefs
+
+ "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive
+
+ "vmlal.s16 q0, d4, d16 \n"// (1) multiply (reversed) samples left
+ "vmlal.s16 q4, d5, d16 \n"// (1) multiply (reversed) samples right
+ "vmlal.s16 q0, d6, d20 \n"// (1) multiply samples left
+ "vmlal.s16 q4, d7, d20 \n"// (1) multiply samples right
+
+ // moving these ARM before neon seems to be slower
+ "subs %[count], %[count], #4 \n"// (1) update loop counter
+ "sub %[sP], %[sP], #16 \n"// (0) move pointer to next set of samples
+
+ // sP used after branch (warning)
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_STEREO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q4", "q5", "q6",
+ "q8", "q10"
+ );
+}
+
+template <>
+inline void Process<1, 8>(int32_t* const out,
+ int count,
+ const int16_t* coefsP,
+ const int16_t* coefsN,
+ const int16_t* coefsP1,
+ const int16_t* coefsN1,
+ const int16_t* sP,
+ const int16_t* sN,
+ uint32_t lerpP,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 1; // template specialization does not preserve params
+ const int STRIDE = 8;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n"// load the positive phase S32 Q15
+ "veor q0, q0, q0 \n"// (0 - combines+) accumulator = 0
+
+ "1: \n"
+
+ "vld1.16 {d4}, [%[sP]] \n"// (2+0d) load 4 16-bits mono samples
+ "vld1.16 {d6}, [%[sN]]! \n"// (2) load 4 16-bits mono samples
+ "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 4 16-bits coefs
+ "vld1.16 {d17}, [%[coefsP1]:64]! \n"// (1) load 4 16-bits coefs for interpolation
+ "vld1.16 {d20}, [%[coefsN1]:64]! \n"// (1) load 4 16-bits coefs
+ "vld1.16 {d21}, [%[coefsN0]:64]! \n"// (1) load 4 16-bits coefs for interpolation
+
+ "vsub.s16 d17, d17, d16 \n"// (1) interpolate (step1) 1st set of coefs
+ "vsub.s16 d21, d21, d20 \n"// (1) interpolate (step1) 2nd set of coets
+
+ "vqrdmulh.s16 d17, d17, d2[0] \n"// (2) interpolate (step2) 1st set of coefs
+ "vqrdmulh.s16 d21, d21, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs
+
+ "vrev64.16 d4, d4 \n"// (1) reverse s3, s2, s1, s0, s7, s6, s5, s4
+
+ "vadd.s16 d16, d16, d17 \n"// (1+2d) interpolate (step3) 1st set
+ "vadd.s16 d20, d20, d21 \n"// (1+1d) interpolate (step3) 2nd set
+
+ // reordering the vmal to do d6, d7 before d4, d5 is slower(?)
+ "vmlal.s16 q0, d4, d16 \n"// (1+0d) multiply (reversed)by coef
+ "vmlal.s16 q0, d6, d20 \n"// (1) multiply neg samples
+
+ // moving these ARM instructions before neon above seems to be slower
+ "subs %[count], %[count], #4 \n"// (1) update loop counter
+ "sub %[sP], %[sP], #8 \n"// move pointer to next set of samples
+
+ // sP used after branch (warning)
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_MONO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q8", "q9", "q10", "q11"
+ );
+}
+
+template <>
+inline void Process<2, 8>(int32_t* const out,
+ int count,
+ const int16_t* coefsP,
+ const int16_t* coefsN,
+ const int16_t* coefsP1,
+ const int16_t* coefsN1,
+ const int16_t* sP,
+ const int16_t* sN,
+ uint32_t lerpP,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 2; // template specialization does not preserve params
+ const int STRIDE = 8;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n"// load the positive phase
+ "veor q0, q0, q0 \n"// (1) acc_L = 0
+ "veor q4, q4, q4 \n"// (0 combines+) acc_R = 0
+
+ "1: \n"
+
+ "vld2.16 {d4, d5}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo samples
+ "vld2.16 {d6, d7}, [%[sN]]! \n"// (3) load 8 16-bits stereo samples
+ "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 8 16-bits coefs
+ "vld1.16 {d17}, [%[coefsP1]:64]! \n"// (1) load 8 16-bits coefs for interpolation
+ "vld1.16 {d20}, [%[coefsN1]:64]! \n"// (1) load 8 16-bits coefs
+ "vld1.16 {d21}, [%[coefsN0]:64]! \n"// (1) load 8 16-bits coefs for interpolation
+
+ "vsub.s16 d17, d17, d16 \n"// (1) interpolate (step1) 1st set of coefs
+ "vsub.s16 d21, d21, d20 \n"// (1) interpolate (step1) 2nd set of coets
+
+ "vqrdmulh.s16 d17, d17, d2[0] \n"// (2) interpolate (step2) 1st set of coefs
+ "vqrdmulh.s16 d21, d21, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs
+
+ "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive
+
+ "vadd.s16 d16, d16, d17 \n"// (1+1d) interpolate (step3) 1st set
+ "vadd.s16 d20, d20, d21 \n"// (1+1d) interpolate (step3) 2nd set
+
+ "vmlal.s16 q0, d4, d16 \n"// (1) multiply (reversed) samples left
+ "vmlal.s16 q4, d5, d16 \n"// (1) multiply (reversed) samples right
+ "vmlal.s16 q0, d6, d20 \n"// (1) multiply samples left
+ "vmlal.s16 q4, d7, d20 \n"// (1) multiply samples right
+
+ // moving these ARM before neon seems to be slower
+ "subs %[count], %[count], #4 \n"// (1) update loop counter
+ "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples
+
+ // sP used after branch (warning)
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_STEREO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q4", "q5", "q6",
+ "q8", "q9", "q10", "q11"
+ );
+}
+
+template <>
+inline void ProcessL<1, 8>(int32_t* const out,
+ int count,
+ const int32_t* coefsP,
+ const int32_t* coefsN,
+ const int16_t* sP,
+ const int16_t* sN,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 1; // template specialization does not preserve params
+ const int STRIDE = 8;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "veor q0, q0, q0 \n"// result, initialize to 0
+
+ "1: \n"
+
+ "vld1.16 {d4}, [%[sP]] \n"// load 4 16-bits mono samples
+ "vld1.16 {d6}, [%[sN]]! \n"// load 4 16-bits mono samples
+ "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs
+ "vld1.32 {q10}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs
+
+ "vrev64.16 d4, d4 \n"// reverse 2 frames of the positive side
+
+ "vshll.s16 q12, d4, #15 \n"// (stall) extend samples to 31 bits
+ "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits
+
+ "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef
+
+ "vadd.s32 q0, q0, q12 \n"// accumulate result
+ "vadd.s32 q0, q0, q14 \n"// (stall) accumulate result
+
+ "subs %[count], %[count], #4 \n"// update loop counter
+ "sub %[sP], %[sP], #8 \n"// move pointer to next set of samples
+
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_MONO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q8", "q9", "q10", "q11",
+ "q12", "q14"
+ );
+}
+
+template <>
+inline void ProcessL<2, 8>(int32_t* const out,
+ int count,
+ const int32_t* coefsP,
+ const int32_t* coefsN,
+ const int16_t* sP,
+ const int16_t* sN,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 2; // template specialization does not preserve params
+ const int STRIDE = 8;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "veor q0, q0, q0 \n"// result, initialize to 0
+ "veor q4, q4, q4 \n"// result, initialize to 0
+
+ "1: \n"
+
+ "vld2.16 {d4, d5}, [%[sP]] \n"// load 4 16-bits stereo samples
+ "vld2.16 {d6, d7}, [%[sN]]! \n"// load 4 16-bits stereo samples
+ "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs
+ "vld1.32 {q10}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs
+
+ "vrev64.16 q2, q2 \n"// reverse 2 frames of the positive side
+
+ "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits
+
+ "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits
+
+ "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by coef
+ "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by coef
+ "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by coef
+ "vqrdmulh.s32 q15, q15, q10 \n"// multiply samples by coef
+
+ "vadd.s32 q0, q0, q12 \n"// accumulate result
+ "vadd.s32 q4, q4, q13 \n"// accumulate result
+ "vadd.s32 q0, q0, q14 \n"// accumulate result
+ "vadd.s32 q4, q4, q15 \n"// accumulate result
+
+ "subs %[count], %[count], #4 \n"// update loop counter
+ "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples
+
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_STEREO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsN0] "+r" (coefsN),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3", "q4",
+ "q8", "q9", "q10", "q11",
+ "q12", "q13", "q14", "q15"
+ );
+}
+
+template <>
+inline void Process<1, 8>(int32_t* const out,
+ int count,
+ const int32_t* coefsP,
+ const int32_t* coefsN,
+ const int32_t* coefsP1,
+ const int32_t* coefsN1,
+ const int16_t* sP,
+ const int16_t* sN,
+ uint32_t lerpP,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 1; // template specialization does not preserve params
+ const int STRIDE = 8;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n"// load the positive phase
+ "veor q0, q0, q0 \n"// result, initialize to 0
+
+ "1: \n"
+
+ "vld1.16 {d4}, [%[sP]] \n"// load 4 16-bits mono samples
+ "vld1.16 {d6}, [%[sN]]! \n"// load 4 16-bits mono samples
+ "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs
+ "vld1.32 {q9}, [%[coefsP1]:128]! \n"// load 4 32-bits coefs for interpolation
+ "vld1.32 {q10}, [%[coefsN1]:128]! \n"// load 4 32-bits coefs
+ "vld1.32 {q11}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs for interpolation
+
+ "vrev64.16 d4, d4 \n"// reverse 2 frames of the positive side
+
+ "vsub.s32 q9, q9, q8 \n"// interpolate (step1) 1st set of coefs
+ "vsub.s32 q11, q11, q10 \n"// interpolate (step1) 2nd set of coets
+ "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits
+
+ "vqrdmulh.s32 q9, q9, d2[0] \n"// interpolate (step2) 1st set of coefs
+ "vqrdmulh.s32 q11, q11, d2[0] \n"// interpolate (step2) 2nd set of coefs
+ "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits
+
+ "vadd.s32 q8, q8, q9 \n"// interpolate (step3) 1st set
+ "vadd.s32 q10, q10, q11 \n"// interpolate (step4) 2nd set
+
+ "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef
+
+ "vadd.s32 q0, q0, q12 \n"// accumulate result
+ "vadd.s32 q0, q0, q14 \n"// accumulate result
+
+ "subs %[count], %[count], #4 \n"// update loop counter
+ "sub %[sP], %[sP], #8 \n"// move pointer to next set of samples
+
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_MONO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN0] "+r" (coefsN),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q8", "q9", "q10", "q11",
+ "q12", "q14"
+ );
+}
+
+template <>
+inline
+void Process<2, 8>(int32_t* const out,
+ int count,
+ const int32_t* coefsP,
+ const int32_t* coefsN,
+ const int32_t* coefsP1,
+ const int32_t* coefsN1,
+ const int16_t* sP,
+ const int16_t* sN,
+ uint32_t lerpP,
+ const int32_t* const volumeLR)
+{
+ const int CHANNELS = 2; // template specialization does not preserve params
+ const int STRIDE = 8;
+ sP -= CHANNELS*((STRIDE>>1)-1);
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n"// load the positive phase
+ "veor q0, q0, q0 \n"// result, initialize to 0
+ "veor q4, q4, q4 \n"// result, initialize to 0
+
+ "1: \n"
+ "vld2.16 {d4, d5}, [%[sP]] \n"// load 4 16-bits stereo samples
+ "vld2.16 {d6, d7}, [%[sN]]! \n"// load 4 16-bits stereo samples
+ "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs
+ "vld1.32 {q9}, [%[coefsP1]:128]! \n"// load 4 32-bits coefs for interpolation
+ "vld1.32 {q10}, [%[coefsN1]:128]! \n"// load 4 32-bits coefs
+ "vld1.32 {q11}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs for interpolation
+
+ "vrev64.16 q2, q2 \n"// (reversed) 2 frames of the positive side
+
+ "vsub.s32 q9, q9, q8 \n"// interpolate (step1) 1st set of coefs
+ "vsub.s32 q11, q11, q10 \n"// interpolate (step1) 2nd set of coets
+ "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits
+
+ "vqrdmulh.s32 q9, q9, d2[0] \n"// interpolate (step2) 1st set of coefs
+ "vqrdmulh.s32 q11, q11, d2[1] \n"// interpolate (step3) 2nd set of coefs
+ "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits
+ "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits
+
+ "vadd.s32 q8, q8, q9 \n"// interpolate (step3) 1st set
+ "vadd.s32 q10, q10, q11 \n"// interpolate (step4) 2nd set
+
+ "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef
+ "vqrdmulh.s32 q15, q15, q10 \n"// multiply samples by interpolated coef
+
+ "vadd.s32 q0, q0, q12 \n"// accumulate result
+ "vadd.s32 q4, q4, q13 \n"// accumulate result
+ "vadd.s32 q0, q0, q14 \n"// accumulate result
+ "vadd.s32 q4, q4, q15 \n"// accumulate result
+
+ "subs %[count], %[count], #4 \n"// update loop counter
+ "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples
+
+ "bne 1b \n"// loop
+
+ ASSEMBLY_ACCUMULATE_STEREO
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN0] "+r" (coefsN),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [vLR] "r" (volumeLR)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3", "q4",
+ "q8", "q9", "q10", "q11",
+ "q12", "q13", "q14", "q15"
+ );
+}
+
+#endif //USE_NEON
+
+}; // namespace android
+
+#endif /*ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_NEON_H*/
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index 207f26b..d0a7a58 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -540,7 +540,7 @@
uint32_t phaseIncrement = mPhaseIncrement;
size_t outputIndex = 0;
size_t outputSampleCount = outFrameCount * 2;
- size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+ size_t inFrameCount = getInFrameCountRequired(outFrameCount);
while (outputIndex < outputSampleCount) {
// buffer is empty, fetch a new one
diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h
index bc2038a..0754d9d 100644
--- a/services/audioflinger/Configuration.h
+++ b/services/audioflinger/Configuration.h
@@ -32,9 +32,6 @@
// uncomment to enable fast mixer to take performance samples for later statistical analysis
#define FAST_MIXER_STATISTICS
-// uncomment to allow fast tracks at non-native sample rate
-//#define FAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
-
// uncomment for debugging timing problems related to StateQueue::push()
//#define STATE_QUEUE_DUMP
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index c6e78a6..29b56db 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -805,7 +805,112 @@
return mOffloaded;
}
-void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
+String8 effectFlagsToString(uint32_t flags) {
+ String8 s;
+
+ s.append("conn. mode: ");
+ switch (flags & EFFECT_FLAG_TYPE_MASK) {
+ case EFFECT_FLAG_TYPE_INSERT: s.append("insert"); break;
+ case EFFECT_FLAG_TYPE_AUXILIARY: s.append("auxiliary"); break;
+ case EFFECT_FLAG_TYPE_REPLACE: s.append("replace"); break;
+ case EFFECT_FLAG_TYPE_PRE_PROC: s.append("preproc"); break;
+ case EFFECT_FLAG_TYPE_POST_PROC: s.append("postproc"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+
+ s.append("insert pref: ");
+ switch (flags & EFFECT_FLAG_INSERT_MASK) {
+ case EFFECT_FLAG_INSERT_ANY: s.append("any"); break;
+ case EFFECT_FLAG_INSERT_FIRST: s.append("first"); break;
+ case EFFECT_FLAG_INSERT_LAST: s.append("last"); break;
+ case EFFECT_FLAG_INSERT_EXCLUSIVE: s.append("exclusive"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+
+ s.append("volume mgmt: ");
+ switch (flags & EFFECT_FLAG_VOLUME_MASK) {
+ case EFFECT_FLAG_VOLUME_NONE: s.append("none"); break;
+ case EFFECT_FLAG_VOLUME_CTRL: s.append("implements control"); break;
+ case EFFECT_FLAG_VOLUME_IND: s.append("requires indication"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+
+ uint32_t devind = flags & EFFECT_FLAG_DEVICE_MASK;
+ if (devind) {
+ s.append("device indication: ");
+ switch (devind) {
+ case EFFECT_FLAG_DEVICE_IND: s.append("requires updates"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ s.append("input mode: ");
+ switch (flags & EFFECT_FLAG_INPUT_MASK) {
+ case EFFECT_FLAG_INPUT_DIRECT: s.append("direct"); break;
+ case EFFECT_FLAG_INPUT_PROVIDER: s.append("provider"); break;
+ case EFFECT_FLAG_INPUT_BOTH: s.append("direct+provider"); break;
+ default: s.append("not set"); break;
+ }
+ s.append(", ");
+
+ s.append("output mode: ");
+ switch (flags & EFFECT_FLAG_OUTPUT_MASK) {
+ case EFFECT_FLAG_OUTPUT_DIRECT: s.append("direct"); break;
+ case EFFECT_FLAG_OUTPUT_PROVIDER: s.append("provider"); break;
+ case EFFECT_FLAG_OUTPUT_BOTH: s.append("direct+provider"); break;
+ default: s.append("not set"); break;
+ }
+ s.append(", ");
+
+ uint32_t accel = flags & EFFECT_FLAG_HW_ACC_MASK;
+ if (accel) {
+ s.append("hardware acceleration: ");
+ switch (accel) {
+ case EFFECT_FLAG_HW_ACC_SIMPLE: s.append("non-tunneled"); break;
+ case EFFECT_FLAG_HW_ACC_TUNNEL: s.append("tunneled"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ uint32_t modeind = flags & EFFECT_FLAG_AUDIO_MODE_MASK;
+ if (modeind) {
+ s.append("mode indication: ");
+ switch (modeind) {
+ case EFFECT_FLAG_AUDIO_MODE_IND: s.append("required"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ uint32_t srcind = flags & EFFECT_FLAG_AUDIO_SOURCE_MASK;
+ if (srcind) {
+ s.append("source indication: ");
+ switch (srcind) {
+ case EFFECT_FLAG_AUDIO_SOURCE_IND: s.append("required"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ if (flags & EFFECT_FLAG_OFFLOAD_MASK) {
+ s.append("offloadable, ");
+ }
+
+ int len = s.length();
+ if (s.length() > 2) {
+ char *str = s.lockBuffer(len);
+ s.unlockBuffer(len - 2);
+ }
+ return s;
+}
+
+
+void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args __unused)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -821,8 +926,8 @@
}
result.append("\t\tSession Status State Engine:\n");
- snprintf(buffer, SIZE, "\t\t%05d %03d %03d 0x%08x\n",
- mSessionId, mStatus, mState, (uint32_t)mEffectInterface);
+ snprintf(buffer, SIZE, "\t\t%05d %03d %03d %p\n",
+ mSessionId, mStatus, mState, mEffectInterface);
result.append(buffer);
result.append("\t\tDescriptor:\n");
@@ -839,9 +944,10 @@
mDescriptor.type.node[2],
mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]);
result.append(buffer);
- snprintf(buffer, SIZE, "\t\t- apiVersion: %08X\n\t\t- flags: %08X\n",
+ snprintf(buffer, SIZE, "\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n",
mDescriptor.apiVersion,
- mDescriptor.flags);
+ mDescriptor.flags,
+ effectFlagsToString(mDescriptor.flags).string());
result.append(buffer);
snprintf(buffer, SIZE, "\t\t- name: %s\n",
mDescriptor.name);
@@ -851,38 +957,38 @@
result.append(buffer);
result.append("\t\t- Input configuration:\n");
- result.append("\t\t\tBuffer Frames Smp rate Channels Format\n");
- snprintf(buffer, SIZE, "\t\t\t0x%08x %05d %05d %08x %d\n",
- (uint32_t)mConfig.inputCfg.buffer.raw,
+ result.append("\t\t\tFrames Smp rate Channels Format Buffer\n");
+ snprintf(buffer, SIZE, "\t\t\t%05zu %05d %08x %6d (%s) %p\n",
mConfig.inputCfg.buffer.frameCount,
mConfig.inputCfg.samplingRate,
mConfig.inputCfg.channels,
- mConfig.inputCfg.format);
+ mConfig.inputCfg.format,
+ formatToString((audio_format_t)mConfig.inputCfg.format),
+ mConfig.inputCfg.buffer.raw);
result.append(buffer);
result.append("\t\t- Output configuration:\n");
result.append("\t\t\tBuffer Frames Smp rate Channels Format\n");
- snprintf(buffer, SIZE, "\t\t\t0x%08x %05d %05d %08x %d\n",
- (uint32_t)mConfig.outputCfg.buffer.raw,
+ snprintf(buffer, SIZE, "\t\t\t%p %05zu %05d %08x %d (%s)\n",
+ mConfig.outputCfg.buffer.raw,
mConfig.outputCfg.buffer.frameCount,
mConfig.outputCfg.samplingRate,
mConfig.outputCfg.channels,
- mConfig.outputCfg.format);
+ mConfig.outputCfg.format,
+ formatToString((audio_format_t)mConfig.outputCfg.format));
result.append(buffer);
- snprintf(buffer, SIZE, "\t\t%d Clients:\n", mHandles.size());
+ snprintf(buffer, SIZE, "\t\t%zu Clients:\n", mHandles.size());
result.append(buffer);
- result.append("\t\t\tPid Priority Ctrl Locked client server\n");
+ result.append("\t\t\t Pid Priority Ctrl Locked client server\n");
for (size_t i = 0; i < mHandles.size(); ++i) {
EffectHandle *handle = mHandles[i];
if (handle != NULL && !handle->destroyed_l()) {
- handle->dump(buffer, SIZE);
+ handle->dumpToBuffer(buffer, SIZE);
result.append(buffer);
}
}
- result.append("\n");
-
write(fd, result.string(), result.length());
if (locked) {
@@ -912,18 +1018,15 @@
}
int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
mCblkMemory = client->heap()->allocate(EFFECT_PARAM_BUFFER_SIZE + bufOffset);
- if (mCblkMemory != 0) {
- mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer());
-
- if (mCblk != NULL) {
- new(mCblk) effect_param_cblk_t();
- mBuffer = (uint8_t *)mCblk + bufOffset;
- }
- } else {
+ if (mCblkMemory == 0 ||
+ (mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer())) == NULL) {
ALOGE("not enough memory for Effect size=%u", EFFECT_PARAM_BUFFER_SIZE +
sizeof(effect_param_cblk_t));
+ mCblkMemory.clear();
return;
}
+ new(mCblk) effect_param_cblk_t();
+ mBuffer = (uint8_t *)mCblk + bufOffset;
}
AudioFlinger::EffectHandle::~EffectHandle()
@@ -940,6 +1043,11 @@
disconnect(false);
}
+status_t AudioFlinger::EffectHandle::initCheck()
+{
+ return mClient == 0 || mCblkMemory != 0 ? OK : NO_MEMORY;
+}
+
status_t AudioFlinger::EffectHandle::enable()
{
ALOGV("enable %p", this);
@@ -973,13 +1081,20 @@
}
mEnabled = false;
} else {
- if (thread != 0 && !mEffect->isOffloadable()) {
- if ((thread->type() == ThreadBase::OFFLOAD)) {
+ if (thread != 0) {
+ if (thread->type() == ThreadBase::OFFLOAD) {
PlaybackThread *t = (PlaybackThread *)thread.get();
- t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
}
- if (mEffect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
- thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable();
+ if (!mEffect->isOffloadable()) {
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ }
+ if (mEffect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+ thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable();
+ }
}
}
}
@@ -1010,6 +1125,11 @@
sp<ThreadBase> thread = mEffect->thread().promote();
if (thread != 0) {
thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
}
return status;
@@ -1168,15 +1288,15 @@
}
-void AudioFlinger::EffectHandle::dump(char* buffer, size_t size)
+void AudioFlinger::EffectHandle::dumpToBuffer(char* buffer, size_t size)
{
bool locked = mCblk != NULL && AudioFlinger::dumpTryLock(mCblk->lock);
- snprintf(buffer, size, "\t\t\t%05d %05d %01u %01u %05u %05u\n",
+ snprintf(buffer, size, "\t\t\t%5d %5d %3s %3s %5u %5u\n",
(mClient == 0) ? getpid_cached : mClient->pid(),
mPriority,
- mHasControl,
- !locked,
+ mHasControl ? "yes" : "no",
+ locked ? "yes" : "no",
mCblk ? mCblk->clientIndex : 0,
mCblk ? mCblk->serverIndex : 0
);
@@ -1280,9 +1400,10 @@
}
bool isGlobalSession = (mSessionId == AUDIO_SESSION_OUTPUT_MIX) ||
(mSessionId == AUDIO_SESSION_OUTPUT_STAGE);
- // always process effects unless no more tracks are on the session and the effect tail
- // has been rendered
- bool doProcess = true;
+ // never process effects when:
+ // - on an OFFLOAD thread
+ // - no more tracks are on the session and the effect tail has been rendered
+ bool doProcess = (thread->type() != ThreadBase::OFFLOAD);
if (!isGlobalSession) {
bool tracksOnSession = (trackCnt() != 0);
@@ -1556,33 +1677,35 @@
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, "Effects for session %d:\n", mSessionId);
+ size_t numEffects = mEffects.size();
+ snprintf(buffer, SIZE, " %d effects for session %d\n", numEffects, mSessionId);
result.append(buffer);
- bool locked = AudioFlinger::dumpTryLock(mLock);
- // failed to lock - AudioFlinger is probably deadlocked
- if (!locked) {
- result.append("\tCould not lock mutex:\n");
- }
-
- result.append("\tNum fx In buffer Out buffer Active tracks:\n");
- snprintf(buffer, SIZE, "\t%02d 0x%08x 0x%08x %d\n",
- mEffects.size(),
- (uint32_t)mInBuffer,
- (uint32_t)mOutBuffer,
- mActiveTrackCnt);
- result.append(buffer);
- write(fd, result.string(), result.size());
-
- for (size_t i = 0; i < mEffects.size(); ++i) {
- sp<EffectModule> effect = mEffects[i];
- if (effect != 0) {
- effect->dump(fd, args);
+ if (numEffects) {
+ bool locked = AudioFlinger::dumpTryLock(mLock);
+ // failed to lock - AudioFlinger is probably deadlocked
+ if (!locked) {
+ result.append("\tCould not lock mutex:\n");
}
- }
- if (locked) {
- mLock.unlock();
+ result.append("\tIn buffer Out buffer Active tracks:\n");
+ snprintf(buffer, SIZE, "\t%p %p %d\n",
+ mInBuffer,
+ mOutBuffer,
+ mActiveTrackCnt);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ for (size_t i = 0; i < numEffects; ++i) {
+ sp<EffectModule> effect = mEffects[i];
+ if (effect != 0) {
+ effect->dump(fd, args);
+ }
+ }
+
+ if (locked) {
+ mLock.unlock();
+ }
}
}
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index b717857..ccc4825 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -169,6 +169,7 @@
const sp<IEffectClient>& effectClient,
int32_t priority);
virtual ~EffectHandle();
+ virtual status_t initCheck();
// IEffect
virtual status_t enable();
@@ -208,7 +209,7 @@
// destroyed_l() must be called with the associated EffectModule mLock held
bool destroyed_l() const { return mDestroyed; }
- void dump(char* buffer, size_t size);
+ void dumpToBuffer(char* buffer, size_t size);
protected:
friend class AudioFlinger; // for mEffect, mHasControl, mEnabled
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 7126e92..90122e0 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -236,10 +236,9 @@
sampleRate = Format_sampleRate(format);
ALOG_ASSERT(Format_channelCount(format) == FCC_2);
}
- dumpState->mSampleRate = sampleRate;
}
- if ((format != previousFormat) || (frameCount != previous->mFrameCount)) {
+ if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) {
// FIXME to avoid priority inversion, don't delete here
delete mixer;
mixer = NULL;
@@ -321,12 +320,8 @@
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
(void *) mixBuffer);
// newly allocated track names default to full scale volume
- if (fastTrack->mSampleRate != 0 && fastTrack->mSampleRate != sampleRate) {
- mixer->setParameter(name, AudioMixer::RESAMPLE,
- AudioMixer::SAMPLE_RATE, (void*) fastTrack->mSampleRate);
- }
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
- (void *) fastTrack->mChannelMask);
+ (void *)(uintptr_t)fastTrack->mChannelMask);
mixer->enable(name);
}
generations[i] = fastTrack->mGeneration;
@@ -353,16 +348,10 @@
mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
(void *)0x1000);
}
- if (fastTrack->mSampleRate != 0 &&
- fastTrack->mSampleRate != sampleRate) {
- mixer->setParameter(name, AudioMixer::RESAMPLE,
- AudioMixer::SAMPLE_RATE, (void*) fastTrack->mSampleRate);
- } else {
- mixer->setParameter(name, AudioMixer::RESAMPLE,
- AudioMixer::REMOVE, NULL);
- }
+ mixer->setParameter(name, AudioMixer::RESAMPLE,
+ AudioMixer::REMOVE, NULL);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
- (void *) fastTrack->mChannelMask);
+ (void *)(uintptr_t) fastTrack->mChannelMask);
// already enabled
}
generations[i] = fastTrack->mGeneration;
@@ -392,16 +381,8 @@
// Refresh the per-track timestamp
if (timestampStatus == NO_ERROR) {
- uint32_t trackFramesWrittenButNotPresented;
- uint32_t trackSampleRate = fastTrack->mSampleRate;
- // There is currently no sample rate conversion for fast tracks currently
- if (trackSampleRate != 0 && trackSampleRate != sampleRate) {
- trackFramesWrittenButNotPresented =
- ((int64_t) nativeFramesWrittenButNotPresented * trackSampleRate) /
- sampleRate;
- } else {
- trackFramesWrittenButNotPresented = nativeFramesWrittenButNotPresented;
- }
+ uint32_t trackFramesWrittenButNotPresented =
+ nativeFramesWrittenButNotPresented;
uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased();
// Can't provide an AudioTimestamp before first frame presented,
// or during the brief 32-bit wraparound window
@@ -419,9 +400,9 @@
if (fastTrack->mVolumeProvider != NULL) {
uint32_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0,
- (void *)(vlr & 0xFFFF));
+ (void *)(uintptr_t)(vlr & 0xFFFF));
mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
- (void *)(vlr >> 16));
+ (void *)(uintptr_t)(vlr >> 16));
}
// FIXME The current implementation of framesReady() for fast tracks
// takes a tryLock, which can block
@@ -715,7 +696,7 @@
void FastMixerDumpState::dump(int fd) const
{
if (mCommand == FastMixerState::INITIAL) {
- fdprintf(fd, "FastMixer not initialized\n");
+ fdprintf(fd, " FastMixer not initialized\n");
return;
}
#define COMMAND_MAX 32
@@ -749,10 +730,10 @@
double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) +
(mMeasuredWarmupTs.tv_nsec / 1000000.0);
double mixPeriodSec = (double) mFrameCount / (double) mSampleRate;
- fdprintf(fd, "FastMixer command=%s writeSequence=%u framesWritten=%u\n"
- " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
- " sampleRate=%u frameCount=%u measuredWarmup=%.3g ms, warmupCycles=%u\n"
- " mixPeriod=%.2f ms\n",
+ fdprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n"
+ " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
+ " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
+ " mixPeriod=%.2f ms\n",
string, mWriteSequence, mFramesWritten,
mNumTracks, mWriteErrors, mUnderruns, mOverruns,
mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
@@ -803,14 +784,20 @@
previousCpukHz = sampleCpukHz;
#endif
}
- fdprintf(fd, "Simple moving statistics over last %.1f seconds:\n", wall.n() * mixPeriodSec);
- fdprintf(fd, " wall clock time in ms per mix cycle:\n"
- " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
- wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, wall.stddev()*1e-6);
- fdprintf(fd, " raw CPU load in us per mix cycle:\n"
- " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
- loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3,
- loadNs.stddev()*1e-3);
+ if (n) {
+ fdprintf(fd, " Simple moving statistics over last %.1f seconds:\n",
+ wall.n() * mixPeriodSec);
+ fdprintf(fd, " wall clock time in ms per mix cycle:\n"
+ " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
+ wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6,
+ wall.stddev()*1e-6);
+ fdprintf(fd, " raw CPU load in us per mix cycle:\n"
+ " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
+ loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3,
+ loadNs.stddev()*1e-3);
+ } else {
+ fdprintf(fd, " No FastMixer statistics available currently\n");
+ }
#ifdef CPU_FREQUENCY_STATISTICS
fdprintf(fd, " CPU clock frequency in MHz:\n"
" mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
@@ -828,9 +815,9 @@
left.sample(tail[i]);
right.sample(tail[n - (i + 1)]);
}
- fdprintf(fd, "Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n"
- " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n"
- " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
+ fdprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n"
+ " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n"
+ " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6,
right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6,
right.stddev()*1e-6);
@@ -843,9 +830,9 @@
// Instead we always display all tracks, with an indication
// of whether we think the track is active.
uint32_t trackMask = mTrackMask;
- fdprintf(fd, "Fast tracks: kMaxFastTracks=%u activeMask=%#x\n",
+ fdprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n",
FastMixerState::kMaxFastTracks, trackMask);
- fdprintf(fd, "Index Active Full Partial Empty Recent Ready\n");
+ fdprintf(fd, " Index Active Full Partial Empty Recent Ready\n");
for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) {
bool isActive = trackMask & 1;
const FastTrackDump *ftDump = &mTracks[i];
@@ -865,7 +852,7 @@
mostRecent = "?";
break;
}
- fdprintf(fd, "%5u %6s %4u %7u %5u %7s %5u\n", i, isActive ? "yes" : "no",
+ fdprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no",
(underruns.mBitFields.mFull) & UNDERRUN_MASK,
(underruns.mBitFields.mPartial) & UNDERRUN_MASK,
(underruns.mBitFields.mEmpty) & UNDERRUN_MASK,
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index 737de97..43ff233 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -20,7 +20,7 @@
namespace android {
FastTrack::FastTrack() :
- mBufferProvider(NULL), mVolumeProvider(NULL), mSampleRate(0),
+ mBufferProvider(NULL), mVolumeProvider(NULL),
mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mGeneration(0)
{
}
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index f6e7903..9739fe9 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -43,7 +43,6 @@
ExtendedAudioBufferProvider* mBufferProvider; // must be NULL if inactive, or non-NULL if active
VolumeProvider* mVolumeProvider; // optional; if NULL then full-scale
- unsigned mSampleRate; // optional; if zero then use mixer sample rate
audio_channel_mask_t mChannelMask; // AUDIO_CHANNEL_OUT_MONO or AUDIO_CHANNEL_OUT_STEREO
int mGeneration; // increment when any field is assigned
};
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index be4b811..b5e763d 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -31,12 +31,13 @@
size_t frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int uid,
IAudioFlinger::track_flags_t flags);
virtual ~Track();
virtual status_t initCheck() const;
static void appendDumpHeader(String8& result);
- void dump(char* buffer, size_t size);
+ void dump(char* buffer, size_t size, bool active);
virtual status_t start(AudioSystem::sync_event_t event =
AudioSystem::SYNC_EVENT_NONE,
int triggerSession = 0);
@@ -61,6 +62,7 @@
int16_t *mainBuffer() const { return mMainBuffer; }
int auxEffectId() const { return mAuxEffectId; }
virtual status_t getTimestamp(AudioTimestamp& timestamp);
+ void signal();
// implement FastMixerState::VolumeProvider interface
virtual uint32_t getVolumeLR();
@@ -92,6 +94,8 @@
bool isReady() const;
void setPaused() { mState = PAUSED; }
void reset();
+ bool isFlushPending() const { return mFlushHwPending; }
+ void flushAck();
bool isOutputTrack() const {
return (mStreamType == AUDIO_STREAM_CNT);
@@ -153,6 +157,7 @@
bool mIsInvalid; // non-resettable latch, set by invalidate()
AudioTrackServerProxy* mAudioTrackServerProxy;
bool mResumeToStopping; // track was paused in stopping state.
+ bool mFlushHwPending; // track requests for thread flush
}; // end of Track
class TimedTrack : public Track {
@@ -165,7 +170,8 @@
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId);
+ int sessionId,
+ int uid);
virtual ~TimedTrack();
class TimedBuffer {
@@ -208,7 +214,8 @@
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId);
+ int sessionId,
+ int uid);
void timedYieldSamples_l(AudioBufferProvider::Buffer* buffer);
void timedYieldSilence_l(uint32_t numFrames,
@@ -255,7 +262,8 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount);
+ size_t frameCount,
+ int uid);
virtual ~OutputTrack();
virtual status_t start(AudioSystem::sync_event_t event =
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index a4e1810..3ec9889 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -28,7 +28,8 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- int sessionId);
+ int sessionId,
+ int uid);
virtual ~RecordTrack();
virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
@@ -44,7 +45,10 @@
return tmp; }
static void appendDumpHeader(String8& result);
- void dump(char* buffer, size_t size);
+ void dump(char* buffer, size_t size, bool active);
+
+ void handleSyncStartEvent(const sp<SyncEvent>& event);
+ void clearSyncStartEvent();
private:
friend class AudioFlinger; // for mState
@@ -58,4 +62,33 @@
// releaseBuffer() not overridden
bool mOverflow; // overflow on most recent attempt to fill client buffer
+
+ // updated by RecordThread::readInputParameters_l()
+ AudioResampler *mResampler;
+
+ // interleaved stereo pairs of fixed-point signed Q19.12
+ int32_t *mRsmpOutBuffer;
+ // current allocated frame count for the above, which may be larger than needed
+ size_t mRsmpOutFrameCount;
+
+ size_t mRsmpInUnrel; // unreleased frames remaining from
+ // most recent getNextBuffer
+ // for debug only
+
+ // rolling counter that is never cleared
+ int32_t mRsmpInFront; // next available frame
+
+ AudioBufferProvider::Buffer mSink; // references client's buffer sink in shared memory
+
+ // sync event triggering actual audio capture. Frames read before this event will
+ // be dropped and therefore not read by the application.
+ sp<SyncEvent> mSyncStartEvent;
+
+ // number of captured frames to drop after the start sync event has been received.
+ // when < 0, maximum frames to drop before starting capture even if sync event is
+ // not received
+ ssize_t mFramesToDrop;
+
+ // used by resampler to find source frames
+ ResamplerBufferProvider *mResamplerBufferProvider;
};
diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp
index c2d3bbd..48399c0 100644
--- a/services/audioflinger/StateQueue.cpp
+++ b/services/audioflinger/StateQueue.cpp
@@ -58,7 +58,11 @@
template<typename T> const T* StateQueue<T>::poll()
{
+#ifdef __LP64__
+ const T *next = (const T *) android_atomic_acquire_load64((volatile int64_t *) &mNext);
+#else
const T *next = (const T *) android_atomic_acquire_load((volatile int32_t *) &mNext);
+#endif
if (next != mCurrent) {
mAck = next; // no additional barrier needed
mCurrent = next;
@@ -140,7 +144,11 @@
}
// publish
+#ifdef __LP64__
+ android_atomic_release_store64((int64_t) mMutating, (volatile int64_t *) &mNext);
+#else
android_atomic_release_store((int32_t) mMutating, (volatile int32_t *) &mNext);
+#endif
mExpecting = mMutating;
// copy with circular wraparound
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index aca6c18..690d0d6 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -104,10 +104,10 @@
// maximum divider applied to the active sleep time in the mixer thread loop
static const uint32_t kMaxThreadSleepTimeShift = 2;
-// minimum normal mix buffer size, expressed in milliseconds rather than frames
-static const uint32_t kMinNormalMixBufferSizeMs = 20;
-// maximum normal mix buffer size
-static const uint32_t kMaxNormalMixBufferSizeMs = 24;
+// minimum normal sink buffer size, expressed in milliseconds rather than frames
+static const uint32_t kMinNormalSinkBufferSizeMs = 20;
+// maximum normal sink buffer size
+static const uint32_t kMaxNormalSinkBufferSizeMs = 24;
// Offloaded output thread standby delay: allows track transition without going to standby
static const nsecs_t kOffloadStandbyDelayNs = seconds(1);
@@ -135,12 +135,12 @@
// IAudioFlinger::createTrack() reports back to client the total size of shared memory area
// for the track. The client then sub-divides this into smaller buffers for its use.
-// Currently the client uses double-buffering by default, but doesn't tell us about that.
-// So for now we just assume that client is double-buffered.
-// FIXME It would be better for client to tell AudioFlinger whether it wants double-buffering or
-// N-buffering, so AudioFlinger could allocate the right amount of memory.
+// Currently the client uses N-buffering by default, but doesn't tell us about the value of N.
+// So for now we just assume that client is double-buffered for fast tracks.
+// FIXME It would be better for client to tell AudioFlinger the value of N,
+// so AudioFlinger could allocate the right amount of memory.
// See the client's minBufCount and mNotificationFramesAct calculations for details.
-static const int kFastTrackMultiplier = 1;
+static const int kFastTrackMultiplier = 2;
// ----------------------------------------------------------------------------
@@ -185,7 +185,11 @@
{
}
-void CpuStats::sample(const String8 &title) {
+void CpuStats::sample(const String8 &title
+#ifndef DEBUG_CPU_USAGE
+ __unused
+#endif
+ ) {
#ifdef DEBUG_CPU_USAGE
// get current thread's delta CPU time in wall clock ns
double wcNs;
@@ -270,8 +274,10 @@
mType(type),
mAudioFlinger(audioFlinger),
// mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, mFormat, mBufferSize
- // are set by PlaybackThread::readOutputParameters() or RecordThread::readInputParameters()
+ // are set by PlaybackThread::readOutputParameters_l() or
+ // RecordThread::readInputParameters_l()
mParamStatus(NO_ERROR),
+ //FIXME: mStandby should be true here. Is this some kind of hack?
mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
// mName will be set by concrete (non-virtual) subclass
@@ -420,7 +426,54 @@
}
}
-void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
+String8 channelMaskToString(audio_channel_mask_t mask, bool output) {
+ String8 s;
+ if (output) {
+ if (mask & AUDIO_CHANNEL_OUT_FRONT_LEFT) s.append("front-left, ");
+ if (mask & AUDIO_CHANNEL_OUT_FRONT_RIGHT) s.append("front-right, ");
+ if (mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) s.append("front-center, ");
+ if (mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) s.append("low freq, ");
+ if (mask & AUDIO_CHANNEL_OUT_BACK_LEFT) s.append("back-left, ");
+ if (mask & AUDIO_CHANNEL_OUT_BACK_RIGHT) s.append("back-right, ");
+ if (mask & AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER) s.append("front-left-of-center, ");
+ if (mask & AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER) s.append("front-right-of-center, ");
+ if (mask & AUDIO_CHANNEL_OUT_BACK_CENTER) s.append("back-center, ");
+ if (mask & AUDIO_CHANNEL_OUT_SIDE_LEFT) s.append("side-left, ");
+ if (mask & AUDIO_CHANNEL_OUT_SIDE_RIGHT) s.append("side-right, ");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_CENTER) s.append("top-center ,");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT) s.append("top-front-left, ");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER) s.append("top-front-center, ");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT) s.append("top-front-right, ");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_LEFT) s.append("top-back-left, ");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_CENTER) s.append("top-back-center, " );
+ if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT) s.append("top-back-right, " );
+ if (mask & ~AUDIO_CHANNEL_OUT_ALL) s.append("unknown, ");
+ } else {
+ if (mask & AUDIO_CHANNEL_IN_LEFT) s.append("left, ");
+ if (mask & AUDIO_CHANNEL_IN_RIGHT) s.append("right, ");
+ if (mask & AUDIO_CHANNEL_IN_FRONT) s.append("front, ");
+ if (mask & AUDIO_CHANNEL_IN_BACK) s.append("back, ");
+ if (mask & AUDIO_CHANNEL_IN_LEFT_PROCESSED) s.append("left-processed, ");
+ if (mask & AUDIO_CHANNEL_IN_RIGHT_PROCESSED) s.append("right-processed, ");
+ if (mask & AUDIO_CHANNEL_IN_FRONT_PROCESSED) s.append("front-processed, ");
+ if (mask & AUDIO_CHANNEL_IN_BACK_PROCESSED) s.append("back-processed, ");
+ if (mask & AUDIO_CHANNEL_IN_PRESSURE) s.append("pressure, ");
+ if (mask & AUDIO_CHANNEL_IN_X_AXIS) s.append("X, ");
+ if (mask & AUDIO_CHANNEL_IN_Y_AXIS) s.append("Y, ");
+ if (mask & AUDIO_CHANNEL_IN_Z_AXIS) s.append("Z, ");
+ if (mask & AUDIO_CHANNEL_IN_VOICE_UPLINK) s.append("voice-uplink, ");
+ if (mask & AUDIO_CHANNEL_IN_VOICE_DNLINK) s.append("voice-dnlink, ");
+ if (mask & ~AUDIO_CHANNEL_IN_ALL) s.append("unknown, ");
+ }
+ int len = s.length();
+ if (s.length() > 2) {
+ char *str = s.lockBuffer(len);
+ s.unlockBuffer(len - 2);
+ }
+ return s;
+}
+
+void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __unused)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -428,49 +481,43 @@
bool locked = AudioFlinger::dumpTryLock(mLock);
if (!locked) {
- snprintf(buffer, SIZE, "thread %p maybe dead locked\n", this);
- write(fd, buffer, strlen(buffer));
+ fdprintf(fd, "thread %p maybe dead locked\n", this);
}
- snprintf(buffer, SIZE, "io handle: %d\n", mId);
- result.append(buffer);
- snprintf(buffer, SIZE, "TID: %d\n", getTid());
- result.append(buffer);
- snprintf(buffer, SIZE, "standby: %d\n", mStandby);
- result.append(buffer);
- snprintf(buffer, SIZE, "Sample rate: %u\n", mSampleRate);
- result.append(buffer);
- snprintf(buffer, SIZE, "HAL frame count: %d\n", mFrameCount);
- result.append(buffer);
- snprintf(buffer, SIZE, "HAL buffer size: %u bytes\n", mBufferSize);
- result.append(buffer);
- snprintf(buffer, SIZE, "Channel Count: %u\n", mChannelCount);
- result.append(buffer);
- snprintf(buffer, SIZE, "Channel Mask: 0x%08x\n", mChannelMask);
- result.append(buffer);
- snprintf(buffer, SIZE, "Format: %d\n", mFormat);
- result.append(buffer);
- snprintf(buffer, SIZE, "Frame size: %u\n", mFrameSize);
- result.append(buffer);
-
- snprintf(buffer, SIZE, "\nPending setParameters commands: \n");
- result.append(buffer);
- result.append(" Index Command");
- for (size_t i = 0; i < mNewParameters.size(); ++i) {
- snprintf(buffer, SIZE, "\n %02d ", i);
- result.append(buffer);
- result.append(mNewParameters[i]);
+ fdprintf(fd, " I/O handle: %d\n", mId);
+ fdprintf(fd, " TID: %d\n", getTid());
+ fdprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no");
+ fdprintf(fd, " Sample rate: %u\n", mSampleRate);
+ fdprintf(fd, " HAL frame count: %zu\n", mFrameCount);
+ fdprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize);
+ fdprintf(fd, " Channel Count: %u\n", mChannelCount);
+ fdprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask,
+ channelMaskToString(mChannelMask, mType != RECORD).string());
+ fdprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat));
+ fdprintf(fd, " Frame size: %zu\n", mFrameSize);
+ fdprintf(fd, " Pending setParameters commands:");
+ size_t numParams = mNewParameters.size();
+ if (numParams) {
+ fdprintf(fd, "\n Index Command");
+ for (size_t i = 0; i < numParams; ++i) {
+ fdprintf(fd, "\n %02zu ", i);
+ fdprintf(fd, mNewParameters[i]);
+ }
+ fdprintf(fd, "\n");
+ } else {
+ fdprintf(fd, " none\n");
}
-
- snprintf(buffer, SIZE, "\n\nPending config events: \n");
- result.append(buffer);
- for (size_t i = 0; i < mConfigEvents.size(); i++) {
- mConfigEvents[i]->dump(buffer, SIZE);
- result.append(buffer);
+ fdprintf(fd, " Pending config events:");
+ size_t numConfig = mConfigEvents.size();
+ if (numConfig) {
+ for (size_t i = 0; i < numConfig; i++) {
+ mConfigEvents[i]->dump(buffer, SIZE);
+ fdprintf(fd, "\n %s", buffer);
+ }
+ fdprintf(fd, "\n");
+ } else {
+ fdprintf(fd, " none\n");
}
- result.append("\n");
-
- write(fd, result.string(), result.size());
if (locked) {
mLock.unlock();
@@ -483,10 +530,11 @@
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, "\n- %d Effect Chains:\n", mEffectChains.size());
+ size_t numEffectChains = mEffectChains.size();
+ snprintf(buffer, SIZE, " %zu Effect Chains\n", numEffectChains);
write(fd, buffer, strlen(buffer));
- for (size_t i = 0; i < mEffectChains.size(); ++i) {
+ for (size_t i = 0; i < numEffectChains; ++i) {
sp<EffectChain> chain = mEffectChains[i];
if (chain != 0) {
chain->dump(fd, args);
@@ -494,31 +542,49 @@
}
}
-void AudioFlinger::ThreadBase::acquireWakeLock()
+void AudioFlinger::ThreadBase::acquireWakeLock(int uid)
{
Mutex::Autolock _l(mLock);
- acquireWakeLock_l();
+ acquireWakeLock_l(uid);
}
-void AudioFlinger::ThreadBase::acquireWakeLock_l()
+String16 AudioFlinger::ThreadBase::getWakeLockTag()
{
- if (mPowerManager == 0) {
- // use checkService() to avoid blocking if power service is not up yet
- sp<IBinder> binder =
- defaultServiceManager()->checkService(String16("power"));
- if (binder == 0) {
- ALOGW("Thread %s cannot connect to the power manager service", mName);
- } else {
- mPowerManager = interface_cast<IPowerManager>(binder);
- binder->linkToDeath(mDeathRecipient);
- }
+ switch (mType) {
+ case MIXER:
+ return String16("AudioMix");
+ case DIRECT:
+ return String16("AudioDirectOut");
+ case DUPLICATING:
+ return String16("AudioDup");
+ case RECORD:
+ return String16("AudioIn");
+ case OFFLOAD:
+ return String16("AudioOffload");
+ default:
+ ALOG_ASSERT(false);
+ return String16("AudioUnknown");
}
+}
+
+void AudioFlinger::ThreadBase::acquireWakeLock_l(int uid)
+{
+ getPowerManager_l();
if (mPowerManager != 0) {
sp<IBinder> binder = new BBinder();
- status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
- binder,
- String16(mName),
- String16("media"));
+ status_t status;
+ if (uid >= 0) {
+ status = mPowerManager->acquireWakeLockWithUid(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ binder,
+ getWakeLockTag(),
+ String16("media"),
+ uid);
+ } else {
+ status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ binder,
+ getWakeLockTag(),
+ String16("media"));
+ }
if (status == NO_ERROR) {
mWakeLockToken = binder;
}
@@ -543,6 +609,41 @@
}
}
+void AudioFlinger::ThreadBase::updateWakeLockUids(const SortedVector<int> &uids) {
+ Mutex::Autolock _l(mLock);
+ updateWakeLockUids_l(uids);
+}
+
+void AudioFlinger::ThreadBase::getPowerManager_l() {
+
+ if (mPowerManager == 0) {
+ // use checkService() to avoid blocking if power service is not up yet
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16("power"));
+ if (binder == 0) {
+ ALOGW("Thread %s cannot connect to the power manager service", mName);
+ } else {
+ mPowerManager = interface_cast<IPowerManager>(binder);
+ binder->linkToDeath(mDeathRecipient);
+ }
+ }
+}
+
+void AudioFlinger::ThreadBase::updateWakeLockUids_l(const SortedVector<int> &uids) {
+
+ getPowerManager_l();
+ if (mWakeLockToken == NULL) {
+ ALOGE("no wake lock to update!");
+ return;
+ }
+ if (mPowerManager != 0) {
+ sp<IBinder> binder = new BBinder();
+ status_t status;
+ status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array());
+ ALOGV("acquireWakeLock_l() %s status %d", mName, status);
+ }
+}
+
void AudioFlinger::ThreadBase::clearPowerManager()
{
Mutex::Autolock _l(mLock);
@@ -550,7 +651,7 @@
mPowerManager.clear();
}
-void AudioFlinger::ThreadBase::PMDeathRecipient::binderDied(const wp<IBinder>& who)
+void AudioFlinger::ThreadBase::PMDeathRecipient::binderDied(const wp<IBinder>& who __unused)
{
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
@@ -792,7 +893,10 @@
}
// create effect handle and connect it to effect module
handle = new EffectHandle(effect, client, effectClient, priority);
- lStatus = effect->addHandle(handle.get());
+ lStatus = handle->initCheck();
+ if (lStatus == OK) {
+ lStatus = effect->addHandle(handle.get());
+ }
if (enabled != NULL) {
*enabled = (int)effect->isEnabled();
}
@@ -962,8 +1066,14 @@
audio_devices_t device,
type_t type)
: ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
- mNormalFrameCount(0), mMixBuffer(NULL),
+ mNormalFrameCount(0), mSinkBuffer(NULL),
+ mMixerBufferEnabled(false),
+ mMixerBuffer(NULL),
+ mMixerBufferSize(0),
+ mMixerBufferFormat(AUDIO_FORMAT_INVALID),
+ mMixerBufferValid(false),
mSuspended(0), mBytesWritten(0),
+ mActiveTracksGeneration(0),
// mStreamTypes[] initialized in constructor body
mOutput(output),
mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
@@ -1004,7 +1114,7 @@
}
}
- readOutputParameters();
+ readOutputParameters_l();
// mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor
// There is no AUDIO_STREAM_MIN, and ++ operator does not compile
@@ -1020,7 +1130,8 @@
AudioFlinger::PlaybackThread::~PlaybackThread()
{
mAudioFlinger->unregisterWriter(mNBLogWriter);
- delete[] mMixBuffer;
+ delete[] mSinkBuffer;
+ free(mMixerBuffer);
}
void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
@@ -1030,13 +1141,13 @@
dumpEffectChains(fd, args);
}
-void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args)
+void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args __unused)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
- result.appendFormat("Output thread %p stream volumes in dB:\n ", this);
+ result.appendFormat(" Stream volumes in dB: ");
for (int i = 0; i < AUDIO_STREAM_CNT; ++i) {
const stream_type_t *st = &mStreamTypes[i];
if (i > 0) {
@@ -1051,60 +1162,63 @@
write(fd, result.string(), result.length());
result.clear();
- snprintf(buffer, SIZE, "Output thread %p tracks\n", this);
- result.append(buffer);
- Track::appendDumpHeader(result);
- for (size_t i = 0; i < mTracks.size(); ++i) {
- sp<Track> track = mTracks[i];
- if (track != 0) {
- track->dump(buffer, SIZE);
- result.append(buffer);
- }
- }
-
- snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
- result.append(buffer);
- Track::appendDumpHeader(result);
- for (size_t i = 0; i < mActiveTracks.size(); ++i) {
- sp<Track> track = mActiveTracks[i].promote();
- if (track != 0) {
- track->dump(buffer, SIZE);
- result.append(buffer);
- }
- }
- write(fd, result.string(), result.size());
-
// These values are "raw"; they will wrap around. See prepareTracks_l() for a better way.
FastTrackUnderruns underruns = getFastTrackUnderruns(0);
- fdprintf(fd, "Normal mixer raw underrun counters: partial=%u empty=%u\n",
+ fdprintf(fd, " Normal mixer raw underrun counters: partial=%u empty=%u\n",
underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty);
+
+ size_t numtracks = mTracks.size();
+ size_t numactive = mActiveTracks.size();
+ fdprintf(fd, " %d Tracks", numtracks);
+ size_t numactiveseen = 0;
+ if (numtracks) {
+ fdprintf(fd, " of which %d are active\n", numactive);
+ Track::appendDumpHeader(result);
+ for (size_t i = 0; i < numtracks; ++i) {
+ sp<Track> track = mTracks[i];
+ if (track != 0) {
+ bool active = mActiveTracks.indexOf(track) >= 0;
+ if (active) {
+ numactiveseen++;
+ }
+ track->dump(buffer, SIZE, active);
+ result.append(buffer);
+ }
+ }
+ } else {
+ result.append("\n");
+ }
+ if (numactiveseen != numactive) {
+ // some tracks in the active list were not in the tracks list
+ snprintf(buffer, SIZE, " The following tracks are in the active list but"
+ " not in the track list\n");
+ result.append(buffer);
+ Track::appendDumpHeader(result);
+ for (size_t i = 0; i < numactive; ++i) {
+ sp<Track> track = mActiveTracks[i].promote();
+ if (track != 0 && mTracks.indexOf(track) < 0) {
+ track->dump(buffer, SIZE, true);
+ result.append(buffer);
+ }
+ }
+ }
+
+ write(fd, result.string(), result.size());
+
}
void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, "\nOutput thread %p internals\n", this);
- result.append(buffer);
- snprintf(buffer, SIZE, "Normal frame count: %d\n", mNormalFrameCount);
- result.append(buffer);
- snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n",
- ns2ms(systemTime() - mLastWriteTime));
- result.append(buffer);
- snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites);
- result.append(buffer);
- snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites);
- result.append(buffer);
- snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite);
- result.append(buffer);
- snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended);
- result.append(buffer);
- snprintf(buffer, SIZE, "mix buffer : %p\n", mMixBuffer);
- result.append(buffer);
- write(fd, result.string(), result.size());
- fdprintf(fd, "Fast track availMask=%#x\n", mFastTrackAvailMask);
+ fdprintf(fd, "\nOutput thread %p:\n", this);
+ fdprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount);
+ fdprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
+ fdprintf(fd, " Total writes: %d\n", mNumWrites);
+ fdprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites);
+ fdprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no");
+ fdprintf(fd, " Suspend count: %d\n", mSuspended);
+ fdprintf(fd, " Sink buffer : %p\n", mSinkBuffer);
+ fdprintf(fd, " Mixer buffer: %p\n", mMixerBuffer);
+ fdprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask);
dumpBase(fd, args);
}
@@ -1132,13 +1246,15 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
+ int uid,
status_t *status)
{
+ size_t frameCount = *pFrameCount;
sp<Track> track;
status_t lStatus;
@@ -1159,7 +1275,7 @@
(
(tid != -1) &&
((frameCount == 0) ||
- (frameCount >= (mFrameCount * kFastTrackMultiplier)))
+ (frameCount >= mFrameCount))
)
) &&
// PCM data
@@ -1167,10 +1283,8 @@
// mono or stereo
( (channelMask == AUDIO_CHANNEL_OUT_MONO) ||
(channelMask == AUDIO_CHANNEL_OUT_STEREO) ) &&
-#ifndef FAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
// hardware sample rate
(sampleRate == mSampleRate) &&
-#endif
// normal mixer has an associated fast mixer
hasFastMixer() &&
// there are sufficient fast track slots available
@@ -1207,12 +1321,13 @@
}
}
}
+ *pFrameCount = frameCount;
if (mType == DIRECT) {
if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) {
if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
- ALOGE("createTrack_l() Bad parameter: sampleRate %u format %d, channelMask 0x%08x "
- "for output %p with format %d",
+ ALOGE("createTrack_l() Bad parameter: sampleRate %u format %#x, channelMask 0x%08x "
+ "for output %p with format %#x",
sampleRate, format, channelMask, mOutput, mFormat);
lStatus = BAD_VALUE;
goto Exit;
@@ -1220,16 +1335,16 @@
}
} else if (mType == OFFLOAD) {
if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
- ALOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelMask 0x%08x \""
- "for output %p with format %d",
+ ALOGE("createTrack_l() Bad parameter: sampleRate %d format %#x, channelMask 0x%08x \""
+ "for output %p with format %#x",
sampleRate, format, channelMask, mOutput, mFormat);
lStatus = BAD_VALUE;
goto Exit;
}
} else {
if ((format & AUDIO_FORMAT_MAIN_MASK) != AUDIO_FORMAT_PCM) {
- ALOGE("createTrack_l() Bad parameter: format %d \""
- "for output %p with format %d",
+ ALOGE("createTrack_l() Bad parameter: format %#x \""
+ "for output %p with format %#x",
format, mOutput, mFormat);
lStatus = BAD_VALUE;
goto Exit;
@@ -1270,17 +1385,18 @@
if (!isTimed) {
track = new Track(this, client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, sessionId, *flags);
+ channelMask, frameCount, sharedBuffer, sessionId, uid, *flags);
} else {
track = TimedTrack::create(this, client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, sessionId);
+ channelMask, frameCount, sharedBuffer, sessionId, uid);
}
// new Track always returns non-NULL,
// but TimedTrack::create() is a factory that could fail by returning NULL
lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY;
if (lStatus != NO_ERROR) {
- track.clear();
+ ALOGE("createTrack_l() initCheck failed %d; no control block?", lStatus);
+ // track must be cleared from the caller as the caller has the AF lock
goto Exit;
}
@@ -1411,6 +1527,9 @@
track->mResetDone = false;
track->mPresentationCompleteFrames = 0;
mActiveTracks.add(track);
+ mWakeLockUids.add(track->uid());
+ mActiveTracksGeneration++;
+ mLatestActiveTrack = track;
sp<EffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(),
@@ -1421,9 +1540,7 @@
status = NO_ERROR;
}
- ALOGV("signal playback thread");
- broadcast_l();
-
+ onAddNewTrack_l();
return status;
}
@@ -1549,7 +1666,7 @@
// static
int AudioFlinger::PlaybackThread::asyncCallback(stream_callback_event_t event,
- void *param,
+ void *param __unused,
void *cookie)
{
AudioFlinger::PlaybackThread *me = (AudioFlinger::PlaybackThread *)cookie;
@@ -1568,7 +1685,7 @@
return 0;
}
-void AudioFlinger::PlaybackThread::readOutputParameters()
+void AudioFlinger::PlaybackThread::readOutputParameters_l()
{
// unfortunately we have no way of recovering from errors here, hence the LOG_FATAL
mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common);
@@ -1583,10 +1700,10 @@
mChannelCount = popcount(mChannelMask);
mFormat = mOutput->stream->common.get_format(&mOutput->stream->common);
if (!audio_is_valid_format(mFormat)) {
- LOG_FATAL("HAL format %d not valid for output", mFormat);
+ LOG_FATAL("HAL format %#x not valid for output", mFormat);
}
if ((mType == MIXER || mType == DUPLICATING) && mFormat != AUDIO_FORMAT_PCM_16_BIT) {
- LOG_FATAL("HAL format %d not supported for mixed output; must be AUDIO_FORMAT_PCM_16_BIT",
+ LOG_FATAL("HAL format %#x not supported for mixed output; must be AUDIO_FORMAT_PCM_16_BIT",
mFormat);
}
mFrameSize = audio_stream_frame_size(&mOutput->stream->common);
@@ -1602,15 +1719,16 @@
if (mOutput->stream->set_callback(mOutput->stream,
AudioFlinger::PlaybackThread::asyncCallback, this) == 0) {
mUseAsyncWrite = true;
+ mCallbackThread = new AudioFlinger::AsyncCallbackThread(this);
}
}
- // Calculate size of normal mix buffer relative to the HAL output buffer size
+ // Calculate size of normal sink buffer relative to the HAL output buffer size
double multiplier = 1.0;
if (mType == MIXER && (kUseFastMixer == FastMixer_Static ||
kUseFastMixer == FastMixer_Dynamic)) {
- size_t minNormalFrameCount = (kMinNormalMixBufferSizeMs * mSampleRate) / 1000;
- size_t maxNormalFrameCount = (kMaxNormalMixBufferSizeMs * mSampleRate) / 1000;
+ size_t minNormalFrameCount = (kMinNormalSinkBufferSizeMs * mSampleRate) / 1000;
+ size_t maxNormalFrameCount = (kMaxNormalSinkBufferSizeMs * mSampleRate) / 1000;
// round up minimum and round down maximum to nearest 16 frames to satisfy AudioMixer
minNormalFrameCount = (minNormalFrameCount + 15) & ~15;
maxNormalFrameCount = maxNormalFrameCount & ~15;
@@ -1628,7 +1746,7 @@
}
} else {
// prefer an even multiplier, for compatibility with doubling of fast tracks due to HAL
- // SRC (it would be unusual for the normal mix buffer size to not be a multiple of fast
+ // SRC (it would be unusual for the normal sink buffer size to not be a multiple of fast
// track, but we sometimes have to do this to satisfy the maximum frame count
// constraint)
// FIXME this rounding up should not be done if no HAL SRC
@@ -1644,18 +1762,29 @@
mNormalFrameCount = multiplier * mFrameCount;
// round up to nearest 16 frames to satisfy AudioMixer
mNormalFrameCount = (mNormalFrameCount + 15) & ~15;
- ALOGI("HAL output buffer size %u frames, normal mix buffer size %u frames", mFrameCount,
+ ALOGI("HAL output buffer size %u frames, normal sink buffer size %u frames", mFrameCount,
mNormalFrameCount);
- delete[] mMixBuffer;
+ delete[] mSinkBuffer;
size_t normalBufferSize = mNormalFrameCount * mFrameSize;
- // For historical reasons mMixBuffer is int16_t[], but mFrameSize can be odd (such as 1)
- mMixBuffer = new int16_t[(normalBufferSize + 1) >> 1];
- memset(mMixBuffer, 0, normalBufferSize);
+ // For historical reasons mSinkBuffer is int16_t[], but mFrameSize can be odd (such as 1)
+ mSinkBuffer = new int16_t[(normalBufferSize + 1) >> 1];
+ memset(mSinkBuffer, 0, normalBufferSize);
+
+ // We resize the mMixerBuffer according to the requirements of the sink buffer which
+ // drives the output.
+ free(mMixerBuffer);
+ mMixerBuffer = NULL;
+ if (mMixerBufferEnabled) {
+ mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; // also valid: AUDIO_FORMAT_PCM_16_BIT.
+ mMixerBufferSize = mNormalFrameCount * mChannelCount
+ * audio_bytes_per_sample(mMixerBufferFormat);
+ (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize);
+ }
// force reconfiguration of effect chains and engines to take new buffer size and audio
// parameters into account
- // Note that mLock is not held when readOutputParameters() is called from the constructor
+ // Note that mLock is not held when readOutputParameters_l() is called from the constructor
// but in this case nothing is done below as no audio sessions have effect yet so it doesn't
// matter.
// create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains
@@ -1666,7 +1795,7 @@
}
-status_t AudioFlinger::PlaybackThread::getRenderPosition(size_t *halFrames, size_t *dspFrames)
+status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
{
if (halFrames == NULL || dspFrames == NULL) {
return BAD_VALUE;
@@ -1684,7 +1813,11 @@
*dspFrames = framesWritten >= latencyFrames ? framesWritten - latencyFrames : 0;
return NO_ERROR;
} else {
- return mOutput->stream->get_render_position(mOutput->stream, dspFrames);
+ status_t status;
+ uint32_t frames;
+ status = mOutput->stream->get_render_position(mOutput->stream, &frames);
+ *dspFrames = (size_t)frames;
+ return status;
}
}
@@ -1843,7 +1976,7 @@
(pipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
}
}
- ssize_t framesWritten = mNormalSink->write(mMixBuffer + offset, count);
+ ssize_t framesWritten = mNormalSink->write(mSinkBuffer + offset, count);
ATRACE_END();
if (framesWritten > 0) {
bytesWritten = framesWritten << mBitShift;
@@ -1861,7 +1994,7 @@
// otherwise use the HAL / AudioStreamOut directly
} else {
// Direct output and offload threads
- size_t offset = (mCurrentWriteLength - mBytesRemaining) / sizeof(int16_t);
+ size_t offset = (mCurrentWriteLength - mBytesRemaining);
if (mUseAsyncWrite) {
ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request");
mWriteAckSequence += 2;
@@ -1872,7 +2005,7 @@
// FIXME We should have an implementation of timestamps for direct output threads.
// They are used e.g for multichannel PCM playback over HDMI.
bytesWritten = mOutput->stream->write(mOutput->stream,
- mMixBuffer + offset, mBytesRemaining);
+ (char *)mSinkBuffer + offset, mBytesRemaining);
if (mUseAsyncWrite &&
((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
// do not wait for async callback in case of error of full write
@@ -1884,7 +2017,7 @@
mNumWrites++;
mInWrite = false;
-
+ mStandby = false;
return bytesWritten;
}
@@ -1911,7 +2044,7 @@
/*
The derived values that are cached:
- - mixBufferSize from frame count * frame size
+ - mSinkBufferSize from frame count * frame size
- activeSleepTime from activeSleepTimeUs()
- idleSleepTime from idleSleepTimeUs()
- standbyDelay from mActiveSleepTimeUs (DIRECT only)
@@ -1930,7 +2063,7 @@
void AudioFlinger::PlaybackThread::cacheParameters_l()
{
- mixBufferSize = mNormalFrameCount * mFrameSize;
+ mSinkBufferSize = mNormalFrameCount * mFrameSize;
activeSleepTime = activeSleepTimeUs();
idleSleepTime = idleSleepTimeUs();
}
@@ -1953,13 +2086,13 @@
status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
{
int session = chain->sessionId();
- int16_t *buffer = mMixBuffer;
+ int16_t *buffer = mSinkBuffer;
bool ownsBuffer = false;
ALOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
if (session > 0) {
// Only one effect chain can be present in direct output thread and it uses
- // the mix buffer as input
+ // the sink buffer as input
if (mType != DIRECT) {
size_t numSamples = mNormalFrameCount * mChannelCount;
buffer = new int16_t[numSamples];
@@ -1993,7 +2126,7 @@
}
chain->setInBuffer(buffer, ownsBuffer);
- chain->setOutBuffer(mMixBuffer);
+ chain->setOutBuffer(mSinkBuffer);
// 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 AUDIO_SESSION_OUTPUT_MIX is inserted before
@@ -2043,7 +2176,7 @@
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> track = mTracks[i];
if (session == track->sessionId()) {
- track->setMainBuffer(mMixBuffer);
+ track->setMainBuffer(mSinkBuffer);
chain->decTrackCnt();
}
}
@@ -2106,6 +2239,8 @@
// FIXME could this be made local to while loop?
writeFrames = 0;
+ int lastGeneration = 0;
+
cacheParameters_l();
sleepTime = idleSleepTime;
@@ -2162,6 +2297,8 @@
break;
}
releaseWakeLock_l();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
ALOGV("wait async completion");
mWaitWorkCV.wait(mLock);
ALOGV("async completion/wake");
@@ -2192,6 +2329,8 @@
}
releaseWakeLock_l();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
// wait until we have something to do...
ALOGV("%s going to sleep", myName.string());
mWaitWorkCV.wait(mLock);
@@ -2216,41 +2355,74 @@
// mMixerStatusIgnoringFastTracks is also updated internally
mMixerStatus = prepareTracks_l(&tracksToRemove);
+ // compare with previously applied list
+ if (lastGeneration != mActiveTracksGeneration) {
+ // update wakelock
+ updateWakeLockUids_l(mWakeLockUids);
+ lastGeneration = mActiveTracksGeneration;
+ }
+
// prevent any changes in effect chain list and in each effect chain
// during mixing and effect process as the audio buffers could be deleted
// or modified if an effect is created or deleted
lockEffectChains_l(effectChains);
- }
+ } // mLock scope ends
if (mBytesRemaining == 0) {
mCurrentWriteLength = 0;
if (mMixerStatus == MIXER_TRACKS_READY) {
// threadLoop_mix() sets mCurrentWriteLength
threadLoop_mix();
+
+ // Merge mMixerBuffer data into mSinkBuffer
+ // This is done pre-effects computation; if effects change to
+ // support higher precision, this needs to move.
+ //
+ // mMixerBufferValid is only set true by MixerThread::prepareTracks_l().
+ if (mMixerBufferValid) {
+ if (mMixerBufferFormat == AUDIO_FORMAT_PCM_FLOAT) {
+ memcpy_to_i16_from_float(mSinkBuffer,
+ reinterpret_cast<float*>(mMixerBuffer),
+ mNormalFrameCount * mChannelCount);
+ } else { // mMixerBufferFormat == AUDIO_FORMAT_PCM_16_BIT
+ memcpy(mSinkBuffer,
+ mMixerBuffer,
+ mNormalFrameCount * mChannelCount * sizeof(int16_t));
+ }
+ }
} else if ((mMixerStatus != MIXER_DRAIN_TRACK)
&& (mMixerStatus != MIXER_DRAIN_ALL)) {
// threadLoop_sleepTime sets sleepTime to 0 if data
// must be written to HAL
threadLoop_sleepTime();
if (sleepTime == 0) {
- mCurrentWriteLength = mixBufferSize;
+ mCurrentWriteLength = mSinkBufferSize;
}
}
mBytesRemaining = mCurrentWriteLength;
if (isSuspended()) {
sleepTime = suspendSleepTimeUs();
// simulate write to HAL when suspended
- mBytesWritten += mixBufferSize;
+ mBytesWritten += mSinkBufferSize;
mBytesRemaining = 0;
}
// only process effects if we're going to write
- if (sleepTime == 0) {
+ if (sleepTime == 0 && mType != OFFLOAD) {
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();
}
}
}
+ // Process effect chains for offloaded thread even if no audio
+ // was read from audio track: process only updates effect state
+ // and thus does have to be synchronized with audio writes but may have
+ // to be called while waiting for async write callback
+ if (mType == OFFLOAD) {
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ }
// enable changes in effect chain
unlockEffectChains(effectChains);
@@ -2270,22 +2442,21 @@
(mMixerStatus == MIXER_DRAIN_ALL)) {
threadLoop_drain();
}
-if (mType == MIXER) {
- // write blocked detection
- nsecs_t now = systemTime();
- nsecs_t delta = now - mLastWriteTime;
- if (!mStandby && delta > maxPeriod) {
- mNumDelayedWrites++;
- if ((now - lastWarning) > kWarningThrottleNs) {
- ATRACE_NAME("underrun");
- ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
- ns2ms(delta), mNumDelayedWrites, this);
- lastWarning = now;
+ if (mType == MIXER) {
+ // write blocked detection
+ nsecs_t now = systemTime();
+ nsecs_t delta = now - mLastWriteTime;
+ if (!mStandby && delta > maxPeriod) {
+ mNumDelayedWrites++;
+ if ((now - lastWarning) > kWarningThrottleNs) {
+ ATRACE_NAME("underrun");
+ ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
+ ns2ms(delta), mNumDelayedWrites, this);
+ lastWarning = now;
+ }
}
}
-}
- mStandby = false;
} else {
usleep(sleepTime);
}
@@ -2321,6 +2492,8 @@
}
releaseWakeLock();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
ALOGV("Thread %p type %d exiting", this, mType);
return false;
@@ -2334,6 +2507,8 @@
for (size_t i=0 ; i<count ; i++) {
const sp<Track>& track = tracksToRemove.itemAt(i);
mActiveTracks.remove(track);
+ mWakeLockUids.remove(track->uid());
+ mActiveTracksGeneration++;
ALOGV("removeTracks_l removing track on session %d", track->sessionId());
sp<EffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
@@ -2632,12 +2807,6 @@
PlaybackThread::threadLoop_standby();
}
-// Empty implementation for standard mixer
-// Overridden for offloaded playback
-void AudioFlinger::PlaybackThread::flushOutput_l()
-{
-}
-
bool AudioFlinger::PlaybackThread::waitingAsyncCallback_l()
{
return false;
@@ -2669,6 +2838,12 @@
}
}
+void AudioFlinger::PlaybackThread::onAddNewTrack_l()
+{
+ ALOGV("signal playback thread");
+ broadcast_l();
+}
+
void AudioFlinger::MixerThread::threadLoop_mix()
{
// obtain the presentation timestamp of the next output buffer
@@ -2687,7 +2862,7 @@
// mix buffers...
mAudioMixer->process(pts);
- mCurrentWriteLength = mixBufferSize;
+ mCurrentWriteLength = mSinkBufferSize;
// increase sleep time progressively when application underrun condition clears.
// Only increase sleep time if the mixer is ready for two consecutive times to avoid
// that a steady state of alternating ready/not ready conditions keeps the sleep time
@@ -2721,7 +2896,7 @@
sleepTime = idleSleepTime;
}
} else if (mBytesWritten != 0 || (mMixerStatus == MIXER_TRACKS_ENABLED)) {
- memset(mMixBuffer, 0, mixBufferSize);
+ memset(mSinkBuffer, 0, mSinkBufferSize);
sleepTime = 0;
ALOGV_IF(mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED),
"anticipated start");
@@ -2768,6 +2943,8 @@
state = sq->begin();
}
+ mMixerBufferValid = false; // mMixerBuffer has no valid data until appropriate tracks found.
+
for (size_t i=0 ; i<count ; i++) {
const sp<Track> t = mActiveTracks[i].promote();
if (t == 0) {
@@ -2896,7 +3073,6 @@
VolumeProvider *vp = track;
fastTrack->mBufferProvider = eabp;
fastTrack->mVolumeProvider = vp;
- fastTrack->mSampleRate = track->mSampleRate;
fastTrack->mChannelMask = track->mChannelMask;
fastTrack->mGeneration++;
state->mTrackMask |= 1 << j;
@@ -2949,25 +3125,20 @@
// add frames already consumed but not yet released by the resampler
// because mAudioTrackServerProxy->framesReady() will include these frames
desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
+#if 0
// the minimum track buffer size is normally twice the number of frames necessary
// to fill one buffer and the resampler should not leave more than one buffer worth
// of unreleased frames after each pass, but just in case...
ALOG_ASSERT(desiredFrames <= cblk->frameCount_);
+#endif
}
uint32_t minFrames = 1;
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
minFrames = desiredFrames;
}
- // It's not safe to call framesReady() for a static buffer track, so assume it's ready
- size_t framesReady;
- if (track->sharedBuffer() == 0) {
- framesReady = track->framesReady();
- } else if (track->isStopped()) {
- framesReady = 0;
- } else {
- framesReady = 1;
- }
+
+ size_t framesReady = track->framesReady();
if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
@@ -2975,10 +3146,11 @@
mixedTracks++;
- // track->mainBuffer() != mMixBuffer means there is an effect chain
- // connected to the track
+ // track->mainBuffer() != mSinkBuffer or mMixerBuffer means
+ // there is an effect chain connected to the track
chain.clear();
- if (track->mainBuffer() != mMixBuffer) {
+ if (track->mainBuffer() != mSinkBuffer &&
+ track->mainBuffer() != mMixerBuffer) {
chain = getEffectChain_l(track->sessionId());
// Delegate volume control to effect in track effect chain if needed
if (chain != 0) {
@@ -3080,9 +3252,9 @@
mAudioMixer->setBufferProvider(name, track);
mAudioMixer->enable(name);
- mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)vl);
- mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)vr);
- mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)va);
+ mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)(uintptr_t)vl);
+ mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)(uintptr_t)vr);
+ mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)(uintptr_t)va);
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
@@ -3090,7 +3262,7 @@
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
- AudioMixer::CHANNEL_MASK, (void *)track->channelMask());
+ AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());
// limit track sample rate to 2 x output sample rate, which changes at re-configuration
uint32_t maxSampleRate = mSampleRate * 2;
uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate();
@@ -3103,11 +3275,41 @@
name,
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
- (void *)reqSampleRate);
- mAudioMixer->setParameter(
- name,
- AudioMixer::TRACK,
- AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
+ (void *)(uintptr_t)reqSampleRate);
+ /*
+ * Select the appropriate output buffer for the track.
+ *
+ * For tracks with effects, only mSinkBuffer can be used (at this time).
+ *
+ * Other tracks can use mMixerBuffer for higher precision
+ * channel accumulation. If this buffer is enabled
+ * (mMixerBufferEnabled true), then selected tracks will accumulate
+ * into it.
+ *
+ */
+ if (mMixerBufferEnabled
+ && (track->mainBuffer() == mSinkBuffer
+ || track->mainBuffer() == mMixerBuffer)) {
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer);
+ // TODO: override track->mainBuffer()?
+ mMixerBufferValid = true;
+ } else {
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::MIXER_FORMAT, (void *)AUDIO_FORMAT_PCM_16_BIT);
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
+ }
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
@@ -3221,13 +3423,25 @@
// remove all the tracks that need to be...
removeTracks_l(*tracksToRemove);
- // mix buffer must be cleared if all tracks are connected to an
- // effect chain as in this case the mixer will not write to
- // mix buffer and track effects will accumulate into it
+ // sink or mix buffer must be cleared if all tracks are connected to an
+ // effect chain as in this case the mixer will not write to the sink or mix buffer
+ // and track effects will accumulate into it
if ((mBytesRemaining == 0) && ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
(mixedTracks == 0 && fastTracks > 0))) {
// FIXME as a performance optimization, should remember previous zero status
- memset(mMixBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
+ if (mMixerBufferValid) {
+ memset(mMixerBuffer, 0, mMixerBufferSize);
+ // TODO: In testing, mSinkBuffer below need not be cleared because
+ // the PlaybackThread::threadLoop() copies mMixerBuffer into mSinkBuffer
+ // after mixing.
+ //
+ // To enforce this guarantee:
+ // ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
+ // (mixedTracks == 0 && fastTracks > 0))
+ // must imply MIXER_TRACKS_READY.
+ // Later, we may clear buffers regardless, and skip much of this logic.
+ }
+ memset(mSinkBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
}
// if any fast tracks, then status is ready
@@ -3352,7 +3566,7 @@
keyValuePair.string());
}
if (status == NO_ERROR && reconfig) {
- readOutputParameters();
+ readOutputParameters_l();
delete mAudioMixer;
mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
for (size_t i = 0; i < mTracks.size() ; i++) {
@@ -3397,9 +3611,7 @@
PlaybackThread::dumpInternals(fd, args);
- snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames());
- result.append(buffer);
- write(fd, result.string(), result.size());
+ fdprintf(fd, " AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames());
// Make a non-atomic copy of fast mixer dump state so it won't change underneath us
const FastMixerDumpState copy(mFastMixerDumpState);
@@ -3531,6 +3743,12 @@
Track* const track = t.get();
audio_track_cblk_t* cblk = track->cblk();
+ // Only consider last track started for volume and mixer state control.
+ // In theory an older track could underrun and restart after the new one starts
+ // but as we only care about the transition phase between two tracks on a
+ // direct output, it is not a problem to ignore the underrun case.
+ sp<Track> l = mLatestActiveTrack.promote();
+ bool last = l.get() == track;
// The first time a track is added we wait
// for all its buffers to be filled before processing it
@@ -3540,11 +3758,6 @@
} else {
minFrames = 1;
}
- // Only consider last track started for volume and mixer state control.
- // This is the last entry in mActiveTracks unless a track underruns.
- // As we only care about the transition phase between two tracks on a
- // direct output, it is not a problem to ignore the underrun case.
- bool last = (i == (count - 1));
if ((track->framesReady() >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
@@ -3571,7 +3784,7 @@
} else {
// clear effect chain input buffer if the last active track started underruns
// to avoid sending previous audio buffer again to effects
- if (!mEffectChains.isEmpty() && (i == (count -1))) {
+ if (!mEffectChains.isEmpty() && last) {
mEffectChains[0]->clearInputBuffer();
}
@@ -3583,7 +3796,8 @@
// TODO: implement behavior for compressed audio
size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
size_t framesWritten = mBytesWritten / mFrameSize;
- if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
+ if (mStandby || !last ||
+ track->presentationComplete(framesWritten, audioHALFrames)) {
if (track->isStopped()) {
track->reset();
}
@@ -3596,6 +3810,9 @@
if (--(track->mRetryCount) <= 0) {
ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
tracksToRemove->add(track);
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
} else if (last) {
mixerStatus = MIXER_TRACKS_ENABLED;
}
@@ -3612,7 +3829,7 @@
void AudioFlinger::DirectOutputThread::threadLoop_mix()
{
size_t frameCount = mFrameCount;
- int8_t *curBuf = (int8_t *)mMixBuffer;
+ int8_t *curBuf = (int8_t *)mSinkBuffer;
// output audio to hardware
while (frameCount) {
AudioBufferProvider::Buffer buffer;
@@ -3627,7 +3844,7 @@
curBuf += buffer.frameCount * mFrameSize;
mActiveTrack->releaseBuffer(&buffer);
}
- mCurrentWriteLength = curBuf - (int8_t *)mMixBuffer;
+ mCurrentWriteLength = curBuf - (int8_t *)mSinkBuffer;
sleepTime = 0;
standbyTime = systemTime() + standbyDelay;
mActiveTrack.clear();
@@ -3642,20 +3859,20 @@
sleepTime = idleSleepTime;
}
} else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
- memset(mMixBuffer, 0, mFrameCount * mFrameSize);
+ memset(mSinkBuffer, 0, mFrameCount * mFrameSize);
sleepTime = 0;
}
}
// getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask,
- int sessionId)
+int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused,
+ int sessionId __unused)
{
return 0;
}
// deleteTrackName_l() must be called with ThreadBase::mLock held
-void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name)
+void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name __unused)
{
}
@@ -3691,7 +3908,7 @@
keyValuePair.string());
}
if (status == NO_ERROR && reconfig) {
- readOutputParameters();
+ readOutputParameters_l();
sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
}
}
@@ -3756,9 +3973,9 @@
// ----------------------------------------------------------------------------
AudioFlinger::AsyncCallbackThread::AsyncCallbackThread(
- const sp<AudioFlinger::OffloadThread>& offloadThread)
+ const wp<AudioFlinger::PlaybackThread>& playbackThread)
: Thread(false /*canCallJava*/),
- mOffloadThread(offloadThread),
+ mPlaybackThread(playbackThread),
mWriteAckSequence(0),
mDrainSequence(0)
{
@@ -3781,7 +3998,12 @@
{
Mutex::Autolock _l(mLock);
- mWaitWorkCV.wait(mLock);
+ while (!((mWriteAckSequence & 1) ||
+ (mDrainSequence & 1) ||
+ exitPending())) {
+ mWaitWorkCV.wait(mLock);
+ }
+
if (exitPending()) {
break;
}
@@ -3793,13 +4015,13 @@
mDrainSequence &= ~1;
}
{
- sp<AudioFlinger::OffloadThread> offloadThread = mOffloadThread.promote();
- if (offloadThread != 0) {
+ sp<AudioFlinger::PlaybackThread> playbackThread = mPlaybackThread.promote();
+ if (playbackThread != 0) {
if (writeAckSequence & 1) {
- offloadThread->resetWriteBlocked(writeAckSequence >> 1);
+ playbackThread->resetWriteBlocked(writeAckSequence >> 1);
}
if (drainSequence & 1) {
- offloadThread->resetDraining(drainSequence >> 1);
+ playbackThread->resetDraining(drainSequence >> 1);
}
}
}
@@ -3855,14 +4077,11 @@
AudioStreamOut* output, audio_io_handle_t id, uint32_t device)
: DirectOutputThread(audioFlinger, output, id, device, OFFLOAD),
mHwPaused(false),
+ mFlushPending(false),
mPausedBytesRemaining(0)
{
- mCallbackThread = new AudioFlinger::AsyncCallbackThread(this);
-}
-
-AudioFlinger::OffloadThread::~OffloadThread()
-{
- mPreviousTrack.clear();
+ //FIXME: mStandby should be set to true by ThreadBase constructor
+ mStandby = true;
}
void AudioFlinger::OffloadThread::threadLoop_exit()
@@ -3899,24 +4118,24 @@
}
Track* const track = t.get();
audio_track_cblk_t* cblk = track->cblk();
- if (mPreviousTrack != NULL) {
- if (t != mPreviousTrack) {
- // Flush any data still being written from last track
- mBytesRemaining = 0;
- if (mPausedBytesRemaining) {
- // Last track was paused so we also need to flush saved
- // mixbuffer state and invalidate track so that it will
- // re-submit that unwritten data when it is next resumed
- mPausedBytesRemaining = 0;
- // Invalidate is a bit drastic - would be more efficient
- // to have a flag to tell client that some of the
- // previously written data was lost
- mPreviousTrack->invalidate();
- }
- }
+ // Only consider last track started for volume and mixer state control.
+ // In theory an older track could underrun and restart after the new one starts
+ // but as we only care about the transition phase between two tracks on a
+ // direct output, it is not a problem to ignore the underrun case.
+ sp<Track> l = mLatestActiveTrack.promote();
+ bool last = l.get() == track;
+
+ if (track->isInvalid()) {
+ ALOGW("An invalidated track shouldn't be in active list");
+ tracksToRemove->add(track);
+ continue;
}
- mPreviousTrack = t;
- bool last = (i == (count - 1));
+
+ if (track->mState == TrackBase::IDLE) {
+ ALOGW("An idle track shouldn't be in active list");
+ continue;
+ }
+
if (track->isPausing()) {
track->setPaused();
if (last) {
@@ -3935,6 +4154,11 @@
mBytesRemaining = 0; // stop writing
}
tracksToRemove->add(track);
+ } else if (track->isFlushPending()) {
+ track->flushAck();
+ if (last) {
+ mFlushPending = true;
+ }
} else if (track->framesReady() && track->isReady() &&
!track->isPaused() && !track->isTerminated() && !track->isStopping_2()) {
ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer);
@@ -3964,6 +4188,30 @@
}
if (last) {
+ sp<Track> previousTrack = mPreviousTrack.promote();
+ if (previousTrack != 0) {
+ if (track != previousTrack.get()) {
+ // Flush any data still being written from last track
+ mBytesRemaining = 0;
+ if (mPausedBytesRemaining) {
+ // Last track was paused so we also need to flush saved
+ // mixbuffer state and invalidate track so that it will
+ // re-submit that unwritten data when it is next resumed
+ mPausedBytesRemaining = 0;
+ // Invalidate is a bit drastic - would be more efficient
+ // to have a flag to tell client that some of the
+ // previously written data was lost
+ previousTrack->invalidate();
+ }
+ // flush data already sent to the DSP if changing audio session as audio
+ // comes from a different source. Also invalidate previous track to force a
+ // seek when resuming.
+ if (previousTrack->sessionId() != track->sessionId()) {
+ previousTrack->invalidate();
+ }
+ }
+ }
+ mPreviousTrack = track;
// reset retry count
track->mRetryCount = kMaxTrackRetriesOffload;
mActiveTrack = t;
@@ -3980,22 +4228,27 @@
// has been written
ALOGV("OffloadThread: underrun and STOPPING_1 -> draining, STOPPING_2");
track->mState = TrackBase::STOPPING_2; // so presentation completes after drain
- if (last) {
- sleepTime = 0;
- standbyTime = systemTime() + standbyDelay;
- mixerStatus = MIXER_DRAIN_TRACK;
- mDrainSequence += 2;
+ // do not drain if no data was ever sent to HAL (mStandby == true)
+ if (last && !mStandby) {
+ // do not modify drain sequence if we are already draining. This happens
+ // when resuming from pause after drain.
+ if ((mDrainSequence & 1) == 0) {
+ sleepTime = 0;
+ standbyTime = systemTime() + standbyDelay;
+ mixerStatus = MIXER_DRAIN_TRACK;
+ mDrainSequence += 2;
+ }
if (mHwPaused) {
// It is possible to move from PAUSED to STOPPING_1 without
// a resume so we must ensure hardware is running
- mOutput->stream->resume(mOutput->stream);
+ doHwResume = true;
mHwPaused = false;
}
}
}
} else if (track->isStopping_2()) {
- // Drain has completed, signal presentation complete
- if (!(mDrainSequence & 1) || !last) {
+ // Drain has completed or we are in standby, signal presentation complete
+ if (!(mDrainSequence & 1) || !last || mStandby) {
track->mState = TrackBase::STOPPED;
size_t audioHALFrames =
(mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
@@ -4012,6 +4265,9 @@
ALOGV("OffloadThread: BUFFER TIMEOUT: remove(%d) from active list",
track->name());
tracksToRemove->add(track);
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
} else if (last){
mixerStatus = MIXER_TRACKS_ENABLED;
}
@@ -4021,15 +4277,18 @@
processVolume_l(track, last);
}
- // make sure the pause/flush/resume sequence is executed in the right order
- if (doHwPause) {
+ // make sure the pause/flush/resume sequence is executed in the right order.
+ // If a flush is pending and a track is active but the HW is not paused, force a HW pause
+ // before flush and then resume HW. This can happen in case of pause/flush/resume
+ // if resume is received before pause is executed.
+ if (!mStandby && (doHwPause || (mFlushPending && !mHwPaused && (count != 0)))) {
mOutput->stream->pause(mOutput->stream);
}
if (mFlushPending) {
flushHw_l();
mFlushPending = false;
}
- if (doHwResume) {
+ if (!mStandby && doHwResume) {
mOutput->stream->resume(mOutput->stream);
}
@@ -4039,11 +4298,6 @@
return mixerStatus;
}
-void AudioFlinger::OffloadThread::flushOutput_l()
-{
- mFlushPending = true;
-}
-
// must be called with thread mutex locked
bool AudioFlinger::OffloadThread::waitingAsyncCallback_l()
{
@@ -4058,15 +4312,15 @@
// must be called with thread mutex locked
bool AudioFlinger::OffloadThread::shouldStandby_l()
{
- bool TrackPaused = false;
+ bool trackPaused = false;
// do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack
// after a timeout and we will enter standby then.
if (mTracks.size() > 0) {
- TrackPaused = mTracks[mTracks.size() - 1]->isPaused();
+ trackPaused = mTracks[mTracks.size() - 1]->isPaused();
}
- return !mStandby && !TrackPaused;
+ return !mStandby && !trackPaused;
}
@@ -4084,6 +4338,8 @@
mBytesRemaining = 0;
mPausedWriteLength = 0;
mPausedBytesRemaining = 0;
+ mHwPaused = false;
+
if (mUseAsyncWrite) {
// discard any pending drain or write ack by incrementing sequence
mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
@@ -4094,6 +4350,18 @@
}
}
+void AudioFlinger::OffloadThread::onAddNewTrack_l()
+{
+ sp<Track> previousTrack = mPreviousTrack.promote();
+ sp<Track> latestTrack = mLatestActiveTrack.promote();
+
+ if (previousTrack != 0 && latestTrack != 0 &&
+ (previousTrack->sessionId() != latestTrack->sessionId())) {
+ mFlushPending = true;
+ }
+ PlaybackThread::onAddNewTrack_l();
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
@@ -4118,11 +4386,11 @@
if (outputsReady(outputTracks)) {
mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
} else {
- memset(mMixBuffer, 0, mixBufferSize);
+ memset(mSinkBuffer, 0, mSinkBufferSize);
}
sleepTime = 0;
writeFrames = mNormalFrameCount;
- mCurrentWriteLength = mixBufferSize;
+ mCurrentWriteLength = mSinkBufferSize;
standbyTime = systemTime() + standbyDelay;
}
@@ -4137,7 +4405,7 @@
} else if (mBytesWritten != 0) {
if (mMixerStatus == MIXER_TRACKS_ENABLED) {
writeFrames = mNormalFrameCount;
- memset(mMixBuffer, 0, mixBufferSize);
+ memset(mSinkBuffer, 0, mSinkBufferSize);
} else {
// flush remaining overflow buffers in output tracks
writeFrames = 0;
@@ -4149,9 +4417,10 @@
ssize_t AudioFlinger::DuplicatingThread::threadLoop_write()
{
for (size_t i = 0; i < outputTracks.size(); i++) {
- outputTracks[i]->write(mMixBuffer, writeFrames);
+ outputTracks[i]->write(mSinkBuffer, writeFrames);
}
- return (ssize_t)mixBufferSize;
+ mStandby = false;
+ return (ssize_t)mSinkBufferSize;
}
void AudioFlinger::DuplicatingThread::threadLoop_standby()
@@ -4182,7 +4451,8 @@
mSampleRate,
mFormat,
mChannelMask,
- frameCount);
+ frameCount,
+ IPCThreadState::self()->getCallingUid());
if (outputTrack->cblk() != NULL) {
thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
mOutputTracks.add(outputTrack);
@@ -4261,8 +4531,6 @@
AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamIn *input,
- uint32_t sampleRate,
- audio_channel_mask_t channelMask,
audio_io_handle_t id,
audio_devices_t outDevice,
audio_devices_t inDevice
@@ -4271,28 +4539,24 @@
#endif
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD),
- mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
- // mRsmpInIndex set by readInputParameters()
- mReqChannelCount(popcount(channelMask)),
- mReqSampleRate(sampleRate)
- // mBytesRead is only meaningful while active, and so is cleared in start()
- // (but might be better to also clear here for dump?)
+ mInput(input), mActiveTracksGen(0), mRsmpInBuffer(NULL),
+ // mRsmpInFrames and mRsmpInFramesP2 are set by readInputParameters_l()
+ mRsmpInRear(0)
#ifdef TEE_SINK
, mTeeSink(teeSink)
#endif
{
snprintf(mName, kNameLength, "AudioIn_%X", id);
+ mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
- readInputParameters();
-
+ readInputParameters_l();
}
AudioFlinger::RecordThread::~RecordThread()
{
+ mAudioFlinger->unregisterWriter(mNBLogWriter);
delete[] mRsmpInBuffer;
- delete mResampler;
- delete[] mRsmpOutBuffer;
}
void AudioFlinger::RecordThread::onFirstRef()
@@ -4302,232 +4566,383 @@
bool AudioFlinger::RecordThread::threadLoop()
{
- AudioBufferProvider::Buffer buffer;
-
nsecs_t lastWarning = 0;
inputStandBy();
- acquireWakeLock();
- // used to verify we've read at least once before evaluating how many bytes were read
- bool readOnce = false;
+reacquire_wakelock:
+ sp<RecordTrack> activeTrack;
+ int activeTracksGen;
+ {
+ Mutex::Autolock _l(mLock);
+ size_t size = mActiveTracks.size();
+ activeTracksGen = mActiveTracksGen;
+ if (size > 0) {
+ // FIXME an arbitrary choice
+ activeTrack = mActiveTracks[0];
+ acquireWakeLock_l(activeTrack->uid());
+ if (size > 1) {
+ SortedVector<int> tmp;
+ for (size_t i = 0; i < size; i++) {
+ tmp.add(mActiveTracks[i]->uid());
+ }
+ updateWakeLockUids_l(tmp);
+ }
+ } else {
+ acquireWakeLock_l(-1);
+ }
+ }
// used to request a deferred sleep, to be executed later while mutex is unlocked
- bool doSleep = false;
+ uint32_t sleepUs = 0;
- // start recording
+ // loop while there is work to do
for (;;) {
- sp<RecordTrack> activeTrack;
- TrackBase::track_state activeTrackState;
Vector< sp<EffectChain> > effectChains;
// sleep with mutex unlocked
- if (doSleep) {
- doSleep = false;
- usleep(kRecordThreadSleepUs);
+ if (sleepUs > 0) {
+ usleep(sleepUs);
+ sleepUs = 0;
}
+ // activeTracks accumulates a copy of a subset of mActiveTracks
+ Vector< sp<RecordTrack> > activeTracks;
+
{ // scope for mLock
Mutex::Autolock _l(mLock);
- if (exitPending()) {
- break;
- }
+
processConfigEvents_l();
// return value 'reconfig' is currently unused
bool reconfig = checkForNewParameters_l();
- // make a stable copy of mActiveTrack
- activeTrack = mActiveTrack;
- if (activeTrack == 0) {
- standby();
+
+ // check exitPending here because checkForNewParameters_l() and
+ // checkForNewParameters_l() can temporarily release mLock
+ if (exitPending()) {
+ break;
+ }
+
+ // if no active track(s), then standby and release wakelock
+ size_t size = mActiveTracks.size();
+ if (size == 0) {
+ standbyIfNotAlreadyInStandby();
// exitPending() can't become true here
releaseWakeLock_l();
ALOGV("RecordThread: loop stopping");
// go to sleep
mWaitWorkCV.wait(mLock);
ALOGV("RecordThread: loop starting");
- acquireWakeLock_l();
- continue;
+ goto reacquire_wakelock;
}
- if (activeTrack->isTerminated()) {
- removeTrack_l(activeTrack);
- mActiveTrack.clear();
- continue;
+ if (mActiveTracksGen != activeTracksGen) {
+ activeTracksGen = mActiveTracksGen;
+ SortedVector<int> tmp;
+ for (size_t i = 0; i < size; i++) {
+ tmp.add(mActiveTracks[i]->uid());
+ }
+ updateWakeLockUids_l(tmp);
}
- activeTrackState = activeTrack->mState;
- switch (activeTrackState) {
- case TrackBase::PAUSING:
- standby();
- mActiveTrack.clear();
- mStartStopCond.broadcast();
- doSleep = true;
- continue;
+ bool doBroadcast = false;
+ for (size_t i = 0; i < size; ) {
- case TrackBase::RESUMING:
- mStandby = false;
- if (mReqChannelCount != activeTrack->channelCount()) {
- mActiveTrack.clear();
- mStartStopCond.broadcast();
+ activeTrack = mActiveTracks[i];
+ if (activeTrack->isTerminated()) {
+ removeTrack_l(activeTrack);
+ mActiveTracks.remove(activeTrack);
+ mActiveTracksGen++;
+ size--;
continue;
}
- if (readOnce) {
- mStartStopCond.broadcast();
- // record start succeeds only if first read from audio input succeeds
- if (mBytesRead < 0) {
- mActiveTrack.clear();
- continue;
- }
+
+ TrackBase::track_state activeTrackState = activeTrack->mState;
+ switch (activeTrackState) {
+
+ case TrackBase::PAUSING:
+ mActiveTracks.remove(activeTrack);
+ mActiveTracksGen++;
+ doBroadcast = true;
+ size--;
+ continue;
+
+ case TrackBase::STARTING_1:
+ sleepUs = 10000;
+ i++;
+ continue;
+
+ case TrackBase::STARTING_2:
+ doBroadcast = true;
+ mStandby = false;
activeTrack->mState = TrackBase::ACTIVE;
+ break;
+
+ case TrackBase::ACTIVE:
+ break;
+
+ case TrackBase::IDLE:
+ i++;
+ continue;
+
+ default:
+ LOG_FATAL("Unexpected activeTrackState %d", activeTrackState);
}
- break;
- case TrackBase::ACTIVE:
- break;
+ activeTracks.add(activeTrack);
+ i++;
- case TrackBase::IDLE:
- doSleep = true;
- continue;
-
- default:
- LOG_FATAL("Unexpected activeTrackState %d", activeTrackState);
}
+ if (doBroadcast) {
+ mStartStopCond.broadcast();
+ }
+
+ // sleep if there are no active tracks to process
+ if (activeTracks.size() == 0) {
+ if (sleepUs == 0) {
+ sleepUs = kRecordThreadSleepUs;
+ }
+ continue;
+ }
+ sleepUs = 0;
lockEffectChains_l(effectChains);
}
- // thread mutex is now unlocked, mActiveTrack unknown, activeTrack != 0, kept, immutable
- // activeTrack->mState unknown, activeTrackState immutable and is ACTIVE or RESUMING
+ // thread mutex is now unlocked, mActiveTracks unknown, activeTracks.size() > 0
- for (size_t i = 0; i < effectChains.size(); i ++) {
+ size_t size = effectChains.size();
+ for (size_t i = 0; i < size; i++) {
// thread mutex is not locked, but effect chain is locked
effectChains[i]->process_l();
}
- buffer.frameCount = mFrameCount;
- status_t status = activeTrack->getNextBuffer(&buffer);
- if (status == NO_ERROR) {
- readOnce = true;
- size_t framesOut = buffer.frameCount;
- if (mResampler == NULL) {
- // no resampling
- while (framesOut) {
- size_t framesIn = mFrameCount - mRsmpInIndex;
- if (framesIn > 0) {
- int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize;
- int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) *
- activeTrack->mFrameSize;
- if (framesIn > framesOut) {
- framesIn = framesOut;
- }
- mRsmpInIndex += framesIn;
- framesOut -= framesIn;
- if (mChannelCount == mReqChannelCount) {
- memcpy(dst, src, framesIn * mFrameSize);
- } else {
- if (mChannelCount == 1) {
- upmix_to_stereo_i16_from_mono_i16((int16_t *)dst,
- (int16_t *)src, framesIn);
- } else {
- downmix_to_mono_i16_from_stereo_i16((int16_t *)dst,
- (int16_t *)src, framesIn);
- }
- }
- }
- if (framesOut > 0 && mFrameCount == mRsmpInIndex) {
- void *readInto;
- if (framesOut == mFrameCount && mChannelCount == mReqChannelCount) {
- readInto = buffer.raw;
- framesOut = 0;
- } else {
- readInto = mRsmpInBuffer;
- mRsmpInIndex = 0;
- }
- mBytesRead = mInput->stream->read(mInput->stream, readInto,
- mBufferSize);
- if (mBytesRead <= 0) {
- // TODO: verify that it's benign to use a stale track state
- if ((mBytesRead < 0) && (activeTrackState == TrackBase::ACTIVE))
- {
- ALOGE("Error reading audio input");
- // Force input into standby so that it tries to
- // recover at next read attempt
- inputStandBy();
- doSleep = true;
- }
- mRsmpInIndex = mFrameCount;
- framesOut = 0;
- buffer.frameCount = 0;
- }
-#ifdef TEE_SINK
- else if (mTeeSink != 0) {
- (void) mTeeSink->write(readInto,
- mBytesRead >> Format_frameBitShift(mTeeSink->format()));
- }
-#endif
- }
- }
- } else {
- // resampling
+ // Read from HAL to keep up with fastest client if multiple active tracks, not slowest one.
+ // Only the client(s) that are too slow will overrun. But if even the fastest client is too
+ // slow, then this RecordThread will overrun by not calling HAL read often enough.
+ // If destination is non-contiguous, first read past the nominal end of buffer, then
+ // copy to the right place. Permitted because mRsmpInBuffer was over-allocated.
- // resampler accumulates, but we only have one source track
- memset(mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
- // alter output frame count as if we were expecting stereo samples
- if (mChannelCount == 1 && mReqChannelCount == 1) {
- framesOut >>= 1;
- }
- mResampler->resample(mRsmpOutBuffer, framesOut,
- this /* AudioBufferProvider* */);
- // ditherAndClamp() works as long as all buffers returned by
- // activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
- if (mChannelCount == 2 && mReqChannelCount == 1) {
- // temporarily type pun mRsmpOutBuffer from Q19.12 to int16_t
- ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
- // the resampler always outputs stereo samples:
- // do post stereo to mono conversion
- downmix_to_mono_i16_from_stereo_i16(buffer.i16, (int16_t *)mRsmpOutBuffer,
- framesOut);
- } else {
- ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
- }
- // now done with mRsmpOutBuffer
-
- }
- if (mFramestoDrop == 0) {
- activeTrack->releaseBuffer(&buffer);
- } else {
- if (mFramestoDrop > 0) {
- mFramestoDrop -= buffer.frameCount;
- if (mFramestoDrop <= 0) {
- clearSyncStartEvent();
- }
- } else {
- mFramestoDrop += buffer.frameCount;
- if (mFramestoDrop >= 0 || mSyncStartEvent == 0 ||
- mSyncStartEvent->isCancelled()) {
- ALOGW("Synced record %s, session %d, trigger session %d",
- (mFramestoDrop >= 0) ? "timed out" : "cancelled",
- activeTrack->sessionId(),
- (mSyncStartEvent != 0) ? mSyncStartEvent->triggerSession() : 0);
- clearSyncStartEvent();
- }
- }
- }
- activeTrack->clearOverflow();
+ int32_t rear = mRsmpInRear & (mRsmpInFramesP2 - 1);
+ ssize_t bytesRead = mInput->stream->read(mInput->stream,
+ &mRsmpInBuffer[rear * mChannelCount], mBufferSize);
+ if (bytesRead <= 0) {
+ ALOGE("read failed: bytesRead=%d < %u", bytesRead, mBufferSize);
+ // Force input into standby so that it tries to recover at next read attempt
+ inputStandBy();
+ sleepUs = kRecordThreadSleepUs;
+ continue;
}
- // client isn't retrieving buffers fast enough
- else {
- if (!activeTrack->setOverflow()) {
- nsecs_t now = systemTime();
- if ((now - lastWarning) > kWarningThrottleNs) {
- ALOGW("RecordThread: buffer overflow");
- lastWarning = now;
+ ALOG_ASSERT((size_t) bytesRead <= mBufferSize);
+ size_t framesRead = bytesRead / mFrameSize;
+ ALOG_ASSERT(framesRead > 0);
+ if (mTeeSink != 0) {
+ (void) mTeeSink->write(&mRsmpInBuffer[rear * mChannelCount], framesRead);
+ }
+ // If destination is non-contiguous, we now correct for reading past end of buffer.
+ size_t part1 = mRsmpInFramesP2 - rear;
+ if (framesRead > part1) {
+ memcpy(mRsmpInBuffer, &mRsmpInBuffer[mRsmpInFramesP2 * mChannelCount],
+ (framesRead - part1) * mFrameSize);
+ }
+ rear = mRsmpInRear += framesRead;
+
+ size = activeTracks.size();
+ // loop over each active track
+ for (size_t i = 0; i < size; i++) {
+ activeTrack = activeTracks[i];
+
+ enum {
+ OVERRUN_UNKNOWN,
+ OVERRUN_TRUE,
+ OVERRUN_FALSE
+ } overrun = OVERRUN_UNKNOWN;
+
+ // loop over getNextBuffer to handle circular sink
+ for (;;) {
+
+ activeTrack->mSink.frameCount = ~0;
+ status_t status = activeTrack->getNextBuffer(&activeTrack->mSink);
+ size_t framesOut = activeTrack->mSink.frameCount;
+ LOG_ALWAYS_FATAL_IF((status == OK) != (framesOut > 0));
+
+ int32_t front = activeTrack->mRsmpInFront;
+ ssize_t filled = rear - front;
+ size_t framesIn;
+
+ if (filled < 0) {
+ // should not happen, but treat like a massive overrun and re-sync
+ framesIn = 0;
+ activeTrack->mRsmpInFront = rear;
+ overrun = OVERRUN_TRUE;
+ } else if ((size_t) filled <= mRsmpInFrames) {
+ framesIn = (size_t) filled;
+ } else {
+ // client is not keeping up with server, but give it latest data
+ framesIn = mRsmpInFrames;
+ activeTrack->mRsmpInFront = front = rear - framesIn;
+ overrun = OVERRUN_TRUE;
+ }
+
+ if (framesOut == 0 || framesIn == 0) {
+ break;
+ }
+
+ if (activeTrack->mResampler == NULL) {
+ // no resampling
+ if (framesIn > framesOut) {
+ framesIn = framesOut;
+ } else {
+ framesOut = framesIn;
+ }
+ int8_t *dst = activeTrack->mSink.i8;
+ while (framesIn > 0) {
+ front &= mRsmpInFramesP2 - 1;
+ size_t part1 = mRsmpInFramesP2 - front;
+ if (part1 > framesIn) {
+ part1 = framesIn;
+ }
+ int8_t *src = (int8_t *)mRsmpInBuffer + (front * mFrameSize);
+ if (mChannelCount == activeTrack->mChannelCount) {
+ memcpy(dst, src, part1 * mFrameSize);
+ } else if (mChannelCount == 1) {
+ upmix_to_stereo_i16_from_mono_i16((int16_t *)dst, (int16_t *)src,
+ part1);
+ } else {
+ downmix_to_mono_i16_from_stereo_i16((int16_t *)dst, (int16_t *)src,
+ part1);
+ }
+ dst += part1 * activeTrack->mFrameSize;
+ front += part1;
+ framesIn -= part1;
+ }
+ activeTrack->mRsmpInFront += framesOut;
+
+ } else {
+ // resampling
+ // FIXME framesInNeeded should really be part of resampler API, and should
+ // depend on the SRC ratio
+ // to keep mRsmpInBuffer full so resampler always has sufficient input
+ size_t framesInNeeded;
+ // FIXME only re-calculate when it changes, and optimize for common ratios
+ double inOverOut = (double) mSampleRate / activeTrack->mSampleRate;
+ double outOverIn = (double) activeTrack->mSampleRate / mSampleRate;
+ framesInNeeded = ceil(framesOut * inOverOut) + 1;
+ ALOGV("need %u frames in to produce %u out given in/out ratio of %.4g",
+ framesInNeeded, framesOut, inOverOut);
+ // Although we theoretically have framesIn in circular buffer, some of those are
+ // unreleased frames, and thus must be discounted for purpose of budgeting.
+ size_t unreleased = activeTrack->mRsmpInUnrel;
+ framesIn = framesIn > unreleased ? framesIn - unreleased : 0;
+ if (framesIn < framesInNeeded) {
+ ALOGV("not enough to resample: have %u frames in but need %u in to "
+ "produce %u out given in/out ratio of %.4g",
+ framesIn, framesInNeeded, framesOut, inOverOut);
+ size_t newFramesOut = framesIn > 0 ? floor((framesIn - 1) * outOverIn) : 0;
+ LOG_ALWAYS_FATAL_IF(newFramesOut >= framesOut);
+ if (newFramesOut == 0) {
+ break;
+ }
+ framesInNeeded = ceil(newFramesOut * inOverOut) + 1;
+ ALOGV("now need %u frames in to produce %u out given out/in ratio of %.4g",
+ framesInNeeded, newFramesOut, outOverIn);
+ LOG_ALWAYS_FATAL_IF(framesIn < framesInNeeded);
+ ALOGV("success 2: have %u frames in and need %u in to produce %u out "
+ "given in/out ratio of %.4g",
+ framesIn, framesInNeeded, newFramesOut, inOverOut);
+ framesOut = newFramesOut;
+ } else {
+ ALOGV("success 1: have %u in and need %u in to produce %u out "
+ "given in/out ratio of %.4g",
+ framesIn, framesInNeeded, framesOut, inOverOut);
+ }
+
+ // reallocate mRsmpOutBuffer as needed; we will grow but never shrink
+ if (activeTrack->mRsmpOutFrameCount < framesOut) {
+ // FIXME why does each track need it's own mRsmpOutBuffer? can't they share?
+ delete[] activeTrack->mRsmpOutBuffer;
+ // resampler always outputs stereo
+ activeTrack->mRsmpOutBuffer = new int32_t[framesOut * FCC_2];
+ activeTrack->mRsmpOutFrameCount = framesOut;
+ }
+
+ // resampler accumulates, but we only have one source track
+ memset(activeTrack->mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
+ activeTrack->mResampler->resample(activeTrack->mRsmpOutBuffer, framesOut,
+ // FIXME how about having activeTrack implement this interface itself?
+ activeTrack->mResamplerBufferProvider
+ /*this*/ /* AudioBufferProvider* */);
+ // ditherAndClamp() works as long as all buffers returned by
+ // activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
+ if (activeTrack->mChannelCount == 1) {
+ // temporarily type pun mRsmpOutBuffer from Q19.12 to int16_t
+ ditherAndClamp(activeTrack->mRsmpOutBuffer, activeTrack->mRsmpOutBuffer,
+ framesOut);
+ // the resampler always outputs stereo samples:
+ // do post stereo to mono conversion
+ downmix_to_mono_i16_from_stereo_i16(activeTrack->mSink.i16,
+ (int16_t *)activeTrack->mRsmpOutBuffer, framesOut);
+ } else {
+ ditherAndClamp((int32_t *)activeTrack->mSink.raw,
+ activeTrack->mRsmpOutBuffer, framesOut);
+ }
+ // now done with mRsmpOutBuffer
+
+ }
+
+ if (framesOut > 0 && (overrun == OVERRUN_UNKNOWN)) {
+ overrun = OVERRUN_FALSE;
+ }
+
+ if (activeTrack->mFramesToDrop == 0) {
+ if (framesOut > 0) {
+ activeTrack->mSink.frameCount = framesOut;
+ activeTrack->releaseBuffer(&activeTrack->mSink);
+ }
+ } else {
+ // FIXME could do a partial drop of framesOut
+ if (activeTrack->mFramesToDrop > 0) {
+ activeTrack->mFramesToDrop -= framesOut;
+ if (activeTrack->mFramesToDrop <= 0) {
+ activeTrack->clearSyncStartEvent();
+ }
+ } else {
+ activeTrack->mFramesToDrop += framesOut;
+ if (activeTrack->mFramesToDrop >= 0 || activeTrack->mSyncStartEvent == 0 ||
+ activeTrack->mSyncStartEvent->isCancelled()) {
+ ALOGW("Synced record %s, session %d, trigger session %d",
+ (activeTrack->mFramesToDrop >= 0) ? "timed out" : "cancelled",
+ activeTrack->sessionId(),
+ (activeTrack->mSyncStartEvent != 0) ?
+ activeTrack->mSyncStartEvent->triggerSession() : 0);
+ activeTrack->clearSyncStartEvent();
+ }
+ }
+ }
+
+ if (framesOut == 0) {
+ break;
}
}
- // Release the processor for a while before asking for a new buffer.
- // This will give the application more chance to read from the buffer and
- // clear the overflow.
- doSleep = true;
+
+ switch (overrun) {
+ case OVERRUN_TRUE:
+ // client isn't retrieving buffers fast enough
+ if (!activeTrack->setOverflow()) {
+ nsecs_t now = systemTime();
+ // FIXME should lastWarning per track?
+ if ((now - lastWarning) > kWarningThrottleNs) {
+ ALOGW("RecordThread: buffer overflow");
+ lastWarning = now;
+ }
+ }
+ break;
+ case OVERRUN_FALSE:
+ activeTrack->clearOverflow();
+ break;
+ case OVERRUN_UNKNOWN:
+ break;
+ }
+
}
// enable changes in effect chain
@@ -4535,7 +4950,7 @@
// effectChains doesn't need to be cleared, since it is cleared by destructor at scope end
}
- standby();
+ standbyIfNotAlreadyInStandby();
{
Mutex::Autolock _l(mLock);
@@ -4543,7 +4958,8 @@
sp<RecordTrack> track = mTracks[i];
track->invalidate();
}
- mActiveTrack.clear();
+ mActiveTracks.clear();
+ mActiveTracksGen++;
mStartStopCond.broadcast();
}
@@ -4553,7 +4969,7 @@
return false;
}
-void AudioFlinger::RecordThread::standby()
+void AudioFlinger::RecordThread::standbyIfNotAlreadyInStandby()
{
if (!mStandby) {
inputStandBy();
@@ -4571,18 +4987,20 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
int sessionId,
+ int uid,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
status_t *status)
{
+ size_t frameCount = *pFrameCount;
sp<RecordTrack> track;
status_t lStatus;
lStatus = initCheck();
if (lStatus != NO_ERROR) {
- ALOGE("Audio driver not initialized.");
+ ALOGE("createRecordTrack_l() audio driver not initialized");
goto Exit;
}
@@ -4593,7 +5011,7 @@
(
(tid != -1) &&
((frameCount == 0) ||
- (frameCount >= (mFrameCount * kFastTrackMultiplier)))
+ (frameCount >= mFrameCount))
) &&
// FIXME when record supports non-PCM data, also check for audio_is_linear_pcm(format)
// mono or stereo
@@ -4636,6 +5054,7 @@
}
}
}
+ *pFrameCount = frameCount;
// FIXME use flags and tid similar to createTrack_l()
@@ -4643,11 +5062,12 @@
Mutex::Autolock _l(mLock);
track = new RecordTrack(this, client, sampleRate,
- format, channelMask, frameCount, sessionId);
+ format, channelMask, frameCount, sessionId, uid);
lStatus = track->initCheck();
if (lStatus != NO_ERROR) {
- track.clear();
+ ALOGE("createRecordTrack_l() initCheck failed %d; no control block?", lStatus);
+ // track must be cleared from the caller as the caller has the AF lock
goto Exit;
}
mTracks.add(track);
@@ -4681,113 +5101,99 @@
status_t status = NO_ERROR;
if (event == AudioSystem::SYNC_EVENT_NONE) {
- clearSyncStartEvent();
+ recordTrack->clearSyncStartEvent();
} else if (event != AudioSystem::SYNC_EVENT_SAME) {
- mSyncStartEvent = mAudioFlinger->createSyncEvent(event,
+ recordTrack->mSyncStartEvent = mAudioFlinger->createSyncEvent(event,
triggerSession,
recordTrack->sessionId(),
syncStartEventCallback,
- this);
+ recordTrack);
// Sync event can be cancelled by the trigger session if the track is not in a
// compatible state in which case we start record immediately
- if (mSyncStartEvent->isCancelled()) {
- clearSyncStartEvent();
+ if (recordTrack->mSyncStartEvent->isCancelled()) {
+ recordTrack->clearSyncStartEvent();
} else {
// do not wait for the event for more than AudioSystem::kSyncRecordStartTimeOutMs
- mFramestoDrop = - ((AudioSystem::kSyncRecordStartTimeOutMs * mReqSampleRate) / 1000);
+ recordTrack->mFramesToDrop = -
+ ((AudioSystem::kSyncRecordStartTimeOutMs * recordTrack->mSampleRate) / 1000);
}
}
{
// This section is a rendezvous between binder thread executing start() and RecordThread
AutoMutex lock(mLock);
- if (mActiveTrack != 0) {
- if (recordTrack != mActiveTrack.get()) {
- status = -EBUSY;
- } else if (mActiveTrack->mState == TrackBase::PAUSING) {
- mActiveTrack->mState = TrackBase::ACTIVE;
+ if (mActiveTracks.indexOf(recordTrack) >= 0) {
+ if (recordTrack->mState == TrackBase::PAUSING) {
+ ALOGV("active record track PAUSING -> ACTIVE");
+ recordTrack->mState = TrackBase::ACTIVE;
+ } else {
+ ALOGV("active record track state %d", recordTrack->mState);
}
return status;
}
- // FIXME why? already set in constructor, 'STARTING_1' would be more accurate
- recordTrack->mState = TrackBase::IDLE;
- mActiveTrack = recordTrack;
+ // TODO consider other ways of handling this, such as changing the state to :STARTING and
+ // adding the track to mActiveTracks after returning from AudioSystem::startInput(),
+ // or using a separate command thread
+ recordTrack->mState = TrackBase::STARTING_1;
+ mActiveTracks.add(recordTrack);
+ mActiveTracksGen++;
mLock.unlock();
status_t status = AudioSystem::startInput(mId);
mLock.lock();
- // FIXME should verify that mActiveTrack is still == recordTrack
+ // FIXME should verify that recordTrack is still in mActiveTracks
if (status != NO_ERROR) {
- mActiveTrack.clear();
- clearSyncStartEvent();
+ mActiveTracks.remove(recordTrack);
+ mActiveTracksGen++;
+ recordTrack->clearSyncStartEvent();
return status;
}
- mRsmpInIndex = mFrameCount;
- mBytesRead = 0;
- if (mResampler != NULL) {
- mResampler->reset();
+ // Catch up with current buffer indices if thread is already running.
+ // This is what makes a new client discard all buffered data. If the track's mRsmpInFront
+ // was initialized to some value closer to the thread's mRsmpInFront, then the track could
+ // see previously buffered data before it called start(), but with greater risk of overrun.
+
+ recordTrack->mRsmpInFront = mRsmpInRear;
+ recordTrack->mRsmpInUnrel = 0;
+ // FIXME why reset?
+ if (recordTrack->mResampler != NULL) {
+ recordTrack->mResampler->reset();
}
- // FIXME hijacking a playback track state name which was intended for start after pause;
- // here 'STARTING_2' would be more accurate
- mActiveTrack->mState = TrackBase::RESUMING;
+ recordTrack->mState = TrackBase::STARTING_2;
// signal thread to start
- ALOGV("Signal record thread");
mWaitWorkCV.broadcast();
- // do not wait for mStartStopCond if exiting
- if (exitPending()) {
- mActiveTrack.clear();
- status = INVALID_OPERATION;
- goto startError;
- }
- // FIXME incorrect usage of wait: no explicit predicate or loop
- mStartStopCond.wait(mLock);
- if (mActiveTrack == 0) {
+ if (mActiveTracks.indexOf(recordTrack) < 0) {
ALOGV("Record failed to start");
status = BAD_VALUE;
goto startError;
}
- ALOGV("Record started OK");
return status;
}
startError:
AudioSystem::stopInput(mId);
- clearSyncStartEvent();
+ recordTrack->clearSyncStartEvent();
+ // FIXME I wonder why we do not reset the state here?
return status;
}
-void AudioFlinger::RecordThread::clearSyncStartEvent()
-{
- if (mSyncStartEvent != 0) {
- mSyncStartEvent->cancel();
- }
- mSyncStartEvent.clear();
- mFramestoDrop = 0;
-}
-
void AudioFlinger::RecordThread::syncStartEventCallback(const wp<SyncEvent>& event)
{
sp<SyncEvent> strongEvent = event.promote();
if (strongEvent != 0) {
- RecordThread *me = (RecordThread *)strongEvent->cookie();
- me->handleSyncStartEvent(strongEvent);
- }
-}
-
-void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event)
-{
- if (event == mSyncStartEvent) {
- // TODO: use actual buffer filling status instead of 2 buffers when info is available
- // from audio HAL
- mFramestoDrop = mFrameCount * 2;
+ sp<RefBase> ptr = strongEvent->cookie().promote();
+ if (ptr != 0) {
+ RecordTrack *recordTrack = (RecordTrack *)ptr.get();
+ recordTrack->handleSyncStartEvent(strongEvent);
+ }
}
}
bool AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
ALOGV("RecordThread::stop");
AutoMutex _l(mLock);
- if (recordTrack != mActiveTrack.get() || recordTrack->mState == TrackBase::PAUSING) {
+ if (mActiveTracks.indexOf(recordTrack) != 0 || recordTrack->mState == TrackBase::PAUSING) {
return false;
}
// note that threadLoop may still be processing the track at this point [without lock]
@@ -4798,20 +5204,20 @@
}
// FIXME incorrect usage of wait: no explicit predicate or loop
mStartStopCond.wait(mLock);
- // if we have been restarted, recordTrack == mActiveTrack.get() here
- if (exitPending() || recordTrack != mActiveTrack.get()) {
+ // if we have been restarted, recordTrack is in mActiveTracks here
+ if (exitPending() || mActiveTracks.indexOf(recordTrack) != 0) {
ALOGV("Record stopped OK");
return true;
}
return false;
}
-bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event) const
+bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event __unused) const
{
return false;
}
-status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event)
+status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event __unused)
{
#if 0 // This branch is currently dead code, but is preserved in case it will be needed in future
if (!isValidSyncEvent(event)) {
@@ -4842,7 +5248,7 @@
track->terminate();
track->mState = TrackBase::STOPPED;
// active tracks are removed by threadLoop()
- if (mActiveTrack != track) {
+ if (mActiveTracks.indexOf(track) < 0) {
removeTrack_l(track);
}
}
@@ -4862,105 +5268,119 @@
void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args)
{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
+ fdprintf(fd, "\nInput thread %p:\n", this);
- snprintf(buffer, SIZE, "\nInput thread %p internals\n", this);
- result.append(buffer);
-
- if (mActiveTrack != 0) {
- snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex);
- result.append(buffer);
- snprintf(buffer, SIZE, "Buffer size: %u bytes\n", mBufferSize);
- result.append(buffer);
- snprintf(buffer, SIZE, "Resampling: %d\n", (mResampler != NULL));
- result.append(buffer);
- snprintf(buffer, SIZE, "Out channel count: %u\n", mReqChannelCount);
- result.append(buffer);
- snprintf(buffer, SIZE, "Out sample rate: %u\n", mReqSampleRate);
- result.append(buffer);
+ if (mActiveTracks.size() > 0) {
+ fdprintf(fd, " Buffer size: %zu bytes\n", mBufferSize);
} else {
- result.append("No active record client\n");
+ fdprintf(fd, " No active record clients\n");
}
- write(fd, result.string(), result.size());
-
dumpBase(fd, args);
}
-void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args)
+void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args __unused)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, "Input thread %p tracks\n", this);
- result.append(buffer);
- RecordTrack::appendDumpHeader(result);
- for (size_t i = 0; i < mTracks.size(); ++i) {
- sp<RecordTrack> track = mTracks[i];
- if (track != 0) {
- track->dump(buffer, SIZE);
- result.append(buffer);
+ size_t numtracks = mTracks.size();
+ size_t numactive = mActiveTracks.size();
+ size_t numactiveseen = 0;
+ fdprintf(fd, " %d Tracks", numtracks);
+ if (numtracks) {
+ fdprintf(fd, " of which %d are active\n", numactive);
+ RecordTrack::appendDumpHeader(result);
+ for (size_t i = 0; i < numtracks ; ++i) {
+ sp<RecordTrack> track = mTracks[i];
+ if (track != 0) {
+ bool active = mActiveTracks.indexOf(track) >= 0;
+ if (active) {
+ numactiveseen++;
+ }
+ track->dump(buffer, SIZE, active);
+ result.append(buffer);
+ }
}
+ } else {
+ fdprintf(fd, "\n");
}
- if (mActiveTrack != 0) {
- snprintf(buffer, SIZE, "\nInput thread %p active tracks\n", this);
+ if (numactiveseen != numactive) {
+ snprintf(buffer, SIZE, " The following tracks are in the active list but"
+ " not in the track list\n");
result.append(buffer);
RecordTrack::appendDumpHeader(result);
- mActiveTrack->dump(buffer, SIZE);
- result.append(buffer);
+ for (size_t i = 0; i < numactive; ++i) {
+ sp<RecordTrack> track = mActiveTracks[i];
+ if (mTracks.indexOf(track) < 0) {
+ track->dump(buffer, SIZE, true);
+ result.append(buffer);
+ }
+ }
}
write(fd, result.string(), result.size());
}
// AudioBufferProvider interface
-status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts)
+status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer(
+ AudioBufferProvider::Buffer* buffer, int64_t pts __unused)
{
- size_t framesReq = buffer->frameCount;
- size_t framesReady = mFrameCount - mRsmpInIndex;
- int channelCount;
-
- if (framesReady == 0) {
- mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mBufferSize);
- if (mBytesRead <= 0) {
- if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) {
- ALOGE("RecordThread::getNextBuffer() Error reading audio input");
- // Force input into standby so that it tries to
- // recover at next read attempt
- inputStandBy();
- // FIXME an awkward place to sleep, consider using doSleep when this is pulled up
- usleep(kRecordThreadSleepUs);
- }
- buffer->raw = NULL;
- buffer->frameCount = 0;
- return NOT_ENOUGH_DATA;
- }
- mRsmpInIndex = 0;
- framesReady = mFrameCount;
+ RecordTrack *activeTrack = mRecordTrack;
+ sp<ThreadBase> threadBase = activeTrack->mThread.promote();
+ if (threadBase == 0) {
+ buffer->frameCount = 0;
+ buffer->raw = NULL;
+ return NOT_ENOUGH_DATA;
+ }
+ RecordThread *recordThread = (RecordThread *) threadBase.get();
+ int32_t rear = recordThread->mRsmpInRear;
+ int32_t front = activeTrack->mRsmpInFront;
+ ssize_t filled = rear - front;
+ // FIXME should not be P2 (don't want to increase latency)
+ // FIXME if client not keeping up, discard
+ LOG_ALWAYS_FATAL_IF(!(0 <= filled && (size_t) filled <= recordThread->mRsmpInFrames));
+ // 'filled' may be non-contiguous, so return only the first contiguous chunk
+ front &= recordThread->mRsmpInFramesP2 - 1;
+ size_t part1 = recordThread->mRsmpInFramesP2 - front;
+ if (part1 > (size_t) filled) {
+ part1 = filled;
+ }
+ size_t ask = buffer->frameCount;
+ ALOG_ASSERT(ask > 0);
+ if (part1 > ask) {
+ part1 = ask;
+ }
+ if (part1 == 0) {
+ // Higher-level should keep mRsmpInBuffer full, and not call resampler if empty
+ LOG_ALWAYS_FATAL("RecordThread::getNextBuffer() starved");
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ activeTrack->mRsmpInUnrel = 0;
+ return NOT_ENOUGH_DATA;
}
- if (framesReq > framesReady) {
- framesReq = framesReady;
- }
-
- if (mChannelCount == 1 && mReqChannelCount == 2) {
- channelCount = 1;
- } else {
- channelCount = 2;
- }
- buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount;
- buffer->frameCount = framesReq;
+ buffer->raw = recordThread->mRsmpInBuffer + front * recordThread->mChannelCount;
+ buffer->frameCount = part1;
+ activeTrack->mRsmpInUnrel = part1;
return NO_ERROR;
}
// AudioBufferProvider interface
-void AudioFlinger::RecordThread::releaseBuffer(AudioBufferProvider::Buffer* buffer)
+void AudioFlinger::RecordThread::ResamplerBufferProvider::releaseBuffer(
+ AudioBufferProvider::Buffer* buffer)
{
- mRsmpInIndex += buffer->frameCount;
+ RecordTrack *activeTrack = mRecordTrack;
+ size_t stepCount = buffer->frameCount;
+ if (stepCount == 0) {
+ return;
+ }
+ ALOG_ASSERT(stepCount <= activeTrack->mRsmpInUnrel);
+ activeTrack->mRsmpInUnrel -= stepCount;
+ activeTrack->mRsmpInFront += stepCount;
+ buffer->raw = NULL;
buffer->frameCount = 0;
}
@@ -4974,11 +5394,14 @@
AudioParameter param = AudioParameter(keyValuePair);
int value;
audio_format_t reqFormat = mFormat;
- uint32_t reqSamplingRate = mReqSampleRate;
- audio_channel_mask_t reqChannelMask = audio_channel_in_mask_from_count(mReqChannelCount);
+ uint32_t samplingRate = mSampleRate;
+ audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(mChannelCount);
+ // TODO Investigate when this code runs. Check with audio policy when a sample rate and
+ // channel count change can be requested. Do we mandate the first client defines the
+ // HAL sampling rate and channel count or do we allow changes on the fly?
if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
- reqSamplingRate = value;
+ samplingRate = value;
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
@@ -4994,7 +5417,7 @@
if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) {
status = BAD_VALUE;
} else {
- reqChannelMask = mask;
+ channelMask = mask;
reconfig = true;
}
}
@@ -5002,7 +5425,7 @@
// do not accept frame count changes if tracks are open as the track buffer
// size depends on frame count and correct behavior would not be guaranteed
// if frame count is changed after track creation
- if (mActiveTrack != 0) {
+ if (mActiveTracks.size() > 0) {
status = INVALID_OPERATION;
} else {
reconfig = true;
@@ -5059,15 +5482,15 @@
reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&
reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
(mInput->stream->common.get_sample_rate(&mInput->stream->common)
- <= (2 * reqSamplingRate)) &&
+ <= (2 * samplingRate)) &&
popcount(mInput->stream->common.get_channels(&mInput->stream->common))
<= FCC_2 &&
- (reqChannelMask == AUDIO_CHANNEL_IN_MONO ||
- reqChannelMask == AUDIO_CHANNEL_IN_STEREO)) {
+ (channelMask == AUDIO_CHANNEL_IN_MONO ||
+ channelMask == AUDIO_CHANNEL_IN_STEREO)) {
status = NO_ERROR;
}
if (status == NO_ERROR) {
- readInputParameters();
+ readInputParameters_l();
sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
}
}
@@ -5097,9 +5520,9 @@
return out_s8;
}
-void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
+void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param __unused) {
AudioSystem::OutputDescriptor desc;
- void *param2 = NULL;
+ const void *param2 = NULL;
switch (event) {
case AudioSystem::INPUT_OPENED:
@@ -5119,52 +5542,35 @@
mAudioFlinger->audioConfigChanged_l(event, mId, param2);
}
-void AudioFlinger::RecordThread::readInputParameters()
+void AudioFlinger::RecordThread::readInputParameters_l()
{
- delete[] mRsmpInBuffer;
- // mRsmpInBuffer is always assigned a new[] below
- delete[] mRsmpOutBuffer;
- mRsmpOutBuffer = NULL;
- delete mResampler;
- mResampler = NULL;
-
mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
mChannelCount = popcount(mChannelMask);
mFormat = mInput->stream->common.get_format(&mInput->stream->common);
if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("HAL format %d not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
+ ALOGE("HAL format %#x not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
}
mFrameSize = audio_stream_frame_size(&mInput->stream->common);
mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
mFrameCount = mBufferSize / mFrameSize;
- mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
+ // This is the formula for calculating the temporary buffer size.
+ // With 7 HAL buffers, we can guarantee ability to down-sample the input by ratio of 6:1 to
+ // 1 full output buffer, regardless of the alignment of the available input.
+ // The value is somewhat arbitrary, and could probably be even larger.
+ // A larger value should allow more old data to be read after a track calls start(),
+ // without increasing latency.
+ mRsmpInFrames = mFrameCount * 7;
+ mRsmpInFramesP2 = roundup(mRsmpInFrames);
+ delete[] mRsmpInBuffer;
+ // Over-allocate beyond mRsmpInFramesP2 to permit a HAL read past end of buffer
+ mRsmpInBuffer = new int16_t[(mRsmpInFramesP2 + mFrameCount - 1) * mChannelCount];
- if (mSampleRate != mReqSampleRate && mChannelCount <= FCC_2 && mReqChannelCount <= FCC_2) {
- int channelCount;
- // optimization: if mono to mono, use the resampler in stereo to stereo mode to avoid
- // stereo to mono post process as the resampler always outputs stereo.
- if (mChannelCount == 1 && mReqChannelCount == 2) {
- channelCount = 1;
- } else {
- channelCount = 2;
- }
- mResampler = AudioResampler::create(16, channelCount, mReqSampleRate);
- mResampler->setSampleRate(mSampleRate);
- mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
- mRsmpOutBuffer = new int32_t[mFrameCount * FCC_2];
-
- // optmization: if mono to mono, alter input frame count as if we were inputing
- // stereo samples
- if (mChannelCount == 1 && mReqChannelCount == 1) {
- mFrameCount >>= 1;
- }
-
- }
- mRsmpInIndex = mFrameCount;
+ // AudioRecord mSampleRate and mChannelCount are constant due to AudioRecord API constraints.
+ // But if thread's mSampleRate or mChannelCount changes, how will that affect active tracks?
}
-unsigned int AudioFlinger::RecordThread::getInputFramesLost()
+uint32_t AudioFlinger::RecordThread::getInputFramesLost()
{
Mutex::Autolock _l(mLock);
if (initCheck() != NO_ERROR) {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index b96e1c8..96642ff 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -65,7 +65,7 @@
class IoConfigEvent : public ConfigEvent {
public:
IoConfigEvent(int event, int param) :
- ConfigEvent(CFG_EVENT_IO), mEvent(event), mParam(event) {}
+ ConfigEvent(CFG_EVENT_IO), mEvent(event), mParam(param) {}
virtual ~IoConfigEvent() {}
int event() const { return mEvent; }
@@ -201,13 +201,13 @@
// effect
void removeEffect_l(const sp< EffectModule>& effect);
// detach all tracks connected to an auxiliary effect
- virtual void detachAuxEffect_l(int effectId) {}
+ virtual void detachAuxEffect_l(int effectId __unused) {}
// returns either EFFECT_SESSION if effects on this audio session exist in one
// chain, or TRACK_SESSION if tracks on this audio session exist, or both
virtual uint32_t hasAudioSession(int sessionId) const = 0;
// the value returned by default implementation is not important as the
// strategy is only meaningful for PlaybackThread which implements this method
- virtual uint32_t getStrategyForSession_l(int sessionId) { return 0; }
+ virtual uint32_t getStrategyForSession_l(int sessionId __unused) { return 0; }
// suspend or restore effect according to the type of effect passed. a NULL
// type pointer means suspend all effects in the session
@@ -240,10 +240,13 @@
effect_uuid_t mType; // effect type UUID
};
- void acquireWakeLock();
- void acquireWakeLock_l();
+ void acquireWakeLock(int uid = -1);
+ void acquireWakeLock_l(int uid = -1);
void releaseWakeLock();
void releaseWakeLock_l();
+ void updateWakeLockUids(const SortedVector<int> &uids);
+ void updateWakeLockUids_l(const SortedVector<int> &uids);
+ void getPowerManager_l();
void setEffectSuspended_l(const effect_uuid_t *type,
bool suspend,
int sessionId);
@@ -254,6 +257,8 @@
// check if some effects must be suspended when an effect chain is added
void checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain);
+ String16 getWakeLockTag();
+
virtual void preExit() { }
friend class AudioFlinger; // for mEffectChains
@@ -265,8 +270,8 @@
const sp<AudioFlinger> mAudioFlinger;
- // updated by PlaybackThread::readOutputParameters() or
- // RecordThread::readInputParameters()
+ // updated by PlaybackThread::readOutputParameters_l() or
+ // RecordThread::readInputParameters_l()
uint32_t mSampleRate;
size_t mFrameCount; // output HAL, direct output, record
audio_channel_mask_t mChannelMask;
@@ -302,12 +307,12 @@
Vector<ConfigEvent *> mConfigEvents;
// These fields are written and read by thread itself without lock or barrier,
- // and read by other threads without lock or barrier via standby() , outDevice()
+ // and read by other threads without lock or barrier via standby(), outDevice()
// and inDevice().
// Because of the absence of a lock or barrier, any other thread that reads
// these fields must use the information in isolation, or be prepared to deal
// with possibility that it might be inconsistent with other information.
- bool mStandby; // Whether thread is currently in standby.
+ bool mStandby; // Whether thread is currently in standby.
audio_devices_t mOutDevice; // output device
audio_devices_t mInDevice; // input device
audio_source_t mAudioSource; // (see audio.h, audio_source_t)
@@ -389,7 +394,7 @@
virtual bool waitingAsyncCallback();
virtual bool waitingAsyncCallback_l();
virtual bool shouldStandby_l();
-
+ virtual void onAddNewTrack_l();
// ThreadBase virtuals
virtual void preExit();
@@ -417,11 +422,12 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
+ int uid,
status_t *status /*non-NULL*/);
AudioStreamOut* getOutput() const;
@@ -443,8 +449,9 @@
virtual String8 getParameters(const String8& keys);
virtual void audioConfigChanged_l(int event, int param = 0);
- status_t getRenderPosition(size_t *halFrames, size_t *dspFrames);
- int16_t *mixBuffer() const { return mMixBuffer; };
+ status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
+ // TODO: rename mixBuffer() to sinkBuffer() or try to remove external use.
+ int16_t *mixBuffer() const { return mSinkBuffer; };
virtual void detachAuxEffect_l(int effectId);
status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track,
@@ -472,10 +479,33 @@
status_t getTimestamp_l(AudioTimestamp& timestamp);
protected:
- // updated by readOutputParameters()
+ // updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
- int16_t* mMixBuffer; // frame size aligned mix buffer
+ int16_t* mSinkBuffer; // frame size aligned sink buffer
+
+ // Mixer Buffer (mMixerBuffer*)
+ //
+ // In the case of floating point or multichannel data, which is not in the
+ // sink format, it is required to accumulate in a higher precision or greater channel count
+ // buffer before downmixing or data conversion to the sink buffer.
+
+ // Set to "true" to enable the Mixer Buffer otherwise mixer output goes to sink buffer.
+ bool mMixerBufferEnabled;
+
+ // Storage, 32 byte aligned (may make this alignment a requirement later).
+ // Due to constraints on mNormalFrameCount, the buffer size is a multiple of 16 frames.
+ void* mMixerBuffer;
+
+ // Size of mMixerBuffer in bytes: mNormalFrameCount * #channels * sampsize.
+ size_t mMixerBufferSize;
+
+ // The audio format of mMixerBuffer. Set to AUDIO_FORMAT_PCM_(FLOAT|16_BIT) only.
+ audio_format_t mMixerBufferFormat;
+
+ // An internal flag set to true by MixerThread::prepareTracks_l()
+ // when mMixerBuffer contains valid data after mixing.
+ bool mMixerBufferValid;
// suspend count, > 0 means suspended. While suspended, the thread continues to pull from
// tracks and mix, but doesn't write to HAL. A2DP and SCO HAL implementations can't handle
@@ -495,6 +525,9 @@
void setMasterMute_l(bool muted) { mMasterMute = muted; }
protected:
SortedVector< wp<Track> > mActiveTracks; // FIXME check if this could be sp<>
+ SortedVector<int> mWakeLockUids;
+ int mActiveTracksGeneration;
+ wp<Track> mLatestActiveTrack; // latest track added to mActiveTracks
// Allocate a track name for a given channel mask.
// Returns name >= 0 if successful, -1 on failure.
@@ -532,7 +565,7 @@
void removeTrack_l(const sp<Track>& track);
void broadcast_l();
- void readOutputParameters();
+ void readOutputParameters_l();
virtual void dumpInternals(int fd, const Vector<String16>& args);
void dumpTracks(int fd, const Vector<String16>& args);
@@ -551,7 +584,7 @@
// FIXME rename these former local variables of threadLoop to standard "m" names
nsecs_t standbyTime;
- size_t mixBufferSize;
+ size_t mSinkBufferSize;
// cached copies of activeSleepTimeUs() and idleSleepTimeUs() made by cacheParameters_l()
uint32_t activeSleepTime;
@@ -616,13 +649,12 @@
sp<NBLog::Writer> mFastMixerNBLogWriter;
public:
virtual bool hasFastMixer() const = 0;
- virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const
+ virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex __unused) const
{ FastTrackUnderruns dummy; return dummy; }
protected:
// accessed by both binder threads and within threadLoop(), lock on mutex needed
unsigned mFastTrackAvailMask; // bit i set if fast track [i] is available
- virtual void flushOutput_l();
private:
// timestamp latch:
@@ -735,17 +767,17 @@
OffloadThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
audio_io_handle_t id, uint32_t device);
- virtual ~OffloadThread();
+ virtual ~OffloadThread() {};
protected:
// threadLoop snippets
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
virtual void threadLoop_exit();
- virtual void flushOutput_l();
virtual bool waitingAsyncCallback();
virtual bool waitingAsyncCallback_l();
virtual bool shouldStandby_l();
+ virtual void onAddNewTrack_l();
private:
void flushHw_l();
@@ -755,13 +787,13 @@
bool mFlushPending;
size_t mPausedWriteLength; // length in bytes of write interrupted by pause
size_t mPausedBytesRemaining; // bytes still waiting in mixbuffer after resume
- sp<Track> mPreviousTrack; // used to detect track switch
+ wp<Track> mPreviousTrack; // used to detect track switch
};
class AsyncCallbackThread : public Thread {
public:
- AsyncCallbackThread(const sp<OffloadThread>& offloadThread);
+ AsyncCallbackThread(const wp<PlaybackThread>& playbackThread);
virtual ~AsyncCallbackThread();
@@ -778,17 +810,17 @@
void resetDraining();
private:
- wp<OffloadThread> mOffloadThread;
+ const wp<PlaybackThread> mPlaybackThread;
// mWriteAckSequence corresponds to the last write sequence passed by the offload thread via
// setWriteBlocked(). The sequence is shifted one bit to the left and the lsb is used
// to indicate that the callback has been received via resetWriteBlocked()
- uint32_t mWriteAckSequence;
+ uint32_t mWriteAckSequence;
// mDrainSequence corresponds to the last drain sequence passed by the offload thread via
// setDraining(). The sequence is shifted one bit to the left and the lsb is used
// to indicate that the callback has been received via resetDraining()
- uint32_t mDrainSequence;
- Condition mWaitWorkCV;
- Mutex mLock;
+ uint32_t mDrainSequence;
+ Condition mWaitWorkCV;
+ Mutex mLock;
};
class DuplicatingThread : public MixerThread {
@@ -831,17 +863,28 @@
// record thread
-class RecordThread : public ThreadBase, public AudioBufferProvider
- // derives from AudioBufferProvider interface for use by resampler
+class RecordThread : public ThreadBase
{
public:
+ class RecordTrack;
+ class ResamplerBufferProvider : public AudioBufferProvider
+ // derives from AudioBufferProvider interface for use by resampler
+ {
+ public:
+ ResamplerBufferProvider(RecordTrack* recordTrack) : mRecordTrack(recordTrack) { }
+ virtual ~ResamplerBufferProvider() { }
+ // AudioBufferProvider interface
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts);
+ virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
+ private:
+ RecordTrack * const mRecordTrack;
+ };
+
#include "RecordTracks.h"
RecordThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamIn *input,
- uint32_t sampleRate,
- audio_channel_mask_t channelMask,
audio_io_handle_t id,
audio_devices_t outDevice,
audio_devices_t inDevice
@@ -871,8 +914,9 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
int sessionId,
+ int uid,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
status_t *status /*non-NULL*/);
@@ -889,15 +933,12 @@
AudioStreamIn* clearInput();
virtual audio_stream_t* stream() const;
- // AudioBufferProvider interface
- virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts);
- virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
virtual bool checkForNewParameters_l();
virtual String8 getParameters(const String8& keys);
virtual void audioConfigChanged_l(int event, int param = 0);
- void readInputParameters();
- virtual unsigned int getInputFramesLost();
+ void readInputParameters_l();
+ virtual uint32_t getInputFramesLost();
virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
@@ -912,43 +953,33 @@
virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const;
static void syncStartEventCallback(const wp<SyncEvent>& event);
- void handleSyncStartEvent(const sp<SyncEvent>& event);
virtual size_t frameCount() const { return mFrameCount; }
bool hasFastRecorder() const { return false; }
private:
- void clearSyncStartEvent();
-
// Enter standby if not already in standby, and set mStandby flag
- void standby();
+ void standbyIfNotAlreadyInStandby();
// Call the HAL standby method unconditionally, and don't change mStandby flag
void inputStandBy();
AudioStreamIn *mInput;
SortedVector < sp<RecordTrack> > mTracks;
- // mActiveTrack has dual roles: it indicates the current active track, and
+ // mActiveTracks has dual roles: it indicates the current active track(s), and
// is used together with mStartStopCond to indicate start()/stop() progress
- sp<RecordTrack> mActiveTrack;
+ SortedVector< sp<RecordTrack> > mActiveTracks;
+ // generation counter for mActiveTracks
+ int mActiveTracksGen;
Condition mStartStopCond;
- // updated by RecordThread::readInputParameters()
- AudioResampler *mResampler;
- // interleaved stereo pairs of fixed-point signed Q19.12
- int32_t *mRsmpOutBuffer;
- int16_t *mRsmpInBuffer; // [mFrameCount * mChannelCount]
- size_t mRsmpInIndex;
- const uint32_t mReqChannelCount;
- const uint32_t mReqSampleRate;
- ssize_t mBytesRead;
- // sync event triggering actual audio capture. Frames read before this event will
- // be dropped and therefore not read by the application.
- sp<SyncEvent> mSyncStartEvent;
- // number of captured frames to drop after the start sync event has been received.
- // when < 0, maximum frames to drop before starting capture even if sync event is
- // not received
- ssize_t mFramestoDrop;
+ // resampler converts input at HAL Hz to output at AudioRecord client Hz
+ int16_t *mRsmpInBuffer; // see new[] for details on the size
+ size_t mRsmpInFrames; // size of resampler input in frames
+ size_t mRsmpInFramesP2;// size rounded up to a power-of-2
+
+ // rolling index that is never cleared
+ int32_t mRsmpInRear; // last filled frame + 1
// For dumpsys
const sp<NBAIO_Sink> mTeeSink;
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 00a91b7..58705c4 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -34,7 +34,9 @@
RESUMING,
ACTIVE,
PAUSING,
- PAUSED
+ PAUSED,
+ STARTING_1, // for RecordTrack only
+ STARTING_2, // for RecordTrack only
};
TrackBase(ThreadBase *thread,
@@ -45,6 +47,7 @@
size_t frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int uid,
bool isOut);
virtual ~TrackBase();
virtual status_t initCheck() const { return getCblk() != 0 ? NO_ERROR : NO_MEMORY; }
@@ -55,6 +58,7 @@
sp<IMemory> getCblk() const { return mCblkMemory; }
audio_track_cblk_t* cblk() const { return mCblk; }
int sessionId() const { return mSessionId; }
+ int uid() const { return mUid; }
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
protected:
@@ -124,6 +128,7 @@
// openRecord(), and then adjusted as needed
const int mSessionId;
+ int mUid;
Vector < sp<SyncEvent> >mSyncEvents;
const bool mIsOut;
ServerProxy* mServerProxy;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 9002f9b..92ed46a 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -68,6 +68,7 @@
size_t frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int clientUid,
bool isOut)
: RefBase(),
mThread(thread),
@@ -88,6 +89,18 @@
mId(android_atomic_inc(&nextTrackId)),
mTerminated(false)
{
+ // if the caller is us, trust the specified uid
+ if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) {
+ int newclientUid = IPCThreadState::self()->getCallingUid();
+ if (clientUid != -1 && clientUid != newclientUid) {
+ ALOGW("uid %d tried to pass itself off as %d", newclientUid, clientUid);
+ }
+ clientUid = newclientUid;
+ }
+ // clientUid contains the uid of the app that is responsible for this track, so we can blame
+ // battery usage on it.
+ mUid = clientUid;
+
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -103,12 +116,11 @@
if (client != 0) {
mCblkMemory = client->heap()->allocate(size);
- if (mCblkMemory != 0) {
- mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
- // can't assume mCblk != NULL
- } else {
+ if (mCblkMemory == 0 ||
+ (mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer())) == NULL) {
ALOGE("not enough memory for AudioTrack size=%u", size);
client->heap()->dump("AudioTrack");
+ mCblkMemory.clear();
return;
}
} else {
@@ -121,7 +133,6 @@
if (mCblk != NULL) {
new(mCblk) audio_track_cblk_t();
// clear all buffers
- mCblk->frameCount_ = frameCount;
if (sharedBuffer == 0) {
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, bufferSize);
@@ -135,7 +146,7 @@
#ifdef TEE_SINK
if (mTeeSinkTrackEnabled) {
NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount);
- if (pipeFormat != Format_Invalid) {
+ if (Format_isValid(pipeFormat)) {
Pipe *pipe = new Pipe(mTeeSinkTrackFrames, pipeFormat);
size_t numCounterOffers = 0;
const NBAIO_Format offers[1] = {pipeFormat};
@@ -262,6 +273,11 @@
if (!mTrack->isTimedTrack())
return INVALID_OPERATION;
+ if (buffer == 0 || buffer->pointer() == NULL) {
+ ALOGE("queueTimedBuffer() buffer is 0 or has NULL pointer()");
+ return BAD_VALUE;
+ }
+
PlaybackThread::TimedTrack* tt =
reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get());
return tt->queueTimedBuffer(buffer, pts);
@@ -288,6 +304,12 @@
return mTrack->getTimestamp(timestamp);
}
+
+void AudioFlinger::TrackHandle::signal()
+{
+ return mTrack->signal();
+}
+
status_t AudioFlinger::TrackHandle::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -307,9 +329,10 @@
size_t frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int uid,
IAudioFlinger::track_flags_t flags)
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer,
- sessionId, true /*isOut*/),
+ sessionId, uid, true /*isOut*/),
mFillingUpStatus(FS_INVALID),
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
@@ -324,7 +347,8 @@
mCachedVolume(1.0),
mIsInvalid(false),
mAudioTrackServerProxy(NULL),
- mResumeToStopping(false)
+ mResumeToStopping(false),
+ mFlushHwPending(false)
{
if (mCblk != NULL) {
if (sharedBuffer == 0) {
@@ -411,17 +435,19 @@
/*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
{
- result.append(" Name Client Type Fmt Chn mask Session fCount S F SRate "
+ result.append(" Name Active Client Type Fmt Chn mask Session fCount S F SRate "
"L dB R dB Server Main buf Aux Buf Flags UndFrmCnt\n");
}
-void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
+void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size, bool active)
{
uint32_t vlr = mAudioTrackServerProxy->getVolumeLR();
if (isFastTrack()) {
- sprintf(buffer, " F %2d", mFastIndex);
+ sprintf(buffer, " F %2d", mFastIndex);
+ } else if (mName >= AudioMixer::TRACK0) {
+ sprintf(buffer, " %4d", mName - AudioMixer::TRACK0);
} else {
- sprintf(buffer, " %4d", mName - AudioMixer::TRACK0);
+ sprintf(buffer, " none");
}
track_state state = mState;
char stateChar;
@@ -476,8 +502,9 @@
nowInUnderrun = '?';
break;
}
- snprintf(&buffer[7], size-7, " %6u %4u %08X %08X %7u %6u %1c %1d %5u %5.2g %5.2g "
- "%08X %08X %08X 0x%03X %9u%c\n",
+ snprintf(&buffer[8], size-8, " %6s %6u %4u %08X %08X %7u %6zu %1c %1d %5u %5.2g %5.2g "
+ "%08X %p %p 0x%03X %9u%c\n",
+ active ? "yes" : "no",
(mClient == 0) ? getpid_cached : mClient->pid(),
mStreamType,
mFormat,
@@ -490,8 +517,8 @@
20.0 * log10((vlr & 0xFFFF) / 4096.0),
20.0 * log10((vlr >> 16) / 4096.0),
mCblk->mServer,
- (int)mMainBuffer,
- (int)mAuxBuffer,
+ mMainBuffer,
+ mAuxBuffer,
mCblk->mFlags,
mAudioTrackServerProxy->getUnderrunFrames(),
nowInUnderrun);
@@ -503,7 +530,7 @@
// AudioBufferProvider interface
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
- AudioBufferProvider::Buffer* buffer, int64_t pts)
+ AudioBufferProvider::Buffer* buffer, int64_t pts __unused)
{
ServerProxy::Buffer buf;
size_t desiredFrames = buffer->frameCount;
@@ -540,7 +567,7 @@
// Don't call for fast tracks; the framesReady() could result in priority inversion
bool AudioFlinger::PlaybackThread::Track::isReady() const {
- if (mFillingUpStatus != FS_FILLING || isStopped() || isPausing()) {
+ if (mFillingUpStatus != FS_FILLING || isStopped() || isPausing() || isStopping()) {
return true;
}
@@ -553,8 +580,8 @@
return false;
}
-status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t event,
- int triggerSession)
+status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t event __unused,
+ int triggerSession __unused)
{
status_t status = NO_ERROR;
ALOGV("start(%d), calling pid %d session %d",
@@ -603,6 +630,15 @@
// track was already in the active list, not a problem
if (status == ALREADY_EXISTS) {
status = NO_ERROR;
+ } else {
+ // Acknowledge any pending flush(), so that subsequent new data isn't discarded.
+ // It is usually unsafe to access the server proxy from a binder thread.
+ // But in this case we know the mixer thread (whether normal mixer or fast mixer)
+ // isn't looking at this track yet: we still hold the normal mixer thread lock,
+ // and for fast tracks the track is not yet in the fast mixer thread's active set.
+ ServerProxy::Buffer buffer;
+ buffer.mFrameCount = 1;
+ (void) mAudioTrackServerProxy->obtainBuffer(&buffer, true /*ackFlush*/);
}
} else {
status = BAD_VALUE;
@@ -699,6 +735,7 @@
mRetryCount = PlaybackThread::kMaxTrackRetriesOffload;
}
+ mFlushHwPending = true;
mResumeToStopping = false;
} else {
if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED &&
@@ -719,11 +756,19 @@
// Prevent flush being lost if the track is flushed and then resumed
// before mixer thread can run. This is important when offloading
// because the hardware buffer could hold a large amount of audio
- playbackThread->flushOutput_l();
playbackThread->broadcast_l();
}
}
+// must be called with thread lock held
+void AudioFlinger::PlaybackThread::Track::flushAck()
+{
+ if (!isOffloaded())
+ return;
+
+ mFlushHwPending = false;
+}
+
void AudioFlinger::PlaybackThread::Track::reset()
{
// Do not reset twice to avoid discarding data written just after a flush and before
@@ -832,6 +877,7 @@
dstChain->strategy(),
AUDIO_SESSION_OUTPUT_MIX,
effect->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
}
status = playbackThread->attachAuxEffect(this, EffectId);
}
@@ -935,6 +981,16 @@
mIsInvalid = true;
}
+void AudioFlinger::PlaybackThread::Track::signal()
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
+}
+
// ----------------------------------------------------------------------------
sp<AudioFlinger::PlaybackThread::TimedTrack>
@@ -947,13 +1003,15 @@
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId) {
+ int sessionId,
+ int uid)
+{
if (!client->reserveTimedTrack())
return 0;
return new TimedTrack(
thread, client, streamType, sampleRate, format, channelMask, frameCount,
- sharedBuffer, sessionId);
+ sharedBuffer, sessionId, uid);
}
AudioFlinger::PlaybackThread::TimedTrack::TimedTrack(
@@ -965,9 +1023,10 @@
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId)
+ int sessionId,
+ int uid)
: Track(thread, client, streamType, sampleRate, format, channelMask,
- frameCount, sharedBuffer, sessionId, IAudioFlinger::TRACK_TIMED),
+ frameCount, sharedBuffer, sessionId, uid, IAudioFlinger::TRACK_TIMED),
mQueueHeadInFlight(false),
mTrimQueueHeadOnRelease(false),
mFramesPendingInQueue(0),
@@ -1018,11 +1077,8 @@
}
sp<IMemory> newBuffer = mTimedMemoryDealer->allocate(size);
- if (newBuffer == NULL) {
- newBuffer = mTimedMemoryDealer->allocate(size);
- if (newBuffer == NULL) {
- return NO_MEMORY;
- }
+ if (newBuffer == 0 || newBuffer->pointer() == NULL) {
+ return NO_MEMORY;
}
*buffer = newBuffer;
@@ -1121,7 +1177,7 @@
void AudioFlinger::PlaybackThread::TimedTrack::updateFramesPendingAfterTrim_l(
const TimedBuffer& buf,
- const char* logTag) {
+ const char* logTag __unused) {
uint32_t bufBytes = buf.buffer()->size();
uint32_t consumedAlready = buf.position();
@@ -1462,9 +1518,10 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount)
+ size_t frameCount,
+ int uid)
: Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount,
- NULL, 0, IAudioFlinger::TRACK_DEFAULT),
+ NULL, 0, uid, IAudioFlinger::TRACK_DEFAULT),
mActive(false), mSourceThread(sourceThread), mClientProxy(NULL)
{
@@ -1472,9 +1529,9 @@
mOutBuffer.frameCount = 0;
playbackThread->mTracks.add(this);
ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, "
- "mCblk->frameCount_ %u, mChannelMask 0x%08x",
+ "frameCount %u, mChannelMask 0x%08x",
mCblk, mBuffer,
- mCblk->frameCount_, mChannelMask);
+ frameCount, mChannelMask);
// since client and server are in the same process,
// the buffer has the same virtual address on both sides
mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize);
@@ -1724,25 +1781,43 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- int sessionId)
+ int sessionId,
+ int uid)
: TrackBase(thread, client, sampleRate, format,
- channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, false /*isOut*/),
- mOverflow(false)
+ channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/),
+ mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
+ // See real initialization of mRsmpInFront at RecordThread::start()
+ mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)
{
ALOGV("RecordTrack constructor");
if (mCblk != NULL) {
mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount, mFrameSize);
}
+
+ uint32_t channelCount = popcount(channelMask);
+ // FIXME I don't understand either of the channel count checks
+ if (thread->mSampleRate != sampleRate && thread->mChannelCount <= FCC_2 &&
+ channelCount <= FCC_2) {
+ // sink SR
+ mResampler = AudioResampler::create(16, thread->mChannelCount, sampleRate);
+ // source SR
+ mResampler->setSampleRate(thread->mSampleRate);
+ mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
+ mResamplerBufferProvider = new ResamplerBufferProvider(this);
+ }
}
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
{
ALOGV("%s", __func__);
+ delete mResampler;
+ delete[] mRsmpOutBuffer;
+ delete mResamplerBufferProvider;
}
// AudioBufferProvider interface
status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer,
- int64_t pts)
+ int64_t pts __unused)
{
ServerProxy::Buffer buf;
buf.mFrameCount = buffer->frameCount;
@@ -1810,19 +1885,45 @@
/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
{
- result.append("Client Fmt Chn mask Session S Server fCount\n");
+ result.append(" Active Client Fmt Chn mask Session S Server fCount Resampling\n");
}
-void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
+void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size, bool active)
{
- snprintf(buffer, size, "%6u %3u %08X %7u %1d %08X %6u\n",
+ snprintf(buffer, size, " %6s %6u %3u %08X %7u %1d %08X %6zu %10d\n",
+ active ? "yes" : "no",
(mClient == 0) ? getpid_cached : mClient->pid(),
mFormat,
mChannelMask,
mSessionId,
mState,
mCblk->mServer,
- mFrameCount);
+ mFrameCount,
+ mResampler != NULL);
+
+}
+
+void AudioFlinger::RecordThread::RecordTrack::handleSyncStartEvent(const sp<SyncEvent>& event)
+{
+ if (event == mSyncStartEvent) {
+ ssize_t framesToDrop = 0;
+ sp<ThreadBase> threadBase = mThread.promote();
+ if (threadBase != 0) {
+ // TODO: use actual buffer filling status instead of 2 buffers when info is available
+ // from audio HAL
+ framesToDrop = threadBase->mFrameCount * 2;
+ }
+ mFramesToDrop = framesToDrop;
+ }
+}
+
+void AudioFlinger::RecordThread::RecordTrack::clearSyncStartEvent()
+{
+ if (mSyncStartEvent != 0) {
+ mSyncStartEvent->cancel();
+ mSyncStartEvent.clear();
+ }
+ mFramesToDrop = 0;
}
}; // namespace android
diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp
index 7a314cf..3ab3ba9 100644
--- a/services/audioflinger/test-resample.cpp
+++ b/services/audioflinger/test-resample.cpp
@@ -26,77 +26,104 @@
#include <errno.h>
#include <time.h>
#include <math.h>
+#include <audio_utils/sndfile.h>
+#include <utils/Vector.h>
using namespace android;
-struct HeaderWav {
- HeaderWav(size_t size, int nc, int sr, int bits) {
- strncpy(RIFF, "RIFF", 4);
- chunkSize = size + sizeof(HeaderWav);
- strncpy(WAVE, "WAVE", 4);
- strncpy(fmt, "fmt ", 4);
- fmtSize = 16;
- audioFormat = 1;
- numChannels = nc;
- samplesRate = sr;
- byteRate = sr * numChannels * (bits/8);
- align = nc*(bits/8);
- bitsPerSample = bits;
- strncpy(data, "data", 4);
- dataSize = size;
- }
-
- char RIFF[4]; // RIFF
- uint32_t chunkSize; // File size
- char WAVE[4]; // WAVE
- char fmt[4]; // fmt\0
- uint32_t fmtSize; // fmt size
- uint16_t audioFormat; // 1=PCM
- uint16_t numChannels; // num channels
- uint32_t samplesRate; // sample rate in hz
- uint32_t byteRate; // Bps
- uint16_t align; // 2=16-bit mono, 4=16-bit stereo
- uint16_t bitsPerSample; // bits per sample
- char data[4]; // "data"
- uint32_t dataSize; // size
-};
+bool gVerbose = false;
static int usage(const char* name) {
- fprintf(stderr,"Usage: %s [-p] [-h] [-s] [-q {dq|lq|mq|hq|vhq}] [-i input-sample-rate] "
- "[-o output-sample-rate] [<input-file>] <output-file>\n", name);
+ fprintf(stderr,"Usage: %s [-p] [-h] [-v] [-s] [-q {dq|lq|mq|hq|vhq|dlq|dmq|dhq}]"
+ " [-i input-sample-rate] [-o output-sample-rate] [-O csv] [-P csv] [<input-file>]"
+ " <output-file>\n", name);
fprintf(stderr," -p enable profiling\n");
fprintf(stderr," -h create wav file\n");
- fprintf(stderr," -s stereo\n");
+ fprintf(stderr," -v verbose : log buffer provider calls\n");
+ fprintf(stderr," -s stereo (ignored if input file is specified)\n");
fprintf(stderr," -q resampler quality\n");
fprintf(stderr," dq : default quality\n");
fprintf(stderr," lq : low quality\n");
fprintf(stderr," mq : medium quality\n");
fprintf(stderr," hq : high quality\n");
fprintf(stderr," vhq : very high quality\n");
- fprintf(stderr," -i input file sample rate\n");
+ fprintf(stderr," dlq : dynamic low quality\n");
+ fprintf(stderr," dmq : dynamic medium quality\n");
+ fprintf(stderr," dhq : dynamic high quality\n");
+ fprintf(stderr," -i input file sample rate (ignored if input file is specified)\n");
fprintf(stderr," -o output file sample rate\n");
+ fprintf(stderr," -O # frames output per call to resample() in CSV format\n");
+ fprintf(stderr," -P # frames provided per call to resample() in CSV format\n");
return -1;
}
+// Convert a list of integers in CSV format to a Vector of those values.
+// Returns the number of elements in the list, or -1 on error.
+int parseCSV(const char *string, Vector<int>& values)
+{
+ // pass 1: count the number of values and do syntax check
+ size_t numValues = 0;
+ bool hadDigit = false;
+ for (const char *p = string; ; ) {
+ switch (*p++) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ hadDigit = true;
+ break;
+ case '\0':
+ if (hadDigit) {
+ // pass 2: allocate and initialize vector of values
+ values.resize(++numValues);
+ values.editItemAt(0) = atoi(p = optarg);
+ for (size_t i = 1; i < numValues; ) {
+ if (*p++ == ',') {
+ values.editItemAt(i++) = atoi(p);
+ }
+ }
+ return numValues;
+ }
+ // fall through
+ case ',':
+ if (hadDigit) {
+ hadDigit = false;
+ numValues++;
+ break;
+ }
+ // fall through
+ default:
+ return -1;
+ }
+ }
+}
+
int main(int argc, char* argv[]) {
const char* const progname = argv[0];
- bool profiling = false;
+ bool profileResample = false;
+ bool profileFilter = false;
bool writeHeader = false;
int channels = 1;
int input_freq = 0;
int output_freq = 0;
AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY;
+ Vector<int> Ovalues;
+ Vector<int> Pvalues;
int ch;
- while ((ch = getopt(argc, argv, "phsq:i:o:")) != -1) {
+ while ((ch = getopt(argc, argv, "pfhvsq:i:o:O:P:")) != -1) {
switch (ch) {
case 'p':
- profiling = true;
+ profileResample = true;
+ break;
+ case 'f':
+ profileFilter = true;
break;
case 'h':
writeHeader = true;
break;
+ case 'v':
+ gVerbose = true;
+ break;
case 's':
channels = 2;
break;
@@ -111,6 +138,12 @@
quality = AudioResampler::HIGH_QUALITY;
else if (!strcmp(optarg, "vhq"))
quality = AudioResampler::VERY_HIGH_QUALITY;
+ else if (!strcmp(optarg, "dlq"))
+ quality = AudioResampler::DYN_LOW_QUALITY;
+ else if (!strcmp(optarg, "dmq"))
+ quality = AudioResampler::DYN_MED_QUALITY;
+ else if (!strcmp(optarg, "dhq"))
+ quality = AudioResampler::DYN_HIGH_QUALITY;
else {
usage(progname);
return -1;
@@ -122,6 +155,18 @@
case 'o':
output_freq = atoi(optarg);
break;
+ case 'O':
+ if (parseCSV(optarg, Ovalues) < 0) {
+ fprintf(stderr, "incorrect syntax for -O option\n");
+ return -1;
+ }
+ break;
+ case 'P':
+ if (parseCSV(optarg, Pvalues) < 0) {
+ fprintf(stderr, "incorrect syntax for -P option\n");
+ return -1;
+ }
+ break;
case '?':
default:
usage(progname);
@@ -148,25 +193,22 @@
size_t input_size;
void* input_vaddr;
if (argc == 2) {
- struct stat st;
- if (stat(file_in, &st) < 0) {
- fprintf(stderr, "stat: %s\n", strerror(errno));
- return -1;
+ SF_INFO info;
+ info.format = 0;
+ SNDFILE *sf = sf_open(file_in, SFM_READ, &info);
+ if (sf == NULL) {
+ perror(file_in);
+ return EXIT_FAILURE;
}
-
- int input_fd = open(file_in, O_RDONLY);
- if (input_fd < 0) {
- fprintf(stderr, "open: %s\n", strerror(errno));
- return -1;
- }
-
- input_size = st.st_size;
- input_vaddr = mmap(0, input_size, PROT_READ, MAP_PRIVATE, input_fd, 0);
- if (input_vaddr == MAP_FAILED ) {
- fprintf(stderr, "mmap: %s\n", strerror(errno));
- return -1;
- }
+ input_size = info.frames * info.channels * sizeof(short);
+ input_vaddr = malloc(input_size);
+ (void) sf_readf_short(sf, (short *) input_vaddr, info.frames);
+ sf_close(sf);
+ channels = info.channels;
+ input_freq = info.samplerate;
} else {
+ // data for testing is exactly (input sampling rate/1000)/2 seconds
+ // so 44.1khz input is 22.05 seconds
double k = 1000; // Hz / s
double time = (input_freq / 2) / k;
size_t input_frames = size_t(input_freq * time);
@@ -178,7 +220,7 @@
double y = sin(M_PI * k * t * t);
int16_t yi = floor(y * 32767.0 + 0.5);
for (size_t j=0 ; j<(size_t)channels ; j++) {
- in[i*channels + j] = yi / (1+j);
+ in[i*channels + j] = yi / (1+j); // right ch. 1/2 left ch.
}
}
}
@@ -186,89 +228,263 @@
// ----------------------------------------------------------
class Provider: public AudioBufferProvider {
- int16_t* mAddr;
- size_t mNumFrames;
+ int16_t* const mAddr; // base address
+ const size_t mNumFrames; // total frames
+ const int mChannels;
+ size_t mNextFrame; // index of next frame to provide
+ size_t mUnrel; // number of frames not yet released
+ const Vector<int> mPvalues; // number of frames provided per call
+ size_t mNextPidx; // index of next entry in mPvalues to use
public:
- Provider(const void* addr, size_t size, int channels) {
- mAddr = (int16_t*) addr;
- mNumFrames = size / (channels*sizeof(int16_t));
+ Provider(const void* addr, size_t size, int channels, const Vector<int>& Pvalues)
+ : mAddr((int16_t*) addr),
+ mNumFrames(size / (channels*sizeof(int16_t))),
+ mChannels(channels),
+ mNextFrame(0), mUnrel(0), mPvalues(Pvalues), mNextPidx(0) {
}
virtual status_t getNextBuffer(Buffer* buffer,
int64_t pts = kInvalidPTS) {
- buffer->frameCount = mNumFrames;
- buffer->i16 = mAddr;
- return NO_ERROR;
+ (void)pts; // suppress warning
+ size_t requestedFrames = buffer->frameCount;
+ if (requestedFrames > mNumFrames - mNextFrame) {
+ buffer->frameCount = mNumFrames - mNextFrame;
+ }
+ if (!mPvalues.isEmpty()) {
+ size_t provided = mPvalues[mNextPidx++];
+ printf("mPvalue[%d]=%u not %u\n", mNextPidx-1, provided, buffer->frameCount);
+ if (provided < buffer->frameCount) {
+ buffer->frameCount = provided;
+ }
+ if (mNextPidx >= mPvalues.size()) {
+ mNextPidx = 0;
+ }
+ }
+ if (gVerbose) {
+ printf("getNextBuffer() requested %u frames out of %u frames available,"
+ " and returned %u frames\n",
+ requestedFrames, mNumFrames - mNextFrame, buffer->frameCount);
+ }
+ mUnrel = buffer->frameCount;
+ if (buffer->frameCount > 0) {
+ buffer->i16 = &mAddr[mChannels * mNextFrame];
+ return NO_ERROR;
+ } else {
+ buffer->i16 = NULL;
+ return NOT_ENOUGH_DATA;
+ }
}
virtual void releaseBuffer(Buffer* buffer) {
+ if (buffer->frameCount > mUnrel) {
+ fprintf(stderr, "ERROR releaseBuffer() released %u frames but only %u available "
+ "to release\n", buffer->frameCount, mUnrel);
+ mNextFrame += mUnrel;
+ mUnrel = 0;
+ } else {
+ if (gVerbose) {
+ printf("releaseBuffer() released %u frames out of %u frames available "
+ "to release\n", buffer->frameCount, mUnrel);
+ }
+ mNextFrame += buffer->frameCount;
+ mUnrel -= buffer->frameCount;
+ }
+ buffer->frameCount = 0;
+ buffer->i16 = NULL;
}
- } provider(input_vaddr, input_size, channels);
+ void reset() {
+ mNextFrame = 0;
+ }
+ } provider(input_vaddr, input_size, channels, Pvalues);
size_t input_frames = input_size / (channels * sizeof(int16_t));
+ if (gVerbose) {
+ printf("%u input frames\n", input_frames);
+ }
size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq;
output_size &= ~7; // always stereo, 32-bits
- void* output_vaddr = malloc(output_size);
-
- if (profiling) {
+ if (profileFilter) {
+ // Check how fast sample rate changes are that require filter changes.
+ // The delta sample rate changes must indicate a downsampling ratio,
+ // and must be larger than 10% changes.
+ //
+ // On fast devices, filters should be generated between 0.1ms - 1ms.
+ // (single threaded).
AudioResampler* resampler = AudioResampler::create(16, channels,
- output_freq, quality);
-
- size_t out_frames = output_size/8;
- resampler->setSampleRate(input_freq);
- resampler->setVolume(0x1000, 0x1000);
-
- memset(output_vaddr, 0, output_size);
+ 8000, quality);
+ int looplimit = 100;
timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
- resampler->resample((int*) output_vaddr, out_frames, &provider);
- resampler->resample((int*) output_vaddr, out_frames, &provider);
- resampler->resample((int*) output_vaddr, out_frames, &provider);
- resampler->resample((int*) output_vaddr, out_frames, &provider);
+ for (int i = 0; i < looplimit; ++i) {
+ resampler->setSampleRate(9000);
+ resampler->setSampleRate(12000);
+ resampler->setSampleRate(20000);
+ resampler->setSampleRate(30000);
+ }
clock_gettime(CLOCK_MONOTONIC, &end);
int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec;
int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec;
- int64_t time = (end_ns - start_ns)/4;
- printf("%f Mspl/s\n", out_frames/(time/1e9)/1e6);
+ int64_t time = end_ns - start_ns;
+ printf("%.2f sample rate changes with filter calculation/sec\n",
+ looplimit * 4 / (time / 1e9));
+ // Check how fast sample rate changes are without filter changes.
+ // This should be very fast, probably 0.1us - 1us per sample rate
+ // change.
+ resampler->setSampleRate(1000);
+ looplimit = 1000;
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ for (int i = 0; i < looplimit; ++i) {
+ resampler->setSampleRate(1000+i);
+ }
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ start_ns = start.tv_sec * 1000000000LL + start.tv_nsec;
+ end_ns = end.tv_sec * 1000000000LL + end.tv_nsec;
+ time = end_ns - start_ns;
+ printf("%.2f sample rate changes without filter calculation/sec\n",
+ looplimit / (time / 1e9));
+ resampler->reset();
delete resampler;
}
+ void* output_vaddr = malloc(output_size);
AudioResampler* resampler = AudioResampler::create(16, channels,
output_freq, quality);
size_t out_frames = output_size/8;
+
+ /* set volume precision to 12 bits, so the volume scale is 1<<12.
+ * This means the "integer" part fits in the Q19.12 precision
+ * representation of output int32_t.
+ *
+ * Generally 0 < volumePrecision <= 14 (due to the limits of
+ * int16_t values for Volume). volumePrecision cannot be 0 due
+ * to rounding and shifts.
+ */
+ const int volumePrecision = 12; // in bits
+
resampler->setSampleRate(input_freq);
- resampler->setVolume(0x1000, 0x1000);
+ resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
+
+ if (profileResample) {
+ /*
+ * For profiling on mobile devices, upon experimentation
+ * it is better to run a few trials with a shorter loop limit,
+ * and take the minimum time.
+ *
+ * Long tests can cause CPU temperature to build up and thermal throttling
+ * to reduce CPU frequency.
+ *
+ * For frequency checks (index=0, or 1, etc.):
+ * "cat /sys/devices/system/cpu/cpu${index}/cpufreq/scaling_*_freq"
+ *
+ * For temperature checks (index=0, or 1, etc.):
+ * "cat /sys/class/thermal/thermal_zone${index}/temp"
+ *
+ * Another way to avoid thermal throttling is to fix the CPU frequency
+ * at a lower level which prevents excessive temperatures.
+ */
+ const int trials = 4;
+ const int looplimit = 4;
+ timespec start, end;
+ int64_t time;
+
+ for (int n = 0; n < trials; ++n) {
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ for (int i = 0; i < looplimit; ++i) {
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+ provider.reset(); // during benchmarking reset only the provider
+ }
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec;
+ int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec;
+ int64_t diff_ns = end_ns - start_ns;
+ if (n == 0 || diff_ns < time) {
+ time = diff_ns; // save the best out of our trials.
+ }
+ }
+ // Mfrms/s is "Millions of output frames per second".
+ printf("quality: %d channels: %d msec: %lld Mfrms/s: %.2lf\n",
+ quality, channels, time/1000000, out_frames * looplimit / (time / 1e9) / 1e6);
+ resampler->reset();
+ }
memset(output_vaddr, 0, output_size);
- resampler->resample((int*) output_vaddr, out_frames, &provider);
+ if (gVerbose) {
+ printf("resample() %u output frames\n", out_frames);
+ }
+ if (Ovalues.isEmpty()) {
+ Ovalues.push(out_frames);
+ }
+ for (size_t i = 0, j = 0; i < out_frames; ) {
+ size_t thisFrames = Ovalues[j++];
+ if (j >= Ovalues.size()) {
+ j = 0;
+ }
+ if (thisFrames == 0 || thisFrames > out_frames - i) {
+ thisFrames = out_frames - i;
+ }
+ resampler->resample((int*) output_vaddr + 2*i, thisFrames, &provider);
+ i += thisFrames;
+ }
+ if (gVerbose) {
+ printf("resample() complete\n");
+ }
+ resampler->reset();
+ if (gVerbose) {
+ printf("reset() complete\n");
+ }
+ delete resampler;
+ resampler = NULL;
- // down-mix (we just truncate and keep the left channel)
+ // mono takes left channel only
+ // stereo right channel is half amplitude of stereo left channel (due to input creation)
int32_t* out = (int32_t*) output_vaddr;
int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t));
+
+ // round to half towards zero and saturate at int16 (non-dithered)
+ const int roundVal = (1<<(volumePrecision-1)) - 1; // volumePrecision > 0
+
for (size_t i = 0; i < out_frames; i++) {
- for (int j=0 ; j<channels ; j++) {
- int32_t s = out[i * 2 + j] >> 12;
- if (s > 32767) s = 32767;
- else if (s < -32768) s = -32768;
+ for (int j = 0; j < channels; j++) {
+ int32_t s = out[i * 2 + j] + roundVal; // add offset here
+ if (s < 0) {
+ s = (s + 1) >> volumePrecision; // round to 0
+ if (s < -32768) {
+ s = -32768;
+ }
+ } else {
+ s = s >> volumePrecision;
+ if (s > 32767) {
+ s = 32767;
+ }
+ }
convert[i * channels + j] = int16_t(s);
}
}
// write output to disk
- int output_fd = open(file_out, O_WRONLY | O_CREAT | O_TRUNC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (output_fd < 0) {
- fprintf(stderr, "open: %s\n", strerror(errno));
- return -1;
- }
-
if (writeHeader) {
- HeaderWav wav(out_frames * channels * sizeof(int16_t), channels, output_freq, 16);
- write(output_fd, &wav, sizeof(wav));
+ SF_INFO info;
+ info.frames = 0;
+ info.samplerate = output_freq;
+ info.channels = channels;
+ info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+ SNDFILE *sf = sf_open(file_out, SFM_WRITE, &info);
+ if (sf == NULL) {
+ perror(file_out);
+ return EXIT_FAILURE;
+ }
+ (void) sf_writef_short(sf, convert, out_frames);
+ sf_close(sf);
+ } else {
+ int output_fd = open(file_out, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (output_fd < 0) {
+ perror(file_out);
+ return EXIT_FAILURE;
+ }
+ write(output_fd, convert, out_frames * channels * sizeof(int16_t));
+ close(output_fd);
}
- write(output_fd, convert, out_frames * channels * sizeof(int16_t));
- close(output_fd);
-
- return 0;
+ return EXIT_SUCCESS;
}
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index d659ebb..51ba698 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -33,7 +33,9 @@
device3/Camera3InputStream.cpp \
device3/Camera3OutputStream.cpp \
device3/Camera3ZslStream.cpp \
+ device3/StatusTracker.cpp \
gui/RingBufferConsumer.cpp \
+ utils/CameraTraces.cpp \
LOCAL_SHARED_LIBRARIES:= \
libui \
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 5e84aaf..79fbf76 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -32,6 +32,7 @@
#include <gui/Surface.h>
#include <hardware/hardware.h>
#include <media/AudioSystem.h>
+#include <media/IMediaHTTPService.h>
#include <media/mediaplayer.h>
#include <utils/Errors.h>
#include <utils/Log.h>
@@ -42,6 +43,7 @@
#include "api1/Camera2Client.h"
#include "api_pro/ProCamera2Client.h"
#include "api2/CameraDeviceClient.h"
+#include "utils/CameraTraces.h"
#include "CameraDeviceFactory.h"
namespace android {
@@ -255,6 +257,12 @@
return BAD_VALUE;
}
+ if (getDeviceVersion(cameraId, &facing) <= CAMERA_DEVICE_API_VERSION_2_1) {
+ // Disable HAL2.x support for camera2 API for now.
+ ALOGW("%s: HAL2.x doesn't support getCameraCharacteristics for now", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
struct camera_info info;
status_t ret = mModule->get_camera_info(cameraId, &info);
*cameraInfo = info.static_camera_characteristics;
@@ -859,7 +867,7 @@
MediaPlayer* CameraService::newMediaPlayer(const char *file) {
MediaPlayer* mp = new MediaPlayer();
- if (mp->setDataSource(file, NULL) == NO_ERROR) {
+ if (mp->setDataSource(NULL /* httpService */, file, NULL) == NO_ERROR) {
mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE);
mp->prepare();
} else {
@@ -1035,13 +1043,13 @@
// ----------------------------------------------------------------------------
Mutex* CameraService::Client::getClientLockFromCookie(void* user) {
- return gCameraService->getClientLockById((int) user);
+ return gCameraService->getClientLockById((int)(intptr_t) user);
}
// Provide client pointer for callbacks. Client lock returned from getClientLockFromCookie should
// be acquired for this to be safe
CameraService::Client* CameraService::Client::getClientFromCookie(void* user) {
- BasicClient *basicClient = gCameraService->getClientByIdUnsafe((int) user);
+ BasicClient *basicClient = gCameraService->getClientByIdUnsafe((int)(intptr_t) user);
// OK: only CameraClient calls this, and they already cast anyway.
Client* client = static_cast<Client*>(basicClient);
@@ -1213,6 +1221,10 @@
if (locked) mServiceLock.unlock();
+ // Dump camera traces if there were any
+ write(fd, "\n", 1);
+ camera3::CameraTraces::dump(fd, args);
+
// change logging level
int n = args.size();
for (int i = 0; i + 1 < n; i++) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 09829ea..80b7cd4 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#define LOG_TAG "Camera2"
+#define LOG_TAG "Camera2Client"
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
+#include <inttypes.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -76,13 +77,15 @@
return res;
}
- SharedParameters::Lock l(mParameters);
+ {
+ SharedParameters::Lock l(mParameters);
- res = l.mParameters.initialize(&(mDevice->info()));
- if (res != OK) {
- ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
- __FUNCTION__, mCameraId, strerror(-res), res);
- return NO_INIT;
+ res = l.mParameters.initialize(&(mDevice->info()));
+ if (res != OK) {
+ ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return NO_INIT;
+ }
}
String8 threadName;
@@ -135,6 +138,7 @@
mCallbackProcessor->run(threadName.string());
if (gLogLevel >= 1) {
+ SharedParameters::Lock l(mParameters);
ALOGD("%s: Default parameters converted from camera %d:", __FUNCTION__,
mCameraId);
ALOGD("%s", l.mParameters.paramsFlattened.string());
@@ -190,7 +194,7 @@
result.appendFormat(" GPS lat x long x alt: %f x %f x %f\n",
p.gpsCoordinates[0], p.gpsCoordinates[1],
p.gpsCoordinates[2]);
- result.appendFormat(" GPS timestamp: %lld\n",
+ result.appendFormat(" GPS timestamp: %" PRId64 "\n",
p.gpsTimestamp);
result.appendFormat(" GPS processing method: %s\n",
p.gpsProcessingMethod.string());
@@ -234,7 +238,7 @@
result.append(" Scene mode: ");
switch (p.sceneMode) {
- case ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED:
+ case ANDROID_CONTROL_SCENE_MODE_DISABLED:
result.append("AUTO\n"); break;
CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_ACTION)
CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_PORTRAIT)
@@ -353,6 +357,10 @@
result.appendFormat(" meteringCropRegion\n");
haveQuirk = true;
}
+ if (p.quirks.partialResults) {
+ result.appendFormat(" usePartialResult\n");
+ haveQuirk = true;
+ }
if (!haveQuirk) {
result.appendFormat(" none\n");
}
@@ -399,12 +407,6 @@
l.mParameters.state = Parameters::DISCONNECTED;
}
- mStreamingProcessor->deletePreviewStream();
- mStreamingProcessor->deleteRecordingStream();
- mJpegProcessor->deleteStream();
- mCallbackProcessor->deleteStream();
- mZslProcessor->deleteStream();
-
mStreamingProcessor->requestExit();
mFrameProcessor->requestExit();
mCaptureSequencer->requestExit();
@@ -421,6 +423,14 @@
mZslProcessorThread->join();
mCallbackProcessor->join();
+ ALOGV("Camera %d: Deleting streams", mCameraId);
+
+ mStreamingProcessor->deletePreviewStream();
+ mStreamingProcessor->deleteRecordingStream();
+ mJpegProcessor->deleteStream();
+ mCallbackProcessor->deleteStream();
+ mZslProcessor->deleteStream();
+
ALOGV("Camera %d: Disconnecting device", mCameraId);
mDevice->disconnect();
@@ -724,6 +734,7 @@
return OK;
}
params.state = Parameters::STOPPED;
+ int lastPreviewStreamId = mStreamingProcessor->getPreviewStreamId();
res = mStreamingProcessor->updatePreviewStream(params);
if (res != OK) {
@@ -732,6 +743,8 @@
return res;
}
+ bool previewStreamChanged = mStreamingProcessor->getPreviewStreamId() != lastPreviewStreamId;
+
// We could wait to create the JPEG output stream until first actual use
// (first takePicture call). However, this would substantially increase the
// first capture latency on HAL3 devices, and potentially on some HAL2
@@ -781,6 +794,19 @@
return res;
}
outputStreams.push(getCallbackStreamId());
+ } else if (previewStreamChanged && mCallbackProcessor->getStreamId() != NO_STREAM) {
+ /**
+ * Delete the unused callback stream when preview stream is changed and
+ * preview is not enabled. Don't need stop preview stream as preview is in
+ * STOPPED state now.
+ */
+ ALOGV("%s: Camera %d: Delete unused preview callback stream.", __FUNCTION__, mCameraId);
+ res = mCallbackProcessor->deleteStream();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to delete callback stream %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
}
if (params.zslMode && !params.recordingHint) {
res = updateProcessorStream(mZslProcessor, params);
@@ -790,6 +816,8 @@
return res;
}
outputStreams.push(getZslStreamId());
+ } else {
+ mZslProcessor->deleteStream();
}
outputStreams.push(getPreviewStreamId());
@@ -1136,7 +1164,7 @@
* Handle quirk mode for AF in scene modes
*/
if (l.mParameters.quirks.triggerAfWithAuto &&
- l.mParameters.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED &&
+ l.mParameters.sceneMode != ANDROID_CONTROL_SCENE_MODE_DISABLED &&
l.mParameters.focusMode != Parameters::FOCUS_MODE_AUTO &&
!l.mParameters.focusingAreas[0].isEmpty()) {
ALOGV("%s: Quirk: Switching from focusMode %d to AUTO",
@@ -1149,6 +1177,8 @@
l.mParameters.currentAfTriggerId = ++l.mParameters.afTriggerCounter;
triggerId = l.mParameters.currentAfTriggerId;
}
+ ATRACE_ASYNC_BEGIN(kAutofocusLabel, triggerId);
+
syncWithDevice();
mDevice->triggerAutofocus(triggerId);
@@ -1171,6 +1201,12 @@
l.mParameters.focusMode == Parameters::FOCUS_MODE_INFINITY) {
return OK;
}
+
+ // An active AF trigger is canceled
+ if (l.mParameters.afTriggerCounter == l.mParameters.currentAfTriggerId) {
+ ATRACE_ASYNC_END(kAutofocusLabel, l.mParameters.currentAfTriggerId);
+ }
+
triggerId = ++l.mParameters.afTriggerCounter;
// When using triggerAfWithAuto quirk, may need to reset focus mode to
@@ -1199,6 +1235,7 @@
status_t res;
if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+ int takePictureCounter;
{
SharedParameters::Lock l(mParameters);
switch (l.mParameters.state) {
@@ -1237,8 +1274,11 @@
__FUNCTION__, mCameraId, strerror(-res), res);
return res;
}
+ takePictureCounter = ++l.mParameters.takePictureCounter;
}
+ ATRACE_ASYNC_BEGIN(kTakepictureLabel, takePictureCounter);
+
// Need HAL to have correct settings before (possibly) triggering precapture
syncWithDevice();
@@ -1466,7 +1506,24 @@
bool afInMotion = false;
{
SharedParameters::Lock l(mParameters);
+ // Trace end of AF state
+ char tmp[32];
+ if (l.mParameters.afStateCounter > 0) {
+ camera_metadata_enum_snprint(
+ ANDROID_CONTROL_AF_STATE, l.mParameters.focusState, tmp, sizeof(tmp));
+ ATRACE_ASYNC_END(tmp, l.mParameters.afStateCounter);
+ }
+
+ // Update state
l.mParameters.focusState = newState;
+ l.mParameters.afStateCounter++;
+
+ // Trace start of AF state
+
+ camera_metadata_enum_snprint(
+ ANDROID_CONTROL_AF_STATE, l.mParameters.focusState, tmp, sizeof(tmp));
+ ATRACE_ASYNC_BEGIN(tmp, l.mParameters.afStateCounter);
+
switch (l.mParameters.focusMode) {
case Parameters::FOCUS_MODE_AUTO:
case Parameters::FOCUS_MODE_MACRO:
@@ -1560,6 +1617,7 @@
}
}
if (sendCompletedMessage) {
+ ATRACE_ASYNC_END(kAutofocusLabel, triggerId);
SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
if (l.mRemoteCallback != 0) {
l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS,
@@ -1769,4 +1827,7 @@
return res;
}
+const char* Camera2Client::kAutofocusLabel = "autofocus";
+const char* Camera2Client::kTakepictureLabel = "take_picture";
+
} // namespace android
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 53629a1..fe0bf74 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -136,6 +136,10 @@
static const int32_t kCaptureRequestIdStart = 30000000;
static const int32_t kCaptureRequestIdEnd = 40000000;
+ // Constant strings for ATRACE logging
+ static const char* kAutofocusLabel;
+ static const char* kTakepictureLabel;
+
private:
/** ICamera interface-related private members */
typedef camera2::Parameters Parameters;
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index bd6805d..30b7bb8 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -85,7 +85,7 @@
mHardware->setCallbacks(notifyCallback,
dataCallback,
dataCallbackTimestamp,
- (void *)mCameraId);
+ (void *)(uintptr_t)mCameraId);
// Enable zoom, error, focus, and metadata messages by default
enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS |
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index 9d8c4a1..d2ac79c 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -312,6 +312,16 @@
return OK;
}
+ if (imgBuffer.width != static_cast<uint32_t>(l.mParameters.previewWidth) ||
+ imgBuffer.height != static_cast<uint32_t>(l.mParameters.previewHeight)) {
+ ALOGW("%s: The preview size has changed to %d x %d from %d x %d, this buffer is"
+ " no longer valid, dropping",__FUNCTION__,
+ l.mParameters.previewWidth, l.mParameters.previewHeight,
+ imgBuffer.width, imgBuffer.height);
+ mCallbackConsumer->unlockBuffer(imgBuffer);
+ return OK;
+ }
+
previewFormat = l.mParameters.previewFormat;
useFlexibleYuv = l.mParameters.fastInfo.useFlexibleYuv &&
(previewFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
index ca3198f..8a4ce4e 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
@@ -43,6 +43,7 @@
mShutterNotified(false),
mClient(client),
mCaptureState(IDLE),
+ mStateTransitionCount(0),
mTriggerId(0),
mTimeoutCount(0),
mCaptureId(Camera2Client::kCaptureRequestIdStart),
@@ -103,12 +104,12 @@
}
}
-void CaptureSequencer::onFrameAvailable(int32_t frameId,
+void CaptureSequencer::onFrameAvailable(int32_t requestId,
const CameraMetadata &frame) {
ALOGV("%s: Listener found new frame", __FUNCTION__);
ATRACE_CALL();
Mutex::Autolock l(mInputMutex);
- mNewFrameId = frameId;
+ mNewFrameId = requestId;
mNewFrame = frame;
if (!mNewFrameReceived) {
mNewFrameReceived = true;
@@ -198,8 +199,14 @@
Mutex::Autolock l(mStateMutex);
if (currentState != mCaptureState) {
+ if (mCaptureState != IDLE) {
+ ATRACE_ASYNC_END(kStateNames[mCaptureState], mStateTransitionCount);
+ }
mCaptureState = currentState;
- ATRACE_INT("cam2_capt_state", mCaptureState);
+ mStateTransitionCount++;
+ if (mCaptureState != IDLE) {
+ ATRACE_ASYNC_BEGIN(kStateNames[mCaptureState], mStateTransitionCount);
+ }
ALOGV("Camera %d: New capture state %s",
client->getCameraId(), kStateNames[mCaptureState]);
mStateChanged.signal();
@@ -243,6 +250,7 @@
mBusy = false;
}
+ int takePictureCounter = 0;
{
SharedParameters::Lock l(client->getParameters());
switch (l.mParameters.state) {
@@ -270,6 +278,7 @@
Parameters::getStateName(l.mParameters.state));
res = INVALID_OPERATION;
}
+ takePictureCounter = l.mParameters.takePictureCounter;
}
sp<ZslProcessorInterface> processor = mZslProcessor.promote();
if (processor != 0) {
@@ -282,6 +291,8 @@
* Fire the jpegCallback in Camera#takePicture(..., jpegCallback)
*/
if (mCaptureBuffer != 0 && res == OK) {
+ ATRACE_ASYNC_END(Camera2Client::kTakepictureLabel, takePictureCounter);
+
Camera2Client::SharedCameraCallbacks::Lock
l(client->mSharedCameraCallbacks);
ALOGV("%s: Sending still image to client", __FUNCTION__);
@@ -379,11 +390,23 @@
sp<Camera2Client> &client) {
ATRACE_CALL();
+ bool isAeConverged = false;
// Get the onFrameAvailable callback when the requestID == mCaptureId
client->registerFrameListener(mCaptureId, mCaptureId + 1,
this);
+
+ {
+ Mutex::Autolock l(mInputMutex);
+ isAeConverged = (mAEState == ANDROID_CONTROL_AE_STATE_CONVERGED);
+ }
+
{
SharedParameters::Lock l(client->getParameters());
+ // Skip AE precapture when it is already converged and not in force flash mode.
+ if (l.mParameters.flashMode != Parameters::FLASH_MODE_ON && isAeConverged) {
+ return STANDARD_CAPTURE;
+ }
+
mTriggerId = l.mParameters.precaptureTriggerCounter++;
}
client->getCameraDevice()->triggerPrecaptureMetering(mTriggerId);
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
index 7ad461a..9fb4ee7 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
@@ -62,7 +62,7 @@
void notifyAutoExposure(uint8_t newState, int triggerId);
// Notifications from the frame processor
- virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+ virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame);
// Notifications from the JPEG processor
void onCaptureAvailable(nsecs_t timestamp, sp<MemoryBase> captureBuffer);
@@ -125,6 +125,7 @@
NUM_CAPTURE_STATES
} mCaptureState;
static const char* kStateNames[];
+ int mStateTransitionCount;
Mutex mStateMutex; // Guards mCaptureState
Condition mStateChanged;
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
index c34cb12..19acae4 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
@@ -29,13 +29,27 @@
namespace camera2 {
FrameProcessor::FrameProcessor(wp<CameraDeviceBase> device,
- wp<Camera2Client> client) :
+ sp<Camera2Client> client) :
FrameProcessorBase(device),
mClient(client),
- mLastFrameNumberOfFaces(0) {
+ mLastFrameNumberOfFaces(0),
+ mLast3AFrameNumber(-1) {
sp<CameraDeviceBase> d = device.promote();
mSynthesize3ANotify = !(d->willNotify3A());
+
+ {
+ SharedParameters::Lock l(client->getParameters());
+ mUsePartialQuirk = l.mParameters.quirks.partialResults;
+
+ // Initialize starting 3A state
+ m3aState.afTriggerId = l.mParameters.afTriggerCounter;
+ m3aState.aeTriggerId = l.mParameters.precaptureTriggerCounter;
+ // Check if lens is fixed-focus
+ if (l.mParameters.focusMode == Parameters::FOCUS_MODE_FIXED) {
+ m3aState.afMode = ANDROID_CONTROL_AF_MODE_OFF;
+ }
+ }
}
FrameProcessor::~FrameProcessor() {
@@ -49,20 +63,25 @@
return false;
}
- if (processFaceDetect(frame, client) != OK) {
+ bool partialResult = false;
+ if (mUsePartialQuirk) {
+ camera_metadata_entry_t entry;
+ entry = frame.find(ANDROID_QUIRKS_PARTIAL_RESULT);
+ if (entry.count > 0 &&
+ entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
+ partialResult = true;
+ }
+ }
+
+ if (!partialResult && processFaceDetect(frame, client) != OK) {
return false;
}
if (mSynthesize3ANotify) {
- // Ignoring missing fields for now
process3aState(frame, client);
}
- if (!FrameProcessorBase::processSingleFrame(frame, device)) {
- return false;
- }
-
- return true;
+ return FrameProcessorBase::processSingleFrame(frame, device);
}
status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame,
@@ -198,86 +217,75 @@
ATRACE_CALL();
camera_metadata_ro_entry_t entry;
- int mId = client->getCameraId();
+ int cameraId = client->getCameraId();
entry = frame.find(ANDROID_REQUEST_FRAME_COUNT);
int32_t frameNumber = entry.data.i32[0];
+ // Don't send 3A notifications for the same frame number twice
+ if (frameNumber <= mLast3AFrameNumber) {
+ ALOGV("%s: Already sent 3A for frame number %d, skipping",
+ __FUNCTION__, frameNumber);
+ return OK;
+ }
+
+ mLast3AFrameNumber = frameNumber;
+
// Get 3A states from result metadata
bool gotAllStates = true;
AlgState new3aState;
- entry = frame.find(ANDROID_CONTROL_AE_STATE);
- if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AE state provided by HAL for frame %d!",
- __FUNCTION__, mId, frameNumber);
- gotAllStates = false;
- } else {
- new3aState.aeState =
- static_cast<camera_metadata_enum_android_control_ae_state>(
- entry.data.u8[0]);
- }
+ // TODO: Also use AE mode, AE trigger ID
- entry = frame.find(ANDROID_CONTROL_AF_STATE);
- if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AF state provided by HAL for frame %d!",
- __FUNCTION__, mId, frameNumber);
- gotAllStates = false;
- } else {
- new3aState.afState =
- static_cast<camera_metadata_enum_android_control_af_state>(
- entry.data.u8[0]);
- }
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AF_MODE,
+ &new3aState.afMode, frameNumber, cameraId);
- entry = frame.find(ANDROID_CONTROL_AWB_STATE);
- if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AWB state provided by HAL for frame %d!",
- __FUNCTION__, mId, frameNumber);
- gotAllStates = false;
- } else {
- new3aState.awbState =
- static_cast<camera_metadata_enum_android_control_awb_state>(
- entry.data.u8[0]);
- }
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AWB_MODE,
+ &new3aState.awbMode, frameNumber, cameraId);
- int32_t afTriggerId = 0;
- entry = frame.find(ANDROID_CONTROL_AF_TRIGGER_ID);
- if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AF trigger ID provided by HAL for frame %d!",
- __FUNCTION__, mId, frameNumber);
- gotAllStates = false;
- } else {
- afTriggerId = entry.data.i32[0];
- }
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AE_STATE,
+ &new3aState.aeState, frameNumber, cameraId);
- int32_t aeTriggerId = 0;
- entry = frame.find(ANDROID_CONTROL_AE_PRECAPTURE_ID);
- if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AE precapture trigger ID provided by HAL"
- " for frame %d!",
- __FUNCTION__, mId, frameNumber);
- gotAllStates = false;
- } else {
- aeTriggerId = entry.data.i32[0];
- }
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AF_STATE,
+ &new3aState.afState, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AWB_STATE,
+ &new3aState.awbState, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<int32_t>(frame, ANDROID_CONTROL_AF_TRIGGER_ID,
+ &new3aState.afTriggerId, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<int32_t>(frame, ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ &new3aState.aeTriggerId, frameNumber, cameraId);
if (!gotAllStates) return BAD_VALUE;
if (new3aState.aeState != m3aState.aeState) {
- ALOGV("%s: AE state changed from 0x%x to 0x%x",
- __FUNCTION__, m3aState.aeState, new3aState.aeState);
- client->notifyAutoExposure(new3aState.aeState, aeTriggerId);
+ ALOGV("%s: Camera %d: AE state %d->%d",
+ __FUNCTION__, cameraId,
+ m3aState.aeState, new3aState.aeState);
+ client->notifyAutoExposure(new3aState.aeState, new3aState.aeTriggerId);
}
- if (new3aState.afState != m3aState.afState) {
- ALOGV("%s: AF state changed from 0x%x to 0x%x",
- __FUNCTION__, m3aState.afState, new3aState.afState);
- client->notifyAutoFocus(new3aState.afState, afTriggerId);
+
+ if (new3aState.afState != m3aState.afState ||
+ new3aState.afMode != m3aState.afMode ||
+ new3aState.afTriggerId != m3aState.afTriggerId) {
+ ALOGV("%s: Camera %d: AF state %d->%d. AF mode %d->%d. Trigger %d->%d",
+ __FUNCTION__, cameraId,
+ m3aState.afState, new3aState.afState,
+ m3aState.afMode, new3aState.afMode,
+ m3aState.afTriggerId, new3aState.afTriggerId);
+ client->notifyAutoFocus(new3aState.afState, new3aState.afTriggerId);
}
- if (new3aState.awbState != m3aState.awbState) {
- ALOGV("%s: AWB state changed from 0x%x to 0x%x",
- __FUNCTION__, m3aState.awbState, new3aState.awbState);
- client->notifyAutoWhitebalance(new3aState.awbState, aeTriggerId);
+ if (new3aState.awbState != m3aState.awbState ||
+ new3aState.awbMode != m3aState.awbMode) {
+ ALOGV("%s: Camera %d: AWB state %d->%d. AWB mode %d->%d",
+ __FUNCTION__, cameraId,
+ m3aState.awbState, new3aState.awbState,
+ m3aState.awbMode, new3aState.awbMode);
+ client->notifyAutoWhitebalance(new3aState.awbState,
+ new3aState.aeTriggerId);
}
m3aState = new3aState;
@@ -285,6 +293,39 @@
return OK;
}
+template<typename Src, typename T>
+bool FrameProcessor::get3aResult(const CameraMetadata& result, int32_t tag,
+ T* value, int32_t frameNumber, int cameraId) {
+ camera_metadata_ro_entry_t entry;
+ if (value == NULL) {
+ ALOGE("%s: Camera %d: Value to write to is NULL",
+ __FUNCTION__, cameraId);
+ return false;
+ }
+
+ entry = result.find(tag);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: No %s provided by HAL for frame %d!",
+ __FUNCTION__, cameraId,
+ get_camera_metadata_tag_name(tag), frameNumber);
+ return false;
+ } else {
+ switch(sizeof(Src)){
+ case sizeof(uint8_t):
+ *value = static_cast<T>(entry.data.u8[0]);
+ break;
+ case sizeof(int32_t):
+ *value = static_cast<T>(entry.data.i32[0]);
+ break;
+ default:
+ ALOGE("%s: Camera %d: Unsupported source",
+ __FUNCTION__, cameraId);
+ return false;
+ }
+ }
+ return true;
+}
+
void FrameProcessor::callbackFaceDetection(sp<Camera2Client> client,
const camera_frame_metadata &metadata) {
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.h b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
index 2a17d45..856ad32 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
@@ -39,7 +39,7 @@
*/
class FrameProcessor : public FrameProcessorBase {
public:
- FrameProcessor(wp<CameraDeviceBase> device, wp<Camera2Client> client);
+ FrameProcessor(wp<CameraDeviceBase> device, sp<Camera2Client> client);
~FrameProcessor();
private:
@@ -61,18 +61,44 @@
status_t process3aState(const CameraMetadata &frame,
const sp<Camera2Client> &client);
+ // Helper for process3aState
+ template<typename Src, typename T>
+ bool get3aResult(const CameraMetadata& result, int32_t tag, T* value,
+ int32_t frameNumber, int cameraId);
+
+
struct AlgState {
+ // TODO: also track AE mode
+ camera_metadata_enum_android_control_af_mode afMode;
+ camera_metadata_enum_android_control_awb_mode awbMode;
+
camera_metadata_enum_android_control_ae_state aeState;
camera_metadata_enum_android_control_af_state afState;
camera_metadata_enum_android_control_awb_state awbState;
+ int32_t afTriggerId;
+ int32_t aeTriggerId;
+
+ // These defaults need to match those in Parameters.cpp
AlgState() :
+ afMode(ANDROID_CONTROL_AF_MODE_AUTO),
+ awbMode(ANDROID_CONTROL_AWB_MODE_AUTO),
aeState(ANDROID_CONTROL_AE_STATE_INACTIVE),
afState(ANDROID_CONTROL_AF_STATE_INACTIVE),
- awbState(ANDROID_CONTROL_AWB_STATE_INACTIVE) {
+ awbState(ANDROID_CONTROL_AWB_STATE_INACTIVE),
+ afTriggerId(0),
+ aeTriggerId(0) {
}
} m3aState;
+ // Whether the partial result quirk is enabled for this device
+ bool mUsePartialQuirk;
+
+ // Track most recent frame number for which 3A notifications were sent for.
+ // Used to filter against sending 3A notifications for the same frame
+ // several times.
+ int32_t mLast3AFrameNumber;
+
// Emit FaceDetection event to java if faces changed
void callbackFaceDetection(sp<Camera2Client> client,
const camera_frame_metadata &metadata);
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index 77d5c8a..ec81456 100644
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -200,51 +200,60 @@
ATRACE_CALL();
status_t res;
sp<Camera2Heap> captureHeap;
+ sp<MemoryBase> captureBuffer;
CpuConsumer::LockedBuffer imgBuffer;
- res = mCaptureConsumer->lockNextBuffer(&imgBuffer);
- if (res != OK) {
- if (res != BAD_VALUE) {
- ALOGE("%s: Camera %d: Error receiving still image buffer: "
- "%s (%d)", __FUNCTION__,
- mId, strerror(-res), res);
+ {
+ Mutex::Autolock l(mInputMutex);
+ if (mCaptureStreamId == NO_STREAM) {
+ ALOGW("%s: Camera %d: No stream is available", __FUNCTION__, mId);
+ return INVALID_OPERATION;
}
- return res;
- }
- ALOGV("%s: Camera %d: Still capture available", __FUNCTION__,
- mId);
+ res = mCaptureConsumer->lockNextBuffer(&imgBuffer);
+ if (res != OK) {
+ if (res != BAD_VALUE) {
+ ALOGE("%s: Camera %d: Error receiving still image buffer: "
+ "%s (%d)", __FUNCTION__,
+ mId, strerror(-res), res);
+ }
+ return res;
+ }
- if (imgBuffer.format != HAL_PIXEL_FORMAT_BLOB) {
- ALOGE("%s: Camera %d: Unexpected format for still image: "
- "%x, expected %x", __FUNCTION__, mId,
- imgBuffer.format,
- HAL_PIXEL_FORMAT_BLOB);
+ ALOGV("%s: Camera %d: Still capture available", __FUNCTION__,
+ mId);
+
+ if (imgBuffer.format != HAL_PIXEL_FORMAT_BLOB) {
+ ALOGE("%s: Camera %d: Unexpected format for still image: "
+ "%x, expected %x", __FUNCTION__, mId,
+ imgBuffer.format,
+ HAL_PIXEL_FORMAT_BLOB);
+ mCaptureConsumer->unlockBuffer(imgBuffer);
+ return OK;
+ }
+
+ // Find size of JPEG image
+ size_t jpegSize = findJpegSize(imgBuffer.data, imgBuffer.width);
+ if (jpegSize == 0) { // failed to find size, default to whole buffer
+ jpegSize = imgBuffer.width;
+ }
+ size_t heapSize = mCaptureHeap->getSize();
+ if (jpegSize > heapSize) {
+ ALOGW("%s: JPEG image is larger than expected, truncating "
+ "(got %d, expected at most %d bytes)",
+ __FUNCTION__, jpegSize, heapSize);
+ jpegSize = heapSize;
+ }
+
+ // TODO: Optimize this to avoid memcopy
+ captureBuffer = new MemoryBase(mCaptureHeap, 0, jpegSize);
+ void* captureMemory = mCaptureHeap->getBase();
+ memcpy(captureMemory, imgBuffer.data, jpegSize);
+
mCaptureConsumer->unlockBuffer(imgBuffer);
- return OK;
}
- // Find size of JPEG image
- size_t jpegSize = findJpegSize(imgBuffer.data, imgBuffer.width);
- if (jpegSize == 0) { // failed to find size, default to whole buffer
- jpegSize = imgBuffer.width;
- }
- size_t heapSize = mCaptureHeap->getSize();
- if (jpegSize > heapSize) {
- ALOGW("%s: JPEG image is larger than expected, truncating "
- "(got %d, expected at most %d bytes)",
- __FUNCTION__, jpegSize, heapSize);
- jpegSize = heapSize;
- }
-
- // TODO: Optimize this to avoid memcopy
- sp<MemoryBase> captureBuffer = new MemoryBase(mCaptureHeap, 0, jpegSize);
- void* captureMemory = mCaptureHeap->getBase();
- memcpy(captureMemory, imgBuffer.data, jpegSize);
-
- mCaptureConsumer->unlockBuffer(imgBuffer);
-
sp<CaptureSequencer> sequencer = mSequencer.promote();
if (sequencer != 0) {
sequencer->onCaptureAvailable(imgBuffer.timestamp, captureBuffer);
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index a6c1083..2cf0d29 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -183,8 +183,7 @@
// still have to do something sane for them
// NOTE: Not scaled like FPS range values are.
- previewFps = fpsFromRange(previewFpsRange[0], previewFpsRange[1]);
- lastSetPreviewFps = previewFps;
+ int previewFps = fpsFromRange(previewFpsRange[0], previewFpsRange[1]);
params.set(CameraParameters::KEY_PREVIEW_FRAME_RATE,
previewFps);
@@ -250,9 +249,17 @@
staticInfo(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, 4);
if (!availableJpegThumbnailSizes.count) return NO_INIT;
- // TODO: Pick default thumbnail size sensibly
- jpegThumbSize[0] = availableJpegThumbnailSizes.data.i32[0];
- jpegThumbSize[1] = availableJpegThumbnailSizes.data.i32[1];
+ // Pick the largest thumbnail size that matches still image aspect ratio.
+ ALOG_ASSERT(pictureWidth > 0 && pictureHeight > 0,
+ "Invalid picture size, %d x %d", pictureWidth, pictureHeight);
+ float picAspectRatio = static_cast<float>(pictureWidth) / pictureHeight;
+ Size thumbnailSize =
+ getMaxSizeForRatio(
+ picAspectRatio,
+ &availableJpegThumbnailSizes.data.i32[0],
+ availableJpegThumbnailSizes.count);
+ jpegThumbSize[0] = thumbnailSize.width;
+ jpegThumbSize[1] = thumbnailSize.height;
params.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH,
jpegThumbSize[0]);
@@ -459,7 +466,7 @@
supportedAntibanding);
}
- sceneMode = ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED;
+ sceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED;
params.set(CameraParameters::KEY_SCENE_MODE,
CameraParameters::SCENE_MODE_AUTO);
@@ -475,7 +482,7 @@
if (addComma) supportedSceneModes += ",";
addComma = true;
switch (availableSceneModes.data.u8[i]) {
- case ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED:
+ case ANDROID_CONTROL_SCENE_MODE_DISABLED:
noSceneModes = true;
break;
case ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY:
@@ -657,13 +664,13 @@
focusState = ANDROID_CONTROL_AF_STATE_INACTIVE;
shadowFocusMode = FOCUS_MODE_INVALID;
- camera_metadata_ro_entry_t max3aRegions =
- staticInfo(ANDROID_CONTROL_MAX_REGIONS, 1, 1);
- if (!max3aRegions.count) return NO_INIT;
+ camera_metadata_ro_entry_t max3aRegions = staticInfo(ANDROID_CONTROL_MAX_REGIONS,
+ Parameters::NUM_REGION, Parameters::NUM_REGION);
+ if (max3aRegions.count != Parameters::NUM_REGION) return NO_INIT;
int32_t maxNumFocusAreas = 0;
if (focusMode != Parameters::FOCUS_MODE_FIXED) {
- maxNumFocusAreas = max3aRegions.data.i32[0];
+ maxNumFocusAreas = max3aRegions.data.i32[Parameters::REGION_AF];
}
params.set(CameraParameters::KEY_MAX_NUM_FOCUS_AREAS, maxNumFocusAreas);
params.set(CameraParameters::KEY_FOCUS_AREAS,
@@ -723,7 +730,7 @@
meteringAreas.add(Parameters::Area(0, 0, 0, 0, 0));
params.set(CameraParameters::KEY_MAX_NUM_METERING_AREAS,
- max3aRegions.data.i32[0]);
+ max3aRegions.data.i32[Parameters::REGION_AE]);
params.set(CameraParameters::KEY_METERING_AREAS,
"(0,0,0,0,0)");
@@ -797,30 +804,25 @@
enableFocusMoveMessages = false;
afTriggerCounter = 1;
+ afStateCounter = 0;
currentAfTriggerId = -1;
afInMotion = false;
precaptureTriggerCounter = 1;
+ takePictureCounter = 0;
+
previewCallbackFlags = 0;
previewCallbackOneShot = false;
previewCallbackSurface = false;
- camera_metadata_ro_entry_t supportedHardwareLevel =
- staticInfo(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, 0, 0, false);
- if (!supportedHardwareLevel.count || (supportedHardwareLevel.data.u8[0] ==
- ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)) {
- ALOGI("Camera %d: ZSL mode disabled for limited mode HALs", cameraId);
+ char value[PROPERTY_VALUE_MAX];
+ property_get("camera.disable_zsl_mode", value, "0");
+ if (!strcmp(value,"1")) {
+ ALOGI("Camera %d: Disabling ZSL mode", cameraId);
zslMode = false;
} else {
- char value[PROPERTY_VALUE_MAX];
- property_get("camera.disable_zsl_mode", value, "0");
- if (!strcmp(value,"1")) {
- ALOGI("Camera %d: Disabling ZSL mode", cameraId);
- zslMode = false;
- } else {
- zslMode = true;
- }
+ zslMode = true;
}
lightFx = LIGHTFX_NONE;
@@ -1044,6 +1046,11 @@
ALOGV_IF(quirks.meteringCropRegion, "Camera %d: Quirk meteringCropRegion"
" enabled", cameraId);
+ entry = info->find(ANDROID_QUIRKS_USE_PARTIAL_RESULT);
+ quirks.partialResults = (entry.count != 0 && entry.data.u8[0] == 1);
+ ALOGV_IF(quirks.partialResults, "Camera %d: Quirk usePartialResult"
+ " enabled", cameraId);
+
return OK;
}
@@ -1126,13 +1133,22 @@
// PREVIEW_FPS_RANGE
bool fpsRangeChanged = false;
+ int32_t lastSetFpsRange[2];
+
+ params.getPreviewFpsRange(&lastSetFpsRange[0], &lastSetFpsRange[1]);
+ lastSetFpsRange[0] /= kFpsToApiScale;
+ lastSetFpsRange[1] /= kFpsToApiScale;
+
newParams.getPreviewFpsRange(&validatedParams.previewFpsRange[0],
&validatedParams.previewFpsRange[1]);
validatedParams.previewFpsRange[0] /= kFpsToApiScale;
validatedParams.previewFpsRange[1] /= kFpsToApiScale;
- if (validatedParams.previewFpsRange[0] != previewFpsRange[0] ||
- validatedParams.previewFpsRange[1] != previewFpsRange[1]) {
+ // Compare the FPS range value from the last set() to the current set()
+ // to determine if the client has changed it
+ if (validatedParams.previewFpsRange[0] != lastSetFpsRange[0] ||
+ validatedParams.previewFpsRange[1] != lastSetFpsRange[1]) {
+
fpsRangeChanged = true;
camera_metadata_ro_entry_t availablePreviewFpsRanges =
staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2);
@@ -1150,16 +1166,6 @@
validatedParams.previewFpsRange[1]);
return BAD_VALUE;
}
- validatedParams.previewFps =
- fpsFromRange(validatedParams.previewFpsRange[0],
- validatedParams.previewFpsRange[1]);
-
- // Update our last-seen single preview FPS, needed for disambiguating
- // when the application is intending to use the deprecated single-FPS
- // setting vs. the range FPS setting
- validatedParams.lastSetPreviewFps = newParams.getPreviewFrameRate();
-
- newParams.setPreviewFrameRate(validatedParams.previewFps);
}
// PREVIEW_FORMAT
@@ -1197,12 +1203,11 @@
// PREVIEW_FRAME_RATE Deprecated, only use if the preview fps range is
// unchanged this time. The single-value FPS is the same as the minimum of
// the range. To detect whether the application has changed the value of
- // previewFps, compare against their last-set preview FPS instead of the
- // single FPS we may have synthesized from a range FPS set.
+ // previewFps, compare against their last-set preview FPS.
if (!fpsRangeChanged) {
- validatedParams.previewFps = newParams.getPreviewFrameRate();
- if (validatedParams.previewFps != lastSetPreviewFps ||
- recordingHintChanged) {
+ int previewFps = newParams.getPreviewFrameRate();
+ int lastSetPreviewFps = params.getPreviewFrameRate();
+ if (previewFps != lastSetPreviewFps || recordingHintChanged) {
camera_metadata_ro_entry_t availableFrameRates =
staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
/**
@@ -1215,8 +1220,8 @@
* Either way, in case of multiple ranges, break the tie by
* selecting the smaller range.
*/
- int targetFps = validatedParams.previewFps;
- // all ranges which have targetFps
+
+ // all ranges which have previewFps
Vector<Range> candidateRanges;
for (i = 0; i < availableFrameRates.count; i+=2) {
Range r = {
@@ -1224,13 +1229,13 @@
availableFrameRates.data.i32[i+1]
};
- if (r.min <= targetFps && targetFps <= r.max) {
+ if (r.min <= previewFps && previewFps <= r.max) {
candidateRanges.push(r);
}
}
if (candidateRanges.isEmpty()) {
ALOGE("%s: Requested preview frame rate %d is not supported",
- __FUNCTION__, validatedParams.previewFps);
+ __FUNCTION__, previewFps);
return BAD_VALUE;
}
// most applicable range with targetFps
@@ -1269,14 +1274,6 @@
validatedParams.previewFpsRange[1],
validatedParams.recordingHint);
}
- newParams.set(CameraParameters::KEY_PREVIEW_FPS_RANGE,
- String8::format("%d,%d",
- validatedParams.previewFpsRange[0] * kFpsToApiScale,
- validatedParams.previewFpsRange[1] * kFpsToApiScale));
- // Update our last-seen single preview FPS, needed for disambiguating
- // when the application is intending to use the deprecated single-FPS
- // setting vs. the range FPS setting
- validatedParams.lastSetPreviewFps = validatedParams.previewFps;
}
// PICTURE_SIZE
@@ -1449,7 +1446,7 @@
newParams.get(CameraParameters::KEY_SCENE_MODE) );
if (validatedParams.sceneMode != sceneMode &&
validatedParams.sceneMode !=
- ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED) {
+ ANDROID_CONTROL_SCENE_MODE_DISABLED) {
camera_metadata_ro_entry_t availableSceneModes =
staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
for (i = 0; i < availableSceneModes.count; i++) {
@@ -1464,7 +1461,7 @@
}
}
bool sceneModeSet =
- validatedParams.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED;
+ validatedParams.sceneMode != ANDROID_CONTROL_SCENE_MODE_DISABLED;
// FLASH_MODE
if (sceneModeSet) {
@@ -1594,10 +1591,11 @@
// FOCUS_AREAS
res = parseAreas(newParams.get(CameraParameters::KEY_FOCUS_AREAS),
&validatedParams.focusingAreas);
- size_t max3aRegions =
- (size_t)staticInfo(ANDROID_CONTROL_MAX_REGIONS, 1, 1).data.i32[0];
+ size_t maxAfRegions = (size_t)staticInfo(ANDROID_CONTROL_MAX_REGIONS,
+ Parameters::NUM_REGION, Parameters::NUM_REGION).
+ data.i32[Parameters::REGION_AF];
if (res == OK) res = validateAreas(validatedParams.focusingAreas,
- max3aRegions, AREA_KIND_FOCUS);
+ maxAfRegions, AREA_KIND_FOCUS);
if (res != OK) {
ALOGE("%s: Requested focus areas are malformed: %s",
__FUNCTION__, newParams.get(CameraParameters::KEY_FOCUS_AREAS));
@@ -1627,10 +1625,13 @@
newParams.get(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK));
// METERING_AREAS
+ size_t maxAeRegions = (size_t)staticInfo(ANDROID_CONTROL_MAX_REGIONS,
+ Parameters::NUM_REGION, Parameters::NUM_REGION).
+ data.i32[Parameters::REGION_AE];
res = parseAreas(newParams.get(CameraParameters::KEY_METERING_AREAS),
&validatedParams.meteringAreas);
if (res == OK) {
- res = validateAreas(validatedParams.meteringAreas, max3aRegions,
+ res = validateAreas(validatedParams.meteringAreas, maxAeRegions,
AREA_KIND_METERING);
}
if (res != OK) {
@@ -1779,7 +1780,7 @@
// (face detection statistics and face priority scene mode). Map from other
// to the other.
bool sceneModeActive =
- sceneMode != (uint8_t)ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED;
+ sceneMode != (uint8_t)ANDROID_CONTROL_SCENE_MODE_DISABLED;
uint8_t reqControlMode = ANDROID_CONTROL_MODE_AUTO;
if (enableFaceDetect || sceneModeActive) {
reqControlMode = ANDROID_CONTROL_MODE_USE_SCENE_MODE;
@@ -1791,7 +1792,7 @@
uint8_t reqSceneMode =
sceneModeActive ? sceneMode :
enableFaceDetect ? (uint8_t)ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY :
- (uint8_t)ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED;
+ (uint8_t)ANDROID_CONTROL_SCENE_MODE_DISABLED;
res = request->update(ANDROID_CONTROL_SCENE_MODE,
&reqSceneMode, 1);
if (res != OK) return res;
@@ -1861,23 +1862,23 @@
size_t reqFocusingAreasSize = focusingAreas.size() * 5;
int32_t *reqFocusingAreas = new int32_t[reqFocusingAreasSize];
- for (size_t i = 0; i < reqFocusingAreasSize; i += 5) {
- if (focusingAreas[i].weight != 0) {
+ for (size_t i = 0, j = 0; i < reqFocusingAreasSize; i += 5, j++) {
+ if (focusingAreas[j].weight != 0) {
reqFocusingAreas[i + 0] =
- normalizedXToArray(focusingAreas[i].left);
+ normalizedXToArray(focusingAreas[j].left);
reqFocusingAreas[i + 1] =
- normalizedYToArray(focusingAreas[i].top);
+ normalizedYToArray(focusingAreas[j].top);
reqFocusingAreas[i + 2] =
- normalizedXToArray(focusingAreas[i].right);
+ normalizedXToArray(focusingAreas[j].right);
reqFocusingAreas[i + 3] =
- normalizedYToArray(focusingAreas[i].bottom);
+ normalizedYToArray(focusingAreas[j].bottom);
} else {
reqFocusingAreas[i + 0] = 0;
reqFocusingAreas[i + 1] = 0;
reqFocusingAreas[i + 2] = 0;
reqFocusingAreas[i + 3] = 0;
}
- reqFocusingAreas[i + 4] = focusingAreas[i].weight;
+ reqFocusingAreas[i + 4] = focusingAreas[j].weight;
}
res = request->update(ANDROID_CONTROL_AF_REGIONS,
reqFocusingAreas, reqFocusingAreasSize);
@@ -1890,28 +1891,45 @@
size_t reqMeteringAreasSize = meteringAreas.size() * 5;
int32_t *reqMeteringAreas = new int32_t[reqMeteringAreasSize];
- for (size_t i = 0; i < reqMeteringAreasSize; i += 5) {
- if (meteringAreas[i].weight != 0) {
+ for (size_t i = 0, j = 0; i < reqMeteringAreasSize; i += 5, j++) {
+ if (meteringAreas[j].weight != 0) {
reqMeteringAreas[i + 0] =
- normalizedXToArray(meteringAreas[i].left);
+ normalizedXToArray(meteringAreas[j].left);
reqMeteringAreas[i + 1] =
- normalizedYToArray(meteringAreas[i].top);
+ normalizedYToArray(meteringAreas[j].top);
reqMeteringAreas[i + 2] =
- normalizedXToArray(meteringAreas[i].right);
+ normalizedXToArray(meteringAreas[j].right);
reqMeteringAreas[i + 3] =
- normalizedYToArray(meteringAreas[i].bottom);
+ normalizedYToArray(meteringAreas[j].bottom);
} else {
reqMeteringAreas[i + 0] = 0;
reqMeteringAreas[i + 1] = 0;
reqMeteringAreas[i + 2] = 0;
reqMeteringAreas[i + 3] = 0;
}
- reqMeteringAreas[i + 4] = meteringAreas[i].weight;
+ reqMeteringAreas[i + 4] = meteringAreas[j].weight;
}
res = request->update(ANDROID_CONTROL_AE_REGIONS,
reqMeteringAreas, reqMeteringAreasSize);
if (res != OK) return res;
+ // Set awb regions to be the same as the metering regions if allowed
+ size_t maxAwbRegions = (size_t)staticInfo(ANDROID_CONTROL_MAX_REGIONS,
+ Parameters::NUM_REGION, Parameters::NUM_REGION).
+ data.i32[Parameters::REGION_AWB];
+ if (maxAwbRegions > 0) {
+ if (maxAwbRegions >= meteringAreas.size()) {
+ res = request->update(ANDROID_CONTROL_AWB_REGIONS,
+ reqMeteringAreas, reqMeteringAreasSize);
+ } else {
+ // Ensure the awb regions are zeroed if the region count is too high.
+ int32_t zeroedAwbAreas[5] = {0, 0, 0, 0, 0};
+ res = request->update(ANDROID_CONTROL_AWB_REGIONS,
+ zeroedAwbAreas, sizeof(zeroedAwbAreas)/sizeof(int32_t));
+ }
+ if (res != OK) return res;
+ }
+
delete[] reqMeteringAreas;
/* don't include jpeg thumbnail size - it's valid for
@@ -2152,9 +2170,9 @@
int Parameters::sceneModeStringToEnum(const char *sceneMode) {
return
!sceneMode ?
- ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED :
+ ANDROID_CONTROL_SCENE_MODE_DISABLED :
!strcmp(sceneMode, CameraParameters::SCENE_MODE_AUTO) ?
- ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED :
+ ANDROID_CONTROL_SCENE_MODE_DISABLED :
!strcmp(sceneMode, CameraParameters::SCENE_MODE_ACTION) ?
ANDROID_CONTROL_SCENE_MODE_ACTION :
!strcmp(sceneMode, CameraParameters::SCENE_MODE_PORTRAIT) ?
@@ -2525,6 +2543,32 @@
return OK;
}
+Parameters::Size Parameters::getMaxSizeForRatio(
+ float ratio, const int32_t* sizeArray, size_t count) {
+ ALOG_ASSERT(sizeArray != NULL, "size array shouldn't be NULL");
+ ALOG_ASSERT(count >= 2 && count % 2 == 0, "count must be a positive even number");
+
+ Size maxSize = {0, 0};
+ for (size_t i = 0; i < count; i += 2) {
+ if (sizeArray[i] > 0 && sizeArray[i+1] > 0) {
+ float curRatio = static_cast<float>(sizeArray[i]) / sizeArray[i+1];
+ if (fabs(curRatio - ratio) < ASPECT_RATIO_TOLERANCE && maxSize.width < sizeArray[i]) {
+ maxSize.width = sizeArray[i];
+ maxSize.height = sizeArray[i+1];
+ }
+ }
+ }
+
+ if (maxSize.width == 0 || maxSize.height == 0) {
+ maxSize.width = sizeArray[0];
+ maxSize.height = sizeArray[1];
+ ALOGW("Unable to find the size to match the given aspect ratio %f."
+ "Fall back to %d x %d", ratio, maxSize.width, maxSize.height);
+ }
+
+ return maxSize;
+}
+
Parameters::CropRegion Parameters::calculateCropRegion(
Parameters::CropRegion::Outputs outputs) const {
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 0505b0e..60c4687 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -46,8 +46,6 @@
int previewWidth, previewHeight;
int32_t previewFpsRange[2];
- int lastSetPreviewFps; // the last single FPS value seen in a set call
- int previewFps; // deprecated, here only for tracking changes
int previewFormat;
int previewTransform; // set by CAMERA_CMD_SET_DISPLAY_ORIENTATION
@@ -115,6 +113,14 @@
bool autoExposureLock;
bool autoWhiteBalanceLock;
+ // 3A region types, for use with ANDROID_CONTROL_MAX_REGIONS
+ enum region_t {
+ REGION_AE = 0,
+ REGION_AWB,
+ REGION_AF,
+ NUM_REGION // Number of region types
+ } region;
+
Vector<Area> meteringAreas;
int zoom;
@@ -141,11 +147,14 @@
bool enableFocusMoveMessages;
int afTriggerCounter;
+ int afStateCounter;
int currentAfTriggerId;
bool afInMotion;
int precaptureTriggerCounter;
+ int takePictureCounter;
+
uint32_t previewCallbackFlags;
bool previewCallbackOneShot;
bool previewCallbackSurface;
@@ -168,6 +177,8 @@
// Max preview size allowed
static const unsigned int MAX_PREVIEW_WIDTH = 1920;
static const unsigned int MAX_PREVIEW_HEIGHT = 1080;
+ // Aspect ratio tolerance
+ static const float ASPECT_RATIO_TOLERANCE = 0.001;
// Full static camera info, object owned by someone else, such as
// Camera2Device.
@@ -204,6 +215,7 @@
bool triggerAfWithAuto;
bool useZslFormat;
bool meteringCropRegion;
+ bool partialResults;
} quirks;
/**
@@ -331,6 +343,8 @@
Vector<Size> availablePreviewSizes;
// Get size list (that are no larger than limit) from static metadata.
status_t getFilteredPreviewSizes(Size limit, Vector<Size> *sizes);
+ // Get max size (from the size array) that matches the given aspect ratio.
+ Size getMaxSizeForRatio(float ratio, const int32_t* sizeArray, size_t count);
};
// This class encapsulates the Parameters class so that it can only be accessed
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 08ab357..453d54c 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -71,7 +71,7 @@
}
}
-void ZslProcessor::onFrameAvailable(int32_t /*frameId*/,
+void ZslProcessor::onFrameAvailable(int32_t /*requestId*/,
const CameraMetadata &frame) {
Mutex::Autolock l(mInputMutex);
camera_metadata_ro_entry_t entry;
@@ -540,7 +540,7 @@
if (entry.count > 0) frameAeState = entry.data.u8[0];
}
String8 result =
- String8::format(" %d: b: %lld\tf: %lld, AE state: %d", i,
+ String8::format(" %zu: b: %lld\tf: %lld, AE state: %d", i,
bufferTimestamp, frameTimestamp, frameAeState);
ALOGV("%s", result.string());
if (fd != -1) {
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.h b/services/camera/libcameraservice/api1/client2/ZslProcessor.h
index 5fb178f..6d3cb85 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.h
@@ -54,7 +54,7 @@
// From mZslConsumer
virtual void onFrameAvailable();
// From FrameProcessor
- virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+ virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame);
virtual void onBufferReleased(buffer_handle_t *handle);
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index 3e05091..c1d0496 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -61,7 +61,7 @@
deleteStream();
}
-void ZslProcessor3::onFrameAvailable(int32_t /*frameId*/,
+void ZslProcessor3::onFrameAvailable(int32_t /*requestId*/,
const CameraMetadata &frame) {
Mutex::Autolock l(mInputMutex);
camera_metadata_ro_entry_t entry;
@@ -273,6 +273,15 @@
return INVALID_OPERATION;
}
+ // Flush device to clear out all in-flight requests pending in HAL.
+ res = client->getCameraDevice()->flush();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Failed to flush device: "
+ "%s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+
// Update JPEG settings
{
SharedParameters::Lock l(client->getParameters());
@@ -355,7 +364,7 @@
if (entry.count > 0) frameAeState = entry.data.u8[0];
}
String8 result =
- String8::format(" %d: b: %lld\tf: %lld, AE state: %d", i,
+ String8::format(" %zu: b: %lld\tf: %lld, AE state: %d", i,
bufferTimestamp, frameTimestamp, frameAeState);
ALOGV("%s", result.string());
if (fd != -1) {
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
index 35b85f5..d2f8322 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
@@ -51,7 +51,7 @@
~ZslProcessor3();
// From FrameProcessor
- virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+ virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame);
/**
****************************************
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 83466cb..187220e 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -45,14 +45,6 @@
cameraId, cameraFacing, clientPid, clientUid, servicePid),
mRemoteCallback(remoteCallback) {
}
-void CameraDeviceClientBase::notifyError() {
- // Thread safe. Don't bother locking.
- sp<ICameraDeviceCallbacks> remoteCb = mRemoteCallback;
-
- if (remoteCb != 0) {
- remoteCb->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0);
- }
-}
// Interface used by CameraService
@@ -89,7 +81,8 @@
mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
FRAME_PROCESSOR_LISTENER_MAX_ID,
- /*listener*/this);
+ /*listener*/this,
+ /*quirkSendPartials*/true);
return OK;
}
@@ -164,7 +157,6 @@
metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0],
outputStreamIds.size());
- // TODO: @hide ANDROID_REQUEST_ID, or use another request token
int32_t requestId = mRequestIdCounter++;
metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1);
ALOGV("%s: Camera %d: Submitting request with ID %d",
@@ -298,11 +290,28 @@
}
}
+ // HACK b/10949105
+ // Query consumer usage bits to set async operation mode for
+ // GLConsumer using controlledByApp parameter.
+ bool useAsync = false;
+ int32_t consumerUsage;
+ if ((res = bufferProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS,
+ &consumerUsage)) != OK) {
+ ALOGE("%s: Camera %d: Failed to query consumer usage", __FUNCTION__,
+ mCameraId);
+ return res;
+ }
+ if (consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) {
+ ALOGW("%s: Camera %d: Forcing asynchronous mode for stream",
+ __FUNCTION__, mCameraId);
+ useAsync = true;
+ }
+
sp<IBinder> binder;
sp<ANativeWindow> anw;
if (bufferProducer != 0) {
binder = bufferProducer->asBinder();
- anw = new Surface(bufferProducer);
+ anw = new Surface(bufferProducer, useAsync);
}
// TODO: remove w,h,f since we are ignoring them
@@ -484,6 +493,34 @@
return dumpDevice(fd, args);
}
+
+void CameraDeviceClient::notifyError() {
+ // Thread safe. Don't bother locking.
+ sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+
+ if (remoteCb != 0) {
+ remoteCb->onDeviceError(ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE);
+ }
+}
+
+void CameraDeviceClient::notifyIdle() {
+ // Thread safe. Don't bother locking.
+ sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+
+ if (remoteCb != 0) {
+ remoteCb->onDeviceIdle();
+ }
+}
+
+void CameraDeviceClient::notifyShutter(int requestId,
+ nsecs_t timestamp) {
+ // Thread safe. Don't bother locking.
+ sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+ if (remoteCb != 0) {
+ remoteCb->onCaptureStarted(requestId, timestamp);
+ }
+}
+
// TODO: refactor the code below this with IProCameraUser.
// it's 100% copy-pasted, so lets not change it right now to make it easier.
@@ -515,8 +552,8 @@
}
/** Device-related methods */
-void CameraDeviceClient::onFrameAvailable(int32_t frameId,
- const CameraMetadata& frame) {
+void CameraDeviceClient::onFrameAvailable(int32_t requestId,
+ const CameraMetadata& frame) {
ATRACE_CALL();
ALOGV("%s", __FUNCTION__);
@@ -524,7 +561,7 @@
sp<ICameraDeviceCallbacks> remoteCb = mRemoteCallback;
if (remoteCb != NULL) {
ALOGV("%s: frame = %p ", __FUNCTION__, &frame);
- remoteCb->onResultReceived(frameId, frame);
+ remoteCb->onResultReceived(requestId, frame);
}
}
@@ -598,26 +635,56 @@
return INVALID_OPERATION;
}
+ camera_metadata_ro_entry_t entryFacing = staticInfo.find(ANDROID_LENS_FACING);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: Can't find android.lens.facing in "
+ "static metadata!", __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ }
+
int32_t& flags = *transform;
+ bool mirror = (entryFacing.data.u8[0] == ANDROID_LENS_FACING_FRONT);
int orientation = entry.data.i32[0];
- switch (orientation) {
- case 0:
- flags = 0;
- break;
- case 90:
- flags = NATIVE_WINDOW_TRANSFORM_ROT_90;
- break;
- case 180:
- flags = NATIVE_WINDOW_TRANSFORM_ROT_180;
- break;
- case 270:
- flags = NATIVE_WINDOW_TRANSFORM_ROT_270;
- break;
- default:
- ALOGE("%s: Invalid HAL android.sensor.orientation value: %d",
- __FUNCTION__, orientation);
- return INVALID_OPERATION;
+ if (!mirror) {
+ switch (orientation) {
+ case 0:
+ flags = 0;
+ break;
+ case 90:
+ flags = NATIVE_WINDOW_TRANSFORM_ROT_90;
+ break;
+ case 180:
+ flags = NATIVE_WINDOW_TRANSFORM_ROT_180;
+ break;
+ case 270:
+ flags = NATIVE_WINDOW_TRANSFORM_ROT_270;
+ break;
+ default:
+ ALOGE("%s: Invalid HAL android.sensor.orientation value: %d",
+ __FUNCTION__, orientation);
+ return INVALID_OPERATION;
+ }
+ } else {
+ switch (orientation) {
+ case 0:
+ flags = HAL_TRANSFORM_FLIP_H;
+ break;
+ case 90:
+ flags = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90;
+ break;
+ case 180:
+ flags = HAL_TRANSFORM_FLIP_V;
+ break;
+ case 270:
+ flags = HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
+ break;
+ default:
+ ALOGE("%s: Invalid HAL android.sensor.orientation value: %d",
+ __FUNCTION__, orientation);
+ return INVALID_OPERATION;
+ }
+
}
/**
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index b490924..b9c16aa 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -45,8 +45,6 @@
uid_t clientUid,
int servicePid);
- virtual void notifyError();
-
sp<ICameraDeviceCallbacks> mRemoteCallback;
};
@@ -112,11 +110,19 @@
virtual status_t dump(int fd, const Vector<String16>& args);
/**
+ * Device listener interface
+ */
+
+ virtual void notifyIdle();
+ virtual void notifyError();
+ virtual void notifyShutter(int requestId, nsecs_t timestamp);
+
+ /**
* Interface used by independent components of CameraDeviceClient.
*/
protected:
/** FilteredListener implementation **/
- virtual void onFrameAvailable(int32_t frameId,
+ virtual void onFrameAvailable(int32_t requestId,
const CameraMetadata& frame);
virtual void detachDevice();
diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
index 2b583e5..1a7a7a7 100644
--- a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
+++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
@@ -374,7 +374,7 @@
}
/** Device-related methods */
-void ProCamera2Client::onFrameAvailable(int32_t frameId,
+void ProCamera2Client::onFrameAvailable(int32_t requestId,
const CameraMetadata& frame) {
ATRACE_CALL();
ALOGV("%s", __FUNCTION__);
@@ -386,7 +386,7 @@
CameraMetadata tmp(frame);
camera_metadata_t* meta = tmp.release();
ALOGV("%s: meta = %p ", __FUNCTION__, meta);
- mRemoteCallback->onResultReceived(frameId, meta);
+ mRemoteCallback->onResultReceived(requestId, meta);
tmp.acquire(meta);
}
diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.h b/services/camera/libcameraservice/api_pro/ProCamera2Client.h
index 0bf6784..8a0f547 100644
--- a/services/camera/libcameraservice/api_pro/ProCamera2Client.h
+++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.h
@@ -97,7 +97,7 @@
protected:
/** FilteredListener implementation **/
- virtual void onFrameAvailable(int32_t frameId,
+ virtual void onFrameAvailable(int32_t requestId,
const CameraMetadata& frame);
virtual void detachDevice();
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index e808bf3..2d1253f 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -226,13 +226,18 @@
}
template <typename TClientBase>
-void Camera2ClientBase<TClientBase>::notifyShutter(int frameNumber,
+void Camera2ClientBase<TClientBase>::notifyIdle() {
+ ALOGV("Camera device is now idle");
+}
+
+template <typename TClientBase>
+void Camera2ClientBase<TClientBase>::notifyShutter(int requestId,
nsecs_t timestamp) {
- (void)frameNumber;
+ (void)requestId;
(void)timestamp;
- ALOGV("%s: Shutter notification for frame %d at time %lld", __FUNCTION__,
- frameNumber, timestamp);
+ ALOGV("%s: Shutter notification for request id %d at time %lld",
+ __FUNCTION__, requestId, timestamp);
}
template <typename TClientBase>
@@ -244,13 +249,6 @@
ALOGV("%s: Autofocus state now %d, last trigger %d",
__FUNCTION__, newState, triggerId);
- typename SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
- if (l.mRemoteCallback != 0) {
- l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS_MOVE, 1, 0);
- }
- if (l.mRemoteCallback != 0) {
- l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS, 1, 0);
- }
}
template <typename TClientBase>
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index d23197c..61e44f0 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -62,7 +62,8 @@
*/
virtual void notifyError(int errorCode, int arg1, int arg2);
- virtual void notifyShutter(int frameNumber, nsecs_t timestamp);
+ virtual void notifyIdle();
+ virtual void notifyShutter(int requestId, nsecs_t timestamp);
virtual void notifyAutoFocus(uint8_t newState, int triggerId);
virtual void notifyAutoExposure(uint8_t newState, int triggerId);
virtual void notifyAutoWhitebalance(uint8_t newState,
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index ebbd4ea..e80abf1 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -138,9 +138,18 @@
*/
class NotificationListener {
public:
- // Refer to the Camera2 HAL definition for notification definitions
+ // The set of notifications is a merge of the notifications required for
+ // API1 and API2.
+
+ // Required for API 1 and 2
virtual void notifyError(int errorCode, int arg1, int arg2) = 0;
- virtual void notifyShutter(int frameNumber, nsecs_t timestamp) = 0;
+
+ // Required only for API2
+ virtual void notifyIdle() = 0;
+ virtual void notifyShutter(int requestId,
+ nsecs_t timestamp) = 0;
+
+ // Required only for API1
virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0;
virtual void notifyAutoExposure(uint8_t newState, int triggerId) = 0;
virtual void notifyAutoWhitebalance(uint8_t newState,
@@ -165,12 +174,14 @@
/**
* Wait for a new frame to be produced, with timeout in nanoseconds.
* Returns TIMED_OUT when no frame produced within the specified duration
+ * May be called concurrently to most methods, except for getNextFrame
*/
virtual status_t waitForNextFrame(nsecs_t timeout) = 0;
/**
* Get next metadata frame from the frame queue. Returns NULL if the queue
* is empty; caller takes ownership of the metadata buffer.
+ * May be called concurrently to most methods, except for waitForNextFrame
*/
virtual status_t getNextFrame(CameraMetadata *frame) = 0;
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.cpp b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
index e7b440a..f2064fb 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.cpp
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
@@ -37,11 +37,11 @@
}
status_t FrameProcessorBase::registerListener(int32_t minId,
- int32_t maxId, wp<FilteredListener> listener) {
+ int32_t maxId, wp<FilteredListener> listener, bool quirkSendPartials) {
Mutex::Autolock l(mInputMutex);
ALOGV("%s: Registering listener for frame id range %d - %d",
__FUNCTION__, minId, maxId);
- RangeListener rListener = { minId, maxId, listener };
+ RangeListener rListener = { minId, maxId, listener, quirkSendPartials };
mRangeListeners.push_back(rListener);
return OK;
}
@@ -66,7 +66,14 @@
void FrameProcessorBase::dump(int fd, const Vector<String16>& /*args*/) {
String8 result(" Latest received frame:\n");
write(fd, result.string(), result.size());
- mLastFrame.dump(fd, 2, 6);
+
+ CameraMetadata lastFrame;
+ {
+ // Don't race while dumping metadata
+ Mutex::Autolock al(mLastFrameMutex);
+ lastFrame = CameraMetadata(mLastFrame);
+ }
+ lastFrame.dump(fd, 2, 6);
}
bool FrameProcessorBase::threadLoop() {
@@ -113,6 +120,7 @@
}
if (!frame.isEmpty()) {
+ Mutex::Autolock al(mLastFrameMutex);
mLastFrame.acquire(frame);
}
}
@@ -137,6 +145,16 @@
ATRACE_CALL();
camera_metadata_ro_entry_t entry;
+ // Quirks: Don't deliver partial results to listeners that don't want them
+ bool quirkIsPartial = false;
+ entry = frame.find(ANDROID_QUIRKS_PARTIAL_RESULT);
+ if (entry.count != 0 &&
+ entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
+ ALOGV("%s: Camera %d: Not forwarding partial result to listeners",
+ __FUNCTION__, device->getId());
+ quirkIsPartial = true;
+ }
+
entry = frame.find(ANDROID_REQUEST_ID);
if (entry.count == 0) {
ALOGE("%s: Camera %d: Error reading frame id",
@@ -152,7 +170,8 @@
List<RangeListener>::iterator item = mRangeListeners.begin();
while (item != mRangeListeners.end()) {
if (requestId >= item->minId &&
- requestId < item->maxId) {
+ requestId < item->maxId &&
+ (!quirkIsPartial || item->quirkSendPartials) ) {
sp<FilteredListener> listener = item->listener.promote();
if (listener == 0) {
item = mRangeListeners.erase(item);
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.h b/services/camera/libcameraservice/common/FrameProcessorBase.h
index 1e46beb..89b608a 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.h
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.h
@@ -39,14 +39,16 @@
virtual ~FrameProcessorBase();
struct FilteredListener: virtual public RefBase {
- virtual void onFrameAvailable(int32_t frameId,
+ virtual void onFrameAvailable(int32_t requestId,
const CameraMetadata &frame) = 0;
};
// Register a listener for a range of IDs [minId, maxId). Multiple listeners
- // can be listening to the same range
+ // can be listening to the same range.
+ // QUIRK: sendPartials controls whether partial results will be sent.
status_t registerListener(int32_t minId, int32_t maxId,
- wp<FilteredListener> listener);
+ wp<FilteredListener> listener,
+ bool quirkSendPartials = true);
status_t removeListener(int32_t minId, int32_t maxId,
wp<FilteredListener> listener);
@@ -58,11 +60,13 @@
virtual bool threadLoop();
Mutex mInputMutex;
+ Mutex mLastFrameMutex;
struct RangeListener {
int32_t minId;
int32_t maxId;
wp<FilteredListener> listener;
+ bool quirkSendPartials;
};
List<RangeListener> mRangeListeners;
diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp
index fe2cd77..dc97c47 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.cpp
+++ b/services/camera/libcameraservice/device2/Camera2Device.cpp
@@ -25,6 +25,7 @@
#define ALOGVV(...) ((void)0)
#endif
+#include <inttypes.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#include <utils/Timers.h>
@@ -464,8 +465,10 @@
listener->notifyError(ext1, ext2, ext3);
break;
case CAMERA2_MSG_SHUTTER: {
- nsecs_t timestamp = (nsecs_t)ext2 | ((nsecs_t)(ext3) << 32 );
- listener->notifyShutter(ext1, timestamp);
+ // TODO: Only needed for camera2 API, which is unsupported
+ // by HAL2 directly.
+ // nsecs_t timestamp = (nsecs_t)ext2 | ((nsecs_t)(ext3) << 32 );
+ // listener->notifyShutter(requestId, timestamp);
break;
}
case CAMERA2_MSG_AUTOFOCUS:
@@ -820,7 +823,7 @@
result.append(" Stream slot: Empty\n");
write(fd, result.string(), result.size());
} else {
- result.appendFormat(" Stream slot: %d entries\n",
+ result.appendFormat(" Stream slot: %zu entries\n",
mStreamSlot.size());
int i = 0;
for (List<camera_metadata_t*>::iterator r = mStreamSlot.begin();
@@ -835,7 +838,7 @@
result = " Main queue is empty\n";
write(fd, result.string(), result.size());
} else {
- result = String8::format(" Main queue has %d entries:\n",
+ result = String8::format(" Main queue has %zu entries:\n",
mEntries.size());
int i = 0;
for (List<camera_metadata_t*>::iterator r = mEntries.begin();
@@ -1212,11 +1215,11 @@
ATRACE_CALL();
String8 result = String8::format(" Stream %d: %d x %d, format 0x%x\n",
mId, mWidth, mHeight, mFormat);
- result.appendFormat(" size %d, usage 0x%x, requested format 0x%x\n",
+ result.appendFormat(" size %zu, usage 0x%x, requested format 0x%x\n",
mSize, mUsage, mFormatRequested);
result.appendFormat(" total buffers: %d, dequeued buffers: %d\n",
mTotalBuffers, mActiveBuffers);
- result.appendFormat(" frame count: %d, last timestamp %lld\n",
+ result.appendFormat(" frame count: %d, last timestamp %" PRId64 "\n",
mFrameCount, mLastTimestamp);
write(fd, result.string(), result.size());
return OK;
diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h
index 2aa22a2..1f53c56 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.h
+++ b/services/camera/libcameraservice/device2/Camera2Device.h
@@ -28,6 +28,10 @@
/**
* CameraDevice for HAL devices with version CAMERA_DEVICE_API_VERSION_2_0
+ *
+ * TODO for camera2 API implementation:
+ * Does not produce notifyShutter / notifyIdle callbacks to NotificationListener
+ * Use waitUntilDrained for idle.
*/
class Camera2Device: public CameraDeviceBase {
public:
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index b468eb3..da3e121 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -41,6 +41,7 @@
#include <utils/Trace.h>
#include <utils/Timers.h>
+#include "utils/CameraTraces.h"
#include "device3/Camera3Device.h"
#include "device3/Camera3OutputStream.h"
#include "device3/Camera3InputStream.h"
@@ -54,6 +55,7 @@
mId(id),
mHal3Device(NULL),
mStatus(STATUS_UNINITIALIZED),
+ mUsePartialResultQuirk(false),
mNextResultFrameNumber(0),
mNextShutterFrameNumber(0),
mListener(NULL)
@@ -82,6 +84,7 @@
status_t Camera3Device::initialize(camera_module_t *module)
{
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId);
@@ -97,8 +100,10 @@
camera3_device_t *device;
+ ATRACE_BEGIN("camera3->open");
res = module->common.methods->open(&module->common, deviceName.string(),
reinterpret_cast<hw_device_t**>(&device));
+ ATRACE_END();
if (res != OK) {
SET_ERR_L("Could not open camera: %s (%d)", strerror(-res), res);
@@ -159,9 +164,20 @@
}
}
+ /** Start up status tracker thread */
+ mStatusTracker = new StatusTracker(this);
+ res = mStatusTracker->run(String8::format("C3Dev-%d-Status", mId).string());
+ if (res != OK) {
+ SET_ERR_L("Unable to start status tracking thread: %s (%d)",
+ strerror(-res), res);
+ device->common.close(&device->common);
+ mStatusTracker.clear();
+ return res;
+ }
+
/** Start up request queue thread */
- mRequestThread = new RequestThread(this, device);
+ mRequestThread = new RequestThread(this, mStatusTracker, device);
res = mRequestThread->run(String8::format("C3Dev-%d-ReqQueue", mId).string());
if (res != OK) {
SET_ERR_L("Unable to start request queue thread: %s (%d)",
@@ -175,81 +191,141 @@
mDeviceInfo = info.static_camera_characteristics;
mHal3Device = device;
- mStatus = STATUS_IDLE;
+ mStatus = STATUS_UNCONFIGURED;
mNextStreamId = 0;
mNeedConfig = true;
+ mPauseStateNotify = false;
+
+ /** Check for quirks */
+
+ // Will the HAL be sending in early partial result metadata?
+ camera_metadata_entry partialResultsQuirk =
+ mDeviceInfo.find(ANDROID_QUIRKS_USE_PARTIAL_RESULT);
+ if (partialResultsQuirk.count > 0 && partialResultsQuirk.data.u8[0] == 1) {
+ mUsePartialResultQuirk = true;
+ }
return OK;
}
status_t Camera3Device::disconnect() {
ATRACE_CALL();
- Mutex::Autolock l(mLock);
+ Mutex::Autolock il(mInterfaceLock);
ALOGV("%s: E", __FUNCTION__);
status_t res = OK;
- if (mStatus == STATUS_UNINITIALIZED) return res;
- if (mStatus == STATUS_ACTIVE ||
- (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
- res = mRequestThread->clearRepeatingRequests();
- if (res != OK) {
- SET_ERR_L("Can't stop streaming");
- // Continue to close device even in case of error
- } else {
- res = waitUntilDrainedLocked();
+ {
+ Mutex::Autolock l(mLock);
+ if (mStatus == STATUS_UNINITIALIZED) return res;
+
+ if (mStatus == STATUS_ACTIVE ||
+ (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
+ res = mRequestThread->clearRepeatingRequests();
if (res != OK) {
- SET_ERR_L("Timeout waiting for HAL to drain");
+ SET_ERR_L("Can't stop streaming");
// Continue to close device even in case of error
+ } else {
+ res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ if (res != OK) {
+ SET_ERR_L("Timeout waiting for HAL to drain");
+ // Continue to close device even in case of error
+ }
}
}
- }
- assert(mStatus == STATUS_IDLE || mStatus == STATUS_ERROR);
- if (mStatus == STATUS_ERROR) {
- CLOGE("Shutting down in an error state");
- }
-
- if (mRequestThread != NULL) {
- mRequestThread->requestExit();
- }
-
- mOutputStreams.clear();
- mInputStream.clear();
-
- if (mRequestThread != NULL) {
- if (mStatus != STATUS_ERROR) {
- // HAL may be in a bad state, so waiting for request thread
- // (which may be stuck in the HAL processCaptureRequest call)
- // could be dangerous.
- mRequestThread->join();
+ if (mStatus == STATUS_ERROR) {
+ CLOGE("Shutting down in an error state");
}
+
+ if (mStatusTracker != NULL) {
+ mStatusTracker->requestExit();
+ }
+
+ if (mRequestThread != NULL) {
+ mRequestThread->requestExit();
+ }
+
+ mOutputStreams.clear();
+ mInputStream.clear();
+ }
+
+ // Joining done without holding mLock, otherwise deadlocks may ensue
+ // as the threads try to access parent state
+ if (mRequestThread != NULL && mStatus != STATUS_ERROR) {
+ // HAL may be in a bad state, so waiting for request thread
+ // (which may be stuck in the HAL processCaptureRequest call)
+ // could be dangerous.
+ mRequestThread->join();
+ }
+
+ if (mStatusTracker != NULL) {
+ mStatusTracker->join();
+ }
+
+ {
+ Mutex::Autolock l(mLock);
+
mRequestThread.clear();
- }
+ mStatusTracker.clear();
- if (mHal3Device != NULL) {
- mHal3Device->common.close(&mHal3Device->common);
- mHal3Device = NULL;
- }
+ if (mHal3Device != NULL) {
+ ATRACE_BEGIN("camera3->close");
+ mHal3Device->common.close(&mHal3Device->common);
+ ATRACE_END();
+ mHal3Device = NULL;
+ }
- mStatus = STATUS_UNINITIALIZED;
+ mStatus = STATUS_UNINITIALIZED;
+ }
ALOGV("%s: X", __FUNCTION__);
return res;
}
+// For dumping/debugging only -
+// try to acquire a lock a few times, eventually give up to proceed with
+// debug/dump operations
+bool Camera3Device::tryLockSpinRightRound(Mutex& lock) {
+ bool gotLock = false;
+ for (size_t i = 0; i < kDumpLockAttempts; ++i) {
+ if (lock.tryLock() == NO_ERROR) {
+ gotLock = true;
+ break;
+ } else {
+ usleep(kDumpSleepDuration);
+ }
+ }
+ return gotLock;
+}
+
status_t Camera3Device::dump(int fd, const Vector<String16> &args) {
ATRACE_CALL();
(void)args;
+
+ // Try to lock, but continue in case of failure (to avoid blocking in
+ // deadlocks)
+ bool gotInterfaceLock = tryLockSpinRightRound(mInterfaceLock);
+ bool gotLock = tryLockSpinRightRound(mLock);
+
+ ALOGW_IF(!gotInterfaceLock,
+ "Camera %d: %s: Unable to lock interface lock, proceeding anyway",
+ mId, __FUNCTION__);
+ ALOGW_IF(!gotLock,
+ "Camera %d: %s: Unable to lock main lock, proceeding anyway",
+ mId, __FUNCTION__);
+
String8 lines;
const char *status =
mStatus == STATUS_ERROR ? "ERROR" :
mStatus == STATUS_UNINITIALIZED ? "UNINITIALIZED" :
- mStatus == STATUS_IDLE ? "IDLE" :
+ mStatus == STATUS_UNCONFIGURED ? "UNCONFIGURED" :
+ mStatus == STATUS_CONFIGURED ? "CONFIGURED" :
mStatus == STATUS_ACTIVE ? "ACTIVE" :
"Unknown";
+
lines.appendFormat(" Device status: %s\n", status);
if (mStatus == STATUS_ERROR) {
lines.appendFormat(" Error cause: %s\n", mErrorCause.string());
@@ -285,7 +361,7 @@
lines = String8(" Last request sent:\n");
write(fd, lines.string(), lines.size());
- CameraMetadata lastRequest = getLatestRequest();
+ CameraMetadata lastRequest = getLatestRequestLocked();
lastRequest.dump(fd, /*verbosity*/2, /*indentation*/6);
}
@@ -295,6 +371,9 @@
mHal3Device->ops->dump(mHal3Device, fd);
}
+ if (gotLock) mLock.unlock();
+ if (gotInterfaceLock) mInterfaceLock.unlock();
+
return OK;
}
@@ -311,6 +390,8 @@
status_t Camera3Device::capture(CameraMetadata &request) {
ATRACE_CALL();
+ status_t res;
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
// TODO: take ownership of the request
@@ -322,7 +403,9 @@
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ // May be lazily configuring streams, will check during setup
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -337,12 +420,23 @@
return BAD_VALUE;
}
- return mRequestThread->queueRequest(newRequest);
+ res = mRequestThread->queueRequest(newRequest);
+ if (res == OK) {
+ waitUntilStateThenRelock(/*active*/ true, kActiveTimeout);
+ if (res != OK) {
+ SET_ERR_L("Can't transition to active in %f seconds!",
+ kActiveTimeout/1e9);
+ }
+ ALOGV("Camera %d: Capture request enqueued", mId);
+ }
+ return res;
}
status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) {
ATRACE_CALL();
+ status_t res;
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
switch (mStatus) {
@@ -352,7 +446,9 @@
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ // May be lazily configuring streams, will check during setup
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -370,7 +466,16 @@
RequestList newRepeatingRequests;
newRepeatingRequests.push_back(newRepeatingRequest);
- return mRequestThread->setRepeatingRequests(newRepeatingRequests);
+ res = mRequestThread->setRepeatingRequests(newRepeatingRequests);
+ if (res == OK) {
+ waitUntilStateThenRelock(/*active*/ true, kActiveTimeout);
+ if (res != OK) {
+ SET_ERR_L("Can't transition to active in %f seconds!",
+ kActiveTimeout/1e9);
+ }
+ ALOGV("Camera %d: Repeating request set", mId);
+ }
+ return res;
}
@@ -378,12 +483,16 @@
const CameraMetadata &request) {
status_t res;
- if (mStatus == STATUS_IDLE) {
+ if (mStatus == STATUS_UNCONFIGURED || mNeedConfig) {
res = configureStreamsLocked();
if (res != OK) {
SET_ERR_L("Can't set up streams: %s (%d)", strerror(-res), res);
return NULL;
}
+ if (mStatus == STATUS_UNCONFIGURED) {
+ CLOGE("No streams configured");
+ return NULL;
+ }
}
sp<CaptureRequest> newRequest = createCaptureRequest(request);
@@ -392,6 +501,7 @@
status_t Camera3Device::clearStreamingRequest() {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
switch (mStatus) {
@@ -401,7 +511,8 @@
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -409,12 +520,13 @@
SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
-
+ ALOGV("Camera %d: Clearing repeating request", mId);
return mRequestThread->clearRepeatingRequests();
}
status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
return mRequestThread->waitUntilRequestProcessed(requestId, timeout);
}
@@ -422,7 +534,10 @@
status_t Camera3Device::createInputStream(
uint32_t width, uint32_t height, int format, int *id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
+ ALOGV("Camera %d: Creating new input stream %d: %d x %d, format %d",
+ mId, mNextStreamId, width, height, format);
status_t res;
bool wasActive = false;
@@ -434,26 +549,24 @@
case STATUS_UNINITIALIZED:
ALOGE("%s: Device not initialized", __FUNCTION__);
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
// OK
break;
case STATUS_ACTIVE:
ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
- mRequestThread->setPaused(true);
- res = waitUntilDrainedLocked();
+ res = internalPauseAndWaitLocked();
if (res != OK) {
- ALOGE("%s: Can't pause captures to reconfigure streams!",
- __FUNCTION__);
- mStatus = STATUS_ERROR;
+ SET_ERR_L("Can't pause captures to reconfigure streams!");
return res;
}
wasActive = true;
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("%s: Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
- assert(mStatus == STATUS_IDLE);
+ assert(mStatus != STATUS_ACTIVE);
if (mInputStream != 0) {
ALOGE("%s: Cannot create more than 1 input stream", __FUNCTION__);
@@ -462,6 +575,7 @@
sp<Camera3InputStream> newStream = new Camera3InputStream(mNextStreamId,
width, height, format);
+ newStream->setStatusTracker(mStatusTracker);
mInputStream = newStream;
@@ -476,9 +590,10 @@
__FUNCTION__, mNextStreamId, strerror(-res), res);
return res;
}
- mRequestThread->setPaused(false);
+ internalResumeLocked();
}
+ ALOGV("Camera %d: Created input stream", mId);
return OK;
}
@@ -490,7 +605,10 @@
int *id,
sp<Camera3ZslStream>* zslStream) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
+ ALOGV("Camera %d: Creating ZSL stream %d: %d x %d, depth %d",
+ mId, mNextStreamId, width, height, depth);
status_t res;
bool wasActive = false;
@@ -502,26 +620,24 @@
case STATUS_UNINITIALIZED:
ALOGE("%s: Device not initialized", __FUNCTION__);
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
// OK
break;
case STATUS_ACTIVE:
ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
- mRequestThread->setPaused(true);
- res = waitUntilDrainedLocked();
+ res = internalPauseAndWaitLocked();
if (res != OK) {
- ALOGE("%s: Can't pause captures to reconfigure streams!",
- __FUNCTION__);
- mStatus = STATUS_ERROR;
+ SET_ERR_L("Can't pause captures to reconfigure streams!");
return res;
}
wasActive = true;
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
- assert(mStatus == STATUS_IDLE);
+ assert(mStatus != STATUS_ACTIVE);
if (mInputStream != 0) {
ALOGE("%s: Cannot create more than 1 input stream", __FUNCTION__);
@@ -530,6 +646,7 @@
sp<Camera3ZslStream> newStream = new Camera3ZslStream(mNextStreamId,
width, height, depth);
+ newStream->setStatusTracker(mStatusTracker);
res = mOutputStreams.add(mNextStreamId, newStream);
if (res < 0) {
@@ -551,16 +668,20 @@
__FUNCTION__, mNextStreamId, strerror(-res), res);
return res;
}
- mRequestThread->setPaused(false);
+ internalResumeLocked();
}
+ ALOGV("Camera %d: Created ZSL stream", mId);
return OK;
}
status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format, size_t size, int *id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
+ ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, size %d",
+ mId, mNextStreamId, width, height, format, size);
status_t res;
bool wasActive = false;
@@ -572,16 +693,15 @@
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
// OK
break;
case STATUS_ACTIVE:
ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
- mRequestThread->setPaused(true);
- res = waitUntilDrainedLocked();
+ res = internalPauseAndWaitLocked();
if (res != OK) {
- ALOGE("%s: Can't pause captures to reconfigure streams!",
- __FUNCTION__);
+ SET_ERR_L("Can't pause captures to reconfigure streams!");
return res;
}
wasActive = true;
@@ -590,7 +710,7 @@
SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
- assert(mStatus == STATUS_IDLE);
+ assert(mStatus != STATUS_ACTIVE);
sp<Camera3OutputStream> newStream;
if (format == HAL_PIXEL_FORMAT_BLOB) {
@@ -600,6 +720,7 @@
newStream = new Camera3OutputStream(mNextStreamId, consumer,
width, height, format);
}
+ newStream->setStatusTracker(mStatusTracker);
res = mOutputStreams.add(mNextStreamId, newStream);
if (res < 0) {
@@ -619,9 +740,9 @@
mNextStreamId, strerror(-res), res);
return res;
}
- mRequestThread->setPaused(false);
+ internalResumeLocked();
}
-
+ ALOGV("Camera %d: Created new stream", mId);
return OK;
}
@@ -637,6 +758,7 @@
status_t Camera3Device::getStreamInfo(int id,
uint32_t *width, uint32_t *height, uint32_t *format) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
switch (mStatus) {
@@ -646,7 +768,8 @@
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized!");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -671,6 +794,7 @@
status_t Camera3Device::setStreamTransform(int id,
int transform) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
switch (mStatus) {
@@ -680,7 +804,8 @@
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -701,6 +826,7 @@
status_t Camera3Device::deleteStream(int id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
status_t res;
@@ -708,22 +834,26 @@
// CameraDevice semantics require device to already be idle before
// deleteStream is called, unlike for createStream.
- if (mStatus != STATUS_IDLE) {
+ if (mStatus == STATUS_ACTIVE) {
ALOGV("%s: Camera %d: Device not idle", __FUNCTION__, mId);
return -EBUSY;
}
sp<Camera3StreamInterface> deletedStream;
+ ssize_t outputStreamIdx = mOutputStreams.indexOfKey(id);
if (mInputStream != NULL && id == mInputStream->getId()) {
deletedStream = mInputStream;
mInputStream.clear();
} else {
- ssize_t idx = mOutputStreams.indexOfKey(id);
- if (idx == NAME_NOT_FOUND) {
+ if (outputStreamIdx == NAME_NOT_FOUND) {
CLOGE("Stream %d does not exist", id);
return BAD_VALUE;
}
- deletedStream = mOutputStreams.editValueAt(idx);
+ }
+
+ // Delete output stream or the output part of a bi-directional stream.
+ if (outputStreamIdx != NAME_NOT_FOUND) {
+ deletedStream = mOutputStreams.editValueAt(outputStreamIdx);
mOutputStreams.removeItem(id);
}
@@ -752,6 +882,7 @@
CameraMetadata *request) {
ATRACE_CALL();
ALOGV("%s: for template %d", __FUNCTION__, templateId);
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
switch (mStatus) {
@@ -761,7 +892,8 @@
case STATUS_UNINITIALIZED:
CLOGE("Device is not initialized!");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -787,61 +919,88 @@
status_t Camera3Device::waitUntilDrained() {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
- return waitUntilDrainedLocked();
-}
-
-status_t Camera3Device::waitUntilDrainedLocked() {
- ATRACE_CALL();
- status_t res;
-
switch (mStatus) {
case STATUS_UNINITIALIZED:
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
ALOGV("%s: Already idle", __FUNCTION__);
return OK;
+ case STATUS_CONFIGURED:
+ // To avoid race conditions, check with tracker to be sure
case STATUS_ERROR:
case STATUS_ACTIVE:
- // Need to shut down
+ // Need to verify shut down
break;
default:
SET_ERR_L("Unexpected status: %d",mStatus);
return INVALID_OPERATION;
}
- if (mRequestThread != NULL) {
- res = mRequestThread->waitUntilPaused(kShutdownTimeout);
- if (res != OK) {
- SET_ERR_L("Can't stop request thread in %f seconds!",
- kShutdownTimeout/1e9);
- return res;
- }
- }
- if (mInputStream != NULL) {
- res = mInputStream->waitUntilIdle(kShutdownTimeout);
- if (res != OK) {
- SET_ERR_L("Can't idle input stream %d in %f seconds!",
- mInputStream->getId(), kShutdownTimeout/1e9);
- return res;
- }
- }
- for (size_t i = 0; i < mOutputStreams.size(); i++) {
- res = mOutputStreams.editValueAt(i)->waitUntilIdle(kShutdownTimeout);
- if (res != OK) {
- SET_ERR_L("Can't idle output stream %d in %f seconds!",
- mOutputStreams.keyAt(i), kShutdownTimeout/1e9);
- return res;
- }
+ ALOGV("%s: Camera %d: Waiting until idle", __FUNCTION__, mId);
+ status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ return res;
+}
+
+// Pause to reconfigure
+status_t Camera3Device::internalPauseAndWaitLocked() {
+ mRequestThread->setPaused(true);
+ mPauseStateNotify = true;
+
+ ALOGV("%s: Camera %d: Internal wait until idle", __FUNCTION__, mId);
+ status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ if (res != OK) {
+ SET_ERR_L("Can't idle device in %f seconds!",
+ kShutdownTimeout/1e9);
}
- if (mStatus != STATUS_ERROR) {
- mStatus = STATUS_IDLE;
- }
+ return res;
+}
+// Resume after internalPauseAndWaitLocked
+status_t Camera3Device::internalResumeLocked() {
+ status_t res;
+
+ mRequestThread->setPaused(false);
+
+ res = waitUntilStateThenRelock(/*active*/ true, kActiveTimeout);
+ if (res != OK) {
+ SET_ERR_L("Can't transition to active in %f seconds!",
+ kActiveTimeout/1e9);
+ }
+ mPauseStateNotify = false;
return OK;
}
+status_t Camera3Device::waitUntilStateThenRelock(bool active,
+ nsecs_t timeout) {
+ status_t res = OK;
+ if (active == (mStatus == STATUS_ACTIVE)) {
+ // Desired state already reached
+ return res;
+ }
+
+ bool stateSeen = false;
+ do {
+ mRecentStatusUpdates.clear();
+
+ res = mStatusChanged.waitRelative(mLock, timeout);
+ if (res != OK) break;
+
+ // Check state change history during wait
+ for (size_t i = 0; i < mRecentStatusUpdates.size(); i++) {
+ if (active == (mRecentStatusUpdates[i] == STATUS_ACTIVE) ) {
+ stateSeen = true;
+ break;
+ }
+ }
+ } while (!stateSeen);
+
+ return res;
+}
+
+
status_t Camera3Device::setNotifyCallback(NotificationListener *listener) {
ATRACE_CALL();
Mutex::Autolock l(mOutputLock);
@@ -859,7 +1018,6 @@
}
status_t Camera3Device::waitForNextFrame(nsecs_t timeout) {
- ATRACE_CALL();
status_t res;
Mutex::Autolock l(mOutputLock);
@@ -893,6 +1051,7 @@
status_t Camera3Device::triggerAutofocus(uint32_t id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id);
// Mix-in this trigger into the next request and only the next request.
@@ -913,6 +1072,7 @@
status_t Camera3Device::triggerCancelAutofocus(uint32_t id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
ALOGV("%s: Triggering cancel autofocus, id %d", __FUNCTION__, id);
// Mix-in this trigger into the next request and only the next request.
@@ -933,6 +1093,7 @@
status_t Camera3Device::triggerPrecaptureMetering(uint32_t id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id);
// Mix-in this trigger into the next request and only the next request.
@@ -963,11 +1124,53 @@
status_t Camera3Device::flush() {
ATRACE_CALL();
ALOGV("%s: Camera %d: Flushing all requests", __FUNCTION__, mId);
-
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
mRequestThread->clear();
- return mHal3Device->ops->flush(mHal3Device);
+ status_t res;
+ if (mHal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_1) {
+ res = mHal3Device->ops->flush(mHal3Device);
+ } else {
+ res = waitUntilDrained();
+ }
+
+ return res;
+}
+
+/**
+ * Methods called by subclasses
+ */
+
+void Camera3Device::notifyStatus(bool idle) {
+ {
+ // Need mLock to safely update state and synchronize to current
+ // state of methods in flight.
+ Mutex::Autolock l(mLock);
+ // We can get various system-idle notices from the status tracker
+ // while starting up. Only care about them if we've actually sent
+ // in some requests recently.
+ if (mStatus != STATUS_ACTIVE && mStatus != STATUS_CONFIGURED) {
+ return;
+ }
+ ALOGV("%s: Camera %d: Now %s", __FUNCTION__, mId,
+ idle ? "idle" : "active");
+ mStatus = idle ? STATUS_CONFIGURED : STATUS_ACTIVE;
+ mRecentStatusUpdates.add(mStatus);
+ mStatusChanged.signal();
+
+ // Skip notifying listener if we're doing some user-transparent
+ // state changes
+ if (mPauseStateNotify) return;
+ }
+ NotificationListener *listener;
+ {
+ Mutex::Autolock l(mOutputLock);
+ listener = mListener;
+ }
+ if (idle && listener != NULL) {
+ listener->notifyIdle();
+ }
}
/**
@@ -1046,18 +1249,18 @@
ATRACE_CALL();
status_t res;
- if (mStatus != STATUS_IDLE) {
+ if (mStatus != STATUS_UNCONFIGURED && mStatus != STATUS_CONFIGURED) {
CLOGE("Not idle");
return INVALID_OPERATION;
}
if (!mNeedConfig) {
ALOGV("%s: Skipping config, no stream changes", __FUNCTION__);
- mStatus = STATUS_ACTIVE;
return OK;
}
// Start configuring the streams
+ ALOGV("%s: Camera %d: Starting stream configuration", __FUNCTION__, mId);
camera3_stream_configuration config;
@@ -1139,11 +1342,18 @@
// across configure_streams() calls
mRequestThread->configurationComplete();
- // Finish configuring the streams lazily on first reference
+ // Update device state
- mStatus = STATUS_ACTIVE;
mNeedConfig = false;
+ if (config.num_streams > 0) {
+ mStatus = STATUS_CONFIGURED;
+ } else {
+ mStatus = STATUS_UNCONFIGURED;
+ }
+
+ ALOGV("%s: Camera %d: Stream configuration complete", __FUNCTION__, mId);
+
return OK;
}
@@ -1179,6 +1389,10 @@
// But only do error state transition steps for the first error
if (mStatus == STATUS_ERROR || mStatus == STATUS_UNINITIALIZED) return;
+ // Save stack trace. View by dumping it later.
+ CameraTraces::saveTrace();
+ // TODO: consider adding errorCause and client pid/procname
+
mErrorCause = errorCause;
mRequestThread->setPaused(true);
@@ -1190,18 +1404,187 @@
*/
status_t Camera3Device::registerInFlight(int32_t frameNumber,
- int32_t numBuffers) {
+ int32_t requestId, int32_t numBuffers) {
ATRACE_CALL();
Mutex::Autolock l(mInFlightLock);
ssize_t res;
- res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers));
+ res = mInFlightMap.add(frameNumber, InFlightRequest(requestId, numBuffers));
if (res < 0) return res;
return OK;
}
/**
+ * QUIRK(partial results)
+ * Check if all 3A fields are ready, and send off a partial 3A-only result
+ * to the output frame queue
+ */
+bool Camera3Device::processPartial3AQuirk(
+ int32_t frameNumber, int32_t requestId,
+ const CameraMetadata& partial) {
+
+ // Check if all 3A states are present
+ // The full list of fields is
+ // android.control.afMode
+ // android.control.awbMode
+ // android.control.aeState
+ // android.control.awbState
+ // android.control.afState
+ // android.control.afTriggerID
+ // android.control.aePrecaptureID
+ // TODO: Add android.control.aeMode
+
+ bool gotAllStates = true;
+
+ uint8_t afMode;
+ uint8_t awbMode;
+ uint8_t aeState;
+ uint8_t afState;
+ uint8_t awbState;
+ int32_t afTriggerId;
+ int32_t aeTriggerId;
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_MODE,
+ &afMode, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AWB_MODE,
+ &awbMode, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AE_STATE,
+ &aeState, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_STATE,
+ &afState, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AWB_STATE,
+ &awbState, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_TRIGGER_ID,
+ &afTriggerId, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ &aeTriggerId, frameNumber);
+
+ if (!gotAllStates) return false;
+
+ ALOGVV("%s: Camera %d: Frame %d, Request ID %d: AF mode %d, AWB mode %d, "
+ "AF state %d, AE state %d, AWB state %d, "
+ "AF trigger %d, AE precapture trigger %d",
+ __FUNCTION__, mId, frameNumber, requestId,
+ afMode, awbMode,
+ afState, aeState, awbState,
+ afTriggerId, aeTriggerId);
+
+ // Got all states, so construct a minimal result to send
+ // In addition to the above fields, this means adding in
+ // android.request.frameCount
+ // android.request.requestId
+ // android.quirks.partialResult
+
+ const size_t kMinimal3AResultEntries = 10;
+
+ Mutex::Autolock l(mOutputLock);
+
+ CameraMetadata& min3AResult =
+ *mResultQueue.insert(
+ mResultQueue.end(),
+ CameraMetadata(kMinimal3AResultEntries, /*dataCapacity*/ 0));
+
+ if (!insert3AResult(min3AResult, ANDROID_REQUEST_FRAME_COUNT,
+ &frameNumber, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_REQUEST_ID,
+ &requestId, frameNumber)) {
+ return false;
+ }
+
+ static const uint8_t partialResult = ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL;
+ if (!insert3AResult(min3AResult, ANDROID_QUIRKS_PARTIAL_RESULT,
+ &partialResult, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_MODE,
+ &afMode, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AWB_MODE,
+ &awbMode, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AE_STATE,
+ &aeState, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_STATE,
+ &afState, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AWB_STATE,
+ &awbState, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_TRIGGER_ID,
+ &afTriggerId, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ &aeTriggerId, frameNumber)) {
+ return false;
+ }
+
+ mResultSignal.signal();
+
+ return true;
+}
+
+template<typename T>
+bool Camera3Device::get3AResult(const CameraMetadata& result, int32_t tag,
+ T* value, int32_t frameNumber) {
+ (void) frameNumber;
+
+ camera_metadata_ro_entry_t entry;
+
+ entry = result.find(tag);
+ if (entry.count == 0) {
+ ALOGVV("%s: Camera %d: Frame %d: No %s provided by HAL!", __FUNCTION__,
+ mId, frameNumber, get_camera_metadata_tag_name(tag));
+ return false;
+ }
+
+ if (sizeof(T) == sizeof(uint8_t)) {
+ *value = entry.data.u8[0];
+ } else if (sizeof(T) == sizeof(int32_t)) {
+ *value = entry.data.i32[0];
+ } else {
+ ALOGE("%s: Unexpected type", __FUNCTION__);
+ return false;
+ }
+ return true;
+}
+
+template<typename T>
+bool Camera3Device::insert3AResult(CameraMetadata& result, int32_t tag,
+ const T* value, int32_t frameNumber) {
+ if (result.update(tag, value, 1) != NO_ERROR) {
+ mResultQueue.erase(--mResultQueue.end(), mResultQueue.end());
+ SET_ERR("Frame %d: Failed to set %s in partial metadata",
+ frameNumber, get_camera_metadata_tag_name(tag));
+ return false;
+ }
+ return true;
+}
+
+/**
* Camera HAL device callback methods
*/
@@ -1216,6 +1599,8 @@
frameNumber);
return;
}
+ bool partialResultQuirk = false;
+ CameraMetadata collectedQuirkResult;
// Get capture timestamp from list of in-flight requests, where it was added
// by the shutter notification for this frame. Then update the in-flight
@@ -1231,19 +1616,58 @@
return;
}
InFlightRequest &request = mInFlightMap.editValueAt(idx);
+
+ // Check if this result carries only partial metadata
+ if (mUsePartialResultQuirk && result->result != NULL) {
+ camera_metadata_ro_entry_t partialResultEntry;
+ res = find_camera_metadata_ro_entry(result->result,
+ ANDROID_QUIRKS_PARTIAL_RESULT, &partialResultEntry);
+ if (res != NAME_NOT_FOUND &&
+ partialResultEntry.count > 0 &&
+ partialResultEntry.data.u8[0] ==
+ ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
+ // A partial result. Flag this as such, and collect this
+ // set of metadata into the in-flight entry.
+ partialResultQuirk = true;
+ request.partialResultQuirk.collectedResult.append(
+ result->result);
+ request.partialResultQuirk.collectedResult.erase(
+ ANDROID_QUIRKS_PARTIAL_RESULT);
+ // Fire off a 3A-only result if possible
+ if (!request.partialResultQuirk.haveSent3A) {
+ request.partialResultQuirk.haveSent3A =
+ processPartial3AQuirk(frameNumber,
+ request.requestId,
+ request.partialResultQuirk.collectedResult);
+ }
+ }
+ }
+
timestamp = request.captureTimestamp;
- if (timestamp == 0) {
+ /**
+ * One of the following must happen before it's legal to call process_capture_result,
+ * unless partial metadata is being provided:
+ * - CAMERA3_MSG_SHUTTER (expected during normal operation)
+ * - CAMERA3_MSG_ERROR (expected during flush)
+ */
+ if (request.requestStatus == OK && timestamp == 0 && !partialResultQuirk) {
SET_ERR("Called before shutter notify for frame %d",
frameNumber);
return;
}
- if (result->result != NULL) {
+ // Did we get the (final) result metadata for this capture?
+ if (result->result != NULL && !partialResultQuirk) {
if (request.haveResultMetadata) {
SET_ERR("Called multiple times with metadata for frame %d",
frameNumber);
return;
}
+ if (mUsePartialResultQuirk &&
+ !request.partialResultQuirk.collectedResult.isEmpty()) {
+ collectedQuirkResult.acquire(
+ request.partialResultQuirk.collectedResult);
+ }
request.haveResultMetadata = true;
}
@@ -1255,7 +1679,10 @@
return;
}
- if (request.haveResultMetadata && request.numBuffersLeft == 0) {
+ // Check if everything has arrived for this result (buffers and metadata), remove it from
+ // InFlightMap if both arrived or HAL reports error for this request (i.e. during flush).
+ if ((request.requestStatus != OK) ||
+ (request.haveResultMetadata && request.numBuffersLeft == 0)) {
ATRACE_ASYNC_END("frame capture", frameNumber);
mInFlightMap.removeItemsAt(idx, 1);
}
@@ -1269,9 +1696,12 @@
}
// Process the result metadata, if provided
- if (result->result != NULL) {
+ bool gotResult = false;
+ if (result->result != NULL && !partialResultQuirk) {
Mutex::Autolock l(mOutputLock);
+ gotResult = true;
+
if (frameNumber != mNextResultFrameNumber) {
SET_ERR("Out-of-order capture result metadata submitted! "
"(got frame number %d, expecting %d)",
@@ -1280,19 +1710,26 @@
}
mNextResultFrameNumber++;
- CameraMetadata &captureResult =
- *mResultQueue.insert(mResultQueue.end(), CameraMetadata());
-
+ CameraMetadata captureResult;
captureResult = result->result;
+
if (captureResult.update(ANDROID_REQUEST_FRAME_COUNT,
(int32_t*)&frameNumber, 1) != OK) {
SET_ERR("Failed to set frame# in metadata (%d)",
frameNumber);
+ gotResult = false;
} else {
ALOGVV("%s: Camera %d: Set frame# in metadata (%d)",
__FUNCTION__, mId, frameNumber);
}
+ // Append any previous partials to form a complete result
+ if (mUsePartialResultQuirk && !collectedQuirkResult.isEmpty()) {
+ captureResult.append(collectedQuirkResult);
+ }
+
+ captureResult.sort();
+
// Check that there's a timestamp in the result metadata
camera_metadata_entry entry =
@@ -1300,10 +1737,19 @@
if (entry.count == 0) {
SET_ERR("No timestamp provided by HAL for frame %d!",
frameNumber);
+ gotResult = false;
} else if (timestamp != entry.data.i64[0]) {
SET_ERR("Timestamp mismatch between shutter notify and result"
" metadata for frame %d (%lld vs %lld respectively)",
frameNumber, timestamp, entry.data.i64[0]);
+ gotResult = false;
+ }
+
+ if (gotResult) {
+ // Valid result, insert into queue
+ CameraMetadata& queuedResult =
+ *mResultQueue.insert(mResultQueue.end(), CameraMetadata());
+ queuedResult.swap(captureResult);
}
} // scope for mOutputLock
@@ -1316,14 +1762,14 @@
// Note: stream may be deallocated at this point, if this buffer was the
// last reference to it.
if (res != OK) {
- SET_ERR("Can't return buffer %d for frame %d to its stream: "
+ ALOGE("Can't return buffer %d for frame %d to its stream: "
" %s (%d)", i, frameNumber, strerror(-res), res);
}
}
// Finally, signal any waiters for new frames
- if (result->result != NULL) {
+ if (gotResult) {
mResultSignal.signal();
}
@@ -1356,6 +1802,16 @@
ALOGV("Camera %d: %s: HAL error, frame %d, stream %d: %d",
mId, __FUNCTION__, msg->message.error.frame_number,
streamId, msg->message.error.error_code);
+
+ // Set request error status for the request in the in-flight tracking
+ {
+ Mutex::Autolock l(mInFlightLock);
+ ssize_t idx = mInFlightMap.indexOfKey(msg->message.error.frame_number);
+ if (idx >= 0) {
+ mInFlightMap.editValueAt(idx).requestStatus = msg->message.error.error_code;
+ }
+ }
+
if (listener != NULL) {
listener->notifyError(msg->message.error.error_code,
msg->message.error.frame_number, streamId);
@@ -1378,12 +1834,17 @@
mNextShutterFrameNumber++;
}
+ int32_t requestId = -1;
+
// Set timestamp for the request in the in-flight tracking
+ // and get the request ID to send upstream
{
Mutex::Autolock l(mInFlightLock);
idx = mInFlightMap.indexOfKey(frameNumber);
if (idx >= 0) {
- mInFlightMap.editValueAt(idx).captureTimestamp = timestamp;
+ InFlightRequest &r = mInFlightMap.editValueAt(idx);
+ r.captureTimestamp = timestamp;
+ requestId = r.requestId;
}
}
if (idx < 0) {
@@ -1391,11 +1852,11 @@
frameNumber);
break;
}
- ALOGVV("Camera %d: %s: Shutter fired for frame %d at %lld",
- mId, __FUNCTION__, frameNumber, timestamp);
+ ALOGVV("Camera %d: %s: Shutter fired for frame %d (id %d) at %lld",
+ mId, __FUNCTION__, frameNumber, requestId, timestamp);
// Call listener, if any
if (listener != NULL) {
- listener->notifyShutter(frameNumber, timestamp);
+ listener->notifyShutter(requestId, timestamp);
}
break;
}
@@ -1405,40 +1866,15 @@
}
}
-CameraMetadata Camera3Device::getLatestRequest() {
+CameraMetadata Camera3Device::getLatestRequestLocked() {
ALOGV("%s", __FUNCTION__);
- bool locked = false;
-
- /**
- * Why trylock instead of autolock?
- *
- * We want to be able to call this function from
- * dumpsys, which often happens during deadlocks.
- */
- for (size_t i = 0; i < kDumpLockAttempts; ++i) {
- if (mLock.tryLock() == NO_ERROR) {
- locked = true;
- break;
- } else {
- usleep(kDumpSleepDuration);
- }
- }
-
- if (!locked) {
- ALOGW("%s: Possible deadlock detected", __FUNCTION__);
- }
-
CameraMetadata retVal;
if (mRequestThread != NULL) {
retVal = mRequestThread->getLatestRequest();
}
- if (locked) {
- mLock.unlock();
- }
-
return retVal;
}
@@ -1447,9 +1883,11 @@
*/
Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
+ sp<StatusTracker> statusTracker,
camera3_device_t *hal3Device) :
Thread(false),
mParent(parent),
+ mStatusTracker(statusTracker),
mHal3Device(hal3Device),
mId(getId(parent)),
mReconfigured(false),
@@ -1457,6 +1895,7 @@
mPaused(true),
mFrameNumber(0),
mLatestRequestId(NAME_NOT_FOUND) {
+ mStatusId = statusTracker->addComponent();
}
void Camera3Device::RequestThread::configurationComplete() {
@@ -1562,19 +2001,6 @@
mDoPauseSignal.signal();
}
-status_t Camera3Device::RequestThread::waitUntilPaused(nsecs_t timeout) {
- ATRACE_CALL();
- status_t res;
- Mutex::Autolock l(mPauseLock);
- while (!mPaused) {
- res = mPausedSignal.waitRelative(mPauseLock, timeout);
- if (res == TIMED_OUT) {
- return res;
- }
- }
- return OK;
-}
-
status_t Camera3Device::RequestThread::waitUntilRequestProcessed(
int32_t requestId, nsecs_t timeout) {
Mutex::Autolock l(mLatestRequestMutex);
@@ -1591,7 +2017,13 @@
return OK;
}
-
+void Camera3Device::RequestThread::requestExit() {
+ // Call parent to set up shutdown
+ Thread::requestExit();
+ // The exit from any possible waits
+ mDoPauseSignal.signal();
+ mRequestSignal.signal();
+}
bool Camera3Device::RequestThread::threadLoop() {
@@ -1613,6 +2045,18 @@
camera3_capture_request_t request = camera3_capture_request_t();
Vector<camera3_stream_buffer_t> outputBuffers;
+ // Get the request ID, if any
+ int requestId;
+ camera_metadata_entry_t requestIdEntry =
+ nextRequest->mSettings.find(ANDROID_REQUEST_ID);
+ if (requestIdEntry.count > 0) {
+ requestId = requestIdEntry.data.i32[0];
+ } else {
+ ALOGW("%s: Did not have android.request.id set in the request",
+ __FUNCTION__);
+ requestId = NAME_NOT_FOUND;
+ }
+
// Insert any queued triggers (before metadata is locked)
int32_t triggerCount;
res = insertTriggers(nextRequest);
@@ -1679,7 +2123,7 @@
request.input_buffer = &inputBuffer;
res = nextRequest->mInputStream->getInputBuffer(&inputBuffer);
if (res != OK) {
- SET_ERR("RequestThread: Can't get input buffer, skipping request:"
+ ALOGE("RequestThread: Can't get input buffer, skipping request:"
" %s (%d)", strerror(-res), res);
cleanUpFailedRequest(request, nextRequest, outputBuffers);
return true;
@@ -1695,8 +2139,8 @@
res = nextRequest->mOutputStreams.editItemAt(i)->
getBuffer(&outputBuffers.editItemAt(i));
if (res != OK) {
- SET_ERR("RequestThread: Can't get output buffer, skipping request:"
- "%s (%d)", strerror(-res), res);
+ ALOGE("RequestThread: Can't get output buffer, skipping request:"
+ " %s (%d)", strerror(-res), res);
cleanUpFailedRequest(request, nextRequest, outputBuffers);
return true;
}
@@ -1713,7 +2157,7 @@
return false;
}
- res = parent->registerInFlight(request.frame_number,
+ res = parent->registerInFlight(request.frame_number, requestId,
request.num_output_buffers);
if (res != OK) {
SET_ERR("RequestThread: Unable to register new in-flight request:"
@@ -1722,6 +2166,14 @@
return false;
}
+ // Inform waitUntilRequestProcessed thread of a new request ID
+ {
+ Mutex::Autolock al(mLatestRequestMutex);
+
+ mLatestRequestId = requestId;
+ mLatestRequestSignal.signal();
+ }
+
// Submit request and block until ready for next one
ATRACE_ASYNC_BEGIN("frame capture", request.frame_number);
ATRACE_BEGIN("camera3->process_capture_request");
@@ -1757,24 +2209,6 @@
}
mPrevTriggers = triggerCount;
- // Read android.request.id from the request settings metadata
- // - inform waitUntilRequestProcessed thread of a new request ID
- {
- Mutex::Autolock al(mLatestRequestMutex);
-
- camera_metadata_entry_t requestIdEntry =
- nextRequest->mSettings.find(ANDROID_REQUEST_ID);
- if (requestIdEntry.count > 0) {
- mLatestRequestId = requestIdEntry.data.i32[0];
- } else {
- ALOGW("%s: Did not have android.request.id set in the request",
- __FUNCTION__);
- mLatestRequestId = NAME_NOT_FOUND;
- }
-
- mLatestRequestSignal.signal();
- }
-
// Return input buffer back to framework
if (request.input_buffer != NULL) {
Camera3Stream *stream =
@@ -1790,8 +2224,6 @@
}
}
-
-
return true;
}
@@ -1849,12 +2281,17 @@
res = mRequestSignal.waitRelative(mRequestLock, kRequestTimeout);
- if (res == TIMED_OUT) {
- // Signal that we're paused by starvation
+ if ((mRequestQueue.empty() && mRepeatingRequests.empty()) ||
+ exitPending()) {
Mutex::Autolock pl(mPauseLock);
if (mPaused == false) {
+ ALOGV("%s: RequestThread: Going idle", __FUNCTION__);
mPaused = true;
- mPausedSignal.signal();
+ // Let the tracker know
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+ }
}
// Stop waiting for now and let thread management happen
return NULL;
@@ -1874,6 +2311,13 @@
// update internal pause state (capture/setRepeatingRequest unpause
// directly).
Mutex::Autolock pl(mPauseLock);
+ if (mPaused) {
+ ALOGV("%s: RequestThread: Unpaused", __FUNCTION__);
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentActive(mStatusId);
+ }
+ }
mPaused = false;
// Check if we've reconfigured since last time, and reset the preview
@@ -1890,13 +2334,18 @@
status_t res;
Mutex::Autolock l(mPauseLock);
while (mDoPause) {
- // Signal that we're paused by request
if (mPaused == false) {
mPaused = true;
- mPausedSignal.signal();
+ ALOGV("%s: RequestThread: Paused", __FUNCTION__);
+ // Let the tracker know
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+ }
}
+
res = mDoPauseSignal.waitRelative(mPauseLock, kRequestTimeout);
- if (res == TIMED_OUT) {
+ if (res == TIMED_OUT || exitPending()) {
return true;
}
}
@@ -1909,8 +2358,16 @@
// With work to do, mark thread as unpaused.
// If paused by request (setPaused), don't resume, to avoid
// extra signaling/waiting overhead to waitUntilPaused
+ mRequestSignal.signal();
Mutex::Autolock p(mPauseLock);
if (!mDoPause) {
+ ALOGV("%s: RequestThread: Going active", __FUNCTION__);
+ if (mPaused) {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentActive(mStatusId);
+ }
+ }
mPaused = false;
}
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 61caf13..468f641 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -26,6 +26,7 @@
#include <hardware/camera3.h>
#include "common/CameraDeviceBase.h"
+#include "device3/StatusTracker.h"
/**
* Function pointer types with C calling convention to
@@ -126,29 +127,47 @@
virtual status_t flush();
+ // Methods called by subclasses
+ void notifyStatus(bool idle); // updates from StatusTracker
+
private:
static const size_t kDumpLockAttempts = 10;
static const size_t kDumpSleepDuration = 100000; // 0.10 sec
static const size_t kInFlightWarnLimit = 20;
static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec
+ static const nsecs_t kActiveTimeout = 500000000; // 500 ms
struct RequestTrigger;
+ // A lock to enforce serialization on the input/configure side
+ // of the public interface.
+ // Only locked by public methods inherited from CameraDeviceBase.
+ // Not locked by methods guarded by mOutputLock, since they may act
+ // concurrently to the input/configure side of the interface.
+ // Must be locked before mLock if both will be locked by a method
+ Mutex mInterfaceLock;
+
+ // The main lock on internal state
Mutex mLock;
+ // Camera device ID
+ const int mId;
+
/**** Scope for mLock ****/
- const int mId;
camera3_device_t *mHal3Device;
CameraMetadata mDeviceInfo;
vendor_tag_query_ops_t mVendorTagOps;
- enum {
+ enum Status {
STATUS_ERROR,
STATUS_UNINITIALIZED,
- STATUS_IDLE,
+ STATUS_UNCONFIGURED,
+ STATUS_CONFIGURED,
STATUS_ACTIVE
} mStatus;
+ Vector<Status> mRecentStatusUpdates;
+ Condition mStatusChanged;
// Tracking cause of fatal errors when in STATUS_ERROR
String8 mErrorCause;
@@ -162,9 +181,16 @@
int mNextStreamId;
bool mNeedConfig;
+ // Whether to send state updates upstream
+ // Pause when doing transparent reconfiguration
+ bool mPauseStateNotify;
+
// Need to hold on to stream references until configure completes.
Vector<sp<camera3::Camera3StreamInterface> > mDeletedStreams;
+ // Whether quirk ANDROID_QUIRKS_USE_PARTIAL_RESULT is enabled
+ bool mUsePartialResultQuirk;
+
/**** End scope for mLock ****/
class CaptureRequest : public LightRefBase<CaptureRequest> {
@@ -181,13 +207,34 @@
*
* Takes mLock.
*/
- virtual CameraMetadata getLatestRequest();
+ virtual CameraMetadata getLatestRequestLocked();
/**
- * Lock-held version of waitUntilDrained. Will transition to IDLE on
- * success.
+ * Pause processing and flush everything, but don't tell the clients.
+ * This is for reconfiguring outputs transparently when according to the
+ * CameraDeviceBase interface we shouldn't need to.
+ * Must be called with mLock and mInterfaceLock both held.
*/
- status_t waitUntilDrainedLocked();
+ status_t internalPauseAndWaitLocked();
+
+ /**
+ * Resume work after internalPauseAndWaitLocked()
+ * Must be called with mLock and mInterfaceLock both held.
+ */
+ status_t internalResumeLocked();
+
+ /**
+ * Wait until status tracker tells us we've transitioned to the target state
+ * set, which is either ACTIVE when active==true or IDLE (which is any
+ * non-ACTIVE state) when active==false.
+ *
+ * Needs to be called with mLock and mInterfaceLock held. This means there
+ * can ever only be one waiter at most.
+ *
+ * During the wait mLock is released.
+ *
+ */
+ status_t waitUntilStateThenRelock(bool active, nsecs_t timeout);
/**
* Do common work for setting up a streaming or single capture request.
@@ -217,6 +264,12 @@
void setErrorStateLocked(const char *fmt, ...);
void setErrorStateLockedV(const char *fmt, va_list args);
+ /**
+ * Debugging trylock/spin method
+ * Try to acquire a lock a few times with sleeps between before giving up.
+ */
+ bool tryLockSpinRightRound(Mutex& lock);
+
struct RequestTrigger {
// Metadata tag number, e.g. android.control.aePrecaptureTrigger
uint32_t metadataTag;
@@ -242,6 +295,7 @@
public:
RequestThread(wp<Camera3Device> parent,
+ sp<camera3::StatusTracker> statusTracker,
camera3_device_t *hal3Device);
/**
@@ -279,13 +333,6 @@
void setPaused(bool paused);
/**
- * Wait until thread is paused, either due to setPaused(true)
- * or due to lack of input requests. Returns TIMED_OUT in case
- * the thread does not pause within the timeout.
- */
- status_t waitUntilPaused(nsecs_t timeout);
-
- /**
* Wait until thread processes the capture request with settings'
* android.request.id == requestId.
*
@@ -295,6 +342,12 @@
status_t waitUntilRequestProcessed(int32_t requestId, nsecs_t timeout);
/**
+ * Shut down the thread. Shutdown is asynchronous, so thread may
+ * still be running once this method returns.
+ */
+ virtual void requestExit();
+
+ /**
* Get the latest request that was sent to the HAL
* with process_capture_request.
*/
@@ -339,9 +392,12 @@
void setErrorState(const char *fmt, ...);
wp<Camera3Device> mParent;
+ wp<camera3::StatusTracker> mStatusTracker;
camera3_device_t *mHal3Device;
- const int mId;
+ const int mId; // The camera ID
+ int mStatusId; // The RequestThread's component ID for
+ // status tracking
Mutex mRequestLock;
Condition mRequestSignal;
@@ -381,22 +437,42 @@
*/
struct InFlightRequest {
+ // android.request.id for the request
+ int requestId;
// Set by notify() SHUTTER call.
nsecs_t captureTimestamp;
+ int requestStatus;
// Set by process_capture_result call with valid metadata
bool haveResultMetadata;
// Decremented by calls to process_capture_result with valid output
// buffers
int numBuffersLeft;
+ // Fields used by the partial result quirk only
+ struct PartialResultQuirkInFlight {
+ // Set by process_capture_result once 3A has been sent to clients
+ bool haveSent3A;
+ // Result metadata collected so far, when partial results are in use
+ CameraMetadata collectedResult;
+
+ PartialResultQuirkInFlight():
+ haveSent3A(false) {
+ }
+ } partialResultQuirk;
+
+ // Default constructor needed by KeyedVector
InFlightRequest() :
+ requestId(0),
captureTimestamp(0),
+ requestStatus(OK),
haveResultMetadata(false),
numBuffersLeft(0) {
}
- explicit InFlightRequest(int numBuffers) :
+ InFlightRequest(int id, int numBuffers) :
+ requestId(id),
captureTimestamp(0),
+ requestStatus(OK),
haveResultMetadata(false),
numBuffersLeft(numBuffers) {
}
@@ -407,7 +483,29 @@
Mutex mInFlightLock; // Protects mInFlightMap
InFlightMap mInFlightMap;
- status_t registerInFlight(int32_t frameNumber, int32_t numBuffers);
+ status_t registerInFlight(int32_t frameNumber, int32_t requestId,
+ int32_t numBuffers);
+
+ /**
+ * For the partial result quirk, check if all 3A state fields are available
+ * and if so, queue up 3A-only result to the client. Returns true if 3A
+ * is sent.
+ */
+ bool processPartial3AQuirk(int32_t frameNumber, int32_t requestId,
+ const CameraMetadata& partial);
+
+ // Helpers for reading and writing 3A metadata into to/from partial results
+ template<typename T>
+ bool get3AResult(const CameraMetadata& result, int32_t tag,
+ T* value, int32_t frameNumber);
+
+ template<typename T>
+ bool insert3AResult(CameraMetadata &result, int32_t tag, const T* value,
+ int32_t frameNumber);
+ /**
+ * Tracking for idle detection
+ */
+ sp<camera3::StatusTracker> mStatusTracker;
/**
* Output result queue and current HAL device 3A state
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 0850566..42e02d8 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -23,7 +23,8 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-#include "Camera3IOStreamBase.h"
+#include "device3/Camera3IOStreamBase.h"
+#include "device3/StatusTracker.h"
namespace android {
@@ -62,53 +63,6 @@
return false;
}
-status_t Camera3IOStreamBase::waitUntilIdle(nsecs_t timeout) {
- status_t res;
- {
- Mutex::Autolock l(mLock);
- while (mDequeuedBufferCount > 0) {
- if (timeout != TIMEOUT_NEVER) {
- nsecs_t startTime = systemTime();
- res = mBufferReturnedSignal.waitRelative(mLock, timeout);
- if (res == TIMED_OUT) {
- return res;
- } else if (res != OK) {
- ALOGE("%s: Error waiting for outstanding buffers: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return res;
- }
- nsecs_t deltaTime = systemTime() - startTime;
- if (timeout <= deltaTime) {
- timeout = 0;
- } else {
- timeout -= deltaTime;
- }
- } else {
- res = mBufferReturnedSignal.wait(mLock);
- if (res != OK) {
- ALOGE("%s: Error waiting for outstanding buffers: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return res;
- }
- }
- }
- }
-
- // No lock
-
- unsigned int timeoutMs;
- if (timeout == TIMEOUT_NEVER) {
- timeoutMs = Fence::TIMEOUT_NEVER;
- } else if (timeout == 0) {
- timeoutMs = 0;
- } else {
- // Round up to wait at least 1 ms
- timeoutMs = (timeout + 999999) / 1000000;
- }
-
- return mCombinedFence->wait(timeoutMs);
-}
-
void Camera3IOStreamBase::dump(int fd, const Vector<String16> &args) const {
(void) args;
String8 lines;
@@ -116,12 +70,12 @@
lines.appendFormat(" Dims: %d x %d, format 0x%x\n",
camera3_stream::width, camera3_stream::height,
camera3_stream::format);
- lines.appendFormat(" Max size: %d\n", mMaxSize);
+ lines.appendFormat(" Max size: %zu\n", mMaxSize);
lines.appendFormat(" Usage: %d, max HAL buffers: %d\n",
camera3_stream::usage, camera3_stream::max_buffers);
lines.appendFormat(" Frames produced: %d, last timestamp: %lld ns\n",
mFrameCount, mLastTimestamp);
- lines.appendFormat(" Total buffers: %d, currently dequeued: %d\n",
+ lines.appendFormat(" Total buffers: %zu, currently dequeued: %zu\n",
mTotalBufferCount, mDequeuedBufferCount);
write(fd, lines.string(), lines.size());
}
@@ -190,6 +144,14 @@
buffer.release_fence = releaseFence;
buffer.status = status;
+ // Inform tracker about becoming busy
+ if (mDequeuedBufferCount == 0 && mState != STATE_IN_CONFIG &&
+ mState != STATE_IN_RECONFIG) {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentActive(mStatusId);
+ }
+ }
mDequeuedBufferCount++;
}
@@ -252,20 +214,32 @@
sp<Fence> releaseFence;
res = returnBufferCheckedLocked(buffer, timestamp, output,
&releaseFence);
- if (res != OK) {
- return res;
+ // Res may be an error, but we still want to decrement our owned count
+ // to enable clean shutdown. So we'll just return the error but otherwise
+ // carry on
+
+ if (releaseFence != 0) {
+ mCombinedFence = Fence::merge(mName, mCombinedFence, releaseFence);
}
- mCombinedFence = Fence::merge(mName, mCombinedFence, releaseFence);
-
mDequeuedBufferCount--;
+ if (mDequeuedBufferCount == 0 && mState != STATE_IN_CONFIG &&
+ mState != STATE_IN_RECONFIG) {
+ ALOGV("%s: Stream %d: All buffers returned; now idle", __FUNCTION__,
+ mId);
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentIdle(mStatusId, mCombinedFence);
+ }
+ }
+
mBufferReturnedSignal.signal();
if (output) {
mLastTimestamp = timestamp;
}
- return OK;
+ return res;
}
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 9432a59..fcb9d04 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -43,7 +43,6 @@
* Camera3Stream interface
*/
- virtual status_t waitUntilIdle(nsecs_t timeout);
virtual void dump(int fd, const Vector<String16> &args) const;
protected:
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index c80f512..e1c492b 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -115,7 +115,6 @@
bufferFound = true;
bufferItem = tmp;
mBuffersInFlight.erase(it);
- mDequeuedBufferCount--;
}
}
}
@@ -148,12 +147,11 @@
if (res != OK) {
ALOGE("%s: Stream %d: Error releasing buffer back to buffer queue:"
" %s (%d)", __FUNCTION__, mId, strerror(-res), res);
- return res;
}
*releaseFenceOut = releaseFence;
- return OK;
+ return res;
}
status_t Camera3InputStream::returnInputBufferLocked(
@@ -201,13 +199,33 @@
assert(mMaxSize == 0);
assert(camera3_stream::format != HAL_PIXEL_FORMAT_BLOB);
- mTotalBufferCount = BufferQueue::MIN_UNDEQUEUED_BUFFERS +
- camera3_stream::max_buffers;
mDequeuedBufferCount = 0;
mFrameCount = 0;
if (mConsumer.get() == 0) {
sp<BufferQueue> bq = new BufferQueue();
+
+ int minUndequeuedBuffers = 0;
+ res = bq->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);
+ if (res != OK || minUndequeuedBuffers < 0) {
+ ALOGE("%s: Stream %d: Could not query min undequeued buffers (error %d, bufCount %d)",
+ __FUNCTION__, mId, res, minUndequeuedBuffers);
+ return res;
+ }
+ size_t minBufs = static_cast<size_t>(minUndequeuedBuffers);
+ /*
+ * We promise never to 'acquire' more than camera3_stream::max_buffers
+ * at any one time.
+ *
+ * Boost the number up to meet the minimum required buffer count.
+ *
+ * (Note that this sets consumer-side buffer count only,
+ * and not the sum of producer+consumer side as in other camera streams).
+ */
+ mTotalBufferCount = camera3_stream::max_buffers > minBufs ?
+ camera3_stream::max_buffers : minBufs;
+ // TODO: somehow set the total buffer count when producer connects?
+
mConsumer = new BufferItemConsumer(bq, camera3_stream::usage,
mTotalBufferCount);
mConsumer->setName(String8::format("Camera3-InputStream-%d", mId));
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index 681d684..ae49467 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -44,6 +44,8 @@
virtual void dump(int fd, const Vector<String16> &args) const;
+ // TODO: expose an interface to get the IGraphicBufferProducer
+
private:
typedef BufferItemConsumer::BufferItem BufferItem;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 35cb5ba..682755d 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -92,7 +92,22 @@
ANativeWindowBuffer* anb;
int fenceFd;
- res = mConsumer->dequeueBuffer(mConsumer.get(), &anb, &fenceFd);
+ /**
+ * Release the lock briefly to avoid deadlock for below scenario:
+ * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
+ * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
+ * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
+ * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
+ * StreamingProcessor lock.
+ * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
+ * and try to lock bufferQueue lock.
+ * Then there is circular locking dependency.
+ */
+ sp<ANativeWindow> currentConsumer = mConsumer;
+ mLock.unlock();
+
+ res = currentConsumer->dequeueBuffer(currentConsumer.get(), &anb, &fenceFd);
+ mLock.lock();
if (res != OK) {
ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
@@ -198,12 +213,11 @@
mLock.lock();
if (res != OK) {
close(anwReleaseFence);
- return res;
}
*releaseFenceOut = releaseFence;
- return OK;
+ return res;
}
void Camera3OutputStream::dump(int fd, const Vector<String16> &args) const {
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index a6872aa..6d2cf94 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -20,13 +20,18 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-#include "Camera3Stream.h"
+#include "device3/Camera3Stream.h"
+#include "device3/StatusTracker.h"
namespace android {
namespace camera3 {
Camera3Stream::~Camera3Stream() {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0 && mStatusId != StatusTracker::NO_STATUS_ID) {
+ statusTracker->removeComponent(mStatusId);
+ }
}
Camera3Stream* Camera3Stream::cast(camera3_stream *stream) {
@@ -44,7 +49,8 @@
mId(id),
mName(String8::format("Camera3Stream[%d]", id)),
mMaxSize(maxSize),
- mState(STATE_CONSTRUCTED) {
+ mState(STATE_CONSTRUCTED),
+ mStatusId(StatusTracker::NO_STATUS_ID) {
camera3_stream::stream_type = type;
camera3_stream::width = width;
@@ -119,6 +125,15 @@
return NULL;
}
+ // Stop tracking if currently doing so
+ if (mStatusId != StatusTracker::NO_STATUS_ID) {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->removeComponent(mStatusId);
+ }
+ mStatusId = StatusTracker::NO_STATUS_ID;
+ }
+
if (mState == STATE_CONSTRUCTED) {
mState = STATE_IN_CONFIG;
} else { // mState == STATE_CONFIGURED
@@ -154,6 +169,12 @@
return INVALID_OPERATION;
}
+ // Register for idle tracking
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ mStatusId = statusTracker->addComponent();
+ }
+
// Check if the stream configuration is unchanged, and skip reallocation if
// so. As documented in hardware/camera3.h:configure_streams().
if (mState == STATE_IN_RECONFIG &&
@@ -265,6 +286,18 @@
return hasOutstandingBuffersLocked();
}
+status_t Camera3Stream::setStatusTracker(sp<StatusTracker> statusTracker) {
+ Mutex::Autolock l(mLock);
+ sp<StatusTracker> oldTracker = mStatusTracker.promote();
+ if (oldTracker != 0 && mStatusId != StatusTracker::NO_STATUS_ID) {
+ oldTracker->removeComponent(mStatusId);
+ }
+ mStatusId = StatusTracker::NO_STATUS_ID;
+ mStatusTracker = statusTracker;
+
+ return OK;
+}
+
status_t Camera3Stream::disconnect() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index b64fd86..6eeb721 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -190,12 +190,11 @@
enum {
TIMEOUT_NEVER = -1
};
+
/**
- * Wait until the HAL is done with all of this stream's buffers, including
- * signalling all release fences. Returns TIMED_OUT if the timeout is exceeded,
- * OK on success. Pass in TIMEOUT_NEVER for timeout to indicate an indefinite wait.
+ * Set the status tracker to notify about idle transitions
*/
- virtual status_t waitUntilIdle(nsecs_t timeout) = 0;
+ virtual status_t setStatusTracker(sp<StatusTracker> statusTracker);
/**
* Disconnect stream from its non-HAL endpoint. After this,
@@ -267,6 +266,11 @@
// INVALID_OPERATION if they cannot be obtained.
virtual status_t getEndpointUsage(uint32_t *usage) = 0;
+ // Tracking for idle state
+ wp<StatusTracker> mStatusTracker;
+ // Status tracker component ID
+ int mStatusId;
+
private:
uint32_t oldUsage;
uint32_t oldMaxBuffers;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 4768536..c93ae15 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -26,6 +26,8 @@
namespace camera3 {
+class StatusTracker;
+
/**
* An interface for managing a single stream of input and/or output data from
* the camera device.
@@ -128,13 +130,11 @@
enum {
TIMEOUT_NEVER = -1
};
+
/**
- * Wait until the HAL is done with all of this stream's buffers, including
- * signalling all release fences. Returns TIMED_OUT if the timeout is
- * exceeded, OK on success. Pass in TIMEOUT_NEVER for timeout to indicate
- * an indefinite wait.
+ * Set the state tracker to use for signaling idle transitions.
*/
- virtual status_t waitUntilIdle(nsecs_t timeout) = 0;
+ virtual status_t setStatusTracker(sp<StatusTracker> statusTracker) = 0;
/**
* Disconnect stream from its non-HAL endpoint. After this,
diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
index 04f5dc5..e509350 100644
--- a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
@@ -109,14 +109,14 @@
} // namespace anonymous
Camera3ZslStream::Camera3ZslStream(int id, uint32_t width, uint32_t height,
- int depth) :
+ int bufferCount) :
Camera3OutputStream(id, CAMERA3_STREAM_BIDIRECTIONAL,
width, height,
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
- mDepth(depth) {
+ mDepth(bufferCount) {
sp<BufferQueue> bq = new BufferQueue();
- mProducer = new RingBufferConsumer(bq, GRALLOC_USAGE_HW_CAMERA_ZSL, depth);
+ mProducer = new RingBufferConsumer(bq, GRALLOC_USAGE_HW_CAMERA_ZSL, bufferCount);
mConsumer = new Surface(bq);
}
@@ -271,7 +271,7 @@
Camera3IOStreamBase::dump(fd, args);
lines = String8();
- lines.appendFormat(" Input buffers pending: %d, in flight %d\n",
+ lines.appendFormat(" Input buffers pending: %zu, in flight %zu\n",
mInputBufferQueue.size(), mBuffersInFlight.size());
write(fd, lines.string(), lines.size());
}
diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.h b/services/camera/libcameraservice/device3/Camera3ZslStream.h
index c7f4490..6721832 100644
--- a/services/camera/libcameraservice/device3/Camera3ZslStream.h
+++ b/services/camera/libcameraservice/device3/Camera3ZslStream.h
@@ -37,10 +37,10 @@
public Camera3OutputStream {
public:
/**
- * Set up a ZSL stream of a given resolution. Depth is the number of buffers
+ * Set up a ZSL stream of a given resolution. bufferCount is the number of buffers
* cached within the stream that can be retrieved for input.
*/
- Camera3ZslStream(int id, uint32_t width, uint32_t height, int depth);
+ Camera3ZslStream(int id, uint32_t width, uint32_t height, int bufferCount);
~Camera3ZslStream();
virtual void dump(int fd, const Vector<String16> &args) const;
diff --git a/services/camera/libcameraservice/device3/StatusTracker.cpp b/services/camera/libcameraservice/device3/StatusTracker.cpp
new file mode 100644
index 0000000..ab5419f
--- /dev/null
+++ b/services/camera/libcameraservice/device3/StatusTracker.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2013 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 "Camera3-Status"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+// This is needed for stdint.h to define INT64_MAX in C++
+#define __STDC_LIMIT_MACROS
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <ui/Fence.h>
+
+#include "device3/StatusTracker.h"
+#include "device3/Camera3Device.h"
+
+namespace android {
+
+namespace camera3 {
+
+StatusTracker::StatusTracker(wp<Camera3Device> parent) :
+ mComponentsChanged(false),
+ mParent(parent),
+ mNextComponentId(0),
+ mIdleFence(new Fence()),
+ mDeviceState(IDLE) {
+}
+
+StatusTracker::~StatusTracker() {
+}
+
+int StatusTracker::addComponent() {
+ int id;
+ ssize_t err;
+ {
+ Mutex::Autolock l(mLock);
+ id = mNextComponentId++;
+ ALOGV("%s: Adding new component %d", __FUNCTION__, id);
+
+ err = mStates.add(id, IDLE);
+ ALOGE_IF(err < 0, "%s: Can't add new component %d: %s (%d)",
+ __FUNCTION__, id, strerror(-err), err);
+ }
+
+ if (err >= 0) {
+ Mutex::Autolock pl(mPendingLock);
+ mComponentsChanged = true;
+ mPendingChangeSignal.signal();
+ }
+
+ return err < 0 ? err : id;
+}
+
+void StatusTracker::removeComponent(int id) {
+ ssize_t idx;
+ {
+ Mutex::Autolock l(mLock);
+ ALOGV("%s: Removing component %d", __FUNCTION__, id);
+ idx = mStates.removeItem(id);
+ }
+
+ if (idx >= 0) {
+ Mutex::Autolock pl(mPendingLock);
+ mComponentsChanged = true;
+ mPendingChangeSignal.signal();
+ }
+
+ return;
+}
+
+
+void StatusTracker::markComponentIdle(int id, const sp<Fence>& componentFence) {
+ markComponent(id, IDLE, componentFence);
+}
+
+void StatusTracker::markComponentActive(int id) {
+ markComponent(id, ACTIVE, Fence::NO_FENCE);
+}
+
+void StatusTracker::markComponent(int id, ComponentState state,
+ const sp<Fence>& componentFence) {
+ ALOGV("%s: Component %d is now %s", __FUNCTION__, id,
+ state == IDLE ? "idle" : "active");
+ Mutex::Autolock l(mPendingLock);
+
+ StateChange newState = {
+ id,
+ state,
+ componentFence
+ };
+
+ mPendingChangeQueue.add(newState);
+ mPendingChangeSignal.signal();
+}
+
+void StatusTracker::requestExit() {
+ // First mark thread dead
+ Thread::requestExit();
+ // Then exit any waits
+ mPendingChangeSignal.signal();
+}
+
+StatusTracker::ComponentState StatusTracker::getDeviceStateLocked() {
+ for (size_t i = 0; i < mStates.size(); i++) {
+ if (mStates.valueAt(i) == ACTIVE) {
+ ALOGV("%s: Component %d not idle", __FUNCTION__,
+ mStates.keyAt(i));
+ return ACTIVE;
+ }
+ }
+ // - If not yet signaled, getSignalTime returns INT64_MAX
+ // - If invalid fence or error, returns -1
+ // - Otherwise returns time of signalling.
+ // Treat -1 as 'signalled', since HAL may not be using fences, and want
+ // to be able to idle in case of errors.
+ nsecs_t signalTime = mIdleFence->getSignalTime();
+ bool fencesDone = signalTime != INT64_MAX;
+
+ ALOGV_IF(!fencesDone, "%s: Fences still to wait on", __FUNCTION__);
+
+ return fencesDone ? IDLE : ACTIVE;
+}
+
+bool StatusTracker::threadLoop() {
+ status_t res;
+
+ // Wait for state updates
+ {
+ Mutex::Autolock pl(mPendingLock);
+ while (mPendingChangeQueue.size() == 0 && !mComponentsChanged) {
+ res = mPendingChangeSignal.waitRelative(mPendingLock,
+ kWaitDuration);
+ if (exitPending()) return false;
+ if (res != OK) {
+ if (res != TIMED_OUT) {
+ ALOGE("%s: Error waiting on state changes: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ }
+ // TIMED_OUT is expected
+ break;
+ }
+ }
+ }
+
+ // After new pending states appear, or timeout, check if we're idle. Even
+ // with timeout, need to check to account for fences that may still be
+ // clearing out
+ sp<Camera3Device> parent;
+ {
+ Mutex::Autolock pl(mPendingLock);
+ Mutex::Autolock l(mLock);
+
+ // Collect all pending state updates and see if the device
+ // collectively transitions between idle and active for each one
+
+ // First pass for changed components or fence completions
+ ComponentState prevState = getDeviceStateLocked();
+ if (prevState != mDeviceState) {
+ // Only collect changes to overall device state
+ mStateTransitions.add(prevState);
+ }
+ // For each pending component state update, check if we've transitioned
+ // to a new overall device state
+ for (size_t i = 0; i < mPendingChangeQueue.size(); i++) {
+ const StateChange &newState = mPendingChangeQueue[i];
+ ssize_t idx = mStates.indexOfKey(newState.id);
+ // Ignore notices for unknown components
+ if (idx >= 0) {
+ // Update single component state
+ mStates.replaceValueAt(idx, newState.state);
+ mIdleFence = Fence::merge(String8("idleFence"),
+ mIdleFence, newState.fence);
+ // .. and see if overall device state has changed
+ ComponentState newState = getDeviceStateLocked();
+ if (newState != prevState) {
+ mStateTransitions.add(newState);
+ }
+ prevState = newState;
+ }
+ }
+ mPendingChangeQueue.clear();
+ mComponentsChanged = false;
+
+ // Store final state after all pending state changes are done with
+
+ mDeviceState = prevState;
+ parent = mParent.promote();
+ }
+
+ // Notify parent for all intermediate transitions
+ if (mStateTransitions.size() > 0 && parent.get()) {
+ for (size_t i = 0; i < mStateTransitions.size(); i++) {
+ bool idle = (mStateTransitions[i] == IDLE);
+ ALOGV("Camera device is now %s", idle ? "idle" : "active");
+ parent->notifyStatus(idle);
+ }
+ }
+ mStateTransitions.clear();
+
+ return true;
+}
+
+} // namespace android
+
+} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/StatusTracker.h b/services/camera/libcameraservice/device3/StatusTracker.h
new file mode 100644
index 0000000..49cecb3
--- /dev/null
+++ b/services/camera/libcameraservice/device3/StatusTracker.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2013 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_SERVERS_CAMERA3_STATUSTRACKER_H
+#define ANDROID_SERVERS_CAMERA3_STATUSTRACKER_H
+
+#include <utils/Condition.h>
+#include <utils/Errors.h>
+#include <utils/List.h>
+#include <utils/Mutex.h>
+#include <utils/Thread.h>
+#include <utils/KeyedVector.h>
+#include <hardware/camera3.h>
+
+#include "common/CameraDeviceBase.h"
+
+namespace android {
+
+class Camera3Device;
+class Fence;
+
+namespace camera3 {
+
+/**
+ * State tracking for idle and other collective state transitions.
+ * Collects idle notifications from different sources and calls the
+ * parent when all of them become idle.
+ *
+ * The parent is responsible for synchronizing the status updates with its
+ * internal state correctly, which means the notifyStatus call to the parent may
+ * block for a while.
+ */
+class StatusTracker: public Thread {
+ public:
+ StatusTracker(wp<Camera3Device> parent);
+ ~StatusTracker();
+
+ // An always-invalid component ID
+ static const int NO_STATUS_ID = -1;
+
+ // Add a component to track; returns non-negative unique ID for the new
+ // component on success, negative error code on failure.
+ // New components start in the idle state.
+ int addComponent();
+
+ // Remove existing component from idle tracking. Ignores unknown IDs
+ void removeComponent(int id);
+
+ // Set the state of a tracked component to be idle. Ignores unknown IDs; can
+ // accept a fence to wait on to complete idle. The fence is merged with any
+ // previous fences given, which means they all must signal before the
+ // component is considered idle.
+ void markComponentIdle(int id, const sp<Fence>& componentFence);
+
+ // Set the state of a tracked component to be active. Ignores unknown IDs.
+ void markComponentActive(int id);
+
+ virtual void requestExit();
+ protected:
+
+ virtual bool threadLoop();
+
+ private:
+ enum ComponentState {
+ IDLE,
+ ACTIVE
+ };
+
+ void markComponent(int id, ComponentState state,
+ const sp<Fence>& componentFence);
+
+ // Guards mPendingChange, mPendingStates, mComponentsChanged
+ Mutex mPendingLock;
+
+ Condition mPendingChangeSignal;
+
+ struct StateChange {
+ int id;
+ ComponentState state;
+ sp<Fence> fence;
+ };
+ // A queue of yet-to-be-processed state changes to components
+ Vector<StateChange> mPendingChangeQueue;
+ bool mComponentsChanged;
+
+ wp<Camera3Device> mParent;
+
+ // Guards rest of internals. Must be locked after mPendingLock if both used.
+ Mutex mLock;
+
+ int mNextComponentId;
+
+ // Current component states
+ KeyedVector<int, ComponentState> mStates;
+ // Merged fence for all processed state changes
+ sp<Fence> mIdleFence;
+ // Current overall device state
+ ComponentState mDeviceState;
+
+ // Private to threadLoop
+
+ // Determine current overall device state
+ // We're IDLE iff
+ // - All components are currently IDLE
+ // - The merged fence for all component updates has signalled
+ ComponentState getDeviceStateLocked();
+
+ Vector<ComponentState> mStateTransitions;
+
+ static const nsecs_t kWaitDuration = 250000000LL; // 250 ms
+};
+
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
index ebc7ea7..9a6dc28 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
@@ -21,11 +21,11 @@
#include <gui/RingBufferConsumer.h>
-#define BI_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
-#define BI_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
-#define BI_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
-#define BI_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
-#define BI_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
#undef assert
#define assert(x) ALOG_ASSERT((x), #x)
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h
index b4ad824..a03736d 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.h
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h
@@ -64,7 +64,7 @@
// bufferCount parameter specifies how many buffers can be pinned for user
// access at the same time.
RingBufferConsumer(const sp<IGraphicBufferConsumer>& consumer, uint32_t consumerUsage,
- int bufferCount = BufferQueue::MIN_UNDEQUEUED_BUFFERS);
+ int bufferCount);
virtual ~RingBufferConsumer();
diff --git a/services/camera/libcameraservice/utils/CameraTraces.cpp b/services/camera/libcameraservice/utils/CameraTraces.cpp
new file mode 100644
index 0000000..346e15f
--- /dev/null
+++ b/services/camera/libcameraservice/utils/CameraTraces.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2013 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 "CameraTraces"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include "utils/CameraTraces.h"
+#include <utils/ProcessCallStack.h>
+
+#include <utils/Mutex.h>
+#include <utils/List.h>
+
+#include <utils/Log.h>
+#include <cutils/trace.h>
+
+namespace android {
+namespace camera3 {
+
+struct CameraTracesImpl {
+ Mutex tracesLock;
+ List<ProcessCallStack> pcsList;
+}; // class CameraTraces::Impl;
+
+static CameraTracesImpl gImpl;
+CameraTracesImpl& CameraTraces::sImpl = gImpl;
+
+void CameraTraces::saveTrace() {
+ ALOGV("%s: begin", __FUNCTION__);
+ ATRACE_BEGIN("CameraTraces::saveTrace");
+ Mutex::Autolock al(sImpl.tracesLock);
+
+ List<ProcessCallStack>& pcsList = sImpl.pcsList;
+
+ // Insert new ProcessCallStack, and immediately crawl all the threads
+ pcsList.push_front(ProcessCallStack());
+ ProcessCallStack& pcs = *pcsList.begin();
+ pcs.update();
+
+ if (pcsList.size() > MAX_TRACES) {
+ // Prune list periodically and discard oldest entry
+ pcsList.erase(--pcsList.end());
+ }
+
+ IF_ALOGV() {
+ pcs.log(LOG_TAG, ANDROID_LOG_VERBOSE);
+ }
+
+ ALOGD("Process trace saved. Use dumpsys media.camera to view.");
+
+ ATRACE_END();
+}
+
+status_t CameraTraces::dump(int fd, const Vector<String16> &args __attribute__((unused))) {
+ ALOGV("%s: fd = %d", __FUNCTION__, fd);
+ Mutex::Autolock al(sImpl.tracesLock);
+ List<ProcessCallStack>& pcsList = sImpl.pcsList;
+
+ if (fd < 0) {
+ ALOGW("%s: Negative FD (%d)", __FUNCTION__, fd);
+ return BAD_VALUE;
+ }
+
+ fdprintf(fd, "Camera traces (%zu):\n", pcsList.size());
+
+ if (pcsList.empty()) {
+ fdprintf(fd, " No camera traces collected.\n");
+ }
+
+ // Print newest items first
+ List<ProcessCallStack>::iterator it, end;
+ for (it = pcsList.begin(), end = pcsList.end(); it != end; ++it) {
+ const ProcessCallStack& pcs = *it;
+ pcs.dump(fd, DUMP_INDENT);
+ }
+
+ return OK;
+}
+
+}; // namespace camera3
+}; // namespace android
diff --git a/services/camera/libcameraservice/utils/CameraTraces.h b/services/camera/libcameraservice/utils/CameraTraces.h
new file mode 100644
index 0000000..d10dbc9
--- /dev/null
+++ b/services/camera/libcameraservice/utils/CameraTraces.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 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_SERVERS_CAMERA_TRACES_H_
+#define ANDROID_SERVERS_CAMERA_TRACES_H_
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+namespace camera3 {
+
+class CameraTracesImpl;
+
+// Collect a list of the process's stack traces
+class CameraTraces {
+public:
+ /**
+ * Save the current stack trace for each thread in the process. At most
+ * MAX_TRACES will be saved, after which the oldest traces will be discarded.
+ *
+ * <p>Use CameraTraces::dump to print out the traces.</p>
+ */
+ static void saveTrace();
+
+ /**
+ * Prints all saved traces to the specified file descriptor.
+ *
+ * <p>Each line is indented by DUMP_INDENT spaces.</p>
+ */
+ static status_t dump(int fd, const Vector<String16>& args);
+
+private:
+ enum {
+ // Don't collect more than 100 traces. Discard oldest.
+ MAX_TRACES = 100,
+
+ // Insert 2 spaces when dumping the traces
+ DUMP_INDENT = 2,
+ };
+
+ CameraTraces();
+ ~CameraTraces();
+ CameraTraces(CameraTraces& rhs);
+
+ static CameraTracesImpl& sImpl;
+}; // class CameraTraces
+
+}; // namespace camera3
+}; // namespace android
+
+#endif // ANDROID_SERVERS_CAMERA_TRACES_H_
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
index 683fdf3..0c7fbbd 100644
--- a/services/medialog/MediaLogService.cpp
+++ b/services/medialog/MediaLogService.cpp
@@ -54,7 +54,7 @@
}
}
-status_t MediaLogService::dump(int fd, const Vector<String16>& args)
+status_t MediaLogService::dump(int fd, const Vector<String16>& args __unused)
{
// FIXME merge with similar but not identical code at services/audioflinger/ServiceUtilities.cpp
static const String16 sDump("android.permission.DUMP");
diff --git a/tools/resampler_tools/fir.cpp b/tools/resampler_tools/fir.cpp
index cc3d509..3d6a74d 100644
--- a/tools/resampler_tools/fir.cpp
+++ b/tools/resampler_tools/fir.cpp
@@ -20,15 +20,25 @@
#include <stdlib.h>
#include <string.h>
-static double sinc(double x) {
+static inline double sinc(double x) {
if (fabs(x) == 0.0f) return 1.0f;
return sin(x) / x;
}
-static double sqr(double x) {
+static inline double sqr(double x) {
return x*x;
}
+static inline int64_t toint(double x, int64_t maxval) {
+ int64_t v;
+
+ v = static_cast<int64_t>(floor(x * maxval + 0.5));
+ if (v >= maxval) {
+ return maxval - 1; // error!
+ }
+ return v;
+}
+
static double I0(double x) {
// from the Numerical Recipes in C p. 237
double ax,ans,y;
@@ -54,11 +64,12 @@
return I0(beta * sqrt(1.0 - sqr((2.0*k)/N - 1.0))) / I0(beta);
}
-
static void usage(char* name) {
fprintf(stderr,
- "usage: %s [-h] [-d] [-s sample_rate] [-c cut-off_frequency] [-n half_zero_crossings] [-f {float|fixed}] [-b beta] [-v dBFS] [-l lerp]\n"
- " %s [-h] [-d] [-s sample_rate] [-c cut-off_frequency] [-n half_zero_crossings] [-f {float|fixed}] [-b beta] [-v dBFS] -p M/N\n"
+ "usage: %s [-h] [-d] [-s sample_rate] [-c cut-off_frequency] [-n half_zero_crossings]"
+ " [-f {float|fixed|fixed16}] [-b beta] [-v dBFS] [-l lerp]\n"
+ " %s [-h] [-d] [-s sample_rate] [-c cut-off_frequency] [-n half_zero_crossings]"
+ " [-f {float|fixed|fixed16}] [-b beta] [-v dBFS] -p M/N\n"
" -h this help message\n"
" -d debug, print comma-separated coefficient table\n"
" -p generate poly-phase filter coefficients, with sample increment M/N\n"
@@ -66,6 +77,7 @@
" -c cut-off frequency (20478)\n"
" -n number of zero-crossings on one side (8)\n"
" -l number of lerping bits (4)\n"
+ " -m number of polyphases (related to -l, default 16)\n"
" -f output format, can be fixed-point or floating-point (fixed)\n"
" -b kaiser window parameter beta (7.865 [-80dB])\n"
" -v attenuation in dBFS (0)\n",
@@ -77,8 +89,7 @@
int main(int argc, char** argv)
{
// nc is the number of bits to store the coefficients
- const int nc = 32;
-
+ int nc = 32;
bool polyphase = false;
unsigned int polyM = 160;
unsigned int polyN = 147;
@@ -88,7 +99,6 @@
double atten = 1;
int format = 0;
-
// in order to keep the errors associated with the linear
// interpolation of the coefficients below the quantization error
// we must satisfy:
@@ -104,7 +114,6 @@
// Smith, J.O. Digital Audio Resampling Home Page
// https://ccrma.stanford.edu/~jos/resample/, 2011-03-29
//
- int nz = 4;
// | 0.1102*(A - 8.7) A > 50
// beta = | 0.5842*(A - 21)^0.4 + 0.07886*(A - 21) 21 <= A <= 50
@@ -123,7 +132,6 @@
// 100 dB 10.056
double beta = 7.865;
-
// 2*nzc = (A - 8) / (2.285 * dw)
// with dw the transition width = 2*pi*dF/Fs
//
@@ -148,8 +156,9 @@
// nzc = 20
//
+ int M = 1 << 4; // number of phases for interpolation
int ch;
- while ((ch = getopt(argc, argv, ":hds:c:n:f:l:b:p:v:")) != -1) {
+ while ((ch = getopt(argc, argv, ":hds:c:n:f:l:m:b:p:v:z:")) != -1) {
switch (ch) {
case 'd':
debug = true;
@@ -169,13 +178,26 @@
case 'n':
nzc = atoi(optarg);
break;
+ case 'm':
+ M = atoi(optarg);
+ break;
case 'l':
- nz = atoi(optarg);
+ M = 1 << atoi(optarg);
break;
case 'f':
- if (!strcmp(optarg,"fixed")) format = 0;
- else if (!strcmp(optarg,"float")) format = 1;
- else usage(argv[0]);
+ if (!strcmp(optarg, "fixed")) {
+ format = 0;
+ }
+ else if (!strcmp(optarg, "fixed16")) {
+ format = 0;
+ nc = 16;
+ }
+ else if (!strcmp(optarg, "float")) {
+ format = 1;
+ }
+ else {
+ usage(argv[0]);
+ }
break;
case 'b':
beta = atof(optarg);
@@ -193,11 +215,14 @@
// cut off frequency ratio Fc/Fs
double Fcr = Fc / Fs;
-
// total number of coefficients (one side)
- const int M = (1 << nz);
+
const int N = M * nzc;
+ // lerp (which is most useful if M is a power of 2)
+
+ int nz = 0; // recalculate nz as the bits needed to represent M
+ for (int i = M-1 ; i; i>>=1, nz++);
// generate the right half of the filter
if (!debug) {
printf("// cmd-line: ");
@@ -207,7 +232,7 @@
printf("\n");
if (!polyphase) {
printf("const int32_t RESAMPLE_FIR_SIZE = %d;\n", N);
- printf("const int32_t RESAMPLE_FIR_LERP_INT_BITS = %d;\n", nz);
+ printf("const int32_t RESAMPLE_FIR_INT_PHASES = %d;\n", M);
printf("const int32_t RESAMPLE_FIR_NUM_COEF = %d;\n", nzc);
} else {
printf("const int32_t RESAMPLE_FIR_SIZE = %d;\n", 2*nzc*polyN);
@@ -224,7 +249,7 @@
for (int i=0 ; i<=M ; i++) { // an extra set of coefs for interpolation
for (int j=0 ; j<nzc ; j++) {
int ix = j*M + i;
- double x = (2.0 * M_PI * ix * Fcr) / (1 << nz);
+ double x = (2.0 * M_PI * ix * Fcr) / M;
double y = kaiser(ix+N, 2*N, beta) * sinc(x) * 2.0 * Fcr;
y *= atten;
@@ -232,11 +257,13 @@
if (j == 0)
printf("\n ");
}
-
if (!format) {
- int64_t yi = floor(y * ((1ULL<<(nc-1))) + 0.5);
- if (yi >= (1LL<<(nc-1))) yi = (1LL<<(nc-1))-1;
- printf("0x%08x, ", int32_t(yi));
+ int64_t yi = toint(y, 1ULL<<(nc-1));
+ if (nc > 16) {
+ printf("0x%08x, ", int32_t(yi));
+ } else {
+ printf("0x%04x, ", int32_t(yi)&0xffff);
+ }
} else {
printf("%.9g%s ", y, debug ? "," : "f,");
}
@@ -254,9 +281,12 @@
double y = kaiser(i+N, 2*N, beta) * sinc(x) * 2.0 * Fcr;;
y *= atten;
if (!format) {
- int64_t yi = floor(y * ((1ULL<<(nc-1))) + 0.5);
- if (yi >= (1LL<<(nc-1))) yi = (1LL<<(nc-1))-1;
- printf("0x%08x", int32_t(yi));
+ int64_t yi = toint(y, 1ULL<<(nc-1));
+ if (nc > 16) {
+ printf("0x%08x, ", int32_t(yi));
+ } else {
+ printf("0x%04x, ", int32_t(yi)&0xffff);
+ }
} else {
printf("%.9g%s", y, debug ? "" : "f");
}
@@ -277,5 +307,3 @@
}
// http://www.csee.umbc.edu/help/sound/AFsp-V2R1/html/audio/ResampAudio.html
-
-