diff options
421 files changed, 25948 insertions, 10855 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index e6d9ebf..b8a9711 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -47,6 +47,11 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libmedia_nativ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/libmedia_native.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libmedia_native.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libmedia_native.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudioflinger_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudioflinger.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicy_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicy.so) + # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/camera/Android.mk b/camera/Android.mk index 5cedab0..369d0c5 100644 --- a/camera/Android.mk +++ b/camera/Android.mk @@ -1,3 +1,17 @@ +# Copyright 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + CAMERA_CLIENT_LOCAL_PATH:= $(call my-dir) include $(call all-subdir-makefiles) include $(CLEAR_VARS) @@ -8,7 +22,6 @@ LOCAL_SRC_FILES:= \ Camera.cpp \ CameraMetadata.cpp \ CameraParameters.cpp \ - CameraParameters2.cpp \ ICamera.cpp \ ICameraClient.cpp \ ICameraService.cpp \ @@ -22,6 +35,7 @@ LOCAL_SRC_FILES:= \ camera2/CaptureRequest.cpp \ ProCamera.cpp \ CameraBase.cpp \ + VendorTagDescriptor.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -35,6 +49,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_C_INCLUDES += \ system/media/camera/include \ + system/media/private/camera/include LOCAL_MODULE:= libcamera_client diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp index 7765914..1567cd1 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; @@ -270,7 +273,8 @@ status_t CameraMetadata::update(uint32_t tag, if ( (res = checkType(tag, TYPE_BYTE)) != OK) { return res; } - return updateImpl(tag, (const void*)string.string(), string.size()); + // string.size() doesn't count the null termination character. + return updateImpl(tag, (const void*)string.string(), string.size() + 1); } status_t CameraMetadata::updateImpl(uint32_t tag, const void *data, @@ -431,40 +435,70 @@ status_t CameraMetadata::readFromParcel(const Parcel& data, *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); @@ -485,38 +519,79 @@ status_t CameraMetadata::writeToParcel(Parcel& data, 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 @@ -528,6 +603,9 @@ status_t CameraMetadata::writeToParcel(Parcel& data, } while(false); blob.release(); + // arg2 = offset (int32) + res = data.writeInt32(static_cast<int32_t>(offset)); + return res; } diff --git a/camera/CameraParameters2.cpp b/camera/CameraParameters2.cpp deleted file mode 100644 index eac79e1..0000000 --- a/camera/CameraParameters2.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "CameraParams2" -// #define LOG_NDEBUG 0 -#include <utils/Log.h> - -#include <string.h> -#include <stdlib.h> -#include <camera/CameraParameters2.h> - -namespace android { - -CameraParameters2::CameraParameters2() - : mMap() -{ -} - -CameraParameters2::~CameraParameters2() -{ -} - -String8 CameraParameters2::flatten() const -{ - String8 flattened(""); - size_t size = mMap.size(); - - for (size_t i = 0; i < size; i++) { - String8 k, v; - k = mMap.keyAt(i); - v = mMap.valueAt(i); - - flattened += k; - flattened += "="; - flattened += v; - if (i != size-1) - flattened += ";"; - } - - ALOGV("%s: Flattened params = %s", __FUNCTION__, flattened.string()); - - return flattened; -} - -void CameraParameters2::unflatten(const String8 ¶ms) -{ - const char *a = params.string(); - const char *b; - - mMap.clear(); - - for (;;) { - // Find the bounds of the key name. - b = strchr(a, '='); - if (b == 0) - break; - - // Create the key string. - String8 k(a, (size_t)(b-a)); - - // Find the value. - a = b+1; - b = strchr(a, ';'); - if (b == 0) { - // If there's no semicolon, this is the last item. - String8 v(a); - mMap.add(k, v); - break; - } - - String8 v(a, (size_t)(b-a)); - mMap.add(k, v); - a = b+1; - } -} - - -void CameraParameters2::set(const char *key, const char *value) -{ - // XXX i think i can do this with strspn() - if (strchr(key, '=') || strchr(key, ';')) { - //XXX ALOGE("Key \"%s\"contains invalid character (= or ;)", key); - return; - } - - if (strchr(value, '=') || strchr(value, ';')) { - //XXX ALOGE("Value \"%s\"contains invalid character (= or ;)", value); - return; - } - - // Replacing a value updates the key's order to be the new largest order - ssize_t res = mMap.replaceValueFor(String8(key), String8(value)); - LOG_ALWAYS_FATAL_IF(res < 0, "replaceValueFor(%s,%s) failed", key, value); -} - -void CameraParameters2::set(const char *key, int value) -{ - char str[16]; - sprintf(str, "%d", value); - set(key, str); -} - -void CameraParameters2::setFloat(const char *key, float value) -{ - char str[16]; // 14 should be enough. We overestimate to be safe. - snprintf(str, sizeof(str), "%g", value); - set(key, str); -} - -const char *CameraParameters2::get(const char *key) const -{ - ssize_t idx = mMap.indexOfKey(String8(key)); - if (idx < 0) { - return NULL; - } else { - return mMap.valueAt(idx).string(); - } -} - -int CameraParameters2::getInt(const char *key) const -{ - const char *v = get(key); - if (v == 0) - return -1; - return strtol(v, 0, 0); -} - -float CameraParameters2::getFloat(const char *key) const -{ - const char *v = get(key); - if (v == 0) return -1; - return strtof(v, 0); -} - -status_t CameraParameters2::compareSetOrder(const char *key1, const char *key2, - int *order) const { - if (key1 == NULL) { - ALOGE("%s: key1 must not be NULL", __FUNCTION__); - return BAD_VALUE; - } else if (key2 == NULL) { - ALOGE("%s: key2 must not be NULL", __FUNCTION__); - return BAD_VALUE; - } else if (order == NULL) { - ALOGE("%s: order must not be NULL", __FUNCTION__); - return BAD_VALUE; - } - - ssize_t index1 = mMap.indexOfKey(String8(key1)); - ssize_t index2 = mMap.indexOfKey(String8(key2)); - if (index1 < 0) { - ALOGW("%s: Key1 (%s) was not set", __FUNCTION__, key1); - return NAME_NOT_FOUND; - } else if (index2 < 0) { - ALOGW("%s: Key2 (%s) was not set", __FUNCTION__, key2); - return NAME_NOT_FOUND; - } - - *order = (index1 == index2) ? 0 : - (index1 < index2) ? -1 : - 1; - - return OK; -} - -void CameraParameters2::remove(const char *key) -{ - mMap.removeItem(String8(key)); -} - -// Parse string like "640x480" or "10000,20000" -static int parse_pair(const char *str, int *first, int *second, char delim, - char **endptr = NULL) -{ - // Find the first integer. - char *end; - int w = (int)strtol(str, &end, 10); - // If a delimeter does not immediately follow, give up. - if (*end != delim) { - ALOGE("Cannot find delimeter (%c) in str=%s", delim, str); - return -1; - } - - // Find the second integer, immediately after the delimeter. - int h = (int)strtol(end+1, &end, 10); - - *first = w; - *second = h; - - if (endptr) { - *endptr = end; - } - - return 0; -} - -static void parseSizesList(const char *sizesStr, Vector<Size> &sizes) -{ - if (sizesStr == 0) { - return; - } - - char *sizeStartPtr = (char *)sizesStr; - - while (true) { - int width, height; - int success = parse_pair(sizeStartPtr, &width, &height, 'x', - &sizeStartPtr); - if (success == -1 || (*sizeStartPtr != ',' && *sizeStartPtr != '\0')) { - ALOGE("Picture sizes string \"%s\" contains invalid character.", sizesStr); - return; - } - sizes.push(Size(width, height)); - - if (*sizeStartPtr == '\0') { - return; - } - sizeStartPtr++; - } -} - -void CameraParameters2::setPreviewSize(int width, int height) -{ - char str[32]; - sprintf(str, "%dx%d", width, height); - set(CameraParameters::KEY_PREVIEW_SIZE, str); -} - -void CameraParameters2::getPreviewSize(int *width, int *height) const -{ - *width = *height = -1; - // Get the current string, if it doesn't exist, leave the -1x-1 - const char *p = get(CameraParameters::KEY_PREVIEW_SIZE); - if (p == 0) return; - parse_pair(p, width, height, 'x'); -} - -void CameraParameters2::getPreferredPreviewSizeForVideo(int *width, int *height) const -{ - *width = *height = -1; - const char *p = get(CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO); - if (p == 0) return; - parse_pair(p, width, height, 'x'); -} - -void CameraParameters2::getSupportedPreviewSizes(Vector<Size> &sizes) const -{ - const char *previewSizesStr = get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES); - parseSizesList(previewSizesStr, sizes); -} - -void CameraParameters2::setVideoSize(int width, int height) -{ - char str[32]; - sprintf(str, "%dx%d", width, height); - set(CameraParameters::KEY_VIDEO_SIZE, str); -} - -void CameraParameters2::getVideoSize(int *width, int *height) const -{ - *width = *height = -1; - const char *p = get(CameraParameters::KEY_VIDEO_SIZE); - if (p == 0) return; - parse_pair(p, width, height, 'x'); -} - -void CameraParameters2::getSupportedVideoSizes(Vector<Size> &sizes) const -{ - const char *videoSizesStr = get(CameraParameters::KEY_SUPPORTED_VIDEO_SIZES); - parseSizesList(videoSizesStr, sizes); -} - -void CameraParameters2::setPreviewFrameRate(int fps) -{ - set(CameraParameters::KEY_PREVIEW_FRAME_RATE, fps); -} - -int CameraParameters2::getPreviewFrameRate() const -{ - return getInt(CameraParameters::KEY_PREVIEW_FRAME_RATE); -} - -void CameraParameters2::getPreviewFpsRange(int *min_fps, int *max_fps) const -{ - *min_fps = *max_fps = -1; - const char *p = get(CameraParameters::KEY_PREVIEW_FPS_RANGE); - if (p == 0) return; - parse_pair(p, min_fps, max_fps, ','); -} - -void CameraParameters2::setPreviewFpsRange(int min_fps, int max_fps) -{ - String8 str = String8::format("%d,%d", min_fps, max_fps); - set(CameraParameters::KEY_PREVIEW_FPS_RANGE, str.string()); -} - -void CameraParameters2::setPreviewFormat(const char *format) -{ - set(CameraParameters::KEY_PREVIEW_FORMAT, format); -} - -const char *CameraParameters2::getPreviewFormat() const -{ - return get(CameraParameters::KEY_PREVIEW_FORMAT); -} - -void CameraParameters2::setPictureSize(int width, int height) -{ - char str[32]; - sprintf(str, "%dx%d", width, height); - set(CameraParameters::KEY_PICTURE_SIZE, str); -} - -void CameraParameters2::getPictureSize(int *width, int *height) const -{ - *width = *height = -1; - // Get the current string, if it doesn't exist, leave the -1x-1 - const char *p = get(CameraParameters::KEY_PICTURE_SIZE); - if (p == 0) return; - parse_pair(p, width, height, 'x'); -} - -void CameraParameters2::getSupportedPictureSizes(Vector<Size> &sizes) const -{ - const char *pictureSizesStr = get(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES); - parseSizesList(pictureSizesStr, sizes); -} - -void CameraParameters2::setPictureFormat(const char *format) -{ - set(CameraParameters::KEY_PICTURE_FORMAT, format); -} - -const char *CameraParameters2::getPictureFormat() const -{ - return get(CameraParameters::KEY_PICTURE_FORMAT); -} - -void CameraParameters2::dump() const -{ - ALOGD("dump: mMap.size = %d", mMap.size()); - for (size_t i = 0; i < mMap.size(); i++) { - String8 k, v; - k = mMap.keyAt(i); - v = mMap.valueAt(i); - ALOGD("%s: %s\n", k.string(), v.string()); - } -} - -status_t CameraParameters2::dump(int fd, const Vector<String16>& args) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - snprintf(buffer, 255, "CameraParameters2::dump: mMap.size = %zu\n", mMap.size()); - result.append(buffer); - for (size_t i = 0; i < mMap.size(); i++) { - String8 k, v; - k = mMap.keyAt(i); - v = mMap.valueAt(i); - snprintf(buffer, 255, "\t%s: %s\n", k.string(), v.string()); - result.append(buffer); - } - write(fd, result.string(), result.size()); - return NO_ERROR; -} - -}; // namespace android diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp index 5fc89fb..b86651f 100644 --- a/camera/ICameraService.cpp +++ b/camera/ICameraService.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "BpCameraService" #include <utils/Log.h> +#include <utils/Errors.h> #include <stdint.h> #include <sys/types.h> @@ -34,6 +35,7 @@ #include <camera/camera2/ICameraDeviceUser.h> #include <camera/camera2/ICameraDeviceCallbacks.h> #include <camera/CameraMetadata.h> +#include <camera/VendorTagDescriptor.h> namespace android { @@ -143,6 +145,24 @@ public: return result; } + // Get enumeration and description of vendor tags for camera + virtual status_t getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) { + Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); + remote()->transact(BnCameraService::GET_CAMERA_VENDOR_TAG_DESCRIPTOR, data, &reply); + + if (readExceptionCode(reply)) return -EPROTO; + status_t result = reply.readInt32(); + + if (reply.readInt32() != 0) { + sp<VendorTagDescriptor> d; + if (VendorTagDescriptor::createFromParcel(&reply, /*out*/d) == OK) { + desc = d; + } + } + return result; + } + // connect to camera service (android.hardware.Camera) virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId, const String16 &clientPackageName, int clientUid, @@ -275,6 +295,22 @@ status_t BnCameraService::onTransact( info.writeToParcel(reply); return NO_ERROR; } break; + case GET_CAMERA_VENDOR_TAG_DESCRIPTOR: { + CHECK_INTERFACE(ICameraService, data, reply); + sp<VendorTagDescriptor> d; + status_t result = getCameraVendorTagDescriptor(d); + reply->writeNoException(); + reply->writeInt32(result); + + // out-variables are after exception and return value + if (d == NULL) { + reply->writeInt32(0); + } else { + reply->writeInt32(1); // means the parcelable is included + d->writeToParcel(reply); + } + return NO_ERROR; + } break; case CONNECT: { CHECK_INTERFACE(ICameraService, data, reply); sp<ICameraClient> cameraClient = @@ -284,7 +320,7 @@ status_t BnCameraService::onTransact( int32_t clientUid = data.readInt32(); sp<ICamera> camera; status_t status = connect(cameraClient, cameraId, - clientName, clientUid, /*out*/ camera); + clientName, clientUid, /*out*/camera); reply->writeNoException(); reply->writeInt32(status); if (camera != NULL) { @@ -304,7 +340,7 @@ status_t BnCameraService::onTransact( int32_t clientUid = data.readInt32(); sp<IProCameraUser> camera; status_t status = connectPro(cameraClient, cameraId, - clientName, clientUid, /*out*/ camera); + clientName, clientUid, /*out*/camera); reply->writeNoException(); reply->writeInt32(status); if (camera != NULL) { @@ -324,7 +360,7 @@ status_t BnCameraService::onTransact( int32_t clientUid = data.readInt32(); sp<ICameraDeviceUser> camera; status_t status = connectDevice(cameraClient, cameraId, - clientName, clientUid, /*out*/ camera); + clientName, clientUid, /*out*/camera); reply->writeNoException(); reply->writeInt32(status); if (camera != NULL) { diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp index ba5a48c..48f8e8e 100644 --- a/camera/ProCamera.cpp +++ b/camera/ProCamera.cpp @@ -249,11 +249,14 @@ status_t ProCamera::createStreamCpu(int width, int height, int format, sp <IProCameraUser> c = mCamera; if (c == 0) return NO_INIT; - sp<BufferQueue> bq = new BufferQueue(); - sp<CpuConsumer> cc = new CpuConsumer(bq, heapCount/*, synchronousMode*/); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + sp<CpuConsumer> cc = new CpuConsumer(consumer, heapCount + /*, synchronousMode*/); cc->setName(String8("ProCamera::mCpuConsumer")); - sp<Surface> stc = new Surface(bq); + sp<Surface> stc = new Surface(producer); status_t s = createStream(width, height, format, stc->getIGraphicBufferProducer(), diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp new file mode 100644 index 0000000..a0a6a51 --- /dev/null +++ b/camera/VendorTagDescriptor.cpp @@ -0,0 +1,319 @@ +/* + * 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_TAG "VenderTagDescriptor" + +#include <binder/Parcel.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/Mutex.h> +#include <utils/Vector.h> +#include <system/camera_metadata.h> +#include <camera_metadata_hidden.h> + +#include "camera/VendorTagDescriptor.h" + +#include <string.h> + +namespace android { + +extern "C" { + +static int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* v); +static void vendor_tag_descriptor_get_all_tags(const vendor_tag_ops_t* v, uint32_t* tagArray); +static const char* vendor_tag_descriptor_get_section_name(const vendor_tag_ops_t* v, uint32_t tag); +static const char* vendor_tag_descriptor_get_tag_name(const vendor_tag_ops_t* v, uint32_t tag); +static int vendor_tag_descriptor_get_tag_type(const vendor_tag_ops_t* v, uint32_t tag); + +} /* extern "C" */ + + +static Mutex sLock; +static sp<VendorTagDescriptor> sGlobalVendorTagDescriptor; + +VendorTagDescriptor::VendorTagDescriptor() {} +VendorTagDescriptor::~VendorTagDescriptor() {} + +status_t VendorTagDescriptor::createDescriptorFromOps(const vendor_tag_ops_t* vOps, + /*out*/ + sp<VendorTagDescriptor>& descriptor) { + if (vOps == NULL) { + ALOGE("%s: vendor_tag_ops argument was NULL.", __FUNCTION__); + return BAD_VALUE; + } + + int tagCount = vOps->get_tag_count(vOps); + if (tagCount < 0 || tagCount > INT32_MAX) { + ALOGE("%s: tag count %d from vendor ops is invalid.", __FUNCTION__, tagCount); + return BAD_VALUE; + } + + Vector<uint32_t> tagArray; + LOG_ALWAYS_FATAL_IF(tagArray.resize(tagCount) != tagCount, + "%s: too many (%u) vendor tags defined.", __FUNCTION__, tagCount); + + vOps->get_all_tags(vOps, /*out*/tagArray.editArray()); + + sp<VendorTagDescriptor> desc = new VendorTagDescriptor(); + desc->mTagCount = tagCount; + + for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) { + uint32_t tag = tagArray[i]; + if (tag < CAMERA_METADATA_VENDOR_TAG_BOUNDARY) { + ALOGE("%s: vendor tag %d not in vendor tag section.", __FUNCTION__, tag); + return BAD_VALUE; + } + const char *tagName = vOps->get_tag_name(vOps, tag); + if (tagName == NULL) { + ALOGE("%s: no tag name defined for vendor tag %d.", __FUNCTION__, tag); + return BAD_VALUE; + } + desc->mTagToNameMap.add(tag, String8(tagName)); + const char *sectionName = vOps->get_section_name(vOps, tag); + if (sectionName == NULL) { + ALOGE("%s: no section name defined for vendor tag %d.", __FUNCTION__, tag); + return BAD_VALUE; + } + desc->mTagToSectionMap.add(tag, String8(sectionName)); + int tagType = vOps->get_tag_type(vOps, tag); + if (tagType < 0 || tagType >= NUM_TYPES) { + ALOGE("%s: tag type %d from vendor ops does not exist.", __FUNCTION__, tagType); + return BAD_VALUE; + } + desc->mTagToTypeMap.add(tag, tagType); + } + descriptor = desc; + return OK; +} + +status_t VendorTagDescriptor::createFromParcel(const Parcel* parcel, + /*out*/ + sp<VendorTagDescriptor>& descriptor) { + status_t res = OK; + if (parcel == NULL) { + ALOGE("%s: parcel argument was NULL.", __FUNCTION__); + return BAD_VALUE; + } + + int32_t tagCount = 0; + if ((res = parcel->readInt32(&tagCount)) != OK) { + ALOGE("%s: could not read tag count from parcel", __FUNCTION__); + return res; + } + + if (tagCount < 0 || tagCount > INT32_MAX) { + ALOGE("%s: tag count %d from vendor ops is invalid.", __FUNCTION__, tagCount); + return BAD_VALUE; + } + + sp<VendorTagDescriptor> desc = new VendorTagDescriptor(); + desc->mTagCount = tagCount; + + uint32_t tag; + int32_t tagType; + for (int32_t i = 0; i < tagCount; ++i) { + if ((res = parcel->readInt32(reinterpret_cast<int32_t*>(&tag))) != OK) { + ALOGE("%s: could not read tag id from parcel for index %d", __FUNCTION__, i); + break; + } + if (tag < CAMERA_METADATA_VENDOR_TAG_BOUNDARY) { + ALOGE("%s: vendor tag %d not in vendor tag section.", __FUNCTION__, tag); + res = BAD_VALUE; + break; + } + if ((res = parcel->readInt32(&tagType)) != OK) { + ALOGE("%s: could not read tag type from parcel for tag %d", __FUNCTION__, tag); + break; + } + if (tagType < 0 || tagType >= NUM_TYPES) { + ALOGE("%s: tag type %d from vendor ops does not exist.", __FUNCTION__, tagType); + res = BAD_VALUE; + break; + } + String8 tagName = parcel->readString8(); + if (tagName.isEmpty()) { + ALOGE("%s: parcel tag name was NULL for tag %d.", __FUNCTION__, tag); + res = NOT_ENOUGH_DATA; + break; + } + String8 sectionName = parcel->readString8(); + if (sectionName.isEmpty()) { + ALOGE("%s: parcel section name was NULL for tag %d.", __FUNCTION__, tag); + res = NOT_ENOUGH_DATA; + break; + } + + desc->mTagToNameMap.add(tag, tagName); + desc->mTagToSectionMap.add(tag, sectionName); + desc->mTagToTypeMap.add(tag, tagType); + } + + if (res != OK) { + return res; + } + + descriptor = desc; + return res; +} + +int VendorTagDescriptor::getTagCount() const { + size_t size = mTagToNameMap.size(); + if (size == 0) { + return VENDOR_TAG_COUNT_ERR; + } + return size; +} + +void VendorTagDescriptor::getTagArray(uint32_t* tagArray) const { + size_t size = mTagToNameMap.size(); + for (size_t i = 0; i < size; ++i) { + tagArray[i] = mTagToNameMap.keyAt(i); + } +} + +const char* VendorTagDescriptor::getSectionName(uint32_t tag) const { + ssize_t index = mTagToSectionMap.indexOfKey(tag); + if (index < 0) { + return VENDOR_SECTION_NAME_ERR; + } + return mTagToSectionMap.valueAt(index).string(); +} + +const char* VendorTagDescriptor::getTagName(uint32_t tag) const { + ssize_t index = mTagToNameMap.indexOfKey(tag); + if (index < 0) { + return VENDOR_TAG_NAME_ERR; + } + return mTagToNameMap.valueAt(index).string(); +} + +int VendorTagDescriptor::getTagType(uint32_t tag) const { + ssize_t index = mTagToNameMap.indexOfKey(tag); + if (index < 0) { + return VENDOR_TAG_TYPE_ERR; + } + return mTagToTypeMap.valueFor(tag); +} + +status_t VendorTagDescriptor::writeToParcel(Parcel* parcel) const { + status_t res = OK; + if (parcel == NULL) { + ALOGE("%s: parcel argument was NULL.", __FUNCTION__); + return BAD_VALUE; + } + + if ((res = parcel->writeInt32(mTagCount)) != OK) { + return res; + } + + size_t size = mTagToNameMap.size(); + uint32_t tag; + int32_t tagType; + for (size_t i = 0; i < size; ++i) { + tag = mTagToNameMap.keyAt(i); + String8 tagName = mTagToNameMap[i]; + String8 sectionName = mTagToSectionMap.valueFor(tag); + tagType = mTagToTypeMap.valueFor(tag); + if ((res = parcel->writeInt32(tag)) != OK) break; + if ((res = parcel->writeInt32(tagType)) != OK) break; + if ((res = parcel->writeString8(tagName)) != OK) break; + if ((res = parcel->writeString8(sectionName)) != OK) break; + } + + return res; +} + +status_t VendorTagDescriptor::setAsGlobalVendorTagDescriptor(const sp<VendorTagDescriptor>& desc) { + status_t res = OK; + Mutex::Autolock al(sLock); + sGlobalVendorTagDescriptor = desc; + + vendor_tag_ops_t* opsPtr = NULL; + if (desc != NULL) { + opsPtr = &(desc->mVendorOps); + opsPtr->get_tag_count = vendor_tag_descriptor_get_tag_count; + opsPtr->get_all_tags = vendor_tag_descriptor_get_all_tags; + opsPtr->get_section_name = vendor_tag_descriptor_get_section_name; + opsPtr->get_tag_name = vendor_tag_descriptor_get_tag_name; + opsPtr->get_tag_type = vendor_tag_descriptor_get_tag_type; + } + if((res = set_camera_metadata_vendor_ops(opsPtr)) != OK) { + ALOGE("%s: Could not set vendor tag descriptor, received error %s (%d)." + , __FUNCTION__, strerror(-res), res); + } + return res; +} + +void VendorTagDescriptor::clearGlobalVendorTagDescriptor() { + Mutex::Autolock al(sLock); + set_camera_metadata_vendor_ops(NULL); + sGlobalVendorTagDescriptor.clear(); +} + +sp<VendorTagDescriptor> VendorTagDescriptor::getGlobalVendorTagDescriptor() { + Mutex::Autolock al(sLock); + return sGlobalVendorTagDescriptor; +} + +extern "C" { + +int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* v) { + Mutex::Autolock al(sLock); + if (sGlobalVendorTagDescriptor == NULL) { + ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); + return VENDOR_TAG_COUNT_ERR; + } + return sGlobalVendorTagDescriptor->getTagCount(); +} + +void vendor_tag_descriptor_get_all_tags(const vendor_tag_ops_t* v, uint32_t* tagArray) { + Mutex::Autolock al(sLock); + if (sGlobalVendorTagDescriptor == NULL) { + ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); + return; + } + sGlobalVendorTagDescriptor->getTagArray(tagArray); +} + +const char* vendor_tag_descriptor_get_section_name(const vendor_tag_ops_t* v, uint32_t tag) { + Mutex::Autolock al(sLock); + if (sGlobalVendorTagDescriptor == NULL) { + ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); + return VENDOR_SECTION_NAME_ERR; + } + return sGlobalVendorTagDescriptor->getSectionName(tag); +} + +const char* vendor_tag_descriptor_get_tag_name(const vendor_tag_ops_t* v, uint32_t tag) { + Mutex::Autolock al(sLock); + if (sGlobalVendorTagDescriptor == NULL) { + ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); + return VENDOR_TAG_NAME_ERR; + } + return sGlobalVendorTagDescriptor->getTagName(tag); +} + +int vendor_tag_descriptor_get_tag_type(const vendor_tag_ops_t* v, uint32_t tag) { + Mutex::Autolock al(sLock); + if (sGlobalVendorTagDescriptor == NULL) { + ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); + return VENDOR_TAG_TYPE_ERR; + } + return sGlobalVendorTagDescriptor->getTagType(tag); +} + +} /* extern "C" */ +} /* namespace android */ diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk index ec13911..61385e5 100644 --- a/camera/tests/Android.mk +++ b/camera/tests/Android.mk @@ -1,9 +1,24 @@ +# 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. + LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ main.cpp \ ProCameraTests.cpp \ + VendorTagDescriptorTests.cpp LOCAL_SHARED_LIBRARIES := \ libutils \ @@ -26,6 +41,8 @@ LOCAL_C_INCLUDES += \ external/gtest/include \ external/stlport/stlport \ system/media/camera/include \ + system/media/private/camera/include \ + system/media/camera/tests \ frameworks/av/services/camera/libcameraservice \ frameworks/av/include/camera \ frameworks/native/include \ diff --git a/camera/tests/VendorTagDescriptorTests.cpp b/camera/tests/VendorTagDescriptorTests.cpp new file mode 100644 index 0000000..6624e79 --- /dev/null +++ b/camera/tests/VendorTagDescriptorTests.cpp @@ -0,0 +1,204 @@ +/* + * 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 "VendorTagDescriptorTests" + +#include <binder/Parcel.h> +#include <camera/VendorTagDescriptor.h> +#include <camera_metadata_tests_fake_vendor.h> +#include <camera_metadata_hidden.h> +#include <system/camera_vendor_tags.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/RefBase.h> + +#include <gtest/gtest.h> +#include <stdint.h> + +using namespace android; + +enum { + BAD_TAG_ARRAY = 0xDEADBEEFu, + BAD_TAG = 0x8DEADBADu, +}; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +static bool ContainsTag(uint32_t* tagArray, size_t size, uint32_t tag) { + for (size_t i = 0; i < size; ++i) { + if (tag == tagArray[i]) return true; + } + return false; +} + +#define EXPECT_CONTAINS_TAG(t, a) \ + EXPECT_TRUE(ContainsTag(a, ARRAY_SIZE(a), t)) + +#define ASSERT_NOT_NULL(x) \ + ASSERT_TRUE((x) != NULL) + +extern "C" { + +static int default_get_tag_count(const vendor_tag_ops_t* vOps) { + return VENDOR_TAG_COUNT_ERR; +} + +static void default_get_all_tags(const vendor_tag_ops_t* vOps, uint32_t* tagArray) { + //Noop +} + +static const char* default_get_section_name(const vendor_tag_ops_t* vOps, uint32_t tag) { + return VENDOR_SECTION_NAME_ERR; +} + +static const char* default_get_tag_name(const vendor_tag_ops_t* vOps, uint32_t tag) { + return VENDOR_TAG_NAME_ERR; +} + +static int default_get_tag_type(const vendor_tag_ops_t* vOps, uint32_t tag) { + return VENDOR_TAG_TYPE_ERR; +} + +} /*extern "C"*/ + +// Set default vendor operations for a vendor_tag_ops struct +static void FillWithDefaults(vendor_tag_ops_t* vOps) { + ASSERT_NOT_NULL(vOps); + vOps->get_tag_count = default_get_tag_count; + vOps->get_all_tags = default_get_all_tags; + vOps->get_section_name = default_get_section_name; + vOps->get_tag_name = default_get_tag_name; + vOps->get_tag_type = default_get_tag_type; +} + +/** + * Test if values from VendorTagDescriptor methods match corresponding values + * from vendor_tag_ops functions. + */ +TEST(VendorTagDescriptorTest, ConsistentWithVendorTags) { + sp<VendorTagDescriptor> vDesc; + const vendor_tag_ops_t *vOps = &fakevendor_ops; + EXPECT_EQ(OK, VendorTagDescriptor::createDescriptorFromOps(vOps, /*out*/vDesc)); + + ASSERT_NOT_NULL(vDesc); + + // Ensure reasonable tag count + int tagCount = vDesc->getTagCount(); + EXPECT_EQ(tagCount, vOps->get_tag_count(vOps)); + + uint32_t descTagArray[tagCount]; + uint32_t opsTagArray[tagCount]; + + // Get all tag ids + vDesc->getTagArray(descTagArray); + vOps->get_all_tags(vOps, opsTagArray); + + ASSERT_NOT_NULL(descTagArray); + ASSERT_NOT_NULL(opsTagArray); + + uint32_t tag; + for (int i = 0; i < tagCount; ++i) { + // For each tag id, check whether type, section name, tag name match + tag = descTagArray[i]; + EXPECT_CONTAINS_TAG(tag, opsTagArray); + EXPECT_EQ(vDesc->getTagType(tag), vOps->get_tag_type(vOps, tag)); + EXPECT_STREQ(vDesc->getSectionName(tag), vOps->get_section_name(vOps, tag)); + EXPECT_STREQ(vDesc->getTagName(tag), vOps->get_tag_name(vOps, tag)); + } +} + +/** + * Test if values from VendorTagDescriptor methods stay consistent after being + * parcelled/unparcelled. + */ +TEST(VendorTagDescriptorTest, ConsistentAcrossParcel) { + sp<VendorTagDescriptor> vDescOriginal, vDescParceled; + const vendor_tag_ops_t *vOps = &fakevendor_ops; + EXPECT_EQ(OK, VendorTagDescriptor::createDescriptorFromOps(vOps, /*out*/vDescOriginal)); + + ASSERT_TRUE(vDescOriginal != NULL); + + Parcel p; + + // Check whether parcel read/write succeed + EXPECT_EQ(OK, vDescOriginal->writeToParcel(&p)); + p.setDataPosition(0); + ASSERT_EQ(OK, VendorTagDescriptor::createFromParcel(&p, vDescParceled)); + + // Ensure consistent tag count + int tagCount = vDescOriginal->getTagCount(); + ASSERT_EQ(tagCount, vDescParceled->getTagCount()); + + uint32_t descTagArray[tagCount]; + uint32_t desc2TagArray[tagCount]; + + // Get all tag ids + vDescOriginal->getTagArray(descTagArray); + vDescParceled->getTagArray(desc2TagArray); + + ASSERT_NOT_NULL(descTagArray); + ASSERT_NOT_NULL(desc2TagArray); + + uint32_t tag; + for (int i = 0; i < tagCount; ++i) { + // For each tag id, check consistency between the two vendor tag + // descriptors for each type, section name, tag name + tag = descTagArray[i]; + EXPECT_CONTAINS_TAG(tag, desc2TagArray); + EXPECT_EQ(vDescOriginal->getTagType(tag), vDescParceled->getTagType(tag)); + EXPECT_STREQ(vDescOriginal->getSectionName(tag), vDescParceled->getSectionName(tag)); + EXPECT_STREQ(vDescOriginal->getTagName(tag), vDescParceled->getTagName(tag)); + } +} + +/** + * Test defaults and error conditions. + */ +TEST(VendorTagDescriptorTest, ErrorConditions) { + sp<VendorTagDescriptor> vDesc; + vendor_tag_ops_t vOps; + FillWithDefaults(&vOps); + + // Ensure create fails when using null vOps + EXPECT_EQ(BAD_VALUE, VendorTagDescriptor::createDescriptorFromOps(/*vOps*/NULL, vDesc)); + + // Ensure create works when there are no vtags defined in a well-formed vOps + ASSERT_EQ(OK, VendorTagDescriptor::createDescriptorFromOps(&vOps, vDesc)); + + // Ensure defaults are returned when no vtags are defined, or tag is unknown + EXPECT_EQ(VENDOR_TAG_COUNT_ERR, vDesc->getTagCount()); + uint32_t* tagArray = reinterpret_cast<uint32_t*>(BAD_TAG_ARRAY); + uint32_t* testArray = tagArray; + vDesc->getTagArray(tagArray); + EXPECT_EQ(testArray, tagArray); + EXPECT_EQ(VENDOR_SECTION_NAME_ERR, vDesc->getSectionName(BAD_TAG)); + EXPECT_EQ(VENDOR_TAG_NAME_ERR, vDesc->getTagName(BAD_TAG)); + EXPECT_EQ(VENDOR_TAG_TYPE_ERR, vDesc->getTagType(BAD_TAG)); + + // Make sure global can be set/cleared + const vendor_tag_ops_t *fakeOps = &fakevendor_ops; + sp<VendorTagDescriptor> prevGlobal = VendorTagDescriptor::getGlobalVendorTagDescriptor(); + VendorTagDescriptor::clearGlobalVendorTagDescriptor(); + + EXPECT_TRUE(VendorTagDescriptor::getGlobalVendorTagDescriptor() == NULL); + EXPECT_EQ(OK, VendorTagDescriptor::setAsGlobalVendorTagDescriptor(vDesc)); + EXPECT_TRUE(VendorTagDescriptor::getGlobalVendorTagDescriptor() != NULL); + EXPECT_EQ(VENDOR_SECTION_NAME_ERR, vDesc->getSectionName(BAD_TAG)); + EXPECT_EQ(OK, VendorTagDescriptor::setAsGlobalVendorTagDescriptor(prevGlobal)); + EXPECT_EQ(prevGlobal, VendorTagDescriptor::getGlobalVendorTagDescriptor()); +} + diff --git a/cmds/screenrecord/Android.mk b/cmds/screenrecord/Android.mk index 57a2234..6ee2884 100644 --- a/cmds/screenrecord/Android.mk +++ b/cmds/screenrecord/Android.mk @@ -19,6 +19,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ screenrecord.cpp \ EglWindow.cpp \ + FrameOutput.cpp \ TextRenderer.cpp \ Overlay.cpp \ Program.cpp diff --git a/cmds/screenrecord/EglWindow.cpp b/cmds/screenrecord/EglWindow.cpp index aa0517f..c16f2ad 100644 --- a/cmds/screenrecord/EglWindow.cpp +++ b/cmds/screenrecord/EglWindow.cpp @@ -35,11 +35,16 @@ using namespace android; status_t EglWindow::createWindow(const sp<IGraphicBufferProducer>& surface) { - status_t err = eglSetupContext(); + 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); @@ -56,6 +61,34 @@ status_t EglWindow::createWindow(const sp<IGraphicBufferProducer>& surface) { 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()); @@ -64,7 +97,7 @@ status_t EglWindow::makeCurrent() const { return NO_ERROR; } -status_t EglWindow::eglSetupContext() { +status_t EglWindow::eglSetupContext(bool forPbuffer) { EGLBoolean result; mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); @@ -82,17 +115,28 @@ status_t EglWindow::eglSetupContext() { ALOGV("Initialized EGL v%d.%d", majorVersion, minorVersion); EGLint numConfigs = 0; - EGLint configAttribs[] = { - 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, - EGL_NONE + 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, configAttribs, &mEglConfig, 1, - &numConfigs); + result = eglChooseConfig(mEglDisplay, + forPbuffer ? pbufferConfigAttribs : windowConfigAttribs, + &mEglConfig, 1, &numConfigs); if (result != EGL_TRUE) { ALOGE("eglChooseConfig error: %#x", eglGetError()); return UNKNOWN_ERROR; diff --git a/cmds/screenrecord/EglWindow.h b/cmds/screenrecord/EglWindow.h index 02a2efc..69d0c31 100644 --- a/cmds/screenrecord/EglWindow.h +++ b/cmds/screenrecord/EglWindow.h @@ -44,6 +44,9 @@ public: // 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; } @@ -65,7 +68,7 @@ private: EglWindow& operator=(const EglWindow&); // Init display, create config and context. - status_t eglSetupContext(); + status_t eglSetupContext(bool forPbuffer); void eglRelease(); // Basic EGL goodies. diff --git a/cmds/screenrecord/FrameOutput.cpp b/cmds/screenrecord/FrameOutput.cpp new file mode 100644 index 0000000..06b1f70 --- /dev/null +++ b/cmds/screenrecord/FrameOutput.cpp @@ -0,0 +1,210 @@ +/* + * 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; + } + + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mGlConsumer = new GLConsumer(consumer, 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 = producer; + + 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..c1148d0 --- /dev/null +++ b/cmds/screenrecord/FrameOutput.h @@ -0,0 +1,99 @@ +/* + * 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; + + // 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 index 2e98874..94f560d 100644 --- a/cmds/screenrecord/Overlay.cpp +++ b/cmds/screenrecord/Overlay.cpp @@ -84,7 +84,7 @@ status_t Overlay::start(const sp<IGraphicBufferProducer>& outputSurface, assert(mState == RUNNING); ALOGV("Overlay::start successful"); - *pBufferProducer = mBufferQueue; + *pBufferProducer = mProducer; return NO_ERROR; } @@ -169,8 +169,9 @@ status_t Overlay::setup_l() { return UNKNOWN_ERROR; } - mBufferQueue = new BufferQueue(/*new GraphicBufferAlloc()*/); - mGlConsumer = new GLConsumer(mBufferQueue, mExtTextureName, + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&mProducer, &consumer); + mGlConsumer = new GLConsumer(consumer, mExtTextureName, GL_TEXTURE_EXTERNAL_OES); mGlConsumer->setName(String8("virtual display")); mGlConsumer->setDefaultBufferSize(width, height); @@ -187,7 +188,7 @@ void Overlay::release_l() { ALOGV("Overlay::release_l"); mOutputSurface.clear(); mGlConsumer.clear(); - mBufferQueue.clear(); + mProducer.clear(); mTexProgram.release(); mExtTexProgram.release(); diff --git a/cmds/screenrecord/Overlay.h b/cmds/screenrecord/Overlay.h index b8473b4..b1b5c29 100644 --- a/cmds/screenrecord/Overlay.h +++ b/cmds/screenrecord/Overlay.h @@ -47,7 +47,6 @@ public: mLastFrameNumber(-1), mTotalDroppedFrames(0) {} - virtual ~Overlay() { assert(mState == UNINITIALIZED || mState == STOPPED); } // Creates a thread that performs the overlay. Pass in the surface that // output will be sent to. @@ -71,6 +70,9 @@ 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); @@ -120,9 +122,9 @@ private: // 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; + // Producer side of queue, passed into the virtual display. + // The consumer end feeds into our GLConsumer. + sp<IGraphicBufferProducer> mProducer; // This receives frames from the virtual display and makes them available // as an external texture. diff --git a/cmds/screenrecord/Program.cpp b/cmds/screenrecord/Program.cpp index a198204..73cae6e 100644 --- a/cmds/screenrecord/Program.cpp +++ b/cmds/screenrecord/Program.cpp @@ -201,7 +201,7 @@ status_t Program::linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm) { status_t Program::blit(GLuint texName, const float* texMatrix, - int32_t x, int32_t y, int32_t w, int32_t h) const { + 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[] = { @@ -218,7 +218,7 @@ status_t Program::blit(GLuint texName, const float* texMatrix, }; status_t err; - err = beforeDraw(texName, texMatrix, pos, uv); + err = beforeDraw(texName, texMatrix, pos, uv, invert); if (err == NO_ERROR) { glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); err = afterDraw(); @@ -232,7 +232,7 @@ status_t Program::drawTriangles(GLuint texName, const float* texMatrix, status_t err; - err = beforeDraw(texName, texMatrix, vertices, texes); + err = beforeDraw(texName, texMatrix, vertices, texes, false); if (err == NO_ERROR) { glDrawArrays(GL_TRIANGLES, 0, count); err = afterDraw(); @@ -241,7 +241,7 @@ status_t Program::drawTriangles(GLuint texName, const float* texMatrix, } status_t Program::beforeDraw(GLuint texName, const float* texMatrix, - const float* vertices, const float* texes) const { + 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); @@ -251,6 +251,10 @@ status_t Program::beforeDraw(GLuint texName, const float* texMatrix, 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); diff --git a/cmds/screenrecord/Program.h b/cmds/screenrecord/Program.h index e47bc0d..558be8d 100644 --- a/cmds/screenrecord/Program.h +++ b/cmds/screenrecord/Program.h @@ -51,9 +51,11 @@ public: // Release the program and associated resources. void release(); - // Blit the specified texture to { x, y, x+w, y+h }. + // 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) const; + 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, @@ -67,7 +69,7 @@ private: // Common code for draw functions. status_t beforeDraw(GLuint texName, const float* texMatrix, - const float* vertices, const float* texes) const; + const float* vertices, const float* texes, bool invert) const; status_t afterDraw() const; // GLES 2 shader utilities. diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index 61f83e3..a17fc51 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -45,10 +45,12 @@ #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; @@ -57,10 +59,14 @@ 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 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? @@ -140,14 +146,14 @@ static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec, 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); @@ -157,16 +163,18 @@ static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec, 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 codec (err=%d)\n", err); + fprintf(stderr, "ERROR: unable to configure %s codec at %dx%d (err=%d)\n", + kMimeTypeAvc, gVideoWidth, gVideoHeight, err); codec->release(); return err; } @@ -298,10 +306,12 @@ static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo, * 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<IBinder>& mainDpy, + 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; @@ -311,6 +321,8 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, 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); if (err != NO_ERROR) { @@ -342,15 +354,16 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, 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); - assert(trackIdx != -1); { // scope ATRACE_NAME("orientation"); @@ -379,14 +392,23 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, 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). - // - // If this blocks for too long we could drop frames. We may - // want to queue these up and do them on a different thread. - { // scope + 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) { @@ -418,12 +440,14 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, 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; @@ -457,7 +481,45 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, } /* - * 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. @@ -499,30 +561,40 @@ static status_t recordScreen(const char* fileName) { // Configure and start the encoder. sp<MediaCodec> encoder; + sp<FrameOutput> frameOutput; sp<IGraphicBufferProducer> encoderInputSurface; - 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, - &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, + &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. + 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; + } + } // Draw the "info" page by rendering a frame with GLES and sending // it directly to the encoder. @@ -539,7 +611,7 @@ static status_t recordScreen(const char* fileName) { overlay = new Overlay(); err = overlay->start(encoderInputSurface, &bufferProducer); if (err != NO_ERROR) { - encoder->release(); + if (encoder != NULL) encoder->release(); return err; } if (gVerbose) { @@ -554,40 +626,91 @@ static status_t recordScreen(const char* fileName) { sp<IBinder> dpy; err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy); if (err != NO_ERROR) { - encoder->release(); + if (encoder != NULL) encoder->release(); return err; } - // Configure muxer. We have to wait for the CSD blob from the encoder - // before we can start it. - sp<MediaMuxer> muxer = new MediaMuxer(fileName, - MediaMuxer::OUTPUT_FORMAT_MPEG_4); - if (gRotate) { - muxer->setOrientationHint(90); // TODO: does this do anything? - } - - // Main encoder loop. - err = runEncoder(encoder, muxer, mainDpy, dpy, mainDpyInfo.orientation); - if (err != NO_ERROR) { - fprintf(stderr, "Encoder failed (err=%d)\n", err); - // fall through to cleanup - } + 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(); + } + + 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. + + // 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(); + + 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"); + if (gVerbose) { + printf("Stopping encoder and muxer\n"); + } } // Shut everything down, starting with the producer side. encoderInputSurface = NULL; SurfaceComposerClient::destroyDisplay(dpy); - if (overlay != NULL) { - overlay->stop(); + 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); } - encoder->stop(); - // If we don't stop muxer explicitly, i.e. let the destructor run, - // it may hang (b/11050628). - muxer->stop(); - encoder->release(); + if (encoder != NULL) encoder->release(); return err; } @@ -749,10 +872,12 @@ int main(int argc, char* const argv[]) { { "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' }, - { "bugreport", no_argument, NULL, 'u' }, { "rotate", no_argument, NULL, 'r' }, + { "output-format", required_argument, NULL, 'o' }, { NULL, 0, NULL, 0 } }; @@ -804,20 +929,32 @@ int main(int argc, char* const argv[]) { return 2; } break; - case 'i': + case 'u': gWantInfoScreen = true; - break; - case 'f': gWantFrameTime = true; break; - case 'u': + 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); @@ -831,17 +968,19 @@ int main(int argc, char* const argv[]) { 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 index 95e8a68..9b058c2 100644 --- a/cmds/screenrecord/screenrecord.h +++ b/cmds/screenrecord/screenrecord.h @@ -18,6 +18,6 @@ #define SCREENRECORD_SCREENRECORD_H #define kVersionMajor 1 -#define kVersionMinor 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 @@ status_t SimplePlayer::onPrepare() { 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 @@ SineSource::~SineSource() { } } -status_t SineSource::start(MetaData *params) { +status_t SineSource::start(MetaData * /* params */) { CHECK(!mStarted); mGroup = new MediaBufferGroup; @@ -58,7 +58,7 @@ sp<MetaData> SineSource::getFormat() { } 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 d125ad1..fd02bcc 100644 --- a/cmds/stagefright/codec.cpp +++ b/cmds/stagefright/codec.cpp @@ -24,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> @@ -76,7 +77,7 @@ static int decode( 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; } diff --git a/cmds/stagefright/muxer.cpp b/cmds/stagefright/muxer.cpp index 90daea2..f4a33e8 100644 --- a/cmds/stagefright/muxer.cpp +++ b/cmds/stagefright/muxer.cpp @@ -20,6 +20,7 @@ #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> @@ -59,7 +60,7 @@ static int muxing( 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; } 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 @@ int main(int argc, char **argv) { } #else -int main(int argc, char **argv) { +int main(int /* argc */, char ** /* argv */) { android::ProcessState::self()->startThreadPool(); OMXClient client; diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp index b2b9ce5..3c0c7ec 100644 --- a/cmds/stagefright/sf2.cpp +++ b/cmds/stagefright/sf2.cpp @@ -19,8 +19,12 @@ #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> @@ -43,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) @@ -63,7 +79,30 @@ protected: 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: { @@ -76,7 +115,8 @@ protected: #endif sp<DataSource> dataSource = - DataSource::CreateFromURI(mURI.c_str()); + DataSource::CreateFromURI( + NULL /* httpService */, mURI.c_str()); sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); @@ -99,7 +139,10 @@ protected: 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); @@ -181,21 +224,7 @@ protected: || what == ACodec::kWhatError) { printf((what == ACodec::kWhatEOS) ? "$\n" : "E\n"); - 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); - } - + printStatistics(); (new AMessage(kWhatStop, id()))->post(); } else if (what == ACodec::kWhatFlushCompleted) { mSeekState = SEEK_FLUSH_COMPLETED; @@ -639,6 +668,8 @@ int main(int argc, char **argv) { 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 8efb39e..daaea27 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -29,6 +29,7 @@ #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" @@ -960,7 +961,8 @@ int main(int argc, char **argv) { 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"); diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp index dba67a9..0566d14 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 @@ private: 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); @@ -371,7 +374,7 @@ int main(int argc, char **argv) { } sp<IMediaPlayer> player = - service->create(client, 0); + service->create(client, AUDIO_SESSION_ALLOCATE); if (player != NULL && player->setDataSource(source) == NO_ERROR) { player->setVideoSurfaceTexture(surface->getIGraphicBufferProducer()); 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 @@ String8 FwdLockEngine::onGetOriginalMimeType(int uniqueId, const String8& path, if (-1 < fileDesc) { if (FwdLockFile_attach(fileDesc) < 0) { + close(fileDesc); return mimeString; } const char* pMimeType = FwdLockFile_GetContentType(fileDesc); diff --git a/include/camera/CameraParameters2.h b/include/camera/CameraParameters2.h deleted file mode 100644 index 88ad812..0000000 --- a/include/camera/CameraParameters2.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_CAMERA_PARAMETERS2_H -#define ANDROID_HARDWARE_CAMERA_PARAMETERS2_H - -#include <utils/Vector.h> -#include <utils/String8.h> -#include "CameraParameters.h" - -namespace android { - -/** - * A copy of CameraParameters plus ABI-breaking changes. Needed - * because some camera HALs directly link to CameraParameters and cannot - * tolerate an ABI change. - */ -class CameraParameters2 -{ -public: - CameraParameters2(); - CameraParameters2(const String8 ¶ms) { unflatten(params); } - ~CameraParameters2(); - - String8 flatten() const; - void unflatten(const String8 ¶ms); - - void set(const char *key, const char *value); - void set(const char *key, int value); - void setFloat(const char *key, float value); - // Look up string value by key. - // -- The string remains valid until the next set/remove of the same key, - // or until the map gets cleared. - const char *get(const char *key) const; - int getInt(const char *key) const; - float getFloat(const char *key) const; - - // Compare the order that key1 was set vs the order that key2 was set. - // - // Sets the order parameter to an integer less than, equal to, or greater - // than zero if key1's set order was respectively, to be less than, to - // match, or to be greater than key2's set order. - // - // Error codes: - // * NAME_NOT_FOUND - if either key has not been set previously - // * BAD_VALUE - if any of the parameters are NULL - status_t compareSetOrder(const char *key1, const char *key2, - /*out*/ - int *order) const; - - void remove(const char *key); - - void setPreviewSize(int width, int height); - void getPreviewSize(int *width, int *height) const; - void getSupportedPreviewSizes(Vector<Size> &sizes) const; - - // Set the dimensions in pixels to the given width and height - // for video frames. The given width and height must be one - // of the supported dimensions returned from - // getSupportedVideoSizes(). Must not be called if - // getSupportedVideoSizes() returns an empty Vector of Size. - void setVideoSize(int width, int height); - // Retrieve the current dimensions (width and height) - // in pixels for video frames, which must be one of the - // supported dimensions returned from getSupportedVideoSizes(). - // Must not be called if getSupportedVideoSizes() returns an - // empty Vector of Size. - void getVideoSize(int *width, int *height) const; - // Retrieve a Vector of supported dimensions (width and height) - // in pixels for video frames. If sizes returned from the method - // is empty, the camera does not support calls to setVideoSize() - // or getVideoSize(). In adddition, it also indicates that - // the camera only has a single output, and does not have - // separate output for video frames and preview frame. - void getSupportedVideoSizes(Vector<Size> &sizes) const; - // Retrieve the preferred preview size (width and height) in pixels - // for video recording. The given width and height must be one of - // supported preview sizes returned from getSupportedPreviewSizes(). - // Must not be called if getSupportedVideoSizes() returns an empty - // Vector of Size. If getSupportedVideoSizes() returns an empty - // Vector of Size, the width and height returned from this method - // is invalid, and is "-1x-1". - void getPreferredPreviewSizeForVideo(int *width, int *height) const; - - void setPreviewFrameRate(int fps); - int getPreviewFrameRate() const; - void getPreviewFpsRange(int *min_fps, int *max_fps) const; - void setPreviewFpsRange(int min_fps, int max_fps); - void setPreviewFormat(const char *format); - const char *getPreviewFormat() const; - void setPictureSize(int width, int height); - void getPictureSize(int *width, int *height) const; - void getSupportedPictureSizes(Vector<Size> &sizes) const; - void setPictureFormat(const char *format); - const char *getPictureFormat() const; - - void dump() const; - status_t dump(int fd, const Vector<String16>& args) const; - -private: - - // Quick and dirty map that maintains insertion order - template <typename KeyT, typename ValueT> - struct OrderedKeyedVector { - - ssize_t add(const KeyT& key, const ValueT& value) { - return mList.add(Pair(key, value)); - } - - size_t size() const { - return mList.size(); - } - - const KeyT& keyAt(size_t idx) const { - return mList[idx].mKey; - } - - const ValueT& valueAt(size_t idx) const { - return mList[idx].mValue; - } - - const ValueT& valueFor(const KeyT& key) const { - ssize_t i = indexOfKey(key); - LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__); - - return valueAt(i); - } - - ssize_t indexOfKey(const KeyT& key) const { - size_t vectorIdx = 0; - for (; vectorIdx < mList.size(); ++vectorIdx) { - if (mList[vectorIdx].mKey == key) { - return (ssize_t) vectorIdx; - } - } - - return NAME_NOT_FOUND; - } - - ssize_t removeItem(const KeyT& key) { - size_t vectorIdx = (size_t) indexOfKey(key); - - if (vectorIdx < 0) { - return vectorIdx; - } - - return mList.removeAt(vectorIdx); - } - - void clear() { - mList.clear(); - } - - // Same as removing and re-adding. The key's index changes to max. - ssize_t replaceValueFor(const KeyT& key, const ValueT& value) { - removeItem(key); - return add(key, value); - } - - private: - - struct Pair { - Pair() : mKey(), mValue() {} - Pair(const KeyT& key, const ValueT& value) : - mKey(key), - mValue(value) {} - KeyT mKey; - ValueT mValue; - }; - - Vector<Pair> mList; - }; - - /** - * Order matters: Keys that are set() later are stored later in the map. - * - * If two keys have meaning that conflict, then the later-set key - * wins. - * - * For example, preview FPS and preview FPS range conflict since only - * we only want to use the FPS range if that's the last thing that was set. - * So in that case, only use preview FPS range if it was set later than - * the preview FPS. - */ - OrderedKeyedVector<String8,String8> mMap; -}; - -}; // namespace android - -#endif diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h index f342122..6e48f22 100644 --- a/include/camera/ICameraService.h +++ b/include/camera/ICameraService.h @@ -31,6 +31,7 @@ class ICameraServiceListener; class ICameraDeviceUser; class ICameraDeviceCallbacks; class CameraMetadata; +class VendorTagDescriptor; class ICameraService : public IInterface { @@ -47,6 +48,7 @@ public: ADD_LISTENER, REMOVE_LISTENER, GET_CAMERA_CHARACTERISTICS, + GET_CAMERA_VENDOR_TAG_DESCRIPTOR, }; enum { @@ -58,10 +60,16 @@ public: virtual int32_t getNumberOfCameras() = 0; virtual status_t getCameraInfo(int cameraId, - struct CameraInfo* cameraInfo) = 0; + /*out*/ + struct CameraInfo* cameraInfo) = 0; virtual status_t getCameraCharacteristics(int cameraId, - CameraMetadata* cameraInfo) = 0; + /*out*/ + CameraMetadata* cameraInfo) = 0; + + virtual status_t getCameraVendorTagDescriptor( + /*out*/ + sp<VendorTagDescriptor>& desc) = 0; // Returns 'OK' if operation succeeded // - Errors: ALREADY_EXISTS if the listener was already added diff --git a/include/camera/VendorTagDescriptor.h b/include/camera/VendorTagDescriptor.h new file mode 100644 index 0000000..ea21d31 --- /dev/null +++ b/include/camera/VendorTagDescriptor.h @@ -0,0 +1,124 @@ +/* + * 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 VENDOR_TAG_DESCRIPTOR_H + +#include <utils/KeyedVector.h> +#include <utils/String8.h> +#include <utils/RefBase.h> +#include <system/camera_vendor_tags.h> + +#include <stdint.h> + +namespace android { + +class Parcel; + +/** + * VendorTagDescriptor objects are parcelable containers for the vendor tag + * definitions provided, and are typically used to pass the vendor tag + * information enumerated by the HAL to clients of the camera service. + */ +class VendorTagDescriptor + : public LightRefBase<VendorTagDescriptor> { + public: + virtual ~VendorTagDescriptor(); + + /** + * The following 'get*' methods implement the corresponding + * functions defined in + * system/media/camera/include/system/camera_vendor_tags.h + */ + + // Returns the number of vendor tags defined. + int getTagCount() const; + + // Returns an array containing the id's of vendor tags defined. + void getTagArray(uint32_t* tagArray) const; + + // Returns the section name string for a given vendor tag id. + const char* getSectionName(uint32_t tag) const; + + // Returns the tag name string for a given vendor tag id. + const char* getTagName(uint32_t tag) const; + + // Returns the tag type for a given vendor tag id. + int getTagType(uint32_t tag) const; + + /** + * Write the VendorTagDescriptor object into the given parcel. + * + * Returns OK on success, or a negative error code. + */ + status_t writeToParcel( + /*out*/ + Parcel* parcel) const; + + // Static methods: + + /** + * Create a VendorTagDescriptor object from the given parcel. + * + * Returns OK on success, or a negative error code. + */ + static status_t createFromParcel(const Parcel* parcel, + /*out*/ + sp<VendorTagDescriptor>& descriptor); + + /** + * Create a VendorTagDescriptor object from the given vendor_tag_ops_t + * struct. + * + * Returns OK on success, or a negative error code. + */ + static status_t createDescriptorFromOps(const vendor_tag_ops_t* vOps, + /*out*/ + sp<VendorTagDescriptor>& descriptor); + + /** + * Sets the global vendor tag descriptor to use for this process. + * Camera metadata operations that access vendor tags will use the + * vendor tag definitions set this way. + * + * Returns OK on success, or a negative error code. + */ + static status_t setAsGlobalVendorTagDescriptor(const sp<VendorTagDescriptor>& desc); + + /** + * Clears the global vendor tag descriptor used by this process. + */ + static void clearGlobalVendorTagDescriptor(); + + /** + * Returns the global vendor tag descriptor used by this process. + * This will contain NULL if no vendor tags are defined. + */ + static sp<VendorTagDescriptor> getGlobalVendorTagDescriptor(); + protected: + VendorTagDescriptor(); + KeyedVector<uint32_t, String8> mTagToNameMap; + KeyedVector<uint32_t, String8> mTagToSectionMap; + KeyedVector<uint32_t, int32_t> mTagToTypeMap; + // must be int32_t to be compatible with Parcel::writeInt32 + int32_t mTagCount; + private: + vendor_tag_ops mVendorOps; +}; + +} /* namespace android */ + +#define VENDOR_TAG_DESCRIPTOR_H +#endif /* VENDOR_TAG_DESCRIPTOR_H */ 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 @@ public: // 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..f98002d 100644 --- a/include/media/AudioEffect.h +++ b/include/media/AudioEffect.h @@ -36,7 +36,7 @@ namespace android { // ---------------------------------------------------------------------------- -class effect_param_cblk_t; +struct effect_param_cblk_t; // ---------------------------------------------------------------------------- @@ -217,8 +217,9 @@ public: * higher priorities, 0 being the normal priority. * cbf: optional callback function (see effect_callback_t) * user: pointer to context for use by the callback receiver. - * sessionID: audio session this effect is associated to. If 0, the effect will be global to - * the output mix. If not 0, the effect will be applied to all players + * sessionID: audio session this effect is associated to. + * If equal to AUDIO_SESSION_OUTPUT_MIX, the effect will be global to + * the output mix. Otherwise, the effect will be applied to all players * (AudioTrack or MediaPLayer) within the same audio session. * io: HAL audio output or input stream to which this effect must be attached. Leave at 0 for * automatic output selection by AudioFlinger. @@ -229,8 +230,8 @@ public: int32_t priority = 0, effect_callback_t cbf = NULL, void* user = NULL, - int sessionId = 0, - audio_io_handle_t io = 0 + int sessionId = AUDIO_SESSION_OUTPUT_MIX, + audio_io_handle_t io = AUDIO_IO_HANDLE_NONE ); /* Constructor. @@ -241,8 +242,8 @@ public: int32_t priority = 0, effect_callback_t cbf = NULL, void* user = NULL, - int sessionId = 0, - audio_io_handle_t io = 0 + int sessionId = AUDIO_SESSION_OUTPUT_MIX, + audio_io_handle_t io = AUDIO_IO_HANDLE_NONE ); /* Terminates the AudioEffect and unregisters it from AudioFlinger. @@ -263,8 +264,8 @@ public: int32_t priority = 0, effect_callback_t cbf = NULL, void* user = NULL, - int sessionId = 0, - audio_io_handle_t io = 0 + int sessionId = AUDIO_SESSION_OUTPUT_MIX, + audio_io_handle_t io = AUDIO_IO_HANDLE_NONE ); /* Result of constructing the AudioEffect. This must be checked diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index 052064d..b3c44a8 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -26,7 +26,7 @@ namespace android { // ---------------------------------------------------------------------------- -class audio_track_cblk_t; +struct audio_track_cblk_t; class AudioRecordClientProxy; // ---------------------------------------------------------------------------- @@ -39,8 +39,12 @@ public: * 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 @@ -60,9 +64,10 @@ public: size_t frameCount; // number of sample frames corresponding to size; // on input it is the number of frames available, // on output is the number of frames actually drained - // (currently ignored, but will make the primary field in future) + // (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 @@ public: /* 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 @@ public: * - 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 @@ public: /* 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 @@ public: * 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 @@ public: 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 @@ public: * 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 @@ public: 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 @@ public: 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 @@ public: * 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 @@ public: * 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 @@ private: 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 @@ private: // 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' @@ -446,12 +458,13 @@ private: // notification callback uint32_t mNotificationFramesAct; // actual number of frames between each // notification callback - bool mRefreshRemaining; // processAudioBuffer() should refresh next 2 + bool mRefreshRemaining; // processAudioBuffer() should refresh + // mRemainingFrames and mRetryOnPartialBuffer // 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; @@ -460,9 +473,13 @@ private: 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 @@ -473,12 +490,11 @@ private: 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 4c22412..402b479 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -67,20 +67,24 @@ public: // 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 @@ public: 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); @@ -107,19 +113,18 @@ public: // returns the audio output stream latency in ms. Corresponds to // audio_stream_out->get_latency() static status_t getLatency(audio_io_handle_t output, - audio_stream_type_t stream, uint32_t* latency); 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); static status_t setVoiceVolume(float volume); // return the number of audio frames written by AudioFlinger to audio HAL and - // audio dsp to DAC since the output on which the specified stream is playing - // has exited standby. + // audio dsp to DAC since the specified output I/O handle has exited standby. // returned status (from utils/Errors.h) can be: // - NO_ERROR: successful operation, halFrames and dspFrames point to valid data // - INVALID_OPERATION: Not supported on current hardware platform @@ -128,15 +133,20 @@ public: // necessary to check returned status before using the returned values. static status_t getRenderPosition(audio_io_handle_t output, uint32_t *halFrames, - uint32_t *dspFrames, - audio_stream_type_t stream = AUDIO_STREAM_DEFAULT); + uint32_t *dspFrames); // 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); + // Allocate a new audio session ID and return that new ID. + // If unable to contact AudioFlinger, returns AUDIO_SESSION_ALLOCATE instead. + // FIXME If AudioFlinger were to ever exhaust the session ID namespace, + // this method could fail by returning either AUDIO_SESSION_ALLOCATE + // or an unspecified existing session ID. 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 { @@ -155,7 +165,8 @@ public: class OutputDescriptor { public: OutputDescriptor() - : samplingRate(0), format(AUDIO_FORMAT_DEFAULT), channelMask(0), frameCount(0), latency(0) {} + : samplingRate(0), format(AUDIO_FORMAT_DEFAULT), channelMask(0), frameCount(0), latency(0) + {} uint32_t samplingRate; audio_format_t format; @@ -193,24 +204,32 @@ public: 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); @@ -302,8 +321,6 @@ private: static sp<IAudioPolicyService> gAudioPolicyService; - // mapping between stream types and outputs - static DefaultKeyedVector<audio_stream_type_t, audio_io_handle_t> gStreamOutputMap; // list of output descriptors containing cached parameters // (sampling rate, framecount, channel count...) static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs; 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 @@ public: 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 4736369..2c48bbf 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -27,7 +27,7 @@ namespace android { // ---------------------------------------------------------------------------- -class audio_track_cblk_t; +struct audio_track_cblk_t; class AudioTrackClientProxy; class StaticAudioTrackClientProxy; @@ -36,11 +36,6 @@ class StaticAudioTrackClientProxy; class AudioTrack : public RefBase { public: - enum channel_index { - MONO = 0, - LEFT = 0, - RIGHT = 1 - }; /* Events used by AudioTrack callback function (callback_t). * Keep in sync with frameworks/base/media/java/android/media/AudioTrack.java NATIVE_EVENT_*. @@ -82,6 +77,7 @@ public: // (currently ignored, but will make the primary field in future) size_t size; // input/output in bytes == frameCount * frameSize + // on input it is unused // on output is the number of bytes actually filled // FIXME this is redundant with respect to frameCount, // and TRANSFER_OBTAIN mode is broken for 8-bit data @@ -91,7 +87,7 @@ public: void* raw; short* i16; // signed 16-bit int8_t* i8; // unsigned 8-bit, offset by 0x80 - }; + }; // input: unused, output: pointer to buffer }; /* As a convenience, if a callback is supplied, a handler thread @@ -123,6 +119,8 @@ public: * - 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 +156,7 @@ public: * 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,15 +178,16 @@ public: 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, - int uid = -1); + 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. @@ -209,11 +208,12 @@ public: 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, - int uid = -1); + int uid = -1, + pid_t pid = -1); /* Terminates the AudioTrack and unregisters it from AudioFlinger. * Also destroys all resources associated with the AudioTrack. @@ -241,17 +241,18 @@ public: 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, - int uid = -1); + 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 @@ -279,7 +280,7 @@ public: 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; } @@ -336,7 +337,7 @@ public: */ 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. @@ -361,7 +362,7 @@ public: /* 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. * @@ -450,9 +451,10 @@ public: * none. * * Returned value: - * handle on audio hardware output + * handle on audio hardware output, or AUDIO_IO_HANDLE_NONE if the + * track needed to be re-created but that failed */ - audio_io_handle_t getOutput(); + audio_io_handle_t getOutput() const; /* Returns the unique session ID associated with this track. * @@ -528,15 +530,6 @@ private: struct timespec *elapsed = NULL, size_t *nonContig = NULL); public: -//EL_FIXME to be reconciled with new obtainBuffer() return codes and control block proxy -// enum { -// NO_MORE_BUFFERS = 0x80000001, // same name in AudioFlinger.h, ok to be different value -// TEAR_DOWN = 0x80000002, -// STOPPED = 1, -// STREAM_END_WAIT, -// STREAM_END -// }; - /* Release a filled buffer of "audioBuffer->frameCount" frames for AudioFlinger to process. */ // FIXME make private when obtainBuffer() for TRANSFER_OBTAIN is removed void releaseBuffer(Buffer* audioBuffer); @@ -551,8 +544,11 @@ public: * WOULD_BLOCK when obtainBuffer() returns same, or * AudioTrack was stopped during the write * or any other error code returned by IAudioTrack::start() or restoreTrack_l(). + * Default behavior is to only return until all data has been transferred. Set 'blocking' to + * false for the method to return immediately without waiting to try multiple times to write + * the full content of the buffer. */ - ssize_t write(const void* buffer, size_t size); + ssize_t write(const void* buffer, size_t size, bool blocking = true); /* * Dumps the state of an audio track. @@ -566,7 +562,7 @@ public: 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); @@ -626,53 +622,50 @@ protected: // 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; mutable uint32_t mSampleRate; // mutable because getSampleRate() can update it. - size_t mFrameCount; // corresponds to current IAudioTrack - size_t mReqFrameCount; // frame count to request the next time a new - // IAudioTrack is needed - + 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. @@ -705,21 +698,25 @@ protected: uint32_t mNotificationFramesAct; // actual number of frames between each // notification callback, // at initial source sample rate - bool mRefreshRemaining; // processAudioBuffer() should refresh next 2 + bool mRefreshRemaining; // processAudioBuffer() should refresh + // mRemainingFrames and mRetryOnPartialBuffer // 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() 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; @@ -739,7 +736,6 @@ protected: sp<AudioTrackClientProxy> mProxy; // primary owner of the memory bool mInUnderrun; // whether track is currently in underrun state - String8 mName; // server's name for this IAudioTrack uint32_t mPausedPosition; private: @@ -754,8 +750,8 @@ private: 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/IAudioFlinger.h b/include/media/IAudioFlinger.h index 282f275..9101f06 100644 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -64,25 +64,27 @@ public: 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, - // input: ignored - // 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, @@ -163,7 +165,7 @@ public: 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; @@ -174,8 +176,8 @@ public: 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; @@ -188,6 +190,7 @@ public: 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/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/media/libstagefright/chromium_http/chromium_http_stub.cpp b/include/media/IMediaHTTPService.h index 289f6de..f66d6c8 100644 --- a/media/libstagefright/chromium_http/chromium_http_stub.cpp +++ b/include/media/IMediaHTTPService.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * 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. @@ -14,25 +14,28 @@ * limitations under the License. */ -#include <dlfcn.h> +#ifndef I_MEDIA_HTTP_SERVICE_H_ -#include <include/chromium_http_stub.h> -#include <include/ChromiumHTTPDataSource.h> -#include <include/DataUriSource.h> +#define I_MEDIA_HTTP_SERVICE_H_ + +#include <binder/IInterface.h> +#include <media/stagefright/foundation/ABase.h> namespace android { -HTTPBase *createChromiumHTTPDataSource(uint32_t flags) { - return new ChromiumHTTPDataSource(flags); -} +struct IMediaHTTPConnection; + +/** MUST stay in sync with IMediaHTTPService.aidl */ + +struct IMediaHTTPService : public IInterface { + DECLARE_META_INTERFACE(MediaHTTPService); + + virtual sp<IMediaHTTPConnection> makeHTTPConnection() = 0; -status_t UpdateChromiumHTTPDataSourceProxyConfig( - const char *host, int32_t port, const char *exclusionList) { - return ChromiumHTTPDataSource::UpdateProxyConfig(host, port, exclusionList); -} +private: + DISALLOW_EVIL_CONSTRUCTORS(IMediaHTTPService); +}; -DataSource *createDataUriSource(const char *uri) { - return new DataUriSource(uri); -} +} // 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 @@ public: 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 Parcel; class Surface; class IStreamSource; class IGraphicBufferProducer; +struct IMediaHTTPService; class IMediaPlayer: public IInterface { @@ -41,8 +42,11 @@ public: 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 @@ namespace android { struct ICrypto; struct IDrm; struct IHDCP; +struct IMediaHTTPService; class IMediaRecorder; class IOMX; class IRemoteDisplay; @@ -49,9 +50,14 @@ public: 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 @@ public: 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 6643736..f6f9e7a 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -143,6 +143,8 @@ public: 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 @@ public: 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 26d8729..87717da 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -137,6 +137,7 @@ public: } virtual status_t setDataSource( + const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers = NULL) = 0; @@ -213,11 +214,6 @@ public: 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 @@ public: 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 4c05fc3..3ca3095 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -189,6 +189,8 @@ public: virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0; }; +struct IMediaHTTPService; + class MediaPlayer : public BnMediaPlayerClient, public virtual IMediaDeathNotifier { @@ -199,6 +201,7 @@ public: void disconnect(); status_t setDataSource( + const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers); @@ -220,13 +223,19 @@ public: status_t getDuration(int *msec); status_t reset(); status_t setAudioStreamType(audio_stream_type_t type); + status_t getAudioStreamType(audio_stream_type_t *type); status_t setLooping(int loop); 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); @@ -242,9 +251,6 @@ public: 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 @@ typedef void (*media_completion_f)(status_t status, void *cookie); 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 @@ namespace android { class MediaScannerClient; class StringArray; +class CharacterEncodingDetector; enum MediaScanResult { // This file or directory was scanned successfully. @@ -94,15 +96,9 @@ public: 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 @@ namespace android { 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 @@ public: // 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 @@ public: // 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 @@ public: // 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..be0c15b 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 @@ enum { // 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 { +// FIXME make this a class, and change Format_... global methods to class methods +//private: + unsigned mSampleRate; + unsigned mChannelCount; + audio_format_t mFormat; + size_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 rename +NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount, audio_format_t format); // 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ class Pipe : public NBAIO_Sink { 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 @@ public: 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 7ba5acc..863a7d5 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -67,8 +67,6 @@ struct ACodec : public AHierarchicalStateMachine { void signalRequestIDRFrame(); - bool isConfiguredForAdaptivePlayback() { return mIsConfiguredForAdaptivePlayback; } - struct PortDescription : public RefBase { size_t countBuffers(); IOMX::buffer_id bufferIDAt(size_t index) const; @@ -178,6 +176,8 @@ private: sp<MemoryDealer> mDealer[2]; sp<ANativeWindow> mNativeWindow; + sp<AMessage> mInputFormat; + sp<AMessage> mOutputFormat; Vector<BufferInfo> mBuffers[2]; bool mPortEOS[2]; @@ -189,7 +189,6 @@ private: bool mIsEncoder; bool mUseMetadataOnEncoderOutput; bool mShutdownInProgress; - bool mIsConfiguredForAdaptivePlayback; // If "mKeepComponentAllocated" we only transition back to Loaded state // and do not release the component instance. @@ -203,10 +202,16 @@ private: unsigned mDequeueCounter; bool mStoreMetaDataInOutputBuffers; int32_t mMetaDataBuffersToSubmit; + size_t mNumUndequeuedBuffers; 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); status_t freeBuffersOnPort(OMX_U32 portIndex); @@ -300,6 +305,7 @@ private: void processDeferredMessages(); void sendFormatChange(const sp<AMessage> &reply); + status_t getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify); void signalError( OMX_ERRORTYPE error = OMX_ErrorUndefined, 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 @@ protected: 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 @@ protected: virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType, const sp<IMemory> &data); + void releaseCamera(); + private: friend class CameraSourceListener; @@ -233,7 +235,6 @@ private: int32_t frameRate); void stopCameraRecording(); - void releaseCamera(); status_t reset(); CameraSource(const CameraSource &); diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h index 157b1aa..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 @@ public: }; static sp<DataSource> CreateFromURI( + const sp<IMediaHTTPService> &httpService, const char *uri, const KeyedVector<String8, String8> *headers = NULL); 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/FileSource.h b/include/media/stagefright/FileSource.h index d994cb3..9838ed2 100644 --- a/include/media/stagefright/FileSource.h +++ b/include/media/stagefright/FileSource.h @@ -30,6 +30,7 @@ namespace android { class FileSource : public DataSource { public: FileSource(const char *filename); + // FileSource takes ownership and will close the fd FileSource(int fd, int64_t offset, int64_t length); virtual status_t initCheck() const; diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h index 76aa503..276543b 100644 --- a/include/media/stagefright/MediaCodec.h +++ b/include/media/stagefright/MediaCodec.h @@ -106,6 +106,7 @@ struct MediaCodec : public AHandler { status_t signalEndOfInputStream(); status_t getOutputFormat(sp<AMessage> *format) const; + status_t getInputFormat(sp<AMessage> *format) const; status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const; status_t getOutputBuffers(Vector<sp<ABuffer> > *buffers) const; @@ -159,6 +160,7 @@ private: kWhatGetBuffers = 'getB', kWhatFlush = 'flus', kWhatGetOutputFormat = 'getO', + kWhatGetInputFormat = 'getI', kWhatDequeueInputTimedOut = 'dITO', kWhatDequeueOutputTimedOut = 'dOTO', kWhatCodecNotify = 'codc', @@ -199,6 +201,7 @@ private: sp<Surface> mNativeWindow; SoftwareRenderer *mSoftRenderer; sp<AMessage> mOutputFormat; + sp<AMessage> mInputFormat; List<size_t> mAvailPortBuffers[2]; Vector<BufferInfo> mPortBuffers[2]; diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h index 590623b..01a5daf 100644 --- a/include/media/stagefright/MediaCodecList.h +++ b/include/media/stagefright/MediaCodecList.h @@ -60,6 +60,7 @@ private: SECTION_DECODER, SECTION_ENCODERS, SECTION_ENCODER, + SECTION_INCLUDE, }; struct CodecInfo { @@ -73,7 +74,9 @@ private: status_t mInitCheck; Section mCurrentSection; + Vector<Section> mPastSections; int32_t mDepth; + AString mHrefBase; Vector<CodecInfo> mCodecInfos; KeyedVector<AString, size_t> mCodecQuirks; @@ -83,7 +86,8 @@ private: ~MediaCodecList(); status_t initCheck() const; - void parseXMLFile(FILE *file); + void parseXMLFile(const char *path); + void parseTopLevelXMLFile(const char *path); static void StartElementHandlerWrapper( void *me, const char *name, const char **attrs); @@ -93,6 +97,7 @@ private: void startElementHandler(const char *name, const char **attrs); void endElementHandler(const char *name); + status_t includeXMLFile(const char **attrs); status_t addMediaCodecFromAttributes(bool encoder, const char **attrs); void addMediaCodec(bool encoder, const char *name, const char *type = NULL); 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 cf5beda..678d642 100644 --- a/include/media/stagefright/MediaDefs.h +++ b/include/media/stagefright/MediaDefs.h @@ -38,6 +38,7 @@ extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II; 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; 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/MediaMuxer.h b/include/media/stagefright/MediaMuxer.h index ff6a66e..bbe4303 100644 --- a/include/media/stagefright/MediaMuxer.h +++ b/include/media/stagefright/MediaMuxer.h @@ -30,7 +30,7 @@ struct MediaAdapter; struct MediaBuffer; struct MediaSource; struct MetaData; -struct MPEG4Writer; +struct MediaWriter; // MediaMuxer is used to mux multiple tracks into a video. Currently, we only // support a mp4 file as the output. @@ -44,6 +44,7 @@ public: // OutputFormat is updated. enum OutputFormat { OUTPUT_FORMAT_MPEG_4 = 0, + OUTPUT_FORMAT_WEBM = 1, OUTPUT_FORMAT_LIST_END // must be last - used to validate format type }; @@ -115,7 +116,8 @@ public: int64_t timeUs, uint32_t flags) ; private: - sp<MPEG4Writer> mWriter; + const OutputFormat mFormat; + sp<MediaWriter> mWriter; Vector< sp<MediaAdapter> > mTrackList; // Each track has its MediaAdapter. sp<MetaData> mFileMeta; // Metadata for the whole file. 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 @@ struct MediaSource : public virtual RefBase { // 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 db8216b..e862ec3 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -56,6 +56,9 @@ enum { 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) 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 @@ namespace android { struct ABuffer; struct AMessage; struct DataSource; +struct IMediaHTTPService; struct MediaBuffer; struct MediaExtractor; struct MediaSource; @@ -45,6 +46,7 @@ struct NuMediaExtractor : public RefBase { NuMediaExtractor(); status_t setDataSource( + const sp<IMediaHTTPService> &httpService, const char *path, const KeyedVector<String8, String8> *headers = NULL); diff --git a/include/media/stagefright/SkipCutBuffer.h b/include/media/stagefright/SkipCutBuffer.h index 2653b53..098aa69 100644 --- a/include/media/stagefright/SkipCutBuffer.h +++ b/include/media/stagefright/SkipCutBuffer.h @@ -47,6 +47,7 @@ class SkipCutBuffer: public RefBase { private: void write(const char *src, size_t num); size_t read(char *dst, size_t num); + int32_t mSkip; int32_t mFrontPadding; int32_t mBackPadding; int32_t mWriteHead; diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h index db5f947..59e83c2 100644 --- a/include/media/stagefright/SurfaceMediaSource.h +++ b/include/media/stagefright/SurfaceMediaSource.h @@ -139,6 +139,10 @@ protected: // frames is separate than the one calling stop. virtual void onBuffersReleased(); + // SurfaceMediaSource can't handle sideband streams, so this is not expected + // to ever be called. Does nothing. + virtual void onSidebandStreamChanged(); + static bool isExternalFormat(uint32_t format); private: 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 DataSource; class TimedTextDriver { public: - TimedTextDriver(const wp<MediaPlayerBase> &listener); + TimedTextDriver( + const wp<MediaPlayerBase> &listener, + const sp<IMediaHTTPService> &httpService); ~TimedTextDriver(); @@ -77,6 +80,7 @@ private: 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 2d033e6..3901e79 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -48,7 +48,7 @@ namespace android { #define CBLK_STREAM_END_DONE 0x400 // set by server on render completion, cleared by client //EL_FIXME 20 seconds may not be enough and must be reconciled with new obtainBuffer implementation -#define MAX_RUN_OFFLOADED_TIMEOUT_MS 20000 //assuming upto a maximum of 20 seconds of offloaded +#define MAX_RUN_OFFLOADED_TIMEOUT_MS 20000 // assuming up to a maximum of 20 seconds of offloaded struct AudioTrackSharedStreaming { // similar to NBAIO MonoPipe @@ -98,11 +98,7 @@ struct audio_track_cblk_t // 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() diff --git a/libvideoeditor/lvpp/Android.mk b/libvideoeditor/lvpp/Android.mk index 06c2e6a..8318d28 100755 --- a/libvideoeditor/lvpp/Android.mk +++ b/libvideoeditor/lvpp/Android.mk @@ -71,7 +71,6 @@ LOCAL_C_INCLUDES += \ $(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/NativeWindowRenderer.cpp b/libvideoeditor/lvpp/NativeWindowRenderer.cpp index 8b362ef..be0f747 100755 --- a/libvideoeditor/lvpp/NativeWindowRenderer.cpp +++ b/libvideoeditor/lvpp/NativeWindowRenderer.cpp @@ -568,9 +568,11 @@ void NativeWindowRenderer::destroyRenderInput(RenderInput* input) { RenderInput::RenderInput(NativeWindowRenderer* renderer, GLuint textureId) : mRenderer(renderer) , mTextureId(textureId) { - sp<BufferQueue> bq = new BufferQueue(); - mST = new GLConsumer(bq, mTextureId); - mSTC = new Surface(bq); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mST = new GLConsumer(consumer, mTextureId); + mSTC = new Surface(producer); native_window_connect(mSTC.get(), NATIVE_WINDOW_API_MEDIA); } 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 @@ status_t PreviewPlayer::finishSetDataSource_l() { 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 cb4b23e..e60030e 100755 --- a/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp +++ b/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp @@ -534,7 +534,8 @@ status_t VideoEditorAudioPlayer::start(bool sourceAlreadyStarted) { mAudioTrack = new AudioTrack( AUDIO_STREAM_MUSIC, mSampleRate, AUDIO_FORMAT_PCM_16_BIT, audio_channel_out_mask_from_count(numChannels), - 0, AUDIO_OUTPUT_FLAG_NONE, &AudioCallback, this, 0); + 0 /*frameCount*/, AUDIO_OUTPUT_FLAG_NONE, &AudioCallback, this, + 0 /*notificationFrames*/); if ((err = mAudioTrack->initCheck()) != OK) { mAudioTrack.clear(); diff --git a/libvideoeditor/lvpp/VideoEditorPlayer.cpp b/libvideoeditor/lvpp/VideoEditorPlayer.cpp index 8d656c4..f9c3879 100755 --- a/libvideoeditor/lvpp/VideoEditorPlayer.cpp +++ b/libvideoeditor/lvpp/VideoEditorPlayer.cpp @@ -57,6 +57,7 @@ status_t VideoEditorPlayer::setAudioPlayer(VideoEditorAudioPlayer *audioPlayer) status_t VideoEditorPlayer::setDataSource( + const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers) { ALOGI("setDataSource('%s')", url); if (headers != NULL) { diff --git a/libvideoeditor/lvpp/VideoEditorPlayer.h b/libvideoeditor/lvpp/VideoEditorPlayer.h index b8c1254..781e4bc 100755 --- a/libvideoeditor/lvpp/VideoEditorPlayer.h +++ b/libvideoeditor/lvpp/VideoEditorPlayer.h @@ -98,6 +98,7 @@ public: 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 c3cd3d0..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 @@ M4OSA_ERR VideoEditorPreviewController::preparePlayer( 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); 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 @@ LOCAL_C_INCLUDES += \ $(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/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk index dd2d306..c92c543 100644 --- a/media/libeffects/visualizer/Android.mk +++ b/media/libeffects/visualizer/Android.mk @@ -17,7 +17,6 @@ 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/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 @@ LOCAL_SRC_FILES:= \ AudioRecord.cpp \ AudioSystem.cpp \ mediaplayer.cpp \ + IMediaHTTPConnection.cpp \ + IMediaHTTPService.cpp \ IMediaLogService.cpp \ IMediaPlayerService.cpp \ IMediaPlayerClient.cpp \ @@ -44,7 +46,7 @@ LOCAL_SRC_FILES:= \ IAudioPolicyService.cpp \ MediaScanner.cpp \ MediaScannerClient.cpp \ - autodetect.cpp \ + CharacterEncodingDetector.cpp \ IMediaDeathNotifier.cpp \ MediaProfiles.cpp \ IEffect.cpp \ @@ -58,26 +60,34 @@ LOCAL_SRC_FILES:= \ 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::enableStatusChanged(bool enabled) } 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 ccbc5a3..a7bf380 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -41,37 +41,29 @@ status_t AudioRecord::getMinFrameCount( 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 @@ AudioRecord::AudioRecord( 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 @@ AudioRecord::~AudioRecord() 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 @@ status_t AudioRecord::set( 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 @@ status_t AudioRecord::set( } 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 @@ status_t AudioRecord::set( // 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 @@ status_t AudioRecord::set( 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 @@ status_t AudioRecord::set( 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 @@ status_t AudioRecord::set( 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); } - mStatus = NO_ERROR; + // create the IAudioRecord + status = openRecord_l(0 /*epoch*/); - // Update buffer size in case it has been limited by AudioFlinger during track creation - mFrameCount = mCblk->frameCount_; + if (status != NO_ERROR) { + if (mAudioRecordThread != 0) { + mAudioRecordThread->requestExit(); // see comment in AudioRecord.h + mAudioRecordThread->requestExitAndWait(); + mAudioRecordThread.clear(); + } + return status; + } + mStatus = NO_ERROR; mActive = false; - mCbf = cbf; - mRefreshRemaining = true; mUserData = user; // TODO: add audio hardware input latency here mLatency = (1000*mFrameCount) / sampleRate; @@ -268,7 +261,7 @@ status_t AudioRecord::set( mMarkerReached = false; mNewPosition = 0; mUpdatePeriod = 0; - AudioSystem::acquireAudioSessionId(mSessionId); + AudioSystem::acquireAudioSessionId(mSessionId, -1); mSequence = 1; mObservedSequence = mSequence; mInOverrun = false; @@ -289,6 +282,9 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) // 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 @@ bool AudioRecord::stopped() const 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::getMarkerPosition(uint32_t *marker) const 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 @@ status_t AudioRecord::getPosition(uint32_t *position) const 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,55 +428,82 @@ status_t AudioRecord::openRecord_l(size_t epoch) 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); + if (input == AUDIO_IO_HANDLE_NONE) { + 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 || status != NO_ERROR) { + 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"); @@ -489,38 +514,56 @@ status_t AudioRecord::openRecord_l(size_t epoch) 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*>(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); @@ -530,6 +573,14 @@ status_t AudioRecord::openRecord_l(size_t epoch) 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) @@ -591,6 +642,9 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *r if (newSequence == oldSequence) { status = restoreRecord_l("obtainBuffer"); if (status != NO_ERROR) { + buffer.mFrameCount = 0; + buffer.mRaw = NULL; + buffer.mNonContig = 0; break; } } @@ -692,7 +746,7 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize) // ------------------------------------------------------------------------- -nsecs_t AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread) +nsecs_t AudioRecord::processAudioBuffer() { mLock.lock(); if (mAwaitBoost) { @@ -760,17 +814,17 @@ nsecs_t AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread) } // 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(); @@ -844,8 +898,8 @@ nsecs_t AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread) "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; @@ -954,7 +1008,7 @@ status_t AudioRecord::restoreRecord_l(const char *from) // ========================================================================= -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) { @@ -966,7 +1020,8 @@ void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who) // ========================================================================= 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) { } @@ -983,6 +1038,10 @@ bool AudioRecord::AudioRecordThread::threadLoop() // caller will check for exitPending() return true; } + if (mIgnoreNextPausedInt) { + mIgnoreNextPausedInt = false; + mPausedInt = false; + } if (mPausedInt) { if (mPausedNs > 0) { (void) mMyCond.waitRelative(mMyLock, mPausedNs); @@ -993,7 +1052,7 @@ bool AudioRecord::AudioRecordThread::threadLoop() return true; } } - nsecs_t ns = mReceiver.processAudioBuffer(this); + nsecs_t ns = mReceiver.processAudioBuffer(); switch (ns) { case 0: return true; @@ -1017,12 +1076,7 @@ void AudioRecord::AudioRecordThread::requestExit() { // 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() @@ -1034,6 +1088,7 @@ void AudioRecord::AudioRecordThread::pause() 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 cc5b810..2f16444 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -35,15 +35,15 @@ Mutex AudioSystem::gLock; sp<IAudioFlinger> AudioSystem::gAudioFlinger; sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient; audio_error_callback AudioSystem::gAudioErrorCallback = NULL; -// Cached values -DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSystem::gOutputs(0); +// Cached values for output handles +DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSystem::gOutputs(NULL); // 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 @@ const sp<IAudioFlinger>& AudioSystem::get_audio_flinger() 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 @@ status_t AudioSystem::setMode(audio_mode_t mode) 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 @@ String8 AudioSystem::getParameters(audio_io_handle_t ioHandle, const String8& ke return result; } +status_t AudioSystem::setParameters(const String8& keyValuePairs) +{ + return setParameters(AUDIO_IO_HANDLE_NONE, keyValuePairs); +} + +String8 AudioSystem::getParameters(const String8& keys) +{ + return getParameters(AUDIO_IO_HANDLE_NONE, keys); +} + // convert volume steps to natural log scale // change this value to change volume scaling @@ -249,6 +263,11 @@ status_t AudioSystem::getSamplingRate(audio_io_handle_t output, *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); @@ -265,7 +284,7 @@ status_t AudioSystem::getOutputFrameCount(size_t* frameCount, audio_stream_type_ } output = getOutput(streamType); - if (output == 0) { + if (output == AUDIO_IO_HANDLE_NONE) { return PERMISSION_DENIED; } @@ -289,6 +308,11 @@ status_t AudioSystem::getFrameCount(audio_io_handle_t output, *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); @@ -305,15 +329,14 @@ status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t st } output = getOutput(streamType); - if (output == 0) { + if (output == AUDIO_IO_HANDLE_NONE) { return PERMISSION_DENIED; } - return getLatency(output, streamType, latency); + return getLatency(output, latency); } status_t AudioSystem::getLatency(audio_io_handle_t output, - audio_stream_type_t streamType, uint32_t* latency) { OutputDescriptor *outputDesc; @@ -330,7 +353,7 @@ status_t AudioSystem::getLatency(audio_io_handle_t output, gLock.unlock(); } - ALOGV("getLatency() streamType %d, output %d, latency %d", streamType, output, *latency); + ALOGV("getLatency() output %d, latency %d", output, *latency); return NO_ERROR; } @@ -349,6 +372,12 @@ status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t for 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; @@ -371,55 +400,52 @@ status_t AudioSystem::setVoiceVolume(float value) } status_t AudioSystem::getRenderPosition(audio_io_handle_t output, uint32_t *halFrames, - uint32_t *dspFrames, audio_stream_type_t stream) + uint32_t *dspFrames) { const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; - if (stream == AUDIO_STREAM_DEFAULT) { - stream = AUDIO_STREAM_MUSIC; - } - - if (output == 0) { - output = getOutput(stream); - } - 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; + if (ioHandle == AUDIO_IO_HANDLE_NONE) return result; result = af->getInputFramesLost(ioHandle); return result; } -int AudioSystem::newAudioSessionId() { +int AudioSystem::newAudioSessionId() +{ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) return 0; + if (af == 0) return AUDIO_SESSION_ALLOCATE; 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(); @@ -438,7 +464,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle const OutputDescriptor *desc; audio_stream_type_t stream; - if (ioHandle == 0) return; + if (ioHandle == AUDIO_IO_HANDLE_NONE) return; Mutex::Autolock _l(AudioSystem::gLock); @@ -455,7 +481,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle 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 +505,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle 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 +522,14 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle } } -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: @@ -702,14 +730,15 @@ uint32_t AudioSystem::getStrategyForStream(audio_stream_type_t stream) audio_devices_t AudioSystem::getDevicesForStream(audio_stream_type_t stream) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); - if (aps == 0) return (audio_devices_t)0; + if (aps == 0) return AUDIO_DEVICE_NONE; return aps->getDevicesForStream(stream); } 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 AUDIO_IO_HANDLE_NONE; return aps->getOutputForEffect(desc); } @@ -804,7 +833,8 @@ bool AudioSystem::isOffloadSupported(const audio_offload_info_t& info) // --------------------------------------------------------------------------- -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 3f3a88c..fbfd3da 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -44,9 +44,6 @@ status_t AudioTrack::getMinFrameCount( 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 @@ status_t AudioTrack::getMinFrameCount( // 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 @@ status_t AudioTrack::getMinFrameCount( *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; @@ -95,15 +109,16 @@ AudioTrack::AudioTrack( 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, - int uid) + int uid, + pid_t pid) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), @@ -113,7 +128,7 @@ AudioTrack::AudioTrack( mStatus = set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType, - offloadInfo, uid); + offloadInfo, uid, pid); } AudioTrack::AudioTrack( @@ -125,11 +140,12 @@ AudioTrack::AudioTrack( 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, - int uid) + int uid, + pid_t pid) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), @@ -138,7 +154,8 @@ AudioTrack::AudioTrack( { mStatus = set(streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, cbf, user, notificationFrames, - sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo, uid); + sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo, + uid, pid); } AudioTrack::~AudioTrack() @@ -157,7 +174,9 @@ AudioTrack::~AudioTrack() 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); } } @@ -166,18 +185,24 @@ status_t AudioTrack::set( 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, - int uid) + 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) { @@ -211,15 +236,9 @@ status_t AudioTrack::set( 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()); @@ -233,19 +252,24 @@ status_t AudioTrack::set( 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; @@ -253,15 +277,21 @@ status_t AudioTrack::set( 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) { @@ -285,14 +315,6 @@ status_t AudioTrack::set( 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); @@ -301,30 +323,36 @@ status_t AudioTrack::set( 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; + mVolume[AUDIO_INTERLEAVE_LEFT] = 1.0f; + mVolume[AUDIO_INTERLEAVE_RIGHT] = 1.0f; mSendLevel = 0.0f; - mFrameCount = frameCount; + // mFrameCount is initialized in createTrack_l mReqFrameCount = frameCount; mNotificationFramesReq = notificationFrames; mNotificationFramesAct = 0; mSessionId = sessionId; - if (uid == -1 || (IPCThreadState::self()->getCallingPid() != getpid())) { + 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; @@ -335,14 +363,7 @@ status_t AudioTrack::set( } // 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) { @@ -350,17 +371,10 @@ status_t AudioTrack::set( mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } - //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); return status; } mStatus = NO_ERROR; - mStreamType = streamType; - mFormat = format; - mSharedBuffer = sharedBuffer; mState = STATE_STOPPED; mUserData = user; mLoopPeriod = 0; @@ -368,11 +382,10 @@ status_t AudioTrack::set( mMarkerReached = false; mNewPosition = 0; mUpdatePeriod = 0; - AudioSystem::acquireAudioSessionId(mSessionId); + AudioSystem::acquireAudioSessionId(mSessionId, mClientPid); mSequence = 1; mObservedSequence = mSequence; mInUnderrun = false; - mOutput = output; return NO_ERROR; } @@ -448,12 +461,11 @@ status_t AudioTrack::start() 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; @@ -475,7 +487,7 @@ void AudioTrack::stop() sp<AudioTrackThread> t = mAudioTrackThread; if (t != 0) { - if (!isOffloaded()) { + if (!isOffloaded_l()) { t->pause(); } } else { @@ -513,7 +525,7 @@ void AudioTrack::flush_l() mRefreshRemaining = true; mState = STATE_FLUSHED; - if (isOffloaded()) { + if (isOffloaded_l()) { mProxy->interrupt(); } mProxy->flush(); @@ -533,8 +545,8 @@ void AudioTrack::pause() mProxy->interrupt(); mAudioTrack->pause(); - if (isOffloaded()) { - if (mOutput != 0) { + if (isOffloaded_l()) { + if (mOutput != AUDIO_IO_HANDLE_NONE) { uint32_t halFrames; // OffloadThread sends HAL pause in its threadLoop.. time saved // here can be slightly off @@ -551,12 +563,12 @@ status_t AudioTrack::setVolume(float left, float right) } AutoMutex lock(mLock); - mVolume[LEFT] = left; - mVolume[RIGHT] = right; + mVolume[AUDIO_INTERLEAVE_LEFT] = left; + mVolume[AUDIO_INTERLEAVE_RIGHT] = right; mProxy->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000)); - if (isOffloaded()) { + if (isOffloaded_l()) { mAudioTrack->signal(); } return NO_ERROR; @@ -620,8 +632,8 @@ uint32_t AudioTrack::getSampleRate() const // 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()) { - if (mOutput != 0) { + if (isOffloaded_l()) { + if (mOutput != AUDIO_IO_HANDLE_NONE) { uint32_t sampleRate = 0; status_t status = AudioSystem::getSamplingRate(mOutput, mStreamType, &sampleRate); if (status == NO_ERROR) { @@ -704,6 +716,7 @@ status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) AutoMutex lock(mLock); mNewPosition = mProxy->getPosition() + updatePeriod; mUpdatePeriod = updatePeriod; + return NO_ERROR; } @@ -757,7 +770,7 @@ status_t AudioTrack::getPosition(uint32_t *position) const } AutoMutex lock(mLock); - if (isOffloaded()) { + if (isOffloaded_l()) { uint32_t dspFrames = 0; if ((mState == STATE_PAUSED) || (mState == STATE_PAUSED_STOPPING)) { @@ -766,7 +779,7 @@ status_t AudioTrack::getPosition(uint32_t *position) const return NO_ERROR; } - if (mOutput != 0) { + if (mOutput != AUDIO_IO_HANDLE_NONE) { uint32_t halFrames; AudioSystem::getRenderPosition(mOutput, &halFrames, &dspFrames); } @@ -812,23 +825,12 @@ status_t AudioTrack::reload() 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); @@ -842,15 +844,7 @@ status_t AudioTrack::attachAuxEffect(int effectId) // ------------------------------------------------------------------------- // 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(); @@ -859,50 +853,57 @@ status_t AudioTrack::createTrack_l( return NO_INIT; } + audio_io_handle_t output = AudioSystem::getOutput(mStreamType, mSampleRate, mFormat, + mChannelMask, mFlags, mOffloadInfo); + if (output == AUDIO_IO_HANDLE_NONE) { + 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, &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); - if ((flags & AUDIO_OUTPUT_FLAG_FAST) && sampleRate != afSampleRate) { - ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client due to mismatching sample rate (%d vs %d)", - sampleRate, afSampleRate); - flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST); - } - // The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where // n = 1 fast track with single buffering; nBuffering is ignored // n = 2 fast track with double buffering @@ -910,43 +911,45 @@ status_t AudioTrack::createTrack_l( // 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 (((uintptr_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 @@ -958,10 +961,10 @@ status_t AudioTrack::createTrack_l( 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; @@ -986,52 +989,64 @@ status_t AudioTrack::createTrack_l( } 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 @@ -1039,12 +1054,13 @@ status_t AudioTrack::createTrack_l( 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) { + 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. @@ -1055,26 +1071,27 @@ status_t AudioTrack::createTrack_l( } 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 @@ -1082,15 +1099,16 @@ status_t AudioTrack::createTrack_l( // 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(). @@ -1099,15 +1117,15 @@ status_t AudioTrack::createTrack_l( } // update proxy - if (sharedBuffer == 0) { + if (mSharedBuffer == 0) { mStaticProxy.clear(); mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF); } else { mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF); mProxy = mStaticProxy; } - mProxy->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | - uint16_t(mVolume[LEFT] * 0x1000)); + mProxy->setVolumeLR((uint32_t(uint16_t(mVolume[AUDIO_INTERLEAVE_RIGHT] * 0x1000)) << 16) | + uint16_t(mVolume[AUDIO_INTERLEAVE_LEFT] * 0x1000)); mProxy->setSendLevel(mSendLevel); mProxy->setSampleRate(mSampleRate); mProxy->setEpoch(epoch); @@ -1117,6 +1135,14 @@ status_t AudioTrack::createTrack_l( 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) @@ -1244,8 +1270,7 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer) if (mState == STATE_ACTIVE) { audio_track_cblk_t* cblk = mCblk; if (android_atomic_and(~CBLK_DISABLED, &cblk->mFlags) & CBLK_DISABLED) { - ALOGW("releaseBuffer() track %p name=%s disabled due to previous underrun, restarting", - this, mName.string()); + ALOGW("releaseBuffer() track %p disabled due to previous underrun, restarting", this); // FIXME ignoring status mAudioTrack->start(); } @@ -1254,7 +1279,7 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer) // ------------------------------------------------------------------------- -ssize_t AudioTrack::write(const void* buffer, size_t userSize) +ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking) { if (mTransfer != TRANSFER_SYNC || mIsTimed) { return INVALID_OPERATION; @@ -1273,7 +1298,8 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) while (userSize >= mFrameSize) { audioBuffer.frameCount = userSize / mFrameSize; - status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever); + status_t err = obtainBuffer(&audioBuffer, + blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking); if (err < 0) { if (written > 0) { break; @@ -1369,7 +1395,7 @@ status_t TimedAudioTrack::setMediaTimeTransform(const LinearTransform& xform, // ------------------------------------------------------------------------- -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? @@ -1407,7 +1433,7 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) // 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 @@ -1462,7 +1488,7 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) // 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; @@ -1626,7 +1652,6 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) 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) { @@ -1692,22 +1717,19 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) 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 @@ -1715,14 +1737,7 @@ status_t AudioTrack::restoreTrack_l(const char *from) // 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 @@ -1750,10 +1765,6 @@ status_t AudioTrack::restoreTrack_l(const char *from) } } if (result != NO_ERROR) { - //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); ALOGW("restoreTrack_l() failed status %d", result); mState = STATE_STOPPED; } @@ -1786,14 +1797,21 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) String8 AudioTrack::getParameters(const String8& keys) { - if (mOutput) { - return AudioSystem::getParameters(mOutput, keys); + audio_io_handle_t output = getOutput(); + if (output != AUDIO_IO_HANDLE_NONE) { + 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; @@ -1823,7 +1841,7 @@ uint32_t AudioTrack::getUnderrunFrames() const // ========================================================================= -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) { @@ -1867,7 +1885,7 @@ bool AudioTrack::AudioTrackThread::threadLoop() return true; } } - nsecs_t ns = mReceiver.processAudioBuffer(this); + nsecs_t ns = mReceiver.processAudioBuffer(); switch (ns) { case 0: return true; diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index e898109..58c9fc1 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -26,7 +26,7 @@ extern "C" { 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)); @@ -200,7 +200,7 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques ts = &remaining; break; default: - LOG_FATAL("obtainBuffer() timeout=%d", timeout); + LOG_ALWAYS_FATAL("obtainBuffer() timeout=%d", timeout); ts = NULL; break; } @@ -429,7 +429,7 @@ status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *request ts = &remaining; break; default: - LOG_FATAL("waitStreamEndDone() timeout=%d", timeout); + LOG_ALWAYS_FATAL("waitStreamEndDone() timeout=%d", timeout); ts = NULL; break; } @@ -470,7 +470,7 @@ StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cbl void StaticAudioTrackClientProxy::flush() { - LOG_FATAL("static flush"); + LOG_ALWAYS_FATAL("static flush"); } void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int loopCount) @@ -771,7 +771,7 @@ ssize_t StaticAudioTrackServerProxy::pollPosition() return (ssize_t) position; } -status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush) +status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush __unused) { if (mIsShutdown) { buffer->mFrameCount = 0; @@ -854,7 +854,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) 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..4992798 --- /dev/null +++ b/media/libmedia/CharacterEncodingDetector.cpp @@ -0,0 +1,441 @@ +/* + * 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; + bool allprintable = true; + 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)); + allprintable = false; + } + } + + const char *combinedenc = "UTF-8"; + if (allprintable) { + // since 'buf' is empty, ICU would return a UTF-8 matcher with low confidence, so + // no need to even call it + ALOGV("all tags are printable, assuming ascii (%d)", strlen(buf)); + } else { + ucsdet_setText(csd, buf, strlen(buf), &status); + int32_t matches; + const UCharsetMatch** ucma = ucsdet_detectAll(csd, &matches, &status); + bool goodmatch = true; + const UCharsetMatch* bestCombinedMatch = getPreferred(buf, strlen(buf), + ucma, matches, &goodmatch); + + if (!goodmatch && strlen(buf) < 20) { + ALOGV("not a good match, trying with more data"); + // This string might be too short for ICU to do anything useful with. + // (real world example: "Björk" in ISO-8859-1 might be detected as GB18030, because + // the ISO detector reports a confidence of 0, while the GB18030 detector reports + // a confidence of 10 with no invalid characters) + // Append artist, album and title if they were previously omitted because they + // were printable ascii. + bool added = false; + 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, "album") || + !strcmp(name, "title"))) { + strlcat(buf, value, sizeof(buf)); + strlcat(buf, " ", sizeof(buf)); + added = true; + } + } + if (added) { + ucsdet_setText(csd, buf, strlen(buf), &status); + ucma = ucsdet_detectAll(csd, &matches, &status); + bestCombinedMatch = getPreferred(buf, strlen(buf), + ucma, matches, &goodmatch); + if (!goodmatch) { + ALOGV("still not a good match after adding printable tags"); + } + } else { + ALOGV("no printable tags to add"); + } + } + + 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 (!allprintable && (!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 { + if (isPrintableAscii(s, inputLength)) { + enc = "UTF-8"; + ALOGV("@@@@ %s is ascii", mNames.getEntry(i)); + } 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 + * - signal to the caller whether this match is considered good: confidence > 15, and confidence + * delta with the next runner up > 15 + */ +const UCharsetMatch *CharacterEncodingDetector::getPreferred( + const char *input, size_t len, + const UCharsetMatch** ucma, size_t nummatches, + bool *goodmatch) { + + *goodmatch = false; + 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) { + int confidence = ucsdet_getConfidence(matches[0], &status); + if (confidence > 15) { + *goodmatch = true; + } + 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; + int runnerup = -10000; + int runnerupidx = -10000; + num = newconfidence.size(); + for (size_t i = 1; i < num; i++) { + if (newconfidence[i] > highest) { + runnerup = highest; + runnerupidx = highestidx; + highest = newconfidence[i]; + highestidx = i; + } else if (newconfidence[i] > runnerup){ + runnerup = newconfidence[i]; + runnerupidx = i; + } + } + status = U_ZERO_ERROR; + ALOGV("selecting: '%s' w/ %d confidence", + ucsdet_getName(matches[highestidx], &status), highest); + if (runnerupidx < 0) { + ALOGV("no runner up"); + if (highest > 15) { + *goodmatch = true; + } + } else { + ALOGV("runner up: '%s' w/ %d confidence", + ucsdet_getName(matches[runnerupidx], &status), runnerup); + if ((highest - runnerup) > 15) { + *goodmatch = true; + } + } + 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..7b5ed86 --- /dev/null +++ b/media/libmedia/CharacterEncodingDetector.h @@ -0,0 +1,63 @@ +/* + * 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 *goodmatch); + + 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 86ff8bd..eb813bd 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -58,7 +58,7 @@ enum { RESTORE_OUTPUT, OPEN_INPUT, CLOSE_INPUT, - SET_STREAM_OUTPUT, + INVALIDATE_STREAM, SET_VOICE_VOLUME, GET_RENDER_POSITION, GET_INPUT_FRAMES_LOST, @@ -89,13 +89,12 @@ public: 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) { @@ -106,9 +105,11 @@ public: 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); + // haveSharedBuffer if (sharedBuffer != 0) { data.writeInt32(true); data.writeStrongBinder(sharedBuffer->asBinder()); @@ -117,7 +118,7 @@ public: } data.writeInt32((int32_t) output); data.writeInt32((int32_t) tid); - int lSessionId = 0; + int lSessionId = AUDIO_SESSION_ALLOCATE; if (sessionId != NULL) { lSessionId = *sessionId; } @@ -127,6 +128,10 @@ public: 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; @@ -135,11 +140,21 @@ public: if (sessionId != NULL) { *sessionId = lSessionId; } - 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) { + if (status != NULL) { *status = lStatus; } return track; @@ -150,7 +165,7 @@ public: 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, @@ -163,11 +178,12 @@ public: 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; } @@ -176,6 +192,10 @@ public: 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; @@ -198,7 +218,7 @@ public: } } } - if (status) { + if (status != NULL) { *status = lStatus; } return record; @@ -391,7 +411,7 @@ public: const audio_offload_info_t *offloadInfo) { Parcel data, reply; - audio_devices_t devices = pDevices != NULL ? *pDevices : (audio_devices_t)0; + audio_devices_t devices = pDevices != NULL ? *pDevices : AUDIO_DEVICE_NONE; uint32_t samplingRate = pSamplingRate != NULL ? *pSamplingRate : 0; audio_format_t format = pFormat != NULL ? *pFormat : AUDIO_FORMAT_DEFAULT; audio_channel_mask_t channelMask = pChannelMask != NULL ? @@ -405,6 +425,7 @@ public: data.writeInt32(channelMask); data.writeInt32(latency); data.writeInt32((int32_t) flags); + // hasOffloadInfo if (offloadInfo == NULL) { data.writeInt32(0); } else { @@ -415,15 +436,25 @@ public: audio_io_handle_t output = (audio_io_handle_t) reply.readInt32(); ALOGV("openOutput() returned output, %d", output); devices = (audio_devices_t)reply.readInt32(); - if (pDevices != NULL) *pDevices = devices; + if (pDevices != NULL) { + *pDevices = devices; + } samplingRate = reply.readInt32(); - if (pSamplingRate != NULL) *pSamplingRate = samplingRate; + if (pSamplingRate != NULL) { + *pSamplingRate = samplingRate; + } format = (audio_format_t) reply.readInt32(); - if (pFormat != NULL) *pFormat = format; + if (pFormat != NULL) { + *pFormat = format; + } channelMask = (audio_channel_mask_t)reply.readInt32(); - if (pChannelMask != NULL) *pChannelMask = channelMask; + if (pChannelMask != NULL) { + *pChannelMask = channelMask; + } latency = reply.readInt32(); - if (pLatencyMs != NULL) *pLatencyMs = latency; + if (pLatencyMs != NULL) { + *pLatencyMs = latency; + } return output; } @@ -472,7 +503,7 @@ public: audio_channel_mask_t *pChannelMask) { Parcel data, reply; - audio_devices_t devices = pDevices != NULL ? *pDevices : (audio_devices_t)0; + audio_devices_t devices = pDevices != NULL ? *pDevices : AUDIO_DEVICE_NONE; uint32_t samplingRate = pSamplingRate != NULL ? *pSamplingRate : 0; audio_format_t format = pFormat != NULL ? *pFormat : AUDIO_FORMAT_DEFAULT; audio_channel_mask_t channelMask = pChannelMask != NULL ? @@ -487,13 +518,21 @@ public: remote()->transact(OPEN_INPUT, data, &reply); audio_io_handle_t input = (audio_io_handle_t) reply.readInt32(); devices = (audio_devices_t)reply.readInt32(); - if (pDevices != NULL) *pDevices = devices; + if (pDevices != NULL) { + *pDevices = devices; + } samplingRate = reply.readInt32(); - if (pSamplingRate != NULL) *pSamplingRate = samplingRate; + if (pSamplingRate != NULL) { + *pSamplingRate = samplingRate; + } format = (audio_format_t) reply.readInt32(); - if (pFormat != NULL) *pFormat = format; + if (pFormat != NULL) { + *pFormat = format; + } channelMask = (audio_channel_mask_t)reply.readInt32(); - if (pChannelMask != NULL) *pChannelMask = channelMask; + if (pChannelMask != NULL) { + *pChannelMask = channelMask; + } return input; } @@ -506,13 +545,12 @@ public: 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(); } @@ -535,11 +573,11 @@ public: status_t status = reply.readInt32(); if (status == NO_ERROR) { uint32_t tmp = reply.readInt32(); - if (halFrames) { + if (halFrames != NULL) { *halFrames = tmp; } tmp = reply.readInt32(); - if (dspFrames) { + if (dspFrames != NULL) { *dspFrames = tmp; } } @@ -551,8 +589,11 @@ public: 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() @@ -560,26 +601,28 @@ public: Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); status_t status = remote()->transact(NEW_AUDIO_SESSION_ID, data, &reply); - int id = 0; + int id = AUDIO_SESSION_ALLOCATE; if (status == NO_ERROR) { id = reply.readInt32(); } 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); } @@ -657,7 +700,7 @@ public: if (pDesc == NULL) { return effect; - if (status) { + if (status != NULL) { *status = BAD_VALUE; } } @@ -675,7 +718,7 @@ public: } else { lStatus = reply.readInt32(); int tmp = reply.readInt32(); - if (id) { + if (id != NULL) { *id = tmp; } tmp = reply.readInt32(); @@ -685,7 +728,7 @@ public: effect = interface_cast<IEffect>(reply.readStrongBinder()); reply.read(pDesc, sizeof(effect_descriptor_t)); } - if (status) { + if (status != NULL) { *status = lStatus; } @@ -765,7 +808,6 @@ status_t BnAudioFlinger::onTransact( pid_t tid = (pid_t) data.readInt32(); int sessionId = data.readInt32(); int clientUid = data.readInt32(); - String8 name; status_t status; sp<IAudioTrack> track; if ((haveSharedBuffer && (buffer == 0)) || @@ -775,12 +817,13 @@ status_t BnAudioFlinger::onTransact( } else { track = createTrack( (audio_stream_type_t) streamType, sampleRate, format, - channelMask, frameCount, &flags, buffer, output, tid, - &sessionId, name, clientUid, &status); + channelMask, &frameCount, &flags, buffer, output, tid, + &sessionId, clientUid, &status); + LOG_ALWAYS_FATAL_IF((track != 0) != (status == NO_ERROR)); } + reply->writeInt32(frameCount); reply->writeInt32(flags); reply->writeInt32(sessionId); - reply->writeString8(name); reply->writeInt32(status); reply->writeStrongBinder(track->asBinder()); return NO_ERROR; @@ -797,8 +840,9 @@ status_t BnAudioFlinger::onTransact( 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); @@ -941,7 +985,7 @@ status_t BnAudioFlinger::onTransact( &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); @@ -997,11 +1041,10 @@ status_t BnAudioFlinger::onTransact( 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: { @@ -1026,7 +1069,7 @@ status_t BnAudioFlinger::onTransact( 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: { @@ -1037,13 +1080,15 @@ status_t BnAudioFlinger::onTransact( 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..9bb4a49 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -137,6 +137,7 @@ public: data.writeInt32(static_cast <uint32_t>(format)); data.writeInt32(channelMask); data.writeInt32(static_cast <uint32_t>(flags)); + // hasOffloadInfo if (offloadInfo == NULL) { data.writeInt32(0); } else { @@ -476,10 +477,11 @@ status_t BnAudioPolicyService::onTransact( 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 +489,11 @@ status_t BnAudioPolicyService::onTransact( 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 +636,7 @@ status_t BnAudioPolicyService::onTransact( 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 +644,7 @@ status_t BnAudioPolicyService::onTransact( 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 @@ public: 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 3cd9cfd..ffc21fc 100644 --- a/media/libmedia/IAudioTrack.cpp +++ b/media/libmedia/IAudioTrack.cpp @@ -60,6 +60,9 @@ public: 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; } @@ -122,6 +125,9 @@ public: status = reply.readInt32(); if (status == NO_ERROR) { *buffer = interface_cast<IMemory>(reply.readStrongBinder()); + if (*buffer != 0 && (*buffer)->pointer() == NULL) { + (*buffer).clear(); + } } } return status; 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 @@ public: 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 @@ IMediaDeathNotifier::removeObitRecipient(const wp<IMediaDeathNotifier>& recipien } 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 @@ public: } 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 @@ status_t BnMediaMetadataRetriever::onTransact( } 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 @@ status_t BnMediaMetadataRetriever::onTransact( } 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 @@ public: 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 @@ status_t BnMediaPlayer::onTransact( } 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 @@ status_t BnMediaPlayer::onTransact( 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 @@ enum { ADD_BATTERY_DATA, PULL_BATTERY_DATA, LISTEN_FOR_REMOTE_DISPLAY, - UPDATE_PROXY_CONFIG, }; class BpMediaPlayerService: public BpInterface<IMediaPlayerService> @@ -86,12 +86,21 @@ public: 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 @@ public: 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 @@ status_t BnMediaPlayerService::onTransact( } 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 @@ status_t BnMediaPlayerService::onTransact( 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/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 @@ int JetPlayer::init() 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 @@ const MediaProfiles::NameToTagMap MediaProfiles::sCamcorderQualityNameMap[] = { {"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 @@ MediaProfiles::logVideoCodec(const MediaProfiles::VideoCodec& codec) } /*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 @@ MediaProfiles::logAudioCodec(const MediaProfiles::AudioCodec& codec) } /*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 @@ MediaProfiles::logVideoEncoderCap(const MediaProfiles::VideoEncoderCap& cap) } /*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 @@ MediaProfiles::logAudioEncoderCap(const MediaProfiles::AudioEncoderCap& cap) } /*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; - - // 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; - } + mEncodingDetector->detectAndConvert(); + + 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/SoundPool.cpp b/media/libmedia/SoundPool.cpp index 22e9fad..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 @@ SoundChannel* SoundPool::findNextChannel(int channelID) 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 @@ int SoundPool::load(const char* path, int priority) 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 @@ status_t Sample::doLoad() 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 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV 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 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV // 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 @@ void SoundChannel::process(int event, void *info, unsigned long toggle) 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) { diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index adef3be..61b6d36 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -1057,7 +1057,7 @@ bool ToneGenerator::initAudioTrack() { 0, // notificationFrames 0, // sharedBuffer mThreadCanCallJava, - 0, // sessionId + AUDIO_SESSION_ALLOCATE, AudioTrack::TRANSFER_CALLBACK); if (mpAudioTrack->initCheck() != NO_ERROR) { 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 @@ void MediaMetadataRetriever::disconnect() } 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 @@ status_t MediaMetadataRetriever::setDataSource( 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 @@ sp<IMemory> MediaMetadataRetriever::extractAlbumArt() 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..0be01a9 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -58,7 +58,7 @@ MediaPlayer::MediaPlayer() 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() MediaPlayer::~MediaPlayer() { ALOGV("destructor"); - AudioSystem::releaseAudioSessionId(mAudioSessionId); + AudioSystem::releaseAudioSessionId(mAudioSessionId, -1); disconnect(); IPCThreadState::self()->flushCommands(); } @@ -136,6 +136,7 @@ status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player) } status_t MediaPlayer::setDataSource( + const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers) { ALOGV("setDataSource(%s)", url); @@ -145,7 +146,7 @@ status_t MediaPlayer::setDataSource( 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); @@ -530,6 +531,14 @@ status_t MediaPlayer::setAudioStreamType(audio_stream_type_t type) return OK; } +status_t MediaPlayer::getAudioStreamType(audio_stream_type_t *type) +{ + ALOGV("getAudioStreamType"); + Mutex::Autolock _l(mLock); + *type = mStreamType; + return OK; +} + status_t MediaPlayer::setLooping(int loop) { ALOGV("MediaPlayer::setLooping"); @@ -575,8 +584,8 @@ status_t MediaPlayer::setAudioSessionId(int sessionId) 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 +663,7 @@ status_t MediaPlayer::setRetransmitEndpoint(const char* addrString, 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 +785,20 @@ void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj) } } -/*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 +846,4 @@ status_t MediaPlayer::setNextMediaPlayer(const sp<MediaPlayer>& next) { 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 85c9464..caf2dfc 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -45,7 +45,6 @@ LOCAL_STATIC_LIBRARIES := \ 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/HDCP.cpp b/media/libmediaplayerservice/HDCP.cpp index c2ac1a3..afe3936 100644 --- a/media/libmediaplayerservice/HDCP.cpp +++ b/media/libmediaplayerservice/HDCP.cpp @@ -107,11 +107,7 @@ uint32_t HDCP::getCaps() { return NO_INIT; } - // TO-DO: - // Only support HDCP_CAPS_ENCRYPT (byte-array to byte-array) for now. - // use mHDCPModule->getCaps() when the HDCP libraries get updated. - //return mHDCPModule->getCaps(); - return HDCPModule::HDCP_CAPS_ENCRYPT; + return mHDCPModule->getCaps(); } status_t HDCP::encrypt( diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp index 90aed39..74e5013 100644 --- a/media/libmediaplayerservice/MediaPlayerFactory.cpp +++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp @@ -67,6 +67,12 @@ player_type MediaPlayerFactory::getDefaultPlayerType() { return NU_PLAYER; } + // TODO: remove this EXPERIMENTAL developer settings property + if (property_get("persist.sys.media.use-nuplayer", value, NULL) + && !strcasecmp("true", value)) { + return NU_PLAYER; + } + return STAGEFRIGHT_PLAYER; } diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index a392b76..778eb9a 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 @@ sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay( 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; @@ -590,7 +586,8 @@ sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre( } if (!p->hardwareOutput()) { - mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid()); + mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(), + mPid); static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput); } @@ -622,7 +619,9 @@ void MediaPlayerService::Client::setDataSource_post( } 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 @@ status_t MediaPlayerService::Client::setDataSource( return NO_INIT; } - setDataSource_post(p, p->setDataSource(url, headers)); + setDataSource_post(p, p->setDataSource(httpService, url, headers)); return mStatus; } } @@ -1176,9 +1175,14 @@ int Antagonizer::callbackThread(void* user) } #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 @@ status_t MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* 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,13 +1300,14 @@ Exit: #undef LOG_TAG #define LOG_TAG "AudioSink" -MediaPlayerService::AudioOutput::AudioOutput(int sessionId, int uid) +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; @@ -1450,7 +1455,7 @@ status_t MediaPlayerService::AudioOutput::open( 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 @@ -1551,7 +1556,8 @@ status_t MediaPlayerService::AudioOutput::open( mSessionId, AudioTrack::TRANSFER_CALLBACK, offloadInfo, - mUid); + mUid, + mPid); } else { t = new AudioTrack( mStreamType, @@ -1566,7 +1572,8 @@ status_t MediaPlayerService::AudioOutput::open( mSessionId, AudioTrack::TRANSFER_DEFAULT, NULL, // offload info - mUid); + mUid, + mPid); } if ((t == 0) || (t->initCheck() != NO_ERROR)) { @@ -1672,7 +1679,7 @@ void MediaPlayerService::AudioOutput::switchToNextOutput() { ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size) { - LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback."); + LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback."); //ALOGV("write(%p, %u)", buffer, size); if (mTrack != 0) { diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 9c084e1..448f27a 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -72,7 +72,7 @@ class MediaPlayerService : public BnMediaPlayerService class CallbackData; public: - AudioOutput(int sessionId, int uid); + AudioOutput(int sessionId, int uid, int pid); virtual ~AudioOutput(); virtual bool ready() const { return mTrack != 0; } @@ -140,6 +140,7 @@ class MediaPlayerService : public BnMediaPlayerService float mMsecsPerFrame; int mSessionId; int mUid; + int mPid; float mSendLevel; int mAuxEffectId; static bool mIsOnEmulator; @@ -211,12 +212,12 @@ class MediaPlayerService : public BnMediaPlayerService virtual void flush() {} virtual void pause() {} virtual void close() {} - void setAudioStreamType(audio_stream_type_t streamType) {} + 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, float right) {} - virtual status_t setPlaybackRatePermille(int32_t ratePermille) { return INVALID_OPERATION; } + 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; } @@ -256,9 +257,15 @@ public: 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, @@ -272,9 +279,6 @@ public: 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 @@ -356,6 +360,7 @@ private: 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 @@ static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType) } 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 @@ status_t MetadataRetrieverClient::setDataSource( 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 @@ public: 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 @@ MidiFile::~MidiFile() { } 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 @@ public: 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 @@ void MidiMetadataRetriever::clearMetadataValues() } 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 @@ status_t MidiMetadataRetriever::setDataSource( 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 @@ public: ~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::setUID(uid_t uid) { } 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::getParameter(int key, Parcel *reply) { } 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 @@ public: 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 4da74e1..5b7a236 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -25,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> @@ -36,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> @@ -72,8 +73,7 @@ StagefrightRecorder::StagefrightRecorder() mAudioSource(AUDIO_SOURCE_CNT), mVideoSource(VIDEO_SOURCE_LIST_END), mCaptureTimeLapse(false), - mStarted(false), - mSurfaceMediaSource(NULL) { + mStarted(false) { ALOGV("Constructor"); reset(); @@ -82,10 +82,19 @@ StagefrightRecorder::StagefrightRecorder() 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; } @@ -94,7 +103,7 @@ status_t StagefrightRecorder::init() { // 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) { @@ -234,7 +243,7 @@ status_t StagefrightRecorder::setPreviewSurface(const sp<IGraphicBufferProducer> 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. @@ -681,10 +690,10 @@ status_t StagefrightRecorder::setParameter( 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()); @@ -739,19 +748,15 @@ status_t StagefrightRecorder::setClientName(const String16& clientName) { 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; @@ -759,31 +764,97 @@ status_t StagefrightRecorder::start() { 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 = setupMPEG2TSRecording(); + break; + + default: + ALOGE("Unsupported output file format: %d", mOutputFormat); + status = UNKNOWN_ERROR; + 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 = startMPEG2TSRecording(); + { + 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)) { @@ -817,58 +888,54 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { 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); @@ -879,16 +946,10 @@ status_t StagefrightRecorder::startAACRecording() { 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); @@ -908,15 +969,10 @@ status_t StagefrightRecorder::startAMRRecording() { } 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; @@ -942,12 +998,11 @@ status_t StagefrightRecorder::startRawAudioRecording() { 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 @@ -974,7 +1029,7 @@ status_t StagefrightRecorder::startRTPRecording() { return err; } - err = setupVideoEncoder(mediaSource, mVideoBitRate, &source); + err = setupVideoEncoder(mediaSource, &source); if (err != OK) { return err; } @@ -984,10 +1039,10 @@ status_t StagefrightRecorder::startRTPRecording() { 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); @@ -1018,7 +1073,7 @@ status_t StagefrightRecorder::startMPEG2TSRecording() { } sp<MediaSource> encoder; - err = setupVideoEncoder(mediaSource, mVideoBitRate, &encoder); + err = setupVideoEncoder(mediaSource, &encoder); if (err != OK) { return err; @@ -1037,7 +1092,7 @@ status_t StagefrightRecorder::startMPEG2TSRecording() { mWriter = writer; - return mWriter->start(); + return OK; } void StagefrightRecorder::clipVideoFrameRate() { @@ -1278,49 +1333,14 @@ status_t StagefrightRecorder::setupMediaSource( 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; @@ -1384,25 +1404,22 @@ status_t StagefrightRecorder::setupCameraSource( 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: @@ -1410,59 +1427,80 @@ status_t StagefrightRecorder::setupVideoEncoder( 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)); + + 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); - 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); 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; @@ -1496,16 +1534,12 @@ status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) { 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) { @@ -1516,13 +1550,13 @@ status_t StagefrightRecorder::setupMPEG4Recording( } 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. @@ -1531,7 +1565,7 @@ status_t StagefrightRecorder::setupMPEG4Recording( if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_CNT)) { err = setupAudioEncoder(writer); if (err != OK) return err; - *totalBitRate += mAudioBitRate; + mTotalBitRate += mAudioBitRate; } if (mInterleaveDurationUs > 0) { @@ -1549,22 +1583,28 @@ status_t StagefrightRecorder::setupMPEG4Recording( 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); @@ -1577,27 +1617,6 @@ void StagefrightRecorder::setupMPEG4MetaData(int64_t startTimeUs, int32_t totalB } } -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) { @@ -1637,6 +1656,8 @@ status_t StagefrightRecorder::stop() { mWriter.clear(); } + mGraphicBufferProducer.clear(); + if (mOutputFd >= 0) { ::close(mOutputFd); mOutputFd = -1; @@ -1656,7 +1677,6 @@ status_t StagefrightRecorder::stop() { addBatteryData(params); } - return err; } @@ -1708,6 +1728,7 @@ status_t StagefrightRecorder::reset() { mRotationDegrees = 0; mLatitudex10000 = -3600000; mLongitudex10000 = -3600000; + mTotalBitRate = 0; mOutputFd = -1; diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 31f09e0..377d168 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -37,6 +37,7 @@ struct AudioSource; class MediaProfiles; class IGraphicBufferProducer; class SurfaceMediaSource; +class ALooper; struct StagefrightRecorder : public MediaRecorderBase { StagefrightRecorder(); @@ -106,6 +107,7 @@ private: int32_t mLatitudex10000; int32_t mLongitudex10000; int32_t mStartTimeOffsetMs; + int32_t mTotalBitRate; bool mCaptureTimeLapse; int64_t mTimeBetweenTimeLapseFrameCaptureUs; @@ -122,22 +124,17 @@ private: // An <IGraphicBufferProducer> pointer // will be sent to the client side using which the // frame buffers will be queued and dequeued - sp<SurfaceMediaSource> mSurfaceMediaSource; - - 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(); + sp<IGraphicBufferProducer> mGraphicBufferProducer; + sp<ALooper> mLooper; + + 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( bool *supportsCameraSourceMetaDataMode); @@ -147,14 +144,8 @@ private: // 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 @@ status_t TestPlayerStub::parseUrl() // 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 @@ status_t TestPlayerStub::setDataSource( } 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 @@ class TestPlayerStub : public MediaPlayerInterface { // @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 @@ LOCAL_SRC_FILES:= \ 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 @@ namespace android { 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 MediaSource; 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 f1782cc..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 @@ namespace android { 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 @@ void NuPlayer::HTTPLiveSource::prepareAsync() { mLiveSession = new LiveSession( notify, (mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0, - mUIDValid, - mUID); + mHTTPService); mLiveLooper->registerHandler(mLiveSession); @@ -140,7 +139,7 @@ status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select) { // 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) { 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 LiveSession; 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 @@ private: 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 25d55a3..d8d939a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -28,18 +28,13 @@ #include "RTSPSource.h" #include "StreamingSource.h" #include "GenericSource.h" -#include "mp4/MP4Source.h" #include "ATSParser.h" -#include "SoftwareRenderer.h" - -#include <cutils/properties.h> // for property_get #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/ACodec.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> @@ -148,7 +143,6 @@ NuPlayer::NuPlayer() : mUIDValid(false), mSourceFlags(0), mVideoIsAVC(false), - mNeedsSwRenderer(false), mAudioEOS(false), mVideoEOS(false), mScanSourcesPending(false), @@ -183,14 +177,7 @@ void NuPlayer::setDataSourceAsync(const sp<IStreamSource> &source) { 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(); } @@ -212,7 +199,9 @@ static bool IsHTTPLiveURL(const char *url) { } 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); @@ -220,16 +209,18 @@ void NuPlayer::setDataSourceAsync( 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); @@ -447,7 +438,6 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGV("kWhatStart"); mVideoIsAVC = false; - mNeedsSwRenderer = false; mAudioEOS = false; mVideoEOS = false; mSkipRenderingAudioUntilMediaTimeUs = -1; @@ -538,24 +528,21 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { { bool audio = msg->what() == kWhatAudioNotify; - sp<AMessage> codecRequest; - CHECK(msg->findMessage("codec-request", &codecRequest)); - int32_t what; - CHECK(codecRequest->findInt32("what", &what)); + CHECK(msg->findInt32("what", &what)); - if (what == ACodec::kWhatFillThisBuffer) { + if (what == Decoder::kWhatFillThisBuffer) { status_t err = feedDecoderInputData( - audio, codecRequest); + audio, msg); if (err == -EWOULDBLOCK) { if (mSource->feedMoreTSData() == OK) { msg->post(10000ll); } } - } else if (what == ACodec::kWhatEOS) { + } else if (what == Decoder::kWhatEOS) { int32_t err; - CHECK(codecRequest->findInt32("err", &err)); + CHECK(msg->findInt32("err", &err)); if (err == ERROR_END_OF_STREAM) { ALOGV("got %s decoder EOS", audio ? "audio" : "video"); @@ -566,7 +553,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } mRenderer->queueEOS(audio, err); - } else if (what == ACodec::kWhatFlushCompleted) { + } else if (what == Decoder::kWhatFlushCompleted) { bool needShutdown; if (audio) { @@ -595,14 +582,17 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } finishFlushIfPossible(); - } else if (what == ACodec::kWhatOutputFormatChanged) { + } else if (what == Decoder::kWhatOutputFormatChanged) { + sp<AMessage> format; + CHECK(msg->findMessage("format", &format)); + if (audio) { int32_t numChannels; - CHECK(codecRequest->findInt32( + CHECK(format->findInt32( "channel-count", &numChannels)); int32_t sampleRate; - CHECK(codecRequest->findInt32("sample-rate", &sampleRate)); + CHECK(format->findInt32("sample-rate", &sampleRate)); ALOGV("Audio output format changed to %d Hz, %d channels", sampleRate, numChannels); @@ -626,7 +616,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } int32_t channelMask; - if (!codecRequest->findInt32("channel-mask", &channelMask)) { + if (!format->findInt32("channel-mask", &channelMask)) { channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; } @@ -647,11 +637,11 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { // video int32_t width, height; - CHECK(codecRequest->findInt32("width", &width)); - CHECK(codecRequest->findInt32("height", &height)); + CHECK(format->findInt32("width", &width)); + CHECK(format->findInt32("height", &height)); int32_t cropLeft, cropTop, cropRight, cropBottom; - CHECK(codecRequest->findRect( + CHECK(format->findRect( "crop", &cropLeft, &cropTop, &cropRight, &cropBottom)); @@ -684,22 +674,8 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { 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) { + } else if (what == Decoder::kWhatShutdownCompleted) { ALOGV("%s shutdown completed", audio ? "audio" : "video"); if (audio) { mAudioDecoder.clear(); @@ -714,22 +690,15 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } finishFlushIfPossible(); - } else if (what == ACodec::kWhatError) { + } else if (what == Decoder::kWhatError) { ALOGE("Received error from %s decoder, aborting playback.", audio ? "audio" : "video"); mRenderer->queueEOS(audio, UNKNOWN_ERROR); - } else if (what == ACodec::kWhatDrainThisBuffer) { - renderBuffer(audio, codecRequest); - } 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'.", + } else if (what == Decoder::kWhatDrainThisBuffer) { + renderBuffer(audio, msg); + } else { + ALOGV("Unhandled decoder notification %d '%c%c%c%c'.", what, what >> 24, (what >> 16) & 0xff, @@ -930,8 +899,7 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { *decoder = audio ? new Decoder(notify) : new Decoder(notify, mNativeWindow); - looper()->registerHandler(*decoder); - + (*decoder)->init(); (*decoder)->configure(format); return OK; @@ -1531,7 +1499,7 @@ void NuPlayer::Source::notifyPrepared(status_t err) { 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 590e1f2..f1d3d55 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -24,7 +24,6 @@ namespace android { -struct ACodec; struct MetaData; struct NuPlayerDriver; @@ -38,7 +37,9 @@ struct NuPlayer : public AHandler { 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,7 +117,6 @@ private: 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 2423fd5..469c9ca 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -17,14 +17,17 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "NuPlayerDecoder" #include <utils/Log.h> +#include <inttypes.h> #include "NuPlayerDecoder.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/ACodec.h> +#include <media/stagefright/MediaCodec.h> #include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> namespace android { @@ -32,122 +35,425 @@ NuPlayer::Decoder::Decoder( const sp<AMessage> ¬ify, const sp<NativeWindowWrapper> &nativeWindow) : mNotify(notify), - mNativeWindow(nativeWindow) { + mNativeWindow(nativeWindow), + mBufferGeneration(0), + mComponentName("decoder") { + // Every decoder has its own looper because MediaCodec operations + // are blocking, but NuPlayer needs asynchronous operations. + mDecoderLooper = new ALooper; + mDecoderLooper->setName("NuPlayerDecoder"); + mDecoderLooper->start(false, false, ANDROID_PRIORITY_AUDIO); + + mCodecLooper = new ALooper; + mCodecLooper->setName("NuPlayerDecoder-MC"); + mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO); } NuPlayer::Decoder::~Decoder() { } -void NuPlayer::Decoder::configure(const sp<AMessage> &format) { +void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { CHECK(mCodec == NULL); + ++mBufferGeneration; + AString mime; CHECK(format->findString("mime", &mime)); - sp<AMessage> notifyMsg = - new AMessage(kWhatCodecNotify, id()); + sp<Surface> surface = NULL; + if (mNativeWindow != NULL) { + surface = mNativeWindow->getSurfaceTextureClient(); + } - mCSDIndex = 0; - for (size_t i = 0;; ++i) { - sp<ABuffer> csd; - if (!format->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) { - break; - } + mComponentName = mime; + mComponentName.append(" decoder"); + ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), surface.get()); - mCSD.push(csd); + mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */); + if (mCodec == NULL) { + ALOGE("Failed to create %s decoder", mime.c_str()); + handleError(UNKNOWN_ERROR); + return; } + mCodec->getName(&mComponentName); + if (mNativeWindow != NULL) { - format->setObject("native-window", mNativeWindow); + // disconnect from surface as MediaCodec will reconnect + CHECK_EQ((int)NO_ERROR, + native_window_api_disconnect( + surface.get(), + NATIVE_WINDOW_API_MEDIA)); + } + status_t err = mCodec->configure( + format, surface, NULL /* crypto */, 0 /* flags */); + if (err != OK) { + ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err); + handleError(err); + return; + } + // the following should work in configured state + CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat)); + CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat)); + + err = mCodec->start(); + if (err != OK) { + ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err); + handleError(err); + return; } - // Current video decoders do not return from OMX_FillThisBuffer - // quickly, violating the OpenMAX specs, until that is remedied - // we need to invest in an extra looper to free the main event - // queue. - bool needDedicatedLooper = !strncasecmp(mime.c_str(), "video/", 6); + // the following should work after start + CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers)); + CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers)); + ALOGV("[%s] got %zu input and %zu output buffers", + mComponentName.c_str(), + mInputBuffers.size(), + mOutputBuffers.size()); - mFormat = format; - mCodec = new ACodec; + requestCodecNotification(); +} - if (needDedicatedLooper && mCodecLooper == NULL) { - mCodecLooper = new ALooper; - mCodecLooper->setName("NuPlayerDecoder"); - mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO); +void NuPlayer::Decoder::requestCodecNotification() { + if (mCodec != NULL) { + sp<AMessage> reply = new AMessage(kWhatCodecNotify, id()); + reply->setInt32("generation", mBufferGeneration); + mCodec->requestActivityNotification(reply); } +} - (needDedicatedLooper ? mCodecLooper : looper())->registerHandler(mCodec); +bool NuPlayer::Decoder::isStaleReply(const sp<AMessage> &msg) { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + return generation != mBufferGeneration; +} - mCodec->setNotificationMessage(notifyMsg); - mCodec->initiateSetup(format); +void NuPlayer::Decoder::init() { + mDecoderLooper->registerHandler(this); } -void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { - switch (msg->what()) { - case kWhatCodecNotify: - { - int32_t what; - CHECK(msg->findInt32("what", &what)); - - if (what == ACodec::kWhatFillThisBuffer) { - onFillThisBuffer(msg); - } else { - sp<AMessage> notify = mNotify->dup(); - notify->setMessage("codec-request", msg); - notify->post(); - } - break; +void NuPlayer::Decoder::configure(const sp<AMessage> &format) { + sp<AMessage> msg = new AMessage(kWhatConfigure, id()); + msg->setMessage("format", format); + msg->post(); +} + +void NuPlayer::Decoder::handleError(int32_t err) +{ + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +bool NuPlayer::Decoder::handleAnInputBuffer() { + size_t bufferIx = -1; + status_t res = mCodec->dequeueInputBuffer(&bufferIx); + ALOGV("[%s] dequeued input: %d", + mComponentName.c_str(), res == OK ? (int)bufferIx : res); + if (res != OK) { + if (res != -EAGAIN) { + handleError(res); } + return false; + } - default: - TRESPASS(); - break; + CHECK_LT(bufferIx, mInputBuffers.size()); + + sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id()); + reply->setSize("buffer-ix", bufferIx); + reply->setInt32("generation", mBufferGeneration); + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatFillThisBuffer); + notify->setBuffer("buffer", mInputBuffers[bufferIx]); + notify->setMessage("reply", reply); + notify->post(); + return true; +} + +void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { + size_t bufferIx; + CHECK(msg->findSize("buffer-ix", &bufferIx)); + CHECK_LT(bufferIx, mInputBuffers.size()); + sp<ABuffer> codecBuffer = mInputBuffers[bufferIx]; + + sp<ABuffer> buffer; + bool hasBuffer = msg->findBuffer("buffer", &buffer); + if (buffer == NULL /* includes !hasBuffer */) { + int32_t streamErr = ERROR_END_OF_STREAM; + CHECK(msg->findInt32("err", &streamErr) || !hasBuffer); + + if (streamErr == OK) { + /* buffers are returned to hold on to */ + return; + } + + // attempt to queue EOS + status_t err = mCodec->queueInputBuffer( + bufferIx, + 0, + 0, + 0, + MediaCodec::BUFFER_FLAG_EOS); + if (streamErr == ERROR_END_OF_STREAM && err != OK) { + streamErr = err; + // err will not be ERROR_END_OF_STREAM + } + + if (streamErr != ERROR_END_OF_STREAM) { + handleError(streamErr); + } + } else { + int64_t timeUs = 0; + uint32_t flags = 0; + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + + int32_t eos; + // we do not expect CODECCONFIG or SYNCFRAME for decoder + if (buffer->meta()->findInt32("eos", &eos) && eos) { + flags |= MediaCodec::BUFFER_FLAG_EOS; + } + + // copy into codec buffer + if (buffer != codecBuffer) { + CHECK_LE(buffer->size(), codecBuffer->capacity()); + codecBuffer->setRange(0, buffer->size()); + memcpy(codecBuffer->data(), buffer->data(), buffer->size()); + } + + status_t err = mCodec->queueInputBuffer( + bufferIx, + codecBuffer->offset(), + codecBuffer->size(), + timeUs, + flags); + if (err != OK) { + ALOGE("Failed to queue input buffer for %s (err=%d)", + mComponentName.c_str(), err); + handleError(err); + } } } -void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) { - sp<AMessage> reply; - CHECK(msg->findMessage("reply", &reply)); +bool NuPlayer::Decoder::handleAnOutputBuffer() { + size_t bufferIx = -1; + size_t offset; + size_t size; + int64_t timeUs; + uint32_t flags; + status_t res = mCodec->dequeueOutputBuffer( + &bufferIx, &offset, &size, &timeUs, &flags); + + if (res != OK) { + ALOGV("[%s] dequeued output: %d", mComponentName.c_str(), res); + } else { + ALOGV("[%s] dequeued output: %d (time=%lld flags=%" PRIu32 ")", + mComponentName.c_str(), (int)bufferIx, timeUs, flags); + } -#if 0 - sp<ABuffer> outBuffer; - CHECK(msg->findBuffer("buffer", &outBuffer)); -#else - sp<ABuffer> outBuffer; -#endif + if (res == INFO_OUTPUT_BUFFERS_CHANGED) { + res = mCodec->getOutputBuffers(&mOutputBuffers); + if (res != OK) { + ALOGE("Failed to get output buffers for %s after INFO event (err=%d)", + mComponentName.c_str(), res); + handleError(res); + return false; + } + // NuPlayer ignores this + return true; + } else if (res == INFO_FORMAT_CHANGED) { + sp<AMessage> format = new AMessage(); + res = mCodec->getOutputFormat(&format); + if (res != OK) { + ALOGE("Failed to get output format for %s after INFO event (err=%d)", + mComponentName.c_str(), res); + handleError(res); + return false; + } - if (mCSDIndex < mCSD.size()) { - outBuffer = mCSD.editItemAt(mCSDIndex++); - outBuffer->meta()->setInt64("timeUs", 0); + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatOutputFormatChanged); + notify->setMessage("format", format); + notify->post(); + return true; + } else if (res == INFO_DISCONTINUITY) { + // nothing to do + return true; + } else if (res != OK) { + if (res != -EAGAIN) { + handleError(res); + } + return false; + } - reply->setBuffer("buffer", outBuffer); - reply->post(); - return; + CHECK_LT(bufferIx, mOutputBuffers.size()); + sp<ABuffer> buffer = mOutputBuffers[bufferIx]; + buffer->setRange(offset, size); + buffer->meta()->clear(); + buffer->meta()->setInt64("timeUs", timeUs); + if (flags & MediaCodec::BUFFER_FLAG_EOS) { + buffer->meta()->setInt32("eos", true); } + // we do not expect CODECCONFIG or SYNCFRAME for decoder + + sp<AMessage> reply = new AMessage(kWhatRenderBuffer, id()); + reply->setSize("buffer-ix", bufferIx); + reply->setInt32("generation", mBufferGeneration); sp<AMessage> notify = mNotify->dup(); - notify->setMessage("codec-request", msg); + notify->setInt32("what", kWhatDrainThisBuffer); + notify->setBuffer("buffer", buffer); + notify->setMessage("reply", reply); notify->post(); + + // FIXME: This should be handled after rendering is complete, + // but Renderer needs it now + if (flags & MediaCodec::BUFFER_FLAG_EOS) { + ALOGV("queueing eos [%s]", mComponentName.c_str()); + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatEOS); + notify->setInt32("err", ERROR_END_OF_STREAM); + notify->post(); + } + return true; } -void NuPlayer::Decoder::signalFlush() { - if (mCodec != NULL) { - mCodec->signalFlush(); +void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) { + status_t err; + int32_t render; + size_t bufferIx; + CHECK(msg->findSize("buffer-ix", &bufferIx)); + if (msg->findInt32("render", &render) && render) { + err = mCodec->renderOutputBufferAndRelease(bufferIx); + } else { + err = mCodec->releaseOutputBuffer(bufferIx); + } + if (err != OK) { + ALOGE("failed to release output buffer for %s (err=%d)", + mComponentName.c_str(), err); + handleError(err); } } -void NuPlayer::Decoder::signalResume() { +void NuPlayer::Decoder::onFlush() { + status_t err = OK; if (mCodec != NULL) { - mCodec->signalResume(); + err = mCodec->flush(); + ++mBufferGeneration; } + + if (err != OK) { + ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err); + handleError(err); + return; + } + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatFlushCompleted); + notify->post(); } -void NuPlayer::Decoder::initiateShutdown() { +void NuPlayer::Decoder::onShutdown() { + status_t err = OK; if (mCodec != NULL) { - mCodec->initiateShutdown(); + err = mCodec->release(); + mCodec = NULL; + ++mBufferGeneration; + + if (mNativeWindow != NULL) { + // reconnect to surface as MediaCodec disconnected from it + CHECK_EQ((int)NO_ERROR, + native_window_api_connect( + mNativeWindow->getNativeWindow().get(), + NATIVE_WINDOW_API_MEDIA)); + } + mComponentName = "decoder"; + } + + if (err != OK) { + ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err); + handleError(err); + return; + } + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatShutdownCompleted); + notify->post(); +} + +void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { + ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str()); + + switch (msg->what()) { + case kWhatConfigure: + { + sp<AMessage> format; + CHECK(msg->findMessage("format", &format)); + onConfigure(format); + break; + } + + case kWhatCodecNotify: + { + if (!isStaleReply(msg)) { + while (handleAnInputBuffer()) { + } + + while (handleAnOutputBuffer()) { + } + } + + requestCodecNotification(); + break; + } + + case kWhatInputBufferFilled: + { + if (!isStaleReply(msg)) { + onInputBufferFilled(msg); + } + break; + } + + case kWhatRenderBuffer: + { + if (!isStaleReply(msg)) { + onRenderBuffer(msg); + } + break; + } + + case kWhatFlush: + { + onFlush(); + break; + } + + case kWhatShutdown: + { + onShutdown(); + break; + } + + default: + TRESPASS(); + break; } } +void NuPlayer::Decoder::signalFlush() { + (new AMessage(kWhatFlush, id()))->post(); +} + +void NuPlayer::Decoder::signalResume() { + // nothing to do +} + +void NuPlayer::Decoder::initiateShutdown() { + (new AMessage(kWhatShutdown, id()))->post(); +} + bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const { if (targetFormat == NULL) { return true; @@ -163,14 +469,16 @@ bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &ta 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) { + if (!mOutputFormat->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 (mOutputFormat->findBuffer("csd-0", &oldBuf) && + targetFormat->findBuffer("csd-0", &newBuf)) { if (oldBuf->size() != newBuf->size()) { return false; } @@ -181,7 +489,7 @@ bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &ta } bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetFormat) const { - if (mFormat == NULL) { + if (mOutputFormat == NULL) { return false; } @@ -190,7 +498,7 @@ bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetF } AString oldMime, newMime; - if (!mFormat->findString("mime", &oldMime) + if (!mOutputFormat->findString("mime", &oldMime) || !targetFormat->findString("mime", &newMime) || !(oldMime == newMime)) { return false; @@ -201,7 +509,10 @@ bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetF if (audio) { seamless = supportsSeamlessAudioFormatChange(targetFormat); } else { - seamless = mCodec != NULL && mCodec->isConfiguredForAdaptivePlayback(); + int32_t isAdaptive; + seamless = (mCodec != NULL && + mInputFormat->findInt32("adaptive-playback", &isAdaptive) && + isAdaptive); } ALOGV("%s seamless support for %s", seamless ? "yes" : "no", oldMime.c_str()); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index 78ea74a..94243fc 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -25,12 +25,14 @@ namespace android { struct ABuffer; +struct MediaCodec; struct NuPlayer::Decoder : public AHandler { Decoder(const sp<AMessage> ¬ify, const sp<NativeWindowWrapper> &nativeWindow = NULL); void configure(const sp<AMessage> &format); + void init(); void signalFlush(); void signalResume(); @@ -38,7 +40,18 @@ struct NuPlayer::Decoder : public AHandler { bool supportsSeamlessFormatChange(const sp<AMessage> &to) const; + enum { + kWhatFillThisBuffer = 'flTB', + kWhatDrainThisBuffer = 'drTB', + kWhatOutputFormatChanged = 'fmtC', + kWhatFlushCompleted = 'flsC', + kWhatShutdownCompleted = 'shDC', + kWhatEOS = 'eos ', + kWhatError = 'err ', + }; + protected: + virtual ~Decoder(); virtual void onMessageReceived(const sp<AMessage> &msg); @@ -46,21 +59,40 @@ protected: private: enum { kWhatCodecNotify = 'cdcN', + kWhatConfigure = 'conf', + kWhatInputBufferFilled = 'inpF', + kWhatRenderBuffer = 'rndr', + kWhatFlush = 'flus', + kWhatShutdown = 'shuD', }; sp<AMessage> mNotify; sp<NativeWindowWrapper> mNativeWindow; - sp<AMessage> mFormat; - sp<ACodec> mCodec; + sp<AMessage> mInputFormat; + sp<AMessage> mOutputFormat; + sp<MediaCodec> mCodec; sp<ALooper> mCodecLooper; + sp<ALooper> mDecoderLooper; + + Vector<sp<ABuffer> > mInputBuffers; + Vector<sp<ABuffer> > mOutputBuffers; + + void handleError(int32_t err); + bool handleAnInputBuffer(); + bool handleAnOutputBuffer(); - Vector<sp<ABuffer> > mCSD; - size_t mCSDIndex; + void requestCodecNotification(); + bool isStaleReply(const sp<AMessage> &msg); - sp<AMessage> makeFormat(const sp<MetaData> &meta); + void onConfigure(const sp<AMessage> &format); + void onFlush(); + void onInputBufferFilled(const sp<AMessage> &msg); + void onRenderBuffer(const sp<AMessage> &msg); + void onShutdown(); - void onFillThisBuffer(const sp<AMessage> &msg); + int32_t mBufferGeneration; + AString mComponentName; bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 239296e..e4850f0 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -71,7 +71,9 @@ status_t NuPlayerDriver::setUID(uid_t uid) { } 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) { @@ -80,7 +82,7 @@ status_t NuPlayerDriver::setDataSource( mState = STATE_SET_DATASOURCE_PENDING; - mPlayer->setDataSourceAsync(url, headers); + mPlayer->setDataSourceAsync(httpService, url, headers); while (mState == STATE_SET_DATASOURCE_PENDING) { mCondition.wait(mLock); @@ -365,7 +367,7 @@ status_t NuPlayerDriver::reset() { return OK; } -status_t NuPlayerDriver::setLooping(int loop) { +status_t NuPlayerDriver::setLooping(int /* loop */) { return INVALID_OPERATION; } @@ -421,16 +423,17 @@ void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) { 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; @@ -494,7 +497,8 @@ void NuPlayerDriver::notifyFrameStats( 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"); 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 @@ struct NuPlayerDriver : public MediaPlayerInterface { 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 bf5271e..a070c1a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -20,8 +20,6 @@ #include "NuPlayerRenderer.h" -#include "SoftwareRenderer.h" - #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> @@ -36,7 +34,6 @@ NuPlayer::Renderer::Renderer( const sp<AMessage> ¬ify, uint32_t flags) : mAudioSink(sink), - mSoftRenderer(NULL), mNotify(notify), mFlags(flags), mNumFramesWritten(0), @@ -60,12 +57,6 @@ NuPlayer::Renderer::Renderer( } NuPlayer::Renderer::~Renderer() { - delete mSoftRenderer; -} - -void NuPlayer::Renderer::setSoftRenderer(SoftwareRenderer *softRenderer) { - delete mSoftRenderer; - mSoftRenderer = softRenderer; } void NuPlayer::Renderer::queueBuffer( @@ -425,9 +416,6 @@ void NuPlayer::Renderer::onDrainVideoQueue() { 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 9124e03..94a05ea 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -23,7 +23,6 @@ namespace android { struct ABuffer; -class SoftwareRenderer; struct NuPlayer::Renderer : public AHandler { enum Flags { @@ -57,8 +56,6 @@ struct NuPlayer::Renderer : public AHandler { kWhatMediaRenderingStart = 'mdrd', }; - void setSoftRenderer(SoftwareRenderer *softRenderer); - protected: virtual ~Renderer(); @@ -86,7 +83,6 @@ private: 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 @@ struct NuPlayer::Source : public AHandler { 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 @@ protected: 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 @@ const int64_t kNearEOSTimeoutUs = 2000000ll; // 2 secs 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 @@ void NuPlayer::RTSPSource::prepareAsync() { 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 SDPLoader; 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 @@ private: 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 2aae4dd..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", total, 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 @@ LOCAL_SHARED_LIBRARIES := \ 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 @@ ssize_t AudioBufferProviderSource::read(void *buffer, } // 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 @@ ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *us 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..80bf61a 100644 --- a/media/libnbaio/AudioStreamInSource.cpp +++ b/media/libnbaio/AudioStreamInSource.cpp @@ -40,16 +40,14 @@ AudioStreamInSource::~AudioStreamInSource() 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) { - uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common); - 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); - } + uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common); + audio_channel_mask_t channelMask = + (audio_channel_mask_t) mStream->common.get_channels(&mStream->common); + mFormat = Format_from_SR_C(sampleRate, popcount(channelMask), streamFormat); + mFrameSize = Format_frameSize(mFormat); } return NBAIO_Source::negotiate(offers, numOffers, counterOffers, numCounterOffers); } @@ -67,12 +65,12 @@ size_t AudioStreamInSource::framesOverrun() 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..c28d34d 100644 --- a/media/libnbaio/AudioStreamOutSink.cpp +++ b/media/libnbaio/AudioStreamOutSink.cpp @@ -37,16 +37,14 @@ AudioStreamOutSink::~AudioStreamOutSink() 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) { - uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common); - 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); - } + uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common); + audio_channel_mask_t channelMask = + (audio_channel_mask_t) mStream->common.get_channels(&mStream->common); + mFormat = Format_from_SR_C(sampleRate, popcount(channelMask), streamFormat); + mFrameSize = Format_frameSize(mFormat); } return NBAIO_Sink::negotiate(offers, numOffers, counterOffers, numCounterOffers); } @@ -56,10 +54,10 @@ ssize_t AudioStreamOutSink::write(const void *buffer, size_t count) 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 3c61b60..4adf018 100644 --- a/media/libnbaio/MonoPipe.cpp +++ b/media/libnbaio/MonoPipe.cpp @@ -30,7 +30,24 @@ namespace android { -MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) : +static uint64_t cacheN; // output of CCHelper::getLocalFreq() +static bool cacheValid; // whether cacheN is valid +static pthread_once_t cacheOnceControl = PTHREAD_ONCE_INIT; + +static void cacheOnceInit() +{ + CCHelper tmpHelper; + status_t res; + if (OK != (res = tmpHelper.getLocalFreq(&cacheN))) { + ALOGE("Failed to fetch local time frequency when constructing a" + " MonoPipe (res = %d). getNextWriteTimestamp calls will be" + " non-functional", res); + return; + } + cacheValid = true; +} + +MonoPipe::MonoPipe(size_t reqFrames, const NBAIO_Format& format, bool writeCanBlock) : NBAIO_Sink(format), mUpdateSeq(0), mReqFrames(reqFrames), @@ -47,8 +64,6 @@ MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) : mTimestampMutator(&mTimestampShared), mTimestampObserver(&mTimestampShared) { - CCHelper tmpHelper; - status_t res; uint64_t N, D; mNextRdPTS = AudioBufferProvider::kInvalidPTS; @@ -59,12 +74,13 @@ MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) : mSamplesToLocalTime.a_to_b_denom = 0; D = Format_sampleRate(format); - if (OK != (res = tmpHelper.getLocalFreq(&N))) { - ALOGE("Failed to fetch local time frequency when constructing a" - " MonoPipe (res = %d). getNextWriteTimestamp calls will be" - " non-functional", res); + + (void) pthread_once(&cacheOnceControl, cacheOnceInit); + if (!cacheValid) { + // log has already been done return; } + N = cacheN; LinearTransform::reduce(&N, &D); static const uint64_t kSignedHiBitsMask = ~(0x7FFFFFFFull); @@ -115,11 +131,11 @@ ssize_t MonoPipe::write(const void *buffer, size_t count) 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 +145,7 @@ ssize_t MonoPipe::write(const void *buffer, size_t count) 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; 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 @@ ssize_t MonoPipeReader::read(void *buffer, size_t count, int64_t readPTS) 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..ff3284c 100644 --- a/media/libnbaio/NBAIO.cpp +++ b/media/libnbaio/NBAIO.cpp @@ -22,119 +22,42 @@ 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) -{ - // sizeof(short) == 2, so frame size == 1 << channels - return Format_channelCount(format); -} - -enum { - Format_SR_8000, - Format_SR_11025, - Format_SR_16000, - Format_SR_22050, - Format_SR_24000, - Format_SR_32000, - Format_SR_44100, - Format_SR_48000, - Format_SR_Mask = 7 -}; - -enum { - Format_C_1 = 0x08, - Format_C_2 = 0x10, - Format_C_Mask = 0x18 -}; +const NBAIO_Format Format_Invalid = { 0, 0, AUDIO_FORMAT_INVALID, 0 }; -unsigned Format_sampleRate(NBAIO_Format format) +unsigned Format_sampleRate(const NBAIO_Format& format) { - if (format == Format_Invalid) { - 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: + if (!Format_isValid(format)) { return 0; } + return format.mSampleRate; } -unsigned Format_channelCount(NBAIO_Format format) +unsigned Format_channelCount(const NBAIO_Format& format) { - if (format == Format_Invalid) { - return 0; - } - switch (format & Format_C_Mask) { - case Format_C_1: - return 1; - case Format_C_2: - return 2; - default: + if (!Format_isValid(format)) { 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 +139,9 @@ ssize_t NBAIO_Port::negotiate(const NBAIO_Format offers[], size_t numOffers, { 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 +156,17 @@ ssize_t NBAIO_Port::negotiate(const NBAIO_Format offers[], size_t numOffers, 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 d74a7a6..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 @@ void NBLog::Reader::dump(int fd, size_t indent) } i -= length + 3; } - if (i > 0) { - lost += i; - if (fd >= 0) { - fdprintf(fd, "%*swarning: lost %zu 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 @@ void NBLog::Reader::dump(int fd, size_t indent) 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 @@ void NBLog::Reader::dump(int fd, size_t indent) 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 @@ ssize_t Pipe::write(const void *buffer, size_t count) 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 @@ ssize_t PipeReader::availableToRead() 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 @@ ssize_t PipeReader::read(void *buffer, size_t count, int64_t readPTS) 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 @@ namespace android { 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 @@ SourceAudioBufferProvider::SourceAudioBufferProvider(const sp<NBAIO_Source>& sou 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 @@ status_t SourceAudioBufferProvider::getNextBuffer(Buffer *buffer, int64_t pts) 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 @@ status_t SourceAudioBufferProvider::getNextBuffer(Buffer *buffer, int64_t pts) 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 @@ sp<MediaSource> AACExtractor::getTrack(size_t index) { 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 @@ AACSource::~AACSource() { } } -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 c9bcaba..deee8e7 100644 --- a/media/libstagefright/AACWriter.cpp +++ b/media/libstagefright/AACWriter.cpp @@ -111,7 +111,7 @@ status_t AACWriter::addSource(const sp<MediaSource> &source) { return OK; } -status_t AACWriter::start(MetaData *params) { +status_t AACWriter::start(MetaData * /* params */) { if (mInitCheck != OK) { return mInitCheck; } diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 6dcedca..ac40568 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -365,7 +365,6 @@ ACodec::ACodec() mIsEncoder(false), mUseMetadataOnEncoderOutput(false), mShutdownInProgress(false), - mIsConfiguredForAdaptivePlayback(false), mEncoderDelay(0), mEncoderPadding(0), mChannelMaskPresent(false), @@ -374,7 +373,10 @@ ACodec::ACodec() mStoreMetaDataInOutputBuffers(false), mMetaDataBuffersToSubmit(0), mRepeatFrameDelayUs(-1ll), - mMaxPtsGapUs(-1l) { + mMaxPtsGapUs(-1ll), + mTimePerCaptureUs(-1ll), + mTimePerFrameUs(-1ll), + mCreateInputBuffersSuspended(false) { mUninitializedState = new UninitializedState(this); mLoadedState = new LoadedState(this); mLoadedToIdleState = new LoadedToIdleState(this); @@ -640,18 +642,34 @@ status_t ACodec::configureOutputBuffersFromNativeWindow( return err; } - // XXX: Is this the right logic to use? It's not clear to me what the OMX - // buffer counts refer to - how do they account for the renderer holding on - // to buffers? - if (def.nBufferCountActual < def.nBufferCountMin + *minUndequeuedBuffers) { - OMX_U32 newBufferCount = def.nBufferCountMin + *minUndequeuedBuffers; + // FIXME: assume that surface is controlled by app (native window + // returns the number for the case when surface is not controlled by app) + // FIXME2: This means that minUndeqeueudBufs can be 1 larger than reported + // For now, try to allocate 1 more buffer, but don't fail if unsuccessful + + // Use conservative allocation while also trying to reduce starvation + // + // 1. allocate at least nBufferCountMin + minUndequeuedBuffers - that is the + // minimum needed for the consumer to be able to work + // 2. try to allocate two (2) additional buffers to reduce starvation from + // the consumer + // plus an extra buffer to account for incorrect minUndequeuedBufs + for (OMX_U32 extraBuffers = 2 + 1; /* condition inside loop */; extraBuffers--) { + OMX_U32 newBufferCount = + def.nBufferCountMin + *minUndequeuedBuffers + extraBuffers; def.nBufferCountActual = newBufferCount; err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); - if (err != OK) { - ALOGE("[%s] setting nBufferCountActual to %lu failed: %d", - mComponentName.c_str(), newBufferCount, err); + if (err == OK) { + *minUndequeuedBuffers += extraBuffers; + break; + } + + ALOGW("[%s] setting nBufferCountActual to %lu failed: %d", + mComponentName.c_str(), newBufferCount, err); + /* exit condition */ + if (extraBuffers == 0) { return err; } } @@ -676,6 +694,7 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { &bufferCount, &bufferSize, &minUndequeuedBuffers); if (err != 0) return err; + mNumUndequeuedBuffers = minUndequeuedBuffers; ALOGV("[%s] Allocating %lu buffers from a native window of size %lu on " "output port", @@ -741,6 +760,7 @@ status_t ACodec::allocateOutputMetaDataBuffers() { &bufferCount, &bufferSize, &minUndequeuedBuffers); if (err != 0) return err; + mNumUndequeuedBuffers = minUndequeuedBuffers; ALOGV("[%s] Allocating %lu meta buffers on output port", mComponentName.c_str(), bufferCount); @@ -961,6 +981,8 @@ status_t ACodec::setComponentRole( "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, @@ -1036,6 +1058,9 @@ status_t ACodec::configureCodec( encoder = false; } + sp<AMessage> inputFormat = new AMessage(); + sp<AMessage> outputFormat = new AMessage(); + mIsEncoder = encoder; status_t err = setComponentRole(encoder /* isEncoder */, mime); @@ -1118,7 +1143,17 @@ status_t ACodec::configureCodec( } if (!msg->findInt64("max-pts-gap-to-encoder", &mMaxPtsGapUs)) { - mMaxPtsGapUs = -1l; + mMaxPtsGapUs = -1ll; + } + + if (!msg->findInt64("time-lapse", &mTimePerCaptureUs)) { + mTimePerCaptureUs = -1ll; + } + + if (!msg->findInt32( + "create-input-buffers-suspended", + (int32_t*)&mCreateInputBuffersSuspended)) { + mCreateInputBuffersSuspended = false; } } @@ -1127,7 +1162,9 @@ status_t ACodec::configureCodec( int32_t haveNativeWindow = msg->findObject("native-window", &obj) && obj != NULL; mStoreMetaDataInOutputBuffers = false; - mIsConfiguredForAdaptivePlayback = false; + if (video && !encoder) { + inputFormat->setInt32("adaptive-playback", false); + } if (!encoder && video && haveNativeWindow) { err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexOutput, OMX_TRUE); if (err != OK) { @@ -1164,7 +1201,7 @@ status_t ACodec::configureCodec( if (canDoAdaptivePlayback && msg->findInt32("max-width", &maxWidth) && msg->findInt32("max-height", &maxHeight)) { - ALOGV("[%s] prepareForAdaptivePlayback(%ldx%ld)", + ALOGV("[%s] prepareForAdaptivePlayback(%dx%d)", mComponentName.c_str(), maxWidth, maxHeight); err = mOMX->prepareForAdaptivePlayback( @@ -1172,14 +1209,19 @@ status_t ACodec::configureCodec( ALOGW_IF(err != OK, "[%s] prepareForAdaptivePlayback failed w/ err %d", mComponentName.c_str(), err); - mIsConfiguredForAdaptivePlayback = (err == OK); + + if (err == OK) { + inputFormat->setInt32("max-width", maxWidth); + inputFormat->setInt32("max-height", maxHeight); + inputFormat->setInt32("adaptive-playback", true); + } } // allow failure err = OK; } else { ALOGV("[%s] storeMetaDataInBuffers succeeded", mComponentName.c_str()); mStoreMetaDataInOutputBuffers = true; - mIsConfiguredForAdaptivePlayback = true; + inputFormat->setInt32("adaptive-playback", true); } int32_t push; @@ -1319,6 +1361,11 @@ status_t ACodec::configureCodec( err = setMinBufferSize(kPortIndexInput, 8192); // XXX } + CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK); + CHECK_EQ(getPortFormat(kPortIndexOutput, outputFormat), (status_t)OK); + mInputFormat = inputFormat; + mOutputFormat = outputFormat; + return err; } @@ -1909,6 +1956,7 @@ status_t ACodec::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) { return INVALID_OPERATION; } frameRate = (float)tmp; + mTimePerFrameUs = (int64_t) (1000000.0f / frameRate); } video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f); @@ -2482,19 +2530,7 @@ void ACodec::waitUntilAllPossibleNativeWindowBuffersAreReturnedToUs() { return; } - int minUndequeuedBufs = 0; - status_t err = mNativeWindow->query( - mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &minUndequeuedBufs); - - if (err != OK) { - ALOGE("[%s] NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)", - mComponentName.c_str(), strerror(-err), -err); - - minUndequeuedBufs = 0; - } - - while (countBuffersOwnedByNativeWindow() > (size_t)minUndequeuedBufs + while (countBuffersOwnedByNativeWindow() > mNumUndequeuedBuffers && dequeueBufferFromNativeWindow() != NULL) { // these buffers will be submitted as regular buffers; account for this if (mStoreMetaDataInOutputBuffers && mMetaDataBuffersToSubmit > 0) { @@ -2540,79 +2576,78 @@ void ACodec::processDeferredMessages() { } } -void ACodec::sendFormatChange(const sp<AMessage> &reply) { - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatOutputFormatChanged); - +status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { + // TODO: catch errors an return them instead of using CHECK OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); - def.nPortIndex = kPortIndexOutput; + def.nPortIndex = portIndex; CHECK_EQ(mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)), (status_t)OK); - CHECK_EQ((int)def.eDir, (int)OMX_DirOutput); + CHECK_EQ((int)def.eDir, + (int)(portIndex == kPortIndexOutput ? OMX_DirOutput : OMX_DirInput)); switch (def.eDomain) { case OMX_PortDomainVideo: { OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video; + switch ((int)videoDef->eCompressionFormat) { + case OMX_VIDEO_CodingUnused: + { + CHECK(mIsEncoder ^ (portIndex == kPortIndexOutput)); + notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW); + + notify->setInt32("stride", videoDef->nStride); + notify->setInt32("slice-height", videoDef->nSliceHeight); + notify->setInt32("color-format", videoDef->eColorFormat); + + OMX_CONFIG_RECTTYPE rect; + InitOMXParams(&rect); + rect.nPortIndex = kPortIndexOutput; + + if (mOMX->getConfig( + mNode, OMX_IndexConfigCommonOutputCrop, + &rect, sizeof(rect)) != OK) { + rect.nLeft = 0; + rect.nTop = 0; + rect.nWidth = videoDef->nFrameWidth; + rect.nHeight = videoDef->nFrameHeight; + } - AString mime; - if (!mIsEncoder) { - notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW); - } else if (GetMimeTypeForVideoCoding( - videoDef->eCompressionFormat, &mime) != OK) { - notify->setString("mime", "application/octet-stream"); - } else { - notify->setString("mime", mime.c_str()); - } - - notify->setInt32("width", videoDef->nFrameWidth); - notify->setInt32("height", videoDef->nFrameHeight); - - if (!mIsEncoder) { - notify->setInt32("stride", videoDef->nStride); - notify->setInt32("slice-height", videoDef->nSliceHeight); - notify->setInt32("color-format", videoDef->eColorFormat); - - OMX_CONFIG_RECTTYPE rect; - InitOMXParams(&rect); - rect.nPortIndex = kPortIndexOutput; - - if (mOMX->getConfig( - mNode, OMX_IndexConfigCommonOutputCrop, - &rect, sizeof(rect)) != OK) { - rect.nLeft = 0; - rect.nTop = 0; - rect.nWidth = videoDef->nFrameWidth; - rect.nHeight = videoDef->nFrameHeight; - } + CHECK_GE(rect.nLeft, 0); + CHECK_GE(rect.nTop, 0); + CHECK_GE(rect.nWidth, 0u); + CHECK_GE(rect.nHeight, 0u); + CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth); + CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight); - CHECK_GE(rect.nLeft, 0); - CHECK_GE(rect.nTop, 0); - CHECK_GE(rect.nWidth, 0u); - CHECK_GE(rect.nHeight, 0u); - CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth); - CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight); - - notify->setRect( - "crop", - rect.nLeft, - rect.nTop, - rect.nLeft + rect.nWidth - 1, - rect.nTop + rect.nHeight - 1); - - if (mNativeWindow != NULL) { - reply->setRect( + notify->setRect( "crop", rect.nLeft, rect.nTop, - rect.nLeft + rect.nWidth, - rect.nTop + rect.nHeight); + rect.nLeft + rect.nWidth - 1, + rect.nTop + rect.nHeight - 1); + + break; + } + default: + { + CHECK(mIsEncoder ^ (portIndex == kPortIndexInput)); + AString mime; + if (GetMimeTypeForVideoCoding( + videoDef->eCompressionFormat, &mime) != OK) { + notify->setString("mime", "application/octet-stream"); + } else { + notify->setString("mime", mime.c_str()); + } + break; } } + + notify->setInt32("width", videoDef->nFrameWidth); + notify->setInt32("height", videoDef->nFrameHeight); break; } @@ -2625,7 +2660,7 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { { OMX_AUDIO_PARAM_PCMMODETYPE params; InitOMXParams(¶ms); - params.nPortIndex = kPortIndexOutput; + params.nPortIndex = portIndex; CHECK_EQ(mOMX->getParameter( mNode, OMX_IndexParamAudioPcm, @@ -2645,20 +2680,6 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW); notify->setInt32("channel-count", params.nChannels); notify->setInt32("sample-rate", params.nSamplingRate); - if (mEncoderDelay + mEncoderPadding) { - size_t frameSize = params.nChannels * sizeof(int16_t); - if (mSkipCutBuffer != NULL) { - size_t prevbufsize = mSkipCutBuffer->size(); - if (prevbufsize != 0) { - ALOGW("Replacing SkipCutBuffer holding %d " - "bytes", - prevbufsize); - } - } - mSkipCutBuffer = new SkipCutBuffer( - mEncoderDelay * frameSize, - mEncoderPadding * frameSize); - } if (mChannelMaskPresent) { notify->setInt32("channel-mask", mChannelMask); @@ -2670,7 +2691,7 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { { OMX_AUDIO_PARAM_AACPROFILETYPE params; InitOMXParams(¶ms); - params.nPortIndex = kPortIndexOutput; + params.nPortIndex = portIndex; CHECK_EQ(mOMX->getParameter( mNode, OMX_IndexParamAudioAac, @@ -2687,7 +2708,7 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { { OMX_AUDIO_PARAM_AMRTYPE params; InitOMXParams(¶ms); - params.nPortIndex = kPortIndexOutput; + params.nPortIndex = portIndex; CHECK_EQ(mOMX->getParameter( mNode, OMX_IndexParamAudioAmr, @@ -2713,7 +2734,7 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { { OMX_AUDIO_PARAM_FLACTYPE params; InitOMXParams(¶ms); - params.nPortIndex = kPortIndexOutput; + params.nPortIndex = portIndex; CHECK_EQ(mOMX->getParameter( mNode, OMX_IndexParamAudioFlac, @@ -2726,11 +2747,45 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { break; } + case OMX_AUDIO_CodingMP3: + { + OMX_AUDIO_PARAM_MP3TYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + CHECK_EQ(mOMX->getParameter( + mNode, OMX_IndexParamAudioMp3, + ¶ms, sizeof(params)), + (status_t)OK); + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_MPEG); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + + case OMX_AUDIO_CodingVORBIS: + { + OMX_AUDIO_PARAM_VORBISTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + CHECK_EQ(mOMX->getParameter( + mNode, OMX_IndexParamAudioVorbis, + ¶ms, sizeof(params)), + (status_t)OK); + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_VORBIS); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + case OMX_AUDIO_CodingAndroidAC3: { OMX_AUDIO_PARAM_ANDROID_AC3TYPE params; InitOMXParams(¶ms); - params.nPortIndex = kPortIndexOutput; + params.nPortIndex = portIndex; CHECK_EQ((status_t)OK, mOMX->getParameter( mNode, @@ -2745,6 +2800,7 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { } default: + ALOGE("UNKNOWN AUDIO CODING: %d\n", audioDef->eEncoding); TRESPASS(); } break; @@ -2754,6 +2810,43 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { TRESPASS(); } + return OK; +} + +void ACodec::sendFormatChange(const sp<AMessage> &reply) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatOutputFormatChanged); + + CHECK_EQ(getPortFormat(kPortIndexOutput, notify), (status_t)OK); + + AString mime; + CHECK(notify->findString("mime", &mime)); + + int32_t left, top, right, bottom; + if (mime == MEDIA_MIMETYPE_VIDEO_RAW && + mNativeWindow != NULL && + notify->findRect("crop", &left, &top, &right, &bottom)) { + // notify renderer of the crop change + // NOTE: native window uses extended right-bottom coordinate + reply->setRect("crop", left, top, right + 1, bottom + 1); + } else if (mime == MEDIA_MIMETYPE_AUDIO_RAW && + (mEncoderDelay || mEncoderPadding)) { + int32_t channelCount; + CHECK(notify->findInt32("channel-count", &channelCount)); + size_t frameSize = channelCount * sizeof(int16_t); + if (mSkipCutBuffer != NULL) { + size_t prevbufsize = mSkipCutBuffer->size(); + if (prevbufsize != 0) { + ALOGW("Replacing SkipCutBuffer holding %d " + "bytes", + prevbufsize); + } + } + mSkipCutBuffer = new SkipCutBuffer( + mEncoderDelay * frameSize, + mEncoderPadding * frameSize); + } + notify->post(); mSentFormat = true; @@ -2960,7 +3053,8 @@ ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState) mCodec(codec) { } -ACodec::BaseState::PortMode ACodec::BaseState::getPortMode(OMX_U32 portIndex) { +ACodec::BaseState::PortMode ACodec::BaseState::getPortMode( + OMX_U32 /* portIndex */) { return KEEP_BUFFERS; } @@ -3009,6 +3103,14 @@ bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { int32_t type; CHECK(msg->findInt32("type", &type)); + // there is a possibility that this is an outstanding message for a + // codec that we have already destroyed + if (mCodec->mNode == NULL) { + ALOGI("ignoring message as already freed component: %s", + msg->debugString().c_str()); + return true; + } + IOMX::node_id nodeID; CHECK(msg->findPointer("node", &nodeID)); CHECK_EQ(nodeID, mCodec->mNode); @@ -3369,8 +3471,8 @@ bool ACodec::BaseState::onOMXFillBufferDone( 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); @@ -3422,7 +3524,7 @@ bool ACodec::BaseState::onOMXFillBufferDone( sp<AMessage> reply = new AMessage(kWhatOutputBufferDrained, mCodec->id()); - if (!mCodec->mSentFormat) { + if (!mCodec->mSentFormat && rangeLength > 0) { mCodec->sendFormatChange(reply); } @@ -3782,7 +3884,8 @@ void ACodec::LoadedState::stateEntered() { mCodec->mDequeueCounter = 0; mCodec->mMetaDataBuffersToSubmit = 0; mCodec->mRepeatFrameDelayUs = -1ll; - mCodec->mIsConfiguredForAdaptivePlayback = false; + mCodec->mInputFormat.clear(); + mCodec->mOutputFormat.clear(); if (mCodec->mShutdownInProgress) { bool keepComponentAllocated = mCodec->mKeepComponentAllocated; @@ -3896,6 +3999,8 @@ bool ACodec::LoadedState::onConfigureComponent( { sp<AMessage> notify = mCodec->mNotify->dup(); notify->setInt32("what", ACodec::kWhatComponentConfigured); + notify->setMessage("input-format", mCodec->mInputFormat); + notify->setMessage("output-format", mCodec->mOutputFormat); notify->post(); } @@ -3903,7 +4008,7 @@ bool ACodec::LoadedState::onConfigureComponent( } void ACodec::LoadedState::onCreateInputSurface( - const sp<AMessage> &msg) { + const sp<AMessage> & /* msg */) { ALOGV("onCreateInputSurface"); sp<AMessage> notify = mCodec->mNotify->dup(); @@ -3931,7 +4036,7 @@ void ACodec::LoadedState::onCreateInputSurface( } } - if (err == OK && mCodec->mMaxPtsGapUs > 0l) { + if (err == OK && mCodec->mMaxPtsGapUs > 0ll) { err = mCodec->mOMX->setInternalOption( mCodec->mNode, kPortIndexInput, @@ -3941,6 +4046,41 @@ void ACodec::LoadedState::onCreateInputSurface( 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); } @@ -4003,6 +4143,7 @@ status_t ACodec::LoadedToIdleState::allocateBuffers() { bool ACodec::LoadedToIdleState::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { + case kWhatSetParameters: case kWhatShutdown: { mCodec->deferMessage(msg); @@ -4069,6 +4210,7 @@ void ACodec::IdleToExecutingState::stateEntered() { bool ACodec::IdleToExecutingState::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { + case kWhatSetParameters: case kWhatShutdown: { mCodec->deferMessage(msg); @@ -4129,7 +4271,7 @@ ACodec::ExecutingState::ExecutingState(ACodec *codec) } ACodec::BaseState::PortMode ACodec::ExecutingState::getPortMode( - OMX_U32 portIndex) { + OMX_U32 /* portIndex */) { return RESUBMIT_BUFFERS; } @@ -4338,6 +4480,22 @@ status_t ACodec::setParameters(const sp<AMessage> ¶ms) { } } + 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 @@ sp<MediaSource> AMRExtractor::getTrack(size_t index) { 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 @@ AMRSource::~AMRSource() { } } -status_t AMRSource::start(MetaData *params) { +status_t AMRSource::start(MetaData * /* params */) { CHECK(!mStarted); mOffset = mIsWide ? 9 : 6; @@ -258,7 +258,7 @@ status_t AMRSource::read( 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 3fe247a..653ca36 100644 --- a/media/libstagefright/AMRWriter.cpp +++ b/media/libstagefright/AMRWriter.cpp @@ -105,7 +105,7 @@ status_t AMRWriter::addSource(const sp<MediaSource> &source) { return OK; } -status_t AMRWriter::start(MetaData *params) { +status_t AMRWriter::start(MetaData * /* params */) { if (mInitCheck != OK) { return mInitCheck; } diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 6a2a696..714b5e0 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -15,6 +15,7 @@ LOCAL_SRC_FILES:= \ CameraSource.cpp \ CameraSourceTimeLapse.cpp \ DataSource.cpp \ + DataURISource.cpp \ DRMExtractor.cpp \ ESDS.cpp \ FileSource.cpp \ @@ -30,8 +31,10 @@ LOCAL_SRC_FILES:= \ 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 @@ LOCAL_SRC_FILES:= \ 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 @@ LOCAL_SHARED_LIBRARIES := \ libicuuc \ liblog \ libmedia \ + libopus \ libsonivox \ libssl \ libstagefright_omx \ @@ -95,6 +97,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_color_conversion \ libstagefright_aacenc \ libstagefright_matroska \ + libstagefright_webm \ libstagefright_timedtext \ libvpx \ libwebm \ @@ -103,13 +106,6 @@ LOCAL_STATIC_LIBRARIES := \ 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 05ee34e..2669849 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -221,7 +221,8 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { mAudioTrack = new AudioTrack( AUDIO_STREAM_MUSIC, mSampleRate, AUDIO_FORMAT_PCM_16_BIT, audioMask, - 0, AUDIO_OUTPUT_FLAG_NONE, &AudioCallback, this, 0); + 0 /*frameCount*/, AUDIO_OUTPUT_FLAG_NONE, &AudioCallback, this, + 0 /*notificationFrames*/); if ((err = mAudioTrack->initCheck()) != OK) { mAudioTrack.clear(); @@ -410,7 +411,7 @@ status_t AudioPlayer::setPlaybackRatePermille(int32_t ratePermille) { // 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; diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index f0d1a14..d0e0e8e 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -65,10 +65,10 @@ AudioSource::AudioSource( 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 @@ AudioSource::AudioSource( 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 @@ void AudioSource::rampVolume( } status_t AudioSource::read( - MediaBuffer **out, const ReadOptions *options) { + MediaBuffer **out, const ReadOptions * /* options */) { Mutex::Autolock autoLock(mLock); *out = NULL; @@ -278,7 +278,7 @@ status_t AudioSource::dataCallback(const AudioRecord::Buffer& audioBuffer) { // 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; } diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 29c007a..4bad14b 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -35,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> @@ -45,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> @@ -83,7 +86,7 @@ struct AwesomeEvent : public TimedEventQueue::Event { protected: virtual ~AwesomeEvent() {} - virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { + virtual void fire(TimedEventQueue * /* queue */, int64_t /* now_us */) { (mPlayer->*mMethod)(); } @@ -277,15 +280,20 @@ void AwesomePlayer::setUID(uid_t uid) { } 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) { @@ -582,6 +590,7 @@ void AwesomePlayer::reset_l() { mSeekNotificationSent = true; mSeekTimeUs = 0; + mHTTPService.clear(); mUri.setTo(""); mUriHeaders.clear(); @@ -1483,7 +1492,7 @@ void AwesomePlayer::addTextSource_l(size_t trackIndex, const sp<MediaSource>& so CHECK(source != NULL); if (mTextDriver == NULL) { - mTextDriver = new TimedTextDriver(mListener); + mTextDriver = new TimedTextDriver(mListener, mHTTPService); } mTextDriver->addInBandTextSource(trackIndex, source); @@ -2193,15 +2202,14 @@ status_t AwesomePlayer::finishSetDataSource_l() { 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( @@ -2209,6 +2217,10 @@ status_t AwesomePlayer::finishSetDataSource_l() { 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) { @@ -2239,8 +2251,6 @@ status_t AwesomePlayer::finishSetDataSource_l() { 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 @@ -2317,7 +2327,8 @@ status_t AwesomePlayer::finishSetDataSource_l() { } } } else { - dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders); + dataSource = DataSource::CreateFromURI( + mHTTPService, mUri.string(), &mUriHeaders); } if (dataSource == NULL) { @@ -2759,7 +2770,7 @@ status_t AwesomePlayer::invoke(const Parcel &request, Parcel *reply) { { 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()); @@ -2771,7 +2782,7 @@ status_t AwesomePlayer::invoke(const Parcel &request, Parcel *reply) { { 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(); @@ -2804,7 +2815,8 @@ bool AwesomePlayer::isStreamingHTTP() const { 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"); @@ -2900,6 +2912,8 @@ void AwesomePlayer::onAudioTearDownEvent() { // get current position so we can start recreated stream from here getPosition(&mAudioTearDownPosition); + sp<IMediaHTTPService> savedHTTPService = mHTTPService; + // Reset and recreate reset_l(); @@ -2909,7 +2923,7 @@ void AwesomePlayer::onAudioTearDownEvent() { mFileSource = fileSource; err = setDataSource_l(fileSource); } else { - err = setDataSource_l(uri, &uriHeaders); + err = setDataSource_l(savedHTTPService, uri, &uriHeaders); } mFlags |= PREPARING; 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 @@ CameraSourceListener::~CameraSourceListener() { } 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 @@ CameraSource::~CameraSource() { } } -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 @@ void CameraSource::startCameraRecording() { } } + 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 @@ status_t CameraSource::start(MetaData *meta) { } } - 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 5772316..60cdf66 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -85,7 +85,8 @@ CameraSourceTimeLapse::CameraSourceTimeLapse( mVideoWidth = videoSize.width; mVideoHeight = videoSize.height; - if (!trySettingVideoSize(videoSize.width, videoSize.height)) { + if (OK == mInitCheck && !trySettingVideoSize(videoSize.width, videoSize.height)) { + releaseCamera(); mInitCheck = NO_INIT; } @@ -231,7 +232,7 @@ sp<IMemory> CameraSourceTimeLapse::createIMemoryCopy( 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 97987e2..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> @@ -180,7 +180,9 @@ void DataSource::RegisterDefaultSniffers() { // 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; @@ -189,7 +191,7 @@ sp<DataSource> DataSource::CreateFromURI( } 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) { @@ -220,11 +222,8 @@ sp<DataSource> DataSource::CreateFromURI( // 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 @@ private: // 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 @@ void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status) // Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved. // These are candidates for optimization if needed. -static void copyMono8(short *dst, const int *const *src, unsigned nSamples, 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 copyMultiCh8(short *dst, const int *const *src, unsigned nSamples, u } } -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 @@ static void copyMultiCh16(short *dst, const int *const *src, unsigned nSamples, // 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 copyMultiCh24(short *dst, const int *const *src, unsigned nSamples, } } -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 @@ FLACSource::~FLACSource() } } -status_t FLACSource::start(MetaData *params) +status_t FLACSource::start(MetaData * /* params */) { ALOGV("FLACSource::start"); @@ -792,8 +813,7 @@ sp<MediaSource> FLACExtractor::getTrack(size_t index) } 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 @@ HTTPBase::HTTPBase() 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 @@ status_t HTTPBase::setBandwidthStatCollectFreq(int32_t freqMs) { 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 @@ sp<MediaSource> MP3Extractor::getTrack(size_t index) { 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 @@ status_t MPEG2TSWriter::addSource(const sp<MediaSource> &source) { return OK; } -status_t MPEG2TSWriter::start(MetaData *param) { +status_t MPEG2TSWriter::start(MetaData * /* param */) { CHECK(!mStarted); mStarted = true; @@ -596,7 +596,8 @@ bool MPEG2TSWriter::reachedEOS() { 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 6a33ce6..2a3fa04 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -488,12 +488,12 @@ status_t MPEG4Extractor::readMetaData() { 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; } @@ -571,7 +571,8 @@ static int32_t readSize(off64_t offset, 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; @@ -912,6 +913,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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) { @@ -974,12 +977,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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; @@ -993,12 +997,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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; } @@ -1043,23 +1048,25 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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) { @@ -1085,12 +1092,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } 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; } @@ -1171,7 +1179,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->meta->setCString( kKeyMediaLanguage, lang_code); - *offset += chunk_size; break; } @@ -1338,11 +1345,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->sampleTable->setChunkOffsetParams( chunk_type, data_offset, chunk_data_size); + *offset += chunk_size; + if (err != OK) { return err; } - *offset += chunk_size; break; } @@ -1352,11 +1360,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->sampleTable->setSampleToChunkParams( data_offset, chunk_data_size); + *offset += chunk_size; + if (err != OK) { return err; } - *offset += chunk_size; break; } @@ -1367,6 +1376,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->sampleTable->setSampleSizeParams( chunk_type, data_offset, chunk_data_size); + *offset += chunk_size; + if (err != OK) { return err; } @@ -1407,7 +1418,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size); } - *offset += chunk_size; // NOTE: setting another piece of metadata invalidates any pointers (such as the // mimetype) previously obtained, so don't cache them. @@ -1431,6 +1441,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('s', 't', 't', 's'): { + *offset += chunk_size; + status_t err = mLastTrack->sampleTable->setTimeToSampleParams( data_offset, chunk_data_size); @@ -1439,12 +1451,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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); @@ -1453,12 +1466,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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); @@ -1467,13 +1481,14 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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, @@ -1502,12 +1517,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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; } @@ -1545,12 +1561,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } } - *offset += chunk_size; break; } case FOURCC('a', 'v', 'c', 'C'): { + *offset += chunk_size; + sp<ABuffer> buffer = new ABuffer(chunk_data_size); if (mDataSource->readAt( @@ -1561,12 +1578,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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 @@ -1592,7 +1609,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size); - *offset += chunk_size; break; } @@ -1600,11 +1616,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { { 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; } @@ -1638,6 +1656,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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); @@ -1646,12 +1666,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } } - *offset += chunk_size; break; } case FOURCC('m', 'v', 'h', 'd'): { + *offset += chunk_size; + if (chunk_data_size < 24) { return ERROR_MALFORMED; } @@ -1679,7 +1700,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mFileMetaData->setCString(kKeyDate, s.string()); - *offset += chunk_size; break; } @@ -1700,6 +1720,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('h', 'd', 'l', 'r'): { + *offset += chunk_size; + uint32_t buffer; if (mDataSource->readAt( data_offset + 8, &buffer, 4) < 4) { @@ -1714,7 +1736,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP); } - *offset += chunk_size; break; } @@ -1739,6 +1760,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { delete[] buffer; buffer = NULL; + // advance read pointer so we don't end up reading this again + *offset += chunk_size; return ERROR_IO; } @@ -1753,6 +1776,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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); @@ -1767,7 +1792,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox); } - *offset += chunk_size; break; } @@ -1778,25 +1802,27 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { 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; } - *offset += chunk_size; break; } case FOURCC('I', 'D', '3', '2'): { + *offset += chunk_size; + if (chunk_data_size < 6) { return ERROR_MALFORMED; } parseID3v2MetaData(data_offset + 6); - *offset += chunk_size; break; } @@ -1920,9 +1946,10 @@ status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) { 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; @@ -2441,6 +2468,58 @@ status_t MPEG4Extractor::verifyTrack(Track *track) { 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); @@ -2523,7 +2602,7 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( sampleRate = kSamplingRate[freqIndex]; } - if (objectType == 5 || objectType == 29) { // SBR specific config per 14496-3 table 1.13 + 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) { @@ -2541,6 +2620,111 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( // mLastTrack->meta->setInt32(kKeyExtSampleRate, extSampleRate); } + 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; + } + } + } + if (numChannels == 0) { return ERROR_UNSUPPORTED; } @@ -2741,9 +2925,20 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { } } 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; } @@ -2802,7 +2997,8 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { 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; @@ -2864,7 +3060,8 @@ status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(off64_t offset, off64 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; @@ -3546,7 +3743,7 @@ status_t MPEG4Source::fragmentedRead( 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 @@ -3559,11 +3756,19 @@ status_t MPEG4Source::fragmentedRead( 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) { @@ -3575,7 +3780,7 @@ status_t MPEG4Source::fragmentedRead( } off64_t offset = 0; - size_t size; + size_t size = 0; uint32_t cts = 0; bool isSyncSample = false; bool newBuffer = false; @@ -3583,16 +3788,18 @@ status_t MPEG4Source::fragmentedRead( 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 2f1f449..24e53b3 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -41,6 +41,12 @@ #include "include/ESDS.h" +#define WARN_UNLESS(condition, message, ...) \ +( (CONDITION(condition)) ? false : ({ \ + ALOGW("Condition %s failed " message, #condition, ##__VA_ARGS__); \ + true; \ +})) + namespace android { static const int64_t kMinStreamableFileSizeInBytes = 5 * 1024 * 1024; @@ -409,7 +415,7 @@ status_t MPEG4Writer::dump( } 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; @@ -975,13 +981,16 @@ void MPEG4Writer::writeFtypBox(MetaData *param) { if (param && param->findInt32(kKeyFileType, &fileType) && fileType != OUTPUT_FORMAT_MPEG_4) { writeFourcc("3gp4"); + writeInt32(0); + writeFourcc("isom"); + writeFourcc("3gp4"); } else { + writeFourcc("mp42"); + writeInt32(0); writeFourcc("isom"); + writeFourcc("mp42"); } - writeInt32(0); - writeFourcc("isom"); - writeFourcc("3gp4"); endBox(); } @@ -1763,7 +1772,7 @@ status_t MPEG4Writer::Track::pause() { } 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; @@ -1774,19 +1783,14 @@ status_t MPEG4Writer::Track::stop() { } 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 = static_cast<status_t>(reinterpret_cast<uintptr_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; - } - } - ALOGD("%s track stopped", mIsAudio? "Audio": "Video"); return err; } @@ -2100,6 +2104,7 @@ status_t MPEG4Writer::Track::threadEntry() { status_t err = OK; MediaBuffer *buffer; + const char *trackName = mIsAudio ? "Audio" : "Video"; while (!mDone && (err = mSource->read(&buffer)) == OK) { if (buffer->range_length() == 0) { buffer->release(); @@ -2195,15 +2200,27 @@ status_t MPEG4Writer::Track::threadEntry() { if (mResumed) { int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs; - CHECK_GE(durExcludingEarlierPausesUs, 0ll); + if (WARN_UNLESS(durExcludingEarlierPausesUs >= 0ll, "for %s track", trackName)) { + copy->release(); + return ERROR_MALFORMED; + } + int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs; - CHECK_GE(pausedDurationUs, lastDurationUs); + if (WARN_UNLESS(pausedDurationUs >= lastDurationUs, "for %s track", trackName)) { + copy->release(); + return ERROR_MALFORMED; + } + previousPausedDurationUs += pausedDurationUs - lastDurationUs; mResumed = false; } timestampUs -= previousPausedDurationUs; - CHECK_GE(timestampUs, 0ll); + if (WARN_UNLESS(timestampUs >= 0ll, "for %s track", trackName)) { + copy->release(); + return ERROR_MALFORMED; + } + if (!mIsAudio) { /* * Composition time: timestampUs @@ -2215,7 +2232,11 @@ status_t MPEG4Writer::Track::threadEntry() { decodingTimeUs -= previousPausedDurationUs; cttsOffsetTimeUs = timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs; - CHECK_GE(cttsOffsetTimeUs, 0ll); + if (WARN_UNLESS(cttsOffsetTimeUs >= 0ll, "for %s track", trackName)) { + copy->release(); + return ERROR_MALFORMED; + } + timestampUs = decodingTimeUs; ALOGV("decoding time: %lld and ctts offset time: %lld", timestampUs, cttsOffsetTimeUs); @@ -2223,7 +2244,11 @@ status_t MPEG4Writer::Track::threadEntry() { // Update ctts box table if necessary currCttsOffsetTimeTicks = (cttsOffsetTimeUs * mTimeScale + 500000LL) / 1000000LL; - CHECK_LE(currCttsOffsetTimeTicks, 0x0FFFFFFFFLL); + if (WARN_UNLESS(currCttsOffsetTimeTicks <= 0x0FFFFFFFFLL, "for %s track", trackName)) { + copy->release(); + return ERROR_MALFORMED; + } + if (mStszTableEntries->count() == 0) { // Force the first ctts table entry to have one single entry // so that we can do adjustment for the initial track start @@ -2261,9 +2286,13 @@ status_t MPEG4Writer::Track::threadEntry() { } } - CHECK_GE(timestampUs, 0ll); + if (WARN_UNLESS(timestampUs >= 0ll, "for %s track", trackName)) { + copy->release(); + return ERROR_MALFORMED; + } + ALOGV("%s media time stamp: %lld and previous paused duration %lld", - mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs); + trackName, timestampUs, previousPausedDurationUs); if (timestampUs > mTrackDurationUs) { mTrackDurationUs = timestampUs; } @@ -2278,10 +2307,27 @@ status_t MPEG4Writer::Track::threadEntry() { (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL); if (currDurationTicks < 0ll) { ALOGE("timestampUs %lld < lastTimestampUs %lld for %s track", - timestampUs, lastTimestampUs, mIsAudio? "Audio": "Video"); + timestampUs, lastTimestampUs, trackName); + copy->release(); 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) { @@ -2302,7 +2348,7 @@ status_t MPEG4Writer::Track::threadEntry() { previousSampleSize = sampleSize; } ALOGV("%s timestampUs/lastTimestampUs: %lld/%lld", - mIsAudio? "Audio": "Video", timestampUs, lastTimestampUs); + trackName, timestampUs, lastTimestampUs); lastDurationUs = timestampUs - lastTimestampUs; lastDurationTicks = currDurationTicks; lastTimestampUs = timestampUs; @@ -2407,7 +2453,7 @@ status_t MPEG4Writer::Track::threadEntry() { sendTrackSummary(hasMultipleTracks); ALOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s", - count, nZeroLengthFrames, mStszTableEntries->count(), mIsAudio? "audio": "video"); + count, nZeroLengthFrames, mStszTableEntries->count(), trackName); if (mIsAudio) { ALOGI("Audio track drift time: %lld us", mOwner->getDriftTimeUs()); } 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 @@ MediaAdapter::~MediaAdapter() { 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 @@ void MediaAdapter::signalBufferReturned(MediaBuffer *buffer) { } 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 c4c47b3..601dccf 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -115,7 +115,7 @@ status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) { 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; @@ -352,6 +352,20 @@ status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const { return OK; } +status_t MediaCodec::getInputFormat(sp<AMessage> *format) const { + sp<AMessage> msg = new AMessage(kWhatGetInputFormat, id()); + + sp<AMessage> response; + status_t err; + if ((err = PostAndAwaitResponse(msg, &response)) != OK) { + return err; + } + + CHECK(response->findMessage("format", format)); + + return OK; +} + status_t MediaCodec::getName(AString *name) const { sp<AMessage> msg = new AMessage(kWhatGetName, id()); @@ -589,6 +603,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { postActivityNotificationIfPossible(); cancelPendingDequeueOperations(); + setState(UNINITIALIZED); break; } @@ -598,6 +613,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { mFlags |= kFlagStickyError; postActivityNotificationIfPossible(); + setState(UNINITIALIZED); break; } } @@ -642,6 +658,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { // reset input surface flag mHaveInputSurface = false; + CHECK(msg->findMessage("input-format", &mInputFormat)); + CHECK(msg->findMessage("output-format", &mOutputFormat)); + (new AMessage)->postReply(mReplyID); break; } @@ -1330,14 +1349,19 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatGetInputFormat: case kWhatGetOutputFormat: { + sp<AMessage> format = + (msg->what() == kWhatGetOutputFormat ? mOutputFormat : mInputFormat); + uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); - if ((mState != STARTED && mState != FLUSHING) + if ((mState != CONFIGURED && mState != STARTING && + mState != STARTED && mState != FLUSHING) || (mFlags & kFlagStickyError) - || mOutputFormat == NULL) { + || format == NULL) { sp<AMessage> response = new AMessage; response->setInt32("err", INVALID_OPERATION); @@ -1346,7 +1370,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { } sp<AMessage> response = new AMessage; - response->setMessage("format", mOutputFormat); + response->setMessage("format", format); response->postReply(replyID); break; } diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index 6248e90..8a451c8 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -48,22 +48,43 @@ const MediaCodecList *MediaCodecList::getInstance() { MediaCodecList::MediaCodecList() : mInitCheck(NO_INIT) { - FILE *file = fopen("/etc/media_codecs.xml", "r"); + parseTopLevelXMLFile("/etc/media_codecs.xml"); +} - if (file == NULL) { - ALOGW("unable to open media codecs configuration xml file."); +void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) { + // get href_base + char *href_base_end = strrchr(codecs_xml, '/'); + if (href_base_end != NULL) { + mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1); + } + + mInitCheck = OK; + mCurrentSection = SECTION_TOPLEVEL; + mDepth = 0; + + parseXMLFile(codecs_xml); + + if (mInitCheck != OK) { + mCodecInfos.clear(); + mCodecQuirks.clear(); return; } - parseXMLFile(file); + // These are currently still used by the video editing suite. + addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm"); + addMediaCodec( + false /* encoder */, "OMX.google.raw.decoder", "audio/raw"); - if (mInitCheck == OK) { - // These are currently still used by the video editing suite. + for (size_t i = mCodecInfos.size(); i-- > 0;) { + CodecInfo *info = &mCodecInfos.editItemAt(i); - addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm"); + if (info->mTypes == 0) { + // No types supported by this component??? + ALOGW("Component %s does not support any type of media?", + info->mName.c_str()); - addMediaCodec( - false /* encoder */, "OMX.google.raw.decoder", "audio/raw"); + mCodecInfos.removeAt(i); + } } #if 0 @@ -84,9 +105,6 @@ MediaCodecList::MediaCodecList() ALOGI("%s", line.c_str()); } #endif - - fclose(file); - file = NULL; } MediaCodecList::~MediaCodecList() { @@ -96,10 +114,14 @@ status_t MediaCodecList::initCheck() const { return mInitCheck; } -void MediaCodecList::parseXMLFile(FILE *file) { - mInitCheck = OK; - mCurrentSection = SECTION_TOPLEVEL; - mDepth = 0; +void MediaCodecList::parseXMLFile(const char *path) { + FILE *file = fopen(path, "r"); + + if (file == NULL) { + ALOGW("unable to open media codecs configuration xml file: %s", path); + mInitCheck = NAME_NOT_FOUND; + return; + } XML_Parser parser = ::XML_ParserCreate(NULL); CHECK(parser != NULL); @@ -112,7 +134,7 @@ void MediaCodecList::parseXMLFile(FILE *file) { while (mInitCheck == OK) { void *buff = ::XML_GetBuffer(parser, BUFF_SIZE); if (buff == NULL) { - ALOGE("failed to in call to XML_GetBuffer()"); + ALOGE("failed in call to XML_GetBuffer()"); mInitCheck = UNKNOWN_ERROR; break; } @@ -124,8 +146,9 @@ void MediaCodecList::parseXMLFile(FILE *file) { break; } - if (::XML_ParseBuffer(parser, bytes_read, bytes_read == 0) - != XML_STATUS_OK) { + XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0); + if (status != XML_STATUS_OK) { + ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser))); mInitCheck = ERROR_MALFORMED; break; } @@ -137,25 +160,8 @@ void MediaCodecList::parseXMLFile(FILE *file) { ::XML_ParserFree(parser); - if (mInitCheck == OK) { - for (size_t i = mCodecInfos.size(); i-- > 0;) { - CodecInfo *info = &mCodecInfos.editItemAt(i); - - if (info->mTypes == 0) { - // No types supported by this component??? - - ALOGW("Component %s does not support any type of media?", - info->mName.c_str()); - - mCodecInfos.removeAt(i); - } - } - } - - if (mInitCheck != OK) { - mCodecInfos.clear(); - mCodecQuirks.clear(); - } + fclose(file); + file = NULL; } // static @@ -169,12 +175,63 @@ void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) { static_cast<MediaCodecList *>(me)->endElementHandler(name); } +status_t MediaCodecList::includeXMLFile(const char **attrs) { + const char *href = NULL; + size_t i = 0; + while (attrs[i] != NULL) { + if (!strcmp(attrs[i], "href")) { + if (attrs[i + 1] == NULL) { + return -EINVAL; + } + href = attrs[i + 1]; + ++i; + } else { + return -EINVAL; + } + ++i; + } + + // For security reasons and for simplicity, file names can only contain + // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml + for (i = 0; href[i] != '\0'; i++) { + if (href[i] == '.' || href[i] == '_' || + (href[i] >= '0' && href[i] <= '9') || + (href[i] >= 'A' && href[i] <= 'Z') || + (href[i] >= 'a' && href[i] <= 'z')) { + continue; + } + ALOGE("invalid include file name: %s", href); + return -EINVAL; + } + + AString filename = href; + if (!filename.startsWith("media_codecs_") || + !filename.endsWith(".xml")) { + ALOGE("invalid include file name: %s", href); + return -EINVAL; + } + filename.insert(mHrefBase, 0); + + parseXMLFile(filename.c_str()); + return mInitCheck; +} + void MediaCodecList::startElementHandler( const char *name, const char **attrs) { if (mInitCheck != OK) { return; } + if (!strcmp(name, "Include")) { + mInitCheck = includeXMLFile(attrs); + if (mInitCheck == OK) { + mPastSections.push(mCurrentSection); + mCurrentSection = SECTION_INCLUDE; + } + ++mDepth; + return; + } + switch (mCurrentSection) { case SECTION_TOPLEVEL: { @@ -264,6 +321,15 @@ void MediaCodecList::endElementHandler(const char *name) { break; } + case SECTION_INCLUDE: + { + if (!strcmp(name, "Include") && mPastSections.size() > 0) { + mCurrentSection = mPastSections.top(); + mPastSections.pop(); + } + break; + } + default: break; } 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 340cba7..c670bb4 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -36,6 +36,7 @@ const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II = "audio/mpeg-L2"; 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"; diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp index d87e910..90335ee 100644 --- a/media/libstagefright/MediaMuxer.cpp +++ b/media/libstagefright/MediaMuxer.cpp @@ -16,6 +16,9 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaMuxer" + +#include "webm/WebmWriter.h" + #include <utils/Log.h> #include <media/stagefright/MediaMuxer.h> @@ -36,19 +39,30 @@ namespace android { MediaMuxer::MediaMuxer(const char *path, OutputFormat format) - : mState(UNINITIALIZED) { + : mFormat(format), + mState(UNINITIALIZED) { if (format == OUTPUT_FORMAT_MPEG_4) { mWriter = new MPEG4Writer(path); + } else if (format == OUTPUT_FORMAT_WEBM) { + mWriter = new WebmWriter(path); + } + + if (mWriter != NULL) { mFileMeta = new MetaData; mState = INITIALIZED; } - } MediaMuxer::MediaMuxer(int fd, OutputFormat format) - : mState(UNINITIALIZED) { + : mFormat(format), + mState(UNINITIALIZED) { if (format == OUTPUT_FORMAT_MPEG_4) { mWriter = new MPEG4Writer(fd); + } else if (format == OUTPUT_FORMAT_WEBM) { + mWriter = new WebmWriter(fd); + } + + if (mWriter != NULL) { mFileMeta = new MetaData; mState = INITIALIZED; } @@ -109,8 +123,13 @@ status_t MediaMuxer::setLocation(int latitude, int longitude) { ALOGE("setLocation() must be called before start()."); return INVALID_OPERATION; } + if (mFormat != OUTPUT_FORMAT_MPEG_4) { + ALOGE("setLocation() is only supported for .mp4 output."); + return INVALID_OPERATION; + } + ALOGV("Setting location: latitude = %d, longitude = %d", latitude, longitude); - return mWriter->setGeoData(latitude, longitude); + return static_cast<MPEG4Writer*>(mWriter.get())->setGeoData(latitude, longitude); } status_t MediaMuxer::start() { 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 @@ NuCachedSource2::NuCachedSource2( 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 @@ void NuCachedSource2::fetchInternal() { 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 @@ void NuCachedSource2::updateCacheParamsFromString(const char *s) { 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 @@ NuMediaExtractor::~NuMediaExtractor() { } 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 @@ status_t NuMediaExtractor::setDataSource( } sp<DataSource> dataSource = - DataSource::CreateFromURI(path, headers); + DataSource::CreateFromURI(httpService, path, headers); if (dataSource == NULL) { return -ENOENT; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 625922f..1cfe6c0 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -94,6 +94,7 @@ static sp<MediaSource> InstantiateSoftwareEncoder( #define CODEC_LOGI(x, ...) ALOGI("[%s] "x, mComponentName, ##__VA_ARGS__) #define CODEC_LOGV(x, ...) ALOGV("[%s] "x, mComponentName, ##__VA_ARGS__) +#define CODEC_LOGW(x, ...) ALOGW("[%s] "x, mComponentName, ##__VA_ARGS__) #define CODEC_LOGE(x, ...) ALOGE("[%s] "x, mComponentName, ##__VA_ARGS__) struct OMXCodecObserver : public BnOMXObserver { @@ -489,6 +490,13 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { 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); } } @@ -1387,6 +1395,8 @@ void OMXCodec::setComponentRole( "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, @@ -1794,21 +1804,42 @@ status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { strerror(-err), -err); return err; } - - // XXX: Is this the right logic to use? It's not clear to me what the OMX - // buffer counts refer to - how do they account for the renderer holding on - // to buffers? - if (def.nBufferCountActual < def.nBufferCountMin + minUndequeuedBufs) { - OMX_U32 newBufferCount = def.nBufferCountMin + minUndequeuedBufs; + // FIXME: assume that surface is controlled by app (native window + // returns the number for the case when surface is not controlled by app) + // FIXME2: This means that minUndeqeueudBufs can be 1 larger than reported + // For now, try to allocate 1 more buffer, but don't fail if unsuccessful + + // Use conservative allocation while also trying to reduce starvation + // + // 1. allocate at least nBufferCountMin + minUndequeuedBuffers - that is the + // minimum needed for the consumer to be able to work + // 2. try to allocate two (2) additional buffers to reduce starvation from + // the consumer + // plus an extra buffer to account for incorrect minUndequeuedBufs + CODEC_LOGI("OMX-buffers: min=%u actual=%u undeq=%d+1", + def.nBufferCountMin, def.nBufferCountActual, minUndequeuedBufs); + + for (OMX_U32 extraBuffers = 2 + 1; /* condition inside loop */; extraBuffers--) { + OMX_U32 newBufferCount = + def.nBufferCountMin + minUndequeuedBufs + extraBuffers; def.nBufferCountActual = newBufferCount; err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); - if (err != OK) { - CODEC_LOGE("setting nBufferCountActual to %lu failed: %d", - newBufferCount, err); + + if (err == OK) { + minUndequeuedBufs += extraBuffers; + break; + } + + CODEC_LOGW("setting nBufferCountActual to %lu failed: %d", + newBufferCount, err); + /* exit condition */ + if (extraBuffers == 0) { return err; } } + CODEC_LOGI("OMX-buffers: min=%u actual=%u undeq=%d+1", + def.nBufferCountMin, def.nBufferCountActual, minUndequeuedBufs); err = native_window_set_buffer_count( mNativeWindow.get(), def.nBufferCountActual); @@ -4125,6 +4156,7 @@ static const char *audioCodingTypeString(OMX_AUDIO_CODINGTYPE type) { "OMX_AUDIO_CodingMP3", "OMX_AUDIO_CodingSBC", "OMX_AUDIO_CodingVORBIS", + "OMX_AUDIO_CodingOPUS", "OMX_AUDIO_CodingWMA", "OMX_AUDIO_CodingRA", "OMX_AUDIO_CodingMIDI", 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 @@ sp<MetaData> OggSource::getFormat() { 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 MyVorbisExtractor::readPage(off64_t offset, Page *page) { 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 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { 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 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { 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<MediaSource> OggExtractor::getTrack(size_t index) { } 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/SkipCutBuffer.cpp b/media/libstagefright/SkipCutBuffer.cpp index 773854f..e2e6d79 100644 --- a/media/libstagefright/SkipCutBuffer.cpp +++ b/media/libstagefright/SkipCutBuffer.cpp @@ -25,7 +25,7 @@ namespace android { SkipCutBuffer::SkipCutBuffer(int32_t skip, int32_t cut) { - mFrontPadding = skip; + mFrontPadding = mSkip = skip; mBackPadding = cut; mWriteHead = 0; mReadHead = 0; @@ -94,6 +94,7 @@ void SkipCutBuffer::submit(const sp<ABuffer>& buffer) { void SkipCutBuffer::clear() { mWriteHead = mReadHead = 0; + mFrontPadding = mSkip; } void SkipCutBuffer::write(const char *src, size_t num) { 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::processFile( } 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 @@ MediaScanResult StagefrightMediaScanner::processFileInternal( 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 fcd9a85..9475d05 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -21,6 +21,7 @@ #include "include/StagefrightMetadataRetriever.h" +#include <media/IMediaHTTPService.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/ColorConverter.h> #include <media/stagefright/DataSource.h> @@ -51,7 +52,9 @@ StagefrightMetadataRetriever::~StagefrightMetadataRetriever() { } 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; @@ -59,7 +62,7 @@ status_t StagefrightMetadataRetriever::setDataSource( 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); diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index 6b934d4..e7cc46d 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -99,8 +99,11 @@ void SurfaceMediaSource::dump(String8& result) const 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 @@ status_t SurfaceMediaSource::stop() return OK; } + mStarted = false; + mFrameAvailableCondition.signal(); + while (mNumPendingBuffers > 0) { ALOGI("Still waiting for %d buffers to be returned.", mNumPendingBuffers); @@ -215,8 +221,6 @@ status_t SurfaceMediaSource::stop() mMediaBuffersAvailableCondition.wait(mMutex); } - mStarted = false; - mFrameAvailableCondition.signal(); mMediaBuffersAvailableCondition.signal(); return mBufferQueue->consumerDisconnect(); @@ -269,9 +273,8 @@ static void passMetadataBuffer(MediaBuffer **buffer, 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); @@ -474,4 +477,8 @@ void SurfaceMediaSource::onBuffersReleased() { } } +void SurfaceMediaSource::onSidebandStreamChanged() { + ALOG_ASSERT(false, "SurfaceMediaSource can't consume sideband streams"); +} + } // end of namespace android diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp index 0afac69..3d2eb1f 100644 --- a/media/libstagefright/TimedEventQueue.cpp +++ b/media/libstagefright/TimedEventQueue.cpp @@ -376,8 +376,8 @@ void TimedEventQueue::clearPowerManager() mPowerManager.clear(); } -void TimedEventQueue::PMDeathRecipient::binderDied(const wp<IBinder>& who) -{ +void TimedEventQueue::PMDeathRecipient::binderDied( + const wp<IBinder>& /* who */) { mQueue->clearPowerManager(); } diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 216a329..4ff805f 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -251,6 +251,13 @@ status_t convertMetaDataToMessage( 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 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { } } + 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 @@ static const struct mime_conv_t mimeLookup[] = { { 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 } }; 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 @@ sp<VBRISeeker> VBRISeeker::CreateFromSource( 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 @@ bool VBRISeeker::getOffsetForTime(int64_t *timeUs, off64_t *pos) { *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<MediaSource> WAVExtractor::getTrack(size_t index) { } 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 @@ WAVSource::~WAVSource() { } } -status_t WAVSource::start(MetaData *params) { +status_t WAVSource::start(MetaData * /* params */) { ALOGV("WAVSource::start"); CHECK(!mStarted); 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 @@ unsigned parseUE(ABitReader *br) { 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 @@ void FindAVCDimensions( 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 @@ status_t getNextNALUnit( 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 @@ sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) { 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 @@ sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) { 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 109e3fe..0000000 --- a/media/libstagefright/chromium_http/Android.mk +++ /dev/null @@ -1,39 +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 += \ - libbinder \ - libstlport \ - libchromium_net \ - libutils \ - libbinder \ - 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 7e5c280..0000000 --- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp +++ /dev/null @@ -1,355 +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::onRedirect(const char *url) { - Mutex::Autolock autoLock(mLock); - mURI = url; -} - -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/support.cpp b/media/libstagefright/chromium_http/support.cpp deleted file mode 100644 index 3de4877..0000000 --- a/media/libstagefright/chromium_http/support.cpp +++ /dev/null @@ -1,659 +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 <arpa/inet.h> -#include <binder/Parcel.h> -#include <cutils/log.h> -#include <media/stagefright/MediaErrors.h> -#include <media/stagefright/Utils.h> -#include <string> - -#include <utils/Errors.h> -#include <binder/IInterface.h> -#include <binder/IServiceManager.h> - -namespace android { - -// must be kept in sync with interface defined in IAudioService.aidl -class IAudioService : public IInterface -{ -public: - DECLARE_META_INTERFACE(AudioService); - - virtual int verifyX509CertChain( - const std::vector<std::string>& cert_chain, - const std::string& hostname, - const std::string& auth_type) = 0; -}; - -class BpAudioService : public BpInterface<IAudioService> -{ -public: - BpAudioService(const sp<IBinder>& impl) - : BpInterface<IAudioService>(impl) - { - } - - virtual int verifyX509CertChain( - const std::vector<std::string>& cert_chain, - const std::string& hostname, - const std::string& auth_type) - { - Parcel data, reply; - data.writeInterfaceToken(IAudioService::getInterfaceDescriptor()); - - // The vector of std::string we get isn't really a vector of strings, - // but rather a vector of binary certificate data. If we try to pass - // it to Java language code as a string, it ends up mangled on the other - // side, so send them as bytes instead. - // Since we can't send an array of byte arrays, send a single array, - // which will be split out by the recipient. - - int numcerts = cert_chain.size(); - data.writeInt32(numcerts); - size_t total = 0; - for (int i = 0; i < numcerts; i++) { - total += cert_chain[i].size(); - } - size_t bytesize = total + numcerts * 4; - uint8_t *bytes = (uint8_t*) malloc(bytesize); - if (!bytes) { - return 5; // SSL_INVALID - } - ALOGV("%d certs: %d -> %d", numcerts, total, bytesize); - - int offset = 0; - for (int i = 0; i < numcerts; i++) { - int32_t certsize = cert_chain[i].size(); - // store this in a known order, which just happens to match the default - // byte order of a java ByteBuffer - int32_t bigsize = htonl(certsize); - ALOGV("cert %d, size %d", i, certsize); - memcpy(bytes + offset, &bigsize, sizeof(bigsize)); - offset += sizeof(bigsize); - memcpy(bytes + offset, cert_chain[i].data(), certsize); - offset += certsize; - } - data.writeByteArray(bytesize, bytes); - free(bytes); - data.writeString16(String16(hostname.c_str())); - data.writeString16(String16(auth_type.c_str())); - - int32_t result; - if (remote()->transact(IBinder::FIRST_CALL_TRANSACTION, data, &reply) != NO_ERROR - || reply.readExceptionCode() < 0 || reply.readInt32(&result) != NO_ERROR) { - return 5; // SSL_INVALID; - } - return result; - } - -}; - -IMPLEMENT_META_INTERFACE(AudioService, "android.media.IAudioService"); - - -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) { - - sp<IBinder> binder = - defaultServiceManager()->checkService(String16("audio")); - if (binder == 0) { - ALOGW("Thread cannot connect to the audio service"); - } else { - sp<IAudioService> service = interface_cast<IAudioService>(binder); - int code = service->verifyX509CertChain(cert_chain, hostname, auth_type); - ALOGV("verified: %d", code); - if (code == -1) { - return VERIFY_OK; - } else if (code == 2) { // SSL_IDMISMATCH - return VERIFY_BAD_HOSTNAME; - } else if (code == 3) { // SSL_UNTRUSTED - return VERIFY_NO_TRUSTED_ROOT; - } - } - return VERIFY_INVOCATION_ERROR; -} - -//////////////////////////////////////////////////////////////////////////////// - -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"); - mOwner->onRedirect(new_url.spec().c_str()); -} - -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_C_INCLUDES := \ 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 1b20cbb..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" @@ -58,6 +58,8 @@ SoftAAC2::SoftAAC2( mIsADTS(false), mInputBufferCount(0), mSignalledError(false), + mSawInputEos(false), + mSignalledOutputEos(false), mAnchorTimeUs(0), mNumSamplesOutput(0), mOutputPortSettingsChange(NONE) { @@ -294,8 +296,11 @@ void SoftAAC2::maybeConfigureDownmix() const { 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) } } } @@ -350,115 +355,83 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { return; } - while (!inQueue.empty() && !outQueue.empty()) { - BufferInfo *inInfo = *inQueue.begin(); - OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { + BufferInfo *inInfo = NULL; + OMX_BUFFERHEADERTYPE *inHeader = NULL; + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + outHeader->nFlags = 0; - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - inQueue.erase(inQueue.begin()); - inInfo->mOwnedByUs = false; - notifyEmptyBufferDone(inHeader); - - if (mDecoderHasData) { - // flush out the decoder's delayed data by calling DecodeFrame - // one more time, with the AACDEC_FLUSH flag set - INT_PCM *outBuffer = - reinterpret_cast<INT_PCM *>( - outHeader->pBuffer + outHeader->nOffset); - - AAC_DECODER_ERROR decoderErr = - aacDecoder_DecodeFrame(mAACDecoder, - outBuffer, - outHeader->nAllocLen, - AACDEC_FLUSH); - mDecoderHasData = false; - - if (decoderErr != AAC_DEC_OK) { - mSignalledError = true; - - notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, - NULL); - - return; - } - - outHeader->nFilledLen = - mStreamInfo->frameSize - * sizeof(int16_t) - * mStreamInfo->numChannels; - } else { - // we never submitted any data to the decoder, so there's nothing to flush out - outHeader->nFilledLen = 0; + if (inHeader) { + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEos = true; } - outHeader->nFlags = OMX_BUFFERFLAG_EOS; - - outQueue.erase(outQueue.begin()); - outInfo->mOwnedByUs = false; - notifyFillBufferDone(outHeader); - return; - } - - if (inHeader->nOffset == 0) { - mAnchorTimeUs = inHeader->nTimeStamp; - mNumSamplesOutput = 0; - } + if (inHeader->nOffset == 0 && inHeader->nFilledLen) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumSamplesOutput = 0; + } - size_t adtsHeaderSize = 0; - if (mIsADTS) { - // skip 30 bits, aac_frame_length follows. - // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? + if (mIsADTS && inHeader->nFilledLen) { + size_t adtsHeaderSize = 0; + // skip 30 bits, aac_frame_length follows. + // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? - const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; + const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; - bool signalError = false; - if (inHeader->nFilledLen < 7) { - ALOGE("Audio data too short to contain even the ADTS header. " - "Got %ld bytes.", inHeader->nFilledLen); - hexdump(adtsHeader, inHeader->nFilledLen); - signalError = true; - } else { - bool protectionAbsent = (adtsHeader[1] & 1); - - unsigned aac_frame_length = - ((adtsHeader[3] & 3) << 11) - | (adtsHeader[4] << 3) - | (adtsHeader[5] >> 5); - - if (inHeader->nFilledLen < aac_frame_length) { - ALOGE("Not enough audio data for the complete frame. " - "Got %ld bytes, frame size according to the ADTS " - "header is %u bytes.", - inHeader->nFilledLen, aac_frame_length); + bool signalError = false; + if (inHeader->nFilledLen < 7) { + ALOGE("Audio data too short to contain even the ADTS header. " + "Got %ld bytes.", inHeader->nFilledLen); hexdump(adtsHeader, inHeader->nFilledLen); signalError = true; } else { - adtsHeaderSize = (protectionAbsent ? 7 : 9); - - inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize; - inBufferLength[0] = aac_frame_length - adtsHeaderSize; - - inHeader->nOffset += adtsHeaderSize; - inHeader->nFilledLen -= adtsHeaderSize; + bool protectionAbsent = (adtsHeader[1] & 1); + + unsigned aac_frame_length = + ((adtsHeader[3] & 3) << 11) + | (adtsHeader[4] << 3) + | (adtsHeader[5] >> 5); + + if (inHeader->nFilledLen < aac_frame_length) { + ALOGE("Not enough audio data for the complete frame. " + "Got %ld bytes, frame size according to the ADTS " + "header is %u bytes.", + inHeader->nFilledLen, aac_frame_length); + hexdump(adtsHeader, inHeader->nFilledLen); + signalError = true; + } else { + adtsHeaderSize = (protectionAbsent ? 7 : 9); + + inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize; + inBufferLength[0] = aac_frame_length - adtsHeaderSize; + + inHeader->nOffset += adtsHeaderSize; + inHeader->nFilledLen -= adtsHeaderSize; + } } - } - if (signalError) { - mSignalledError = true; + if (signalError) { + mSignalledError = true; - notify(OMX_EventError, - OMX_ErrorStreamCorrupt, - ERROR_MALFORMED, - NULL); + notify(OMX_EventError, + OMX_ErrorStreamCorrupt, + ERROR_MALFORMED, + NULL); - return; + return; + } + } else { + inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; + inBufferLength[0] = inHeader->nFilledLen; } } else { - inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; - inBufferLength[0] = inHeader->nFilledLen; + inBufferLength[0] = 0; } // Fill and decode @@ -471,50 +444,66 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { int prevNumChannels = mStreamInfo->numChannels; AAC_DECODER_ERROR decoderErr = AAC_DEC_NOT_ENOUGH_BITS; - while (bytesValid[0] > 0 && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { + while ((bytesValid[0] > 0 || mSawInputEos) && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { + mDecoderHasData |= (bytesValid[0] > 0); aacDecoder_Fill(mAACDecoder, inBuffer, inBufferLength, bytesValid); - mDecoderHasData = true; decoderErr = aacDecoder_DecodeFrame(mAACDecoder, outBuffer, outHeader->nAllocLen, 0 /* flags */); - if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { - ALOGW("Not enough bits, bytesValid %d", bytesValid[0]); + if (mSawInputEos && bytesValid[0] <= 0) { + if (mDecoderHasData) { + // flush out the decoder's delayed data by calling DecodeFrame + // one more time, with the AACDEC_FLUSH flag set + decoderErr = aacDecoder_DecodeFrame(mAACDecoder, + outBuffer, + outHeader->nAllocLen, + AACDEC_FLUSH); + mDecoderHasData = false; + } + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mSignalledOutputEos = true; + break; + } else { + ALOGW("Not enough bits, bytesValid %d", bytesValid[0]); + } } } size_t numOutBytes = mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels; - if (decoderErr == AAC_DEC_OK) { - UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; - inHeader->nFilledLen -= inBufferUsedLength; - inHeader->nOffset += inBufferUsedLength; - } else { - ALOGW("AAC decoder returned error %d, substituting silence", - decoderErr); + if (inHeader) { + if (decoderErr == AAC_DEC_OK) { + UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; + inHeader->nFilledLen -= inBufferUsedLength; + inHeader->nOffset += inBufferUsedLength; + } else { + ALOGW("AAC decoder returned error %d, substituting silence", + decoderErr); - memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); + memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); - // Discard input buffer. - inHeader->nFilledLen = 0; + // Discard input buffer. + inHeader->nFilledLen = 0; - aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); + aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); - // fall through - } + // fall through + } - if (inHeader->nFilledLen == 0) { - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } } /* @@ -555,7 +544,6 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { // we've previously decoded valid data, in the latter case // (decode failed) we'll output a silent frame. outHeader->nFilledLen = numOutBytes; - outHeader->nFlags = 0; outHeader->nTimeStamp = mAnchorTimeUs @@ -582,6 +570,12 @@ void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { // depend on fragments from the last one decoded. // drain all existing data drainDecoder(); + // force decoder loop to drop the first decoded buffer by resetting these state variables, + // but only if initialization has already happened. + if (mInputBufferCount != 0) { + mInputBufferCount = 1; + mStreamInfo->sampleRate = 0; + } } } @@ -606,6 +600,8 @@ void SoftAAC2::onReset() { mStreamInfo->sampleRate = 0; mSignalledError = false; + mSawInputEos = false; + mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h index 2d960ab..a7ea1e2 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.h +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h @@ -55,6 +55,8 @@ private: bool mDecoderHasData; size_t mInputBufferCount; bool mSignalledError; + bool mSawInputEos; + bool mSignalledOutputEos; int64_t mAnchorTimeUs; int64_t mNumSamplesOutput; 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/ARMV5E LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV7 endif +LOCAL_CFLAGS += -Werror + include $(BUILD_STATIC_LIBRARY) ################################################################################ @@ -106,6 +108,8 @@ ifeq ($(AAC_LIBRARY), fraunhofer) LOCAL_CFLAGS := + LOCAL_CFLAGS += -Werror + LOCAL_STATIC_LIBRARIES := libFraunhoferAAC LOCAL_SHARED_LIBRARIES := \ @@ -128,6 +132,8 @@ else # visualon 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 @@ status_t SoftAACEncoder2::setAudioParams() { 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 @@ Word16 iLog4(Word32 value) 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_U32 VO_API voAACEncInit(VO_HANDLE * phCodec,VO_AUDIO_CODINGTYPE vType, VO_COD VO_MEM_OPERATOR *pMemOP; int interMem; + UNUSED(vType); + interMem = 0; error = 0; @@ -471,6 +475,10 @@ VO_U32 VO_API voAACEncSetParam(VO_HANDLE hCodec, VO_S32 uParamID, VO_PTR pData) */ 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 @@ static void calcThreshExp(Word32 thrExp[MAX_CHANNELS][MAX_GROUPED_SFB], 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 WriteBitstream (HANDLE_BIT_BUF hBitStream, 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 @@ gmStage2(SECTION_INFO *sectionInfo, } 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 PsyOutNew(PSY_OUT *hPsyOut, VO_MEM_OPERATOR *pMemOP) *****************************************************************************/ 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 @@ Word16 QCNew(QC_STATE *hQC, VO_MEM_OPERATOR *pMemOP) **********************************************************************************/ 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 @@ static Word16 CalcTnsFilter(const Word16 *signal, 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_C_INCLUDES := \ 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_C_INCLUDES := \ LOCAL_CFLAGS := \ -DOSCL_UNUSED_ARG= -DOSCL_IMPORT_REF= +LOCAL_CFLAGS += -Werror + LOCAL_MODULE := libstagefright_amrnbdec include $(BUILD_STATIC_LIBRARY) @@ -68,6 +70,8 @@ LOCAL_C_INCLUDES := \ 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 @@ static size_t getFrameSize(unsigned FT) { 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::onQueueFilled(OMX_U32 portIndex) { } } -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_C_INCLUDES := \ LOCAL_CFLAGS := \ -DOSCL_UNUSED_ARG= +LOCAL_CFLAGS += -Werror + LOCAL_MODULE := libstagefright_amrnbenc include $(BUILD_STATIC_LIBRARY) @@ -88,6 +90,8 @@ LOCAL_C_INCLUDES := \ $(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 @@ OMX_ERRORTYPE SoftAMRNBEncoder::internalSetParameter( } } -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_C_INCLUDES := \ 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/ARMV5E LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV7 endif +LOCAL_CFLAGS += -Werror + include $(BUILD_STATIC_LIBRARY) ################################################################################ @@ -126,6 +128,8 @@ LOCAL_C_INCLUDES := \ 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 @@ OMX_ERRORTYPE SoftAMRWBEncoder::internalSetParameter( } } -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 @@ void Autocorr( 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 @@ void Convolve ( 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 @@ static void Norm_Corr( 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 @@ void Syn_filt_32( 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_U32 VO_API voAMRWB_Init(VO_HANDLE * phCodec, /* o: the audi 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_C_INCLUDES := \ $(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_C_INCLUDES := \ LOCAL_CFLAGS := \ -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF= +LOCAL_CFLAGS += -Werror + include $(BUILD_STATIC_LIBRARY) ################################################################################ @@ -69,4 +71,6 @@ LOCAL_SHARED_LIBRARIES := \ 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 1d398fb..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 @@ inline static void ConvertYUV420SemiPlanarToYUV420Planar( } 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 @@ OMX_ERRORTYPE SoftAVCEncoder::initEncParams() { 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; @@ -722,7 +728,7 @@ OMX_ERRORTYPE SoftAVCEncoder::internalSetParameter( } } -void SoftAVCEncoder::onQueueFilled(OMX_U32 portIndex) { +void SoftAVCEncoder::onQueueFilled(OMX_U32 /* portIndex */) { if (mSignalledError || mSawInputEOS) { return; } @@ -795,7 +801,7 @@ void SoftAVCEncoder::onQueueFilled(OMX_U32 portIndex) { } } - buffer_handle_t srcBuffer; // for MetaDataMode only + buffer_handle_t srcBuffer = NULL; // for MetaDataMode only // Get next input video frame if (mReadyForNextFrame) { @@ -964,6 +970,7 @@ int32_t SoftAVCEncoder::bindOutputBuffer(int32_t index, uint8_t **yuv) { } void SoftAVCEncoder::signalBufferReturned(MediaBuffer *buffer) { + UNUSED_UNLESS_VERBOSE(buffer); ALOGV("signalBufferReturned: %p", buffer); } 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_STATIC_LIBRARIES := 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 cmnMemAlloc (VO_S32 uID, VO_MEM_INFO * pMemInfo) 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 @@ LOCAL_C_INCLUDES := \ 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..0c62ec0 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> @@ -241,7 +247,7 @@ OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter( if (defParams->nPortIndex == 0) { if (defParams->nBufferSize > kMaxInputBufferSize) { - ALOGE("Input buffer size must be at most %zu bytes", + ALOGE("Input buffer size must be at most %d bytes", kMaxInputBufferSize); return OMX_ErrorUnsupportedSetting; } @@ -257,7 +263,7 @@ OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter( } void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) { - + UNUSED_UNLESS_VERBOSE(portIndex); ALOGV("SoftFlacEncoder::onQueueFilled(portIndex=%ld)", portIndex); if (mSignalledError) { @@ -343,16 +349,17 @@ void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) { } } - FLAC__StreamEncoderWriteStatus SoftFlacEncoder::onEncodedFlacAvailable( const FLAC__byte buffer[], - size_t bytes, unsigned samples, unsigned current_frame) { - ALOGV("SoftFlacEncoder::onEncodedFlacAvailable(bytes=%d, samples=%d, curr_frame=%d)", + size_t bytes, unsigned samples, + unsigned current_frame) { + UNUSED_UNLESS_VERBOSE(current_frame); + ALOGV("SoftFlacEncoder::onEncodedFlacAvailable(bytes=%zu, samples=%u, curr_frame=%u)", bytes, samples, current_frame); #ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER if (samples == 0) { - ALOGI(" saving %d bytes of header", bytes); + ALOGI(" saving %zu bytes of header", bytes); memcpy(mHeader + mHeaderOffset, buffer, bytes); mHeaderOffset += bytes;// will contain header size when finished receiving header return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; @@ -363,7 +370,7 @@ FLAC__StreamEncoderWriteStatus SoftFlacEncoder::onEncodedFlacAvailable( if ((samples == 0) || !mEncoderWriteData) { // called by the encoder because there's header data to save, but it's not the role // of this component (unless WRITE_FLAC_HEADER_IN_FIRST_BUFFER is defined) - ALOGV("ignoring %d bytes of header data (samples=%d)", bytes, samples); + ALOGV("ignoring %zu bytes of header data (samples=%d)", bytes, samples); return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; } @@ -384,9 +391,9 @@ FLAC__StreamEncoderWriteStatus SoftFlacEncoder::onEncodedFlacAvailable( #endif // write encoded data - ALOGV(" writing %d bytes of encoded data on output port", bytes); + ALOGV(" writing %zu bytes of encoded data on output port", bytes); if (bytes > outHeader->nAllocLen - outHeader->nOffset - outHeader->nFilledLen) { - ALOGE(" not enough space left to write encoded data, dropping %u bytes", bytes); + ALOGE(" not enough space left to write encoded data, dropping %zu bytes", bytes); // a fatal error would stop the encoding return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; } @@ -444,8 +451,12 @@ return_result: // 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_SHARED_LIBRARIES := \ 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 @@ OMX_ERRORTYPE SoftG711::internalSetParameter( } } -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 @@ LOCAL_C_INCLUDES := \ 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 @@ OMX_ERRORTYPE SoftGSM::internalSetParameter( } } -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_C_INCLUDES := \ LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF= +LOCAL_CFLAGS += -Werror + include $(BUILD_STATIC_LIBRARY) ################################################################################ @@ -72,4 +74,6 @@ LOCAL_SHARED_LIBRARIES := \ 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 @@ status_t SoftMPEG4::initDecoder() { 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/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 @@ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/av/media/libstagefright/include \ $(TOP)/frameworks/native/include/media/openmax +LOCAL_CFLAGS += -Werror + include $(BUILD_STATIC_LIBRARY) ################################################################################ @@ -72,4 +74,6 @@ LOCAL_SHARED_LIBRARIES := \ 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..ee8dcf2 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp @@ -33,6 +33,8 @@ #include "SoftMPEG4Encoder.h" +#include <inttypes.h> + namespace android { template<class T> @@ -620,7 +622,7 @@ OMX_ERRORTYPE SoftMPEG4Encoder::internalSetParameter( } } -void SoftMPEG4Encoder::onQueueFilled(OMX_U32 portIndex) { +void SoftMPEG4Encoder::onQueueFilled(OMX_U32 /* portIndex */) { if (mSignalledError || mSawInputEOS) { return; } @@ -677,7 +679,7 @@ void SoftMPEG4Encoder::onQueueFilled(OMX_U32 portIndex) { 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) { @@ -725,7 +727,7 @@ void SoftMPEG4Encoder::onQueueFilled(OMX_U32 portIndex) { if (!PVEncodeVideoFrame(mHandle, &vin, &vout, &modTimeMs, outPtr, &dataLength, &nLayer) || !PVGetHintTrack(mHandle, &hintTrack)) { - ALOGE("Failed to encode frame or get hink track at frame %lld", + ALOGE("Failed to encode frame or get hink track at frame %" PRId64, mNumInputFrames); mSignalledError = true; notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); 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_C_INCLUDES := \ LOCAL_CFLAGS := \ -DOSCL_UNUSED_ARG= +LOCAL_CFLAGS += -Werror + LOCAL_MODULE := libstagefright_mp3dec LOCAL_ARM_MODE := arm @@ -69,6 +71,8 @@ LOCAL_C_INCLUDES := \ $(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 7c382fb..5396022 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -49,6 +49,8 @@ SoftMP3::SoftMP3( mNumChannels(2), mSamplingRate(44100), mSignalledError(false), + mSawInputEos(false), + mSignalledOutputEos(false), mOutputPortSettingsChange(NONE) { initPorts(); initDecoder(); @@ -144,6 +146,23 @@ OMX_ERRORTYPE SoftMP3::internalGetParameter( return OMX_ErrorNone; } + case OMX_IndexParamAudioMp3: + { + OMX_AUDIO_PARAM_MP3TYPE *mp3Params = + (OMX_AUDIO_PARAM_MP3TYPE *)params; + + if (mp3Params->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + mp3Params->nChannels = mNumChannels; + mp3Params->nBitRate = 0 /* unknown */; + mp3Params->nSampleRate = mSamplingRate; + // other fields are encoder-only + + return OMX_ErrorNone; + } + default: return SimpleSoftOMXComponent::internalGetParameter(index, params); } @@ -186,7 +205,7 @@ OMX_ERRORTYPE SoftMP3::internalSetParameter( } } -void SoftMP3::onQueueFilled(OMX_U32 portIndex) { +void SoftMP3::onQueueFilled(OMX_U32 /* portIndex */) { if (mSignalledError || mOutputPortSettingsChange != NONE) { return; } @@ -194,48 +213,36 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); - while (!inQueue.empty() && !outQueue.empty()) { - BufferInfo *inInfo = *inQueue.begin(); - OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { + BufferInfo *inInfo = NULL; + OMX_BUFFERHEADERTYPE *inHeader = NULL; + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + outHeader->nFlags = 0; - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - inQueue.erase(inQueue.begin()); - inInfo->mOwnedByUs = false; - notifyEmptyBufferDone(inHeader); - - if (!mIsFirst) { - // pad the end of the stream with 529 samples, since that many samples - // were trimmed off the beginning when decoding started - outHeader->nFilledLen = - kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t); - - memset(outHeader->pBuffer, 0, outHeader->nFilledLen); - } else { - // Since we never discarded frames from the start, we won't have - // to add any padding at the end either. - outHeader->nFilledLen = 0; + if (inHeader) { + if (inHeader->nOffset == 0 && inHeader->nFilledLen) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; } - outHeader->nFlags = OMX_BUFFERFLAG_EOS; + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEos = true; + } - outQueue.erase(outQueue.begin()); - outInfo->mOwnedByUs = false; - notifyFillBufferDone(outHeader); - return; - } + mConfig->pInputBuffer = + inHeader->pBuffer + inHeader->nOffset; - if (inHeader->nOffset == 0) { - mAnchorTimeUs = inHeader->nTimeStamp; - mNumFramesOutput = 0; + mConfig->inputBufferCurrentLength = inHeader->nFilledLen; + } else { + mConfig->pInputBuffer = NULL; + mConfig->inputBufferCurrentLength = 0; } - - mConfig->pInputBuffer = - inHeader->pBuffer + inHeader->nOffset; - - mConfig->inputBufferCurrentLength = inHeader->nFilledLen; mConfig->inputBufferMaxLength = 0; mConfig->inputBufferUsedLength = 0; @@ -262,13 +269,28 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); } - // This is recoverable, just ignore the current frame and - // play silence instead. - memset(outHeader->pBuffer, - 0, - mConfig->outputFrameSize * sizeof(int16_t)); - - mConfig->inputBufferUsedLength = inHeader->nFilledLen; + if (decoderErr == NO_ENOUGH_MAIN_DATA_ERROR && mSawInputEos) { + if (!mIsFirst) { + // pad the end of the stream with 529 samples, since that many samples + // were trimmed off the beginning when decoding started + outHeader->nOffset = 0; + outHeader->nFilledLen = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t); + + memset(outHeader->pBuffer, 0, outHeader->nFilledLen); + } + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mSignalledOutputEos = true; + } else { + // This is recoverable, just ignore the current frame and + // play silence instead. + memset(outHeader->pBuffer, + 0, + mConfig->outputFrameSize * sizeof(int16_t)); + + if (inHeader) { + mConfig->inputBufferUsedLength = inHeader->nFilledLen; + } + } } else if (mConfig->samplingRate != mSamplingRate || mConfig->num_channels != mNumChannels) { mSamplingRate = mConfig->samplingRate; @@ -289,7 +311,7 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t) - outHeader->nOffset; - } else { + } else if (!mSignalledOutputEos) { outHeader->nOffset = 0; outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t); } @@ -298,23 +320,24 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { mAnchorTimeUs + (mNumFramesOutput * 1000000ll) / mConfig->samplingRate; - outHeader->nFlags = 0; - - CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength); + if (inHeader) { + CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength); - inHeader->nOffset += mConfig->inputBufferUsedLength; - inHeader->nFilledLen -= mConfig->inputBufferUsedLength; + inHeader->nOffset += mConfig->inputBufferUsedLength; + inHeader->nFilledLen -= mConfig->inputBufferUsedLength; - mNumFramesOutput += mConfig->outputFrameSize / mNumChannels; - if (inHeader->nFilledLen == 0) { - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } } + mNumFramesOutput += mConfig->outputFrameSize / mNumChannels; + outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; @@ -329,6 +352,9 @@ void SoftMP3::onPortFlushCompleted(OMX_U32 portIndex) { // depend on fragments from the last one decoded. pvmp3_InitDecoder(mConfig, mDecoderBuf); mIsFirst = true; + mSignalledError = false; + mSawInputEos = false; + mSignalledOutputEos = false; } } @@ -362,6 +388,8 @@ void SoftMP3::onReset() { pvmp3_InitDecoder(mConfig, mDecoderBuf); mIsFirst = true; mSignalledError = false; + mSawInputEos = false; + mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h index 4af91ea..f9e7b53 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.h +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h @@ -61,6 +61,8 @@ private: bool mIsFirst; bool mSignalledError; + bool mSawInputEos; + bool mSignalledOutputEos; enum { NONE, 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 @@ void pvmp3_mpeg2_get_scale_data(mp3SideInfo *si, 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_SHARED_LIBRARIES := \ 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 @@ status_t SoftVPX::initDecoder() { 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 5efe022..b3a6bcc 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -141,9 +141,9 @@ SoftVPXEncoder::SoftVPXEncoder(const char *name, mWidth(176), mHeight(144), mBitrate(192000), // in bps + mFramerate(30 << 16), // in Q16 format mBitrateUpdated(false), mBitrateControlMode(VPX_VBR), // variable bitrate - mFrameDurationUs(33333), // Defaults to 30 fps mDCTPartitions(0), mErrorResilience(OMX_FALSE), mColorFormat(OMX_COLOR_FormatYUV420Planar), @@ -180,9 +180,8 @@ void SoftVPXEncoder::initPorts() { inputPort.format.video.nStride = inputPort.format.video.nFrameWidth; inputPort.format.video.nSliceHeight = inputPort.format.video.nFrameHeight; inputPort.format.video.nBitrate = 0; - // frameRate is reciprocal of frameDuration, which is - // in microseconds. It is also in Q16 format. - inputPort.format.video.xFramerate = (1000000/mFrameDurationUs) << 16; + // frameRate is in Q16 format. + inputPort.format.video.xFramerate = mFramerate; inputPort.format.video.bFlagErrorConcealment = OMX_FALSE; inputPort.nPortIndex = kInputPortIndex; inputPort.eDir = OMX_DirInput; @@ -220,7 +219,7 @@ void SoftVPXEncoder::initPorts() { outputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingVP8; outputPort.format.video.eColorFormat = OMX_COLOR_FormatUnused; outputPort.format.video.pNativeWindow = NULL; - outputPort.nBufferSize = 256 * 1024; // arbitrary + outputPort.nBufferSize = 1024 * 1024; // arbitrary addPort(outputPort); } @@ -277,8 +276,39 @@ status_t SoftVPXEncoder::initEncoder() { mCodecConfiguration->g_timebase.num = 1; mCodecConfiguration->g_timebase.den = 1000000; // rc_target_bitrate is in kbps, mBitrate in bps - mCodecConfiguration->rc_target_bitrate = mBitrate/1000; + mCodecConfiguration->rc_target_bitrate = mBitrate / 1000; mCodecConfiguration->rc_end_usage = mBitrateControlMode; + // Disable frame drop - not allowed in MediaCodec now. + mCodecConfiguration->rc_dropframe_thresh = 0; + if (mBitrateControlMode == VPX_CBR) { + // Disable spatial resizing. + mCodecConfiguration->rc_resize_allowed = 0; + // Single-pass mode. + mCodecConfiguration->g_pass = VPX_RC_ONE_PASS; + // Minimum quantization level. + mCodecConfiguration->rc_min_quantizer = 2; + // Maximum quantization level. + mCodecConfiguration->rc_max_quantizer = 63; + // Maximum amount of bits that can be subtracted from the target + // bitrate - expressed as percentage of the target bitrate. + mCodecConfiguration->rc_undershoot_pct = 100; + // Maximum amount of bits that can be added to the target + // bitrate - expressed as percentage of the target bitrate. + mCodecConfiguration->rc_overshoot_pct = 15; + // Initial value of the buffer level in ms. + mCodecConfiguration->rc_buf_initial_sz = 500; + // Amount of data that the encoder should try to maintain in ms. + mCodecConfiguration->rc_buf_optimal_sz = 600; + // The amount of data that may be buffered by the decoding + // application in ms. + mCodecConfiguration->rc_buf_sz = 1000; + // Enable error resilience - needed for packet loss. + mCodecConfiguration->g_error_resilient = 1; + // Disable lagged encoding. + mCodecConfiguration->g_lag_in_frames = 0; + // Encoder determines optimal key frame placement automatically. + mCodecConfiguration->kf_mode = VPX_KF_AUTO; + } codec_return = vpx_codec_enc_init(mCodecContext, mCodecInterface, @@ -298,6 +328,33 @@ status_t SoftVPXEncoder::initEncoder() { return UNKNOWN_ERROR; } + // Extra CBR settings + if (mBitrateControlMode == VPX_CBR) { + codec_return = vpx_codec_control(mCodecContext, + VP8E_SET_STATIC_THRESHOLD, + 1); + if (codec_return == VPX_CODEC_OK) { + uint32_t rc_max_intra_target = + mCodecConfiguration->rc_buf_optimal_sz * (mFramerate >> 17) / 10; + // Don't go below 3 times per frame bandwidth. + if (rc_max_intra_target < 300) { + rc_max_intra_target = 300; + } + codec_return = vpx_codec_control(mCodecContext, + VP8E_SET_MAX_INTRA_BITRATE_PCT, + rc_max_intra_target); + } + if (codec_return == VPX_CODEC_OK) { + codec_return = vpx_codec_control(mCodecContext, + VP8E_SET_CPUUSED, + -8); + } + if (codec_return != VPX_CODEC_OK) { + ALOGE("Error setting cbr parameters for vpx encoder."); + return UNKNOWN_ERROR; + } + } + if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || mInputDataIsMeta) { if (mConversionBuffer == NULL) { mConversionBuffer = (uint8_t *)malloc(mWidth * mHeight * 3 / 2); @@ -361,9 +418,7 @@ OMX_ERRORTYPE SoftVPXEncoder::internalGetParameter(OMX_INDEXTYPE index, } formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; - // Converting from microseconds - // Also converting to Q16 format - formatParams->xFramerate = (1000000/mFrameDurationUs) << 16; + formatParams->xFramerate = mFramerate; return OMX_ErrorNone; } else if (formatParams->nPortIndex == kOutputPortIndex) { formatParams->eCompressionFormat = OMX_VIDEO_CodingVP8; @@ -660,9 +715,7 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams( mHeight = port->format.video.nFrameHeight; // xFramerate comes in Q16 format, in frames per second unit - const uint32_t framerate = port->format.video.xFramerate >> 16; - // frame duration is in microseconds - mFrameDurationUs = (1000000/framerate); + mFramerate = port->format.video.xFramerate; if (port->format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar || port->format.video.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || @@ -684,6 +737,13 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams( return OMX_ErrorNone; } else if (port->nPortIndex == kOutputPortIndex) { mBitrate = port->format.video.nBitrate; + mWidth = port->format.video.nFrameWidth; + mHeight = port->format.video.nFrameHeight; + + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; + def->format.video.nFrameWidth = mWidth; + def->format.video.nFrameHeight = mHeight; + def->format.video.nBitrate = mBitrate; return OMX_ErrorNone; } else { return OMX_ErrorBadPortIndex; @@ -814,11 +874,12 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { mBitrateUpdated = false; } + uint32_t frameDuration = (uint32_t)(((uint64_t)1000000 << 16) / mFramerate); codec_return = vpx_codec_encode( mCodecContext, &raw_frame, inputBufferHeader->nTimeStamp, // in timebase units - mFrameDurationUs, // frame duration in timebase units + frameDuration, // frame duration in timebase units flags, // frame flags VPX_DL_REALTIME); // encoding deadline if (codec_return != VPX_CODEC_OK) { diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index 076830f..1c983ab 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -130,16 +130,15 @@ private: // Target bitrate set for the encoder, in bits per second. uint32_t mBitrate; + // Target framerate set for the encoder. + uint32_t mFramerate; + // If a request for a change it bitrate has been received. bool mBitrateUpdated; // Bitrate control mode, either constant or variable vpx_rc_mode mBitrateControlMode; - // Frame duration is the reciprocal of framerate, denoted - // in microseconds - uint64_t mFrameDurationUs; - // vp8 specific configuration parameter // that enables token partitioning of // the stream into substreams 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 @@ status_t SoftAVC::initDecoder() { 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 @@ H264DEC_EVALUATION Compile evaluation version, restricts number of frames #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 @@ u32 ConcealMb(mbStorage_t *pMb, image_t *currImage, u32 row, u32 col, 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_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 @@ static void FillRow1( 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 h264bsdCheckPriorPicsFlag(u32 * noOutputOfPriorPicsFlag, 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 @@ u32 h264bsdMoreRbspData(strmData_t *pStrmData) 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/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 @@ LOCAL_C_INCLUDES := \ 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 @@ OMX_ERRORTYPE SoftRaw::internalSetParameter( } } -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_SHARED_LIBRARIES := \ LOCAL_MODULE := libstagefright_soft_vorbisdec LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS += -Werror + include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp index 51bb958..515e4d3 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -54,6 +54,8 @@ SoftVorbis::SoftVorbis( mAnchorTimeUs(0), mNumFramesOutput(0), mNumFramesLeftOnPage(-1), + mSawInputEos(false), + mSignalledOutputEos(false), mOutputPortSettingsChange(NONE) { initPorts(); CHECK_EQ(initDecoder(), (status_t)OK); @@ -290,48 +292,47 @@ void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { return; } - while (!inQueue.empty() && !outQueue.empty()) { - BufferInfo *inInfo = *inQueue.begin(); - OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { + BufferInfo *inInfo = NULL; + OMX_BUFFERHEADERTYPE *inHeader = NULL; + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + 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); + int32_t numPageSamples = 0; - outHeader->nFilledLen = 0; - outHeader->nFlags = OMX_BUFFERFLAG_EOS; + if (inHeader) { + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEos = true; + } - outQueue.erase(outQueue.begin()); - outInfo->mOwnedByUs = false; - notifyFillBufferDone(outHeader); - return; - } + if (inHeader->nFilledLen || !mSawInputEos) { + CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples)); + memcpy(&numPageSamples, + inHeader->pBuffer + + inHeader->nOffset + inHeader->nFilledLen - 4, + sizeof(numPageSamples)); - int32_t numPageSamples; - CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples)); - memcpy(&numPageSamples, - inHeader->pBuffer - + inHeader->nOffset + inHeader->nFilledLen - 4, - sizeof(numPageSamples)); + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; + } - if (numPageSamples >= 0) { - mNumFramesLeftOnPage = numPageSamples; + inHeader->nFilledLen -= sizeof(numPageSamples);; + } } - if (inHeader->nOffset == 0) { - mAnchorTimeUs = inHeader->nTimeStamp; - mNumFramesOutput = 0; + if (numPageSamples >= 0) { + mNumFramesLeftOnPage = numPageSamples; } - inHeader->nFilledLen -= sizeof(numPageSamples);; - ogg_buffer buf; - buf.data = inHeader->pBuffer + inHeader->nOffset; - buf.size = inHeader->nFilledLen; + buf.data = inHeader ? inHeader->pBuffer + inHeader->nOffset : NULL; + buf.size = inHeader ? inHeader->nFilledLen : 0; buf.refcount = 1; buf.ptr.owner = NULL; @@ -351,6 +352,7 @@ void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { int numFrames = 0; + outHeader->nFlags = 0; int err = vorbis_dsp_synthesis(mState, &pack, 1); if (err != 0) { ALOGW("vorbis_dsp_synthesis returned %d", err); @@ -370,13 +372,16 @@ void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { ALOGV("discarding %d frames at end of page", numFrames - mNumFramesLeftOnPage); numFrames = mNumFramesLeftOnPage; + if (mSawInputEos) { + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mSignalledOutputEos = true; + } } mNumFramesLeftOnPage -= numFrames; } outHeader->nFilledLen = numFrames * sizeof(int16_t) * mVi->channels; outHeader->nOffset = 0; - outHeader->nFlags = 0; outHeader->nTimeStamp = mAnchorTimeUs @@ -384,11 +389,13 @@ void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { mNumFramesOutput += numFrames; - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; + if (inHeader) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); @@ -425,6 +432,8 @@ void SoftVorbis::onReset() { mVi = NULL; } + mSawInputEos = false; + mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h index cb628a0..1d00816 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h @@ -59,6 +59,8 @@ private: int64_t mAnchorTimeUs; int64_t mNumFramesOutput; int32_t mNumFramesLeftOnPage; + bool mSawInputEos; + bool mSignalledOutputEos; enum { NONE, diff --git a/media/libstagefright/data/media_codecs_google_audio.xml b/media/libstagefright/data/media_codecs_google_audio.xml new file mode 100644 index 0000000..b1f93de --- /dev/null +++ b/media/libstagefright/data/media_codecs_google_audio.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- 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. +--> + +<Included> + <Decoders> + <MediaCodec name="OMX.google.mp3.decoder" type="audio/mpeg" /> + <MediaCodec name="OMX.google.amrnb.decoder" type="audio/3gpp" /> + <MediaCodec name="OMX.google.amrwb.decoder" type="audio/amr-wb" /> + <MediaCodec name="OMX.google.aac.decoder" type="audio/mp4a-latm" /> + <MediaCodec name="OMX.google.g711.alaw.decoder" type="audio/g711-alaw" /> + <MediaCodec name="OMX.google.g711.mlaw.decoder" type="audio/g711-mlaw" /> + <MediaCodec name="OMX.google.vorbis.decoder" type="audio/vorbis" /> + <MediaCodec name="OMX.google.opus.decoder" type="audio/opus" /> + </Decoders> + + <Encoders> + <MediaCodec name="OMX.google.aac.encoder" type="audio/mp4a-latm" /> + <MediaCodec name="OMX.google.amrnb.encoder" type="audio/3gpp" /> + <MediaCodec name="OMX.google.amrwb.encoder" type="audio/amr-wb" /> + <MediaCodec name="OMX.google.flac.encoder" type="audio/flac" /> + </Encoders> +</Included> diff --git a/media/libstagefright/data/media_codecs_google_telephony.xml b/media/libstagefright/data/media_codecs_google_telephony.xml new file mode 100644 index 0000000..28f5ffc --- /dev/null +++ b/media/libstagefright/data/media_codecs_google_telephony.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- 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. +--> + +<Included> + <Decoders> + <MediaCodec name="OMX.google.gsm.decoder" type="audio/gsm" /> + </Decoders> +</Included> diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml new file mode 100644 index 0000000..41e0efb --- /dev/null +++ b/media/libstagefright/data/media_codecs_google_video.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- 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. +--> + +<Included> + <Decoders> + <MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es" /> + <MediaCodec name="OMX.google.h263.decoder" type="video/3gpp" /> + <MediaCodec name="OMX.google.h264.decoder" type="video/avc" /> + <MediaCodec name="OMX.google.vp8.decoder" type="video/x-vnd.on2.vp8" /> + <MediaCodec name="OMX.google.vp9.decoder" type="video/x-vnd.on2.vp9" /> + </Decoders> + + <Encoders> + <MediaCodec name="OMX.google.h263.encoder" type="video/3gpp" /> + <MediaCodec name="OMX.google.h264.encoder" type="video/avc" /> + <MediaCodec name="OMX.google.mpeg4.encoder" type="video/mp4v-es" /> + <MediaCodec name="OMX.google.vp8.encoder" type="video/x-vnd.on2.vp8" /> + </Encoders> +</Included> diff --git a/media/libstagefright/foundation/ANetworkSession.cpp b/media/libstagefright/foundation/ANetworkSession.cpp index 4cd51d0..af5be70 100644 --- a/media/libstagefright/foundation/ANetworkSession.cpp +++ b/media/libstagefright/foundation/ANetworkSession.cpp @@ -521,7 +521,7 @@ status_t ANetworkSession::Session::readMore() { 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 @@ LOCAL_SHARED_LIBRARIES := \ 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 @@ sp<ABuffer> decodeBase64(const AString &s) { if (n >= 2 && s.c_str()[n - 2] == '=') { padding = 2; + + if (n >= 3 && s.c_str()[n - 3] == '=') { + padding = 3; + } } } @@ -71,7 +75,7 @@ sp<ABuffer> decodeBase64(const AString &s) { 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 @@ LOCAL_C_INCLUDES:= \ $(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 61c54c1..fd42e77 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,29 +36,27 @@ #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 <inttypes.h> #include <openssl/aes.h> #include <openssl/md5.h> 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), @@ -69,9 +69,6 @@ LiveSession::LiveSession( mSwitchInProgress(false), mDisconnectReplyID(0), mSeekReplyID(0) { - if (mUIDValid) { - mHTTPDataSource->setUID(mUID); - } mStreams[kAudioIndex] = StreamItem("audio"); mStreams[kVideoIndex] = StreamItem("video"); @@ -172,7 +169,7 @@ status_t LiveSession::dequeueAccessUnit( if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) { int64_t timeUs; CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs)); - ALOGV("[%s] read buffer at time %lld us", streamStr, timeUs); + ALOGV("[%s] read buffer at time %" PRId64 " us", streamStr, timeUs); mLastDequeuedTimeUs = timeUs; mRealTimeBaseUs = ALooper::GetNowUs() - timeUs; @@ -679,7 +676,7 @@ ssize_t LiveSession::fetchFile( ssize_t bytesRead = 0; // adjust range_length if only reading partial block - if (block_size > 0 && (range_length == -1 || buffer->size() + block_size < range_length)) { + if (block_size > 0 && (range_length == -1 || (int64_t)(buffer->size() + block_size) < range_length)) { range_length = buffer->size() + block_size; } for (;;) { @@ -688,7 +685,7 @@ ssize_t LiveSession::fetchFile( if (bufferRemaining == 0 && getSizeErr != OK) { bufferRemaining = 32768; - ALOGV("increasing download buffer to %d bytes", + ALOGV("increasing download buffer to %zu bytes", buffer->size() + bufferRemaining); sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining); @@ -701,7 +698,7 @@ ssize_t LiveSession::fetchFile( size_t maxBytesToRead = bufferRemaining; if (range_length >= 0) { int64_t bytesLeftInRange = range_length - buffer->size(); - if (bytesLeftInRange < maxBytesToRead) { + if (bytesLeftInRange < (int64_t)maxBytesToRead) { maxBytesToRead = bytesLeftInRange; if (bytesLeftInRange == 0) { @@ -968,7 +965,7 @@ void LiveSession::changeConfiguration( mPrevBandwidthIndex = bandwidthIndex; - ALOGV("changeConfiguration => timeUs:%lld us, bwIndex:%d, pickTrack:%d", + ALOGV("changeConfiguration => timeUs:%" PRId64 " us, bwIndex:%zu, pickTrack:%d", timeUs, bandwidthIndex, pickTrack); if (pickTrack) { diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h index 3f8fee5..d7ed56f 100644 --- a/media/libstagefright/httplive/LiveSession.h +++ b/media/libstagefright/httplive/LiveSession.h @@ -28,6 +28,7 @@ struct ABuffer; struct AnotherPacketSource; struct DataSource; struct HTTPBase; +struct IMediaHTTPService; struct LiveDataSource; struct M3UParser; struct PlaylistFetcher; @@ -40,7 +41,8 @@ struct LiveSession : public AHandler { }; 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, @@ -134,8 +136,7 @@ private: sp<AMessage> mNotify; uint32_t mFlags; - bool mUIDValid; - uid_t mUID; + sp<IMediaHTTPService> mHTTPService; bool mInPreparationPhase; diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 7b06a2f..f22d650 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -126,7 +126,7 @@ void M3UParser::MediaGroup::pickRandomMediaItems() { 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 { @@ -163,21 +163,21 @@ status_t M3UParser::MediaGroup::selectTrack(size_t index, bool select) { if (select) { if (index >= mMediaItems.size()) { - ALOGE("track %d does not exist", index); + ALOGE("track %zu does not exist", index); return INVALID_OPERATION; } - if (mSelectedIndex == index) { - ALOGE("track %d already selected", index); + if (mSelectedIndex == (ssize_t)index) { + ALOGE("track %zu already selected", index); return BAD_VALUE; } - ALOGV("selected track %d", index); + ALOGV("selected track %zu", index); mSelectedIndex = index; } else { - if (mSelectedIndex != index) { - ALOGE("track %d is not selected", index); + if (mSelectedIndex != (ssize_t)index) { + ALOGE("track %zu is not selected", index); return BAD_VALUE; } - ALOGV("unselected track %d", index); + ALOGV("unselected track %zu", index); mSelectedIndex = -1; } diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index 668cbd4..5011bc1 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -40,6 +40,7 @@ #include <media/stagefright/Utils.h> #include <ctype.h> +#include <inttypes.h> #include <openssl/aes.h> #include <openssl/md5.h> @@ -316,7 +317,7 @@ void PlaylistFetcher::postMonitorQueue(int64_t delayUs, int64_t minDelayUs) { maxDelayUs = minDelayUs; } if (delayUs > maxDelayUs) { - ALOGV("Need to refresh playlist in %lld", maxDelayUs); + ALOGV("Need to refresh playlist in %" PRId64 , maxDelayUs); delayUs = maxDelayUs; } sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id()); @@ -627,7 +628,7 @@ void PlaylistFetcher::onMonitorQueue() { int64_t bufferedStreamDurationUs = mPacketSources.valueAt(i)->getBufferedDurationUs(&finalResult); - ALOGV("buffered %lld for stream %d", + ALOGV("buffered %" PRId64 " for stream %d", bufferedStreamDurationUs, mPacketSources.keyAt(i)); if (bufferedStreamDurationUs > bufferedDurationUs) { bufferedDurationUs = bufferedStreamDurationUs; @@ -640,7 +641,7 @@ void PlaylistFetcher::onMonitorQueue() { if (!mPrepared && bufferedDurationUs > targetDurationUs && downloadMore) { mPrepared = true; - ALOGV("prepared, buffered=%lld > %lld", + ALOGV("prepared, buffered=%" PRId64 " > %" PRId64 "", bufferedDurationUs, targetDurationUs); sp<AMessage> msg = mNotify->dup(); msg->setInt32("what", kWhatTemporarilyDoneFetching); @@ -648,7 +649,7 @@ void PlaylistFetcher::onMonitorQueue() { } if (finalResult == OK && downloadMore) { - ALOGV("monitoring, buffered=%lld < %lld", + ALOGV("monitoring, buffered=%" PRId64 " < %" PRId64 "", bufferedDurationUs, durationToBufferUs); // delay the next download slightly; hopefully this gives other concurrent fetchers // a better chance to run. @@ -664,7 +665,7 @@ void PlaylistFetcher::onMonitorQueue() { msg->post(); int64_t delayUs = mPrepared ? kMaxMonitorDelayUs : targetDurationUs / 2; - ALOGV("pausing for %lld, buffered=%lld > %lld", + ALOGV("pausing for %" PRId64 ", buffered=%" PRId64 " > %" PRId64 "", delayUs, bufferedDurationUs, durationToBufferUs); // :TRICKY: need to enforce minimum delay because the delay to // refresh the playlist will become 0 @@ -738,7 +739,7 @@ void PlaylistFetcher::onDownloadNext() { if (mPlaylist->isComplete() || mPlaylist->isEvent()) { mSeqNumber = getSeqNumberForTime(mStartTimeUs); - ALOGV("Initial sequence number for time %lld is %ld from (%ld .. %ld)", + ALOGV("Initial sequence number for time %" PRId64 " is %d from (%d .. %d)", mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); } else { @@ -747,7 +748,7 @@ void PlaylistFetcher::onDownloadNext() { if (mSeqNumber < firstSeqNumberInPlaylist) { mSeqNumber = firstSeqNumberInPlaylist; } - ALOGV("Initial sequence number for live event %ld from (%ld .. %ld)", + ALOGV("Initial sequence number for live event %d from (%d .. %d)", mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); } @@ -771,7 +772,8 @@ void PlaylistFetcher::onDownloadNext() { if (delayUs > kMaxMonitorDelayUs) { delayUs = kMaxMonitorDelayUs; } - ALOGV("sequence number high: %ld from (%ld .. %ld), monitor in %lld (retry=%d)", + ALOGV("sequence number high: %d from (%d .. %d), " + "monitor in %" PRId64 " (retry=%d)", mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist, delayUs, mNumRetries); postMonitorQueue(delayUs); @@ -796,7 +798,7 @@ void PlaylistFetcher::onDownloadNext() { ALOGE("Cannot find sequence number %d in playlist " "(contains %d - %d)", mSeqNumber, firstSeqNumberInPlaylist, - firstSeqNumberInPlaylist + mPlaylist->size() - 1); + firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1); notifyError(ERROR_END_OF_STREAM); return; 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 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ ID3.cpp +LOCAL_CFLAGS += -Werror + LOCAL_MODULE := libstagefright_id3 include $(BUILD_STATIC_LIBRARY) @@ -15,6 +17,8 @@ include $(CLEAR_VARS) 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 1ec4a40..7f221a0 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -41,9 +41,9 @@ struct MemorySource : public DataSource { } virtual ssize_t readAt(off64_t offset, void *data, size_t size) { - off64_t available = (offset >= mSize) ? 0ll : mSize - offset; + off64_t available = (offset >= (off64_t)mSize) ? 0ll : mSize - offset; - size_t copy = (available > size) ? size : available; + size_t copy = (available > (off64_t)size) ? size : available; memcpy(data, mData + offset, copy); return copy; @@ -172,7 +172,7 @@ struct id3_header { } if (size > kMaxMetadataSize) { - ALOGE("skipping huge ID3 metadata of size %d", size); + ALOGE("skipping huge ID3 metadata of size %zu", size); return false; } @@ -468,49 +468,6 @@ void ID3::Iterator::getID(String8 *id) const { } } -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 @@ void ID3::Iterator::getstring(String8 *id, bool otherdata) const { 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 @@ void ID3::Iterator::getstring(String8 *id, bool otherdata) const { } 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 @@ void ID3::Iterator::getstring(String8 *id, bool otherdata) const { 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 @@ void ID3::Iterator::getstring(String8 *id, bool otherdata) const { 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; } @@ -654,8 +633,8 @@ void ID3::Iterator::findFrame() { mFrameSize += 6; if (mOffset + mFrameSize > mParent.mSize) { - ALOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)", - mOffset, mFrameSize, mParent.mSize - mOffset - 6); + ALOGV("partial frame at offset %zu (size = %zu, bytes-remaining = %zu)", + mOffset, mFrameSize, mParent.mSize - mOffset - (size_t)6); return; } @@ -695,8 +674,8 @@ void ID3::Iterator::findFrame() { mFrameSize = 10 + baseSize; if (mOffset + mFrameSize > mParent.mSize) { - ALOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)", - mOffset, mFrameSize, mParent.mSize - mOffset - 10); + ALOGV("partial frame at offset %zu (size = %zu, bytes-remaining = %zu)", + mOffset, mFrameSize, mParent.mSize - mOffset - (size_t)10); return; } diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 271df8e..a81bbba 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -63,6 +63,7 @@ struct AwesomePlayer { 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 @@ private: SystemTimeSource mSystemTimeSource; TimeSource *mTimeSource; + sp<IMediaHTTPService> mHTTPService; String8 mUri; KeyedVector<String8, String8> mUriHeaders; @@ -247,6 +249,7 @@ private: sp<MediaExtractor> mExtractor; status_t setDataSource_l( + const sp<IMediaHTTPService> &httpService, const char *uri, const KeyedVector<String8, String8> *headers = NULL); diff --git a/media/libstagefright/include/ChromiumHTTPDataSource.h b/media/libstagefright/include/ChromiumHTTPDataSource.h deleted file mode 100644 index da188dd..0000000 --- a/media/libstagefright/include/ChromiumHTTPDataSource.h +++ /dev/null @@ -1,125 +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 onRedirect(const char *url); - - 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 @@ struct HTTPBase : public DataSource { 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 @@ private: int32_t mPrevEstimatedBandWidthKbps; int32_t mBandWidthCollectFreqMs; - bool mUIDValid; - uid_t mUID; - DISALLOW_EVIL_CONSTRUCTORS(HTTPBase); }; 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 @@ struct SDPLoader : public AHandler { 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 @@ private: 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 @@ struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface { 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 3e84256..2963150 100644 --- a/media/libstagefright/include/TimedEventQueue.h +++ b/media/libstagefright/include/TimedEventQueue.h @@ -122,7 +122,7 @@ private: }; 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; } }; 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 @@ LOCAL_C_INCLUDES:= \ $(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 dcb1cda..d4a7c7f 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -33,6 +33,8 @@ #include <media/stagefright/Utils.h> #include <utils/String8.h> +#include <inttypes.h> + namespace android { struct DataSourceReader : public mkvparser::IMkvReader { @@ -103,7 +105,7 @@ struct BlockIterator { private: MatroskaExtractor *mExtractor; - unsigned long mTrackNum; + long long mTrackNum; const mkvparser::Cluster *mCluster; const mkvparser::BlockEntry *mBlockEntry; @@ -183,7 +185,7 @@ MatroskaSource::MatroskaSource( CHECK_GE(avccSize, 5u); mNALSizeLen = 1 + (avcc[4] & 3); - ALOGV("mNALSizeLen = %d", mNALSizeLen); + ALOGV("mNALSizeLen = %zu", mNALSizeLen); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { mType = AAC; } @@ -193,7 +195,7 @@ MatroskaSource::~MatroskaSource() { clearPendingFrames(); } -status_t MatroskaSource::start(MetaData *params) { +status_t MatroskaSource::start(MetaData * /* params */) { mBlockIter.reset(); return OK; @@ -313,14 +315,14 @@ void BlockIterator::seek( *actualFrameTimeUs = -1ll; - const int64_t seekTimeNs = seekTimeUs * 1000ll; + const int64_t seekTimeNs = seekTimeUs * 1000ll - mExtractor->mSeekPreRollNs; mkvparser::Segment* const pSegment = mExtractor->mSegment; // Special case the 0 seek to avoid loading Cues when the application // extraneously seeks to 0 before playing. if (seekTimeNs <= 0) { - ALOGV("Seek to beginning: %lld", seekTimeUs); + ALOGV("Seek to beginning: %" PRId64, seekTimeUs); mCluster = pSegment->GetFirst(); mBlockEntryIndex = 0; do { @@ -329,7 +331,7 @@ void BlockIterator::seek( return; } - ALOGV("Seeking to: %lld", seekTimeUs); + ALOGV("Seeking to: %" PRId64, seekTimeUs); // If the Cues have not been located then find them. const mkvparser::Cues* pCues = pSegment->GetCues(); @@ -378,7 +380,7 @@ void BlockIterator::seek( for (size_t index = 0; index < pTracks->GetTracksCount(); ++index) { pTrack = pTracks->GetTrackByIndex(index); if (pTrack && pTrack->GetType() == 1) { // VIDEO_TRACK - ALOGV("Video track located at %d", index); + ALOGV("Video track located at %zu", index); break; } } @@ -409,8 +411,8 @@ void BlockIterator::seek( if (isAudio || block()->IsKey()) { // Accept the first key frame *actualFrameTimeUs = (block()->GetTime(mCluster) + 500LL) / 1000LL; - ALOGV("Requested seek point: %lld actual: %lld", - seekTimeUs, actualFrameTimeUs); + ALOGV("Requested seek point: %" PRId64 " actual: %" PRId64, + seekTimeUs, *actualFrameTimeUs); break; } } @@ -628,7 +630,8 @@ MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source) mReader(new DataSourceReader(mDataSource)), mSegment(NULL), mExtractedThumbnails(false), - mIsWebm(false) { + mIsWebm(false), + mSeekPreRollNs(0) { off64_t size; mIsLiveStreaming = (mDataSource->flags() @@ -919,6 +922,12 @@ void MatroskaExtractor::addTracks() { 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 @@ private: 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 cb57a2f..d1afd8b 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -36,6 +36,8 @@ #include <media/IStreamSource.h> #include <utils/KeyedVector.h> +#include <inttypes.h> + namespace android { // I want the expression "y" evaluated even if verbose logging is off. @@ -586,7 +588,7 @@ status_t ATSParser::Stream::parse( // Increment in multiples of 64K. neededSize = (neededSize + 65535) & ~65535; - ALOGI("resizing buffer to %d bytes", neededSize); + ALOGI("resizing buffer to %zu bytes", neededSize); sp<ABuffer> newBuffer = new ABuffer(neededSize); memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size()); @@ -748,7 +750,7 @@ status_t ATSParser::Stream::parsePES(ABitReader *br) { PTS |= br->getBits(15); CHECK_EQ(br->getBits(1), 1u); - ALOGV("PTS = 0x%016llx (%.2f)", PTS, PTS / 90000.0); + ALOGV("PTS = 0x%016" PRIx64 " (%.2f)", PTS, PTS / 90000.0); optional_bytes_remaining -= 5; @@ -764,7 +766,7 @@ status_t ATSParser::Stream::parsePES(ABitReader *br) { DTS |= br->getBits(15); CHECK_EQ(br->getBits(1), 1u); - ALOGV("DTS = %llu", DTS); + ALOGV("DTS = %" PRIu64, DTS); optional_bytes_remaining -= 5; } @@ -782,7 +784,7 @@ status_t ATSParser::Stream::parsePES(ABitReader *br) { ESCR |= br->getBits(15); CHECK_EQ(br->getBits(1), 1u); - ALOGV("ESCR = %llu", ESCR); + ALOGV("ESCR = %" PRIu64, ESCR); MY_LOGV("ESCR_extension = %u", br->getBits(9)); CHECK_EQ(br->getBits(1), 1u); @@ -812,7 +814,7 @@ status_t ATSParser::Stream::parsePES(ABitReader *br) { if (br->numBitsLeft() < dataLength * 8) { ALOGE("PES packet does not carry enough data to contain " - "payload. (numBitsLeft = %d, required = %d)", + "payload. (numBitsLeft = %zu, required = %u)", br->numBitsLeft(), dataLength * 8); return ERROR_MALFORMED; @@ -832,7 +834,7 @@ status_t ATSParser::Stream::parsePES(ABitReader *br) { size_t payloadSizeBits = br->numBitsLeft(); CHECK_EQ(payloadSizeBits % 8, 0u); - ALOGV("There's %d bytes of payload.", payloadSizeBits / 8); + ALOGV("There's %zu bytes of payload.", payloadSizeBits / 8); } } else if (stream_id == 0xbe) { // padding_stream CHECK_NE(PES_packet_length, 0u); @@ -850,7 +852,7 @@ status_t ATSParser::Stream::flush() { return OK; } - ALOGV("flushing stream 0x%04x size = %d", mElementaryPID, mBuffer->size()); + ALOGV("flushing stream 0x%04x size = %zu", mElementaryPID, mBuffer->size()); ABitReader br(mBuffer->data(), mBuffer->size()); @@ -862,7 +864,7 @@ status_t ATSParser::Stream::flush() { } 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", @@ -1172,7 +1174,7 @@ void ATSParser::parseAdaptationField(ABitReader *br, unsigned PID) { uint64_t PCR = PCR_base * 300 + PCR_ext; - ALOGV("PID 0x%04x: PCR = 0x%016llx (%.2f)", + ALOGV("PID 0x%04x: PCR = 0x%016" PRIx64 " (%.2f)", PID, PCR, PCR / 27E6); // The number of bytes received by this parser up to and @@ -1267,8 +1269,8 @@ bool ATSParser::PTSTimeDeltaEstablished() { } void ATSParser::updatePCR( - unsigned PID, uint64_t PCR, size_t byteOffsetFromStart) { - ALOGV("PCR 0x%016llx @ %d", PCR, byteOffsetFromStart); + unsigned /* PID */, uint64_t PCR, size_t byteOffsetFromStart) { + ALOGV("PCR 0x%016" PRIx64 " @ %zu", PCR, byteOffsetFromStart); if (mNumPCRs == 2) { mPCR[0] = mPCR[1]; 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 @@ LOCAL_C_INCLUDES:= \ $(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 2b0bf30..021b640 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -26,6 +26,8 @@ #include <media/stagefright/MetaData.h> #include <utils/Vector.h> +#include <inttypes.h> + namespace android { const int64_t kNearEOSMarkUs = 2000000ll; // 2 secs @@ -62,7 +64,7 @@ void AnotherPacketSource::setFormat(const sp<MetaData> &meta) { AnotherPacketSource::~AnotherPacketSource() { } -status_t AnotherPacketSource::start(MetaData *params) { +status_t AnotherPacketSource::start(MetaData * /* params */) { return OK; } @@ -186,7 +188,7 @@ void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) { int64_t lastQueuedTimeUs; CHECK(buffer->meta()->findInt64("timeUs", &lastQueuedTimeUs)); mLastQueuedTimeUs = lastQueuedTimeUs; - ALOGV("queueAccessUnit timeUs=%lld us (%.2f secs)", mLastQueuedTimeUs, mLastQueuedTimeUs / 1E6); + ALOGV("queueAccessUnit timeUs=%" PRIi64 " us (%.2f secs)", mLastQueuedTimeUs, mLastQueuedTimeUs / 1E6); Mutex::Autolock autoLock(mLock); mBuffers.push_back(buffer); diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index c0c9717..f7abf01 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -31,6 +31,7 @@ #include "include/avc_utils.h" +#include <inttypes.h> #include <netinet/in.h> namespace android { @@ -264,7 +265,7 @@ status_t ElementaryStreamQueue::appendData( if (startOffset > 0) { ALOGI("found something resembling an H.264/MPEG syncword " - "at offset %d", + "at offset %zd", startOffset); } @@ -297,7 +298,7 @@ status_t ElementaryStreamQueue::appendData( if (startOffset > 0) { ALOGI("found something resembling an H.264/MPEG syncword " - "at offset %d", + "at offset %zd", startOffset); } @@ -330,7 +331,7 @@ status_t ElementaryStreamQueue::appendData( if (startOffset > 0) { ALOGI("found something resembling an AAC syncword at " - "offset %d", + "offset %zd", startOffset); } @@ -358,7 +359,7 @@ status_t ElementaryStreamQueue::appendData( if (startOffset > 0) { ALOGI("found something resembling an AC3 syncword at " - "offset %d", + "offset %zd", startOffset); } @@ -385,7 +386,7 @@ status_t ElementaryStreamQueue::appendData( if (startOffset > 0) { ALOGI("found something resembling an MPEG audio " - "syncword at offset %d", + "syncword at offset %zd", startOffset); } @@ -409,7 +410,7 @@ status_t ElementaryStreamQueue::appendData( if (mBuffer == NULL || neededSize > mBuffer->capacity()) { neededSize = (neededSize + 65535) & ~65535; - ALOGV("resizing buffer to size %d", neededSize); + ALOGV("resizing buffer to size %zu", neededSize); sp<ABuffer> buffer = new ABuffer(neededSize); if (mBuffer != NULL) { @@ -432,7 +433,7 @@ status_t ElementaryStreamQueue::appendData( #if 0 if (mMode == AAC) { - ALOGI("size = %d, timeUs = %.2f secs", size, timeUs / 1E6); + ALOGI("size = %zu, timeUs = %.2f secs", size, timeUs / 1E6); hexdump(data, size); } #endif @@ -1027,7 +1028,7 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGVideo() { accessUnit->meta()->setInt64("timeUs", timeUs); - ALOGV("returning MPEG video access unit at time %lld us", + ALOGV("returning MPEG video access unit at time %" PRId64 " us", timeUs); // hexdump(accessUnit->data(), accessUnit->size()); @@ -1186,7 +1187,7 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEG4Video() { accessUnit->meta()->setInt64("timeUs", timeUs); - ALOGV("returning MPEG4 video access unit at time %lld us", + ALOGV("returning MPEG4 video access unit at time %" PRId64 " us", timeUs); // hexdump(accessUnit->data(), accessUnit->size()); diff --git a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp index dd714c9..85859f7 100644 --- a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp @@ -36,6 +36,8 @@ #include <media/stagefright/Utils.h> #include <utils/String8.h> +#include <inttypes.h> + namespace android { struct MPEG2PSExtractor::Track : public MediaSource { @@ -130,7 +132,8 @@ sp<MediaSource> MPEG2PSExtractor::getTrack(size_t index) { 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; } @@ -408,7 +411,7 @@ ssize_t MPEG2PSExtractor::dequeuePES() { PTS |= br.getBits(15); CHECK_EQ(br.getBits(1), 1u); - ALOGV("PTS = %llu", PTS); + ALOGV("PTS = %" PRIu64, PTS); // ALOGI("PTS = %.2f secs", PTS / 90000.0f); optional_bytes_remaining -= 5; @@ -425,7 +428,7 @@ ssize_t MPEG2PSExtractor::dequeuePES() { DTS |= br.getBits(15); CHECK_EQ(br.getBits(1), 1u); - ALOGV("DTS = %llu", DTS); + ALOGV("DTS = %" PRIu64, DTS); optional_bytes_remaining -= 5; } @@ -443,7 +446,7 @@ ssize_t MPEG2PSExtractor::dequeuePES() { ESCR |= br.getBits(15); CHECK_EQ(br.getBits(1), 1u); - ALOGV("ESCR = %llu", ESCR); + ALOGV("ESCR = %" PRIu64, ESCR); /* unsigned ESCR_extension = */br.getBits(9); CHECK_EQ(br.getBits(1), 1u); @@ -472,7 +475,7 @@ ssize_t MPEG2PSExtractor::dequeuePES() { if (br.numBitsLeft() < dataLength * 8) { ALOGE("PES packet does not carry enough data to contain " - "payload. (numBitsLeft = %d, required = %d)", + "payload. (numBitsLeft = %zu, required = %u)", br.numBitsLeft(), dataLength * 8); return ERROR_MALFORMED; @@ -625,7 +628,7 @@ status_t MPEG2PSExtractor::Track::read( 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<MediaSource> MPEG2TSExtractor::getTrack(size_t index) { } 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 44f0be7..5bea7a6 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -41,16 +41,21 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, mNumFramesAvailable(0), mEndOfStream(false), mEndOfStreamSent(false), - mRepeatAfterUs(-1ll), 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); @@ -414,7 +419,18 @@ bool GraphicBufferSource::fillCodecBuffer_l() { 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, @@ -548,7 +564,30 @@ status_t GraphicBufferSource::signalEndOfInputStream() { int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) { int64_t timeUs = item.mTimestamp / 1000; - if (mMaxTimestampGapUs > 0ll) { + 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 @@ -699,7 +738,7 @@ void GraphicBufferSource::onFrameAvailable() { // If this is the first time we're seeing this buffer, add it to our // slot table. if (item.mGraphicBuffer != NULL) { - ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf); + ALOGV("onFrameAvailable: setting mBufferSlot %d", item.mBuf); mBufferSlot[item.mBuf] = item.mGraphicBuffer; } mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber, @@ -738,6 +777,11 @@ void GraphicBufferSource::onBuffersReleased() { } } +// BufferQueue::ConsumerListener callback +void GraphicBufferSource::onSidebandStreamChanged() { + ALOG_ASSERT(false, "GraphicBufferSource can't consume sideband streams"); +} + status_t GraphicBufferSource::setRepeatPreviousFrameDelayUs( int64_t repeatAfterUs) { Mutex::Autolock autoLock(mMutex); @@ -762,6 +806,27 @@ status_t GraphicBufferSource::setMaxTimestampGapUs(int64_t 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 3b0e454..757edc8 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -118,6 +118,17 @@ public: // 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 @@ -132,6 +143,11 @@ protected: // set of mBufferSlot entries. virtual void onBuffersReleased(); + // BufferQueue::ConsumerListener interface, called when the client has + // changed the sideband stream. GraphicBufferSource doesn't handle sideband + // streams so this is a no-op (and should never be called). + virtual void onSidebandStreamChanged(); + private: // Keep track of codec input buffers. They may either be available // (mGraphicBuffer == NULL) or in use by the codec. @@ -223,16 +239,17 @@ private: enum { kRepeatLastFrameCount = 10, }; - int64_t mRepeatAfterUs; - int64_t mMaxTimestampGapUs; 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; @@ -245,6 +262,12 @@ private: // 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 274f2eb..a608479 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -185,7 +185,7 @@ void OMX::binderDied(const wp<IBinder> &the_late_who) { instance->onObserverDied(mMaster); } -bool OMX::livesLocally(node_id node, pid_t pid) { +bool OMX::livesLocally(node_id /* node */, pid_t pid) { return pid == getpid(); } @@ -424,7 +424,7 @@ OMX_ERRORTYPE OMX::OnEvent( 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. diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 6c5c857..0fb38fa 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -266,7 +266,7 @@ status_t OMXNodeInstance::sendCommand( } 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); @@ -275,7 +275,7 @@ status_t OMXNodeInstance::getParameter( } 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( @@ -285,7 +285,7 @@ status_t OMXNodeInstance::setParameter( } 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); @@ -293,7 +293,7 @@ status_t OMXNodeInstance::getConfig( } 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( @@ -610,7 +610,7 @@ status_t OMXNodeInstance::useGraphicBuffer( } 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); @@ -850,6 +850,8 @@ status_t OMXNodeInstance::setInternalOption( 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(); @@ -874,7 +876,8 @@ status_t OMXNodeInstance::setInternalOption( int64_t delayUs = *(int64_t *)data; return bufferSource->setRepeatPreviousFrameDelayUs(delayUs); - } else { + } else if (type == + IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP){ if (size != sizeof(int64_t)) { return INVALID_OPERATION; } @@ -882,6 +885,20 @@ status_t OMXNodeInstance::setInternalOption( 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; @@ -961,7 +978,7 @@ void OMXNodeInstance::onEvent( // 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, @@ -977,7 +994,7 @@ OMX_ERRORTYPE OMXNodeInstance::OnEvent( // 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); @@ -989,7 +1006,7 @@ OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone( // 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::GetStateWrapper( //////////////////////////////////////////////////////////////////////////////// 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 @@ static const struct { { "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::destroyComponentInstance( 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 e368134..447b29e 100644 --- a/media/libstagefright/omx/tests/Android.mk +++ b/media/libstagefright/omx/tests/Android.mk @@ -11,6 +11,8 @@ LOCAL_C_INCLUDES := \ $(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 44e4f9d..f4dfd6b 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -26,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> @@ -242,7 +243,8 @@ private: }; 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; @@ -461,6 +463,7 @@ static const char *GetMimeFromComponentRole(const char *componentRole) { { "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 }, }; @@ -493,6 +496,7 @@ static const char *GetURLForMime(const char *mime) { { "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" }, diff --git a/media/libstagefright/rtsp/AAVCAssembler.cpp b/media/libstagefright/rtsp/AAVCAssembler.cpp index a6825eb..4bc67e8 100644 --- a/media/libstagefright/rtsp/AAVCAssembler.cpp +++ b/media/libstagefright/rtsp/AAVCAssembler.cpp @@ -124,7 +124,7 @@ ARTPAssembler::AssemblyStatus AAVCAssembler::addNALUnit( } void AAVCAssembler::addSingleNALUnit(const sp<ABuffer> &buffer) { - ALOGV("addSingleNALUnit of size %d", buffer->size()); + ALOGV("addSingleNALUnit of size %zu", buffer->size()); #if !LOG_NDEBUG hexdump(buffer->data(), buffer->size()); #endif @@ -191,7 +191,7 @@ ARTPAssembler::AssemblyStatus AAVCAssembler::addFragmentedNALUnit( CHECK((indicator & 0x1f) == 28); if (size < 2) { - ALOGV("Ignoring malformed FU buffer (size = %d)", size); + ALOGV("Ignoring malformed FU buffer (size = %zu)", size); queue->erase(queue->begin()); ++mNextExpectedSeqNo; @@ -225,7 +225,7 @@ ARTPAssembler::AssemblyStatus AAVCAssembler::addFragmentedNALUnit( } else { List<sp<ABuffer> >::iterator it = ++queue->begin(); while (it != queue->end()) { - ALOGV("sequence length %d", totalCount); + ALOGV("sequence length %zu", totalCount); const sp<ABuffer> &buffer = *it; @@ -294,7 +294,7 @@ ARTPAssembler::AssemblyStatus AAVCAssembler::addFragmentedNALUnit( for (size_t i = 0; i < totalCount; ++i) { const sp<ABuffer> &buffer = *it; - ALOGV("piece #%d/%d", i + 1, totalCount); + ALOGV("piece #%zu/%zu", i + 1, totalCount); #if !LOG_NDEBUG hexdump(buffer->data(), buffer->size()); #endif @@ -317,7 +317,7 @@ ARTPAssembler::AssemblyStatus AAVCAssembler::addFragmentedNALUnit( void AAVCAssembler::submitAccessUnit() { CHECK(!mNALUnits.empty()); - ALOGV("Access unit complete (%d nal units)", mNALUnits.size()); + ALOGV("Access unit complete (%zu nal units)", mNALUnits.size()); size_t totalSize = 0; for (List<sp<ABuffer> >::iterator it = mNALUnits.begin(); 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/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp index eefceba..98b50dd 100644 --- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp @@ -365,7 +365,7 @@ ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket( void AMPEG4ElementaryAssembler::submitAccessUnit() { CHECK(!mPackets.empty()); - ALOGV("Access unit complete (%d nal units)", mPackets.size()); + ALOGV("Access unit complete (%zu nal units)", mPackets.size()); sp<ABuffer> accessUnit; 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/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp index af369b5..372fbe9 100644 --- a/media/libstagefright/rtsp/ARTPConnection.cpp +++ b/media/libstagefright/rtsp/ARTPConnection.cpp @@ -563,7 +563,7 @@ status_t ARTPConnection::parseRTCP(StreamInfo *s, const sp<ABuffer> &buffer) { default: { - ALOGW("Unknown RTCP packet type %u of size %d", + ALOGW("Unknown RTCP packet type %u of size %zu", (unsigned)data[1], headerLength); break; } diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp index 0d07043..793d116 100644 --- a/media/libstagefright/rtsp/ARTPWriter.cpp +++ b/media/libstagefright/rtsp/ARTPWriter.cpp @@ -114,7 +114,7 @@ bool ARTPWriter::reachedEOS() { 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; @@ -277,7 +277,7 @@ void ARTPWriter::onRead(const sp<AMessage> &msg) { } if (mediaBuf->range_length() > 0) { - ALOGV("read buffer of size %d", mediaBuf->range_length()); + ALOGV("read buffer of size %zu", mediaBuf->range_length()); if (mMode == H264) { StripStartcode(mediaBuf); diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index 492bd4a..cc3b63c 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 @@ LOCAL_SRC_FILES:= \ 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 @@ ifeq ($(TARGET_ARCH),arm) 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 e7580c2..f3dfc59 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 @@ struct MyHandler : public AHandler { mConn->connect(mOriginalSessionURL.c_str(), reply); } - AString getControlURL(sp<ASessionDescription> desc) { + AString getControlURL() { AString sessionLevelControlURL; if (mSessionDesc->findAttribute( 0, @@ -556,7 +566,7 @@ struct MyHandler : public AHandler { mBaseURL = tmp; } - mControlURL = getControlURL(mSessionDesc); + mControlURL = getControlURL(); if (mSessionDesc->countTracks() < 2) { // There's no actual tracks in this session. @@ -602,7 +612,7 @@ struct MyHandler : public AHandler { mSeekable = !isLiveStream(mSessionDesc); - mControlURL = getControlURL(mSessionDesc); + mControlURL = getControlURL(); if (mSessionDesc->countTracks() < 2) { // There's no actual tracks in this session. @@ -1816,6 +1826,8 @@ private: 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 3c7d82a..09f7eee 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 @@ void SDPLoader::onLoad(const sp<AMessage> &msg) { 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 = %zu", readSize); err = UNKNOWN_ERROR; } else { desc = new ASessionDescription; 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 @@ void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) { } // 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 @@ TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuYV12BufferNpotWriteMediaS 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 @@ TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaSameImageEachBufNpotWrite) { } 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 @@ TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaDiffImageEachBufNpotWrite) { } 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 @@ LOCAL_SRC_FILES:= \ 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 @@ status_t TimedTextDriver::addOutOfBandTextSource( } sp<DataSource> dataSource = - DataSource::CreateFromURI(uri); + DataSource::CreateFromURI(mHTTPService, uri); return createOutOfBandTextSource(trackIndex, mimeType, dataSource); } diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp index 9fb0afe..a070487 100644 --- a/media/libstagefright/timedtext/TimedTextPlayer.cpp +++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "TimedTextPlayer" #include <utils/Log.h> +#include <inttypes.h> #include <limits.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> @@ -271,7 +272,7 @@ int64_t TimedTextPlayer::delayUsFromCurrentTime(int64_t fireTimeUs) { sp<MediaPlayerBase> listener = mListener.promote(); if (listener == NULL) { // TODO: it may be better to return kInvalidTimeUs - ALOGE("%s: Listener is NULL. (fireTimeUs = %lld)", + ALOGE("%s: Listener is NULL. (fireTimeUs = %" PRId64" )", __FUNCTION__, fireTimeUs); return 0; } 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 @@ class TimedTextSource : public RefBase { 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/timedtext/test/Android.mk b/media/libstagefright/timedtext/test/Android.mk index a5e7ba2..9a9fde2 100644 --- a/media/libstagefright/timedtext/test/Android.mk +++ b/media/libstagefright/timedtext/test/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH:= $(call my-dir) # ================================================================ # Unit tests for libstagefright_timedtext -# See also /development/testrunner/test_defs.xml # ================================================================ # ================================================================ @@ -18,10 +17,13 @@ LOCAL_SRC_FILES := TimedTextSRTSource_test.cpp LOCAL_C_INCLUDES := \ $(TOP)/external/expat/lib \ - $(TOP)/frameworks/base/media/libstagefright/timedtext + $(TOP)/frameworks/av/media/libstagefright/timedtext LOCAL_SHARED_LIBRARIES := \ + libbinder \ libexpat \ - libstagefright + libstagefright \ + libstagefright_foundation \ + libutils include $(BUILD_NATIVE_TEST) diff --git a/media/libstagefright/webm/Android.mk b/media/libstagefright/webm/Android.mk new file mode 100644 index 0000000..7081463 --- /dev/null +++ b/media/libstagefright/webm/Android.mk @@ -0,0 +1,23 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_CPPFLAGS += -D__STDINT_LIMITS \ + -Werror + +LOCAL_SRC_FILES:= EbmlUtil.cpp \ + WebmElement.cpp \ + WebmFrame.cpp \ + WebmFrameThread.cpp \ + WebmWriter.cpp + + +LOCAL_C_INCLUDES += $(TOP)/frameworks/av/include + +LOCAL_SHARED_LIBRARIES += libstagefright_foundation \ + libstagefright \ + libutils \ + liblog + +LOCAL_MODULE:= libstagefright_webm + +include $(BUILD_STATIC_LIBRARY) diff --git a/media/libstagefright/webm/EbmlUtil.cpp b/media/libstagefright/webm/EbmlUtil.cpp new file mode 100644 index 0000000..449fec6 --- /dev/null +++ b/media/libstagefright/webm/EbmlUtil.cpp @@ -0,0 +1,108 @@ +/* + * 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 <stdint.h> + +namespace { + +// Table for Seal's algorithm for Number of Trailing Zeros. Hacker's Delight +// online, Figure 5-18 (http://www.hackersdelight.org/revisions.pdf) +// The entries whose value is -1 are never referenced. +int NTZ_TABLE[] = { + 32, 0, 1, 12, 2, 6, -1, 13, 3, -1, 7, -1, -1, -1, -1, 14, + 10, 4, -1, -1, 8, -1, -1, 25, -1, -1, -1, -1, -1, 21, 27, 15, + 31, 11, 5, -1, -1, -1, -1, -1, 9, -1, -1, 24, -1, -1, 20, 26, + 30, -1, -1, -1, -1, 23, -1, 19, 29, -1, 22, 18, 28, 17, 16, -1 +}; + +int numberOfTrailingZeros32(int32_t i) { + uint32_t u = (i & -i) * 0x0450FBAF; + return NTZ_TABLE[(u) >> 26]; +} + +uint64_t highestOneBit(uint64_t n) { + n |= (n >> 1); + n |= (n >> 2); + n |= (n >> 4); + n |= (n >> 8); + n |= (n >> 16); + n |= (n >> 32); + return n - (n >> 1); +} + +uint64_t _powerOf2(uint64_t u) { + uint64_t powerOf2 = highestOneBit(u); + return powerOf2 ? powerOf2 : 1; +} + +// Based on Long.numberOfTrailingZeros in Long.java +int numberOfTrailingZeros(uint64_t u) { + int32_t low = u; + return low !=0 ? numberOfTrailingZeros32(low) + : 32 + numberOfTrailingZeros32((int32_t) (u >> 32)); +} +} + +namespace webm { + +// Encode the id and/or size of an EBML element bytes by setting a leading length descriptor bit: +// +// 1xxxxxxx - 1-byte values +// 01xxxxxx xxxxxxxx - +// 001xxxxx xxxxxxxx xxxxxxxx - +// 0001xxxx xxxxxxxx xxxxxxxx xxxxxxxx - ... +// 00001xxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - +// 000001xx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - +// 0000001x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - +// 00000001 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - 8-byte values +// +// This function uses the least the number of bytes possible. +uint64_t encodeUnsigned(uint64_t u) { + uint64_t powerOf2 = _powerOf2(u); + if (u + 1 == powerOf2 << 1) + powerOf2 <<= 1; + int shiftWidth = (7 + numberOfTrailingZeros(powerOf2)) / 7 * 7; + long lengthDescriptor = 1 << shiftWidth; + return lengthDescriptor | u; +} + +// Like above but pads the input value with leading zeros up to the specified width. The length +// descriptor is calculated based on width. +uint64_t encodeUnsigned(uint64_t u, int width) { + int shiftWidth = 7 * width; + uint64_t lengthDescriptor = 1; + lengthDescriptor <<= shiftWidth; + return lengthDescriptor | u; +} + +// Calculate the length of an EBML coded id or size from its length descriptor. +int sizeOf(uint64_t u) { + uint64_t powerOf2 = _powerOf2(u); + int unsignedLength = numberOfTrailingZeros(powerOf2) / 8 + 1; + return unsignedLength; +} + +// Serialize an EBML coded id or size in big-endian order. +int serializeCodedUnsigned(uint64_t u, uint8_t* bary) { + int unsignedLength = sizeOf(u); + for (int i = unsignedLength - 1; i >= 0; i--) { + bary[i] = u & 0xff; + u >>= 8; + } + return unsignedLength; +} + +} diff --git a/media/libstagefright/webm/EbmlUtil.h b/media/libstagefright/webm/EbmlUtil.h new file mode 100644 index 0000000..eb9c37c --- /dev/null +++ b/media/libstagefright/webm/EbmlUtil.h @@ -0,0 +1,50 @@ +/* + * 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 EBMLUTIL_H_ +#define EBMLUTIL_H_ + +#include <stdint.h> + +namespace webm { + +// Encode the id and/or size of an EBML element bytes by setting a leading length descriptor bit: +// +// 1xxxxxxx - 1-byte values +// 01xxxxxx xxxxxxxx - +// 001xxxxx xxxxxxxx xxxxxxxx - +// 0001xxxx xxxxxxxx xxxxxxxx xxxxxxxx - ... +// 00001xxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - +// 000001xx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - +// 0000001x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - +// 00000001 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - 8-byte values +// +// This function uses the least the number of bytes possible. +uint64_t encodeUnsigned(uint64_t u); + +// Like above but pads the input value with leading zeros up to the specified width. The length +// descriptor is calculated based on width. +uint64_t encodeUnsigned(uint64_t u, int width); + +// Serialize an EBML coded id or size in big-endian order. +int serializeCodedUnsigned(uint64_t u, uint8_t* bary); + +// Calculate the length of an EBML coded id or size from its length descriptor. +int sizeOf(uint64_t u); + +} + +#endif /* EBMLUTIL_H_ */ diff --git a/media/libstagefright/webm/LinkedBlockingQueue.h b/media/libstagefright/webm/LinkedBlockingQueue.h new file mode 100644 index 0000000..0b6a9a1 --- /dev/null +++ b/media/libstagefright/webm/LinkedBlockingQueue.h @@ -0,0 +1,79 @@ +/* + * 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 LINKEDBLOCKINGQUEUE_H_ +#define LINKEDBLOCKINGQUEUE_H_ + +#include <utils/List.h> +#include <utils/Mutex.h> +#include <utils/Condition.h> + +namespace android { + +template<typename T> +class LinkedBlockingQueue { + List<T> mList; + Mutex mLock; + Condition mContentAvailableCondition; + + T front(bool remove) { + Mutex::Autolock autolock(mLock); + while (mList.empty()) { + mContentAvailableCondition.wait(mLock); + } + T e = *(mList.begin()); + if (remove) { + mList.erase(mList.begin()); + } + return e; + } + + DISALLOW_EVIL_CONSTRUCTORS(LinkedBlockingQueue); + +public: + LinkedBlockingQueue() { + } + + ~LinkedBlockingQueue() { + } + + bool empty() { + Mutex::Autolock autolock(mLock); + return mList.empty(); + } + + void clear() { + Mutex::Autolock autolock(mLock); + mList.clear(); + } + + T peek() { + return front(false); + } + + T take() { + return front(true); + } + + void push(T e) { + Mutex::Autolock autolock(mLock); + mList.push_back(e); + mContentAvailableCondition.signal(); + } +}; + +} /* namespace android */ +#endif /* LINKEDBLOCKINGQUEUE_H_ */ diff --git a/media/libstagefright/webm/WebmConstants.h b/media/libstagefright/webm/WebmConstants.h new file mode 100644 index 0000000..c53f458 --- /dev/null +++ b/media/libstagefright/webm/WebmConstants.h @@ -0,0 +1,133 @@ +/* + * 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 WEBMCONSTANTS_H_ +#define WEBMCONSTANTS_H_ + +#include <stdint.h> + +namespace webm { + +const int kMinEbmlVoidSize = 2; +const int64_t kMaxMetaSeekSize = 64; +const int64_t kMkvUnknownLength = 0x01ffffffffffffffl; + +// EBML element id's from http://matroska.org/technical/specs/index.html +enum Mkv { + kMkvEbml = 0x1A45DFA3, + kMkvEbmlVersion = 0x4286, + kMkvEbmlReadVersion = 0x42F7, + kMkvEbmlMaxIdlength = 0x42F2, + kMkvEbmlMaxSizeLength = 0x42F3, + kMkvDocType = 0x4282, + kMkvDocTypeVersion = 0x4287, + kMkvDocTypeReadVersion = 0x4285, + kMkvVoid = 0xEC, + kMkvSignatureSlot = 0x1B538667, + kMkvSignatureAlgo = 0x7E8A, + kMkvSignatureHash = 0x7E9A, + kMkvSignaturePublicKey = 0x7EA5, + kMkvSignature = 0x7EB5, + kMkvSignatureElements = 0x7E5B, + kMkvSignatureElementList = 0x7E7B, + kMkvSignedElement = 0x6532, + kMkvSegment = 0x18538067, + kMkvSeekHead = 0x114D9B74, + kMkvSeek = 0x4DBB, + kMkvSeekId = 0x53AB, + kMkvSeekPosition = 0x53AC, + kMkvInfo = 0x1549A966, + kMkvTimecodeScale = 0x2AD7B1, + kMkvSegmentDuration = 0x4489, + kMkvDateUtc = 0x4461, + kMkvMuxingApp = 0x4D80, + kMkvWritingApp = 0x5741, + kMkvCluster = 0x1F43B675, + kMkvTimecode = 0xE7, + kMkvPrevSize = 0xAB, + kMkvBlockGroup = 0xA0, + kMkvBlock = 0xA1, + kMkvBlockAdditions = 0x75A1, + kMkvBlockMore = 0xA6, + kMkvBlockAddId = 0xEE, + kMkvBlockAdditional = 0xA5, + kMkvBlockDuration = 0x9B, + kMkvReferenceBlock = 0xFB, + kMkvLaceNumber = 0xCC, + kMkvSimpleBlock = 0xA3, + kMkvTracks = 0x1654AE6B, + kMkvTrackEntry = 0xAE, + kMkvTrackNumber = 0xD7, + kMkvTrackUid = 0x73C5, + kMkvTrackType = 0x83, + kMkvFlagEnabled = 0xB9, + kMkvFlagDefault = 0x88, + kMkvFlagForced = 0x55AA, + kMkvFlagLacing = 0x9C, + kMkvDefaultDuration = 0x23E383, + kMkvMaxBlockAdditionId = 0x55EE, + kMkvName = 0x536E, + kMkvLanguage = 0x22B59C, + kMkvCodecId = 0x86, + kMkvCodecPrivate = 0x63A2, + kMkvCodecName = 0x258688, + kMkvVideo = 0xE0, + kMkvFlagInterlaced = 0x9A, + kMkvStereoMode = 0x53B8, + kMkvAlphaMode = 0x53C0, + kMkvPixelWidth = 0xB0, + kMkvPixelHeight = 0xBA, + kMkvPixelCropBottom = 0x54AA, + kMkvPixelCropTop = 0x54BB, + kMkvPixelCropLeft = 0x54CC, + kMkvPixelCropRight = 0x54DD, + kMkvDisplayWidth = 0x54B0, + kMkvDisplayHeight = 0x54BA, + kMkvDisplayUnit = 0x54B2, + kMkvAspectRatioType = 0x54B3, + kMkvFrameRate = 0x2383E3, + kMkvAudio = 0xE1, + kMkvSamplingFrequency = 0xB5, + kMkvOutputSamplingFrequency = 0x78B5, + kMkvChannels = 0x9F, + kMkvBitDepth = 0x6264, + kMkvCues = 0x1C53BB6B, + kMkvCuePoint = 0xBB, + kMkvCueTime = 0xB3, + kMkvCueTrackPositions = 0xB7, + kMkvCueTrack = 0xF7, + kMkvCueClusterPosition = 0xF1, + kMkvCueBlockNumber = 0x5378 +}; + +enum TrackTypes { + kInvalidType = -1, + kVideoType = 0x1, + kAudioType = 0x2, + kComplexType = 0x3, + kLogoType = 0x10, + kSubtitleType = 0x11, + kButtonsType = 0x12, + kControlType = 0x20 +}; + +enum TrackNum { + kVideoTrackNum = 0x1, + kAudioTrackNum = 0x2 +}; +} + +#endif /* WEBMCONSTANTS_H_ */ diff --git a/media/libstagefright/webm/WebmElement.cpp b/media/libstagefright/webm/WebmElement.cpp new file mode 100644 index 0000000..a008cab --- /dev/null +++ b/media/libstagefright/webm/WebmElement.cpp @@ -0,0 +1,367 @@ +/* + * 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 "WebmElement" + +#include "EbmlUtil.h" +#include "WebmElement.h" +#include "WebmConstants.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <utils/Log.h> + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> + +using namespace android; +using namespace webm; + +namespace { + +int64_t voidSize(int64_t totalSize) { + if (totalSize < 2) { + return -1; + } + if (totalSize < 9) { + return totalSize - 2; + } + return totalSize - 9; +} + +uint64_t childrenSum(const List<sp<WebmElement> >& children) { + uint64_t total = 0; + for (List<sp<WebmElement> >::const_iterator it = children.begin(); + it != children.end(); ++it) { + total += (*it)->totalSize(); + } + return total; +} + +void populateCommonTrackEntries( + int num, + uint64_t uid, + bool lacing, + const char *lang, + const char *codec, + TrackTypes type, + List<sp<WebmElement> > &ls) { + ls.push_back(new WebmUnsigned(kMkvTrackNumber, num)); + ls.push_back(new WebmUnsigned(kMkvTrackUid, uid)); + ls.push_back(new WebmUnsigned(kMkvFlagLacing, lacing)); + ls.push_back(new WebmString(kMkvLanguage, lang)); + ls.push_back(new WebmString(kMkvCodecId, codec)); + ls.push_back(new WebmUnsigned(kMkvTrackType, type)); +} +} + +namespace android { + +WebmElement::WebmElement(uint64_t id, uint64_t size) + : mId(id), mSize(size) { +} + +WebmElement::~WebmElement() { +} + +int WebmElement::serializePayloadSize(uint8_t *buf) { + return serializeCodedUnsigned(encodeUnsigned(mSize), buf); +} + +uint64_t WebmElement::serializeInto(uint8_t *buf) { + uint8_t *cur = buf; + int head = serializeCodedUnsigned(mId, cur); + cur += head; + int neck = serializePayloadSize(cur); + cur += neck; + serializePayload(cur); + cur += mSize; + return cur - buf; +} + +uint64_t WebmElement::totalSize() { + uint8_t buf[8]; + //............... + sizeOf(encodeUnsigned(size)) + return sizeOf(mId) + serializePayloadSize(buf) + mSize; +} + +uint8_t *WebmElement::serialize(uint64_t& size) { + size = totalSize(); + uint8_t *buf = new uint8_t[size]; + serializeInto(buf); + return buf; +} + +int WebmElement::write(int fd, uint64_t& size) { + uint8_t buf[8]; + size = totalSize(); + off64_t off = ::lseek64(fd, (size - 1), SEEK_CUR) - (size - 1); + ::write(fd, buf, 1); // extend file + + off64_t curOff = off + size; + off64_t alignedOff = off & ~(::sysconf(_SC_PAGE_SIZE) - 1); + off64_t mapSize = curOff - alignedOff; + off64_t pageOff = off - alignedOff; + void *dst = ::mmap64(NULL, mapSize, PROT_WRITE, MAP_SHARED, fd, alignedOff); + if (dst == MAP_FAILED) { + ALOGE("mmap64 failed; errno = %d", errno); + ALOGE("fd %d; flags: %o", fd, ::fcntl(fd, F_GETFL, 0)); + return errno; + } else { + serializeInto((uint8_t*) dst + pageOff); + ::msync(dst, mapSize, MS_SYNC); + return ::munmap(dst, mapSize); + } +} + +//================================================================================================= + +WebmUnsigned::WebmUnsigned(uint64_t id, uint64_t value) + : WebmElement(id, sizeOf(value)), mValue(value) { +} + +void WebmUnsigned::serializePayload(uint8_t *buf) { + serializeCodedUnsigned(mValue, buf); +} + +//================================================================================================= + +WebmFloat::WebmFloat(uint64_t id, double value) + : WebmElement(id, sizeof(double)), mValue(value) { +} + +WebmFloat::WebmFloat(uint64_t id, float value) + : WebmElement(id, sizeof(float)), mValue(value) { +} + +void WebmFloat::serializePayload(uint8_t *buf) { + uint64_t data; + if (mSize == sizeof(float)) { + float f = mValue; + data = *reinterpret_cast<const uint32_t*>(&f); + } else { + data = *reinterpret_cast<const uint64_t*>(&mValue); + } + for (int i = mSize - 1; i >= 0; --i) { + buf[i] = data & 0xff; + data >>= 8; + } +} + +//================================================================================================= + +WebmBinary::WebmBinary(uint64_t id, const sp<ABuffer> &ref) + : WebmElement(id, ref->size()), mRef(ref) { +} + +void WebmBinary::serializePayload(uint8_t *buf) { + memcpy(buf, mRef->data(), mRef->size()); +} + +//================================================================================================= + +WebmString::WebmString(uint64_t id, const char *str) + : WebmElement(id, strlen(str)), mStr(str) { +} + +void WebmString::serializePayload(uint8_t *buf) { + memcpy(buf, mStr, strlen(mStr)); +} + +//================================================================================================= + +WebmSimpleBlock::WebmSimpleBlock( + int trackNum, + int16_t relTimecode, + bool key, + const sp<ABuffer>& orig) + // ............................ trackNum*1 + timecode*2 + flags*1 + // ^^^ + // Only the least significant byte of trackNum is encoded + : WebmElement(kMkvSimpleBlock, orig->size() + 4), + mTrackNum(trackNum), + mRelTimecode(relTimecode), + mKey(key), + mRef(orig) { +} + +void WebmSimpleBlock::serializePayload(uint8_t *buf) { + serializeCodedUnsigned(encodeUnsigned(mTrackNum), buf); + buf[1] = (mRelTimecode & 0xff00) >> 8; + buf[2] = mRelTimecode & 0xff; + buf[3] = mKey ? 0x80 : 0; + memcpy(buf + 4, mRef->data(), mSize - 4); +} + +//================================================================================================= + +EbmlVoid::EbmlVoid(uint64_t totalSize) + : WebmElement(kMkvVoid, voidSize(totalSize)), + mSizeWidth(totalSize - sizeOf(kMkvVoid) - voidSize(totalSize)) { + CHECK_GE(voidSize(totalSize), 0); +} + +int EbmlVoid::serializePayloadSize(uint8_t *buf) { + return serializeCodedUnsigned(encodeUnsigned(mSize, mSizeWidth), buf); +} + +void EbmlVoid::serializePayload(uint8_t *buf) { + ::memset(buf, 0, mSize); + return; +} + +//================================================================================================= + +WebmMaster::WebmMaster(uint64_t id, const List<sp<WebmElement> >& children) + : WebmElement(id, childrenSum(children)), mChildren(children) { +} + +WebmMaster::WebmMaster(uint64_t id) + : WebmElement(id, 0) { +} + +int WebmMaster::serializePayloadSize(uint8_t *buf) { + if (mSize == 0){ + return serializeCodedUnsigned(kMkvUnknownLength, buf); + } + return WebmElement::serializePayloadSize(buf); +} + +void WebmMaster::serializePayload(uint8_t *buf) { + uint64_t off = 0; + for (List<sp<WebmElement> >::const_iterator it = mChildren.begin(); it != mChildren.end(); + ++it) { + sp<WebmElement> child = (*it); + child->serializeInto(buf + off); + off += child->totalSize(); + } +} + +//================================================================================================= + +sp<WebmElement> WebmElement::CuePointEntry(uint64_t time, int track, uint64_t off) { + List<sp<WebmElement> > cuePointEntryFields; + cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueTrack, track)); + cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueClusterPosition, off)); + WebmElement *cueTrackPositions = new WebmMaster(kMkvCueTrackPositions, cuePointEntryFields); + + cuePointEntryFields.clear(); + cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueTime, time)); + cuePointEntryFields.push_back(cueTrackPositions); + return new WebmMaster(kMkvCuePoint, cuePointEntryFields); +} + +sp<WebmElement> WebmElement::SeekEntry(uint64_t id, uint64_t off) { + List<sp<WebmElement> > seekEntryFields; + seekEntryFields.push_back(new WebmUnsigned(kMkvSeekId, id)); + seekEntryFields.push_back(new WebmUnsigned(kMkvSeekPosition, off)); + return new WebmMaster(kMkvSeek, seekEntryFields); +} + +sp<WebmElement> WebmElement::EbmlHeader( + int ver, + int readVer, + int maxIdLen, + int maxSizeLen, + int docVer, + int docReadVer) { + List<sp<WebmElement> > headerFields; + headerFields.push_back(new WebmUnsigned(kMkvEbmlVersion, ver)); + headerFields.push_back(new WebmUnsigned(kMkvEbmlReadVersion, readVer)); + headerFields.push_back(new WebmUnsigned(kMkvEbmlMaxIdlength, maxIdLen)); + headerFields.push_back(new WebmUnsigned(kMkvEbmlMaxSizeLength, maxSizeLen)); + headerFields.push_back(new WebmString(kMkvDocType, "webm")); + headerFields.push_back(new WebmUnsigned(kMkvDocTypeVersion, docVer)); + headerFields.push_back(new WebmUnsigned(kMkvDocTypeReadVersion, docReadVer)); + return new WebmMaster(kMkvEbml, headerFields); +} + +sp<WebmElement> WebmElement::SegmentInfo(uint64_t scale, double dur) { + List<sp<WebmElement> > segmentInfo; + // place duration first; easier to patch + segmentInfo.push_back(new WebmFloat(kMkvSegmentDuration, dur)); + segmentInfo.push_back(new WebmUnsigned(kMkvTimecodeScale, scale)); + segmentInfo.push_back(new WebmString(kMkvMuxingApp, "android")); + segmentInfo.push_back(new WebmString(kMkvWritingApp, "android")); + return new WebmMaster(kMkvInfo, segmentInfo); +} + +sp<WebmElement> WebmElement::AudioTrackEntry( + int chans, + double rate, + const sp<ABuffer> &buf, + int bps, + uint64_t uid, + bool lacing, + const char *lang) { + if (uid == 0) { + uid = kAudioTrackNum; + } + + List<sp<WebmElement> > trackEntryFields; + populateCommonTrackEntries( + kAudioTrackNum, + uid, + lacing, + lang, + "A_VORBIS", + kAudioType, + trackEntryFields); + + List<sp<WebmElement> > audioInfo; + audioInfo.push_back(new WebmUnsigned(kMkvChannels, chans)); + audioInfo.push_back(new WebmFloat(kMkvSamplingFrequency, rate)); + if (bps) { + WebmElement *bitDepth = new WebmUnsigned(kMkvBitDepth, bps); + audioInfo.push_back(bitDepth); + } + + trackEntryFields.push_back(new WebmMaster(kMkvAudio, audioInfo)); + trackEntryFields.push_back(new WebmBinary(kMkvCodecPrivate, buf)); + return new WebmMaster(kMkvTrackEntry, trackEntryFields); +} + +sp<WebmElement> WebmElement::VideoTrackEntry( + uint64_t width, + uint64_t height, + uint64_t uid, + bool lacing, + const char *lang) { + if (uid == 0) { + uid = kVideoTrackNum; + } + + List<sp<WebmElement> > trackEntryFields; + populateCommonTrackEntries( + kVideoTrackNum, + uid, + lacing, + lang, + "V_VP8", + kVideoType, + trackEntryFields); + + List<sp<WebmElement> > videoInfo; + videoInfo.push_back(new WebmUnsigned(kMkvPixelWidth, width)); + videoInfo.push_back(new WebmUnsigned(kMkvPixelHeight, height)); + + trackEntryFields.push_back(new WebmMaster(kMkvVideo, videoInfo)); + return new WebmMaster(kMkvTrackEntry, trackEntryFields); +} +} /* namespace android */ diff --git a/media/libstagefright/webm/WebmElement.h b/media/libstagefright/webm/WebmElement.h new file mode 100644 index 0000000..f19933e --- /dev/null +++ b/media/libstagefright/webm/WebmElement.h @@ -0,0 +1,127 @@ +/* + * 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 WEBMELEMENT_H_ +#define WEBMELEMENT_H_ + +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <utils/List.h> + +namespace android { + +struct WebmElement : public LightRefBase<WebmElement> { + const uint64_t mId, mSize; + + WebmElement(uint64_t id, uint64_t size); + virtual ~WebmElement(); + + virtual int serializePayloadSize(uint8_t *buf); + virtual void serializePayload(uint8_t *buf)=0; + uint64_t totalSize(); + uint64_t serializeInto(uint8_t *buf); + uint8_t *serialize(uint64_t& size); + int write(int fd, uint64_t& size); + + static sp<WebmElement> EbmlHeader( + int ver = 1, + int readVer = 1, + int maxIdLen = 4, + int maxSizeLen = 8, + int docVer = 2, + int docReadVer = 2); + + static sp<WebmElement> SegmentInfo(uint64_t scale = 1000000, double dur = 0); + + static sp<WebmElement> AudioTrackEntry( + int chans, + double rate, + const sp<ABuffer> &buf, + int bps = 0, + uint64_t uid = 0, + bool lacing = false, + const char *lang = "und"); + + static sp<WebmElement> VideoTrackEntry( + uint64_t width, + uint64_t height, + uint64_t uid = 0, + bool lacing = false, + const char *lang = "und"); + + static sp<WebmElement> SeekEntry(uint64_t id, uint64_t off); + static sp<WebmElement> CuePointEntry(uint64_t time, int track, uint64_t off); + static sp<WebmElement> SimpleBlock( + int trackNum, + int16_t timecode, + bool key, + const uint8_t *data, + uint64_t dataSize); +}; + +struct WebmUnsigned : public WebmElement { + WebmUnsigned(uint64_t id, uint64_t value); + const uint64_t mValue; + void serializePayload(uint8_t *buf); +}; + +struct WebmFloat : public WebmElement { + const double mValue; + WebmFloat(uint64_t id, float value); + WebmFloat(uint64_t id, double value); + void serializePayload(uint8_t *buf); +}; + +struct WebmBinary : public WebmElement { + const sp<ABuffer> mRef; + WebmBinary(uint64_t id, const sp<ABuffer> &ref); + void serializePayload(uint8_t *buf); +}; + +struct WebmString : public WebmElement { + const char *const mStr; + WebmString(uint64_t id, const char *str); + void serializePayload(uint8_t *buf); +}; + +struct WebmSimpleBlock : public WebmElement { + const int mTrackNum; + const int16_t mRelTimecode; + const bool mKey; + const sp<ABuffer> mRef; + + WebmSimpleBlock(int trackNum, int16_t timecode, bool key, const sp<ABuffer>& orig); + void serializePayload(uint8_t *buf); +}; + +struct EbmlVoid : public WebmElement { + const uint64_t mSizeWidth; + EbmlVoid(uint64_t totalSize); + int serializePayloadSize(uint8_t *buf); + void serializePayload(uint8_t *buf); +}; + +struct WebmMaster : public WebmElement { + const List<sp<WebmElement> > mChildren; + WebmMaster(uint64_t id); + WebmMaster(uint64_t id, const List<sp<WebmElement> > &children); + int serializePayloadSize(uint8_t *buf); + void serializePayload(uint8_t *buf); +}; + +} /* namespace android */ +#endif /* WEBMELEMENT_H_ */ diff --git a/media/libstagefright/webm/WebmFrame.cpp b/media/libstagefright/webm/WebmFrame.cpp new file mode 100644 index 0000000..e5134ed --- /dev/null +++ b/media/libstagefright/webm/WebmFrame.cpp @@ -0,0 +1,83 @@ +/* + * 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 "WebmFrame" + +#include "WebmFrame.h" +#include "WebmConstants.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <unistd.h> + +using namespace android; +using namespace webm; + +namespace { +sp<ABuffer> toABuffer(MediaBuffer *mbuf) { + sp<ABuffer> abuf = new ABuffer(mbuf->range_length()); + memcpy(abuf->data(), (uint8_t*) mbuf->data() + mbuf->range_offset(), mbuf->range_length()); + return abuf; +} +} + +namespace android { + +const sp<WebmFrame> WebmFrame::EOS = new WebmFrame(); + +WebmFrame::WebmFrame() + : mType(kInvalidType), + mKey(false), + mAbsTimecode(UINT64_MAX), + mData(new ABuffer(0)), + mEos(true) { +} + +WebmFrame::WebmFrame(int type, bool key, uint64_t absTimecode, MediaBuffer *mbuf) + : mType(type), + mKey(key), + mAbsTimecode(absTimecode), + mData(toABuffer(mbuf)), + mEos(false) { +} + +sp<WebmElement> WebmFrame::SimpleBlock(uint64_t baseTimecode) const { + return new WebmSimpleBlock( + mType == kVideoType ? kVideoTrackNum : kAudioTrackNum, + mAbsTimecode - baseTimecode, + mKey, + mData); +} + +bool WebmFrame::operator<(const WebmFrame &other) const { + if (this->mEos) { + return false; + } + if (other.mEos) { + return true; + } + if (this->mAbsTimecode == other.mAbsTimecode) { + if (this->mType == kAudioType && other.mType == kVideoType) { + return true; + } + if (this->mType == kVideoType && other.mType == kAudioType) { + return false; + } + return false; + } + return this->mAbsTimecode < other.mAbsTimecode; +} +} /* namespace android */ diff --git a/media/libstagefright/webm/WebmFrame.h b/media/libstagefright/webm/WebmFrame.h new file mode 100644 index 0000000..4f0b055 --- /dev/null +++ b/media/libstagefright/webm/WebmFrame.h @@ -0,0 +1,46 @@ +/* + * 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 WEBMFRAME_H_ +#define WEBMFRAME_H_ + +#include "WebmElement.h" + +namespace android { + +struct WebmFrame : LightRefBase<WebmFrame> { +public: + const int mType; + const bool mKey; + const uint64_t mAbsTimecode; + const sp<ABuffer> mData; + const bool mEos; + + WebmFrame(); + WebmFrame(int type, bool key, uint64_t absTimecode, MediaBuffer *buf); + ~WebmFrame() {} + + sp<WebmElement> SimpleBlock(uint64_t baseTimecode) const; + + bool operator<(const WebmFrame &other) const; + + static const sp<WebmFrame> EOS; +private: + DISALLOW_EVIL_CONSTRUCTORS(WebmFrame); +}; + +} /* namespace android */ +#endif /* WEBMFRAME_H_ */ diff --git a/media/libstagefright/webm/WebmFrameThread.cpp b/media/libstagefright/webm/WebmFrameThread.cpp new file mode 100644 index 0000000..a4b8a42 --- /dev/null +++ b/media/libstagefright/webm/WebmFrameThread.cpp @@ -0,0 +1,399 @@ +/* + * 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 "WebmFrameThread" + +#include "WebmConstants.h" +#include "WebmFrameThread.h" + +#include <media/stagefright/MetaData.h> +#include <media/stagefright/foundation/ADebug.h> + +#include <utils/Log.h> +#include <inttypes.h> + +using namespace webm; + +namespace android { + +void *WebmFrameThread::wrap(void *arg) { + WebmFrameThread *worker = reinterpret_cast<WebmFrameThread*>(arg); + worker->run(); + return NULL; +} + +status_t WebmFrameThread::start() { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_create(&mThread, &attr, WebmFrameThread::wrap, this); + pthread_attr_destroy(&attr); + return OK; +} + +status_t WebmFrameThread::stop() { + void *status; + pthread_join(mThread, &status); + return (status_t)(intptr_t)status; +} + +//================================================================================================= + +WebmFrameSourceThread::WebmFrameSourceThread( + int type, + LinkedBlockingQueue<const sp<WebmFrame> >& sink) + : mType(type), mSink(sink) { +} + +//================================================================================================= + +WebmFrameSinkThread::WebmFrameSinkThread( + const int& fd, + const uint64_t& off, + sp<WebmFrameSourceThread> videoThread, + sp<WebmFrameSourceThread> audioThread, + List<sp<WebmElement> >& cues) + : mFd(fd), + mSegmentDataStart(off), + mVideoFrames(videoThread->mSink), + mAudioFrames(audioThread->mSink), + mCues(cues), + mDone(true) { +} + +WebmFrameSinkThread::WebmFrameSinkThread( + const int& fd, + const uint64_t& off, + LinkedBlockingQueue<const sp<WebmFrame> >& videoSource, + LinkedBlockingQueue<const sp<WebmFrame> >& audioSource, + List<sp<WebmElement> >& cues) + : mFd(fd), + mSegmentDataStart(off), + mVideoFrames(videoSource), + mAudioFrames(audioSource), + mCues(cues), + mDone(true) { +} + +// Initializes a webm cluster with its starting timecode. +// +// frames: +// sequence of input audio/video frames received from the source. +// +// clusterTimecodeL: +// the starting timecode of the cluster; this is the timecode of the first +// frame since frames are ordered by timestamp. +// +// children: +// list to hold child elements in a webm cluster (start timecode and +// simple blocks). +// +// static +void WebmFrameSinkThread::initCluster( + List<const sp<WebmFrame> >& frames, + uint64_t& clusterTimecodeL, + List<sp<WebmElement> >& children) { + CHECK(!frames.empty() && children.empty()); + + const sp<WebmFrame> f = *(frames.begin()); + clusterTimecodeL = f->mAbsTimecode; + WebmUnsigned *clusterTimecode = new WebmUnsigned(kMkvTimecode, clusterTimecodeL); + children.clear(); + children.push_back(clusterTimecode); +} + +void WebmFrameSinkThread::writeCluster(List<sp<WebmElement> >& children) { + // children must contain at least one simpleblock and its timecode + CHECK_GE(children.size(), 2); + + uint64_t size; + sp<WebmElement> cluster = new WebmMaster(kMkvCluster, children); + cluster->write(mFd, size); + children.clear(); +} + +// Write out (possibly multiple) webm cluster(s) from frames split on video key frames. +// +// last: +// current flush is triggered by EOS instead of a second outstanding video key frame. +void WebmFrameSinkThread::flushFrames(List<const sp<WebmFrame> >& frames, bool last) { + if (frames.empty()) { + return; + } + + uint64_t clusterTimecodeL; + List<sp<WebmElement> > children; + initCluster(frames, clusterTimecodeL, children); + + uint64_t cueTime = clusterTimecodeL; + off_t fpos = ::lseek(mFd, 0, SEEK_CUR); + size_t n = frames.size(); + if (!last) { + // If we are not flushing the last sequence of outstanding frames, flushFrames + // must have been called right after we have pushed a second outstanding video key + // frame (the last frame), which belongs to the next cluster; also hold back on + // flushing the second to last frame before we check its type. A audio frame + // should precede the aforementioned video key frame in the next sequence, a video + // frame should be the last frame in the current (to-be-flushed) sequence. + CHECK_GE(n, 2); + n -= 2; + } + + for (size_t i = 0; i < n; i++) { + const sp<WebmFrame> f = *(frames.begin()); + if (f->mType == kVideoType && f->mKey) { + cueTime = f->mAbsTimecode; + } + + if (f->mAbsTimecode - clusterTimecodeL > INT16_MAX) { + writeCluster(children); + initCluster(frames, clusterTimecodeL, children); + } + + frames.erase(frames.begin()); + children.push_back(f->SimpleBlock(clusterTimecodeL)); + } + + // equivalent to last==false + if (!frames.empty()) { + // decide whether to write out the second to last frame. + const sp<WebmFrame> secondLastFrame = *(frames.begin()); + if (secondLastFrame->mType == kVideoType) { + frames.erase(frames.begin()); + children.push_back(secondLastFrame->SimpleBlock(clusterTimecodeL)); + } + } + + writeCluster(children); + sp<WebmElement> cuePoint = WebmElement::CuePointEntry(cueTime, 1, fpos - mSegmentDataStart); + mCues.push_back(cuePoint); +} + +status_t WebmFrameSinkThread::start() { + mDone = false; + return WebmFrameThread::start(); +} + +status_t WebmFrameSinkThread::stop() { + mDone = true; + mVideoFrames.push(WebmFrame::EOS); + mAudioFrames.push(WebmFrame::EOS); + return WebmFrameThread::stop(); +} + +void WebmFrameSinkThread::run() { + int numVideoKeyFrames = 0; + List<const sp<WebmFrame> > outstandingFrames; + while (!mDone) { + ALOGV("wait v frame"); + const sp<WebmFrame> videoFrame = mVideoFrames.peek(); + ALOGV("v frame: %p", videoFrame.get()); + + ALOGV("wait a frame"); + const sp<WebmFrame> audioFrame = mAudioFrames.peek(); + ALOGV("a frame: %p", audioFrame.get()); + + if (videoFrame->mEos && audioFrame->mEos) { + break; + } + + if (*audioFrame < *videoFrame) { + ALOGV("take a frame"); + mAudioFrames.take(); + outstandingFrames.push_back(audioFrame); + } else { + ALOGV("take v frame"); + mVideoFrames.take(); + outstandingFrames.push_back(videoFrame); + if (videoFrame->mKey) + numVideoKeyFrames++; + } + + if (numVideoKeyFrames == 2) { + flushFrames(outstandingFrames, /* last = */ false); + numVideoKeyFrames--; + } + } + ALOGV("flushing last cluster (size %zu)", outstandingFrames.size()); + flushFrames(outstandingFrames, /* last = */ true); + mDone = true; +} + +//================================================================================================= + +static const int64_t kInitialDelayTimeUs = 700000LL; + +void WebmFrameMediaSourceThread::clearFlags() { + mDone = false; + mPaused = false; + mResumed = false; + mStarted = false; + mReachedEOS = false; +} + +WebmFrameMediaSourceThread::WebmFrameMediaSourceThread( + const sp<MediaSource>& source, + int type, + LinkedBlockingQueue<const sp<WebmFrame> >& sink, + uint64_t timeCodeScale, + int64_t startTimeRealUs, + int32_t startTimeOffsetMs, + int numTracks, + bool realTimeRecording) + : WebmFrameSourceThread(type, sink), + mSource(source), + mTimeCodeScale(timeCodeScale), + mTrackDurationUs(0) { + clearFlags(); + mStartTimeUs = startTimeRealUs; + if (realTimeRecording && numTracks > 1) { + /* + * Copied from MPEG4Writer + * + * This extra delay of accepting incoming audio/video signals + * helps to align a/v start time at the beginning of a recording + * session, and it also helps eliminate the "recording" sound for + * camcorder applications. + * + * If client does not set the start time offset, we fall back to + * use the default initial delay value. + */ + int64_t startTimeOffsetUs = startTimeOffsetMs * 1000LL; + if (startTimeOffsetUs < 0) { // Start time offset was not set + startTimeOffsetUs = kInitialDelayTimeUs; + } + mStartTimeUs += startTimeOffsetUs; + ALOGI("Start time offset: %" PRId64 " us", startTimeOffsetUs); + } +} + +status_t WebmFrameMediaSourceThread::start() { + sp<MetaData> meta = new MetaData; + meta->setInt64(kKeyTime, mStartTimeUs); + status_t err = mSource->start(meta.get()); + if (err != OK) { + mDone = true; + mReachedEOS = true; + return err; + } else { + mStarted = true; + return WebmFrameThread::start(); + } +} + +status_t WebmFrameMediaSourceThread::resume() { + if (!mDone && mPaused) { + mPaused = false; + mResumed = true; + } + return OK; +} + +status_t WebmFrameMediaSourceThread::pause() { + if (mStarted) { + mPaused = true; + } + return OK; +} + +status_t WebmFrameMediaSourceThread::stop() { + if (mStarted) { + mStarted = false; + mDone = true; + mSource->stop(); + return WebmFrameThread::stop(); + } + return OK; +} + +void WebmFrameMediaSourceThread::run() { + int32_t count = 0; + int64_t timestampUs = 0xdeadbeef; + int64_t lastTimestampUs = 0; // Previous sample time stamp + int64_t lastDurationUs = 0; // Previous sample duration + int64_t previousPausedDurationUs = 0; + + const uint64_t kUninitialized = 0xffffffffffffffffL; + mStartTimeUs = kUninitialized; + + status_t err = OK; + MediaBuffer *buffer; + while (!mDone && (err = mSource->read(&buffer, NULL)) == OK) { + if (buffer->range_length() == 0) { + buffer->release(); + buffer = NULL; + continue; + } + + sp<MetaData> md = buffer->meta_data(); + CHECK(md->findInt64(kKeyTime, ×tampUs)); + if (mStartTimeUs == kUninitialized) { + mStartTimeUs = timestampUs; + } + timestampUs -= mStartTimeUs; + + if (mPaused && !mResumed) { + lastDurationUs = timestampUs - lastTimestampUs; + lastTimestampUs = timestampUs; + buffer->release(); + buffer = NULL; + continue; + } + ++count; + + // adjust time-stamps after pause/resume + if (mResumed) { + int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs; + CHECK_GE(durExcludingEarlierPausesUs, 0ll); + int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs; + CHECK_GE(pausedDurationUs, lastDurationUs); + previousPausedDurationUs += pausedDurationUs - lastDurationUs; + mResumed = false; + } + timestampUs -= previousPausedDurationUs; + CHECK_GE(timestampUs, 0ll); + + int32_t isSync = false; + md->findInt32(kKeyIsSyncFrame, &isSync); + const sp<WebmFrame> f = new WebmFrame( + mType, + isSync, + timestampUs * 1000 / mTimeCodeScale, + buffer); + mSink.push(f); + + ALOGV( + "%s %s frame at %" PRId64 " size %zu\n", + mType == kVideoType ? "video" : "audio", + isSync ? "I" : "P", + timestampUs * 1000 / mTimeCodeScale, + buffer->range_length()); + + buffer->release(); + buffer = NULL; + + if (timestampUs > mTrackDurationUs) { + mTrackDurationUs = timestampUs; + } + lastDurationUs = timestampUs - lastTimestampUs; + lastTimestampUs = timestampUs; + } + + mTrackDurationUs += lastDurationUs; + mSink.push(WebmFrame::EOS); +} +} diff --git a/media/libstagefright/webm/WebmFrameThread.h b/media/libstagefright/webm/WebmFrameThread.h new file mode 100644 index 0000000..d65d9b7 --- /dev/null +++ b/media/libstagefright/webm/WebmFrameThread.h @@ -0,0 +1,160 @@ +/* + * 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 WEBMFRAMETHREAD_H_ +#define WEBMFRAMETHREAD_H_ + +#include "WebmFrame.h" +#include "LinkedBlockingQueue.h" + +#include <media/stagefright/FileSource.h> +#include <media/stagefright/MediaSource.h> + +#include <utils/List.h> +#include <utils/Errors.h> + +#include <pthread.h> + +namespace android { + +class WebmFrameThread : public LightRefBase<WebmFrameThread> { +public: + virtual void run() = 0; + virtual bool running() { return false; } + virtual status_t start(); + virtual status_t pause() { return OK; } + virtual status_t resume() { return OK; } + virtual status_t stop(); + virtual ~WebmFrameThread() { stop(); } + static void *wrap(void *arg); + +protected: + WebmFrameThread() + : mThread(0) { + } + +private: + pthread_t mThread; + DISALLOW_EVIL_CONSTRUCTORS(WebmFrameThread); +}; + +//================================================================================================= + +class WebmFrameSourceThread; +class WebmFrameSinkThread : public WebmFrameThread { +public: + WebmFrameSinkThread( + const int& fd, + const uint64_t& off, + sp<WebmFrameSourceThread> videoThread, + sp<WebmFrameSourceThread> audioThread, + List<sp<WebmElement> >& cues); + + WebmFrameSinkThread( + const int& fd, + const uint64_t& off, + LinkedBlockingQueue<const sp<WebmFrame> >& videoSource, + LinkedBlockingQueue<const sp<WebmFrame> >& audioSource, + List<sp<WebmElement> >& cues); + + void run(); + bool running() { + return !mDone; + } + status_t start(); + status_t stop(); + +private: + const int& mFd; + const uint64_t& mSegmentDataStart; + LinkedBlockingQueue<const sp<WebmFrame> >& mVideoFrames; + LinkedBlockingQueue<const sp<WebmFrame> >& mAudioFrames; + List<sp<WebmElement> >& mCues; + + volatile bool mDone; + + static void initCluster( + List<const sp<WebmFrame> >& frames, + uint64_t& clusterTimecodeL, + List<sp<WebmElement> >& children); + void writeCluster(List<sp<WebmElement> >& children); + void flushFrames(List<const sp<WebmFrame> >& frames, bool last); +}; + +//================================================================================================= + +class WebmFrameSourceThread : public WebmFrameThread { +public: + WebmFrameSourceThread(int type, LinkedBlockingQueue<const sp<WebmFrame> >& sink); + virtual int64_t getDurationUs() = 0; +protected: + const int mType; + LinkedBlockingQueue<const sp<WebmFrame> >& mSink; + + friend class WebmFrameSinkThread; +}; + +//================================================================================================= + +class WebmFrameEmptySourceThread : public WebmFrameSourceThread { +public: + WebmFrameEmptySourceThread(int type, LinkedBlockingQueue<const sp<WebmFrame> >& sink) + : WebmFrameSourceThread(type, sink) { + } + void run() { mSink.push(WebmFrame::EOS); } + int64_t getDurationUs() { return 0; } +}; + +//================================================================================================= + +class WebmFrameMediaSourceThread: public WebmFrameSourceThread { +public: + WebmFrameMediaSourceThread( + const sp<MediaSource>& source, + int type, + LinkedBlockingQueue<const sp<WebmFrame> >& sink, + uint64_t timeCodeScale, + int64_t startTimeRealUs, + int32_t startTimeOffsetMs, + int numPeers, + bool realTimeRecording); + + void run(); + status_t start(); + status_t resume(); + status_t pause(); + status_t stop(); + int64_t getDurationUs() { + return mTrackDurationUs; + } + +private: + const sp<MediaSource> mSource; + const uint64_t mTimeCodeScale; + uint64_t mStartTimeUs; + + volatile bool mDone; + volatile bool mPaused; + volatile bool mResumed; + volatile bool mStarted; + volatile bool mReachedEOS; + int64_t mTrackDurationUs; + + void clearFlags(); +}; +} /* namespace android */ + +#endif /* WEBMFRAMETHREAD_H_ */ diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp new file mode 100644 index 0000000..03cf92a --- /dev/null +++ b/media/libstagefright/webm/WebmWriter.cpp @@ -0,0 +1,551 @@ +/* + * 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 "WebmWriter" + +#include "EbmlUtil.h" +#include "WebmWriter.h" + +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/foundation/ADebug.h> + +#include <utils/Errors.h> + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <inttypes.h> + +using namespace webm; + +namespace { +size_t XiphLaceCodeLen(size_t size) { + return size / 0xff + 1; +} + +size_t XiphLaceEnc(uint8_t *buf, size_t size) { + size_t i; + for (i = 0; size >= 0xff; ++i, size -= 0xff) { + buf[i] = 0xff; + } + buf[i++] = size; + return i; +} +} + +namespace android { + +static const int64_t kMinStreamableFileSizeInBytes = 5 * 1024 * 1024; + +WebmWriter::WebmWriter(int fd) + : mFd(dup(fd)), + mInitCheck(mFd < 0 ? NO_INIT : OK), + mTimeCodeScale(1000000), + mStartTimestampUs(0), + mStartTimeOffsetMs(0), + mSegmentOffset(0), + mSegmentDataStart(0), + mInfoOffset(0), + mInfoSize(0), + mTracksOffset(0), + mCuesOffset(0), + mPaused(false), + mStarted(false), + mIsFileSizeLimitExplicitlyRequested(false), + mIsRealTimeRecording(false), + mStreamableFile(true), + mEstimatedCuesSize(0) { + mStreams[kAudioIndex] = WebmStream(kAudioType, "Audio", &WebmWriter::audioTrack); + mStreams[kVideoIndex] = WebmStream(kVideoType, "Video", &WebmWriter::videoTrack); + mSinkThread = new WebmFrameSinkThread( + mFd, + mSegmentDataStart, + mStreams[kVideoIndex].mSink, + mStreams[kAudioIndex].mSink, + mCuePoints); +} + +WebmWriter::WebmWriter(const char *filename) + : mInitCheck(NO_INIT), + mTimeCodeScale(1000000), + mStartTimestampUs(0), + mStartTimeOffsetMs(0), + mSegmentOffset(0), + mSegmentDataStart(0), + mInfoOffset(0), + mInfoSize(0), + mTracksOffset(0), + mCuesOffset(0), + mPaused(false), + mStarted(false), + mIsFileSizeLimitExplicitlyRequested(false), + mIsRealTimeRecording(false), + mStreamableFile(true), + mEstimatedCuesSize(0) { + mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); + if (mFd >= 0) { + ALOGV("fd %d; flags: %o", mFd, fcntl(mFd, F_GETFL, 0)); + mInitCheck = OK; + } + mStreams[kAudioIndex] = WebmStream(kAudioType, "Audio", &WebmWriter::audioTrack); + mStreams[kVideoIndex] = WebmStream(kVideoType, "Video", &WebmWriter::videoTrack); + mSinkThread = new WebmFrameSinkThread( + mFd, + mSegmentDataStart, + mStreams[kVideoIndex].mSink, + mStreams[kAudioIndex].mSink, + mCuePoints); +} + +// static +sp<WebmElement> WebmWriter::videoTrack(const sp<MetaData>& md) { + int32_t width, height; + CHECK(md->findInt32(kKeyWidth, &width)); + CHECK(md->findInt32(kKeyHeight, &height)); + return WebmElement::VideoTrackEntry(width, height); +} + +// static +sp<WebmElement> WebmWriter::audioTrack(const sp<MetaData>& md) { + int32_t nChannels, samplerate; + uint32_t type; + const void *headerData1; + const char headerData2[] = { 3, 'v', 'o', 'r', 'b', 'i', 's', 7, 0, 0, 0, + 'a', 'n', 'd', 'r', 'o', 'i', 'd', 0, 0, 0, 0, 1 }; + const void *headerData3; + size_t headerSize1, headerSize2 = sizeof(headerData2), headerSize3; + + CHECK(md->findInt32(kKeyChannelCount, &nChannels)); + CHECK(md->findInt32(kKeySampleRate, &samplerate)); + CHECK(md->findData(kKeyVorbisInfo, &type, &headerData1, &headerSize1)); + CHECK(md->findData(kKeyVorbisBooks, &type, &headerData3, &headerSize3)); + + size_t codecPrivateSize = 1; + codecPrivateSize += XiphLaceCodeLen(headerSize1); + codecPrivateSize += XiphLaceCodeLen(headerSize2); + codecPrivateSize += headerSize1 + headerSize2 + headerSize3; + + off_t off = 0; + sp<ABuffer> codecPrivateBuf = new ABuffer(codecPrivateSize); + uint8_t *codecPrivateData = codecPrivateBuf->data(); + codecPrivateData[off++] = 2; + + off += XiphLaceEnc(codecPrivateData + off, headerSize1); + off += XiphLaceEnc(codecPrivateData + off, headerSize2); + + memcpy(codecPrivateData + off, headerData1, headerSize1); + off += headerSize1; + memcpy(codecPrivateData + off, headerData2, headerSize2); + off += headerSize2; + memcpy(codecPrivateData + off, headerData3, headerSize3); + + sp<WebmElement> entry = WebmElement::AudioTrackEntry( + nChannels, + samplerate, + codecPrivateBuf); + return entry; +} + +size_t WebmWriter::numTracks() { + Mutex::Autolock autolock(mLock); + + size_t numTracks = 0; + for (size_t i = 0; i < kMaxStreams; ++i) { + if (mStreams[i].mTrackEntry != NULL) { + numTracks++; + } + } + + return numTracks; +} + +uint64_t WebmWriter::estimateCuesSize(int32_t bitRate) { + // This implementation is based on estimateMoovBoxSize in MPEG4Writer. + // + // Statistical analysis shows that metadata usually accounts + // for a small portion of the total file size, usually < 0.6%. + + // The default MIN_MOOV_BOX_SIZE is set to 0.6% x 1MB / 2, + // where 1MB is the common file size limit for MMS application. + // The default MAX _MOOV_BOX_SIZE value is based on about 3 + // minute video recording with a bit rate about 3 Mbps, because + // statistics also show that most of the video captured are going + // to be less than 3 minutes. + + // If the estimation is wrong, we will pay the price of wasting + // some reserved space. This should not happen so often statistically. + static const int32_t factor = 2; + static const int64_t MIN_CUES_SIZE = 3 * 1024; // 3 KB + static const int64_t MAX_CUES_SIZE = (180 * 3000000 * 6LL / 8000); + int64_t size = MIN_CUES_SIZE; + + // Max file size limit is set + if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) { + size = mMaxFileSizeLimitBytes * 6 / 1000; + } + + // Max file duration limit is set + if (mMaxFileDurationLimitUs != 0) { + if (bitRate > 0) { + int64_t size2 = ((mMaxFileDurationLimitUs * bitRate * 6) / 1000 / 8000000); + if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) { + // When both file size and duration limits are set, + // we use the smaller limit of the two. + if (size > size2) { + size = size2; + } + } else { + // Only max file duration limit is set + size = size2; + } + } + } + + if (size < MIN_CUES_SIZE) { + size = MIN_CUES_SIZE; + } + + // Any long duration recording will be probably end up with + // non-streamable webm file. + if (size > MAX_CUES_SIZE) { + size = MAX_CUES_SIZE; + } + + ALOGV("limits: %" PRId64 "/%" PRId64 " bytes/us," + " bit rate: %d bps and the estimated cues size %" PRId64 " bytes", + mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size); + return factor * size; +} + +void WebmWriter::initStream(size_t idx) { + if (mStreams[idx].mThread != NULL) { + return; + } + if (mStreams[idx].mSource == NULL) { + ALOGV("adding dummy source ... "); + mStreams[idx].mThread = new WebmFrameEmptySourceThread( + mStreams[idx].mType, mStreams[idx].mSink); + } else { + ALOGV("adding source %p", mStreams[idx].mSource.get()); + mStreams[idx].mThread = new WebmFrameMediaSourceThread( + mStreams[idx].mSource, + mStreams[idx].mType, + mStreams[idx].mSink, + mTimeCodeScale, + mStartTimestampUs, + mStartTimeOffsetMs, + numTracks(), + mIsRealTimeRecording); + } +} + +void WebmWriter::release() { + close(mFd); + mFd = -1; + mInitCheck = NO_INIT; + mStarted = false; +} + +status_t WebmWriter::reset() { + if (mInitCheck != OK) { + return OK; + } else { + if (!mStarted) { + release(); + return OK; + } + } + + status_t err = OK; + int64_t maxDurationUs = 0; + int64_t minDurationUs = 0x7fffffffffffffffLL; + for (int i = 0; i < kMaxStreams; ++i) { + if (mStreams[i].mThread == NULL) { + continue; + } + + status_t status = mStreams[i].mThread->stop(); + if (err == OK && status != OK) { + err = status; + } + + int64_t durationUs = mStreams[i].mThread->getDurationUs(); + if (durationUs > maxDurationUs) { + maxDurationUs = durationUs; + } + if (durationUs < minDurationUs) { + minDurationUs = durationUs; + } + } + + if (numTracks() > 1) { + ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us", minDurationUs, maxDurationUs); + } + + mSinkThread->stop(); + + // Do not write out movie header on error. + if (err != OK) { + release(); + return err; + } + + sp<WebmElement> cues = new WebmMaster(kMkvCues, mCuePoints); + uint64_t cuesSize = cues->totalSize(); + // TRICKY Even when the cues do fit in the space we reserved, if they do not fit + // perfectly, we still need to check if there is enough "extra space" to write an + // EBML void element. + if (cuesSize != mEstimatedCuesSize && cuesSize > mEstimatedCuesSize - kMinEbmlVoidSize) { + mCuesOffset = ::lseek(mFd, 0, SEEK_CUR); + cues->write(mFd, cuesSize); + } else { + uint64_t spaceSize; + ::lseek(mFd, mCuesOffset, SEEK_SET); + cues->write(mFd, cuesSize); + sp<WebmElement> space = new EbmlVoid(mEstimatedCuesSize - cuesSize); + space->write(mFd, spaceSize); + } + + mCuePoints.clear(); + mStreams[kVideoIndex].mSink.clear(); + mStreams[kAudioIndex].mSink.clear(); + + uint8_t bary[sizeof(uint64_t)]; + uint64_t totalSize = ::lseek(mFd, 0, SEEK_END); + uint64_t segmentSize = totalSize - mSegmentDataStart; + ::lseek(mFd, mSegmentOffset + sizeOf(kMkvSegment), SEEK_SET); + uint64_t segmentSizeCoded = encodeUnsigned(segmentSize, sizeOf(kMkvUnknownLength)); + serializeCodedUnsigned(segmentSizeCoded, bary); + ::write(mFd, bary, sizeOf(kMkvUnknownLength)); + + uint64_t size; + uint64_t durationOffset = mInfoOffset + sizeOf(kMkvInfo) + sizeOf(mInfoSize) + + sizeOf(kMkvSegmentDuration) + sizeOf(sizeof(double)); + sp<WebmElement> duration = new WebmFloat( + kMkvSegmentDuration, + (double) (maxDurationUs * 1000 / mTimeCodeScale)); + duration->serializePayload(bary); + ::lseek(mFd, durationOffset, SEEK_SET); + ::write(mFd, bary, sizeof(double)); + + List<sp<WebmElement> > seekEntries; + seekEntries.push_back(WebmElement::SeekEntry(kMkvInfo, mInfoOffset - mSegmentDataStart)); + seekEntries.push_back(WebmElement::SeekEntry(kMkvTracks, mTracksOffset - mSegmentDataStart)); + seekEntries.push_back(WebmElement::SeekEntry(kMkvCues, mCuesOffset - mSegmentDataStart)); + sp<WebmElement> seekHead = new WebmMaster(kMkvSeekHead, seekEntries); + + uint64_t metaSeekSize; + ::lseek(mFd, mSegmentDataStart, SEEK_SET); + seekHead->write(mFd, metaSeekSize); + + uint64_t spaceSize; + sp<WebmElement> space = new EbmlVoid(kMaxMetaSeekSize - metaSeekSize); + space->write(mFd, spaceSize); + + release(); + return err; +} + +status_t WebmWriter::addSource(const sp<MediaSource> &source) { + Mutex::Autolock l(mLock); + if (mStarted) { + ALOGE("Attempt to add source AFTER recording is started"); + return UNKNOWN_ERROR; + } + + // At most 2 tracks can be supported. + if (mStreams[kVideoIndex].mTrackEntry != NULL + && mStreams[kAudioIndex].mTrackEntry != NULL) { + ALOGE("Too many tracks (2) to add"); + return ERROR_UNSUPPORTED; + } + + CHECK(source != NULL); + + // A track of type other than video or audio is not supported. + const char *mime; + source->getFormat()->findCString(kKeyMIMEType, &mime); + const char *vp8 = MEDIA_MIMETYPE_VIDEO_VP8; + const char *vorbis = MEDIA_MIMETYPE_AUDIO_VORBIS; + + size_t streamIndex; + if (!strncasecmp(mime, vp8, strlen(vp8))) { + streamIndex = kVideoIndex; + } else if (!strncasecmp(mime, vorbis, strlen(vorbis))) { + streamIndex = kAudioIndex; + } else { + ALOGE("Track (%s) other than %s or %s is not supported", mime, vp8, vorbis); + return ERROR_UNSUPPORTED; + } + + // No more than one video or one audio track is supported. + if (mStreams[streamIndex].mTrackEntry != NULL) { + ALOGE("%s track already exists", mStreams[streamIndex].mName); + return ERROR_UNSUPPORTED; + } + + // This is the first track of either audio or video. + // Go ahead to add the track. + mStreams[streamIndex].mSource = source; + mStreams[streamIndex].mTrackEntry = mStreams[streamIndex].mMakeTrack(source->getFormat()); + + return OK; +} + +status_t WebmWriter::start(MetaData *params) { + if (mInitCheck != OK) { + return UNKNOWN_ERROR; + } + + if (mStreams[kVideoIndex].mTrackEntry == NULL + && mStreams[kAudioIndex].mTrackEntry == NULL) { + ALOGE("No source added"); + return INVALID_OPERATION; + } + + if (mMaxFileSizeLimitBytes != 0) { + mIsFileSizeLimitExplicitlyRequested = true; + } + + if (params) { + int32_t isRealTimeRecording; + params->findInt32(kKeyRealTimeRecording, &isRealTimeRecording); + mIsRealTimeRecording = isRealTimeRecording; + } + + if (mStarted) { + if (mPaused) { + mPaused = false; + mStreams[kAudioIndex].mThread->resume(); + mStreams[kVideoIndex].mThread->resume(); + } + return OK; + } + + if (params) { + int32_t tcsl; + if (params->findInt32(kKeyTimeScale, &tcsl)) { + mTimeCodeScale = tcsl; + } + } + CHECK_GT(mTimeCodeScale, 0); + ALOGV("movie time scale: %" PRIu64, mTimeCodeScale); + + /* + * When the requested file size limit is small, the priority + * is to meet the file size limit requirement, rather than + * to make the file streamable. mStreamableFile does not tell + * whether the actual recorded file is streamable or not. + */ + mStreamableFile = (!mMaxFileSizeLimitBytes) + || (mMaxFileSizeLimitBytes >= kMinStreamableFileSizeInBytes); + + /* + * Write various metadata. + */ + sp<WebmElement> ebml, segment, info, seekHead, tracks, cues; + ebml = WebmElement::EbmlHeader(); + segment = new WebmMaster(kMkvSegment); + seekHead = new EbmlVoid(kMaxMetaSeekSize); + info = WebmElement::SegmentInfo(mTimeCodeScale, 0); + + List<sp<WebmElement> > children; + for (size_t i = 0; i < kMaxStreams; ++i) { + if (mStreams[i].mTrackEntry != NULL) { + children.push_back(mStreams[i].mTrackEntry); + } + } + tracks = new WebmMaster(kMkvTracks, children); + + if (!mStreamableFile) { + cues = NULL; + } else { + int32_t bitRate = -1; + if (params) { + params->findInt32(kKeyBitRate, &bitRate); + } + mEstimatedCuesSize = estimateCuesSize(bitRate); + CHECK_GE(mEstimatedCuesSize, 8); + cues = new EbmlVoid(mEstimatedCuesSize); + } + + sp<WebmElement> elems[] = { ebml, segment, seekHead, info, tracks, cues }; + size_t nElems = sizeof(elems) / sizeof(elems[0]); + uint64_t offsets[nElems]; + uint64_t sizes[nElems]; + for (uint32_t i = 0; i < nElems; i++) { + WebmElement *e = elems[i].get(); + if (!e) { + continue; + } + + uint64_t size; + offsets[i] = ::lseek(mFd, 0, SEEK_CUR); + sizes[i] = e->mSize; + e->write(mFd, size); + } + + mSegmentOffset = offsets[1]; + mSegmentDataStart = offsets[2]; + mInfoOffset = offsets[3]; + mInfoSize = sizes[3]; + mTracksOffset = offsets[4]; + mCuesOffset = offsets[5]; + + // start threads + if (params) { + params->findInt64(kKeyTime, &mStartTimestampUs); + } + + initStream(kAudioIndex); + initStream(kVideoIndex); + + mStreams[kAudioIndex].mThread->start(); + mStreams[kVideoIndex].mThread->start(); + mSinkThread->start(); + + mStarted = true; + return OK; +} + +status_t WebmWriter::pause() { + if (mInitCheck != OK) { + return OK; + } + mPaused = true; + status_t err = OK; + for (int i = 0; i < kMaxStreams; ++i) { + if (mStreams[i].mThread == NULL) { + continue; + } + status_t status = mStreams[i].mThread->pause(); + if (status != OK) { + err = status; + } + } + return err; +} + +status_t WebmWriter::stop() { + return reset(); +} + +bool WebmWriter::reachedEOS() { + return !mSinkThread->running(); +} +} /* namespace android */ diff --git a/media/libstagefright/webm/WebmWriter.h b/media/libstagefright/webm/WebmWriter.h new file mode 100644 index 0000000..529dec8 --- /dev/null +++ b/media/libstagefright/webm/WebmWriter.h @@ -0,0 +1,130 @@ +/* + * 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 WEBMWRITER_H_ +#define WEBMWRITER_H_ + +#include "WebmConstants.h" +#include "WebmFrameThread.h" +#include "LinkedBlockingQueue.h" + +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MediaWriter.h> + +#include <utils/Errors.h> +#include <utils/Mutex.h> +#include <utils/StrongPointer.h> + +#include <stdint.h> + +using namespace webm; + +namespace android { + +class WebmWriter : public MediaWriter { +public: + WebmWriter(int fd); + WebmWriter(const char *filename); + ~WebmWriter() { reset(); } + + + status_t addSource(const sp<MediaSource> &source); + status_t start(MetaData *param = NULL); + status_t stop(); + status_t pause(); + bool reachedEOS(); + + void setStartTimeOffsetMs(int ms) { mStartTimeOffsetMs = ms; } + int32_t getStartTimeOffsetMs() const { return mStartTimeOffsetMs; } + +private: + int mFd; + status_t mInitCheck; + + uint64_t mTimeCodeScale; + int64_t mStartTimestampUs; + int32_t mStartTimeOffsetMs; + + uint64_t mSegmentOffset; + uint64_t mSegmentDataStart; + uint64_t mInfoOffset; + uint64_t mInfoSize; + uint64_t mTracksOffset; + uint64_t mCuesOffset; + + bool mPaused; + bool mStarted; + bool mIsFileSizeLimitExplicitlyRequested; + bool mIsRealTimeRecording; + bool mStreamableFile; + uint64_t mEstimatedCuesSize; + + Mutex mLock; + List<sp<WebmElement> > mCuePoints; + + enum { + kAudioIndex = 0, + kVideoIndex = 1, + kMaxStreams = 2, + }; + + struct WebmStream { + int mType; + const char *mName; + sp<WebmElement> (*mMakeTrack)(const sp<MetaData>&); + + sp<MediaSource> mSource; + sp<WebmElement> mTrackEntry; + sp<WebmFrameSourceThread> mThread; + LinkedBlockingQueue<const sp<WebmFrame> > mSink; + + WebmStream() + : mType(kInvalidType), + mName("Invalid"), + mMakeTrack(NULL) { + } + + WebmStream(int type, const char *name, sp<WebmElement> (*makeTrack)(const sp<MetaData>&)) + : mType(type), + mName(name), + mMakeTrack(makeTrack) { + } + + WebmStream &operator=(const WebmStream &other) { + mType = other.mType; + mName = other.mName; + mMakeTrack = other.mMakeTrack; + return *this; + } + }; + WebmStream mStreams[kMaxStreams]; + + sp<WebmFrameSinkThread> mSinkThread; + + size_t numTracks(); + uint64_t estimateCuesSize(int32_t bitRate); + void initStream(size_t idx); + void release(); + status_t reset(); + + static sp<WebmElement> videoTrack(const sp<MetaData>& md); + static sp<WebmElement> audioTrack(const sp<MetaData>& md); + + DISALLOW_EVIL_CONSTRUCTORS(WebmWriter); +}; + +} /* namespace android */ +#endif /* WEBMWRITER_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 @@ status_t RTPSender::onRTCPData(const sp<ABuffer> &buffer) { 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/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 @@ status_t WifiDisplaySource::PlaybackSession::setupMediaPacketizer( 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 @@ status_t RepeaterSource::stop() { ALOGV("stopping"); + status_t err = mSource->stop(); + if (mLooper != NULL) { mLooper->stop(); mLooper.clear(); @@ -92,7 +94,6 @@ status_t RepeaterSource::stop() { mBuffer = NULL; } - status_t err = mSource->stop(); ALOGV("stopped"); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 05e4018..da405e2 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -746,7 +746,7 @@ status_t WifiDisplaySource::sendM16(int32_t sessionID) { } 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; @@ -991,7 +991,7 @@ status_t WifiDisplaySource::onReceiveM4Response( } 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; @@ -1005,7 +1005,7 @@ status_t WifiDisplaySource::onReceiveM5Response( } 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); @@ -1680,7 +1680,7 @@ WifiDisplaySource::HDCPObserver::HDCPObserver( } 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/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_SHARED_LIBRARIES := \ 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 @@ void YUVImage::fastCopyRectangle420Planar( &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 @@ void YUVImage::fastCopyRectangle420SemiPlanar( int32_t yDestOffsetIncrement; int32_t uDestOffsetIncrement; - int32_t vDestOffsetIncrement; + int32_t vDestOffsetIncrement = 0; destImage.getOffsetIncrementsPerDataRow( &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement); @@ -393,9 +393,9 @@ bool YUVImage::writeToPPM(const char *filename) const { 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 d07bc99..d3e546a 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -15,6 +15,8 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libaudioflinger \ + libaudiopolicy \ + libcamera_metadata\ libcameraservice \ libmedialogservice \ libcutils \ @@ -32,6 +34,7 @@ LOCAL_C_INCLUDES := \ frameworks/av/media/libmediaplayerservice \ frameworks/av/services/medialog \ frameworks/av/services/audioflinger \ + frameworks/av/services/audiopolicy \ frameworks/av/services/camera/libcameraservice LOCAL_MODULE:= mediaserver 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/MtpProperty.cpp b/media/mtp/MtpProperty.cpp index 375ed9a..c500901 100644 --- a/media/mtp/MtpProperty.cpp +++ b/media/mtp/MtpProperty.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "MtpProperty" #include <inttypes.h> +#include <cutils/compiler.h> #include "MtpDataPacket.h" #include "MtpDebug.h" #include "MtpProperty.h" @@ -190,9 +191,9 @@ void MtpProperty::write(MtpDataPacket& packet) { if (deviceProp) writeValue(packet, mCurrentValue); } - packet.putUInt32(mGroupCode); if (!deviceProp) - packet.putUInt8(mFormFlag); + packet.putUInt32(mGroupCode); + packet.putUInt8(mFormFlag); if (mFormFlag == kFormRange) { writeValue(packet, mMinimumValue); writeValue(packet, mMaximumValue); @@ -518,8 +519,14 @@ void MtpProperty::writeValue(MtpDataPacket& packet, MtpPropertyValue& value) { MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, int& length) { length = packet.getUInt32(); - if (length == 0) + // Fail if resulting array is over 2GB. This is because the maximum array + // size may be less than SIZE_MAX on some platforms. + if ( CC_UNLIKELY( + length == 0 || + length >= INT32_MAX / sizeof(MtpPropertyValue)) ) { + length = 0; return NULL; + } MtpPropertyValue* result = new MtpPropertyValue[length]; for (int i = 0; i < length; i++) readValue(packet, result[i]); diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index df87db4..dadfb54 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -93,6 +93,7 @@ static const MtpEventCode kSupportedEventCodes[] = { MTP_EVENT_OBJECT_REMOVED, MTP_EVENT_STORE_ADDED, MTP_EVENT_STORE_REMOVED, + MTP_EVENT_DEVICE_PROP_CHANGED, }; MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp, @@ -261,6 +262,11 @@ void MtpServer::sendStoreRemoved(MtpStorageID id) { sendEvent(MTP_EVENT_STORE_REMOVED, id); } +void MtpServer::sendDevicePropertyChanged(MtpDeviceProperty property) { + ALOGV("sendDevicePropertyChanged %d\n", property); + sendEvent(MTP_EVENT_DEVICE_PROP_CHANGED, property); +} + void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) { if (mSessionOpen) { mEvent.setEventCode(code); diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h index dfa8258..b3a11e0 100644 --- a/media/mtp/MtpServer.h +++ b/media/mtp/MtpServer.h @@ -104,6 +104,7 @@ public: void sendObjectAdded(MtpObjectHandle handle); void sendObjectRemoved(MtpObjectHandle handle); + void sendDevicePropertyChanged(MtpDeviceProperty property); private: void sendStoreAdded(MtpStorageID id); diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index b895027..27e38a3 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -13,18 +13,27 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) +LOCAL_SRC_FILES := \ + ServiceUtilities.cpp + +# FIXME Move this library to frameworks/native +LOCAL_MODULE := libserviceutility + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + LOCAL_SRC_FILES:= \ AudioFlinger.cpp \ Threads.cpp \ Tracks.cpp \ Effects.cpp \ AudioMixer.cpp.arm \ - AudioPolicyService.cpp \ - ServiceUtilities.cpp \ LOCAL_SRC_FILES += StateQueue.cpp LOCAL_C_INCLUDES := \ + $(TOPDIR)frameworks/av/services/audiopolicy \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) @@ -46,12 +55,13 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libscheduling_policy \ libcpustats \ - libmedia_helper + libmedia_helper \ + libserviceutility LOCAL_MODULE:= libaudioflinger LOCAL_32_BIT_ONLY := true -LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp +LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp FastThreadState.cpp LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"' @@ -72,10 +82,21 @@ include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - test-resample.cpp \ + test-resample.cpp \ + +LOCAL_C_INCLUDES := \ + $(call include-path-for, audio-utils) + +LOCAL_STATIC_LIBRARIES := \ + libsndfile LOCAL_SHARED_LIBRARIES := \ libaudioresampler \ + libaudioutils \ + libdl \ + libcutils \ + libutils \ + liblog LOCAL_MODULE:= test-resample @@ -88,7 +109,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ AudioResampler.cpp.arm \ AudioResamplerCubic.cpp.arm \ - AudioResamplerSinc.cpp.arm + AudioResamplerSinc.cpp.arm \ + AudioResamplerDyn.cpp.arm LOCAL_SHARED_LIBRARIES := \ libcutils \ diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 26dac95..bb8c15e 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -104,6 +104,27 @@ static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200); // ---------------------------------------------------------------------------- +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; @@ -138,6 +159,7 @@ out: AudioFlinger::AudioFlinger() : BnAudioFlinger(), mPrimaryHardwareDev(NULL), + mAudioHwDevs(NULL), mHardwareStatus(AUDIO_HW_IDLE), mMasterVolume(1.0f), mMasterMute(false), @@ -152,7 +174,7 @@ AudioFlinger::AudioFlinger() char value[PROPERTY_VALUE_MAX]; bool doLog = (property_get("ro.test_harness", value, "0") > 0) && (atoi(value) == 1); if (doLog) { - mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters"); + mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters", MemoryHeapBase::READ_ONLY); } #ifdef TEE_SINK (void) property_get("ro.debuggable", value, "0"); @@ -162,12 +184,16 @@ AudioFlinger::AudioFlinger() (void) property_get("af.tee", value, "0"); teeEnabled = atoi(value); } - if (teeEnabled & 1) + // FIXME symbolic constants here + if (teeEnabled & 1) { mTeeSinkInputEnabled = true; - if (teeEnabled & 2) + } + if (teeEnabled & 2) { mTeeSinkOutputEnabled = true; - if (teeEnabled & 4) + } + if (teeEnabled & 4) { mTeeSinkTrackEnabled = true; + } #endif } @@ -210,6 +236,18 @@ AudioFlinger::~AudioFlinger() 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[] = { @@ -249,7 +287,7 @@ AudioFlinger::AudioHwDevice* AudioFlinger::findSuitableHwDev_l( 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]; @@ -271,17 +309,17 @@ void AudioFlinger::dumpClients(int fd, const Vector<String16>& args) } 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]; @@ -296,7 +334,7 @@ void AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) 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]; @@ -403,16 +441,44 @@ sp<AudioFlinger::Client> AudioFlinger::registerPid_l(pid_t pid) 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(); + } + 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(); } - return writer; +success: + mediaLogService->registerWriter(shared, size, name); + return new NBLog::Writer(size, shared); } void AudioFlinger::unregisterWriter(const sp<NBLog::Writer>& writer) @@ -424,13 +490,10 @@ void AudioFlinger::unregisterWriter(const sp<NBLog::Writer>& writer) 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 @@ -441,13 +504,12 @@ sp<IAudioTrack> AudioFlinger::createTrack( 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) { @@ -465,10 +527,31 @@ sp<IAudioTrack> AudioFlinger::createTrack( 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; } @@ -476,7 +559,6 @@ sp<IAudioTrack> AudioFlinger::createTrack( { Mutex::Autolock _l(mLock); PlaybackThread *thread = checkPlaybackThread_l(output); - PlaybackThread *effectThread = NULL; if (thread == NULL) { ALOGE("no playback thread found for output handle %d", output); lStatus = BAD_VALUE; @@ -484,24 +566,23 @@ sp<IAudioTrack> AudioFlinger::createTrack( } 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) { + PlaybackThread *effectThread = NULL; + if (sessionId != NULL && *sessionId != AUDIO_SESSION_ALLOCATE) { + lSessionId = *sessionId; // 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++) { sp<PlaybackThread> t = mPlaybackThreads.valueAt(i); if (mPlaybackThreads.keyAt(i) != output) { - uint32_t sessions = t->hasAudioSession(*sessionId); + uint32_t sessions = t->hasAudioSession(lSessionId); if (sessions & PlaybackThread::EFFECT_SESSION) { effectThread = t.get(); break; } } } - lSessionId = *sessionId; } else { // if no audio session id is provided, create one here lSessionId = nextUniqueId(); @@ -519,6 +600,7 @@ sp<IAudioTrack> AudioFlinger::createTrack( // move effect chain to this output thread if an effect on same session was waiting // for a track to be created if (lStatus == NO_ERROR && effectThread != NULL) { + // no risk of deadlock because AudioFlinger::mLock is held Mutex::Autolock _dl(thread->mLock); Mutex::Autolock _sl(effectThread->mLock); moveEffectChain_l(lSessionId, effectThread, thread, true); @@ -538,23 +620,22 @@ sp<IAudioTrack> AudioFlinger::createTrack( } } } + } - if (lStatus == NO_ERROR) { - // s for server's pid, n for normal mixer name, f for fast index - name = String8::format("s:%d;n:%d;f:%d", getpid_cached, track->name() - AudioMixer::TRACK0, - track->fastIndex()); - trackHandle = new TrackHandle(track); - } else { - // remove local strong reference to Client before deleting the Track so that the Client - // destructor is called by the TrackBase destructor with mLock held + + if (lStatus != NO_ERROR) { + // remove local strong reference to Client before deleting the Track so that the + // Client destructor is called by the TrackBase destructor with mLock held client.clear(); track.clear(); + goto Exit; } + // return handle to client + trackHandle = new TrackHandle(track); + Exit: - if (status != NULL) { - *status = lStatus; - } + *status = lStatus; return trackHandle; } @@ -796,7 +877,7 @@ status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, AutoMutex lock(mLock); PlaybackThread *thread = NULL; - if (output) { + if (output != AUDIO_IO_HANDLE_NONE) { thread = checkPlaybackThread_l(output); if (thread == NULL) { return BAD_VALUE; @@ -845,7 +926,7 @@ float AudioFlinger::streamVolume(audio_stream_type_t stream, audio_io_handle_t o AutoMutex lock(mLock); float volume; - if (output) { + if (output != AUDIO_IO_HANDLE_NONE) { PlaybackThread *thread = checkPlaybackThread_l(output); if (thread == NULL) { return 0.0f; @@ -878,8 +959,8 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& return PERMISSION_DENIED; } - // ioHandle == 0 means the parameters are global to the audio hardware interface - if (ioHandle == 0) { + // AUDIO_IO_HANDLE_NONE means the parameters are global to the audio hardware interface + if (ioHandle == AUDIO_IO_HANDLE_NONE) { Mutex::Autolock _l(mLock); status_t final_result = NO_ERROR; { @@ -961,7 +1042,7 @@ String8 AudioFlinger::getParameters(audio_io_handle_t ioHandle, const String8& k Mutex::Autolock _l(mLock); - if (ioHandle == 0) { + if (ioHandle == AUDIO_IO_HANDLE_NONE) { String8 out_s8; for (size_t i = 0; i < mAudioHwDevs.size(); i++) { @@ -1212,7 +1293,7 @@ AudioFlinger::NotificationClient::~NotificationClient() { } -void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who) +void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who __unused) { sp<NotificationClient> keep(this); mAudioFlinger->removeNotificationClient(mPid); @@ -1230,7 +1311,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( 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, @@ -1240,8 +1321,6 @@ sp<IAudioRecord> AudioFlinger::openRecord( sp<RecordHandle> recordHandle; sp<Client> client; status_t lStatus; - RecordThread *thread; - size_t inFrameCount; int lSessionId; // check calling permissions @@ -1251,16 +1330,31 @@ sp<IAudioRecord> AudioFlinger::openRecord( goto Exit; } - if (format != AUDIO_FORMAT_PCM_16_BIT) { - ALOGE("openRecord() invalid format %d", format); + // further sample rate checks are performed by createRecordTrack_l() + if (sampleRate == 0) { + ALOGE("openRecord() invalid sample rate %u", sampleRate); + lStatus = BAD_VALUE; + goto Exit; + } + + // we don't yet support anything other than 16-bit PCM + if (!(audio_is_valid_format(format) && + audio_is_linear_pcm(format) && format == AUDIO_FORMAT_PCM_16_BIT)) { + 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; } - // add client to list - { // scope for mLock + { Mutex::Autolock _l(mLock); - thread = checkRecordThread_l(input); + RecordThread *thread = checkRecordThread_l(input); if (thread == NULL) { ALOGE("openRecord() checkRecordThread_l failed"); lStatus = BAD_VALUE; @@ -1277,17 +1371,17 @@ sp<IAudioRecord> AudioFlinger::openRecord( pid_t pid = IPCThreadState::self()->getCallingPid(); 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 { + // if no audio session id is provided, create one here lSessionId = nextUniqueId(); if (sessionId != NULL) { *sessionId = lSessionId; } } - // create new record track. - // The record track uses one track in mHardwareMixerThread by convention. + ALOGV("openRecord() lSessionId: %d", lSessionId); + // TODO: the uid should be passed in as a parameter to openRecord recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask, frameCount, lSessionId, @@ -1295,6 +1389,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( flags, tid, &lStatus); LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0)); } + if (lStatus != NO_ERROR) { // remove local strong reference to Client before deleting the RecordTrack so that the // Client destructor is called by the TrackBase destructor with mLock held @@ -1303,14 +1398,11 @@ sp<IAudioRecord> AudioFlinger::openRecord( goto Exit; } - // return to handle to client + // return handle to client recordHandle = new RecordHandle(recordTrack); - lStatus = NO_ERROR; Exit: - if (status) { - *status = lStatus; - } + *status = lStatus; return recordHandle; } @@ -1451,18 +1543,15 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, audio_output_flags_t flags, const audio_offload_info_t *offloadInfo) { - PlaybackThread *thread = NULL; struct audio_config config; + memset(&config, 0, sizeof(config)); config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0; config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0; config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT; - if (offloadInfo) { + if (offloadInfo != NULL) { config.offload_info = *offloadInfo; } - audio_stream_out_t *outStream = NULL; - AudioHwDevice *outHwDev; - ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x", module, (pDevices != NULL) ? *pDevices : 0, @@ -1471,23 +1560,25 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, config.channel_mask, flags); ALOGV("openOutput(), offloadInfo %p version 0x%04x", - offloadInfo, offloadInfo == NULL ? -1 : offloadInfo->version ); + offloadInfo, offloadInfo == NULL ? -1 : offloadInfo->version); - if (pDevices == NULL || *pDevices == 0) { - return 0; + if (pDevices == NULL || *pDevices == AUDIO_DEVICE_NONE) { + return AUDIO_IO_HANDLE_NONE; } Mutex::Autolock _l(mLock); - outHwDev = findSuitableHwDev_l(module, *pDevices); - if (outHwDev == NULL) - return 0; + AudioHwDevice *outHwDev = findSuitableHwDev_l(module, *pDevices); + if (outHwDev == NULL) { + return AUDIO_IO_HANDLE_NONE; + } audio_hw_device_t *hwDevHal = outHwDev->hwDevice(); audio_io_handle_t id = nextUniqueId(); mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; + audio_stream_out_t *outStream = NULL; status_t status = hwDevHal->open_output_stream(hwDevHal, id, *pDevices, @@ -1507,6 +1598,7 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, if (status == NO_ERROR && outStream != NULL) { AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream, flags); + PlaybackThread *thread; if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { thread = new OffloadThread(this, output, id, *pDevices); ALOGV("openOutput() created offload output: ID %d thread %p", id, thread); @@ -1550,7 +1642,7 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, return id; } - return 0; + return AUDIO_IO_HANDLE_NONE; } audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1, @@ -1563,7 +1655,7 @@ audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1, if (thread1 == NULL || thread2 == NULL) { ALOGW("openDuplicateOutput() wrong output mixer type for output %d or %d", output1, output2); - return 0; + return AUDIO_IO_HANDLE_NONE; } audio_io_handle_t id = nextUniqueId(); @@ -1674,35 +1766,34 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, audio_format_t *pFormat, audio_channel_mask_t *pChannelMask) { - status_t status; - RecordThread *thread = NULL; struct audio_config config; + memset(&config, 0, sizeof(config)); config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0; config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0; config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT; uint32_t reqSamplingRate = config.sample_rate; audio_format_t reqFormat = config.format; - audio_channel_mask_t reqChannels = config.channel_mask; - audio_stream_in_t *inStream = NULL; - AudioHwDevice *inHwDev; + audio_channel_mask_t reqChannelMask = config.channel_mask; - if (pDevices == NULL || *pDevices == 0) { + if (pDevices == NULL || *pDevices == AUDIO_DEVICE_NONE) { return 0; } Mutex::Autolock _l(mLock); - inHwDev = findSuitableHwDev_l(module, *pDevices); - if (inHwDev == NULL) + AudioHwDevice *inHwDev = findSuitableHwDev_l(module, *pDevices); + if (inHwDev == NULL) { return 0; + } audio_hw_device_t *inHwHal = inHwDev->hwDevice(); audio_io_handle_t id = nextUniqueId(); - status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, + 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, @@ -1716,10 +1807,12 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, if (status == BAD_VALUE && reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT && (config.sample_rate <= 2 * reqSamplingRate) && - (popcount(config.channel_mask) <= FCC_2) && (popcount(reqChannels) <= FCC_2)) { + (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) { @@ -1737,13 +1830,13 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, 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; @@ -1778,10 +1871,8 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, // Start record thread // RecordThread requires both input and output device indication to forward to audio // pre processing modules - thread = new RecordThread(this, + RecordThread *thread = new RecordThread(this, input, - reqSamplingRate, - reqChannels, id, primaryOutputDevice_l(), *pDevices @@ -1798,7 +1889,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, *pFormat = config.format; } if (pChannelMask != NULL) { - *pChannelMask = reqChannels; + *pChannelMask = reqChannelMask; } // notify client processes of the new input creation @@ -1843,10 +1934,10 @@ status_t AudioFlinger::closeInput_nonvirtual(audio_io_handle_t input) 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(); @@ -1862,18 +1953,21 @@ int AudioFlinger::newAudioSessionId() 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; } @@ -1890,11 +1984,14 @@ void AudioFlinger::acquireAudioSessionId(int audioSession) 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); @@ -1956,7 +2053,7 @@ void AudioFlinger::purgeStaleEffects_l() { } } if (!found) { - Mutex::Autolock _l (t->mLock); + Mutex::Autolock _l(t->mLock); // remove all effects from the chain while (ec->mEffects.size()) { sp<EffectModule> effect = ec->mEffects[0]; @@ -1993,7 +2090,7 @@ AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(audio_io_handle_t uint32_t AudioFlinger::nextUniqueId() { - return android_atomic_inc(&mNextUniqueId); + return (uint32_t) android_atomic_inc(&mNextUniqueId); } AudioFlinger::PlaybackThread *AudioFlinger::primaryPlaybackThread_l() const @@ -2023,7 +2120,7 @@ sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_even int triggerSession, int listenerSession, sync_event_callback_t callBack, - void *cookie) + wp<RefBase> cookie) { Mutex::Autolock _l(mLock); @@ -2185,7 +2282,7 @@ sp<IEffect> AudioFlinger::createEffect( // return effect descriptor *pDesc = desc; - if (io == 0 && sessionId == AUDIO_SESSION_OUTPUT_MIX) { + if (io == AUDIO_IO_HANDLE_NONE && sessionId == AUDIO_SESSION_OUTPUT_MIX) { // if the output returned by getOutputForEffect() is removed before we lock the // mutex below, the call to checkPlaybackThread_l(io) below will detect it // and we will exit safely @@ -2200,7 +2297,7 @@ sp<IEffect> AudioFlinger::createEffect( // If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX // because of code checking output when entering the function. // Note: io is never 0 when creating an effect on an input - if (io == 0) { + if (io == AUDIO_IO_HANDLE_NONE) { if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) { // output must be specified by AudioPolicyManager when using session // AUDIO_SESSION_OUTPUT_STAGE @@ -2225,7 +2322,7 @@ sp<IEffect> AudioFlinger::createEffect( // If no output thread contains the requested session ID, default to // first output. The effect chain will be moved to the correct output // thread when a track with the same session ID is created - if (io == 0 && mPlaybackThreads.size()) { + if (io == AUDIO_IO_HANDLE_NONE && mPlaybackThreads.size() > 0) { io = mPlaybackThreads.keyAt(0); } ALOGV("createEffect() got io %d for effect %s", io, desc.name); @@ -2251,9 +2348,7 @@ sp<IEffect> AudioFlinger::createEffect( } Exit: - if (status != NULL) { - *status = lStatus; - } + *status = lStatus; return handle; } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 7320144..ec32edd 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,26 +102,25 @@ public: 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); + status_t *status /*non-NULL*/); virtual sp<IAudioRecord> openRecord( audio_io_handle_t input, 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, - status_t *status); + status_t *status /*non-NULL*/); virtual uint32_t sampleRate(audio_io_handle_t output) const; virtual int channelCount(audio_io_handle_t output) const; @@ -182,7 +181,7 @@ public: 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); @@ -193,9 +192,9 @@ public: 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; @@ -210,7 +209,7 @@ public: int32_t priority, audio_io_handle_t io, int sessionId, - status_t *status, + status_t *status /*non-NULL*/, int *id, int *enabled); @@ -235,8 +234,12 @@ public: 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; @@ -249,7 +252,7 @@ public: int triggerSession, int listenerSession, sync_event_callback_t callBack, - void *cookie) + wp<RefBase> cookie) : mType(type), mTriggerSession(triggerSession), mListenerSession(listenerSession), mCallback(callBack), mCookie(cookie) {} @@ -262,14 +265,14 @@ public: 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; }; @@ -277,7 +280,7 @@ public: int triggerSession, int listenerSession, sync_event_callback_t callBack, - void *cookie); + wp<RefBase> cookie); private: class AudioHwDevice; // fwd declaration for findSuitableHwDev_l @@ -451,7 +454,14 @@ private: { return mStreamTypes[stream].volume; } void audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2); - // allocate an audio_io_handle_t, session ID, or effect ID + // Allocate an audio_io_handle_t, session ID, effect ID, or audio_module_handle_t. + // They all share the same ID space, but the namespaces are actually independent + // because there are separate KeyedVectors for each kind of ID. + // The return value is uint32_t, but is cast to signed for some IDs. + // FIXME This API does not handle rollover to zero (for unsigned IDs), + // or from positive to negative (for signed IDs). + // Thus it may fail by returning an ID of the wrong sign, + // or by returning a non-unique ID. uint32_t nextUniqueId(); status_t moveEffectChain_l(int sessionId, @@ -499,7 +509,7 @@ private: 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. @@ -509,7 +519,7 @@ private: 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(); } @@ -587,7 +597,11 @@ private: DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> > mRecordThreads; DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients; + volatile int32_t mNextUniqueId; // updated by android_atomic_inc + // nextUniqueId() returns uint32_t, but this is declared int32_t + // because the atomic operations require an int32_t + audio_mode_t mMode; bool mBtNrecIsOff; @@ -634,7 +648,7 @@ public: // 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 @@ -651,6 +665,8 @@ private: #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 f92421e..a1783fe 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -58,7 +58,7 @@ AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider() 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 @@ status_t AudioMixer::DownmixerBufferProvider::getNextBuffer(AudioBufferProvider: 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 @@ void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buf // ---------------------------------------------------------------------------- -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::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr // 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 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr 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 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId) 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) { @@ -229,7 +207,7 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId) void AudioMixer::invalidateState(uint32_t mask) { - if (mask) { + if (mask != 0) { mState.needsChanged |= mask; mState.hook = process__validate; } @@ -252,7 +230,7 @@ status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackNum, audio_chann 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 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName) 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); @@ -463,8 +441,15 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) // 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"); + LOG_ALWAYS_FATAL("setParameter track: bad param %d", param); } break; @@ -489,7 +474,7 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) invalidateState(1 << name); break; default: - LOG_FATAL("bad param"); + LOG_ALWAYS_FATAL("setParameter resample: bad param %d", param); } break; @@ -537,12 +522,12 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) } break; default: - LOG_FATAL("bad param"); + LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param); } break; default: - LOG_FATAL("bad target"); + LOG_ALWAYS_FATAL("setParameter: bad target %d", target); } } @@ -560,14 +545,14 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate) // 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); } @@ -668,27 +653,29 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) 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; @@ -710,7 +697,7 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) // select the processing hooks state->hook = process__nop; - if (countActiveTracks) { + if (countActiveTracks > 0) { if (resampling) { if (!state->outputTemp) { state->outputTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; @@ -746,16 +733,15 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) // Now that the volume ramp has been done, set optimal state and // track hooks for subsequent mixer process - if (countActiveTracks) { + if (countActiveTracks > 0) { bool allMuted = true; uint32_t en = state->enabledTracks; while (en) { const int i = 31 - __builtin_clz(en); en &= ~(1<<i); track_t& t = state->tracks[i]; - if (!t.doesResample() && t.volumeRL == 0) - { - t.needs |= NEEDS_MUTE_ENABLED; + if (!t.doesResample() && t.volumeRL == 0) { + t.needs |= NEEDS_MUTE; t.hook = track__nop; } else { allMuted = false; @@ -806,8 +792,8 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram } } -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) { } @@ -883,8 +869,8 @@ void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32 } } -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); @@ -974,8 +960,8 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount 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); @@ -1065,7 +1051,7 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, 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 @@ -1084,7 +1070,8 @@ void AudioMixer::process__nop(state_t* state, int64_t pts) } e0 &= ~(e1); - memset(t1.mainBuffer, 0, bufSize); + memset(t1.mainBuffer, 0, sampleCount + * audio_bytes_per_sample(t1.mMixerFormat)); } while (e1) { @@ -1154,7 +1141,7 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) 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) { @@ -1166,7 +1153,7 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) break; } size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount; - if (inFrames) { + if (inFrames > 0) { t.hook(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux); t.frameCount -= inFrames; @@ -1192,8 +1179,18 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) } } } - 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); } @@ -1242,14 +1239,14 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts) 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 { @@ -1275,7 +1272,16 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts) } } } - 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); + } } } @@ -1316,27 +1322,46 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, } 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. - 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 { + 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; - *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); + } 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); @@ -1449,8 +1474,9 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS, int outputFrameIndex) { - if (AudioBufferProvider::kInvalidPTS == basePTS) + if (AudioBufferProvider::kInvalidPTS == basePTS) { return AudioBufferProvider::kInvalidPTS; + } return basePTS + ((outputFrameIndex * sLocalTimeFreq) / t.sampleRate); } @@ -1462,6 +1488,28 @@ int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS, { 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ private: 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 @@ private: 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/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp index e5cceb1..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 @@ private: int mX0R; }; +/*static*/ +const double AudioResampler::kPhaseMultiplier = 1L << AudioResampler::kNumPhaseBits; + bool AudioResampler::qualityIsSupported(src_quality quality) { switch (quality) { @@ -85,6 +89,9 @@ bool AudioResampler::qualityIsSupported(src_quality quality) 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 @@ void AudioResampler::init_routine() 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 @@ uint32_t AudioResampler::qualityMHz(src_quality quality) 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 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, 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 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, // 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 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, 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 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, switch (quality) { default: - case DEFAULT_QUALITY: case LOW_QUALITY: ALOGV("Create linear Resampler"); resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate); @@ -200,6 +230,12 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, 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 @@ void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, 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); @@ -339,8 +375,9 @@ void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction); out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction); Advance(&inputIndex, &phaseFraction, phaseIncrement); - if (outputIndex == outputSampleCount) + if (outputIndex == outputSampleCount) { break; + } } // process input samples @@ -402,7 +439,7 @@ void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, 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); @@ -434,8 +471,9 @@ void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, out[outputIndex++] += vl * sample; out[outputIndex++] += vr * sample; Advance(&inputIndex, &phaseFraction, phaseIncrement); - if (outputIndex == outputSampleCount) + if (outputIndex == outputSampleCount) { break; + } } // process input samples @@ -514,6 +552,16 @@ void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t 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( @@ -625,6 +673,16 @@ void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32 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" 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 @@ public: 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 @@ protected: 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 @@ protected: 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 18e59e9..8f14ff9 100644 --- a/services/audioflinger/AudioResamplerCubic.cpp +++ b/services/audioflinger/AudioResamplerCubic.cpp @@ -60,14 +60,15 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, 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) { mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer, mPTS); - if (mBuffer.raw == NULL) + if (mBuffer.raw == NULL) { return; + } // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); } int16_t *in = mBuffer.i16; @@ -97,8 +98,9 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer, calculateOutputPTS(outputIndex / 2)); - if (mBuffer.raw == NULL) + if (mBuffer.raw == NULL) { goto save_state; // ugly, but efficient + } in = mBuffer.i16; // ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount); } @@ -126,14 +128,15 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, 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) { mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer, mPTS); - if (mBuffer.raw == NULL) + if (mBuffer.raw == NULL) { return; + } // ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount); } int16_t *in = mBuffer.i16; @@ -163,8 +166,9 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer, calculateOutputPTS(outputIndex / 2)); - if (mBuffer.raw == NULL) + if (mBuffer.raw == NULL) { goto save_state; // ugly, but efficient + } // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); in = mBuffer.i16; } 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 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, 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/Effects.cpp b/services/audioflinger/Effects.cpp index 010e233..29b56db 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -116,8 +116,9 @@ status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle) continue; } // first non destroyed handle is considered in control - if (controlHandle == NULL) + if (controlHandle == NULL) { controlHandle = h; + } if (h->priority() <= priority) { break; } @@ -804,7 +805,112 @@ bool AudioFlinger::EffectModule::isOffloaded() const 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]; @@ -838,9 +944,10 @@ void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) 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,37 +958,37 @@ void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) result.append("\t\t- Input configuration:\n"); result.append("\t\t\tFrames Smp rate Channels Format Buffer\n"); - snprintf(buffer, SIZE, "\t\t\t%05zu %05d %08x %6d %p\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, + 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\t%p %05zu %05d %08x %d\n", + 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%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) { @@ -911,18 +1018,15 @@ AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect, } 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() @@ -939,6 +1043,11 @@ AudioFlinger::EffectHandle::~EffectHandle() disconnect(false); } +status_t AudioFlinger::EffectHandle::initCheck() +{ + return mClient == 0 || mCblkMemory != 0 ? OK : NO_MEMORY; +} + status_t AudioFlinger::EffectHandle::enable() { ALOGV("enable %p", this); @@ -1179,15 +1288,15 @@ status_t AudioFlinger::EffectHandle::onTransact( } -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 ); @@ -1568,33 +1677,35 @@ void AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args) 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"); - } + if (numEffects) { + 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%02zu %p %p %d\n", - mEffects.size(), - mInBuffer, - mOutBuffer, - mActiveTrackCnt); - result.append(buffer); - write(fd, result.string(), result.size()); + 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 < mEffects.size(); ++i) { - sp<EffectModule> effect = mEffects[i]; - if (effect != 0) { - effect->dump(fd, args); + for (size_t i = 0; i < numEffects; ++i) { + sp<EffectModule> effect = mEffects[i]; + if (effect != 0) { + effect->dump(fd, args); + } } - } - if (locked) { - mLock.unlock(); + 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 @@ public: const sp<IEffectClient>& effectClient, int32_t priority); virtual ~EffectHandle(); + virtual status_t initCheck(); // IEffect virtual status_t enable(); @@ -208,7 +209,7 @@ public: // 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 85d637e..adb4aca 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -212,7 +212,7 @@ bool FastMixer::threadLoop() case FastMixerState::MIX_WRITE: break; default: - LOG_FATAL("bad command %d", command); + LOG_ALWAYS_FATAL("bad command %d", command); } // there is a non-idle state available to us; did the state change? @@ -238,7 +238,7 @@ bool FastMixer::threadLoop() } } - 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; @@ -440,8 +440,9 @@ bool FastMixer::threadLoop() } int64_t pts; - if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts))) + if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts))) { pts = AudioBufferProvider::kInvalidPTS; + } // process() is CPU-bound mixer->process(pts); @@ -695,7 +696,7 @@ static int compare_uint32_t(const void *pa, const void *pb) 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 @@ -729,10 +730,10 @@ void FastMixerDumpState::dump(int fd) const 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=%zu 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, @@ -783,14 +784,20 @@ void FastMixerDumpState::dump(int fd) const 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", @@ -808,9 +815,9 @@ void FastMixerDumpState::dump(int fd) const 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); @@ -823,9 +830,9 @@ void FastMixerDumpState::dump(int fd) const // 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]; @@ -845,7 +852,7 @@ void FastMixerDumpState::dump(int fd) const mostRecent = "?"; break; } - fdprintf(fd, "%5u %6s %4u %7u %5u %7s %5zu\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/FastMixer.h b/services/audioflinger/FastMixer.h index 6158925..7aeddef 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -18,10 +18,10 @@ #define ANDROID_AUDIO_FAST_MIXER_H #include <utils/Debug.h> -#include <utils/Thread.h> extern "C" { #include "../private/bionic_futex.h" } +#include "FastThread.h" #include "StateQueue.h" #include "FastMixerState.h" @@ -29,10 +29,10 @@ namespace android { typedef StateQueue<FastMixerState> FastMixerStateQueue; -class FastMixer : public Thread { +class FastMixer : public FastThread { public: - FastMixer() : Thread(false /*canCallJava*/) { } + FastMixer() : FastThread() { } virtual ~FastMixer() { } FastMixerStateQueue* sq() { return &mSQ; } diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp index 43ff233..4631274 100644 --- a/services/audioflinger/FastMixerState.cpp +++ b/services/audioflinger/FastMixerState.cpp @@ -29,10 +29,10 @@ FastTrack::~FastTrack() { } -FastMixerState::FastMixerState() : +FastMixerState::FastMixerState() : FastThreadState(), mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0), - mFrameCount(0), mCommand(INITIAL), mColdFutexAddr(NULL), mColdGen(0), - mDumpState(NULL), mTeeSink(NULL), mNBLogWriter(NULL) + mFrameCount(0), + mDumpState(NULL), mTeeSink(NULL) { } diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index 9739fe9..10696e8 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -21,6 +21,7 @@ #include <media/ExtendedAudioBufferProvider.h> #include <media/nbaio/NBAIO.h> #include <media/nbaio/NBLog.h> +#include "FastThreadState.h" namespace android { @@ -48,7 +49,7 @@ struct FastTrack { }; // Represents a single state of the fast mixer -struct FastMixerState { +struct FastMixerState : FastThreadState { FastMixerState(); /*virtual*/ ~FastMixerState(); @@ -61,23 +62,17 @@ struct FastMixerState { NBAIO_Sink* mOutputSink; // HAL output device, must already be negotiated int mOutputSinkGen; // increment when mOutputSink is assigned size_t mFrameCount; // number of frames per fast mix buffer - enum Command { - INITIAL = 0, // used only for the initial state - HOT_IDLE = 1, // do nothing - COLD_IDLE = 2, // wait for the futex - IDLE = 3, // either HOT_IDLE or COLD_IDLE - EXIT = 4, // exit from thread + + // Extends FastThreadState::Command + static const Command // The following commands also process configuration changes, and can be "or"ed: MIX = 0x8, // mix tracks WRITE = 0x10, // write to output sink - MIX_WRITE = 0x18, // mix tracks and write to output sink - } mCommand; - int32_t* mColdFutexAddr; // for COLD_IDLE only, pointer to the associated futex - unsigned mColdGen; // increment when COLD_IDLE is requested so it's only performed once + MIX_WRITE = 0x18; // mix tracks and write to output sink + // This might be a one-time configuration rather than per-state FastMixerDumpState* mDumpState; // if non-NULL, then update dump state periodically NBAIO_Sink* mTeeSink; // if non-NULL, then duplicate write()s to this non-blocking sink - NBLog::Writer* mNBLogWriter; // non-blocking logger }; // struct FastMixerState } // namespace android diff --git a/services/audioflinger/FastThread.h b/services/audioflinger/FastThread.h new file mode 100644 index 0000000..6caf7bd --- /dev/null +++ b/services/audioflinger/FastThread.h @@ -0,0 +1,38 @@ +/* + * 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 ANDROID_AUDIO_FAST_THREAD_H +#define ANDROID_AUDIO_FAST_THREAD_H + +#include <utils/Thread.h> + +namespace android { + +// FastThread is the common abstract base class of FastMixer and FastCapture +class FastThread : public Thread { + +public: + FastThread() : Thread(false /*canCallJava*/) { } + virtual ~FastThread() { } + +protected: + virtual bool threadLoop() = 0; + +}; // class FastThread + +} // android + +#endif // ANDROID_AUDIO_FAST_THREAD_H diff --git a/media/libstagefright/include/chromium_http_stub.h b/services/audioflinger/FastThreadState.cpp index e0651a4..427ada5 100644 --- a/media/libstagefright/include/chromium_http_stub.h +++ b/services/audioflinger/FastThreadState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * 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. @@ -14,21 +14,17 @@ * limitations under the License. */ -#ifndef CHROMIUM_HTTP_STUB_H_ -#define CHROMIUM_HTTP_STUB_H_ - -#include <include/HTTPBase.h> -#include <media/stagefright/DataSource.h> +#include "FastThreadState.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); +FastThreadState::FastThreadState() : + mCommand(INITIAL), mColdFutexAddr(NULL), mColdGen(0), mNBLogWriter(NULL) +{ } + +FastThreadState::~FastThreadState() +{ } -#endif +} // namespace android diff --git a/services/audioflinger/FastThreadState.h b/services/audioflinger/FastThreadState.h new file mode 100644 index 0000000..148fb7b --- /dev/null +++ b/services/audioflinger/FastThreadState.h @@ -0,0 +1,48 @@ +/* + * 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 ANDROID_AUDIO_FAST_THREAD_STATE_H +#define ANDROID_AUDIO_FAST_THREAD_STATE_H + +#include <stdint.h> +#include <media/nbaio/NBLog.h> + +namespace android { + +// Represents a single state of a FastThread +struct FastThreadState { + FastThreadState(); + /*virtual*/ ~FastThreadState(); + + typedef uint32_t Command; + static const Command + INITIAL = 0, // used only for the initial state + HOT_IDLE = 1, // do nothing + COLD_IDLE = 2, // wait for the futex + IDLE = 3, // either HOT_IDLE or COLD_IDLE + EXIT = 4; // exit from thread + // additional values defined per subclass + Command mCommand; + + int32_t* mColdFutexAddr; // for COLD_IDLE only, pointer to the associated futex + unsigned mColdGen; // increment when COLD_IDLE is requested so it's only performed once + + NBLog::Writer* mNBLogWriter; // non-blocking logger +}; // struct FastThreadState + +} // android + +#endif // ANDROID_AUDIO_FAST_THREAD_STATE_H diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 43b77f3..e9c6834 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -34,9 +34,10 @@ public: 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); @@ -93,6 +94,10 @@ protected: bool isReady() const; void setPaused() { mState = PAUSED; } void reset(); + bool isFlushPending() const { return mFlushHwPending; } + void flushAck(); + bool isResumePending(); + void resumeAck(); bool isOutputTrack() const { return (mStreamType == AUDIO_STREAM_CNT); @@ -154,6 +159,7 @@ private: 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 { diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index 57de568..3ec9889 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -45,7 +45,10 @@ public: 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 @@ -59,5 +62,33 @@ private: // releaseBuffer() not overridden bool mOverflow; // overflow on most recent attempt to fill client buffer - AudioRecordServerProxy* mAudioRecordServerProxy; + + // 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/Threads.cpp b/services/audioflinger/Threads.cpp index cac785a..65e9eec 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -34,6 +34,7 @@ #include <audio_effects/effect_ns.h> #include <audio_effects/effect_aec.h> #include <audio_utils/primitives.h> +#include <audio_utils/format.h> // NBAIO implementations #include <media/nbaio/AudioStreamOutSink.h> @@ -104,10 +105,10 @@ static const uint32_t kMinThreadSleepTimeUs = 5000; // 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); @@ -185,7 +186,11 @@ CpuStats::CpuStats() { } -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; @@ -269,8 +274,9 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio : Thread(false /*canCallJava*/), mType(type), mAudioFlinger(audioFlinger), - // mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, and mFormat are - // set by PlaybackThread::readOutputParameters() or RecordThread::readInputParameters() + // mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, mFormat, mBufferSize + // 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), @@ -297,6 +303,17 @@ AudioFlinger::ThreadBase::~ThreadBase() } } +status_t AudioFlinger::ThreadBase::readyToRun() +{ + status_t status = initCheck(); + if (status == NO_ERROR) { + ALOGI("AudioFlinger's thread %p ready to run", this); + } else { + ALOGE("No working audio driver found."); + } + return status; +} + void AudioFlinger::ThreadBase::exit() { ALOGV("ThreadBase::exit"); @@ -369,7 +386,13 @@ void AudioFlinger::ThreadBase::sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32 void AudioFlinger::ThreadBase::processConfigEvents() { - mLock.lock(); + Mutex::Autolock _l(mLock); + processConfigEvents_l(); +} + +// post condition: mConfigEvents.isEmpty() +void AudioFlinger::ThreadBase::processConfigEvents_l() +{ while (!mConfigEvents.isEmpty()) { ALOGV("processConfigEvents() remaining events %d", mConfigEvents.size()); ConfigEvent *event = mConfigEvents[0]; @@ -377,35 +400,81 @@ void AudioFlinger::ThreadBase::processConfigEvents() // release mLock before locking AudioFlinger mLock: lock order is always // AudioFlinger then ThreadBase to avoid cross deadlock mLock.unlock(); - switch(event->type()) { - case CFG_EVENT_PRIO: { - PrioConfigEvent *prioEvent = static_cast<PrioConfigEvent *>(event); - // FIXME Need to understand why this has be done asynchronously - int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio(), - true /*asynchronous*/); - if (err != 0) { - ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; " - "error %d", - prioEvent->prio(), prioEvent->pid(), prioEvent->tid(), err); - } - } break; - case CFG_EVENT_IO: { - IoConfigEvent *ioEvent = static_cast<IoConfigEvent *>(event); - mAudioFlinger->mLock.lock(); + switch (event->type()) { + case CFG_EVENT_PRIO: { + PrioConfigEvent *prioEvent = static_cast<PrioConfigEvent *>(event); + // FIXME Need to understand why this has be done asynchronously + int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio(), + true /*asynchronous*/); + if (err != 0) { + ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d", + prioEvent->prio(), prioEvent->pid(), prioEvent->tid(), err); + } + } break; + case CFG_EVENT_IO: { + IoConfigEvent *ioEvent = static_cast<IoConfigEvent *>(event); + { + Mutex::Autolock _l(mAudioFlinger->mLock); audioConfigChanged_l(ioEvent->event(), ioEvent->param()); - mAudioFlinger->mLock.unlock(); - } break; - default: - ALOGE("processConfigEvents() unknown event type %d", event->type()); - break; + } + } break; + default: + ALOGE("processConfigEvents() unknown event type %d", event->type()); + break; } delete event; mLock.lock(); } - mLock.unlock(); } -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]; @@ -413,47 +482,43 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args) bool locked = AudioFlinger::dumpTryLock(mLock); if (!locked) { - snprintf(buffer, SIZE, "thread %p maybe dead locked\n", this); - write(fd, buffer, strlen(buffer)); - } - - 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: %zu\n", mFrameCount); - 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: %zu\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 %02zu ", i); - result.append(buffer); - result.append(mNewParameters[i]); + fdprintf(fd, "thread %p maybe dead locked\n", this); + } + + 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(); @@ -466,10 +531,11 @@ void AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>& char buffer[SIZE]; String8 result; - snprintf(buffer, SIZE, "\n- %zu 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); @@ -586,7 +652,7 @@ void AudioFlinger::ThreadBase::clearPowerManager() 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) { @@ -739,8 +805,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( int sessionId, effect_descriptor_t *desc, int *enabled, - status_t *status - ) + status_t *status) { sp<EffectModule> effect; sp<EffectHandle> handle; @@ -756,6 +821,15 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( goto Exit; } + // Reject any effect on Direct output threads for now, since the format of + // mSinkBuffer is not guaranteed to be compatible with effect processing (PCM 16 stereo). + if (mType == DIRECT) { + ALOGW("createEffect_l() Cannot add effect %s on Direct output type thread %s", + desc->name, mName); + lStatus = BAD_VALUE; + goto Exit; + } + // Allow global effects only on offloaded and mixer threads if (sessionId == AUDIO_SESSION_OUTPUT_MIX) { switch (mType) { @@ -829,7 +903,10 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( } // 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(); } @@ -850,9 +927,7 @@ Exit: handle.clear(); } - if (status != NULL) { - *status = lStatus; - } + *status = lStatus; return handle; } @@ -1001,8 +1076,18 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge audio_devices_t device, type_t type) : ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type), - mNormalFrameCount(0), mMixBuffer(NULL), - mAllocMixBuffer(NULL), mSuspended(0), mBytesWritten(0), + mNormalFrameCount(0), mSinkBuffer(NULL), + mMixerBufferEnabled(false), + mMixerBuffer(NULL), + mMixerBufferSize(0), + mMixerBufferFormat(AUDIO_FORMAT_INVALID), + mMixerBufferValid(false), + mEffectBufferEnabled(false), + mEffectBuffer(NULL), + mEffectBufferSize(0), + mEffectBufferFormat(AUDIO_FORMAT_INVALID), + mEffectBufferValid(false), + mSuspended(0), mBytesWritten(0), mActiveTracksGeneration(0), // mStreamTypes[] initialized in constructor body mOutput(output), @@ -1044,11 +1129,11 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge } } - 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 - for (audio_stream_type_t stream = (audio_stream_type_t) 0; stream < AUDIO_STREAM_CNT; + for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT; stream = (audio_stream_type_t) (stream + 1)) { mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream); mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream); @@ -1060,7 +1145,9 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge AudioFlinger::PlaybackThread::~PlaybackThread() { mAudioFlinger->unregisterWriter(mNBLogWriter); - delete [] mAllocMixBuffer; + free(mSinkBuffer); + free(mMixerBuffer); + free(mEffectBuffer); } void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args) @@ -1070,13 +1157,13 @@ void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args) 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) { @@ -1091,75 +1178,69 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar 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); + // 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", + 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"); } - - 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); + 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()); - // 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", - underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty); } 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: %zu\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, " Effect buffer: %p\n", mEffectBuffer); + fdprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); dumpBase(fd, args); } // Thread virtuals -status_t AudioFlinger::PlaybackThread::readyToRun() -{ - status_t status = initCheck(); - if (status == NO_ERROR) { - ALOGI("AudioFlinger's thread %p ready to run", this); - } else { - ALOGE("No working audio driver found."); - } - return status; -} void AudioFlinger::PlaybackThread::onFirstRef() { @@ -1182,7 +1263,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac 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, @@ -1190,6 +1271,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac int uid, status_t *status) { + size_t frameCount = *pFrameCount; sp<Track> track; status_t lStatus; @@ -1256,29 +1338,36 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac } } } + *pFrameCount = frameCount; - if (mType == DIRECT) { + switch (mType) { + + case 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; } } - } else if (mType == OFFLOAD) { + break; + + case 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 { + break; + + default: 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; @@ -1289,11 +1378,13 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac lStatus = BAD_VALUE; goto Exit; } + break; + } lStatus = initCheck(); if (lStatus != NO_ERROR) { - ALOGE("Audio driver not initialized."); + ALOGE("createTrack_l() audio driver not initialized"); goto Exit; } @@ -1325,12 +1416,14 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac channelMask, frameCount, sharedBuffer, sessionId, uid); } - if (track == 0 || track->getCblk() == NULL || track->name() < 0) { - lStatus = NO_MEMORY; + // 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) { + 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; } - mTracks.add(track); sp<EffectChain> chain = getEffectChain_l(sessionId); @@ -1352,9 +1445,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac lStatus = NO_ERROR; Exit: - if (status) { - *status = lStatus; - } + *status = lStatus; return track; } @@ -1473,9 +1564,7 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track) status = NO_ERROR; } - ALOGV("signal playback thread"); - broadcast_l(); - + onAddNewTrack_l(); return status; } @@ -1601,7 +1690,7 @@ void AudioFlinger::PlaybackThread::resetDraining(uint32_t sequence) // static int AudioFlinger::PlaybackThread::asyncCallback(stream_callback_event_t event, - void *param, + void *param __unused, void *cookie) { AudioFlinger::PlaybackThread *me = (AudioFlinger::PlaybackThread *)cookie; @@ -1620,29 +1709,30 @@ int AudioFlinger::PlaybackThread::asyncCallback(stream_callback_event_t event, 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 + // unfortunately we have no way of recovering from errors here, hence the LOG_ALWAYS_FATAL mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common); mChannelMask = mOutput->stream->common.get_channels(&mOutput->stream->common); if (!audio_is_output_channel(mChannelMask)) { - LOG_FATAL("HAL channel mask %#x not valid for output", mChannelMask); + LOG_ALWAYS_FATAL("HAL channel mask %#x not valid for output", mChannelMask); } if ((mType == MIXER || mType == DUPLICATING) && mChannelMask != AUDIO_CHANNEL_OUT_STEREO) { - LOG_FATAL("HAL channel mask %#x not supported for mixed output; " + LOG_ALWAYS_FATAL("HAL channel mask %#x not supported for mixed output; " "must be AUDIO_CHANNEL_OUT_STEREO", mChannelMask); } 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_ALWAYS_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", - mFormat); + LOG_ALWAYS_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); - mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize; + mBufferSize = mOutput->stream->common.get_buffer_size(&mOutput->stream->common); + mFrameCount = mBufferSize / mFrameSize; if (mFrameCount & 15) { ALOGW("HAL output buffer size is %u frames but AudioMixer requires multiples of 16 frames", mFrameCount); @@ -1657,12 +1747,12 @@ void AudioFlinger::PlaybackThread::readOutputParameters() } } - // 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; @@ -1680,7 +1770,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters() } } 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 @@ -1696,18 +1786,40 @@ void AudioFlinger::PlaybackThread::readOutputParameters() 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[] mAllocMixBuffer; - size_t align = (mFrameSize < sizeof(int16_t)) ? sizeof(int16_t) : mFrameSize; - mAllocMixBuffer = new int8_t[mNormalFrameCount * mFrameSize + align - 1]; - mMixBuffer = (int16_t *) ((((size_t)mAllocMixBuffer + align - 1) / align) * align); - memset(mMixBuffer, 0, mNormalFrameCount * mFrameSize); + // mSinkBuffer is the sink buffer. Size is always multiple-of-16 frames. + // Originally this was int16_t[] array, need to remove legacy implications. + free(mSinkBuffer); + mSinkBuffer = NULL; + // For sink buffer size, we use the frame size from the downstream sink to avoid problems + // with non PCM formats for compressed music, e.g. AAC, and Offload threads. + const size_t sinkBufferSize = mNormalFrameCount * mFrameSize; + (void)posix_memalign(&mSinkBuffer, 32, sinkBufferSize); + + // 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); + } + free(mEffectBuffer); + mEffectBuffer = NULL; + if (mEffectBufferEnabled) { + mEffectBufferFormat = AUDIO_FORMAT_PCM_16_BIT; // Note: Effects support 16b only + mEffectBufferSize = mNormalFrameCount * mChannelCount + * audio_bytes_per_sample(mEffectBufferFormat); + (void)posix_memalign(&mEffectBuffer, 32, mEffectBufferSize); + } // 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 @@ -1841,7 +1953,7 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks( const Vector< sp<Track> >& tracksToRemove) { size_t count = tracksToRemove.size(); - if (count) { + if (count > 0) { for (size_t i = 0 ; i < count ; i++) { const sp<Track>& track = tracksToRemove.itemAt(i); if (!track->isOutputTrack()) { @@ -1882,12 +1994,12 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() mLastWriteTime = systemTime(); mInWrite = true; ssize_t bytesWritten; + const size_t offset = mCurrentWriteLength - mBytesRemaining; // If an NBAIO sink is present, use it to write the normal mixer's submix if (mNormalSink != 0) { -#define mBitShift 2 // FIXME - size_t count = mBytesRemaining >> mBitShift; - size_t offset = (mCurrentWriteLength - mBytesRemaining) >> 1; + const size_t count = mBytesRemaining / mFrameSize; + ATRACE_BEGIN("write"); // update the setpoint when AudioFlinger::mScreenState changes uint32_t screenState = AudioFlinger::mScreenState; @@ -1899,10 +2011,10 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() (pipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2); } } - ssize_t framesWritten = mNormalSink->write(mMixBuffer + offset, count); + ssize_t framesWritten = mNormalSink->write((char *)mSinkBuffer + offset, count); ATRACE_END(); if (framesWritten > 0) { - bytesWritten = framesWritten << mBitShift; + bytesWritten = framesWritten * mFrameSize; } else { bytesWritten = framesWritten; } @@ -1917,7 +2029,7 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() // otherwise use the HAL / AudioStreamOut directly } else { // Direct output and offload threads - size_t offset = (mCurrentWriteLength - mBytesRemaining); + if (mUseAsyncWrite) { ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request"); mWriteAckSequence += 2; @@ -1928,7 +2040,7 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() // 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, - (char *)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 @@ -1967,7 +2079,7 @@ void AudioFlinger::PlaybackThread::threadLoop_exit() /* 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) @@ -1986,7 +2098,7 @@ The parameters that affect these derived values are: void AudioFlinger::PlaybackThread::cacheParameters_l() { - mixBufferSize = mNormalFrameCount * mFrameSize; + mSinkBufferSize = mNormalFrameCount * mFrameSize; activeSleepTime = activeSleepTimeUs(); idleSleepTime = idleSleepTimeUs(); } @@ -2009,13 +2121,14 @@ void AudioFlinger::PlaybackThread::invalidateTracks(audio_stream_type_t streamTy status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain) { int session = chain->sessionId(); - int16_t *buffer = mMixBuffer; + int16_t* buffer = reinterpret_cast<int16_t*>(mEffectBufferEnabled + ? mEffectBuffer : 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]; @@ -2049,7 +2162,8 @@ status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& c } chain->setInBuffer(buffer, ownsBuffer); - chain->setOutBuffer(mMixBuffer); + chain->setOutBuffer(reinterpret_cast<int16_t*>(mEffectBufferEnabled + ? mEffectBuffer : 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 @@ -2099,7 +2213,7 @@ size_t AudioFlinger::PlaybackThread::removeEffectChain_l(const sp<EffectChain>& for (size_t i = 0; i < mTracks.size(); ++i) { sp<Track> track = mTracks[i]; if (session == track->sessionId()) { - track->setMainBuffer(mMixBuffer); + track->setMainBuffer(reinterpret_cast<int16_t*>(mSinkBuffer)); chain->decTrackCnt(); } } @@ -2302,14 +2416,32 @@ bool AudioFlinger::PlaybackThread::threadLoop() // must be written to HAL threadLoop_sleepTime(); if (sleepTime == 0) { - mCurrentWriteLength = mixBufferSize; + mCurrentWriteLength = mSinkBufferSize; } } + // Either threadLoop_mix() or threadLoop_sleepTime() should have set + // mMixerBuffer with data if mMixerBufferValid is true and sleepTime == 0. + // Merge mMixerBuffer data into mEffectBuffer (if any effects are valid) + // or mSinkBuffer (if there are no effects). + // + // 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(). + // TODO use sleepTime == 0 as an additional condition. + if (mMixerBufferValid) { + void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer; + audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat; + + memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat, + mNormalFrameCount * mChannelCount); + } + mBytesRemaining = mCurrentWriteLength; if (isSuspended()) { sleepTime = suspendSleepTimeUs(); // simulate write to HAL when suspended - mBytesWritten += mixBufferSize; + mBytesWritten += mSinkBufferSize; mBytesRemaining = 0; } @@ -2330,6 +2462,16 @@ bool AudioFlinger::PlaybackThread::threadLoop() } } + // Only if the Effects buffer is enabled and there is data in the + // Effects buffer (buffer valid), we need to + // copy into the sink buffer. + // TODO use sleepTime == 0 as an additional condition. + if (mEffectBufferValid) { + //ALOGV("writing effect buffer to sink buffer format %#x", mFormat); + memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat, + mNormalFrameCount * mChannelCount); + } + // enable changes in effect chain unlockEffectChains(effectChains); @@ -2348,20 +2490,20 @@ bool AudioFlinger::PlaybackThread::threadLoop() (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; + } } } -} } else { usleep(sleepTime); @@ -2409,7 +2551,7 @@ if (mType == MIXER) { void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tracksToRemove) { size_t count = tracksToRemove.size(); - if (count) { + if (count > 0) { for (size_t i=0 ; i<count ; i++) { const sp<Track>& track = tracksToRemove.itemAt(i); mActiveTracks.remove(track); @@ -2473,7 +2615,7 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud // create an NBAIO sink for the HAL output stream, and negotiate mOutputSink = new AudioStreamOutSink(output->stream); size_t numCounterOffers = 0; - const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount)}; + const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount, mFormat)}; ssize_t index = mOutputSink->negotiate(offers, 1, NULL, numCounterOffers); ALOG_ASSERT(index == 0); @@ -2713,12 +2855,6 @@ void AudioFlinger::MixerThread::threadLoop_standby() PlaybackThread::threadLoop_standby(); } -// Empty implementation for standard mixer -// Overridden for offloaded playback -void AudioFlinger::PlaybackThread::flushOutput_l() -{ -} - bool AudioFlinger::PlaybackThread::waitingAsyncCallback_l() { return false; @@ -2750,6 +2886,12 @@ void AudioFlinger::PlaybackThread::threadLoop_standby() } } +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 @@ -2768,7 +2910,7 @@ void AudioFlinger::MixerThread::threadLoop_mix() // 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 @@ -2802,7 +2944,13 @@ void AudioFlinger::MixerThread::threadLoop_sleepTime() sleepTime = idleSleepTime; } } else if (mBytesWritten != 0 || (mMixerStatus == MIXER_TRACKS_ENABLED)) { - memset (mMixBuffer, 0, mixBufferSize); + // clear out mMixerBuffer or mSinkBuffer, to ensure buffers are cleared + // before effects processing or output. + if (mMixerBufferValid) { + memset(mMixerBuffer, 0, mMixerBufferSize); + } else { + memset(mSinkBuffer, 0, mSinkBufferSize); + } sleepTime = 0; ALOGV_IF(mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED), "anticipated start"); @@ -2849,6 +2997,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac state = sq->begin(); } + mMixerBufferValid = false; // mMixerBuffer has no valid data until appropriate tracks found. + mEffectBufferValid = false; // mEffectBuffer has no valid data until tracks found. + for (size_t i=0 ; i<count ; i++) { const sp<Track> t = mActiveTracks[i].promote(); if (t == 0) { @@ -2967,7 +3118,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac break; case TrackBase::IDLE: default: - LOG_FATAL("unexpected track state %d", track->mState); + LOG_ALWAYS_FATAL("unexpected track state %d", track->mState); } if (isActive) { @@ -2998,7 +3149,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // because we're about to decrement the last sp<> on those tracks. block = FastMixerStateQueue::BLOCK_UNTIL_ACKED; } else { - LOG_FATAL("fast track %d should have been active", j); + LOG_ALWAYS_FATAL("fast track %d should have been active", j); } tracksToRemove->add(track); // Avoids a misleading display in dumpsys @@ -3027,12 +3178,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // +1 for rounding and +1 for additional sample needed for interpolation desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1; // add frames already consumed but not yet released by the resampler - // because cblk->framesReady() will include these frames + // 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() && @@ -3048,10 +3201,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac 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) { + if (mEffectBufferEnabled) { + mEffectBufferValid = true; // Later can set directly. + } chain = getEffectChain_l(track->sessionId()); // Delegate volume control to effect in track effect chain if needed if (chain != 0) { @@ -3177,10 +3334,41 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac AudioMixer::RESAMPLE, AudioMixer::SAMPLE_RATE, (void *)(uintptr_t)reqSampleRate); - mAudioMixer->setParameter( - name, - AudioMixer::TRACK, - AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer()); + /* + * Select the appropriate output buffer for the track. + * + * Tracks with effects go into their own effects chain buffer + * and from there into either mEffectBuffer or mSinkBuffer. + * + * 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, @@ -3294,13 +3482,30 @@ track_is_ready: ; // 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. + } + // TODO - either mEffectBuffer or mSinkBuffer needs to be cleared. + if (mEffectBufferValid) { + memset(mEffectBuffer, 0, mEffectBufferSize); + } + // FIXME as a performance optimization, should remember previous zero status + memset(mSinkBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t)); } // if any fast tracks, then status is ready @@ -3358,6 +3563,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) { status = BAD_VALUE; } else { + // no need to save value, since it's constant reconfig = true; } } @@ -3365,6 +3571,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() if ((audio_channel_mask_t) value != AUDIO_CHANNEL_OUT_STEREO) { status = BAD_VALUE; } else { + // no need to save value, since it's constant reconfig = true; } } @@ -3423,7 +3630,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() 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++) { @@ -3468,9 +3675,7 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& ar 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); @@ -3688,7 +3893,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep 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; @@ -3703,7 +3908,7 @@ void AudioFlinger::DirectOutputThread::threadLoop_mix() curBuf += buffer.frameCount * mFrameSize; mActiveTrack->releaseBuffer(&buffer); } - mCurrentWriteLength = curBuf - (int8_t *)mMixBuffer; + mCurrentWriteLength = curBuf - (int8_t *)mSinkBuffer; sleepTime = 0; standbyTime = systemTime() + standbyDelay; mActiveTrack.clear(); @@ -3718,20 +3923,20 @@ void AudioFlinger::DirectOutputThread::threadLoop_sleepTime() 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) { } @@ -3767,7 +3972,7 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameters_l() keyValuePair.string()); } if (status == NO_ERROR && reconfig) { - readOutputParameters(); + readOutputParameters_l(); sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); } } @@ -3984,6 +4189,17 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr 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; + } + + if (track->mState == TrackBase::IDLE) { + ALOGW("An idle track shouldn't be in active list"); + continue; + } + if (track->isPausing()) { track->setPaused(); if (last) { @@ -4002,32 +4218,39 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr mBytesRemaining = 0; // stop writing } tracksToRemove->add(track); - } else if (track->framesReady() && track->isReady() && + } else if (track->isFlushPending()) { + track->flushAck(); + if (last) { + mFlushPending = true; + } + } else if (track->isResumePending()){ + track->resumeAck(); + if (last) { + if (mPausedBytesRemaining) { + // Need to continue write that was interrupted + mCurrentWriteLength = mPausedWriteLength; + mBytesRemaining = mPausedBytesRemaining; + mPausedBytesRemaining = 0; + } + if (mHwPaused) { + doHwResume = true; + mHwPaused = false; + // threadLoop_mix() will handle the case that we need to + // resume an interrupted write + } + // enable write to audio HAL + sleepTime = 0; + + // Do not handle new data in this iteration even if track->framesReady() + mixerStatus = MIXER_TRACKS_ENABLED; + } + } else if (track->framesReady() && track->isReady() && !track->isPaused() && !track->isTerminated() && !track->isStopping_2()) { ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer); if (track->mFillingUpStatus == Track::FS_FILLED) { track->mFillingUpStatus = Track::FS_ACTIVE; // make sure processVolume_l() will apply new volume even if 0 mLeftVolFloat = mRightVolFloat = -1.0; - if (track->mState == TrackBase::RESUMING) { - track->mState = TrackBase::ACTIVE; - if (last) { - if (mPausedBytesRemaining) { - // Need to continue write that was interrupted - mCurrentWriteLength = mPausedWriteLength; - mBytesRemaining = mPausedBytesRemaining; - mPausedBytesRemaining = 0; - } - if (mHwPaused) { - doHwResume = true; - mHwPaused = false; - // threadLoop_mix() will handle the case that we need to - // resume an interrupted write - } - // enable write to audio HAL - sleepTime = 0; - } - } } if (last) { @@ -4051,7 +4274,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr // seek when resuming. if (previousTrack->sessionId() != track->sessionId()) { previousTrack->invalidate(); - mFlushPending = true; } } } @@ -4127,9 +4349,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr // if resume is received before pause is executed. if (!mStandby && (doHwPause || (mFlushPending && !mHwPaused && (count != 0)))) { mOutput->stream->pause(mOutput->stream); - if (!doHwPause) { - doHwResume = true; - } } if (mFlushPending) { flushHw_l(); @@ -4145,11 +4364,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr return mixerStatus; } -void AudioFlinger::OffloadThread::flushOutput_l() -{ - mFlushPending = true; -} - // must be called with thread mutex locked bool AudioFlinger::OffloadThread::waitingAsyncCallback_l() { @@ -4164,15 +4378,15 @@ bool AudioFlinger::OffloadThread::waitingAsyncCallback_l() // 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; } @@ -4190,6 +4404,8 @@ void AudioFlinger::OffloadThread::flushHw_l() mBytesRemaining = 0; mPausedWriteLength = 0; mPausedBytesRemaining = 0; + mHwPaused = false; + if (mUseAsyncWrite) { // discard any pending drain or write ack by incrementing sequence mWriteAckSequence = (mWriteAckSequence + 2) & ~1; @@ -4200,6 +4416,18 @@ void AudioFlinger::OffloadThread::flushHw_l() } } +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, @@ -4224,11 +4452,11 @@ void AudioFlinger::DuplicatingThread::threadLoop_mix() 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; } @@ -4243,7 +4471,7 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime() } 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; @@ -4255,10 +4483,18 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime() ssize_t AudioFlinger::DuplicatingThread::threadLoop_write() { for (size_t i = 0; i < outputTracks.size(); i++) { - outputTracks[i]->write(mMixBuffer, writeFrames); + // We convert the duplicating thread format to AUDIO_FORMAT_PCM_16_BIT + // for delivery downstream as needed. This in-place conversion is safe as + // AUDIO_FORMAT_PCM_16_BIT is smaller than any other supported format + // (AUDIO_FORMAT_PCM_8_BIT is not allowed here). + if (mFormat != AUDIO_FORMAT_PCM_16_BIT) { + memcpy_by_audio_format(mSinkBuffer, AUDIO_FORMAT_PCM_16_BIT, + mSinkBuffer, mFormat, writeFrames * mChannelCount); + } + outputTracks[i]->write(reinterpret_cast<int16_t*>(mSinkBuffer), writeFrames); } mStandby = false; - return (ssize_t)mixBufferSize; + return (ssize_t)mSinkBufferSize; } void AudioFlinger::DuplicatingThread::threadLoop_standby() @@ -4284,10 +4520,16 @@ void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) Mutex::Autolock _l(mLock); // FIXME explain this formula size_t frameCount = (3 * mNormalFrameCount * mSampleRate) / thread->sampleRate(); + // OutputTrack is forced to AUDIO_FORMAT_PCM_16_BIT regardless of mFormat + // due to current usage case and restrictions on the AudioBufferProvider. + // Actual buffer conversion is done in threadLoop_write(). + // + // TODO: This may change in the future, depending on multichannel + // (and non int16_t*) support on AF::PlaybackThread::OutputTrack OutputTrack *outputTrack = new OutputTrack(thread, this, mSampleRate, - mFormat, + AUDIO_FORMAT_PCM_16_BIT, mChannelMask, frameCount, IPCThreadState::self()->getCallingUid()); @@ -4369,8 +4611,6 @@ void AudioFlinger::DuplicatingThread::cacheParameters_l() 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 @@ -4379,27 +4619,24 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, #endif ) : ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD), - mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL), - // mRsmpInIndex and mBufferSize 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() @@ -4407,230 +4644,393 @@ void AudioFlinger::RecordThread::onFirstRef() run(mName, PRIORITY_URGENT_AUDIO); } -status_t AudioFlinger::RecordThread::readyToRun() -{ - status_t status = initCheck(); - ALOGW_IF(status != NO_ERROR,"RecordThread %p could not initialize", this); - return status; -} - bool AudioFlinger::RecordThread::threadLoop() { - AudioBufferProvider::Buffer buffer; - sp<RecordTrack> activeTrack; - Vector< sp<EffectChain> > effectChains; - nsecs_t lastWarning = 0; inputStandBy(); + +reacquire_wakelock: + sp<RecordTrack> activeTrack; + int activeTracksGen; { Mutex::Autolock _l(mLock); - activeTrack = mActiveTrack; - acquireWakeLock_l(activeTrack != 0 ? activeTrack->uid() : -1); + 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 verify we've read at least once before evaluating how many bytes were read - bool readOnce = false; + // used to request a deferred sleep, to be executed later while mutex is unlocked + uint32_t sleepUs = 0; - // start recording - while (!exitPending()) { + // loop while there is work to do + for (;;) { + Vector< sp<EffectChain> > effectChains; - processConfigEvents(); + // sleep with mutex unlocked + 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); - checkForNewParameters_l(); - if (mActiveTrack != 0 && activeTrack != mActiveTrack) { - SortedVector<int> tmp; - tmp.add(mActiveTrack->uid()); - updateWakeLockUids_l(tmp); - } - activeTrack = mActiveTrack; - if (mActiveTrack == 0 && mConfigEvents.isEmpty()) { - standby(); - if (exitPending()) { - break; - } + processConfigEvents_l(); + // return value 'reconfig' is currently unused + bool reconfig = checkForNewParameters_l(); + // 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(mActiveTrack != 0 ? mActiveTrack->uid() : -1); - continue; + goto reacquire_wakelock; } - if (mActiveTrack != 0) { - if (mActiveTrack->isTerminated()) { - removeTrack_l(mActiveTrack); - mActiveTrack.clear(); - } else if (mActiveTrack->mState == TrackBase::PAUSING) { - standby(); - mActiveTrack.clear(); - mStartStopCond.broadcast(); - } else if (mActiveTrack->mState == TrackBase::RESUMING) { - if (mReqChannelCount != mActiveTrack->channelCount()) { - mActiveTrack.clear(); - mStartStopCond.broadcast(); - } else if (readOnce) { - // record start succeeds only if first read from audio input - // succeeds - if (mBytesRead >= 0) { - mActiveTrack->mState = TrackBase::ACTIVE; - } else { - mActiveTrack.clear(); - } - mStartStopCond.broadcast(); - } + + if (mActiveTracksGen != activeTracksGen) { + activeTracksGen = mActiveTracksGen; + SortedVector<int> tmp; + for (size_t i = 0; i < size; i++) { + tmp.add(mActiveTracks[i]->uid()); + } + updateWakeLockUids_l(tmp); + } + + bool doBroadcast = false; + for (size_t i = 0; i < size; ) { + + activeTrack = mActiveTracks[i]; + if (activeTrack->isTerminated()) { + removeTrack_l(activeTrack); + mActiveTracks.remove(activeTrack); + mActiveTracksGen++; + size--; + 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_ALWAYS_FATAL("Unexpected activeTrackState %d", activeTrackState); } + + activeTracks.add(activeTrack); + i++; + + } + 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); } - if (mActiveTrack != 0) { - if (mActiveTrack->mState != TrackBase::ACTIVE && - mActiveTrack->mState != TrackBase::RESUMING) { - unlockEffectChains(effectChains); - usleep(kRecordThreadSleepUs); - continue; - } - for (size_t i = 0; i < effectChains.size(); i ++) { - effectChains[i]->process_l(); - } + // thread mutex is now unlocked, mActiveTracks unknown, activeTracks.size() > 0 + + 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(); + } + + // 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. - buffer.frameCount = mFrameCount; - status_t status = mActiveTrack->getNextBuffer(&buffer); - if (status == NO_ERROR) { - readOnce = true; - size_t framesOut = buffer.frameCount; - if (mResampler == NULL) { + 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; + } + 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 - while (framesOut) { - size_t framesIn = mFrameCount - mRsmpInIndex; - if (framesIn) { - int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize; - int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) * - mActiveTrack->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 (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; } - if (framesOut && 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) { - if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) - { - ALOGE("Error reading audio input"); - // Force input into standby so that it tries to - // recover at next read attempt - inputStandBy(); - usleep(kRecordThreadSleepUs); - } - mRsmpInIndex = mFrameCount; - framesOut = 0; - buffer.frameCount = 0; - } -#ifdef TEE_SINK - else if (mTeeSink != 0) { - (void) mTeeSink->write(readInto, - mBytesRead >> Format_frameBitShift(mTeeSink->format())); - } -#endif + 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); + } - // 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; + // 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; } - mResampler->resample(mRsmpOutBuffer, framesOut, - this /* AudioBufferProvider* */); + + // 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 - // mActiveTrack->getNextBuffer() are 32 bit aligned which should be always true. - if (mChannelCount == 2 && mReqChannelCount == 1) { + // 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(mRsmpOutBuffer, mRsmpOutBuffer, framesOut); + 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(buffer.i16, (int16_t *)mRsmpOutBuffer, - framesOut); + downmix_to_mono_i16_from_stereo_i16(activeTrack->mSink.i16, + (int16_t *)activeTrack->mRsmpOutBuffer, framesOut); } else { - ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut); + ditherAndClamp((int32_t *)activeTrack->mSink.raw, + activeTrack->mRsmpOutBuffer, framesOut); } // now done with mRsmpOutBuffer } - if (mFramestoDrop == 0) { - mActiveTrack->releaseBuffer(&buffer); + + 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 { - if (mFramestoDrop > 0) { - mFramestoDrop -= buffer.frameCount; - if (mFramestoDrop <= 0) { - clearSyncStartEvent(); + // FIXME could do a partial drop of framesOut + if (activeTrack->mFramesToDrop > 0) { + activeTrack->mFramesToDrop -= framesOut; + if (activeTrack->mFramesToDrop <= 0) { + activeTrack->clearSyncStartEvent(); } } else { - mFramestoDrop += buffer.frameCount; - if (mFramestoDrop >= 0 || mSyncStartEvent == 0 || - mSyncStartEvent->isCancelled()) { + activeTrack->mFramesToDrop += framesOut; + if (activeTrack->mFramesToDrop >= 0 || activeTrack->mSyncStartEvent == 0 || + activeTrack->mSyncStartEvent->isCancelled()) { ALOGW("Synced record %s, session %d, trigger session %d", - (mFramestoDrop >= 0) ? "timed out" : "cancelled", - mActiveTrack->sessionId(), - (mSyncStartEvent != 0) ? mSyncStartEvent->triggerSession() : 0); - clearSyncStartEvent(); + (activeTrack->mFramesToDrop >= 0) ? "timed out" : "cancelled", + activeTrack->sessionId(), + (activeTrack->mSyncStartEvent != 0) ? + activeTrack->mSyncStartEvent->triggerSession() : 0); + activeTrack->clearSyncStartEvent(); } } } - mActiveTrack->clearOverflow(); + + if (framesOut == 0) { + break; + } } - // client isn't retrieving buffers fast enough - else { - if (!mActiveTrack->setOverflow()) { + + 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; } } - // 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. - usleep(kRecordThreadSleepUs); + break; + case OVERRUN_FALSE: + activeTrack->clearOverflow(); + break; + case OVERRUN_UNKNOWN: + break; } + } + // enable changes in effect chain unlockEffectChains(effectChains); - effectChains.clear(); + // effectChains doesn't need to be cleared, since it is cleared by destructor at scope end } - standby(); + standbyIfNotAlreadyInStandby(); { Mutex::Autolock _l(mLock); @@ -4638,7 +5038,8 @@ bool AudioFlinger::RecordThread::threadLoop() sp<RecordTrack> track = mTracks[i]; track->invalidate(); } - mActiveTrack.clear(); + mActiveTracks.clear(); + mActiveTracksGen++; mStartStopCond.broadcast(); } @@ -4648,7 +5049,7 @@ bool AudioFlinger::RecordThread::threadLoop() return false; } -void AudioFlinger::RecordThread::standby() +void AudioFlinger::RecordThread::standbyIfNotAlreadyInStandby() { if (!mStandby) { inputStandBy(); @@ -4661,26 +5062,23 @@ void AudioFlinger::RecordThread::inputStandBy() mInput->stream->common.standby(&mInput->stream->common); } -sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l( +// RecordThread::createRecordTrack_l() must be called with AudioFlinger::mLock held +sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l( const sp<AudioFlinger::Client>& client, 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("createRecordTrack_l() audio driver not initialized"); - goto Exit; - } // client expresses a preference for FAST, but we get the final say if (*flags & IAudioFlinger::TRACK_FAST) { if ( @@ -4688,21 +5086,24 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR ( (tid != -1) && ((frameCount == 0) || + // FIXME not necessarily true, should be native frame count for native SR! (frameCount >= mFrameCount)) ) && - // FIXME when record supports non-PCM data, also check for audio_is_linear_pcm(format) + // PCM data + audio_is_linear_pcm(format) && // mono or stereo ( (channelMask == AUDIO_CHANNEL_OUT_MONO) || (channelMask == AUDIO_CHANNEL_OUT_STEREO) ) && // hardware sample rate + // FIXME actually the native hardware sample rate (sampleRate == mSampleRate) && - // record thread has an associated fast recorder - hasFastRecorder() - // FIXME test that RecordThread for this fast track has a capable output HAL - // FIXME add a permission test also? + // record thread has an associated fast capture + hasFastCapture() + // fast capture does not require slots ) { - // if frameCount not specified, then it defaults to fast recorder (HAL) frame count + // if frameCount not specified, then it defaults to fast capture (HAL) frame count if (frameCount == 0) { + // FIXME wrong mFrameCount frameCount = mFrameCount * kFastTrackMultiplier; } ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d", @@ -4710,11 +5111,12 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR } else { ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%d " "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u " - "hasFastRecorder=%d tid=%d", + "hasFastCapture=%d tid=%d", frameCount, mFrameCount, format, audio_is_linear_pcm(format), - channelMask, sampleRate, mSampleRate, hasFastRecorder(), tid); + channelMask, sampleRate, mSampleRate, hasFastCapture(), tid); *flags &= ~IAudioFlinger::TRACK_FAST; + // FIXME It's not clear that we need to enforce this any more, since we have a pipe. // For compatibility with AudioRecord calculation, buffer depth is forced // to be at least 2 x the record thread frame count and cover audio hardware latency. // This is probably too conservative, but legacy application code may depend on it. @@ -4731,8 +5133,13 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR } } } + *pFrameCount = frameCount; - // FIXME use flags and tid similar to createTrack_l() + lStatus = initCheck(); + if (lStatus != NO_ERROR) { + ALOGE("createRecordTrack_l() audio driver not initialized"); + goto Exit; + } { // scope for mLock Mutex::Autolock _l(mLock); @@ -4740,9 +5147,9 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR track = new RecordTrack(this, client, sampleRate, format, channelMask, frameCount, sessionId, uid); - if (track->getCblk() == 0) { - ALOGE("createRecordTrack_l() no control block"); - lStatus = NO_MEMORY; + lStatus = track->initCheck(); + if (lStatus != NO_ERROR) { + 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; } @@ -4761,12 +5168,11 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp); } } + lStatus = NO_ERROR; Exit: - if (status) { - *status = lStatus; - } + *status = lStatus; return track; } @@ -4779,129 +5185,123 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac 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; } - 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 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(); } - 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; - } - 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] recordTrack->mState = TrackBase::PAUSING; // do not wait for mStartStopCond if exiting if (exitPending()) { return true; } + // 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)) { @@ -4932,7 +5332,7 @@ void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track) track->terminate(); track->mState = TrackBase::STOPPED; // active tracks are removed by threadLoop() - if (mActiveTrack != track) { + if (mActiveTracks.indexOf(track) < 0) { removeTrack_l(track); } } @@ -4952,104 +5352,119 @@ void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args) { - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "\nInput thread %p internals\n", this); - result.append(buffer); + fdprintf(fd, "\nInput thread %p:\n", this); - if (mActiveTrack != 0) { - snprintf(buffer, SIZE, "In index: %zu\n", mRsmpInIndex); - result.append(buffer); - snprintf(buffer, SIZE, "Buffer size: %zu 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) -{ - 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(); - usleep(kRecordThreadSleepUs); - } - buffer->raw = NULL; - buffer->frameCount = 0; - return NOT_ENOUGH_DATA; - } - mRsmpInIndex = 0; - framesReady = mFrameCount; - } - - if (framesReq > framesReady) { - framesReq = framesReady; - } - - if (mChannelCount == 1 && mReqChannelCount == 2) { - channelCount = 1; - } else { - channelCount = 2; - } - buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount; - buffer->frameCount = framesReq; +status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer( + AudioBufferProvider::Buffer* buffer, int64_t pts __unused) +{ + 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; + } + + 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; } @@ -5063,11 +5478,14 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l() AudioParameter param = AudioParameter(keyValuePair); int value; audio_format_t reqFormat = mFormat; - uint32_t reqSamplingRate = mReqSampleRate; - uint32_t reqChannelCount = 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) { @@ -5079,14 +5497,19 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l() } } if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { - reqChannelCount = popcount(value); - reconfig = true; + audio_channel_mask_t mask = (audio_channel_mask_t) value; + if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) { + status = BAD_VALUE; + } else { + channelMask = mask; + reconfig = true; + } } if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { // 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; @@ -5129,6 +5552,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l() } mAudioSource = (audio_source_t)value; } + if (status == NO_ERROR) { status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string()); @@ -5142,14 +5566,15 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l() 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 && - (reqChannelCount <= FCC_2)) { + (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); } } @@ -5179,9 +5604,9 @@ String8 AudioFlinger::RecordThread::getParameters(const String8& keys) 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: @@ -5201,53 +5626,35 @@ void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) { 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]; - - 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; - } + // 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]; - } - 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 a2fb874..5617c0c 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -36,6 +36,8 @@ public: audio_devices_t outDevice, audio_devices_t inDevice, type_t type); virtual ~ThreadBase(); + virtual status_t readyToRun(); + void dumpBase(int fd, const Vector<String16>& args); void dumpEffectChains(int fd, const Vector<String16>& args); @@ -63,7 +65,7 @@ public: 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; } @@ -141,6 +143,7 @@ public: void sendIoConfigEvent_l(int event, int param = 0); void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio); void processConfigEvents(); + void processConfigEvents_l(); // see note at declaration of mStandby, mOutDevice and mInDevice bool standby() const { return mStandby; } @@ -156,7 +159,7 @@ public: int sessionId, effect_descriptor_t *desc, int *enabled, - status_t *status); + status_t *status /*non-NULL*/); void disconnectEffect(const sp< EffectModule>& effect, EffectHandle *handle, bool unpinIfLast); @@ -198,13 +201,13 @@ public: // 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 @@ -267,14 +270,15 @@ protected: 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; uint32_t mChannelCount; size_t mFrameSize; audio_format_t mFormat; + size_t mBufferSize; // HAL buffer size for read() or write() // Parameter sequence by client: binder thread calling setParameters(): // 1. Lock mLock @@ -303,12 +307,12 @@ protected: 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) @@ -358,7 +362,6 @@ public: void dump(int fd, const Vector<String16>& args); // Thread virtuals - virtual status_t readyToRun(); virtual bool threadLoop(); // RefBase @@ -391,7 +394,7 @@ protected: virtual bool waitingAsyncCallback(); virtual bool waitingAsyncCallback_l(); virtual bool shouldStandby_l(); - + virtual void onAddNewTrack_l(); // ThreadBase virtuals virtual void preExit(); @@ -419,13 +422,13 @@ public: 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); + status_t *status /*non-NULL*/); AudioStreamOut* getOutput() const; AudioStreamOut* clearOutput(); @@ -447,7 +450,11 @@ public: virtual String8 getParameters(const String8& keys); virtual void audioConfigChanged_l(int event, int param = 0); status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames); - int16_t *mixBuffer() const { return mMixBuffer; }; + // FIXME rename mixBuffer() to sinkBuffer() and remove int16_t* dependency. + // Consider also removing and passing an explicit mMainBuffer initialization + // parameter to AF::PlaybackThread::Track::Track(). + int16_t *mixBuffer() const { + return reinterpret_cast<int16_t *>(mSinkBuffer); }; virtual void detachAuxEffect_l(int effectId); status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, @@ -475,11 +482,68 @@ public: 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 - int8_t* mAllocMixBuffer; // mixer buffer allocation address + void* mSinkBuffer; // frame size aligned sink buffer + + // TODO: + // Rearrange the buffer info into a struct/class with + // clear, copy, construction, destruction methods. + // + // mSinkBuffer also has associated with it: + // + // mSinkBufferSize: Sink Buffer Size + // mFormat: Sink Buffer Format + + // 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; + + // Effects Buffer (mEffectsBuffer*) + // + // In the case of effects data, which is not in the sink format, + // it is required to accumulate in a different buffer before data conversion + // to the sink buffer. + + // Set to "true" to enable the Effects Buffer otherwise effects output goes to sink buffer. + bool mEffectBufferEnabled; + + // 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* mEffectBuffer; + + // Size of mEffectsBuffer in bytes: mNormalFrameCount * #channels * sampsize. + size_t mEffectBufferSize; + + // The audio format of mEffectsBuffer. Set to AUDIO_FORMAT_PCM_16_BIT only. + audio_format_t mEffectBufferFormat; + + // An internal flag set to true by MixerThread::prepareTracks_l() + // when mEffectsBuffer contains valid data after mixing. + // + // When this is set, all mixer data is routed into the effects buffer + // for any processing (including output processing). + bool mEffectBufferValid; // 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 @@ -539,7 +603,7 @@ private: 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); @@ -558,7 +622,7 @@ private: // 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; @@ -623,13 +687,12 @@ private: 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: @@ -748,11 +811,11 @@ 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(); @@ -838,17 +901,28 @@ public: // 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 @@ -867,23 +941,23 @@ public: // Thread virtuals virtual bool threadLoop(); - virtual status_t readyToRun(); // RefBase virtual void onFirstRef(); virtual status_t initCheck() const { return (mInput == NULL) ? NO_INIT : NO_ERROR; } + sp<AudioFlinger::RecordThread::RecordTrack> createRecordTrack_l( const sp<AudioFlinger::Client>& client, 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); + status_t *status /*non-NULL*/); status_t start(RecordTrack* recordTrack, AudioSystem::sync_event_t event, @@ -897,15 +971,12 @@ public: 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); @@ -920,44 +991,33 @@ public: 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; } + bool hasFastCapture() 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(); + 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; - size_t mBufferSize; // stream buffer size for read() - 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 cd201d9..58705c4 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -34,7 +34,9 @@ public: RESUMING, ACTIVE, PAUSING, - PAUSED + PAUSED, + STARTING_1, // for RecordTrack only + STARTING_2, // for RecordTrack only }; TrackBase(ThreadBase *thread, @@ -48,6 +50,7 @@ public: int uid, bool isOut); virtual ~TrackBase(); + virtual status_t initCheck() const { return getCblk() != 0 ? NO_ERROR : NO_MEMORY; } virtual status_t start(AudioSystem::sync_event_t event, int triggerSession) = 0; @@ -78,15 +81,6 @@ protected: virtual uint32_t sampleRate() const { return mSampleRate; } - // Return a pointer to the start of a contiguous slice of the track buffer. - // Parameter 'offset' is the requested start position, expressed in - // monotonically increasing frame units relative to the track epoch. - // Parameter 'frames' is the requested length, also in frame units. - // Always returns non-NULL. It is the caller's responsibility to - // verify that this will be successful; the result of calling this - // function with invalid 'offset' or 'frames' is undefined. - void* getBuffer(uint32_t offset, uint32_t frames) const; - bool isStopped() const { return (mState == STOPPED || mState == FLUSHED); } diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 813ec8a..2cf10e2 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -116,12 +116,11 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( 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 { @@ -134,7 +133,6 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( 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); @@ -148,7 +146,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( #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}; @@ -275,6 +273,11 @@ status_t AudioFlinger::TrackHandle::queueTimedBuffer(const sp<IMemory>& buffer, 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); @@ -344,41 +347,42 @@ AudioFlinger::PlaybackThread::Track::Track( mCachedVolume(1.0), mIsInvalid(false), mAudioTrackServerProxy(NULL), - mResumeToStopping(false) + mResumeToStopping(false), + mFlushHwPending(false) { - if (mCblk != NULL) { - if (sharedBuffer == 0) { - mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount, - mFrameSize); - } else { - mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount, - mFrameSize); - } - mServerProxy = mAudioTrackServerProxy; - // to avoid leaking a track name, do not allocate one unless there is an mCblk - mName = thread->getTrackName_l(channelMask, sessionId); - if (mName < 0) { - ALOGE("no more track names available"); - return; - } - // only allocate a fast track index if we were able to allocate a normal track name - if (flags & IAudioFlinger::TRACK_FAST) { - mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads(); - ALOG_ASSERT(thread->mFastTrackAvailMask != 0); - int i = __builtin_ctz(thread->mFastTrackAvailMask); - ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks); - // FIXME This is too eager. We allocate a fast track index before the - // fast track becomes active. Since fast tracks are a scarce resource, - // this means we are potentially denying other more important fast tracks from - // being created. It would be better to allocate the index dynamically. - mFastIndex = i; - // Read the initial underruns because this field is never cleared by the fast mixer - mObservedUnderruns = thread->getFastTrackUnderruns(i); - thread->mFastTrackAvailMask &= ~(1 << i); - } + if (mCblk == NULL) { + return; + } + + if (sharedBuffer == 0) { + mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount, + mFrameSize); + } else { + mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount, + mFrameSize); + } + mServerProxy = mAudioTrackServerProxy; + + mName = thread->getTrackName_l(channelMask, sessionId); + if (mName < 0) { + ALOGE("no more track names available"); + return; + } + // only allocate a fast track index if we were able to allocate a normal track name + if (flags & IAudioFlinger::TRACK_FAST) { + mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads(); + ALOG_ASSERT(thread->mFastTrackAvailMask != 0); + int i = __builtin_ctz(thread->mFastTrackAvailMask); + ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks); + // FIXME This is too eager. We allocate a fast track index before the + // fast track becomes active. Since fast tracks are a scarce resource, + // this means we are potentially denying other more important fast tracks from + // being created. It would be better to allocate the index dynamically. + mFastIndex = i; + // Read the initial underruns because this field is never cleared by the fast mixer + mObservedUnderruns = thread->getFastTrackUnderruns(i); + thread->mFastTrackAvailMask &= ~(1 << i); } - ALOGV("Track constructor name %d, calling pid %d", mName, - IPCThreadState::self()->getCallingPid()); } AudioFlinger::PlaybackThread::Track::~Track() @@ -396,6 +400,15 @@ AudioFlinger::PlaybackThread::Track::~Track() } } +status_t AudioFlinger::PlaybackThread::Track::initCheck() const +{ + status_t status = TrackBase::initCheck(); + if (status == NO_ERROR && mName < 0) { + status = NO_MEMORY; + } + return status; +} + void AudioFlinger::PlaybackThread::Track::destroy() { // NOTE: destroyTrack_l() can remove a strong reference to this Track @@ -422,17 +435,19 @@ void AudioFlinger::PlaybackThread::Track::destroy() /*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; @@ -487,8 +502,9 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) nowInUnderrun = '?'; break; } - snprintf(&buffer[7], size-7, " %6u %4u %08X %08X %7u %6zu %1c %1d %5u %5.2g %5.2g " + 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, @@ -514,7 +530,7 @@ uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const { // 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; @@ -551,7 +567,14 @@ size_t AudioFlinger::PlaybackThread::Track::framesReleased() const // 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() || isStopping()) { + if (mFillingUpStatus != FS_FILLING || isStopped() || isPausing()) { + return true; + } + + if (isStopping()) { + if (framesReady() > 0) { + mFillingUpStatus = FS_FILLED; + } return true; } @@ -564,8 +587,8 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const { 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", @@ -588,7 +611,10 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev // here the track could be either new, or restarted // in both cases "unstop" the track - if (state == PAUSED) { + // initial state-stopping. next state-pausing. + // What if resume is called ? + + if (state == PAUSED || state == PAUSING) { if (mResumeToStopping) { // happened we need to resume to STOPPING_1 mState = TrackBase::STOPPING_1; @@ -719,6 +745,7 @@ void AudioFlinger::PlaybackThread::Track::flush() mRetryCount = PlaybackThread::kMaxTrackRetriesOffload; } + mFlushHwPending = true; mResumeToStopping = false; } else { if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED && @@ -739,11 +766,19 @@ void AudioFlinger::PlaybackThread::Track::flush() // 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 @@ -966,6 +1001,33 @@ void AudioFlinger::PlaybackThread::Track::signal() } } +//To be called with thread lock held +bool AudioFlinger::PlaybackThread::Track::isResumePending() { + + if (mState == RESUMING) + return true; + /* Resume is pending if track was stopping before pause was called */ + if (mState == STOPPING_1 && + mResumeToStopping) + return true; + + return false; +} + +//To be called with thread lock held +void AudioFlinger::PlaybackThread::Track::resumeAck() { + + + if (mState == RESUMING) + mState = ACTIVE; + + // Other possibility of pending resume is stopping_1 state + // Do not update the state from stopping as this prevents + // drain being called. + if (mState == STOPPING_1) { + mResumeToStopping = false; + } +} // ---------------------------------------------------------------------------- sp<AudioFlinger::PlaybackThread::TimedTrack> @@ -979,7 +1041,8 @@ AudioFlinger::PlaybackThread::TimedTrack::create( size_t frameCount, const sp<IMemory>& sharedBuffer, int sessionId, - int uid) { + int uid) +{ if (!client->reserveTimedTrack()) return 0; @@ -1045,15 +1108,14 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::allocateTimedBuffer( mTimedMemoryDealer = new MemoryDealer(kTimedBufferHeapSize, "AudioFlingerTimed"); - if (mTimedMemoryDealer == NULL) + if (mTimedMemoryDealer == NULL) { return NO_MEMORY; + } } 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; @@ -1152,7 +1214,7 @@ void AudioFlinger::PlaybackThread::TimedTrack::trimTimedBufferQueueHead_l( 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(); @@ -1463,7 +1525,7 @@ void AudioFlinger::PlaybackThread::TimedTrack::releaseBuffer( mTrimQueueHeadOnRelease = false; } } else { - LOG_FATAL("TimedTrack::releaseBuffer of non-silence buffer with no" + LOG_ALWAYS_FATAL("TimedTrack::releaseBuffer of non-silence buffer with no" " buffers in the timed buffer queue"); } @@ -1504,9 +1566,9 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( 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); @@ -1748,7 +1810,7 @@ status_t AudioFlinger::RecordHandle::onTransact( // ---------------------------------------------------------------------------- -// RecordTrack constructor must be called with AudioFlinger::mLock held +// RecordTrack constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held AudioFlinger::RecordThread::RecordTrack::RecordTrack( RecordThread *thread, const sp<Client>& client, @@ -1760,24 +1822,40 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( int uid) : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/), - mOverflow(false) + 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) { - mAudioRecordServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount, - mFrameSize); - mServerProxy = mAudioRecordServerProxy; + if (mCblk == NULL) { + return; + } + + 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; @@ -1845,19 +1923,45 @@ void AudioFlinger::RecordThread::RecordTrack::invalidate() /*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 %6zu\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 @@ int main(int argc, char* argv[]) { 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 @@ int main(int argc, char* argv[]) { 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 @@ int main(int argc, char* argv[]) { 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; - } - - 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; + SF_INFO info; + info.format = 0; + SNDFILE *sf = sf_open(file_in, SFM_READ, &info); + if (sf == NULL) { + perror(file_in); + return EXIT_FAILURE; } + 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 @@ int main(int argc, char* argv[]) { 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 @@ int main(int argc, char* argv[]) { // ---------------------------------------------------------- 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; + } + void reset() { + mNextFrame = 0; } - } provider(input_vaddr, input_size, channels); + } 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/audiopolicy/Android.mk b/services/audiopolicy/Android.mk new file mode 100644 index 0000000..f270bfc --- /dev/null +++ b/services/audiopolicy/Android.mk @@ -0,0 +1,44 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyService.cpp + +USE_LEGACY_AUDIO_POLICY = 1 +ifeq ($(USE_LEGACY_AUDIO_POLICY), 1) +LOCAL_SRC_FILES += \ + AudioPolicyInterfaceImplLegacy.cpp \ + AudioPolicyClientImplLegacy.cpp + + LOCAL_CFLAGS += -DUSE_LEGACY_AUDIO_POLICY +else +LOCAL_SRC_FILES += \ + AudioPolicyInterfaceImpl.cpp \ + AudioPolicyClientImpl.cpp \ + AudioPolicyManager.cpp +endif + +LOCAL_C_INCLUDES := \ + $(TOPDIR)frameworks/av/services/audioflinger \ + $(call include-path-for, audio-effects) \ + $(call include-path-for, audio-utils) + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libbinder \ + libmedia \ + libhardware \ + libhardware_legacy + +LOCAL_STATIC_LIBRARIES := \ + libmedia_helper \ + libserviceutility + +LOCAL_MODULE:= libaudiopolicy + +LOCAL_CFLAGS += -fvisibility=hidden + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/audiopolicy/AudioPolicyClientImpl.cpp b/services/audiopolicy/AudioPolicyClientImpl.cpp new file mode 100644 index 0000000..44c47c3 --- /dev/null +++ b/services/audiopolicy/AudioPolicyClientImpl.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioPolicyClientImpl" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include "AudioPolicyService.h" + +namespace android { + +/* implementation of the client interface from the policy manager */ + +audio_module_handle_t AudioPolicyService::AudioPolicyClient::loadHwModule(const char *name) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + + return af->loadHwModule(name); +} + +audio_io_handle_t AudioPolicyService::AudioPolicyClient::openOutput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + return af->openOutput(module, pDevices, pSamplingRate, pFormat, pChannelMask, + pLatencyMs, flags, offloadInfo); +} + +audio_io_handle_t AudioPolicyService::AudioPolicyClient::openDuplicateOutput( + audio_io_handle_t output1, + audio_io_handle_t output2) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + return af->openDuplicateOutput(output1, output2); +} + +status_t AudioPolicyService::AudioPolicyClient::closeOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeOutput(output); +} + +status_t AudioPolicyService::AudioPolicyClient::suspendOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->suspendOutput(output); +} + +status_t AudioPolicyService::AudioPolicyClient::restoreOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->restoreOutput(output); +} + +audio_io_handle_t AudioPolicyService::AudioPolicyClient::openInput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + + return af->openInput(module, pDevices, pSamplingRate, pFormat, pChannelMask); +} + +status_t AudioPolicyService::AudioPolicyClient::closeInput(audio_io_handle_t input) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeInput(input); +} + +status_t AudioPolicyService::AudioPolicyClient::setStreamVolume(audio_stream_type_t stream, + float volume, audio_io_handle_t output, + int delay_ms) +{ + return mAudioPolicyService->setStreamVolume(stream, volume, output, + delay_ms); +} + +status_t AudioPolicyService::AudioPolicyClient::invalidateStream(audio_stream_type_t stream) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->invalidateStream(stream); +} + +void AudioPolicyService::AudioPolicyClient::setParameters(audio_io_handle_t io_handle, + const String8& keyValuePairs, + int delay_ms) +{ + mAudioPolicyService->setParameters(io_handle, keyValuePairs.string(), delay_ms); +} + +String8 AudioPolicyService::AudioPolicyClient::getParameters(audio_io_handle_t io_handle, + const String8& keys) +{ + String8 result = AudioSystem::getParameters(io_handle, keys); + return result; +} + +status_t AudioPolicyService::AudioPolicyClient::startTone(audio_policy_tone_t tone, + audio_stream_type_t stream) +{ + return mAudioPolicyService->startTone(tone, stream); +} + +status_t AudioPolicyService::AudioPolicyClient::stopTone() +{ + return mAudioPolicyService->stopTone(); +} + +status_t AudioPolicyService::AudioPolicyClient::setVoiceVolume(float volume, int delay_ms) +{ + return mAudioPolicyService->setVoiceVolume(volume, delay_ms); +} + +status_t AudioPolicyService::AudioPolicyClient::moveEffects(int session, + audio_io_handle_t src_output, + audio_io_handle_t dst_output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->moveEffects(session, src_output, dst_output); +} + + + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyClientImplLegacy.cpp b/services/audiopolicy/AudioPolicyClientImplLegacy.cpp new file mode 100644 index 0000000..53f3e2d --- /dev/null +++ b/services/audiopolicy/AudioPolicyClientImplLegacy.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioPolicyService" +//#define LOG_NDEBUG 0 + +#include "Configuration.h" +#undef __STRICT_ANSI__ +#define __STDINT_LIMITS +#define __STDC_LIMIT_MACROS +#include <stdint.h> + +#include <sys/time.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> +#include <cutils/properties.h> +#include <binder/IPCThreadState.h> +#include <utils/String16.h> +#include <utils/threads.h> +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" +#include <hardware_legacy/power.h> +#include <media/AudioEffect.h> +#include <media/EffectsFactoryApi.h> +//#include <media/IAudioFlinger.h> + +#include <hardware/hardware.h> +#include <system/audio.h> +#include <system/audio_policy.h> +#include <hardware/audio_policy.h> +#include <audio_effects/audio_effects_conf.h> +#include <media/AudioParameter.h> + + +namespace android { + +/* implementation of the interface to the policy manager */ +extern "C" { + +audio_module_handle_t aps_load_hw_module(void *service __unused, + const char *name) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + + return af->loadHwModule(name); +} + +// deprecated: replaced by aps_open_output_on_module() +audio_io_handle_t aps_open_output(void *service __unused, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + + return af->openOutput((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask, + pLatencyMs, flags); +} + +audio_io_handle_t aps_open_output_on_module(void *service __unused, + audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + return af->openOutput(module, pDevices, pSamplingRate, pFormat, pChannelMask, + pLatencyMs, flags, offloadInfo); +} + +audio_io_handle_t aps_open_dup_output(void *service __unused, + audio_io_handle_t output1, + audio_io_handle_t output2) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + return af->openDuplicateOutput(output1, output2); +} + +int aps_close_output(void *service __unused, audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeOutput(output); +} + +int aps_suspend_output(void *service __unused, audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->suspendOutput(output); +} + +int aps_restore_output(void *service __unused, audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->restoreOutput(output); +} + +// deprecated: replaced by aps_open_input_on_module(), and acoustics parameter is ignored +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 __unused) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + + return af->openInput((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask); +} + +audio_io_handle_t aps_open_input_on_module(void *service __unused, + audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + + return af->openInput(module, pDevices, pSamplingRate, pFormat, pChannelMask); +} + +int aps_close_input(void *service __unused, audio_io_handle_t input) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeInput(input); +} + +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->invalidateStream(stream); +} + +int aps_move_effects(void *service __unused, int session, + audio_io_handle_t src_output, + audio_io_handle_t dst_output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->moveEffects(session, src_output, dst_output); +} + +char * aps_get_parameters(void *service __unused, audio_io_handle_t io_handle, + const char *keys) +{ + String8 result = AudioSystem::getParameters(io_handle, String8(keys)); + return strdup(result.string()); +} + +void aps_set_parameters(void *service, audio_io_handle_t io_handle, + const char *kv_pairs, int delay_ms) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + audioPolicyService->setParameters(io_handle, kv_pairs, delay_ms); +} + +int aps_set_stream_volume(void *service, audio_stream_type_t stream, + float volume, audio_io_handle_t output, + int delay_ms) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->setStreamVolume(stream, volume, output, + delay_ms); +} + +int aps_start_tone(void *service, audio_policy_tone_t tone, + audio_stream_type_t stream) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->startTone(tone, stream); +} + +int aps_stop_tone(void *service) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->stopTone(); +} + +int aps_set_voice_volume(void *service, float volume, int delay_ms) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->setVoiceVolume(volume, delay_ms); +} + +}; // extern "C" + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h new file mode 100644 index 0000000..66260e3 --- /dev/null +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_AUDIOPOLICY_INTERFACE_H +#define ANDROID_AUDIOPOLICY_INTERFACE_H + +#include <media/AudioSystem.h> +#include <utils/String8.h> + +#include <hardware/audio_policy.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +// The AudioPolicyInterface and AudioPolicyClientInterface classes define the communication interfaces +// between the platform specific audio policy manager and Android generic audio policy manager. +// The platform specific audio policy manager must implement methods of the AudioPolicyInterface class. +// This implementation makes use of the AudioPolicyClientInterface to control the activity and +// configuration of audio input and output streams. +// +// The platform specific audio policy manager is in charge of the audio routing and volume control +// policies for a given platform. +// The main roles of this module are: +// - keep track of current system state (removable device connections, phone state, user requests...). +// System state changes and user actions are notified to audio policy manager with methods of the AudioPolicyInterface. +// - process getOutput() queries received when AudioTrack objects are created: Those queries +// return a handler on an output that has been selected, configured and opened by the audio policy manager and that +// must be used by the AudioTrack when registering to the AudioFlinger with the createTrack() method. +// When the AudioTrack object is released, a putOutput() query is received and the audio policy manager can decide +// to close or reconfigure the output depending on other streams using this output and current system state. +// - similarly process getInput() and putInput() queries received from AudioRecord objects and configure audio inputs. +// - process volume control requests: the stream volume is converted from an index value (received from UI) to a float value +// applicable to each output as a function of platform specific settings and current output route (destination device). It +// also make sure that streams are not muted if not allowed (e.g. camera shutter sound in some countries). +// +// The platform specific audio policy manager is provided as a shared library by platform vendors (as for libaudio.so) +// and is linked with libaudioflinger.so + + +// Audio Policy Manager Interface +class AudioPolicyInterface +{ + +public: + virtual ~AudioPolicyInterface() {} + // + // configuration functions + // + + // indicate a change in device connection status + virtual status_t setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) = 0; + // retrieve a device connection status + virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, + const char *device_address) = 0; + // indicate a change in phone state. Valid phones states are defined by audio_mode_t + virtual void setPhoneState(audio_mode_t state) = 0; + // force using a specific device category for the specified usage + virtual void setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) = 0; + // retrieve current device category forced for a given usage + virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) = 0; + // set a system property (e.g. camera sound always audible) + virtual void setSystemProperty(const char* property, const char* value) = 0; + // check proper initialization + virtual status_t initCheck() = 0; + + // + // Audio routing query functions + // + + // request an output appropriate for playback of the supplied stream type and parameters + virtual audio_io_handle_t getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) = 0; + // indicates to the audio policy manager that the output starts being used by corresponding stream. + virtual status_t startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0) = 0; + // indicates to the audio policy manager that the output stops being used by corresponding stream. + virtual status_t stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0) = 0; + // releases the output. + virtual void releaseOutput(audio_io_handle_t output) = 0; + + // request an input appropriate for record from the supplied device with supplied parameters. + virtual audio_io_handle_t getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_in_acoustics_t acoustics) = 0; + // indicates to the audio policy manager that the input starts being used. + virtual status_t startInput(audio_io_handle_t input) = 0; + // indicates to the audio policy manager that the input stops being used. + virtual status_t stopInput(audio_io_handle_t input) = 0; + // releases the input. + virtual void releaseInput(audio_io_handle_t input) = 0; + + // + // volume control functions + // + + // initialises stream volume conversion parameters by specifying volume index range. + virtual void initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax) = 0; + + // sets the new stream volume at a level corresponding to the supplied index for the + // supplied device. By convention, specifying AUDIO_DEVICE_OUT_DEFAULT means + // setting volume for all devices + virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) = 0; + + // retrieve current volume index for the specified stream and the + // specified device. By convention, specifying AUDIO_DEVICE_OUT_DEFAULT means + // querying the volume of the active device. + virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device) = 0; + + // return the strategy corresponding to a given stream type + virtual uint32_t getStrategyForStream(audio_stream_type_t stream) = 0; + + // return the enabled output devices for the given stream type + virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream) = 0; + + // Audio effect management + virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc) = 0; + virtual status_t registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) = 0; + virtual status_t unregisterEffect(int id) = 0; + virtual status_t setEffectEnabled(int id, bool enabled) = 0; + + virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const = 0; + virtual bool isStreamActiveRemotely(audio_stream_type_t stream, + uint32_t inPastMs = 0) const = 0; + virtual bool isSourceActive(audio_source_t source) const = 0; + + //dump state + virtual status_t dump(int fd) = 0; + + virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo) = 0; +}; + + +// Audio Policy client Interface +class AudioPolicyClientInterface +{ +public: + virtual ~AudioPolicyClientInterface() {} + + // + // Audio HW module functions + // + + // loads a HW module. + virtual audio_module_handle_t loadHwModule(const char *name) = 0; + + // + // Audio output Control functions + // + + // opens an audio output with the requested parameters. The parameter values can indicate to use the default values + // in case the audio policy manager has no specific requirements for the output being opened. + // When the function returns, the parameter values reflect the actual values used by the audio hardware output stream. + // The audio policy manager can check if the proposed parameters are suitable or not and act accordingly. + virtual audio_io_handle_t openOutput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo = NULL) = 0; + // creates a special output that is duplicated to the two outputs passed as arguments. The duplication is performed by + // a special mixer thread in the AudioFlinger. + virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2) = 0; + // closes the output stream + virtual status_t closeOutput(audio_io_handle_t output) = 0; + // suspends the output. When an output is suspended, the corresponding audio hardware output stream is placed in + // standby and the AudioTracks attached to the mixer thread are still processed but the output mix is discarded. + virtual status_t suspendOutput(audio_io_handle_t output) = 0; + // restores a suspended output. + virtual status_t restoreOutput(audio_io_handle_t output) = 0; + + // + // Audio input Control functions + // + + // opens an audio input + virtual audio_io_handle_t openInput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask) = 0; + // closes an audio input + virtual status_t closeInput(audio_io_handle_t input) = 0; + // + // misc control functions + // + + // set a stream volume for a particular output. For the same user setting, a given stream type can have different volumes + // for each output (destination device) it is attached to. + virtual status_t setStreamVolume(audio_stream_type_t stream, float volume, audio_io_handle_t output, int delayMs = 0) = 0; + + // invalidate a stream type, causing a reroute to an unspecified new output + virtual status_t invalidateStream(audio_stream_type_t stream) = 0; + + // function enabling to send proprietary informations directly from audio policy manager to audio hardware interface. + virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0) = 0; + // function enabling to receive proprietary informations directly from audio hardware interface to audio policy manager. + virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys) = 0; + + // request the playback of a tone on the specified stream: used for instance to replace notification sounds when playing + // over a telephony device during a phone call. + virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream) = 0; + virtual status_t stopTone() = 0; + + // set down link audio volume. + virtual status_t setVoiceVolume(float volume, int delayMs = 0) = 0; + + // move effect to the specified output + virtual status_t moveEffects(int session, + audio_io_handle_t srcOutput, + audio_io_handle_t dstOutput) = 0; + +}; + +extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface); +extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface); + + +}; // namespace android + +#endif // ANDROID_AUDIOPOLICY_INTERFACE_H diff --git a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp new file mode 100644 index 0000000..c57c4fa --- /dev/null +++ b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioPolicyIntefaceImpl" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" + +namespace android { + + +// ---------------------------------------------------------------------------- + +status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (!audio_is_output_device(device) && !audio_is_input_device(device)) { + return BAD_VALUE; + } + if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE && + state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) { + return BAD_VALUE; + } + + ALOGV("setDeviceConnectionState()"); + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->setDeviceConnectionState(device, + state, device_address); +} + +audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( + audio_devices_t device, + const char *device_address) +{ + if (mAudioPolicyManager == NULL) { + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } + return mAudioPolicyManager->getDeviceConnectionState(device, + device_address); +} + +status_t AudioPolicyService::setPhoneState(audio_mode_t state) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(state) >= AUDIO_MODE_CNT) { + return BAD_VALUE; + } + + ALOGV("setPhoneState()"); + + // TODO: check if it is more appropriate to do it in platform specific policy manager + AudioSystem::setMode(state); + + Mutex::Autolock _l(mLock); + mAudioPolicyManager->setPhoneState(state); + return NO_ERROR; +} + +status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return BAD_VALUE; + } + if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) { + return BAD_VALUE; + } + ALOGV("setForceUse()"); + Mutex::Autolock _l(mLock); + mAudioPolicyManager->setForceUse(usage, config); + return NO_ERROR; +} + +audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage) +{ + if (mAudioPolicyManager == NULL) { + return AUDIO_POLICY_FORCE_NONE; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return AUDIO_POLICY_FORCE_NONE; + } + return mAudioPolicyManager->getForceUse(usage); +} + +audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + ALOGV("getOutput()"); + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getOutput(stream, samplingRate, + format, channelMask, flags, offloadInfo); +} + +status_t AudioPolicyService::startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + ALOGV("startOutput()"); + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->startOutput(output, stream, session); +} + +status_t AudioPolicyService::stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + ALOGV("stopOutput()"); + mOutputCommandThread->stopOutputCommand(output, stream, session); + return NO_ERROR; +} + +status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + ALOGV("doStopOutput from tid %d", gettid()); + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->stopOutput(output, stream, session); +} + +void AudioPolicyService::releaseOutput(audio_io_handle_t output) +{ + if (mAudioPolicyManager == NULL) { + return; + } + ALOGV("releaseOutput()"); + mOutputCommandThread->releaseOutputCommand(output); +} + +void AudioPolicyService::doReleaseOutput(audio_io_handle_t output) +{ + ALOGV("doReleaseOutput from tid %d", gettid()); + Mutex::Autolock _l(mLock); + mAudioPolicyManager->releaseOutput(output); +} + +audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + int audioSession) +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + // already checked by client, but double-check in case the client wrapper is bypassed + if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD) { + return 0; + } + + if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) { + return 0; + } + + Mutex::Autolock _l(mLock); + // the audio_in_acoustics_t parameter is ignored by get_input() + audio_io_handle_t input = mAudioPolicyManager->getInput(inputSource, samplingRate, + format, channelMask, (audio_in_acoustics_t) 0); + + if (input == 0) { + return input; + } + // create audio pre processors according to input source + audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ? + AUDIO_SOURCE_VOICE_RECOGNITION : inputSource; + + ssize_t index = mInputSources.indexOfKey(aliasSource); + if (index < 0) { + return input; + } + ssize_t idx = mInputs.indexOfKey(input); + InputDesc *inputDesc; + if (idx < 0) { + inputDesc = new InputDesc(audioSession); + mInputs.add(input, inputDesc); + } else { + inputDesc = mInputs.valueAt(idx); + } + + Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects; + for (size_t i = 0; i < effects.size(); i++) { + EffectDesc *effect = effects[i]; + sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, -1, 0, 0, audioSession, input); + status_t status = fx->initCheck(); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to create Fx %s on input %d", effect->mName, input); + // fx goes out of scope and strong ref on AudioEffect is released + continue; + } + for (size_t j = 0; j < effect->mParams.size(); j++) { + fx->setParameter(effect->mParams[j]); + } + inputDesc->mEffects.add(fx); + } + setPreProcessorEnabled(inputDesc, true); + return input; +} + +status_t AudioPolicyService::startInput(audio_io_handle_t input) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mAudioPolicyManager->startInput(input); +} + +status_t AudioPolicyService::stopInput(audio_io_handle_t input) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mAudioPolicyManager->stopInput(input); +} + +void AudioPolicyService::releaseInput(audio_io_handle_t input) +{ + if (mAudioPolicyManager == NULL) { + return; + } + Mutex::Autolock _l(mLock); + mAudioPolicyManager->releaseInput(input); + + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + return; + } + InputDesc *inputDesc = mInputs.valueAt(index); + setPreProcessorEnabled(inputDesc, false); + delete inputDesc; + mInputs.removeItemsAt(index); +} + +status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + mAudioPolicyManager->initStreamVolume(stream, indexMin, indexMax); + return NO_ERROR; +} + +status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->setStreamVolumeIndex(stream, + index, + device); +} + +status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getStreamVolumeIndex(stream, + index, + device); +} + +uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + return mAudioPolicyManager->getStrategyForStream(stream); +} + +//audio policy: use audio_device_t appropriately + +audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) +{ + if (mAudioPolicyManager == NULL) { + return (audio_devices_t)0; + } + return mAudioPolicyManager->getDevicesForStream(stream); +} + +audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc) +{ + // FIXME change return type to status_t, and return NO_INIT here + if (mAudioPolicyManager == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getOutputForEffect(desc); +} + +status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->registerEffect(desc, io, strategy, session, id); +} + +status_t AudioPolicyService::unregisterEffect(int id) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->unregisterEffect(id); +} + +status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->setEffectEnabled(id, enabled); +} + +bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->isStreamActive(stream, inPastMs); +} + +bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->isStreamActiveRemotely(stream, inPastMs); +} + +bool AudioPolicyService::isSourceActive(audio_source_t source) const +{ + if (mAudioPolicyManager == NULL) { + return false; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->isSourceActive(source); +} + +status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + + if (mAudioPolicyManager == NULL) { + *count = 0; + return NO_INIT; + } + Mutex::Autolock _l(mLock); + status_t status = NO_ERROR; + + size_t index; + for (index = 0; index < mInputs.size(); index++) { + if (mInputs.valueAt(index)->mSessionId == audioSession) { + break; + } + } + if (index == mInputs.size()) { + *count = 0; + return BAD_VALUE; + } + Vector< sp<AudioEffect> > effects = mInputs.valueAt(index)->mEffects; + + for (size_t i = 0; i < effects.size(); i++) { + effect_descriptor_t desc = effects[i]->descriptor(); + if (i < *count) { + descriptors[i] = desc; + } + } + if (effects.size() > *count) { + status = NO_MEMORY; + } + *count = effects.size(); + return status; +} + +bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) +{ + if (mAudioPolicyManager == NULL) { + ALOGV("mAudioPolicyManager == NULL"); + return false; + } + + return mAudioPolicyManager->isOffloadSupported(info); +} + + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp new file mode 100644 index 0000000..bb62ab3 --- /dev/null +++ b/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioPolicyService" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" + +#include <system/audio.h> +#include <system/audio_policy.h> +#include <hardware/audio_policy.h> + +namespace android { + + +// ---------------------------------------------------------------------------- + +status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (!audio_is_output_device(device) && !audio_is_input_device(device)) { + return BAD_VALUE; + } + if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE && + state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) { + return BAD_VALUE; + } + + ALOGV("setDeviceConnectionState()"); + Mutex::Autolock _l(mLock); + return mpAudioPolicy->set_device_connection_state(mpAudioPolicy, device, + state, device_address); +} + +audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( + audio_devices_t device, + const char *device_address) +{ + if (mpAudioPolicy == NULL) { + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } + return mpAudioPolicy->get_device_connection_state(mpAudioPolicy, device, + device_address); +} + +status_t AudioPolicyService::setPhoneState(audio_mode_t state) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(state) >= AUDIO_MODE_CNT) { + return BAD_VALUE; + } + + ALOGV("setPhoneState()"); + + // TODO: check if it is more appropriate to do it in platform specific policy manager + AudioSystem::setMode(state); + + Mutex::Autolock _l(mLock); + mpAudioPolicy->set_phone_state(mpAudioPolicy, state); + return NO_ERROR; +} + +status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return BAD_VALUE; + } + if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) { + return BAD_VALUE; + } + ALOGV("setForceUse()"); + Mutex::Autolock _l(mLock); + mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config); + return NO_ERROR; +} + +audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage) +{ + if (mpAudioPolicy == NULL) { + return AUDIO_POLICY_FORCE_NONE; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return AUDIO_POLICY_FORCE_NONE; + } + return mpAudioPolicy->get_force_use(mpAudioPolicy, usage); +} + +audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + if (mpAudioPolicy == NULL) { + return 0; + } + ALOGV("getOutput()"); + Mutex::Autolock _l(mLock); + return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, + format, channelMask, flags, offloadInfo); +} + +status_t AudioPolicyService::startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + ALOGV("startOutput()"); + Mutex::Autolock _l(mLock); + return mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session); +} + +status_t AudioPolicyService::stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + ALOGV("stopOutput()"); + mOutputCommandThread->stopOutputCommand(output, stream, session); + return NO_ERROR; +} + +status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + ALOGV("doStopOutput from tid %d", gettid()); + Mutex::Autolock _l(mLock); + return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session); +} + +void AudioPolicyService::releaseOutput(audio_io_handle_t output) +{ + if (mpAudioPolicy == NULL) { + return; + } + ALOGV("releaseOutput()"); + mOutputCommandThread->releaseOutputCommand(output); +} + +void AudioPolicyService::doReleaseOutput(audio_io_handle_t output) +{ + ALOGV("doReleaseOutput from tid %d", gettid()); + Mutex::Autolock _l(mLock); + mpAudioPolicy->release_output(mpAudioPolicy, output); +} + +audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + int audioSession) +{ + if (mpAudioPolicy == NULL) { + return 0; + } + // already checked by client, but double-check in case the client wrapper is bypassed + if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD) { + return 0; + } + + if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) { + return 0; + } + + Mutex::Autolock _l(mLock); + // the audio_in_acoustics_t parameter is ignored by get_input() + audio_io_handle_t input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate, + format, channelMask, (audio_in_acoustics_t) 0); + + if (input == 0) { + return input; + } + // create audio pre processors according to input source + audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ? + AUDIO_SOURCE_VOICE_RECOGNITION : inputSource; + + ssize_t index = mInputSources.indexOfKey(aliasSource); + if (index < 0) { + return input; + } + ssize_t idx = mInputs.indexOfKey(input); + InputDesc *inputDesc; + if (idx < 0) { + inputDesc = new InputDesc(audioSession); + mInputs.add(input, inputDesc); + } else { + inputDesc = mInputs.valueAt(idx); + } + + Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects; + for (size_t i = 0; i < effects.size(); i++) { + EffectDesc *effect = effects[i]; + sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, -1, 0, 0, audioSession, input); + status_t status = fx->initCheck(); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to create Fx %s on input %d", effect->mName, input); + // fx goes out of scope and strong ref on AudioEffect is released + continue; + } + for (size_t j = 0; j < effect->mParams.size(); j++) { + fx->setParameter(effect->mParams[j]); + } + inputDesc->mEffects.add(fx); + } + setPreProcessorEnabled(inputDesc, true); + return input; +} + +status_t AudioPolicyService::startInput(audio_io_handle_t input) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mpAudioPolicy->start_input(mpAudioPolicy, input); +} + +status_t AudioPolicyService::stopInput(audio_io_handle_t input) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mpAudioPolicy->stop_input(mpAudioPolicy, input); +} + +void AudioPolicyService::releaseInput(audio_io_handle_t input) +{ + if (mpAudioPolicy == NULL) { + return; + } + Mutex::Autolock _l(mLock); + mpAudioPolicy->release_input(mpAudioPolicy, input); + + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + return; + } + InputDesc *inputDesc = mInputs.valueAt(index); + setPreProcessorEnabled(inputDesc, false); + delete inputDesc; + mInputs.removeItemsAt(index); +} + +status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + mpAudioPolicy->init_stream_volume(mpAudioPolicy, stream, indexMin, indexMax); + return NO_ERROR; +} + +status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + if (mpAudioPolicy->set_stream_volume_index_for_device) { + return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy, + stream, + index, + device); + } else { + return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index); + } +} + +status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + if (mpAudioPolicy->get_stream_volume_index_for_device) { + return mpAudioPolicy->get_stream_volume_index_for_device(mpAudioPolicy, + stream, + index, + device); + } else { + return mpAudioPolicy->get_stream_volume_index(mpAudioPolicy, stream, index); + } +} + +uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) +{ + if (mpAudioPolicy == NULL) { + return 0; + } + return mpAudioPolicy->get_strategy_for_stream(mpAudioPolicy, stream); +} + +//audio policy: use audio_device_t appropriately + +audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) +{ + if (mpAudioPolicy == NULL) { + return (audio_devices_t)0; + } + return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream); +} + +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 0; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc); +} + +status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + return mpAudioPolicy->register_effect(mpAudioPolicy, desc, io, strategy, session, id); +} + +status_t AudioPolicyService::unregisterEffect(int id) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + return mpAudioPolicy->unregister_effect(mpAudioPolicy, id); +} + +status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + return mpAudioPolicy->set_effect_enabled(mpAudioPolicy, id, enabled); +} + +bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (mpAudioPolicy == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->is_stream_active(mpAudioPolicy, stream, inPastMs); +} + +bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (mpAudioPolicy == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->is_stream_active_remotely(mpAudioPolicy, stream, inPastMs); +} + +bool AudioPolicyService::isSourceActive(audio_source_t source) const +{ + if (mpAudioPolicy == NULL) { + return false; + } + if (mpAudioPolicy->is_source_active == 0) { + return false; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->is_source_active(mpAudioPolicy, source); +} + +status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + + if (mpAudioPolicy == NULL) { + *count = 0; + return NO_INIT; + } + Mutex::Autolock _l(mLock); + status_t status = NO_ERROR; + + size_t index; + for (index = 0; index < mInputs.size(); index++) { + if (mInputs.valueAt(index)->mSessionId == audioSession) { + break; + } + } + if (index == mInputs.size()) { + *count = 0; + return BAD_VALUE; + } + Vector< sp<AudioEffect> > effects = mInputs.valueAt(index)->mEffects; + + for (size_t i = 0; i < effects.size(); i++) { + effect_descriptor_t desc = effects[i]->descriptor(); + if (i < *count) { + descriptors[i] = desc; + } + } + if (effects.size() > *count) { + status = NO_MEMORY; + } + *count = effects.size(); + return status; +} + +bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) +{ + if (mpAudioPolicy == NULL) { + ALOGV("mpAudioPolicy == NULL"); + return false; + } + + if (mpAudioPolicy->is_offload_supported == NULL) { + ALOGV("HAL does not implement is_offload_supported"); + return false; + } + + return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info); +} + + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp new file mode 100644 index 0000000..45f98d2 --- /dev/null +++ b/services/audiopolicy/AudioPolicyManager.cpp @@ -0,0 +1,4296 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioPolicyManager" +//#define LOG_NDEBUG 0 + +//#define VERY_VERBOSE_LOGGING +#ifdef VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +// A device mask for all audio input devices that are considered "virtual" when evaluating +// active inputs in getActiveInput() +#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL AUDIO_DEVICE_IN_REMOTE_SUBMIX +// A device mask for all audio output devices that are considered "remote" when evaluating +// active output devices in isStreamActiveRemotely() +#define APM_AUDIO_OUT_DEVICE_REMOTE_ALL AUDIO_DEVICE_OUT_REMOTE_SUBMIX + +#include <utils/Log.h> +#include "AudioPolicyManager.h" +#include <hardware/audio_effect.h> +#include <hardware/audio.h> +#include <math.h> +#include <hardware_legacy/audio_policy_conf.h> +#include <cutils/properties.h> +#include <media/AudioParameter.h> + +namespace android { + +// ---------------------------------------------------------------------------- +// Definitions for audio_policy.conf file parsing +// ---------------------------------------------------------------------------- + +struct StringToEnum { + const char *name; + uint32_t value; +}; + +#define STRING_TO_ENUM(string) { #string, string } +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +const StringToEnum sDeviceNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADPHONE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_SCO), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_A2DP), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_DIGITAL), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_ACCESSORY), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_DEVICE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_USB), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_REMOTE_SUBMIX), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BUILTIN_MIC), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_ALL_SCO), + STRING_TO_ENUM(AUDIO_DEVICE_IN_WIRED_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_AUX_DIGITAL), + STRING_TO_ENUM(AUDIO_DEVICE_IN_VOICE_CALL), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BACK_MIC), + STRING_TO_ENUM(AUDIO_DEVICE_IN_REMOTE_SUBMIX), + STRING_TO_ENUM(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_ACCESSORY), +}; + +const StringToEnum sFlagNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_FAST), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_NON_BLOCKING), +}; + +const StringToEnum sFormatNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_32_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_24_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_FLOAT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_PACKED), + STRING_TO_ENUM(AUDIO_FORMAT_MP3), + STRING_TO_ENUM(AUDIO_FORMAT_AAC), + STRING_TO_ENUM(AUDIO_FORMAT_VORBIS), +}; + +const StringToEnum sOutChannelsNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_MONO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1), +}; + +const StringToEnum sInChannelsNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_CHANNEL_IN_MONO), + STRING_TO_ENUM(AUDIO_CHANNEL_IN_STEREO), + STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK), +}; + + +uint32_t AudioPolicyManager::stringToEnum(const struct StringToEnum *table, + size_t size, + const char *name) +{ + for (size_t i = 0; i < size; i++) { + if (strcmp(table[i].name, name) == 0) { + ALOGV("stringToEnum() found %s", table[i].name); + return table[i].value; + } + } + return 0; +} + +const char *AudioPolicyManager::enumToString(const struct StringToEnum *table, + size_t size, + uint32_t value) +{ + for (size_t i = 0; i < size; i++) { + if (table[i].value == value) { + return table[i].name; + } + } + return ""; +} + +bool AudioPolicyManager::stringToBool(const char *value) +{ + return ((strcasecmp("true", value) == 0) || (strcmp("1", value) == 0)); +} + + +// ---------------------------------------------------------------------------- +// AudioPolicyInterface implementation +// ---------------------------------------------------------------------------- + + +status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) +{ + SortedVector <audio_io_handle_t> outputs; + String8 address = String8(device_address); + + ALOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address); + + // connect/disconnect only 1 device at a time + if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE; + + // handle output devices + if (audio_is_output_device(device)) { + sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device, + address, + 0); + ssize_t index = mAvailableOutputDevices.indexOf(devDesc); + + // save a copy of the opened output descriptors before any output is opened or closed + // by checkOutputsForDevice(). This will be needed by checkOutputForAllStrategies() + mPreviousOutputs = mOutputs; + switch (state) + { + // handle output device connection + case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: + if (index >= 0) { + ALOGW("setDeviceConnectionState() device already connected: %x", device); + return INVALID_OPERATION; + } + ALOGV("setDeviceConnectionState() connecting device %x", device); + + if (checkOutputsForDevice(device, state, outputs, address) != NO_ERROR) { + return INVALID_OPERATION; + } + ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %d outputs", + outputs.size()); + // register new device as available + index = mAvailableOutputDevices.add(devDesc); + if (index >= 0) { + mAvailableOutputDevices[index]->mId = nextUniqueId(); + } else { + return NO_MEMORY; + } + + break; + // handle output device disconnection + case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: { + if (index < 0) { + ALOGW("setDeviceConnectionState() device not connected: %x", device); + return INVALID_OPERATION; + } + + ALOGV("setDeviceConnectionState() disconnecting device %x", device); + // remove device from available output devices + mAvailableOutputDevices.remove(devDesc); + + checkOutputsForDevice(device, state, outputs, address); + // not currently handling multiple simultaneous submixes: ignoring remote submix + // case and address + } break; + + default: + ALOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + + // checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP + // output is suspended before any tracks are moved to it + checkA2dpSuspend(); + checkOutputForAllStrategies(); + // outputs must be closed after checkOutputForAllStrategies() is executed + if (!outputs.isEmpty()) { + for (size_t i = 0; i < outputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueFor(outputs[i]); + // close unused outputs after device disconnection or direct outputs that have been + // opened by checkOutputsForDevice() to query dynamic parameters + if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) || + (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) && + (desc->mDirectOpenCount == 0))) { + closeOutput(outputs[i]); + } + } + // check again after closing A2DP output to reset mA2dpSuspended if needed + checkA2dpSuspend(); + } + + updateDevicesAndOutputs(); + for (size_t i = 0; i < mOutputs.size(); i++) { + // do not force device change on duplicated output because if device is 0, it will + // also force a device 0 for the two outputs it is duplicated to which may override + // a valid device selection on those outputs. + setOutputDevice(mOutputs.keyAt(i), + getNewDevice(mOutputs.keyAt(i), true /*fromCache*/), + !mOutputs.valueAt(i)->isDuplicated(), + 0); + } + + if (device == AUDIO_DEVICE_OUT_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO || + device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET || + device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) { + device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + } else { + return NO_ERROR; + } + } + // handle input devices + if (audio_is_input_device(device)) { + sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device, + address, + 0); + + ssize_t index = mAvailableInputDevices.indexOf(devDesc); + switch (state) + { + // handle input device connection + case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: { + if (index >= 0) { + ALOGW("setDeviceConnectionState() device already connected: %d", device); + return INVALID_OPERATION; + } + index = mAvailableInputDevices.add(devDesc); + if (index >= 0) { + mAvailableInputDevices[index]->mId = nextUniqueId(); + } else { + return NO_MEMORY; + } + } + break; + + // handle input device disconnection + case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: { + if (index < 0) { + ALOGW("setDeviceConnectionState() device not connected: %d", device); + return INVALID_OPERATION; + } + mAvailableInputDevices.remove(devDesc); + } break; + + default: + ALOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + + audio_io_handle_t activeInput = getActiveInput(); + if (activeInput != 0) { + AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput); + audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource); + if ((newDevice != AUDIO_DEVICE_NONE) && (newDevice != inputDesc->mDevice)) { + ALOGV("setDeviceConnectionState() changing device from %x to %x for input %d", + inputDesc->mDevice, newDevice, activeInput); + inputDesc->mDevice = newDevice; + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), (int)newDevice); + mpClientInterface->setParameters(activeInput, param.toString()); + } + } + + return NO_ERROR; + } + + ALOGW("setDeviceConnectionState() invalid device: %x", device); + return BAD_VALUE; +} + +audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devices_t device, + const char *device_address) +{ + audio_policy_dev_state_t state = AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + String8 address = String8(device_address); + sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device, + String8(device_address), + 0); + ssize_t index; + DeviceVector *deviceVector; + + if (audio_is_output_device(device)) { + deviceVector = &mAvailableOutputDevices; + } else if (audio_is_input_device(device)) { + deviceVector = &mAvailableInputDevices; + } else { + ALOGW("getDeviceConnectionState() invalid device type %08x", device); + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } + + index = deviceVector->indexOf(devDesc); + if (index >= 0) { + return AUDIO_POLICY_DEVICE_STATE_AVAILABLE; + } else { + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } +} + +void AudioPolicyManager::setPhoneState(audio_mode_t state) +{ + ALOGV("setPhoneState() state %d", state); + audio_devices_t newDevice = AUDIO_DEVICE_NONE; + if (state < 0 || state >= AUDIO_MODE_CNT) { + ALOGW("setPhoneState() invalid state %d", state); + return; + } + + if (state == mPhoneState ) { + ALOGW("setPhoneState() setting same state %d", state); + return; + } + + // if leaving call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (isInCall()) { + ALOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + handleIncallSonification((audio_stream_type_t)stream, false, true); + } + } + + // store previous phone state for management of sonification strategy below + int oldState = mPhoneState; + mPhoneState = state; + bool force = false; + + // are we entering or starting a call + if (!isStateInCall(oldState) && isStateInCall(state)) { + ALOGV(" Entering call in setPhoneState()"); + // force routing command to audio hardware when starting a call + // even if no device change is needed + force = true; + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = + sVolumeProfiles[AUDIO_STREAM_VOICE_CALL][j]; + } + } else if (isStateInCall(oldState) && !isStateInCall(state)) { + ALOGV(" Exiting call in setPhoneState()"); + // force routing command to audio hardware when exiting a call + // even if no device change is needed + force = true; + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = + sVolumeProfiles[AUDIO_STREAM_DTMF][j]; + } + } else if (isStateInCall(state) && (state != oldState)) { + ALOGV(" Switching between telephony and VoIP in setPhoneState()"); + // force routing command to audio hardware when switching between telephony and VoIP + // even if no device change is needed + force = true; + } + + // check for device and output changes triggered by new phone state + newDevice = getNewDevice(mPrimaryOutput, false /*fromCache*/); + checkA2dpSuspend(); + checkOutputForAllStrategies(); + updateDevicesAndOutputs(); + + AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mPrimaryOutput); + + // force routing command to audio hardware when ending call + // even if no device change is needed + if (isStateInCall(oldState) && newDevice == AUDIO_DEVICE_NONE) { + newDevice = hwOutputDesc->device(); + } + + int delayMs = 0; + if (isStateInCall(state)) { + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueAt(i); + // mute media and sonification strategies and delay device switch by the largest + // latency of any output where either strategy is active. + // This avoid sending the ring tone or music tail into the earpiece or headset. + if ((desc->isStrategyActive(STRATEGY_MEDIA, + SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime) || + desc->isStrategyActive(STRATEGY_SONIFICATION, + SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime)) && + (delayMs < (int)desc->mLatency*2)) { + delayMs = desc->mLatency*2; + } + setStrategyMute(STRATEGY_MEDIA, true, mOutputs.keyAt(i)); + setStrategyMute(STRATEGY_MEDIA, false, mOutputs.keyAt(i), MUTE_TIME_MS, + getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/)); + setStrategyMute(STRATEGY_SONIFICATION, true, mOutputs.keyAt(i)); + setStrategyMute(STRATEGY_SONIFICATION, false, mOutputs.keyAt(i), MUTE_TIME_MS, + getDeviceForStrategy(STRATEGY_SONIFICATION, true /*fromCache*/)); + } + } + + // change routing is necessary + setOutputDevice(mPrimaryOutput, newDevice, force, delayMs); + + // if entering in call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (isStateInCall(state)) { + ALOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + handleIncallSonification((audio_stream_type_t)stream, true, true); + } + } + + // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE + if (state == AUDIO_MODE_RINGTONE && + isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)) { + mLimitRingtoneVolume = true; + } else { + mLimitRingtoneVolume = false; + } +} + +void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) +{ + ALOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); + + bool forceVolumeReeval = false; + switch(usage) { + case AUDIO_POLICY_FORCE_FOR_COMMUNICATION: + if (config != AUDIO_POLICY_FORCE_SPEAKER && config != AUDIO_POLICY_FORCE_BT_SCO && + config != AUDIO_POLICY_FORCE_NONE) { + ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config); + return; + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_MEDIA: + if (config != AUDIO_POLICY_FORCE_HEADPHONES && config != AUDIO_POLICY_FORCE_BT_A2DP && + config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_ANALOG_DOCK && + config != AUDIO_POLICY_FORCE_DIGITAL_DOCK && config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_NO_BT_A2DP) { + ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config); + return; + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_RECORD: + if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_NONE) { + ALOGW("setForceUse() invalid config %d for FOR_RECORD", config); + return; + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_DOCK: + if (config != AUDIO_POLICY_FORCE_NONE && config != AUDIO_POLICY_FORCE_BT_CAR_DOCK && + config != AUDIO_POLICY_FORCE_BT_DESK_DOCK && + config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_ANALOG_DOCK && + config != AUDIO_POLICY_FORCE_DIGITAL_DOCK) { + ALOGW("setForceUse() invalid config %d for FOR_DOCK", config); + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_SYSTEM: + if (config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { + ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config); + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + default: + ALOGW("setForceUse() invalid usage %d", usage); + break; + } + + // check for device and output changes triggered by new force usage + checkA2dpSuspend(); + checkOutputForAllStrategies(); + updateDevicesAndOutputs(); + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t output = mOutputs.keyAt(i); + audio_devices_t newDevice = getNewDevice(output, true /*fromCache*/); + setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE)); + if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) { + applyStreamVolumes(output, newDevice, 0, true); + } + } + + audio_io_handle_t activeInput = getActiveInput(); + if (activeInput != 0) { + AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput); + audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource); + if ((newDevice != AUDIO_DEVICE_NONE) && (newDevice != inputDesc->mDevice)) { + ALOGV("setForceUse() changing device from %x to %x for input %d", + inputDesc->mDevice, newDevice, activeInput); + inputDesc->mDevice = newDevice; + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), (int)newDevice); + mpClientInterface->setParameters(activeInput, param.toString()); + } + } + +} + +audio_policy_forced_cfg_t AudioPolicyManager::getForceUse(audio_policy_force_use_t usage) +{ + return mForceUse[usage]; +} + +void AudioPolicyManager::setSystemProperty(const char* property, const char* value) +{ + ALOGV("setSystemProperty() property %s, value %s", property, value); +} + +// Find a direct output profile compatible with the parameters passed, even if the input flags do +// not explicitly request a direct output +AudioPolicyManager::IOProfile *AudioPolicyManager::getProfileForDirectOutput( + audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags) +{ + for (size_t i = 0; i < mHwModules.size(); i++) { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) { + IOProfile *profile = mHwModules[i]->mOutputProfiles[j]; + bool found = false; + if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { + if (profile->isCompatibleProfile(device, samplingRate, format, + channelMask, + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) { + found = true; + } + } else { + if (profile->isCompatibleProfile(device, samplingRate, format, + channelMask, + AUDIO_OUTPUT_FLAG_DIRECT)) { + found = true; + } + } + if (found && (mAvailableOutputDevices.types() & profile->mSupportedDevices.types())) { + return profile; + } + } + } + return 0; +} + +audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + audio_io_handle_t output = 0; + uint32_t latency = 0; + routing_strategy strategy = getStrategy(stream); + audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); + ALOGV("getOutput() device %d, stream %d, samplingRate %d, format %x, channelMask %x, flags %x", + device, stream, samplingRate, format, channelMask, flags); + +#ifdef AUDIO_POLICY_TEST + if (mCurOutput != 0) { + ALOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channelMask %x, mDirectOutput %d", + mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput); + + if (mTestOutputs[mCurOutput] == 0) { + ALOGV("getOutput() opening test output"); + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(NULL); + outputDesc->mDevice = mTestDevice; + outputDesc->mSamplingRate = mTestSamplingRate; + outputDesc->mFormat = mTestFormat; + outputDesc->mChannelMask = mTestChannels; + outputDesc->mLatency = mTestLatencyMs; + outputDesc->mFlags = + (audio_output_flags_t)(mDirectOutput ? AUDIO_OUTPUT_FLAG_DIRECT : 0); + outputDesc->mRefCount[stream] = 0; + mTestOutputs[mCurOutput] = mpClientInterface->openOutput(0, &outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannelMask, + &outputDesc->mLatency, + outputDesc->mFlags, + offloadInfo); + if (mTestOutputs[mCurOutput]) { + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"),mCurOutput); + mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString()); + addOutput(mTestOutputs[mCurOutput], outputDesc); + } + } + return mTestOutputs[mCurOutput]; + } +#endif //AUDIO_POLICY_TEST + + // open a direct output if required by specified parameters + //force direct flag if offload flag is set: offloading implies a direct output stream + // and all common behaviors are driven by checking only the direct flag + // this should normally be set appropriately in the policy configuration file + if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT); + } + + // Do not allow offloading if one non offloadable effect is enabled. This prevents from + // creating an offloaded track and tearing it down immediately after start when audioflinger + // detects there is an active non offloadable effect. + // FIXME: We should check the audio session here but we do not have it in this context. + // This may prevent offloading in rare situations where effects are left active by apps + // in the background. + IOProfile *profile = NULL; + if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || + !isNonOffloadableEffectEnabled()) { + profile = getProfileForDirectOutput(device, + samplingRate, + format, + channelMask, + (audio_output_flags_t)flags); + } + + if (profile != NULL) { + AudioOutputDescriptor *outputDesc = NULL; + + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueAt(i); + if (!desc->isDuplicated() && (profile == desc->mProfile)) { + outputDesc = desc; + // reuse direct output if currently open and configured with same parameters + if ((samplingRate == outputDesc->mSamplingRate) && + (format == outputDesc->mFormat) && + (channelMask == outputDesc->mChannelMask)) { + outputDesc->mDirectOpenCount++; + ALOGV("getOutput() reusing direct output %d", mOutputs.keyAt(i)); + return mOutputs.keyAt(i); + } + } + } + // close direct output if currently open and configured with different parameters + if (outputDesc != NULL) { + closeOutput(outputDesc->mId); + } + outputDesc = new AudioOutputDescriptor(profile); + outputDesc->mDevice = device; + outputDesc->mSamplingRate = samplingRate; + outputDesc->mFormat = format; + outputDesc->mChannelMask = channelMask; + outputDesc->mLatency = 0; + outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags); + outputDesc->mRefCount[stream] = 0; + outputDesc->mStopTime[stream] = 0; + outputDesc->mDirectOpenCount = 1; + output = mpClientInterface->openOutput(profile->mModule->mHandle, + &outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannelMask, + &outputDesc->mLatency, + outputDesc->mFlags, + offloadInfo); + + // only accept an output with the requested parameters + if (output == 0 || + (samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) || + (format != AUDIO_FORMAT_DEFAULT && format != outputDesc->mFormat) || + (channelMask != 0 && channelMask != outputDesc->mChannelMask)) { + ALOGV("getOutput() failed opening direct output: output %d samplingRate %d %d," + "format %d %d, channelMask %04x %04x", output, samplingRate, + outputDesc->mSamplingRate, format, outputDesc->mFormat, channelMask, + outputDesc->mChannelMask); + if (output != 0) { + mpClientInterface->closeOutput(output); + } + delete outputDesc; + return 0; + } + audio_io_handle_t srcOutput = getOutputForEffect(); + addOutput(output, outputDesc); + audio_io_handle_t dstOutput = getOutputForEffect(); + if (dstOutput == output) { + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, srcOutput, dstOutput); + } + mPreviousOutputs = mOutputs; + ALOGV("getOutput() returns new direct output %d", output); + return output; + } + + // ignoring channel mask due to downmix capability in mixer + + // open a non direct output + + // for non direct outputs, only PCM is supported + if (audio_is_linear_pcm(format)) { + // get which output is suitable for the specified stream. The actual + // routing change will happen when startOutput() will be called + SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs); + + output = selectOutput(outputs, flags); + } + ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d," + "format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags); + + ALOGV("getOutput() returns output %d", output); + + return output; +} + +audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_handle_t>& outputs, + audio_output_flags_t flags) +{ + // select one output among several that provide a path to a particular device or set of + // devices (the list was previously build by getOutputsForDevice()). + // The priority is as follows: + // 1: the output with the highest number of requested policy flags + // 2: the primary output + // 3: the first output in the list + + if (outputs.size() == 0) { + return 0; + } + if (outputs.size() == 1) { + return outputs[0]; + } + + int maxCommonFlags = 0; + audio_io_handle_t outputFlags = 0; + audio_io_handle_t outputPrimary = 0; + + for (size_t i = 0; i < outputs.size(); i++) { + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(outputs[i]); + if (!outputDesc->isDuplicated()) { + int commonFlags = popcount(outputDesc->mProfile->mFlags & flags); + if (commonFlags > maxCommonFlags) { + outputFlags = outputs[i]; + maxCommonFlags = commonFlags; + ALOGV("selectOutput() commonFlags for output %d, %04x", outputs[i], commonFlags); + } + if (outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) { + outputPrimary = outputs[i]; + } + } + } + + if (outputFlags != 0) { + return outputFlags; + } + if (outputPrimary != 0) { + return outputPrimary; + } + + return outputs[0]; +} + +status_t AudioPolicyManager::startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + ALOGV("startOutput() output %d, stream %d, session %d", output, stream, session); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + ALOGW("startOutput() unknown output %d", output); + return BAD_VALUE; + } + + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + + // increment usage count for this stream on the requested output: + // NOTE that the usage count is the same for duplicated output and hardware output which is + // necessary for a correct control of hardware output routing by startOutput() and stopOutput() + outputDesc->changeRefCount(stream, 1); + + if (outputDesc->mRefCount[stream] == 1) { + audio_devices_t newDevice = getNewDevice(output, false /*fromCache*/); + routing_strategy strategy = getStrategy(stream); + bool shouldWait = (strategy == STRATEGY_SONIFICATION) || + (strategy == STRATEGY_SONIFICATION_RESPECTFUL); + uint32_t waitMs = 0; + bool force = false; + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueAt(i); + if (desc != outputDesc) { + // force a device change if any other output is managed by the same hw + // module and has a current device selection that differs from selected device. + // In this case, the audio HAL must receive the new device selection so that it can + // change the device currently selected by the other active output. + if (outputDesc->sharesHwModuleWith(desc) && + desc->device() != newDevice) { + force = true; + } + // wait for audio on other active outputs to be presented when starting + // a notification so that audio focus effect can propagate. + uint32_t latency = desc->latency(); + if (shouldWait && desc->isActive(latency * 2) && (waitMs < latency)) { + waitMs = latency; + } + } + } + uint32_t muteWaitMs = setOutputDevice(output, newDevice, force); + + // handle special case for sonification while in call + if (isInCall()) { + handleIncallSonification(stream, true, false); + } + + // apply volume rules for current stream and device if necessary + checkAndSetVolume(stream, + mStreams[stream].getVolumeIndex(newDevice), + output, + newDevice); + + // update the outputs if starting an output with a stream that can affect notification + // routing + handleNotificationRoutingForStream(stream); + if (waitMs > muteWaitMs) { + usleep((waitMs - muteWaitMs) * 2 * 1000); + } + } + return NO_ERROR; +} + + +status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + ALOGV("stopOutput() output %d, stream %d, session %d", output, stream, session); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + ALOGW("stopOutput() unknown output %d", output); + return BAD_VALUE; + } + + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + + // handle special case for sonification while in call + if (isInCall()) { + handleIncallSonification(stream, false, false); + } + + if (outputDesc->mRefCount[stream] > 0) { + // decrement usage count of this stream on the output + outputDesc->changeRefCount(stream, -1); + // store time at which the stream was stopped - see isStreamActive() + if (outputDesc->mRefCount[stream] == 0) { + outputDesc->mStopTime[stream] = systemTime(); + audio_devices_t newDevice = getNewDevice(output, false /*fromCache*/); + // delay the device switch by twice the latency because stopOutput() is executed when + // the track stop() command is received and at that time the audio track buffer can + // still contain data that needs to be drained. The latency only covers the audio HAL + // and kernel buffers. Also the latency does not always include additional delay in the + // audio path (audio DSP, CODEC ...) + setOutputDevice(output, newDevice, false, outputDesc->mLatency*2); + + // force restoring the device selection on other active outputs if it differs from the + // one being selected for this output + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t curOutput = mOutputs.keyAt(i); + AudioOutputDescriptor *desc = mOutputs.valueAt(i); + if (curOutput != output && + desc->isActive() && + outputDesc->sharesHwModuleWith(desc) && + (newDevice != desc->device())) { + setOutputDevice(curOutput, + getNewDevice(curOutput, false /*fromCache*/), + true, + outputDesc->mLatency*2); + } + } + // update the outputs if stopping one with a stream that can affect notification routing + handleNotificationRoutingForStream(stream); + } + return NO_ERROR; + } else { + ALOGW("stopOutput() refcount is already 0 for output %d", output); + return INVALID_OPERATION; + } +} + +void AudioPolicyManager::releaseOutput(audio_io_handle_t output) +{ + ALOGV("releaseOutput() %d", output); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + ALOGW("releaseOutput() releasing unknown output %d", output); + return; + } + +#ifdef AUDIO_POLICY_TEST + int testIndex = testOutputIndex(output); + if (testIndex != 0) { + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + if (outputDesc->isActive()) { + mpClientInterface->closeOutput(output); + delete mOutputs.valueAt(index); + mOutputs.removeItem(output); + mTestOutputs[testIndex] = 0; + } + return; + } +#endif //AUDIO_POLICY_TEST + + AudioOutputDescriptor *desc = mOutputs.valueAt(index); + if (desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { + if (desc->mDirectOpenCount <= 0) { + ALOGW("releaseOutput() invalid open count %d for output %d", + desc->mDirectOpenCount, output); + return; + } + if (--desc->mDirectOpenCount == 0) { + closeOutput(output); + // If effects where present on the output, audioflinger moved them to the primary + // output by default: move them back to the appropriate output. + audio_io_handle_t dstOutput = getOutputForEffect(); + if (dstOutput != mPrimaryOutput) { + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput); + } + } + } +} + + +audio_io_handle_t AudioPolicyManager::getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_in_acoustics_t acoustics) +{ + audio_io_handle_t input = 0; + audio_devices_t device = getDeviceForInputSource(inputSource); + + ALOGV("getInput() inputSource %d, samplingRate %d, format %d, channelMask %x, acoustics %x", + inputSource, samplingRate, format, channelMask, acoustics); + + if (device == AUDIO_DEVICE_NONE) { + ALOGW("getInput() could not find device for inputSource %d", inputSource); + return 0; + } + + // adapt channel selection to input source + switch(inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK; + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + channelMask = AUDIO_CHANNEL_IN_VOICE_DNLINK; + break; + case AUDIO_SOURCE_VOICE_CALL: + channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK; + break; + default: + break; + } + + IOProfile *profile = getInputProfile(device, + samplingRate, + format, + channelMask); + if (profile == NULL) { + ALOGW("getInput() could not find profile for device %04x, samplingRate %d, format %d, " + "channelMask %04x", + device, samplingRate, format, channelMask); + return 0; + } + + if (profile->mModule->mHandle == 0) { + ALOGE("getInput(): HW module %s not opened", profile->mModule->mName); + return 0; + } + + AudioInputDescriptor *inputDesc = new AudioInputDescriptor(profile); + + inputDesc->mInputSource = inputSource; + inputDesc->mDevice = device; + inputDesc->mSamplingRate = samplingRate; + inputDesc->mFormat = format; + inputDesc->mChannelMask = channelMask; + inputDesc->mRefCount = 0; + input = mpClientInterface->openInput(profile->mModule->mHandle, + &inputDesc->mDevice, + &inputDesc->mSamplingRate, + &inputDesc->mFormat, + &inputDesc->mChannelMask); + + // only accept input with the exact requested set of parameters + if (input == 0 || + (samplingRate != inputDesc->mSamplingRate) || + (format != inputDesc->mFormat) || + (channelMask != inputDesc->mChannelMask)) { + ALOGI("getInput() failed opening input: samplingRate %d, format %d, channelMask %x", + samplingRate, format, channelMask); + if (input != 0) { + mpClientInterface->closeInput(input); + } + delete inputDesc; + return 0; + } + mInputs.add(input, inputDesc); + return input; +} + +status_t AudioPolicyManager::startInput(audio_io_handle_t input) +{ + ALOGV("startInput() input %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("startInput() unknown input %d", input); + return BAD_VALUE; + } + AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + +#ifdef AUDIO_POLICY_TEST + if (mTestInput == 0) +#endif //AUDIO_POLICY_TEST + { + // refuse 2 active AudioRecord clients at the same time except if the active input + // uses AUDIO_SOURCE_HOTWORD in which case it is closed. + audio_io_handle_t activeInput = getActiveInput(); + if (!isVirtualInputDevice(inputDesc->mDevice) && activeInput != 0) { + AudioInputDescriptor *activeDesc = mInputs.valueFor(activeInput); + if (activeDesc->mInputSource == AUDIO_SOURCE_HOTWORD) { + ALOGW("startInput() preempting already started low-priority input %d", activeInput); + stopInput(activeInput); + releaseInput(activeInput); + } else { + ALOGW("startInput() input %d failed: other input already started", input); + return INVALID_OPERATION; + } + } + } + + audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource); + if ((newDevice != AUDIO_DEVICE_NONE) && (newDevice != inputDesc->mDevice)) { + inputDesc->mDevice = newDevice; + } + + // automatically enable the remote submix output when input is started + if (audio_is_remote_submix_device(inputDesc->mDevice)) { + setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS); + } + + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice); + + int aliasSource = (inputDesc->mInputSource == AUDIO_SOURCE_HOTWORD) ? + AUDIO_SOURCE_VOICE_RECOGNITION : inputDesc->mInputSource; + + param.addInt(String8(AudioParameter::keyInputSource), aliasSource); + ALOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource); + + mpClientInterface->setParameters(input, param.toString()); + + inputDesc->mRefCount = 1; + return NO_ERROR; +} + +status_t AudioPolicyManager::stopInput(audio_io_handle_t input) +{ + ALOGV("stopInput() input %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("stopInput() unknown input %d", input); + return BAD_VALUE; + } + AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + + if (inputDesc->mRefCount == 0) { + ALOGW("stopInput() input %d already stopped", input); + return INVALID_OPERATION; + } else { + // automatically disable the remote submix output when input is stopped + if (audio_is_remote_submix_device(inputDesc->mDevice)) { + setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS); + } + + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), 0); + mpClientInterface->setParameters(input, param.toString()); + inputDesc->mRefCount = 0; + return NO_ERROR; + } +} + +void AudioPolicyManager::releaseInput(audio_io_handle_t input) +{ + ALOGV("releaseInput() %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("releaseInput() releasing unknown input %d", input); + return; + } + mpClientInterface->closeInput(input); + delete mInputs.valueAt(index); + mInputs.removeItem(input); + ALOGV("releaseInput() exit"); +} + +void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax) +{ + ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); + if (indexMin < 0 || indexMin >= indexMax) { + ALOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax); + return; + } + mStreams[stream].mIndexMin = indexMin; + mStreams[stream].mIndexMax = indexMax; +} + +status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) +{ + + if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) { + return BAD_VALUE; + } + if (!audio_is_output_device(device)) { + return BAD_VALUE; + } + + // Force max volume if stream cannot be muted + if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax; + + ALOGV("setStreamVolumeIndex() stream %d, device %04x, index %d", + stream, device, index); + + // if device is AUDIO_DEVICE_OUT_DEFAULT set default value and + // clear all device specific values + if (device == AUDIO_DEVICE_OUT_DEFAULT) { + mStreams[stream].mIndexCur.clear(); + } + mStreams[stream].mIndexCur.add(device, index); + + // compute and apply stream volume on all outputs according to connected device + status_t status = NO_ERROR; + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_devices_t curDevice = + getDeviceForVolume(mOutputs.valueAt(i)->device()); + if ((device == AUDIO_DEVICE_OUT_DEFAULT) || (device == curDevice)) { + status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice); + if (volStatus != NO_ERROR) { + status = volStatus; + } + } + } + return status; +} + +status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device) +{ + if (index == NULL) { + return BAD_VALUE; + } + if (!audio_is_output_device(device)) { + return BAD_VALUE; + } + // if device is AUDIO_DEVICE_OUT_DEFAULT, return volume for device corresponding to + // the strategy the stream belongs to. + if (device == AUDIO_DEVICE_OUT_DEFAULT) { + device = getDeviceForStrategy(getStrategy(stream), true /*fromCache*/); + } + device = getDeviceForVolume(device); + + *index = mStreams[stream].getVolumeIndex(device); + ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index); + return NO_ERROR; +} + +audio_io_handle_t AudioPolicyManager::selectOutputForEffects( + const SortedVector<audio_io_handle_t>& outputs) +{ + // select one output among several suitable for global effects. + // The priority is as follows: + // 1: An offloaded output. If the effect ends up not being offloadable, + // AudioFlinger will invalidate the track and the offloaded output + // will be closed causing the effect to be moved to a PCM output. + // 2: A deep buffer output + // 3: the first output in the list + + if (outputs.size() == 0) { + return 0; + } + + audio_io_handle_t outputOffloaded = 0; + audio_io_handle_t outputDeepBuffer = 0; + + for (size_t i = 0; i < outputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueFor(outputs[i]); + ALOGV("selectOutputForEffects outputs[%d] flags %x", i, desc->mFlags); + if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + outputOffloaded = outputs[i]; + } + if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) { + outputDeepBuffer = outputs[i]; + } + } + + ALOGV("selectOutputForEffects outputOffloaded %d outputDeepBuffer %d", + outputOffloaded, outputDeepBuffer); + if (outputOffloaded != 0) { + return outputOffloaded; + } + if (outputDeepBuffer != 0) { + return outputDeepBuffer; + } + + return outputs[0]; +} + +audio_io_handle_t AudioPolicyManager::getOutputForEffect(const effect_descriptor_t *desc) +{ + // apply simple rule where global effects are attached to the same output as MUSIC streams + + routing_strategy strategy = getStrategy(AUDIO_STREAM_MUSIC); + audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); + SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(device, mOutputs); + + audio_io_handle_t output = selectOutputForEffects(dstOutputs); + ALOGV("getOutputForEffect() got output %d for fx %s flags %x", + output, (desc == NULL) ? "unspecified" : desc->name, (desc == NULL) ? 0 : desc->flags); + + return output; +} + +status_t AudioPolicyManager::registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + ssize_t index = mOutputs.indexOfKey(io); + if (index < 0) { + index = mInputs.indexOfKey(io); + if (index < 0) { + ALOGW("registerEffect() unknown io %d", io); + return INVALID_OPERATION; + } + } + + if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) { + ALOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB", + desc->name, desc->memoryUsage); + return INVALID_OPERATION; + } + mTotalEffectsMemory += desc->memoryUsage; + ALOGV("registerEffect() effect %s, io %d, strategy %d session %d id %d", + desc->name, io, strategy, session, id); + ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory); + + EffectDescriptor *pDesc = new EffectDescriptor(); + memcpy (&pDesc->mDesc, desc, sizeof(effect_descriptor_t)); + pDesc->mIo = io; + pDesc->mStrategy = (routing_strategy)strategy; + pDesc->mSession = session; + pDesc->mEnabled = false; + + mEffects.add(id, pDesc); + + return NO_ERROR; +} + +status_t AudioPolicyManager::unregisterEffect(int id) +{ + ssize_t index = mEffects.indexOfKey(id); + if (index < 0) { + ALOGW("unregisterEffect() unknown effect ID %d", id); + return INVALID_OPERATION; + } + + EffectDescriptor *pDesc = mEffects.valueAt(index); + + setEffectEnabled(pDesc, false); + + if (mTotalEffectsMemory < pDesc->mDesc.memoryUsage) { + ALOGW("unregisterEffect() memory %d too big for total %d", + pDesc->mDesc.memoryUsage, mTotalEffectsMemory); + pDesc->mDesc.memoryUsage = mTotalEffectsMemory; + } + mTotalEffectsMemory -= pDesc->mDesc.memoryUsage; + ALOGV("unregisterEffect() effect %s, ID %d, memory %d total memory %d", + pDesc->mDesc.name, id, pDesc->mDesc.memoryUsage, mTotalEffectsMemory); + + mEffects.removeItem(id); + delete pDesc; + + return NO_ERROR; +} + +status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled) +{ + ssize_t index = mEffects.indexOfKey(id); + if (index < 0) { + ALOGW("unregisterEffect() unknown effect ID %d", id); + return INVALID_OPERATION; + } + + return setEffectEnabled(mEffects.valueAt(index), enabled); +} + +status_t AudioPolicyManager::setEffectEnabled(EffectDescriptor *pDesc, bool enabled) +{ + if (enabled == pDesc->mEnabled) { + ALOGV("setEffectEnabled(%s) effect already %s", + enabled?"true":"false", enabled?"enabled":"disabled"); + return INVALID_OPERATION; + } + + if (enabled) { + if (mTotalEffectsCpuLoad + pDesc->mDesc.cpuLoad > getMaxEffectsCpuLoad()) { + ALOGW("setEffectEnabled(true) CPU Load limit exceeded for Fx %s, CPU %f MIPS", + pDesc->mDesc.name, (float)pDesc->mDesc.cpuLoad/10); + return INVALID_OPERATION; + } + mTotalEffectsCpuLoad += pDesc->mDesc.cpuLoad; + ALOGV("setEffectEnabled(true) total CPU %d", mTotalEffectsCpuLoad); + } else { + if (mTotalEffectsCpuLoad < pDesc->mDesc.cpuLoad) { + ALOGW("setEffectEnabled(false) CPU load %d too high for total %d", + pDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad); + pDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad; + } + mTotalEffectsCpuLoad -= pDesc->mDesc.cpuLoad; + ALOGV("setEffectEnabled(false) total CPU %d", mTotalEffectsCpuLoad); + } + pDesc->mEnabled = enabled; + return NO_ERROR; +} + +bool AudioPolicyManager::isNonOffloadableEffectEnabled() +{ + for (size_t i = 0; i < mEffects.size(); i++) { + const EffectDescriptor * const pDesc = mEffects.valueAt(i); + if (pDesc->mEnabled && (pDesc->mStrategy == STRATEGY_MEDIA) && + ((pDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) { + ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d", + pDesc->mDesc.name, pDesc->mSession); + return true; + } + } + return false; +} + +bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +{ + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < mOutputs.size(); i++) { + const AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, + uint32_t inPastMs) const +{ + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < mOutputs.size(); i++) { + const AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) && + outputDesc->isStreamActive(stream, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioPolicyManager::isSourceActive(audio_source_t source) const +{ + for (size_t i = 0; i < mInputs.size(); i++) { + const AudioInputDescriptor * inputDescriptor = mInputs.valueAt(i); + if ((inputDescriptor->mInputSource == (int)source || + (source == AUDIO_SOURCE_VOICE_RECOGNITION && + inputDescriptor->mInputSource == AUDIO_SOURCE_HOTWORD)) + && (inputDescriptor->mRefCount > 0)) { + return true; + } + } + return false; +} + + +status_t AudioPolicyManager::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this); + result.append(buffer); + + snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput); + result.append(buffer); + snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for communications %d\n", + mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for system %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM]); + result.append(buffer); + + snprintf(buffer, SIZE, " Available output devices:\n"); + result.append(buffer); + write(fd, result.string(), result.size()); + DeviceDescriptor::dumpHeader(fd, 2); + for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) { + mAvailableOutputDevices[i]->dump(fd, 2); + } + snprintf(buffer, SIZE, "\n Available input devices:\n"); + write(fd, buffer, strlen(buffer)); + DeviceDescriptor::dumpHeader(fd, 2); + for (size_t i = 0; i < mAvailableInputDevices.size(); i++) { + mAvailableInputDevices[i]->dump(fd, 2); + } + + snprintf(buffer, SIZE, "\nHW Modules dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mHwModules.size(); i++) { + snprintf(buffer, SIZE, "- HW Module %d:\n", i + 1); + write(fd, buffer, strlen(buffer)); + mHwModules[i]->dump(fd); + } + + snprintf(buffer, SIZE, "\nOutputs dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mOutputs.size(); i++) { + snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i)); + write(fd, buffer, strlen(buffer)); + mOutputs.valueAt(i)->dump(fd); + } + + snprintf(buffer, SIZE, "\nInputs dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mInputs.size(); i++) { + snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i)); + write(fd, buffer, strlen(buffer)); + mInputs.valueAt(i)->dump(fd); + } + + snprintf(buffer, SIZE, "\nStreams dump:\n"); + write(fd, buffer, strlen(buffer)); + snprintf(buffer, SIZE, + " Stream Can be muted Index Min Index Max Index Cur [device : index]...\n"); + write(fd, buffer, strlen(buffer)); + for (int i = 0; i < AUDIO_STREAM_CNT; i++) { + snprintf(buffer, SIZE, " %02d ", i); + write(fd, buffer, strlen(buffer)); + mStreams[i].dump(fd); + } + + snprintf(buffer, SIZE, "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB\n", + (float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory); + write(fd, buffer, strlen(buffer)); + + snprintf(buffer, SIZE, "Registered effects:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mEffects.size(); i++) { + snprintf(buffer, SIZE, "- Effect %d dump:\n", mEffects.keyAt(i)); + write(fd, buffer, strlen(buffer)); + mEffects.valueAt(i)->dump(fd); + } + + + return NO_ERROR; +} + +// This function checks for the parameters which can be offloaded. +// This can be enhanced depending on the capability of the DSP and policy +// of the system. +bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadInfo) +{ + ALOGV("isOffloadSupported: SR=%u, CM=0x%x, Format=0x%x, StreamType=%d," + " BitRate=%u, duration=%lld us, has_video=%d", + offloadInfo.sample_rate, offloadInfo.channel_mask, + offloadInfo.format, + offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us, + offloadInfo.has_video); + + // Check if offload has been disabled + char propValue[PROPERTY_VALUE_MAX]; + if (property_get("audio.offload.disable", propValue, "0")) { + if (atoi(propValue) != 0) { + ALOGV("offload disabled by audio.offload.disable=%s", propValue ); + return false; + } + } + + // Check if stream type is music, then only allow offload as of now. + if (offloadInfo.stream_type != AUDIO_STREAM_MUSIC) + { + ALOGV("isOffloadSupported: stream_type != MUSIC, returning false"); + return false; + } + + //TODO: enable audio offloading with video when ready + if (offloadInfo.has_video) + { + ALOGV("isOffloadSupported: has_video == true, returning false"); + return false; + } + + //If duration is less than minimum value defined in property, return false + if (property_get("audio.offload.min.duration.secs", propValue, NULL)) { + if (offloadInfo.duration_us < (atoi(propValue) * 1000000 )) { + ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%s)", propValue); + return false; + } + } else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) { + ALOGV("Offload denied by duration < default min(=%u)", OFFLOAD_DEFAULT_MIN_DURATION_SECS); + return false; + } + + // Do not allow offloading if one non offloadable effect is enabled. This prevents from + // creating an offloaded track and tearing it down immediately after start when audioflinger + // detects there is an active non offloadable effect. + // FIXME: We should check the audio session here but we do not have it in this context. + // This may prevent offloading in rare situations where effects are left active by apps + // in the background. + if (isNonOffloadableEffectEnabled()) { + return false; + } + + // See if there is a profile to support this. + // AUDIO_DEVICE_NONE + IOProfile *profile = getProfileForDirectOutput(AUDIO_DEVICE_NONE /*ignore device */, + offloadInfo.sample_rate, + offloadInfo.format, + offloadInfo.channel_mask, + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD); + ALOGV("isOffloadSupported() profile %sfound", profile != NULL ? "" : "NOT "); + return (profile != NULL); +} + +// ---------------------------------------------------------------------------- +// AudioPolicyManager +// ---------------------------------------------------------------------------- + +uint32_t AudioPolicyManager::nextUniqueId() +{ + return android_atomic_inc(&mNextUniqueId); +} + +AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface) + : +#ifdef AUDIO_POLICY_TEST + Thread(false), +#endif //AUDIO_POLICY_TEST + mPrimaryOutput((audio_io_handle_t)0), + mPhoneState(AUDIO_MODE_NORMAL), + mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), + mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0), + mA2dpSuspended(false), + mSpeakerDrcEnabled(false), mNextUniqueId(0) +{ + mpClientInterface = clientInterface; + + for (int i = 0; i < AUDIO_POLICY_FORCE_USE_CNT; i++) { + mForceUse[i] = AUDIO_POLICY_FORCE_NONE; + } + + mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER); + if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE) != NO_ERROR) { + if (loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE) != NO_ERROR) { + ALOGE("could not load audio policy configuration file, setting defaults"); + defaultAudioPolicyConfig(); + } + } + // mAvailableOutputDevices and mAvailableInputDevices now contain all attached devices + + // must be done after reading the policy + initializeVolumeCurves(); + + // open all output streams needed to access attached devices + audio_devices_t outputDeviceTypes = mAvailableOutputDevices.types(); + audio_devices_t inputDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; + for (size_t i = 0; i < mHwModules.size(); i++) { + mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->mName); + if (mHwModules[i]->mHandle == 0) { + ALOGW("could not open HW module %s", mHwModules[i]->mName); + continue; + } + // open all output streams needed to access attached devices + // except for direct output streams that are only opened when they are actually + // required by an app. + // This also validates mAvailableOutputDevices list + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) + { + const IOProfile *outProfile = mHwModules[i]->mOutputProfiles[j]; + + if (outProfile->mSupportedDevices.isEmpty()) { + ALOGW("Output profile contains no device on module %s", mHwModules[i]->mName); + continue; + } + + audio_devices_t profileTypes = outProfile->mSupportedDevices.types(); + if ((profileTypes & outputDeviceTypes) && + ((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) { + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(outProfile); + + outputDesc->mDevice = (audio_devices_t)(mDefaultOutputDevice->mType & profileTypes); + audio_io_handle_t output = mpClientInterface->openOutput( + outProfile->mModule->mHandle, + &outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannelMask, + &outputDesc->mLatency, + outputDesc->mFlags); + if (output == 0) { + ALOGW("Cannot open output stream for device %08x on hw module %s", + outputDesc->mDevice, + mHwModules[i]->mName); + delete outputDesc; + } else { + for (size_t i = 0; i < outProfile->mSupportedDevices.size(); i++) { + audio_devices_t type = outProfile->mSupportedDevices[i]->mType; + ssize_t index = + mAvailableOutputDevices.indexOf(outProfile->mSupportedDevices[i]); + // give a valid ID to an attached device once confirmed it is reachable + if ((index >= 0) && (mAvailableOutputDevices[index]->mId == 0)) { + mAvailableOutputDevices[index]->mId = nextUniqueId(); + } + } + if (mPrimaryOutput == 0 && + outProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) { + mPrimaryOutput = output; + } + addOutput(output, outputDesc); + setOutputDevice(output, + outputDesc->mDevice, + true); + } + } + } + // open input streams needed to access attached devices to validate + // mAvailableInputDevices list + for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) + { + const IOProfile *inProfile = mHwModules[i]->mInputProfiles[j]; + + if (inProfile->mSupportedDevices.isEmpty()) { + ALOGW("Input profile contains no device on module %s", mHwModules[i]->mName); + continue; + } + + audio_devices_t profileTypes = inProfile->mSupportedDevices.types(); + if (profileTypes & inputDeviceTypes) { + AudioInputDescriptor *inputDesc = new AudioInputDescriptor(inProfile); + + inputDesc->mInputSource = AUDIO_SOURCE_MIC; + inputDesc->mDevice = inProfile->mSupportedDevices[0]->mType; + audio_io_handle_t input = mpClientInterface->openInput( + inProfile->mModule->mHandle, + &inputDesc->mDevice, + &inputDesc->mSamplingRate, + &inputDesc->mFormat, + &inputDesc->mChannelMask); + + if (input != 0) { + for (size_t i = 0; i < inProfile->mSupportedDevices.size(); i++) { + audio_devices_t type = inProfile->mSupportedDevices[i]->mType; + ssize_t index = + mAvailableInputDevices.indexOf(inProfile->mSupportedDevices[i]); + // give a valid ID to an attached device once confirmed it is reachable + if ((index >= 0) && (mAvailableInputDevices[index]->mId == 0)) { + mAvailableInputDevices[index]->mId = nextUniqueId(); + } + } + mpClientInterface->closeInput(input); + } else { + ALOGW("Cannot open input stream for device %08x on hw module %s", + inputDesc->mDevice, + mHwModules[i]->mName); + } + delete inputDesc; + } + } + } + // make sure all attached devices have been allocated a unique ID + for (size_t i = 0; i < mAvailableOutputDevices.size();) { + if (mAvailableOutputDevices[i]->mId == 0) { + ALOGW("Input device %08x unreachable", mAvailableOutputDevices[i]->mType); + mAvailableOutputDevices.remove(mAvailableOutputDevices[i]); + continue; + } + i++; + } + for (size_t i = 0; i < mAvailableInputDevices.size();) { + if (mAvailableInputDevices[i]->mId == 0) { + ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->mType); + mAvailableInputDevices.remove(mAvailableInputDevices[i]); + continue; + } + i++; + } + // make sure default device is reachable + if (mAvailableOutputDevices.indexOf(mDefaultOutputDevice) < 0) { + ALOGE("Default device %08x is unreachable", mDefaultOutputDevice->mType); + } + + ALOGE_IF((mPrimaryOutput == 0), "Failed to open primary output"); + + updateDevicesAndOutputs(); + +#ifdef AUDIO_POLICY_TEST + if (mPrimaryOutput != 0) { + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"), 0); + mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString()); + + mTestDevice = AUDIO_DEVICE_OUT_SPEAKER; + mTestSamplingRate = 44100; + mTestFormat = AUDIO_FORMAT_PCM_16_BIT; + mTestChannels = AUDIO_CHANNEL_OUT_STEREO; + mTestLatencyMs = 0; + mCurOutput = 0; + mDirectOutput = false; + for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { + mTestOutputs[i] = 0; + } + + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, "AudioPolicyManagerTest"); + run(buffer, ANDROID_PRIORITY_AUDIO); + } +#endif //AUDIO_POLICY_TEST +} + +AudioPolicyManager::~AudioPolicyManager() +{ +#ifdef AUDIO_POLICY_TEST + exit(); +#endif //AUDIO_POLICY_TEST + for (size_t i = 0; i < mOutputs.size(); i++) { + mpClientInterface->closeOutput(mOutputs.keyAt(i)); + delete mOutputs.valueAt(i); + } + for (size_t i = 0; i < mInputs.size(); i++) { + mpClientInterface->closeInput(mInputs.keyAt(i)); + delete mInputs.valueAt(i); + } + for (size_t i = 0; i < mHwModules.size(); i++) { + delete mHwModules[i]; + } + mAvailableOutputDevices.clear(); + mAvailableInputDevices.clear(); +} + +status_t AudioPolicyManager::initCheck() +{ + return (mPrimaryOutput == 0) ? NO_INIT : NO_ERROR; +} + +#ifdef AUDIO_POLICY_TEST +bool AudioPolicyManager::threadLoop() +{ + ALOGV("entering threadLoop()"); + while (!exitPending()) + { + String8 command; + int valueInt; + String8 value; + + Mutex::Autolock _l(mLock); + mWaitWorkCV.waitRelative(mLock, milliseconds(50)); + + command = mpClientInterface->getParameters(0, String8("test_cmd_policy")); + AudioParameter param = AudioParameter(command); + + if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR && + valueInt != 0) { + ALOGV("Test command %s received", command.string()); + String8 target; + if (param.get(String8("target"), target) != NO_ERROR) { + target = "Manager"; + } + if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_output")); + mCurOutput = valueInt; + } + if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_direct")); + if (value == "false") { + mDirectOutput = false; + } else if (value == "true") { + mDirectOutput = true; + } + } + if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_input")); + mTestInput = valueInt; + } + + if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_format")); + int format = AUDIO_FORMAT_INVALID; + if (value == "PCM 16 bits") { + format = AUDIO_FORMAT_PCM_16_BIT; + } else if (value == "PCM 8 bits") { + format = AUDIO_FORMAT_PCM_8_BIT; + } else if (value == "Compressed MP3") { + format = AUDIO_FORMAT_MP3; + } + if (format != AUDIO_FORMAT_INVALID) { + if (target == "Manager") { + mTestFormat = format; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("format"), format); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_channels")); + int channels = 0; + + if (value == "Channels Stereo") { + channels = AUDIO_CHANNEL_OUT_STEREO; + } else if (value == "Channels Mono") { + channels = AUDIO_CHANNEL_OUT_MONO; + } + if (channels != 0) { + if (target == "Manager") { + mTestChannels = channels; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("channels"), channels); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_sampleRate")); + if (valueInt >= 0 && valueInt <= 96000) { + int samplingRate = valueInt; + if (target == "Manager") { + mTestSamplingRate = samplingRate; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("sampling_rate"), samplingRate); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + + if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_reopen")); + + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mPrimaryOutput); + mpClientInterface->closeOutput(mPrimaryOutput); + + audio_module_handle_t moduleHandle = outputDesc->mModule->mHandle; + + delete mOutputs.valueFor(mPrimaryOutput); + mOutputs.removeItem(mPrimaryOutput); + + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(NULL); + outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER; + mPrimaryOutput = mpClientInterface->openOutput(moduleHandle, + &outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannelMask, + &outputDesc->mLatency, + outputDesc->mFlags); + if (mPrimaryOutput == 0) { + ALOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d", + outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannelMask); + } else { + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"), 0); + mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString()); + addOutput(mPrimaryOutput, outputDesc); + } + } + + + mpClientInterface->setParameters(0, String8("test_cmd_policy=")); + } + } + return false; +} + +void AudioPolicyManager::exit() +{ + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +int AudioPolicyManager::testOutputIndex(audio_io_handle_t output) +{ + for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { + if (output == mTestOutputs[i]) return i; + } + return 0; +} +#endif //AUDIO_POLICY_TEST + +// --- + +void AudioPolicyManager::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc) +{ + outputDesc->mId = id; + mOutputs.add(id, outputDesc); +} + + +String8 AudioPolicyManager::addressToParameter(audio_devices_t device, const String8 address) +{ + if (device & AUDIO_DEVICE_OUT_ALL_A2DP) { + return String8("a2dp_sink_address=")+address; + } + return address; +} + +status_t AudioPolicyManager::checkOutputsForDevice(audio_devices_t device, + audio_policy_dev_state_t state, + SortedVector<audio_io_handle_t>& outputs, + const String8 address) +{ + AudioOutputDescriptor *desc; + + if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { + // first list already open outputs that can be routed to this device + for (size_t i = 0; i < mOutputs.size(); i++) { + desc = mOutputs.valueAt(i); + if (!desc->isDuplicated() && (desc->mProfile->mSupportedDevices.types() & device)) { + ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i)); + outputs.add(mOutputs.keyAt(i)); + } + } + // then look for output profiles that can be routed to this device + SortedVector<IOProfile *> profiles; + for (size_t i = 0; i < mHwModules.size(); i++) + { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) + { + if (mHwModules[i]->mOutputProfiles[j]->mSupportedDevices.types() & device) { + ALOGV("checkOutputsForDevice(): adding profile %d from module %d", j, i); + profiles.add(mHwModules[i]->mOutputProfiles[j]); + } + } + } + + if (profiles.isEmpty() && outputs.isEmpty()) { + ALOGW("checkOutputsForDevice(): No output available for device %04x", device); + return BAD_VALUE; + } + + // open outputs for matching profiles if needed. Direct outputs are also opened to + // query for dynamic parameters and will be closed later by setDeviceConnectionState() + for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) { + IOProfile *profile = profiles[profile_index]; + + // nothing to do if one output is already opened for this profile + size_t j; + for (j = 0; j < mOutputs.size(); j++) { + desc = mOutputs.valueAt(j); + if (!desc->isDuplicated() && desc->mProfile == profile) { + break; + } + } + if (j != mOutputs.size()) { + continue; + } + + ALOGV("opening output for device %08x with params %s", device, address.string()); + desc = new AudioOutputDescriptor(profile); + desc->mDevice = device; + audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; + offloadInfo.sample_rate = desc->mSamplingRate; + offloadInfo.format = desc->mFormat; + offloadInfo.channel_mask = desc->mChannelMask; + + audio_io_handle_t output = mpClientInterface->openOutput(profile->mModule->mHandle, + &desc->mDevice, + &desc->mSamplingRate, + &desc->mFormat, + &desc->mChannelMask, + &desc->mLatency, + desc->mFlags, + &offloadInfo); + if (output != 0) { + if (!address.isEmpty()) { + mpClientInterface->setParameters(output, addressToParameter(device, address)); + } + + if (desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { + String8 reply; + char *value; + if (profile->mSamplingRates[0] == 0) { + reply = mpClientInterface->getParameters(output, + String8(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)); + ALOGV("checkOutputsForDevice() direct output sup sampling rates %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + loadSamplingRates(value + 1, profile); + } + } + if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) { + reply = mpClientInterface->getParameters(output, + String8(AUDIO_PARAMETER_STREAM_SUP_FORMATS)); + ALOGV("checkOutputsForDevice() direct output sup formats %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + loadFormats(value + 1, profile); + } + } + if (profile->mChannelMasks[0] == 0) { + reply = mpClientInterface->getParameters(output, + String8(AUDIO_PARAMETER_STREAM_SUP_CHANNELS)); + ALOGV("checkOutputsForDevice() direct output sup channel masks %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + loadOutChannels(value + 1, profile); + } + } + if (((profile->mSamplingRates[0] == 0) && + (profile->mSamplingRates.size() < 2)) || + ((profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) && + (profile->mFormats.size() < 2)) || + ((profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) && + (profile->mChannelMasks.size() < 2))) { + ALOGW("checkOutputsForDevice() direct output missing param"); + mpClientInterface->closeOutput(output); + output = 0; + } else { + addOutput(output, desc); + } + } else { + audio_io_handle_t duplicatedOutput = 0; + // add output descriptor + addOutput(output, desc); + // set initial stream volume for device + applyStreamVolumes(output, device, 0, true); + + //TODO: configure audio effect output stage here + + // open a duplicating output thread for the new output and the primary output + duplicatedOutput = mpClientInterface->openDuplicateOutput(output, + mPrimaryOutput); + if (duplicatedOutput != 0) { + // add duplicated output descriptor + AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor(NULL); + dupOutputDesc->mOutput1 = mOutputs.valueFor(mPrimaryOutput); + dupOutputDesc->mOutput2 = mOutputs.valueFor(output); + dupOutputDesc->mSamplingRate = desc->mSamplingRate; + dupOutputDesc->mFormat = desc->mFormat; + dupOutputDesc->mChannelMask = desc->mChannelMask; + dupOutputDesc->mLatency = desc->mLatency; + addOutput(duplicatedOutput, dupOutputDesc); + applyStreamVolumes(duplicatedOutput, device, 0, true); + } else { + ALOGW("checkOutputsForDevice() could not open dup output for %d and %d", + mPrimaryOutput, output); + mpClientInterface->closeOutput(output); + mOutputs.removeItem(output); + output = 0; + } + } + } + if (output == 0) { + ALOGW("checkOutputsForDevice() could not open output for device %x", device); + delete desc; + profiles.removeAt(profile_index); + profile_index--; + } else { + outputs.add(output); + ALOGV("checkOutputsForDevice(): adding output %d", output); + } + } + + if (profiles.isEmpty()) { + ALOGW("checkOutputsForDevice(): No output available for device %04x", device); + return BAD_VALUE; + } + } else { + // check if one opened output is not needed any more after disconnecting one device + for (size_t i = 0; i < mOutputs.size(); i++) { + desc = mOutputs.valueAt(i); + if (!desc->isDuplicated() && + !(desc->mProfile->mSupportedDevices.types() & + mAvailableOutputDevices.types())) { + ALOGV("checkOutputsForDevice(): disconnecting adding output %d", mOutputs.keyAt(i)); + outputs.add(mOutputs.keyAt(i)); + } + } + for (size_t i = 0; i < mHwModules.size(); i++) + { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) + { + IOProfile *profile = mHwModules[i]->mOutputProfiles[j]; + if ((profile->mSupportedDevices.types() & device) && + (profile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { + ALOGV("checkOutputsForDevice(): clearing direct output profile %d on module %d", + j, i); + if (profile->mSamplingRates[0] == 0) { + profile->mSamplingRates.clear(); + profile->mSamplingRates.add(0); + } + if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) { + profile->mFormats.clear(); + profile->mFormats.add(AUDIO_FORMAT_DEFAULT); + } + if (profile->mChannelMasks[0] == 0) { + profile->mChannelMasks.clear(); + profile->mChannelMasks.add(0); + } + } + } + } + } + return NO_ERROR; +} + +void AudioPolicyManager::closeOutput(audio_io_handle_t output) +{ + ALOGV("closeOutput(%d)", output); + + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + if (outputDesc == NULL) { + ALOGW("closeOutput() unknown output %d", output); + return; + } + + // look for duplicated outputs connected to the output being removed. + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *dupOutputDesc = mOutputs.valueAt(i); + if (dupOutputDesc->isDuplicated() && + (dupOutputDesc->mOutput1 == outputDesc || + dupOutputDesc->mOutput2 == outputDesc)) { + AudioOutputDescriptor *outputDesc2; + if (dupOutputDesc->mOutput1 == outputDesc) { + outputDesc2 = dupOutputDesc->mOutput2; + } else { + outputDesc2 = dupOutputDesc->mOutput1; + } + // As all active tracks on duplicated output will be deleted, + // and as they were also referenced on the other output, the reference + // count for their stream type must be adjusted accordingly on + // the other output. + for (int j = 0; j < AUDIO_STREAM_CNT; j++) { + int refCount = dupOutputDesc->mRefCount[j]; + outputDesc2->changeRefCount((audio_stream_type_t)j,-refCount); + } + audio_io_handle_t duplicatedOutput = mOutputs.keyAt(i); + ALOGV("closeOutput() closing also duplicated output %d", duplicatedOutput); + + mpClientInterface->closeOutput(duplicatedOutput); + delete mOutputs.valueFor(duplicatedOutput); + mOutputs.removeItem(duplicatedOutput); + } + } + + AudioParameter param; + param.add(String8("closing"), String8("true")); + mpClientInterface->setParameters(output, param.toString()); + + mpClientInterface->closeOutput(output); + delete outputDesc; + mOutputs.removeItem(output); + mPreviousOutputs = mOutputs; +} + +SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device, + DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> openOutputs) +{ + SortedVector<audio_io_handle_t> outputs; + + ALOGVV("getOutputsForDevice() device %04x", device); + for (size_t i = 0; i < openOutputs.size(); i++) { + ALOGVV("output %d isDuplicated=%d device=%04x", + i, openOutputs.valueAt(i)->isDuplicated(), openOutputs.valueAt(i)->supportedDevices()); + if ((device & openOutputs.valueAt(i)->supportedDevices()) == device) { + ALOGVV("getOutputsForDevice() found output %d", openOutputs.keyAt(i)); + outputs.add(openOutputs.keyAt(i)); + } + } + return outputs; +} + +bool AudioPolicyManager::vectorsEqual(SortedVector<audio_io_handle_t>& outputs1, + SortedVector<audio_io_handle_t>& outputs2) +{ + if (outputs1.size() != outputs2.size()) { + return false; + } + for (size_t i = 0; i < outputs1.size(); i++) { + if (outputs1[i] != outputs2[i]) { + return false; + } + } + return true; +} + +void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) +{ + audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/); + audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/); + SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs); + SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(newDevice, mOutputs); + + if (!vectorsEqual(srcOutputs,dstOutputs)) { + ALOGV("checkOutputForStrategy() strategy %d, moving from output %d to output %d", + strategy, srcOutputs[0], dstOutputs[0]); + // mute strategy while moving tracks from one output to another + for (size_t i = 0; i < srcOutputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueFor(srcOutputs[i]); + if (desc->isStrategyActive(strategy)) { + setStrategyMute(strategy, true, srcOutputs[i]); + setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice); + } + } + + // Move effects associated to this strategy from previous output to new output + if (strategy == STRATEGY_MEDIA) { + audio_io_handle_t fxOutput = selectOutputForEffects(dstOutputs); + SortedVector<audio_io_handle_t> moved; + for (size_t i = 0; i < mEffects.size(); i++) { + EffectDescriptor *desc = mEffects.valueAt(i); + if (desc->mSession == AUDIO_SESSION_OUTPUT_MIX && + desc->mIo != fxOutput) { + if (moved.indexOf(desc->mIo) < 0) { + ALOGV("checkOutputForStrategy() moving effect %d to output %d", + mEffects.keyAt(i), fxOutput); + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, desc->mIo, + fxOutput); + moved.add(desc->mIo); + } + desc->mIo = fxOutput; + } + } + } + // Move tracks associated to this strategy from previous output to new output + for (int i = 0; i < AUDIO_STREAM_CNT; i++) { + if (getStrategy((audio_stream_type_t)i) == strategy) { + mpClientInterface->invalidateStream((audio_stream_type_t)i); + } + } + } +} + +void AudioPolicyManager::checkOutputForAllStrategies() +{ + checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE); + checkOutputForStrategy(STRATEGY_PHONE); + checkOutputForStrategy(STRATEGY_SONIFICATION); + checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); + checkOutputForStrategy(STRATEGY_MEDIA); + checkOutputForStrategy(STRATEGY_DTMF); +} + +audio_io_handle_t AudioPolicyManager::getA2dpOutput() +{ + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) { + return mOutputs.keyAt(i); + } + } + + return 0; +} + +void AudioPolicyManager::checkA2dpSuspend() +{ + audio_io_handle_t a2dpOutput = getA2dpOutput(); + if (a2dpOutput == 0) { + mA2dpSuspended = false; + return; + } + + bool isScoConnected = + (mAvailableInputDevices.types() & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) != 0; + // suspend A2DP output if: + // (NOT already suspended) && + // ((SCO device is connected && + // (forced usage for communication || for record is SCO))) || + // (phone state is ringing || in call) + // + // restore A2DP output if: + // (Already suspended) && + // ((SCO device is NOT connected || + // (forced usage NOT for communication && NOT for record is SCO))) && + // (phone state is NOT ringing && NOT in call) + // + if (mA2dpSuspended) { + if ((!isScoConnected || + ((mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] != AUDIO_POLICY_FORCE_BT_SCO) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] != AUDIO_POLICY_FORCE_BT_SCO))) && + ((mPhoneState != AUDIO_MODE_IN_CALL) && + (mPhoneState != AUDIO_MODE_RINGTONE))) { + + mpClientInterface->restoreOutput(a2dpOutput); + mA2dpSuspended = false; + } + } else { + if ((isScoConnected && + ((mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) || + (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO))) || + ((mPhoneState == AUDIO_MODE_IN_CALL) || + (mPhoneState == AUDIO_MODE_RINGTONE))) { + + mpClientInterface->suspendOutput(a2dpOutput); + mA2dpSuspended = true; + } + } +} + +audio_devices_t AudioPolicyManager::getNewDevice(audio_io_handle_t output, bool fromCache) +{ + audio_devices_t device = AUDIO_DEVICE_NONE; + + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + // check the following by order of priority to request a routing change if necessary: + // 1: the strategy enforced audible is active on the output: + // use device for strategy enforced audible + // 2: we are in call or the strategy phone is active on the output: + // use device for strategy phone + // 3: the strategy sonification is active on the output: + // use device for strategy sonification + // 4: the strategy "respectful" sonification is active on the output: + // use device for strategy "respectful" sonification + // 5: the strategy media is active on the output: + // use device for strategy media + // 6: the strategy DTMF is active on the output: + // use device for strategy DTMF + if (outputDesc->isStrategyActive(STRATEGY_ENFORCED_AUDIBLE)) { + device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); + } else if (isInCall() || + outputDesc->isStrategyActive(STRATEGY_PHONE)) { + device = getDeviceForStrategy(STRATEGY_PHONE, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION)) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION_RESPECTFUL)) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION_RESPECTFUL, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_MEDIA)) { + device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_DTMF)) { + device = getDeviceForStrategy(STRATEGY_DTMF, fromCache); + } + + ALOGV("getNewDevice() selected device %x", device); + return device; +} + +uint32_t AudioPolicyManager::getStrategyForStream(audio_stream_type_t stream) { + return (uint32_t)getStrategy(stream); +} + +audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stream) { + audio_devices_t devices; + // By checking the range of stream before calling getStrategy, we avoid + // getStrategy's behavior for invalid streams. getStrategy would do a ALOGE + // and then return STRATEGY_MEDIA, but we want to return the empty set. + if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_CNT) { + devices = AUDIO_DEVICE_NONE; + } else { + AudioPolicyManager::routing_strategy strategy = getStrategy(stream); + devices = getDeviceForStrategy(strategy, true /*fromCache*/); + } + return devices; +} + +AudioPolicyManager::routing_strategy AudioPolicyManager::getStrategy( + audio_stream_type_t stream) { + // stream to strategy mapping + switch (stream) { + case AUDIO_STREAM_VOICE_CALL: + case AUDIO_STREAM_BLUETOOTH_SCO: + return STRATEGY_PHONE; + case AUDIO_STREAM_RING: + case AUDIO_STREAM_ALARM: + return STRATEGY_SONIFICATION; + case AUDIO_STREAM_NOTIFICATION: + return STRATEGY_SONIFICATION_RESPECTFUL; + case AUDIO_STREAM_DTMF: + return STRATEGY_DTMF; + default: + ALOGE("unknown stream type"); + case AUDIO_STREAM_SYSTEM: + // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs + // while key clicks are played produces a poor result + case AUDIO_STREAM_TTS: + case AUDIO_STREAM_MUSIC: + return STRATEGY_MEDIA; + case AUDIO_STREAM_ENFORCED_AUDIBLE: + return STRATEGY_ENFORCED_AUDIBLE; + } +} + +void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t stream) { + switch(stream) { + case AUDIO_STREAM_MUSIC: + checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); + updateDevicesAndOutputs(); + break; + default: + break; + } +} + +audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy, + bool fromCache) +{ + uint32_t device = AUDIO_DEVICE_NONE; + + if (fromCache) { + ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x", + strategy, mDeviceForStrategy[strategy]); + return mDeviceForStrategy[strategy]; + } + audio_devices_t availableOutputDeviceTypes = mAvailableOutputDevices.types(); + switch (strategy) { + + case STRATEGY_SONIFICATION_RESPECTFUL: + if (isInCall()) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + } else if (isStreamActiveRemotely(AUDIO_STREAM_MUSIC, + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + // while media is playing on a remote device, use the the sonification behavior. + // Note that we test this usecase before testing if media is playing because + // the isStreamActive() method only informs about the activity of a stream, not + // if it's for local playback. Note also that we use the same delay between both tests + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + } else if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + // while media is playing (or has recently played), use the same device + device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + } else { + // when media is not playing anymore, fall back on the sonification behavior + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + } + + break; + + case STRATEGY_DTMF: + if (!isInCall()) { + // when off call, DTMF strategy follows the same rules as MEDIA strategy + device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + break; + } + // when in call, DTMF and PHONE strategies follow the same rules + // FALL THROUGH + + case STRATEGY_PHONE: + // for phone strategy, we first consider the forced use and then the available devices by order + // of priority + switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { + case AUDIO_POLICY_FORCE_BT_SCO: + if (!isInCall() || strategy != STRATEGY_DTMF) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO; + if (device) break; + // if SCO device is requested but no SCO device is available, fall back to default case + // FALL THROUGH + + default: // FORCE_NONE + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP + if (!isInCall() && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADSET; + if (device) break; + if (mPhoneState != AUDIO_MODE_IN_CALL) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_EARPIECE; + if (device) break; + device = mDefaultOutputDevice->mType; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE"); + } + break; + + case AUDIO_POLICY_FORCE_SPEAKER: + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to + // A2DP speaker when forcing to speaker output + if (!isInCall() && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + if (device) break; + } + if (mPhoneState != AUDIO_MODE_IN_CALL) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; + if (device) break; + device = mDefaultOutputDevice->mType; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER"); + } + break; + } + break; + + case STRATEGY_SONIFICATION: + + // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by + // handleIncallSonification(). + if (isInCall()) { + device = getDeviceForStrategy(STRATEGY_PHONE, false /*fromCache*/); + break; + } + // FALL THROUGH + + case STRATEGY_ENFORCED_AUDIBLE: + // strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION + // except: + // - when in call where it doesn't default to STRATEGY_PHONE behavior + // - in countries where not enforced in which case it follows STRATEGY_MEDIA + + if ((strategy == STRATEGY_SONIFICATION) || + (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() speaker device not found for STRATEGY_SONIFICATION"); + } + } + // The second device used for sonification is the same as the device used by media strategy + // FALL THROUGH + + case STRATEGY_MEDIA: { + uint32_t device2 = AUDIO_DEVICE_NONE; + if (strategy != STRATEGY_SONIFICATION) { + // no sonification on remote submix (e.g. WFD) + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + } + if ((device2 == AUDIO_DEVICE_NONE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + } + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADSET; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + } + if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) { + // no sonification on aux digital (e.g. HDMI) + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; + } + if ((device2 == AUDIO_DEVICE_NONE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK] == AUDIO_POLICY_FORCE_ANALOG_DOCK)) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; + } + + // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or + // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise + device |= device2; + if (device) break; + device = mDefaultOutputDevice->mType; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA"); + } + } break; + + default: + ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy); + break; + } + + ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device); + return device; +} + +void AudioPolicyManager::updateDevicesAndOutputs() +{ + for (int i = 0; i < NUM_STRATEGIES; i++) { + mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/); + } + mPreviousOutputs = mOutputs; +} + +uint32_t AudioPolicyManager::checkDeviceMuteStrategies(AudioOutputDescriptor *outputDesc, + audio_devices_t prevDevice, + uint32_t delayMs) +{ + // mute/unmute strategies using an incompatible device combination + // if muting, wait for the audio in pcm buffer to be drained before proceeding + // if unmuting, unmute only after the specified delay + if (outputDesc->isDuplicated()) { + return 0; + } + + uint32_t muteWaitMs = 0; + audio_devices_t device = outputDesc->device(); + bool shouldMute = outputDesc->isActive() && (popcount(device) >= 2); + // temporary mute output if device selection changes to avoid volume bursts due to + // different per device volumes + bool tempMute = outputDesc->isActive() && (device != prevDevice); + + for (size_t i = 0; i < NUM_STRATEGIES; i++) { + audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/); + bool mute = shouldMute && (curDevice & device) && (curDevice != device); + bool doMute = false; + + if (mute && !outputDesc->mStrategyMutedByDevice[i]) { + doMute = true; + outputDesc->mStrategyMutedByDevice[i] = true; + } else if (!mute && outputDesc->mStrategyMutedByDevice[i]){ + doMute = true; + outputDesc->mStrategyMutedByDevice[i] = false; + } + if (doMute || tempMute) { + for (size_t j = 0; j < mOutputs.size(); j++) { + AudioOutputDescriptor *desc = mOutputs.valueAt(j); + // skip output if it does not share any device with current output + if ((desc->supportedDevices() & outputDesc->supportedDevices()) + == AUDIO_DEVICE_NONE) { + continue; + } + audio_io_handle_t curOutput = mOutputs.keyAt(j); + ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x) on output %d", + mute ? "muting" : "unmuting", i, curDevice, curOutput); + setStrategyMute((routing_strategy)i, mute, curOutput, mute ? 0 : delayMs); + if (desc->isStrategyActive((routing_strategy)i)) { + // do tempMute only for current output + if (tempMute && (desc == outputDesc)) { + setStrategyMute((routing_strategy)i, true, curOutput); + setStrategyMute((routing_strategy)i, false, curOutput, + desc->latency() * 2, device); + } + if ((tempMute && (desc == outputDesc)) || mute) { + if (muteWaitMs < desc->latency()) { + muteWaitMs = desc->latency(); + } + } + } + } + } + } + + // FIXME: should not need to double latency if volume could be applied immediately by the + // audioflinger mixer. We must account for the delay between now and the next time + // the audioflinger thread for this output will process a buffer (which corresponds to + // one buffer size, usually 1/2 or 1/4 of the latency). + muteWaitMs *= 2; + // wait for the PCM output buffers to empty before proceeding with the rest of the command + if (muteWaitMs > delayMs) { + muteWaitMs -= delayMs; + usleep(muteWaitMs * 1000); + return muteWaitMs; + } + return 0; +} + +uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output, + audio_devices_t device, + bool force, + int delayMs) +{ + ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs); + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + AudioParameter param; + uint32_t muteWaitMs; + + if (outputDesc->isDuplicated()) { + muteWaitMs = setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs); + muteWaitMs += setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs); + return muteWaitMs; + } + // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current + // output profile + if ((device != AUDIO_DEVICE_NONE) && + ((device & outputDesc->mProfile->mSupportedDevices.types()) == 0)) { + return 0; + } + + // filter devices according to output selected + device = (audio_devices_t)(device & outputDesc->mProfile->mSupportedDevices.types()); + + audio_devices_t prevDevice = outputDesc->mDevice; + + ALOGV("setOutputDevice() prevDevice %04x", prevDevice); + + if (device != AUDIO_DEVICE_NONE) { + outputDesc->mDevice = device; + } + muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs); + + // Do not change the routing if: + // - the requested device is AUDIO_DEVICE_NONE + // - the requested device is the same as current device and force is not specified. + // Doing this check here allows the caller to call setOutputDevice() without conditions + if ((device == AUDIO_DEVICE_NONE || device == prevDevice) && !force) { + ALOGV("setOutputDevice() setting same device %04x or null device for output %d", device, output); + return muteWaitMs; + } + + ALOGV("setOutputDevice() changing device"); + // do the routing + param.addInt(String8(AudioParameter::keyRouting), (int)device); + mpClientInterface->setParameters(output, param.toString(), delayMs); + + // update stream volumes according to new device + applyStreamVolumes(output, device, delayMs); + + return muteWaitMs; +} + +AudioPolicyManager::IOProfile *AudioPolicyManager::getInputProfile(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask) +{ + // Choose an input profile based on the requested capture parameters: select the first available + // profile supporting all requested parameters. + + for (size_t i = 0; i < mHwModules.size(); i++) + { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) + { + IOProfile *profile = mHwModules[i]->mInputProfiles[j]; + if (profile->isCompatibleProfile(device, samplingRate, format, + channelMask, AUDIO_OUTPUT_FLAG_NONE)) { + return profile; + } + } + } + return NULL; +} + +audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource) +{ + uint32_t device = AUDIO_DEVICE_NONE; + audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & + ~AUDIO_DEVICE_BIT_IN; + switch (inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { + device = AUDIO_DEVICE_IN_VOICE_CALL; + break; + } + // FALL THROUGH + + case AUDIO_SOURCE_DEFAULT: + case AUDIO_SOURCE_MIC: + case AUDIO_SOURCE_VOICE_RECOGNITION: + case AUDIO_SOURCE_HOTWORD: + case AUDIO_SOURCE_VOICE_COMMUNICATION: + if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO && + availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_CAMCORDER: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { + device = AUDIO_DEVICE_IN_BACK_MIC; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + case AUDIO_SOURCE_VOICE_CALL: + if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { + device = AUDIO_DEVICE_IN_VOICE_CALL; + } + break; + case AUDIO_SOURCE_REMOTE_SUBMIX: + if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { + device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + } + break; + default: + ALOGW("getDeviceForInputSource() invalid input source %d", inputSource); + break; + } + ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device); + return device; +} + +bool AudioPolicyManager::isVirtualInputDevice(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~APM_AUDIO_IN_DEVICE_VIRTUAL_ALL) == 0)) + return true; + } + return false; +} + +audio_io_handle_t AudioPolicyManager::getActiveInput(bool ignoreVirtualInputs) +{ + for (size_t i = 0; i < mInputs.size(); i++) { + const AudioInputDescriptor * input_descriptor = mInputs.valueAt(i); + if ((input_descriptor->mRefCount > 0) + && (!ignoreVirtualInputs || !isVirtualInputDevice(input_descriptor->mDevice))) { + return mInputs.keyAt(i); + } + } + return 0; +} + + +audio_devices_t AudioPolicyManager::getDeviceForVolume(audio_devices_t device) +{ + if (device == AUDIO_DEVICE_NONE) { + // this happens when forcing a route update and no track is active on an output. + // In this case the returned category is not important. + device = AUDIO_DEVICE_OUT_SPEAKER; + } else if (popcount(device) > 1) { + // Multiple device selection is either: + // - speaker + one other device: give priority to speaker in this case. + // - one A2DP device + another device: happens with duplicated output. In this case + // retain the device on the A2DP output as the other must not correspond to an active + // selection if not the speaker. + if (device & AUDIO_DEVICE_OUT_SPEAKER) { + device = AUDIO_DEVICE_OUT_SPEAKER; + } else { + device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP); + } + } + + ALOGW_IF(popcount(device) != 1, + "getDeviceForVolume() invalid device combination: %08x", + device); + + return device; +} + +AudioPolicyManager::device_category AudioPolicyManager::getDeviceCategory(audio_devices_t device) +{ + switch(getDeviceForVolume(device)) { + case AUDIO_DEVICE_OUT_EARPIECE: + return DEVICE_CATEGORY_EARPIECE; + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: + return DEVICE_CATEGORY_HEADSET; + case AUDIO_DEVICE_OUT_SPEAKER: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: + case AUDIO_DEVICE_OUT_AUX_DIGITAL: + case AUDIO_DEVICE_OUT_USB_ACCESSORY: + case AUDIO_DEVICE_OUT_USB_DEVICE: + case AUDIO_DEVICE_OUT_REMOTE_SUBMIX: + default: + return DEVICE_CATEGORY_SPEAKER; + } +} + +float AudioPolicyManager::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi) +{ + device_category deviceCategory = getDeviceCategory(device); + const VolumeCurvePoint *curve = streamDesc.mVolumeCurve[deviceCategory]; + + // the volume index in the UI is relative to the min and max volume indices for this stream type + int nbSteps = 1 + curve[VOLMAX].mIndex - + curve[VOLMIN].mIndex; + int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / + (streamDesc.mIndexMax - streamDesc.mIndexMin); + + // find what part of the curve this index volume belongs to, or if it's out of bounds + int segment = 0; + if (volIdx < curve[VOLMIN].mIndex) { // out of bounds + return 0.0f; + } else if (volIdx < curve[VOLKNEE1].mIndex) { + segment = 0; + } else if (volIdx < curve[VOLKNEE2].mIndex) { + segment = 1; + } else if (volIdx <= curve[VOLMAX].mIndex) { + segment = 2; + } else { // out of bounds + return 1.0f; + } + + // linear interpolation in the attenuation table in dB + float decibels = curve[segment].mDBAttenuation + + ((float)(volIdx - curve[segment].mIndex)) * + ( (curve[segment+1].mDBAttenuation - + curve[segment].mDBAttenuation) / + ((float)(curve[segment+1].mIndex - + curve[segment].mIndex)) ); + + float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) + + ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f", + curve[segment].mIndex, volIdx, + curve[segment+1].mIndex, + curve[segment].mDBAttenuation, + decibels, + curve[segment+1].mDBAttenuation, + amplification); + + return amplification; +} + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sDefaultVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -49.5f}, {33, -33.5f}, {66, -17.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sDefaultMediaVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -58.0f}, {20, -40.0f}, {60, -17.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -56.0f}, {20, -34.0f}, {60, -11.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerSonificationVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { + {1, -35.7f}, {33, -26.1f}, {66, -13.2f}, {100, 0.0f} +}; + +// AUDIO_STREAM_SYSTEM, AUDIO_STREAM_ENFORCED_AUDIBLE and AUDIO_STREAM_DTMF volume tracks +// AUDIO_STREAM_RING on phones and AUDIO_STREAM_MUSIC on tablets. +// AUDIO_STREAM_DTMF tracks AUDIO_STREAM_VOICE_CALL while in call (See AudioService.java). +// The range is constrained between -24dB and -6dB over speaker and -30dB and -18dB over headset. + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sDefaultSystemVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -24.0f}, {33, -18.0f}, {66, -12.0f}, {100, -6.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sDefaultSystemVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { + {1, -34.0f}, {33, -24.0f}, {66, -15.0f}, {100, -6.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sHeadsetSystemVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -30.0f}, {33, -26.0f}, {66, -22.0f}, {100, -18.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sDefaultVoiceVolumeCurve[AudioPolicyManager::VOLCNT] = { + {0, -42.0f}, {33, -28.0f}, {66, -14.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerVoiceVolumeCurve[AudioPolicyManager::VOLCNT] = { + {0, -24.0f}, {33, -16.0f}, {66, -8.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + *AudioPolicyManager::sVolumeProfiles[AUDIO_STREAM_CNT] + [AudioPolicyManager::DEVICE_CATEGORY_CNT] = { + { // AUDIO_STREAM_VOICE_CALL + sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVoiceVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_SYSTEM + sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultSystemVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_RING + sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_MUSIC + sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_ALARM + sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_NOTIFICATION + sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_BLUETOOTH_SCO + sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVoiceVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_ENFORCED_AUDIBLE + sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultSystemVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_DTMF + sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultSystemVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_TTS + sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, +}; + +void AudioPolicyManager::initializeVolumeCurves() +{ + for (int i = 0; i < AUDIO_STREAM_CNT; i++) { + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[i].mVolumeCurve[j] = + sVolumeProfiles[i][j]; + } + } + + // Check availability of DRC on speaker path: if available, override some of the speaker curves + if (mSpeakerDrcEnabled) { + mStreams[AUDIO_STREAM_SYSTEM].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sDefaultSystemVolumeCurveDrc; + mStreams[AUDIO_STREAM_RING].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_ALARM].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_NOTIFICATION].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerSonificationVolumeCurveDrc; + } +} + +float AudioPolicyManager::computeVolume(audio_stream_type_t stream, + int index, + audio_io_handle_t output, + audio_devices_t device) +{ + float volume = 1.0; + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + StreamDescriptor &streamDesc = mStreams[stream]; + + if (device == AUDIO_DEVICE_NONE) { + device = outputDesc->device(); + } + + // if volume is not 0 (not muted), force media volume to max on digital output + if (stream == AUDIO_STREAM_MUSIC && + index != mStreams[stream].mIndexMin && + (device == AUDIO_DEVICE_OUT_AUX_DIGITAL || + device == AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET || + device == AUDIO_DEVICE_OUT_USB_ACCESSORY || + device == AUDIO_DEVICE_OUT_USB_DEVICE)) { + return 1.0; + } + + volume = volIndexToAmpl(device, streamDesc, index); + + // if a headset is connected, apply the following rules to ring tones and notifications + // to avoid sound level bursts in user's ears: + // - always attenuate ring tones and notifications volume by 6dB + // - if music is playing, always limit the volume to current music volume, + // with a minimum threshold at -36dB so that notification is always perceived. + const routing_strategy stream_strategy = getStrategy(stream); + if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_WIRED_HEADPHONE)) && + ((stream_strategy == STRATEGY_SONIFICATION) + || (stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL) + || (stream == AUDIO_STREAM_SYSTEM) + || ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_NONE))) && + streamDesc.mCanBeMuted) { + volume *= SONIFICATION_HEADSET_VOLUME_FACTOR; + // when the phone is ringing we must consider that music could have been paused just before + // by the music application and behave as if music was active if the last music track was + // just stopped + if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) || + mLimitRingtoneVolume) { + audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/); + float musicVol = computeVolume(AUDIO_STREAM_MUSIC, + mStreams[AUDIO_STREAM_MUSIC].getVolumeIndex(musicDevice), + output, + musicDevice); + float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? + musicVol : SONIFICATION_HEADSET_VOLUME_MIN; + if (volume > minVol) { + volume = minVol; + ALOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol); + } + } + } + + return volume; +} + +status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, + int index, + audio_io_handle_t output, + audio_devices_t device, + int delayMs, + bool force) +{ + + // do not change actual stream volume if the stream is muted + if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) { + ALOGVV("checkAndSetVolume() stream %d muted count %d", + stream, mOutputs.valueFor(output)->mMuteCount[stream]); + return NO_ERROR; + } + + // do not change in call volume if bluetooth is connected and vice versa + if ((stream == AUDIO_STREAM_VOICE_CALL && + mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) || + (stream == AUDIO_STREAM_BLUETOOTH_SCO && + mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] != AUDIO_POLICY_FORCE_BT_SCO)) { + ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm", + stream, mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]); + return INVALID_OPERATION; + } + + float volume = computeVolume(stream, index, output, device); + // We actually change the volume if: + // - the float value returned by computeVolume() changed + // - the force flag is set + if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || + force) { + mOutputs.valueFor(output)->mCurVolume[stream] = volume; + ALOGVV("checkAndSetVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs); + // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is + // enabled + if (stream == AUDIO_STREAM_BLUETOOTH_SCO) { + mpClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volume, output, delayMs); + } + mpClientInterface->setStreamVolume(stream, volume, output, delayMs); + } + + if (stream == AUDIO_STREAM_VOICE_CALL || + stream == AUDIO_STREAM_BLUETOOTH_SCO) { + float voiceVolume; + // Force voice volume to max for bluetooth SCO as volume is managed by the headset + if (stream == AUDIO_STREAM_VOICE_CALL) { + voiceVolume = (float)index/(float)mStreams[stream].mIndexMax; + } else { + voiceVolume = 1.0; + } + + if (voiceVolume != mLastVoiceVolume && output == mPrimaryOutput) { + mpClientInterface->setVoiceVolume(voiceVolume, delayMs); + mLastVoiceVolume = voiceVolume; + } + } + + return NO_ERROR; +} + +void AudioPolicyManager::applyStreamVolumes(audio_io_handle_t output, + audio_devices_t device, + int delayMs, + bool force) +{ + ALOGVV("applyStreamVolumes() for output %d and device %x", output, device); + + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + checkAndSetVolume((audio_stream_type_t)stream, + mStreams[stream].getVolumeIndex(device), + output, + device, + delayMs, + force); + } +} + +void AudioPolicyManager::setStrategyMute(routing_strategy strategy, + bool on, + audio_io_handle_t output, + int delayMs, + audio_devices_t device) +{ + ALOGVV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output); + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + if (getStrategy((audio_stream_type_t)stream) == strategy) { + setStreamMute((audio_stream_type_t)stream, on, output, delayMs, device); + } + } +} + +void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, + bool on, + audio_io_handle_t output, + int delayMs, + audio_devices_t device) +{ + StreamDescriptor &streamDesc = mStreams[stream]; + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + if (device == AUDIO_DEVICE_NONE) { + device = outputDesc->device(); + } + + ALOGVV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d device %04x", + stream, on, output, outputDesc->mMuteCount[stream], device); + + if (on) { + if (outputDesc->mMuteCount[stream] == 0) { + if (streamDesc.mCanBeMuted && + ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) || + (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_NONE))) { + checkAndSetVolume(stream, 0, output, device, delayMs); + } + } + // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored + outputDesc->mMuteCount[stream]++; + } else { + if (outputDesc->mMuteCount[stream] == 0) { + ALOGV("setStreamMute() unmuting non muted stream!"); + return; + } + if (--outputDesc->mMuteCount[stream] == 0) { + checkAndSetVolume(stream, + streamDesc.getVolumeIndex(device), + output, + device, + delayMs); + } + } +} + +void AudioPolicyManager::handleIncallSonification(audio_stream_type_t stream, + bool starting, bool stateChange) +{ + // if the stream pertains to sonification strategy and we are in call we must + // mute the stream if it is low visibility. If it is high visibility, we must play a tone + // in the device used for phone strategy and play the tone if the selected device does not + // interfere with the device used for phone strategy + // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as + // many times as there are active tracks on the output + const routing_strategy stream_strategy = getStrategy(stream); + if ((stream_strategy == STRATEGY_SONIFICATION) || + ((stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL))) { + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mPrimaryOutput); + ALOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d", + stream, starting, outputDesc->mDevice, stateChange); + if (outputDesc->mRefCount[stream]) { + int muteCount = 1; + if (stateChange) { + muteCount = outputDesc->mRefCount[stream]; + } + if (audio_is_low_visibility(stream)) { + ALOGV("handleIncallSonification() low visibility, muteCount %d", muteCount); + for (int i = 0; i < muteCount; i++) { + setStreamMute(stream, starting, mPrimaryOutput); + } + } else { + ALOGV("handleIncallSonification() high visibility"); + if (outputDesc->device() & + getDeviceForStrategy(STRATEGY_PHONE, true /*fromCache*/)) { + ALOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount); + for (int i = 0; i < muteCount; i++) { + setStreamMute(stream, starting, mPrimaryOutput); + } + } + if (starting) { + mpClientInterface->startTone(AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION, + AUDIO_STREAM_VOICE_CALL); + } else { + mpClientInterface->stopTone(); + } + } + } + } +} + +bool AudioPolicyManager::isInCall() +{ + return isStateInCall(mPhoneState); +} + +bool AudioPolicyManager::isStateInCall(int state) { + return ((state == AUDIO_MODE_IN_CALL) || + (state == AUDIO_MODE_IN_COMMUNICATION)); +} + +uint32_t AudioPolicyManager::getMaxEffectsCpuLoad() +{ + return MAX_EFFECTS_CPU_LOAD; +} + +uint32_t AudioPolicyManager::getMaxEffectsMemory() +{ + return MAX_EFFECTS_MEMORY; +} + +// --- AudioOutputDescriptor class implementation + +AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor( + const IOProfile *profile) + : mId(0), mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), + mChannelMask(0), mLatency(0), + mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), + mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0) +{ + // clear usage count for all stream types + for (int i = 0; i < AUDIO_STREAM_CNT; i++) { + mRefCount[i] = 0; + mCurVolume[i] = -1.0; + mMuteCount[i] = 0; + mStopTime[i] = 0; + } + for (int i = 0; i < NUM_STRATEGIES; i++) { + mStrategyMutedByDevice[i] = false; + } + if (profile != NULL) { + mSamplingRate = profile->mSamplingRates[0]; + mFormat = profile->mFormats[0]; + mChannelMask = profile->mChannelMasks[0]; + mFlags = profile->mFlags; + } +} + +audio_devices_t AudioPolicyManager::AudioOutputDescriptor::device() const +{ + if (isDuplicated()) { + return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice); + } else { + return mDevice; + } +} + +uint32_t AudioPolicyManager::AudioOutputDescriptor::latency() +{ + if (isDuplicated()) { + return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency; + } else { + return mLatency; + } +} + +bool AudioPolicyManager::AudioOutputDescriptor::sharesHwModuleWith( + const AudioOutputDescriptor *outputDesc) +{ + if (isDuplicated()) { + return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc); + } else if (outputDesc->isDuplicated()){ + return sharesHwModuleWith(outputDesc->mOutput1) || sharesHwModuleWith(outputDesc->mOutput2); + } else { + return (mProfile->mModule == outputDesc->mProfile->mModule); + } +} + +void AudioPolicyManager::AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, + int delta) +{ + // forward usage count change to attached outputs + if (isDuplicated()) { + mOutput1->changeRefCount(stream, delta); + mOutput2->changeRefCount(stream, delta); + } + if ((delta + (int)mRefCount[stream]) < 0) { + ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", + delta, stream, mRefCount[stream]); + mRefCount[stream] = 0; + return; + } + mRefCount[stream] += delta; + ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]); +} + +audio_devices_t AudioPolicyManager::AudioOutputDescriptor::supportedDevices() +{ + if (isDuplicated()) { + return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices()); + } else { + return mProfile->mSupportedDevices.types() ; + } +} + +bool AudioPolicyManager::AudioOutputDescriptor::isActive(uint32_t inPastMs) const +{ + return isStrategyActive(NUM_STRATEGIES, inPastMs); +} + +bool AudioPolicyManager::AudioOutputDescriptor::isStrategyActive(routing_strategy strategy, + uint32_t inPastMs, + nsecs_t sysTime) const +{ + if ((sysTime == 0) && (inPastMs != 0)) { + sysTime = systemTime(); + } + for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { + if (((getStrategy((audio_stream_type_t)i) == strategy) || + (NUM_STRATEGIES == strategy)) && + isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioPolicyManager::AudioOutputDescriptor::isStreamActive(audio_stream_type_t stream, + uint32_t inPastMs, + nsecs_t sysTime) const +{ + if (mRefCount[stream] != 0) { + return true; + } + if (inPastMs == 0) { + return false; + } + if (sysTime == 0) { + sysTime = systemTime(); + } + if (ns2ms(sysTime - mStopTime[stream]) < inPastMs) { + return true; + } + return false; +} + + +status_t AudioPolicyManager::AudioOutputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); + result.append(buffer); + snprintf(buffer, SIZE, " Format: %08x\n", mFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask); + result.append(buffer); + snprintf(buffer, SIZE, " Latency: %d\n", mLatency); + result.append(buffer); + snprintf(buffer, SIZE, " Flags %08x\n", mFlags); + result.append(buffer); + snprintf(buffer, SIZE, " Devices %08x\n", device()); + result.append(buffer); + snprintf(buffer, SIZE, " Stream volume refCount muteCount\n"); + result.append(buffer); + for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { + snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", + i, mCurVolume[i], mRefCount[i], mMuteCount[i]); + result.append(buffer); + } + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- AudioInputDescriptor class implementation + +AudioPolicyManager::AudioInputDescriptor::AudioInputDescriptor(const IOProfile *profile) + : mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(0), + mDevice(AUDIO_DEVICE_NONE), mRefCount(0), + mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile) +{ + if (profile != NULL) { + mSamplingRate = profile->mSamplingRates[0]; + mFormat = profile->mFormats[0]; + mChannelMask = profile->mChannelMasks[0]; + } +} + +status_t AudioPolicyManager::AudioInputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); + result.append(buffer); + snprintf(buffer, SIZE, " Format: %d\n", mFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask); + result.append(buffer); + snprintf(buffer, SIZE, " Devices %08x\n", mDevice); + result.append(buffer); + snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount); + result.append(buffer); + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- StreamDescriptor class implementation + +AudioPolicyManager::StreamDescriptor::StreamDescriptor() + : mIndexMin(0), mIndexMax(1), mCanBeMuted(true) +{ + mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT, 0); +} + +int AudioPolicyManager::StreamDescriptor::getVolumeIndex(audio_devices_t device) +{ + device = AudioPolicyManager::getDeviceForVolume(device); + // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT + if (mIndexCur.indexOfKey(device) < 0) { + device = AUDIO_DEVICE_OUT_DEFAULT; + } + return mIndexCur.valueFor(device); +} + +void AudioPolicyManager::StreamDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "%s %02d %02d ", + mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); + result.append(buffer); + for (size_t i = 0; i < mIndexCur.size(); i++) { + snprintf(buffer, SIZE, "%04x : %02d, ", + mIndexCur.keyAt(i), + mIndexCur.valueAt(i)); + result.append(buffer); + } + result.append("\n"); + + write(fd, result.string(), result.size()); +} + +// --- EffectDescriptor class implementation + +status_t AudioPolicyManager::EffectDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " I/O: %d\n", mIo); + result.append(buffer); + snprintf(buffer, SIZE, " Strategy: %d\n", mStrategy); + result.append(buffer); + snprintf(buffer, SIZE, " Session: %d\n", mSession); + result.append(buffer); + snprintf(buffer, SIZE, " Name: %s\n", mDesc.name); + result.append(buffer); + snprintf(buffer, SIZE, " %s\n", mEnabled ? "Enabled" : "Disabled"); + result.append(buffer); + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- IOProfile class implementation + +AudioPolicyManager::HwModule::HwModule(const char *name) + : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), mHandle(0) +{ +} + +AudioPolicyManager::HwModule::~HwModule() +{ + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + mOutputProfiles[i]->mSupportedDevices.clear(); + delete mOutputProfiles[i]; + } + for (size_t i = 0; i < mInputProfiles.size(); i++) { + mInputProfiles[i]->mSupportedDevices.clear(); + delete mInputProfiles[i]; + } + free((void *)mName); +} + +void AudioPolicyManager::HwModule::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " - name: %s\n", mName); + result.append(buffer); + snprintf(buffer, SIZE, " - handle: %d\n", mHandle); + result.append(buffer); + write(fd, result.string(), result.size()); + if (mOutputProfiles.size()) { + write(fd, " - outputs:\n", strlen(" - outputs:\n")); + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + snprintf(buffer, SIZE, " output %d:\n", i); + write(fd, buffer, strlen(buffer)); + mOutputProfiles[i]->dump(fd); + } + } + if (mInputProfiles.size()) { + write(fd, " - inputs:\n", strlen(" - inputs:\n")); + for (size_t i = 0; i < mInputProfiles.size(); i++) { + snprintf(buffer, SIZE, " input %d:\n", i); + write(fd, buffer, strlen(buffer)); + mInputProfiles[i]->dump(fd); + } + } +} + +AudioPolicyManager::IOProfile::IOProfile(HwModule *module) + : mFlags((audio_output_flags_t)0), mModule(module) +{ +} + +AudioPolicyManager::IOProfile::~IOProfile() +{ +} + +// checks if the IO profile is compatible with specified parameters. +// Sampling rate, format and channel mask must be specified in order to +// get a valid a match +bool AudioPolicyManager::IOProfile::isCompatibleProfile(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags) const +{ + if (samplingRate == 0 || !audio_is_valid_format(format) || channelMask == 0) { + return false; + } + + if ((mSupportedDevices.types() & device) != device) { + return false; + } + if ((mFlags & flags) != flags) { + return false; + } + size_t i; + for (i = 0; i < mSamplingRates.size(); i++) + { + if (mSamplingRates[i] == samplingRate) { + break; + } + } + if (i == mSamplingRates.size()) { + return false; + } + for (i = 0; i < mFormats.size(); i++) + { + if (mFormats[i] == format) { + break; + } + } + if (i == mFormats.size()) { + return false; + } + for (i = 0; i < mChannelMasks.size(); i++) + { + if (mChannelMasks[i] == channelMask) { + break; + } + } + if (i == mChannelMasks.size()) { + return false; + } + return true; +} + +void AudioPolicyManager::IOProfile::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " - sampling rates: "); + result.append(buffer); + for (size_t i = 0; i < mSamplingRates.size(); i++) { + snprintf(buffer, SIZE, "%d", mSamplingRates[i]); + result.append(buffer); + result.append(i == (mSamplingRates.size() - 1) ? "\n" : ", "); + } + + snprintf(buffer, SIZE, " - channel masks: "); + result.append(buffer); + for (size_t i = 0; i < mChannelMasks.size(); i++) { + snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]); + result.append(buffer); + result.append(i == (mChannelMasks.size() - 1) ? "\n" : ", "); + } + + snprintf(buffer, SIZE, " - formats: "); + result.append(buffer); + for (size_t i = 0; i < mFormats.size(); i++) { + snprintf(buffer, SIZE, "0x%08x", mFormats[i]); + result.append(buffer); + result.append(i == (mFormats.size() - 1) ? "\n" : ", "); + } + + snprintf(buffer, SIZE, " - devices:\n"); + result.append(buffer); + write(fd, result.string(), result.size()); + DeviceDescriptor::dumpHeader(fd, 6); + for (size_t i = 0; i < mSupportedDevices.size(); i++) { + mSupportedDevices[i]->dump(fd, 6); + } + + snprintf(buffer, SIZE, " - flags: 0x%04x\n", mFlags); + result.append(buffer); + + write(fd, result.string(), result.size()); +} + +// --- DeviceDescriptor implementation + +bool AudioPolicyManager::DeviceDescriptor::equals(const sp<DeviceDescriptor>& other) const +{ + // Devices are considered equal if they: + // - are of the same type (a device type cannot be AUDIO_DEVICE_NONE) + // - have the same address or one device does not specify the address + // - have the same channel mask or one device does not specify the channel mask + return (mType == other->mType) && + (mAddress == "" || other->mAddress == "" || mAddress == other->mAddress) && + (mChannelMask == 0 || other->mChannelMask == 0 || + mChannelMask == other->mChannelMask); +} + +void AudioPolicyManager::DeviceVector::refreshTypes() +{ + mTypes = AUDIO_DEVICE_NONE; + for(size_t i = 0; i < size(); i++) { + mTypes |= itemAt(i)->mType; + } + ALOGV("DeviceVector::refreshTypes() mTypes %08x", mTypes); +} + +ssize_t AudioPolicyManager::DeviceVector::indexOf(const sp<DeviceDescriptor>& item) const +{ + for(size_t i = 0; i < size(); i++) { + if (item->equals(itemAt(i))) { + return i; + } + } + return -1; +} + +ssize_t AudioPolicyManager::DeviceVector::add(const sp<DeviceDescriptor>& item) +{ + ssize_t ret = indexOf(item); + + if (ret < 0) { + ret = SortedVector::add(item); + if (ret >= 0) { + refreshTypes(); + } + } else { + ALOGW("DeviceVector::add device %08x already in", item->mType); + ret = -1; + } + return ret; +} + +ssize_t AudioPolicyManager::DeviceVector::remove(const sp<DeviceDescriptor>& item) +{ + size_t i; + ssize_t ret = indexOf(item); + + if (ret < 0) { + ALOGW("DeviceVector::remove device %08x not in", item->mType); + } else { + ret = SortedVector::removeAt(ret); + if (ret >= 0) { + refreshTypes(); + } + } + return ret; +} + +void AudioPolicyManager::DeviceVector::loadDevicesFromType(audio_devices_t types) +{ + DeviceVector deviceList; + + uint32_t role_bit = AUDIO_DEVICE_BIT_IN & types; + types &= ~role_bit; + + while (types) { + uint32_t i = 31 - __builtin_clz(types); + uint32_t type = 1 << i; + types &= ~type; + add(new DeviceDescriptor(type | role_bit)); + } +} + +void AudioPolicyManager::DeviceDescriptor::dumpHeader(int fd, int spaces) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "%*s%-48s %-2s %-8s %-32s \n", + spaces, "", "Type", "ID", "Cnl Mask", "Address"); + write(fd, buffer, strlen(buffer)); +} + +status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "%*s%-48s %2d %08x %-32s \n", + spaces, "", + enumToString(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + mType), + mId, mChannelMask, mAddress.string()); + write(fd, buffer, strlen(buffer)); + + return NO_ERROR; +} + + +// --- audio_policy.conf file parsing + +audio_output_flags_t AudioPolicyManager::parseFlagNames(char *name) +{ + uint32_t flag = 0; + + // it is OK to cast name to non const here as we are not going to use it after + // strtok() modifies it + char *flagName = strtok(name, "|"); + while (flagName != NULL) { + if (strlen(flagName) != 0) { + flag |= stringToEnum(sFlagNameToEnumTable, + ARRAY_SIZE(sFlagNameToEnumTable), + flagName); + } + flagName = strtok(NULL, "|"); + } + //force direct flag if offload flag is set: offloading implies a direct output stream + // and all common behaviors are driven by checking only the direct flag + // this should normally be set appropriately in the policy configuration file + if ((flag & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + flag |= AUDIO_OUTPUT_FLAG_DIRECT; + } + + return (audio_output_flags_t)flag; +} + +audio_devices_t AudioPolicyManager::parseDeviceNames(char *name) +{ + uint32_t device = 0; + + char *devName = strtok(name, "|"); + while (devName != NULL) { + if (strlen(devName) != 0) { + device |= stringToEnum(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + devName); + } + devName = strtok(NULL, "|"); + } + return device; +} + +void AudioPolicyManager::loadSamplingRates(char *name, IOProfile *profile) +{ + char *str = strtok(name, "|"); + + // by convention, "0' in the first entry in mSamplingRates indicates the supported sampling + // rates should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + profile->mSamplingRates.add(0); + return; + } + + while (str != NULL) { + uint32_t rate = atoi(str); + if (rate != 0) { + ALOGV("loadSamplingRates() adding rate %d", rate); + profile->mSamplingRates.add(rate); + } + str = strtok(NULL, "|"); + } + return; +} + +void AudioPolicyManager::loadFormats(char *name, IOProfile *profile) +{ + char *str = strtok(name, "|"); + + // by convention, "0' in the first entry in mFormats indicates the supported formats + // should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + profile->mFormats.add(AUDIO_FORMAT_DEFAULT); + return; + } + + while (str != NULL) { + audio_format_t format = (audio_format_t)stringToEnum(sFormatNameToEnumTable, + ARRAY_SIZE(sFormatNameToEnumTable), + str); + if (format != AUDIO_FORMAT_DEFAULT) { + profile->mFormats.add(format); + } + str = strtok(NULL, "|"); + } + return; +} + +void AudioPolicyManager::loadInChannels(char *name, IOProfile *profile) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadInChannels() %s", name); + + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + profile->mChannelMasks.add(0); + return; + } + + while (str != NULL) { + audio_channel_mask_t channelMask = + (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable, + ARRAY_SIZE(sInChannelsNameToEnumTable), + str); + if (channelMask != 0) { + ALOGV("loadInChannels() adding channelMask %04x", channelMask); + profile->mChannelMasks.add(channelMask); + } + str = strtok(NULL, "|"); + } + return; +} + +void AudioPolicyManager::loadOutChannels(char *name, IOProfile *profile) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadOutChannels() %s", name); + + // by convention, "0' in the first entry in mChannelMasks indicates the supported channel + // masks should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + profile->mChannelMasks.add(0); + return; + } + + while (str != NULL) { + audio_channel_mask_t channelMask = + (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable, + ARRAY_SIZE(sOutChannelsNameToEnumTable), + str); + if (channelMask != 0) { + profile->mChannelMasks.add(channelMask); + } + str = strtok(NULL, "|"); + } + return; +} + +status_t AudioPolicyManager::loadInput(cnode *root, HwModule *module) +{ + cnode *node = root->first_child; + + IOProfile *profile = new IOProfile(module); + + while (node) { + if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { + loadSamplingRates((char *)node->value, profile); + } else if (strcmp(node->name, FORMATS_TAG) == 0) { + loadFormats((char *)node->value, profile); + } else if (strcmp(node->name, CHANNELS_TAG) == 0) { + loadInChannels((char *)node->value, profile); + } else if (strcmp(node->name, DEVICES_TAG) == 0) { + profile->mSupportedDevices.loadDevicesFromType(parseDeviceNames((char *)node->value)); + } + node = node->next; + } + ALOGW_IF(profile->mSupportedDevices.isEmpty(), + "loadInput() invalid supported devices"); + ALOGW_IF(profile->mChannelMasks.size() == 0, + "loadInput() invalid supported channel masks"); + ALOGW_IF(profile->mSamplingRates.size() == 0, + "loadInput() invalid supported sampling rates"); + ALOGW_IF(profile->mFormats.size() == 0, + "loadInput() invalid supported formats"); + if (!profile->mSupportedDevices.isEmpty() && + (profile->mChannelMasks.size() != 0) && + (profile->mSamplingRates.size() != 0) && + (profile->mFormats.size() != 0)) { + + ALOGV("loadInput() adding input Supported Devices %04x", + profile->mSupportedDevices.types()); + + module->mInputProfiles.add(profile); + return NO_ERROR; + } else { + delete profile; + return BAD_VALUE; + } +} + +status_t AudioPolicyManager::loadOutput(cnode *root, HwModule *module) +{ + cnode *node = root->first_child; + + IOProfile *profile = new IOProfile(module); + + while (node) { + if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { + loadSamplingRates((char *)node->value, profile); + } else if (strcmp(node->name, FORMATS_TAG) == 0) { + loadFormats((char *)node->value, profile); + } else if (strcmp(node->name, CHANNELS_TAG) == 0) { + loadOutChannels((char *)node->value, profile); + } else if (strcmp(node->name, DEVICES_TAG) == 0) { + profile->mSupportedDevices.loadDevicesFromType(parseDeviceNames((char *)node->value)); + } else if (strcmp(node->name, FLAGS_TAG) == 0) { + profile->mFlags = parseFlagNames((char *)node->value); + } + node = node->next; + } + ALOGW_IF(profile->mSupportedDevices.isEmpty(), + "loadOutput() invalid supported devices"); + ALOGW_IF(profile->mChannelMasks.size() == 0, + "loadOutput() invalid supported channel masks"); + ALOGW_IF(profile->mSamplingRates.size() == 0, + "loadOutput() invalid supported sampling rates"); + ALOGW_IF(profile->mFormats.size() == 0, + "loadOutput() invalid supported formats"); + if (!profile->mSupportedDevices.isEmpty() && + (profile->mChannelMasks.size() != 0) && + (profile->mSamplingRates.size() != 0) && + (profile->mFormats.size() != 0)) { + + ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x", + profile->mSupportedDevices.types(), profile->mFlags); + + module->mOutputProfiles.add(profile); + return NO_ERROR; + } else { + delete profile; + return BAD_VALUE; + } +} + +void AudioPolicyManager::loadHwModule(cnode *root) +{ + cnode *node = config_find(root, OUTPUTS_TAG); + status_t status = NAME_NOT_FOUND; + + HwModule *module = new HwModule(root->name); + + if (node != NULL) { + node = node->first_child; + while (node) { + ALOGV("loadHwModule() loading output %s", node->name); + status_t tmpStatus = loadOutput(node, module); + if (status == NAME_NOT_FOUND || status == NO_ERROR) { + status = tmpStatus; + } + node = node->next; + } + } + node = config_find(root, INPUTS_TAG); + if (node != NULL) { + node = node->first_child; + while (node) { + ALOGV("loadHwModule() loading input %s", node->name); + status_t tmpStatus = loadInput(node, module); + if (status == NAME_NOT_FOUND || status == NO_ERROR) { + status = tmpStatus; + } + node = node->next; + } + } + if (status == NO_ERROR) { + mHwModules.add(module); + } else { + delete module; + } +} + +void AudioPolicyManager::loadHwModules(cnode *root) +{ + cnode *node = config_find(root, AUDIO_HW_MODULE_TAG); + if (node == NULL) { + return; + } + + node = node->first_child; + while (node) { + ALOGV("loadHwModules() loading module %s", node->name); + loadHwModule(node); + node = node->next; + } +} + +void AudioPolicyManager::loadGlobalConfig(cnode *root) +{ + cnode *node = config_find(root, GLOBAL_CONFIG_TAG); + if (node == NULL) { + return; + } + node = node->first_child; + while (node) { + if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) { + mAvailableOutputDevices.loadDevicesFromType(parseDeviceNames((char *)node->value)); + ALOGV("loadGlobalConfig() Attached Output Devices %08x", + mAvailableOutputDevices.types()); + } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) { + audio_devices_t device = (audio_devices_t)stringToEnum(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + (char *)node->value); + if (device != AUDIO_DEVICE_NONE) { + mDefaultOutputDevice = new DeviceDescriptor(device); + } else { + ALOGW("loadGlobalConfig() default device not specified"); + } + ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", mDefaultOutputDevice->mType); + } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) { + mAvailableInputDevices.loadDevicesFromType(parseDeviceNames((char *)node->value)); + ALOGV("loadGlobalConfig() Available InputDevices %08x", mAvailableInputDevices.types()); + } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) { + mSpeakerDrcEnabled = stringToBool((char *)node->value); + ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", mSpeakerDrcEnabled); + } + node = node->next; + } +} + +status_t AudioPolicyManager::loadAudioPolicyConfig(const char *path) +{ + cnode *root; + char *data; + + data = (char *)load_file(path, NULL); + if (data == NULL) { + return -ENODEV; + } + root = config_node("", ""); + config_load(root, data); + + loadGlobalConfig(root); + loadHwModules(root); + + config_free(root); + free(root); + free(data); + + ALOGI("loadAudioPolicyConfig() loaded %s\n", path); + + return NO_ERROR; +} + +void AudioPolicyManager::defaultAudioPolicyConfig(void) +{ + HwModule *module; + IOProfile *profile; + sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC); + mAvailableOutputDevices.add(mDefaultOutputDevice); + mAvailableInputDevices.add(defaultInputDevice); + + module = new HwModule("primary"); + + profile = new IOProfile(module); + profile->mSamplingRates.add(44100); + profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT); + profile->mChannelMasks.add(AUDIO_CHANNEL_OUT_STEREO); + profile->mSupportedDevices.add(mDefaultOutputDevice); + profile->mFlags = AUDIO_OUTPUT_FLAG_PRIMARY; + module->mOutputProfiles.add(profile); + + profile = new IOProfile(module); + profile->mSamplingRates.add(8000); + profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT); + profile->mChannelMasks.add(AUDIO_CHANNEL_IN_MONO); + profile->mSupportedDevices.add(defaultInputDevice); + module->mInputProfiles.add(profile); + + mHwModules.add(module); +} + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h new file mode 100644 index 0000000..8a631ba --- /dev/null +++ b/services/audiopolicy/AudioPolicyManager.h @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <stdint.h> +#include <sys/types.h> +#include <cutils/config_utils.h> +#include <cutils/misc.h> +#include <utils/Timers.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/SortedVector.h> +#include "AudioPolicyInterface.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +// Attenuation applied to STRATEGY_SONIFICATION streams when a headset is connected: 6dB +#define SONIFICATION_HEADSET_VOLUME_FACTOR 0.5 +// Min volume for STRATEGY_SONIFICATION streams when limited by music volume: -36dB +#define SONIFICATION_HEADSET_VOLUME_MIN 0.016 +// Time in milliseconds during which we consider that music is still active after a music +// track was stopped - see computeVolume() +#define SONIFICATION_HEADSET_MUSIC_DELAY 5000 +// Time in milliseconds after media stopped playing during which we consider that the +// sonification should be as unobtrusive as during the time media was playing. +#define SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY 5000 +// Time in milliseconds during witch some streams are muted while the audio path +// is switched +#define MUTE_TIME_MS 2000 + +#define NUM_TEST_OUTPUTS 5 + +#define NUM_VOL_CURVE_KNEES 2 + +// Default minimum length allowed for offloading a compressed track +// Can be overridden by the audio.offload.min.duration.secs property +#define OFFLOAD_DEFAULT_MIN_DURATION_SECS 60 + +// ---------------------------------------------------------------------------- +// AudioPolicyManager implements audio policy manager behavior common to all platforms. +// ---------------------------------------------------------------------------- + +class AudioPolicyManager: public AudioPolicyInterface +#ifdef AUDIO_POLICY_TEST + , public Thread +#endif //AUDIO_POLICY_TEST +{ + +public: + AudioPolicyManager(AudioPolicyClientInterface *clientInterface); + virtual ~AudioPolicyManager(); + + // AudioPolicyInterface + virtual status_t setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address); + virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, + const char *device_address); + virtual void setPhoneState(audio_mode_t state); + virtual void setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config); + virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); + virtual void setSystemProperty(const char* property, const char* value); + virtual status_t initCheck(); + virtual audio_io_handle_t getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo); + virtual status_t startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0); + virtual status_t stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0); + virtual void releaseOutput(audio_io_handle_t output); + virtual audio_io_handle_t getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_in_acoustics_t acoustics); + + // indicates to the audio policy manager that the input starts being used. + virtual status_t startInput(audio_io_handle_t input); + + // indicates to the audio policy manager that the input stops being used. + virtual status_t stopInput(audio_io_handle_t input); + virtual void releaseInput(audio_io_handle_t input); + virtual void initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax); + virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device); + virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device); + + // return the strategy corresponding to a given stream type + virtual uint32_t getStrategyForStream(audio_stream_type_t stream); + + // return the enabled output devices for the given stream type + virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream); + + virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc = NULL); + virtual status_t registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id); + virtual status_t unregisterEffect(int id); + virtual status_t setEffectEnabled(int id, bool enabled); + + virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + // return whether a stream is playing remotely, override to change the definition of + // local/remote playback, used for instance by notification manager to not make + // media players lose audio focus when not playing locally + virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + virtual bool isSourceActive(audio_source_t source) const; + + virtual status_t dump(int fd); + + virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo); + +protected: + + enum routing_strategy { + STRATEGY_MEDIA, + STRATEGY_PHONE, + STRATEGY_SONIFICATION, + STRATEGY_SONIFICATION_RESPECTFUL, + STRATEGY_DTMF, + STRATEGY_ENFORCED_AUDIBLE, + NUM_STRATEGIES + }; + + // 4 points to define the volume attenuation curve, each characterized by the volume + // index (from 0 to 100) at which they apply, and the attenuation in dB at that index. + // we use 100 steps to avoid rounding errors when computing the volume in volIndexToAmpl() + + enum { VOLMIN = 0, VOLKNEE1 = 1, VOLKNEE2 = 2, VOLMAX = 3, VOLCNT = 4}; + + class VolumeCurvePoint + { + public: + int mIndex; + float mDBAttenuation; + }; + + // device categories used for volume curve management. + enum device_category { + DEVICE_CATEGORY_HEADSET, + DEVICE_CATEGORY_SPEAKER, + DEVICE_CATEGORY_EARPIECE, + DEVICE_CATEGORY_CNT + }; + + class IOProfile; + + class DeviceDescriptor: public RefBase + { + public: + DeviceDescriptor(audio_devices_t type, String8 address, + audio_channel_mask_t channelMask) : + mType(type), mAddress(address), + mChannelMask(channelMask), mId(0) {} + + DeviceDescriptor(audio_devices_t type) : + mType(type), mAddress(""), + mChannelMask(0), mId(0) {} + + status_t dump(int fd, int spaces) const; + static void dumpHeader(int fd, int spaces); + + bool equals(const sp<DeviceDescriptor>& other) const; + + audio_devices_t mType; + String8 mAddress; + audio_channel_mask_t mChannelMask; + uint32_t mId; + }; + + class DeviceVector : public SortedVector< sp<DeviceDescriptor> > + { + public: + DeviceVector() : SortedVector(), mTypes(AUDIO_DEVICE_NONE) {} + + ssize_t add(const sp<DeviceDescriptor>& item); + ssize_t remove(const sp<DeviceDescriptor>& item); + ssize_t indexOf(const sp<DeviceDescriptor>& item) const; + + audio_devices_t types() const { return mTypes; } + + void loadDevicesFromType(audio_devices_t types); + + private: + void refreshTypes(); + audio_devices_t mTypes; + }; + + class HwModule { + public: + HwModule(const char *name); + ~HwModule(); + + void dump(int fd); + + const char *const mName; // base name of the audio HW module (primary, a2dp ...) + audio_module_handle_t mHandle; + Vector <IOProfile *> mOutputProfiles; // output profiles exposed by this module + Vector <IOProfile *> mInputProfiles; // input profiles exposed by this module + }; + + // the IOProfile class describes the capabilities of an output or input stream. + // It is currently assumed that all combination of listed parameters are supported. + // It is used by the policy manager to determine if an output or input is suitable for + // a given use case, open/close it accordingly and connect/disconnect audio tracks + // to/from it. + class IOProfile + { + public: + IOProfile(HwModule *module); + ~IOProfile(); + + bool isCompatibleProfile(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags) const; + + void dump(int fd); + + // by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats + // indicates the supported parameters should be read from the output stream + // after it is opened for the first time + Vector <uint32_t> mSamplingRates; // supported sampling rates + Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks + Vector <audio_format_t> mFormats; // supported audio formats + DeviceVector mSupportedDevices; // supported devices + // (devices this output can be routed to) + audio_output_flags_t mFlags; // attribute flags (e.g primary output, + // direct output...). For outputs only. + HwModule *mModule; // audio HW module exposing this I/O stream + }; + + // default volume curve + static const VolumeCurvePoint sDefaultVolumeCurve[AudioPolicyManager::VOLCNT]; + // default volume curve for media strategy + static const VolumeCurvePoint sDefaultMediaVolumeCurve[AudioPolicyManager::VOLCNT]; + // volume curve for media strategy on speakers + static const VolumeCurvePoint sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT]; + // volume curve for sonification strategy on speakers + static const VolumeCurvePoint sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sSpeakerSonificationVolumeCurveDrc[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sDefaultSystemVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sDefaultSystemVolumeCurveDrc[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sHeadsetSystemVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sDefaultVoiceVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sSpeakerVoiceVolumeCurve[AudioPolicyManager::VOLCNT]; + // default volume curves per stream and device category. See initializeVolumeCurves() + static const VolumeCurvePoint *sVolumeProfiles[AUDIO_STREAM_CNT][DEVICE_CATEGORY_CNT]; + + // descriptor for audio outputs. Used to maintain current configuration of each opened audio output + // and keep track of the usage of this output by each audio stream type. + class AudioOutputDescriptor + { + public: + AudioOutputDescriptor(const IOProfile *profile); + + status_t dump(int fd); + + audio_devices_t device() const; + void changeRefCount(audio_stream_type_t stream, int delta); + + bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); } + audio_devices_t supportedDevices(); + uint32_t latency(); + bool sharesHwModuleWith(const AudioOutputDescriptor *outputDesc); + bool isActive(uint32_t inPastMs = 0) const; + bool isStreamActive(audio_stream_type_t stream, + uint32_t inPastMs = 0, + nsecs_t sysTime = 0) const; + bool isStrategyActive(routing_strategy strategy, + uint32_t inPastMs = 0, + nsecs_t sysTime = 0) const; + + audio_io_handle_t mId; // output handle + uint32_t mSamplingRate; // + audio_format_t mFormat; // + audio_channel_mask_t mChannelMask; // output configuration + uint32_t mLatency; // + audio_output_flags_t mFlags; // + audio_devices_t mDevice; // current device this output is routed to + uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output + nsecs_t mStopTime[AUDIO_STREAM_CNT]; + AudioOutputDescriptor *mOutput1; // used by duplicated outputs: first output + AudioOutputDescriptor *mOutput2; // used by duplicated outputs: second output + float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume + int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter + const IOProfile *mProfile; // I/O profile this output derives from + bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible + // device selection. See checkDeviceMuteStrategies() + uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only) + }; + + // descriptor for audio inputs. Used to maintain current configuration of each opened audio input + // and keep track of the usage of this input. + class AudioInputDescriptor + { + public: + AudioInputDescriptor(const IOProfile *profile); + + status_t dump(int fd); + + uint32_t mSamplingRate; // + audio_format_t mFormat; // input configuration + audio_channel_mask_t mChannelMask; // + audio_devices_t mDevice; // current device this input is routed to + uint32_t mRefCount; // number of AudioRecord clients using this output + audio_source_t mInputSource; // input source selected by application (mediarecorder.h) + const IOProfile *mProfile; // I/O profile this output derives from + }; + + // stream descriptor used for volume control + class StreamDescriptor + { + public: + StreamDescriptor(); + + int getVolumeIndex(audio_devices_t device); + void dump(int fd); + + int mIndexMin; // min volume index + int mIndexMax; // max volume index + KeyedVector<audio_devices_t, int> mIndexCur; // current volume index per device + bool mCanBeMuted; // true is the stream can be muted + + const VolumeCurvePoint *mVolumeCurve[DEVICE_CATEGORY_CNT]; + }; + + // stream descriptor used for volume control + class EffectDescriptor + { + public: + + status_t dump(int fd); + + int mIo; // io the effect is attached to + routing_strategy mStrategy; // routing strategy the effect is associated to + int mSession; // audio session the effect is on + effect_descriptor_t mDesc; // effect descriptor + bool mEnabled; // enabled state: CPU load being used or not + }; + + void addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc); + + // return the strategy corresponding to a given stream type + static routing_strategy getStrategy(audio_stream_type_t stream); + + // return appropriate device for streams handled by the specified strategy according to current + // phone state, connected devices... + // if fromCache is true, the device is returned from mDeviceForStrategy[], + // otherwise it is determine by current state + // (device connected,phone state, force use, a2dp output...) + // This allows to: + // 1 speed up process when the state is stable (when starting or stopping an output) + // 2 access to either current device selection (fromCache == true) or + // "future" device selection (fromCache == false) when called from a context + // where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND + // before updateDevicesAndOutputs() is called. + virtual audio_devices_t getDeviceForStrategy(routing_strategy strategy, + bool fromCache); + + // change the route of the specified output. Returns the number of ms we have slept to + // allow new routing to take effect in certain cases. + uint32_t setOutputDevice(audio_io_handle_t output, + audio_devices_t device, + bool force = false, + int delayMs = 0); + + // select input device corresponding to requested audio source + virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource); + + // return io handle of active input or 0 if no input is active + // Only considers inputs from physical devices (e.g. main mic, headset mic) when + // ignoreVirtualInputs is true. + audio_io_handle_t getActiveInput(bool ignoreVirtualInputs = true); + + // initialize volume curves for each strategy and device category + void initializeVolumeCurves(); + + // compute the actual volume for a given stream according to the requested index and a particular + // device + virtual float computeVolume(audio_stream_type_t stream, int index, + audio_io_handle_t output, audio_devices_t device); + + // check that volume change is permitted, compute and send new volume to audio hardware + status_t checkAndSetVolume(audio_stream_type_t stream, int index, audio_io_handle_t output, + audio_devices_t device, int delayMs = 0, bool force = false); + + // apply all stream volumes to the specified output and device + void applyStreamVolumes(audio_io_handle_t output, audio_devices_t device, int delayMs = 0, bool force = false); + + // Mute or unmute all streams handled by the specified strategy on the specified output + void setStrategyMute(routing_strategy strategy, + bool on, + audio_io_handle_t output, + int delayMs = 0, + audio_devices_t device = (audio_devices_t)0); + + // Mute or unmute the stream on the specified output + void setStreamMute(audio_stream_type_t stream, + bool on, + audio_io_handle_t output, + int delayMs = 0, + audio_devices_t device = (audio_devices_t)0); + + // handle special cases for sonification strategy while in call: mute streams or replace by + // a special tone in the device used for communication + void handleIncallSonification(audio_stream_type_t stream, bool starting, bool stateChange); + + // true if device is in a telephony or VoIP call + virtual bool isInCall(); + + // true if given state represents a device in a telephony or VoIP call + virtual bool isStateInCall(int state); + + // when a device is connected, checks if an open output can be routed + // to this device. If none is open, tries to open one of the available outputs. + // Returns an output suitable to this device or 0. + // when a device is disconnected, checks if an output is not used any more and + // returns its handle if any. + // transfers the audio tracks and effects from one output thread to another accordingly. + status_t checkOutputsForDevice(audio_devices_t device, + audio_policy_dev_state_t state, + SortedVector<audio_io_handle_t>& outputs, + const String8 address); + + // close an output and its companion duplicating output. + void closeOutput(audio_io_handle_t output); + + // checks and if necessary changes outputs used for all strategies. + // must be called every time a condition that affects the output choice for a given strategy + // changes: connected device, phone state, force use... + // Must be called before updateDevicesAndOutputs() + void checkOutputForStrategy(routing_strategy strategy); + + // Same as checkOutputForStrategy() but for a all strategies in order of priority + void checkOutputForAllStrategies(); + + // manages A2DP output suspend/restore according to phone state and BT SCO usage + void checkA2dpSuspend(); + + // returns the A2DP output handle if it is open or 0 otherwise + audio_io_handle_t getA2dpOutput(); + + // selects the most appropriate device on output for current state + // must be called every time a condition that affects the device choice for a given output is + // changed: connected device, phone state, force use, output start, output stop.. + // see getDeviceForStrategy() for the use of fromCache parameter + + audio_devices_t getNewDevice(audio_io_handle_t output, bool fromCache); + // updates cache of device used by all strategies (mDeviceForStrategy[]) + // must be called every time a condition that affects the device choice for a given strategy is + // changed: connected device, phone state, force use... + // cached values are used by getDeviceForStrategy() if parameter fromCache is true. + // Must be called after checkOutputForAllStrategies() + + void updateDevicesAndOutputs(); + + virtual uint32_t getMaxEffectsCpuLoad(); + virtual uint32_t getMaxEffectsMemory(); +#ifdef AUDIO_POLICY_TEST + virtual bool threadLoop(); + void exit(); + int testOutputIndex(audio_io_handle_t output); +#endif //AUDIO_POLICY_TEST + + status_t setEffectEnabled(EffectDescriptor *pDesc, bool enabled); + + // returns the category the device belongs to with regard to volume curve management + static device_category getDeviceCategory(audio_devices_t device); + + // extract one device relevant for volume control from multiple device selection + static audio_devices_t getDeviceForVolume(audio_devices_t device); + + SortedVector<audio_io_handle_t> getOutputsForDevice(audio_devices_t device, + DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> openOutputs); + bool vectorsEqual(SortedVector<audio_io_handle_t>& outputs1, + SortedVector<audio_io_handle_t>& outputs2); + + // mute/unmute strategies using an incompatible device combination + // if muting, wait for the audio in pcm buffer to be drained before proceeding + // if unmuting, unmute only after the specified delay + // Returns the number of ms waited + uint32_t checkDeviceMuteStrategies(AudioOutputDescriptor *outputDesc, + audio_devices_t prevDevice, + uint32_t delayMs); + + audio_io_handle_t selectOutput(const SortedVector<audio_io_handle_t>& outputs, + audio_output_flags_t flags); + IOProfile *getInputProfile(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask); + IOProfile *getProfileForDirectOutput(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags); + + audio_io_handle_t selectOutputForEffects(const SortedVector<audio_io_handle_t>& outputs); + + bool isNonOffloadableEffectEnabled(); + + // + // Audio policy configuration file parsing (audio_policy.conf) + // + static uint32_t stringToEnum(const struct StringToEnum *table, + size_t size, + const char *name); + static const char *enumToString(const struct StringToEnum *table, + size_t size, + uint32_t value); + static bool stringToBool(const char *value); + static audio_output_flags_t parseFlagNames(char *name); + static audio_devices_t parseDeviceNames(char *name); + void loadSamplingRates(char *name, IOProfile *profile); + void loadFormats(char *name, IOProfile *profile); + void loadOutChannels(char *name, IOProfile *profile); + void loadInChannels(char *name, IOProfile *profile); + status_t loadOutput(cnode *root, HwModule *module); + status_t loadInput(cnode *root, HwModule *module); + void loadHwModule(cnode *root); + void loadHwModules(cnode *root); + void loadGlobalConfig(cnode *root); + status_t loadAudioPolicyConfig(const char *path); + void defaultAudioPolicyConfig(void); + + + AudioPolicyClientInterface *mpClientInterface; // audio policy client interface + audio_io_handle_t mPrimaryOutput; // primary output handle + // list of descriptors for outputs currently opened + DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs; + // copy of mOutputs before setDeviceConnectionState() opens new outputs + // reset to mOutputs when updateDevicesAndOutputs() is called. + DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mPreviousOutputs; + DefaultKeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs; // list of input descriptors + DeviceVector mAvailableOutputDevices; // bit field of all available output devices + DeviceVector mAvailableInputDevices; // bit field of all available input devices + // without AUDIO_DEVICE_BIT_IN to allow direct bit + // field comparisons + int mPhoneState; // current phone state + audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT]; // current forced use configuration + + StreamDescriptor mStreams[AUDIO_STREAM_CNT]; // stream descriptors for volume control + bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected + audio_devices_t mDeviceForStrategy[NUM_STRATEGIES]; + float mLastVoiceVolume; // last voice volume value sent to audio HAL + + // Maximum CPU load allocated to audio effects in 0.1 MIPS (ARMv5TE, 0 WS memory) units + static const uint32_t MAX_EFFECTS_CPU_LOAD = 1000; + // Maximum memory allocated to audio effects in KB + static const uint32_t MAX_EFFECTS_MEMORY = 512; + uint32_t mTotalEffectsCpuLoad; // current CPU load used by effects + uint32_t mTotalEffectsMemory; // current memory used by effects + KeyedVector<int, EffectDescriptor *> mEffects; // list of registered audio effects + bool mA2dpSuspended; // true if A2DP output is suspended + sp<DeviceDescriptor> mDefaultOutputDevice; // output device selected by default at boot time + bool mSpeakerDrcEnabled;// true on devices that use DRC on the DEVICE_CATEGORY_SPEAKER path + // to boost soft sounds, used to adjust volume curves accordingly + + Vector <HwModule *> mHwModules; + volatile int32_t mNextUniqueId; + +#ifdef AUDIO_POLICY_TEST + Mutex mLock; + Condition mWaitWorkCV; + + int mCurOutput; + bool mDirectOutput; + audio_io_handle_t mTestOutputs[NUM_TEST_OUTPUTS]; + int mTestInput; + uint32_t mTestDevice; + uint32_t mTestSamplingRate; + uint32_t mTestFormat; + uint32_t mTestChannels; + uint32_t mTestLatencyMs; +#endif //AUDIO_POLICY_TEST + +private: + static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi); + // updates device caching and output for streams that can influence the + // routing of notifications + void handleNotificationRoutingForStream(audio_stream_type_t stream); + static bool isVirtualInputDevice(audio_devices_t device); + uint32_t nextUniqueId(); + // converts device address to string sent to audio HAL via setParameters + static String8 addressToParameter(audio_devices_t device, const String8 address); +}; + +}; diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp index a37272d..4a708a0 100644 --- a/services/audioflinger/AudioPolicyService.cpp +++ b/services/audiopolicy/AudioPolicyService.cpp @@ -60,7 +60,8 @@ namespace { // ---------------------------------------------------------------------------- AudioPolicyService::AudioPolicyService() - : BnAudioPolicyService() , mpAudioPolicyDev(NULL) , mpAudioPolicy(NULL) + : BnAudioPolicyService(), mpAudioPolicyDev(NULL), mpAudioPolicy(NULL), + mAudioPolicyManager(NULL), mAudioPolicyClient(NULL) { char value[PROPERTY_VALUE_MAX]; const struct hw_module_t *module; @@ -75,28 +76,40 @@ AudioPolicyService::AudioPolicyService() mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this); // start output activity command thread mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this); + +#ifdef USE_LEGACY_AUDIO_POLICY + ALOGI("AudioPolicyService CSTOR in legacy mode"); + /* instantiate the audio policy manager */ rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module); - if (rc) + if (rc) { return; - + } rc = audio_policy_dev_open(module, &mpAudioPolicyDev); ALOGE_IF(rc, "couldn't open audio policy device (%s)", strerror(-rc)); - if (rc) + if (rc) { return; + } rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this, &mpAudioPolicy); ALOGE_IF(rc, "couldn't create audio policy (%s)", strerror(-rc)); - if (rc) + if (rc) { return; + } rc = mpAudioPolicy->init_check(mpAudioPolicy); ALOGE_IF(rc, "couldn't init_check the audio policy (%s)", strerror(-rc)); - if (rc) + if (rc) { return; - + } ALOGI("Loaded audio policy from %s (%s)", module->name, module->id); +#else + ALOGI("AudioPolicyService CSTOR in new mode"); + + mAudioPolicyClient = new AudioPolicyClient(this); + mAudioPolicyManager = new AudioPolicyManager(mAudioPolicyClient); +#endif // load audio pre processing modules if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) { @@ -126,450 +139,19 @@ AudioPolicyService::~AudioPolicyService() } mInputs.clear(); - if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL) +#ifdef USE_LEGACY_AUDIO_POLICY + if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL) { mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy); - if (mpAudioPolicyDev != NULL) - audio_policy_dev_close(mpAudioPolicyDev); -} - -status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, - audio_policy_dev_state_t state, - const char *device_address) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if (!audio_is_output_device(device) && !audio_is_input_device(device)) { - return BAD_VALUE; - } - if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE && - state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) { - return BAD_VALUE; - } - - ALOGV("setDeviceConnectionState()"); - Mutex::Autolock _l(mLock); - return mpAudioPolicy->set_device_connection_state(mpAudioPolicy, device, - state, device_address); -} - -audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( - audio_devices_t device, - const char *device_address) -{ - if (mpAudioPolicy == NULL) { - return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; - } - return mpAudioPolicy->get_device_connection_state(mpAudioPolicy, device, - device_address); -} - -status_t AudioPolicyService::setPhoneState(audio_mode_t state) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if (uint32_t(state) >= AUDIO_MODE_CNT) { - return BAD_VALUE; - } - - ALOGV("setPhoneState()"); - - // TODO: check if it is more appropriate to do it in platform specific policy manager - AudioSystem::setMode(state); - - Mutex::Autolock _l(mLock); - mpAudioPolicy->set_phone_state(mpAudioPolicy, state); - return NO_ERROR; -} - -status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage, - audio_policy_forced_cfg_t config) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { - return BAD_VALUE; - } - if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) { - return BAD_VALUE; - } - ALOGV("setForceUse()"); - Mutex::Autolock _l(mLock); - mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config); - return NO_ERROR; -} - -audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage) -{ - if (mpAudioPolicy == NULL) { - return AUDIO_POLICY_FORCE_NONE; - } - if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { - return AUDIO_POLICY_FORCE_NONE; - } - return mpAudioPolicy->get_force_use(mpAudioPolicy, usage); -} - -audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) -{ - if (mpAudioPolicy == NULL) { - return 0; - } - ALOGV("getOutput()"); - Mutex::Autolock _l(mLock); - return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, - format, channelMask, flags, offloadInfo); -} - -status_t AudioPolicyService::startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - int session) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - ALOGV("startOutput()"); - Mutex::Autolock _l(mLock); - return mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session); -} - -status_t AudioPolicyService::stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - int session) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - ALOGV("stopOutput()"); - mOutputCommandThread->stopOutputCommand(output, stream, session); - return NO_ERROR; -} - -status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - int session) -{ - ALOGV("doStopOutput from tid %d", gettid()); - Mutex::Autolock _l(mLock); - return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session); -} - -void AudioPolicyService::releaseOutput(audio_io_handle_t output) -{ - if (mpAudioPolicy == NULL) { - return; - } - ALOGV("releaseOutput()"); - mOutputCommandThread->releaseOutputCommand(output); -} - -void AudioPolicyService::doReleaseOutput(audio_io_handle_t output) -{ - ALOGV("doReleaseOutput from tid %d", gettid()); - Mutex::Autolock _l(mLock); - mpAudioPolicy->release_output(mpAudioPolicy, output); -} - -audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - int audioSession) -{ - if (mpAudioPolicy == NULL) { - return 0; - } - // already checked by client, but double-check in case the client wrapper is bypassed - if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD) { - return 0; - } - - if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) { - return 0; - } - - Mutex::Autolock _l(mLock); - // the audio_in_acoustics_t parameter is ignored by get_input() - audio_io_handle_t input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate, - format, channelMask, (audio_in_acoustics_t) 0); - - if (input == 0) { - return input; - } - // create audio pre processors according to input source - audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ? - AUDIO_SOURCE_VOICE_RECOGNITION : inputSource; - - ssize_t index = mInputSources.indexOfKey(aliasSource); - if (index < 0) { - return input; - } - ssize_t idx = mInputs.indexOfKey(input); - InputDesc *inputDesc; - if (idx < 0) { - inputDesc = new InputDesc(audioSession); - mInputs.add(input, inputDesc); - } else { - inputDesc = mInputs.valueAt(idx); - } - - Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects; - for (size_t i = 0; i < effects.size(); i++) { - EffectDesc *effect = effects[i]; - sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, -1, 0, 0, audioSession, input); - status_t status = fx->initCheck(); - if (status != NO_ERROR && status != ALREADY_EXISTS) { - ALOGW("Failed to create Fx %s on input %d", effect->mName, input); - // fx goes out of scope and strong ref on AudioEffect is released - continue; - } - for (size_t j = 0; j < effect->mParams.size(); j++) { - fx->setParameter(effect->mParams[j]); - } - inputDesc->mEffects.add(fx); - } - setPreProcessorEnabled(inputDesc, true); - return input; -} - -status_t AudioPolicyService::startInput(audio_io_handle_t input) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - Mutex::Autolock _l(mLock); - - return mpAudioPolicy->start_input(mpAudioPolicy, input); -} - -status_t AudioPolicyService::stopInput(audio_io_handle_t input) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - Mutex::Autolock _l(mLock); - - return mpAudioPolicy->stop_input(mpAudioPolicy, input); -} - -void AudioPolicyService::releaseInput(audio_io_handle_t input) -{ - if (mpAudioPolicy == NULL) { - return; - } - Mutex::Autolock _l(mLock); - mpAudioPolicy->release_input(mpAudioPolicy, input); - - ssize_t index = mInputs.indexOfKey(input); - if (index < 0) { - return; - } - InputDesc *inputDesc = mInputs.valueAt(index); - setPreProcessorEnabled(inputDesc, false); - delete inputDesc; - mInputs.removeItemsAt(index); -} - -status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, - int indexMin, - int indexMax) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { - return BAD_VALUE; - } - Mutex::Autolock _l(mLock); - mpAudioPolicy->init_stream_volume(mpAudioPolicy, stream, indexMin, indexMax); - return NO_ERROR; -} - -status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, - int index, - audio_devices_t device) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { - return BAD_VALUE; - } - Mutex::Autolock _l(mLock); - if (mpAudioPolicy->set_stream_volume_index_for_device) { - return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy, - stream, - index, - device); - } else { - return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index); - } -} - -status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, - int *index, - audio_devices_t device) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { - return BAD_VALUE; - } - Mutex::Autolock _l(mLock); - if (mpAudioPolicy->get_stream_volume_index_for_device) { - return mpAudioPolicy->get_stream_volume_index_for_device(mpAudioPolicy, - stream, - index, - device); - } else { - return mpAudioPolicy->get_stream_volume_index(mpAudioPolicy, stream, index); - } -} - -uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) -{ - if (mpAudioPolicy == NULL) { - return 0; - } - return mpAudioPolicy->get_strategy_for_stream(mpAudioPolicy, stream); -} - -//audio policy: use audio_device_t appropriately - -audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) -{ - if (mpAudioPolicy == NULL) { - return (audio_devices_t)0; - } - return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream); -} - -audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - Mutex::Autolock _l(mLock); - return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc); -} - -status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc, - audio_io_handle_t io, - uint32_t strategy, - int session, - int id) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - return mpAudioPolicy->register_effect(mpAudioPolicy, desc, io, strategy, session, id); -} - -status_t AudioPolicyService::unregisterEffect(int id) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - return mpAudioPolicy->unregister_effect(mpAudioPolicy, id); -} - -status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - return mpAudioPolicy->set_effect_enabled(mpAudioPolicy, id, enabled); -} - -bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const -{ - if (mpAudioPolicy == NULL) { - return 0; - } - Mutex::Autolock _l(mLock); - return mpAudioPolicy->is_stream_active(mpAudioPolicy, stream, inPastMs); -} - -bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const -{ - if (mpAudioPolicy == NULL) { - return 0; - } - Mutex::Autolock _l(mLock); - return mpAudioPolicy->is_stream_active_remotely(mpAudioPolicy, stream, inPastMs); -} - -bool AudioPolicyService::isSourceActive(audio_source_t source) const -{ - if (mpAudioPolicy == NULL) { - return false; } - if (mpAudioPolicy->is_source_active == 0) { - return false; + if (mpAudioPolicyDev != NULL) { + audio_policy_dev_close(mpAudioPolicyDev); } - Mutex::Autolock _l(mLock); - return mpAudioPolicy->is_source_active(mpAudioPolicy, source); +#else + delete mAudioPolicyManager; + delete mAudioPolicyClient; +#endif } -status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession, - effect_descriptor_t *descriptors, - uint32_t *count) -{ - - if (mpAudioPolicy == NULL) { - *count = 0; - return NO_INIT; - } - Mutex::Autolock _l(mLock); - status_t status = NO_ERROR; - - size_t index; - for (index = 0; index < mInputs.size(); index++) { - if (mInputs.valueAt(index)->mSessionId == audioSession) { - break; - } - } - if (index == mInputs.size()) { - *count = 0; - return BAD_VALUE; - } - Vector< sp<AudioEffect> > effects = mInputs.valueAt(index)->mEffects; - - for (size_t i = 0; i < effects.size(); i++) { - effect_descriptor_t desc = effects[i]->descriptor(); - if (i < *count) { - descriptors[i] = desc; - } - } - if (effects.size() > *count) { - status = NO_MEMORY; - } - *count = effects.size(); - return status; -} void AudioPolicyService::binderDied(const wp<IBinder>& who) { ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(), @@ -595,7 +177,11 @@ status_t AudioPolicyService::dumpInternals(int fd) char buffer[SIZE]; String8 result; +#ifdef USE_LEGACY_AUDIO_POLICY snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpAudioPolicy); +#else + snprintf(buffer, SIZE, "AudioPolicyManager: %p\n", mAudioPolicyManager); +#endif result.append(buffer); snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get()); result.append(buffer); @@ -606,7 +192,7 @@ status_t AudioPolicyService::dumpInternals(int fd) 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); @@ -625,9 +211,15 @@ status_t AudioPolicyService::dump(int fd, const Vector<String16>& args) mTonePlaybackThread->dump(fd); } +#ifdef USE_LEGACY_AUDIO_POLICY if (mpAudioPolicy) { mpAudioPolicy->dump(mpAudioPolicy, fd); } +#else + if (mAudioPolicyManager) { + mAudioPolicyManager->dump(fd); + } +#endif if (locked) mLock.unlock(); } @@ -1114,11 +706,13 @@ int AudioPolicyService::setStreamVolume(audio_stream_type_t stream, int AudioPolicyService::startTone(audio_policy_tone_t tone, audio_stream_type_t stream) { - if (tone != AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION) + if (tone != AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION) { ALOGE("startTone: illegal tone requested (%d)", tone); - if (stream != AUDIO_STREAM_VOICE_CALL) + } + if (stream != AUDIO_STREAM_VOICE_CALL) { ALOGE("startTone: illegal stream (%d) requested for tone %d", stream, tone); + } mTonePlaybackThread->startToneCommand(ToneGenerator::TONE_SUP_CALL_WAITING, AUDIO_STREAM_VOICE_CALL); return 0; @@ -1135,21 +729,6 @@ int AudioPolicyService::setVoiceVolume(float volume, int delayMs) return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs); } -bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) -{ - if (mpAudioPolicy == NULL) { - ALOGV("mpAudioPolicy == NULL"); - return false; - } - - if (mpAudioPolicy->is_offload_supported == NULL) { - ALOGV("HAL does not implement is_offload_supported"); - return false; - } - - return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info); -} - // ---------------------------------------------------------------------------- // Audio pre-processing configuration // ---------------------------------------------------------------------------- @@ -1448,42 +1027,18 @@ status_t AudioPolicyService::loadPreProcessorConfig(const char *path) return NO_ERROR; } -/* implementation of the interface to the policy manager */ extern "C" { - - -static audio_module_handle_t aps_load_hw_module(void *service, - const char *name) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - - return af->loadHwModule(name); -} - -// deprecated: replaced by aps_open_output_on_module() -static audio_io_handle_t aps_open_output(void *service, +audio_module_handle_t aps_load_hw_module(void *service __unused, + const char *name); +audio_io_handle_t aps_open_output(void *service __unused, audio_devices_t *pDevices, uint32_t *pSamplingRate, audio_format_t *pFormat, audio_channel_mask_t *pChannelMask, uint32_t *pLatencyMs, - audio_output_flags_t flags) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } + audio_output_flags_t flags); - return af->openOutput((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask, - pLatencyMs, flags); -} - -static audio_io_handle_t aps_open_output_on_module(void *service, +audio_io_handle_t aps_open_output_on_module(void *service __unused, audio_module_handle_t module, audio_devices_t *pDevices, uint32_t *pSamplingRate, @@ -1491,192 +1046,63 @@ static audio_io_handle_t aps_open_output_on_module(void *service, audio_channel_mask_t *pChannelMask, uint32_t *pLatencyMs, audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - return af->openOutput(module, pDevices, pSamplingRate, pFormat, pChannelMask, - pLatencyMs, flags, offloadInfo); -} - -static audio_io_handle_t aps_open_dup_output(void *service, + const audio_offload_info_t *offloadInfo); +audio_io_handle_t aps_open_dup_output(void *service __unused, audio_io_handle_t output1, - audio_io_handle_t output2) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - return af->openDuplicateOutput(output1, output2); -} - -static int aps_close_output(void *service, audio_io_handle_t output) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) - return PERMISSION_DENIED; - - return af->closeOutput(output); -} - -static int aps_suspend_output(void *service, audio_io_handle_t output) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return PERMISSION_DENIED; - } - - return af->suspendOutput(output); -} - -static int aps_restore_output(void *service, audio_io_handle_t output) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return PERMISSION_DENIED; - } - - return af->restoreOutput(output); -} - -// deprecated: replaced by aps_open_input_on_module(), and acoustics parameter is ignored -static audio_io_handle_t aps_open_input(void *service, + audio_io_handle_t output2); +int aps_close_output(void *service __unused, audio_io_handle_t output); +int aps_suspend_output(void *service __unused, audio_io_handle_t output); +int aps_restore_output(void *service __unused, audio_io_handle_t output); +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) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - - return af->openInput((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask); -} - -static audio_io_handle_t aps_open_input_on_module(void *service, + audio_in_acoustics_t acoustics __unused); +audio_io_handle_t aps_open_input_on_module(void *service __unused, audio_module_handle_t module, audio_devices_t *pDevices, uint32_t *pSamplingRate, audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - - return af->openInput(module, pDevices, pSamplingRate, pFormat, pChannelMask); -} - -static int aps_close_input(void *service, audio_io_handle_t input) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) - return PERMISSION_DENIED; - - return af->closeInput(input); -} - -static int aps_set_stream_output(void *service, audio_stream_type_t stream, - audio_io_handle_t output) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) - return PERMISSION_DENIED; - - return af->setStreamOutput(stream, output); -} - -static int aps_move_effects(void *service, int session, + audio_channel_mask_t *pChannelMask); +int aps_close_input(void *service __unused, audio_io_handle_t input); +int aps_invalidate_stream(void *service __unused, audio_stream_type_t stream); +int aps_move_effects(void *service __unused, int session, audio_io_handle_t src_output, - audio_io_handle_t dst_output) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) - return PERMISSION_DENIED; - - return af->moveEffects(session, src_output, dst_output); -} - -static char * aps_get_parameters(void *service, audio_io_handle_t io_handle, - const char *keys) -{ - String8 result = AudioSystem::getParameters(io_handle, String8(keys)); - return strdup(result.string()); -} - -static void aps_set_parameters(void *service, audio_io_handle_t io_handle, - const char *kv_pairs, int delay_ms) -{ - AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; - - audioPolicyService->setParameters(io_handle, kv_pairs, delay_ms); -} - -static int aps_set_stream_volume(void *service, audio_stream_type_t stream, + audio_io_handle_t dst_output); +char * aps_get_parameters(void *service __unused, audio_io_handle_t io_handle, + const char *keys); +void aps_set_parameters(void *service, audio_io_handle_t io_handle, + const char *kv_pairs, int delay_ms); +int aps_set_stream_volume(void *service, audio_stream_type_t stream, float volume, audio_io_handle_t output, - int delay_ms) -{ - AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; - - return audioPolicyService->setStreamVolume(stream, volume, output, - delay_ms); -} - -static int aps_start_tone(void *service, audio_policy_tone_t tone, - audio_stream_type_t stream) -{ - AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; - - return audioPolicyService->startTone(tone, stream); -} - -static int aps_stop_tone(void *service) -{ - AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; - - return audioPolicyService->stopTone(); -} - -static int aps_set_voice_volume(void *service, float volume, int delay_ms) -{ - AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; - - return audioPolicyService->setVoiceVolume(volume, delay_ms); -} - -}; // extern "C" + int delay_ms); +int aps_start_tone(void *service, audio_policy_tone_t tone, + audio_stream_type_t stream); +int aps_stop_tone(void *service); +int aps_set_voice_volume(void *service, float volume, int delay_ms); +}; 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/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h index ae053a9..cdc90d0 100644 --- a/services/audioflinger/AudioPolicyService.h +++ b/services/audiopolicy/AudioPolicyService.h @@ -30,6 +30,8 @@ #include <media/IAudioPolicyService.h> #include <media/ToneGenerator.h> #include <media/AudioEffect.h> +#include <hardware_legacy/AudioPolicyInterface.h> +#include "AudioPolicyManager.h" namespace android { @@ -38,7 +40,6 @@ namespace android { class AudioPolicyService : public BinderService<AudioPolicyService>, public BnAudioPolicyService, -// public AudioPolicyClientInterface, public IBinder::DeathRecipient { friend class BinderService<AudioPolicyService>; @@ -313,6 +314,91 @@ private: Vector< sp<AudioEffect> >mEffects; }; + class AudioPolicyClient : public AudioPolicyClientInterface + { + public: + AudioPolicyClient(AudioPolicyService *service) : mAudioPolicyService(service) {} + virtual ~AudioPolicyClient() {} + + // + // Audio HW module functions + // + + // loads a HW module. + virtual audio_module_handle_t loadHwModule(const char *name); + + // + // Audio output Control functions + // + + // opens an audio output with the requested parameters. The parameter values can indicate to use the default values + // in case the audio policy manager has no specific requirements for the output being opened. + // When the function returns, the parameter values reflect the actual values used by the audio hardware output stream. + // The audio policy manager can check if the proposed parameters are suitable or not and act accordingly. + virtual audio_io_handle_t openOutput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo = NULL); + // creates a special output that is duplicated to the two outputs passed as arguments. The duplication is performed by + // a special mixer thread in the AudioFlinger. + virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2); + // closes the output stream + virtual status_t closeOutput(audio_io_handle_t output); + // suspends the output. When an output is suspended, the corresponding audio hardware output stream is placed in + // standby and the AudioTracks attached to the mixer thread are still processed but the output mix is discarded. + virtual status_t suspendOutput(audio_io_handle_t output); + // restores a suspended output. + virtual status_t restoreOutput(audio_io_handle_t output); + + // + // Audio input Control functions + // + + // opens an audio input + virtual audio_io_handle_t openInput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask); + // closes an audio input + virtual status_t closeInput(audio_io_handle_t input); + // + // misc control functions + // + + // set a stream volume for a particular output. For the same user setting, a given stream type can have different volumes + // for each output (destination device) it is attached to. + virtual status_t setStreamVolume(audio_stream_type_t stream, float volume, audio_io_handle_t output, int delayMs = 0); + + // invalidate a stream type, causing a reroute to an unspecified new output + virtual status_t invalidateStream(audio_stream_type_t stream); + + // function enabling to send proprietary informations directly from audio policy manager to audio hardware interface. + virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0); + // function enabling to receive proprietary informations directly from audio hardware interface to audio policy manager. + virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys); + + // request the playback of a tone on the specified stream: used for instance to replace notification sounds when playing + // over a telephony device during a phone call. + virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream); + virtual status_t stopTone(); + + // set down link audio volume. + virtual status_t setVoiceVolume(float volume, int delayMs = 0); + + // move effect to the specified output + virtual status_t moveEffects(int session, + audio_io_handle_t srcOutput, + audio_io_handle_t dstOutput); + + private: + AudioPolicyService *mAudioPolicyService; + }; + static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1]; void setPreProcessorEnabled(const InputDesc *inputDesc, bool enabled); @@ -344,6 +430,9 @@ private: sp<AudioCommandThread> mOutputCommandThread; // process stop and release output struct audio_policy_device *mpAudioPolicyDev; struct audio_policy *mpAudioPolicy; + AudioPolicyManager *mAudioPolicyManager; + AudioPolicyClient *mAudioPolicyClient; + KeyedVector< audio_source_t, InputSourceDesc* > mInputSources; KeyedVector< audio_io_handle_t, InputDesc* > mInputs; }; diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 51ba698..2f485b9 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -1,3 +1,17 @@ +# Copyright 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + LOCAL_PATH:= $(call my-dir) # @@ -53,6 +67,7 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_C_INCLUDES += \ system/media/camera/include \ + system/media/private/camera/include \ external/jpeg diff --git a/services/camera/libcameraservice/CameraDeviceFactory.cpp b/services/camera/libcameraservice/CameraDeviceFactory.cpp index 7fdf304..bfef50e 100644 --- a/services/camera/libcameraservice/CameraDeviceFactory.cpp +++ b/services/camera/libcameraservice/CameraDeviceFactory.cpp @@ -46,6 +46,8 @@ sp<CameraDeviceBase> CameraDeviceFactory::createDevice(int cameraId) { device = new Camera2Device(cameraId); break; case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: device = new Camera3Device(cameraId); break; default: diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 9ce7daf..5c6f653 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -1,24 +1,24 @@ /* -** -** 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. -*/ + * 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. + */ #define LOG_TAG "CameraService" //#define LOG_NDEBUG 0 #include <stdio.h> +#include <string.h> #include <sys/types.h> #include <pthread.h> @@ -32,10 +32,13 @@ #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> #include <utils/String16.h> +#include <utils/Trace.h> +#include <system/camera_vendor_tags.h> #include "CameraService.h" #include "api1/CameraClient.h" @@ -130,6 +133,12 @@ void CameraService::onFirstRef() mModule->set_callbacks(this); } + VendorTagDescriptor::clearGlobalVendorTagDescriptor(); + + if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_2) { + setUpVendorTags(); + } + CameraDeviceFactory::registerService(this); } } @@ -141,6 +150,7 @@ CameraService::~CameraService() { } } + VendorTagDescriptor::clearGlobalVendorTagDescriptor(); gCameraService = NULL; } @@ -269,6 +279,22 @@ status_t CameraService::getCameraCharacteristics(int cameraId, return ret; } +status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) { + if (!mModule) { + ALOGE("%s: camera hardware module doesn't exist", __FUNCTION__); + return -ENODEV; + } + + if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_2) { + // TODO: Remove this check once HAL1 shim is in place. + ALOGW("%s: Only HAL module version V2.2 or higher supports vendor tags", __FUNCTION__); + return -EOPNOTSUPP; + } + + desc = VendorTagDescriptor::getGlobalVendorTagDescriptor(); + return OK; +} + int CameraService::getDeviceVersion(int cameraId, int* facing) { struct camera_info info; if (mModule->get_camera_info(cameraId, &info) != OK) { @@ -298,6 +324,8 @@ bool CameraService::isValidCameraId(int cameraId) { case CAMERA_DEVICE_API_VERSION_2_0: case CAMERA_DEVICE_API_VERSION_2_1: case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: return true; default: return false; @@ -306,6 +334,44 @@ bool CameraService::isValidCameraId(int cameraId) { return false; } +bool CameraService::setUpVendorTags() { + vendor_tag_ops_t vOps = vendor_tag_ops_t(); + + // Check if vendor operations have been implemented + if (mModule->get_vendor_tag_ops == NULL) { + ALOGI("%s: No vendor tags defined for this device.", __FUNCTION__); + return false; + } + + ATRACE_BEGIN("camera3->get_metadata_vendor_tag_ops"); + mModule->get_vendor_tag_ops(&vOps); + ATRACE_END(); + + // Ensure all vendor operations are present + if (vOps.get_tag_count == NULL || vOps.get_all_tags == NULL || + vOps.get_section_name == NULL || vOps.get_tag_name == NULL || + vOps.get_tag_type == NULL) { + ALOGE("%s: Vendor tag operations not fully defined. Ignoring definitions." + , __FUNCTION__); + return false; + } + + // Read all vendor tag definitions into a descriptor + sp<VendorTagDescriptor> desc; + status_t res; + if ((res = VendorTagDescriptor::createDescriptorFromOps(&vOps, /*out*/desc)) + != OK) { + ALOGE("%s: Could not generate descriptor from vendor tag operations," + "received error %s (%d). Camera clients will not be able to use" + "vendor tags", __FUNCTION__, strerror(res), res); + return false; + } + + // Set the global descriptor to use with camera metadata + VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc); + return true; +} + status_t CameraService::validateConnect(int cameraId, /*inout*/ int& clientUid) const { @@ -455,6 +521,8 @@ status_t CameraService::connect( case CAMERA_DEVICE_API_VERSION_2_0: case CAMERA_DEVICE_API_VERSION_2_1: case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: client = new Camera2Client(this, cameraClient, clientPackageName, cameraId, facing, callingPid, clientUid, getpid(), @@ -541,6 +609,8 @@ status_t CameraService::connectPro( case CAMERA_DEVICE_API_VERSION_2_0: case CAMERA_DEVICE_API_VERSION_2_1: case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: client = new ProCamera2Client(this, cameraCb, String16(), cameraId, facing, callingPid, USE_CALLING_UID, getpid()); break; @@ -619,6 +689,8 @@ status_t CameraService::connectDevice( case CAMERA_DEVICE_API_VERSION_2_0: case CAMERA_DEVICE_API_VERSION_2_1: case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: client = new CameraDeviceClient(this, cameraCb, String16(), cameraId, facing, callingPid, USE_CALLING_UID, getpid()); break; @@ -876,7 +948,7 @@ void CameraService::setCameraFree(int cameraId) { 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 { diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index ad6a582..8853e48 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -1,19 +1,18 @@ /* -** -** 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. -*/ + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H #define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H @@ -31,6 +30,7 @@ #include <camera/IProCameraCallbacks.h> #include <camera/camera2/ICameraDeviceUser.h> #include <camera/camera2/ICameraDeviceCallbacks.h> +#include <camera/VendorTagDescriptor.h> #include <camera/ICameraServiceListener.h> @@ -73,6 +73,7 @@ public: struct CameraInfo* cameraInfo); virtual status_t getCameraCharacteristics(int cameraId, CameraMetadata* cameraInfo); + virtual status_t getCameraVendorTagDescriptor(/*out*/ sp<VendorTagDescriptor>& desc); virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId, const String16& clientPackageName, int clientUid, @@ -387,6 +388,8 @@ private: // Helpers bool isValidCameraId(int cameraId); + + bool setUpVendorTags(); }; } // namespace android diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index af23557..0447979 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -118,7 +118,9 @@ status_t Camera2Client::initialize(camera_module_t *module) mZslProcessorThread = zslProc; break; } - case CAMERA_DEVICE_API_VERSION_3_0:{ + case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: { sp<ZslProcessor3> zslProc = new ZslProcessor3(this, mCaptureSequencer); mZslProcessor = zslProc; @@ -238,7 +240,7 @@ status_t Camera2Client::dump(int fd, const Vector<String16>& args) { 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) @@ -816,6 +818,8 @@ status_t Camera2Client::startPreviewL(Parameters ¶ms, bool restart) { return res; } outputStreams.push(getZslStreamId()); + } else { + mZslProcessor->deleteStream(); } outputStreams.push(getPreviewStreamId()); @@ -1162,7 +1166,7 @@ status_t Camera2Client::autoFocus() { * 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", diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp index d2ac79c..c266213 100644 --- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp @@ -110,11 +110,13 @@ status_t CallbackProcessor::updateStream(const Parameters ¶ms) { if (!mCallbackToApp && mCallbackConsumer == 0) { // Create CPU buffer queue endpoint, since app hasn't given us one // Make it async to avoid disconnect deadlocks - sp<BufferQueue> bq = new BufferQueue(); - mCallbackConsumer = new CpuConsumer(bq, kCallbackHeapCount); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mCallbackConsumer = new CpuConsumer(consumer, kCallbackHeapCount); mCallbackConsumer->setFrameAvailableListener(this); mCallbackConsumer->setName(String8("Camera2Client::CallbackConsumer")); - mCallbackWindow = new Surface(bq); + mCallbackWindow = new Surface(producer); } if (mCallbackStreamId != NO_STREAM) { diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp index 2de7a2b..964d278 100644 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp @@ -83,11 +83,13 @@ status_t JpegProcessor::updateStream(const Parameters ¶ms) { if (mCaptureConsumer == 0) { // Create CPU buffer queue endpoint - sp<BufferQueue> bq = new BufferQueue(); - mCaptureConsumer = new CpuConsumer(bq, 1); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mCaptureConsumer = new CpuConsumer(consumer, 1); mCaptureConsumer->setFrameAvailableListener(this); mCaptureConsumer->setName(String8("Camera2Client::CaptureConsumer")); - mCaptureWindow = new Surface(bq); + mCaptureWindow = new Surface(producer); // Create memory for API consumption mCaptureHeap = new MemoryHeapBase(maxJpegSize.data.i32[0], 0, "Camera2Client::CaptureHeap"); diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp index 07654c0..5bfb969 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.cpp +++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "Camera2-Parameters" #define ATRACE_TAG ATRACE_TAG_CAMERA -// #define LOG_NDEBUG 0 +//#define LOG_NDEBUG 0 #include <utils/Log.h> #include <utils/Trace.h> @@ -92,6 +92,26 @@ status_t Parameters::initialize(const CameraMetadata *info) { staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2); if (!availableFpsRanges.count) return NO_INIT; + previewFpsRange[0] = availableFpsRanges.data.i32[0]; + previewFpsRange[1] = availableFpsRanges.data.i32[1]; + + params.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, + String8::format("%d,%d", + previewFpsRange[0] * kFpsToApiScale, + previewFpsRange[1] * kFpsToApiScale)); + + { + String8 supportedPreviewFpsRange; + for (size_t i=0; i < availableFpsRanges.count; i += 2) { + if (i != 0) supportedPreviewFpsRange += ","; + supportedPreviewFpsRange += String8::format("(%d,%d)", + availableFpsRanges.data.i32[i] * kFpsToApiScale, + availableFpsRanges.data.i32[i+1] * kFpsToApiScale); + } + params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, + supportedPreviewFpsRange); + } + previewFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP; params.set(CameraParameters::KEY_PREVIEW_FORMAT, formatEnumToString(previewFormat)); // NV21 @@ -159,9 +179,6 @@ status_t Parameters::initialize(const CameraMetadata *info) { supportedPreviewFormats); } - previewFpsRange[0] = availableFpsRanges.data.i32[0]; - previewFpsRange[1] = availableFpsRanges.data.i32[1]; - // PREVIEW_FRAME_RATE / SUPPORTED_PREVIEW_FRAME_RATES are deprecated, but // still have to do something sane for them @@ -170,27 +187,6 @@ status_t Parameters::initialize(const CameraMetadata *info) { params.set(CameraParameters::KEY_PREVIEW_FRAME_RATE, previewFps); - // PREVIEW_FPS_RANGE - // -- Order matters. Set range after single value to so that a roundtrip - // of setParameters(getParameters()) would keep the FPS range in higher - // order. - params.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, - String8::format("%d,%d", - previewFpsRange[0] * kFpsToApiScale, - previewFpsRange[1] * kFpsToApiScale)); - - { - String8 supportedPreviewFpsRange; - for (size_t i=0; i < availableFpsRanges.count; i += 2) { - if (i != 0) supportedPreviewFpsRange += ","; - supportedPreviewFpsRange += String8::format("(%d,%d)", - availableFpsRanges.data.i32[i] * kFpsToApiScale, - availableFpsRanges.data.i32[i+1] * kFpsToApiScale); - } - params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, - supportedPreviewFpsRange); - } - { SortedVector<int32_t> sortedPreviewFrameRates; @@ -470,7 +466,7 @@ status_t Parameters::initialize(const CameraMetadata *info) { supportedAntibanding); } - sceneMode = ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED; + sceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED; params.set(CameraParameters::KEY_SCENE_MODE, CameraParameters::SCENE_MODE_AUTO); @@ -486,7 +482,7 @@ status_t Parameters::initialize(const CameraMetadata *info) { 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: @@ -668,13 +664,13 @@ status_t Parameters::initialize(const CameraMetadata *info) { 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, @@ -734,7 +730,7 @@ status_t Parameters::initialize(const CameraMetadata *info) { 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)"); @@ -1088,7 +1084,7 @@ camera_metadata_ro_entry_t Parameters::staticInfo(uint32_t tag, status_t Parameters::set(const String8& paramString) { status_t res; - CameraParameters2 newParams(paramString); + CameraParameters newParams(paramString); // TODO: Currently ignoring any changes to supposedly read-only parameters // such as supported preview sizes, etc. Should probably produce an error if @@ -1131,73 +1127,29 @@ status_t Parameters::set(const String8& paramString) { // RECORDING_HINT (always supported) validatedParams.recordingHint = boolFromString( newParams.get(CameraParameters::KEY_RECORDING_HINT) ); - IF_ALOGV() { // Avoid unused variable warning - bool recordingHintChanged = - validatedParams.recordingHint != recordingHint; - if (recordingHintChanged) { - ALOGV("%s: Recording hint changed to %d", - __FUNCTION__, validatedParams.recordingHint); - } - } + bool recordingHintChanged = validatedParams.recordingHint != recordingHint; + ALOGV_IF(recordingHintChanged, "%s: Recording hint changed to %d", + __FUNCTION__, recordingHintChanged); // PREVIEW_FPS_RANGE + bool fpsRangeChanged = false; + int32_t lastSetFpsRange[2]; - /** - * Use the single FPS value if it was set later than the range. - * Otherwise, use the range value. - */ - bool fpsUseSingleValue; - { - const char *fpsRange, *fpsSingle; - - fpsRange = newParams.get(CameraParameters::KEY_PREVIEW_FRAME_RATE); - fpsSingle = newParams.get(CameraParameters::KEY_PREVIEW_FPS_RANGE); - - /** - * Pick either the range or the single key if only one was set. - * - * If both are set, pick the one that has greater set order. - */ - if (fpsRange == NULL && fpsSingle == NULL) { - ALOGE("%s: FPS was not set. One of %s or %s must be set.", - __FUNCTION__, CameraParameters::KEY_PREVIEW_FRAME_RATE, - CameraParameters::KEY_PREVIEW_FPS_RANGE); - return BAD_VALUE; - } else if (fpsRange == NULL) { - fpsUseSingleValue = true; - ALOGV("%s: FPS range not set, using FPS single value", - __FUNCTION__); - } else if (fpsSingle == NULL) { - fpsUseSingleValue = false; - ALOGV("%s: FPS single not set, using FPS range value", - __FUNCTION__); - } else { - int fpsKeyOrder; - res = newParams.compareSetOrder( - CameraParameters::KEY_PREVIEW_FRAME_RATE, - CameraParameters::KEY_PREVIEW_FPS_RANGE, - &fpsKeyOrder); - LOG_ALWAYS_FATAL_IF(res != OK, "Impossibly bad FPS keys"); - - fpsUseSingleValue = (fpsKeyOrder > 0); + params.getPreviewFpsRange(&lastSetFpsRange[0], &lastSetFpsRange[1]); + lastSetFpsRange[0] /= kFpsToApiScale; + lastSetFpsRange[1] /= kFpsToApiScale; - } - - ALOGV("%s: Preview FPS value is used from '%s'", - __FUNCTION__, fpsUseSingleValue ? "single" : "range"); - } newParams.getPreviewFpsRange(&validatedParams.previewFpsRange[0], &validatedParams.previewFpsRange[1]); - validatedParams.previewFpsRange[0] /= kFpsToApiScale; validatedParams.previewFpsRange[1] /= kFpsToApiScale; - // Ignore the FPS range if the FPS single has higher precedence - if (!fpsUseSingleValue) { - ALOGV("%s: Preview FPS range (%d, %d)", __FUNCTION__, - validatedParams.previewFpsRange[0], - validatedParams.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); for (i = 0; i < availablePreviewFpsRanges.count; i += 2) { @@ -1248,13 +1200,14 @@ status_t Parameters::set(const String8& paramString) { } } - // PREVIEW_FRAME_RATE Deprecated - // - Use only if the single FPS value was set later than the FPS range - if (fpsUseSingleValue) { + // 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. + if (!fpsRangeChanged) { int previewFps = newParams.getPreviewFrameRate(); - ALOGV("%s: Preview FPS single value requested: %d", - __FUNCTION__, previewFps); - { + int lastSetPreviewFps = params.getPreviewFrameRate(); + if (previewFps != lastSetPreviewFps || recordingHintChanged) { camera_metadata_ro_entry_t availableFrameRates = staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); /** @@ -1323,35 +1276,6 @@ status_t Parameters::set(const String8& paramString) { } } - /** - * Update Preview FPS and Preview FPS ranges based on - * what we actually set. - * - * This updates the API-visible (Camera.Parameters#getParameters) values of - * the FPS fields, not only the internal versions. - * - * Order matters: The value that was set last takes precedence. - * - If the client does a setParameters(getParameters()) we retain - * the same order for preview FPS. - */ - if (!fpsUseSingleValue) { - // Set fps single, then fps range (range wins) - newParams.setPreviewFrameRate( - fpsFromRange(/*min*/validatedParams.previewFpsRange[0], - /*max*/validatedParams.previewFpsRange[1])); - newParams.setPreviewFpsRange( - validatedParams.previewFpsRange[0] * kFpsToApiScale, - validatedParams.previewFpsRange[1] * kFpsToApiScale); - } else { - // Set fps range, then fps single (single wins) - newParams.setPreviewFpsRange( - validatedParams.previewFpsRange[0] * kFpsToApiScale, - validatedParams.previewFpsRange[1] * kFpsToApiScale); - // Set this to the same value, but with higher priority - newParams.setPreviewFrameRate( - newParams.getPreviewFrameRate()); - } - // PICTURE_SIZE newParams.getPictureSize(&validatedParams.pictureWidth, &validatedParams.pictureHeight); @@ -1522,7 +1446,7 @@ status_t Parameters::set(const String8& paramString) { 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++) { @@ -1537,7 +1461,7 @@ status_t Parameters::set(const String8& paramString) { } } bool sceneModeSet = - validatedParams.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED; + validatedParams.sceneMode != ANDROID_CONTROL_SCENE_MODE_DISABLED; // FLASH_MODE if (sceneModeSet) { @@ -1667,10 +1591,11 @@ status_t Parameters::set(const String8& paramString) { // 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)); @@ -1700,10 +1625,13 @@ status_t Parameters::set(const String8& paramString) { 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) { @@ -1852,7 +1780,7 @@ status_t Parameters::updateRequest(CameraMetadata *request) const { // (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; @@ -1864,7 +1792,7 @@ status_t Parameters::updateRequest(CameraMetadata *request) const { 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; @@ -1985,6 +1913,23 @@ status_t Parameters::updateRequest(CameraMetadata *request) const { 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 @@ -2225,9 +2170,9 @@ int Parameters::abModeStringToEnum(const char *abMode) { 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) ? diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h index da07ccf..60c4687 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.h +++ b/services/camera/libcameraservice/api1/client2/Parameters.h @@ -25,7 +25,6 @@ #include <utils/Vector.h> #include <utils/KeyedVector.h> #include <camera/CameraParameters.h> -#include <camera/CameraParameters2.h> #include <camera/CameraMetadata.h> namespace android { @@ -33,7 +32,7 @@ namespace camera2 { /** * Current camera state; this is the full state of the Camera under the old - * camera API (contents of the CameraParameters2 object in a more-efficient + * camera API (contents of the CameraParameters object in a more-efficient * format, plus other state). The enum values are mostly based off the * corresponding camera2 enums, not the camera1 strings. A few are defined here * if they don't cleanly map to camera2 values. @@ -114,6 +113,14 @@ struct Parameters { 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; @@ -129,7 +136,7 @@ struct Parameters { LIGHTFX_HDR } lightFx; - CameraParameters2 params; + CameraParameters params; String8 paramsFlattened; // These parameters are also part of the camera API-visible state, but not diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp index 77ae7ec..2064e2c 100644 --- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp @@ -319,13 +319,15 @@ status_t StreamingProcessor::updateRecordingStream(const Parameters ¶ms) { // Create CPU buffer queue endpoint. We need one more buffer here so that we can // always acquire and free a buffer when the heap is full; otherwise the consumer // will have buffers in flight we'll never clear out. - sp<BufferQueue> bq = new BufferQueue(); - mRecordingConsumer = new BufferItemConsumer(bq, + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mRecordingConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_VIDEO_ENCODER, mRecordingHeapCount + 1); mRecordingConsumer->setFrameAvailableListener(this); mRecordingConsumer->setName(String8("Camera2-RecordingConsumer")); - mRecordingWindow = new Surface(bq); + mRecordingWindow = new Surface(producer); newConsumer = true; // Allocate memory later, since we don't know buffer size until receipt } diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp index 130f81a..6ab9e1a 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp @@ -130,13 +130,15 @@ status_t ZslProcessor::updateStream(const Parameters ¶ms) { if (mZslConsumer == 0) { // Create CPU buffer queue endpoint - sp<BufferQueue> bq = new BufferQueue(); - mZslConsumer = new BufferItemConsumer(bq, + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mZslConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_CAMERA_ZSL, kZslBufferDepth); mZslConsumer->setFrameAvailableListener(this); mZslConsumer->setName(String8("Camera2Client::ZslConsumer")); - mZslWindow = new Surface(bq); + mZslWindow = new Surface(producer); } if (mZslStreamId != NO_STREAM) { diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp index 2fce2b6..3949b90 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp @@ -275,6 +275,15 @@ status_t ZslProcessor3::pushToReprocess(int32_t requestId) { 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()); diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 142da9e..1c9a342 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -159,7 +159,7 @@ status_t CameraDeviceClient::submitRequest(sp<CaptureRequest> request, int32_t requestId = mRequestIdCounter++; metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1); - ALOGV("%s: Camera %d: Submitting request with ID %d", + ALOGV("%s: Camera %d: Creating request with ID %d", __FUNCTION__, mCameraId, requestId); if (streaming) { @@ -186,6 +186,116 @@ status_t CameraDeviceClient::submitRequest(sp<CaptureRequest> request, return res; } +status_t CameraDeviceClient::submitRequestList(List<sp<CaptureRequest> > requests, + bool streaming) { + ATRACE_CALL(); + ALOGV("%s-start of function", __FUNCTION__); + + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + Mutex::Autolock icl(mBinderSerializationLock); + + if (!mDevice.get()) return DEAD_OBJECT; + + if (requests.empty()) { + ALOGE("%s: Camera %d: Sent null request. Rejecting request.", + __FUNCTION__, mCameraId); + return BAD_VALUE; + } + + List<const CameraMetadata> metadataRequestList; + int32_t requestId = mRequestIdCounter; + uint32_t loopCounter = 0; + + for (List<sp<CaptureRequest> >::iterator it = requests.begin(); it != requests.end(); ++it) { + sp<CaptureRequest> request = *it; + if (request == 0) { + ALOGE("%s: Camera %d: Sent null request.", + __FUNCTION__, mCameraId); + return BAD_VALUE; + } + + CameraMetadata metadata(request->mMetadata); + if (metadata.isEmpty()) { + ALOGE("%s: Camera %d: Sent empty metadata packet. Rejecting request.", + __FUNCTION__, mCameraId); + return BAD_VALUE; + } else if (request->mSurfaceList.isEmpty()) { + ALOGE("%s: Camera %d: Requests must have at least one surface target. " + "Rejecting request.", __FUNCTION__, mCameraId); + return BAD_VALUE; + } + + if (!enforceRequestPermissions(metadata)) { + // Callee logs + return PERMISSION_DENIED; + } + + /** + * Write in the output stream IDs which we calculate from + * the capture request's list of surface targets + */ + Vector<int32_t> outputStreamIds; + outputStreamIds.setCapacity(request->mSurfaceList.size()); + for (Vector<sp<Surface> >::iterator surfaceIt = 0; + surfaceIt != request->mSurfaceList.end(); ++surfaceIt) { + sp<Surface> surface = *surfaceIt; + if (surface == 0) continue; + + sp<IGraphicBufferProducer> gbp = surface->getIGraphicBufferProducer(); + int idx = mStreamMap.indexOfKey(gbp->asBinder()); + + // Trying to submit request with surface that wasn't created + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: Camera %d: Tried to submit a request with a surface that" + " we have not called createStream on", + __FUNCTION__, mCameraId); + return BAD_VALUE; + } + + int streamId = mStreamMap.valueAt(idx); + outputStreamIds.push_back(streamId); + ALOGV("%s: Camera %d: Appending output stream %d to request", + __FUNCTION__, mCameraId, streamId); + } + + metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0], + outputStreamIds.size()); + + metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1); + loopCounter++; // loopCounter starts from 1 + ALOGV("%s: Camera %d: Creating request with ID %d (%d of %d)", + __FUNCTION__, mCameraId, requestId, loopCounter, requests.size()); + + metadataRequestList.push_back(metadata); + } + mRequestIdCounter++; + + if (streaming) { + res = mDevice->setStreamingRequestList(metadataRequestList); + if (res != OK) { + ALOGE("%s: Camera %d: Got error %d after trying to set streaming " + "request", __FUNCTION__, mCameraId, res); + } else { + mStreamingRequestList.push_back(requestId); + } + } else { + res = mDevice->captureList(metadataRequestList); + if (res != OK) { + ALOGE("%s: Camera %d: Got error %d after trying to set capture", + __FUNCTION__, mCameraId, res); + } + } + + ALOGV("%s: Camera %d: End of function", __FUNCTION__, mCameraId); + if (res == OK) { + return requestId; + } + + return res; +} + status_t CameraDeviceClient::cancelRequest(int requestId) { ATRACE_CALL(); ALOGV("%s, requestId = %d", __FUNCTION__, requestId); @@ -635,26 +745,56 @@ status_t CameraDeviceClient::getRotationTransformLocked(int32_t* transform) { 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 b9c16aa..e96e1ae 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.h +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h @@ -63,8 +63,11 @@ public: */ // Note that the callee gets a copy of the metadata. - virtual int submitRequest(sp<CaptureRequest> request, - bool streaming = false); + virtual status_t submitRequest(sp<CaptureRequest> request, + bool streaming = false); + // List of requests are copied. + virtual status_t submitRequestList(List<sp<CaptureRequest> > requests, + bool streaming = false); virtual status_t cancelRequest(int requestId); // Returns -EBUSY if device is not idle diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h index e80abf1..a4ae179 100644 --- a/services/camera/libcameraservice/common/CameraDeviceBase.h +++ b/services/camera/libcameraservice/common/CameraDeviceBase.h @@ -22,6 +22,7 @@ #include <utils/String16.h> #include <utils/Vector.h> #include <utils/Timers.h> +#include <utils/List.h> #include "hardware/camera2.h" #include "camera/CameraMetadata.h" @@ -58,12 +59,22 @@ class CameraDeviceBase : public virtual RefBase { virtual status_t capture(CameraMetadata &request) = 0; /** + * Submit a list of requests. + */ + virtual status_t captureList(const List<const CameraMetadata> &requests) = 0; + + /** * Submit request for streaming. The CameraDevice makes a copy of the * passed-in buffer and the caller retains ownership. */ virtual status_t setStreamingRequest(const CameraMetadata &request) = 0; /** + * Submit a list of requests for streaming. + */ + virtual status_t setStreamingRequestList(const List<const CameraMetadata> &requests) = 0; + + /** * Clear the streaming request slot. */ virtual status_t clearStreamingRequest() = 0; diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp index 2966d82..0cc3a04 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.cpp +++ b/services/camera/libcameraservice/device2/Camera2Device.cpp @@ -112,20 +112,6 @@ status_t Camera2Device::initialize(camera_module_t *module) return res; } - res = device->ops->get_metadata_vendor_tag_ops(device, &mVendorTagOps); - if (res != OK ) { - ALOGE("%s: Camera %d: Unable to retrieve tag ops from device: %s (%d)", - __FUNCTION__, mId, strerror(-res), res); - device->common.close(&device->common); - return res; - } - res = set_camera_metadata_vendor_tag_ops(mVendorTagOps); - if (res != OK) { - ALOGE("%s: Camera %d: Unable to set tag ops: %s (%d)", - __FUNCTION__, mId, strerror(-res), res); - device->common.close(&device->common); - return res; - } res = device->ops->set_notify_callback(device, notificationCallback, NULL); if (res != OK) { @@ -221,6 +207,12 @@ status_t Camera2Device::capture(CameraMetadata &request) { return OK; } +status_t Camera2Device::captureList(const List<const CameraMetadata> &requests) { + ATRACE_CALL(); + ALOGE("%s: Camera2Device burst capture not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + status_t Camera2Device::setStreamingRequest(const CameraMetadata &request) { ATRACE_CALL(); @@ -229,6 +221,12 @@ status_t Camera2Device::setStreamingRequest(const CameraMetadata &request) { return mRequestQueue.setStreamSlot(streamRequest.release()); } +status_t Camera2Device::setStreamingRequestList(const List<const CameraMetadata> &requests) { + ATRACE_CALL(); + ALOGE("%s, Camera2Device streaming burst not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + status_t Camera2Device::clearStreamingRequest() { ATRACE_CALL(); return mRequestQueue.setStreamSlot(NULL); diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h index 1f53c56..61bfd1a 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.h +++ b/services/camera/libcameraservice/device2/Camera2Device.h @@ -48,7 +48,9 @@ class Camera2Device: public CameraDeviceBase { virtual status_t dump(int fd, const Vector<String16>& args); virtual const CameraMetadata& info() const; virtual status_t capture(CameraMetadata &request); + virtual status_t captureList(const List<const CameraMetadata> &requests); virtual status_t setStreamingRequest(const CameraMetadata &request); + virtual status_t setStreamingRequestList(const List<const CameraMetadata> &requests); virtual status_t clearStreamingRequest(); virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout); virtual status_t createStream(sp<ANativeWindow> consumer, @@ -78,7 +80,6 @@ class Camera2Device: public CameraDeviceBase { camera2_device_t *mHal2Device; CameraMetadata mDeviceInfo; - vendor_tag_query_ops_t *mVendorTagOps; /** * Queue class for both sending requests to a camera2 device, and for diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 7e11a3b..f586e75 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -102,8 +102,10 @@ status_t Camera3Device::initialize(camera_module_t *module) 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); @@ -112,9 +114,9 @@ status_t Camera3Device::initialize(camera_module_t *module) /** Cross-check device version */ - if (device->common.version != CAMERA_DEVICE_API_VERSION_3_0) { + if (device->common.version < CAMERA_DEVICE_API_VERSION_3_0) { SET_ERR_L("Could not open camera: " - "Camera device is not version %x, reports %x instead", + "Camera device should be at least %x, reports %x instead", CAMERA_DEVICE_API_VERSION_3_0, device->common.version); device->common.close(&device->common); @@ -128,7 +130,7 @@ status_t Camera3Device::initialize(camera_module_t *module) if (info.device_version != device->common.version) { SET_ERR_L("HAL reporting mismatched camera_info version (%x)" " and device version (%x).", - device->common.version, info.device_version); + info.device_version, device->common.version); device->common.close(&device->common); return BAD_VALUE; } @@ -146,24 +148,6 @@ status_t Camera3Device::initialize(camera_module_t *module) return BAD_VALUE; } - /** Get vendor metadata tags */ - - mVendorTagOps.get_camera_vendor_section_name = NULL; - - ATRACE_BEGIN("camera3->get_metadata_vendor_tag_ops"); - device->ops->get_metadata_vendor_tag_ops(device, &mVendorTagOps); - ATRACE_END(); - - if (mVendorTagOps.get_camera_vendor_section_name != NULL) { - res = set_camera_metadata_vendor_tag_ops(&mVendorTagOps); - if (res != OK) { - SET_ERR_L("Unable to set tag ops: %s (%d)", - strerror(-res), res); - device->common.close(&device->common); - return res; - } - } - /** Start up status tracker thread */ mStatusTracker = new StatusTracker(this); res = mStatusTracker->run(String8::format("C3Dev-%d-Status", mId).string()); @@ -271,7 +255,9 @@ status_t Camera3Device::disconnect() { mStatusTracker.clear(); if (mHal3Device != NULL) { + ATRACE_BEGIN("camera3->close"); mHal3Device->common.close(&mHal3Device->common); + ATRACE_END(); mHal3Device = NULL; } @@ -386,6 +372,45 @@ const CameraMetadata& Camera3Device::info() const { return mDeviceInfo; } +status_t Camera3Device::checkStatusOkToCaptureLocked() { + switch (mStatus) { + case STATUS_ERROR: + CLOGE("Device has encountered a serious error"); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + CLOGE("Device not initialized"); + return INVALID_OPERATION; + case STATUS_UNCONFIGURED: + case STATUS_CONFIGURED: + case STATUS_ACTIVE: + // OK + break; + default: + SET_ERR_L("Unexpected status: %d", mStatus); + return INVALID_OPERATION; + } + return OK; +} + +status_t Camera3Device::convertMetadataListToRequestListLocked( + const List<const CameraMetadata> &metadataList, RequestList *requestList) { + if (requestList == NULL) { + CLOGE("requestList cannot be NULL."); + return BAD_VALUE; + } + + for (List<const CameraMetadata>::const_iterator it = metadataList.begin(); + it != metadataList.end(); ++it) { + sp<CaptureRequest> newRequest = setUpRequestLocked(*it); + if (newRequest == 0) { + CLOGE("Can't create capture request"); + return BAD_VALUE; + } + requestList->push_back(newRequest); + } + return OK; +} + status_t Camera3Device::capture(CameraMetadata &request) { ATRACE_CALL(); status_t res; @@ -426,10 +451,59 @@ status_t Camera3Device::capture(CameraMetadata &request) { kActiveTimeout/1e9); } ALOGV("Camera %d: Capture request enqueued", mId); + } else { + CLOGE("Cannot queue request. Impossible."); // queueRequest always returns OK. + return BAD_VALUE; } return res; } +status_t Camera3Device::submitRequestsHelper( + const List<const CameraMetadata> &requests, bool repeating) { + ATRACE_CALL(); + Mutex::Autolock il(mInterfaceLock); + Mutex::Autolock l(mLock); + + status_t res = checkStatusOkToCaptureLocked(); + if (res != OK) { + // error logged by previous call + return res; + } + + RequestList requestList; + + res = convertMetadataListToRequestListLocked(requests, /*out*/&requestList); + if (res != OK) { + // error logged by previous call + return res; + } + + if (repeating) { + res = mRequestThread->setRepeatingRequests(requestList); + } else { + res = mRequestThread->queueRequestList(requestList); + } + + 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); + } else { + CLOGE("Cannot queue request. Impossible."); + return BAD_VALUE; + } + + return res; +} + +status_t Camera3Device::captureList(const List<const CameraMetadata> &requests) { + ATRACE_CALL(); + + return submitRequestsHelper(requests, /*repeating*/false); +} status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) { ATRACE_CALL(); @@ -476,6 +550,11 @@ status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) { return res; } +status_t Camera3Device::setStreamingRequestList(const List<const CameraMetadata> &requests) { + ATRACE_CALL(); + + return submitRequestsHelper(requests, /*repeating*/true); +} sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked( const CameraMetadata &request) { @@ -838,16 +917,20 @@ status_t Camera3Device::deleteStream(int id) { } 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); } @@ -916,6 +999,10 @@ status_t Camera3Device::waitUntilDrained() { Mutex::Autolock il(mInterfaceLock); Mutex::Autolock l(mLock); + return waitUntilDrainedLocked(); +} + +status_t Camera3Device::waitUntilDrainedLocked() { switch (mStatus) { case STATUS_UNINITIALIZED: case STATUS_UNCONFIGURED: @@ -1122,7 +1209,14 @@ status_t Camera3Device::flush() { 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 = waitUntilDrainedLocked(); + } + + return res; } /** @@ -1666,8 +1760,10 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { return; } - // Check if everything has arrived for this result (buffers and metadata) - 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); } @@ -1898,6 +1994,19 @@ status_t Camera3Device::RequestThread::queueRequest( return OK; } +status_t Camera3Device::RequestThread::queueRequestList( + List<sp<CaptureRequest> > &requests) { + Mutex::Autolock l(mRequestLock); + for (List<sp<CaptureRequest> >::iterator it = requests.begin(); it != requests.end(); + ++it) { + mRequestQueue.push_back(*it); + } + + unpauseForNewRequests(); + + return OK; +} + status_t Camera3Device::RequestThread::queueTrigger( RequestTrigger trigger[], diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index 468f641..ed58246 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -54,7 +54,7 @@ class Camera3StreamInterface; } /** - * CameraDevice for HAL devices with version CAMERA_DEVICE_API_VERSION_3_0 + * CameraDevice for HAL devices with version CAMERA_DEVICE_API_VERSION_3_0 or higher. */ class Camera3Device : public CameraDeviceBase, @@ -79,7 +79,9 @@ class Camera3Device : // Capture and setStreamingRequest will configure streams if currently in // idle state virtual status_t capture(CameraMetadata &request); + virtual status_t captureList(const List<const CameraMetadata> &requests); virtual status_t setStreamingRequest(const CameraMetadata &request); + virtual status_t setStreamingRequestList(const List<const CameraMetadata> &requests); virtual status_t clearStreamingRequest(); virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout); @@ -157,7 +159,6 @@ class Camera3Device : camera3_device_t *mHal3Device; CameraMetadata mDeviceInfo; - vendor_tag_query_ops_t mVendorTagOps; enum Status { STATUS_ERROR, @@ -202,6 +203,14 @@ class Camera3Device : }; typedef List<sp<CaptureRequest> > RequestList; + status_t checkStatusOkToCaptureLocked(); + + status_t convertMetadataListToRequestListLocked( + const List<const CameraMetadata> &metadataList, + /*out*/RequestList *requestList); + + status_t submitRequestsHelper(const List<const CameraMetadata> &requests, bool repeating); + /** * Get the last request submitted to the hal by the request thread. * @@ -237,6 +246,13 @@ class Camera3Device : status_t waitUntilStateThenRelock(bool active, nsecs_t timeout); /** + * Implementation of waitUntilDrained. On success, will transition to IDLE state. + * + * Need to be called with mLock and mInterfaceLock held. + */ + status_t waitUntilDrainedLocked(); + + /** * Do common work for setting up a streaming or single capture request. * On success, will transition to ACTIVE if in IDLE. */ @@ -313,6 +329,8 @@ class Camera3Device : status_t queueRequest(sp<CaptureRequest> request); + status_t queueRequestList(List<sp<CaptureRequest> > &requests); + /** * Remove all queued and repeating requests, and pending triggers */ diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp index 5aa9a3e..dd7fb6c 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp @@ -199,14 +199,36 @@ status_t Camera3InputStream::configureQueueLocked() { 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(); - mConsumer = new BufferItemConsumer(bq, camera3_stream::usage, + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + int minUndequeuedBuffers = 0; + res = producer->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(consumer, 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 @@ class Camera3InputStream : public Camera3IOStreamBase { 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/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp index 44d8188..09e14c5 100644 --- a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp @@ -111,15 +111,17 @@ struct TimestampFinder : public RingBufferConsumer::RingBufferComparator { } // 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); - mConsumer = new Surface(bq); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mProducer = new RingBufferConsumer(consumer, GRALLOC_USAGE_HW_CAMERA_ZSL, bufferCount); + mConsumer = new Surface(producer); } Camera3ZslStream::~Camera3ZslStream() { 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 @@ class Camera3ZslStream : 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/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 @@ class RingBufferConsumer : public ConsumerBase, // 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/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 @@ void MediaLogService::unregisterWriter(const sp<IMemory>& shared) } } -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..62eddca 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 @@ static double kaiser(int k, int N, double beta) { 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 @@ static void usage(char* name) { " -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 @@ static void usage(char* name) { 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 @@ int main(int argc, char** argv) 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 @@ int main(int argc, char** argv) // 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,33 +132,33 @@ int main(int argc, char** argv) // 100 dB 10.056 double beta = 7.865; - // 2*nzc = (A - 8) / (2.285 * dw) // with dw the transition width = 2*pi*dF/Fs // int nzc = 8; - // - // Example: - // 44.1 KHz to 48 KHz resampling - // 100 dB rejection above 28 KHz - // (the spectrum will fold around 24 KHz and we want 100 dB rejection - // at the point where the folding reaches 20 KHz) - // ...___|_____ - // | \| - // | ____/|\____ - // |/alias| \ - // ------/------+------\---------> KHz - // 20 24 28 - - // Transition band 8 KHz, or dw = 1.0472 - // - // beta = 10.056 - // nzc = 20 - // + /* + * Example: + * 44.1 KHz to 48 KHz resampling + * 100 dB rejection above 28 KHz + * (the spectrum will fold around 24 KHz and we want 100 dB rejection + * at the point where the folding reaches 20 KHz) + * ...___|_____ + * | \| + * | ____/|\____ + * |/alias| \ + * ------/------+------\---------> KHz + * 20 24 28 + * + * Transition band 8 KHz, or dw = 1.0472 + * + * beta = 10.056 + * 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 @@ int main(int argc, char** argv) 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 @@ int main(int argc, char** argv) // 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 @@ int main(int argc, char** argv) 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 @@ int main(int argc, char** argv) 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,18 +257,20 @@ int main(int argc, char** argv) 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,"); } } } } else { - for (int j=0 ; j<polyN ; j++) { + for (unsigned int j=0 ; j<polyN ; j++) { // calculate the phase double p = ((polyM*j) % polyN) / double(polyN); if (!debug) printf("\n "); @@ -254,9 +281,12 @@ int main(int argc, char** argv) 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 @@ int main(int argc, char** argv) } // http://www.csee.umbc.edu/help/sound/AFsp-V2R1/html/audio/ResampAudio.html - - |