diff options
933 files changed, 31828 insertions, 17164 deletions
diff --git a/camera/Android.mk b/camera/Android.mk index da5ac59..df7279f 100644 --- a/camera/Android.mk +++ b/camera/Android.mk @@ -35,6 +35,7 @@ LOCAL_SRC_FILES:= \ camera2/ICameraDeviceUser.cpp \ camera2/ICameraDeviceCallbacks.cpp \ camera2/CaptureRequest.cpp \ + camera2/OutputConfiguration.cpp \ ProCamera.cpp \ CameraBase.cpp \ CameraUtils.cpp \ diff --git a/camera/Camera.cpp b/camera/Camera.cpp index 85f44f0..3a9fb4c 100644 --- a/camera/Camera.cpp +++ b/camera/Camera.cpp @@ -55,7 +55,7 @@ sp<Camera> Camera::create(const sp<ICamera>& camera) if (camera->connect(c) == NO_ERROR) { c->mStatus = NO_ERROR; c->mCamera = camera; - camera->asBinder()->linkToDeath(c); + IInterface::asBinder(camera)->linkToDeath(c); return c; } return 0; @@ -93,7 +93,7 @@ status_t Camera::connectLegacy(int cameraId, int halVersion, clientUid, /*out*/c->mCamera); } if (status == OK && c->mCamera != 0) { - c->mCamera->asBinder()->linkToDeath(c); + IInterface::asBinder(c->mCamera)->linkToDeath(c); c->mStatus = NO_ERROR; camera = c; } else { diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp index 04694cd..65a1a47 100644 --- a/camera/CameraBase.cpp +++ b/camera/CameraBase.cpp @@ -107,7 +107,7 @@ sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId, /*out*/ c->mCamera); } if (status == OK && c->mCamera != 0) { - c->mCamera->asBinder()->linkToDeath(c); + IInterface::asBinder(c->mCamera)->linkToDeath(c); c->mStatus = NO_ERROR; } else { ALOGW("An error occurred while connecting to camera: %d", cameraId); @@ -122,7 +122,7 @@ void CameraBase<TCam, TCamTraits>::disconnect() ALOGV("%s: disconnect", __FUNCTION__); if (mCamera != 0) { mCamera->disconnect(); - mCamera->asBinder()->unlinkToDeath(this); + IInterface::asBinder(mCamera)->unlinkToDeath(this); mCamera = 0; } ALOGV("%s: disconnect (done)", __FUNCTION__); diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp index 25d632d..68969cf 100644 --- a/camera/CameraParameters.cpp +++ b/camera/CameraParameters.cpp @@ -488,6 +488,11 @@ void CameraParameters::getSupportedPreviewFormats(Vector<int>& formats) const { const char* supportedPreviewFormats = get(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS); + if (supportedPreviewFormats == NULL) { + ALOGW("%s: No supported preview formats.", __FUNCTION__); + return; + } + String8 fmtStr(supportedPreviewFormats); char* prevFmts = fmtStr.lockBuffer(fmtStr.size()); @@ -521,8 +526,12 @@ int CameraParameters::previewFormatToEnum(const char* format) { !strcmp(format, PIXEL_FORMAT_RGBA8888) ? HAL_PIXEL_FORMAT_RGBA_8888 : // RGB8888 !strcmp(format, PIXEL_FORMAT_BAYER_RGGB) ? - HAL_PIXEL_FORMAT_RAW_SENSOR : // Raw sensor data + HAL_PIXEL_FORMAT_RAW16 : // Raw sensor data -1; } +bool CameraParameters::isEmpty() const { + return mMap.isEmpty(); +} + }; // namespace android diff --git a/camera/ICamera.cpp b/camera/ICamera.cpp index 8c6e1f7..9943be6 100644 --- a/camera/ICamera.cpp +++ b/camera/ICamera.cpp @@ -75,7 +75,7 @@ public: ALOGV("setPreviewTarget"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); - sp<IBinder> b(bufferProducer->asBinder()); + sp<IBinder> b(IInterface::asBinder(bufferProducer)); data.writeStrongBinder(b); remote()->transact(SET_PREVIEW_TARGET, data, &reply); return reply.readInt32(); @@ -98,7 +98,7 @@ public: ALOGV("setPreviewCallbackTarget"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); - sp<IBinder> b(callbackProducer->asBinder()); + sp<IBinder> b(IInterface::asBinder(callbackProducer)); data.writeStrongBinder(b); remote()->transact(SET_PREVIEW_CALLBACK_TARGET, data, &reply); return reply.readInt32(); @@ -147,7 +147,7 @@ public: ALOGV("releaseRecordingFrame"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); - data.writeStrongBinder(mem->asBinder()); + data.writeStrongBinder(IInterface::asBinder(mem)); remote()->transact(RELEASE_RECORDING_FRAME, data, &reply); } @@ -250,7 +250,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); - data.writeStrongBinder(cameraClient->asBinder()); + data.writeStrongBinder(IInterface::asBinder(cameraClient)); remote()->transact(CONNECT, data, &reply); return reply.readInt32(); } diff --git a/camera/ICameraClient.cpp b/camera/ICameraClient.cpp index 205c8ba..179a341 100644 --- a/camera/ICameraClient.cpp +++ b/camera/ICameraClient.cpp @@ -58,7 +58,7 @@ public: Parcel data, reply; data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); data.writeInt32(msgType); - data.writeStrongBinder(imageData->asBinder()); + data.writeStrongBinder(IInterface::asBinder(imageData)); if (metadata) { data.writeInt32(metadata->number_of_faces); data.write(metadata->faces, sizeof(camera_face_t) * metadata->number_of_faces); @@ -74,7 +74,7 @@ public: data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); data.writeInt64(timestamp); data.writeInt32(msgType); - data.writeStrongBinder(imageData->asBinder()); + data.writeStrongBinder(IInterface::asBinder(imageData)); remote()->transact(DATA_CALLBACK_TIMESTAMP, data, &reply, IBinder::FLAG_ONEWAY); } }; diff --git a/camera/ICameraRecordingProxy.cpp b/camera/ICameraRecordingProxy.cpp index 7223b6d..3dc0ffb 100644 --- a/camera/ICameraRecordingProxy.cpp +++ b/camera/ICameraRecordingProxy.cpp @@ -45,7 +45,7 @@ public: ALOGV("startRecording"); Parcel data, reply; data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor()); - data.writeStrongBinder(listener->asBinder()); + data.writeStrongBinder(IInterface::asBinder(listener)); remote()->transact(START_RECORDING, data, &reply); return reply.readInt32(); } @@ -63,7 +63,7 @@ public: ALOGV("releaseRecordingFrame"); Parcel data, reply; data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor()); - data.writeStrongBinder(mem->asBinder()); + data.writeStrongBinder(IInterface::asBinder(mem)); remote()->transact(RELEASE_RECORDING_FRAME, data, &reply); } }; diff --git a/camera/ICameraRecordingProxyListener.cpp b/camera/ICameraRecordingProxyListener.cpp index cb17f19..cf848fc 100644 --- a/camera/ICameraRecordingProxyListener.cpp +++ b/camera/ICameraRecordingProxyListener.cpp @@ -42,7 +42,7 @@ public: data.writeInterfaceToken(ICameraRecordingProxyListener::getInterfaceDescriptor()); data.writeInt64(timestamp); data.writeInt32(msgType); - data.writeStrongBinder(imageData->asBinder()); + data.writeStrongBinder(IInterface::asBinder(imageData)); remote()->transact(DATA_CALLBACK_TIMESTAMP, data, &reply, IBinder::FLAG_ONEWAY); } }; diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp index 5485205..a75cb48 100644 --- a/camera/ICameraService.cpp +++ b/camera/ICameraService.cpp @@ -172,7 +172,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); - data.writeStrongBinder(cameraClient->asBinder()); + data.writeStrongBinder(IInterface::asBinder(cameraClient)); data.writeInt32(cameraId); data.writeString16(clientPackageName); data.writeInt32(clientUid); @@ -194,7 +194,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); - data.writeStrongBinder(cameraClient->asBinder()); + data.writeStrongBinder(IInterface::asBinder(cameraClient)); data.writeInt32(cameraId); data.writeInt32(halVersion); data.writeString16(clientPackageName); @@ -209,6 +209,20 @@ public: return status; } + virtual status_t setTorchMode(const String16& cameraId, bool enabled, + const sp<IBinder>& clientBinder) + { + Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); + data.writeString16(cameraId); + data.writeInt32(enabled ? 1 : 0); + data.writeStrongBinder(clientBinder); + remote()->transact(BnCameraService::SET_TORCH_MODE, data, &reply); + + if (readExceptionCode(reply)) return -EPROTO; + return reply.readInt32(); + } + // connect to camera service (pro client) virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb, int cameraId, const String16 &clientPackageName, int clientUid, @@ -217,7 +231,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); - data.writeStrongBinder(cameraCb->asBinder()); + data.writeStrongBinder(IInterface::asBinder(cameraCb)); data.writeInt32(cameraId); data.writeString16(clientPackageName); data.writeInt32(clientUid); @@ -242,7 +256,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); - data.writeStrongBinder(cameraCb->asBinder()); + data.writeStrongBinder(IInterface::asBinder(cameraCb)); data.writeInt32(cameraId); data.writeString16(clientPackageName); data.writeInt32(clientUid); @@ -260,7 +274,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); - data.writeStrongBinder(listener->asBinder()); + data.writeStrongBinder(IInterface::asBinder(listener)); remote()->transact(BnCameraService::ADD_LISTENER, data, &reply); if (readExceptionCode(reply)) return -EPROTO; @@ -271,7 +285,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); - data.writeStrongBinder(listener->asBinder()); + data.writeStrongBinder(IInterface::asBinder(listener)); remote()->transact(BnCameraService::REMOVE_LISTENER, data, &reply); if (readExceptionCode(reply)) return -EPROTO; @@ -384,7 +398,7 @@ status_t BnCameraService::onTransact( reply->writeInt32(status); if (camera != NULL) { reply->writeInt32(1); - reply->writeStrongBinder(camera->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(camera)); } else { reply->writeInt32(0); } @@ -404,7 +418,7 @@ status_t BnCameraService::onTransact( reply->writeInt32(status); if (camera != NULL) { reply->writeInt32(1); - reply->writeStrongBinder(camera->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(camera)); } else { reply->writeInt32(0); } @@ -424,7 +438,7 @@ status_t BnCameraService::onTransact( reply->writeInt32(status); if (camera != NULL) { reply->writeInt32(1); - reply->writeStrongBinder(camera->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(camera)); } else { reply->writeInt32(0); } @@ -484,12 +498,22 @@ status_t BnCameraService::onTransact( reply->writeInt32(status); if (camera != NULL) { reply->writeInt32(1); - reply->writeStrongBinder(camera->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(camera)); } else { reply->writeInt32(0); } return NO_ERROR; } break; + case SET_TORCH_MODE: { + CHECK_INTERFACE(ICameraService, data, reply); + String16 cameraId = data.readString16(); + bool enabled = data.readInt32() != 0 ? true : false; + const sp<IBinder> clientBinder = data.readStrongBinder(); + status_t status = setTorchMode(cameraId, enabled, clientBinder); + reply->writeNoException(); + reply->writeInt32(status); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/camera/ICameraServiceListener.cpp b/camera/ICameraServiceListener.cpp index b2f1729..90a8bc2 100644 --- a/camera/ICameraServiceListener.cpp +++ b/camera/ICameraServiceListener.cpp @@ -29,6 +29,7 @@ namespace android { namespace { enum { STATUS_CHANGED = IBinder::FIRST_CALL_TRANSACTION, + TORCH_STATUS_CHANGED, }; }; // namespace anonymous @@ -54,8 +55,21 @@ public: data, &reply, IBinder::FLAG_ONEWAY); + } - reply.readExceptionCode(); + virtual void onTorchStatusChanged(TorchStatus status, const String16 &cameraId) + { + Parcel data, reply; + data.writeInterfaceToken( + ICameraServiceListener::getInterfaceDescriptor()); + + data.writeInt32(static_cast<int32_t>(status)); + data.writeString16(cameraId); + + remote()->transact(TORCH_STATUS_CHANGED, + data, + &reply, + IBinder::FLAG_ONEWAY); } }; @@ -75,7 +89,16 @@ status_t BnCameraServiceListener::onTransact( int32_t cameraId = data.readInt32(); onStatusChanged(status, cameraId); - reply->writeNoException(); + + return NO_ERROR; + } break; + case TORCH_STATUS_CHANGED: { + CHECK_INTERFACE(ICameraServiceListener, data, reply); + + TorchStatus status = static_cast<TorchStatus>(data.readInt32()); + String16 cameraId = data.readString16(); + + onTorchStatusChanged(status, cameraId); return NO_ERROR; } break; diff --git a/camera/IProCameraUser.cpp b/camera/IProCameraUser.cpp index 8f22124..9bd7597 100644 --- a/camera/IProCameraUser.cpp +++ b/camera/IProCameraUser.cpp @@ -65,7 +65,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); - data.writeStrongBinder(cameraClient->asBinder()); + data.writeStrongBinder(IInterface::asBinder(cameraClient)); remote()->transact(CONNECT, data, &reply); return reply.readInt32(); } @@ -150,7 +150,7 @@ public: data.writeInt32(height); data.writeInt32(format); - sp<IBinder> b(bufferProducer->asBinder()); + sp<IBinder> b(IInterface::asBinder(bufferProducer)); data.writeStrongBinder(b); remote()->transact(CREATE_STREAM, data, &reply); diff --git a/camera/camera2/CaptureRequest.cpp b/camera/camera2/CaptureRequest.cpp index 57e5319..66d6913 100644 --- a/camera/camera2/CaptureRequest.cpp +++ b/camera/camera2/CaptureRequest.cpp @@ -63,9 +63,9 @@ status_t CaptureRequest::readFromParcel(Parcel* parcel) { } // Surface.writeToParcel - String16 name = parcel->readString16(); - ALOGV("%s: Read surface name = %s", - __FUNCTION__, String8(name).string()); + const char16_t* name = parcel->readString16Inplace(&len); + ALOGV("%s: Read surface name = %s", __FUNCTION__, + name != NULL ? String8(name).string() : "<null>"); sp<IBinder> binder(parcel->readStrongBinder()); ALOGV("%s: Read surface binder = %p", __FUNCTION__, binder.get()); @@ -106,7 +106,7 @@ status_t CaptureRequest::writeToParcel(Parcel* parcel) const { sp<IBinder> binder; if (surface != 0) { - binder = surface->getIGraphicBufferProducer()->asBinder(); + binder = IInterface::asBinder(surface->getIGraphicBufferProducer()); } // not sure if readParcelableArray does this, hard to tell from source diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp index ff4a0c2..89c6fb7 100644 --- a/camera/camera2/ICameraDeviceUser.cpp +++ b/camera/camera2/ICameraDeviceUser.cpp @@ -26,6 +26,7 @@ #include <gui/Surface.h> #include <camera/CameraMetadata.h> #include <camera/camera2/CaptureRequest.h> +#include <camera/camera2/OutputConfiguration.h> namespace android { @@ -107,7 +108,7 @@ public: } } - if ((res != NO_ERROR) || (resFrameNumber != NO_ERROR)) { + if ((res < NO_ERROR) || (resFrameNumber != NO_ERROR)) { res = FAILED_TRANSACTION; } return res; @@ -147,7 +148,7 @@ public: resFrameNumber = reply.readInt64(lastFrameNumber); } } - if ((res != NO_ERROR) || (resFrameNumber != NO_ERROR)) { + if ((res < NO_ERROR) || (resFrameNumber != NO_ERROR)) { res = FAILED_TRANSACTION; } return res; @@ -167,7 +168,7 @@ public: status_t resFrameNumber = BAD_VALUE; if (reply.readInt32() != 0) { if (lastFrameNumber != NULL) { - res = reply.readInt64(lastFrameNumber); + resFrameNumber = reply.readInt64(lastFrameNumber); } } if ((res != NO_ERROR) || (resFrameNumber != NO_ERROR)) { @@ -208,20 +209,16 @@ public: return reply.readInt32(); } - virtual status_t createStream(int width, int height, int format, - const sp<IGraphicBufferProducer>& bufferProducer) + virtual status_t createStream(const OutputConfiguration& outputConfiguration) { Parcel data, reply; data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor()); - data.writeInt32(width); - data.writeInt32(height); - data.writeInt32(format); - - data.writeInt32(1); // marker that bufferProducer is not null - data.writeString16(String16("unknown_name")); // name of surface - sp<IBinder> b(bufferProducer->asBinder()); - data.writeStrongBinder(b); - + if (outputConfiguration.getGraphicBufferProducer() != NULL) { + data.writeInt32(1); // marker that OutputConfiguration is not null. Mimic aidl behavior + outputConfiguration.writeToParcel(data); + } else { + data.writeInt32(0); + } remote()->transact(CREATE_STREAM, data, &reply); reply.readExceptionCode(); @@ -296,7 +293,7 @@ public: status_t resFrameNumber = BAD_VALUE; if (reply.readInt32() != 0) { if (lastFrameNumber != NULL) { - res = reply.readInt64(lastFrameNumber); + resFrameNumber = reply.readInt64(lastFrameNumber); } } if ((res != NO_ERROR) || (resFrameNumber != NO_ERROR)) { @@ -396,31 +393,15 @@ status_t BnCameraDeviceUser::onTransact( } break; case CREATE_STREAM: { CHECK_INTERFACE(ICameraDeviceUser, data, reply); - int width, height, format; - - width = data.readInt32(); - ALOGV("%s: CREATE_STREAM: width = %d", __FUNCTION__, width); - height = data.readInt32(); - ALOGV("%s: CREATE_STREAM: height = %d", __FUNCTION__, height); - format = data.readInt32(); - ALOGV("%s: CREATE_STREAM: format = %d", __FUNCTION__, format); - sp<IGraphicBufferProducer> bp; + status_t ret = BAD_VALUE; if (data.readInt32() != 0) { - String16 name = readMaybeEmptyString16(data); - bp = interface_cast<IGraphicBufferProducer>( - data.readStrongBinder()); - - ALOGV("%s: CREATE_STREAM: bp = %p, name = %s", __FUNCTION__, - bp.get(), String8(name).string()); + OutputConfiguration outputConfiguration(data); + ret = createStream(outputConfiguration); } else { - ALOGV("%s: CREATE_STREAM: bp = unset, name = unset", - __FUNCTION__); + ALOGE("%s: cannot take an empty OutputConfiguration", __FUNCTION__); } - status_t ret; - ret = createStream(width, height, format, bp); - reply->writeNoException(); ALOGV("%s: CREATE_STREAM: write noException", __FUNCTION__); reply->writeInt32(ret); diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp new file mode 100644 index 0000000..24acaa0 --- /dev/null +++ b/camera/camera2/OutputConfiguration.cpp @@ -0,0 +1,79 @@ +/* +** +** Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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 "OutputConfiguration" +#include <utils/Log.h> + +#include <camera/camera2/OutputConfiguration.h> +#include <binder/Parcel.h> + +namespace android { + + +const int OutputConfiguration::INVALID_ROTATION = -1; + +// Read empty strings without printing a false error message. +String16 OutputConfiguration::readMaybeEmptyString16(const Parcel& parcel) { + size_t len; + const char16_t* str = parcel.readString16Inplace(&len); + if (str != NULL) { + return String16(str, len); + } else { + return String16(); + } +} + +sp<IGraphicBufferProducer> OutputConfiguration::getGraphicBufferProducer() const { + return mGbp; +} + +int OutputConfiguration::getRotation() const { + return mRotation; +} + +OutputConfiguration::OutputConfiguration(const Parcel& parcel) { + status_t err; + int rotation = 0; + if ((err = parcel.readInt32(&rotation)) != OK) { + ALOGE("%s: Failed to read rotation from parcel", __FUNCTION__); + mGbp = NULL; + mRotation = INVALID_ROTATION; + return; + } + + String16 name = readMaybeEmptyString16(parcel); + const sp<IGraphicBufferProducer>& gbp = + interface_cast<IGraphicBufferProducer>(parcel.readStrongBinder()); + mGbp = gbp; + mRotation = rotation; + + ALOGV("%s: OutputConfiguration: bp = %p, name = %s", __FUNCTION__, + gbp.get(), String8(name).string()); +} + +status_t OutputConfiguration::writeToParcel(Parcel& parcel) const { + + parcel.writeInt32(mRotation); + parcel.writeString16(String16("unknown_name")); // name of surface + sp<IBinder> b(IInterface::asBinder(mGbp)); + parcel.writeStrongBinder(b); + + return OK; +} + +}; // namespace android + diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp index 1f5867a..24b2327 100644 --- a/camera/tests/ProCameraTests.cpp +++ b/camera/tests/ProCameraTests.cpp @@ -89,6 +89,12 @@ struct ServiceListener : public BnCameraServiceListener { mCondition.broadcast(); } + void onTorchStatusChanged(TorchStatus status, const String16& cameraId) { + dout << "On torch status changed: 0x" << std::hex + << (unsigned int) status << " cameraId " << cameraId.string() + << std::endl; + } + status_t waitForStatusChange(Status& newStatus) { Mutex::Autolock al(mMutex); @@ -469,7 +475,7 @@ protected: CMP_STR(NV16, YCbCr_422_SP); CMP_STR(NV21, YCrCb_420_SP); CMP_STR(YUY2, YCbCr_422_I); - CMP_STR(RAW, RAW_SENSOR); + CMP_STR(RAW, RAW16); CMP_STR(RGBA, RGBA_8888); std::cerr << "Unknown format string " << str << std::endl; diff --git a/cmds/screenrecord/FrameOutput.cpp b/cmds/screenrecord/FrameOutput.cpp index 03e0062..bef74f5 100644 --- a/cmds/screenrecord/FrameOutput.cpp +++ b/cmds/screenrecord/FrameOutput.cpp @@ -206,7 +206,7 @@ void FrameOutput::reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount) { } // Callback; executes on arbitrary thread. -void FrameOutput::onFrameAvailable() { +void FrameOutput::onFrameAvailable(const BufferItem& /* item */) { Mutex::Autolock _l(mMutex); mFrameAvailable = true; mEventCond.signal(); diff --git a/cmds/screenrecord/FrameOutput.h b/cmds/screenrecord/FrameOutput.h index c49ec3b..4c0c3be 100644 --- a/cmds/screenrecord/FrameOutput.h +++ b/cmds/screenrecord/FrameOutput.h @@ -62,7 +62,7 @@ private: } // (overrides GLConsumer::FrameAvailableListener method) - virtual void onFrameAvailable(); + virtual void onFrameAvailable(const BufferItem& item); // Reduces RGBA to RGB, in place. static void reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount); diff --git a/cmds/screenrecord/Overlay.cpp b/cmds/screenrecord/Overlay.cpp index 7fef53d..c659170 100644 --- a/cmds/screenrecord/Overlay.cpp +++ b/cmds/screenrecord/Overlay.cpp @@ -274,7 +274,7 @@ void Overlay::getTimeString_l(nsecs_t monotonicNsec, char* buf, size_t bufLen) { } // Callback; executes on arbitrary thread. -void Overlay::onFrameAvailable() { +void Overlay::onFrameAvailable(const BufferItem& /* item */) { ALOGV("Overlay::onFrameAvailable"); Mutex::Autolock _l(mMutex); mFrameAvailable = true; diff --git a/cmds/screenrecord/Overlay.h b/cmds/screenrecord/Overlay.h index b1b5c29..ee3444d 100644 --- a/cmds/screenrecord/Overlay.h +++ b/cmds/screenrecord/Overlay.h @@ -78,7 +78,7 @@ private: const Program& texRender, TextRenderer& textRenderer); // (overrides GLConsumer::FrameAvailableListener method) - virtual void onFrameAvailable(); + virtual void onFrameAvailable(const BufferItem& item); // (overrides Thread method) virtual bool threadLoop(); diff --git a/cmds/screenrecord/TextRenderer.cpp b/cmds/screenrecord/TextRenderer.cpp index 6a9176b..01f73e0 100644 --- a/cmds/screenrecord/TextRenderer.cpp +++ b/cmds/screenrecord/TextRenderer.cpp @@ -21,6 +21,8 @@ #include "TextRenderer.h" #include <assert.h> +#include <malloc.h> +#include <string.h> namespace android { #include "FontBitmap.h" diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index 02df1d2..36a7e73 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -23,7 +23,10 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <sys/types.h> #include <sys/wait.h> + #include <termios.h> #include <unistd.h> @@ -637,7 +640,13 @@ static status_t recordScreen(const char* fileName) { 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); + int fd = open(fileName, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "ERROR: couldn't open file\n"); + abort(); + } + muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_MPEG_4); + close(fd); if (gRotate) { muxer->setOrientationHint(90); // TODO: does this do anything? } diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index 78c89bb..0e3bc68 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -169,8 +169,6 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) -LOCAL_NDK_STL_VARIANT := stlport_static - LOCAL_SRC_FILES:= \ filters/argbtorgba.rs \ filters/nightvision.rs \ @@ -178,12 +176,18 @@ LOCAL_SRC_FILES:= \ mediafilter.cpp \ LOCAL_SHARED_LIBRARIES := \ - libstagefright liblog libutils libbinder libstagefright_foundation \ - libmedia libgui libcutils libui + libstagefright \ + liblog \ + libutils \ + libbinder \ + libstagefright_foundation \ + libmedia \ + libgui \ + libcutils \ + libui \ + libRScpp \ LOCAL_C_INCLUDES:= \ - $(TOP)/bionic \ - $(TOP)/external/stlport/stlport \ $(TOP)/frameworks/av/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/frameworks/rs/cpp \ diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp index 1b2f792..ac1a547 100644 --- a/cmds/stagefright/SimplePlayer.cpp +++ b/cmds/stagefright/SimplePlayer.cpp @@ -59,14 +59,14 @@ status_t PostAndAwaitResponse( return err; } status_t SimplePlayer::setDataSource(const char *path) { - sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); + sp<AMessage> msg = new AMessage(kWhatSetDataSource, this); msg->setString("path", path); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } status_t SimplePlayer::setSurface(const sp<IGraphicBufferProducer> &bufferProducer) { - sp<AMessage> msg = new AMessage(kWhatSetSurface, id()); + sp<AMessage> msg = new AMessage(kWhatSetSurface, this); sp<Surface> surface; if (bufferProducer != NULL) { @@ -81,25 +81,25 @@ status_t SimplePlayer::setSurface(const sp<IGraphicBufferProducer> &bufferProduc } status_t SimplePlayer::prepare() { - sp<AMessage> msg = new AMessage(kWhatPrepare, id()); + sp<AMessage> msg = new AMessage(kWhatPrepare, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } status_t SimplePlayer::start() { - sp<AMessage> msg = new AMessage(kWhatStart, id()); + sp<AMessage> msg = new AMessage(kWhatStart, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } status_t SimplePlayer::stop() { - sp<AMessage> msg = new AMessage(kWhatStop, id()); + sp<AMessage> msg = new AMessage(kWhatStop, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } status_t SimplePlayer::reset() { - sp<AMessage> msg = new AMessage(kWhatReset, id()); + sp<AMessage> msg = new AMessage(kWhatReset, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } @@ -116,7 +116,7 @@ void SimplePlayer::onMessageReceived(const sp<AMessage> &msg) { mState = UNPREPARED; } - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); sp<AMessage> response = new AMessage; @@ -139,7 +139,7 @@ void SimplePlayer::onMessageReceived(const sp<AMessage> &msg) { err = OK; } - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); sp<AMessage> response = new AMessage; @@ -161,7 +161,7 @@ void SimplePlayer::onMessageReceived(const sp<AMessage> &msg) { } } - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); sp<AMessage> response = new AMessage; @@ -194,7 +194,7 @@ void SimplePlayer::onMessageReceived(const sp<AMessage> &msg) { } } - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); sp<AMessage> response = new AMessage; @@ -217,7 +217,7 @@ void SimplePlayer::onMessageReceived(const sp<AMessage> &msg) { } } - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); sp<AMessage> response = new AMessage; @@ -240,7 +240,7 @@ void SimplePlayer::onMessageReceived(const sp<AMessage> &msg) { mState = UNINITIALIZED; } - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); sp<AMessage> response = new AMessage; @@ -332,7 +332,7 @@ status_t SimplePlayer::onPrepare() { size_t j = 0; sp<ABuffer> buffer; - while (format->findBuffer(StringPrintf("csd-%d", j).c_str(), &buffer)) { + while (format->findBuffer(AStringPrintf("csd-%d", j).c_str(), &buffer)) { state->mCSD.push_back(buffer); ++j; @@ -382,7 +382,7 @@ status_t SimplePlayer::onStart() { mStartTimeRealUs = -1ll; - sp<AMessage> msg = new AMessage(kWhatDoMoreStuff, id()); + sp<AMessage> msg = new AMessage(kWhatDoMoreStuff, this); msg->setInt32("generation", ++mDoMoreStuffGeneration); msg->post(); diff --git a/cmds/stagefright/SineSource.h b/cmds/stagefright/SineSource.h index 76ab669..be05661 100644 --- a/cmds/stagefright/SineSource.h +++ b/cmds/stagefright/SineSource.h @@ -3,10 +3,11 @@ #define SINE_SOURCE_H_ #include <media/stagefright/MediaSource.h> +#include <utils/Compat.h> namespace android { -struct MediaBufferGroup; +class MediaBufferGroup; struct SineSource : public MediaSource { SineSource(int32_t sampleRate, int32_t numChannels); @@ -24,7 +25,7 @@ protected: private: enum { kBufferSize = 8192 }; - static const double kFrequency = 500.0; + static const CONSTEXPR double kFrequency = 500.0; bool mStarted; int32_t mSampleRate; diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp index 96073f1..7b0de24 100644 --- a/cmds/stagefright/audioloop.cpp +++ b/cmds/stagefright/audioloop.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + #include <binder/ProcessState.h> #include <media/mediarecorder.h> #include <media/stagefright/foundation/ADebug.h> @@ -109,7 +113,12 @@ int main(int argc, char* argv[]) if (fileOut != NULL) { // target file specified, write encoded AMR output - sp<AMRWriter> writer = new AMRWriter(fileOut); + int fd = open(fileOut, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); + if (fd < 0) { + return 1; + } + sp<AMRWriter> writer = new AMRWriter(fd); + close(fd); writer->addSource(encoder); writer->start(); sleep(duration); diff --git a/cmds/stagefright/muxer.cpp b/cmds/stagefright/muxer.cpp index f4a33e8..461b56c 100644 --- a/cmds/stagefright/muxer.cpp +++ b/cmds/stagefright/muxer.cpp @@ -17,6 +17,9 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "muxer" #include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include <utils/Log.h> #include <binder/ProcessState.h> @@ -72,8 +75,15 @@ static int muxing( ALOGV("input file %s, output file %s", path, outputFileName); ALOGV("useAudio %d, useVideo %d", useAudio, useVideo); - sp<MediaMuxer> muxer = new MediaMuxer(outputFileName, + int fd = open(outputFileName, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); + + if (fd < 0) { + ALOGE("couldn't open file"); + return fd; + } + sp<MediaMuxer> muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_MPEG_4); + close(fd); size_t trackCount = extractor->countTracks(); // Map the extractor's track index to the muxer's track index. diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp index 9f547c7..2ad40bd 100644 --- a/cmds/stagefright/recordvideo.cpp +++ b/cmds/stagefright/recordvideo.cpp @@ -17,6 +17,10 @@ #include "SineSource.h" #include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + #include <binder/ProcessState.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/AudioPlayer.h> @@ -300,7 +304,13 @@ int main(int argc, char **argv) { client.interface(), enc_meta, true /* createEncoder */, source, 0, preferSoftwareCodec ? OMXCodec::kPreferSoftwareCodecs : 0); - sp<MPEG4Writer> writer = new MPEG4Writer(fileName); + int fd = open(fileName, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "couldn't open file"); + return 1; + } + sp<MPEG4Writer> writer = new MPEG4Writer(fd); + close(fd); writer->addSource(encoder); int64_t start = systemTime(); CHECK_EQ((status_t)OK, writer->start()); diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp index 0f729a3..172dc36 100644 --- a/cmds/stagefright/sf2.cpp +++ b/cmds/stagefright/sf2.cpp @@ -72,7 +72,7 @@ struct Controller : public AHandler { } void startAsync() { - (new AMessage(kWhatStart, id()))->post(); + (new AMessage(kWhatStart, this))->post(); } protected: @@ -100,7 +100,7 @@ protected: if (ctrlc) { printf("\n"); printStatistics(); - (new AMessage(kWhatStop, id()))->post(); + (new AMessage(kWhatStop, this))->post(); ctrlc = false; } switch (msg->what()) { @@ -149,7 +149,7 @@ protected: mDecodeLooper->registerHandler(mCodec); mCodec->setNotificationMessage( - new AMessage(kWhatCodecNotify, id())); + new AMessage(kWhatCodecNotify, this)); sp<AMessage> format = makeFormat(mSource->getFormat()); @@ -168,7 +168,7 @@ protected: mFinalResult = OK; mSeekState = SEEK_NONE; - // (new AMessage(kWhatSeek, id()))->post(5000000ll); + // (new AMessage(kWhatSeek, this))->post(5000000ll); break; } @@ -225,12 +225,12 @@ protected: printf((what == CodecBase::kWhatEOS) ? "$\n" : "E\n"); printStatistics(); - (new AMessage(kWhatStop, id()))->post(); + (new AMessage(kWhatStop, this))->post(); } else if (what == CodecBase::kWhatFlushCompleted) { mSeekState = SEEK_FLUSH_COMPLETED; mCodec->signalResume(); - (new AMessage(kWhatSeek, id()))->post(5000000ll); + (new AMessage(kWhatSeek, this))->post(5000000ll); } else if (what == CodecBase::kWhatOutputFormatChanged) { } else if (what == CodecBase::kWhatShutdownCompleted) { mDecodeLooper->unregisterHandler(mCodec->id()); diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 81edcb4..318b56d 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -19,6 +19,8 @@ #include <stdlib.h> #include <string.h> #include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> //#define LOG_NDEBUG 0 #define LOG_TAG "stagefright" @@ -506,8 +508,13 @@ static void writeSourcesToMP4( sp<MPEG4Writer> writer = new MPEG4Writer(gWriteMP4Filename.string()); #else + int fd = open(gWriteMP4Filename.string(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "couldn't open file"); + return; + } sp<MPEG2TSWriter> writer = - new MPEG2TSWriter(gWriteMP4Filename.string()); + new MPEG2TSWriter(fd); #endif // at most one minute. diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp index 36cb612..3f62ed7 100644 --- a/drm/common/IDrmManagerService.cpp +++ b/drm/common/IDrmManagerService.cpp @@ -148,8 +148,7 @@ status_t BpDrmManagerService::setDrmServiceListener( data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); data.writeInt32(uniqueId); - data.writeStrongBinder( - drmServiceListener != NULL ? drmServiceListener->asBinder() : NULL); + data.writeStrongBinder(IInterface::asBinder(drmServiceListener)); remote()->transact(SET_DRM_SERVICE_LISTENER, data, &reply); return reply.readInt32(); } diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp index 2d2c90e..9457bb6 100644 --- a/drm/libdrmframework/DrmManagerClientImpl.cpp +++ b/drm/libdrmframework/DrmManagerClientImpl.cpp @@ -346,7 +346,7 @@ status_t DrmManagerClientImpl::notify(const DrmInfoEvent& event) { DrmManagerClientImpl::DeathNotifier::~DeathNotifier() { Mutex::Autolock lock(sMutex); if (NULL != sDrmManagerService.get()) { - sDrmManagerService->asBinder()->unlinkToDeath(this); + IInterface::asBinder(sDrmManagerService)->unlinkToDeath(this); } } diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk index 48b0afe..933464f 100644 --- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk @@ -58,8 +58,7 @@ LOCAL_C_INCLUDES += \ $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/common \ $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/converter \ $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/decoder \ - $(LOCAL_PATH)/include \ - external/openssl/include + $(LOCAL_PATH)/include LOCAL_MODULE_RELATIVE_PATH := drm diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk index 6c5d3cf..3b4c8b4 100644 --- a/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk @@ -20,9 +20,6 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ FwdLockGlue.c -LOCAL_C_INCLUDES := \ - external/openssl/include - LOCAL_SHARED_LIBRARIES := libcrypto LOCAL_MODULE := libfwdlock-common diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk index 8f08c88..2f51f0c 100644 --- a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk @@ -21,8 +21,7 @@ LOCAL_SRC_FILES := \ FwdLockConv.c LOCAL_C_INCLUDES := \ - frameworks/av/drm/libdrmframework/plugins/forward-lock/internal-format/common \ - external/openssl/include + frameworks/av/drm/libdrmframework/plugins/forward-lock/internal-format/common LOCAL_SHARED_LIBRARIES := libcrypto diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk index 7b493c3..3399ae5 100644 --- a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk @@ -21,8 +21,7 @@ LOCAL_SRC_FILES := \ FwdLockFile.c LOCAL_C_INCLUDES := \ - frameworks/av/drm/libdrmframework/plugins/forward-lock/internal-format/common \ - external/openssl/include + frameworks/av/drm/libdrmframework/plugins/forward-lock/internal-format/common LOCAL_SHARED_LIBRARIES := libcrypto diff --git a/drm/mediadrm/plugins/clearkey/Android.mk b/drm/mediadrm/plugins/clearkey/Android.mk index 22a85b4..2efdcf5 100644 --- a/drm/mediadrm/plugins/clearkey/Android.mk +++ b/drm/mediadrm/plugins/clearkey/Android.mk @@ -31,9 +31,7 @@ LOCAL_SRC_FILES := \ Utils.cpp \ LOCAL_C_INCLUDES := \ - bionic \ external/jsmn \ - external/openssl/include \ frameworks/av/drm/mediadrm/plugins/clearkey \ frameworks/av/include \ frameworks/native/include \ diff --git a/drm/mediadrm/plugins/clearkey/tests/Android.mk b/drm/mediadrm/plugins/clearkey/tests/Android.mk index ac5bb21..392f218 100644 --- a/drm/mediadrm/plugins/clearkey/tests/Android.mk +++ b/drm/mediadrm/plugins/clearkey/tests/Android.mk @@ -28,25 +28,16 @@ LOCAL_SRC_FILES := \ JsonWebKeyUnittest.cpp \ LOCAL_C_INCLUDES := \ - bionic \ - external/gtest/include \ external/jsmn \ - external/openssl/include \ - external/stlport/stlport \ frameworks/av/drm/mediadrm/plugins/clearkey \ frameworks/av/include \ frameworks/native/include \ -LOCAL_STATIC_LIBRARIES := \ - libgtest \ - libgtest_main \ - LOCAL_SHARED_LIBRARIES := \ libcrypto \ libdrmclearkeyplugin \ liblog \ libstagefright_foundation \ - libstlport \ libutils \ include $(BUILD_NATIVE_TEST) diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h index c6074fc..ba33ffe 100644 --- a/include/camera/CameraParameters.h +++ b/include/camera/CameraParameters.h @@ -108,6 +108,9 @@ public: */ void getSupportedPreviewFormats(Vector<int>& formats) const; + // Returns true if no keys are present + bool isEmpty() const; + // Parameter keys to communicate between camera application and driver. // The access (read/write, read only, or write only) is viewed from the // perspective of applications, not driver. diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h index f7f06bb..194a646 100644 --- a/include/camera/ICameraService.h +++ b/include/camera/ICameraService.h @@ -53,6 +53,7 @@ public: GET_LEGACY_PARAMETERS, SUPPORTS_CAMERA_API, CONNECT_LEGACY, + SET_TORCH_MODE, }; enum { @@ -142,6 +143,21 @@ public: int clientUid, /*out*/ sp<ICamera>& device) = 0; + + /** + * Turn on or off a camera's torch mode. Torch mode will be turned off by + * camera service if the lastest client binder that turns it on dies. + * + * return values: + * 0: on a successful operation. + * -ENOSYS: the camera device doesn't support this operation. It it returned + * if and only if android.flash.into.available is false. + * -EBUSY: the camera device is opened. + * -EINVAL: camera_id is invalid or clientBinder is NULL when enabling a + * torch mode. + */ + virtual status_t setTorchMode(const String16& cameraId, bool enabled, + const sp<IBinder>& clientBinder) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/camera/ICameraServiceListener.h b/include/camera/ICameraServiceListener.h index 0a0e43a..709ff31 100644 --- a/include/camera/ICameraServiceListener.h +++ b/include/camera/ICameraServiceListener.h @@ -66,9 +66,35 @@ public: STATUS_UNKNOWN = 0xFFFFFFFF, }; + /** + * The torch mode status of a camera. + * + * Initial status will be transmitted with onTorchStatusChanged immediately + * after this listener is added to the service listener list. + * + * The enums should be set to values matching + * include/hardware/camera_common.h + */ + enum TorchStatus { + // The camera's torch mode has become not available to use via + // setTorchMode(). + TORCH_STATUS_NOT_AVAILABLE = TORCH_MODE_STATUS_NOT_AVAILABLE, + // The camera's torch mode is off and available to be turned on via + // setTorchMode(). + TORCH_STATUS_AVAILABLE_OFF = TORCH_MODE_STATUS_AVAILABLE_OFF, + // The camera's torch mode is on and available to be turned off via + // setTorchMode(). + TORCH_STATUS_AVAILABLE_ON = TORCH_MODE_STATUS_AVAILABLE_ON, + + // Use to initialize variables only + TORCH_STATUS_UNKNOWN = 0xFFFFFFFF, + }; + DECLARE_META_INTERFACE(CameraServiceListener); virtual void onStatusChanged(Status status, int32_t cameraId) = 0; + + virtual void onTorchStatusChanged(TorchStatus status, const String16& cameraId) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h index 83a3028..e9b687a 100644 --- a/include/camera/ProCamera.h +++ b/include/camera/ProCamera.h @@ -265,7 +265,7 @@ private: } protected: - virtual void onFrameAvailable() { + virtual void onFrameAvailable(const BufferItem& /* item */) { sp<ProCamera> c = mCamera.promote(); if (c.get() != NULL) { c->onFrameAvailable(mStreamId); diff --git a/include/camera/camera2/ICameraDeviceUser.h b/include/camera/camera2/ICameraDeviceUser.h index 35488bb..e9f1f5a 100644 --- a/include/camera/camera2/ICameraDeviceUser.h +++ b/include/camera/camera2/ICameraDeviceUser.h @@ -27,9 +27,9 @@ namespace android { class ICameraDeviceUserClient; class IGraphicBufferProducer; -class Surface; class CaptureRequest; class CameraMetadata; +class OutputConfiguration; enum { NO_IN_FLIGHT_REPEATING_FRAMES = -1, @@ -100,9 +100,8 @@ public: virtual status_t endConfigure() = 0; virtual status_t deleteStream(int streamId) = 0; - virtual status_t createStream( - int width, int height, int format, - const sp<IGraphicBufferProducer>& bufferProducer) = 0; + + virtual status_t createStream(const OutputConfiguration& outputConfiguration) = 0; // Create a request object from a template. virtual status_t createDefaultRequest(int templateId, diff --git a/include/camera/camera2/OutputConfiguration.h b/include/camera/camera2/OutputConfiguration.h new file mode 100644 index 0000000..e6b679f --- /dev/null +++ b/include/camera/camera2/OutputConfiguration.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_CAMERA2_OUTPUTCONFIGURATION_H +#define ANDROID_HARDWARE_CAMERA2_OUTPUTCONFIGURATION_H + +#include <utils/RefBase.h> +#include <gui/IGraphicBufferProducer.h> + +namespace android { + +class Surface; + +class OutputConfiguration : public virtual RefBase { +public: + + static const int INVALID_ROTATION; + sp<IGraphicBufferProducer> getGraphicBufferProducer() const; + int getRotation() const; + + /** + * Keep impl up-to-date with OutputConfiguration.java in frameworks/base + */ + status_t writeToParcel(Parcel& parcel) const; + // getGraphicBufferProducer will be NULL if error occurred + // getRotation will be INVALID_ROTATION if error occurred + OutputConfiguration(const Parcel& parcel); + +private: + sp<IGraphicBufferProducer> mGbp; + int mRotation; + + // helper function + static String16 readMaybeEmptyString16(const Parcel& parcel); +}; +}; // namespace android + +#endif diff --git a/include/media/AudioPolicy.h b/include/media/AudioPolicy.h new file mode 100644 index 0000000..a755e1e --- /dev/null +++ b/include/media/AudioPolicy.h @@ -0,0 +1,82 @@ +/* + * 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_POLICY_H +#define ANDROID_AUDIO_POLICY_H + +#include <system/audio.h> +#include <system/audio_policy.h> +#include <binder/Parcel.h> +#include <utils/String8.h> +#include <utils/Vector.h> + +namespace android { + +// Keep in sync with AudioMix.java, AudioMixingRule.java, AudioPolicyConfig.java +#define RULE_EXCLUSION_MASK 0x8000 +#define RULE_MATCH_ATTRIBUTE_USAGE 0x1 +#define RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET (0x1 << 1) +#define RULE_EXCLUDE_ATTRIBUTE_USAGE (RULE_EXCLUSION_MASK|RULE_MATCH_ATTRIBUTE_USAGE) +#define RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET \ + (RULE_EXCLUSION_MASK|RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET) + +#define MIX_TYPE_INVALID -1 +#define MIX_TYPE_PLAYERS 0 +#define MIX_TYPE_RECORDERS 1 + +#define ROUTE_FLAG_RENDER 0x1 +#define ROUTE_FLAG_LOOP_BACK (0x1 << 1) + +#define MAX_MIXES_PER_POLICY 10 +#define MAX_CRITERIA_PER_MIX 20 + +class AttributeMatchCriterion { +public: + AttributeMatchCriterion() {} + AttributeMatchCriterion(audio_usage_t usage, audio_source_t source, uint32_t rule); + + status_t readFromParcel(Parcel *parcel); + status_t writeToParcel(Parcel *parcel) const; + + union { + audio_usage_t mUsage; + audio_source_t mSource; + } mAttr; + uint32_t mRule; +}; + +class AudioMix { +public: + AudioMix() {} + AudioMix(Vector<AttributeMatchCriterion> criteria, uint32_t mixType, audio_config_t format, + uint32_t routeFlags, String8 registrationId) : + mCriteria(criteria), mMixType(mixType), mFormat(format), + mRouteFlags(routeFlags), mRegistrationId(registrationId) {} + + status_t readFromParcel(Parcel *parcel); + status_t writeToParcel(Parcel *parcel) const; + + Vector<AttributeMatchCriterion> mCriteria; + uint32_t mMixType; + audio_config_t mFormat; + uint32_t mRouteFlags; + String8 mRegistrationId; +}; + +}; // namespace android + +#endif // ANDROID_AUDIO_POLICY_H diff --git a/include/media/AudioPolicyHelper.h b/include/media/AudioPolicyHelper.h index f4afd45..79231be 100644 --- a/include/media/AudioPolicyHelper.h +++ b/include/media/AudioPolicyHelper.h @@ -18,7 +18,7 @@ #include <system/audio.h> -audio_stream_type_t audio_attributes_to_stream_type(const audio_attributes_t *attr) +static audio_stream_type_t audio_attributes_to_stream_type(const audio_attributes_t *attr) { // flags to stream type mapping if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) { @@ -61,4 +61,55 @@ audio_stream_type_t audio_attributes_to_stream_type(const audio_attributes_t *at } } +static void stream_type_to_audio_attributes(audio_stream_type_t streamType, + audio_attributes_t *attr) { + memset(attr, 0, sizeof(audio_attributes_t)); + + switch (streamType) { + case AUDIO_STREAM_DEFAULT: + case AUDIO_STREAM_MUSIC: + attr->content_type = AUDIO_CONTENT_TYPE_MUSIC; + attr->usage = AUDIO_USAGE_MEDIA; + break; + case AUDIO_STREAM_VOICE_CALL: + attr->content_type = AUDIO_CONTENT_TYPE_SPEECH; + attr->usage = AUDIO_USAGE_VOICE_COMMUNICATION; + break; + case AUDIO_STREAM_ENFORCED_AUDIBLE: + attr->flags |= AUDIO_FLAG_AUDIBILITY_ENFORCED; + // intended fall through, attributes in common with STREAM_SYSTEM + case AUDIO_STREAM_SYSTEM: + attr->content_type = AUDIO_CONTENT_TYPE_SONIFICATION; + attr->usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION; + break; + case AUDIO_STREAM_RING: + attr->content_type = AUDIO_CONTENT_TYPE_SONIFICATION; + attr->usage = AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE; + break; + case AUDIO_STREAM_ALARM: + attr->content_type = AUDIO_CONTENT_TYPE_SONIFICATION; + attr->usage = AUDIO_USAGE_ALARM; + break; + case AUDIO_STREAM_NOTIFICATION: + attr->content_type = AUDIO_CONTENT_TYPE_SONIFICATION; + attr->usage = AUDIO_USAGE_NOTIFICATION; + break; + case AUDIO_STREAM_BLUETOOTH_SCO: + attr->content_type = AUDIO_CONTENT_TYPE_SPEECH; + attr->usage = AUDIO_USAGE_VOICE_COMMUNICATION; + attr->flags |= AUDIO_FLAG_SCO; + break; + case AUDIO_STREAM_DTMF: + attr->content_type = AUDIO_CONTENT_TYPE_SONIFICATION; + attr->usage = AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; + break; + case AUDIO_STREAM_TTS: + attr->content_type = AUDIO_CONTENT_TYPE_SPEECH; + attr->usage = AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; + break; + default: + ALOGE("invalid stream type %d when converting to attributes", streamType); + } +} + #endif //AUDIO_POLICY_HELPER_H_ diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index 4edc1bf..8e0b8f8 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -88,8 +88,8 @@ public: * user: Pointer to context for use by the callback receiver. * info: Pointer to optional parameter according to event type: * - EVENT_MORE_DATA: pointer to AudioRecord::Buffer struct. The callback must not read - * more bytes than indicated by 'size' field and update 'size' if fewer bytes are - * consumed. + * more bytes than indicated by 'size' field and update 'size' if + * fewer bytes are consumed. * - EVENT_OVERRUN: unused. * - EVENT_MARKER: pointer to const uint32_t containing the marker position in frames. * - EVENT_NEW_POS: pointer to const uint32_t containing the new position in frames. @@ -106,6 +106,7 @@ public: * - BAD_VALUE: unsupported configuration * frameCount is guaranteed to be non-zero if status is NO_ERROR, * and is undefined otherwise. + * FIXME This API assumes a route, and so should be deprecated. */ static status_t getMinFrameCount(size_t* frameCount, @@ -152,6 +153,7 @@ public: * transferType: How data is transferred from AudioRecord. * flags: See comments on audio_input_flags_t in <system/audio.h> * threadCanCallJava: Not present in parameter list, and so is fixed at false. + * pAttributes: if not NULL, supersedes inputSource for use case selection */ AudioRecord(audio_source_t inputSource, @@ -164,7 +166,8 @@ public: uint32_t notificationFrames = 0, int sessionId = AUDIO_SESSION_ALLOCATE, transfer_type transferType = TRANSFER_DEFAULT, - audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE); + audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE, + const audio_attributes_t* pAttributes = NULL); /* Terminates the AudioRecord and unregisters it from AudioFlinger. * Also destroys all resources associated with the AudioRecord. @@ -198,7 +201,8 @@ public: bool threadCanCallJava = false, int sessionId = AUDIO_SESSION_ALLOCATE, transfer_type transferType = TRANSFER_DEFAULT, - audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE); + audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE, + const audio_attributes_t* pAttributes = NULL); /* Result of constructing the AudioRecord. This must be checked for successful initialization * before using any AudioRecord API (except for set()), because using @@ -219,7 +223,7 @@ public: uint32_t channelCount() const { return mChannelCount; } size_t frameCount() const { return mFrameCount; } size_t frameSize() const { return mFrameSize; } - audio_source_t inputSource() const { return mInputSource; } + audio_source_t inputSource() const { return mAttributes.source; } /* After it's created the track is not active. Call start() to * make it active. If set, the callback will start being called. @@ -413,6 +417,7 @@ private: void pause(); // suspend thread from execution at next loop boundary void resume(); // allow thread to execute, if not requested to exit + void wake(); // wake to handle changed notification conditions. private: void pauseInternal(nsecs_t ns = 0LL); @@ -427,7 +432,9 @@ 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 + bool mIgnoreNextPausedInt; // skip any internal pause and go immediately + // to processAudioBuffer() as state may have changed + // since pause time calculated. }; // body of AudioRecordThread::threadLoop() @@ -489,7 +496,6 @@ private: audio_format_t mFormat; uint32_t mChannelCount; size_t mFrameSize; // app-level frame size == AudioFlinger frame size - audio_source_t mInputSource; uint32_t mLatency; // in ms audio_channel_mask_t mChannelMask; audio_input_flags_t mFlags; @@ -529,6 +535,7 @@ private: sp<DeathNotifier> mDeathNotifier; uint32_t mSequence; // incremented for each new IAudioRecord attempt + audio_attributes_t mAttributes; }; }; // namespace android diff --git a/include/media/AudioResamplerPublic.h b/include/media/AudioResamplerPublic.h index 97847a0..b705efa 100644 --- a/include/media/AudioResamplerPublic.h +++ b/include/media/AudioResamplerPublic.h @@ -26,4 +26,17 @@ // TODO: replace with an API #define AUDIO_RESAMPLER_DOWN_RATIO_MAX 256 +// Returns the source frames needed to resample to destination frames. This is not a precise +// value and depends on the resampler (and possibly how it handles rounding internally). +// Nevertheless, this should be an upper bound on the requirements of the resampler. +// If srcSampleRate and dstSampleRate are equal, then it returns destination frames, which +// may not be true if the resampler is asynchronous. +static inline size_t sourceFramesNeeded( + uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate) { + // +1 for rounding - always do this even if matched ratio (resampler may use phases not ratio) + // +1 for additional sample needed for interpolation + return srcSampleRate == dstSampleRate ? dstFramesRequired : + size_t((uint64_t)dstFramesRequired * srcSampleRate / dstSampleRate + 1 + 1); +} + #endif // ANDROID_AUDIO_RESAMPLER_PUBLIC_H diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index f8c0198..ad5d6ed 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -18,6 +18,7 @@ #define ANDROID_AUDIOSYSTEM_H_ #include <hardware/audio_effect.h> +#include <media/AudioPolicy.h> #include <media/IAudioFlingerClient.h> #include <media/IAudioPolicyServiceClient.h> #include <system/audio.h> @@ -90,19 +91,20 @@ public: static void setErrorCallback(audio_error_callback cb); // helper function to obtain AudioFlinger service handle - static const sp<IAudioFlinger>& get_audio_flinger(); + static const sp<IAudioFlinger> get_audio_flinger(); 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 + // FIXME This API assumes a route, and so should be deprecated. static status_t getOutputSamplingRate(uint32_t* samplingRate, audio_stream_type_t stream); - static status_t getOutputSamplingRateForAttr(uint32_t* samplingRate, - const audio_attributes_t *attr); + // FIXME This API assumes a route, and so should be deprecated. static status_t getOutputFrameCount(size_t* frameCount, audio_stream_type_t stream); + // FIXME This API assumes a route, and so should be deprecated. static status_t getOutputLatency(uint32_t* latency, audio_stream_type_t stream); static status_t getSamplingRate(audio_io_handle_t output, @@ -111,21 +113,20 @@ public: // audio_stream->get_buffer_size()/audio_stream_out_frame_size() static status_t getFrameCount(audio_io_handle_t output, size_t* frameCount); - // returns the audio output stream latency in ms. Corresponds to + // returns the audio output latency in ms. Corresponds to // audio_stream_out->get_latency() static status_t getLatency(audio_io_handle_t output, uint32_t* latency); - static bool routedToA2dpOutput(audio_stream_type_t streamType); - // return status NO_ERROR implies *buffSize > 0 + // FIXME This API assumes a route, and so should deprecated. 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 specified output I/O handle has exited standby. + // audio dsp to DAC since the specified output 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 @@ -204,7 +205,7 @@ public: // IAudioPolicyService interface (see AudioPolicyInterface for method descriptions) // static status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address); + const char *device_address, const char *device_name); static audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address); static status_t setPhoneState(audio_mode_t state); @@ -219,7 +220,10 @@ public: 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 audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr, + static status_t getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + 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, @@ -227,20 +231,23 @@ public: const audio_offload_info_t *offloadInfo = NULL); static status_t startOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session); + audio_session_t session); static status_t stopOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session); - static void releaseOutput(audio_io_handle_t output); + audio_session_t session); + static void releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); // 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, + static status_t getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, - int sessionId, - audio_input_flags_t); + audio_input_flags_t flags); static status_t startInput(audio_io_handle_t input, audio_session_t session); @@ -274,7 +281,7 @@ public: // and output configuration cache (gOutputs) static void clearAudioConfigCache(); - static const sp<IAudioPolicyService>& get_audio_policy_service(); + static const sp<IAudioPolicyService> get_audio_policy_service(); // helpers for android.media.AudioManager.getProperty(), see description there for meaning static uint32_t getPrimaryOutputSamplingRate(); @@ -322,6 +329,8 @@ public: static audio_mode_t getPhoneState(); + static status_t registerPolicyMixes(Vector<AudioMix> mixes, bool registration); + // ---------------------------------------------------------------------------- class AudioPortCallback : public RefBase @@ -377,7 +386,11 @@ private: friend class AudioFlingerClient; friend class AudioPolicyServiceClient; - static Mutex gLock; + static Mutex gLock; // protects gAudioFlinger and gAudioErrorCallback, + static Mutex gLockCache; // protects gOutputs, gPrevInSamplingRate, gPrevInFormat, + // gPrevInChannelMask and gInBuffSize + static Mutex gLockAPS; // protects gAudioPolicyService and gAudioPolicyServiceClient + static Mutex gLockAPC; // protects gAudioPortCallback static sp<IAudioFlinger> gAudioFlinger; static audio_error_callback gAudioErrorCallback; diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index b5256f0..3de0774 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -63,7 +63,7 @@ public: // See AudioTimestamp for the information included with event. }; - /* Client should declare Buffer on the stack and pass address to obtainBuffer() + /* Client should declare a Buffer and pass the address to obtainBuffer() * and releaseBuffer(). See also callback_t for EVENT_MORE_DATA. */ @@ -72,16 +72,20 @@ public: public: // FIXME use m prefix size_t frameCount; // number of sample frames corresponding to size; - // on input it is the number of frames desired, - // on output is the number of frames actually filled - // (currently ignored, but will make the primary field in future) + // on input to obtainBuffer() it is the number of frames desired, + // on output from obtainBuffer() it is the number of available + // [empty slots for] frames to be filled + // on input to releaseBuffer() it is currently ignored 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 - // since we don't define the frame format + // on input to obtainBuffer() it is ignored + // on output from obtainBuffer() it is the number of available + // [empty slots for] bytes to be filled, + // which is frameCount * frameSize + // on input to releaseBuffer() it is the number of bytes to + // release + // FIXME This is redundant with respect to frameCount. Consider + // removing size and making frameCount the primary field. union { void* raw; @@ -154,9 +158,9 @@ public: * streamType: Select the type of audio stream this track is attached to * (e.g. AUDIO_STREAM_MUSIC). * sampleRate: Data source sampling rate in Hz. - * format: Audio format. For mixed tracks, any PCM format supported by server is OK - * or AUDIO_FORMAT_PCM_8_BIT which is handled on client side. For direct - * and offloaded tracks, the possible format(s) depends on the output sink. + * format: Audio format. For mixed tracks, any PCM format supported by server is OK. + * For direct and offloaded tracks, the possible format(s) depends on the + * output sink. * 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 @@ -193,7 +197,6 @@ public: /* Creates an audio track and registers it with AudioFlinger. * With this constructor, the track is configured for static buffer mode. - * The format must not be 8-bit linear PCM. * Data to be rendered is passed in a shared memory buffer * identified by the argument sharedBuffer, which must be non-0. * The memory should be initialized to the desired data before calling start(). @@ -239,6 +242,9 @@ public: * Parameters not listed in the AudioTrack constructors above: * * threadCanCallJava: Whether callbacks are made from an attached thread and thus can call JNI. + * + * Internal state post condition: + * (mStreamType == AUDIO_STREAM_DEFAULT) implies this AudioTrack has valid attributes */ status_t set(audio_stream_type_t streamType, uint32_t sampleRate, @@ -273,7 +279,7 @@ public: /* getters, see constructors and set() */ - audio_stream_type_t streamType() const { return mStreamType; } + audio_stream_type_t streamType() const; audio_format_t format() const { return mFormat; } /* Return frame size in bytes, which for linear PCM is @@ -484,10 +490,18 @@ public: */ status_t attachAuxEffect(int effectId); - /* Obtains a buffer of up to "audioBuffer->frameCount" empty slots for frames. + /* Public API for TRANSFER_OBTAIN mode. + * Obtains a buffer of up to "audioBuffer->frameCount" empty slots for frames. * After filling these slots with data, the caller should release them with releaseBuffer(). * If the track buffer is not full, obtainBuffer() returns as many contiguous * [empty slots for] frames as are available immediately. + * + * If nonContig is non-NULL, it is an output parameter that will be set to the number of + * additional non-contiguous frames that are predicted to be available immediately, + * if the client were to release the first frames and then call obtainBuffer() again. + * This value is only a prediction, and needs to be confirmed. + * It will be set to zero for an error return. + * * If the track buffer is full and track is stopped, obtainBuffer() returns WOULD_BLOCK * regardless of the value of waitCount. * If the track buffer is full and track is not stopped, obtainBuffer() blocks with a @@ -496,7 +510,6 @@ public: * is exhausted, at which point obtainBuffer() will either block * or return WOULD_BLOCK depending on the value of the "waitCount" * parameter. - * Each sample is 16-bit signed PCM. * * obtainBuffer() and releaseBuffer() are deprecated for direct use by applications, * which should use write() or callback EVENT_MORE_DATA instead. @@ -508,24 +521,29 @@ public: * * Buffer fields * On entry: - * frameCount number of frames requested + * frameCount number of [empty slots for] frames requested + * size ignored + * raw ignored * After error return: * frameCount 0 * size 0 * raw undefined * After successful return: - * frameCount actual number of frames available, <= number requested + * frameCount actual number of [empty slots for] frames available, <= number requested * size actual number of bytes available * raw pointer to the buffer */ - /* FIXME Deprecated public API for TRANSFER_OBTAIN mode */ - status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount) + status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount, + size_t *nonContig = NULL) __attribute__((__deprecated__)); private: /* If nonContig is non-NULL, it is an output parameter that will be set to the number of - * additional non-contiguous frames that are available immediately. + * additional non-contiguous frames that are predicted to be available immediately, + * if the client were to release the first frames and then call obtainBuffer() again. + * This value is only a prediction, and needs to be confirmed. + * It will be set to zero for an error return. * FIXME We could pass an array of Buffers instead of only one Buffer to obtainBuffer(), * in case the requested amount of frames is in two or more non-contiguous regions. * FIXME requested and elapsed are both relative times. Consider changing to absolute time. @@ -534,9 +552,17 @@ private: struct timespec *elapsed = NULL, size_t *nonContig = NULL); public: - /* Release a filled buffer of "audioBuffer->frameCount" frames for AudioFlinger to process. */ + /* Public API for TRANSFER_OBTAIN mode. + * Release a filled buffer of frames for AudioFlinger to process. + * + * Buffer fields: + * frameCount currently ignored but recommend to set to actual number of frames filled + * size actual number of bytes filled, must be multiple of frameSize + * raw ignored + * + */ // FIXME make private when obtainBuffer() for TRANSFER_OBTAIN is removed - void releaseBuffer(Buffer* audioBuffer); + void releaseBuffer(const Buffer* audioBuffer); /* As a convenience we provide a write() interface to the audio buffer. * Input parameter 'size' is in byte units. @@ -598,9 +624,6 @@ protected: AudioTrack& operator = (const AudioTrack& other); void setAttributesFromStreamType(audio_stream_type_t streamType); - void setStreamTypeFromAttributes(audio_attributes_t& aa); - /* paa is guaranteed non-NULL */ - bool isValidAttributes(const audio_attributes_t *paa); /* a small internal class to handle the callback */ class AudioTrackThread : public Thread @@ -614,6 +637,7 @@ protected: void pause(); // suspend thread from execution at next loop boundary void resume(); // allow thread to execute, if not requested to exit + void wake(); // wake to handle changed notification conditions. private: void pauseInternal(nsecs_t ns = 0LL); @@ -628,7 +652,9 @@ protected: 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 + bool mIgnoreNextPausedInt; // skip any internal pause and go immediately + // to processAudioBuffer() as state may have changed + // since pause time calculated. }; // body of AudioTrackThread::threadLoop() @@ -680,7 +706,7 @@ protected: float mVolume[2]; float mSendLevel; - mutable uint32_t mSampleRate; // mutable because getSampleRate() can update it. + mutable uint32_t mSampleRate; // mutable because getSampleRate() can update it size_t mFrameCount; // corresponds to current IAudioTrack, value is // reported back by AudioFlinger to the client size_t mReqFrameCount; // frame count to request the first or next time @@ -688,7 +714,8 @@ protected: // constant after constructor or set() audio_format_t mFormat; // as requested by client, not forced to 16-bit - audio_stream_type_t mStreamType; + audio_stream_type_t mStreamType; // mStreamType == AUDIO_STREAM_DEFAULT implies + // this AudioTrack has valid attributes uint32_t mChannelCount; audio_channel_mask_t mChannelMask; sp<IMemory> mSharedBuffer; @@ -697,10 +724,7 @@ protected: const audio_offload_info_t* mOffloadInfo; audio_attributes_t mAttributes; - // 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. - size_t mFrameSize; // app-level frame size - size_t mFrameSizeAF; // AudioFlinger frame size + size_t mFrameSize; // frame size in bytes status_t mStatus; @@ -731,13 +755,20 @@ protected: bool mRefreshRemaining; // processAudioBuffer() should refresh // mRemainingFrames and mRetryOnPartialBuffer + // used for static track cbf and restoration + int32_t mLoopCount; // last setLoop loopCount; zero means disabled + uint32_t mLoopStart; // last setLoop loopStart + uint32_t mLoopEnd; // last setLoop loopEnd + int32_t mLoopCountNotified; // the last loopCount notified by callback. + // mLoopCountNotified counts down, matching + // the remaining loop count for static track + // playback. + // 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 - 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 diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h index 31a14f0..f927a80 100644 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -94,6 +94,8 @@ public: sp<IMemory>& buffers, // return value 0 means it follows cblk status_t *status) = 0; + // FIXME Surprisingly, sampleRate/format/frameCount/latency don't work for input handles + /* query the audio hardware state. This state never changes, * and therefore can be cached. */ @@ -142,6 +144,7 @@ public: virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0; // retrieve the audio recording buffer size + // FIXME This API assumes a route, and so should be deprecated. virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask) const = 0; diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h index 16fe9cf..fecc6f1 100644 --- a/include/media/IAudioPolicyService.h +++ b/include/media/IAudioPolicyService.h @@ -25,6 +25,7 @@ #include <utils/Errors.h> #include <binder/IInterface.h> #include <media/AudioSystem.h> +#include <media/AudioPolicy.h> #include <media/IAudioPolicyServiceClient.h> #include <system/audio_policy.h> @@ -43,7 +44,8 @@ public: // virtual status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) = 0; + const char *device_address, + const char *device_name) = 0; virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address) = 0; virtual status_t setPhoneState(audio_mode_t state) = 0; @@ -56,25 +58,31 @@ public: audio_channel_mask_t channelMask = 0, audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, const audio_offload_info_t *offloadInfo = NULL) = 0; - virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr, - uint32_t samplingRate = 0, - audio_format_t format = AUDIO_FORMAT_DEFAULT, - audio_channel_mask_t channelMask = 0, - audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, - const audio_offload_info_t *offloadInfo = NULL) = 0; + virtual status_t getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + audio_stream_type_t *stream, + uint32_t samplingRate = 0, + audio_format_t format = AUDIO_FORMAT_DEFAULT, + audio_channel_mask_t channelMask = 0, + audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, + const audio_offload_info_t *offloadInfo = NULL) = 0; virtual status_t startOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session = 0) = 0; + audio_session_t session) = 0; virtual status_t stopOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session = 0) = 0; - virtual void releaseOutput(audio_io_handle_t output) = 0; - virtual audio_io_handle_t getInput(audio_source_t inputSource, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - int audioSession, - audio_input_flags_t flags) = 0; + audio_session_t session) = 0; + virtual void releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) = 0; + virtual status_t getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags) = 0; virtual status_t startInput(audio_io_handle_t input, audio_session_t session) = 0; virtual status_t stopInput(audio_io_handle_t input, @@ -144,6 +152,8 @@ public: virtual status_t releaseSoundTriggerSession(audio_session_t session) = 0; virtual audio_mode_t getPhoneState() = 0; + + virtual status_t registerPolicyMixes(Vector<AudioMix> mixes, bool registration) = 0; }; diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h index db62cd5..4153c25 100644 --- a/include/media/IMediaPlayer.h +++ b/include/media/IMediaPlayer.h @@ -56,6 +56,7 @@ public: virtual status_t stop() = 0; virtual status_t pause() = 0; virtual status_t isPlaying(bool* state) = 0; + virtual status_t setPlaybackRate(float rate) = 0; virtual status_t seekTo(int msec) = 0; virtual status_t getCurrentPosition(int* msec) = 0; virtual status_t getDuration(int* msec) = 0; diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h index d7e584a..49a3d61 100644 --- a/include/media/IMediaPlayerService.h +++ b/include/media/IMediaPlayerService.h @@ -49,19 +49,9 @@ public: virtual sp<IMediaRecorder> createMediaRecorder() = 0; virtual sp<IMediaMetadataRetriever> createMetadataRetriever() = 0; - virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId = 0) = 0; + virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId = 0) + = 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; virtual sp<IOMX> getOMX() = 0; virtual sp<ICrypto> makeCrypto() = 0; virtual sp<IDrm> makeDrm() = 0; diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h index 3e67550..509c06b 100644 --- a/include/media/IMediaRecorder.h +++ b/include/media/IMediaRecorder.h @@ -41,7 +41,6 @@ public: virtual status_t setOutputFormat(int of) = 0; virtual status_t setVideoEncoder(int ve) = 0; virtual status_t setAudioEncoder(int ae) = 0; - virtual status_t setOutputFile(const char* path) = 0; virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0; virtual status_t setVideoSize(int width, int height) = 0; virtual status_t setVideoFrameRate(int frames_per_second) = 0; diff --git a/include/media/IOMX.h b/include/media/IOMX.h index 627f23b..6def65b 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -147,6 +147,7 @@ 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_MAX_FPS, // data is float INTERNAL_OPTION_START_TIME, // data is an int64_t INTERNAL_OPTION_TIME_LAPSE, // data is an int64_t[2] }; diff --git a/include/media/JetPlayer.h b/include/media/JetPlayer.h index 388f767..63d1980 100644 --- a/include/media/JetPlayer.h +++ b/include/media/JetPlayer.h @@ -22,6 +22,7 @@ #include <libsonivox/jet.h> #include <libsonivox/eas_types.h> #include <media/AudioTrack.h> +#include <media/MidiIoWrapper.h> namespace android { @@ -86,15 +87,13 @@ private: int mMaxTracks; // max number of MIDI tracks, usually 32 EAS_DATA_HANDLE mEasData; - EAS_FILE_LOCATOR mEasJetFileLoc; + sp<MidiIoWrapper> mIoWrapper; EAS_PCM* mAudioBuffer;// EAS renders the MIDI data into this buffer, sp<AudioTrack> mAudioTrack; // and we play it in this audio track int mTrackBufferSize; S_JET_STATUS mJetStatus; S_JET_STATUS mPreviousJetStatus; - char mJetFilePath[PATH_MAX]; - class JetPlayerThread : public Thread { public: JetPlayerThread(JetPlayer *player) : mPlayer(player) { diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index c412299..d6fe390 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -43,8 +43,6 @@ class IGraphicBufferProducer; template<typename T> class SortedVector; enum player_type { - PV_PLAYER = 1, - SONIVOX_PLAYER = 2, STAGEFRIGHT_PLAYER = 3, NU_PLAYER = 4, // Test players are available only in the 'test' and 'eng' builds. @@ -90,7 +88,6 @@ public: virtual ~AudioSink() {} virtual bool ready() const = 0; // audio output is open and ready - virtual bool realtime() const = 0; // audio output is real-time output virtual ssize_t bufferSize() const = 0; virtual ssize_t frameCount() const = 0; virtual ssize_t channelCount() const = 0; @@ -116,7 +113,19 @@ public: const audio_offload_info_t *offloadInfo = NULL) = 0; virtual status_t start() = 0; - virtual ssize_t write(const void* buffer, size_t size) = 0; + + /* Input parameter |size| is in byte units stored in |buffer|. + * Data is copied over and actual number of bytes written (>= 0) + * is returned, or no data is copied and a negative status code + * is returned (even when |blocking| is true). + * When |blocking| is false, AudioSink will immediately return after + * part of or full |buffer| is copied over. + * When |blocking| is true, AudioSink will wait to copy the entire + * buffer, unless an error occurs or the copy operation is + * prematurely stopped. + */ + virtual ssize_t write(const void* buffer, size_t size, bool blocking = true) = 0; + virtual void stop() = 0; virtual void flush() = 0; virtual void pause() = 0; @@ -159,6 +168,7 @@ public: virtual status_t stop() = 0; virtual status_t pause() = 0; virtual bool isPlaying() = 0; + virtual status_t setPlaybackRate(float rate) { return INVALID_OPERATION; } virtual status_t seekTo(int msec) = 0; virtual status_t getCurrentPosition(int *msec) = 0; virtual status_t getDuration(int *msec) = 0; diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h index d7ac302..f55063e 100644 --- a/include/media/MediaRecorderBase.h +++ b/include/media/MediaRecorderBase.h @@ -43,7 +43,6 @@ struct MediaRecorderBase { virtual status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy) = 0; virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface) = 0; - virtual status_t setOutputFile(const char *path) = 0; virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0; virtual status_t setOutputFileAuxiliary(int fd) {return INVALID_OPERATION;} virtual status_t setParameters(const String8& params) = 0; diff --git a/include/media/MidiIoWrapper.h b/include/media/MidiIoWrapper.h new file mode 100644 index 0000000..e6f8cf7 --- /dev/null +++ b/include/media/MidiIoWrapper.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 MIDI_IO_WRAPPER_H_ +#define MIDI_IO_WRAPPER_H_ + +#include <libsonivox/eas_types.h> + +#include "media/stagefright/DataSource.h" + +namespace android { + +class MidiIoWrapper : public RefBase { +public: + MidiIoWrapper(const char *path); + MidiIoWrapper(int fd, off64_t offset, int64_t size); + MidiIoWrapper(const sp<DataSource> &source); + + ~MidiIoWrapper(); + + int readAt(void *buffer, int offset, int size); + int size(); + + EAS_FILE_LOCATOR getLocator(); + +private: + int mFd; + off64_t mBase; + int64_t mLength; + sp<DataSource> mDataSource; + EAS_FILE mEasFile; +}; + + +} // namespace android + +#endif // MIDI_IO_WRAPPER_H_ diff --git a/include/media/SingleStateQueue.h b/include/media/SingleStateQueue.h index 04c5fd0..d423962 100644 --- a/include/media/SingleStateQueue.h +++ b/include/media/SingleStateQueue.h @@ -21,6 +21,7 @@ // Non-blocking single-reader / single-writer multi-word atomic load / store #include <stdint.h> +#include <cutils/atomic.h> namespace android { @@ -31,6 +32,12 @@ public: class Mutator; class Observer; + enum SSQ_STATUS { + SSQ_PENDING, /* = 0 */ + SSQ_READ, + SSQ_DONE, + }; + struct Shared { // needs to be part of a union so don't define constructor or destructor @@ -41,28 +48,56 @@ private: void init() { mAck = 0; mSequence = 0; } volatile int32_t mAck; -#if 0 - int mPad[7]; - // cache line boundary -#endif volatile int32_t mSequence; T mValue; }; class Mutator { public: - Mutator(Shared *shared); - /*virtual*/ ~Mutator() { } + Mutator(Shared *shared) + : mSequence(0), mShared(shared) + { + // exactly one of Mutator and Observer must initialize, currently it is Observer + // shared->init(); + } // push new value onto state queue, overwriting previous value; // returns a sequence number which can be used with ack() - int32_t push(const T& value); - - // return true if most recent push has been observed - bool ack(); + int32_t push(const T& value) + { + Shared *shared = mShared; + int32_t sequence = mSequence; + sequence++; + android_atomic_acquire_store(sequence, &shared->mSequence); + shared->mValue = value; + sequence++; + android_atomic_release_store(sequence, &shared->mSequence); + mSequence = sequence; + // consider signalling a futex here, if we know that observer is waiting + return sequence; + } + + // returns the status of the last state push. This may be a stale value. + // + // SSQ_PENDING, or 0, means it has not been observed + // SSQ_READ means it has been read + // SSQ_DONE means it has been acted upon, after Observer::done() is called + enum SSQ_STATUS ack() const + { + // in the case of SSQ_DONE, prevent any subtle data-races of subsequent reads + // being performed (out-of-order) before the ack read, should the caller be + // depending on sequentiality of reads. + const int32_t ack = android_atomic_acquire_load(&mShared->mAck); + return ack - mSequence & ~1 ? SSQ_PENDING /* seq differ */ : + ack & 1 ? SSQ_DONE : SSQ_READ; + } // return true if a push with specified sequence number or later has been observed - bool ack(int32_t sequence); + bool ack(int32_t sequence) const + { + // this relies on 2's complement rollover to detect an ancient sequence number + return mShared->mAck - sequence >= 0; + } private: int32_t mSequence; @@ -71,11 +106,54 @@ private: class Observer { public: - Observer(Shared *shared); - /*virtual*/ ~Observer() { } + Observer(Shared *shared) + : mSequence(0), mSeed(1), mShared(shared) + { + // exactly one of Mutator and Observer must initialize, currently it is Observer + shared->init(); + } // return true if value has changed - bool poll(T& value); + bool poll(T& value) + { + Shared *shared = mShared; + int32_t before = shared->mSequence; + if (before == mSequence) { + return false; + } + for (int tries = 0; ; ) { + const int MAX_TRIES = 5; + if (before & 1) { + if (++tries >= MAX_TRIES) { + return false; + } + before = shared->mSequence; + } else { + android_memory_barrier(); + T temp = shared->mValue; + int32_t after = android_atomic_release_load(&shared->mSequence); + if (after == before) { + value = temp; + shared->mAck = before; + mSequence = before; // mSequence is even after poll success + return true; + } + if (++tries >= MAX_TRIES) { + return false; + } + before = after; + } + } + } + + // (optional) used to indicate to the Mutator that the state that has been polled + // has also been acted upon. + void done() + { + const int32_t ack = mShared->mAck + 1; + // ensure all previous writes have been performed. + android_atomic_release_store(ack, &mShared->mAck); // mSequence is odd after "done" + } private: int32_t mSequence; diff --git a/include/media/SoundPool.h b/include/media/SoundPool.h deleted file mode 100644 index 5830475..0000000 --- a/include/media/SoundPool.h +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SOUNDPOOL_H_ -#define SOUNDPOOL_H_ - -#include <utils/threads.h> -#include <utils/List.h> -#include <utils/Vector.h> -#include <utils/KeyedVector.h> -#include <media/AudioTrack.h> -#include <binder/MemoryHeapBase.h> -#include <binder/MemoryBase.h> - -namespace android { - -static const int IDLE_PRIORITY = -1; - -// forward declarations -class SoundEvent; -class SoundPoolThread; -class SoundPool; - -// for queued events -class SoundPoolEvent { -public: - SoundPoolEvent(int msg, int arg1=0, int arg2=0) : - mMsg(msg), mArg1(arg1), mArg2(arg2) {} - int mMsg; - int mArg1; - int mArg2; - enum MessageType { INVALID, SAMPLE_LOADED }; -}; - -// callback function prototype -typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user); - -// tracks samples used by application -class Sample : public RefBase { -public: - enum sample_state { UNLOADED, LOADING, READY, UNLOADING }; - Sample(int sampleID, const char* url); - Sample(int sampleID, int fd, int64_t offset, int64_t length); - ~Sample(); - int sampleID() { return mSampleID; } - int numChannels() { return mNumChannels; } - int sampleRate() { return mSampleRate; } - audio_format_t format() { return mFormat; } - size_t size() { return mSize; } - int state() { return mState; } - uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); } - status_t doLoad(); - void startLoad() { mState = LOADING; } - sp<IMemory> getIMemory() { return mData; } - - // hack - void init(int numChannels, int sampleRate, audio_format_t format, size_t size, - sp<IMemory> data ) { - mNumChannels = numChannels; mSampleRate = sampleRate; mFormat = format; mSize = size; - mData = data; } - -private: - void init(); - - size_t mSize; - volatile int32_t mRefCount; - uint16_t mSampleID; - uint16_t mSampleRate; - uint8_t mState : 3; - uint8_t mNumChannels : 2; - audio_format_t mFormat; - int mFd; - int64_t mOffset; - int64_t mLength; - char* mUrl; - sp<IMemory> mData; - sp<MemoryHeapBase> mHeap; -}; - -// stores pending events for stolen channels -class SoundEvent -{ -public: - SoundEvent() : mChannelID(0), mLeftVolume(0), mRightVolume(0), - mPriority(IDLE_PRIORITY), mLoop(0), mRate(0) {} - void set(const sp<Sample>& sample, int channelID, float leftVolume, - float rightVolume, int priority, int loop, float rate); - sp<Sample> sample() { return mSample; } - int channelID() { return mChannelID; } - float leftVolume() { return mLeftVolume; } - float rightVolume() { return mRightVolume; } - int priority() { return mPriority; } - int loop() { return mLoop; } - float rate() { return mRate; } - void clear() { mChannelID = 0; mSample.clear(); } - -protected: - sp<Sample> mSample; - int mChannelID; - float mLeftVolume; - float mRightVolume; - int mPriority; - int mLoop; - float mRate; -}; - -// for channels aka AudioTracks -class SoundChannel : public SoundEvent { -public: - enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING }; - SoundChannel() : mState(IDLE), mNumChannels(1), - mPos(0), mToggle(0), mAutoPaused(false) {} - ~SoundChannel(); - void init(SoundPool* soundPool); - void play(const sp<Sample>& sample, int channelID, float leftVolume, float rightVolume, - int priority, int loop, float rate); - void setVolume_l(float leftVolume, float rightVolume); - void setVolume(float leftVolume, float rightVolume); - void stop_l(); - void stop(); - void pause(); - void autoPause(); - void resume(); - void autoResume(); - void setRate(float rate); - int state() { return mState; } - void setPriority(int priority) { mPriority = priority; } - void setLoop(int loop); - int numChannels() { return mNumChannels; } - void clearNextEvent() { mNextEvent.clear(); } - void nextEvent(); - int nextChannelID() { return mNextEvent.channelID(); } - void dump(); - -private: - static void callback(int event, void* user, void *info); - void process(int event, void *info, unsigned long toggle); - bool doStop_l(); - - SoundPool* mSoundPool; - sp<AudioTrack> mAudioTrack; - SoundEvent mNextEvent; - Mutex mLock; - int mState; - int mNumChannels; - int mPos; - int mAudioBufferSize; - unsigned long mToggle; - bool mAutoPaused; -}; - -// application object for managing a pool of sounds -class SoundPool { - friend class SoundPoolThread; - friend class SoundChannel; -public: - SoundPool(int maxChannels, const audio_attributes_t* pAttributes); - ~SoundPool(); - int load(const char* url, int priority); - int load(int fd, int64_t offset, int64_t length, int priority); - bool unload(int sampleID); - int play(int sampleID, float leftVolume, float rightVolume, int priority, - int loop, float rate); - void pause(int channelID); - void autoPause(); - void resume(int channelID); - void autoResume(); - void stop(int channelID); - void setVolume(int channelID, float leftVolume, float rightVolume); - void setPriority(int channelID, int priority); - void setLoop(int channelID, int loop); - void setRate(int channelID, float rate); - const audio_attributes_t* attributes() { return &mAttributes; } - - // called from SoundPoolThread - void sampleLoaded(int sampleID); - - // called from AudioTrack thread - void done_l(SoundChannel* channel); - - // callback function - void setCallback(SoundPoolCallback* callback, void* user); - void* getUserData() { return mUserData; } - -private: - SoundPool() {} // no default constructor - bool startThreads(); - void doLoad(sp<Sample>& sample); - sp<Sample> findSample(int sampleID) { return mSamples.valueFor(sampleID); } - SoundChannel* findChannel (int channelID); - SoundChannel* findNextChannel (int channelID); - SoundChannel* allocateChannel_l(int priority); - void moveToFront_l(SoundChannel* channel); - void notify(SoundPoolEvent event); - void dump(); - - // restart thread - void addToRestartList(SoundChannel* channel); - void addToStopList(SoundChannel* channel); - static int beginThread(void* arg); - int run(); - void quit(); - - Mutex mLock; - Mutex mRestartLock; - Condition mCondition; - SoundPoolThread* mDecodeThread; - SoundChannel* mChannelPool; - List<SoundChannel*> mChannels; - List<SoundChannel*> mRestart; - List<SoundChannel*> mStop; - DefaultKeyedVector< int, sp<Sample> > mSamples; - int mMaxChannels; - audio_attributes_t mAttributes; - int mAllocated; - int mNextSampleID; - int mNextChannelID; - bool mQuit; - - // callback - Mutex mCallbackLock; - SoundPoolCallback* mCallback; - void* mUserData; -}; - -} // end namespace android - -#endif /*SOUNDPOOL_H_*/ diff --git a/include/media/StringArray.h b/include/media/StringArray.h index ae47085..48d98bf 100644 --- a/include/media/StringArray.h +++ b/include/media/StringArray.h @@ -16,7 +16,7 @@ // // Sortable array of strings. STL-ish, but STL-free. -// +// #ifndef _LIBS_MEDIA_STRING_ARRAY_H #define _LIBS_MEDIA_STRING_ARRAY_H diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index 98c4332..8406ed6 100644 --- a/include/media/ToneGenerator.h +++ b/include/media/ToneGenerator.h @@ -17,11 +17,12 @@ #ifndef ANDROID_TONEGENERATOR_H_ #define ANDROID_TONEGENERATOR_H_ -#include <utils/RefBase.h> -#include <utils/KeyedVector.h> -#include <utils/threads.h> #include <media/AudioSystem.h> #include <media/AudioTrack.h> +#include <utils/Compat.h> +#include <utils/KeyedVector.h> +#include <utils/RefBase.h> +#include <utils/threads.h> namespace android { @@ -207,7 +208,7 @@ private: static const unsigned int TONEGEN_MAX_WAVES = 3; // Maximun number of sine waves in a tone segment static const unsigned int TONEGEN_MAX_SEGMENTS = 12; // Maximun number of segments in a tone descriptor static const unsigned int TONEGEN_INF = 0xFFFFFFFF; // Represents infinite time duration - static const float TONEGEN_GAIN = 0.9; // Default gain passed to WaveGenerator(). + static const CONSTEXPR float TONEGEN_GAIN = 0.9; // Default gain passed to WaveGenerator(). // ToneDescriptor class contains all parameters needed to generate a tone: // - The array waveFreq[]: diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h index 9cc208e..808e893 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -220,6 +220,7 @@ public: status_t stop(); status_t pause(); bool isPlaying(); + status_t setPlaybackRate(float rate); status_t getVideoWidth(int *w); status_t getVideoHeight(int *h); status_t seekTo(int msec); @@ -232,17 +233,6 @@ public: 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 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); status_t invoke(const Parcel& request, Parcel *reply); status_t setMetadataFilter(const Parcel& filter); status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata); @@ -285,6 +275,7 @@ private: int mVideoWidth; int mVideoHeight; int mAudioSessionId; + float mPlaybackRate; float mSendLevel; struct sockaddr_in mRetransmitEndpoint; bool mRetransmitEndpointValid; diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index b0a62a7..74a6469 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -221,7 +221,6 @@ public: status_t setOutputFormat(int of); status_t setVideoEncoder(int ve); status_t setAudioEncoder(int ae); - status_t setOutputFile(const char* path); status_t setOutputFile(int fd, int64_t offset, int64_t length); status_t setVideoSize(int width, int height); status_t setVideoFrameRate(int frames_per_second); diff --git a/include/media/nbaio/NBAIO.h b/include/media/nbaio/NBAIO.h index d422576..d9bbc8d 100644 --- a/include/media/nbaio/NBAIO.h +++ b/include/media/nbaio/NBAIO.h @@ -231,7 +231,8 @@ public: virtual status_t getTimestamp(AudioTimestamp& timestamp) { return INVALID_OPERATION; } protected: - NBAIO_Sink(const 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 @@ -322,7 +323,8 @@ public: virtual void onTimestamp(const AudioTimestamp& timestamp) { } protected: - NBAIO_Source(const 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 bcbbc04..1297b51 100644 --- a/include/media/nbaio/NBLog.h +++ b/include/media/nbaio/NBLog.h @@ -21,7 +21,7 @@ #include <binder/IMemory.h> #include <utils/Mutex.h> -#include <media/nbaio/roundup.h> +#include <audio_utils/roundup.h> namespace android { diff --git a/include/media/stagefright/AACWriter.h b/include/media/stagefright/AACWriter.h index d22707a..86417a5 100644 --- a/include/media/stagefright/AACWriter.h +++ b/include/media/stagefright/AACWriter.h @@ -27,7 +27,6 @@ struct MediaSource; struct MetaData; struct AACWriter : public MediaWriter { - AACWriter(const char *filename); AACWriter(int fd); status_t initCheck() const; diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index fcccc6d..aa91485 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -78,7 +78,7 @@ struct ACodec : public AHierarchicalStateMachine, public CodecBase { static bool isFlexibleColorFormat( const sp<IOMX> &omx, IOMX::node_id node, - uint32_t colorFormat, OMX_U32 *flexibleEquivalent); + uint32_t colorFormat, bool usingNativeBuffers, OMX_U32 *flexibleEquivalent); // Returns 0 if configuration is not supported. NOTE: this is treated by // some OMX components as auto level, and by others as invalid level. @@ -131,6 +131,7 @@ private: enum { kFlagIsSecure = 1, kFlagPushBlankBuffersToNativeWindowOnShutdown = 2, + kFlagIsGrallocUsageProtected = 4, }; struct BufferInfo { @@ -182,6 +183,7 @@ private: sp<ANativeWindow> mNativeWindow; sp<AMessage> mInputFormat; sp<AMessage> mOutputFormat; + sp<AMessage> mBaseOutputFormat; Vector<BufferInfo> mBuffers[2]; bool mPortEOS[2]; @@ -212,6 +214,7 @@ private: int64_t mRepeatFrameDelayUs; int64_t mMaxPtsGapUs; + float mMaxFps; int64_t mTimePerFrameUs; int64_t mTimePerCaptureUs; @@ -249,12 +252,13 @@ private: status_t setVideoPortFormatType( OMX_U32 portIndex, OMX_VIDEO_CODINGTYPE compressionFormat, - OMX_COLOR_FORMATTYPE colorFormat); + OMX_COLOR_FORMATTYPE colorFormat, + bool usingNativeBuffers = false); - status_t setSupportedOutputFormat(); + status_t setSupportedOutputFormat(bool getLegacyFlexibleFormat); status_t setupVideoDecoder( - const char *mime, const sp<AMessage> &msg); + const char *mime, const sp<AMessage> &msg, bool usingNativeBuffers); status_t setupVideoEncoder( const char *mime, const sp<AMessage> &msg); @@ -262,7 +266,7 @@ private: status_t setVideoFormatOnPort( OMX_U32 portIndex, int32_t width, int32_t height, - OMX_VIDEO_CODINGTYPE compressionFormat); + OMX_VIDEO_CODINGTYPE compressionFormat, float frameRate = -1.0); typedef struct drcParams { int32_t drcCut; @@ -281,6 +285,8 @@ private: status_t setupAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate); + status_t setupEAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate); + status_t selectAudioPortFormat( OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat); @@ -293,6 +299,8 @@ private: status_t setupRawAudioFormat( OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels); + status_t setPriority(int32_t priority); + status_t setMinBufferSize(OMX_U32 portIndex, size_t size); status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg); diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h index 392f968..bac878b 100644 --- a/include/media/stagefright/AMRWriter.h +++ b/include/media/stagefright/AMRWriter.h @@ -29,7 +29,6 @@ struct MediaSource; struct MetaData; struct AMRWriter : public MediaWriter { - AMRWriter(const char *filename); AMRWriter(int fd); status_t initCheck() const; diff --git a/include/media/stagefright/BufferProducerWrapper.h b/include/media/stagefright/BufferProducerWrapper.h index d8acf30..4caa2c6 100644 --- a/include/media/stagefright/BufferProducerWrapper.h +++ b/include/media/stagefright/BufferProducerWrapper.h @@ -19,6 +19,7 @@ #define BUFFER_PRODUCER_WRAPPER_H_ #include <gui/IGraphicBufferProducer.h> +#include <media/stagefright/foundation/ABase.h> namespace android { diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h index 2e2922e..3d7960b 100644 --- a/include/media/stagefright/MPEG2TSWriter.h +++ b/include/media/stagefright/MPEG2TSWriter.h @@ -29,7 +29,6 @@ struct ABuffer; struct MPEG2TSWriter : public MediaWriter { MPEG2TSWriter(int fd); - MPEG2TSWriter(const char *filename); MPEG2TSWriter( void *cookie, diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h index 26ce5f9..a195fe8 100644 --- a/include/media/stagefright/MPEG4Writer.h +++ b/include/media/stagefright/MPEG4Writer.h @@ -26,13 +26,13 @@ namespace android { +class AMessage; class MediaBuffer; class MediaSource; class MetaData; class MPEG4Writer : public MediaWriter { public: - MPEG4Writer(const char *filename); MPEG4Writer(int fd); // Limitations @@ -49,6 +49,7 @@ public: virtual status_t dump(int fd, const Vector<String16>& args); void beginBox(const char *fourcc); + void beginBox(uint32_t id); void writeInt8(int8_t x); void writeInt16(int16_t x); void writeInt32(int32_t x); @@ -63,6 +64,7 @@ public: int32_t getTimeScale() const { return mTimeScale; } status_t setGeoData(int latitudex10000, int longitudex10000); + status_t setCaptureRate(float captureFps); virtual void setStartTimeOffsetMs(int ms) { mStartTimeOffsetMs = ms; } virtual int32_t getStartTimeOffsetMs() const { return mStartTimeOffsetMs; } @@ -89,6 +91,7 @@ private: off64_t mFreeBoxOffset; bool mStreamableFile; off64_t mEstimatedMoovBoxSize; + off64_t mMoovExtraSize; uint32_t mInterleaveDurationUs; int32_t mTimeScale; int64_t mStartTimestampUs; @@ -103,6 +106,8 @@ private: List<off64_t> mBoxes; + sp<AMessage> mMetaKeys; + void setStartTimestampUs(int64_t timeUs); int64_t getStartTimestampUs(); // Not const status_t startTracks(MetaData *params); @@ -196,6 +201,12 @@ private: void writeGeoDataBox(); void writeLatitude(int degreex10000); void writeLongitude(int degreex10000); + + void addDeviceMeta(); + void writeHdlr(); + void writeKeys(); + void writeIlst(); + void writeMetaBox(); void sendSessionSummary(); void release(); status_t reset(); diff --git a/include/media/stagefright/MediaClock.h b/include/media/stagefright/MediaClock.h new file mode 100644 index 0000000..660764f --- /dev/null +++ b/include/media/stagefright/MediaClock.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_CLOCK_H_ + +#define MEDIA_CLOCK_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <utils/Mutex.h> +#include <utils/RefBase.h> + +namespace android { + +struct AMessage; + +struct MediaClock : public RefBase { + MediaClock(); + + void setStartingTimeMedia(int64_t startingTimeMediaUs); + + void clearAnchor(); + // It's required to use timestamp of just rendered frame as + // anchor time in paused state. + void updateAnchor( + int64_t anchorTimeMediaUs, + int64_t anchorTimeRealUs, + int64_t maxTimeMediaUs = INT64_MAX); + + void updateMaxTimeMedia(int64_t maxTimeMediaUs); + + void setPlaybackRate(float rate); + + // query media time corresponding to real time |realUs|, and save the + // result in |outMediaUs|. + status_t getMediaTime(int64_t realUs, + int64_t *outMediaUs, + bool allowPastMaxTime = false); + // query real time corresponding to media time |targetMediaUs|. + // The result is saved in |outRealUs|. + status_t getRealTimeFor(int64_t targetMediaUs, int64_t *outRealUs); + +protected: + virtual ~MediaClock(); + +private: + status_t getMediaTime_l(int64_t realUs, + int64_t *outMediaUs, + bool allowPastMaxTime); + + Mutex mLock; + + int64_t mAnchorTimeMediaUs; + int64_t mAnchorTimeRealUs; + int64_t mMaxTimeMediaUs; + int64_t mStartingTimeMediaUs; + + float mPlaybackRate; + + DISALLOW_EVIL_CONSTRUCTORS(MediaClock); +}; + +} // namespace android + +#endif // MEDIA_CLOCK_H_ diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h index 54a4e8b..8241e19 100644 --- a/include/media/stagefright/MediaCodec.h +++ b/include/media/stagefright/MediaCodec.h @@ -27,6 +27,7 @@ namespace android { struct ABuffer; struct AMessage; +struct AReplyToken; struct AString; struct CodecBase; struct ICrypto; @@ -194,7 +195,7 @@ private: }; enum { - kFlagIsSoftwareCodec = 1, + kFlagUsesSoftwareRenderer = 1, kFlagOutputFormatChanged = 2, kFlagOutputBuffersChanged = 4, kFlagStickyError = 8, @@ -222,7 +223,7 @@ private: sp<ALooper> mCodecLooper; sp<CodecBase> mCodec; AString mComponentName; - uint32_t mReplyID; + sp<AReplyToken> mReplyID; uint32_t mFlags; status_t mStickyError; sp<Surface> mNativeWindow; @@ -249,10 +250,10 @@ private: Vector<BufferInfo> mPortBuffers[2]; int32_t mDequeueInputTimeoutGeneration; - uint32_t mDequeueInputReplyID; + sp<AReplyToken> mDequeueInputReplyID; int32_t mDequeueOutputTimeoutGeneration; - uint32_t mDequeueOutputReplyID; + sp<AReplyToken> mDequeueOutputReplyID; sp<ICrypto> mCrypto; @@ -267,7 +268,7 @@ private: static status_t PostAndAwaitResponse( const sp<AMessage> &msg, sp<AMessage> *response); - static void PostReplyWithError(int32_t replyID, int32_t err); + static void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err); status_t init(const AString &name, bool nameIsType, bool encoder); @@ -283,8 +284,8 @@ private: size_t portIndex, size_t index, sp<ABuffer> *buffer, sp<AMessage> *format); - bool handleDequeueInputBuffer(uint32_t replyID, bool newRequest = false); - bool handleDequeueOutputBuffer(uint32_t replyID, bool newRequest = false); + bool handleDequeueInputBuffer(const sp<AReplyToken> &replyID, bool newRequest = false); + bool handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool newRequest = false); void cancelPendingDequeueOperations(); void extractCSD(const sp<AMessage> &format); diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h index 8605d99..c2bbe4d 100644 --- a/include/media/stagefright/MediaCodecList.h +++ b/include/media/stagefright/MediaCodecList.h @@ -52,6 +52,12 @@ struct MediaCodecList : public BnMediaCodecList { static sp<IMediaCodecList> getLocalInstance(); private: + class BinderDeathObserver : public IBinder::DeathRecipient { + void binderDied(const wp<IBinder> &the_late_who __unused); + }; + + static sp<BinderDeathObserver> sBinderDeathObserver; + enum Section { SECTION_TOPLEVEL, SECTION_DECODERS, diff --git a/include/media/stagefright/MediaCodecSource.h b/include/media/stagefright/MediaCodecSource.h index 3629c8b..7b8f59d 100644 --- a/include/media/stagefright/MediaCodecSource.h +++ b/include/media/stagefright/MediaCodecSource.h @@ -25,6 +25,7 @@ namespace android { class ALooper; class AMessage; +struct AReplyToken; class IGraphicBufferProducer; class MediaCodec; class MetaData; @@ -85,8 +86,6 @@ private: status_t initEncoder(); void releaseEncoder(); status_t feedEncoderInputBuffers(); - void scheduleDoMoreWork(); - status_t doMoreWork(int32_t numInput, int32_t numOutput); void suspend(); void resume(int64_t skipFramesBeforeUs = -1ll); void signalEOS(status_t err = ERROR_END_OF_STREAM); @@ -101,15 +100,13 @@ private: sp<Puller> mPuller; sp<MediaCodec> mEncoder; uint32_t mFlags; - List<uint32_t> mStopReplyIDQueue; + List<sp<AReplyToken>> mStopReplyIDQueue; bool mIsVideo; bool mStarted; bool mStopping; bool mDoMoreWorkPending; 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 diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h index e67d4d5..a0036e0 100644 --- a/include/media/stagefright/MediaDefs.h +++ b/include/media/stagefright/MediaDefs.h @@ -36,6 +36,7 @@ extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB; extern const char *MEDIA_MIMETYPE_AUDIO_MPEG; // layer III extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I; extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II; +extern const char *MEDIA_MIMETYPE_AUDIO_MIDI; extern const char *MEDIA_MIMETYPE_AUDIO_AAC; extern const char *MEDIA_MIMETYPE_AUDIO_QCELP; extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS; @@ -47,6 +48,7 @@ extern const char *MEDIA_MIMETYPE_AUDIO_FLAC; extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS; extern const char *MEDIA_MIMETYPE_AUDIO_MSGSM; extern const char *MEDIA_MIMETYPE_AUDIO_AC3; +extern const char *MEDIA_MIMETYPE_AUDIO_EAC3; extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4; extern const char *MEDIA_MIMETYPE_CONTAINER_WAV; diff --git a/include/media/stagefright/MediaMuxer.h b/include/media/stagefright/MediaMuxer.h index 9da98d9..e6538d1 100644 --- a/include/media/stagefright/MediaMuxer.h +++ b/include/media/stagefright/MediaMuxer.h @@ -50,9 +50,6 @@ public: OUTPUT_FORMAT_LIST_END // must be last - used to validate format type }; - // Construct the muxer with the output file path. - MediaMuxer(const char *path, OutputFormat format); - // Construct the muxer with the file descriptor. Note that the MediaMuxer // will close this file at stop(). MediaMuxer(int fd, OutputFormat format); diff --git a/include/media/stagefright/ProcessInfo.h b/include/media/stagefright/ProcessInfo.h new file mode 100644 index 0000000..ec0cdff --- /dev/null +++ b/include/media/stagefright/ProcessInfo.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 PROCESS_INFO_H_ + +#define PROCESS_INFO_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/ProcessInfoInterface.h> + +namespace android { + +struct ProcessInfo : public ProcessInfoInterface { + ProcessInfo(); + + virtual bool getPriority(int pid, int* priority); + +protected: + virtual ~ProcessInfo(); + +private: + DISALLOW_EVIL_CONSTRUCTORS(ProcessInfo); +}; + +} // namespace android + +#endif // PROCESS_INFO_H_ diff --git a/media/libmedia/SingleStateQueueInstantiations.cpp b/include/media/stagefright/ProcessInfoInterface.h index 0265c8c..222f92d 100644 --- a/media/libmedia/SingleStateQueueInstantiations.cpp +++ b/include/media/stagefright/ProcessInfoInterface.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2015 The Android Open 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,15 +14,20 @@ * limitations under the License. */ -#include <media/SingleStateQueue.h> -#include <private/media/StaticAudioTrackState.h> -#include <media/AudioTimestamp.h> +#ifndef PROCESS_INFO_INTERFACE_H_ +#define PROCESS_INFO_INTERFACE_H_ -// FIXME hack for gcc +#include <utils/RefBase.h> namespace android { -template class SingleStateQueue<StaticAudioTrackState>; // typedef StaticAudioTrackSingleStateQueue -template class SingleStateQueue<AudioTimestamp>; // typedef AudioTimestampSingleStateQueue +struct ProcessInfoInterface : public RefBase { + virtual bool getPriority(int pid, int* priority) = 0; -} +protected: + virtual ~ProcessInfoInterface() {} +}; + +} // namespace android + +#endif // PROCESS_INFO_INTERFACE_H_ diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h index ffe4f4c..2177c00 100644 --- a/include/media/stagefright/SurfaceMediaSource.h +++ b/include/media/stagefright/SurfaceMediaSource.h @@ -126,7 +126,7 @@ protected: // Implementation of the BufferQueue::ConsumerListener interface. These // calls are used to notify the Surface of asynchronous events in the // BufferQueue. - virtual void onFrameAvailable(); + virtual void onFrameAvailable(const BufferItem& item); // Used as a hook to BufferQueue::disconnect() // This is called by the client side when it is done diff --git a/include/media/stagefright/foundation/ABase.h b/include/media/stagefright/foundation/ABase.h index 72e3d87..ef1e010 100644 --- a/include/media/stagefright/foundation/ABase.h +++ b/include/media/stagefright/foundation/ABase.h @@ -18,7 +18,9 @@ #define A_BASE_H_ +#ifndef ARRAY_SIZE #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) +#endif #define DISALLOW_EVIL_CONSTRUCTORS(name) \ name(const name &); \ diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h index 450dcfe..1d0e2cb 100644 --- a/include/media/stagefright/foundation/ADebug.h +++ b/include/media/stagefright/foundation/ADebug.h @@ -80,6 +80,36 @@ MAKE_COMPARATOR(GT,>) __FILE__ ":" LITERAL_TO_STRING(__LINE__) \ " Should not be here."); +struct ADebug { + enum Level { + kDebugNone, // no debug + kDebugLifeCycle, // lifecycle events: creation/deletion + kDebugState, // commands and events + kDebugConfig, // configuration + kDebugInternalState, // internal state changes + kDebugAll, // all + kDebugMax = kDebugAll, + + }; + + // parse the property or string to get the debug level for a component name + // string format is: + // <level>[:<glob>][,<level>[:<glob>]...] + // - <level> is 0-5 corresponding to ADebug::Level + // - <glob> is used to match component name case insensitively, if omitted, it + // matches all components + // - string is read left-to-right, and the last matching level is returned, or + // the def if no terms matched + static Level GetDebugLevelFromProperty( + const char *name, const char *propertyName, Level def = kDebugNone); + static Level GetDebugLevelFromString( + const char *name, const char *value, Level def = kDebugNone); + + // remove redundant segments of a codec name, and return a newly allocated + // string suitable for debugging + static char *GetDebugName(const char *name); +}; + } // namespace android #endif // A_DEBUG_H_ diff --git a/include/media/stagefright/foundation/AHandler.h b/include/media/stagefright/foundation/AHandler.h index b008b54..fe02a86 100644 --- a/include/media/stagefright/foundation/AHandler.h +++ b/include/media/stagefright/foundation/AHandler.h @@ -19,6 +19,7 @@ #define A_HANDLER_H_ #include <media/stagefright/foundation/ALooper.h> +#include <utils/KeyedVector.h> #include <utils/RefBase.h> namespace android { @@ -27,27 +28,49 @@ struct AMessage; struct AHandler : public RefBase { AHandler() - : mID(0) { + : mID(0), + mVerboseStats(false), + mMessageCounter(0) { } ALooper::handler_id id() const { return mID; } - sp<ALooper> looper(); + sp<ALooper> looper() const { + return mLooper.promote(); + } + + wp<ALooper> getLooper() const { + return mLooper; + } + + wp<AHandler> getHandler() const { + // allow getting a weak reference to a const handler + return const_cast<AHandler *>(this); + } protected: virtual void onMessageReceived(const sp<AMessage> &msg) = 0; private: - friend struct ALooperRoster; + friend struct AMessage; // deliverMessage() + friend struct ALooperRoster; // setID() ALooper::handler_id mID; + wp<ALooper> mLooper; - void setID(ALooper::handler_id id) { + inline void setID(ALooper::handler_id id, wp<ALooper> looper) { mID = id; + mLooper = looper; } + bool mVerboseStats; + uint32_t mMessageCounter; + KeyedVector<uint32_t, uint32_t> mMessages; + + void deliverMessage(const sp<AMessage> &msg); + DISALLOW_EVIL_CONSTRUCTORS(AHandler); }; diff --git a/include/media/stagefright/foundation/ALooper.h b/include/media/stagefright/foundation/ALooper.h index 70e0c5e..09c469b 100644 --- a/include/media/stagefright/foundation/ALooper.h +++ b/include/media/stagefright/foundation/ALooper.h @@ -30,6 +30,7 @@ namespace android { struct AHandler; struct AMessage; +struct AReplyToken; struct ALooper : public RefBase { typedef int32_t event_id; @@ -53,11 +54,15 @@ struct ALooper : public RefBase { static int64_t GetNowUs(); + const char *getName() const { + return mName.c_str(); + } + protected: virtual ~ALooper(); private: - friend struct ALooperRoster; + friend struct AMessage; // post() struct Event { int64_t mWhenUs; @@ -75,12 +80,32 @@ private: sp<LooperThread> mThread; bool mRunningLocally; + // use a separate lock for reply handling, as it is always on another thread + // use a central lock, however, to avoid creating a mutex for each reply + Mutex mRepliesLock; + Condition mRepliesCondition; + + // START --- methods used only by AMessage + + // posts a message on this looper with the given timeout void post(const sp<AMessage> &msg, int64_t delayUs); + + // creates a reply token to be used with this looper + sp<AReplyToken> createReplyToken(); + // waits for a response for the reply token. If status is OK, the response + // is stored into the supplied variable. Otherwise, it is unchanged. + status_t awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response); + // posts a reply for a reply token. If the reply could be successfully posted, + // it returns OK. Otherwise, it returns an error value. + status_t postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &msg); + + // END --- methods used only by AMessage + bool loop(); DISALLOW_EVIL_CONSTRUCTORS(ALooper); }; -} // namespace android +} // namespace android #endif // A_LOOPER_H_ diff --git a/include/media/stagefright/foundation/ALooperRoster.h b/include/media/stagefright/foundation/ALooperRoster.h index 4d76b64..9912455 100644 --- a/include/media/stagefright/foundation/ALooperRoster.h +++ b/include/media/stagefright/foundation/ALooperRoster.h @@ -20,6 +20,7 @@ #include <media/stagefright/foundation/ALooper.h> #include <utils/KeyedVector.h> +#include <utils/String16.h> namespace android { @@ -32,15 +33,7 @@ struct ALooperRoster { void unregisterHandler(ALooper::handler_id handlerID); void unregisterStaleHandlers(); - status_t postMessage(const sp<AMessage> &msg, int64_t delayUs = 0); - void deliverMessage(const sp<AMessage> &msg); - - status_t postAndAwaitResponse( - const sp<AMessage> &msg, sp<AMessage> *response); - - void postReply(uint32_t replyID, const sp<AMessage> &reply); - - sp<ALooper> findLooper(ALooper::handler_id handlerID); + void dump(int fd, const Vector<String16>& args); private: struct HandlerInfo { @@ -51,10 +44,6 @@ private: Mutex mLock; KeyedVector<ALooper::handler_id, HandlerInfo> mHandlers; ALooper::handler_id mNextHandlerID; - uint32_t mNextReplyID; - Condition mRepliesCondition; - - KeyedVector<uint32_t, sp<AMessage> > mReplies; DISALLOW_EVIL_CONSTRUCTORS(ALooperRoster); }; diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h index a9e235b..4c6bd21 100644 --- a/include/media/stagefright/foundation/AMessage.h +++ b/include/media/stagefright/foundation/AMessage.h @@ -26,11 +26,41 @@ namespace android { struct ABuffer; +struct AHandler; struct AString; struct Parcel; +struct AReplyToken : public RefBase { + AReplyToken(const sp<ALooper> &looper) + : mLooper(looper), + mReplied(false) { + } + +private: + friend struct AMessage; + friend struct ALooper; + wp<ALooper> mLooper; + sp<AMessage> mReply; + bool mReplied; + + sp<ALooper> getLooper() const { + return mLooper.promote(); + } + // if reply is not set, returns false; otherwise, it retrieves the reply and returns true + bool retrieveReply(sp<AMessage> *reply) { + if (mReplied) { + *reply = mReply; + mReply.clear(); + } + return mReplied; + } + // sets the reply for this token. returns OK or error + status_t setReply(const sp<AMessage> &reply); +}; + struct AMessage : public RefBase { - AMessage(uint32_t what = 0, ALooper::handler_id target = 0); + AMessage(); + AMessage(uint32_t what, const sp<const AHandler> &handler); static sp<AMessage> FromParcel(const Parcel &parcel); void writeToParcel(Parcel *parcel) const; @@ -38,8 +68,7 @@ struct AMessage : public RefBase { void setWhat(uint32_t what); uint32_t what() const; - void setTarget(ALooper::handler_id target); - ALooper::handler_id target() const; + void setTarget(const sp<const AHandler> &handler); void clear(); @@ -76,18 +105,22 @@ struct AMessage : public RefBase { const char *name, int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const; - void post(int64_t delayUs = 0); + status_t post(int64_t delayUs = 0); // Posts the message to its target and waits for a response (or error) // before returning. status_t postAndAwaitResponse(sp<AMessage> *response); // If this returns true, the sender of this message is synchronously - // awaiting a response, the "replyID" can be used to send the response - // via "postReply" below. - bool senderAwaitsResponse(uint32_t *replyID) const; + // awaiting a response and the reply token is consumed from the message + // and stored into replyID. The reply token must be used to send the response + // using "postReply" below. + bool senderAwaitsResponse(sp<AReplyToken> *replyID); - void postReply(uint32_t replyID); + // Posts the message as a response to a reply token. A reply token can + // only be used once. Returns OK if the response could be posted; otherwise, + // an error. + status_t postReply(const sp<AReplyToken> &replyID); // Performs a deep-copy of "this", contained messages are in turn "dup'ed". // Warning: RefBase items, i.e. "objects" are _not_ copied but only have @@ -117,9 +150,16 @@ protected: virtual ~AMessage(); private: + friend struct ALooper; // deliver() + uint32_t mWhat; + + // used only for debugging ALooper::handler_id mTarget; + wp<AHandler> mHandler; + wp<ALooper> mLooper; + struct Rect { int32_t mLeft, mTop, mRight, mBottom; }; @@ -157,6 +197,8 @@ private: size_t findItemIndex(const char *name, size_t len) const; + void deliver(); + DISALLOW_EVIL_CONSTRUCTORS(AMessage); }; diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h index 7c98699..822dbb3 100644 --- a/include/media/stagefright/foundation/AString.h +++ b/include/media/stagefright/foundation/AString.h @@ -23,7 +23,7 @@ namespace android { -struct String8; +class String8; struct Parcel; struct AString { @@ -102,7 +102,7 @@ private: void makeMutable(); }; -AString StringPrintf(const char *format, ...); +AString AStringPrintf(const char *format, ...); } // namespace android diff --git a/include/media/stagefright/foundation/AStringUtils.h b/include/media/stagefright/foundation/AStringUtils.h new file mode 100644 index 0000000..76a7791 --- /dev/null +++ b/include/media/stagefright/foundation/AStringUtils.h @@ -0,0 +1,36 @@ +/* + * 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 A_STRING_UTILS_H_ +#define A_STRING_UTILS_H_ + +#include <stdlib.h> + +namespace android { + +struct AStringUtils { + // similar to strncmp or strcasecmp, but case sensitivity is parametric + static int Compare(const char *a, const char *b, size_t len, bool ignoreCase); + + // matches a string (str) to a glob pattern that supports: + // * - matches any number of characters + static bool MatchesGlob( + const char *glob, size_t globLen, const char *str, size_t strLen, bool ignoreCase); +}; + +} // namespace android + +#endif // A_STRING_UTILS_H_ diff --git a/include/media/stagefright/foundation/AUtils.h b/include/media/stagefright/foundation/AUtils.h index 3a73a39..d7ecf50 100644 --- a/include/media/stagefright/foundation/AUtils.h +++ b/include/media/stagefright/foundation/AUtils.h @@ -40,6 +40,12 @@ inline static const T divUp(const T &nom, const T &den) { } } +/* == ceil(nom / den) * den. T must be integer type, alignment must be positive power of 2 */ +template<class T, class U> +inline static const T align(const T &nom, const U &den) { + return (nom + (T)(den - 1)) & (T)~(den - 1); +} + template<class T> inline static T abs(const T &a) { return a < 0 ? -a : a; diff --git a/include/media/stagefright/foundation/AWakeLock.h b/include/media/stagefright/foundation/AWakeLock.h new file mode 100644 index 0000000..57716c1 --- /dev/null +++ b/include/media/stagefright/foundation/AWakeLock.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef A_WAKELOCK_H_ +#define A_WAKELOCK_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <powermanager/IPowerManager.h> +#include <utils/RefBase.h> + +namespace android { + +class AWakeLock : public RefBase { + +public: + AWakeLock(); + + // NOTE: acquire and release are not thread safe + + // returns true if wakelock was acquired + bool acquire(); + void release(bool force = false); + + virtual ~AWakeLock(); + +private: + sp<IPowerManager> mPowerManager; + sp<IBinder> mWakeLockToken; + uint32_t mWakeLockCount; + + class PMDeathRecipient : public IBinder::DeathRecipient { + public: + PMDeathRecipient(AWakeLock *wakeLock) : mWakeLock(wakeLock) {} + virtual ~PMDeathRecipient() {} + + // IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder> &who); + + private: + PMDeathRecipient(const PMDeathRecipient&); + PMDeathRecipient& operator= (const PMDeathRecipient&); + + AWakeLock *mWakeLock; + }; + + const sp<PMDeathRecipient> mDeathRecipient; + + void clearPowerManager(); + + DISALLOW_EVIL_CONSTRUCTORS(AWakeLock); +}; + +} // namespace android + +#endif // A_WAKELOCK_H_ diff --git a/include/ndk/NdkMediaCodec.h b/include/ndk/NdkMediaCodec.h index c07f4c9..4f6a1ef 100644 --- a/include/ndk/NdkMediaCodec.h +++ b/include/ndk/NdkMediaCodec.h @@ -142,7 +142,8 @@ media_status_t AMediaCodec_queueSecureInputBuffer(AMediaCodec*, /** * Get the index of the next available buffer of processed data. */ -ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec*, AMediaCodecBufferInfo *info, int64_t timeoutUs); +ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec*, AMediaCodecBufferInfo *info, + int64_t timeoutUs); AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec*); /** diff --git a/include/ndk/NdkMediaDrm.h b/include/ndk/NdkMediaDrm.h index 10afdd9..3c312a9 100644 --- a/include/ndk/NdkMediaDrm.h +++ b/include/ndk/NdkMediaDrm.h @@ -327,24 +327,24 @@ media_status_t AMediaDrm_releaseSecureStops(AMediaDrm *, /** * String property name: identifies the maker of the DRM engine plugin */ -const char *PROPERTY_VENDOR = "vendor"; +#define PROPERTY_VENDOR "vendor" /** * String property name: identifies the version of the DRM engine plugin */ -const char *PROPERTY_VERSION = "version"; +#define PROPERTY_VERSION "version" /** * String property name: describes the DRM engine plugin */ -const char *PROPERTY_DESCRIPTION = "description"; +#define PROPERTY_DESCRIPTION "description" /** * String property name: a comma-separated list of cipher and mac algorithms * supported by CryptoSession. The list may be empty if the DRM engine * plugin does not support CryptoSession operations. */ -const char *PROPERTY_ALGORITHMS = "algorithms"; +#define PROPERTY_ALGORITHMS "algorithms" /** * Read a DRM engine plugin String property value, given the property name string. @@ -361,7 +361,7 @@ media_status_t AMediaDrm_getPropertyString(AMediaDrm *, const char *propertyName * Byte array property name: the device unique identifier is established during * device provisioning and provides a means of uniquely identifying each device. */ -const char *PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId"; +#define PROPERTY_DEVICE_UNIQUE_ID "deviceUniqueId" /** * Read a DRM engine plugin byte array property value, given the property name string. diff --git a/include/ndk/NdkMediaExtractor.h b/include/ndk/NdkMediaExtractor.h index 7a4e702..7324d31 100644 --- a/include/ndk/NdkMediaExtractor.h +++ b/include/ndk/NdkMediaExtractor.h @@ -55,12 +55,14 @@ media_status_t AMediaExtractor_delete(AMediaExtractor*); /** * Set the file descriptor from which the extractor will read. */ -media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor*, int fd, off64_t offset, off64_t length); +media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor*, int fd, off64_t offset, + off64_t length); /** * Set the URI from which the extractor will read. */ -media_status_t AMediaExtractor_setDataSource(AMediaExtractor*, const char *location); // TODO support headers +media_status_t AMediaExtractor_setDataSource(AMediaExtractor*, const char *location); + // TODO support headers /** * Return the number of tracks in the previously specified media file diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index fa1b20a..5644428 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -24,9 +24,8 @@ #include <utils/threads.h> #include <utils/Log.h> #include <utils/RefBase.h> -#include <media/nbaio/roundup.h> +#include <audio_utils/roundup.h> #include <media/SingleStateQueue.h> -#include <private/media/StaticAudioTrackState.h> namespace android { @@ -54,22 +53,64 @@ namespace android { struct AudioTrackSharedStreaming { // similar to NBAIO MonoPipe // in continuously incrementing frame units, take modulo buffer size, which must be a power of 2 - volatile int32_t mFront; // read by server - volatile int32_t mRear; // write by client + volatile int32_t mFront; // read by consumer (output: server, input: client) + volatile int32_t mRear; // written by producer (output: client, input: server) volatile int32_t mFlush; // incremented by client to indicate a request to flush; // server notices and discards all data between mFront and mRear volatile uint32_t mUnderrunFrames; // server increments for each unavailable but desired frame }; +// Represents a single state of an AudioTrack that was created in static mode (shared memory buffer +// supplied by the client). This state needs to be communicated from the client to server. As this +// state is too large to be updated atomically without a mutex, and mutexes aren't allowed here, the +// state is wrapped by a SingleStateQueue. +struct StaticAudioTrackState { + // Do not define constructors, destructors, or virtual methods as this is part of a + // union in shared memory and they will not get called properly. + + // These fields should both be size_t, but since they are located in shared memory we + // force to 32-bit. The client and server may have different typedefs for size_t. + + // The state has a sequence counter to indicate whether changes are made to loop or position. + // The sequence counter also currently indicates whether loop or position is first depending + // on which is greater; it jumps by max(mLoopSequence, mPositionSequence) + 1. + + uint32_t mLoopStart; + uint32_t mLoopEnd; + int32_t mLoopCount; + uint32_t mLoopSequence; // a sequence counter to indicate changes to loop + uint32_t mPosition; + uint32_t mPositionSequence; // a sequence counter to indicate changes to position +}; + typedef SingleStateQueue<StaticAudioTrackState> StaticAudioTrackSingleStateQueue; +struct StaticAudioTrackPosLoop { + // Do not define constructors, destructors, or virtual methods as this is part of a + // union in shared memory and will not get called properly. + + // These fields should both be size_t, but since they are located in shared memory we + // force to 32-bit. The client and server may have different typedefs for size_t. + + // This struct information is stored in a single state queue to communicate the + // static AudioTrack server state to the client while data is consumed. + // It is smaller than StaticAudioTrackState to prevent unnecessary information from + // being sent. + + uint32_t mBufferPosition; + int32_t mLoopCount; +}; + +typedef SingleStateQueue<StaticAudioTrackPosLoop> StaticAudioTrackPosLoopQueue; + struct AudioTrackSharedStatic { + // client requests to the server for loop or position changes. StaticAudioTrackSingleStateQueue::Shared mSingleStateQueue; - // This field should be a size_t, but since it is located in shared memory we - // force to 32-bit. The client and server may have different typedefs for size_t. - uint32_t mBufferPosition; // updated asynchronously by server, - // "for entertainment purposes only" + // position info updated asynchronously by server and read by client, + // "for entertainment purposes only" + StaticAudioTrackPosLoopQueue::Shared + mPosLoopQueue; }; // ---------------------------------------------------------------------------- @@ -96,7 +137,8 @@ struct audio_track_cblk_t uint32_t mServer; // Number of filled frames consumed by server (mIsOut), // or filled frames provided by server (!mIsOut). // It is updated asynchronously by server without a barrier. - // The value should be used "for entertainment purposes only", + // The value should be used + // "for entertainment purposes only", // which means don't make important decisions based on it. uint32_t mPad1; // unused @@ -313,8 +355,28 @@ public: virtual void flush(); #define MIN_LOOP 16 // minimum length of each loop iteration in frames + + // setLoop(), setBufferPosition(), and setBufferPositionAndLoop() set the + // static buffer position and looping parameters. These commands are not + // synchronous (they do not wait or block); instead they take effect at the + // next buffer data read from the server side. However, the client side + // getters will read a cached version of the position and loop variables + // until the setting takes effect. + // + // setBufferPositionAndLoop() is equivalent to calling, in order, setLoop() and + // setBufferPosition(). + // + // The functions should not be relied upon to do parameter or state checking. + // That is done at the AudioTrack level. + void setLoop(size_t loopStart, size_t loopEnd, int loopCount); + void setBufferPosition(size_t position); + void setBufferPositionAndLoop(size_t position, size_t loopStart, size_t loopEnd, + int loopCount); size_t getBufferPosition(); + // getBufferPositionAndLoopCount() provides the proper snapshot of + // position and loopCount together. + void getBufferPositionAndLoopCount(size_t *position, int *loopCount); virtual size_t getMisalignment() { return 0; @@ -326,7 +388,9 @@ public: private: StaticAudioTrackSingleStateQueue::Mutator mMutator; - size_t mBufferPosition; // so that getBufferPosition() appears to be synchronous + StaticAudioTrackPosLoopQueue::Observer mPosLoopObserver; + StaticAudioTrackState mState; // last communicated state to server + StaticAudioTrackPosLoop mPosLoop; // snapshot of position and loop. }; // ---------------------------------------------------------------------------- @@ -447,12 +511,23 @@ public: virtual uint32_t getUnderrunFrames() const { return 0; } private: + status_t updateStateWithLoop(StaticAudioTrackState *localState, + const StaticAudioTrackState &update) const; + status_t updateStateWithPosition(StaticAudioTrackState *localState, + const StaticAudioTrackState &update) const; ssize_t pollPosition(); // poll for state queue update, and return current position StaticAudioTrackSingleStateQueue::Observer mObserver; - size_t mPosition; // server's current play position in frames, relative to 0 - size_t mEnd; // cached value computed from mState, safe for asynchronous read + StaticAudioTrackPosLoopQueue::Mutator mPosLoopMutator; + size_t mFramesReadySafe; // Assuming size_t read/writes are atomic on 32 / 64 bit + // processors, this is a thread-safe version of + // mFramesReady. + int64_t mFramesReady; // The number of frames ready in the static buffer + // including loops. This is 64 bits since loop mode + // can cause a track to appear to have a large number + // of frames. INT64_MAX means an infinite loop. bool mFramesReadyIsCalledByMultipleThreads; - StaticAudioTrackState mState; + StaticAudioTrackState mState; // Server side state. Any updates from client must be + // passed by the mObserver SingleStateQueue. }; // Proxy used by AudioFlinger for servicing AudioRecord diff --git a/include/private/media/StaticAudioTrackState.h b/include/private/media/StaticAudioTrackState.h deleted file mode 100644 index d483061..0000000 --- a/include/private/media/StaticAudioTrackState.h +++ /dev/null @@ -1,39 +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 STATIC_AUDIO_TRACK_STATE_H -#define STATIC_AUDIO_TRACK_STATE_H - -namespace android { - -// Represents a single state of an AudioTrack that was created in static mode (shared memory buffer -// supplied by the client). This state needs to be communicated from the client to server. As this -// state is too large to be updated atomically without a mutex, and mutexes aren't allowed here, the -// state is wrapped by a SingleStateQueue. -struct StaticAudioTrackState { - // do not define constructors, destructors, or virtual methods - - // These fields should both be size_t, but since they are located in shared memory we - // force to 32-bit. The client and server may have different typedefs for size_t. - uint32_t mLoopStart; - uint32_t mLoopEnd; - - int mLoopCount; -}; - -} // namespace android - -#endif // STATIC_AUDIO_TRACK_STATE_H diff --git a/include/radio/IRadio.h b/include/radio/IRadio.h new file mode 100644 index 0000000..1877f8f --- /dev/null +++ b/include/radio/IRadio.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_IRADIO_H +#define ANDROID_HARDWARE_IRADIO_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <system/radio.h> + +namespace android { + +class IRadio : public IInterface +{ +public: + + DECLARE_META_INTERFACE(Radio); + + virtual void detach() = 0; + + virtual status_t setConfiguration(const struct radio_band_config *config) = 0; + + virtual status_t getConfiguration(struct radio_band_config *config) = 0; + + virtual status_t setMute(bool mute) = 0; + + virtual status_t getMute(bool *mute) = 0; + + virtual status_t step(radio_direction_t direction, bool skipSubChannel) = 0; + + virtual status_t scan(radio_direction_t direction, bool skipSubChannel) = 0; + + virtual status_t tune(unsigned int channel, unsigned int subChannel) = 0; + + virtual status_t cancel() = 0; + + virtual status_t getProgramInformation(struct radio_program_info *info) = 0; + + virtual status_t hasControl(bool *hasControl) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnRadio: public BnInterface<IRadio> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_IRADIO_H diff --git a/include/radio/IRadioClient.h b/include/radio/IRadioClient.h new file mode 100644 index 0000000..9062ad6 --- /dev/null +++ b/include/radio/IRadioClient.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_IRADIO_CLIENT_H +#define ANDROID_HARDWARE_IRADIO_CLIENT_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> + +namespace android { + +class IRadioClient : public IInterface +{ +public: + + DECLARE_META_INTERFACE(RadioClient); + + virtual void onEvent(const sp<IMemory>& eventMemory) = 0; + +}; + +// ---------------------------------------------------------------------------- + +class BnRadioClient : public BnInterface<IRadioClient> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_IRADIO_CLIENT_H diff --git a/include/radio/IRadioService.h b/include/radio/IRadioService.h new file mode 100644 index 0000000..a946dd5 --- /dev/null +++ b/include/radio/IRadioService.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_IRADIO_SERVICE_H +#define ANDROID_HARDWARE_IRADIO_SERVICE_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <system/radio.h> + +namespace android { + +class IRadio; +class IRadioClient; + +class IRadioService : public IInterface +{ +public: + + DECLARE_META_INTERFACE(RadioService); + + virtual status_t listModules(struct radio_properties *properties, + uint32_t *numModules) = 0; + + virtual status_t attach(const radio_handle_t handle, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool withAudio, + sp<IRadio>& radio) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnRadioService: public BnInterface<IRadioService> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_IRADIO_SERVICE_H diff --git a/include/radio/Radio.h b/include/radio/Radio.h new file mode 100644 index 0000000..302bf16 --- /dev/null +++ b/include/radio/Radio.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_RADIO_H +#define ANDROID_HARDWARE_RADIO_H + +#include <binder/IBinder.h> +#include <utils/threads.h> +#include <radio/RadioCallback.h> +#include <radio/IRadio.h> +#include <radio/IRadioService.h> +#include <radio/IRadioClient.h> +#include <system/radio.h> + +namespace android { + +class MemoryDealer; + +class Radio : public BnRadioClient, + public IBinder::DeathRecipient +{ +public: + + virtual ~Radio(); + + static status_t listModules(struct radio_properties *properties, + uint32_t *numModules); + static sp<Radio> attach(radio_handle_t handle, + const struct radio_band_config *config, + bool withAudio, + const sp<RadioCallback>& callback); + + + void detach(); + + status_t setConfiguration(const struct radio_band_config *config); + + status_t getConfiguration(struct radio_band_config *config); + + status_t setMute(bool mute); + + status_t getMute(bool *mute); + + status_t step(radio_direction_t direction, bool skipSubChannel); + + status_t scan(radio_direction_t direction, bool skipSubChannel); + + status_t tune(unsigned int channel, unsigned int subChannel); + + status_t cancel(); + + status_t getProgramInformation(struct radio_program_info *info); + + status_t hasControl(bool *hasControl); + + // BpRadioClient + virtual void onEvent(const sp<IMemory>& eventMemory); + + //IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + +private: + Radio(radio_handle_t handle, + const sp<RadioCallback>&); + static const sp<IRadioService>& getRadioService(); + + Mutex mLock; + sp<IRadio> mIRadio; + const radio_handle_t mHandle; + sp<RadioCallback> mCallback; +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_RADIO_H diff --git a/media/libnbaio/roundup.c b/include/radio/RadioCallback.h index 1d552d1..4a7f1a6 100644 --- a/media/libnbaio/roundup.c +++ b/include/radio/RadioCallback.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2015 The Android Open 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,19 +14,25 @@ * limitations under the License. */ -#include <media/nbaio/roundup.h> +#ifndef ANDROID_HARDWARE_RADIO_CALLBACK_H +#define ANDROID_HARDWARE_RADIO_CALLBACK_H -unsigned roundup(unsigned v) +#include <utils/RefBase.h> +#include <system/radio.h> + +namespace android { + +class RadioCallback : public RefBase { - // __builtin_clz is undefined for zero input - if (v == 0) { - v = 1; - } - int lz = __builtin_clz((int) v); - unsigned rounded = ((unsigned) 0x80000000) >> lz; - // 0x800000001 and higher are actually rounded _down_ to prevent overflow - if (v > rounded && lz > 0) { - rounded <<= 1; - } - return rounded; -} +public: + + RadioCallback() {} + virtual ~RadioCallback() {} + + virtual void onEvent(struct radio_event *event) = 0; + +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_RADIO_CALLBACK_H diff --git a/media/common_time/ICommonClock.cpp b/media/common_time/ICommonClock.cpp index 25ae69e..19b7d6e 100644 --- a/media/common_time/ICommonClock.cpp +++ b/media/common_time/ICommonClock.cpp @@ -206,7 +206,7 @@ class BpCommonClock : public BpInterface<ICommonClock> const sp<ICommonClockListener>& listener) { Parcel data, reply; data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - data.writeStrongBinder(listener->asBinder()); + data.writeStrongBinder(IInterface::asBinder(listener)); status_t status = remote()->transact(REGISTER_LISTENER, data, &reply); @@ -221,7 +221,7 @@ class BpCommonClock : public BpInterface<ICommonClock> const sp<ICommonClockListener>& listener) { Parcel data, reply; data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - data.writeStrongBinder(listener->asBinder()); + data.writeStrongBinder(IInterface::asBinder(listener)); status_t status = remote()->transact(UNREGISTER_LISTENER, data, &reply); if (status == OK) { diff --git a/media/img_utils/include/img_utils/TiffEntryImpl.h b/media/img_utils/include/img_utils/TiffEntryImpl.h index f5ccb5e..c73e231 100644 --- a/media/img_utils/include/img_utils/TiffEntryImpl.h +++ b/media/img_utils/include/img_utils/TiffEntryImpl.h @@ -147,7 +147,7 @@ status_t TiffEntryImpl<T>::writeTagInfo(uint32_t offset, /*out*/EndianOutput* ou } template<typename T> -status_t TiffEntryImpl<T>::writeData(uint32_t offset, EndianOutput* out) const { +status_t TiffEntryImpl<T>::writeData(uint32_t /*offset*/, EndianOutput* out) const { status_t ret = OK; // Some tags have fixed-endian value output diff --git a/media/img_utils/src/FileInput.cpp b/media/img_utils/src/FileInput.cpp index 498e715..4c85a51 100644 --- a/media/img_utils/src/FileInput.cpp +++ b/media/img_utils/src/FileInput.cpp @@ -78,7 +78,7 @@ status_t FileInput::close() { ret = BAD_VALUE; } mOpen = false; - return OK; + return ret; } } /*namespace img_utils*/ diff --git a/media/img_utils/src/FileOutput.cpp b/media/img_utils/src/FileOutput.cpp index ce763ff..0346762 100644 --- a/media/img_utils/src/FileOutput.cpp +++ b/media/img_utils/src/FileOutput.cpp @@ -72,7 +72,7 @@ status_t FileOutput::close() { ret = BAD_VALUE; } mOpen = false; - return OK; + return ret; } } /*namespace img_utils*/ diff --git a/media/img_utils/src/TiffWriter.cpp b/media/img_utils/src/TiffWriter.cpp index ac41734..a6f9218 100644 --- a/media/img_utils/src/TiffWriter.cpp +++ b/media/img_utils/src/TiffWriter.cpp @@ -106,7 +106,6 @@ status_t TiffWriter::write(Output* out, StripSource** sources, size_t sourcesCou for (size_t i = 0; i < offVecSize; ++i) { uint32_t ifdKey = offsetVector.keyAt(i); - uint32_t nextOffset = offsetVector[i]; uint32_t sizeToWrite = mNamedIfds[ifdKey]->getStripSize(); bool found = false; for (size_t j = 0; j < sourcesCount; ++j) { @@ -124,7 +123,7 @@ status_t TiffWriter::write(Output* out, StripSource** sources, size_t sourcesCou ALOGE("%s: No stream for byte strips for IFD %u", __FUNCTION__, ifdKey); return BAD_VALUE; } - assert(nextOffset == endOut.getCurrentOffset()); + assert(offsetVector[i] == endOut.getCurrentOffset()); } return ret; diff --git a/media/libcpustats/ThreadCpuUsage.cpp b/media/libcpustats/ThreadCpuUsage.cpp index cfdcb51..b43b36c 100644 --- a/media/libcpustats/ThreadCpuUsage.cpp +++ b/media/libcpustats/ThreadCpuUsage.cpp @@ -19,6 +19,7 @@ #include <errno.h> #include <stdlib.h> +#include <string.h> #include <time.h> #include <utils/Log.h> @@ -74,7 +75,6 @@ bool ThreadCpuUsage::setEnabled(bool isEnabled) bool ThreadCpuUsage::sampleAndEnable(double& ns) { - bool ret; bool wasEverEnabled = mWasEverEnabled; if (enable()) { // already enabled, so add a new sample relative to previous diff --git a/media/libeffects/loudness/Android.mk b/media/libeffects/loudness/Android.mk index 70d7984..55d0611 100644 --- a/media/libeffects/loudness/Android.mk +++ b/media/libeffects/loudness/Android.mk @@ -19,5 +19,4 @@ LOCAL_MODULE:= libldnhncr LOCAL_C_INCLUDES := \ $(call include-path-for, audio-effects) \ -include external/stlport/libstlport.mk include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index 6c2cbe3..6aeb919 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -766,6 +766,122 @@ int LvmBundle_process(LVM_INT16 *pIn, return 0; } /* end LvmBundle_process */ + +//---------------------------------------------------------------------------- +// EqualizerUpdateActiveParams() +//---------------------------------------------------------------------------- +// Purpose: Update ActiveParams for Equalizer +// +// Inputs: +// pContext: effect engine context +// +// Outputs: +// +//---------------------------------------------------------------------------- +void EqualizerUpdateActiveParams(EffectContext *pContext) { + LVM_ControlParams_t ActiveParams; /* Current control Parameters */ + LVM_ReturnStatus_en LvmStatus=LVM_SUCCESS; /* Function call status */ + + /* Get the current settings */ + LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); + LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "EqualizerUpdateActiveParams") + //ALOGV("\tEqualizerUpdateActiveParams Succesfully returned from LVM_GetControlParameters\n"); + //ALOGV("\tEqualizerUpdateActiveParams just Got -> %d\n", + // ActiveParams.pEQNB_BandDefinition[band].Gain); + + + for (int i = 0; i < FIVEBAND_NUMBANDS; i++) { + ActiveParams.pEQNB_BandDefinition[i].Frequency = EQNB_5BandPresetsFrequencies[i]; + ActiveParams.pEQNB_BandDefinition[i].QFactor = EQNB_5BandPresetsQFactors[i]; + ActiveParams.pEQNB_BandDefinition[i].Gain = pContext->pBundledContext->bandGaindB[i]; + } + + /* Activate the initial settings */ + LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); + LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "EqualizerUpdateActiveParams") + //ALOGV("\tEqualizerUpdateActiveParams just Set -> %d\n", + // ActiveParams.pEQNB_BandDefinition[band].Gain); + +} + +//---------------------------------------------------------------------------- +// LvmEffect_limitLevel() +//---------------------------------------------------------------------------- +// Purpose: limit the overall level to a value less than 0 dB preserving +// the overall EQ band gain and BassBoost relative levels. +// +// Inputs: +// pContext: effect engine context +// +// Outputs: +// +//---------------------------------------------------------------------------- +void LvmEffect_limitLevel(EffectContext *pContext) { + LVM_ControlParams_t ActiveParams; /* Current control Parameters */ + LVM_ReturnStatus_en LvmStatus=LVM_SUCCESS; /* Function call status */ + + /* Get the current settings */ + LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); + LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "LvmEffect_limitLevel") + //ALOGV("\tLvmEffect_limitLevel Succesfully returned from LVM_GetControlParameters\n"); + //ALOGV("\tLvmEffect_limitLevel just Got -> %d\n", + // ActiveParams.pEQNB_BandDefinition[band].Gain); + + int gainCorrection = 0; + //Count the energy contribution per band for EQ and BassBoost only if they are active. + float energyContribution = 0; + + //EQ contribution + if (pContext->pBundledContext->bEqualizerEnabled == LVM_TRUE) { + for (int i = 0; i < FIVEBAND_NUMBANDS; i++) { + float bandEnergy = (pContext->pBundledContext->bandGaindB[i] * + LimitLevel_bandEnergyContribution[i])/15.0; + if (bandEnergy > 0) + energyContribution += bandEnergy; + } + } + + //BassBoost contribution + if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) { + float bandEnergy = (pContext->pBundledContext->BassStrengthSaved * + LimitLevel_bassBoostEnergyContribution)/1000.0; + if (bandEnergy > 0) + energyContribution += bandEnergy; + } + + //Virtualizer contribution + if (pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE) { + energyContribution += LimitLevel_virtualizerContribution; + } + + //roundoff + int maxLevelRound = (int)(energyContribution + 0.99); + if (maxLevelRound + pContext->pBundledContext->volume > 0) { + gainCorrection = maxLevelRound + pContext->pBundledContext->volume; + } + + ActiveParams.VC_EffectLevel = pContext->pBundledContext->volume - gainCorrection; + if (ActiveParams.VC_EffectLevel < -96) { + ActiveParams.VC_EffectLevel = -96; + } + ALOGV("\tVol:%d, GainCorrection: %d, Actual vol: %d", pContext->pBundledContext->volume, + gainCorrection, ActiveParams.VC_EffectLevel); + + /* Activate the initial settings */ + LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); + LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "LvmEffect_limitLevel") + //ALOGV("\tLvmEffect_limitLevel just Set -> %d\n", + // ActiveParams.pEQNB_BandDefinition[band].Gain); + + //ALOGV("\tLvmEffect_limitLevel just set (-96dB -> 0dB) -> %d\n",ActiveParams.VC_EffectLevel ); + if (pContext->pBundledContext->firstVolume == LVM_TRUE){ + LvmStatus = LVM_SetVolumeNoSmoothing(pContext->pBundledContext->hInstance, &ActiveParams); + LVM_ERROR_CHECK(LvmStatus, "LVM_SetVolumeNoSmoothing", "LvmBundle_process") + ALOGV("\tLVM_VOLUME: Disabling Smoothing for first volume change to remove spikes/clicks"); + pContext->pBundledContext->firstVolume = LVM_FALSE; + } +} + //---------------------------------------------------------------------------- // LvmEffect_enable() //---------------------------------------------------------------------------- @@ -814,6 +930,7 @@ int LvmEffect_enable(EffectContext *pContext){ //ALOGV("\tLvmEffect_enable Succesfully called LVM_SetControlParameters\n"); //ALOGV("\tLvmEffect_enable end"); + LvmEffect_limitLevel(pContext); return 0; } @@ -864,6 +981,7 @@ int LvmEffect_disable(EffectContext *pContext){ //ALOGV("\tLvmEffect_disable Succesfully called LVM_SetControlParameters\n"); //ALOGV("\tLvmEffect_disable end"); + LvmEffect_limitLevel(pContext); return 0; } @@ -1099,6 +1217,8 @@ void BassSetStrength(EffectContext *pContext, uint32_t strength){ LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "BassSetStrength") //ALOGV("\tBassSetStrength Succesfully called LVM_SetControlParameters\n"); + + LvmEffect_limitLevel(pContext); } /* end BassSetStrength */ //---------------------------------------------------------------------------- @@ -1159,13 +1279,14 @@ void VirtualizerSetStrength(EffectContext *pContext, uint32_t strength){ /* Virtualizer parameters */ ActiveParams.CS_EffectLevel = (int)((strength*32767)/1000); - //ALOGV("\tVirtualizerSetStrength() (0-1000) -> %d\n", strength ); - //ALOGV("\tVirtualizerSetStrength() (0- 100) -> %d\n", ActiveParams.CS_EffectLevel ); + ALOGV("\tVirtualizerSetStrength() (0-1000) -> %d\n", strength ); + ALOGV("\tVirtualizerSetStrength() (0- 100) -> %d\n", ActiveParams.CS_EffectLevel ); /* Activate the initial settings */ LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "VirtualizerSetStrength") //ALOGV("\tVirtualizerSetStrength Succesfully called LVM_SetControlParameters\n\n"); + LvmEffect_limitLevel(pContext); } /* end setStrength */ //---------------------------------------------------------------------------- @@ -1236,10 +1357,12 @@ int VirtualizerForceVirtualizationMode(EffectContext *pContext, audio_devices_t bool useVirtualizer = false; if (VirtualizerIsDeviceSupported(forcedDevice) != 0) { - // forced device is not supported, make it behave as a reset of forced mode - forcedDevice = AUDIO_DEVICE_NONE; - // but return an error - status = -EINVAL; + if (forcedDevice != AUDIO_DEVICE_NONE) { + //forced device is not supported, make it behave as a reset of forced mode + forcedDevice = AUDIO_DEVICE_NONE; + // but return an error + status = -EINVAL; + } } if (forcedDevice == AUDIO_DEVICE_NONE) { @@ -1341,104 +1464,6 @@ audio_devices_t VirtualizerGetVirtualizationMode(EffectContext *pContext) { } //---------------------------------------------------------------------------- -// EqualizerLimitBandLevels() -//---------------------------------------------------------------------------- -// Purpose: limit all EQ band gains to a value less than 0 dB while -// preserving the relative band levels. -// -// Inputs: -// pContext: effect engine context -// -// Outputs: -// -//---------------------------------------------------------------------------- -void EqualizerLimitBandLevels(EffectContext *pContext) { - LVM_ControlParams_t ActiveParams; /* Current control Parameters */ - LVM_ReturnStatus_en LvmStatus=LVM_SUCCESS; /* Function call status */ - - /* Get the current settings */ - LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); - LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "EqualizerLimitBandLevels") - //ALOGV("\tEqualizerLimitBandLevels Succesfully returned from LVM_GetControlParameters\n"); - //ALOGV("\tEqualizerLimitBandLevels just Got -> %d\n", - // ActiveParams.pEQNB_BandDefinition[band].Gain); - - // Apply a volume correction to avoid clipping in the EQ based on 2 factors: - // - the maximum EQ band gain: the volume correction is such that the total of volume + max - // band gain is <= 0 dB - // - the average gain in all bands weighted by their proximity to max gain band. - int maxGain = 0; - int avgGain = 0; - int avgCount = 0; - for (int i = 0; i < FIVEBAND_NUMBANDS; i++) { - if (pContext->pBundledContext->bandGaindB[i] >= maxGain) { - int tmpMaxGain = pContext->pBundledContext->bandGaindB[i]; - int tmpAvgGain = 0; - int tmpAvgCount = 0; - for (int j = 0; j < FIVEBAND_NUMBANDS; j++) { - int gain = pContext->pBundledContext->bandGaindB[j]; - // skip current band and gains < 0 dB - if (j == i || gain < 0) - continue; - // no need to continue if one band not processed yet has a higher gain than current - // max - if (gain > tmpMaxGain) { - // force skipping "if (tmpAvgGain >= avgGain)" below as tmpAvgGain is not - // meaningful in this case - tmpAvgGain = -1; - break; - } - - int weight = 1; - if (j < (i + 2) && j > (i - 2)) - weight = 4; - tmpAvgGain += weight * gain; - tmpAvgCount += weight; - } - if (tmpAvgGain >= avgGain) { - maxGain = tmpMaxGain; - avgGain = tmpAvgGain; - avgCount = tmpAvgCount; - } - } - ActiveParams.pEQNB_BandDefinition[i].Frequency = EQNB_5BandPresetsFrequencies[i]; - ActiveParams.pEQNB_BandDefinition[i].QFactor = EQNB_5BandPresetsQFactors[i]; - ActiveParams.pEQNB_BandDefinition[i].Gain = pContext->pBundledContext->bandGaindB[i]; - } - - int gainCorrection = 0; - if (maxGain + pContext->pBundledContext->volume > 0) { - gainCorrection = maxGain + pContext->pBundledContext->volume; - } - if (avgCount) { - gainCorrection += avgGain/avgCount; - } - - ALOGV("EqualizerLimitBandLevels() gainCorrection %d maxGain %d avgGain %d avgCount %d", - gainCorrection, maxGain, avgGain, avgCount); - - ActiveParams.VC_EffectLevel = pContext->pBundledContext->volume - gainCorrection; - if (ActiveParams.VC_EffectLevel < -96) { - ActiveParams.VC_EffectLevel = -96; - } - - /* Activate the initial settings */ - LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); - LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "EqualizerLimitBandLevels") - //ALOGV("\tEqualizerLimitBandLevels just Set -> %d\n", - // ActiveParams.pEQNB_BandDefinition[band].Gain); - - //ALOGV("\tEqualizerLimitBandLevels just set (-96dB -> 0dB) -> %d\n",ActiveParams.VC_EffectLevel ); - if(pContext->pBundledContext->firstVolume == LVM_TRUE){ - LvmStatus = LVM_SetVolumeNoSmoothing(pContext->pBundledContext->hInstance, &ActiveParams); - LVM_ERROR_CHECK(LvmStatus, "LVM_SetVolumeNoSmoothing", "LvmBundle_process") - ALOGV("\tLVM_VOLUME: Disabling Smoothing for first volume change to remove spikes/clicks"); - pContext->pBundledContext->firstVolume = LVM_FALSE; - } -} - - -//---------------------------------------------------------------------------- // EqualizerGetBandLevel() //---------------------------------------------------------------------------- // Purpose: Retrieve the gain currently being used for the band passed in @@ -1480,7 +1505,8 @@ void EqualizerSetBandLevel(EffectContext *pContext, int band, short Gain){ pContext->pBundledContext->bandGaindB[band] = gainRounded; pContext->pBundledContext->CurPreset = PRESET_CUSTOM; - EqualizerLimitBandLevels(pContext); + EqualizerUpdateActiveParams(pContext); + LvmEffect_limitLevel(pContext); } //---------------------------------------------------------------------------- @@ -1615,7 +1641,8 @@ void EqualizerSetPreset(EffectContext *pContext, int preset){ EQNB_5BandSoftPresets[i + preset * FIVEBAND_NUMBANDS]; } - EqualizerLimitBandLevels(pContext); + EqualizerUpdateActiveParams(pContext); + LvmEffect_limitLevel(pContext); //ALOGV("\tEqualizerSetPreset Succesfully called LVM_SetControlParameters\n"); return; @@ -1670,7 +1697,7 @@ int VolumeSetVolumeLevel(EffectContext *pContext, int16_t level){ pContext->pBundledContext->volume = level / 100; } - EqualizerLimitBandLevels(pContext); + LvmEffect_limitLevel(pContext); return 0; } /* end VolumeSetVolumeLevel */ @@ -1719,7 +1746,7 @@ int32_t VolumeSetMute(EffectContext *pContext, uint32_t mute){ pContext->pBundledContext->volume = pContext->pBundledContext->levelSaved; } - EqualizerLimitBandLevels(pContext); + LvmEffect_limitLevel(pContext); return 0; } /* end setMute */ diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h index 420f973..b3071f4 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h @@ -142,6 +142,7 @@ static const uint32_t bandFreqRange[FIVEBAND_NUMBANDS][2] = { {1800001, 7000000}, {7000001, 1}}; +//Note: If these frequencies change, please update LimitLevel values accordingly. static const LVM_UINT16 EQNB_5BandPresetsFrequencies[] = { 60, /* Frequencies in Hz */ 230, @@ -192,6 +193,20 @@ static const PresetConfig gEqualizerPresets[] = { {"Pop"}, {"Rock"}}; +/* The following tables have been computed using the actual levels measured by the output of + * white noise or pink noise (IEC268-1) for the EQ and BassBoost Effects. These are estimates of + * the actual energy that 'could' be present in the given band. + * If the frequency values in EQNB_5BandPresetsFrequencies change, these values might need to be + * updated. + */ + +static const float LimitLevel_bandEnergyContribution[FIVEBAND_NUMBANDS] = { + 5.0, 6.5, 6.45, 4.8, 1.7 }; + +static const float LimitLevel_bassBoostEnergyContribution = 6.7; + +static const float LimitLevel_virtualizerContribution = 1.9; + #if __cplusplus } // extern "C" #endif diff --git a/media/libeffects/testlibs/Android.mk_ b/media/libeffects/testlibs/Android.mk_ index 672ebba..14c373f 100644 --- a/media/libeffects/testlibs/Android.mk_ +++ b/media/libeffects/testlibs/Android.mk_ @@ -3,24 +3,18 @@ LOCAL_PATH:= $(call my-dir) # Test Reverb library include $(CLEAR_VARS) -LOCAL_SRC_FILES:= \ +LOCAL_SRC_FILES := \ EffectReverb.c.arm \ EffectsMath.c.arm -LOCAL_CFLAGS+= -O2 + +LOCAL_CFLAGS := -O2 LOCAL_SHARED_LIBRARIES := \ - libcutils + libcutils \ + libdl LOCAL_MODULE_RELATIVE_PATH := soundfx -LOCAL_MODULE:= libreverbtest - -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif +LOCAL_MODULE := libreverbtest LOCAL_C_INCLUDES := \ $(call include-path-for, audio-effects) \ @@ -33,7 +27,7 @@ include $(BUILD_SHARED_LIBRARY) # Test Equalizer library include $(CLEAR_VARS) -LOCAL_SRC_FILES:= \ +LOCAL_SRC_FILES := \ EffectsMath.c.arm \ EffectEqualizer.cpp \ AudioBiquadFilter.cpp.arm \ @@ -42,21 +36,14 @@ LOCAL_SRC_FILES:= \ AudioShelvingFilter.cpp.arm \ AudioEqualizer.cpp.arm -LOCAL_CFLAGS+= -O2 +LOCAL_CFLAGS := -O2 LOCAL_SHARED_LIBRARIES := \ - libcutils + libcutils \ + libdl LOCAL_MODULE_RELATIVE_PATH := soundfx -LOCAL_MODULE:= libequalizertest - -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif +LOCAL_MODULE := libequalizertest LOCAL_C_INCLUDES := \ $(call include-path-for, graphics corecg) \ diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index e012116..5378bf2 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -42,6 +42,7 @@ LOCAL_SRC_FILES:= \ mediarecorder.cpp \ IMediaMetadataRetriever.cpp \ mediametadataretriever.cpp \ + MidiIoWrapper.cpp \ ToneGenerator.cpp \ JetPlayer.cpp \ IOMX.cpp \ @@ -57,42 +58,26 @@ LOCAL_SRC_FILES:= \ AudioEffect.cpp \ Visualizer.cpp \ MemoryLeakTrackUtil.cpp \ - SoundPool.cpp \ - SoundPoolThread.cpp \ - StringArray.cpp - -LOCAL_SRC_FILES += ../libnbaio/roundup.c + StringArray.cpp \ + AudioPolicy.cpp LOCAL_SHARED_LIBRARIES := \ libui liblog libcutils libutils libbinder libsonivox libicuuc libicui18n libexpat \ libcamera_client libstagefright_foundation \ libgui libdl libaudioutils libnbaio -LOCAL_STATIC_LIBRARIES += libinstantssq - LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper LOCAL_MODULE:= libmedia +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + LOCAL_C_INCLUDES := \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/frameworks/av/include/media/ \ $(TOP)/frameworks/av/media/libstagefright \ - $(TOP)/external/icu/icu4c/source/common \ - $(TOP)/external/icu/icu4c/source/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 0d5d7e4..af103c1 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -150,7 +150,7 @@ status_t AudioEffect::set(const effect_uuid_t *type, int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int); mCblk->buffer = (uint8_t *)mCblk + bufOffset; - iEffect->asBinder()->linkToDeath(mIEffectClient); + IInterface::asBinder(iEffect)->linkToDeath(mIEffectClient); mClientPid = IPCThreadState::self()->getCallingPid(); ALOGV("set() %p OK effect: %s id: %d status %d enabled %d pid %d", this, mDescriptor.name, mId, mStatus, mEnabled, mClientPid); @@ -173,7 +173,7 @@ AudioEffect::~AudioEffect() } if (mIEffect != NULL) { mIEffect->disconnect(); - mIEffect->asBinder()->unlinkToDeath(mIEffectClient); + IInterface::asBinder(mIEffect)->unlinkToDeath(mIEffectClient); } IPCThreadState::self()->flushCommands(); } diff --git a/media/libmedia/AudioPolicy.cpp b/media/libmedia/AudioPolicy.cpp new file mode 100644 index 0000000..d2d0971 --- /dev/null +++ b/media/libmedia/AudioPolicy.cpp @@ -0,0 +1,115 @@ +/* + * 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 "AudioPolicy" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> +#include <media/AudioPolicy.h> + +namespace android { + +// +// AttributeMatchCriterion implementation +// +AttributeMatchCriterion::AttributeMatchCriterion(audio_usage_t usage, + audio_source_t source, + uint32_t rule) +: mRule(rule) +{ + if (mRule == RULE_MATCH_ATTRIBUTE_USAGE || + mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE) { + mAttr.mUsage = usage; + } else { + mAttr.mSource = source; + } +} + +status_t AttributeMatchCriterion::readFromParcel(Parcel *parcel) +{ + mRule = parcel->readInt32(); + if (mRule == RULE_MATCH_ATTRIBUTE_USAGE || + mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE) { + mAttr.mUsage = (audio_usage_t)parcel->readInt32(); + } else { + mAttr.mSource = (audio_source_t)parcel->readInt32(); + } + return NO_ERROR; +} + +status_t AttributeMatchCriterion::writeToParcel(Parcel *parcel) const +{ + parcel->writeInt32(mRule); + parcel->writeInt32(mAttr.mUsage); + return NO_ERROR; +} + +// +// AudioMix implementation +// + +status_t AudioMix::readFromParcel(Parcel *parcel) +{ + mMixType = parcel->readInt32(); + mFormat.sample_rate = (uint32_t)parcel->readInt32(); + mFormat.channel_mask = (audio_channel_mask_t)parcel->readInt32(); + mFormat.format = (audio_format_t)parcel->readInt32(); + mRouteFlags = parcel->readInt32(); + mRegistrationId = parcel->readString8(); + size_t size = (size_t)parcel->readInt32(); + if (size > MAX_CRITERIA_PER_MIX) { + size = MAX_CRITERIA_PER_MIX; + } + for (size_t i = 0; i < size; i++) { + AttributeMatchCriterion criterion; + if (criterion.readFromParcel(parcel) == NO_ERROR) { + mCriteria.add(criterion); + } + } + return NO_ERROR; +} + +status_t AudioMix::writeToParcel(Parcel *parcel) const +{ + parcel->writeInt32(mMixType); + parcel->writeInt32(mFormat.sample_rate); + parcel->writeInt32(mFormat.channel_mask); + parcel->writeInt32(mFormat.format); + parcel->writeInt32(mRouteFlags); + parcel->writeString8(mRegistrationId); + size_t size = mCriteria.size(); + if (size > MAX_CRITERIA_PER_MIX) { + size = MAX_CRITERIA_PER_MIX; + } + size_t sizePosition = parcel->dataPosition(); + parcel->writeInt32(size); + size_t finalSize = size; + for (size_t i = 0; i < size; i++) { + size_t position = parcel->dataPosition(); + if (mCriteria[i].writeToParcel(parcel) != NO_ERROR) { + parcel->setDataPosition(position); + finalSize--; + } + } + if (size != finalSize) { + size_t position = parcel->dataPosition(); + parcel->setDataPosition(sizePosition); + parcel->writeInt32(finalSize); + parcel->setDataPosition(position); + } + return NO_ERROR; +} + +}; // namespace android diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 9e7ba88..84077ec 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -82,14 +82,16 @@ AudioRecord::AudioRecord( uint32_t notificationFrames, int sessionId, transfer_type transferType, - audio_input_flags_t flags) + audio_input_flags_t flags, + const audio_attributes_t* pAttributes) : mStatus(NO_INIT), mSessionId(AUDIO_SESSION_ALLOCATE), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), mProxy(NULL) { mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user, - notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags); + notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags, + pAttributes); } AudioRecord::~AudioRecord() @@ -105,7 +107,7 @@ AudioRecord::~AudioRecord() mAudioRecordThread->requestExitAndWait(); mAudioRecordThread.clear(); } - mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this); + IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this); mAudioRecord.clear(); mCblkMemory.clear(); mBufferMemory.clear(); @@ -126,7 +128,8 @@ status_t AudioRecord::set( bool threadCanCallJava, int sessionId, transfer_type transferType, - audio_input_flags_t flags) + audio_input_flags_t flags, + const audio_attributes_t* pAttributes) { ALOGV("set(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, " "notificationFrames %u, sessionId %d, transferType %d, flags %#x", @@ -164,11 +167,15 @@ status_t AudioRecord::set( return INVALID_OPERATION; } - // handle default values first. - if (inputSource == AUDIO_SOURCE_DEFAULT) { - inputSource = AUDIO_SOURCE_MIC; + if (pAttributes == NULL) { + memset(&mAttributes, 0, sizeof(audio_attributes_t)); + mAttributes.source = inputSource; + } else { + // stream type shouldn't be looked at, this track has audio attributes + memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t)); + ALOGV("Building AudioRecord with attributes: source=%d flags=0x%x tags=[%s]", + mAttributes.source, mAttributes.flags, mAttributes.tags); } - mInputSource = inputSource; if (sampleRate == 0) { ALOGE("Invalid sample rate %u", sampleRate); @@ -345,6 +352,10 @@ status_t AudioRecord::setMarkerPosition(uint32_t marker) mMarkerPosition = marker; mMarkerReached = false; + sp<AudioRecordThread> t = mAudioRecordThread; + if (t != 0) { + t->wake(); + } return NO_ERROR; } @@ -371,6 +382,10 @@ status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod) mNewPosition = mProxy->getPosition() + updatePeriod; mUpdatePeriod = updatePeriod; + sp<AudioRecordThread> t = mAudioRecordThread; + if (t != 0) { + t->wake(); + } return NO_ERROR; } @@ -444,12 +459,14 @@ status_t AudioRecord::openRecord_l(size_t epoch) } } - audio_io_handle_t input = AudioSystem::getInput(mInputSource, mSampleRate, mFormat, - mChannelMask, mSessionId, mFlags); - if (input == AUDIO_IO_HANDLE_NONE) { + audio_io_handle_t input; + status = AudioSystem::getInputForAttr(&mAttributes, &input, (audio_session_t)mSessionId, + mSampleRate, mFormat, mChannelMask, mFlags); + + if (status != NO_ERROR) { ALOGE("Could not get audio input for record source %d, sample rate %u, format %#x, " "channel mask %#x, session %d, flags %#x", - mInputSource, mSampleRate, mFormat, mChannelMask, mSessionId, mFlags); + mAttributes.source, mSampleRate, mFormat, mChannelMask, mSessionId, mFlags); return BAD_VALUE; } { @@ -516,7 +533,7 @@ status_t AudioRecord::openRecord_l(size_t epoch) // invariant that mAudioRecord != 0 is true only after set() returns successfully if (mAudioRecord != 0) { - mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this); + IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this); mDeathNotifier.clear(); } mAudioRecord = record; @@ -566,7 +583,7 @@ status_t AudioRecord::openRecord_l(size_t epoch) mProxy->setMinimum(mNotificationFramesAct); mDeathNotifier = new DeathNotifier(this); - mAudioRecord->asBinder()->linkToDeath(mDeathNotifier, this); + IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this); return NO_ERROR; } @@ -854,8 +871,11 @@ nsecs_t AudioRecord::processAudioBuffer() if (!markerReached && position < markerPosition) { minFrames = markerPosition - position; } - if (updatePeriod > 0 && updatePeriod < minFrames) { - minFrames = updatePeriod; + if (updatePeriod > 0) { + uint32_t remaining = newPosition - position; + if (remaining < minFrames) { + minFrames = remaining; + } } // If > 0, poll periodically to recover from a stuck server. A good value is 2. @@ -1060,8 +1080,8 @@ bool AudioRecord::AudioRecordThread::threadLoop() case NS_NEVER: return false; case NS_WHENEVER: - // FIXME increase poll interval, or make event-driven - ns = 1000000000LL; + // Event driven: call wake() when callback notifications conditions change. + ns = INT64_MAX; // fall through default: LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %" PRId64, ns); @@ -1094,6 +1114,17 @@ void AudioRecord::AudioRecordThread::resume() } } +void AudioRecord::AudioRecordThread::wake() +{ + AutoMutex _l(mMyLock); + if (!mPaused && mPausedInt && mPausedNs > 0) { + // audio record is active and internally paused with timeout. + mIgnoreNextPausedInt = true; + mPausedInt = false; + mMyCond.signal(); + } +} + void AudioRecord::AudioRecordThread::pauseInternal(nsecs_t ns) { AutoMutex _l(mMyLock); diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index dda3657..f5a5712 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -32,6 +32,9 @@ namespace android { // client singleton for AudioFlinger binder interface Mutex AudioSystem::gLock; +Mutex AudioSystem::gLockCache; +Mutex AudioSystem::gLockAPS; +Mutex AudioSystem::gLockAPC; sp<IAudioFlinger> AudioSystem::gAudioFlinger; sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient; audio_error_callback AudioSystem::gAudioErrorCallback = NULL; @@ -48,33 +51,40 @@ size_t AudioSystem::gInBuffSize = 0; // zero indicates cache is invalid sp<AudioSystem::AudioPortCallback> AudioSystem::gAudioPortCallback; // establish binder interface to AudioFlinger service -const sp<IAudioFlinger>& AudioSystem::get_audio_flinger() -{ - Mutex::Autolock _l(gLock); - if (gAudioFlinger == 0) { - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder; - do { - binder = sm->getService(String16("media.audio_flinger")); - if (binder != 0) - break; - ALOGW("AudioFlinger not published, waiting..."); - usleep(500000); // 0.5 s - } while (true); - if (gAudioFlingerClient == NULL) { - gAudioFlingerClient = new AudioFlingerClient(); - } else { - if (gAudioErrorCallback) { - gAudioErrorCallback(NO_ERROR); +const sp<IAudioFlinger> AudioSystem::get_audio_flinger() +{ + sp<IAudioFlinger> af; + sp<AudioFlingerClient> afc; + { + Mutex::Autolock _l(gLock); + if (gAudioFlinger == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16("media.audio_flinger")); + if (binder != 0) + break; + ALOGW("AudioFlinger not published, waiting..."); + usleep(500000); // 0.5 s + } while (true); + if (gAudioFlingerClient == NULL) { + gAudioFlingerClient = new AudioFlingerClient(); + } else { + if (gAudioErrorCallback) { + gAudioErrorCallback(NO_ERROR); + } } + binder->linkToDeath(gAudioFlingerClient); + gAudioFlinger = interface_cast<IAudioFlinger>(binder); + LOG_ALWAYS_FATAL_IF(gAudioFlinger == 0); + afc = gAudioFlingerClient; } - binder->linkToDeath(gAudioFlingerClient); - gAudioFlinger = interface_cast<IAudioFlinger>(binder); - gAudioFlinger->registerClient(gAudioFlingerClient); + af = gAudioFlinger; } - ALOGE_IF(gAudioFlinger==0, "no AudioFlinger!?"); - - return gAudioFlinger; + if (afc != 0) { + af->registerClient(afc); + } + return af; } /* static */ status_t AudioSystem::checkAudioFlinger() @@ -245,36 +255,23 @@ status_t AudioSystem::getOutputSamplingRate(uint32_t* samplingRate, audio_stream return getSamplingRate(output, samplingRate); } -status_t AudioSystem::getOutputSamplingRateForAttr(uint32_t* samplingRate, - const audio_attributes_t *attr) -{ - if (attr == NULL) { - return BAD_VALUE; - } - audio_io_handle_t output = getOutputForAttr(attr); - if (output == 0) { - return PERMISSION_DENIED; - } - return getSamplingRate(output, samplingRate); -} - status_t AudioSystem::getSamplingRate(audio_io_handle_t output, uint32_t* samplingRate) { - OutputDescriptor *outputDesc; + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + + Mutex::Autolock _l(gLockCache); - gLock.lock(); - outputDesc = AudioSystem::gOutputs.valueFor(output); + OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output); if (outputDesc == NULL) { ALOGV("getOutputSamplingRate() no output descriptor for output %d in gOutputs", output); - gLock.unlock(); - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) return PERMISSION_DENIED; + gLockCache.unlock(); *samplingRate = af->sampleRate(output); + gLockCache.lock(); } else { ALOGV("getOutputSamplingRate() reading from output desc"); *samplingRate = outputDesc->samplingRate; - gLock.unlock(); } if (*samplingRate == 0) { ALOGE("AudioSystem::getSamplingRate failed for output %d", output); @@ -305,18 +302,18 @@ status_t AudioSystem::getOutputFrameCount(size_t* frameCount, audio_stream_type_ status_t AudioSystem::getFrameCount(audio_io_handle_t output, size_t* frameCount) { - OutputDescriptor *outputDesc; + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; - gLock.lock(); - outputDesc = AudioSystem::gOutputs.valueFor(output); + Mutex::Autolock _l(gLockCache); + + OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output); if (outputDesc == NULL) { - gLock.unlock(); - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) return PERMISSION_DENIED; + gLockCache.unlock(); *frameCount = af->frameCount(output); + gLockCache.lock(); } else { *frameCount = outputDesc->frameCount; - gLock.unlock(); } if (*frameCount == 0) { ALOGE("AudioSystem::getFrameCount failed for output %d", output); @@ -347,18 +344,18 @@ status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t st status_t AudioSystem::getLatency(audio_io_handle_t output, uint32_t* latency) { - OutputDescriptor *outputDesc; + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; - gLock.lock(); - outputDesc = AudioSystem::gOutputs.valueFor(output); + Mutex::Autolock _l(gLockCache); + + OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output); if (outputDesc == NULL) { - gLock.unlock(); - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) return PERMISSION_DENIED; + gLockCache.unlock(); *latency = af->latency(output); + gLockCache.lock(); } else { *latency = outputDesc->latency; - gLock.unlock(); } ALOGV("getLatency() output %d, latency %d", output, *latency); @@ -369,24 +366,24 @@ status_t AudioSystem::getLatency(audio_io_handle_t output, status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t* buffSize) { - gLock.lock(); + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + Mutex::Autolock _l(gLockCache); // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values size_t inBuffSize = gInBuffSize; if ((inBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat) || (channelMask != gPrevInChannelMask)) { - gLock.unlock(); - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) { - return PERMISSION_DENIED; - } + gLockCache.unlock(); inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask); + gLockCache.lock(); 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; gPrevInFormat = format; @@ -394,7 +391,6 @@ status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t for gInBuffSize = inBuffSize; } - gLock.unlock(); *buffSize = inBuffSize; return NO_ERROR; @@ -461,14 +457,21 @@ audio_hw_sync_t AudioSystem::getAudioHwSyncForSession(audio_session_t sessionId) void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused) { - Mutex::Autolock _l(AudioSystem::gLock); + audio_error_callback cb = NULL; + { + Mutex::Autolock _l(AudioSystem::gLock); + AudioSystem::gAudioFlinger.clear(); + cb = gAudioErrorCallback; + } - AudioSystem::gAudioFlinger.clear(); - // clear output handles and stream to output map caches - AudioSystem::gOutputs.clear(); + { + // clear output handles and stream to output map caches + Mutex::Autolock _l(gLockCache); + AudioSystem::gOutputs.clear(); + } - if (gAudioErrorCallback) { - gAudioErrorCallback(DEAD_OBJECT); + if (cb) { + cb(DEAD_OBJECT); } ALOGW("AudioFlinger server died!"); } @@ -481,7 +484,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle if (ioHandle == AUDIO_IO_HANDLE_NONE) return; - Mutex::Autolock _l(AudioSystem::gLock); + Mutex::Autolock _l(AudioSystem::gLockCache); switch (event) { case STREAM_CONFIG_CHANGED: @@ -496,8 +499,8 @@ 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 %#x channel mask %#x frameCount %zu " - "latency %d", + ALOGV("ioConfigChanged() new output samplingRate %u, format %#x channel mask %#x " + "frameCount %zu latency %d", outputDesc->samplingRate, outputDesc->format, outputDesc->channelMask, outputDesc->frameCount, outputDesc->latency); } break; @@ -520,8 +523,8 @@ 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 %#x channel mask %#x " - "frameCount %zu latency %d", + ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %#x " + "channel mask %#x frameCount %zu latency %d", ioHandle, desc->samplingRate, desc->format, desc->channelMask, desc->frameCount, desc->latency); OutputDescriptor *outputDesc = gOutputs.valueAt(index); @@ -543,73 +546,66 @@ void AudioSystem::setErrorCallback(audio_error_callback cb) gAudioErrorCallback = cb; } - -bool AudioSystem::routedToA2dpOutput(audio_stream_type_t streamType) -{ - switch (streamType) { - case AUDIO_STREAM_MUSIC: - case AUDIO_STREAM_VOICE_CALL: - case AUDIO_STREAM_BLUETOOTH_SCO: - case AUDIO_STREAM_SYSTEM: - return true; - default: - return false; - } -} - - // client singleton for AudioPolicyService binder interface +// protected by gLockAPS sp<IAudioPolicyService> AudioSystem::gAudioPolicyService; sp<AudioSystem::AudioPolicyServiceClient> AudioSystem::gAudioPolicyServiceClient; // establish binder interface to AudioPolicy service -const sp<IAudioPolicyService>& AudioSystem::get_audio_policy_service() -{ - gLock.lock(); - if (gAudioPolicyService == 0) { - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder; - do { - binder = sm->getService(String16("media.audio_policy")); - if (binder != 0) - break; - ALOGW("AudioPolicyService not published, waiting..."); - usleep(500000); // 0.5 s - } while (true); - if (gAudioPolicyServiceClient == NULL) { - gAudioPolicyServiceClient = new AudioPolicyServiceClient(); +const sp<IAudioPolicyService> AudioSystem::get_audio_policy_service() +{ + sp<IAudioPolicyService> ap; + sp<AudioPolicyServiceClient> apc; + { + Mutex::Autolock _l(gLockAPS); + if (gAudioPolicyService == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16("media.audio_policy")); + if (binder != 0) + break; + ALOGW("AudioPolicyService not published, waiting..."); + usleep(500000); // 0.5 s + } while (true); + if (gAudioPolicyServiceClient == NULL) { + gAudioPolicyServiceClient = new AudioPolicyServiceClient(); + } + binder->linkToDeath(gAudioPolicyServiceClient); + gAudioPolicyService = interface_cast<IAudioPolicyService>(binder); + LOG_ALWAYS_FATAL_IF(gAudioPolicyService == 0); + apc = gAudioPolicyServiceClient; } - binder->linkToDeath(gAudioPolicyServiceClient); - gAudioPolicyService = interface_cast<IAudioPolicyService>(binder); - gLock.unlock(); - // Registering the client takes the AudioPolicyService lock. - // Don't hold the AudioSystem lock at the same time. - gAudioPolicyService->registerClient(gAudioPolicyServiceClient); - } else { - // There exists a benign race condition where gAudioPolicyService - // is set, but gAudioPolicyServiceClient is not yet registered. - gLock.unlock(); + ap = gAudioPolicyService; } - return gAudioPolicyService; + if (apc != 0) { + ap->registerClient(apc); + } + + return ap; } // --------------------------------------------------------------------------- status_t AudioSystem::setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) + const char *device_address, + const char *device_name) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); const char *address = ""; + const char *name = ""; if (aps == 0) return PERMISSION_DENIED; if (device_address != NULL) { address = device_address; } - - return aps->setDeviceConnectionState(device, state, address); + if (device_name != NULL) { + name = device_name; + } + return aps->setDeviceConnectionState(device, state, address, name); } audio_policy_dev_state_t AudioSystem::getDeviceConnectionState(audio_devices_t device, @@ -657,22 +653,26 @@ audio_io_handle_t AudioSystem::getOutput(audio_stream_type_t stream, return aps->getOutput(stream, samplingRate, format, channelMask, flags, offloadInfo); } -audio_io_handle_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) +status_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + 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 (attr == NULL) return 0; const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); - if (aps == 0) return 0; - return aps->getOutputForAttr(attr, samplingRate, format, channelMask, flags, offloadInfo); + if (aps == 0) return NO_INIT; + return aps->getOutputForAttr(attr, output, session, stream, + samplingRate, format, channelMask, + flags, offloadInfo); } status_t AudioSystem::startOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; @@ -681,30 +681,33 @@ status_t AudioSystem::startOutput(audio_io_handle_t output, status_t AudioSystem::stopOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; return aps->stopOutput(output, stream, session); } -void AudioSystem::releaseOutput(audio_io_handle_t output) +void AudioSystem::releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return; - aps->releaseOutput(output); + aps->releaseOutput(output, stream, session); } -audio_io_handle_t AudioSystem::getInput(audio_source_t inputSource, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - int sessionId, - audio_input_flags_t flags) +status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); - if (aps == 0) return 0; - return aps->getInput(inputSource, samplingRate, format, channelMask, sessionId, flags); + if (aps == 0) return NO_INIT; + return aps->getInputForAttr(attr, input, session, samplingRate, format, channelMask, flags); } status_t AudioSystem::startInput(audio_io_handle_t input, @@ -856,9 +859,21 @@ status_t AudioSystem::setLowRamDevice(bool isLowRamDevice) void AudioSystem::clearAudioConfigCache() { - Mutex::Autolock _l(gLock); + // called by restoreTrack_l(), which needs new IAudioFlinger and IAudioPolicyService instances ALOGV("clearAudioConfigCache()"); - gOutputs.clear(); + { + Mutex::Autolock _l(gLockCache); + gOutputs.clear(); + } + { + Mutex::Autolock _l(gLock); + gAudioFlinger.clear(); + } + { + Mutex::Autolock _l(gLockAPS); + gAudioPolicyService.clear(); + } + // Do not clear gAudioPortCallback } bool AudioSystem::isOffloadSupported(const audio_offload_info_t& info) @@ -920,7 +935,7 @@ status_t AudioSystem::setAudioPortConfig(const struct audio_port_config *config) void AudioSystem::setAudioPortCallback(sp<AudioPortCallback> callBack) { - Mutex::Autolock _l(gLock); + Mutex::Autolock _l(gLockAPC); gAudioPortCallback = callBack; } @@ -947,23 +962,34 @@ audio_mode_t AudioSystem::getPhoneState() return aps->getPhoneState(); } +status_t AudioSystem::registerPolicyMixes(Vector<AudioMix> mixes, bool registration) +{ + const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->registerPolicyMixes(mixes, registration); +} // --------------------------------------------------------------------------- void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused) { - Mutex::Autolock _l(gLock); - if (gAudioPortCallback != 0) { - gAudioPortCallback->onServiceDied(); + { + Mutex::Autolock _l(gLockAPC); + if (gAudioPortCallback != 0) { + gAudioPortCallback->onServiceDied(); + } + } + { + Mutex::Autolock _l(gLockAPS); + AudioSystem::gAudioPolicyService.clear(); } - AudioSystem::gAudioPolicyService.clear(); ALOGW("AudioPolicyService server died!"); } void AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate() { - Mutex::Autolock _l(gLock); + Mutex::Autolock _l(gLockAPC); if (gAudioPortCallback != 0) { gAudioPortCallback->onAudioPortListUpdate(); } @@ -971,7 +997,7 @@ void AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate() void AudioSystem::AudioPolicyServiceClient::onAudioPatchListUpdate() { - Mutex::Autolock _l(gLock); + Mutex::Autolock _l(gLockAPC); if (gAudioPortCallback != 0) { gAudioPortCallback->onAudioPatchListUpdate(); } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 082a5e1..c775e7b 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -28,15 +28,21 @@ #include <utils/Log.h> #include <private/media/AudioTrackShared.h> #include <media/IAudioFlinger.h> +#include <media/AudioPolicyHelper.h> #include <media/AudioResamplerPublic.h> #define WAIT_PERIOD_MS 10 #define WAIT_STREAM_END_TIMEOUT_SEC 120 - +static const int kMaxLoopCountNotifications = 32; namespace android { // --------------------------------------------------------------------------- +template <typename T> +const T &min(const T &x, const T &y) { + return x < y ? x : y; +} + static int64_t convertTimespecToUs(const struct timespec &tv) { return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000; @@ -60,12 +66,11 @@ status_t AudioTrack::getMinFrameCount( return BAD_VALUE; } - // FIXME merge with similar code in createTrack_l(), except we're missing - // some information here that is available in createTrack_l(): + // FIXME handle in server, like createTrack_l(), possible missing info: // audio_io_handle_t output // audio_format_t format // audio_channel_mask_t channelMask - // audio_output_flags_t flags + // audio_output_flags_t flags (FAST) uint32_t afSampleRate; status_t status; status = AudioSystem::getOutputSamplingRate(&afSampleRate, streamType); @@ -95,16 +100,16 @@ status_t AudioTrack::getMinFrameCount( minBufCount = 2; } - *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount : - afFrameCount * minBufCount * uint64_t(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. + *frameCount = minBufCount * sourceFramesNeeded(sampleRate, afFrameCount, afSampleRate); + // The formula above should always produce a non-zero value under normal circumstances: + // AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX. + // Return 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", + ALOGE("AudioTrack::getMinFrameCount failed for streamType %d, sampleRate %u", streamType, sampleRate); return BAD_VALUE; } - ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%d, afSampleRate=%d, afLatency=%d", + ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%u, afSampleRate=%u, afLatency=%u", *frameCount, afFrameCount, minBufCount, afSampleRate, afLatency); return NO_ERROR; } @@ -193,7 +198,7 @@ AudioTrack::~AudioTrack() mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } - mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this); + IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this); mAudioTrack.clear(); mCblkMemory.clear(); mSharedBuffer.clear(); @@ -278,42 +283,26 @@ status_t AudioTrack::set( } // handle default values first. - // TODO once AudioPolicyManager fully supports audio_attributes_t, - // remove stream "text-to-speech" redirect - if ((streamType == AUDIO_STREAM_DEFAULT) || (streamType == AUDIO_STREAM_TTS)) { + if (streamType == AUDIO_STREAM_DEFAULT) { streamType = AUDIO_STREAM_MUSIC; } - if (pAttributes == NULL) { - if (uint32_t(streamType) >= AUDIO_STREAM_CNT) { + if (uint32_t(streamType) >= AUDIO_STREAM_PUBLIC_CNT) { ALOGE("Invalid stream type %d", streamType); return BAD_VALUE; } - setAttributesFromStreamType(streamType); mStreamType = streamType; + } else { - if (!isValidAttributes(pAttributes)) { - ALOGE("Invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]", - pAttributes->usage, pAttributes->content_type, pAttributes->flags, - pAttributes->tags); - } // stream type shouldn't be looked at, this track has audio attributes memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t)); - setStreamTypeFromAttributes(mAttributes); ALOGV("Building AudioTrack with attributes: usage=%d content=%d flags=0x%x tags=[%s]", mAttributes.usage, mAttributes.content_type, mAttributes.flags, mAttributes.tags); - } - - status_t status; - if (sampleRate == 0) { - status = AudioSystem::getOutputSamplingRateForAttr(&sampleRate, &mAttributes); - if (status != NO_ERROR) { - ALOGE("Could not get output sample rate for stream type %d; status %d", - mStreamType, status); - return status; + mStreamType = AUDIO_STREAM_DEFAULT; + if ((mAttributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { + flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); } } - mSampleRate = sampleRate; // these below should probably come from the audioFlinger too... if (format == AUDIO_FORMAT_DEFAULT) { @@ -335,12 +324,6 @@ status_t AudioTrack::set( uint32_t channelCount = audio_channel_count_from_out_mask(channelMask); mChannelCount = channelCount; - // AudioFlinger does not currently support 8-bit data in shared memory - if (format == AUDIO_FORMAT_PCM_8_BIT && sharedBuffer != 0) { - ALOGE("8-bit data in shared memory is not supported"); - return BAD_VALUE; - } - // force direct flag if format is not linear PCM // or offload was requested if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) @@ -352,9 +335,10 @@ status_t AudioTrack::set( // FIXME why can't we allow direct AND fast? ((flags | AUDIO_OUTPUT_FLAG_DIRECT) & ~AUDIO_OUTPUT_FLAG_FAST); } - // only allow deep buffering for music stream type - if (mStreamType != AUDIO_STREAM_MUSIC) { - flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER); + + // force direct flag if HW A/V sync requested + if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) { + flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT); } if (flags & AUDIO_OUTPUT_FLAG_DIRECT) { @@ -363,16 +347,19 @@ status_t AudioTrack::set( } else { mFrameSize = sizeof(uint8_t); } - mFrameSizeAF = mFrameSize; } else { ALOG_ASSERT(audio_is_linear_pcm(format)); mFrameSize = channelCount * audio_bytes_per_sample(format); - mFrameSizeAF = channelCount * audio_bytes_per_sample( - format == AUDIO_FORMAT_PCM_8_BIT ? AUDIO_FORMAT_PCM_16_BIT : format); // createTrack will return an error if PCM format is not supported by server, // so no need to check for specific PCM formats here } + // sampling rate must be specified for direct outputs + if (sampleRate == 0 && (flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) { + return BAD_VALUE; + } + mSampleRate = sampleRate; + // 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 @@ -390,7 +377,11 @@ status_t AudioTrack::set( mReqFrameCount = frameCount; mNotificationFramesReq = notificationFrames; mNotificationFramesAct = 0; - mSessionId = sessionId; + if (sessionId == AUDIO_SESSION_ALLOCATE) { + mSessionId = AudioSystem::newAudioUniqueId(); + } else { + mSessionId = sessionId; + } int callingpid = IPCThreadState::self()->getCallingPid(); int mypid = getpid(); if (uid == -1 || (callingpid != mypid)) { @@ -413,7 +404,7 @@ status_t AudioTrack::set( } // create the IAudioTrack - status = createTrack_l(); + status_t status = createTrack_l(); if (status != NO_ERROR) { if (mAudioTrackThread != 0) { @@ -427,7 +418,10 @@ status_t AudioTrack::set( mStatus = NO_ERROR; mState = STATE_STOPPED; mUserData = user; - mLoopPeriod = 0; + mLoopCount = 0; + mLoopStart = 0; + mLoopEnd = 0; + mLoopCountNotified = 0; mMarkerPosition = 0; mMarkerReached = false; mNewPosition = 0; @@ -538,14 +532,12 @@ void AudioTrack::stop() // the playback head position will reset to 0, so if a marker is set, we need // to activate it again mMarkerReached = false; -#if 0 - // Force flush if a shared buffer is used otherwise audioflinger - // will not stop before end of buffer is reached. - // It may be needed to make sure that we stop playback, likely in case looping is on. + if (mSharedBuffer != 0) { - flush_l(); + // clear buffer position and loop count. + mStaticProxy->setBufferPositionAndLoop(0 /* position */, + 0 /* loopStart */, 0 /* loopEnd */, 0 /* loopCount */); } -#endif sp<AudioTrackThread> t = mAudioTrackThread; if (t != 0) { @@ -680,15 +672,18 @@ status_t AudioTrack::setSampleRate(uint32_t rate) return INVALID_OPERATION; } + AutoMutex lock(mLock); + if (mOutput == AUDIO_IO_HANDLE_NONE) { + return NO_INIT; + } uint32_t afSamplingRate; - if (AudioSystem::getOutputSamplingRateForAttr(&afSamplingRate, &mAttributes) != NO_ERROR) { + if (AudioSystem::getSamplingRate(mOutput, &afSamplingRate) != NO_ERROR) { return NO_INIT; } if (rate == 0 || rate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) { return BAD_VALUE; } - AutoMutex lock(mLock); mSampleRate = rate; mProxy->setSampleRate(rate); @@ -744,11 +739,15 @@ status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount void AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount) { - // FIXME If setting a loop also sets position to start of loop, then - // this is correct. Otherwise it should be removed. - mNewPosition = updateAndGetPosition_l() + mUpdatePeriod; - mLoopPeriod = loopCount != 0 ? loopEnd - loopStart : 0; + // We do not update the periodic notification point. + // mNewPosition = updateAndGetPosition_l() + mUpdatePeriod; + mLoopCount = loopCount; + mLoopEnd = loopEnd; + mLoopStart = loopStart; + mLoopCountNotified = loopCount; mStaticProxy->setLoop(loopStart, loopEnd, loopCount); + + // Waking the AudioTrackThread is not needed as this cannot be called when active. } status_t AudioTrack::setMarkerPosition(uint32_t marker) @@ -762,6 +761,10 @@ status_t AudioTrack::setMarkerPosition(uint32_t marker) mMarkerPosition = marker; mMarkerReached = false; + sp<AudioTrackThread> t = mAudioTrackThread; + if (t != 0) { + t->wake(); + } return NO_ERROR; } @@ -791,6 +794,10 @@ status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) mNewPosition = updateAndGetPosition_l() + updatePeriod; mUpdatePeriod = updatePeriod; + sp<AudioTrackThread> t = mAudioTrackThread; + if (t != 0) { + t->wake(); + } return NO_ERROR; } @@ -828,12 +835,11 @@ status_t AudioTrack::setPosition(uint32_t position) if (mState == STATE_ACTIVE) { return INVALID_OPERATION; } + // After setting the position, use full update period before notification. mNewPosition = updateAndGetPosition_l() + mUpdatePeriod; - mLoopPeriod = 0; - // FIXME Check whether loops and setting position are incompatible in old code. - // If we use setLoop for both purposes we lose the capability to set the position while looping. - mStaticProxy->setLoop(position, mFrameCount, 0); + mStaticProxy->setBufferPosition(position); + // Waking the AudioTrackThread is not needed as this cannot be called when active. return NO_ERROR; } @@ -861,6 +867,10 @@ status_t AudioTrack::getPosition(uint32_t *position) // due to hardware latency. We leave this behavior for now. *position = dspFrames; } else { + if (mCblk->mFlags & CBLK_INVALID) { + restoreTrack_l("getPosition"); + } + // IAudioTrack::stop() isn't synchronous; we don't know when presentation completes *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ? 0 : updateAndGetPosition_l(); @@ -894,10 +904,18 @@ status_t AudioTrack::reload() return INVALID_OPERATION; } mNewPosition = mUpdatePeriod; - mLoopPeriod = 0; - // FIXME The new code cannot reload while keeping a loop specified. - // Need to check how the old code handled this, and whether it's a significant change. - mStaticProxy->setLoop(0, mFrameCount, 0); + (void) updateAndGetPosition_l(); + mPosition = 0; +#if 0 + // The documentation is not clear on the behavior of reload() and the restoration + // of loop count. Historically we have not restored loop count, start, end, + // but it makes sense if one desires to repeat playing a particular sound. + if (mLoopCount != 0) { + mLoopCountNotified = mLoopCount; + mStaticProxy->setLoop(mLoopStart, mLoopEnd, mLoopCount); + } +#endif + mStaticProxy->setBufferPosition(0); return NO_ERROR; } @@ -917,24 +935,38 @@ status_t AudioTrack::attachAuxEffect(int effectId) return status; } +audio_stream_type_t AudioTrack::streamType() const +{ + if (mStreamType == AUDIO_STREAM_DEFAULT) { + return audio_attributes_to_stream_type(&mAttributes); + } + return mStreamType; +} + // ------------------------------------------------------------------------- // must be called with mLock held status_t AudioTrack::createTrack_l() { - status_t status; const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); if (audioFlinger == 0) { ALOGE("Could not get audioflinger"); return NO_INIT; } - audio_io_handle_t output = AudioSystem::getOutputForAttr(&mAttributes, mSampleRate, mFormat, - mChannelMask, mFlags, mOffloadInfo); - if (output == AUDIO_IO_HANDLE_NONE) { + audio_io_handle_t output; + audio_stream_type_t streamType = mStreamType; + audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL; + status_t status = AudioSystem::getOutputForAttr(attr, &output, + (audio_session_t)mSessionId, &streamType, + mSampleRate, mFormat, mChannelMask, + mFlags, mOffloadInfo); + + + if (status != NO_ERROR || output == AUDIO_IO_HANDLE_NONE) { ALOGE("Could not get audio output for stream type %d, usage %d, sample rate %u, format %#x," " channel mask %#x, flags %#x", - mStreamType, mAttributes.usage, mSampleRate, mFormat, mChannelMask, mFlags); + streamType, mAttributes.usage, mSampleRate, mFormat, mChannelMask, mFlags); return BAD_VALUE; } { @@ -963,7 +995,9 @@ status_t AudioTrack::createTrack_l() ALOGE("getSamplingRate(output=%d) status %d", output, status); goto release; } - + if (mSampleRate == 0) { + mSampleRate = afSampleRate; + } // Client decides whether the track is TIMED (see below), but can only express a preference // for FAST. Server will perform additional tests. if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) && !(( @@ -971,7 +1005,9 @@ status_t AudioTrack::createTrack_l() // use case 1: shared buffer (mSharedBuffer != 0) || // use case 2: callback transfer mode - (mTransfer == TRANSFER_CALLBACK)) && + (mTransfer == TRANSFER_CALLBACK) || + // use case 3: obtain/release mode + (mTransfer == TRANSFER_OBTAIN)) && // matching sample rate (mSampleRate == afSampleRate))) { ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client"); @@ -983,11 +1019,9 @@ status_t AudioTrack::createTrack_l() // 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 - // n = 2 normal track, no sample rate conversion - // n = 3 normal track, with sample rate conversion - // (pessimistic; some non-1:1 conversion ratios don't actually need triple-buffering) - // n > 3 very high latency or very small notification interval; nBuffering is ignored - const uint32_t nBuffering = (mSampleRate == afSampleRate) ? 2 : 3; + // n = 2 normal track, (including those with sample rate conversion) + // n >= 3 very high latency or very small notification interval (unused). + const uint32_t nBuffering = 2; mNotificationFramesAct = mNotificationFramesReq; @@ -1004,12 +1038,12 @@ status_t AudioTrack::createTrack_l() mNotificationFramesAct = frameCount; } } 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 = audio_bytes_per_sample( - mFormat == AUDIO_FORMAT_PCM_8_BIT ? AUDIO_FORMAT_PCM_16_BIT : mFormat); + // FIXME: Ensure client side memory buffers need + // not have additional alignment beyond sample + // (e.g. 16 bit stereo accessed as 32 bit frame). + size_t alignment = audio_bytes_per_sample(mFormat); if (alignment & 1) { + // for AUDIO_FORMAT_PCM_24_BIT_PACKED (not exposed through Java). alignment = 1; } if (mChannelCount > 1) { @@ -1027,40 +1061,10 @@ status_t AudioTrack::createTrack_l() // there's no frameCount parameter. // But when initializing a shared buffer AudioTrack via set(), // there _is_ a frameCount parameter. We silently ignore it. - frameCount = mSharedBuffer->size() / mFrameSizeAF; - - } else if (!(mFlags & AUDIO_OUTPUT_FLAG_FAST)) { - - // FIXME move these calculations and associated checks to server - - // Ensure that buffer depth covers at least audio hardware latency - uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); - ALOGV("afFrameCount=%zu, minBufCount=%d, afSampleRate=%u, afLatency=%d", - afFrameCount, minBufCount, afSampleRate, afLatency); - if (minBufCount <= nBuffering) { - minBufCount = nBuffering; - } - - size_t minFrameCount = afFrameCount * minBufCount * uint64_t(mSampleRate) / afSampleRate; - ALOGV("minFrameCount: %zu, afFrameCount=%zu, minBufCount=%d, sampleRate=%u, afSampleRate=%u" - ", afLatency=%d", - minFrameCount, afFrameCount, minBufCount, mSampleRate, afSampleRate, afLatency); - - if (frameCount == 0) { - frameCount = minFrameCount; - } else if (frameCount < minFrameCount) { - // not ALOGW because it happens all the time when playing key clicks over A2DP - ALOGV("Minimum buffer size corrected from %zu to %zu", - frameCount, minFrameCount); - frameCount = minFrameCount; - } - // Make sure that application is notified with sufficient margin before underrun - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) { - mNotificationFramesAct = frameCount/nBuffering; - } - + frameCount = mSharedBuffer->size() / mFrameSize; } else { - // For fast tracks, the frame count calculations and checks are done by server + // For fast and normal streaming tracks, + // the frame count calculations and checks are done by server } IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT; @@ -1086,12 +1090,9 @@ status_t AudioTrack::createTrack_l() 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, + sp<IAudioTrack> track = audioFlinger->createTrack(streamType, mSampleRate, - // AudioFlinger only sees 16-bit PCM - mFormat == AUDIO_FORMAT_PCM_8_BIT && - !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT) ? - AUDIO_FORMAT_PCM_16_BIT : mFormat, + mFormat, mChannelMask, &temp, &trackFlags, @@ -1123,7 +1124,7 @@ status_t AudioTrack::createTrack_l() } // invariant that mAudioTrack != 0 is true only after set() returns successfully if (mAudioTrack != 0) { - mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this); + IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this); mDeathNotifier.clear(); } mAudioTrack = track; @@ -1146,23 +1147,10 @@ status_t AudioTrack::createTrack_l() if (trackFlags & IAudioFlinger::TRACK_FAST) { ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %zu", frameCount); mAwaitBoost = true; - if (mSharedBuffer == 0) { - // Theoretically double-buffering is not required for fast tracks, - // due to tighter scheduling. But in practice, to accommodate kernels with - // scheduling jitter, and apps with computation jitter, we use double-buffering. - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) { - mNotificationFramesAct = frameCount/nBuffering; - } - } } else { ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu", frameCount); // once denied, do not request again if IAudioTrack is re-created mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST); - if (mSharedBuffer == 0) { - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) { - mNotificationFramesAct = frameCount/nBuffering; - } - } } } if (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { @@ -1185,6 +1173,16 @@ status_t AudioTrack::createTrack_l() //return NO_INIT; } } + // Make sure that application is notified with sufficient margin before underrun + if (mSharedBuffer == 0 && audio_is_linear_pcm(mFormat)) { + // 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 + // for fast tracks just like normal streaming tracks. + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount / nBuffering) { + mNotificationFramesAct = frameCount / nBuffering; + } + } // We retain a copy of the I/O handle, but don't own the reference mOutput = output; @@ -1215,31 +1213,35 @@ status_t AudioTrack::createTrack_l() // update proxy if (mSharedBuffer == 0) { mStaticProxy.clear(); - mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF); + mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize); } else { - mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF); + mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize); mProxy = mStaticProxy; } - mProxy->setVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY); + + mProxy->setVolumeLR(gain_minifloat_pack( + gain_from_float(mVolume[AUDIO_INTERLEAVE_LEFT]), + gain_from_float(mVolume[AUDIO_INTERLEAVE_RIGHT]))); + mProxy->setSendLevel(mSendLevel); mProxy->setSampleRate(mSampleRate); mProxy->setMinimum(mNotificationFramesAct); mDeathNotifier = new DeathNotifier(this); - mAudioTrack->asBinder()->linkToDeath(mDeathNotifier, this); + IInterface::asBinder(mAudioTrack)->linkToDeath(mDeathNotifier, this); return NO_ERROR; } release: - AudioSystem::releaseOutput(output); + AudioSystem::releaseOutput(output, streamType, (audio_session_t)mSessionId); if (status == NO_ERROR) { status = NO_INIT; } return status; } -status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) +status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig) { if (audioBuffer == NULL) { return BAD_VALUE; @@ -1266,7 +1268,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) ALOGE("%s invalid waitCount %d", __func__, waitCount); requested = NULL; } - return obtainBuffer(audioBuffer, requested); + return obtainBuffer(audioBuffer, requested, NULL /*elapsed*/, nonContig); } status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested, @@ -1333,7 +1335,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re } while ((status == DEAD_OBJECT) && (tryCounter-- > 0)); audioBuffer->frameCount = buffer.mFrameCount; - audioBuffer->size = buffer.mFrameCount * mFrameSizeAF; + audioBuffer->size = buffer.mFrameCount * mFrameSize; audioBuffer->raw = buffer.mRaw; if (nonContig != NULL) { *nonContig = buffer.mNonContig; @@ -1341,13 +1343,14 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re return status; } -void AudioTrack::releaseBuffer(Buffer* audioBuffer) +void AudioTrack::releaseBuffer(const Buffer* audioBuffer) { + // FIXME add error checking on mode, by adding an internal version if (mTransfer == TRANSFER_SHARED) { return; } - size_t stepCount = audioBuffer->size / mFrameSizeAF; + size_t stepCount = audioBuffer->size / mFrameSize; if (stepCount == 0) { return; } @@ -1413,14 +1416,8 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking) } size_t toWrite; - if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { - // Divide capacity by 2 to take expansion into account - toWrite = audioBuffer.size >> 1; - memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) buffer, toWrite); - } else { - toWrite = audioBuffer.size; - memcpy(audioBuffer.i8, buffer, toWrite); - } + toWrite = audioBuffer.size; + memcpy(audioBuffer.i8, buffer, toWrite); buffer = ((const char *) buffer) + toWrite; userSize -= toWrite; written += toWrite; @@ -1540,9 +1537,8 @@ nsecs_t AudioTrack::processAudioBuffer() // that the upper layers can recreate the track if (!isOffloadedOrDirect_l() || (mSequence == mObservedSequence)) { status_t status = restoreTrack_l("processAudioBuffer"); - mLock.unlock(); - // Run again immediately, but with a new IAudioTrack - return 0; + // after restoration, continue below to make sure that the loop and buffer events + // are notified because they have been cleared from mCblk->mFlags above. } } @@ -1591,7 +1587,6 @@ nsecs_t AudioTrack::processAudioBuffer() } // Cache other fields that will be needed soon - uint32_t loopPeriod = mLoopPeriod; uint32_t sampleRate = mSampleRate; uint32_t notificationFrames = mNotificationFramesAct; if (mRefreshRemaining) { @@ -1603,8 +1598,30 @@ nsecs_t AudioTrack::processAudioBuffer() uint32_t sequence = mSequence; sp<AudioTrackClientProxy> proxy = mProxy; + // Determine the number of new loop callback(s) that will be needed, while locked. + int loopCountNotifications = 0; + uint32_t loopPeriod = 0; // time in frames for next EVENT_LOOP_END or EVENT_BUFFER_END + + if (mLoopCount > 0) { + int loopCount; + size_t bufferPosition; + mStaticProxy->getBufferPositionAndLoopCount(&bufferPosition, &loopCount); + loopPeriod = ((loopCount > 0) ? mLoopEnd : mFrameCount) - bufferPosition; + loopCountNotifications = min(mLoopCountNotified - loopCount, kMaxLoopCountNotifications); + mLoopCountNotified = loopCount; // discard any excess notifications + } else if (mLoopCount < 0) { + // FIXME: We're not accurate with notification count and position with infinite looping + // since loopCount from server side will always return -1 (we could decrement it). + size_t bufferPosition = mStaticProxy->getBufferPosition(); + loopCountNotifications = int((flags & (CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL)) != 0); + loopPeriod = mLoopEnd - bufferPosition; + } else if (/* mLoopCount == 0 && */ mSharedBuffer != 0) { + size_t bufferPosition = mStaticProxy->getBufferPosition(); + loopPeriod = mFrameCount - bufferPosition; + } + // These fields don't need to be cached, because they are assigned only by set(): - // mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFrameSizeAF, mFlags + // mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFlags // mFlags is also assigned by createTrack_l(), but not the bit we care about. mLock.unlock(); @@ -1643,10 +1660,9 @@ nsecs_t AudioTrack::processAudioBuffer() if (newUnderrun) { mCbf(EVENT_UNDERRUN, mUserData, NULL); } - // FIXME we will miss loops if loop cycle was signaled several times since last call - // to processAudioBuffer() - if (flags & (CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL)) { + while (loopCountNotifications > 0) { mCbf(EVENT_LOOP_END, mUserData, NULL); + --loopCountNotifications; } if (flags & CBLK_BUFFER_END) { mCbf(EVENT_BUFFER_END, mUserData, NULL); @@ -1682,10 +1698,11 @@ nsecs_t AudioTrack::processAudioBuffer() minFrames = markerPosition - position; } if (loopPeriod > 0 && loopPeriod < minFrames) { + // loopPeriod is already adjusted for actual position. minFrames = loopPeriod; } - if (updatePeriod > 0 && updatePeriod < minFrames) { - minFrames = updatePeriod; + if (updatePeriod > 0) { + minFrames = min(minFrames, uint32_t(newPosition - position)); } // If > 0, poll periodically to recover from a stuck server. A good value is 2. @@ -1748,13 +1765,6 @@ nsecs_t AudioTrack::processAudioBuffer() } } - // Divide buffer size by 2 to take into account the expansion - // due to 8 to 16 bit conversion: the callback must fill only half - // of the destination buffer - if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { - audioBuffer.size >>= 1; - } - size_t reqSize = audioBuffer.size; mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); size_t writtenSize = audioBuffer.size; @@ -1774,13 +1784,7 @@ nsecs_t AudioTrack::processAudioBuffer() return WAIT_PERIOD_MS * 1000000LL; } - if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { - // 8 to 16 bit conversion, note that source and destination are the same address - memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) audioBuffer.i8, writtenSize); - audioBuffer.size <<= 1; - } - - size_t releasedFrames = audioBuffer.size / mFrameSizeAF; + size_t releasedFrames = audioBuffer.size / mFrameSize; audioBuffer.frameCount = releasedFrames; mRemainingFrames -= releasedFrames; if (misalignment >= releasedFrames) { @@ -1828,7 +1832,7 @@ status_t AudioTrack::restoreTrack_l(const char *from) status_t result; // refresh the audio configuration cache in this process to make sure we get new - // output parameters in createTrack_l() + // output parameters and new IAudioFlinger in createTrack_l() AudioSystem::clearAudioConfigCache(); if (isOffloadedOrDirect_l()) { @@ -1837,7 +1841,11 @@ status_t AudioTrack::restoreTrack_l(const char *from) } // save the old static buffer position - size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0; + size_t bufferPosition = 0; + int loopCount = 0; + if (mStaticProxy != 0) { + mStaticProxy->getBufferPositionAndLoopCount(&bufferPosition, &loopCount); + } // If a new IAudioTrack is successfully created, createTrack_l() will modify the // following member variables: mAudioTrack, mCblkMemory and mCblk. @@ -1846,30 +1854,26 @@ status_t AudioTrack::restoreTrack_l(const char *from) result = createTrack_l(); // take the frames that will be lost by track recreation into account in saved position + // For streaming tracks, this is the amount we obtained from the user/client + // (not the number actually consumed at the server - those are already lost). (void) updateAndGetPosition_l(); - mPosition = mReleased; + if (mStaticProxy != 0) { + mPosition = mReleased; + } if (result == NO_ERROR) { - // continue playback from last known position, but - // don't attempt to restore loop after invalidation; it's difficult and not worthwhile - if (mStaticProxy != NULL) { - mLoopPeriod = 0; - mStaticProxy->setLoop(bufferPosition, mFrameCount, 0); - } - // FIXME How do we simulate the fact that all frames present in the buffer at the time of - // track destruction have been played? This is critical for SoundPool implementation - // This must be broken, and needs to be tested/debugged. -#if 0 - // restore write index and set other indexes to reflect empty buffer status - if (!strcmp(from, "start")) { - // Make sure that a client relying on callback events indicating underrun or - // the actual amount of audio frames played (e.g SoundPool) receives them. - if (mSharedBuffer == 0) { - // restart playback even if buffer is not completely filled. - android_atomic_or(CBLK_FORCEREADY, &mCblk->mFlags); + // Continue playback from last known position and restore loop. + if (mStaticProxy != 0) { + if (loopCount != 0) { + mStaticProxy->setBufferPositionAndLoop(bufferPosition, + mLoopStart, mLoopEnd, loopCount); + } else { + mStaticProxy->setBufferPosition(bufferPosition); + if (bufferPosition == mFrameCount) { + ALOGD("restoring track at end of static buffer"); + } } } -#endif if (mState == STATE_ACTIVE) { result = mAudioTrack->start(); } @@ -1936,6 +1940,10 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) break; } + if (mCblk->mFlags & CBLK_INVALID) { + restoreTrack_l("getTimestamp"); + } + // The presented frame count must always lag behind the consumed frame count. // To avoid a race, read the presented frames first. This ensures that presented <= consumed. status_t status = mAudioTrack->getTimestamp(timestamp); @@ -2066,156 +2074,6 @@ uint32_t AudioTrack::getUnderrunFrames() const return mProxy->getUnderrunFrames(); } -void AudioTrack::setAttributesFromStreamType(audio_stream_type_t streamType) { - mAttributes.flags = 0x0; - - switch(streamType) { - case AUDIO_STREAM_DEFAULT: - case AUDIO_STREAM_MUSIC: - mAttributes.content_type = AUDIO_CONTENT_TYPE_MUSIC; - mAttributes.usage = AUDIO_USAGE_MEDIA; - break; - case AUDIO_STREAM_VOICE_CALL: - mAttributes.content_type = AUDIO_CONTENT_TYPE_SPEECH; - mAttributes.usage = AUDIO_USAGE_VOICE_COMMUNICATION; - break; - case AUDIO_STREAM_ENFORCED_AUDIBLE: - mAttributes.flags |= AUDIO_FLAG_AUDIBILITY_ENFORCED; - // intended fall through, attributes in common with STREAM_SYSTEM - case AUDIO_STREAM_SYSTEM: - mAttributes.content_type = AUDIO_CONTENT_TYPE_SONIFICATION; - mAttributes.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION; - break; - case AUDIO_STREAM_RING: - mAttributes.content_type = AUDIO_CONTENT_TYPE_SONIFICATION; - mAttributes.usage = AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE; - break; - case AUDIO_STREAM_ALARM: - mAttributes.content_type = AUDIO_CONTENT_TYPE_SONIFICATION; - mAttributes.usage = AUDIO_USAGE_ALARM; - break; - case AUDIO_STREAM_NOTIFICATION: - mAttributes.content_type = AUDIO_CONTENT_TYPE_SONIFICATION; - mAttributes.usage = AUDIO_USAGE_NOTIFICATION; - break; - case AUDIO_STREAM_BLUETOOTH_SCO: - mAttributes.content_type = AUDIO_CONTENT_TYPE_SPEECH; - mAttributes.usage = AUDIO_USAGE_VOICE_COMMUNICATION; - mAttributes.flags |= AUDIO_FLAG_SCO; - break; - case AUDIO_STREAM_DTMF: - mAttributes.content_type = AUDIO_CONTENT_TYPE_SONIFICATION; - mAttributes.usage = AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - break; - case AUDIO_STREAM_TTS: - mAttributes.content_type = AUDIO_CONTENT_TYPE_SPEECH; - mAttributes.usage = AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - break; - default: - ALOGE("invalid stream type %d when converting to attributes", streamType); - } -} - -void AudioTrack::setStreamTypeFromAttributes(audio_attributes_t& aa) { - // flags to stream type mapping - if ((aa.flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) { - mStreamType = AUDIO_STREAM_ENFORCED_AUDIBLE; - return; - } - if ((aa.flags & AUDIO_FLAG_SCO) == AUDIO_FLAG_SCO) { - mStreamType = AUDIO_STREAM_BLUETOOTH_SCO; - return; - } - // TODO once AudioPolicyManager fully supports audio_attributes_t, - // remove stream remap, the flag will be enough - if ((aa.flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) { - mStreamType = AUDIO_STREAM_TTS; - return; - } - - // usage to stream type mapping - switch (aa.usage) { - case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: { - // TODO once AudioPolicyManager fully supports audio_attributes_t, - // remove stream change based on stream activity - bool active; - status_t status = AudioSystem::isStreamActive(AUDIO_STREAM_RING, &active, 0); - if (status == NO_ERROR && active == true) { - mStreamType = AUDIO_STREAM_RING; - break; - } - status = AudioSystem::isStreamActive(AUDIO_STREAM_ALARM, &active, 0); - if (status == NO_ERROR && active == true) { - mStreamType = AUDIO_STREAM_ALARM; - break; - } - } /// FALL THROUGH - case AUDIO_USAGE_MEDIA: - case AUDIO_USAGE_GAME: - case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: - mStreamType = AUDIO_STREAM_MUSIC; - return; - case AUDIO_USAGE_ASSISTANCE_SONIFICATION: - mStreamType = AUDIO_STREAM_SYSTEM; - return; - case AUDIO_USAGE_VOICE_COMMUNICATION: - mStreamType = AUDIO_STREAM_VOICE_CALL; - return; - - case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: - mStreamType = AUDIO_STREAM_DTMF; - return; - - case AUDIO_USAGE_ALARM: - mStreamType = AUDIO_STREAM_ALARM; - return; - case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: - mStreamType = AUDIO_STREAM_RING; - return; - - case AUDIO_USAGE_NOTIFICATION: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: - case AUDIO_USAGE_NOTIFICATION_EVENT: - mStreamType = AUDIO_STREAM_NOTIFICATION; - return; - - case AUDIO_USAGE_UNKNOWN: - default: - mStreamType = AUDIO_STREAM_MUSIC; - } -} - -bool AudioTrack::isValidAttributes(const audio_attributes_t *paa) { - // has flags that map to a strategy? - if ((paa->flags & (AUDIO_FLAG_AUDIBILITY_ENFORCED | AUDIO_FLAG_SCO | AUDIO_FLAG_BEACON)) != 0) { - return true; - } - - // has known usage? - switch (paa->usage) { - case AUDIO_USAGE_UNKNOWN: - case AUDIO_USAGE_MEDIA: - case AUDIO_USAGE_VOICE_COMMUNICATION: - case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: - case AUDIO_USAGE_ALARM: - case AUDIO_USAGE_NOTIFICATION: - case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: - case AUDIO_USAGE_NOTIFICATION_EVENT: - case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: - case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: - case AUDIO_USAGE_ASSISTANCE_SONIFICATION: - case AUDIO_USAGE_GAME: - break; - default: - return false; - } - return true; -} // ========================================================================= void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who __unused) @@ -2275,8 +2133,8 @@ bool AudioTrack::AudioTrackThread::threadLoop() case NS_NEVER: return false; case NS_WHENEVER: - // FIXME increase poll interval, or make event-driven - ns = 1000000000LL; + // Event driven: call wake() when callback notifications conditions change. + ns = INT64_MAX; // fall through default: LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %" PRId64, ns); @@ -2309,6 +2167,17 @@ void AudioTrack::AudioTrackThread::resume() } } +void AudioTrack::AudioTrackThread::wake() +{ + AutoMutex _l(mMyLock); + if (!mPaused && mPausedInt && mPausedNs > 0) { + // audio track is active and internally paused with timeout. + mIgnoreNextPausedInt = true; + mPausedInt = false; + mMyCond.signal(); + } +} + void AudioTrack::AudioTrackThread::pauseInternal(nsecs_t ns) { AutoMutex _l(mMyLock); diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 561cb24..6d5f1af 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -25,6 +25,26 @@ namespace android { +// used to clamp a value to size_t. TODO: move to another file. +template <typename T> +size_t clampToSize(T x) { + return sizeof(T) > sizeof(size_t) && x > (T) SIZE_MAX ? SIZE_MAX : x < 0 ? 0 : (size_t) x; +} + +// incrementSequence is used to determine the next sequence value +// for the loop and position sequence counters. It should return +// a value between "other" + 1 and "other" + INT32_MAX, the choice of +// which needs to be the "least recently used" sequence value for "self". +// In general, this means (new_self) returned is max(self, other) + 1. + +static uint32_t incrementSequence(uint32_t self, uint32_t other) { + int32_t diff = self - other; + if (diff >= 0 && diff < INT32_MAX) { + return self + 1; // we're already ahead of other. + } + return other + 1; // we're behind, so move just ahead of other. +} + audio_track_cblk_t::audio_track_cblk_t() : mServer(0), mFutex(0), mMinimum(0), mVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY), mSampleRate(0), mSendLevel(0), mFlags(0) @@ -301,6 +321,7 @@ void ClientProxy::binderDied() { audio_track_cblk_t* cblk = mCblk; if (!(android_atomic_or(CBLK_INVALID, &cblk->mFlags) & CBLK_INVALID)) { + android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex); // it seems that a FUTEX_WAKE_PRIVATE will not wake a FUTEX_WAIT, even within same process (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1); @@ -311,6 +332,7 @@ void ClientProxy::interrupt() { audio_track_cblk_t* cblk = mCblk; if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->mFlags) & CBLK_INTERRUPT)) { + android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex); (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1); } @@ -401,7 +423,6 @@ status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *request goto end; } // check for obtainBuffer interrupted by client - // check for obtainBuffer interrupted by client if (flags & CBLK_INTERRUPT) { ALOGV("waitStreamEndDone() interrupted by client"); status = -EINTR; @@ -477,8 +498,11 @@ end: StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize) : AudioTrackClientProxy(cblk, buffers, frameCount, frameSize), - mMutator(&cblk->u.mStatic.mSingleStateQueue), mBufferPosition(0) + mMutator(&cblk->u.mStatic.mSingleStateQueue), + mPosLoopObserver(&cblk->u.mStatic.mPosLoopQueue) { + memset(&mState, 0, sizeof(mState)); + memset(&mPosLoop, 0, sizeof(mPosLoop)); } void StaticAudioTrackClientProxy::flush() @@ -493,26 +517,72 @@ void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int // FIXME Should return an error status return; } - StaticAudioTrackState newState; - newState.mLoopStart = (uint32_t) loopStart; - newState.mLoopEnd = (uint32_t) loopEnd; - newState.mLoopCount = loopCount; - mBufferPosition = loopStart; - (void) mMutator.push(newState); + mState.mLoopStart = (uint32_t) loopStart; + mState.mLoopEnd = (uint32_t) loopEnd; + mState.mLoopCount = loopCount; + mState.mLoopSequence = incrementSequence(mState.mLoopSequence, mState.mPositionSequence); + // set patch-up variables until the mState is acknowledged by the ServerProxy. + // observed buffer position and loop count will freeze until then to give the + // illusion of a synchronous change. + getBufferPositionAndLoopCount(NULL, NULL); + // preserve behavior to restart at mState.mLoopStart if position exceeds mState.mLoopEnd. + if (mState.mLoopCount != 0 && mPosLoop.mBufferPosition >= mState.mLoopEnd) { + mPosLoop.mBufferPosition = mState.mLoopStart; + } + mPosLoop.mLoopCount = mState.mLoopCount; + (void) mMutator.push(mState); +} + +void StaticAudioTrackClientProxy::setBufferPosition(size_t position) +{ + // This can only happen on a 64-bit client + if (position > UINT32_MAX) { + // FIXME Should return an error status + return; + } + mState.mPosition = (uint32_t) position; + mState.mPositionSequence = incrementSequence(mState.mPositionSequence, mState.mLoopSequence); + // set patch-up variables until the mState is acknowledged by the ServerProxy. + // observed buffer position and loop count will freeze until then to give the + // illusion of a synchronous change. + if (mState.mLoopCount > 0) { // only check if loop count is changing + getBufferPositionAndLoopCount(NULL, NULL); // get last position + } + mPosLoop.mBufferPosition = position; + if (position >= mState.mLoopEnd) { + // no ongoing loop is possible if position is greater than loopEnd. + mPosLoop.mLoopCount = 0; + } + (void) mMutator.push(mState); +} + +void StaticAudioTrackClientProxy::setBufferPositionAndLoop(size_t position, size_t loopStart, + size_t loopEnd, int loopCount) +{ + setLoop(loopStart, loopEnd, loopCount); + setBufferPosition(position); } size_t StaticAudioTrackClientProxy::getBufferPosition() { - size_t bufferPosition; - if (mMutator.ack()) { - bufferPosition = (size_t) mCblk->u.mStatic.mBufferPosition; - if (bufferPosition > mFrameCount) { - bufferPosition = mFrameCount; - } - } else { - bufferPosition = mBufferPosition; + getBufferPositionAndLoopCount(NULL, NULL); + return mPosLoop.mBufferPosition; +} + +void StaticAudioTrackClientProxy::getBufferPositionAndLoopCount( + size_t *position, int *loopCount) +{ + if (mMutator.ack() == StaticAudioTrackSingleStateQueue::SSQ_DONE) { + if (mPosLoopObserver.poll(mPosLoop)) { + ; // a valid mPosLoop should be available if ackDone is true. + } + } + if (position != NULL) { + *position = mPosLoop.mBufferPosition; + } + if (loopCount != NULL) { + *loopCount = mPosLoop.mLoopCount; } - return bufferPosition; } // --------------------------------------------------------------------------- @@ -548,7 +618,8 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush) ssize_t filled = rear - newFront; // Rather than shutting down on a corrupt flush, just treat it as a full flush if (!(0 <= filled && (size_t) filled <= mFrameCount)) { - ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, filled %d=%#x", + ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, " + "filled %d=%#x", mFlush, flush, front, rear, mask, newFront, filled, filled); newFront = rear; } @@ -727,12 +798,12 @@ void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount) StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize) : AudioTrackServerProxy(cblk, buffers, frameCount, frameSize), - mObserver(&cblk->u.mStatic.mSingleStateQueue), mPosition(0), - mEnd(frameCount), mFramesReadyIsCalledByMultipleThreads(false) + mObserver(&cblk->u.mStatic.mSingleStateQueue), + mPosLoopMutator(&cblk->u.mStatic.mPosLoopQueue), + mFramesReadySafe(frameCount), mFramesReady(frameCount), + mFramesReadyIsCalledByMultipleThreads(false) { - mState.mLoopStart = 0; - mState.mLoopEnd = 0; - mState.mLoopCount = 0; + memset(&mState, 0, sizeof(mState)); } void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads() @@ -742,59 +813,104 @@ void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads() size_t StaticAudioTrackServerProxy::framesReady() { - // FIXME - // This is racy if called by normal mixer thread, - // as we're reading 2 independent variables without a lock. - // Can't call mObserver.poll(), as we might be called from wrong thread. - // If looping is enabled, should return a higher number (since includes non-contiguous). - size_t position = mPosition; + // Can't call pollPosition() from multiple threads. if (!mFramesReadyIsCalledByMultipleThreads) { - ssize_t positionOrStatus = pollPosition(); - if (positionOrStatus >= 0) { - position = (size_t) positionOrStatus; - } + (void) pollPosition(); } - size_t end = mEnd; - return position < end ? end - position : 0; + return mFramesReadySafe; } -ssize_t StaticAudioTrackServerProxy::pollPosition() +status_t StaticAudioTrackServerProxy::updateStateWithLoop( + StaticAudioTrackState *localState, const StaticAudioTrackState &update) const { - size_t position = mPosition; - StaticAudioTrackState state; - if (mObserver.poll(state)) { + if (localState->mLoopSequence != update.mLoopSequence) { bool valid = false; - size_t loopStart = state.mLoopStart; - size_t loopEnd = state.mLoopEnd; - if (state.mLoopCount == 0) { - if (loopStart > mFrameCount) { - loopStart = mFrameCount; - } - // ignore loopEnd - mPosition = position = loopStart; - mEnd = mFrameCount; - mState.mLoopCount = 0; + const size_t loopStart = update.mLoopStart; + const size_t loopEnd = update.mLoopEnd; + size_t position = localState->mPosition; + if (update.mLoopCount == 0) { valid = true; - } else { + } else if (update.mLoopCount >= -1) { if (loopStart < loopEnd && loopEnd <= mFrameCount && loopEnd - loopStart >= MIN_LOOP) { - if (!(loopStart <= position && position < loopEnd)) { - mPosition = position = loopStart; + // If the current position is greater than the end of the loop + // we "wrap" to the loop start. This might cause an audible pop. + if (position >= loopEnd) { + position = loopStart; } - mEnd = loopEnd; - mState = state; valid = true; } } - if (!valid) { + if (!valid || position > mFrameCount) { + return NO_INIT; + } + localState->mPosition = position; + localState->mLoopCount = update.mLoopCount; + localState->mLoopEnd = loopEnd; + localState->mLoopStart = loopStart; + localState->mLoopSequence = update.mLoopSequence; + } + return OK; +} + +status_t StaticAudioTrackServerProxy::updateStateWithPosition( + StaticAudioTrackState *localState, const StaticAudioTrackState &update) const +{ + if (localState->mPositionSequence != update.mPositionSequence) { + if (update.mPosition > mFrameCount) { + return NO_INIT; + } else if (localState->mLoopCount != 0 && update.mPosition >= localState->mLoopEnd) { + localState->mLoopCount = 0; // disable loop count if position is beyond loop end. + } + localState->mPosition = update.mPosition; + localState->mPositionSequence = update.mPositionSequence; + } + return OK; +} + +ssize_t StaticAudioTrackServerProxy::pollPosition() +{ + StaticAudioTrackState state; + if (mObserver.poll(state)) { + StaticAudioTrackState trystate = mState; + bool result; + const int32_t diffSeq = state.mLoopSequence - state.mPositionSequence; + + if (diffSeq < 0) { + result = updateStateWithLoop(&trystate, state) == OK && + updateStateWithPosition(&trystate, state) == OK; + } else { + result = updateStateWithPosition(&trystate, state) == OK && + updateStateWithLoop(&trystate, state) == OK; + } + if (!result) { + mObserver.done(); + // caution: no update occurs so server state will be inconsistent with client state. ALOGE("%s client pushed an invalid state, shutting down", __func__); mIsShutdown = true; return (ssize_t) NO_INIT; } + mState = trystate; + if (mState.mLoopCount == -1) { + mFramesReady = INT64_MAX; + } else if (mState.mLoopCount == 0) { + mFramesReady = mFrameCount - mState.mPosition; + } else if (mState.mLoopCount > 0) { + // TODO: Later consider fixing overflow, but does not seem needed now + // as will not overflow if loopStart and loopEnd are Java "ints". + mFramesReady = int64_t(mState.mLoopCount) * (mState.mLoopEnd - mState.mLoopStart) + + mFrameCount - mState.mPosition; + } + mFramesReadySafe = clampToSize(mFramesReady); // This may overflow, but client is not supposed to rely on it - mCblk->u.mStatic.mBufferPosition = (uint32_t) position; + StaticAudioTrackPosLoop posLoop; + + posLoop.mLoopCount = (int32_t) mState.mLoopCount; + posLoop.mBufferPosition = (uint32_t) mState.mPosition; + mPosLoopMutator.push(posLoop); + mObserver.done(); // safe to read mStatic variables. } - return (ssize_t) position; + return (ssize_t) mState.mPosition; } status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush __unused) @@ -815,9 +931,10 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush return (status_t) positionOrStatus; } size_t position = (size_t) positionOrStatus; + size_t end = mState.mLoopCount != 0 ? mState.mLoopEnd : mFrameCount; size_t avail; - if (position < mEnd) { - avail = mEnd - position; + if (position < end) { + avail = end - position; size_t wanted = buffer->mFrameCount; if (avail < wanted) { buffer->mFrameCount = avail; @@ -830,7 +947,10 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush buffer->mFrameCount = 0; buffer->mRaw = NULL; } - buffer->mNonContig = 0; // FIXME should be > 0 for looping + // As mFramesReady is the total remaining frames in the static audio track, + // it is always larger or equal to avail. + LOG_ALWAYS_FATAL_IF(mFramesReady < (int64_t) avail); + buffer->mNonContig = mFramesReady == INT64_MAX ? SIZE_MAX : clampToSize(mFramesReady - avail); mUnreleased = avail; return NO_ERROR; } @@ -838,6 +958,7 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) { size_t stepCount = buffer->mFrameCount; + LOG_ALWAYS_FATAL_IF(!((int64_t) stepCount <= mFramesReady)); LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased)); if (stepCount == 0) { // prevent accidental re-use of buffer @@ -847,29 +968,36 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) } mUnreleased -= stepCount; audio_track_cblk_t* cblk = mCblk; - size_t position = mPosition; + size_t position = mState.mPosition; size_t newPosition = position + stepCount; int32_t setFlags = 0; if (!(position <= newPosition && newPosition <= mFrameCount)) { - ALOGW("%s newPosition %zu outside [%zu, %zu]", __func__, newPosition, position, mFrameCount); + ALOGW("%s newPosition %zu outside [%zu, %zu]", __func__, newPosition, position, + mFrameCount); newPosition = mFrameCount; } else if (mState.mLoopCount != 0 && newPosition == mState.mLoopEnd) { + newPosition = mState.mLoopStart; if (mState.mLoopCount == -1 || --mState.mLoopCount != 0) { - newPosition = mState.mLoopStart; setFlags = CBLK_LOOP_CYCLE; } else { - mEnd = mFrameCount; // this is what allows playback to continue after the loop setFlags = CBLK_LOOP_FINAL; } } if (newPosition == mFrameCount) { setFlags |= CBLK_BUFFER_END; } - mPosition = newPosition; + mState.mPosition = newPosition; + if (mFramesReady != INT64_MAX) { + mFramesReady -= stepCount; + } + mFramesReadySafe = clampToSize(mFramesReady); cblk->mServer += stepCount; // This may overflow, but client is not supposed to rely on it - cblk->u.mStatic.mBufferPosition = (uint32_t) newPosition; + StaticAudioTrackPosLoop posLoop; + posLoop.mBufferPosition = mState.mPosition; + posLoop.mLoopCount = mState.mLoopCount; + mPosLoopMutator.push(posLoop); if (setFlags != 0) { (void) android_atomic_or(setFlags, &cblk->mFlags); // this would be a good place to wake a futex diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 6099a05..8e3b633 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -119,7 +119,7 @@ public: // haveSharedBuffer if (sharedBuffer != 0) { data.writeInt32(true); - data.writeStrongBinder(sharedBuffer->asBinder()); + data.writeStrongBinder(IInterface::asBinder(sharedBuffer)); } else { data.writeInt32(false); } @@ -419,7 +419,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); - data.writeStrongBinder(client->asBinder()); + data.writeStrongBinder(IInterface::asBinder(client)); remote()->transact(REGISTER_CLIENT, data, &reply); } @@ -716,7 +716,7 @@ public: data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); data.write(pDesc, sizeof(effect_descriptor_t)); - data.writeStrongBinder(client->asBinder()); + data.writeStrongBinder(IInterface::asBinder(client)); data.writeInt32(priority); data.writeInt32((int32_t) output); data.writeInt32(sessionId); @@ -939,7 +939,7 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32(flags); reply->writeInt32(sessionId); reply->writeInt32(status); - reply->writeStrongBinder(track->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(track)); return NO_ERROR; } break; case OPEN_RECORD: { @@ -966,11 +966,9 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32(sessionId); reply->writeInt64(notificationFrames); reply->writeInt32(status); - reply->writeStrongBinder(record != NULL ? record->asBinder() - : NULL); - reply->writeStrongBinder(cblk != NULL ? cblk->asBinder() : NULL); - reply->writeStrongBinder(buffers != NULL ? buffers->asBinder() - : NULL); + reply->writeStrongBinder(IInterface::asBinder(record)); + reply->writeStrongBinder(IInterface::asBinder(cblk)); + reply->writeStrongBinder(IInterface::asBinder(buffers)); return NO_ERROR; } break; case SAMPLE_RATE: { @@ -1256,7 +1254,7 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32(status); reply->writeInt32(id); reply->writeInt32(enabled); - reply->writeStrongBinder(effect->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(effect)); reply->write(&desc, sizeof(effect_descriptor_t)); return NO_ERROR; } break; diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 89178f1..f2ff27b 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -41,7 +41,7 @@ enum { START_OUTPUT, STOP_OUTPUT, RELEASE_OUTPUT, - GET_INPUT, + GET_INPUT_FOR_ATTR, START_INPUT, STOP_INPUT, RELEASE_INPUT, @@ -69,9 +69,12 @@ enum { GET_OUTPUT_FOR_ATTR, ACQUIRE_SOUNDTRIGGER_SESSION, RELEASE_SOUNDTRIGGER_SESSION, - GET_PHONE_STATE + GET_PHONE_STATE, + REGISTER_POLICY_MIXES, }; +#define MAX_ITEMS_PER_LIST 1024 + class BpAudioPolicyService : public BpInterface<IAudioPolicyService> { public: @@ -83,13 +86,15 @@ public: virtual status_t setDeviceConnectionState( audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) + const char *device_address, + const char *device_name) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(static_cast <uint32_t>(device)); data.writeInt32(static_cast <uint32_t>(state)); data.writeCString(device_address); + data.writeCString(device_name); remote()->transact(SET_DEVICE_CONNECTION_STATE, data, &reply); return static_cast <status_t> (reply.readInt32()); } @@ -160,21 +165,45 @@ public: return static_cast <audio_io_handle_t> (reply.readInt32()); } - virtual audio_io_handle_t getOutputForAttr( - const audio_attributes_t *attr, - 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 getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + 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) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); if (attr == NULL) { - ALOGE("Writing NULL audio attributes - shouldn't happen"); - return (audio_io_handle_t) 0; + if (stream == NULL) { + ALOGE("getOutputForAttr(): NULL audio attributes and stream type"); + return BAD_VALUE; + } + if (*stream == AUDIO_STREAM_DEFAULT) { + ALOGE("getOutputForAttr unspecified stream type"); + return BAD_VALUE; + } + } + if (output == NULL) { + ALOGE("getOutputForAttr NULL output - shouldn't happen"); + return BAD_VALUE; + } + if (attr == NULL) { + data.writeInt32(0); + } else { + data.writeInt32(1); + data.write(attr, sizeof(audio_attributes_t)); + } + data.writeInt32(session); + if (stream == NULL) { + data.writeInt32(0); + } else { + data.writeInt32(1); + data.writeInt32(*stream); } - data.write(attr, sizeof(audio_attributes_t)); data.writeInt32(samplingRate); data.writeInt32(static_cast <uint32_t>(format)); data.writeInt32(channelMask); @@ -186,62 +215,93 @@ public: data.writeInt32(1); data.write(offloadInfo, sizeof(audio_offload_info_t)); } - remote()->transact(GET_OUTPUT_FOR_ATTR, data, &reply); - return static_cast <audio_io_handle_t> (reply.readInt32()); + status_t status = remote()->transact(GET_OUTPUT_FOR_ATTR, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = (status_t)reply.readInt32(); + if (status != NO_ERROR) { + return status; + } + *output = (audio_io_handle_t)reply.readInt32(); + if (stream != NULL) { + *stream = (audio_stream_type_t)reply.readInt32(); + } + return status; } virtual status_t startOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(output); data.writeInt32((int32_t) stream); - data.writeInt32(session); + data.writeInt32((int32_t)session); remote()->transact(START_OUTPUT, data, &reply); return static_cast <status_t> (reply.readInt32()); } virtual status_t stopOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(output); data.writeInt32((int32_t) stream); - data.writeInt32(session); + data.writeInt32((int32_t)session); remote()->transact(STOP_OUTPUT, data, &reply); return static_cast <status_t> (reply.readInt32()); } - virtual void releaseOutput(audio_io_handle_t output) + virtual void releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(output); + data.writeInt32((int32_t)stream); + data.writeInt32((int32_t)session); remote()->transact(RELEASE_OUTPUT, data, &reply); } - virtual audio_io_handle_t getInput( - audio_source_t inputSource, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - int audioSession, - audio_input_flags_t flags) + virtual status_t getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); - data.writeInt32((int32_t) inputSource); + if (attr == NULL) { + ALOGE("getInputForAttr NULL attr - shouldn't happen"); + return BAD_VALUE; + } + if (input == NULL) { + ALOGE("getInputForAttr NULL input - shouldn't happen"); + return BAD_VALUE; + } + data.write(attr, sizeof(audio_attributes_t)); + data.writeInt32(session); data.writeInt32(samplingRate); data.writeInt32(static_cast <uint32_t>(format)); data.writeInt32(channelMask); - data.writeInt32(audioSession); data.writeInt32(flags); - remote()->transact(GET_INPUT, data, &reply); - return static_cast <audio_io_handle_t> (reply.readInt32()); + status_t status = remote()->transact(GET_INPUT_FOR_ATTR, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = reply.readInt32(); + if (status != NO_ERROR) { + return status; + } + *input = (audio_io_handle_t)reply.readInt32(); + return NO_ERROR; } virtual status_t startInput(audio_io_handle_t input, @@ -572,7 +632,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); - data.writeStrongBinder(client->asBinder()); + data.writeStrongBinder(IInterface::asBinder(client)); remote()->transact(REGISTER_CLIENT, data, &reply); } @@ -620,6 +680,38 @@ public: } return (audio_mode_t)reply.readInt32(); } + + virtual status_t registerPolicyMixes(Vector<AudioMix> mixes, bool registration) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(registration ? 1 : 0); + size_t size = mixes.size(); + if (size > MAX_MIXES_PER_POLICY) { + size = MAX_MIXES_PER_POLICY; + } + size_t sizePosition = data.dataPosition(); + data.writeInt32(size); + size_t finalSize = size; + for (size_t i = 0; i < size; i++) { + size_t position = data.dataPosition(); + if (mixes[i].writeToParcel(&data) != NO_ERROR) { + data.setDataPosition(position); + finalSize--; + } + } + if (size != finalSize) { + size_t position = data.dataPosition(); + data.setDataPosition(sizePosition); + data.writeInt32(finalSize); + data.setDataPosition(position); + } + status_t status = remote()->transact(REGISTER_POLICY_MIXES, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); @@ -638,9 +730,11 @@ status_t BnAudioPolicyService::onTransact( audio_policy_dev_state_t state = static_cast <audio_policy_dev_state_t>(data.readInt32()); const char *device_address = data.readCString(); + const char *device_name = data.readCString(); reply->writeInt32(static_cast<uint32_t> (setDeviceConnectionState(device, state, - device_address))); + device_address, + device_name))); return NO_ERROR; } break; @@ -706,7 +800,16 @@ status_t BnAudioPolicyService::onTransact( case GET_OUTPUT_FOR_ATTR: { CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_attributes_t attr; - data.read(&attr, sizeof(audio_attributes_t)); + bool hasAttributes = data.readInt32() != 0; + if (hasAttributes) { + data.read(&attr, sizeof(audio_attributes_t)); + } + audio_session_t session = (audio_session_t)data.readInt32(); + audio_stream_type_t stream = AUDIO_STREAM_DEFAULT; + bool hasStream = data.readInt32() != 0; + if (hasStream) { + stream = (audio_stream_type_t)data.readInt32(); + } uint32_t samplingRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); @@ -717,13 +820,14 @@ status_t BnAudioPolicyService::onTransact( if (hasOffloadInfo) { data.read(&offloadInfo, sizeof(audio_offload_info_t)); } - audio_io_handle_t output = getOutputForAttr(&attr, - samplingRate, - format, - channelMask, - flags, - hasOffloadInfo ? &offloadInfo : NULL); - reply->writeInt32(static_cast <int>(output)); + audio_io_handle_t output; + status_t status = getOutputForAttr(hasAttributes ? &attr : NULL, + &output, session, &stream, + samplingRate, format, channelMask, + flags, hasOffloadInfo ? &offloadInfo : NULL); + reply->writeInt32(status); + reply->writeInt32(output); + reply->writeInt32(stream); return NO_ERROR; } break; @@ -732,7 +836,7 @@ status_t BnAudioPolicyService::onTransact( audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32()); audio_stream_type_t stream = static_cast <audio_stream_type_t>(data.readInt32()); - int session = data.readInt32(); + audio_session_t session = (audio_session_t)data.readInt32(); reply->writeInt32(static_cast <uint32_t>(startOutput(output, stream, session))); @@ -744,7 +848,7 @@ status_t BnAudioPolicyService::onTransact( audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32()); audio_stream_type_t stream = static_cast <audio_stream_type_t>(data.readInt32()); - int session = data.readInt32(); + audio_session_t session = (audio_session_t)data.readInt32(); reply->writeInt32(static_cast <uint32_t>(stopOutput(output, stream, session))); @@ -754,25 +858,29 @@ status_t BnAudioPolicyService::onTransact( case RELEASE_OUTPUT: { CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32()); - releaseOutput(output); + audio_stream_type_t stream = (audio_stream_type_t)data.readInt32(); + audio_session_t session = (audio_session_t)data.readInt32(); + releaseOutput(output, stream, session); return NO_ERROR; } break; - case GET_INPUT: { + case GET_INPUT_FOR_ATTR: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - audio_source_t inputSource = (audio_source_t) data.readInt32(); + audio_attributes_t attr; + data.read(&attr, sizeof(audio_attributes_t)); + audio_session_t session = (audio_session_t)data.readInt32(); uint32_t samplingRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); - int audioSession = data.readInt32(); audio_input_flags_t flags = (audio_input_flags_t) data.readInt32(); - audio_io_handle_t input = getInput(inputSource, - samplingRate, - format, - channelMask, - audioSession, - flags); - reply->writeInt32(static_cast <int>(input)); + audio_io_handle_t input; + status_t status = getInputForAttr(&attr, &input, session, + samplingRate, format, channelMask, + flags); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->writeInt32(input); + } return NO_ERROR; } break; @@ -952,10 +1060,18 @@ status_t BnAudioPolicyService::onTransact( audio_port_role_t role = (audio_port_role_t)data.readInt32(); audio_port_type_t type = (audio_port_type_t)data.readInt32(); unsigned int numPortsReq = data.readInt32(); + if (numPortsReq > MAX_ITEMS_PER_LIST) { + numPortsReq = MAX_ITEMS_PER_LIST; + } unsigned int numPorts = numPortsReq; - unsigned int generation; struct audio_port *ports = (struct audio_port *)calloc(numPortsReq, sizeof(struct audio_port)); + if (ports == NULL) { + reply->writeInt32(NO_MEMORY); + reply->writeInt32(0); + return NO_ERROR; + } + unsigned int generation; status_t status = listAudioPorts(role, type, &numPorts, ports, &generation); reply->writeInt32(status); reply->writeInt32(numPorts); @@ -1009,11 +1125,19 @@ status_t BnAudioPolicyService::onTransact( case LIST_AUDIO_PATCHES: { CHECK_INTERFACE(IAudioPolicyService, data, reply); unsigned int numPatchesReq = data.readInt32(); + if (numPatchesReq > MAX_ITEMS_PER_LIST) { + numPatchesReq = MAX_ITEMS_PER_LIST; + } unsigned int numPatches = numPatchesReq; - unsigned int generation; struct audio_patch *patches = (struct audio_patch *)calloc(numPatchesReq, sizeof(struct audio_patch)); + if (patches == NULL) { + reply->writeInt32(NO_MEMORY); + reply->writeInt32(0); + return NO_ERROR; + } + unsigned int generation; status_t status = listAudioPatches(&numPatches, patches, &generation); reply->writeInt32(status); reply->writeInt32(numPatches); @@ -1078,6 +1202,25 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } break; + case REGISTER_POLICY_MIXES: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + bool registration = data.readInt32() == 1; + Vector<AudioMix> mixes; + size_t size = (size_t)data.readInt32(); + if (size > MAX_MIXES_PER_POLICY) { + size = MAX_MIXES_PER_POLICY; + } + for (size_t i = 0; i < size; i++) { + AudioMix mix; + if (mix.readFromParcel((Parcel*)&data) == NO_ERROR) { + mixes.add(mix); + } + } + status_t status = registerPolicyMixes(mixes, registration); + reply->writeInt32(status); + return NO_ERROR; + } break; + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp index 265bb1b..df209fd 100644 --- a/media/libmedia/IAudioTrack.cpp +++ b/media/libmedia/IAudioTrack.cpp @@ -137,7 +137,7 @@ public: int64_t pts) { Parcel data, reply; data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); - data.writeStrongBinder(buffer->asBinder()); + data.writeStrongBinder(IInterface::asBinder(buffer)); data.writeInt64(pts); status_t status = remote()->transact(QUEUE_TIMED_BUFFER, data, &reply); @@ -207,7 +207,7 @@ status_t BnAudioTrack::onTransact( switch (code) { case GET_CBLK: { CHECK_INTERFACE(IAudioTrack, data, reply); - reply->writeStrongBinder(getCblk()->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(getCblk())); return NO_ERROR; } break; case START: { @@ -241,7 +241,7 @@ status_t BnAudioTrack::onTransact( status_t status = allocateTimedBuffer(data.readInt64(), &buffer); reply->writeInt32(status); if (status == NO_ERROR) { - reply->writeStrongBinder(buffer->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(buffer)); } return NO_ERROR; } break; diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp index 7e74de9..b08fa82 100644 --- a/media/libmedia/IDrm.cpp +++ b/media/libmedia/IDrm.cpp @@ -450,7 +450,7 @@ struct BpDrm : public BpInterface<IDrm> { virtual status_t setListener(const sp<IDrmClient>& listener) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - data.writeStrongBinder(listener->asBinder()); + data.writeStrongBinder(IInterface::asBinder(listener)); remote()->transact(SET_LISTENER, data, &reply); return reply.readInt32(); } diff --git a/media/libmedia/IEffect.cpp b/media/libmedia/IEffect.cpp index b94012a..c2fff78 100644 --- a/media/libmedia/IEffect.cpp +++ b/media/libmedia/IEffect.cpp @@ -190,7 +190,7 @@ status_t BnEffect::onTransact( case GET_CBLK: { CHECK_INTERFACE(IEffect, data, reply); - reply->writeStrongBinder(getCblk()->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(getCblk())); return NO_ERROR; } break; diff --git a/media/libmedia/IHDCP.cpp b/media/libmedia/IHDCP.cpp index 1cf987a..9122f75 100644 --- a/media/libmedia/IHDCP.cpp +++ b/media/libmedia/IHDCP.cpp @@ -65,7 +65,7 @@ struct BpHDCP : public BpInterface<IHDCP> { virtual status_t setObserver(const sp<IHDCPObserver> &observer) { Parcel data, reply; data.writeInterfaceToken(IHDCP::getInterfaceDescriptor()); - data.writeStrongBinder(observer->asBinder()); + data.writeStrongBinder(IInterface::asBinder(observer)); remote()->transact(HDCP_SET_OBSERVER, data, &reply); return reply.readInt32(); } diff --git a/media/libmedia/IMediaDeathNotifier.cpp b/media/libmedia/IMediaDeathNotifier.cpp index 10b4934..38e9ca0 100644 --- a/media/libmedia/IMediaDeathNotifier.cpp +++ b/media/libmedia/IMediaDeathNotifier.cpp @@ -104,7 +104,7 @@ IMediaDeathNotifier::DeathNotifier::~DeathNotifier() Mutex::Autolock _l(sServiceLock); sObitRecipients.clear(); if (sMediaPlayerService != 0) { - sMediaPlayerService->asBinder()->unlinkToDeath(this); + IInterface::asBinder(sMediaPlayerService)->unlinkToDeath(this); } } diff --git a/media/libmedia/IMediaLogService.cpp b/media/libmedia/IMediaLogService.cpp index 8a66c7c..a4af7b7 100644 --- a/media/libmedia/IMediaLogService.cpp +++ b/media/libmedia/IMediaLogService.cpp @@ -42,7 +42,7 @@ public: virtual void registerWriter(const sp<IMemory>& shared, size_t size, const char *name) { Parcel data, reply; data.writeInterfaceToken(IMediaLogService::getInterfaceDescriptor()); - data.writeStrongBinder(shared->asBinder()); + data.writeStrongBinder(IInterface::asBinder(shared)); data.writeInt64((int64_t) size); data.writeCString(name); status_t status = remote()->transact(REGISTER_WRITER, data, &reply); @@ -52,7 +52,7 @@ public: virtual void unregisterWriter(const sp<IMemory>& shared) { Parcel data, reply; data.writeInterfaceToken(IMediaLogService::getInterfaceDescriptor()); - data.writeStrongBinder(shared->asBinder()); + data.writeStrongBinder(IInterface::asBinder(shared)); status_t status = remote()->transact(UNREGISTER_WRITER, data, &reply); // FIXME ignores status } diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp index 38f717c..aa2665a 100644 --- a/media/libmedia/IMediaMetadataRetriever.cpp +++ b/media/libmedia/IMediaMetadataRetriever.cpp @@ -95,7 +95,7 @@ public: data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); data.writeInt32(httpService != NULL); if (httpService != NULL) { - data.writeStrongBinder(httpService->asBinder()); + data.writeStrongBinder(IInterface::asBinder(httpService)); } data.writeCString(srcUrl); @@ -246,7 +246,7 @@ status_t BnMediaMetadataRetriever::onTransact( sp<IMemory> bitmap = getFrameAtTime(timeUs, option); if (bitmap != 0) { // Don't send NULL across the binder interface reply->writeInt32(NO_ERROR); - reply->writeStrongBinder(bitmap->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(bitmap)); } else { reply->writeInt32(UNKNOWN_ERROR); } @@ -263,7 +263,7 @@ status_t BnMediaMetadataRetriever::onTransact( sp<IMemory> albumArt = extractAlbumArt(); if (albumArt != 0) { // Don't send NULL across the binder interface reply->writeInt32(NO_ERROR); - reply->writeStrongBinder(albumArt->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(albumArt)); } else { reply->writeInt32(UNKNOWN_ERROR); } diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index d778d05..dcd5670 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -39,6 +39,7 @@ enum { START, STOP, IS_PLAYING, + SET_PLAYBACK_RATE, PAUSE, SEEK_TO, GET_CURRENT_POSITION, @@ -85,7 +86,7 @@ public: data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); data.writeInt32(httpService != NULL); if (httpService != NULL) { - data.writeStrongBinder(httpService->asBinder()); + data.writeStrongBinder(IInterface::asBinder(httpService)); } data.writeCString(url); if (headers == NULL) { @@ -115,7 +116,7 @@ public: status_t setDataSource(const sp<IStreamSource> &source) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); - data.writeStrongBinder(source->asBinder()); + data.writeStrongBinder(IInterface::asBinder(source)); remote()->transact(SET_DATA_SOURCE_STREAM, data, &reply); return reply.readInt32(); } @@ -125,7 +126,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); - sp<IBinder> b(bufferProducer->asBinder()); + sp<IBinder> b(IInterface::asBinder(bufferProducer)); data.writeStrongBinder(b); remote()->transact(SET_VIDEO_SURFACETEXTURE, data, &reply); return reply.readInt32(); @@ -164,6 +165,15 @@ public: return reply.readInt32(); } + status_t setPlaybackRate(float rate) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeFloat(rate); + remote()->transact(SET_PLAYBACK_RATE, data, &reply); + return reply.readInt32(); + } + status_t pause() { Parcel data, reply; @@ -323,7 +333,7 @@ public: status_t setNextPlayer(const sp<IMediaPlayer>& player) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); - sp<IBinder> b(player->asBinder()); + sp<IBinder> b(IInterface::asBinder(player)); data.writeStrongBinder(b); remote()->transact(SET_NEXT_PLAYER, data, &reply); return reply.readInt32(); @@ -426,6 +436,11 @@ status_t BnMediaPlayer::onTransact( reply->writeInt32(ret); return NO_ERROR; } break; + case SET_PLAYBACK_RATE: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(setPlaybackRate(data.readFloat())); + return NO_ERROR; + } break; case PAUSE: { CHECK_INTERFACE(IMediaPlayer, data, reply); reply->writeInt32(pause()); diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index 2e02d17..feea267 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -39,8 +39,6 @@ namespace android { enum { CREATE = IBinder::FIRST_CALL_TRANSACTION, - DECODE_URL, - DECODE_FD, CREATE_MEDIA_RECORDER, CREATE_METADATA_RETRIEVER, GET_OMX, @@ -73,7 +71,7 @@ public: const sp<IMediaPlayerClient>& client, int audioSessionId) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); - data.writeStrongBinder(client->asBinder()); + data.writeStrongBinder(IInterface::asBinder(client)); data.writeInt32(audioSessionId); remote()->transact(CREATE, data, &reply); @@ -88,59 +86,6 @@ public: return interface_cast<IMediaRecorder>(reply.readStrongBinder()); } - 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); - if (status == NO_ERROR) { - status = (status_t)reply.readInt32(); - if (status == NO_ERROR) { - *pSampleRate = uint32_t(reply.readInt32()); - *pNumChannels = reply.readInt32(); - *pFormat = (audio_format_t)reply.readInt32(); - *pSize = (size_t)reply.readInt32(); - } - } - return status; - } - - 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) - { - Parcel data, reply; - data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); - data.writeFileDescriptor(fd); - data.writeInt64(offset); - data.writeInt64(length); - data.writeStrongBinder(heap->asBinder()); - status_t status = remote()->transact(DECODE_FD, data, &reply); - if (status == NO_ERROR) { - status = (status_t)reply.readInt32(); - if (status == NO_ERROR) { - *pSampleRate = uint32_t(reply.readInt32()); - *pNumChannels = reply.readInt32(); - *pFormat = (audio_format_t)reply.readInt32(); - *pSize = (size_t)reply.readInt32(); - } - } - return status; - } - virtual sp<IOMX> getOMX() { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); @@ -188,7 +133,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); - data.writeStrongBinder(client->asBinder()); + data.writeStrongBinder(IInterface::asBinder(client)); data.writeString8(iface); remote()->transact(LISTEN_FOR_REMOTE_DISPLAY, data, &reply); return interface_cast<IRemoteDisplay>(reply.readStrongBinder()); @@ -216,95 +161,44 @@ status_t BnMediaPlayerService::onTransact( interface_cast<IMediaPlayerClient>(data.readStrongBinder()); int audioSessionId = data.readInt32(); sp<IMediaPlayer> player = create(client, audioSessionId); - reply->writeStrongBinder(player->asBinder()); - return NO_ERROR; - } 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(httpService, - url, - &sampleRate, - &numChannels, - &format, - heap, - &size); - reply->writeInt32(status); - if (status == NO_ERROR) { - reply->writeInt32(sampleRate); - reply->writeInt32(numChannels); - reply->writeInt32((int32_t)format); - reply->writeInt32((int32_t)size); - } - return NO_ERROR; - } break; - case DECODE_FD: { - CHECK_INTERFACE(IMediaPlayerService, data, reply); - int fd = dup(data.readFileDescriptor()); - int64_t offset = data.readInt64(); - int64_t length = data.readInt64(); - sp<IMemoryHeap> heap = interface_cast<IMemoryHeap>(data.readStrongBinder()); - uint32_t sampleRate; - int numChannels; - audio_format_t format; - size_t size; - status_t status = decode(fd, offset, length, &sampleRate, &numChannels, &format, - heap, &size); - reply->writeInt32(status); - if (status == NO_ERROR) { - reply->writeInt32(sampleRate); - reply->writeInt32(numChannels); - reply->writeInt32((int32_t)format); - reply->writeInt32((int32_t)size); - } + reply->writeStrongBinder(IInterface::asBinder(player)); return NO_ERROR; } break; case CREATE_MEDIA_RECORDER: { CHECK_INTERFACE(IMediaPlayerService, data, reply); sp<IMediaRecorder> recorder = createMediaRecorder(); - reply->writeStrongBinder(recorder->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(recorder)); return NO_ERROR; } break; case CREATE_METADATA_RETRIEVER: { CHECK_INTERFACE(IMediaPlayerService, data, reply); sp<IMediaMetadataRetriever> retriever = createMetadataRetriever(); - reply->writeStrongBinder(retriever->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(retriever)); return NO_ERROR; } break; case GET_OMX: { CHECK_INTERFACE(IMediaPlayerService, data, reply); sp<IOMX> omx = getOMX(); - reply->writeStrongBinder(omx->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(omx)); return NO_ERROR; } break; case MAKE_CRYPTO: { CHECK_INTERFACE(IMediaPlayerService, data, reply); sp<ICrypto> crypto = makeCrypto(); - reply->writeStrongBinder(crypto->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(crypto)); return NO_ERROR; } break; case MAKE_DRM: { CHECK_INTERFACE(IMediaPlayerService, data, reply); sp<IDrm> drm = makeDrm(); - reply->writeStrongBinder(drm->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(drm)); return NO_ERROR; } break; case MAKE_HDCP: { CHECK_INTERFACE(IMediaPlayerService, data, reply); bool createEncryptionModule = data.readInt32(); sp<IHDCP> hdcp = makeHDCP(createEncryptionModule); - reply->writeStrongBinder(hdcp->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(hdcp)); return NO_ERROR; } break; case ADD_BATTERY_DATA: { @@ -324,13 +218,13 @@ status_t BnMediaPlayerService::onTransact( interface_cast<IRemoteDisplayClient>(data.readStrongBinder())); String8 iface(data.readString8()); sp<IRemoteDisplay> display(listenForRemoteDisplay(client, iface)); - reply->writeStrongBinder(display->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(display)); return NO_ERROR; } break; case GET_CODEC_LIST: { CHECK_INTERFACE(IMediaPlayerService, data, reply); sp<IMediaCodecList> mcl = getCodecList(); - reply->writeStrongBinder(mcl->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(mcl)); return NO_ERROR; } break; default: diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index 95af006..9181f86 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -46,7 +46,6 @@ enum { SET_OUTPUT_FORMAT, SET_VIDEO_ENCODER, SET_AUDIO_ENCODER, - SET_OUTPUT_FILE_PATH, SET_OUTPUT_FILE_FD, SET_VIDEO_SIZE, SET_VIDEO_FRAMERATE, @@ -70,8 +69,8 @@ public: ALOGV("setCamera(%p,%p)", camera.get(), proxy.get()); Parcel data, reply; data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); - data.writeStrongBinder(camera->asBinder()); - data.writeStrongBinder(proxy->asBinder()); + data.writeStrongBinder(IInterface::asBinder(camera)); + data.writeStrongBinder(IInterface::asBinder(proxy)); remote()->transact(SET_CAMERA, data, &reply); return reply.readInt32(); } @@ -94,7 +93,7 @@ public: ALOGV("setPreviewSurface(%p)", surface.get()); Parcel data, reply; data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); - data.writeStrongBinder(surface->asBinder()); + data.writeStrongBinder(IInterface::asBinder(surface)); remote()->transact(SET_PREVIEW_SURFACE, data, &reply); return reply.readInt32(); } @@ -158,16 +157,6 @@ public: return reply.readInt32(); } - status_t setOutputFile(const char* path) - { - ALOGV("setOutputFile(%s)", path); - Parcel data, reply; - data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); - data.writeCString(path); - remote()->transact(SET_OUTPUT_FILE_PATH, data, &reply); - return reply.readInt32(); - } - status_t setOutputFile(int fd, int64_t offset, int64_t length) { ALOGV("setOutputFile(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); Parcel data, reply; @@ -215,7 +204,7 @@ public: ALOGV("setListener(%p)", listener.get()); Parcel data, reply; data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); - data.writeStrongBinder(listener->asBinder()); + data.writeStrongBinder(IInterface::asBinder(listener)); remote()->transact(SET_LISTENER, data, &reply); return reply.readInt32(); } @@ -300,7 +289,8 @@ IMPLEMENT_META_INTERFACE(MediaRecorder, "android.media.IMediaRecorder"); // ---------------------------------------------------------------------- status_t BnMediaRecorder::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) + uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { switch (code) { case RELEASE: { @@ -390,13 +380,6 @@ status_t BnMediaRecorder::onTransact( return NO_ERROR; } break; - case SET_OUTPUT_FILE_PATH: { - ALOGV("SET_OUTPUT_FILE_PATH"); - CHECK_INTERFACE(IMediaRecorder, data, reply); - const char* path = data.readCString(); - reply->writeInt32(setOutputFile(path)); - return NO_ERROR; - } break; case SET_OUTPUT_FILE_FD: { ALOGV("SET_OUTPUT_FILE_FD"); CHECK_INTERFACE(IMediaRecorder, data, reply); @@ -445,7 +428,8 @@ status_t BnMediaRecorder::onTransact( case SET_PREVIEW_SURFACE: { ALOGV("SET_PREVIEW_SURFACE"); CHECK_INTERFACE(IMediaRecorder, data, reply); - sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(data.readStrongBinder()); + sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>( + data.readStrongBinder()); reply->writeInt32(setPreviewSurface(surface)); return NO_ERROR; } break; @@ -468,7 +452,7 @@ status_t BnMediaRecorder::onTransact( int returnedNull= (surfaceMediaSource == NULL) ? 1 : 0 ; reply->writeInt32(returnedNull); if (!returnedNull) { - reply->writeStrongBinder(surfaceMediaSource->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(surfaceMediaSource)); } return NO_ERROR; } break; diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index c583d32..e208df9 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -100,7 +100,7 @@ public: Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeCString(name); - data.writeStrongBinder(observer->asBinder()); + data.writeStrongBinder(IInterface::asBinder(observer)); remote()->transact(ALLOCATE_NODE, data, &reply); status_t err = reply.readInt32(); @@ -248,7 +248,7 @@ public: data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); data.writeInt32(port_index); - data.writeStrongBinder(params->asBinder()); + data.writeStrongBinder(IInterface::asBinder(params)); remote()->transact(USE_BUFFER, data, &reply); status_t err = reply.readInt32(); @@ -418,7 +418,7 @@ public: data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); data.writeInt32(port_index); - data.writeStrongBinder(params->asBinder()); + data.writeStrongBinder(IInterface::asBinder(params)); remote()->transact(ALLOC_BUFFER_WITH_BACKUP, data, &reply); status_t err = reply.readInt32(); @@ -775,7 +775,7 @@ status_t BnOMX::onTransact( reply->writeInt32(err); if (err == OK) { - reply->writeStrongBinder(bufferProducer->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(bufferProducer)); } return NO_ERROR; diff --git a/media/libmedia/IRemoteDisplayClient.cpp b/media/libmedia/IRemoteDisplayClient.cpp index 7190879..9d63bc9 100644 --- a/media/libmedia/IRemoteDisplayClient.cpp +++ b/media/libmedia/IRemoteDisplayClient.cpp @@ -42,7 +42,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(IRemoteDisplayClient::getInterfaceDescriptor()); - data.writeStrongBinder(bufferProducer->asBinder()); + data.writeStrongBinder(IInterface::asBinder(bufferProducer)); data.writeInt32(width); data.writeInt32(height); data.writeInt32(flags); diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp index fe2cc61..d480aef 100644 --- a/media/libmedia/IStreamSource.cpp +++ b/media/libmedia/IStreamSource.cpp @@ -55,7 +55,7 @@ struct BpStreamSource : public BpInterface<IStreamSource> { virtual void setListener(const sp<IStreamListener> &listener) { Parcel data, reply; data.writeInterfaceToken(IStreamSource::getInterfaceDescriptor()); - data.writeStrongBinder(listener->asBinder()); + data.writeStrongBinder(IInterface::asBinder(listener)); remote()->transact(SET_LISTENER, data, &reply); } @@ -64,7 +64,7 @@ struct BpStreamSource : public BpInterface<IStreamSource> { data.writeInterfaceToken(IStreamSource::getInterfaceDescriptor()); data.writeInt64(static_cast<int64_t>(buffers.size())); for (size_t i = 0; i < buffers.size(); ++i) { - data.writeStrongBinder(buffers.itemAt(i)->asBinder()); + data.writeStrongBinder(IInterface::asBinder(buffers.itemAt(i))); } remote()->transact(SET_BUFFERS, data, &reply); } diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp index f0f1832..271be0c 100644 --- a/media/libmedia/JetPlayer.cpp +++ b/media/libmedia/JetPlayer.cpp @@ -36,7 +36,6 @@ JetPlayer::JetPlayer(void *javaJetPlayer, int maxTracks, int trackBufferSize) : mPaused(false), mMaxTracks(maxTracks), mEasData(NULL), - mEasJetFileLoc(NULL), mTrackBufferSize(trackBufferSize) { ALOGV("JetPlayer constructor"); @@ -133,10 +132,7 @@ int JetPlayer::release() JET_Shutdown(mEasData); EAS_Shutdown(mEasData); } - if (mEasJetFileLoc) { - free(mEasJetFileLoc); - mEasJetFileLoc = NULL; - } + mIoWrapper.clear(); if (mAudioTrack != 0) { mAudioTrack->stop(); mAudioTrack->flush(); @@ -327,16 +323,9 @@ int JetPlayer::loadFromFile(const char* path) Mutex::Autolock lock(mMutex); - mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE)); - strncpy(mJetFilePath, path, sizeof(mJetFilePath)); - mJetFilePath[sizeof(mJetFilePath) - 1] = '\0'; - mEasJetFileLoc->path = mJetFilePath; - - mEasJetFileLoc->fd = 0; - mEasJetFileLoc->length = 0; - mEasJetFileLoc->offset = 0; + mIoWrapper = new MidiIoWrapper(path); - EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc); + EAS_RESULT result = JET_OpenFile(mEasData, mIoWrapper->getLocator()); if (result != EAS_SUCCESS) mState = EAS_STATE_ERROR; else @@ -352,13 +341,9 @@ int JetPlayer::loadFromFD(const int fd, const long long offset, const long long Mutex::Autolock lock(mMutex); - mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE)); - mEasJetFileLoc->fd = fd; - mEasJetFileLoc->offset = offset; - mEasJetFileLoc->length = length; - mEasJetFileLoc->path = NULL; + mIoWrapper = new MidiIoWrapper(fd, offset, length); - EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc); + EAS_RESULT result = JET_OpenFile(mEasData, mIoWrapper->getLocator()); if (result != EAS_SUCCESS) mState = EAS_STATE_ERROR; else @@ -423,7 +408,8 @@ int JetPlayer::queueSegment(int segmentNum, int libNum, int repeatCount, int tra ALOGV("JetPlayer::queueSegment segmentNum=%d, libNum=%d, repeatCount=%d, transpose=%d", segmentNum, libNum, repeatCount, transpose); Mutex::Autolock lock(mMutex); - return JET_QueueSegment(mEasData, segmentNum, libNum, repeatCount, transpose, muteFlags, userID); + return JET_QueueSegment(mEasData, segmentNum, libNum, repeatCount, transpose, muteFlags, + userID); } //------------------------------------------------------------------------------------------------- @@ -459,13 +445,13 @@ int JetPlayer::clearQueue() //------------------------------------------------------------------------------------------------- void JetPlayer::dump() { - ALOGE("JetPlayer dump: JET file=%s", mEasJetFileLoc->path); } void JetPlayer::dumpJetStatus(S_JET_STATUS* pJetStatus) { if (pJetStatus!=NULL) - ALOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d paused=%d", + ALOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d " + "paused=%d", pJetStatus->currentUserID, pJetStatus->segmentRepeatCount, pJetStatus->numQueuedSegments, pJetStatus->paused); else diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index e2e6042..47f9258 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -163,7 +163,8 @@ MediaProfiles::logVideoEditorCap(const MediaProfiles::VideoEditorCap& cap UNUSED } /*static*/ int -MediaProfiles::findTagForName(const MediaProfiles::NameToTagMap *map, size_t nMappings, const char *name) +MediaProfiles::findTagForName(const MediaProfiles::NameToTagMap *map, size_t nMappings, + const char *name) { int tag = -1; for (size_t i = 0; i < nMappings; ++i) { @@ -295,9 +296,8 @@ MediaProfiles::createAudioEncoderCap(const char **atts) CHECK(codec != -1); MediaProfiles::AudioEncoderCap *cap = - new MediaProfiles::AudioEncoderCap(static_cast<audio_encoder>(codec), atoi(atts[5]), atoi(atts[7]), - atoi(atts[9]), atoi(atts[11]), atoi(atts[13]), - atoi(atts[15])); + new MediaProfiles::AudioEncoderCap(static_cast<audio_encoder>(codec), atoi(atts[5]), + atoi(atts[7]), atoi(atts[9]), atoi(atts[11]), atoi(atts[13]), atoi(atts[15])); logAudioEncoderCap(*cap); return cap; } @@ -330,7 +330,8 @@ MediaProfiles::createCamcorderProfile(int cameraId, const char **atts, Vector<in !strcmp("fileFormat", atts[2]) && !strcmp("duration", atts[4])); - const size_t nProfileMappings = sizeof(sCamcorderQualityNameMap)/sizeof(sCamcorderQualityNameMap[0]); + const size_t nProfileMappings = sizeof(sCamcorderQualityNameMap)/ + sizeof(sCamcorderQualityNameMap[0]); const int quality = findTagForName(sCamcorderQualityNameMap, nProfileMappings, atts[1]); CHECK(quality != -1); @@ -722,16 +723,20 @@ MediaProfiles::createDefaultCamcorderTimeLapse480pProfile(camcorder_quality qual MediaProfiles::createDefaultCamcorderTimeLapseLowProfiles( MediaProfiles::CamcorderProfile **lowTimeLapseProfile, MediaProfiles::CamcorderProfile **lowSpecificTimeLapseProfile) { - *lowTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile(CAMCORDER_QUALITY_TIME_LAPSE_LOW); - *lowSpecificTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile(CAMCORDER_QUALITY_TIME_LAPSE_QCIF); + *lowTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile( + CAMCORDER_QUALITY_TIME_LAPSE_LOW); + *lowSpecificTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile( + CAMCORDER_QUALITY_TIME_LAPSE_QCIF); } /*static*/ void MediaProfiles::createDefaultCamcorderTimeLapseHighProfiles( MediaProfiles::CamcorderProfile **highTimeLapseProfile, MediaProfiles::CamcorderProfile **highSpecificTimeLapseProfile) { - *highTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile(CAMCORDER_QUALITY_TIME_LAPSE_HIGH); - *highSpecificTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile(CAMCORDER_QUALITY_TIME_LAPSE_480P); + *highTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile( + CAMCORDER_QUALITY_TIME_LAPSE_HIGH); + *highSpecificTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile( + CAMCORDER_QUALITY_TIME_LAPSE_480P); } /*static*/ MediaProfiles::CamcorderProfile* @@ -809,7 +814,8 @@ MediaProfiles::createDefaultCamcorderProfiles(MediaProfiles *profiles) // high camcorder time lapse profiles. MediaProfiles::CamcorderProfile *highTimeLapseProfile, *highSpecificTimeLapseProfile; - createDefaultCamcorderTimeLapseHighProfiles(&highTimeLapseProfile, &highSpecificTimeLapseProfile); + createDefaultCamcorderTimeLapseHighProfiles(&highTimeLapseProfile, + &highSpecificTimeLapseProfile); profiles->mCamcorderProfiles.add(highTimeLapseProfile); profiles->mCamcorderProfiles.add(highSpecificTimeLapseProfile); diff --git a/media/libmedia/MemoryLeakTrackUtil.cpp b/media/libmedia/MemoryLeakTrackUtil.cpp index 66f7161..d31f721 100644 --- a/media/libmedia/MemoryLeakTrackUtil.cpp +++ b/media/libmedia/MemoryLeakTrackUtil.cpp @@ -18,6 +18,7 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <sys/types.h> #include <unistd.h> diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp new file mode 100644 index 0000000..5197ce2 --- /dev/null +++ b/media/libmedia/MidiIoWrapper.cpp @@ -0,0 +1,92 @@ +/* + * 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 "MidiIoWrapper" +#include <utils/Log.h> +#include <utils/RefBase.h> + +#include <sys/stat.h> +#include <fcntl.h> + +#include "media/MidiIoWrapper.h" + +static int readAt(void *handle, void *buffer, int pos, int size) { + return ((android::MidiIoWrapper*)handle)->readAt(buffer, pos, size); +} +static int size(void *handle) { + return ((android::MidiIoWrapper*)handle)->size(); +} + +namespace android { + +MidiIoWrapper::MidiIoWrapper(const char *path) { + ALOGV("MidiIoWrapper(%s)", path); + mFd = open(path, O_RDONLY | O_LARGEFILE); + mBase = 0; + mLength = lseek(mFd, 0, SEEK_END); +} + +MidiIoWrapper::MidiIoWrapper(int fd, off64_t offset, int64_t size) { + ALOGV("MidiIoWrapper(fd=%d)", fd); + mFd = dup(fd); + mBase = offset; + mLength = size; +} + +MidiIoWrapper::MidiIoWrapper(const sp<DataSource> &source) { + ALOGV("MidiIoWrapper(DataSource)"); + mFd = -1; + mDataSource = source; + off64_t l; + if (mDataSource->getSize(&l) == OK) { + mLength = l; + } else { + mLength = 0; + } +} + +MidiIoWrapper::~MidiIoWrapper() { + ALOGV("~MidiIoWrapper"); + close(mFd); +} + +int MidiIoWrapper::readAt(void *buffer, int offset, int size) { + ALOGV("readAt(%p, %d, %d)", buffer, offset, size); + + if (mDataSource != NULL) { + return mDataSource->readAt(offset, buffer, size); + } + lseek(mFd, mBase + offset, SEEK_SET); + if (offset + size > mLength) { + size = mLength - offset; + } + return read(mFd, buffer, size); +} + +int MidiIoWrapper::size() { + ALOGV("size() = %d", int(mLength)); + return mLength; +} + +EAS_FILE_LOCATOR MidiIoWrapper::getLocator() { + mEasFile.handle = this; + mEasFile.readAt = ::readAt; + mEasFile.size = ::size; + return &mEasFile; +} + +} // namespace android diff --git a/media/libmedia/SingleStateQueue.cpp b/media/libmedia/SingleStateQueue.cpp deleted file mode 100644 index 3503baa..0000000 --- a/media/libmedia/SingleStateQueue.cpp +++ /dev/null @@ -1,107 +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 <new> -#include <cutils/atomic.h> -#include <cutils/atomic-inline.h> // for android_memory_barrier() -#include <media/SingleStateQueue.h> - -namespace android { - -template<typename T> SingleStateQueue<T>::Mutator::Mutator(Shared *shared) - : mSequence(0), mShared((Shared *) shared) -{ - // exactly one of Mutator and Observer must initialize, currently it is Observer - //shared->init(); -} - -template<typename T> int32_t SingleStateQueue<T>::Mutator::push(const T& value) -{ - Shared *shared = mShared; - int32_t sequence = mSequence; - sequence++; - android_atomic_acquire_store(sequence, &shared->mSequence); - shared->mValue = value; - sequence++; - android_atomic_release_store(sequence, &shared->mSequence); - mSequence = sequence; - // consider signalling a futex here, if we know that observer is waiting - return sequence; -} - -template<typename T> bool SingleStateQueue<T>::Mutator::ack() -{ - return mShared->mAck - mSequence == 0; -} - -template<typename T> bool SingleStateQueue<T>::Mutator::ack(int32_t sequence) -{ - // this relies on 2's complement rollover to detect an ancient sequence number - return mShared->mAck - sequence >= 0; -} - -template<typename T> SingleStateQueue<T>::Observer::Observer(Shared *shared) - : mSequence(0), mSeed(1), mShared((Shared *) shared) -{ - // exactly one of Mutator and Observer must initialize, currently it is Observer - shared->init(); -} - -template<typename T> bool SingleStateQueue<T>::Observer::poll(T& value) -{ - Shared *shared = mShared; - int32_t before = shared->mSequence; - if (before == mSequence) { - return false; - } - for (int tries = 0; ; ) { - const int MAX_TRIES = 5; - if (before & 1) { - if (++tries >= MAX_TRIES) { - return false; - } - before = shared->mSequence; - } else { - android_memory_barrier(); - T temp = shared->mValue; - int32_t after = android_atomic_release_load(&shared->mSequence); - if (after == before) { - value = temp; - shared->mAck = before; - mSequence = before; - return true; - } - if (++tries >= MAX_TRIES) { - return false; - } - before = after; - } - } -} - -#if 0 -template<typename T> SingleStateQueue<T>::SingleStateQueue(void /*Shared*/ *shared) -{ - ((Shared *) shared)->init(); -} -#endif - -} // namespace android - -// hack for gcc -#ifdef SINGLE_STATE_QUEUE_INSTANTIATIONS -#include SINGLE_STATE_QUEUE_INSTANTIATIONS -#endif diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp deleted file mode 100644 index d2e381b..0000000 --- a/media/libmedia/SoundPool.cpp +++ /dev/null @@ -1,921 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "SoundPool" - -#include <inttypes.h> - -#include <utils/Log.h> - -#define USE_SHARED_MEM_BUFFER - -#include <media/AudioTrack.h> -#include <media/IMediaHTTPService.h> -#include <media/mediaplayer.h> -#include <media/SoundPool.h> -#include "SoundPoolThread.h" -#include <media/AudioPolicyHelper.h> - -namespace android -{ - -int kDefaultBufferCount = 4; -uint32_t kMaxSampleRate = 48000; -uint32_t kDefaultSampleRate = 44100; -uint32_t kDefaultFrameCount = 1200; -size_t kDefaultHeapSize = 1024 * 1024; // 1MB - - -SoundPool::SoundPool(int maxChannels, const audio_attributes_t* pAttributes) -{ - ALOGV("SoundPool constructor: maxChannels=%d, attr.usage=%d, attr.flags=0x%x, attr.tags=%s", - maxChannels, pAttributes->usage, pAttributes->flags, pAttributes->tags); - - // check limits - mMaxChannels = maxChannels; - if (mMaxChannels < 1) { - mMaxChannels = 1; - } - else if (mMaxChannels > 32) { - mMaxChannels = 32; - } - ALOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels); - - mQuit = false; - mDecodeThread = 0; - memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t)); - mAllocated = 0; - mNextSampleID = 0; - mNextChannelID = 0; - - mCallback = 0; - mUserData = 0; - - mChannelPool = new SoundChannel[mMaxChannels]; - for (int i = 0; i < mMaxChannels; ++i) { - mChannelPool[i].init(this); - mChannels.push_back(&mChannelPool[i]); - } - - // start decode thread - startThreads(); -} - -SoundPool::~SoundPool() -{ - ALOGV("SoundPool destructor"); - mDecodeThread->quit(); - quit(); - - Mutex::Autolock lock(&mLock); - - mChannels.clear(); - if (mChannelPool) - delete [] mChannelPool; - // clean up samples - ALOGV("clear samples"); - mSamples.clear(); - - if (mDecodeThread) - delete mDecodeThread; -} - -void SoundPool::addToRestartList(SoundChannel* channel) -{ - Mutex::Autolock lock(&mRestartLock); - if (!mQuit) { - mRestart.push_back(channel); - mCondition.signal(); - } -} - -void SoundPool::addToStopList(SoundChannel* channel) -{ - Mutex::Autolock lock(&mRestartLock); - if (!mQuit) { - mStop.push_back(channel); - mCondition.signal(); - } -} - -int SoundPool::beginThread(void* arg) -{ - SoundPool* p = (SoundPool*)arg; - return p->run(); -} - -int SoundPool::run() -{ - mRestartLock.lock(); - while (!mQuit) { - mCondition.wait(mRestartLock); - ALOGV("awake"); - if (mQuit) break; - - while (!mStop.empty()) { - SoundChannel* channel; - ALOGV("Getting channel from stop list"); - List<SoundChannel* >::iterator iter = mStop.begin(); - channel = *iter; - mStop.erase(iter); - mRestartLock.unlock(); - if (channel != 0) { - Mutex::Autolock lock(&mLock); - channel->stop(); - } - mRestartLock.lock(); - if (mQuit) break; - } - - while (!mRestart.empty()) { - SoundChannel* channel; - ALOGV("Getting channel from list"); - List<SoundChannel*>::iterator iter = mRestart.begin(); - channel = *iter; - mRestart.erase(iter); - mRestartLock.unlock(); - if (channel != 0) { - Mutex::Autolock lock(&mLock); - channel->nextEvent(); - } - mRestartLock.lock(); - if (mQuit) break; - } - } - - mStop.clear(); - mRestart.clear(); - mCondition.signal(); - mRestartLock.unlock(); - ALOGV("goodbye"); - return 0; -} - -void SoundPool::quit() -{ - mRestartLock.lock(); - mQuit = true; - mCondition.signal(); - mCondition.wait(mRestartLock); - ALOGV("return from quit"); - mRestartLock.unlock(); -} - -bool SoundPool::startThreads() -{ - createThreadEtc(beginThread, this, "SoundPool"); - if (mDecodeThread == NULL) - mDecodeThread = new SoundPoolThread(this); - return mDecodeThread != NULL; -} - -SoundChannel* SoundPool::findChannel(int channelID) -{ - for (int i = 0; i < mMaxChannels; ++i) { - if (mChannelPool[i].channelID() == channelID) { - return &mChannelPool[i]; - } - } - return NULL; -} - -SoundChannel* SoundPool::findNextChannel(int channelID) -{ - for (int i = 0; i < mMaxChannels; ++i) { - if (mChannelPool[i].nextChannelID() == channelID) { - return &mChannelPool[i]; - } - } - return NULL; -} - -int SoundPool::load(const char* path, int priority __unused) -{ - ALOGV("load: path=%s, priority=%d", path, priority); - Mutex::Autolock lock(&mLock); - sp<Sample> sample = new Sample(++mNextSampleID, path); - mSamples.add(sample->sampleID(), sample); - doLoad(sample); - return sample->sampleID(); -} - -int SoundPool::load(int fd, int64_t offset, int64_t length, int priority __unused) -{ - ALOGV("load: fd=%d, offset=%" PRId64 ", length=%" PRId64 ", priority=%d", - fd, offset, length, priority); - Mutex::Autolock lock(&mLock); - sp<Sample> sample = new Sample(++mNextSampleID, fd, offset, length); - mSamples.add(sample->sampleID(), sample); - doLoad(sample); - return sample->sampleID(); -} - -void SoundPool::doLoad(sp<Sample>& sample) -{ - ALOGV("doLoad: loading sample sampleID=%d", sample->sampleID()); - sample->startLoad(); - mDecodeThread->loadSample(sample->sampleID()); -} - -bool SoundPool::unload(int sampleID) -{ - ALOGV("unload: sampleID=%d", sampleID); - Mutex::Autolock lock(&mLock); - return mSamples.removeItem(sampleID); -} - -int SoundPool::play(int sampleID, float leftVolume, float rightVolume, - int priority, int loop, float rate) -{ - ALOGV("play sampleID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f", - sampleID, leftVolume, rightVolume, priority, loop, rate); - sp<Sample> sample; - SoundChannel* channel; - int channelID; - - Mutex::Autolock lock(&mLock); - - if (mQuit) { - return 0; - } - // is sample ready? - sample = findSample(sampleID); - if ((sample == 0) || (sample->state() != Sample::READY)) { - ALOGW(" sample %d not READY", sampleID); - return 0; - } - - dump(); - - // allocate a channel - channel = allocateChannel_l(priority); - - // no channel allocated - return 0 - if (!channel) { - ALOGV("No channel allocated"); - return 0; - } - - channelID = ++mNextChannelID; - - ALOGV("play channel %p state = %d", channel, channel->state()); - channel->play(sample, channelID, leftVolume, rightVolume, priority, loop, rate); - return channelID; -} - -SoundChannel* SoundPool::allocateChannel_l(int priority) -{ - List<SoundChannel*>::iterator iter; - SoundChannel* channel = NULL; - - // allocate a channel - if (!mChannels.empty()) { - iter = mChannels.begin(); - if (priority >= (*iter)->priority()) { - channel = *iter; - mChannels.erase(iter); - ALOGV("Allocated active channel"); - } - } - - // update priority and put it back in the list - if (channel) { - channel->setPriority(priority); - for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) { - if (priority < (*iter)->priority()) { - break; - } - } - mChannels.insert(iter, channel); - } - return channel; -} - -// move a channel from its current position to the front of the list -void SoundPool::moveToFront_l(SoundChannel* channel) -{ - for (List<SoundChannel*>::iterator iter = mChannels.begin(); iter != mChannels.end(); ++iter) { - if (*iter == channel) { - mChannels.erase(iter); - mChannels.push_front(channel); - break; - } - } -} - -void SoundPool::pause(int channelID) -{ - ALOGV("pause(%d)", channelID); - Mutex::Autolock lock(&mLock); - SoundChannel* channel = findChannel(channelID); - if (channel) { - channel->pause(); - } -} - -void SoundPool::autoPause() -{ - ALOGV("autoPause()"); - Mutex::Autolock lock(&mLock); - for (int i = 0; i < mMaxChannels; ++i) { - SoundChannel* channel = &mChannelPool[i]; - channel->autoPause(); - } -} - -void SoundPool::resume(int channelID) -{ - ALOGV("resume(%d)", channelID); - Mutex::Autolock lock(&mLock); - SoundChannel* channel = findChannel(channelID); - if (channel) { - channel->resume(); - } -} - -void SoundPool::autoResume() -{ - ALOGV("autoResume()"); - Mutex::Autolock lock(&mLock); - for (int i = 0; i < mMaxChannels; ++i) { - SoundChannel* channel = &mChannelPool[i]; - channel->autoResume(); - } -} - -void SoundPool::stop(int channelID) -{ - ALOGV("stop(%d)", channelID); - Mutex::Autolock lock(&mLock); - SoundChannel* channel = findChannel(channelID); - if (channel) { - channel->stop(); - } else { - channel = findNextChannel(channelID); - if (channel) - channel->clearNextEvent(); - } -} - -void SoundPool::setVolume(int channelID, float leftVolume, float rightVolume) -{ - Mutex::Autolock lock(&mLock); - SoundChannel* channel = findChannel(channelID); - if (channel) { - channel->setVolume(leftVolume, rightVolume); - } -} - -void SoundPool::setPriority(int channelID, int priority) -{ - ALOGV("setPriority(%d, %d)", channelID, priority); - Mutex::Autolock lock(&mLock); - SoundChannel* channel = findChannel(channelID); - if (channel) { - channel->setPriority(priority); - } -} - -void SoundPool::setLoop(int channelID, int loop) -{ - ALOGV("setLoop(%d, %d)", channelID, loop); - Mutex::Autolock lock(&mLock); - SoundChannel* channel = findChannel(channelID); - if (channel) { - channel->setLoop(loop); - } -} - -void SoundPool::setRate(int channelID, float rate) -{ - ALOGV("setRate(%d, %f)", channelID, rate); - Mutex::Autolock lock(&mLock); - SoundChannel* channel = findChannel(channelID); - if (channel) { - channel->setRate(rate); - } -} - -// call with lock held -void SoundPool::done_l(SoundChannel* channel) -{ - ALOGV("done_l(%d)", channel->channelID()); - // if "stolen", play next event - if (channel->nextChannelID() != 0) { - ALOGV("add to restart list"); - addToRestartList(channel); - } - - // return to idle state - else { - ALOGV("move to front"); - moveToFront_l(channel); - } -} - -void SoundPool::setCallback(SoundPoolCallback* callback, void* user) -{ - Mutex::Autolock lock(&mCallbackLock); - mCallback = callback; - mUserData = user; -} - -void SoundPool::notify(SoundPoolEvent event) -{ - Mutex::Autolock lock(&mCallbackLock); - if (mCallback != NULL) { - mCallback(event, this, mUserData); - } -} - -void SoundPool::dump() -{ - for (int i = 0; i < mMaxChannels; ++i) { - mChannelPool[i].dump(); - } -} - - -Sample::Sample(int sampleID, const char* url) -{ - init(); - mSampleID = sampleID; - mUrl = strdup(url); - ALOGV("create sampleID=%d, url=%s", mSampleID, mUrl); -} - -Sample::Sample(int sampleID, int fd, int64_t offset, int64_t length) -{ - init(); - mSampleID = sampleID; - mFd = dup(fd); - mOffset = offset; - mLength = length; - ALOGV("create sampleID=%d, fd=%d, offset=%" PRId64 " length=%" PRId64, - mSampleID, mFd, mLength, mOffset); -} - -void Sample::init() -{ - mSize = 0; - mRefCount = 0; - mSampleID = 0; - mState = UNLOADED; - mFd = -1; - mOffset = 0; - mLength = 0; - mUrl = 0; -} - -Sample::~Sample() -{ - ALOGV("Sample::destructor sampleID=%d, fd=%d", mSampleID, mFd); - if (mFd > 0) { - ALOGV("close(%d)", mFd); - ::close(mFd); - } - free(mUrl); -} - -status_t Sample::doLoad() -{ - uint32_t sampleRate; - int numChannels; - audio_format_t format; - status_t status; - mHeap = new MemoryHeapBase(kDefaultHeapSize); - - ALOGV("Start decode"); - if (mUrl) { - status = MediaPlayer::decode( - NULL /* httpService */, - mUrl, - &sampleRate, - &numChannels, - &format, - mHeap, - &mSize); - } else { - status = MediaPlayer::decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format, - mHeap, &mSize); - ALOGV("close(%d)", mFd); - ::close(mFd); - mFd = -1; - } - if (status != NO_ERROR) { - ALOGE("Unable to load sample: %s", mUrl); - goto error; - } - ALOGV("pointer = %p, size = %zu, sampleRate = %u, numChannels = %d", - mHeap->getBase(), mSize, sampleRate, numChannels); - - if (sampleRate > kMaxSampleRate) { - ALOGE("Sample rate (%u) out of range", sampleRate); - status = BAD_VALUE; - goto error; - } - - if ((numChannels < 1) || (numChannels > 2)) { - ALOGE("Sample channel count (%d) out of range", numChannels); - status = BAD_VALUE; - goto error; - } - - mData = new MemoryBase(mHeap, 0, mSize); - mSampleRate = sampleRate; - mNumChannels = numChannels; - mFormat = format; - mState = READY; - return NO_ERROR; - -error: - mHeap.clear(); - return status; -} - - -void SoundChannel::init(SoundPool* soundPool) -{ - mSoundPool = soundPool; -} - -// call with sound pool lock held -void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume, - float rightVolume, int priority, int loop, float rate) -{ - sp<AudioTrack> oldTrack; - sp<AudioTrack> newTrack; - status_t status; - - { // scope for the lock - Mutex::Autolock lock(&mLock); - - ALOGV("SoundChannel::play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f," - " priority=%d, loop=%d, rate=%f", - this, sample->sampleID(), nextChannelID, leftVolume, rightVolume, - priority, loop, rate); - - // if not idle, this voice is being stolen - if (mState != IDLE) { - ALOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID); - mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate); - stop_l(); - return; - } - - // initialize track - size_t afFrameCount; - uint32_t afSampleRate; - audio_stream_type_t streamType = audio_attributes_to_stream_type(mSoundPool->attributes()); - if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { - afFrameCount = kDefaultFrameCount; - } - if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { - afSampleRate = kDefaultSampleRate; - } - int numChannels = sample->numChannels(); - 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; - size_t frameCount = 0; - - if (loop) { - frameCount = sample->size()/numChannels/ - ((sample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); - } - -#ifndef USE_SHARED_MEM_BUFFER - // Ensure minimum audio buffer size in case of short looped sample - if(frameCount < totalFrames) { - frameCount = totalFrames; - } -#endif - - // mToggle toggles each time a track is started on a given channel. - // The toggle is concatenated with the SoundChannel address and passed to AudioTrack - // as callback user data. This enables the detection of callbacks received from the old - // audio track while the new one is being started and avoids processing them with - // wrong audio audio buffer size (mAudioBufferSize) - unsigned long toggle = mToggle ^ 1; - void *userData = (void *)((unsigned long)this | toggle); - 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(), - channelMask, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData); -#else - newTrack = new AudioTrack(streamType, sampleRate, sample->format(), - channelMask, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData, - bufferFrames); -#endif - oldTrack = mAudioTrack; - status = newTrack->initCheck(); - if (status != NO_ERROR) { - ALOGE("Error creating AudioTrack"); - goto exit; - } - ALOGV("setVolume %p", newTrack.get()); - newTrack->setVolume(leftVolume, rightVolume); - newTrack->setLoop(0, frameCount, loop); - - // From now on, AudioTrack callbacks received with previous toggle value will be ignored. - mToggle = toggle; - mAudioTrack = newTrack; - mPos = 0; - mSample = sample; - mChannelID = nextChannelID; - mPriority = priority; - mLoop = loop; - mLeftVolume = leftVolume; - mRightVolume = rightVolume; - mNumChannels = numChannels; - mRate = rate; - clearNextEvent(); - mState = PLAYING; - mAudioTrack->start(); - mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize(); - } - -exit: - ALOGV("delete oldTrack %p", oldTrack.get()); - if (status != NO_ERROR) { - mAudioTrack.clear(); - } -} - -void SoundChannel::nextEvent() -{ - sp<Sample> sample; - int nextChannelID; - float leftVolume; - float rightVolume; - int priority; - int loop; - float rate; - - // check for valid event - { - Mutex::Autolock lock(&mLock); - nextChannelID = mNextEvent.channelID(); - if (nextChannelID == 0) { - ALOGV("stolen channel has no event"); - return; - } - - sample = mNextEvent.sample(); - leftVolume = mNextEvent.leftVolume(); - rightVolume = mNextEvent.rightVolume(); - priority = mNextEvent.priority(); - loop = mNextEvent.loop(); - rate = mNextEvent.rate(); - } - - ALOGV("Starting stolen channel %d -> %d", channelID(), nextChannelID); - play(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate); -} - -void SoundChannel::callback(int event, void* user, void *info) -{ - SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1)); - - channel->process(event, info, (unsigned long)user & 1); -} - -void SoundChannel::process(int event, void *info, unsigned long toggle) -{ - //ALOGV("process(%d)", mChannelID); - - Mutex::Autolock lock(&mLock); - - AudioTrack::Buffer* b = NULL; - if (event == AudioTrack::EVENT_MORE_DATA) { - b = static_cast<AudioTrack::Buffer *>(info); - } - - if (mToggle != toggle) { - ALOGV("process wrong toggle %p channel %d", this, mChannelID); - if (b != NULL) { - b->size = 0; - } - return; - } - - sp<Sample> sample = mSample; - -// ALOGV("SoundChannel::process event %d", event); - - if (event == AudioTrack::EVENT_MORE_DATA) { - - // check for stop state - if (b->size == 0) return; - - if (mState == IDLE) { - b->size = 0; - return; - } - - if (sample != 0) { - // fill buffer - uint8_t* q = (uint8_t*) b->i8; - size_t count = 0; - - if (mPos < (int)sample->size()) { - uint8_t* p = sample->data() + mPos; - count = sample->size() - mPos; - if (count > b->size) { - 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); - } else if (mPos < mAudioBufferSize) { - count = mAudioBufferSize - mPos; - if (count > b->size) { - count = b->size; - } - memset(q, 0, count); -// ALOGV("fill extra: q=%p, mPos=%u, b->size=%u, count=%d", q, mPos, b->size, count); - } - - mPos += count; - b->size = count; - //ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]); - } - } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END || - event == AudioTrack::EVENT_NEW_IAUDIOTRACK) { - ALOGV("process %p channel %d event %s", - this, mChannelID, (event == AudioTrack::EVENT_UNDERRUN) ? "UNDERRUN" : - (event == AudioTrack::EVENT_BUFFER_END) ? "BUFFER_END" : "NEW_IAUDIOTRACK"); - mSoundPool->addToStopList(this); - } else if (event == AudioTrack::EVENT_LOOP_END) { - ALOGV("End loop %p channel %d", this, mChannelID); - } else { - ALOGW("SoundChannel::process unexpected event %d", event); - } -} - - -// call with lock held -bool SoundChannel::doStop_l() -{ - if (mState != IDLE) { - setVolume_l(0, 0); - ALOGV("stop"); - mAudioTrack->stop(); - mSample.clear(); - mState = IDLE; - mPriority = IDLE_PRIORITY; - return true; - } - return false; -} - -// call with lock held and sound pool lock held -void SoundChannel::stop_l() -{ - if (doStop_l()) { - mSoundPool->done_l(this); - } -} - -// call with sound pool lock held -void SoundChannel::stop() -{ - bool stopped; - { - Mutex::Autolock lock(&mLock); - stopped = doStop_l(); - } - - if (stopped) { - mSoundPool->done_l(this); - } -} - -//FIXME: Pause is a little broken right now -void SoundChannel::pause() -{ - Mutex::Autolock lock(&mLock); - if (mState == PLAYING) { - ALOGV("pause track"); - mState = PAUSED; - mAudioTrack->pause(); - } -} - -void SoundChannel::autoPause() -{ - Mutex::Autolock lock(&mLock); - if (mState == PLAYING) { - ALOGV("pause track"); - mState = PAUSED; - mAutoPaused = true; - mAudioTrack->pause(); - } -} - -void SoundChannel::resume() -{ - Mutex::Autolock lock(&mLock); - if (mState == PAUSED) { - ALOGV("resume track"); - mState = PLAYING; - mAutoPaused = false; - mAudioTrack->start(); - } -} - -void SoundChannel::autoResume() -{ - Mutex::Autolock lock(&mLock); - if (mAutoPaused && (mState == PAUSED)) { - ALOGV("resume track"); - mState = PLAYING; - mAutoPaused = false; - mAudioTrack->start(); - } -} - -void SoundChannel::setRate(float rate) -{ - Mutex::Autolock lock(&mLock); - if (mAudioTrack != NULL && mSample != 0) { - uint32_t sampleRate = uint32_t(float(mSample->sampleRate()) * rate + 0.5); - mAudioTrack->setSampleRate(sampleRate); - mRate = rate; - } -} - -// call with lock held -void SoundChannel::setVolume_l(float leftVolume, float rightVolume) -{ - mLeftVolume = leftVolume; - mRightVolume = rightVolume; - if (mAudioTrack != NULL) - mAudioTrack->setVolume(leftVolume, rightVolume); -} - -void SoundChannel::setVolume(float leftVolume, float rightVolume) -{ - Mutex::Autolock lock(&mLock); - setVolume_l(leftVolume, rightVolume); -} - -void SoundChannel::setLoop(int loop) -{ - Mutex::Autolock lock(&mLock); - if (mAudioTrack != NULL && mSample != 0) { - uint32_t loopEnd = mSample->size()/mNumChannels/ - ((mSample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); - mAudioTrack->setLoop(0, loopEnd, loop); - mLoop = loop; - } -} - -SoundChannel::~SoundChannel() -{ - ALOGV("SoundChannel destructor %p", this); - { - Mutex::Autolock lock(&mLock); - clearNextEvent(); - doStop_l(); - } - // do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack - // callback thread to exit which may need to execute process() and acquire the mLock. - mAudioTrack.clear(); -} - -void SoundChannel::dump() -{ - ALOGV("mState = %d mChannelID=%d, mNumChannels=%d, mPos = %d, mPriority=%d, mLoop=%d", - mState, mChannelID, mNumChannels, mPos, mPriority, mLoop); -} - -void SoundEvent::set(const sp<Sample>& sample, int channelID, float leftVolume, - float rightVolume, int priority, int loop, float rate) -{ - mSample = sample; - mChannelID = channelID; - mLeftVolume = leftVolume; - mRightVolume = rightVolume; - mPriority = priority; - mLoop = loop; - mRate =rate; -} - -} // end namespace android diff --git a/media/libmedia/SoundPoolThread.cpp b/media/libmedia/SoundPoolThread.cpp deleted file mode 100644 index ba3b482..0000000 --- a/media/libmedia/SoundPoolThread.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "SoundPoolThread" -#include "utils/Log.h" - -#include "SoundPoolThread.h" - -namespace android { - -void SoundPoolThread::write(SoundPoolMsg msg) { - Mutex::Autolock lock(&mLock); - while (mMsgQueue.size() >= maxMessages) { - mCondition.wait(mLock); - } - - // if thread is quitting, don't add to queue - if (mRunning) { - mMsgQueue.push(msg); - mCondition.signal(); - } -} - -const SoundPoolMsg SoundPoolThread::read() { - Mutex::Autolock lock(&mLock); - while (mMsgQueue.size() == 0) { - mCondition.wait(mLock); - } - SoundPoolMsg msg = mMsgQueue[0]; - mMsgQueue.removeAt(0); - mCondition.signal(); - return msg; -} - -void SoundPoolThread::quit() { - Mutex::Autolock lock(&mLock); - if (mRunning) { - mRunning = false; - mMsgQueue.clear(); - mMsgQueue.push(SoundPoolMsg(SoundPoolMsg::KILL, 0)); - mCondition.signal(); - mCondition.wait(mLock); - } - ALOGV("return from quit"); -} - -SoundPoolThread::SoundPoolThread(SoundPool* soundPool) : - mSoundPool(soundPool) -{ - mMsgQueue.setCapacity(maxMessages); - if (createThreadEtc(beginThread, this, "SoundPoolThread")) { - mRunning = true; - } -} - -SoundPoolThread::~SoundPoolThread() -{ - quit(); -} - -int SoundPoolThread::beginThread(void* arg) { - ALOGV("beginThread"); - SoundPoolThread* soundPoolThread = (SoundPoolThread*)arg; - return soundPoolThread->run(); -} - -int SoundPoolThread::run() { - ALOGV("run"); - for (;;) { - SoundPoolMsg msg = read(); - ALOGV("Got message m=%d, mData=%d", msg.mMessageType, msg.mData); - switch (msg.mMessageType) { - case SoundPoolMsg::KILL: - ALOGV("goodbye"); - return NO_ERROR; - case SoundPoolMsg::LOAD_SAMPLE: - doLoadSample(msg.mData); - break; - default: - ALOGW("run: Unrecognized message %d\n", - msg.mMessageType); - break; - } - } -} - -void SoundPoolThread::loadSample(int sampleID) { - write(SoundPoolMsg(SoundPoolMsg::LOAD_SAMPLE, sampleID)); -} - -void SoundPoolThread::doLoadSample(int sampleID) { - sp <Sample> sample = mSoundPool->findSample(sampleID); - status_t status = -1; - if (sample != 0) { - status = sample->doLoad(); - } - mSoundPool->notify(SoundPoolEvent(SoundPoolEvent::SAMPLE_LOADED, sampleID, status)); -} - -} // end namespace android diff --git a/media/libmedia/SoundPoolThread.h b/media/libmedia/SoundPoolThread.h deleted file mode 100644 index 7e96900..0000000 --- a/media/libmedia/SoundPoolThread.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SOUNDPOOLTHREAD_H_ -#define SOUNDPOOLTHREAD_H_ - -#include <utils/threads.h> -#include <utils/Vector.h> -#include <media/AudioTrack.h> - -#include <media/SoundPool.h> - -namespace android { - -class SoundPoolMsg { -public: - enum MessageType { INVALID, KILL, LOAD_SAMPLE }; - SoundPoolMsg() : mMessageType(INVALID), mData(0) {} - SoundPoolMsg(MessageType MessageType, int data) : - mMessageType(MessageType), mData(data) {} - uint16_t mMessageType; - uint16_t mData; -}; - -/* - * This class handles background requests from the SoundPool - */ -class SoundPoolThread { -public: - SoundPoolThread(SoundPool* SoundPool); - ~SoundPoolThread(); - void loadSample(int sampleID); - void quit(); - void write(SoundPoolMsg msg); - -private: - static const size_t maxMessages = 5; - - static int beginThread(void* arg); - int run(); - void doLoadSample(int sampleID); - const SoundPoolMsg read(); - - Mutex mLock; - Condition mCondition; - Vector<SoundPoolMsg> mMsgQueue; - SoundPool* mSoundPool; - bool mRunning; -}; - -} // end namespace android - -#endif /*SOUNDPOOLTHREAD_H_*/ diff --git a/media/libmedia/StringArray.cpp b/media/libmedia/StringArray.cpp index 5f5b57a..477e3fd 100644 --- a/media/libmedia/StringArray.cpp +++ b/media/libmedia/StringArray.cpp @@ -16,7 +16,7 @@ // // Sortable array of strings. STL-ish, but STL-free. -// +// #include <stdlib.h> #include <string.h> diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 61b6d36..2cc4685 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -28,718 +28,718 @@ namespace android { // Descriptors for all available tones (See ToneGenerator::ToneDescriptor class declaration for details) const ToneGenerator::ToneDescriptor ToneGenerator::sToneDescriptors[] = { - { segments: {{ duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 941, 0 }, 0, 0}, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_0 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 697, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_1 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 697, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_2 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 697, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_3 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 770, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_4 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 770, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_5 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 770, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_6 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 852, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_7 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 852, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_8 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 852, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_9 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 941, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_S - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 941, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_P - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 697, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_A - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 770, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_B - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 852, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_C - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 941, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_DTMF_D - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 425, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_SUP_DIAL - { segments: { { duration: 500 , waveFreq: { 425, 0 }, 0, 0}, - { duration: 500, waveFreq: { 0 }, 0, 0}, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_SUP_BUSY - { segments: { { duration: 200, waveFreq: { 425, 0 }, 0, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_SUP_CONGESTION - { segments: { { duration: 200, waveFreq: { 425, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_SUP_RADIO_ACK - { segments: { { duration: 200, waveFreq: { 425, 0 }, 0, 0}, - { duration: 200, waveFreq: { 0 }, 0, 0}, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 2, - repeatSegment: 0 }, // TONE_SUP_RADIO_NOTAVAIL - { segments: { { duration: 330, waveFreq: { 950, 1400, 1800, 0 }, 0, 0}, - { duration: 1000, waveFreq: { 0 }, 0, 0}, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_SUP_ERROR - { segments: { { duration: 200, waveFreq: { 425, 0 }, 0, 0 }, - { duration: 600, waveFreq: { 0 }, 0, 0 }, - { duration: 200, waveFreq: { 425, 0 }, 0, 0 }, - { duration: 3000, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_SUP_CALL_WAITING - { segments: { { duration: 1000, waveFreq: { 425, 0 }, 0, 0 }, - { duration: 4000, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_SUP_RINGTONE - { segments: { { duration: 40, waveFreq: { 400, 1200, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_PROP_BEEP - { segments: { { duration: 100, waveFreq: { 1200, 0 }, 0, 0 }, - { duration: 100, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 1, - repeatSegment: 0 }, // TONE_PROP_ACK - { segments: { { duration: 400, waveFreq: { 300, 400, 500, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_PROP_NACK - { segments: { { duration: 200, waveFreq: { 400, 1200, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_PROP_PROMPT - { segments: { { duration: 40, waveFreq: { 400, 1200, 0 }, 0, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 40, waveFreq: { 400, 1200, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_PROP_BEEP2 - { segments: { { duration: 250, waveFreq: { 440, 0 }, 0, 0 }, - { duration: 250, waveFreq: { 620, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_SUP_INTERCEPT - { segments: { { duration: 250, waveFreq: { 440, 0 }, 0, 0 }, - { duration: 250, waveFreq: { 620, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 7, - repeatSegment: 0 }, // TONE_SUP_INTERCEPT_ABBREV - { segments: { { duration: 250, waveFreq: { 480, 620, 0 }, 0, 0 }, - { duration: 250, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 7, - repeatSegment: 0 }, // TONE_SUP_CONGESTION_ABBREV - { segments: { { duration: 100, waveFreq: { 350, 440, 0 }, 0, 0 }, - { duration: 100, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 2, - repeatSegment: 0 }, // TONE_SUP_CONFIRM - { segments: { { duration: 100, waveFreq: { 480, 0 }, 0, 0 }, - { duration: 100, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 3, - repeatSegment: 0 }, // TONE_SUP_PIP - { segments: {{ duration: ToneGenerator::TONEGEN_INF, waveFreq: { 425, 0 }, 0, 0}, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_DIAL_TONE_LITE - { segments: { { duration: 2000, waveFreq: { 440, 480, 0 }, 0, 0 }, - { duration: 4000, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_NETWORK_USA_RINGBACK - { segments: { { duration: 250, waveFreq: { 440, 0 }, 0, 0 }, - { duration: 250, waveFreq: { 620, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_INTERCEPT - { segments: { { duration: 250, waveFreq: { 440, 0 }, 0, 0 }, - { duration: 250, waveFreq: { 620, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_ABBR_INTERCEPT - { segments: { { duration: 250, waveFreq: { 480, 620, 0 }, 0, 0 }, - { duration: 250, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_REORDER - { segments: { { duration: 250, waveFreq: { 480, 620, 0 }, 0, 0 }, - { duration: 250, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 7, - repeatSegment: 0 }, // TONE_CDMA_ABBR_REORDER - { segments: { { duration: 500, waveFreq: { 480, 620, 0 }, 0, 0 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_NETWORK_BUSY - { segments: { { duration: 100, waveFreq: { 350, 440, 0 }, 0, 0 }, - { duration: 100, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 2, - repeatSegment: 0 }, // TONE_CDMA_CONFIRM - { segments: { { duration: 500, waveFreq: { 660, 1000, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_ANSWER - { segments: { { duration: 300, waveFreq: { 440, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_NETWORK_CALLWAITING - { segments: { { duration: 100, waveFreq: { 480, 0 }, 0, 0 }, - { duration: 100, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: 3, - repeatSegment: 0 }, // TONE_CDMA_PIP - - { segments: { { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, - { duration: 64, waveFreq: { 2556, 0}, 19, 0}, - { duration: 32, waveFreq: { 2091, 0}, 0, 0}, - { duration: 48, waveFreq: { 2556, 0}, 0, 0}, - { duration: 4000, waveFreq: { 0 }, 0, 0}, - { duration: 0, waveFreq: { 0 }, 0, 0}}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL - { segments: { { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, - { duration: 64, waveFreq: { 2556, 0}, 7, 0 }, - { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, - { duration: 400, waveFreq: { 0 }, 0, 0 }, - { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, - { duration: 64, waveFreq: { 2556, 0}, 7, 4 }, - { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, - { duration: 4000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP - { segments: { { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, - { duration: 64, waveFreq: { 2556, 0}, 3, 0 }, - { duration: 16, waveFreq: { 2091, 0}, 0, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, - { duration: 64, waveFreq: { 2556, 0}, 3, 4 }, - { duration: 16, waveFreq: { 2091, 0}, 0, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_SP_PRI - { segments: { { duration: 0, waveFreq: { 0 }, 0, 0} }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT3 - { segments: { { duration: 32, waveFreq: { 2091, 0 }, 0, 0 }, - { duration: 64, waveFreq: { 2556, 0 }, 4, 0 }, - { duration: 20, waveFreq: { 2091, 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 } , 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PING_RING - { segments: { { duration: 0, waveFreq: { 0 }, 0, 0} }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT5 - { segments: { { duration: 0, waveFreq: { 0 }, 0, 0} }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT6 - { segments: { { duration: 0, waveFreq: { 0 }, 0, 0} }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT7 - - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 39, 0 }, - { duration: 4000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_L - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 39, 0 }, - { duration: 4000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_L - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 39, 0 }, - { duration: 4000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_L - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 15, 0 }, - { duration: 400, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_SS - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 15, 0 }, - { duration: 400, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_SS - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 15, 0 }, - { duration: 400, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_SS - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 15, 6 }, - { duration: 4000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_SSL - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 15, 6 }, - { duration: 4000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_SSL - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 15, 6 }, - { duration: 4000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_SSL - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 19, 0 }, - { duration: 1000, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 19, 3 }, - { duration: 3000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_SS_2 - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 19, 0 }, - { duration: 1000, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 19, 3 }, - { duration: 3000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_SS_2 - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 19, 0 }, - { duration: 1000, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 19, 3 }, - { duration: 3000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_SS_2 - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 9, 0 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 19, 3 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 9, 6 }, - { duration: 3000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_SLS - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 9, 0 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 19, 3 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 9, 6 }, - { duration: 3000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_SLS - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 9, 0 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 19, 3 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 9, 6 }, - { duration: 3000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_SLS - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 9, 0 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 9, 3 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 9, 6 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 9, 9 }, - { duration: 2500, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_S_X4 - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 9, 0 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 9, 3 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 9, 6 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 9, 9 }, - { duration: 2500, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_S_X4 - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 9, 0 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 9, 3 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 9, 6 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 9, 9 }, - { duration: 2500, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_S_X4 - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 19, 0 }, - { duration: 2000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_PBX_L - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 19, 0 }, - { duration: 2000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_PBX_L - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 19, 0 }, - { duration: 2000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_PBX_L - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 3 }, - { duration: 2000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_PBX_SS - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 3 }, - { duration: 2000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_PBX_SS - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 3 }, - { duration: 2000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_PBX_SS - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 15, 6 }, - { duration: 1000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_PBX_SSL - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 15, 6 }, - { duration: 1000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_PBX_SSL - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 15, 6 }, - { duration: 1000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_PBX_SSL - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 15, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 6 }, - { duration: 1000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_PBX_SLS - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 15, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 6 }, - { duration: 1000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_PBX_SLS - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 15, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 6 }, - { duration: 1000, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_PBX_SLS - { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 6 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 4000, 0 }, 7, 9 }, - { duration: 800, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_HIGH_PBX_S_X4 - { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 6 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 2900, 0 }, 7, 9 }, - { duration: 800, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_MED_PBX_S_X4 - { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 0 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 3 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 6 }, - { duration: 200, waveFreq: { 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, - { duration: 25, waveFreq: { 1450, 0 }, 7, 9 }, - { duration: 800, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_LOW_PBX_S_X4 - - { segments: { { duration: 62, waveFreq: { 1109, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 784, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 740, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 622, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 1109, 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_ALERT_NETWORK_LITE - { segments: { { duration: 62, waveFreq: { 1245, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 659, 0 }, 2, 0 }, - { duration: 62, waveFreq: { 1245, 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_ALERT_AUTOREDIAL_LITE - { segments: { { duration: 400, waveFreq: { 1150, 770, 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_ONE_MIN_BEEP - { segments: { { duration: 120, waveFreq: { 941, 1477, 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_KEYPAD_VOLUME_KEY_LITE - { segments: { { duration: 375, waveFreq: { 587, 0 }, 0, 0 }, - { duration: 125, waveFreq: { 1175, 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_PRESSHOLDKEY_LITE - { segments: { { duration: 62, waveFreq: { 587, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 784, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 831, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 784, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 1109, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 784, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 831, 0 }, 0, 0 }, - { duration: 62, waveFreq: { 784, 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_ALERT_INCALL_LITE - { segments: { { duration: 125, waveFreq: { 941, 0 }, 0, 0 }, - { duration: 10, waveFreq: { 0 }, 2, 0 }, - { duration: 4990, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_CDMA_EMERGENCY_RINGBACK - { segments: { { duration: 125, waveFreq: { 1319, 0 }, 0, 0 }, - { duration: 125, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 2, - repeatSegment: 0 }, // TONE_CDMA_ALERT_CALL_GUARD - { segments: { { duration: 125, waveFreq: { 1047, 0 }, 0, 0 }, - { duration: 125, waveFreq: { 370, 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_SOFT_ERROR_LITE - { segments: { { duration: 125, waveFreq: { 1480, 0 }, 0, 0 }, - { duration: 125, waveFreq: { 1397, 0 }, 0, 0 }, - { duration: 125, waveFreq: { 784, 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 } }, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_CALLDROP_LITE - - { segments: { { duration: 500, waveFreq: { 425, 0 }, 0, 0 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_NETWORK_BUSY_ONE_SHOT - { segments: { { duration: 400, waveFreq: { 1150, 770 }, 0, 0 }, - { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_ABBR_ALERT - { segments: { { duration: 0, waveFreq: { 0 }, 0, 0 }}, - repeatCnt: 0, - repeatSegment: 0 }, // TONE_CDMA_SIGNAL_OFF - - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 350, 440, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_ANSI_DIAL - { segments: { { duration: 500, waveFreq: { 480, 620, 0 }, 0, 0 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_ANSI_BUSY - { segments: { { duration: 250, waveFreq: { 480, 620, 0 }, 0, 0 }, - { duration: 250, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_ANSI_CONGESTION - { segments: { { duration: 300, waveFreq: { 440, 0 }, 0, 0 }, - { duration: 9700, waveFreq: { 0 }, 0, 0 }, - { duration: 100, waveFreq: { 440, 0 }, 0, 0 }, - { duration: 100, waveFreq: { 0 }, 0, 0 }, - { duration: 100, waveFreq: { 440, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 1 }, // TONE_ANSI_CALL_WAITING - { segments: { { duration: 2000, waveFreq: { 440, 480, 0 }, 0, 0 }, - { duration: 4000, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_ANSI_RINGTONE - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 400, 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_JAPAN_DIAL - { segments: { { duration: 500, waveFreq: { 400, 0 }, 0, 0 }, - { duration: 500, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_JAPAN_BUSY - { segments: { { duration: 1000, waveFreq: { 400, 0 }, 0, 0 }, - { duration: 2000, waveFreq: { 0 }, 0, 0 }, - { duration: 0 , waveFreq: { 0 }, 0, 0}}, - repeatCnt: ToneGenerator::TONEGEN_INF, - repeatSegment: 0 }, // TONE_JAPAN_RADIO_ACK + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1336, 941, 0 }, 0, 0}, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_0 + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1209, 697, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_1 + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1336, 697, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_2 + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1477, 697, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_3 + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1209, 770, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_4 + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1336, 770, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_5 + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1477, 770, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_6 + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1209, 852, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_7 + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1336, 852, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_8 + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1477, 852, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_9 + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1209, 941, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_S + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1477, 941, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_P + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1633, 697, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_A + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1633, 770, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_B + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1633, 852, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_C + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1633, 941, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_DTMF_D + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 425, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_SUP_DIAL + { .segments = { { .duration = 500 , .waveFreq = { 425, 0 }, 0, 0}, + { .duration = 500, .waveFreq = { 0 }, 0, 0}, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_SUP_BUSY + { .segments = { { .duration = 200, .waveFreq = { 425, 0 }, 0, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_SUP_CONGESTION + { .segments = { { .duration = 200, .waveFreq = { 425, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_SUP_RADIO_ACK + { .segments = { { .duration = 200, .waveFreq = { 425, 0 }, 0, 0}, + { .duration = 200, .waveFreq = { 0 }, 0, 0}, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 2, + .repeatSegment = 0 }, // TONE_SUP_RADIO_NOTAVAIL + { .segments = { { .duration = 330, .waveFreq = { 950, 1400, 1800, 0 }, 0, 0}, + { .duration = 1000, .waveFreq = { 0 }, 0, 0}, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_SUP_ERROR + { .segments = { { .duration = 200, .waveFreq = { 425, 0 }, 0, 0 }, + { .duration = 600, .waveFreq = { 0 }, 0, 0 }, + { .duration = 200, .waveFreq = { 425, 0 }, 0, 0 }, + { .duration = 3000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_SUP_CALL_WAITING + { .segments = { { .duration = 1000, .waveFreq = { 425, 0 }, 0, 0 }, + { .duration = 4000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_SUP_RINGTONE + { .segments = { { .duration = 40, .waveFreq = { 400, 1200, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_PROP_BEEP + { .segments = { { .duration = 100, .waveFreq = { 1200, 0 }, 0, 0 }, + { .duration = 100, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 1, + .repeatSegment = 0 }, // TONE_PROP_ACK + { .segments = { { .duration = 400, .waveFreq = { 300, 400, 500, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_PROP_NACK + { .segments = { { .duration = 200, .waveFreq = { 400, 1200, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_PROP_PROMPT + { .segments = { { .duration = 40, .waveFreq = { 400, 1200, 0 }, 0, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 40, .waveFreq = { 400, 1200, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_PROP_BEEP2 + { .segments = { { .duration = 250, .waveFreq = { 440, 0 }, 0, 0 }, + { .duration = 250, .waveFreq = { 620, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_SUP_INTERCEPT + { .segments = { { .duration = 250, .waveFreq = { 440, 0 }, 0, 0 }, + { .duration = 250, .waveFreq = { 620, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 7, + .repeatSegment = 0 }, // TONE_SUP_INTERCEPT_ABBREV + { .segments = { { .duration = 250, .waveFreq = { 480, 620, 0 }, 0, 0 }, + { .duration = 250, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 7, + .repeatSegment = 0 }, // TONE_SUP_CONGESTION_ABBREV + { .segments = { { .duration = 100, .waveFreq = { 350, 440, 0 }, 0, 0 }, + { .duration = 100, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 2, + .repeatSegment = 0 }, // TONE_SUP_CONFIRM + { .segments = { { .duration = 100, .waveFreq = { 480, 0 }, 0, 0 }, + { .duration = 100, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 3, + .repeatSegment = 0 }, // TONE_SUP_PIP + { .segments = {{ .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 425, 0 }, 0, 0}, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_DIAL_TONE_LITE + { .segments = { { .duration = 2000, .waveFreq = { 440, 480, 0 }, 0, 0 }, + { .duration = 4000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_NETWORK_USA_RINGBACK + { .segments = { { .duration = 250, .waveFreq = { 440, 0 }, 0, 0 }, + { .duration = 250, .waveFreq = { 620, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_INTERCEPT + { .segments = { { .duration = 250, .waveFreq = { 440, 0 }, 0, 0 }, + { .duration = 250, .waveFreq = { 620, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_ABBR_INTERCEPT + { .segments = { { .duration = 250, .waveFreq = { 480, 620, 0 }, 0, 0 }, + { .duration = 250, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_REORDER + { .segments = { { .duration = 250, .waveFreq = { 480, 620, 0 }, 0, 0 }, + { .duration = 250, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 7, + .repeatSegment = 0 }, // TONE_CDMA_ABBR_REORDER + { .segments = { { .duration = 500, .waveFreq = { 480, 620, 0 }, 0, 0 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_NETWORK_BUSY + { .segments = { { .duration = 100, .waveFreq = { 350, 440, 0 }, 0, 0 }, + { .duration = 100, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 2, + .repeatSegment = 0 }, // TONE_CDMA_CONFIRM + { .segments = { { .duration = 500, .waveFreq = { 660, 1000, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_ANSWER + { .segments = { { .duration = 300, .waveFreq = { 440, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_NETWORK_CALLWAITING + { .segments = { { .duration = 100, .waveFreq = { 480, 0 }, 0, 0 }, + { .duration = 100, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 3, + .repeatSegment = 0 }, // TONE_CDMA_PIP + + { .segments = { { .duration = 32, .waveFreq = { 2091, 0}, 0, 0 }, + { .duration = 64, .waveFreq = { 2556, 0}, 19, 0}, + { .duration = 32, .waveFreq = { 2091, 0}, 0, 0}, + { .duration = 48, .waveFreq = { 2556, 0}, 0, 0}, + { .duration = 4000, .waveFreq = { 0 }, 0, 0}, + { .duration = 0, .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL + { .segments = { { .duration = 32, .waveFreq = { 2091, 0}, 0, 0 }, + { .duration = 64, .waveFreq = { 2556, 0}, 7, 0 }, + { .duration = 32, .waveFreq = { 2091, 0}, 0, 0 }, + { .duration = 400, .waveFreq = { 0 }, 0, 0 }, + { .duration = 32, .waveFreq = { 2091, 0}, 0, 0 }, + { .duration = 64, .waveFreq = { 2556, 0}, 7, 4 }, + { .duration = 32, .waveFreq = { 2091, 0}, 0, 0 }, + { .duration = 4000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP + { .segments = { { .duration = 32, .waveFreq = { 2091, 0}, 0, 0 }, + { .duration = 64, .waveFreq = { 2556, 0}, 3, 0 }, + { .duration = 16, .waveFreq = { 2091, 0}, 0, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 32, .waveFreq = { 2091, 0}, 0, 0 }, + { .duration = 64, .waveFreq = { 2556, 0}, 3, 4 }, + { .duration = 16, .waveFreq = { 2091, 0}, 0, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_SP_PRI + { .segments = { { .duration = 0, .waveFreq = { 0 }, 0, 0} }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT3 + { .segments = { { .duration = 32, .waveFreq = { 2091, 0 }, 0, 0 }, + { .duration = 64, .waveFreq = { 2556, 0 }, 4, 0 }, + { .duration = 20, .waveFreq = { 2091, 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 } , 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PING_RING + { .segments = { { .duration = 0, .waveFreq = { 0 }, 0, 0} }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT5 + { .segments = { { .duration = 0, .waveFreq = { 0 }, 0, 0} }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT6 + { .segments = { { .duration = 0, .waveFreq = { 0 }, 0, 0} }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT7 + + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 39, 0 }, + { .duration = 4000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_L + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 39, 0 }, + { .duration = 4000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_L + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 39, 0 }, + { .duration = 4000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_L + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 15, 0 }, + { .duration = 400, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_SS + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 15, 0 }, + { .duration = 400, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_SS + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 15, 0 }, + { .duration = 400, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_SS + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 15, 6 }, + { .duration = 4000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_SSL + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 15, 6 }, + { .duration = 4000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_SSL + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 15, 6 }, + { .duration = 4000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_SSL + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 19, 0 }, + { .duration = 1000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 19, 3 }, + { .duration = 3000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_SS_2 + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 19, 0 }, + { .duration = 1000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 19, 3 }, + { .duration = 3000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_SS_2 + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 19, 0 }, + { .duration = 1000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 19, 3 }, + { .duration = 3000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_SS_2 + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 9, 0 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 19, 3 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 9, 6 }, + { .duration = 3000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_SLS + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 9, 0 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 19, 3 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 9, 6 }, + { .duration = 3000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_SLS + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 9, 0 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 19, 3 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 9, 6 }, + { .duration = 3000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_SLS + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 9, 0 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 9, 3 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 9, 6 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 9, 9 }, + { .duration = 2500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_S_X4 + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 9, 0 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 9, 3 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 9, 6 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 9, 9 }, + { .duration = 2500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_S_X4 + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 9, 0 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 9, 3 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 9, 6 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 9, 9 }, + { .duration = 2500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_S_X4 + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 19, 0 }, + { .duration = 2000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_PBX_L + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 19, 0 }, + { .duration = 2000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_PBX_L + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 19, 0 }, + { .duration = 2000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_PBX_L + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 3 }, + { .duration = 2000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_PBX_SS + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 3 }, + { .duration = 2000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_PBX_SS + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 3 }, + { .duration = 2000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_PBX_SS + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 15, 6 }, + { .duration = 1000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_PBX_SSL + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 15, 6 }, + { .duration = 1000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_PBX_SSL + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 15, 6 }, + { .duration = 1000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_PBX_SSL + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 15, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 6 }, + { .duration = 1000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_PBX_SLS + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 15, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 6 }, + { .duration = 1000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_PBX_SLS + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 15, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 6 }, + { .duration = 1000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_PBX_SLS + { .segments = { { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 6 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 3700, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 4000, 0 }, 7, 9 }, + { .duration = 800, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_HIGH_PBX_S_X4 + { .segments = { { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 6 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2600, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 2900, 0 }, 7, 9 }, + { .duration = 800, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_MED_PBX_S_X4 + { .segments = { { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 0 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 3 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 6 }, + { .duration = 200, .waveFreq = { 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1300, 0 }, 0, 0 }, + { .duration = 25, .waveFreq = { 1450, 0 }, 7, 9 }, + { .duration = 800, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_LOW_PBX_S_X4 + + { .segments = { { .duration = 62, .waveFreq = { 1109, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 784, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 740, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 622, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 1109, 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_ALERT_NETWORK_LITE + { .segments = { { .duration = 62, .waveFreq = { 1245, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 659, 0 }, 2, 0 }, + { .duration = 62, .waveFreq = { 1245, 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_ALERT_AUTOREDIAL_LITE + { .segments = { { .duration = 400, .waveFreq = { 1150, 770, 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_ONE_MIN_BEEP + { .segments = { { .duration = 120, .waveFreq = { 941, 1477, 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_KEYPAD_VOLUME_KEY_LITE + { .segments = { { .duration = 375, .waveFreq = { 587, 0 }, 0, 0 }, + { .duration = 125, .waveFreq = { 1175, 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_PRESSHOLDKEY_LITE + { .segments = { { .duration = 62, .waveFreq = { 587, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 784, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 831, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 784, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 1109, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 784, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 831, 0 }, 0, 0 }, + { .duration = 62, .waveFreq = { 784, 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_ALERT_INCALL_LITE + { .segments = { { .duration = 125, .waveFreq = { 941, 0 }, 0, 0 }, + { .duration = 10, .waveFreq = { 0 }, 2, 0 }, + { .duration = 4990, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_CDMA_EMERGENCY_RINGBACK + { .segments = { { .duration = 125, .waveFreq = { 1319, 0 }, 0, 0 }, + { .duration = 125, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 2, + .repeatSegment = 0 }, // TONE_CDMA_ALERT_CALL_GUARD + { .segments = { { .duration = 125, .waveFreq = { 1047, 0 }, 0, 0 }, + { .duration = 125, .waveFreq = { 370, 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_SOFT_ERROR_LITE + { .segments = { { .duration = 125, .waveFreq = { 1480, 0 }, 0, 0 }, + { .duration = 125, .waveFreq = { 1397, 0 }, 0, 0 }, + { .duration = 125, .waveFreq = { 784, 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 } }, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_CALLDROP_LITE + + { .segments = { { .duration = 500, .waveFreq = { 425, 0 }, 0, 0 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_NETWORK_BUSY_ONE_SHOT + { .segments = { { .duration = 400, .waveFreq = { 1150, 770 }, 0, 0 }, + { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_ABBR_ALERT + { .segments = { { .duration = 0, .waveFreq = { 0 }, 0, 0 }}, + .repeatCnt = 0, + .repeatSegment = 0 }, // TONE_CDMA_SIGNAL_OFF + + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 350, 440, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_ANSI_DIAL + { .segments = { { .duration = 500, .waveFreq = { 480, 620, 0 }, 0, 0 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_ANSI_BUSY + { .segments = { { .duration = 250, .waveFreq = { 480, 620, 0 }, 0, 0 }, + { .duration = 250, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_ANSI_CONGESTION + { .segments = { { .duration = 300, .waveFreq = { 440, 0 }, 0, 0 }, + { .duration = 9700, .waveFreq = { 0 }, 0, 0 }, + { .duration = 100, .waveFreq = { 440, 0 }, 0, 0 }, + { .duration = 100, .waveFreq = { 0 }, 0, 0 }, + { .duration = 100, .waveFreq = { 440, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 1 }, // TONE_ANSI_CALL_WAITING + { .segments = { { .duration = 2000, .waveFreq = { 440, 480, 0 }, 0, 0 }, + { .duration = 4000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_ANSI_RINGTONE + { .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 400, 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_JAPAN_DIAL + { .segments = { { .duration = 500, .waveFreq = { 400, 0 }, 0, 0 }, + { .duration = 500, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_JAPAN_BUSY + { .segments = { { .duration = 1000, .waveFreq = { 400, 0 }, 0, 0 }, + { .duration = 2000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = ToneGenerator::TONEGEN_INF, + .repeatSegment = 0 }, // TONE_JAPAN_RADIO_ACK diff --git a/media/libmedia/docs/Makefile b/media/libmedia/docs/Makefile new file mode 100644 index 0000000..bddbc9b --- /dev/null +++ b/media/libmedia/docs/Makefile @@ -0,0 +1,2 @@ +paused.png : paused.dot + dot -Tpng < $< > $@ diff --git a/media/libmedia/docs/paused.dot b/media/libmedia/docs/paused.dot new file mode 100644 index 0000000..11e1777 --- /dev/null +++ b/media/libmedia/docs/paused.dot @@ -0,0 +1,85 @@ +digraph paused { +initial [label="INITIAL\n\ +mIgnoreNextPausedInt = false\n\ +mPaused = false\n\ +mPausedInt = false"]; + +resume_body [label="mIgnoreNextPausedInt = true\nif (mPaused || mPausedInt)"]; +resume_paused [label="mPaused = false\nmPausedInt = false\nsignal()"]; +resume_paused -> resume_merged; +resume_merged [label="return"]; + +Application -> ATstop; +ATstop [label="AudioTrack::stop()"]; +ATstop -> pause; +Application -> ATpause; +ATpause [label="AudioTrack::pause()"]; +ATpause -> pause; +ATstart -> resume; +ATstart [label="AudioTrack::start()"]; +destructor [label="~AudioTrack()"]; +destructor -> requestExit; +requestExit [label="AudioTrackThread::requestExit()"]; +requestExit -> resume; +Application -> ATsetMarkerPosition +ATsetMarkerPosition [label="AudioTrack::setMarkerPosition()\n[sets marker variables]"]; +ATsetMarkerPosition -> ATTwake +Application -> ATsetPositionUpdatePeriod +ATsetPositionUpdatePeriod [label="AudioTrack::setPositionUpdatePeriod()\n[sets update period variables]"]; +ATsetPositionUpdatePeriod -> ATTwake +Application -> ATstart; + +resume [label="AudioTrackThread::resume()"]; +resume -> resume_body; + +resume_body -> resume_paused [label="true"]; +resume_body -> resume_merged [label="false"]; + +ATTwake [label="AudioTrackThread::wake()\nif (!mPaused && mPausedInt && mPausedNs > 0)"]; +ATTwake-> ATTWake_wakeable [label="true"]; +ATTWake_wakeable [label="mIgnoreNextPausedInt = true\nmPausedInt = false\nsignal()"]; +ATTwake-> ATTWake_cannotwake [label="false"] +ATTWake_cannotwake [label="ignore"]; + +pause [label="mPaused = true"]; +pause -> return; + +threadLoop [label="AudioTrackThread::threadLoop()\nENTRY"]; +threadLoop -> threadLoop_1; +threadLoop_1 [label="if (mPaused)"]; +threadLoop_1 -> threadLoop_1_true [label="true"]; +threadLoop_1 -> threadLoop_2 [label="false"]; +threadLoop_1_true [label="wait()\nreturn true"]; +threadLoop_2 [label="if (mIgnoreNextPausedInt)"]; +threadLoop_2 -> threadLoop_2_true [label="true"]; +threadLoop_2 -> threadLoop_3 [label="false"]; +threadLoop_2_true [label="mIgnoreNextPausedInt = false\nmPausedInt = false"]; +threadLoop_2_true -> threadLoop_3; +threadLoop_3 [label="if (mPausedInt)"]; +threadLoop_3 -> threadLoop_3_true [label="true"]; +threadLoop_3 -> threadLoop_4 [label="false"]; +threadLoop_3_true [label="wait()\nmPausedInt = false\nreturn true"]; +threadLoop_4 [label="if (exitPending)"]; +threadLoop_4 -> threadLoop_4_true [label="true"]; +threadLoop_4 -> threadLoop_5 [label="false"]; +threadLoop_4_true [label="return false"]; +threadLoop_5 [label="ns = processAudioBuffer()"]; +threadLoop_5 -> threadLoop_6; +threadLoop_6 [label="case ns"]; +threadLoop_6 -> threadLoop_6_0 [label="0"]; +threadLoop_6 -> threadLoop_6_NS_INACTIVE [label="NS_INACTIVE"]; +threadLoop_6 -> threadLoop_6_NS_NEVER [label="NS_NEVER"]; +threadLoop_6 -> threadLoop_6_NS_WHENEVER [label="NS_WHENEVER"]; +threadLoop_6 -> threadLoop_6_default [label="default"]; +threadLoop_6_default [label="if (ns < 0)"]; +threadLoop_6_default -> threadLoop_6_default_true [label="true"]; +threadLoop_6_default -> threadLoop_6_default_false [label="false"]; +threadLoop_6_default_true [label="FATAL"]; +threadLoop_6_default_false [label="pauseInternal(ns) [wake()-able]\nmPausedInternal = true\nmPausedNs = ns\nreturn true"]; +threadLoop_6_0 [label="return true"]; +threadLoop_6_NS_INACTIVE [label="pauseInternal()\nmPausedInternal = true\nmPausedNs = 0\nreturn true"]; +threadLoop_6_NS_NEVER [label="return false"]; +threadLoop_6_NS_WHENEVER [label="ns = 1s"]; +threadLoop_6_NS_WHENEVER -> threadLoop_6_default_false; + +} diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp index 39a239d..8e8a1ed 100644 --- a/media/libmedia/mediametadataretriever.cpp +++ b/media/libmedia/mediametadataretriever.cpp @@ -172,7 +172,7 @@ MediaMetadataRetriever::DeathNotifier::~DeathNotifier() { Mutex::Autolock lock(sServiceLock); if (sService != 0) { - sService->asBinder()->unlinkToDeath(this); + IInterface::asBinder(sService)->unlinkToDeath(this); } } diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 9611ac7..d1d51cc 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -59,6 +59,7 @@ MediaPlayer::MediaPlayer() mLoop = false; mLeftVolume = mRightVolume = 1.0; mVideoWidth = mVideoHeight = 0; + mPlaybackRate = 1.0; mLockThreadId = 0; mAudioSessionId = AudioSystem::newAudioUniqueId(); AudioSystem::acquireAudioSessionId(mAudioSessionId, -1); @@ -240,7 +241,7 @@ status_t MediaPlayer::setVideoSurfaceTexture( // must call with lock held status_t MediaPlayer::prepareAsync_l() { - if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) { + if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) { mPlayer->setAudioStreamType(mStreamType); if (mAudioAttributesParcel != NULL) { mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel); @@ -378,6 +379,24 @@ bool MediaPlayer::isPlaying() return false; } +status_t MediaPlayer::setPlaybackRate(float rate) +{ + ALOGV("setPlaybackRate: %f", rate); + if (rate <= 0.0) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + if (mPlaybackRate == rate) { + return NO_ERROR; + } + mPlaybackRate = rate; + return mPlayer->setPlaybackRate(rate); + } + ALOGV("setPlaybackRate: no active player"); + return INVALID_OPERATION; +} + status_t MediaPlayer::getVideoWidth(int *w) { ALOGV("getVideoWidth"); @@ -414,7 +433,8 @@ status_t MediaPlayer::getCurrentPosition(int *msec) status_t MediaPlayer::getDuration_l(int *msec) { ALOGV("getDuration_l"); - bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE)); + bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | + MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE)); if (mPlayer != 0 && isValidState) { int durationMs; status_t ret = mPlayer->getDuration(&durationMs); @@ -443,7 +463,8 @@ status_t MediaPlayer::getDuration(int *msec) status_t MediaPlayer::seekTo_l(int msec) { ALOGV("seekTo %d", msec); - if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) { + if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | + MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) { if ( msec < 0 ) { ALOGW("Attempt to seek to invalid position: %d", msec); msec = 0; @@ -477,7 +498,8 @@ status_t MediaPlayer::seekTo_l(int msec) return NO_ERROR; } } - ALOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), mCurrentState); + ALOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), + mCurrentState); return INVALID_OPERATION; } @@ -835,53 +857,12 @@ void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj) } } -/*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(httpService, url, pSampleRate, pNumChannels, pFormat, heap, pSize); - } else { - ALOGE("Unable to locate media service"); - status = DEAD_OBJECT; - } - return status; - -} - void MediaPlayer::died() { ALOGV("died"); notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); } -/*static*/ status_t MediaPlayer::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) -{ - ALOGV("decode(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); - status_t status; - const sp<IMediaPlayerService>& service = getMediaPlayerService(); - if (service != 0) { - status = service->decode(fd, offset, length, pSampleRate, - pNumChannels, pFormat, heap, pSize); - } else { - ALOGE("Unable to locate media service"); - status = DEAD_OBJECT; - } - return status; - -} - status_t MediaPlayer::setNextMediaPlayer(const sp<MediaPlayer>& next) { if (mPlayer == NULL) { return NO_INIT; diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 1952b86..973e156 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -264,32 +264,6 @@ status_t MediaRecorder::setAudioEncoder(int ae) return ret; } -status_t MediaRecorder::setOutputFile(const char* path) -{ - ALOGV("setOutputFile(%s)", path); - if (mMediaRecorder == NULL) { - ALOGE("media recorder is not initialized yet"); - return INVALID_OPERATION; - } - if (mIsOutputFileSet) { - ALOGE("output file has already been set"); - return INVALID_OPERATION; - } - if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { - ALOGE("setOutputFile called in an invalid state(%d)", mCurrentState); - return INVALID_OPERATION; - } - - status_t ret = mMediaRecorder->setOutputFile(path); - if (OK != ret) { - ALOGV("setOutputFile failed: %d", ret); - mCurrentState = MEDIA_RECORDER_ERROR; - return ret; - } - mIsOutputFileSet = true; - return ret; -} - status_t MediaRecorder::setOutputFile(int fd, int64_t offset, int64_t length) { ALOGV("setOutputFile(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 2cf5710..4b31715 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -10,13 +10,12 @@ LOCAL_SRC_FILES:= \ ActivityManager.cpp \ Crypto.cpp \ Drm.cpp \ + DrmSessionManager.cpp \ HDCP.cpp \ MediaPlayerFactory.cpp \ MediaPlayerService.cpp \ MediaRecorderClient.cpp \ MetadataRetrieverClient.cpp \ - MidiFile.cpp \ - MidiMetadataRetriever.cpp \ RemoteDisplay.cpp \ SharedLibrary.cpp \ StagefrightPlayer.cpp \ diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp index 2a8b2c6..d4f6fab 100644 --- a/media/libmediaplayerservice/Drm.cpp +++ b/media/libmediaplayerservice/Drm.cpp @@ -23,6 +23,8 @@ #include "Drm.h" +#include "DrmSessionClientInterface.h" +#include "DrmSessionManager.h" #include <media/drm/DrmAPI.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AString.h> @@ -33,6 +35,10 @@ namespace android { +static inline int getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + static bool checkPermission(const char* permissionString) { #ifndef HAVE_ANDROID_OS return true; @@ -57,14 +63,41 @@ static bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs) { return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0; } +struct DrmSessionClient : public DrmSessionClientInterface { + DrmSessionClient(Drm* drm) : mDrm(drm) {} + + virtual bool reclaimSession(const Vector<uint8_t>& sessionId) { + sp<Drm> drm = mDrm.promote(); + if (drm == NULL) { + return true; + } + status_t err = drm->closeSession(sessionId); + if (err != OK) { + return false; + } + drm->sendEvent(DrmPlugin::kDrmPluginEventSessionReclaimed, 0, &sessionId, NULL); + return true; + } + +protected: + virtual ~DrmSessionClient() {} + +private: + wp<Drm> mDrm; + + DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient); +}; + Drm::Drm() : mInitCheck(NO_INIT), + mDrmSessionClient(new DrmSessionClient(this)), mListener(NULL), mFactory(NULL), mPlugin(NULL) { } Drm::~Drm() { + DrmSessionManager::Instance()->removeDrm(mDrmSessionClient); delete mPlugin; mPlugin = NULL; closeFactory(); @@ -84,10 +117,10 @@ status_t Drm::setListener(const sp<IDrmClient>& listener) { Mutex::Autolock lock(mEventLock); if (mListener != NULL){ - mListener->asBinder()->unlinkToDeath(this); + IInterface::asBinder(mListener)->unlinkToDeath(this); } if (listener != NULL) { - listener->asBinder()->linkToDeath(this); + IInterface::asBinder(listener)->linkToDeath(this); } mListener = listener; return NO_ERROR; @@ -289,7 +322,18 @@ status_t Drm::openSession(Vector<uint8_t> &sessionId) { return -EINVAL; } - return mPlugin->openSession(sessionId); + status_t err = mPlugin->openSession(sessionId); + if (err == ERROR_DRM_RESOURCE_BUSY) { + bool retry = false; + retry = DrmSessionManager::Instance()->reclaimSession(getCallingPid()); + if (retry) { + err = mPlugin->openSession(sessionId); + } + } + if (err == OK) { + DrmSessionManager::Instance()->addSession(getCallingPid(), mDrmSessionClient, sessionId); + } + return err; } status_t Drm::closeSession(Vector<uint8_t> const &sessionId) { @@ -303,7 +347,11 @@ status_t Drm::closeSession(Vector<uint8_t> const &sessionId) { return -EINVAL; } - return mPlugin->closeSession(sessionId); + status_t err = mPlugin->closeSession(sessionId); + if (err == OK) { + DrmSessionManager::Instance()->removeSession(sessionId); + } + return err; } status_t Drm::getKeyRequest(Vector<uint8_t> const &sessionId, @@ -321,6 +369,8 @@ status_t Drm::getKeyRequest(Vector<uint8_t> const &sessionId, return -EINVAL; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->getKeyRequest(sessionId, initData, mimeType, keyType, optionalParameters, request, defaultUrl); } @@ -338,6 +388,8 @@ status_t Drm::provideKeyResponse(Vector<uint8_t> const &sessionId, return -EINVAL; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->provideKeyResponse(sessionId, response, keySetId); } @@ -367,6 +419,8 @@ status_t Drm::restoreKeys(Vector<uint8_t> const &sessionId, return -EINVAL; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->restoreKeys(sessionId, keySetId); } @@ -382,6 +436,8 @@ status_t Drm::queryKeyStatus(Vector<uint8_t> const &sessionId, return -EINVAL; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->queryKeyStatus(sessionId, infoMap); } @@ -561,6 +617,8 @@ status_t Drm::setCipherAlgorithm(Vector<uint8_t> const &sessionId, return -EINVAL; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->setCipherAlgorithm(sessionId, algorithm); } @@ -576,6 +634,8 @@ status_t Drm::setMacAlgorithm(Vector<uint8_t> const &sessionId, return -EINVAL; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->setMacAlgorithm(sessionId, algorithm); } @@ -594,6 +654,8 @@ status_t Drm::encrypt(Vector<uint8_t> const &sessionId, return -EINVAL; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->encrypt(sessionId, keyId, input, iv, output); } @@ -612,6 +674,8 @@ status_t Drm::decrypt(Vector<uint8_t> const &sessionId, return -EINVAL; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->decrypt(sessionId, keyId, input, iv, output); } @@ -629,6 +693,8 @@ status_t Drm::sign(Vector<uint8_t> const &sessionId, return -EINVAL; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->sign(sessionId, keyId, message, signature); } @@ -647,6 +713,8 @@ status_t Drm::verify(Vector<uint8_t> const &sessionId, return -EINVAL; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->verify(sessionId, keyId, message, signature, match); } @@ -669,15 +737,21 @@ status_t Drm::signRSA(Vector<uint8_t> const &sessionId, return -EPERM; } + DrmSessionManager::Instance()->useSession(sessionId); + return mPlugin->signRSA(sessionId, algorithm, message, wrappedKey, signature); } void Drm::binderDied(const wp<IBinder> &the_late_who) { + mEventLock.lock(); + mListener.clear(); + mEventLock.unlock(); + + Mutex::Autolock autoLock(mLock); delete mPlugin; mPlugin = NULL; closeFactory(); - mListener.clear(); } } // namespace android diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h index 0e1eb2c..0cea639 100644 --- a/media/libmediaplayerservice/Drm.h +++ b/media/libmediaplayerservice/Drm.h @@ -28,6 +28,7 @@ namespace android { struct DrmFactory; struct DrmPlugin; +struct DrmSessionClientInterface; struct Drm : public BnDrm, public IBinder::DeathRecipient, @@ -138,6 +139,8 @@ private: status_t mInitCheck; + sp<DrmSessionClientInterface> mDrmSessionClient; + sp<IDrmClient> mListener; mutable Mutex mEventLock; mutable Mutex mNotifyLock; diff --git a/media/libmediaplayerservice/DrmSessionClientInterface.h b/media/libmediaplayerservice/DrmSessionClientInterface.h new file mode 100644 index 0000000..17faf08 --- /dev/null +++ b/media/libmediaplayerservice/DrmSessionClientInterface.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DRM_PROXY_INTERFACE_H_ +#define DRM_PROXY_INTERFACE_H_ + +#include <utils/RefBase.h> +#include <utils/Vector.h> + +namespace android { + +struct DrmSessionClientInterface : public RefBase { + virtual bool reclaimSession(const Vector<uint8_t>& sessionId) = 0; + +protected: + virtual ~DrmSessionClientInterface() {} +}; + +} // namespace android + +#endif // DRM_PROXY_INTERFACE_H_ diff --git a/media/libmediaplayerservice/DrmSessionManager.cpp b/media/libmediaplayerservice/DrmSessionManager.cpp new file mode 100644 index 0000000..641f881 --- /dev/null +++ b/media/libmediaplayerservice/DrmSessionManager.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DrmSessionManager" +#include <utils/Log.h> + +#include "DrmSessionManager.h" + +#include "DrmSessionClientInterface.h" +#include <binder/IPCThreadState.h> +#include <binder/IProcessInfoService.h> +#include <binder/IServiceManager.h> +#include <media/stagefright/ProcessInfo.h> +#include <unistd.h> +#include <utils/String8.h> + +namespace android { + +static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) { + String8 sessionIdStr; + for (size_t i = 0; i < sessionId.size(); ++i) { + sessionIdStr.appendFormat("%u ", sessionId[i]); + } + return sessionIdStr; +} + +bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) { + if (sessionId1.size() != sessionId2.size()) { + return false; + } + for (size_t i = 0; i < sessionId1.size(); ++i) { + if (sessionId1[i] != sessionId2[i]) { + return false; + } + } + return true; +} + +sp<DrmSessionManager> DrmSessionManager::Instance() { + static sp<DrmSessionManager> drmSessionManager = new DrmSessionManager(); + return drmSessionManager; +} + +DrmSessionManager::DrmSessionManager() + : mProcessInfo(new ProcessInfo()), + mTime(0) {} + +DrmSessionManager::DrmSessionManager(sp<ProcessInfoInterface> processInfo) + : mProcessInfo(processInfo), + mTime(0) {} + +DrmSessionManager::~DrmSessionManager() {} + +void DrmSessionManager::addSession( + int pid, sp<DrmSessionClientInterface> drm, const Vector<uint8_t> &sessionId) { + ALOGV("addSession(pid %d, drm %p, sessionId %s)", pid, drm.get(), + GetSessionIdString(sessionId).string()); + + Mutex::Autolock lock(mLock); + SessionInfo info; + info.drm = drm; + info.sessionId = sessionId; + info.timeStamp = getTime_l(); + ssize_t index = mSessionMap.indexOfKey(pid); + if (index < 0) { + // new pid + SessionInfos infosForPid; + infosForPid.push_back(info); + mSessionMap.add(pid, infosForPid); + } else { + mSessionMap.editValueAt(index).push_back(info); + } +} + +void DrmSessionManager::useSession(const Vector<uint8_t> &sessionId) { + ALOGV("useSession(%s)", GetSessionIdString(sessionId).string()); + + Mutex::Autolock lock(mLock); + for (size_t i = 0; i < mSessionMap.size(); ++i) { + SessionInfos& infos = mSessionMap.editValueAt(i); + for (size_t j = 0; j < infos.size(); ++j) { + SessionInfo& info = infos.editItemAt(j); + if (isEqualSessionId(sessionId, info.sessionId)) { + info.timeStamp = getTime_l(); + return; + } + } + } +} + +void DrmSessionManager::removeSession(const Vector<uint8_t> &sessionId) { + ALOGV("removeSession(%s)", GetSessionIdString(sessionId).string()); + + Mutex::Autolock lock(mLock); + for (size_t i = 0; i < mSessionMap.size(); ++i) { + SessionInfos& infos = mSessionMap.editValueAt(i); + for (size_t j = 0; j < infos.size(); ++j) { + if (isEqualSessionId(sessionId, infos[j].sessionId)) { + infos.removeAt(j); + return; + } + } + } +} + +void DrmSessionManager::removeDrm(sp<DrmSessionClientInterface> drm) { + ALOGV("removeDrm(%p)", drm.get()); + + Mutex::Autolock lock(mLock); + bool found = false; + for (size_t i = 0; i < mSessionMap.size(); ++i) { + SessionInfos& infos = mSessionMap.editValueAt(i); + for (size_t j = 0; j < infos.size();) { + if (infos[j].drm == drm) { + ALOGV("removed session (%s)", GetSessionIdString(infos[j].sessionId).string()); + j = infos.removeAt(j); + found = true; + } else { + ++j; + } + } + if (found) { + break; + } + } +} + +bool DrmSessionManager::reclaimSession(int callingPid) { + ALOGV("reclaimSession(%d)", callingPid); + + sp<DrmSessionClientInterface> drm; + Vector<uint8_t> sessionId; + int lowestPriorityPid; + int lowestPriority; + { + Mutex::Autolock lock(mLock); + int callingPriority; + if (!mProcessInfo->getPriority(callingPid, &callingPriority)) { + return false; + } + if (!getLowestPriority_l(&lowestPriorityPid, &lowestPriority)) { + return false; + } + if (lowestPriority <= callingPriority) { + return false; + } + + if (!getLeastUsedSession_l(lowestPriorityPid, &drm, &sessionId)) { + return false; + } + } + + if (drm == NULL) { + return false; + } + + ALOGV("reclaim session(%s) opened by pid %d", + GetSessionIdString(sessionId).string(), lowestPriorityPid); + + return drm->reclaimSession(sessionId); +} + +int64_t DrmSessionManager::getTime_l() { + return mTime++; +} + +bool DrmSessionManager::getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority) { + int pid = -1; + int priority = -1; + for (size_t i = 0; i < mSessionMap.size(); ++i) { + if (mSessionMap.valueAt(i).size() == 0) { + // no opened session by this process. + continue; + } + int tempPid = mSessionMap.keyAt(i); + int tempPriority; + if (!mProcessInfo->getPriority(tempPid, &tempPriority)) { + // shouldn't happen. + return false; + } + if (pid == -1) { + pid = tempPid; + priority = tempPriority; + } else { + if (tempPriority > priority) { + pid = tempPid; + priority = tempPriority; + } + } + } + if (pid != -1) { + *lowestPriorityPid = pid; + *lowestPriority = priority; + } + return (pid != -1); +} + +bool DrmSessionManager::getLeastUsedSession_l( + int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId) { + ssize_t index = mSessionMap.indexOfKey(pid); + if (index < 0) { + return false; + } + + int leastUsedIndex = -1; + int64_t minTs = LLONG_MAX; + const SessionInfos& infos = mSessionMap.valueAt(index); + for (size_t j = 0; j < infos.size(); ++j) { + if (leastUsedIndex == -1) { + leastUsedIndex = j; + minTs = infos[j].timeStamp; + } else { + if (infos[j].timeStamp < minTs) { + leastUsedIndex = j; + minTs = infos[j].timeStamp; + } + } + } + if (leastUsedIndex != -1) { + *drm = infos[leastUsedIndex].drm; + *sessionId = infos[leastUsedIndex].sessionId; + } + return (leastUsedIndex != -1); +} + +} // namespace android diff --git a/media/libmediaplayerservice/DrmSessionManager.h b/media/libmediaplayerservice/DrmSessionManager.h new file mode 100644 index 0000000..ba5c268 --- /dev/null +++ b/media/libmediaplayerservice/DrmSessionManager.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DRM_SESSION_MANAGER_H_ + +#define DRM_SESSION_MANAGER_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <utils/RefBase.h> +#include <utils/KeyedVector.h> +#include <utils/threads.h> +#include <utils/Vector.h> + +namespace android { + +class DrmSessionManagerTest; +struct DrmSessionClientInterface; +struct ProcessInfoInterface; + +bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2); + +struct SessionInfo { + sp<DrmSessionClientInterface> drm; + Vector<uint8_t> sessionId; + int64_t timeStamp; +}; + +typedef Vector<SessionInfo > SessionInfos; +typedef KeyedVector<int, SessionInfos > PidSessionInfosMap; + +struct DrmSessionManager : public RefBase { + static sp<DrmSessionManager> Instance(); + + DrmSessionManager(); + DrmSessionManager(sp<ProcessInfoInterface> processInfo); + + void addSession(int pid, sp<DrmSessionClientInterface> drm, const Vector<uint8_t>& sessionId); + void useSession(const Vector<uint8_t>& sessionId); + void removeSession(const Vector<uint8_t>& sessionId); + void removeDrm(sp<DrmSessionClientInterface> drm); + bool reclaimSession(int callingPid); + +protected: + virtual ~DrmSessionManager(); + +private: + friend class DrmSessionManagerTest; + + int64_t getTime_l(); + bool getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority); + bool getLeastUsedSession_l( + int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId); + + sp<ProcessInfoInterface> mProcessInfo; + mutable Mutex mLock; + PidSessionInfosMap mSessionMap; + int64_t mTime; + + DISALLOW_EVIL_CONSTRUCTORS(DrmSessionManager); +}; + +} // namespace android + +#endif // DRM_SESSION_MANAGER_H_ diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp index 3e0fc0d..48884b9 100644 --- a/media/libmediaplayerservice/MediaPlayerFactory.cpp +++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp @@ -15,18 +15,21 @@ ** limitations under the License. */ +//#define LOG_NDEBUG 0 #define LOG_TAG "MediaPlayerFactory" #include <utils/Log.h> #include <cutils/properties.h> #include <media/IMediaPlayer.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/FileSource.h> #include <media/stagefright/foundation/ADebug.h> #include <utils/Errors.h> #include <utils/misc.h> +#include <../libstagefright/include/WVMExtractor.h> #include "MediaPlayerFactory.h" -#include "MidiFile.h" #include "TestPlayerStub.h" #include "StagefrightPlayer.h" #include "nuplayer/NuPlayerDriver.h" @@ -179,10 +182,18 @@ class StagefrightPlayerFactory : virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/, int fd, int64_t offset, - int64_t /*length*/, + int64_t length, float /*curScore*/) { - if (getDefaultPlayerType() - == STAGEFRIGHT_PLAYER) { + if (legacyDrm()) { + sp<DataSource> source = new FileSource(dup(fd), offset, length); + String8 mimeType; + float confidence; + if (SniffWVM(source, &mimeType, &confidence, NULL /* format */)) { + return 1.0; + } + } + + if (getDefaultPlayerType() == STAGEFRIGHT_PLAYER) { char buf[20]; lseek(fd, offset, SEEK_SET); read(fd, buf, sizeof(buf)); @@ -198,10 +209,28 @@ class StagefrightPlayerFactory : return 0.0; } + virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/, + const char* url, + float /*curScore*/) { + if (legacyDrm() && !strncasecmp("widevine://", url, 11)) { + return 1.0; + } + return 0.0; + } + virtual sp<MediaPlayerBase> createPlayer() { ALOGV(" create StagefrightPlayer"); return new StagefrightPlayer(); } + private: + bool legacyDrm() { + char value[PROPERTY_VALUE_MAX]; + if (property_get("persist.sys.media.legacy-drm", value, NULL) + && (!strcmp("1", value) || !strcasecmp("true", value))) { + return true; + } + return false; + } }; class NuPlayerFactory : public MediaPlayerFactory::IFactory { @@ -250,75 +279,6 @@ class NuPlayerFactory : public MediaPlayerFactory::IFactory { } }; -class SonivoxPlayerFactory : public MediaPlayerFactory::IFactory { - public: - virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/, - const char* url, - float curScore) { - static const float kOurScore = 0.4; - static const char* const FILE_EXTS[] = { ".mid", - ".midi", - ".smf", - ".xmf", - ".mxmf", - ".imy", - ".rtttl", - ".rtx", - ".ota" }; - if (kOurScore <= curScore) - return 0.0; - - // use MidiFile for MIDI extensions - int lenURL = strlen(url); - for (int i = 0; i < NELEM(FILE_EXTS); ++i) { - int len = strlen(FILE_EXTS[i]); - int start = lenURL - len; - if (start > 0) { - if (!strncasecmp(url + start, FILE_EXTS[i], len)) { - return kOurScore; - } - } - } - - return 0.0; - } - - virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/, - int fd, - int64_t offset, - int64_t length, - float curScore) { - static const float kOurScore = 0.8; - - if (kOurScore <= curScore) - return 0.0; - - // Some kind of MIDI? - EAS_DATA_HANDLE easdata; - if (EAS_Init(&easdata) == EAS_SUCCESS) { - EAS_FILE locator; - locator.path = NULL; - locator.fd = fd; - locator.offset = offset; - locator.length = length; - EAS_HANDLE eashandle; - if (EAS_OpenFile(easdata, &locator, &eashandle) == EAS_SUCCESS) { - EAS_CloseFile(easdata, eashandle); - EAS_Shutdown(easdata); - return kOurScore; - } - EAS_Shutdown(easdata); - } - - return 0.0; - } - - virtual sp<MediaPlayerBase> createPlayer() { - ALOGV(" create MidiFile"); - return new MidiFile(); - } -}; - class TestPlayerFactory : public MediaPlayerFactory::IFactory { public: virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/, @@ -345,7 +305,6 @@ void MediaPlayerFactory::registerBuiltinFactories() { registerFactory_l(new StagefrightPlayerFactory(), STAGEFRIGHT_PLAYER); registerFactory_l(new NuPlayerFactory(), NU_PLAYER); - registerFactory_l(new SonivoxPlayerFactory(), SONIVOX_PLAYER); registerFactory_l(new TestPlayerFactory(), TEST_PLAYER); sInitComplete = true; diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index c120898..f113e21 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -59,6 +59,7 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/AudioPlayer.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALooperRoster.h> #include <system/audio.h> @@ -70,7 +71,6 @@ #include "MetadataRetrieverClient.h" #include "MediaPlayerFactory.h" -#include "MidiFile.h" #include "TestPlayerStub.h" #include "StagefrightPlayer.h" #include "nuplayer/NuPlayerDriver.h" @@ -248,6 +248,9 @@ void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attribu namespace android { +extern ALooperRoster gLooperRoster; + + static bool checkPermission(const char* permissionString) { #ifndef HAVE_ANDROID_OS return true; @@ -287,8 +290,9 @@ MediaPlayerService::MediaPlayerService() const sp<IServiceManager> sm(defaultServiceManager()); if (sm != NULL) { const String16 name("batterystats"); + // use checkService() to avoid blocking if service is not up yet sp<IBatteryStats> batteryStats = - interface_cast<IBatteryStats>(sm->getService(name)); + interface_cast<IBatteryStats>(sm->checkService(name)); if (batteryStats != NULL) { batteryStats->noteResetVideo(); batteryStats->noteResetAudio(); @@ -385,28 +389,6 @@ sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay( return new RemoteDisplay(client, iface.string()); } -status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& /*args*/) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - result.append(" AudioCache\n"); - if (mHeap != 0) { - snprintf(buffer, 255, " heap base(%p), size(%zu), flags(%d)\n", - mHeap->getBase(), mHeap->getSize(), mHeap->getFlags()); - result.append(buffer); - } - snprintf(buffer, 255, " msec per frame(%f), channel count(%d), format(%d), frame count(%zd)\n", - mMsecsPerFrame, mChannelCount, mFormat, mFrameCount); - result.append(buffer); - snprintf(buffer, 255, " sample rate(%d), size(%d), error(%d), command complete(%s)\n", - mSampleRate, mSize, mError, mCommandComplete?"true":"false"); - result.append(buffer); - ::write(fd, result.string(), result.size()); - return NO_ERROR; -} - status_t MediaPlayerService::AudioOutput::dump(int fd, const Vector<String16>& args) const { const size_t SIZE = 256; @@ -451,11 +433,18 @@ status_t MediaPlayerService::Client::dump(int fd, const Vector<String16>& args) return NO_ERROR; } +/** + * The only arguments this understands right now are -c, -von and -voff, + * which are parsed by ALooperRoster::dump() + */ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; + SortedVector< sp<Client> > clients; //to serialise the mutex unlock & client destruction. + SortedVector< sp<MediaRecorderClient> > mediaRecorderClients; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { snprintf(buffer, SIZE, "Permission Denial: " "can't dump MediaPlayerService from pid=%d, uid=%d\n", @@ -467,6 +456,7 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) for (int i = 0, n = mClients.size(); i < n; ++i) { sp<Client> c = mClients[i].promote(); if (c != 0) c->dump(fd, args); + clients.add(c); } if (mMediaRecorderClients.size() == 0) { result.append(" No media recorder client\n\n"); @@ -479,12 +469,13 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) write(fd, result.string(), result.size()); result = "\n"; c->dump(fd, args); + mediaRecorderClients.add(c); } } } result.append(" Files opened and/or mapped:\n"); - snprintf(buffer, SIZE, "/proc/%d/maps", gettid()); + snprintf(buffer, SIZE, "/proc/%d/maps", getpid()); FILE *f = fopen(buffer, "r"); if (f) { while (!feof(f)) { @@ -504,13 +495,13 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) result.append("\n"); } - snprintf(buffer, SIZE, "/proc/%d/fd", gettid()); + snprintf(buffer, SIZE, "/proc/%d/fd", getpid()); DIR *d = opendir(buffer); if (d) { struct dirent *ent; while((ent = readdir(d)) != NULL) { if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) { - snprintf(buffer, SIZE, "/proc/%d/fd/%s", gettid(), ent->d_name); + snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name); struct stat s; if (lstat(buffer, &s) == 0) { if ((s.st_mode & S_IFMT) == S_IFLNK) { @@ -551,6 +542,8 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) result.append("\n"); } + gLooperRoster.dump(fd, args); + bool dumpMem = false; for (size_t i = 0; i < args.size(); i++) { if (args[i] == String16("-m")) { @@ -817,8 +810,7 @@ status_t MediaPlayerService::Client::setVideoSurfaceTexture( sp<MediaPlayerBase> p = getPlayer(); if (p == 0) return UNKNOWN_ERROR; - sp<IBinder> binder(bufferProducer == NULL ? NULL : - bufferProducer->asBinder()); + sp<IBinder> binder(IInterface::asBinder(bufferProducer)); if (mConnectedWindowBinder == binder) { return OK; } @@ -975,6 +967,14 @@ status_t MediaPlayerService::Client::isPlaying(bool* state) return NO_ERROR; } +status_t MediaPlayerService::Client::setPlaybackRate(float rate) +{ + ALOGV("[%d] setPlaybackRate(%f)", mConnId, rate); + sp<MediaPlayerBase> p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + return p->setPlaybackRate(rate); +} + status_t MediaPlayerService::Client::getCurrentPosition(int *msec) { ALOGV("getCurrentPosition"); @@ -1281,129 +1281,6 @@ int Antagonizer::callbackThread(void* user) } #endif -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; - status_t status = BAD_VALUE; - - // Protect our precious, precious DRMd ringtones by only allowing - // decoding of http, but not filesystem paths or content Uris. - // If the application wants to decode those, it should open a - // filedescriptor for them and use that. - if (url != NULL && strncmp(url, "http://", 7) != 0) { - ALOGD("Can't decode %s by path, use filedescriptor instead", url); - return BAD_VALUE; - } - - player_type playerType = - MediaPlayerFactory::getPlayerType(NULL /* client */, url); - ALOGV("player type = %d", playerType); - - // create the right type of player - sp<AudioCache> cache = new AudioCache(heap); - player = MediaPlayerFactory::createPlayer(playerType, cache.get(), cache->notify); - if (player == NULL) goto Exit; - if (player->hardwareOutput()) goto Exit; - - static_cast<MediaPlayerInterface*>(player.get())->setAudioSink(cache); - - // set data source - if (player->setDataSource(httpService, url) != NO_ERROR) goto Exit; - - ALOGV("prepare"); - player->prepareAsync(); - - ALOGV("wait for prepare"); - if (cache->wait() != NO_ERROR) goto Exit; - - ALOGV("start"); - player->start(); - - ALOGV("wait for playback complete"); - cache->wait(); - // in case of error, return what was successfully decoded. - if (cache->size() == 0) { - goto Exit; - } - - *pSize = cache->size(); - *pSampleRate = cache->sampleRate(); - *pNumChannels = cache->channelCount(); - *pFormat = cache->format(); - ALOGV("return size %d sampleRate=%u, channelCount = %d, format = %d", - *pSize, *pSampleRate, *pNumChannels, *pFormat); - status = NO_ERROR; - -Exit: - if (player != 0) player->reset(); - return status; -} - -status_t MediaPlayerService::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) -{ - ALOGV("decode(%d, %lld, %lld)", fd, offset, length); - sp<MediaPlayerBase> player; - status_t status = BAD_VALUE; - - player_type playerType = MediaPlayerFactory::getPlayerType(NULL /* client */, - fd, - offset, - length); - ALOGV("player type = %d", playerType); - - // create the right type of player - sp<AudioCache> cache = new AudioCache(heap); - player = MediaPlayerFactory::createPlayer(playerType, cache.get(), cache->notify); - if (player == NULL) goto Exit; - if (player->hardwareOutput()) goto Exit; - - static_cast<MediaPlayerInterface*>(player.get())->setAudioSink(cache); - - // set data source - if (player->setDataSource(fd, offset, length) != NO_ERROR) goto Exit; - - ALOGV("prepare"); - player->prepareAsync(); - - ALOGV("wait for prepare"); - if (cache->wait() != NO_ERROR) goto Exit; - - ALOGV("start"); - player->start(); - - ALOGV("wait for playback complete"); - cache->wait(); - // in case of error, return what was successfully decoded. - if (cache->size() == 0) { - goto Exit; - } - - *pSize = cache->size(); - *pSampleRate = cache->sampleRate(); - *pNumChannels = cache->channelCount(); - *pFormat = cache->format(); - ALOGV("return size %d, sampleRate=%u, channelCount = %d, format = %d", - *pSize, *pSampleRate, *pNumChannels, *pFormat); - status = NO_ERROR; - -Exit: - if (player != 0) player->reset(); - ::close(fd); - return status; -} - - #undef LOG_TAG #define LOG_TAG "AudioSink" MediaPlayerService::AudioOutput::AudioOutput(int sessionId, int uid, int pid, @@ -1801,13 +1678,13 @@ void MediaPlayerService::AudioOutput::switchToNextOutput() { } } -ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size) +ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size, bool blocking) { LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback."); //ALOGV("write(%p, %u)", buffer, size); if (mTrack != 0) { - ssize_t ret = mTrack->write(buffer, size); + ssize_t ret = mTrack->write(buffer, size, blocking); if (ret >= 0) { mBytesWritten += ret; } @@ -1953,47 +1830,6 @@ uint32_t MediaPlayerService::AudioOutput::getSampleRate() const return mTrack->getSampleRate(); } -#undef LOG_TAG -#define LOG_TAG "AudioCache" -MediaPlayerService::AudioCache::AudioCache(const sp<IMemoryHeap>& heap) : - mHeap(heap), mChannelCount(0), mFrameCount(1024), mSampleRate(0), mSize(0), - mFrameSize(1), mError(NO_ERROR), mCommandComplete(false) -{ -} - -uint32_t MediaPlayerService::AudioCache::latency () const -{ - return 0; -} - -float MediaPlayerService::AudioCache::msecsPerFrame() const -{ - return mMsecsPerFrame; -} - -status_t MediaPlayerService::AudioCache::getPosition(uint32_t *position) const -{ - if (position == 0) return BAD_VALUE; - *position = mSize / mFrameSize; - return NO_ERROR; -} - -status_t MediaPlayerService::AudioCache::getTimestamp(AudioTimestamp &ts) const -{ - ts.mPosition = mSize / mFrameSize; - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - ts.mTime.tv_sec = now / 1000000000LL; - ts.mTime.tv_nsec = now - (1000000000LL * ts.mTime.tv_sec); - return NO_ERROR; -} - -status_t MediaPlayerService::AudioCache::getFramesWritten(uint32_t *written) const -{ - if (written == 0) return BAD_VALUE; - *written = mSize / mFrameSize; - return NO_ERROR; -} - //////////////////////////////////////////////////////////////////////////////// struct CallbackThread : public Thread { @@ -2061,138 +1897,6 @@ bool CallbackThread::threadLoop() { //////////////////////////////////////////////////////////////////////////////// -status_t MediaPlayerService::AudioCache::open( - uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, - audio_format_t format, int bufferCount, - AudioCallback cb, void *cookie, audio_output_flags_t /*flags*/, - const audio_offload_info_t* /*offloadInfo*/) -{ - ALOGV("open(%u, %d, 0x%x, %d, %d)", sampleRate, channelCount, channelMask, format, bufferCount); - if (mHeap->getHeapID() < 0) { - return NO_INIT; - } - - mSampleRate = sampleRate; - mChannelCount = (uint16_t)channelCount; - mFormat = format; - mMsecsPerFrame = 1.e3 / (float) sampleRate; - mFrameSize = audio_is_linear_pcm(mFormat) - ? mChannelCount * audio_bytes_per_sample(mFormat) : 1; - mFrameCount = mHeap->getSize() / mFrameSize; - - if (cb != NULL) { - mCallbackThread = new CallbackThread(this, cb, cookie); - } - return NO_ERROR; -} - -status_t MediaPlayerService::AudioCache::start() { - if (mCallbackThread != NULL) { - mCallbackThread->run("AudioCache callback"); - } - return NO_ERROR; -} - -void MediaPlayerService::AudioCache::stop() { - if (mCallbackThread != NULL) { - mCallbackThread->requestExitAndWait(); - } -} - -ssize_t MediaPlayerService::AudioCache::write(const void* buffer, size_t size) -{ - ALOGV("write(%p, %u)", buffer, size); - if ((buffer == 0) || (size == 0)) return size; - - uint8_t* p = static_cast<uint8_t*>(mHeap->getBase()); - if (p == NULL) return NO_INIT; - p += mSize; - ALOGV("memcpy(%p, %p, %u)", p, buffer, size); - - bool overflow = mSize + size > mHeap->getSize(); - if (overflow) { - ALOGE("Heap size overflow! req size: %d, max size: %d", (mSize + size), mHeap->getSize()); - size = mHeap->getSize() - mSize; - } - size -= size % mFrameSize; // consume only integral amounts of frame size - memcpy(p, buffer, size); - mSize += size; - - if (overflow) { - // Signal heap filled here (last frame may be truncated). - // After this point, no more data should be written as the - // heap is filled and the AudioCache should be effectively - // immutable with respect to future writes. - // - // It is thus safe for another thread to read the AudioCache. - mCommandComplete = true; - mSignal.signal(); - } - return size; -} - -// call with lock held -status_t MediaPlayerService::AudioCache::wait() -{ - Mutex::Autolock lock(mLock); - while (!mCommandComplete) { - mSignal.wait(mLock); - } - mCommandComplete = false; - - if (mError == NO_ERROR) { - ALOGV("wait - success"); - } else { - ALOGV("wait - error"); - } - return mError; -} - -void MediaPlayerService::AudioCache::notify( - void* cookie, int msg, int ext1, int ext2, const Parcel* /*obj*/) -{ - ALOGV("notify(%p, %d, %d, %d)", cookie, msg, ext1, ext2); - AudioCache* p = static_cast<AudioCache*>(cookie); - - // ignore buffering messages - switch (msg) - { - case MEDIA_ERROR: - ALOGE("Error %d, %d occurred", ext1, ext2); - break; - case MEDIA_PREPARED: - ALOGV("prepared"); - break; - case MEDIA_PLAYBACK_COMPLETE: - ALOGV("playback complete"); - break; - default: - ALOGV("ignored"); - return; - } - - // wake up thread - Mutex::Autolock lock(p->mLock); - if (msg == MEDIA_ERROR) { - p->mError = ext1; - } - p->mCommandComplete = true; - p->mSignal.signal(); -} - -int MediaPlayerService::AudioCache::getSessionId() const -{ - return 0; -} - -uint32_t MediaPlayerService::AudioCache::getSampleRate() const -{ - if (mMsecsPerFrame == 0) { - return 0; - } - return (uint32_t)(1.e3 / mMsecsPerFrame); -} - void MediaPlayerService::addBatteryData(uint32_t params) { Mutex::Autolock lock(mLock); @@ -2236,7 +1940,7 @@ void MediaPlayerService::addBatteryData(uint32_t params) return; } - // an sudio stream is started + // an audio stream is started if (params & kBatteryDataAudioFlingerStart) { // record the start time only if currently no other audio // is being played diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 3b96e88..4ce4b81 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -77,7 +77,6 @@ class MediaPlayerService : public BnMediaPlayerService virtual ~AudioOutput(); virtual bool ready() const { return mTrack != 0; } - virtual bool realtime() const { return true; } virtual ssize_t bufferSize() const; virtual ssize_t frameCount() const; virtual ssize_t channelCount() const; @@ -98,7 +97,7 @@ class MediaPlayerService : public BnMediaPlayerService const audio_offload_info_t *offloadInfo = NULL); virtual status_t start(); - virtual ssize_t write(const void* buffer, size_t size); + virtual ssize_t write(const void* buffer, size_t size, bool blocking = true); virtual void stop(); virtual void flush(); virtual void pause(); @@ -184,75 +183,6 @@ class MediaPlayerService : public BnMediaPlayerService }; // AudioOutput - class AudioCache : public MediaPlayerBase::AudioSink - { - public: - AudioCache(const sp<IMemoryHeap>& heap); - virtual ~AudioCache() {} - - virtual bool ready() const { return (mChannelCount > 0) && (mHeap->getHeapID() > 0); } - virtual bool realtime() const { return false; } - virtual ssize_t bufferSize() const { return frameSize() * mFrameCount; } - virtual ssize_t frameCount() const { return mFrameCount; } - virtual ssize_t channelCount() const { return (ssize_t)mChannelCount; } - virtual ssize_t frameSize() const { return (ssize_t)mFrameSize; } - virtual uint32_t latency() const; - virtual float msecsPerFrame() const; - virtual status_t getPosition(uint32_t *position) const; - virtual status_t getTimestamp(AudioTimestamp &ts) const; - virtual status_t getFramesWritten(uint32_t *frameswritten) const; - virtual int getSessionId() const; - virtual uint32_t getSampleRate() const; - - virtual status_t open( - uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, - audio_format_t format, int bufferCount = 1, - AudioCallback cb = NULL, void *cookie = NULL, - audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, - const audio_offload_info_t *offloadInfo = NULL); - - virtual status_t start(); - virtual ssize_t write(const void* buffer, size_t size); - virtual void stop(); - virtual void flush() {} - virtual void pause() {} - virtual void close() {} - void setAudioStreamType(audio_stream_type_t streamType __unused) {} - // stream type is not used for AudioCache - virtual audio_stream_type_t getAudioStreamType() const { return AUDIO_STREAM_DEFAULT; } - - void setVolume(float left __unused, float right __unused) {} - virtual status_t setPlaybackRatePermille(int32_t ratePermille __unused) { return INVALID_OPERATION; } - uint32_t sampleRate() const { return mSampleRate; } - audio_format_t format() const { return mFormat; } - size_t size() const { return mSize; } - status_t wait(); - - sp<IMemoryHeap> getHeap() const { return mHeap; } - - static void notify(void* cookie, int msg, - int ext1, int ext2, const Parcel *obj); - virtual status_t dump(int fd, const Vector<String16>& args) const; - - private: - AudioCache(); - - Mutex mLock; - Condition mSignal; - sp<IMemoryHeap> mHeap; - float mMsecsPerFrame; - uint16_t mChannelCount; - audio_format_t mFormat; - ssize_t mFrameCount; - uint32_t mSampleRate; - uint32_t mSize; - size_t mFrameSize; - int mError; - bool mCommandComplete; - - sp<Thread> mCallbackThread; - }; // AudioCache - public: static void instantiate(); @@ -263,19 +193,6 @@ public: virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId); - 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, - const sp<IMemoryHeap>& heap, size_t *pSize); virtual sp<IMediaCodecList> getCodecList() const; virtual sp<IOMX> getOMX(); virtual sp<ICrypto> makeCrypto(); @@ -344,6 +261,7 @@ private: virtual status_t stop(); virtual status_t pause(); virtual status_t isPlaying(bool* state); + virtual status_t setPlaybackRate(float rate); virtual status_t seekTo(int msec); virtual status_t getCurrentPosition(int* msec); virtual status_t getDuration(int* msec); diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 194abbb..4d4de9b 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -154,17 +154,6 @@ status_t MediaRecorderClient::setAudioEncoder(int ae) return mRecorder->setAudioEncoder((audio_encoder)ae); } -status_t MediaRecorderClient::setOutputFile(const char* path) -{ - ALOGV("setOutputFile(%s)", path); - Mutex::Autolock lock(mLock); - if (mRecorder == NULL) { - ALOGE("recorder is not initialized"); - return NO_INIT; - } - return mRecorder->setOutputFile(path); -} - status_t MediaRecorderClient::setOutputFile(int fd, int64_t offset, int64_t length) { ALOGV("setOutputFile(%d, %lld, %lld)", fd, offset, length); diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index a65ec9f..a444b6c 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -38,7 +38,6 @@ public: virtual status_t setOutputFormat(int of); virtual status_t setVideoEncoder(int ve); virtual status_t setAudioEncoder(int ae); - virtual status_t setOutputFile(const char* path); virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); virtual status_t setVideoSize(int width, int height); diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index fa28451..715cc0c 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -35,7 +35,6 @@ #include <media/MediaMetadataRetrieverInterface.h> #include <media/MediaPlayerInterface.h> #include <private/media/VideoFrame.h> -#include "MidiMetadataRetriever.h" #include "MetadataRetrieverClient.h" #include "StagefrightMetadataRetriever.h" #include "MediaPlayerFactory.h" @@ -90,10 +89,6 @@ static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType) p = new StagefrightMetadataRetriever; break; } - case SONIVOX_PLAYER: - ALOGV("create midi metadata retriever"); - p = new MidiMetadataRetriever(); - break; default: // TODO: // support for TEST_PLAYER diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp deleted file mode 100644 index 749ef96..0000000 --- a/media/libmediaplayerservice/MidiFile.cpp +++ /dev/null @@ -1,560 +0,0 @@ -/* MidiFile.cpp -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "MidiFile" -#include "utils/Log.h" - -#include <stdio.h> -#include <assert.h> -#include <limits.h> -#include <unistd.h> -#include <fcntl.h> -#include <sched.h> -#include <utils/threads.h> -#include <libsonivox/eas_reverb.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <system/audio.h> - -#include "MidiFile.h" - -// ---------------------------------------------------------------------------- - -namespace android { - -// ---------------------------------------------------------------------------- - -// The midi engine buffers are a bit small (128 frames), so we batch them up -static const int NUM_BUFFERS = 4; - -// TODO: Determine appropriate return codes -static status_t ERROR_NOT_OPEN = -1; -static status_t ERROR_OPEN_FAILED = -2; -static status_t ERROR_EAS_FAILURE = -3; -static status_t ERROR_ALLOCATE_FAILED = -4; - -static const S_EAS_LIB_CONFIG* pLibConfig = NULL; - -MidiFile::MidiFile() : - mEasData(NULL), mEasHandle(NULL), mAudioBuffer(NULL), - mPlayTime(-1), mDuration(-1), mState(EAS_STATE_ERROR), - mStreamType(AUDIO_STREAM_MUSIC), mLoop(false), mExit(false), - mPaused(false), mRender(false), mTid(-1) -{ - ALOGV("constructor"); - - mFileLocator.path = NULL; - mFileLocator.fd = -1; - mFileLocator.offset = 0; - mFileLocator.length = 0; - - // get the library configuration and do sanity check - if (pLibConfig == NULL) - pLibConfig = EAS_Config(); - if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) { - ALOGE("EAS library/header mismatch"); - goto Failed; - } - - // initialize EAS library - if (EAS_Init(&mEasData) != EAS_SUCCESS) { - ALOGE("EAS_Init failed"); - goto Failed; - } - - // select reverb preset and enable - EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER); - EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE); - - // create playback thread - { - Mutex::Autolock l(mMutex); - mThread = new MidiFileThread(this); - mThread->run("midithread", ANDROID_PRIORITY_AUDIO); - mCondition.wait(mMutex); - ALOGV("thread started"); - } - - // indicate success - if (mTid > 0) { - ALOGV(" render thread(%d) started", mTid); - mState = EAS_STATE_READY; - } - -Failed: - return; -} - -status_t MidiFile::initCheck() -{ - if (mState == EAS_STATE_ERROR) return ERROR_EAS_FAILURE; - return NO_ERROR; -} - -MidiFile::~MidiFile() { - ALOGV("MidiFile destructor"); - release(); -} - -status_t MidiFile::setDataSource( - const sp<IMediaHTTPService> & /*httpService*/, - const char* path, - const KeyedVector<String8, String8> *) { - ALOGV("MidiFile::setDataSource url=%s", path); - Mutex::Autolock lock(mMutex); - - // file still open? - if (mEasHandle) { - reset_nosync(); - } - - // open file and set paused state - mFileLocator.path = strdup(path); - mFileLocator.fd = -1; - mFileLocator.offset = 0; - mFileLocator.length = 0; - EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle); - if (result == EAS_SUCCESS) { - updateState(); - } - - if (result != EAS_SUCCESS) { - ALOGE("EAS_OpenFile failed: [%d]", (int)result); - mState = EAS_STATE_ERROR; - return ERROR_OPEN_FAILED; - } - - mState = EAS_STATE_OPEN; - mPlayTime = 0; - return NO_ERROR; -} - -status_t MidiFile::setDataSource(int fd, int64_t offset, int64_t length) -{ - ALOGV("MidiFile::setDataSource fd=%d", fd); - Mutex::Autolock lock(mMutex); - - // file still open? - if (mEasHandle) { - reset_nosync(); - } - - // open file and set paused state - mFileLocator.fd = dup(fd); - mFileLocator.offset = offset; - mFileLocator.length = length; - EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle); - updateState(); - - if (result != EAS_SUCCESS) { - ALOGE("EAS_OpenFile failed: [%d]", (int)result); - mState = EAS_STATE_ERROR; - return ERROR_OPEN_FAILED; - } - - mState = EAS_STATE_OPEN; - mPlayTime = 0; - return NO_ERROR; -} - -status_t MidiFile::prepare() -{ - ALOGV("MidiFile::prepare"); - Mutex::Autolock lock(mMutex); - if (!mEasHandle) { - return ERROR_NOT_OPEN; - } - EAS_RESULT result; - if ((result = EAS_Prepare(mEasData, mEasHandle)) != EAS_SUCCESS) { - ALOGE("EAS_Prepare failed: [%ld]", result); - return ERROR_EAS_FAILURE; - } - updateState(); - return NO_ERROR; -} - -status_t MidiFile::prepareAsync() -{ - ALOGV("MidiFile::prepareAsync"); - status_t ret = prepare(); - - // don't hold lock during callback - if (ret == NO_ERROR) { - sendEvent(MEDIA_PREPARED); - } else { - sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ret); - } - return ret; -} - -status_t MidiFile::start() -{ - ALOGV("MidiFile::start"); - Mutex::Autolock lock(mMutex); - if (!mEasHandle) { - return ERROR_NOT_OPEN; - } - - // resuming after pause? - if (mPaused) { - if (EAS_Resume(mEasData, mEasHandle) != EAS_SUCCESS) { - return ERROR_EAS_FAILURE; - } - mPaused = false; - updateState(); - } - - mRender = true; - if (mState == EAS_STATE_PLAY) { - sendEvent(MEDIA_STARTED); - } - - // wake up render thread - ALOGV(" wakeup render thread"); - mCondition.signal(); - return NO_ERROR; -} - -status_t MidiFile::stop() -{ - ALOGV("MidiFile::stop"); - Mutex::Autolock lock(mMutex); - if (!mEasHandle) { - return ERROR_NOT_OPEN; - } - if (!mPaused && (mState != EAS_STATE_STOPPED)) { - EAS_RESULT result = EAS_Pause(mEasData, mEasHandle); - if (result != EAS_SUCCESS) { - ALOGE("EAS_Pause returned error %ld", result); - return ERROR_EAS_FAILURE; - } - } - mPaused = false; - sendEvent(MEDIA_STOPPED); - return NO_ERROR; -} - -status_t MidiFile::seekTo(int position) -{ - ALOGV("MidiFile::seekTo %d", position); - // hold lock during EAS calls - { - Mutex::Autolock lock(mMutex); - if (!mEasHandle) { - return ERROR_NOT_OPEN; - } - EAS_RESULT result; - if ((result = EAS_Locate(mEasData, mEasHandle, position, false)) - != EAS_SUCCESS) - { - ALOGE("EAS_Locate returned %ld", result); - return ERROR_EAS_FAILURE; - } - EAS_GetLocation(mEasData, mEasHandle, &mPlayTime); - } - sendEvent(MEDIA_SEEK_COMPLETE); - return NO_ERROR; -} - -status_t MidiFile::pause() -{ - ALOGV("MidiFile::pause"); - Mutex::Autolock lock(mMutex); - if (!mEasHandle) { - return ERROR_NOT_OPEN; - } - if ((mState == EAS_STATE_PAUSING) || (mState == EAS_STATE_PAUSED)) return NO_ERROR; - if (EAS_Pause(mEasData, mEasHandle) != EAS_SUCCESS) { - return ERROR_EAS_FAILURE; - } - mPaused = true; - sendEvent(MEDIA_PAUSED); - return NO_ERROR; -} - -bool MidiFile::isPlaying() -{ - ALOGV("MidiFile::isPlaying, mState=%d", int(mState)); - if (!mEasHandle || mPaused) return false; - return (mState == EAS_STATE_PLAY); -} - -status_t MidiFile::getCurrentPosition(int* position) -{ - ALOGV("MidiFile::getCurrentPosition"); - if (!mEasHandle) { - ALOGE("getCurrentPosition(): file not open"); - return ERROR_NOT_OPEN; - } - if (mPlayTime < 0) { - ALOGE("getCurrentPosition(): mPlayTime = %ld", mPlayTime); - return ERROR_EAS_FAILURE; - } - *position = mPlayTime; - return NO_ERROR; -} - -status_t MidiFile::getDuration(int* duration) -{ - - ALOGV("MidiFile::getDuration"); - { - Mutex::Autolock lock(mMutex); - if (!mEasHandle) return ERROR_NOT_OPEN; - *duration = mDuration; - } - - // if no duration cached, get the duration - // don't need a lock here because we spin up a new engine - if (*duration < 0) { - EAS_I32 temp; - EAS_DATA_HANDLE easData = NULL; - EAS_HANDLE easHandle = NULL; - EAS_RESULT result = EAS_Init(&easData); - if (result == EAS_SUCCESS) { - result = EAS_OpenFile(easData, &mFileLocator, &easHandle); - } - if (result == EAS_SUCCESS) { - result = EAS_Prepare(easData, easHandle); - } - if (result == EAS_SUCCESS) { - result = EAS_ParseMetaData(easData, easHandle, &temp); - } - if (easHandle) { - EAS_CloseFile(easData, easHandle); - } - if (easData) { - EAS_Shutdown(easData); - } - - if (result != EAS_SUCCESS) { - return ERROR_EAS_FAILURE; - } - - // cache successful result - mDuration = *duration = int(temp); - } - - return NO_ERROR; -} - -status_t MidiFile::release() -{ - ALOGV("MidiFile::release"); - Mutex::Autolock l(mMutex); - reset_nosync(); - - // wait for render thread to exit - mExit = true; - mCondition.signal(); - - // wait for thread to exit - if (mAudioBuffer) { - mCondition.wait(mMutex); - } - - // release resources - if (mEasData) { - EAS_Shutdown(mEasData); - mEasData = NULL; - } - return NO_ERROR; -} - -status_t MidiFile::reset() -{ - ALOGV("MidiFile::reset"); - Mutex::Autolock lock(mMutex); - return reset_nosync(); -} - -// call only with mutex held -status_t MidiFile::reset_nosync() -{ - ALOGV("MidiFile::reset_nosync"); - sendEvent(MEDIA_STOPPED); - // close file - if (mEasHandle) { - EAS_CloseFile(mEasData, mEasHandle); - mEasHandle = NULL; - } - if (mFileLocator.path) { - free((void*)mFileLocator.path); - mFileLocator.path = NULL; - } - if (mFileLocator.fd >= 0) { - close(mFileLocator.fd); - } - mFileLocator.fd = -1; - mFileLocator.offset = 0; - mFileLocator.length = 0; - - mPlayTime = -1; - mDuration = -1; - mLoop = false; - mPaused = false; - mRender = false; - return NO_ERROR; -} - -status_t MidiFile::setLooping(int loop) -{ - ALOGV("MidiFile::setLooping"); - Mutex::Autolock lock(mMutex); - if (!mEasHandle) { - return ERROR_NOT_OPEN; - } - loop = loop ? -1 : 0; - if (EAS_SetRepeat(mEasData, mEasHandle, loop) != EAS_SUCCESS) { - return ERROR_EAS_FAILURE; - } - return NO_ERROR; -} - -status_t MidiFile::createOutputTrack() { - if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, - CHANNEL_MASK_USE_CHANNEL_ORDER, AUDIO_FORMAT_PCM_16_BIT, 2 /*bufferCount*/) != NO_ERROR) { - ALOGE("mAudioSink open failed"); - return ERROR_OPEN_FAILED; - } - return NO_ERROR; -} - -int MidiFile::render() { - EAS_RESULT result = EAS_FAILURE; - EAS_I32 count; - int temp; - bool audioStarted = false; - - ALOGV("MidiFile::render"); - - // allocate render buffer - mAudioBuffer = new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * NUM_BUFFERS]; - if (!mAudioBuffer) { - ALOGE("mAudioBuffer allocate failed"); - goto threadExit; - } - - // signal main thread that we started - { - Mutex::Autolock l(mMutex); - mTid = gettid(); - ALOGV("render thread(%d) signal", mTid); - mCondition.signal(); - } - - while (1) { - mMutex.lock(); - - // nothing to render, wait for client thread to wake us up - while (!mRender && !mExit) - { - ALOGV("MidiFile::render - signal wait"); - mCondition.wait(mMutex); - ALOGV("MidiFile::render - signal rx'd"); - } - if (mExit) { - mMutex.unlock(); - break; - } - - // render midi data into the input buffer - //ALOGV("MidiFile::render - rendering audio"); - int num_output = 0; - EAS_PCM* p = mAudioBuffer; - for (int i = 0; i < NUM_BUFFERS; i++) { - result = EAS_Render(mEasData, p, pLibConfig->mixBufferSize, &count); - if (result != EAS_SUCCESS) { - ALOGE("EAS_Render returned %ld", result); - } - p += count * pLibConfig->numChannels; - num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM); - } - - // update playback state and position - // ALOGV("MidiFile::render - updating state"); - EAS_GetLocation(mEasData, mEasHandle, &mPlayTime); - EAS_State(mEasData, mEasHandle, &mState); - mMutex.unlock(); - - // create audio output track if necessary - if (!mAudioSink->ready()) { - ALOGV("MidiFile::render - create output track"); - if (createOutputTrack() != NO_ERROR) - goto threadExit; - } - - // Write data to the audio hardware - // ALOGV("MidiFile::render - writing to audio output"); - if ((temp = mAudioSink->write(mAudioBuffer, num_output)) < 0) { - ALOGE("Error in writing:%d",temp); - return temp; - } - - // start audio output if necessary - if (!audioStarted) { - //ALOGV("MidiFile::render - starting audio"); - mAudioSink->start(); - audioStarted = true; - } - - // still playing? - if ((mState == EAS_STATE_STOPPED) || (mState == EAS_STATE_ERROR) || - (mState == EAS_STATE_PAUSED)) - { - switch(mState) { - case EAS_STATE_STOPPED: - { - ALOGV("MidiFile::render - stopped"); - sendEvent(MEDIA_PLAYBACK_COMPLETE); - break; - } - case EAS_STATE_ERROR: - { - ALOGE("MidiFile::render - error"); - sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN); - break; - } - case EAS_STATE_PAUSED: - ALOGV("MidiFile::render - paused"); - break; - default: - break; - } - mAudioSink->stop(); - audioStarted = false; - mRender = false; - } - } - -threadExit: - mAudioSink.clear(); - if (mAudioBuffer) { - delete [] mAudioBuffer; - mAudioBuffer = NULL; - } - mMutex.lock(); - mTid = -1; - mCondition.signal(); - mMutex.unlock(); - return result; -} - -} // end namespace android diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h deleted file mode 100644 index 82e4e88..0000000 --- a/media/libmediaplayerservice/MidiFile.h +++ /dev/null @@ -1,115 +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. -*/ - -#ifndef ANDROID_MIDIFILE_H -#define ANDROID_MIDIFILE_H - -#include <media/MediaPlayerInterface.h> -#include <libsonivox/eas.h> - -namespace android { - -// Note that the name MidiFile is misleading; this actually represents a MIDI file player -class MidiFile : public MediaPlayerInterface { -public: - MidiFile(); - ~MidiFile(); - - virtual status_t initCheck(); - - virtual status_t setDataSource( - 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( - const sp<IGraphicBufferProducer>& /*bufferProducer*/) - { return UNKNOWN_ERROR; } - virtual status_t prepare(); - virtual status_t prepareAsync(); - virtual status_t start(); - virtual status_t stop(); - virtual status_t seekTo(int msec); - virtual status_t pause(); - virtual bool isPlaying(); - virtual status_t getCurrentPosition(int* msec); - virtual status_t getDuration(int* msec); - virtual status_t release(); - virtual status_t reset(); - virtual status_t setLooping(int loop); - virtual player_type playerType() { return SONIVOX_PLAYER; } - virtual status_t invoke(const Parcel& /*request*/, Parcel* /*reply*/) { - return INVALID_OPERATION; - } - virtual status_t setParameter(int /*key*/, const Parcel &/*request*/) { - return INVALID_OPERATION; - } - virtual status_t getParameter(int /*key*/, Parcel* /*reply*/) { - return INVALID_OPERATION; - } - - -private: - status_t createOutputTrack(); - status_t reset_nosync(); - int render(); - void updateState(){ EAS_State(mEasData, mEasHandle, &mState); } - - Mutex mMutex; - Condition mCondition; - EAS_DATA_HANDLE mEasData; - EAS_HANDLE mEasHandle; - EAS_PCM* mAudioBuffer; - EAS_I32 mPlayTime; - EAS_I32 mDuration; - EAS_STATE mState; - EAS_FILE mFileLocator; - audio_stream_type_t mStreamType; - bool mLoop; - volatile bool mExit; - bool mPaused; - volatile bool mRender; - pid_t mTid; - - class MidiFileThread : public Thread { - public: - MidiFileThread(MidiFile *midiPlayer) : mMidiFile(midiPlayer) { - } - - protected: - virtual ~MidiFileThread() {} - - private: - MidiFile *mMidiFile; - - bool threadLoop() { - int result; - result = mMidiFile->render(); - return false; - } - - MidiFileThread(const MidiFileThread &); - MidiFileThread &operator=(const MidiFileThread &); - }; - - sp<MidiFileThread> mThread; -}; - -}; // namespace android - -#endif // ANDROID_MIDIFILE_H diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.cpp b/media/libmediaplayerservice/MidiMetadataRetriever.cpp deleted file mode 100644 index f3cf6ef..0000000 --- a/media/libmediaplayerservice/MidiMetadataRetriever.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* -** -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "MidiMetadataRetriever" -#include <utils/Log.h> - -#include "MidiMetadataRetriever.h" -#include <media/mediametadataretriever.h> - -#include <media/IMediaHTTPService.h> - -namespace android { - -static status_t ERROR_NOT_OPEN = -1; -static status_t ERROR_OPEN_FAILED = -2; -static status_t ERROR_EAS_FAILURE = -3; -static status_t ERROR_ALLOCATE_FAILED = -4; - -void MidiMetadataRetriever::clearMetadataValues() -{ - ALOGV("clearMetadataValues"); - mMetadataValues[0][0] = '\0'; -} - -status_t MidiMetadataRetriever::setDataSource( - const sp<IMediaHTTPService> &httpService, - const char *url, - const KeyedVector<String8, String8> *headers) -{ - ALOGV("setDataSource: %s", url? url: "NULL pointer"); - Mutex::Autolock lock(mLock); - clearMetadataValues(); - if (mMidiPlayer == 0) { - mMidiPlayer = new MidiFile(); - } - return mMidiPlayer->setDataSource(httpService, url, headers); -} - -status_t MidiMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length) -{ - ALOGV("setDataSource: fd(%d), offset(%lld), and length(%lld)", fd, offset, length); - Mutex::Autolock lock(mLock); - clearMetadataValues(); - if (mMidiPlayer == 0) { - mMidiPlayer = new MidiFile(); - } - return mMidiPlayer->setDataSource(fd, offset, length);; -} - -const char* MidiMetadataRetriever::extractMetadata(int keyCode) -{ - ALOGV("extractMetdata: key(%d)", keyCode); - Mutex::Autolock lock(mLock); - if (mMidiPlayer == 0 || mMidiPlayer->initCheck() != NO_ERROR) { - ALOGE("Midi player is not initialized yet"); - return NULL; - } - switch (keyCode) { - case METADATA_KEY_DURATION: - { - if (mMetadataValues[0][0] == '\0') { - int duration = -1; - if (mMidiPlayer->getDuration(&duration) != NO_ERROR) { - ALOGE("failed to get duration"); - return NULL; - } - snprintf(mMetadataValues[0], MAX_METADATA_STRING_LENGTH, "%d", duration); - } - - ALOGV("duration: %s ms", mMetadataValues[0]); - return mMetadataValues[0]; - } - default: - ALOGE("Unsupported key code (%d)", keyCode); - return NULL; - } - return NULL; -} - -}; - diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.h b/media/libmediaplayerservice/MidiMetadataRetriever.h deleted file mode 100644 index b8214ee..0000000 --- a/media/libmediaplayerservice/MidiMetadataRetriever.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -** -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_MIDIMETADATARETRIEVER_H -#define ANDROID_MIDIMETADATARETRIEVER_H - -#include <utils/threads.h> -#include <utils/Errors.h> -#include <media/MediaMetadataRetrieverInterface.h> - -#include "MidiFile.h" - -namespace android { - -class MidiMetadataRetriever : public MediaMetadataRetrieverInterface { -public: - MidiMetadataRetriever() {} - ~MidiMetadataRetriever() {} - - virtual status_t setDataSource( - const sp<IMediaHTTPService> &httpService, - const char *url, - const KeyedVector<String8, String8> *headers); - - virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - virtual const char* extractMetadata(int keyCode); - -private: - static const uint32_t MAX_METADATA_STRING_LENGTH = 128; - void clearMetadataValues(); - - Mutex mLock; - sp<MidiFile> mMidiPlayer; - char mMetadataValues[1][MAX_METADATA_STRING_LENGTH]; -}; - -}; // namespace android - -#endif // ANDROID_MIDIMETADATARETRIEVER_H diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 3d093fa..55763f0 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -75,6 +75,7 @@ StagefrightRecorder::StagefrightRecorder() mAudioSource(AUDIO_SOURCE_CNT), mVideoSource(VIDEO_SOURCE_LIST_END), mCaptureTimeLapse(false), + mCaptureFps(0.0f), mStarted(false) { ALOGV("Constructor"); @@ -206,7 +207,7 @@ status_t StagefrightRecorder::setVideoSize(int width, int height) { status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) { ALOGV("setVideoFrameRate: %d", frames_per_second); if ((frames_per_second <= 0 && frames_per_second != -1) || - frames_per_second > 120) { + frames_per_second > kMaxHighSpeedFps) { ALOGE("Invalid video frame rate: %d", frames_per_second); return BAD_VALUE; } @@ -241,14 +242,6 @@ status_t StagefrightRecorder::setPreviewSurface(const sp<IGraphicBufferProducer> return OK; } -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. - - return -EPERM; -} - status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) { ALOGV("setOutputFile: %d, %lld, %lld", fd, offset, length); // These don't make any sense, do they? @@ -260,6 +253,9 @@ status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t leng return -EBADF; } + // start with a clean, empty file + ftruncate(fd, 0); + if (mOutputFd >= 0) { ::close(mOutputFd); } @@ -268,6 +264,31 @@ status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t leng return OK; } +// Attempt to parse an float literal optionally surrounded by whitespace, +// returns true on success, false otherwise. +static bool safe_strtof(const char *s, float *val) { + char *end; + + // It is lame, but according to man page, we have to set errno to 0 + // before calling strtof(). + errno = 0; + *val = strtof(s, &end); + + if (end == s || errno == ERANGE) { + return false; + } + + // Skip trailing whitespace + while (isspace(*end)) { + ++end; + } + + // For a successful return, the string must contain nothing but a valid + // float literal optionally surrounded by whitespace. + + return *end == '\0'; +} + // Attempt to parse an int64 literal optionally surrounded by whitespace, // returns true on success, false otherwise. static bool safe_strtoi64(const char *s, int64_t *val) { @@ -551,8 +572,10 @@ status_t StagefrightRecorder::setParamTimeLapseEnable(int32_t timeLapseEnable) { return OK; } -status_t StagefrightRecorder::setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs) { - ALOGV("setParamTimeBetweenTimeLapseFrameCapture: %lld us", timeUs); +status_t StagefrightRecorder::setParamTimeLapseFps(float fps) { + ALOGV("setParamTimeLapseFps: %.2f", fps); + + int64_t timeUs = (int64_t) (1000000.0 / fps + 0.5f); // Not allowing time more than a day if (timeUs <= 0 || timeUs > 86400*1E6) { @@ -560,6 +583,7 @@ status_t StagefrightRecorder::setParamTimeBetweenTimeLapseFrameCapture(int64_t t return BAD_VALUE; } + mCaptureFps = fps; mTimeBetweenTimeLapseFrameCaptureUs = timeUs; return OK; } @@ -687,11 +711,10 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi32(value.string(), &timeLapseEnable)) { return setParamTimeLapseEnable(timeLapseEnable); } - } else if (key == "time-between-time-lapse-frame-capture") { - int64_t timeBetweenTimeLapseFrameCaptureUs; - if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureUs)) { - return setParamTimeBetweenTimeLapseFrameCapture( - timeBetweenTimeLapseFrameCaptureUs); + } else if (key == "time-lapse-fps") { + float fps; + if (safe_strtof(value.string(), &fps)) { + return setParamTimeLapseFps(fps); } } else { ALOGE("setParameter: failed to find key %s", key.string()); @@ -1586,10 +1609,11 @@ status_t StagefrightRecorder::setupMPEG4orWEBMRecording() { status_t err = OK; sp<MediaWriter> writer; + sp<MPEG4Writer> mp4writer; if (mOutputFormat == OUTPUT_FORMAT_WEBM) { writer = new WebmWriter(mOutputFd); } else { - writer = new MPEG4Writer(mOutputFd); + writer = mp4writer = new MPEG4Writer(mOutputFd); } if (mVideoSource < VIDEO_SOURCE_LIST_END) { @@ -1622,13 +1646,15 @@ status_t StagefrightRecorder::setupMPEG4orWEBMRecording() { mTotalBitRate += mAudioBitRate; } + if (mCaptureTimeLapse) { + mp4writer->setCaptureRate(mCaptureFps); + } + if (mInterleaveDurationUs > 0) { - reinterpret_cast<MPEG4Writer *>(writer.get())-> - setInterleaveDuration(mInterleaveDurationUs); + mp4writer->setInterleaveDuration(mInterleaveDurationUs); } if (mLongitudex10000 > -3600000 && mLatitudex10000 > -3600000) { - reinterpret_cast<MPEG4Writer *>(writer.get())-> - setGeoData(mLatitudex10000, mLongitudex10000); + mp4writer->setGeoData(mLatitudex10000, mLongitudex10000); } } if (mMaxFileDurationUs != 0) { diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 54c38d3..f34c229 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -53,7 +53,6 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t setVideoFrameRate(int frames_per_second); virtual status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy); virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface); - virtual status_t setOutputFile(const char *path); virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); virtual status_t setParameters(const String8& params); virtual status_t setListener(const sp<IMediaRecorderClient>& listener); @@ -110,6 +109,7 @@ private: int32_t mTotalBitRate; bool mCaptureTimeLapse; + float mCaptureFps; int64_t mTimeBetweenTimeLapseFrameCaptureUs; sp<CameraSourceTimeLapse> mCameraSourceTimeLapse; @@ -127,6 +127,8 @@ private: sp<IGraphicBufferProducer> mGraphicBufferProducer; sp<ALooper> mLooper; + static const int kMaxHighSpeedFps = 1000; + status_t prepareInternal(); status_t setupMPEG4orWEBMRecording(); void setupMPEG4orWEBMMetaData(sp<MetaData> *meta); @@ -154,7 +156,7 @@ private: status_t setParamAudioSamplingRate(int32_t sampleRate); status_t setParamAudioTimeScale(int32_t timeScale); status_t setParamTimeLapseEnable(int32_t timeLapseEnable); - status_t setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs); + status_t setParamTimeLapseFps(float fps); status_t setParamVideoEncodingBitRate(int32_t bitRate); status_t setParamVideoIFramesInterval(int32_t seconds); status_t setParamVideoEncoderProfile(int32_t profile); diff --git a/media/libmediaplayerservice/TestPlayerStub.cpp b/media/libmediaplayerservice/TestPlayerStub.cpp index 5795773..c8bf6c5 100644 --- a/media/libmediaplayerservice/TestPlayerStub.cpp +++ b/media/libmediaplayerservice/TestPlayerStub.cpp @@ -45,7 +45,7 @@ bool isTestBuild() { char prop[PROPERTY_VALUE_MAX] = { '\0', }; - property_get(kBuildTypePropName, prop, '\0'); + property_get(kBuildTypePropName, prop, "\0"); return strcmp(prop, kEngBuild) == 0 || strcmp(prop, kTestBuild) == 0; } diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk index 676c0a6..6609874 100644 --- a/media/libmediaplayerservice/nuplayer/Android.mk +++ b/media/libmediaplayerservice/nuplayer/Android.mk @@ -5,7 +5,9 @@ LOCAL_SRC_FILES:= \ GenericSource.cpp \ HTTPLiveSource.cpp \ NuPlayer.cpp \ + NuPlayerCCDecoder.cpp \ NuPlayerDecoder.cpp \ + NuPlayerDecoderBase.cpp \ NuPlayerDecoderPassThrough.cpp \ NuPlayerDriver.cpp \ NuPlayerRenderer.cpp \ diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index 0e520a8..a040343 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -40,35 +40,50 @@ namespace android { +static int64_t kLowWaterMarkUs = 2000000ll; // 2secs +static int64_t kHighWaterMarkUs = 5000000ll; // 5secs +static const ssize_t kLowWaterMarkBytes = 40000; +static const ssize_t kHighWaterMarkBytes = 200000; + NuPlayer::GenericSource::GenericSource( const sp<AMessage> ¬ify, bool uidValid, uid_t uid) : Source(notify), + mAudioTimeUs(0), + mAudioLastDequeueTimeUs(0), + mVideoTimeUs(0), + mVideoLastDequeueTimeUs(0), mFetchSubtitleDataGeneration(0), mFetchTimedTextDataGeneration(0), mDurationUs(0ll), mAudioIsVorbis(false), mIsWidevine(false), + mIsSecure(false), + mIsStreaming(false), mUIDValid(uidValid), mUID(uid), + mFd(-1), mDrmManagerClient(NULL), - mMetaDataSize(-1ll), mBitrate(-1ll), mPollBufferingGeneration(0), - mPendingReadBufferTypes(0) { + mPendingReadBufferTypes(0), + mBuffering(false), + mPrepareBuffering(false), + mPrevBufferPercentage(-1) { resetDataSource(); DataSource::RegisterDefaultSniffers(); } void NuPlayer::GenericSource::resetDataSource() { - mAudioTimeUs = 0; - mVideoTimeUs = 0; mHTTPService.clear(); mHttpSource.clear(); mUri.clear(); mUriHeaders.clear(); - mFd = -1; + if (mFd >= 0) { + close(mFd); + mFd = -1; + } mOffset = 0; mLength = 0; setDrmPlaybackStatusIfNeeded(Playback::STOP, 0); @@ -115,23 +130,34 @@ sp<MetaData> NuPlayer::GenericSource::getFileFormatMeta() const { status_t NuPlayer::GenericSource::initFromDataSource() { sp<MediaExtractor> extractor; + String8 mimeType; + float confidence; + sp<AMessage> dummy; + bool isWidevineStreaming = false; CHECK(mDataSource != NULL); if (mIsWidevine) { - String8 mimeType; - float confidence; - sp<AMessage> dummy; - bool success; - - success = SniffWVM(mDataSource, &mimeType, &confidence, &dummy); - if (!success - || strcasecmp( + isWidevineStreaming = SniffWVM( + mDataSource, &mimeType, &confidence, &dummy); + if (!isWidevineStreaming || + strcasecmp( mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) { ALOGE("unsupported widevine mime: %s", mimeType.string()); return UNKNOWN_ERROR; } + } else if (mIsStreaming) { + if (!mDataSource->sniff(&mimeType, &confidence, &dummy)) { + return UNKNOWN_ERROR; + } + isWidevineStreaming = !strcasecmp( + mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM); + } + if (isWidevineStreaming) { + // we don't want cached source for widevine streaming. + mCachedSource.clear(); + mDataSource = mHttpSource; mWVMExtractor = new WVMExtractor(mDataSource); mWVMExtractor->setAdaptiveStreamingMode(true); if (mUIDValid) { @@ -140,7 +166,7 @@ status_t NuPlayer::GenericSource::initFromDataSource() { extractor = mWVMExtractor; } else { extractor = MediaExtractor::Create(mDataSource, - mSniffedMIME.empty() ? NULL: mSniffedMIME.c_str()); + mimeType.isEmpty() ? NULL : mimeType.string()); } if (extractor == NULL) { @@ -157,6 +183,17 @@ status_t NuPlayer::GenericSource::initFromDataSource() { if (mFileMeta->findInt64(kKeyDuration, &duration)) { mDurationUs = duration; } + + if (!mIsWidevine) { + // Check mime to see if we actually have a widevine source. + // If the data source is not URL-type (eg. file source), we + // won't be able to tell until now. + const char *fileMime; + if (mFileMeta->findCString(kKeyMIMEType, &fileMime) + && !strncasecmp(fileMime, "video/wvm", 9)) { + mIsWidevine = true; + } + } } int32_t totalBitrate = 0; @@ -202,7 +239,7 @@ status_t NuPlayer::GenericSource::initFromDataSource() { int32_t secure; if (meta->findInt32(kKeyRequiresSecureBuffers, &secure) && secure) { - mIsWidevine = true; + mIsSecure = true; if (mUIDValid) { extractor->setUID(mUID); } @@ -228,6 +265,20 @@ status_t NuPlayer::GenericSource::initFromDataSource() { } } + // Start the selected A/V tracks now before we start buffering. + // Widevine sources might re-initialize crypto when starting, if we delay + // this to start(), all data buffered during prepare would be wasted. + // (We don't actually start reading until start().) + if (mAudioTrack.mSource != NULL && mAudioTrack.mSource->start() != OK) { + ALOGE("failed to start audio track!"); + return UNKNOWN_ERROR; + } + + if (mVideoTrack.mSource != NULL && mVideoTrack.mSource->start() != OK) { + ALOGE("failed to start video track!"); + return UNKNOWN_ERROR; + } + mBitrate = totalBitrate; return OK; @@ -257,7 +308,7 @@ int64_t NuPlayer::GenericSource::getLastReadPosition() { status_t NuPlayer::GenericSource::setBuffers( bool audio, Vector<MediaBuffer *> &buffers) { - if (mIsWidevine && !audio) { + if (mIsSecure && !audio) { return mVideoTrack.mSource->setBuffers(buffers); } return INVALID_OPERATION; @@ -268,6 +319,7 @@ NuPlayer::GenericSource::~GenericSource() { mLooper->unregisterHandler(id()); mLooper->stop(); } + resetDataSource(); } void NuPlayer::GenericSource::prepareAsync() { @@ -279,15 +331,20 @@ void NuPlayer::GenericSource::prepareAsync() { mLooper->registerHandler(this); } - sp<AMessage> msg = new AMessage(kWhatPrepareAsync, id()); + sp<AMessage> msg = new AMessage(kWhatPrepareAsync, this); msg->post(); } void NuPlayer::GenericSource::onPrepareAsync() { // delayed data source creation if (mDataSource == NULL) { + // set to false first, if the extractor + // comes back as secure, set it to true then. + mIsSecure = false; + if (!mUri.empty()) { const char* uri = mUri.c_str(); + String8 contentType; mIsWidevine = !strncasecmp(uri, "widevine://", 11); if (!strncasecmp("http://", uri, 7) @@ -302,14 +359,13 @@ void NuPlayer::GenericSource::onPrepareAsync() { } mDataSource = DataSource::CreateFromURI( - mHTTPService, uri, &mUriHeaders, &mContentType, + mHTTPService, uri, &mUriHeaders, &contentType, static_cast<HTTPBase *>(mHttpSource.get())); } else { - // set to false first, if the extractor - // comes back as secure, set it to true then. mIsWidevine = false; mDataSource = new FileSource(mFd, mOffset, mLength); + mFd = -1; } if (mDataSource == NULL) { @@ -322,25 +378,17 @@ void NuPlayer::GenericSource::onPrepareAsync() { mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get()); } - if (mIsWidevine || mCachedSource != NULL) { - schedulePollBuffering(); - } + // For widevine or other cached streaming cases, we need to wait for + // enough buffering before reporting prepared. + // Note that even when URL doesn't start with widevine://, mIsWidevine + // could still be set to true later, if the streaming or file source + // is sniffed to be widevine. We don't want to buffer for file source + // in that case, so must check the flag now. + mIsStreaming = (mIsWidevine || mCachedSource != NULL); } - // check initial caching status - status_t err = prefillCacheIfNecessary(); - if (err != OK) { - if (err == -EAGAIN) { - (new AMessage(kWhatPrepareAsync, id()))->post(200000); - } else { - ALOGE("Failed to prefill data cache!"); - notifyPreparedAndCleanup(UNKNOWN_ERROR); - } - return; - } - - // init extrator from data source - err = initFromDataSource(); + // init extractor from data source + status_t err = initFromDataSource(); if (err != OK) { ALOGE("Failed to init from data source!"); @@ -360,20 +408,25 @@ void NuPlayer::GenericSource::onPrepareAsync() { } notifyFlagsChanged( - (mIsWidevine ? FLAG_SECURE : 0) + (mIsSecure ? FLAG_SECURE : 0) + | (mDecryptHandle != NULL ? FLAG_PROTECTED : 0) | FLAG_CAN_PAUSE | FLAG_CAN_SEEK_BACKWARD | FLAG_CAN_SEEK_FORWARD | FLAG_CAN_SEEK); - notifyPrepared(); + if (mIsStreaming) { + mPrepareBuffering = true; + + ensureCacheIsFetching(); + restartPollBuffering(); + } else { + notifyPrepared(); + } } void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) { if (err != OK) { - mMetaDataSize = -1ll; - mContentType = ""; - mSniffedMIME = ""; mDataSource.clear(); mCachedSource.clear(); mHttpSource.clear(); @@ -383,103 +436,31 @@ void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) { notifyPrepared(err); } -status_t NuPlayer::GenericSource::prefillCacheIfNecessary() { - CHECK(mDataSource != NULL); - - if (mCachedSource == NULL) { - // no prefill if the data source is not cached - return OK; - } - - // We're not doing this for streams that appear to be audio-only - // streams to ensure that even low bandwidth streams start - // playing back fairly instantly. - if (!strncasecmp(mContentType.string(), "audio/", 6)) { - return OK; - } - - // We're going to prefill the cache before trying to instantiate - // the extractor below, as the latter is an operation that otherwise - // could block on the datasource for a significant amount of time. - // During that time we'd be unable to abort the preparation phase - // without this prefill. - - // Initially make sure we have at least 192 KB for the sniff - // to complete without blocking. - static const size_t kMinBytesForSniffing = 192 * 1024; - static const size_t kDefaultMetaSize = 200000; - - status_t finalStatus; - - size_t cachedDataRemaining = - mCachedSource->approxDataRemaining(&finalStatus); - - if (finalStatus != OK || (mMetaDataSize >= 0 - && (off64_t)cachedDataRemaining >= mMetaDataSize)) { - ALOGV("stop caching, status %d, " - "metaDataSize %lld, cachedDataRemaining %zu", - finalStatus, mMetaDataSize, cachedDataRemaining); - return OK; - } - - ALOGV("now cached %zu bytes of data", cachedDataRemaining); - - if (mMetaDataSize < 0 - && cachedDataRemaining >= kMinBytesForSniffing) { - String8 tmp; - float confidence; - sp<AMessage> meta; - if (!mCachedSource->sniff(&tmp, &confidence, &meta)) { - return UNKNOWN_ERROR; - } - - // We successfully identified the file's extractor to - // be, remember this mime type so we don't have to - // sniff it again when we call MediaExtractor::Create() - mSniffedMIME = tmp.string(); - - if (meta == NULL - || !meta->findInt64("meta-data-size", - reinterpret_cast<int64_t*>(&mMetaDataSize))) { - mMetaDataSize = kDefaultMetaSize; - } - - if (mMetaDataSize < 0ll) { - ALOGE("invalid metaDataSize = %lld bytes", mMetaDataSize); - return UNKNOWN_ERROR; - } - } - - return -EAGAIN; -} - void NuPlayer::GenericSource::start() { ALOGI("start"); mStopRead = false; if (mAudioTrack.mSource != NULL) { - CHECK_EQ(mAudioTrack.mSource->start(), (status_t)OK); - postReadBuffer(MEDIA_TRACK_TYPE_AUDIO); } if (mVideoTrack.mSource != NULL) { - CHECK_EQ(mVideoTrack.mSource->start(), (status_t)OK); - postReadBuffer(MEDIA_TRACK_TYPE_VIDEO); } setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000); mStarted = true; + + (new AMessage(kWhatStart, this))->post(); } void NuPlayer::GenericSource::stop() { // nothing to do, just account for DRM playback status setDrmPlaybackStatusIfNeeded(Playback::STOP, 0); mStarted = false; - if (mIsWidevine) { - // For a widevine source we need to prevent any further reads. - sp<AMessage> msg = new AMessage(kWhatStopWidevine, id()); + if (mIsWidevine || mIsSecure) { + // For widevine or secure sources we need to prevent any further reads. + sp<AMessage> msg = new AMessage(kWhatStopWidevine, this); sp<AMessage> response; (void) msg->postAndAwaitResponse(&response); } @@ -495,6 +476,8 @@ void NuPlayer::GenericSource::resume() { // nothing to do, just account for DRM playback status setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000); mStarted = true; + + (new AMessage(kWhatResume, this))->post(); } void NuPlayer::GenericSource::disconnect() { @@ -521,28 +504,123 @@ status_t NuPlayer::GenericSource::feedMoreTSData() { } void NuPlayer::GenericSource::schedulePollBuffering() { - sp<AMessage> msg = new AMessage(kWhatPollBuffering, id()); + sp<AMessage> msg = new AMessage(kWhatPollBuffering, this); msg->setInt32("generation", mPollBufferingGeneration); msg->post(1000000ll); } void NuPlayer::GenericSource::cancelPollBuffering() { + mBuffering = false; ++mPollBufferingGeneration; + mPrevBufferPercentage = -1; +} + +void NuPlayer::GenericSource::restartPollBuffering() { + if (mIsStreaming) { + cancelPollBuffering(); + onPollBuffering(); + } } -void NuPlayer::GenericSource::notifyBufferingUpdate(int percentage) { +void NuPlayer::GenericSource::notifyBufferingUpdate(int32_t percentage) { + // Buffering percent could go backward as it's estimated from remaining + // data and last access time. This could cause the buffering position + // drawn on media control to jitter slightly. Remember previously reported + // percentage and don't allow it to go backward. + if (percentage < mPrevBufferPercentage) { + percentage = mPrevBufferPercentage; + } else if (percentage > 100) { + percentage = 100; + } + + mPrevBufferPercentage = percentage; + + ALOGV("notifyBufferingUpdate: buffering %d%%", percentage); + sp<AMessage> msg = dupNotify(); msg->setInt32("what", kWhatBufferingUpdate); msg->setInt32("percentage", percentage); msg->post(); } +void NuPlayer::GenericSource::startBufferingIfNecessary() { + ALOGV("startBufferingIfNecessary: mPrepareBuffering=%d, mBuffering=%d", + mPrepareBuffering, mBuffering); + + if (mPrepareBuffering) { + return; + } + + if (!mBuffering) { + mBuffering = true; + + ensureCacheIsFetching(); + sendCacheStats(); + + sp<AMessage> notify = dupNotify(); + notify->setInt32("what", kWhatPauseOnBufferingStart); + notify->post(); + } +} + +void NuPlayer::GenericSource::stopBufferingIfNecessary() { + ALOGV("stopBufferingIfNecessary: mPrepareBuffering=%d, mBuffering=%d", + mPrepareBuffering, mBuffering); + + if (mPrepareBuffering) { + mPrepareBuffering = false; + notifyPrepared(); + return; + } + + if (mBuffering) { + mBuffering = false; + + sendCacheStats(); + + sp<AMessage> notify = dupNotify(); + notify->setInt32("what", kWhatResumeOnBufferingEnd); + notify->post(); + } +} + +void NuPlayer::GenericSource::sendCacheStats() { + int32_t kbps = 0; + status_t err = UNKNOWN_ERROR; + + if (mWVMExtractor != NULL) { + err = mWVMExtractor->getEstimatedBandwidthKbps(&kbps); + } else if (mCachedSource != NULL) { + err = mCachedSource->getEstimatedBandwidthKbps(&kbps); + } + + if (err == OK) { + sp<AMessage> notify = dupNotify(); + notify->setInt32("what", kWhatCacheStats); + notify->setInt32("bandwidth", kbps); + notify->post(); + } +} + +void NuPlayer::GenericSource::ensureCacheIsFetching() { + if (mCachedSource != NULL) { + mCachedSource->resumeFetchingIfNecessary(); + } +} + void NuPlayer::GenericSource::onPollBuffering() { status_t finalStatus = UNKNOWN_ERROR; - int64_t cachedDurationUs = 0ll; + int64_t cachedDurationUs = -1ll; + ssize_t cachedDataRemaining = -1; - if (mCachedSource != NULL) { - size_t cachedDataRemaining = + ALOGW_IF(mWVMExtractor != NULL && mCachedSource != NULL, + "WVMExtractor and NuCachedSource both present"); + + if (mWVMExtractor != NULL) { + cachedDurationUs = + mWVMExtractor->getCachedDurationUs(&finalStatus); + } else if (mCachedSource != NULL) { + cachedDataRemaining = mCachedSource->approxDataRemaining(&finalStatus); if (finalStatus == OK) { @@ -557,28 +635,50 @@ void NuPlayer::GenericSource::onPollBuffering() { cachedDurationUs = cachedDataRemaining * 8000000ll / bitrate; } } - } else if (mWVMExtractor != NULL) { - cachedDurationUs - = mWVMExtractor->getCachedDurationUs(&finalStatus); } - if (finalStatus == ERROR_END_OF_STREAM) { - notifyBufferingUpdate(100); - cancelPollBuffering(); + if (finalStatus != OK) { + ALOGV("onPollBuffering: EOS (finalStatus = %d)", finalStatus); + + if (finalStatus == ERROR_END_OF_STREAM) { + notifyBufferingUpdate(100); + } + + stopBufferingIfNecessary(); return; - } else if (cachedDurationUs > 0ll && mDurationUs > 0ll) { - int percentage = 100.0 * cachedDurationUs / mDurationUs; - if (percentage > 100) { - percentage = 100; + } else if (cachedDurationUs >= 0ll) { + if (mDurationUs > 0ll) { + int64_t cachedPosUs = getLastReadPosition() + cachedDurationUs; + int percentage = 100.0 * cachedPosUs / mDurationUs; + if (percentage > 100) { + percentage = 100; + } + + notifyBufferingUpdate(percentage); } - notifyBufferingUpdate(percentage); + ALOGV("onPollBuffering: cachedDurationUs %.1f sec", + cachedDurationUs / 1000000.0f); + + if (cachedDurationUs < kLowWaterMarkUs) { + startBufferingIfNecessary(); + } else if (cachedDurationUs > kHighWaterMarkUs) { + stopBufferingIfNecessary(); + } + } else if (cachedDataRemaining >= 0) { + ALOGV("onPollBuffering: cachedDataRemaining %d bytes", + cachedDataRemaining); + + if (cachedDataRemaining < kLowWaterMarkBytes) { + startBufferingIfNecessary(); + } else if (cachedDataRemaining > kHighWaterMarkBytes) { + stopBufferingIfNecessary(); + } } schedulePollBuffering(); } - void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatPrepareAsync: @@ -644,23 +744,27 @@ void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) { track->mSource->start(); track->mIndex = trackIndex; - status_t avail; - if (!track->mPackets->hasBufferAvailable(&avail)) { - // sync from other source - TRESPASS(); - break; - } - int64_t timeUs, actualTimeUs; const bool formatChange = true; - sp<AMessage> latestMeta = track->mPackets->getLatestEnqueuedMeta(); - CHECK(latestMeta != NULL && latestMeta->findInt64("timeUs", &timeUs)); + if (trackType == MEDIA_TRACK_TYPE_AUDIO) { + timeUs = mAudioLastDequeueTimeUs; + } else { + timeUs = mVideoLastDequeueTimeUs; + } readBuffer(trackType, timeUs, &actualTimeUs, formatChange); readBuffer(counterpartType, -1, NULL, formatChange); ALOGV("timeUs %lld actualTimeUs %lld", timeUs, actualTimeUs); break; } + + case kWhatStart: + case kWhatResume: + { + restartPollBuffering(); + break; + } + case kWhatPollBuffering: { int32_t generation; @@ -710,7 +814,7 @@ void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) { mVideoTrack.mPackets->clear(); } sp<AMessage> response = new AMessage; - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); response->postReply(replyID); break; @@ -750,7 +854,7 @@ void NuPlayer::GenericSource::fetchTextData( const int64_t oneSecUs = 1000000ll; delayUs -= oneSecUs; } - sp<AMessage> msg2 = new AMessage(sendWhat, id()); + sp<AMessage> msg2 = new AMessage(sendWhat, this); msg2->setInt32("generation", msgGeneration); msg2->post(delayUs < 0 ? 0 : delayUs); } @@ -790,7 +894,7 @@ void NuPlayer::GenericSource::sendTextData( } sp<MetaData> NuPlayer::GenericSource::getFormatMeta(bool audio) { - sp<AMessage> msg = new AMessage(kWhatGetFormat, id()); + sp<AMessage> msg = new AMessage(kWhatGetFormat, this); msg->setInt32("audio", audio); sp<AMessage> response; @@ -812,7 +916,7 @@ void NuPlayer::GenericSource::onGetFormatMeta(sp<AMessage> msg) const { sp<MetaData> format = doGetFormatMeta(audio); response->setPointer("format", format.get()); - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); response->postReply(replyID); } @@ -842,7 +946,12 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit( status_t finalResult; if (!track->mPackets->hasBufferAvailable(&finalResult)) { - return (finalResult == OK ? -EWOULDBLOCK : finalResult); + if (finalResult == OK) { + postReadBuffer( + audio ? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO); + return -EWOULDBLOCK; + } + return finalResult; } status_t result = track->mPackets->dequeueAccessUnit(accessUnit); @@ -866,10 +975,15 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit( int64_t timeUs; status_t eosResult; // ignored CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs)); + if (audio) { + mAudioLastDequeueTimeUs = timeUs; + } else { + mVideoLastDequeueTimeUs = timeUs; + } if (mSubtitleTrack.mSource != NULL && !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) { - sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id()); + sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this); msg->setInt64("timeUs", timeUs); msg->setInt32("generation", mFetchSubtitleDataGeneration); msg->post(); @@ -877,7 +991,7 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit( if (mTimedTextTrack.mSource != NULL && !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) { - sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, id()); + sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, this); msg->setInt64("timeUs", timeUs); msg->setInt32("generation", mFetchTimedTextDataGeneration); msg->post(); @@ -942,7 +1056,7 @@ sp<AMessage> NuPlayer::GenericSource::getTrackInfo(size_t trackIndex) const { } ssize_t NuPlayer::GenericSource::getSelectedTrack(media_track_type type) const { - sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id()); + sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, this); msg->setInt32("type", type); sp<AMessage> response; @@ -965,7 +1079,7 @@ void NuPlayer::GenericSource::onGetSelectedTrack(sp<AMessage> msg) const { ssize_t index = doGetSelectedTrack(type); response->setInt32("index", index); - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); response->postReply(replyID); } @@ -996,11 +1110,12 @@ ssize_t NuPlayer::GenericSource::doGetSelectedTrack(media_track_type type) const return -1; } -status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select) { +status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select, int64_t timeUs) { ALOGV("%s track: %zu", select ? "select" : "deselect", trackIndex); - sp<AMessage> msg = new AMessage(kWhatSelectTrack, id()); + sp<AMessage> msg = new AMessage(kWhatSelectTrack, this); msg->setInt32("trackIndex", trackIndex); msg->setInt32("select", select); + msg->setInt64("timeUs", timeUs); sp<AMessage> response; status_t err = msg->postAndAwaitResponse(&response); @@ -1013,19 +1128,21 @@ status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select) { void NuPlayer::GenericSource::onSelectTrack(sp<AMessage> msg) { int32_t trackIndex, select; + int64_t timeUs; CHECK(msg->findInt32("trackIndex", &trackIndex)); CHECK(msg->findInt32("select", &select)); + CHECK(msg->findInt64("timeUs", &timeUs)); sp<AMessage> response = new AMessage; - status_t err = doSelectTrack(trackIndex, select); + status_t err = doSelectTrack(trackIndex, select, timeUs); response->setInt32("err", err); - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); response->postReply(replyID); } -status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select) { +status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select, int64_t timeUs) { if (trackIndex >= mSources.size()) { return BAD_INDEX; } @@ -1078,6 +1195,23 @@ status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select) mFetchTimedTextDataGeneration++; } + status_t eosResult; // ignored + if (mSubtitleTrack.mSource != NULL + && !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) { + sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this); + msg->setInt64("timeUs", timeUs); + msg->setInt32("generation", mFetchSubtitleDataGeneration); + msg->post(); + } + + if (mTimedTextTrack.mSource != NULL + && !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) { + sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, this); + msg->setInt64("timeUs", timeUs); + msg->setInt32("generation", mFetchTimedTextDataGeneration); + msg->post(); + } + return OK; } else if (!strncasecmp(mime, "audio/", 6) || !strncasecmp(mime, "video/", 6)) { bool audio = !strncasecmp(mime, "audio/", 6); @@ -1086,7 +1220,7 @@ status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select) return OK; } - sp<AMessage> msg = new AMessage(kWhatChangeAVSource, id()); + sp<AMessage> msg = new AMessage(kWhatChangeAVSource, this); msg->setInt32("trackIndex", trackIndex); msg->post(); return OK; @@ -1096,7 +1230,7 @@ status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select) } status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) { - sp<AMessage> msg = new AMessage(kWhatSeek, id()); + sp<AMessage> msg = new AMessage(kWhatSeek, this); msg->setInt64("seekTimeUs", seekTimeUs); sp<AMessage> response; @@ -1116,7 +1250,7 @@ void NuPlayer::GenericSource::onSeek(sp<AMessage> msg) { status_t err = doSeek(seekTimeUs); response->setInt32("err", err); - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); response->postReply(replyID); } @@ -1132,22 +1266,32 @@ status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) { readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, &actualTimeUs); seekTimeUs = actualTimeUs; + mVideoLastDequeueTimeUs = seekTimeUs; } if (mAudioTrack.mSource != NULL) { readBuffer(MEDIA_TRACK_TYPE_AUDIO, seekTimeUs); + mAudioLastDequeueTimeUs = seekTimeUs; } setDrmPlaybackStatusIfNeeded(Playback::START, seekTimeUs / 1000); if (!mStarted) { setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0); } + + // If currently buffering, post kWhatBufferingEnd first, so that + // NuPlayer resumes. Otherwise, if cache hits high watermark + // before new polling happens, no one will resume the playback. + stopBufferingIfNecessary(); + restartPollBuffering(); + return OK; } sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer( MediaBuffer* mb, media_track_type trackType, + int64_t /* seekTimeUs */, int64_t *actualTimeUs) { bool audio = trackType == MEDIA_TRACK_TYPE_AUDIO; size_t outLength = mb->range_length(); @@ -1157,7 +1301,7 @@ sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer( } sp<ABuffer> ab; - if (mIsWidevine && !audio) { + if (mIsSecure && !audio) { // data is already provided in the buffer ab = new ABuffer(NULL, mb->range_length()); mb->add_ref(); @@ -1185,6 +1329,16 @@ sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer( CHECK(mb->meta_data()->findInt64(kKeyTime, &timeUs)); meta->setInt64("timeUs", timeUs); +#if 0 + // Temporarily disable pre-roll till we have a full solution to handle + // both single seek and continous seek gracefully. + if (seekTimeUs > timeUs) { + sp<AMessage> extra = new AMessage; + extra->setInt64("resume-at-mediaTimeUs", seekTimeUs); + meta->setMessage("extra", extra); + } +#endif + if (trackType == MEDIA_TRACK_TYPE_TIMEDTEXT) { const char *mime; CHECK(mTimedTextTrack.mSource != NULL @@ -1216,7 +1370,7 @@ void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) { if ((mPendingReadBufferTypes & (1 << trackType)) == 0) { mPendingReadBufferTypes |= (1 << trackType); - sp<AMessage> msg = new AMessage(kWhatReadBuffer, id()); + sp<AMessage> msg = new AMessage(kWhatReadBuffer, this); msg->setInt32("trackType", trackType); msg->post(); } @@ -1226,14 +1380,13 @@ void NuPlayer::GenericSource::onReadBuffer(sp<AMessage> msg) { int32_t tmpType; CHECK(msg->findInt32("trackType", &tmpType)); media_track_type trackType = (media_track_type)tmpType; + readBuffer(trackType); { // only protect the variable change, as readBuffer may - // take considerable time. This may result in one extra - // read being processed, but that is benign. + // take considerable time. Mutex::Autolock _l(mReadBufferLock); mPendingReadBufferTypes &= ~(1 << trackType); } - readBuffer(trackType); } void NuPlayer::GenericSource::readBuffer( @@ -1286,7 +1439,7 @@ void NuPlayer::GenericSource::readBuffer( seeking = true; } - if (mIsWidevine && trackType != MEDIA_TRACK_TYPE_AUDIO) { + if (mIsWidevine) { options.setNonBlocking(); } @@ -1317,7 +1470,8 @@ void NuPlayer::GenericSource::readBuffer( track->mPackets->queueDiscontinuity( type, NULL, true /* discard */); } - sp<ABuffer> buffer = mediaBufferToABuffer(mbuf, trackType, actualTimeUs); + sp<ABuffer> buffer = mediaBufferToABuffer( + mbuf, trackType, seekTimeUs, actualTimeUs); track->mPackets->queueAccessUnit(buffer); formatChange = false; seeking = false; diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index f8601ea..5fc41ec 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -67,7 +67,7 @@ struct NuPlayer::GenericSource : public NuPlayer::Source { virtual size_t getTrackCount() const; virtual sp<AMessage> getTrackInfo(size_t trackIndex) const; virtual ssize_t getSelectedTrack(media_track_type type) const; - virtual status_t selectTrack(size_t trackIndex, bool select); + virtual status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs); virtual status_t seekTo(int64_t seekTimeUs); virtual status_t setBuffers(bool audio, Vector<MediaBuffer *> &buffers); @@ -94,20 +94,23 @@ private: kWhatSeek, kWhatReadBuffer, kWhatStopWidevine, + kWhatStart, + kWhatResume, }; - Vector<sp<MediaSource> > mSources; - struct Track { size_t mIndex; sp<MediaSource> mSource; sp<AnotherPacketSource> mPackets; }; + Vector<sp<MediaSource> > mSources; Track mAudioTrack; int64_t mAudioTimeUs; + int64_t mAudioLastDequeueTimeUs; Track mVideoTrack; int64_t mVideoTimeUs; + int64_t mVideoLastDequeueTimeUs; Track mSubtitleTrack; Track mTimedTextTrack; @@ -116,6 +119,8 @@ private: int64_t mDurationUs; bool mAudioIsVorbis; bool mIsWidevine; + bool mIsSecure; + bool mIsStreaming; bool mUIDValid; uid_t mUID; sp<IMediaHTTPService> mHTTPService; @@ -134,12 +139,13 @@ private: sp<DecryptHandle> mDecryptHandle; bool mStarted; bool mStopRead; - String8 mContentType; - AString mSniffedMIME; - off64_t mMetaDataSize; int64_t mBitrate; int32_t mPollBufferingGeneration; uint32_t mPendingReadBufferTypes; + bool mBuffering; + bool mPrepareBuffering; + int32_t mPrevBufferPercentage; + mutable Mutex mReadBufferLock; sp<ALooper> mLooper; @@ -151,8 +157,6 @@ private: int64_t getLastReadPosition(); void setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position); - status_t prefillCacheIfNecessary(); - void notifyPreparedAndCleanup(status_t err); void onGetFormatMeta(sp<AMessage> msg) const; @@ -162,7 +166,7 @@ private: ssize_t doGetSelectedTrack(media_track_type type) const; void onSelectTrack(sp<AMessage> msg); - status_t doSelectTrack(size_t trackIndex, bool select); + status_t doSelectTrack(size_t trackIndex, bool select, int64_t timeUs); void onSeek(sp<AMessage> msg); status_t doSeek(int64_t seekTimeUs); @@ -180,6 +184,7 @@ private: sp<ABuffer> mediaBufferToABuffer( MediaBuffer *mbuf, media_track_type trackType, + int64_t seekTimeUs, int64_t *actualTimeUs = NULL); void postReadBuffer(media_track_type trackType); @@ -190,8 +195,13 @@ private: void schedulePollBuffering(); void cancelPollBuffering(); + void restartPollBuffering(); void onPollBuffering(); - void notifyBufferingUpdate(int percentage); + void notifyBufferingUpdate(int32_t percentage); + void startBufferingIfNecessary(); + void stopBufferingIfNecessary(); + void sendCacheStats(); + void ensureCacheIsFetching(); DISALLOW_EVIL_CONSTRUCTORS(GenericSource); }; diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index a003c81..d01e83a 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -81,7 +81,7 @@ void NuPlayer::HTTPLiveSource::prepareAsync() { mLiveLooper->registerHandler(this); } - sp<AMessage> notify = new AMessage(kWhatSessionNotify, id()); + sp<AMessage> notify = new AMessage(kWhatSessionNotify, this); mLiveSession = new LiveSession( notify, @@ -98,6 +98,10 @@ void NuPlayer::HTTPLiveSource::start() { } sp<AMessage> NuPlayer::HTTPLiveSource::getFormat(bool audio) { + if (mLiveSession == NULL) { + return NULL; + } + sp<AMessage> format; status_t err = mLiveSession->getStreamFormat( audio ? LiveSession::STREAMTYPE_AUDIO @@ -135,13 +139,21 @@ sp<AMessage> NuPlayer::HTTPLiveSource::getTrackInfo(size_t trackIndex) const { return mLiveSession->getTrackInfo(trackIndex); } -status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select) { +ssize_t NuPlayer::HTTPLiveSource::getSelectedTrack(media_track_type type) const { + if (mLiveSession == NULL) { + return -1; + } else { + return mLiveSession->getSelectedTrack(type); + } +} + +status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select, int64_t /*timeUs*/) { status_t err = mLiveSession->selectTrack(trackIndex, select); if (err == OK) { mFetchSubtitleDataGeneration++; if (select) { - sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id()); + sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this); msg->setInt32("generation", mFetchSubtitleDataGeneration); msg->post(); } diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index 6b5f6af..bbb8981 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -42,7 +42,8 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { virtual status_t getDuration(int64_t *durationUs); virtual size_t getTrackCount() const; virtual sp<AMessage> getTrackInfo(size_t trackIndex) const; - virtual status_t selectTrack(size_t trackIndex, bool select); + virtual ssize_t getSelectedTrack(media_track_type /* type */) const; + virtual status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs); virtual status_t seekTo(int64_t seekTimeUs); protected: diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 47bd989..f4d3794 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -21,7 +21,9 @@ #include "NuPlayer.h" #include "HTTPLiveSource.h" +#include "NuPlayerCCDecoder.h" #include "NuPlayerDecoder.h" +#include "NuPlayerDecoderBase.h" #include "NuPlayerDecoderPassThrough.h" #include "NuPlayerDriver.h" #include "NuPlayerRenderer.h" @@ -33,6 +35,8 @@ #include "ATSParser.h" +#include <cutils/properties.h> + #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -50,10 +54,6 @@ namespace android { -// TODO optimize buffer size for power consumption -// The offload read buffer size is 32 KB but 24 KB uses less power. -const size_t NuPlayer::kAggregateBufferSizeBytes = 24 * 1024; - struct NuPlayer::Action : public RefBase { Action() {} @@ -80,6 +80,21 @@ private: DISALLOW_EVIL_CONSTRUCTORS(SeekAction); }; +struct NuPlayer::ResumeDecoderAction : public Action { + ResumeDecoderAction(bool needNotify) + : mNeedNotify(needNotify) { + } + + virtual void execute(NuPlayer *player) { + player->performResumeDecoders(mNeedNotify); + } + +private: + bool mNeedNotify; + + DISALLOW_EVIL_CONSTRUCTORS(ResumeDecoderAction); +}; + struct NuPlayer::SetSurfaceAction : public Action { SetSurfaceAction(const sp<NativeWindowWrapper> &wrapper) : mWrapper(wrapper) { @@ -151,7 +166,6 @@ private: NuPlayer::NuPlayer() : mUIDValid(false), mSourceFlags(0), - mVideoIsAVC(false), mOffloadAudio(false), mAudioDecoderGeneration(0), mVideoDecoderGeneration(0), @@ -162,15 +176,14 @@ NuPlayer::NuPlayer() mScanSourcesGeneration(0), mPollDurationGeneration(0), mTimedTextGeneration(0), - mTimeDiscontinuityPending(false), mFlushingAudio(NONE), mFlushingVideo(NONE), - mSkipRenderingAudioUntilMediaTimeUs(-1ll), - mSkipRenderingVideoUntilMediaTimeUs(-1ll), - mNumFramesTotal(0ll), - mNumFramesDropped(0ll), + mResumePending(false), mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW), - mStarted(false) { + mPlaybackRate(1.0), + mStarted(false), + mPaused(false), + mPausedByClient(false) { clearFlushComplete(); } @@ -187,9 +200,9 @@ void NuPlayer::setDriver(const wp<NuPlayerDriver> &driver) { } void NuPlayer::setDataSourceAsync(const sp<IStreamSource> &source) { - sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); + sp<AMessage> msg = new AMessage(kWhatSetDataSource, this); - sp<AMessage> notify = new AMessage(kWhatSourceNotify, id()); + sp<AMessage> notify = new AMessage(kWhatSourceNotify, this); msg->setObject("source", new StreamingSource(notify, source)); msg->post(); @@ -217,10 +230,10 @@ void NuPlayer::setDataSourceAsync( const char *url, const KeyedVector<String8, String8> *headers) { - sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); + sp<AMessage> msg = new AMessage(kWhatSetDataSource, this); size_t len = strlen(url); - sp<AMessage> notify = new AMessage(kWhatSourceNotify, id()); + sp<AMessage> notify = new AMessage(kWhatSourceNotify, this); sp<Source> source; if (IsHTTPLiveURL(url)) { @@ -254,9 +267,9 @@ void NuPlayer::setDataSourceAsync( } void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) { - sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); + sp<AMessage> msg = new AMessage(kWhatSetDataSource, this); - sp<AMessage> notify = new AMessage(kWhatSourceNotify, id()); + sp<AMessage> notify = new AMessage(kWhatSourceNotify, this); sp<GenericSource> source = new GenericSource(notify, mUIDValid, mUID); @@ -273,12 +286,12 @@ void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) { } void NuPlayer::prepareAsync() { - (new AMessage(kWhatPrepare, id()))->post(); + (new AMessage(kWhatPrepare, this))->post(); } void NuPlayer::setVideoSurfaceTextureAsync( const sp<IGraphicBufferProducer> &bufferProducer) { - sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, id()); + sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, this); if (bufferProducer == NULL) { msg->setObject("native-window", NULL); @@ -293,17 +306,23 @@ void NuPlayer::setVideoSurfaceTextureAsync( } void NuPlayer::setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink) { - sp<AMessage> msg = new AMessage(kWhatSetAudioSink, id()); + sp<AMessage> msg = new AMessage(kWhatSetAudioSink, this); msg->setObject("sink", sink); msg->post(); } void NuPlayer::start() { - (new AMessage(kWhatStart, id()))->post(); + (new AMessage(kWhatStart, this))->post(); +} + +void NuPlayer::setPlaybackRate(float rate) { + sp<AMessage> msg = new AMessage(kWhatSetRate, this); + msg->setFloat("rate", rate); + msg->post(); } void NuPlayer::pause() { - (new AMessage(kWhatPause, id()))->post(); + (new AMessage(kWhatPause, this))->post(); } void NuPlayer::resetAsync() { @@ -317,11 +336,11 @@ void NuPlayer::resetAsync() { mSource->disconnect(); } - (new AMessage(kWhatReset, id()))->post(); + (new AMessage(kWhatReset, this))->post(); } void NuPlayer::seekToAsync(int64_t seekTimeUs, bool needNotify) { - sp<AMessage> msg = new AMessage(kWhatSeek, id()); + sp<AMessage> msg = new AMessage(kWhatSeek, this); msg->setInt64("seekTimeUs", seekTimeUs); msg->setInt32("needNotify", needNotify); msg->post(); @@ -389,7 +408,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { case kWhatGetTrackInfo: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); Parcel* reply; @@ -442,7 +461,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { sp<AMessage> response = new AMessage; response->setInt32("err", err); - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); response->postReply(replyID); break; @@ -450,13 +469,15 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { case kWhatSelectTrack: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); size_t trackIndex; int32_t select; + int64_t timeUs; CHECK(msg->findSize("trackIndex", &trackIndex)); CHECK(msg->findInt32("select", &select)); + CHECK(msg->findInt64("timeUs", &timeUs)); status_t err = INVALID_OPERATION; @@ -470,7 +491,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } if (trackIndex < inbandTracks) { - err = mSource->selectTrack(trackIndex, select); + err = mSource->selectTrack(trackIndex, select, timeUs); if (!select && err == OK) { int32_t type; @@ -525,7 +546,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { sp<RefBase> obj; CHECK(msg->findObject("native-window", &obj)); - if (mSource->getFormat(false /* audio */) == NULL) { + if (mSource == NULL || mSource->getFormat(false /* audio */) == NULL) { performSetSurface(static_cast<NativeWindowWrapper *>(obj.get())); break; } @@ -557,6 +578,12 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { new SimpleAction(&NuPlayer::performScanSources)); } + // After a flush without shutdown, decoder is paused. + // Don't resume it until source seek is done, otherwise it could + // start pulling stale data too soon. + mDeferredActions.push_back( + new ResumeDecoderAction(false /* needNotify */)); + processDeferredActions(); break; } @@ -580,6 +607,17 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } else { onStart(); } + mPausedByClient = false; + break; + } + + case kWhatSetRate: + { + ALOGV("kWhatSetRate"); + CHECK(msg->findFloat("rate", &mPlaybackRate)); + if (mRenderer != NULL) { + mRenderer->setPlaybackRate(mPlaybackRate); + } break; } @@ -606,11 +644,21 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { instantiateDecoder(false, &mVideoDecoder); } - if (mAudioSink != NULL) { - if (mOffloadAudio) { + // Don't try to re-open audio sink if there's an existing decoder. + if (mAudioSink != NULL && mAudioDecoder == NULL) { + sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */); + sp<AMessage> videoFormat = mSource->getFormat(false /* audio */); + audio_stream_type_t streamType = mAudioSink->getAudioStreamType(); + const bool hasVideo = (videoFormat != NULL); + const bool canOffload = canOffloadStream( + audioMeta, hasVideo, true /* is_streaming */, streamType); + if (canOffload) { + if (!mOffloadAudio) { + mRenderer->signalEnableOffloadAudio(); + } // open audio sink early under offload mode. sp<AMessage> format = mSource->getFormat(true /*audio*/); - openAudioSink(format, true /*offloadOnly*/); + tryOpenAudioSinkForOffload(format, hasVideo); } instantiateDecoder(true, &mAudioDecoder); } @@ -674,16 +722,26 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { int32_t what; CHECK(msg->findInt32("what", &what)); - if (what == Decoder::kWhatFillThisBuffer) { - status_t err = feedDecoderInputData( - audio, msg); + if (what == DecoderBase::kWhatInputDiscontinuity) { + int32_t formatChange; + CHECK(msg->findInt32("formatChange", &formatChange)); - if (err == -EWOULDBLOCK) { - if (mSource->feedMoreTSData() == OK) { - msg->post(10 * 1000ll); - } + ALOGV("%s discontinuity: formatChange %d", + audio ? "audio" : "video", formatChange); + + if (formatChange) { + mDeferredActions.push_back( + new FlushDecoderAction( + audio ? FLUSH_CMD_SHUTDOWN : FLUSH_CMD_NONE, + audio ? FLUSH_CMD_NONE : FLUSH_CMD_SHUTDOWN)); } - } else if (what == Decoder::kWhatEOS) { + + mDeferredActions.push_back( + new SimpleAction( + &NuPlayer::performScanSources)); + + processDeferredActions(); + } else if (what == DecoderBase::kWhatEOS) { int32_t err; CHECK(msg->findInt32("err", &err)); @@ -696,25 +754,20 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } mRenderer->queueEOS(audio, err); - } else if (what == Decoder::kWhatFlushCompleted) { + } else if (what == DecoderBase::kWhatFlushCompleted) { ALOGV("decoder %s flush completed", audio ? "audio" : "video"); handleFlushComplete(audio, true /* isDecoder */); finishFlushIfPossible(); - } else if (what == Decoder::kWhatOutputFormatChanged) { + } else if (what == DecoderBase::kWhatVideoSizeChanged) { sp<AMessage> format; CHECK(msg->findMessage("format", &format)); - if (audio) { - openAudioSink(format, false /*offloadOnly*/); - } else { - // video - sp<AMessage> inputFormat = - mSource->getFormat(false /* audio */); + sp<AMessage> inputFormat = + mSource->getFormat(false /* audio */); - updateVideoSize(inputFormat, format); - } - } else if (what == Decoder::kWhatShutdownCompleted) { + updateVideoSize(inputFormat, format); + } else if (what == DecoderBase::kWhatShutdownCompleted) { ALOGV("%s shutdown completed", audio ? "audio" : "video"); if (audio) { mAudioDecoder.clear(); @@ -731,7 +784,9 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } finishFlushIfPossible(); - } else if (what == Decoder::kWhatError) { + } else if (what == DecoderBase::kWhatResumeCompleted) { + finishResume(); + } else if (what == DecoderBase::kWhatError) { status_t err; if (!msg->findInt32("err", &err) || err == OK) { err = UNKNOWN_ERROR; @@ -740,6 +795,8 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { // Decoder errors can be due to Source (e.g. from streaming), // or from decoding corrupted bitstreams, or from other decoder // MediaCodec operations (e.g. from an ongoing reset or seek). + // They may also be due to openAudioSink failure at + // decoder start or after a format change. // // We try to gracefully shut down the affected decoder if possible, // rather than trying to force the shutdown with something @@ -779,8 +836,6 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { break; // Finish anyways. } notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); - } else if (what == Decoder::kWhatDrainThisBuffer) { - renderBuffer(audio, msg); } else { ALOGV("Unhandled decoder notification %d '%c%c%c%c'.", what, @@ -846,7 +901,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGV("media rendering started"); notifyListener(MEDIA_STARTED, 0, 0); } else if (what == Renderer::kWhatAudioOffloadTearDown) { - ALOGV("Tear down audio offload, fall back to s/w path"); + ALOGV("Tear down audio offload, fall back to s/w path if due to error."); int64_t positionUs; CHECK(msg->findInt64("positionUs", &positionUs)); int32_t reason; @@ -854,15 +909,17 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { closeAudioSink(); mAudioDecoder.clear(); ++mAudioDecoderGeneration; - mRenderer->flush(true /* audio */); + mRenderer->flush( + true /* audio */, false /* notifyComplete */); if (mVideoDecoder != NULL) { - mRenderer->flush(false /* audio */); + mRenderer->flush( + false /* audio */, false /* notifyComplete */); } - mRenderer->signalDisableOffloadAudio(); - mOffloadAudio = false; performSeek(positionUs, false /* needNotify */); if (reason == Renderer::kDueToError) { + mRenderer->signalDisableOffloadAudio(); + mOffloadAudio = false; instantiateDecoder(true /* audio */, &mAudioDecoder); } } @@ -907,22 +964,20 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { mDeferredActions.push_back( new SeekAction(seekTimeUs, needNotify)); + // After a flush without shutdown, decoder is paused. + // Don't resume it until source seek is done, otherwise it could + // start pulling stale data too soon. + mDeferredActions.push_back( + new ResumeDecoderAction(needNotify)); + processDeferredActions(); break; } case kWhatPause: { - if (mSource != NULL) { - mSource->pause(); - } else { - ALOGW("pause called when source is gone or not set"); - } - if (mRenderer != NULL) { - mRenderer->pause(); - } else { - ALOGW("pause called when renderer is gone or not set"); - } + onPause(); + mPausedByClient = true; break; } @@ -945,6 +1000,10 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } void NuPlayer::onResume() { + if (!mPaused) { + return; + } + mPaused = false; if (mSource != NULL) { mSource->resume(); } else { @@ -963,14 +1022,9 @@ void NuPlayer::onResume() { } void NuPlayer::onStart() { - mVideoIsAVC = false; mOffloadAudio = false; mAudioEOS = false; mVideoEOS = false; - mSkipRenderingAudioUntilMediaTimeUs = -1; - mSkipRenderingVideoUntilMediaTimeUs = -1; - mNumFramesTotal = 0; - mNumFramesDropped = 0; mStarted = true; /* instantiate decoders now for secure playback */ @@ -1007,15 +1061,17 @@ void NuPlayer::onStart() { flags |= Renderer::FLAG_OFFLOAD_AUDIO; } - sp<AMessage> notify = new AMessage(kWhatRendererNotify, id()); + sp<AMessage> notify = new AMessage(kWhatRendererNotify, this); ++mRendererGeneration; notify->setInt32("generation", mRendererGeneration); mRenderer = new Renderer(mAudioSink, notify, flags); - mRendererLooper = new ALooper; mRendererLooper->setName("NuPlayerRenderer"); mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO); mRendererLooper->registerHandler(mRenderer); + if (mPlaybackRate != 1.0) { + mRenderer->setPlaybackRate(mPlaybackRate); + } sp<MetaData> meta = getFileMeta(); int32_t rate; @@ -1024,9 +1080,33 @@ void NuPlayer::onStart() { mRenderer->setVideoFrameRate(rate); } + if (mVideoDecoder != NULL) { + mVideoDecoder->setRenderer(mRenderer); + } + if (mAudioDecoder != NULL) { + mAudioDecoder->setRenderer(mRenderer); + } + postScanSources(); } +void NuPlayer::onPause() { + if (mPaused) { + return; + } + mPaused = true; + if (mSource != NULL) { + mSource->pause(); + } else { + ALOGW("pause called when source is gone or not set"); + } + if (mRenderer != NULL) { + mRenderer->pause(); + } else { + ALOGW("pause called when renderer is gone or not set"); + } +} + bool NuPlayer::audioDecoderStillNeeded() { // Audio decoder is no longer needed if it's in shut/shutting down status. return ((mFlushingAudio != SHUT_DOWN) && (mFlushingAudio != SHUTTING_DOWN_DECODER)); @@ -1084,22 +1164,6 @@ void NuPlayer::finishFlushIfPossible() { ALOGV("both audio and video are flushed now."); - mPendingAudioAccessUnit.clear(); - mAggregateBuffer.clear(); - - if (mTimeDiscontinuityPending) { - mRenderer->signalTimeDiscontinuity(); - mTimeDiscontinuityPending = false; - } - - if (mAudioDecoder != NULL && mFlushingAudio == FLUSHED) { - mAudioDecoder->signalResume(); - } - - if (mVideoDecoder != NULL && mFlushingVideo == FLUSHED) { - mVideoDecoder->signalResume(); - } - mFlushingAudio = NONE; mFlushingVideo = NONE; @@ -1113,35 +1177,23 @@ void NuPlayer::postScanSources() { return; } - sp<AMessage> msg = new AMessage(kWhatScanSources, id()); + sp<AMessage> msg = new AMessage(kWhatScanSources, this); msg->setInt32("generation", mScanSourcesGeneration); msg->post(); mScanSourcesPending = true; } -void NuPlayer::openAudioSink(const sp<AMessage> &format, bool offloadOnly) { - uint32_t flags; - int64_t durationUs; - bool hasVideo = (mVideoDecoder != NULL); - // FIXME: we should handle the case where the video decoder - // is created after we receive the format change indication. - // Current code will just make that we select deep buffer - // with video which should not be a problem as it should - // not prevent from keeping A/V sync. - if (!hasVideo && - mSource->getDuration(&durationUs) == OK && - durationUs - > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { - flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; - } else { - flags = AUDIO_OUTPUT_FLAG_NONE; - } - - mOffloadAudio = mRenderer->openAudioSink( - format, offloadOnly, hasVideo, flags); +void NuPlayer::tryOpenAudioSinkForOffload(const sp<AMessage> &format, bool hasVideo) { + // Note: This is called early in NuPlayer to determine whether offloading + // is possible; otherwise the decoders call the renderer openAudioSink directly. - if (mOffloadAudio) { + status_t err = mRenderer->openAudioSink( + format, true /* offloadOnly */, hasVideo, AUDIO_OUTPUT_FLAG_NONE, &mOffloadAudio); + if (err != OK) { + // Any failure we turn off mOffloadAudio. + mOffloadAudio = false; + } else if (mOffloadAudio) { sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */); sendMetaDataToHal(mAudioSink, audioMeta); @@ -1152,7 +1204,7 @@ void NuPlayer::closeAudioSink() { mRenderer->closeAudioSink(); } -status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { +status_t NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder) { if (*decoder != NULL) { return OK; } @@ -1166,32 +1218,49 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { if (!audio) { AString mime; CHECK(format->findString("mime", &mime)); - mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str()); - sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, id()); - mCCDecoder = new CCDecoder(ccNotify); + sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, this); + if (mCCDecoder == NULL) { + mCCDecoder = new CCDecoder(ccNotify); + } if (mSourceFlags & Source::FLAG_SECURE) { format->setInt32("secure", true); } + + if (mSourceFlags & Source::FLAG_PROTECTED) { + format->setInt32("protected", true); + } } if (audio) { - sp<AMessage> notify = new AMessage(kWhatAudioNotify, id()); + sp<AMessage> notify = new AMessage(kWhatAudioNotify, this); ++mAudioDecoderGeneration; notify->setInt32("generation", mAudioDecoderGeneration); if (mOffloadAudio) { - *decoder = new DecoderPassThrough(notify); + *decoder = new DecoderPassThrough(notify, mSource, mRenderer); } else { - *decoder = new Decoder(notify); + *decoder = new Decoder(notify, mSource, mRenderer); } } else { - sp<AMessage> notify = new AMessage(kWhatVideoNotify, id()); + sp<AMessage> notify = new AMessage(kWhatVideoNotify, this); ++mVideoDecoderGeneration; notify->setInt32("generation", mVideoDecoderGeneration); - *decoder = new Decoder(notify, mNativeWindow); + *decoder = new Decoder( + notify, mSource, mRenderer, mNativeWindow, mCCDecoder); + + // enable FRC if high-quality AV sync is requested, even if not + // queuing to native window, as this will even improve textureview + // playback. + { + char value[PROPERTY_VALUE_MAX]; + if (property_get("persist.sys.media.avsync", value, NULL) && + (!strcmp("1", value) || !strcasecmp("true", value))) { + format->setInt32("auto-frc", 1); + } + } } (*decoder)->init(); (*decoder)->configure(format); @@ -1221,281 +1290,6 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { return OK; } -status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { - sp<AMessage> reply; - CHECK(msg->findMessage("reply", &reply)); - - if ((audio && mFlushingAudio != NONE) - || (!audio && mFlushingVideo != NONE) - || mSource == NULL) { - reply->setInt32("err", INFO_DISCONTINUITY); - reply->post(); - return OK; - } - - sp<ABuffer> accessUnit; - - // Aggregate smaller buffers into a larger buffer. - // The goal is to reduce power consumption. - // Note this will not work if the decoder requires one frame per buffer. - bool doBufferAggregation = (audio && mOffloadAudio); - bool needMoreData = false; - - bool dropAccessUnit; - do { - status_t err; - // Did we save an accessUnit earlier because of a discontinuity? - if (audio && (mPendingAudioAccessUnit != NULL)) { - accessUnit = mPendingAudioAccessUnit; - mPendingAudioAccessUnit.clear(); - err = mPendingAudioErr; - ALOGV("feedDecoderInputData() use mPendingAudioAccessUnit"); - } else { - err = mSource->dequeueAccessUnit(audio, &accessUnit); - } - - if (err == -EWOULDBLOCK) { - return err; - } else if (err != OK) { - if (err == INFO_DISCONTINUITY) { - if (doBufferAggregation && (mAggregateBuffer != NULL)) { - // We already have some data so save this for later. - mPendingAudioErr = err; - mPendingAudioAccessUnit = accessUnit; - accessUnit.clear(); - ALOGD("feedDecoderInputData() save discontinuity for later"); - break; - } - int32_t type; - CHECK(accessUnit->meta()->findInt32("discontinuity", &type)); - - bool formatChange = - (audio && - (type & ATSParser::DISCONTINUITY_AUDIO_FORMAT)) - || (!audio && - (type & ATSParser::DISCONTINUITY_VIDEO_FORMAT)); - - bool timeChange = (type & ATSParser::DISCONTINUITY_TIME) != 0; - - ALOGI("%s discontinuity (formatChange=%d, time=%d)", - audio ? "audio" : "video", formatChange, timeChange); - - if (audio) { - mSkipRenderingAudioUntilMediaTimeUs = -1; - } else { - mSkipRenderingVideoUntilMediaTimeUs = -1; - } - - if (timeChange) { - sp<AMessage> extra; - if (accessUnit->meta()->findMessage("extra", &extra) - && extra != NULL) { - int64_t resumeAtMediaTimeUs; - if (extra->findInt64( - "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) { - ALOGI("suppressing rendering of %s until %lld us", - audio ? "audio" : "video", resumeAtMediaTimeUs); - - if (audio) { - mSkipRenderingAudioUntilMediaTimeUs = - resumeAtMediaTimeUs; - } else { - mSkipRenderingVideoUntilMediaTimeUs = - resumeAtMediaTimeUs; - } - } - } - } - - mTimeDiscontinuityPending = - mTimeDiscontinuityPending || timeChange; - - bool seamlessFormatChange = false; - sp<AMessage> newFormat = mSource->getFormat(audio); - if (formatChange) { - seamlessFormatChange = - getDecoder(audio)->supportsSeamlessFormatChange(newFormat); - // treat seamless format change separately - formatChange = !seamlessFormatChange; - } - bool shutdownOrFlush = formatChange || timeChange; - - // We want to queue up scan-sources only once per discontinuity. - // We control this by doing it only if neither audio nor video are - // flushing or shutting down. (After handling 1st discontinuity, one - // of the flushing states will not be NONE.) - // No need to scan sources if this discontinuity does not result - // in a flush or shutdown, as the flushing state will stay NONE. - if (mFlushingAudio == NONE && mFlushingVideo == NONE && - shutdownOrFlush) { - // And we'll resume scanning sources once we're done - // flushing. - mDeferredActions.push_front( - new SimpleAction( - &NuPlayer::performScanSources)); - } - - if (formatChange /* not seamless */) { - // must change decoder - flushDecoder(audio, /* needShutdown = */ true); - } else if (timeChange) { - // need to flush - flushDecoder(audio, /* needShutdown = */ false, newFormat); - err = OK; - } else if (seamlessFormatChange) { - // reuse existing decoder and don't flush - updateDecoderFormatWithoutFlush(audio, newFormat); - err = OK; - } else { - // This stream is unaffected by the discontinuity - return -EWOULDBLOCK; - } - } - - reply->setInt32("err", err); - reply->post(); - return OK; - } - - if (!audio) { - ++mNumFramesTotal; - } - - dropAccessUnit = false; - if (!audio - && !(mSourceFlags & Source::FLAG_SECURE) - && mRenderer->getVideoLateByUs() > 100000ll - && mVideoIsAVC - && !IsAVCReferenceFrame(accessUnit)) { - dropAccessUnit = true; - ++mNumFramesDropped; - } - - size_t smallSize = accessUnit->size(); - needMoreData = false; - if (doBufferAggregation && (mAggregateBuffer == NULL) - // Don't bother if only room for a few small buffers. - && (smallSize < (kAggregateBufferSizeBytes / 3))) { - // Create a larger buffer for combining smaller buffers from the extractor. - mAggregateBuffer = new ABuffer(kAggregateBufferSizeBytes); - mAggregateBuffer->setRange(0, 0); // start empty - } - - if (doBufferAggregation && (mAggregateBuffer != NULL)) { - int64_t timeUs; - int64_t dummy; - bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs); - bool bigTimestampValid = mAggregateBuffer->meta()->findInt64("timeUs", &dummy); - // Will the smaller buffer fit? - size_t bigSize = mAggregateBuffer->size(); - size_t roomLeft = mAggregateBuffer->capacity() - bigSize; - // Should we save this small buffer for the next big buffer? - // If the first small buffer did not have a timestamp then save - // any buffer that does have a timestamp until the next big buffer. - if ((smallSize > roomLeft) - || (!bigTimestampValid && (bigSize > 0) && smallTimestampValid)) { - mPendingAudioErr = err; - mPendingAudioAccessUnit = accessUnit; - accessUnit.clear(); - } else { - // Grab time from first small buffer if available. - if ((bigSize == 0) && smallTimestampValid) { - mAggregateBuffer->meta()->setInt64("timeUs", timeUs); - } - // Append small buffer to the bigger buffer. - memcpy(mAggregateBuffer->base() + bigSize, accessUnit->data(), smallSize); - bigSize += smallSize; - mAggregateBuffer->setRange(0, bigSize); - - // Keep looping until we run out of room in the mAggregateBuffer. - needMoreData = true; - - ALOGV("feedDecoderInputData() smallSize = %zu, bigSize = %zu, capacity = %zu", - smallSize, bigSize, mAggregateBuffer->capacity()); - } - } - } while (dropAccessUnit || needMoreData); - - // ALOGV("returned a valid buffer of %s data", audio ? "audio" : "video"); - -#if 0 - int64_t mediaTimeUs; - CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs)); - ALOGV("feeding %s input buffer at media time %.2f secs", - audio ? "audio" : "video", - mediaTimeUs / 1E6); -#endif - - if (!audio) { - mCCDecoder->decode(accessUnit); - } - - if (doBufferAggregation && (mAggregateBuffer != NULL)) { - ALOGV("feedDecoderInputData() reply with aggregated buffer, %zu", - mAggregateBuffer->size()); - reply->setBuffer("buffer", mAggregateBuffer); - mAggregateBuffer.clear(); - } else { - reply->setBuffer("buffer", accessUnit); - } - - reply->post(); - - return OK; -} - -void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) { - // ALOGV("renderBuffer %s", audio ? "audio" : "video"); - - sp<AMessage> reply; - CHECK(msg->findMessage("reply", &reply)); - - if ((audio && mFlushingAudio != NONE) - || (!audio && mFlushingVideo != NONE)) { - // We're currently attempting to flush the decoder, in order - // to complete this, the decoder wants all its buffers back, - // so we don't want any output buffers it sent us (from before - // we initiated the flush) to be stuck in the renderer's queue. - - ALOGV("we're still flushing the %s decoder, sending its output buffer" - " right back.", audio ? "audio" : "video"); - - reply->post(); - return; - } - - sp<ABuffer> buffer; - CHECK(msg->findBuffer("buffer", &buffer)); - - int64_t mediaTimeUs; - CHECK(buffer->meta()->findInt64("timeUs", &mediaTimeUs)); - - int64_t &skipUntilMediaTimeUs = - audio - ? mSkipRenderingAudioUntilMediaTimeUs - : mSkipRenderingVideoUntilMediaTimeUs; - - if (skipUntilMediaTimeUs >= 0) { - - if (mediaTimeUs < skipUntilMediaTimeUs) { - ALOGV("dropping %s buffer at time %lld as requested.", - audio ? "audio" : "video", - mediaTimeUs); - - reply->post(); - return; - } - - skipUntilMediaTimeUs = -1; - } - - if (!audio && mCCDecoder->isSelected()) { - mCCDecoder->display(mediaTimeUs); - } - - mRenderer->queueBuffer(audio, buffer, reply); -} - void NuPlayer::updateVideoSize( const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) { @@ -1576,12 +1370,11 @@ void NuPlayer::notifyListener(int msg, int ext1, int ext2, const Parcel *in) { driver->notifyListener(msg, ext1, ext2, in); } -void NuPlayer::flushDecoder( - bool audio, bool needShutdown, const sp<AMessage> &newFormat) { +void NuPlayer::flushDecoder(bool audio, bool needShutdown) { ALOGV("[%s] flushDecoder needShutdown=%d", audio ? "audio" : "video", needShutdown); - const sp<Decoder> &decoder = getDecoder(audio); + const sp<DecoderBase> &decoder = getDecoder(audio); if (decoder == NULL) { ALOGI("flushDecoder %s without decoder present", audio ? "audio" : "video"); @@ -1592,8 +1385,7 @@ void NuPlayer::flushDecoder( ++mScanSourcesGeneration; mScanSourcesPending = false; - decoder->signalFlush(newFormat); - mRenderer->flush(audio); + decoder->signalFlush(); FlushStatus newStatus = needShutdown ? FLUSHING_DECODER_SHUTDOWN : FLUSHING_DECODER; @@ -1608,25 +1400,7 @@ void NuPlayer::flushDecoder( ALOGE_IF(mFlushingVideo != NONE, "video flushDecoder() is called in state %d", mFlushingVideo); mFlushingVideo = newStatus; - - if (mCCDecoder != NULL) { - mCCDecoder->flush(); - } - } -} - -void NuPlayer::updateDecoderFormatWithoutFlush( - bool audio, const sp<AMessage> &format) { - ALOGV("[%s] updateDecoderFormatWithoutFlush", audio ? "audio" : "video"); - - const sp<Decoder> &decoder = getDecoder(audio); - if (decoder == NULL) { - ALOGI("updateDecoderFormatWithoutFlush %s without decoder present", - audio ? "audio" : "video"); - return; } - - decoder->signalUpdateFormat(format); } void NuPlayer::queueDecoderShutdown( @@ -1661,7 +1435,7 @@ status_t NuPlayer::setVideoScalingMode(int32_t mode) { } status_t NuPlayer::getTrackInfo(Parcel* reply) const { - sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, id()); + sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, this); msg->setPointer("reply", reply); sp<AMessage> response; @@ -1670,7 +1444,7 @@ status_t NuPlayer::getTrackInfo(Parcel* reply) const { } status_t NuPlayer::getSelectedTrack(int32_t type, Parcel* reply) const { - sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id()); + sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, this); msg->setPointer("reply", reply); msg->setInt32("type", type); @@ -1682,10 +1456,11 @@ status_t NuPlayer::getSelectedTrack(int32_t type, Parcel* reply) const { return err; } -status_t NuPlayer::selectTrack(size_t trackIndex, bool select) { - sp<AMessage> msg = new AMessage(kWhatSelectTrack, id()); +status_t NuPlayer::selectTrack(size_t trackIndex, bool select, int64_t timeUs) { + sp<AMessage> msg = new AMessage(kWhatSelectTrack, this); msg->setSize("trackIndex", trackIndex); msg->setInt32("select", select); + msg->setInt64("timeUs", timeUs); sp<AMessage> response; status_t err = msg->postAndAwaitResponse(&response); @@ -1711,8 +1486,13 @@ status_t NuPlayer::getCurrentPosition(int64_t *mediaUs) { } void NuPlayer::getStats(int64_t *numFramesTotal, int64_t *numFramesDropped) { - *numFramesTotal = mNumFramesTotal; - *numFramesDropped = mNumFramesDropped; + sp<DecoderBase> decoder = getDecoder(false /* audio */); + if (decoder != NULL) { + decoder->getStats(numFramesTotal, numFramesDropped); + } else { + *numFramesTotal = 0; + *numFramesDropped = 0; + } } sp<MetaData> NuPlayer::getFileMeta() { @@ -1720,7 +1500,7 @@ sp<MetaData> NuPlayer::getFileMeta() { } void NuPlayer::schedulePollDuration() { - sp<AMessage> msg = new AMessage(kWhatPollDuration, id()); + sp<AMessage> msg = new AMessage(kWhatPollDuration, this); msg->setInt32("generation", mPollDurationGeneration); msg->post(); } @@ -1769,15 +1549,6 @@ void NuPlayer::performSeek(int64_t seekTimeUs, bool needNotify) { mSource->seekTo(seekTimeUs); ++mTimedTextGeneration; - if (mDriver != NULL) { - sp<NuPlayerDriver> driver = mDriver.promote(); - if (driver != NULL) { - if (needNotify) { - driver->notifySeekComplete(); - } - } - } - // everything's flushed, continue playback. } @@ -1789,8 +1560,6 @@ void NuPlayer::performDecoderFlush(FlushCommand audio, FlushCommand video) { return; } - mTimeDiscontinuityPending = true; - if (audio != FLUSH_CMD_NONE && mAudioDecoder != NULL) { flushDecoder(true /* audio */, (audio == FLUSH_CMD_SHUTDOWN)); } @@ -1865,6 +1634,42 @@ void NuPlayer::performSetSurface(const sp<NativeWindowWrapper> &wrapper) { } } +void NuPlayer::performResumeDecoders(bool needNotify) { + if (needNotify) { + mResumePending = true; + if (mVideoDecoder == NULL) { + // if audio-only, we can notify seek complete now, + // as the resume operation will be relatively fast. + finishResume(); + } + } + + if (mVideoDecoder != NULL) { + // When there is continuous seek, MediaPlayer will cache the seek + // position, and send down new seek request when previous seek is + // complete. Let's wait for at least one video output frame before + // notifying seek complete, so that the video thumbnail gets updated + // when seekbar is dragged. + mVideoDecoder->signalResume(needNotify); + } + + if (mAudioDecoder != NULL) { + mAudioDecoder->signalResume(false /* needNotify */); + } +} + +void NuPlayer::finishResume() { + if (mResumePending) { + mResumePending = false; + if (mDriver != NULL) { + sp<NuPlayerDriver> driver = mDriver.promote(); + if (driver != NULL) { + driver->notifySeekComplete(); + } + } + } +} + void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { int32_t what; CHECK(msg->findInt32("what", &what)); @@ -1903,6 +1708,10 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { sp<NuPlayerDriver> driver = mDriver.promote(); if (driver != NULL) { + if ((flags & NuPlayer::Source::FLAG_CAN_SEEK) == 0) { + driver->notifyListener( + MEDIA_INFO, MEDIA_INFO_NOT_SEEKABLE, 0); + } driver->notifyFlagsChanged(flags); } @@ -1937,18 +1746,49 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { break; } + case Source::kWhatPauseOnBufferingStart: + { + // ignore if not playing + if (mStarted && !mPausedByClient) { + ALOGI("buffer low, pausing..."); + + onPause(); + } + // fall-thru + } + case Source::kWhatBufferingStart: { notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_START, 0); break; } + case Source::kWhatResumeOnBufferingEnd: + { + // ignore if not playing + if (mStarted && !mPausedByClient) { + ALOGI("buffer ready, resuming..."); + + onResume(); + } + // fall-thru + } + case Source::kWhatBufferingEnd: { notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_END, 0); break; } + case Source::kWhatCacheStats: + { + int32_t kbps; + CHECK(msg->findInt32("bandwidth", &kbps)); + + notifyListener(MEDIA_INFO, MEDIA_INFO_NETWORK_BANDWIDTH, kbps); + break; + } + case Source::kWhatSubtitleData: { sp<ABuffer> buffer; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 121f7dd..a2cb53e 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -26,7 +26,7 @@ namespace android { struct ABuffer; struct AMessage; -struct MetaData; +class MetaData; struct NuPlayerDriver; struct NuPlayer : public AHandler { @@ -51,6 +51,7 @@ struct NuPlayer : public AHandler { const sp<IGraphicBufferProducer> &bufferProducer); void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink); + void setPlaybackRate(float rate); void start(); void pause(); @@ -65,14 +66,12 @@ struct NuPlayer : public AHandler { status_t setVideoScalingMode(int32_t mode); status_t getTrackInfo(Parcel* reply) const; status_t getSelectedTrack(int32_t type, Parcel* reply) const; - status_t selectTrack(size_t trackIndex, bool select); + status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs); status_t getCurrentPosition(int64_t *mediaUs); void getStats(int64_t *mNumFramesTotal, int64_t *mNumFramesDropped); sp<MetaData> getFileMeta(); - static const size_t kAggregateBufferSizeBytes; - protected: virtual ~NuPlayer(); @@ -84,6 +83,7 @@ public: private: struct Decoder; + struct DecoderBase; struct DecoderPassThrough; struct CCDecoder; struct GenericSource; @@ -94,6 +94,7 @@ private: struct Action; struct SeekAction; struct SetSurfaceAction; + struct ResumeDecoderAction; struct FlushDecoderAction; struct PostMessageAction; struct SimpleAction; @@ -104,6 +105,7 @@ private: kWhatSetVideoNativeWindow = '=NaW', kWhatSetAudioSink = '=AuS', kWhatMoreDataQueued = 'more', + kWhatSetRate = 'setR', kWhatStart = 'strt', kWhatScanSources = 'scan', kWhatVideoNotify = 'vidN', @@ -128,10 +130,9 @@ private: uint32_t mSourceFlags; sp<NativeWindowWrapper> mNativeWindow; sp<MediaPlayerBase::AudioSink> mAudioSink; - sp<Decoder> mVideoDecoder; - bool mVideoIsAVC; + sp<DecoderBase> mVideoDecoder; bool mOffloadAudio; - sp<Decoder> mAudioDecoder; + sp<DecoderBase> mAudioDecoder; sp<CCDecoder> mCCDecoder; sp<Renderer> mRenderer; sp<ALooper> mRendererLooper; @@ -165,32 +166,29 @@ private: FLUSH_CMD_SHUTDOWN, }; - // Once the current flush is complete this indicates whether the - // notion of time has changed. - bool mTimeDiscontinuityPending; - // Status of flush responses from the decoder and renderer. bool mFlushComplete[2][2]; - // Used by feedDecoderInputData to aggregate small buffers into - // one large buffer. - sp<ABuffer> mPendingAudioAccessUnit; - status_t mPendingAudioErr; - sp<ABuffer> mAggregateBuffer; - FlushStatus mFlushingAudio; FlushStatus mFlushingVideo; - int64_t mSkipRenderingAudioUntilMediaTimeUs; - int64_t mSkipRenderingVideoUntilMediaTimeUs; - - int64_t mNumFramesTotal, mNumFramesDropped; + // Status of flush responses from the decoder and renderer. + bool mResumePending; int32_t mVideoScalingMode; + float mPlaybackRate; bool mStarted; - inline const sp<Decoder> &getDecoder(bool audio) { + // Actual pause state, either as requested by client or due to buffering. + bool mPaused; + + // Pause state as requested by client. Note that if mPausedByClient is + // true, mPaused is always true; if mPausedByClient is false, mPaused could + // still become true, when we pause internally due to buffering. + bool mPausedByClient; + + inline const sp<DecoderBase> &getDecoder(bool audio) { return audio ? mAudioDecoder : mVideoDecoder; } @@ -201,18 +199,15 @@ private: mFlushComplete[1][1] = false; } - void openAudioSink(const sp<AMessage> &format, bool offloadOnly); + void tryOpenAudioSinkForOffload(const sp<AMessage> &format, bool hasVideo); void closeAudioSink(); - status_t instantiateDecoder(bool audio, sp<Decoder> *decoder); + status_t instantiateDecoder(bool audio, sp<DecoderBase> *decoder); void updateVideoSize( const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat = NULL); - status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg); - void renderBuffer(bool audio, const sp<AMessage> &msg); - void notifyListener(int msg, int ext1, int ext2, const Parcel *in = NULL); void handleFlushComplete(bool audio, bool isDecoder); @@ -220,12 +215,13 @@ private: void onStart(); void onResume(); + void onPause(); bool audioDecoderStillNeeded(); - void flushDecoder( - bool audio, bool needShutdown, const sp<AMessage> &newFormat = NULL); - void updateDecoderFormatWithoutFlush(bool audio, const sp<AMessage> &format); + void flushDecoder(bool audio, bool needShutdown); + + void finishResume(); void postScanSources(); @@ -239,6 +235,7 @@ private: void performReset(); void performScanSources(); void performSetSurface(const sp<NativeWindowWrapper> &wrapper); + void performResumeDecoders(bool needNotify); void onSourceNotify(const sp<AMessage> &msg); void onClosedCaptionNotify(const sp<AMessage> &msg); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp new file mode 100644 index 0000000..cf3e8ad --- /dev/null +++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp @@ -0,0 +1,389 @@ +/* + * 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 "NuPlayerCCDecoder" +#include <utils/Log.h> +#include <inttypes.h> + +#include "avc_utils.h" +#include "NuPlayerCCDecoder.h" + +#include <media/stagefright/foundation/ABitReader.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaDefs.h> + +namespace android { + +struct CCData { + CCData(uint8_t type, uint8_t data1, uint8_t data2) + : mType(type), mData1(data1), mData2(data2) { + } + bool getChannel(size_t *channel) const { + if (mData1 >= 0x10 && mData1 <= 0x1f) { + *channel = (mData1 >= 0x18 ? 1 : 0) + (mType ? 2 : 0); + return true; + } + return false; + } + + uint8_t mType; + uint8_t mData1; + uint8_t mData2; +}; + +static bool isNullPad(CCData *cc) { + return cc->mData1 < 0x10 && cc->mData2 < 0x10; +} + +static void dumpBytePair(const sp<ABuffer> &ccBuf) { + size_t offset = 0; + AString out; + + while (offset < ccBuf->size()) { + char tmp[128]; + + CCData *cc = (CCData *) (ccBuf->data() + offset); + + if (isNullPad(cc)) { + // 1 null pad or XDS metadata, ignore + offset += sizeof(CCData); + continue; + } + + if (cc->mData1 >= 0x20 && cc->mData1 <= 0x7f) { + // 2 basic chars + sprintf(tmp, "[%d]Basic: %c %c", cc->mType, cc->mData1, cc->mData2); + } else if ((cc->mData1 == 0x11 || cc->mData1 == 0x19) + && cc->mData2 >= 0x30 && cc->mData2 <= 0x3f) { + // 1 special char + sprintf(tmp, "[%d]Special: %02x %02x", cc->mType, cc->mData1, cc->mData2); + } else if ((cc->mData1 == 0x12 || cc->mData1 == 0x1A) + && cc->mData2 >= 0x20 && cc->mData2 <= 0x3f){ + // 1 Spanish/French char + sprintf(tmp, "[%d]Spanish: %02x %02x", cc->mType, cc->mData1, cc->mData2); + } else if ((cc->mData1 == 0x13 || cc->mData1 == 0x1B) + && cc->mData2 >= 0x20 && cc->mData2 <= 0x3f){ + // 1 Portuguese/German/Danish char + sprintf(tmp, "[%d]German: %02x %02x", cc->mType, cc->mData1, cc->mData2); + } else if ((cc->mData1 == 0x11 || cc->mData1 == 0x19) + && cc->mData2 >= 0x20 && cc->mData2 <= 0x2f){ + // Mid-Row Codes (Table 69) + sprintf(tmp, "[%d]Mid-row: %02x %02x", cc->mType, cc->mData1, cc->mData2); + } else if (((cc->mData1 == 0x14 || cc->mData1 == 0x1c) + && cc->mData2 >= 0x20 && cc->mData2 <= 0x2f) + || + ((cc->mData1 == 0x17 || cc->mData1 == 0x1f) + && cc->mData2 >= 0x21 && cc->mData2 <= 0x23)){ + // Misc Control Codes (Table 70) + sprintf(tmp, "[%d]Ctrl: %02x %02x", cc->mType, cc->mData1, cc->mData2); + } else if ((cc->mData1 & 0x70) == 0x10 + && (cc->mData2 & 0x40) == 0x40 + && ((cc->mData1 & 0x07) || !(cc->mData2 & 0x20)) ) { + // Preamble Address Codes (Table 71) + sprintf(tmp, "[%d]PAC: %02x %02x", cc->mType, cc->mData1, cc->mData2); + } else { + sprintf(tmp, "[%d]Invalid: %02x %02x", cc->mType, cc->mData1, cc->mData2); + } + + if (out.size() > 0) { + out.append(", "); + } + + out.append(tmp); + + offset += sizeof(CCData); + } + + ALOGI("%s", out.c_str()); +} + +NuPlayer::CCDecoder::CCDecoder(const sp<AMessage> ¬ify) + : mNotify(notify), + mCurrentChannel(0), + mSelectedTrack(-1) { + for (size_t i = 0; i < sizeof(mTrackIndices)/sizeof(mTrackIndices[0]); ++i) { + mTrackIndices[i] = -1; + } +} + +size_t NuPlayer::CCDecoder::getTrackCount() const { + return mFoundChannels.size(); +} + +sp<AMessage> NuPlayer::CCDecoder::getTrackInfo(size_t index) const { + if (!isTrackValid(index)) { + return NULL; + } + + sp<AMessage> format = new AMessage(); + + format->setInt32("type", MEDIA_TRACK_TYPE_SUBTITLE); + format->setString("language", "und"); + format->setString("mime", MEDIA_MIMETYPE_TEXT_CEA_608); + //CC1, field 0 channel 0 + bool isDefaultAuto = (mFoundChannels[index] == 0); + format->setInt32("auto", isDefaultAuto); + format->setInt32("default", isDefaultAuto); + format->setInt32("forced", 0); + + return format; +} + +status_t NuPlayer::CCDecoder::selectTrack(size_t index, bool select) { + if (!isTrackValid(index)) { + return BAD_VALUE; + } + + if (select) { + if (mSelectedTrack == (ssize_t)index) { + ALOGE("track %zu already selected", index); + return BAD_VALUE; + } + ALOGV("selected track %zu", index); + mSelectedTrack = index; + } else { + if (mSelectedTrack != (ssize_t)index) { + ALOGE("track %zu is not selected", index); + return BAD_VALUE; + } + ALOGV("unselected track %zu", index); + mSelectedTrack = -1; + } + + return OK; +} + +bool NuPlayer::CCDecoder::isSelected() const { + return mSelectedTrack >= 0 && mSelectedTrack < (int32_t) getTrackCount(); +} + +bool NuPlayer::CCDecoder::isTrackValid(size_t index) const { + return index < getTrackCount(); +} + +int32_t NuPlayer::CCDecoder::getTrackIndex(size_t channel) const { + if (channel < sizeof(mTrackIndices)/sizeof(mTrackIndices[0])) { + return mTrackIndices[channel]; + } + return -1; +} + +// returns true if a new CC track is found +bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) { + sp<ABuffer> sei; + if (!accessUnit->meta()->findBuffer("sei", &sei) || sei == NULL) { + return false; + } + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + bool trackAdded = false; + + const NALPosition *nal = (NALPosition *) sei->data(); + + for (size_t i = 0; i < sei->size() / sizeof(NALPosition); ++i, ++nal) { + trackAdded |= parseSEINalUnit( + timeUs, accessUnit->data() + nal->nalOffset, nal->nalSize); + } + + return trackAdded; +} + +// returns true if a new CC track is found +bool NuPlayer::CCDecoder::parseSEINalUnit( + int64_t timeUs, const uint8_t *nalStart, size_t nalSize) { + unsigned nalType = nalStart[0] & 0x1f; + + // the buffer should only have SEI in it + if (nalType != 6) { + return false; + } + + bool trackAdded = false; + NALBitReader br(nalStart + 1, nalSize - 1); + // sei_message() + while (br.atLeastNumBitsLeft(16)) { // at least 16-bit for sei_message() + uint32_t payload_type = 0; + size_t payload_size = 0; + uint8_t last_byte; + + do { + last_byte = br.getBits(8); + payload_type += last_byte; + } while (last_byte == 0xFF); + + do { + last_byte = br.getBits(8); + payload_size += last_byte; + } while (last_byte == 0xFF); + + // sei_payload() + if (payload_type == 4) { + bool isCC = false; + if (payload_size > 1 + 2 + 4 + 1) { + // user_data_registered_itu_t_t35() + + // ATSC A/72: 6.4.2 + uint8_t itu_t_t35_country_code = br.getBits(8); + uint16_t itu_t_t35_provider_code = br.getBits(16); + uint32_t user_identifier = br.getBits(32); + uint8_t user_data_type_code = br.getBits(8); + + payload_size -= 1 + 2 + 4 + 1; + + isCC = itu_t_t35_country_code == 0xB5 + && itu_t_t35_provider_code == 0x0031 + && user_identifier == 'GA94' + && user_data_type_code == 0x3; + } + + if (isCC && payload_size > 2) { + // MPEG_cc_data() + // ATSC A/53 Part 4: 6.2.3.1 + br.skipBits(1); //process_em_data_flag + bool process_cc_data_flag = br.getBits(1); + br.skipBits(1); //additional_data_flag + size_t cc_count = br.getBits(5); + br.skipBits(8); // em_data; + payload_size -= 2; + + if (process_cc_data_flag) { + AString out; + + sp<ABuffer> ccBuf = new ABuffer(cc_count * sizeof(CCData)); + ccBuf->setRange(0, 0); + + for (size_t i = 0; i < cc_count && payload_size >= 3; i++) { + uint8_t marker = br.getBits(5); + CHECK_EQ(marker, 0x1f); + + bool cc_valid = br.getBits(1); + uint8_t cc_type = br.getBits(2); + // remove odd parity bit + uint8_t cc_data_1 = br.getBits(8) & 0x7f; + uint8_t cc_data_2 = br.getBits(8) & 0x7f; + + payload_size -= 3; + + if (cc_valid + && (cc_type == 0 || cc_type == 1)) { + CCData cc(cc_type, cc_data_1, cc_data_2); + if (!isNullPad(&cc)) { + size_t channel; + if (cc.getChannel(&channel) && getTrackIndex(channel) < 0) { + mTrackIndices[channel] = mFoundChannels.size(); + mFoundChannels.push_back(channel); + trackAdded = true; + } + memcpy(ccBuf->data() + ccBuf->size(), + (void *)&cc, sizeof(cc)); + ccBuf->setRange(0, ccBuf->size() + sizeof(CCData)); + } + } + } + + mCCMap.add(timeUs, ccBuf); + break; + } + } else { + ALOGV("Malformed SEI payload type 4"); + } + } else { + ALOGV("Unsupported SEI payload type %d", payload_type); + } + + // skipping remaining bits of this payload + br.skipBits(payload_size * 8); + } + + return trackAdded; +} + +sp<ABuffer> NuPlayer::CCDecoder::filterCCBuf( + const sp<ABuffer> &ccBuf, size_t index) { + sp<ABuffer> filteredCCBuf = new ABuffer(ccBuf->size()); + filteredCCBuf->setRange(0, 0); + + size_t cc_count = ccBuf->size() / sizeof(CCData); + const CCData* cc_data = (const CCData*)ccBuf->data(); + for (size_t i = 0; i < cc_count; ++i) { + size_t channel; + if (cc_data[i].getChannel(&channel)) { + mCurrentChannel = channel; + } + if (mCurrentChannel == mFoundChannels[index]) { + memcpy(filteredCCBuf->data() + filteredCCBuf->size(), + (void *)&cc_data[i], sizeof(CCData)); + filteredCCBuf->setRange(0, filteredCCBuf->size() + sizeof(CCData)); + } + } + + return filteredCCBuf; +} + +void NuPlayer::CCDecoder::decode(const sp<ABuffer> &accessUnit) { + if (extractFromSEI(accessUnit)) { + ALOGI("Found CEA-608 track"); + sp<AMessage> msg = mNotify->dup(); + msg->setInt32("what", kWhatTrackAdded); + msg->post(); + } + // TODO: extract CC from other sources +} + +void NuPlayer::CCDecoder::display(int64_t timeUs) { + if (!isTrackValid(mSelectedTrack)) { + ALOGE("Could not find current track(index=%d)", mSelectedTrack); + return; + } + + ssize_t index = mCCMap.indexOfKey(timeUs); + if (index < 0) { + ALOGV("cc for timestamp %" PRId64 " not found", timeUs); + return; + } + + sp<ABuffer> ccBuf = filterCCBuf(mCCMap.valueAt(index), mSelectedTrack); + + if (ccBuf->size() > 0) { +#if 0 + dumpBytePair(ccBuf); +#endif + + ccBuf->meta()->setInt32("trackIndex", mSelectedTrack); + ccBuf->meta()->setInt64("timeUs", timeUs); + ccBuf->meta()->setInt64("durationUs", 0ll); + + sp<AMessage> msg = mNotify->dup(); + msg->setInt32("what", kWhatClosedCaptionData); + msg->setBuffer("buffer", ccBuf); + msg->post(); + } + + // remove all entries before timeUs + mCCMap.removeItemsAt(0, index + 1); +} + +void NuPlayer::CCDecoder::flush() { + mCCMap.clear(); +} + +} // namespace android + diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h new file mode 100644 index 0000000..77fb0fe --- /dev/null +++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h @@ -0,0 +1,60 @@ +/* + * 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 NUPLAYER_CCDECODER_H_ + +#define NUPLAYER_CCDECODER_H_ + +#include "NuPlayer.h" + +namespace android { + +struct NuPlayer::CCDecoder : public RefBase { + enum { + kWhatClosedCaptionData, + kWhatTrackAdded, + }; + + CCDecoder(const sp<AMessage> ¬ify); + + size_t getTrackCount() const; + sp<AMessage> getTrackInfo(size_t index) const; + status_t selectTrack(size_t index, bool select); + bool isSelected() const; + void decode(const sp<ABuffer> &accessUnit); + void display(int64_t timeUs); + void flush(); + +private: + sp<AMessage> mNotify; + KeyedVector<int64_t, sp<ABuffer> > mCCMap; + size_t mCurrentChannel; + int32_t mSelectedTrack; + int32_t mTrackIndices[4]; + Vector<size_t> mFoundChannels; + + bool isTrackValid(size_t index) const; + int32_t getTrackIndex(size_t channel) const; + bool extractFromSEI(const sp<ABuffer> &accessUnit); + bool parseSEINalUnit(int64_t timeUs, const uint8_t *nalStart, size_t nalSize); + sp<ABuffer> filterCCBuf(const sp<ABuffer> &ccBuf, size_t index); + + DISALLOW_EVIL_CONSTRUCTORS(CCDecoder); +}; + +} // namespace android + +#endif // NUPLAYER_CCDECODER_H_ diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 27f6131..04ac699 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * 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. @@ -19,10 +19,12 @@ #include <utils/Log.h> #include <inttypes.h> +#include "NuPlayerCCDecoder.h" #include "NuPlayerDecoder.h" +#include "NuPlayerRenderer.h" +#include "NuPlayerSource.h" #include <media/ICrypto.h> -#include <media/stagefright/foundation/ABitReader.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> @@ -31,71 +33,105 @@ #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> +#include "avc_utils.h" +#include "ATSParser.h" + namespace android { NuPlayer::Decoder::Decoder( const sp<AMessage> ¬ify, - const sp<NativeWindowWrapper> &nativeWindow) - : mNotify(notify), + const sp<Source> &source, + const sp<Renderer> &renderer, + const sp<NativeWindowWrapper> &nativeWindow, + const sp<CCDecoder> &ccDecoder) + : DecoderBase(notify), mNativeWindow(nativeWindow), - mBufferGeneration(0), + mSource(source), + mRenderer(renderer), + mCCDecoder(ccDecoder), + mSkipRenderingUntilMediaTimeUs(-1ll), + mNumFramesTotal(0ll), + mNumFramesDropped(0ll), + mIsAudio(true), + mIsVideoAVC(false), + mIsSecure(false), + mFormatChangePending(false), + mTimeChangePending(false), mPaused(true), + mResumePending(false), mComponentName("decoder") { - // Every decoder has its own looper because MediaCodec operations - // are blocking, but NuPlayer needs asynchronous operations. - mDecoderLooper = new ALooper; - mDecoderLooper->setName("NPDecoder"); - mDecoderLooper->start(false, false, ANDROID_PRIORITY_AUDIO); - mCodecLooper = new ALooper; mCodecLooper->setName("NPDecoder-CL"); mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO); } NuPlayer::Decoder::~Decoder() { - mDecoderLooper->unregisterHandler(id()); - mDecoderLooper->stop(); - releaseAndResetMediaBuffers(); } -static -status_t PostAndAwaitResponse( - const sp<AMessage> &msg, sp<AMessage> *response) { - status_t err = msg->postAndAwaitResponse(response); +void NuPlayer::Decoder::getStats( + int64_t *numFramesTotal, + int64_t *numFramesDropped) const { + *numFramesTotal = mNumFramesTotal; + *numFramesDropped = mNumFramesDropped; +} - if (err != OK) { - return err; - } +void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { + ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str()); - if (!(*response)->findInt32("err", &err)) { - err = OK; - } + switch (msg->what()) { + case kWhatCodecNotify: + { + if (!isStaleReply(msg)) { + int32_t numInput, numOutput; - return err; -} + if (!msg->findInt32("input-buffers", &numInput)) { + numInput = INT32_MAX; + } -void NuPlayer::Decoder::rememberCodecSpecificData(const sp<AMessage> &format) { - mCSDsForCurrentFormat.clear(); - for (int32_t i = 0; ; ++i) { - AString tag = "csd-"; - tag.append(i); - sp<ABuffer> buffer; - if (!format->findBuffer(tag.c_str(), &buffer)) { + if (!msg->findInt32("output-buffers", &numOutput)) { + numOutput = INT32_MAX; + } + + if (!mPaused) { + while (numInput-- > 0 && handleAnInputBuffer()) {} + } + + while (numOutput-- > 0 && handleAnOutputBuffer()) {} + } + + requestCodecNotification(); break; } - mCSDsForCurrentFormat.push(buffer); + + case kWhatRenderBuffer: + { + if (!isStaleReply(msg)) { + onRenderBuffer(msg); + } + break; + } + + default: + DecoderBase::onMessageReceived(msg); + break; } } void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { CHECK(mCodec == NULL); + mFormatChangePending = false; + mTimeChangePending = false; + ++mBufferGeneration; AString mime; CHECK(format->findString("mime", &mime)); + mIsAudio = !strncasecmp("audio/", mime.c_str(), 6); + mIsVideoAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str()); + sp<Surface> surface = NULL; if (mNativeWindow != NULL) { surface = mNativeWindow->getSurfaceTextureClient(); @@ -123,6 +159,7 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { handleError(UNKNOWN_ERROR); return; } + mIsSecure = secure; mCodec->getName(&mComponentName); @@ -169,83 +206,151 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { mInputBuffers.size(), mOutputBuffers.size()); - requestCodecNotification(); + if (mRenderer != NULL) { + requestCodecNotification(); + } mPaused = false; + mResumePending = false; } -void NuPlayer::Decoder::releaseAndResetMediaBuffers() { - for (size_t i = 0; i < mMediaBuffers.size(); i++) { - if (mMediaBuffers[i] != NULL) { - mMediaBuffers[i]->release(); - mMediaBuffers.editItemAt(i) = NULL; - } - } - mMediaBuffers.resize(mInputBuffers.size()); - for (size_t i = 0; i < mMediaBuffers.size(); i++) { - mMediaBuffers.editItemAt(i) = NULL; +void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) { + bool hadNoRenderer = (mRenderer == NULL); + mRenderer = renderer; + if (hadNoRenderer && mRenderer != NULL) { + requestCodecNotification(); } - mInputBufferIsDequeued.clear(); - mInputBufferIsDequeued.resize(mInputBuffers.size()); - for (size_t i = 0; i < mInputBufferIsDequeued.size(); i++) { - mInputBufferIsDequeued.editItemAt(i) = false; +} + +void NuPlayer::Decoder::onGetInputBuffers( + Vector<sp<ABuffer> > *dstBuffers) { + dstBuffers->clear(); + for (size_t i = 0; i < mInputBuffers.size(); i++) { + dstBuffers->push(mInputBuffers[i]); } +} - mPendingInputMessages.clear(); +void NuPlayer::Decoder::onResume(bool notifyComplete) { + mPaused = false; + + if (notifyComplete) { + mResumePending = true; + } } -void NuPlayer::Decoder::requestCodecNotification() { +void NuPlayer::Decoder::doFlush(bool notifyComplete) { + if (mCCDecoder != NULL) { + mCCDecoder->flush(); + } + + if (mRenderer != NULL) { + mRenderer->flush(mIsAudio, notifyComplete); + mRenderer->signalTimeDiscontinuity(); + } + + status_t err = OK; if (mCodec != NULL) { - sp<AMessage> reply = new AMessage(kWhatCodecNotify, id()); - reply->setInt32("generation", mBufferGeneration); - mCodec->requestActivityNotification(reply); + err = mCodec->flush(); + mCSDsToSubmit = mCSDsForCurrentFormat; // copy operator + ++mBufferGeneration; } -} -bool NuPlayer::Decoder::isStaleReply(const sp<AMessage> &msg) { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - return generation != mBufferGeneration; + if (err != OK) { + ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err); + handleError(err); + // finish with posting kWhatFlushCompleted. + // we attempt to release the buffers even if flush fails. + } + releaseAndResetMediaBuffers(); } -void NuPlayer::Decoder::init() { - mDecoderLooper->registerHandler(this); -} +void NuPlayer::Decoder::onFlush() { + doFlush(true); -void NuPlayer::Decoder::configure(const sp<AMessage> &format) { - sp<AMessage> msg = new AMessage(kWhatConfigure, id()); - msg->setMessage("format", format); - msg->post(); -} + if (isDiscontinuityPending()) { + // This could happen if the client starts seeking/shutdown + // after we queued an EOS for discontinuities. + // We can consider discontinuity handled. + finishHandleDiscontinuity(false /* flushOnTimeChange */); + } -void NuPlayer::Decoder::signalUpdateFormat(const sp<AMessage> &format) { - sp<AMessage> msg = new AMessage(kWhatUpdateFormat, id()); - msg->setMessage("format", format); - msg->post(); + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatFlushCompleted); + notify->post(); + mPaused = true; } -status_t NuPlayer::Decoder::getInputBuffers(Vector<sp<ABuffer> > *buffers) const { - sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id()); - msg->setPointer("buffers", buffers); +void NuPlayer::Decoder::onShutdown(bool notifyComplete) { + status_t err = OK; + + // if there is a pending resume request, notify complete now + notifyResumeCompleteIfNecessary(); - sp<AMessage> response; - return PostAndAwaitResponse(msg, &response); + if (mCodec != NULL) { + err = mCodec->release(); + mCodec = NULL; + ++mBufferGeneration; + + if (mNativeWindow != NULL) { + // reconnect to surface as MediaCodec disconnected from it + status_t error = + native_window_api_connect( + mNativeWindow->getNativeWindow().get(), + NATIVE_WINDOW_API_MEDIA); + ALOGW_IF(error != NO_ERROR, + "[%s] failed to connect to native window, error=%d", + mComponentName.c_str(), error); + } + mComponentName = "decoder"; + } + + releaseAndResetMediaBuffers(); + + if (err != OK) { + ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err); + handleError(err); + // finish with posting kWhatShutdownCompleted. + } + + if (notifyComplete) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatShutdownCompleted); + notify->post(); + mPaused = true; + } } -void NuPlayer::Decoder::handleError(int32_t err) -{ - // We cannot immediately release the codec due to buffers still outstanding - // in the renderer. We signal to the player the error so it can shutdown/release the - // decoder after flushing and increment the generation to discard unnecessary messages. +void NuPlayer::Decoder::doRequestBuffers() { + if (isDiscontinuityPending()) { + return; + } + status_t err = OK; + while (err == OK && !mDequeuedInputBuffers.empty()) { + size_t bufferIx = *mDequeuedInputBuffers.begin(); + sp<AMessage> msg = new AMessage(); + msg->setSize("buffer-ix", bufferIx); + err = fetchInputData(msg); + if (err != OK && err != ERROR_END_OF_STREAM) { + // if EOS, need to queue EOS buffer + break; + } + mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin()); - ++mBufferGeneration; + if (!mPendingInputMessages.empty() + || !onInputBufferFetched(msg)) { + mPendingInputMessages.push_back(msg); + } + } - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatError); - notify->setInt32("err", err); - notify->post(); + if (err == -EWOULDBLOCK + && mSource->feedMoreTSData() == OK) { + scheduleRequestBuffers(); + } } bool NuPlayer::Decoder::handleAnInputBuffer() { + if (isDiscontinuityPending()) { + return false; + } size_t bufferIx = -1; status_t res = mCodec->dequeueInputBuffer(&bufferIx); ALOGV("[%s] dequeued input: %d", @@ -267,22 +372,21 @@ bool NuPlayer::Decoder::handleAnInputBuffer() { } mInputBufferIsDequeued.editItemAt(bufferIx) = true; - sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id()); - reply->setSize("buffer-ix", bufferIx); - reply->setInt32("generation", mBufferGeneration); - if (!mCSDsToSubmit.isEmpty()) { + sp<AMessage> msg = new AMessage(); + msg->setSize("buffer-ix", bufferIx); + sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0); ALOGI("[%s] resubmitting CSD", mComponentName.c_str()); - reply->setBuffer("buffer", buffer); + msg->setBuffer("buffer", buffer); mCSDsToSubmit.removeAt(0); - CHECK(onInputBufferFilled(reply)); + CHECK(onInputBufferFetched(msg)); return true; } while (!mPendingInputMessages.empty()) { sp<AMessage> msg = *mPendingInputMessages.begin(); - if (!onInputBufferFilled(msg)) { + if (!onInputBufferFetched(msg)) { break; } mPendingInputMessages.erase(mPendingInputMessages.begin()); @@ -292,15 +396,262 @@ bool NuPlayer::Decoder::handleAnInputBuffer() { return true; } - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatFillThisBuffer); - notify->setBuffer("buffer", mInputBuffers[bufferIx]); - notify->setMessage("reply", reply); - notify->post(); + mDequeuedInputBuffers.push_back(bufferIx); + + onRequestInputBuffers(); return true; } -bool android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { +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 (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 (!mIsAudio) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatVideoSizeChanged); + notify->setMessage("format", format); + notify->post(); + } else if (mRenderer != NULL) { + uint32_t flags; + int64_t durationUs; + bool hasVideo = (mSource->getFormat(false /* audio */) != NULL); + if (!hasVideo && + mSource->getDuration(&durationUs) == OK && + durationUs + > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { + flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } else { + flags = AUDIO_OUTPUT_FLAG_NONE; + } + + res = mRenderer->openAudioSink( + format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaded */); + if (res != OK) { + ALOGE("Failed to open AudioSink on format change for %s (err=%d)", + mComponentName.c_str(), res); + handleError(res); + return false; + } + } + return true; + } else if (res == INFO_DISCONTINUITY) { + // nothing to do + return true; + } else if (res != OK) { + if (res != -EAGAIN) { + ALOGE("Failed to dequeue output buffer for %s (err=%d)", + mComponentName.c_str(), res); + handleError(res); + } + return false; + } + + CHECK_LT(bufferIx, mOutputBuffers.size()); + sp<ABuffer> buffer = mOutputBuffers[bufferIx]; + buffer->setRange(offset, size); + buffer->meta()->clear(); + buffer->meta()->setInt64("timeUs", timeUs); + + bool eos = flags & MediaCodec::BUFFER_FLAG_EOS; + // we do not expect CODECCONFIG or SYNCFRAME for decoder + + sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this); + reply->setSize("buffer-ix", bufferIx); + reply->setInt32("generation", mBufferGeneration); + + if (eos) { + ALOGI("[%s] saw output EOS", mIsAudio ? "audio" : "video"); + + buffer->meta()->setInt32("eos", true); + reply->setInt32("eos", true); + } else if (mSkipRenderingUntilMediaTimeUs >= 0) { + if (timeUs < mSkipRenderingUntilMediaTimeUs) { + ALOGV("[%s] dropping buffer at time %lld as requested.", + mComponentName.c_str(), (long long)timeUs); + + reply->post(); + return true; + } + + mSkipRenderingUntilMediaTimeUs = -1; + } + + // wait until 1st frame comes out to signal resume complete + notifyResumeCompleteIfNecessary(); + + if (mRenderer != NULL) { + // send the buffer to renderer. + mRenderer->queueBuffer(mIsAudio, buffer, reply); + if (eos && !isDiscontinuityPending()) { + mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM); + } + } + + return true; +} + +void NuPlayer::Decoder::releaseAndResetMediaBuffers() { + for (size_t i = 0; i < mMediaBuffers.size(); i++) { + if (mMediaBuffers[i] != NULL) { + mMediaBuffers[i]->release(); + mMediaBuffers.editItemAt(i) = NULL; + } + } + mMediaBuffers.resize(mInputBuffers.size()); + for (size_t i = 0; i < mMediaBuffers.size(); i++) { + mMediaBuffers.editItemAt(i) = NULL; + } + mInputBufferIsDequeued.clear(); + mInputBufferIsDequeued.resize(mInputBuffers.size()); + for (size_t i = 0; i < mInputBufferIsDequeued.size(); i++) { + mInputBufferIsDequeued.editItemAt(i) = false; + } + + mPendingInputMessages.clear(); + mDequeuedInputBuffers.clear(); + mSkipRenderingUntilMediaTimeUs = -1; +} + +void NuPlayer::Decoder::requestCodecNotification() { + if (mCodec != NULL) { + sp<AMessage> reply = new AMessage(kWhatCodecNotify, this); + reply->setInt32("generation", mBufferGeneration); + mCodec->requestActivityNotification(reply); + } +} + +bool NuPlayer::Decoder::isStaleReply(const sp<AMessage> &msg) { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + return generation != mBufferGeneration; +} + +status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) { + sp<ABuffer> accessUnit; + bool dropAccessUnit; + do { + status_t err = mSource->dequeueAccessUnit(mIsAudio, &accessUnit); + + if (err == -EWOULDBLOCK) { + return err; + } else if (err != OK) { + if (err == INFO_DISCONTINUITY) { + int32_t type; + CHECK(accessUnit->meta()->findInt32("discontinuity", &type)); + + bool formatChange = + (mIsAudio && + (type & ATSParser::DISCONTINUITY_AUDIO_FORMAT)) + || (!mIsAudio && + (type & ATSParser::DISCONTINUITY_VIDEO_FORMAT)); + + bool timeChange = (type & ATSParser::DISCONTINUITY_TIME) != 0; + + ALOGI("%s discontinuity (format=%d, time=%d)", + mIsAudio ? "audio" : "video", formatChange, timeChange); + + bool seamlessFormatChange = false; + sp<AMessage> newFormat = mSource->getFormat(mIsAudio); + if (formatChange) { + seamlessFormatChange = + supportsSeamlessFormatChange(newFormat); + // treat seamless format change separately + formatChange = !seamlessFormatChange; + } + + // For format or time change, return EOS to queue EOS input, + // then wait for EOS on output. + if (formatChange /* not seamless */) { + mFormatChangePending = true; + err = ERROR_END_OF_STREAM; + } else if (timeChange) { + rememberCodecSpecificData(newFormat); + mTimeChangePending = true; + err = ERROR_END_OF_STREAM; + } else if (seamlessFormatChange) { + // reuse existing decoder and don't flush + rememberCodecSpecificData(newFormat); + continue; + } else { + // This stream is unaffected by the discontinuity + return -EWOULDBLOCK; + } + } + + // reply should only be returned without a buffer set + // when there is an error (including EOS) + CHECK(err != OK); + + reply->setInt32("err", err); + return ERROR_END_OF_STREAM; + } + + if (!mIsAudio) { + ++mNumFramesTotal; + } + + dropAccessUnit = false; + if (!mIsAudio + && !mIsSecure + && mRenderer->getVideoLateByUs() > 100000ll + && mIsVideoAVC + && !IsAVCReferenceFrame(accessUnit)) { + dropAccessUnit = true; + ++mNumFramesDropped; + } + } while (dropAccessUnit); + + // ALOGV("returned a valid buffer of %s data", mIsAudio ? "mIsAudio" : "video"); +#if 0 + int64_t mediaTimeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs)); + ALOGV("[%s] feeding input buffer at media time %" PRId64, + mIsAudio ? "audio" : "video", + mediaTimeUs / 1E6); +#endif + + if (mCCDecoder != NULL) { + mCCDecoder->decode(accessUnit); + } + + reply->setBuffer("buffer", accessUnit); + + return OK; +} + +bool NuPlayer::Decoder::onInputBufferFetched(const sp<AMessage> &msg) { size_t bufferIx; CHECK(msg->findSize("buffer-ix", &bufferIx)); CHECK_LT(bufferIx, mInputBuffers.size()); @@ -342,16 +693,11 @@ bool android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { } } - - 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 true; - } + CHECK(streamErr != OK); // attempt to queue EOS status_t err = mCodec->queueInputBuffer( @@ -375,6 +721,17 @@ bool android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { handleError(streamErr); } } else { + sp<AMessage> extra; + if (buffer->meta()->findMessage("extra", &extra) && extra != NULL) { + int64_t resumeAtMediaTimeUs; + if (extra->findInt64( + "resume-at-mediaTimeUs", &resumeAtMediaTimeUs)) { + ALOGI("[%s] suppressing rendering until %lld us", + mComponentName.c_str(), (long long)resumeAtMediaTimeUs); + mSkipRenderingUntilMediaTimeUs = resumeAtMediaTimeUs; + } + } + int64_t timeUs = 0; uint32_t flags = 0; CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); @@ -418,96 +775,23 @@ bool android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { return true; } -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 (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; - } - - 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) { - ALOGE("Failed to dequeue output buffer for %s (err=%d)", - mComponentName.c_str(), res); - handleError(res); - } - return false; - } - - 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->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::onRenderBuffer(const sp<AMessage> &msg) { status_t err; int32_t render; size_t bufferIx; + int32_t eos; CHECK(msg->findSize("buffer-ix", &bufferIx)); + + if (!mIsAudio) { + int64_t timeUs; + sp<ABuffer> buffer = mOutputBuffers[bufferIx]; + buffer->meta()->findInt64("timeUs", &timeUs); + + if (mCCDecoder != NULL && mCCDecoder->isSelected()) { + mCCDecoder->display(timeUs); + } + } + if (msg->findInt32("render", &render) && render) { int64_t timestampNs; CHECK(msg->findInt64("timestampNs", ×tampNs)); @@ -520,194 +804,46 @@ void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) { mComponentName.c_str(), err); handleError(err); } -} - -void NuPlayer::Decoder::onFlush() { - status_t err = OK; - if (mCodec != NULL) { - err = mCodec->flush(); - mCSDsToSubmit = mCSDsForCurrentFormat; // copy operator - ++mBufferGeneration; + if (msg->findInt32("eos", &eos) && eos + && isDiscontinuityPending()) { + finishHandleDiscontinuity(true /* flushOnTimeChange */); } - - if (err != OK) { - ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err); - handleError(err); - // finish with posting kWhatFlushCompleted. - // we attempt to release the buffers even if flush fails. - } - releaseAndResetMediaBuffers(); - - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatFlushCompleted); - notify->post(); - mPaused = true; } -void NuPlayer::Decoder::onResume() { - mPaused = false; +bool NuPlayer::Decoder::isDiscontinuityPending() const { + return mFormatChangePending || mTimeChangePending; } -void NuPlayer::Decoder::onShutdown() { - status_t err = OK; - if (mCodec != NULL) { - err = mCodec->release(); - mCodec = NULL; - ++mBufferGeneration; - - if (mNativeWindow != NULL) { - // reconnect to surface as MediaCodec disconnected from it - status_t error = - native_window_api_connect( - mNativeWindow->getNativeWindow().get(), - NATIVE_WINDOW_API_MEDIA); - ALOGW_IF(error != NO_ERROR, - "[%s] failed to connect to native window, error=%d", - mComponentName.c_str(), error); - } - mComponentName = "decoder"; - } - - releaseAndResetMediaBuffers(); - - if (err != OK) { - ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err); - handleError(err); - // finish with posting kWhatShutdownCompleted. - } - - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatShutdownCompleted); - notify->post(); - mPaused = true; -} - -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 kWhatUpdateFormat: - { - sp<AMessage> format; - CHECK(msg->findMessage("format", &format)); - rememberCodecSpecificData(format); - break; - } - - case kWhatGetInputBuffers: - { - uint32_t replyID; - CHECK(msg->senderAwaitsResponse(&replyID)); - - Vector<sp<ABuffer> > *dstBuffers; - CHECK(msg->findPointer("buffers", (void **)&dstBuffers)); - - dstBuffers->clear(); - for (size_t i = 0; i < mInputBuffers.size(); i++) { - dstBuffers->push(mInputBuffers[i]); - } - - (new AMessage)->postReply(replyID); - break; - } - - case kWhatCodecNotify: - { - if (!isStaleReply(msg)) { - int32_t numInput, numOutput; - - if (!msg->findInt32("input-buffers", &numInput)) { - numInput = INT32_MAX; - } - - if (!msg->findInt32("output-buffers", &numOutput)) { - numOutput = INT32_MAX; - } - - if (!mPaused) { - while (numInput-- > 0 && handleAnInputBuffer()) {} - } - - while (numOutput-- > 0 && handleAnOutputBuffer()) {} - } - - requestCodecNotification(); - break; - } - - case kWhatInputBufferFilled: - { - if (!isStaleReply(msg)) { - if (!mPendingInputMessages.empty() - || !onInputBufferFilled(msg)) { - mPendingInputMessages.push_back(msg); - } - } - - break; - } - - case kWhatRenderBuffer: - { - if (!isStaleReply(msg)) { - onRenderBuffer(msg); - } - break; - } - - case kWhatFlush: - { - sp<AMessage> format; - if (msg->findMessage("new-format", &format)) { - rememberCodecSpecificData(format); - } - onFlush(); - break; - } +void NuPlayer::Decoder::finishHandleDiscontinuity(bool flushOnTimeChange) { + ALOGV("finishHandleDiscontinuity: format %d, time %d, flush %d", + mFormatChangePending, mTimeChangePending, flushOnTimeChange); - case kWhatResume: - { - onResume(); - break; - } + // If we have format change, pause and wait to be killed; + // If we have time change only, flush and restart fetching. - case kWhatShutdown: - { - onShutdown(); - break; + if (mFormatChangePending) { + mPaused = true; + } else if (mTimeChangePending) { + if (flushOnTimeChange) { + doFlush(false /*notifyComplete*/); } - default: - TRESPASS(); - break; + // restart fetching input + scheduleRequestBuffers(); } -} -void NuPlayer::Decoder::signalFlush(const sp<AMessage> &format) { - sp<AMessage> msg = new AMessage(kWhatFlush, id()); - if (format != NULL) { - msg->setMessage("new-format", format); - } + // Notify NuPlayer to either shutdown decoder, or rescan sources + sp<AMessage> msg = mNotify->dup(); + msg->setInt32("what", kWhatInputDiscontinuity); + msg->setInt32("formatChange", mFormatChangePending); msg->post(); -} - -void NuPlayer::Decoder::signalResume() { - (new AMessage(kWhatResume, id()))->post(); -} -void NuPlayer::Decoder::initiateShutdown() { - (new AMessage(kWhatShutdown, id()))->post(); + mFormatChangePending = false; + mTimeChangePending = false; } -bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const { +bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange( + const sp<AMessage> &targetFormat) const { if (targetFormat == NULL) { return true; } @@ -722,7 +858,7 @@ 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 (!mOutputFormat->findInt32(keys[i], &oldVal) || + if (!mInputFormat->findInt32(keys[i], &oldVal) || !targetFormat->findInt32(keys[i], &newVal) || oldVal != newVal) { return false; @@ -730,7 +866,7 @@ bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &ta } sp<ABuffer> oldBuf, newBuf; - if (mOutputFormat->findBuffer("csd-0", &oldBuf) && + if (mInputFormat->findBuffer("csd-0", &oldBuf) && targetFormat->findBuffer("csd-0", &newBuf)) { if (oldBuf->size() != newBuf->size()) { return false; @@ -742,7 +878,7 @@ bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &ta } bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetFormat) const { - if (mOutputFormat == NULL) { + if (mInputFormat == NULL) { return false; } @@ -751,7 +887,7 @@ bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetF } AString oldMime, newMime; - if (!mOutputFormat->findString("mime", &oldMime) + if (!mInputFormat->findString("mime", &oldMime) || !targetFormat->findString("mime", &newMime) || !(oldMime == newMime)) { return false; @@ -772,332 +908,30 @@ bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetF return seamless; } -struct CCData { - CCData(uint8_t type, uint8_t data1, uint8_t data2) - : mType(type), mData1(data1), mData2(data2) { - } - bool getChannel(size_t *channel) const { - if (mData1 >= 0x10 && mData1 <= 0x1f) { - *channel = (mData1 >= 0x18 ? 1 : 0) + (mType ? 2 : 0); - return true; - } - return false; - } - - uint8_t mType; - uint8_t mData1; - uint8_t mData2; -}; - -static bool isNullPad(CCData *cc) { - return cc->mData1 < 0x10 && cc->mData2 < 0x10; -} - -static void dumpBytePair(const sp<ABuffer> &ccBuf) { - size_t offset = 0; - AString out; - - while (offset < ccBuf->size()) { - char tmp[128]; - - CCData *cc = (CCData *) (ccBuf->data() + offset); - - if (isNullPad(cc)) { - // 1 null pad or XDS metadata, ignore - offset += sizeof(CCData); - continue; - } - - if (cc->mData1 >= 0x20 && cc->mData1 <= 0x7f) { - // 2 basic chars - sprintf(tmp, "[%d]Basic: %c %c", cc->mType, cc->mData1, cc->mData2); - } else if ((cc->mData1 == 0x11 || cc->mData1 == 0x19) - && cc->mData2 >= 0x30 && cc->mData2 <= 0x3f) { - // 1 special char - sprintf(tmp, "[%d]Special: %02x %02x", cc->mType, cc->mData1, cc->mData2); - } else if ((cc->mData1 == 0x12 || cc->mData1 == 0x1A) - && cc->mData2 >= 0x20 && cc->mData2 <= 0x3f){ - // 1 Spanish/French char - sprintf(tmp, "[%d]Spanish: %02x %02x", cc->mType, cc->mData1, cc->mData2); - } else if ((cc->mData1 == 0x13 || cc->mData1 == 0x1B) - && cc->mData2 >= 0x20 && cc->mData2 <= 0x3f){ - // 1 Portuguese/German/Danish char - sprintf(tmp, "[%d]German: %02x %02x", cc->mType, cc->mData1, cc->mData2); - } else if ((cc->mData1 == 0x11 || cc->mData1 == 0x19) - && cc->mData2 >= 0x20 && cc->mData2 <= 0x2f){ - // Mid-Row Codes (Table 69) - sprintf(tmp, "[%d]Mid-row: %02x %02x", cc->mType, cc->mData1, cc->mData2); - } else if (((cc->mData1 == 0x14 || cc->mData1 == 0x1c) - && cc->mData2 >= 0x20 && cc->mData2 <= 0x2f) - || - ((cc->mData1 == 0x17 || cc->mData1 == 0x1f) - && cc->mData2 >= 0x21 && cc->mData2 <= 0x23)){ - // Misc Control Codes (Table 70) - sprintf(tmp, "[%d]Ctrl: %02x %02x", cc->mType, cc->mData1, cc->mData2); - } else if ((cc->mData1 & 0x70) == 0x10 - && (cc->mData2 & 0x40) == 0x40 - && ((cc->mData1 & 0x07) || !(cc->mData2 & 0x20)) ) { - // Preamble Address Codes (Table 71) - sprintf(tmp, "[%d]PAC: %02x %02x", cc->mType, cc->mData1, cc->mData2); - } else { - sprintf(tmp, "[%d]Invalid: %02x %02x", cc->mType, cc->mData1, cc->mData2); - } - - if (out.size() > 0) { - out.append(", "); - } - - out.append(tmp); - - offset += sizeof(CCData); - } - - ALOGI("%s", out.c_str()); -} - -NuPlayer::CCDecoder::CCDecoder(const sp<AMessage> ¬ify) - : mNotify(notify), - mCurrentChannel(0), - mSelectedTrack(-1) { - for (size_t i = 0; i < sizeof(mTrackIndices)/sizeof(mTrackIndices[0]); ++i) { - mTrackIndices[i] = -1; - } -} - -size_t NuPlayer::CCDecoder::getTrackCount() const { - return mFoundChannels.size(); -} - -sp<AMessage> NuPlayer::CCDecoder::getTrackInfo(size_t index) const { - if (!isTrackValid(index)) { - return NULL; - } - - sp<AMessage> format = new AMessage(); - - format->setInt32("type", MEDIA_TRACK_TYPE_SUBTITLE); - format->setString("language", "und"); - format->setString("mime", MEDIA_MIMETYPE_TEXT_CEA_608); - //CC1, field 0 channel 0 - bool isDefaultAuto = (mFoundChannels[index] == 0); - format->setInt32("auto", isDefaultAuto); - format->setInt32("default", isDefaultAuto); - format->setInt32("forced", 0); - - return format; -} - -status_t NuPlayer::CCDecoder::selectTrack(size_t index, bool select) { - if (!isTrackValid(index)) { - return BAD_VALUE; - } - - if (select) { - if (mSelectedTrack == (ssize_t)index) { - ALOGE("track %zu already selected", index); - return BAD_VALUE; - } - ALOGV("selected track %zu", index); - mSelectedTrack = index; - } else { - if (mSelectedTrack != (ssize_t)index) { - ALOGE("track %zu is not selected", index); - return BAD_VALUE; - } - ALOGV("unselected track %zu", index); - mSelectedTrack = -1; - } - - return OK; -} - -bool NuPlayer::CCDecoder::isSelected() const { - return mSelectedTrack >= 0 && mSelectedTrack < (int32_t) getTrackCount(); -} - -bool NuPlayer::CCDecoder::isTrackValid(size_t index) const { - return index < getTrackCount(); -} - -int32_t NuPlayer::CCDecoder::getTrackIndex(size_t channel) const { - if (channel < sizeof(mTrackIndices)/sizeof(mTrackIndices[0])) { - return mTrackIndices[channel]; - } - return -1; -} - -// returns true if a new CC track is found -bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) { - int64_t timeUs; - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - - sp<ABuffer> sei; - if (!accessUnit->meta()->findBuffer("sei", &sei) || sei == NULL) { - return false; - } - - bool trackAdded = false; - - NALBitReader br(sei->data() + 1, sei->size() - 1); - // sei_message() - while (br.atLeastNumBitsLeft(16)) { // at least 16-bit for sei_message() - uint32_t payload_type = 0; - size_t payload_size = 0; - uint8_t last_byte; - - do { - last_byte = br.getBits(8); - payload_type += last_byte; - } while (last_byte == 0xFF); - - do { - last_byte = br.getBits(8); - payload_size += last_byte; - } while (last_byte == 0xFF); - - // sei_payload() - if (payload_type == 4) { - // user_data_registered_itu_t_t35() - - // ATSC A/72: 6.4.2 - uint8_t itu_t_t35_country_code = br.getBits(8); - uint16_t itu_t_t35_provider_code = br.getBits(16); - uint32_t user_identifier = br.getBits(32); - uint8_t user_data_type_code = br.getBits(8); - - payload_size -= 1 + 2 + 4 + 1; - - if (itu_t_t35_country_code == 0xB5 - && itu_t_t35_provider_code == 0x0031 - && user_identifier == 'GA94' - && user_data_type_code == 0x3) { - // MPEG_cc_data() - // ATSC A/53 Part 4: 6.2.3.1 - br.skipBits(1); //process_em_data_flag - bool process_cc_data_flag = br.getBits(1); - br.skipBits(1); //additional_data_flag - size_t cc_count = br.getBits(5); - br.skipBits(8); // em_data; - payload_size -= 2; - - if (process_cc_data_flag) { - AString out; - - sp<ABuffer> ccBuf = new ABuffer(cc_count * sizeof(CCData)); - ccBuf->setRange(0, 0); - - for (size_t i = 0; i < cc_count; i++) { - uint8_t marker = br.getBits(5); - CHECK_EQ(marker, 0x1f); - - bool cc_valid = br.getBits(1); - uint8_t cc_type = br.getBits(2); - // remove odd parity bit - uint8_t cc_data_1 = br.getBits(8) & 0x7f; - uint8_t cc_data_2 = br.getBits(8) & 0x7f; - - if (cc_valid - && (cc_type == 0 || cc_type == 1)) { - CCData cc(cc_type, cc_data_1, cc_data_2); - if (!isNullPad(&cc)) { - size_t channel; - if (cc.getChannel(&channel) && getTrackIndex(channel) < 0) { - mTrackIndices[channel] = mFoundChannels.size(); - mFoundChannels.push_back(channel); - trackAdded = true; - } - memcpy(ccBuf->data() + ccBuf->size(), - (void *)&cc, sizeof(cc)); - ccBuf->setRange(0, ccBuf->size() + sizeof(CCData)); - } - } - } - payload_size -= cc_count * 3; - - mCCMap.add(timeUs, ccBuf); - break; - } - } else { - ALOGV("Malformed SEI payload type 4"); - } - } else { - ALOGV("Unsupported SEI payload type %d", payload_type); - } - - // skipping remaining bits of this payload - br.skipBits(payload_size * 8); +void NuPlayer::Decoder::rememberCodecSpecificData(const sp<AMessage> &format) { + if (format == NULL) { + return; } - - return trackAdded; -} - -sp<ABuffer> NuPlayer::CCDecoder::filterCCBuf( - const sp<ABuffer> &ccBuf, size_t index) { - sp<ABuffer> filteredCCBuf = new ABuffer(ccBuf->size()); - filteredCCBuf->setRange(0, 0); - - size_t cc_count = ccBuf->size() / sizeof(CCData); - const CCData* cc_data = (const CCData*)ccBuf->data(); - for (size_t i = 0; i < cc_count; ++i) { - size_t channel; - if (cc_data[i].getChannel(&channel)) { - mCurrentChannel = channel; - } - if (mCurrentChannel == mFoundChannels[index]) { - memcpy(filteredCCBuf->data() + filteredCCBuf->size(), - (void *)&cc_data[i], sizeof(CCData)); - filteredCCBuf->setRange(0, filteredCCBuf->size() + sizeof(CCData)); + mCSDsForCurrentFormat.clear(); + for (int32_t i = 0; ; ++i) { + AString tag = "csd-"; + tag.append(i); + sp<ABuffer> buffer; + if (!format->findBuffer(tag.c_str(), &buffer)) { + break; } + mCSDsForCurrentFormat.push(buffer); } - - return filteredCCBuf; -} - -void NuPlayer::CCDecoder::decode(const sp<ABuffer> &accessUnit) { - if (extractFromSEI(accessUnit)) { - ALOGI("Found CEA-608 track"); - sp<AMessage> msg = mNotify->dup(); - msg->setInt32("what", kWhatTrackAdded); - msg->post(); - } - // TODO: extract CC from other sources } -void NuPlayer::CCDecoder::display(int64_t timeUs) { - if (!isTrackValid(mSelectedTrack)) { - ALOGE("Could not find current track(index=%d)", mSelectedTrack); - return; - } - - ssize_t index = mCCMap.indexOfKey(timeUs); - if (index < 0) { - ALOGV("cc for timestamp %" PRId64 " not found", timeUs); - return; - } - - sp<ABuffer> ccBuf = filterCCBuf(mCCMap.valueAt(index), mSelectedTrack); +void NuPlayer::Decoder::notifyResumeCompleteIfNecessary() { + if (mResumePending) { + mResumePending = false; - if (ccBuf->size() > 0) { -#if 0 - dumpBytePair(ccBuf); -#endif - - ccBuf->meta()->setInt32("trackIndex", mSelectedTrack); - ccBuf->meta()->setInt64("timeUs", timeUs); - ccBuf->meta()->setInt64("durationUs", 0ll); - - sp<AMessage> msg = mNotify->dup(); - msg->setInt32("what", kWhatClosedCaptionData); - msg->setBuffer("buffer", ccBuf); - msg->post(); + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatResumeCompleted); + notify->post(); } - - // remove all entries before timeUs - mCCMap.removeItemsAt(0, index + 1); -} - -void NuPlayer::CCDecoder::flush() { - mCCMap.clear(); } } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index dba3eee..4aab2c6 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * 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. @@ -15,70 +15,54 @@ */ #ifndef NUPLAYER_DECODER_H_ - #define NUPLAYER_DECODER_H_ #include "NuPlayer.h" -#include <media/stagefright/foundation/AHandler.h> +#include "NuPlayerDecoderBase.h" namespace android { -struct ABuffer; -struct MediaCodec; -struct MediaBuffer; - -struct NuPlayer::Decoder : public AHandler { +struct NuPlayer::Decoder : public DecoderBase { Decoder(const sp<AMessage> ¬ify, - const sp<NativeWindowWrapper> &nativeWindow = NULL); - - virtual void configure(const sp<AMessage> &format); - virtual void init(); - - status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const; - virtual void signalFlush(const sp<AMessage> &format = NULL); - virtual void signalUpdateFormat(const sp<AMessage> &format); - virtual void signalResume(); - virtual void initiateShutdown(); + const sp<Source> &source, + const sp<Renderer> &renderer = NULL, + const sp<NativeWindowWrapper> &nativeWindow = NULL, + const sp<CCDecoder> &ccDecoder = NULL); - virtual bool supportsSeamlessFormatChange(const sp<AMessage> &to) const; - - enum { - kWhatFillThisBuffer = 'flTB', - kWhatDrainThisBuffer = 'drTB', - kWhatOutputFormatChanged = 'fmtC', - kWhatFlushCompleted = 'flsC', - kWhatShutdownCompleted = 'shDC', - kWhatEOS = 'eos ', - kWhatError = 'err ', - }; + virtual void getStats( + int64_t *mNumFramesTotal, + int64_t *mNumFramesDropped) const; protected: - virtual ~Decoder(); virtual void onMessageReceived(const sp<AMessage> &msg); + virtual void onConfigure(const sp<AMessage> &format); + virtual void onSetRenderer(const sp<Renderer> &renderer); + virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers); + virtual void onResume(bool notifyComplete); + virtual void onFlush(); + virtual void onShutdown(bool notifyComplete); + virtual void doRequestBuffers(); + private: enum { - kWhatCodecNotify = 'cdcN', - kWhatConfigure = 'conf', - kWhatGetInputBuffers = 'gInB', - kWhatInputBufferFilled = 'inpF', - kWhatRenderBuffer = 'rndr', - kWhatFlush = 'flus', - kWhatShutdown = 'shuD', - kWhatUpdateFormat = 'uFmt', + kWhatCodecNotify = 'cdcN', + kWhatRenderBuffer = 'rndr', }; - sp<AMessage> mNotify; sp<NativeWindowWrapper> mNativeWindow; + sp<Source> mSource; + sp<Renderer> mRenderer; + sp<CCDecoder> mCCDecoder; + sp<AMessage> mInputFormat; sp<AMessage> mOutputFormat; sp<MediaCodec> mCodec; sp<ALooper> mCodecLooper; - sp<ALooper> mDecoderLooper; List<sp<AMessage> > mPendingInputMessages; @@ -88,8 +72,21 @@ private: Vector<sp<ABuffer> > mCSDsToSubmit; Vector<bool> mInputBufferIsDequeued; Vector<MediaBuffer *> mMediaBuffers; + Vector<size_t> mDequeuedInputBuffers; + + int64_t mSkipRenderingUntilMediaTimeUs; + int64_t mNumFramesTotal; + int64_t mNumFramesDropped; + bool mIsAudio; + bool mIsVideoAVC; + bool mIsSecure; + bool mFormatChangePending; + bool mTimeChangePending; + + bool mPaused; + bool mResumePending; + AString mComponentName; - void handleError(int32_t err); bool handleAnInputBuffer(); bool handleAnOutputBuffer(); @@ -97,53 +94,20 @@ private: void requestCodecNotification(); bool isStaleReply(const sp<AMessage> &msg); - void onConfigure(const sp<AMessage> &format); - void onFlush(); - void onResume(); - bool onInputBufferFilled(const sp<AMessage> &msg); + void doFlush(bool notifyComplete); + status_t fetchInputData(sp<AMessage> &reply); + bool onInputBufferFetched(const sp<AMessage> &msg); void onRenderBuffer(const sp<AMessage> &msg); - void onShutdown(); - - int32_t mBufferGeneration; - bool mPaused; - AString mComponentName; + bool supportsSeamlessFormatChange(const sp<AMessage> &to) const; bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const; void rememberCodecSpecificData(const sp<AMessage> &format); + bool isDiscontinuityPending() const; + void finishHandleDiscontinuity(bool flushOnTimeChange); - DISALLOW_EVIL_CONSTRUCTORS(Decoder); -}; + void notifyResumeCompleteIfNecessary(); -struct NuPlayer::CCDecoder : public RefBase { - enum { - kWhatClosedCaptionData, - kWhatTrackAdded, - }; - - CCDecoder(const sp<AMessage> ¬ify); - - size_t getTrackCount() const; - sp<AMessage> getTrackInfo(size_t index) const; - status_t selectTrack(size_t index, bool select); - bool isSelected() const; - void decode(const sp<ABuffer> &accessUnit); - void display(int64_t timeUs); - void flush(); - -private: - sp<AMessage> mNotify; - KeyedVector<int64_t, sp<ABuffer> > mCCMap; - size_t mCurrentChannel; - int32_t mSelectedTrack; - int32_t mTrackIndices[4]; - Vector<size_t> mFoundChannels; - - bool isTrackValid(size_t index) const; - int32_t getTrackIndex(size_t channel) const; - bool extractFromSEI(const sp<ABuffer> &accessUnit); - sp<ABuffer> filterCCBuf(const sp<ABuffer> &ccBuf, size_t index); - - DISALLOW_EVIL_CONSTRUCTORS(CCDecoder); + DISALLOW_EVIL_CONSTRUCTORS(Decoder); }; } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp new file mode 100644 index 0000000..4636f0a --- /dev/null +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "NuPlayerDecoderBase" +#include <utils/Log.h> +#include <inttypes.h> + +#include "NuPlayerDecoderBase.h" + +#include "NuPlayerRenderer.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> + +namespace android { + +NuPlayer::DecoderBase::DecoderBase(const sp<AMessage> ¬ify) + : mNotify(notify), + mBufferGeneration(0), + mRequestInputBuffersPending(false) { + // Every decoder has its own looper because MediaCodec operations + // are blocking, but NuPlayer needs asynchronous operations. + mDecoderLooper = new ALooper; + mDecoderLooper->setName("NPDecoder"); + mDecoderLooper->start(false, false, ANDROID_PRIORITY_AUDIO); +} + +NuPlayer::DecoderBase::~DecoderBase() { + mDecoderLooper->unregisterHandler(id()); + mDecoderLooper->stop(); +} + +static +status_t PostAndAwaitResponse( + 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 NuPlayer::DecoderBase::configure(const sp<AMessage> &format) { + sp<AMessage> msg = new AMessage(kWhatConfigure, this); + msg->setMessage("format", format); + msg->post(); +} + +void NuPlayer::DecoderBase::init() { + mDecoderLooper->registerHandler(this); +} + +void NuPlayer::DecoderBase::setRenderer(const sp<Renderer> &renderer) { + sp<AMessage> msg = new AMessage(kWhatSetRenderer, this); + msg->setObject("renderer", renderer); + msg->post(); +} + +status_t NuPlayer::DecoderBase::getInputBuffers(Vector<sp<ABuffer> > *buffers) const { + sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, this); + msg->setPointer("buffers", buffers); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +void NuPlayer::DecoderBase::signalFlush() { + (new AMessage(kWhatFlush, this))->post(); +} + +void NuPlayer::DecoderBase::signalResume(bool notifyComplete) { + sp<AMessage> msg = new AMessage(kWhatResume, this); + msg->setInt32("notifyComplete", notifyComplete); + msg->post(); +} + +void NuPlayer::DecoderBase::initiateShutdown() { + (new AMessage(kWhatShutdown, this))->post(); +} + +void NuPlayer::DecoderBase::onRequestInputBuffers() { + if (mRequestInputBuffersPending) { + return; + } + + doRequestBuffers(); +} + +void NuPlayer::DecoderBase::scheduleRequestBuffers() { + if (mRequestInputBuffersPending) { + return; + } + mRequestInputBuffersPending = true; + sp<AMessage> msg = new AMessage(kWhatRequestInputBuffers, this); + msg->post(10 * 1000ll); +} + +void NuPlayer::DecoderBase::onMessageReceived(const sp<AMessage> &msg) { + + switch (msg->what()) { + case kWhatConfigure: + { + sp<AMessage> format; + CHECK(msg->findMessage("format", &format)); + onConfigure(format); + break; + } + + case kWhatSetRenderer: + { + sp<RefBase> obj; + CHECK(msg->findObject("renderer", &obj)); + onSetRenderer(static_cast<Renderer *>(obj.get())); + break; + } + + case kWhatGetInputBuffers: + { + sp<AReplyToken> replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + Vector<sp<ABuffer> > *dstBuffers; + CHECK(msg->findPointer("buffers", (void **)&dstBuffers)); + + onGetInputBuffers(dstBuffers); + + (new AMessage)->postReply(replyID); + break; + } + + case kWhatRequestInputBuffers: + { + mRequestInputBuffersPending = false; + onRequestInputBuffers(); + break; + } + + case kWhatFlush: + { + onFlush(); + break; + } + + case kWhatResume: + { + int32_t notifyComplete; + CHECK(msg->findInt32("notifyComplete", ¬ifyComplete)); + + onResume(notifyComplete); + break; + } + + case kWhatShutdown: + { + onShutdown(true); + break; + } + + default: + TRESPASS(); + break; + } +} + +void NuPlayer::DecoderBase::handleError(int32_t err) +{ + // We cannot immediately release the codec due to buffers still outstanding + // in the renderer. We signal to the player the error so it can shutdown/release the + // decoder after flushing and increment the generation to discard unnecessary messages. + + ++mBufferGeneration; + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +} // namespace android + diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h new file mode 100644 index 0000000..97e9269 --- /dev/null +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NUPLAYER_DECODER_BASE_H_ + +#define NUPLAYER_DECODER_BASE_H_ + +#include "NuPlayer.h" + +#include <media/stagefright/foundation/AHandler.h> + +namespace android { + +struct ABuffer; +struct MediaCodec; +struct MediaBuffer; + +struct NuPlayer::DecoderBase : public AHandler { + DecoderBase(const sp<AMessage> ¬ify); + + void configure(const sp<AMessage> &format); + void init(); + + void setRenderer(const sp<Renderer> &renderer); + + status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const; + void signalFlush(); + void signalResume(bool notifyComplete); + void initiateShutdown(); + + virtual void getStats( + int64_t *mNumFramesTotal, + int64_t *mNumFramesDropped) const = 0; + + enum { + kWhatInputDiscontinuity = 'inDi', + kWhatVideoSizeChanged = 'viSC', + kWhatFlushCompleted = 'flsC', + kWhatShutdownCompleted = 'shDC', + kWhatResumeCompleted = 'resC', + kWhatEOS = 'eos ', + kWhatError = 'err ', + }; + +protected: + + virtual ~DecoderBase(); + + virtual void onMessageReceived(const sp<AMessage> &msg); + + virtual void onConfigure(const sp<AMessage> &format) = 0; + virtual void onSetRenderer(const sp<Renderer> &renderer) = 0; + virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers) = 0; + virtual void onResume(bool notifyComplete) = 0; + virtual void onFlush() = 0; + virtual void onShutdown(bool notifyComplete) = 0; + + void onRequestInputBuffers(); + void scheduleRequestBuffers(); + virtual void doRequestBuffers() = 0; + virtual void handleError(int32_t err); + + sp<AMessage> mNotify; + int32_t mBufferGeneration; + +private: + enum { + kWhatConfigure = 'conf', + kWhatSetRenderer = 'setR', + kWhatGetInputBuffers = 'gInB', + kWhatRequestInputBuffers = 'reqB', + kWhatFlush = 'flus', + kWhatShutdown = 'shuD', + }; + + sp<ALooper> mDecoderLooper; + bool mRequestInputBuffersPending; + + DISALLOW_EVIL_CONSTRUCTORS(DecoderBase); +}; + +} // namespace android + +#endif // NUPLAYER_DECODER_BASE_H_ diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp index f7aacdd..29b4c26 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp @@ -21,79 +21,80 @@ #include "NuPlayerDecoderPassThrough.h" +#include "NuPlayerRenderer.h" +#include "NuPlayerSource.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/MediaDefs.h> #include <media/stagefright/MediaErrors.h> +#include "ATSParser.h" + namespace android { +// TODO optimize buffer size for power consumption +// The offload read buffer size is 32 KB but 24 KB uses less power. +static const size_t kAggregateBufferSizeBytes = 24 * 1024; static const size_t kMaxCachedBytes = 200000; -// The buffers will contain a bit less than kAggregateBufferSizeBytes. -// So we can start off with just enough buffers to keep the cache full. -static const size_t kMaxPendingBuffers = 1 + (kMaxCachedBytes / NuPlayer::kAggregateBufferSizeBytes); NuPlayer::DecoderPassThrough::DecoderPassThrough( - const sp<AMessage> ¬ify) - : Decoder(notify), - mNotify(notify), - mBufferGeneration(0), + const sp<AMessage> ¬ify, + const sp<Source> &source, + const sp<Renderer> &renderer) + : DecoderBase(notify), + mSource(source), + mRenderer(renderer), + mSkipRenderingUntilMediaTimeUs(-1ll), + mPaused(false), mReachedEOS(true), - mPendingBuffersToFill(0), + mPendingAudioErr(OK), mPendingBuffersToDrain(0), mCachedBytes(0), mComponentName("pass through decoder") { - mDecoderLooper = new ALooper; - mDecoderLooper->setName("NuPlayerDecoderPassThrough"); - mDecoderLooper->start(false, false, ANDROID_PRIORITY_AUDIO); + ALOGW_IF(renderer == NULL, "expect a non-NULL renderer"); } NuPlayer::DecoderPassThrough::~DecoderPassThrough() { } -void NuPlayer::DecoderPassThrough::configure(const sp<AMessage> &format) { - sp<AMessage> msg = new AMessage(kWhatConfigure, id()); - msg->setMessage("format", format); - msg->post(); -} - -void NuPlayer::DecoderPassThrough::init() { - mDecoderLooper->registerHandler(this); -} - -void NuPlayer::DecoderPassThrough::signalFlush() { - (new AMessage(kWhatFlush, id()))->post(); -} - -void NuPlayer::DecoderPassThrough::signalResume() { - (new AMessage(kWhatResume, id()))->post(); -} - -void NuPlayer::DecoderPassThrough::initiateShutdown() { - (new AMessage(kWhatShutdown, id()))->post(); -} - -bool NuPlayer::DecoderPassThrough::supportsSeamlessFormatChange( - const sp<AMessage> & /* targetFormat */) const { - return true; +void NuPlayer::DecoderPassThrough::getStats( + int64_t *numFramesTotal, int64_t *numFramesDropped) const { + *numFramesTotal = 0; + *numFramesDropped = 0; } void NuPlayer::DecoderPassThrough::onConfigure(const sp<AMessage> &format) { ALOGV("[%s] onConfigure", mComponentName.c_str()); mCachedBytes = 0; - mPendingBuffersToFill = 0; mPendingBuffersToDrain = 0; mReachedEOS = false; ++mBufferGeneration; - requestMaxBuffers(); + onRequestInputBuffers(); - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatOutputFormatChanged); - notify->setMessage("format", format); - notify->post(); + // The audio sink is already opened before the PassThrough decoder is created. + // Opening again might be relevant if decoder is instantiated after shutdown and + // format is different. + status_t err = mRenderer->openAudioSink( + format, true /* offloadOnly */, false /* hasVideo */, + AUDIO_OUTPUT_FLAG_NONE /* flags */, NULL /* isOffloaded */); + if (err != OK) { + handleError(err); + } +} + +void NuPlayer::DecoderPassThrough::onSetRenderer( + const sp<Renderer> &renderer) { + // renderer can't be changed during offloading + ALOGW_IF(renderer != mRenderer, + "ignoring request to change renderer"); +} + +void NuPlayer::DecoderPassThrough::onGetInputBuffers( + Vector<sp<ABuffer> > * /* dstBuffers */) { + ALOGE("onGetInputBuffers() called unexpectedly"); } bool NuPlayer::DecoderPassThrough::isStaleReply(const sp<AMessage> &msg) { @@ -102,100 +103,304 @@ bool NuPlayer::DecoderPassThrough::isStaleReply(const sp<AMessage> &msg) { return generation != mBufferGeneration; } -bool NuPlayer::DecoderPassThrough::requestABuffer() { - if (mCachedBytes >= kMaxCachedBytes) { - ALOGV("[%s] mCachedBytes = %zu", - mComponentName.c_str(), mCachedBytes); - return false; +bool NuPlayer::DecoderPassThrough::isDoneFetching() const { + ALOGV("[%s] mCachedBytes = %zu, mReachedEOS = %d mPaused = %d", + mComponentName.c_str(), mCachedBytes, mReachedEOS, mPaused); + + return mCachedBytes >= kMaxCachedBytes || mReachedEOS || mPaused; +} + +void NuPlayer::DecoderPassThrough::doRequestBuffers() { + status_t err = OK; + while (!isDoneFetching()) { + sp<AMessage> msg = new AMessage(); + + err = fetchInputData(msg); + if (err != OK) { + break; + } + + onInputBufferFetched(msg); } - if (mReachedEOS) { - ALOGV("[%s] reached EOS", mComponentName.c_str()); - return false; + + if (err == -EWOULDBLOCK + && mSource->feedMoreTSData() == OK) { + scheduleRequestBuffers(); } +} - sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id()); - reply->setInt32("generation", mBufferGeneration); +status_t NuPlayer::DecoderPassThrough::dequeueAccessUnit(sp<ABuffer> *accessUnit) { + status_t err; + + // Did we save an accessUnit earlier because of a discontinuity? + if (mPendingAudioAccessUnit != NULL) { + *accessUnit = mPendingAudioAccessUnit; + mPendingAudioAccessUnit.clear(); + err = mPendingAudioErr; + ALOGV("feedDecoderInputData() use mPendingAudioAccessUnit"); + } else { + err = mSource->dequeueAccessUnit(true /* audio */, accessUnit); + } - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatFillThisBuffer); - notify->setMessage("reply", reply); - notify->post(); - mPendingBuffersToFill++; - ALOGV("requestABuffer: #ToFill = %zu, #ToDrain = %zu", mPendingBuffersToFill, - mPendingBuffersToDrain); + if (err == INFO_DISCONTINUITY || err == ERROR_END_OF_STREAM) { + if (mAggregateBuffer != NULL) { + // We already have some data so save this for later. + mPendingAudioErr = err; + mPendingAudioAccessUnit = *accessUnit; + (*accessUnit).clear(); + ALOGD("return aggregated buffer and save err(=%d) for later", err); + err = OK; + } + } - return true; + return err; } -void android::NuPlayer::DecoderPassThrough::onInputBufferFilled( +sp<ABuffer> NuPlayer::DecoderPassThrough::aggregateBuffer( + const sp<ABuffer> &accessUnit) { + sp<ABuffer> aggregate; + + if (accessUnit == NULL) { + // accessUnit is saved to mPendingAudioAccessUnit + // return current mAggregateBuffer + aggregate = mAggregateBuffer; + mAggregateBuffer.clear(); + return aggregate; + } + + size_t smallSize = accessUnit->size(); + if ((mAggregateBuffer == NULL) + // Don't bother if only room for a few small buffers. + && (smallSize < (kAggregateBufferSizeBytes / 3))) { + // Create a larger buffer for combining smaller buffers from the extractor. + mAggregateBuffer = new ABuffer(kAggregateBufferSizeBytes); + mAggregateBuffer->setRange(0, 0); // start empty + } + + if (mAggregateBuffer != NULL) { + int64_t timeUs; + int64_t dummy; + bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs); + bool bigTimestampValid = mAggregateBuffer->meta()->findInt64("timeUs", &dummy); + // Will the smaller buffer fit? + size_t bigSize = mAggregateBuffer->size(); + size_t roomLeft = mAggregateBuffer->capacity() - bigSize; + // Should we save this small buffer for the next big buffer? + // If the first small buffer did not have a timestamp then save + // any buffer that does have a timestamp until the next big buffer. + if ((smallSize > roomLeft) + || (!bigTimestampValid && (bigSize > 0) && smallTimestampValid)) { + mPendingAudioErr = OK; + mPendingAudioAccessUnit = accessUnit; + aggregate = mAggregateBuffer; + mAggregateBuffer.clear(); + } else { + // Grab time from first small buffer if available. + if ((bigSize == 0) && smallTimestampValid) { + mAggregateBuffer->meta()->setInt64("timeUs", timeUs); + } + // Append small buffer to the bigger buffer. + memcpy(mAggregateBuffer->base() + bigSize, accessUnit->data(), smallSize); + bigSize += smallSize; + mAggregateBuffer->setRange(0, bigSize); + + ALOGV("feedDecoderInputData() smallSize = %zu, bigSize = %zu, capacity = %zu", + smallSize, bigSize, mAggregateBuffer->capacity()); + } + } else { + // decided not to aggregate + aggregate = accessUnit; + } + + return aggregate; +} + +status_t NuPlayer::DecoderPassThrough::fetchInputData(sp<AMessage> &reply) { + sp<ABuffer> accessUnit; + + do { + status_t err = dequeueAccessUnit(&accessUnit); + + if (err == -EWOULDBLOCK) { + return err; + } else if (err != OK) { + if (err == INFO_DISCONTINUITY) { + int32_t type; + CHECK(accessUnit->meta()->findInt32("discontinuity", &type)); + + bool formatChange = + (type & ATSParser::DISCONTINUITY_AUDIO_FORMAT) != 0; + + bool timeChange = + (type & ATSParser::DISCONTINUITY_TIME) != 0; + + ALOGI("audio discontinuity (formatChange=%d, time=%d)", + formatChange, timeChange); + + if (formatChange || timeChange) { + sp<AMessage> msg = mNotify->dup(); + msg->setInt32("what", kWhatInputDiscontinuity); + // will perform seamless format change, + // only notify NuPlayer to scan sources + msg->setInt32("formatChange", false); + msg->post(); + } + + if (timeChange) { + doFlush(false /* notifyComplete */); + err = OK; + } else if (formatChange) { + // do seamless format change + err = OK; + } else { + // This stream is unaffected by the discontinuity + return -EWOULDBLOCK; + } + } + + reply->setInt32("err", err); + return OK; + } + + accessUnit = aggregateBuffer(accessUnit); + } while (accessUnit == NULL); + +#if 0 + int64_t mediaTimeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs)); + ALOGV("feeding audio input buffer at media time %.2f secs", + mediaTimeUs / 1E6); +#endif + + reply->setBuffer("buffer", accessUnit); + + return OK; +} + +void NuPlayer::DecoderPassThrough::onInputBufferFetched( const sp<AMessage> &msg) { - --mPendingBuffersToFill; if (mReachedEOS) { return; } sp<ABuffer> buffer; - msg->findBuffer("buffer", &buffer); + bool hasBuffer = msg->findBuffer("buffer", &buffer); if (buffer == NULL) { - mReachedEOS = true; + int32_t streamErr = ERROR_END_OF_STREAM; + CHECK(msg->findInt32("err", &streamErr) || !hasBuffer); + if (streamErr == OK) { + return; + } - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatEOS); - notify->setInt32("err", ERROR_END_OF_STREAM); - notify->post(); + mReachedEOS = true; + if (mRenderer != NULL) { + mRenderer->queueEOS(true /* audio */, ERROR_END_OF_STREAM); + } return; } - mCachedBytes += buffer->size(); + sp<AMessage> extra; + if (buffer->meta()->findMessage("extra", &extra) && extra != NULL) { + int64_t resumeAtMediaTimeUs; + if (extra->findInt64( + "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) { + ALOGI("[%s] suppressing rendering until %lld us", + mComponentName.c_str(), (long long)resumeAtMediaTimeUs); + mSkipRenderingUntilMediaTimeUs = resumeAtMediaTimeUs; + } + } + + int32_t bufferSize = buffer->size(); + mCachedBytes += bufferSize; + + if (mSkipRenderingUntilMediaTimeUs >= 0) { + int64_t timeUs = 0; + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + + if (timeUs < mSkipRenderingUntilMediaTimeUs) { + ALOGV("[%s] dropping buffer at time %lld as requested.", + mComponentName.c_str(), (long long)timeUs); - sp<AMessage> reply = new AMessage(kWhatBufferConsumed, id()); + onBufferConsumed(bufferSize); + return; + } + + mSkipRenderingUntilMediaTimeUs = -1; + } + + if (mRenderer == NULL) { + onBufferConsumed(bufferSize); + return; + } + + sp<AMessage> reply = new AMessage(kWhatBufferConsumed, this); reply->setInt32("generation", mBufferGeneration); - reply->setInt32("size", buffer->size()); + reply->setInt32("size", bufferSize); + + mRenderer->queueBuffer(true /* audio */, buffer, reply); - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatDrainThisBuffer); - notify->setBuffer("buffer", buffer); - notify->setMessage("reply", reply); - notify->post(); ++mPendingBuffersToDrain; - ALOGV("onInputBufferFilled: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu", - mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes); + ALOGV("onInputBufferFilled: #ToDrain = %zu, cachedBytes = %zu", + mPendingBuffersToDrain, mCachedBytes); } void NuPlayer::DecoderPassThrough::onBufferConsumed(int32_t size) { --mPendingBuffersToDrain; mCachedBytes -= size; - ALOGV("onBufferConsumed: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu", - mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes); - requestABuffer(); + ALOGV("onBufferConsumed: #ToDrain = %zu, cachedBytes = %zu", + mPendingBuffersToDrain, mCachedBytes); + onRequestInputBuffers(); } -void NuPlayer::DecoderPassThrough::onFlush() { +void NuPlayer::DecoderPassThrough::onResume(bool notifyComplete) { + mPaused = false; + + onRequestInputBuffers(); + + if (notifyComplete) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatResumeCompleted); + notify->post(); + } +} + +void NuPlayer::DecoderPassThrough::doFlush(bool notifyComplete) { ++mBufferGeneration; + mSkipRenderingUntilMediaTimeUs = -1; + mPendingAudioAccessUnit.clear(); + mPendingAudioErr = OK; + mAggregateBuffer.clear(); + + if (mRenderer != NULL) { + mRenderer->flush(true /* audio */, notifyComplete); + mRenderer->signalTimeDiscontinuity(); + } - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatFlushCompleted); - notify->post(); - mPendingBuffersToFill = 0; mPendingBuffersToDrain = 0; mCachedBytes = 0; mReachedEOS = false; } -void NuPlayer::DecoderPassThrough::requestMaxBuffers() { - for (size_t i = 0; i < kMaxPendingBuffers; i++) { - if (!requestABuffer()) { - break; - } - } +void NuPlayer::DecoderPassThrough::onFlush() { + doFlush(true /* notifyComplete */); + + mPaused = true; + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatFlushCompleted); + notify->post(); + } -void NuPlayer::DecoderPassThrough::onShutdown() { +void NuPlayer::DecoderPassThrough::onShutdown(bool notifyComplete) { ++mBufferGeneration; + mSkipRenderingUntilMediaTimeUs = -1; + + if (notifyComplete) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatShutdownCompleted); + notify->post(); + } - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatShutdownCompleted); - notify->post(); mReachedEOS = true; } @@ -204,31 +409,6 @@ void NuPlayer::DecoderPassThrough::onMessageReceived(const sp<AMessage> &msg) { msg->debugString().c_str()); switch (msg->what()) { - case kWhatConfigure: - { - sp<AMessage> format; - CHECK(msg->findMessage("format", &format)); - onConfigure(format); - break; - } - - case kWhatRequestABuffer: - { - if (!isStaleReply(msg)) { - requestABuffer(); - } - - break; - } - - case kWhatInputBufferFilled: - { - if (!isStaleReply(msg)) { - onInputBufferFilled(msg); - } - break; - } - case kWhatBufferConsumed: { if (!isStaleReply(msg)) { @@ -239,26 +419,8 @@ void NuPlayer::DecoderPassThrough::onMessageReceived(const sp<AMessage> &msg) { break; } - case kWhatFlush: - { - onFlush(); - break; - } - - case kWhatResume: - { - requestMaxBuffers(); - break; - } - - case kWhatShutdown: - { - onShutdown(); - break; - } - default: - TRESPASS(); + DecoderBase::onMessageReceived(msg); break; } } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h index fb20257..173cfbd 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h @@ -20,21 +20,18 @@ #include "NuPlayer.h" -#include "NuPlayerDecoder.h" +#include "NuPlayerDecoderBase.h" namespace android { -struct NuPlayer::DecoderPassThrough : public Decoder { - DecoderPassThrough(const sp<AMessage> ¬ify); +struct NuPlayer::DecoderPassThrough : public DecoderBase { + DecoderPassThrough(const sp<AMessage> ¬ify, + const sp<Source> &source, + const sp<Renderer> &renderer); - virtual void configure(const sp<AMessage> &format); - virtual void init(); - - virtual void signalFlush(); - virtual void signalResume(); - virtual void initiateShutdown(); - - bool supportsSeamlessFormatChange(const sp<AMessage> &to) const; + virtual void getStats( + int64_t *mNumFramesTotal, + int64_t *mNumFramesDropped) const; protected: @@ -42,41 +39,49 @@ protected: virtual void onMessageReceived(const sp<AMessage> &msg); + virtual void onConfigure(const sp<AMessage> &format); + virtual void onSetRenderer(const sp<Renderer> &renderer); + virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers); + virtual void onResume(bool notifyComplete); + virtual void onFlush(); + virtual void onShutdown(bool notifyComplete); + virtual void doRequestBuffers(); + private: enum { - kWhatRequestABuffer = 'reqB', - kWhatConfigure = 'conf', - kWhatInputBufferFilled = 'inpF', kWhatBufferConsumed = 'bufC', - kWhatFlush = 'flus', - kWhatShutdown = 'shuD', }; - sp<AMessage> mNotify; - sp<ALooper> mDecoderLooper; + sp<Source> mSource; + sp<Renderer> mRenderer; + int64_t mSkipRenderingUntilMediaTimeUs; + bool mPaused; - /** Returns true if a buffer was requested. - * Returns false if at EOS or cache already full. - */ - bool requestABuffer(); - bool isStaleReply(const sp<AMessage> &msg); + bool mReachedEOS; - void onConfigure(const sp<AMessage> &format); - void onFlush(); - void onInputBufferFilled(const sp<AMessage> &msg); - void onBufferConsumed(int32_t size); - void requestMaxBuffers(); - void onShutdown(); + // Used by feedDecoderInputData to aggregate small buffers into + // one large buffer. + sp<ABuffer> mPendingAudioAccessUnit; + status_t mPendingAudioErr; + sp<ABuffer> mAggregateBuffer; - int32_t mBufferGeneration; - bool mReachedEOS; - // TODO mPendingBuffersToFill and mPendingBuffersToDrain are only for - // debugging. They can be removed when the power investigation is done. - size_t mPendingBuffersToFill; + // mPendingBuffersToDrain are only for debugging. It can be removed + // when the power investigation is done. size_t mPendingBuffersToDrain; size_t mCachedBytes; AString mComponentName; + bool isStaleReply(const sp<AMessage> &msg); + bool isDoneFetching() const; + + status_t dequeueAccessUnit(sp<ABuffer> *accessUnit); + sp<ABuffer> aggregateBuffer(const sp<ABuffer> &accessUnit); + status_t fetchInputData(sp<AMessage> &reply); + void doFlush(bool notifyComplete); + + void onInputBufferFetched(const sp<AMessage> &msg); + void onBufferConsumed(int32_t size); + DISALLOW_EVIL_CONSTRUCTORS(DecoderPassThrough); }; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 2b1c559..5887e50 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -310,6 +310,13 @@ status_t NuPlayerDriver::stop() { } status_t NuPlayerDriver::pause() { + // The NuPlayerRenderer may get flushed if pause for long enough, e.g. the pause timeout tear + // down for audio offload mode. If that happens, the NuPlayerRenderer will no longer know the + // current position. So similar to seekTo, update |mPositionUs| to the pause position by calling + // getCurrentPosition here. + int msec; + getCurrentPosition(&msec); + Mutex::Autolock autoLock(mLock); switch (mState) { @@ -334,6 +341,11 @@ bool NuPlayerDriver::isPlaying() { return mState == STATE_RUNNING && !mAtEOS; } +status_t NuPlayerDriver::setPlaybackRate(float rate) { + mPlayer->setPlaybackRate(rate); + return OK; +} + status_t NuPlayerDriver::seekTo(int msec) { ALOGD("seekTo(%p) %d ms", this, msec); Mutex::Autolock autoLock(mLock); @@ -381,13 +393,22 @@ status_t NuPlayerDriver::seekTo(int msec) { status_t NuPlayerDriver::getCurrentPosition(int *msec) { int64_t tempUs = 0; + { + Mutex::Autolock autoLock(mLock); + if (mSeekInProgress || mState == STATE_PAUSED) { + tempUs = (mPositionUs <= 0) ? 0 : mPositionUs; + *msec = (int)divRound(tempUs, (int64_t)(1000)); + return OK; + } + } + status_t ret = mPlayer->getCurrentPosition(&tempUs); Mutex::Autolock autoLock(mLock); // We need to check mSeekInProgress here because mPlayer->seekToAsync is an async call, which // means getCurrentPosition can be called before seek is completed. Iow, renderer may return a // position value that's different the seek to position. - if (ret != OK || mSeekInProgress) { + if (ret != OK) { tempUs = (mPositionUs <= 0) ? 0 : mPositionUs; } else { mPositionUs = tempUs; @@ -488,13 +509,16 @@ status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) { case INVOKE_ID_SELECT_TRACK: { int trackIndex = request.readInt32(); - return mPlayer->selectTrack(trackIndex, true /* select */); + int msec = 0; + // getCurrentPosition should always return OK + getCurrentPosition(&msec); + return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll); } case INVOKE_ID_UNSELECT_TRACK: { int trackIndex = request.readInt32(); - return mPlayer->selectTrack(trackIndex, false /* select */); + return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */); } case INVOKE_ID_GET_SELECTED_TRACK: @@ -633,9 +657,23 @@ void NuPlayerDriver::notifyListener_l( case MEDIA_PLAYBACK_COMPLETE: { if (mState != STATE_RESET_IN_PROGRESS) { - if (mLooping || (mAutoLoop - && (mAudioSink == NULL || mAudioSink->realtime()))) { + if (mAutoLoop) { + audio_stream_type_t streamType = AUDIO_STREAM_MUSIC; + if (mAudioSink != NULL) { + streamType = mAudioSink->getAudioStreamType(); + } + if (streamType == AUDIO_STREAM_NOTIFICATION) { + ALOGW("disabling auto-loop for notification"); + mAutoLoop = false; + } + } + if (mLooping || mAutoLoop) { mPlayer->seekToAsync(0); + if (mAudioSink != NULL) { + // The renderer has stopped the sink at the end in order to play out + // the last little bit of audio. If we're looping, we need to restart it. + mAudioSink->start(); + } break; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h index 5cba7d9..e53abcd 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h @@ -47,6 +47,7 @@ struct NuPlayerDriver : public MediaPlayerInterface { virtual status_t stop(); virtual status_t pause(); virtual bool isPlaying(); + virtual status_t setPlaybackRate(float rate); virtual status_t seekTo(int msec); virtual status_t getCurrentPosition(int *msec); virtual status_t getDuration(int *msec); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 5d9001c..a2ec51c 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -20,12 +20,12 @@ #include "NuPlayerRenderer.h" -#include <cutils/properties.h> - #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/foundation/AUtils.h> +#include <media/stagefright/foundation/AWakeLock.h> +#include <media/stagefright/MediaClock.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/Utils.h> @@ -41,17 +41,16 @@ namespace android { static const int64_t kOffloadPauseMaxUs = 10000000ll; // static -const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll; - -static bool sFrameAccurateAVsync = false; +const NuPlayer::Renderer::PcmInfo NuPlayer::Renderer::AUDIO_PCMINFO_INITIALIZER = { + AUDIO_CHANNEL_NONE, + AUDIO_OUTPUT_FLAG_NONE, + AUDIO_FORMAT_INVALID, + 0, // mNumChannels + 0 // mSampleRate +}; -static void readProperties() { - char value[PROPERTY_VALUE_MAX]; - if (property_get("persist.sys.media.avsync", value, NULL)) { - sFrameAccurateAVsync = - !strcmp("1", value) || !strcasecmp("true", value); - } -} +// static +const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll; NuPlayer::Renderer::Renderer( const sp<MediaPlayerBase::AudioSink> &sink, @@ -65,17 +64,17 @@ NuPlayer::Renderer::Renderer( mDrainVideoQueuePending(false), mAudioQueueGeneration(0), mVideoQueueGeneration(0), + mAudioDrainGeneration(0), + mVideoDrainGeneration(0), + mPlaybackRate(1.0), mAudioFirstAnchorTimeMediaUs(-1), mAnchorTimeMediaUs(-1), - mAnchorTimeRealUs(-1), mAnchorNumFramesWritten(-1), - mAnchorMaxMediaUs(-1), mVideoLateByUs(0ll), mHasAudio(false), mHasVideo(false), - mPauseStartedTimeRealUs(-1), - mFlushingAudio(false), - mFlushingVideo(false), + mNotifyCompleteAudio(false), + mNotifyCompleteVideo(false), mSyncQueues(false), mPaused(false), mVideoSampleReceived(false), @@ -85,9 +84,11 @@ NuPlayer::Renderer::Renderer( mAudioOffloadPauseTimeoutGeneration(0), mAudioOffloadTornDown(false), mCurrentOffloadInfo(AUDIO_INFO_INITIALIZER), + mCurrentPcmInfo(AUDIO_PCMINFO_INITIALIZER), mTotalBuffersQueued(0), - mLastAudioBufferDrained(0) { - readProperties(); + mLastAudioBufferDrained(0), + mWakeLock(new AWakeLock()) { + mMediaClock = new MediaClock; } NuPlayer::Renderer::~Renderer() { @@ -102,7 +103,8 @@ void NuPlayer::Renderer::queueBuffer( bool audio, const sp<ABuffer> &buffer, const sp<AMessage> ¬ifyConsumed) { - sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id()); + sp<AMessage> msg = new AMessage(kWhatQueueBuffer, this); + msg->setInt32("queueGeneration", getQueueGeneration(audio)); msg->setInt32("audio", static_cast<int32_t>(audio)); msg->setBuffer("buffer", buffer); msg->setMessage("notifyConsumed", notifyConsumed); @@ -112,155 +114,108 @@ void NuPlayer::Renderer::queueBuffer( void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) { CHECK_NE(finalResult, (status_t)OK); - sp<AMessage> msg = new AMessage(kWhatQueueEOS, id()); + sp<AMessage> msg = new AMessage(kWhatQueueEOS, this); + msg->setInt32("queueGeneration", getQueueGeneration(audio)); msg->setInt32("audio", static_cast<int32_t>(audio)); msg->setInt32("finalResult", finalResult); msg->post(); } -void NuPlayer::Renderer::flush(bool audio) { +void NuPlayer::Renderer::setPlaybackRate(float rate) { + sp<AMessage> msg = new AMessage(kWhatSetRate, this); + msg->setFloat("rate", rate); + msg->post(); +} + +void NuPlayer::Renderer::flush(bool audio, bool notifyComplete) { { - Mutex::Autolock autoLock(mFlushLock); + Mutex::Autolock autoLock(mLock); if (audio) { - if (mFlushingAudio) { - return; - } - mFlushingAudio = true; + mNotifyCompleteAudio |= notifyComplete; + ++mAudioQueueGeneration; + ++mAudioDrainGeneration; } else { - if (mFlushingVideo) { - return; - } - mFlushingVideo = true; + mNotifyCompleteVideo |= notifyComplete; + ++mVideoQueueGeneration; + ++mVideoDrainGeneration; } + + clearAnchorTime_l(); + clearAudioFirstAnchorTime_l(); + mVideoLateByUs = 0; + mSyncQueues = false; } - sp<AMessage> msg = new AMessage(kWhatFlush, id()); + sp<AMessage> msg = new AMessage(kWhatFlush, this); msg->setInt32("audio", static_cast<int32_t>(audio)); msg->post(); } void NuPlayer::Renderer::signalTimeDiscontinuity() { - Mutex::Autolock autoLock(mLock); - // CHECK(mAudioQueue.empty()); - // CHECK(mVideoQueue.empty()); - setAudioFirstAnchorTime(-1); - setAnchorTime(-1, -1); - setVideoLateByUs(0); - mSyncQueues = false; } -void NuPlayer::Renderer::signalAudioSinkChanged() { - (new AMessage(kWhatAudioSinkChanged, id()))->post(); +void NuPlayer::Renderer::signalDisableOffloadAudio() { + (new AMessage(kWhatDisableOffloadAudio, this))->post(); } -void NuPlayer::Renderer::signalDisableOffloadAudio() { - (new AMessage(kWhatDisableOffloadAudio, id()))->post(); +void NuPlayer::Renderer::signalEnableOffloadAudio() { + (new AMessage(kWhatEnableOffloadAudio, this))->post(); } void NuPlayer::Renderer::pause() { - (new AMessage(kWhatPause, id()))->post(); + (new AMessage(kWhatPause, this))->post(); } void NuPlayer::Renderer::resume() { - (new AMessage(kWhatResume, id()))->post(); + (new AMessage(kWhatResume, this))->post(); } void NuPlayer::Renderer::setVideoFrameRate(float fps) { - sp<AMessage> msg = new AMessage(kWhatSetVideoFrameRate, id()); + sp<AMessage> msg = new AMessage(kWhatSetVideoFrameRate, this); msg->setFloat("frame-rate", fps); msg->post(); } +// Called on any threads. status_t NuPlayer::Renderer::getCurrentPosition(int64_t *mediaUs) { - return getCurrentPosition(mediaUs, ALooper::GetNowUs()); -} - -status_t NuPlayer::Renderer::getCurrentPosition( - int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo) { - Mutex::Autolock autoLock(mTimeLock); - if (!mHasAudio && !mHasVideo) { - return NO_INIT; - } - - if (mAnchorTimeMediaUs < 0) { - return NO_INIT; - } - - int64_t positionUs = (nowUs - mAnchorTimeRealUs) + mAnchorTimeMediaUs; - - if (mPauseStartedTimeRealUs != -1) { - positionUs -= (nowUs - mPauseStartedTimeRealUs); - } - - // limit position to the last queued media time (for video only stream - // position will be discrete as we don't know how long each frame lasts) - if (mAnchorMaxMediaUs >= 0 && !allowPastQueuedVideo) { - if (positionUs > mAnchorMaxMediaUs) { - positionUs = mAnchorMaxMediaUs; - } - } - - if (positionUs < mAudioFirstAnchorTimeMediaUs) { - positionUs = mAudioFirstAnchorTimeMediaUs; - } - - *mediaUs = (positionUs <= 0) ? 0 : positionUs; - return OK; -} - -void NuPlayer::Renderer::setHasMedia(bool audio) { - Mutex::Autolock autoLock(mTimeLock); - if (audio) { - mHasAudio = true; - } else { - mHasVideo = true; - } + return mMediaClock->getMediaTime(ALooper::GetNowUs(), mediaUs); } -void NuPlayer::Renderer::setAudioFirstAnchorTime(int64_t mediaUs) { - Mutex::Autolock autoLock(mTimeLock); - mAudioFirstAnchorTimeMediaUs = mediaUs; +void NuPlayer::Renderer::clearAudioFirstAnchorTime_l() { + mAudioFirstAnchorTimeMediaUs = -1; + mMediaClock->setStartingTimeMedia(-1); } -void NuPlayer::Renderer::setAudioFirstAnchorTimeIfNeeded(int64_t mediaUs) { - Mutex::Autolock autoLock(mTimeLock); +void NuPlayer::Renderer::setAudioFirstAnchorTimeIfNeeded_l(int64_t mediaUs) { if (mAudioFirstAnchorTimeMediaUs == -1) { mAudioFirstAnchorTimeMediaUs = mediaUs; + mMediaClock->setStartingTimeMedia(mediaUs); } } -void NuPlayer::Renderer::setAnchorTime( - int64_t mediaUs, int64_t realUs, int64_t numFramesWritten, bool resume) { - Mutex::Autolock autoLock(mTimeLock); - mAnchorTimeMediaUs = mediaUs; - mAnchorTimeRealUs = realUs; - mAnchorNumFramesWritten = numFramesWritten; - if (resume) { - mPauseStartedTimeRealUs = -1; - } +void NuPlayer::Renderer::clearAnchorTime_l() { + mMediaClock->clearAnchor(); + mAnchorTimeMediaUs = -1; + mAnchorNumFramesWritten = -1; } void NuPlayer::Renderer::setVideoLateByUs(int64_t lateUs) { - Mutex::Autolock autoLock(mTimeLock); + Mutex::Autolock autoLock(mLock); mVideoLateByUs = lateUs; } int64_t NuPlayer::Renderer::getVideoLateByUs() { - Mutex::Autolock autoLock(mTimeLock); + Mutex::Autolock autoLock(mLock); return mVideoLateByUs; } -void NuPlayer::Renderer::setPauseStartedTimeRealUs(int64_t realUs) { - Mutex::Autolock autoLock(mTimeLock); - mPauseStartedTimeRealUs = realUs; -} - -bool NuPlayer::Renderer::openAudioSink( +status_t NuPlayer::Renderer::openAudioSink( const sp<AMessage> &format, bool offloadOnly, bool hasVideo, - uint32_t flags) { - sp<AMessage> msg = new AMessage(kWhatOpenAudioSink, id()); + uint32_t flags, + bool *isOffloaded) { + sp<AMessage> msg = new AMessage(kWhatOpenAudioSink, this); msg->setMessage("format", format); msg->setInt32("offload-only", offloadOnly); msg->setInt32("has-video", hasVideo); @@ -269,13 +224,19 @@ bool NuPlayer::Renderer::openAudioSink( sp<AMessage> response; msg->postAndAwaitResponse(&response); - int32_t offload; - CHECK(response->findInt32("offload", &offload)); - return (offload != 0); + int32_t err; + if (!response->findInt32("err", &err)) { + err = INVALID_OPERATION; + } else if (err == OK && isOffloaded != NULL) { + int32_t offload; + CHECK(response->findInt32("offload", &offload)); + *isOffloaded = (offload != 0); + } + return err; } void NuPlayer::Renderer::closeAudioSink() { - sp<AMessage> msg = new AMessage(kWhatCloseAudioSink, id()); + sp<AMessage> msg = new AMessage(kWhatCloseAudioSink, this); sp<AMessage> response; msg->postAndAwaitResponse(&response); @@ -297,12 +258,13 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { uint32_t flags; CHECK(msg->findInt32("flags", (int32_t *)&flags)); - bool offload = onOpenAudioSink(format, offloadOnly, hasVideo, flags); + status_t err = onOpenAudioSink(format, offloadOnly, hasVideo, flags); sp<AMessage> response = new AMessage; - response->setInt32("offload", offload); + response->setInt32("err", err); + response->setInt32("offload", offloadingAudio()); - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); response->postReply(replyID); @@ -311,7 +273,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { case kWhatCloseAudioSink: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); onCloseAudioSink(); @@ -330,8 +292,8 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { case kWhatDrainAudioQueue: { int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - if (generation != mAudioQueueGeneration) { + CHECK(msg->findInt32("drainGeneration", &generation)); + if (generation != getDrainGeneration(true /* audio */)) { break; } @@ -353,9 +315,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { // Let's give it more data after about half that time // has elapsed. - // kWhatDrainAudioQueue is used for non-offloading mode, - // and mLock is used only for offloading mode. Therefore, - // no need to acquire mLock here. + Mutex::Autolock autoLock(mLock); postDrainAudioQueue_l(delayUs / 2); } break; @@ -364,8 +324,8 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { case kWhatDrainVideoQueue: { int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - if (generation != mVideoQueueGeneration) { + CHECK(msg->findInt32("drainGeneration", &generation)); + if (generation != getDrainGeneration(false /* audio */)) { break; } @@ -380,8 +340,8 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { case kWhatPostDrainVideoQueue: { int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - if (generation != mVideoQueueGeneration) { + CHECK(msg->findInt32("drainGeneration", &generation)); + if (generation != getDrainGeneration(false /* audio */)) { break; } @@ -402,15 +362,19 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { break; } - case kWhatFlush: + case kWhatSetRate: { - onFlush(msg); + CHECK(msg->findFloat("rate", &mPlaybackRate)); + int32_t ratePermille = (int32_t)(0.5f + 1000 * mPlaybackRate); + mPlaybackRate = ratePermille / 1000.0f; + mMediaClock->setPlaybackRate(mPlaybackRate); + mAudioSink->setPlaybackRatePermille(ratePermille); break; } - case kWhatAudioSinkChanged: + case kWhatFlush: { - onAudioSinkChanged(); + onFlush(msg); break; } @@ -420,6 +384,12 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatEnableOffloadAudio: + { + onEnableOffloadAudio(); + break; + } + case kWhatPause: { onPause(); @@ -449,12 +419,13 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { case kWhatAudioOffloadPauseTimeout: { int32_t generation; - CHECK(msg->findInt32("generation", &generation)); + CHECK(msg->findInt32("drainGeneration", &generation)); if (generation != mAudioOffloadPauseTimeoutGeneration) { break; } ALOGV("Audio Offload tear down due to pause timeout."); onAudioOffloadTearDown(kDueToTimeout); + mWakeLock->release(); break; } @@ -475,19 +446,19 @@ void NuPlayer::Renderer::postDrainAudioQueue_l(int64_t delayUs) { } mDrainAudioQueuePending = true; - sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id()); - msg->setInt32("generation", mAudioQueueGeneration); + sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, this); + msg->setInt32("drainGeneration", mAudioDrainGeneration); msg->post(delayUs); } -void NuPlayer::Renderer::prepareForMediaRenderingStart() { - mAudioRenderingStartGeneration = mAudioQueueGeneration; - mVideoRenderingStartGeneration = mVideoQueueGeneration; +void NuPlayer::Renderer::prepareForMediaRenderingStart_l() { + mAudioRenderingStartGeneration = mAudioDrainGeneration; + mVideoRenderingStartGeneration = mVideoDrainGeneration; } -void NuPlayer::Renderer::notifyIfMediaRenderingStarted() { - if (mVideoRenderingStartGeneration == mVideoQueueGeneration && - mAudioRenderingStartGeneration == mAudioQueueGeneration) { +void NuPlayer::Renderer::notifyIfMediaRenderingStarted_l() { + if (mVideoRenderingStartGeneration == mVideoDrainGeneration && + mAudioRenderingStartGeneration == mAudioDrainGeneration) { mVideoRenderingStartGeneration = -1; mAudioRenderingStartGeneration = -1; @@ -555,7 +526,7 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { int64_t mediaTimeUs; CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6); - setAudioFirstAnchorTimeIfNeeded(mediaTimeUs); + setAudioFirstAnchorTimeIfNeeded_l(mediaTimeUs); } size_t copy = entry->mBuffer->size() - entry->mOffset; @@ -575,34 +546,45 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { entry = NULL; } sizeCopied += copy; - notifyIfMediaRenderingStarted(); + + notifyIfMediaRenderingStarted_l(); } if (mAudioFirstAnchorTimeMediaUs >= 0) { int64_t nowUs = ALooper::GetNowUs(); - setAnchorTime(mAudioFirstAnchorTimeMediaUs, nowUs - getPlayedOutAudioDurationUs(nowUs)); + int64_t nowMediaUs = + mAudioFirstAnchorTimeMediaUs + getPlayedOutAudioDurationUs(nowUs); + // we don't know how much data we are queueing for offloaded tracks. + mMediaClock->updateAnchor(nowMediaUs, nowUs, INT64_MAX); } - // we don't know how much data we are queueing for offloaded tracks - mAnchorMaxMediaUs = -1; - if (hasEOS) { - (new AMessage(kWhatStopAudioSink, id()))->post(); + (new AMessage(kWhatStopAudioSink, this))->post(); } return sizeCopied; } bool NuPlayer::Renderer::onDrainAudioQueue() { + // TODO: This call to getPosition checks if AudioTrack has been created + // in AudioSink before draining audio. If AudioTrack doesn't exist, then + // CHECKs on getPosition will fail. + // We still need to figure out why AudioTrack is not created when + // this function is called. One possible reason could be leftover + // audio. Another possible place is to check whether decoder + // has received INFO_FORMAT_CHANGED as the first buffer since + // AudioSink is opened there, and possible interactions with flush + // immediately after start. Investigate error message + // "vorbis_dsp_synthesis returned -135", along with RTSP. uint32_t numFramesPlayed; if (mAudioSink->getPosition(&numFramesPlayed) != OK) { return false; } +#if 0 ssize_t numFramesAvailableToWrite = mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed); -#if 0 if (numFramesAvailableToWrite == mAudioSink->frameCount()) { ALOGI("audio sink underrun"); } else { @@ -611,10 +593,7 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { } #endif - size_t numBytesAvailableToWrite = - numFramesAvailableToWrite * mAudioSink->frameSize(); - - while (numBytesAvailableToWrite > 0 && !mAudioQueue.empty()) { + while (!mAudioQueue.empty()) { QueueEntry *entry = &*mAudioQueue.begin(); mLastAudioBufferDrained = entry->mBufferOrdinal; @@ -629,6 +608,13 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { mAudioQueue.erase(mAudioQueue.begin()); entry = NULL; + if (mAudioSink->needsTrailingPadding()) { + // If we're not in gapless playback (i.e. through setNextPlayer), we + // need to stop the track here, because that will play out the last + // little bit at the end of the file. Otherwise short files won't play. + mAudioSink->stop(); + mNumFramesWritten = 0; + } return false; } @@ -640,14 +626,17 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { } size_t copy = entry->mBuffer->size() - entry->mOffset; - if (copy > numBytesAvailableToWrite) { - copy = numBytesAvailableToWrite; - } - ssize_t written = mAudioSink->write(entry->mBuffer->data() + entry->mOffset, copy); + ssize_t written = mAudioSink->write(entry->mBuffer->data() + entry->mOffset, + copy, false /* blocking */); if (written < 0) { - // An error in AudioSink write is fatal here. - LOG_ALWAYS_FATAL("AudioSink write error(%zd) when writing %zu bytes", written, copy); + // An error in AudioSink write. Perhaps the AudioSink was not properly opened. + if (written == WOULD_BLOCK) { + ALOGW("AudioSink write would block when writing %zu bytes", copy); + } else { + ALOGE("AudioSink write error(%zd) when writing %zu bytes", written, copy); + } + break; } entry->mOffset += written; @@ -658,72 +647,92 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { entry = NULL; } - numBytesAvailableToWrite -= written; size_t copiedFrames = written / mAudioSink->frameSize(); mNumFramesWritten += copiedFrames; - notifyIfMediaRenderingStarted(); + { + Mutex::Autolock autoLock(mLock); + notifyIfMediaRenderingStarted_l(); + } if (written != (ssize_t)copy) { // A short count was received from AudioSink::write() // - // AudioSink write should block until exactly the number of bytes are delivered. - // But it may return with a short count (without an error) when: + // AudioSink write is called in non-blocking mode. + // It may return with a short count when: // // 1) Size to be copied is not a multiple of the frame size. We consider this fatal. - // 2) AudioSink is an AudioCache for data retrieval, and the AudioCache is exceeded. + // 2) The data to be copied exceeds the available buffer in AudioSink. + // 3) An error occurs and data has been partially copied to the buffer in AudioSink. + // 4) AudioSink is an AudioCache for data retrieval, and the AudioCache is exceeded. // (Case 1) // Must be a multiple of the frame size. If it is not a multiple of a frame size, it // needs to fail, as we should not carry over fractional frames between calls. CHECK_EQ(copy % mAudioSink->frameSize(), 0); - // (Case 2) + // (Case 2, 3, 4) // Return early to the caller. // Beware of calling immediately again as this may busy-loop if you are not careful. - ALOGW("AudioSink write short frame count %zd < %zu", written, copy); + ALOGV("AudioSink write short frame count %zd < %zu", written, copy); break; } } - mAnchorMaxMediaUs = - mAnchorTimeMediaUs + - (int64_t)(max((long long)mNumFramesWritten - mAnchorNumFramesWritten, 0LL) - * 1000LL * mAudioSink->msecsPerFrame()); + int64_t maxTimeMedia; + { + Mutex::Autolock autoLock(mLock); + maxTimeMedia = + mAnchorTimeMediaUs + + (int64_t)(max((long long)mNumFramesWritten - mAnchorNumFramesWritten, 0LL) + * 1000LL * mAudioSink->msecsPerFrame()); + } + mMediaClock->updateMaxTimeMedia(maxTimeMedia); return !mAudioQueue.empty(); } +int64_t NuPlayer::Renderer::getDurationUsIfPlayedAtSampleRate(uint32_t numFrames) { + int32_t sampleRate = offloadingAudio() ? + mCurrentOffloadInfo.sample_rate : mCurrentPcmInfo.mSampleRate; + // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours. + return (int64_t)((int32_t)numFrames * 1000000LL / sampleRate); +} + +// Calculate duration of pending samples if played at normal rate (i.e., 1.0). int64_t NuPlayer::Renderer::getPendingAudioPlayoutDurationUs(int64_t nowUs) { - int64_t writtenAudioDurationUs = - mNumFramesWritten * 1000LL * mAudioSink->msecsPerFrame(); + int64_t writtenAudioDurationUs = getDurationUsIfPlayedAtSampleRate(mNumFramesWritten); return writtenAudioDurationUs - getPlayedOutAudioDurationUs(nowUs); } int64_t NuPlayer::Renderer::getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs) { - int64_t currentPositionUs; - if (getCurrentPosition(¤tPositionUs, nowUs, true /* allowPastQueuedVideo */) != OK) { - // If failed to get current position, e.g. due to audio clock is not ready, then just - // play out video immediately without delay. + int64_t realUs; + if (mMediaClock->getRealTimeFor(mediaTimeUs, &realUs) != OK) { + // If failed to get current position, e.g. due to audio clock is + // not ready, then just play out video immediately without delay. return nowUs; } - return (mediaTimeUs - currentPositionUs) + nowUs; + return realUs; } void NuPlayer::Renderer::onNewAudioMediaTime(int64_t mediaTimeUs) { + Mutex::Autolock autoLock(mLock); // TRICKY: vorbis decoder generates multiple frames with the same // timestamp, so only update on the first frame with a given timestamp if (mediaTimeUs == mAnchorTimeMediaUs) { return; } - setAudioFirstAnchorTimeIfNeeded(mediaTimeUs); + setAudioFirstAnchorTimeIfNeeded_l(mediaTimeUs); int64_t nowUs = ALooper::GetNowUs(); - setAnchorTime( - mediaTimeUs, nowUs + getPendingAudioPlayoutDurationUs(nowUs), mNumFramesWritten); + int64_t nowMediaUs = mediaTimeUs - getPendingAudioPlayoutDurationUs(nowUs); + mMediaClock->updateAnchor(nowMediaUs, nowUs, mediaTimeUs); + mAnchorNumFramesWritten = mNumFramesWritten; + mAnchorTimeMediaUs = mediaTimeUs; } +// Called without mLock acquired. void NuPlayer::Renderer::postDrainVideoQueue() { if (mDrainVideoQueuePending - || mSyncQueues + || getSyncQueues() || (mPaused && mVideoSampleReceived)) { return; } @@ -734,8 +743,8 @@ void NuPlayer::Renderer::postDrainVideoQueue() { QueueEntry &entry = *mVideoQueue.begin(); - sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id()); - msg->setInt32("generation", mVideoQueueGeneration); + sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, this); + msg->setInt32("drainGeneration", getDrainGeneration(false /* audio */)); if (entry.mBuffer == NULL) { // EOS doesn't carry a timestamp. @@ -755,14 +764,19 @@ void NuPlayer::Renderer::postDrainVideoQueue() { int64_t mediaTimeUs; CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); - if (mAnchorTimeMediaUs < 0) { - setAnchorTime(mediaTimeUs, nowUs); - realTimeUs = nowUs; - } else { - realTimeUs = getRealTimeUs(mediaTimeUs, nowUs); + { + Mutex::Autolock autoLock(mLock); + if (mAnchorTimeMediaUs < 0) { + mMediaClock->updateAnchor(mediaTimeUs, nowUs, mediaTimeUs); + mAnchorTimeMediaUs = mediaTimeUs; + realTimeUs = nowUs; + } else { + realTimeUs = getRealTimeUs(mediaTimeUs, nowUs); + } } if (!mHasAudio) { - mAnchorMaxMediaUs = mediaTimeUs + 100000; // smooth out videos >= 10fps + // smooth out videos >= 10fps + mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000); } // Heuristics to handle situation when media time changed without a @@ -792,11 +806,6 @@ void NuPlayer::Renderer::postDrainVideoQueue() { ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs); // post 2 display refreshes before rendering is due - // FIXME currently this increases power consumption, so unless frame-accurate - // AV sync is requested, post closer to required render time (at 0.63 vsyncs) - if (!sFrameAccurateAVsync) { - twoVsyncsUs >>= 4; - } msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0); mDrainVideoQueuePending = true; @@ -846,16 +855,19 @@ void NuPlayer::Renderer::onDrainVideoQueue() { ALOGV("video late by %lld us (%.2f secs)", mVideoLateByUs, mVideoLateByUs / 1E6); } else { + int64_t mediaUs = 0; + mMediaClock->getMediaTime(realTimeUs, &mediaUs); ALOGV("rendering video at media time %.2f secs", (mFlags & FLAG_REAL_TIME ? realTimeUs : - (realTimeUs + mAnchorTimeMediaUs - mAnchorTimeRealUs)) / 1E6); + mediaUs) / 1E6); } } else { setVideoLateByUs(0); if (!mVideoSampleReceived && !mHasAudio) { // This will ensure that the first frame after a flush won't be used as anchor // when renderer is in paused state, because resume can happen any time after seek. - setAnchorTime(-1, -1); + Mutex::Autolock autoLock(mLock); + clearAnchorTime_l(); } } @@ -872,7 +884,8 @@ void NuPlayer::Renderer::onDrainVideoQueue() { mVideoRenderingStarted = true; notifyVideoRenderingStart(); } - notifyIfMediaRenderingStarted(); + Mutex::Autolock autoLock(mLock); + notifyIfMediaRenderingStarted_l(); } } @@ -891,14 +904,22 @@ void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult, int64_t del } void NuPlayer::Renderer::notifyAudioOffloadTearDown() { - (new AMessage(kWhatAudioOffloadTearDown, id()))->post(); + (new AMessage(kWhatAudioOffloadTearDown, this))->post(); } void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) { int32_t audio; CHECK(msg->findInt32("audio", &audio)); - setHasMedia(audio); + if (dropBufferIfStale(audio, msg)) { + return; + } + + if (audio) { + mHasAudio = true; + } else { + mHasVideo = true; + } if (mHasVideo) { if (mVideoScheduler == NULL) { @@ -907,10 +928,6 @@ void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) { } } - if (dropBufferWhileFlushing(audio, msg)) { - return; - } - sp<ABuffer> buffer; CHECK(msg->findBuffer("buffer", &buffer)); @@ -982,7 +999,9 @@ void NuPlayer::Renderer::syncQueuesDone_l() { } if (!mVideoQueue.empty()) { + mLock.unlock(); postDrainVideoQueue(); + mLock.lock(); } } @@ -990,7 +1009,7 @@ void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) { int32_t audio; CHECK(msg->findInt32("audio", &audio)); - if (dropBufferWhileFlushing(audio, msg)) { + if (dropBufferIfStale(audio, msg)) { return; } @@ -1009,7 +1028,7 @@ void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) { mAudioQueue.push_back(entry); postDrainAudioQueue_l(); } else { - if (mVideoQueue.empty() && mSyncQueues) { + if (mVideoQueue.empty() && getSyncQueues()) { Mutex::Autolock autoLock(mLock); syncQueuesDone_l(); } @@ -1019,30 +1038,29 @@ void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) { } void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) { - int32_t audio; + int32_t audio, notifyComplete; CHECK(msg->findInt32("audio", &audio)); { - Mutex::Autolock autoLock(mFlushLock); + Mutex::Autolock autoLock(mLock); if (audio) { - mFlushingAudio = false; + notifyComplete = mNotifyCompleteAudio; + mNotifyCompleteAudio = false; } else { - mFlushingVideo = false; + notifyComplete = mNotifyCompleteVideo; + mNotifyCompleteVideo = false; } - } - // If we're currently syncing the queues, i.e. dropping audio while - // aligning the first audio/video buffer times and only one of the - // two queues has data, we may starve that queue by not requesting - // more buffers from the decoder. If the other source then encounters - // a discontinuity that leads to flushing, we'll never find the - // corresponding discontinuity on the other queue. - // Therefore we'll stop syncing the queues if at least one of them - // is flushed. - { - Mutex::Autolock autoLock(mLock); - syncQueuesDone_l(); - setPauseStartedTimeRealUs(-1); + // If we're currently syncing the queues, i.e. dropping audio while + // aligning the first audio/video buffer times and only one of the + // two queues has data, we may starve that queue by not requesting + // more buffers from the decoder. If the other source then encounters + // a discontinuity that leads to flushing, we'll never find the + // corresponding discontinuity on the other queue. + // Therefore we'll stop syncing the queues if at least one of them + // is flushed. + syncQueuesDone_l(); + clearAnchorTime_l(); } ALOGV("flushing %s", audio ? "audio" : "video"); @@ -1051,11 +1069,11 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) { Mutex::Autolock autoLock(mLock); flushQueue(&mAudioQueue); - ++mAudioQueueGeneration; - prepareForMediaRenderingStart(); + ++mAudioDrainGeneration; + prepareForMediaRenderingStart_l(); if (offloadingAudio()) { - setAudioFirstAnchorTime(-1); + clearAudioFirstAnchorTime_l(); } } @@ -1070,17 +1088,21 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) { flushQueue(&mVideoQueue); mDrainVideoQueuePending = false; - ++mVideoQueueGeneration; if (mVideoScheduler != NULL) { mVideoScheduler->restart(); } - prepareForMediaRenderingStart(); + Mutex::Autolock autoLock(mLock); + ++mVideoDrainGeneration; + prepareForMediaRenderingStart_l(); } mVideoSampleReceived = false; - notifyFlushComplete(audio); + + if (notifyComplete) { + notifyFlushComplete(audio); + } } void NuPlayer::Renderer::flushQueue(List<QueueEntry> *queue) { @@ -1103,20 +1125,12 @@ void NuPlayer::Renderer::notifyFlushComplete(bool audio) { notify->post(); } -bool NuPlayer::Renderer::dropBufferWhileFlushing( +bool NuPlayer::Renderer::dropBufferIfStale( bool audio, const sp<AMessage> &msg) { - bool flushing = false; - - { - Mutex::Autolock autoLock(mFlushLock); - if (audio) { - flushing = mFlushingAudio; - } else { - flushing = mFlushingVideo; - } - } + int32_t queueGeneration; + CHECK(msg->findInt32("queueGeneration", &queueGeneration)); - if (!flushing) { + if (queueGeneration == getQueueGeneration(audio)) { return false; } @@ -1134,7 +1148,10 @@ void NuPlayer::Renderer::onAudioSinkChanged() { } CHECK(!mDrainAudioQueuePending); mNumFramesWritten = 0; - mAnchorNumFramesWritten = -1; + { + Mutex::Autolock autoLock(mLock); + mAnchorNumFramesWritten = -1; + } uint32_t written; if (mAudioSink->getFramesWritten(&written) == OK) { mNumFramesWritten = written; @@ -1144,7 +1161,13 @@ void NuPlayer::Renderer::onAudioSinkChanged() { void NuPlayer::Renderer::onDisableOffloadAudio() { Mutex::Autolock autoLock(mLock); mFlags &= ~FLAG_OFFLOAD_AUDIO; - ++mAudioQueueGeneration; + ++mAudioDrainGeneration; +} + +void NuPlayer::Renderer::onEnableOffloadAudio() { + Mutex::Autolock autoLock(mLock); + mFlags |= FLAG_OFFLOAD_AUDIO; + ++mAudioDrainGeneration; } void NuPlayer::Renderer::onPause() { @@ -1152,13 +1175,14 @@ void NuPlayer::Renderer::onPause() { ALOGW("Renderer::onPause() called while already paused!"); return; } + int64_t currentPositionUs; { Mutex::Autolock autoLock(mLock); - ++mAudioQueueGeneration; - ++mVideoQueueGeneration; - prepareForMediaRenderingStart(); + ++mAudioDrainGeneration; + ++mVideoDrainGeneration; + prepareForMediaRenderingStart_l(); mPaused = true; - setPauseStartedTimeRealUs(ALooper::GetNowUs()); + mMediaClock->setPlaybackRate(0.0); } mDrainAudioQueuePending = false; @@ -1174,8 +1198,6 @@ void NuPlayer::Renderer::onPause() { } void NuPlayer::Renderer::onResume() { - readProperties(); - if (!mPaused) { return; } @@ -1185,17 +1207,14 @@ void NuPlayer::Renderer::onResume() { mAudioSink->start(); } - Mutex::Autolock autoLock(mLock); - mPaused = false; - if (mPauseStartedTimeRealUs != -1) { - int64_t newAnchorRealUs = - mAnchorTimeRealUs + ALooper::GetNowUs() - mPauseStartedTimeRealUs; - setAnchorTime( - mAnchorTimeMediaUs, newAnchorRealUs, mAnchorNumFramesWritten, true /* resume */); - } + { + Mutex::Autolock autoLock(mLock); + mPaused = false; + mMediaClock->setPlaybackRate(mPlaybackRate); - if (!mAudioQueue.empty()) { - postDrainAudioQueue_l(); + if (!mAudioQueue.empty()) { + postDrainAudioQueue_l(); + } } if (!mVideoQueue.empty()) { @@ -1210,6 +1229,21 @@ void NuPlayer::Renderer::onSetVideoFrameRate(float fps) { mVideoScheduler->init(fps); } +int32_t NuPlayer::Renderer::getQueueGeneration(bool audio) { + Mutex::Autolock autoLock(mLock); + return (audio ? mAudioQueueGeneration : mVideoQueueGeneration); +} + +int32_t NuPlayer::Renderer::getDrainGeneration(bool audio) { + Mutex::Autolock autoLock(mLock); + return (audio ? mAudioDrainGeneration : mVideoDrainGeneration); +} + +bool NuPlayer::Renderer::getSyncQueues() { + Mutex::Autolock autoLock(mLock); + return mSyncQueues; +} + // TODO: Remove unnecessary calls to getPlayedOutAudioDurationUs() // as it acquires locks and may query the audio driver. // @@ -1217,6 +1251,7 @@ void NuPlayer::Renderer::onSetVideoFrameRate(float fps) { // accessing getTimestamp() or getPosition() every time a data buffer with // a media time is received. // +// Calculate duration of played samples if played at normal rate (i.e., 1.0). int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) { uint32_t numFramesPlayed; int64_t numFramesPlayedAt; @@ -1254,9 +1289,8 @@ int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) { //ALOGD("getPosition: %d %lld", numFramesPlayed, numFramesPlayedAt); } - // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours. //CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test - int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000LL * mAudioSink->msecsPerFrame()) + int64_t durationUs = getDurationUsIfPlayedAtSampleRate(numFramesPlayed) + nowUs - numFramesPlayedAt; if (durationUs < 0) { // Occurs when numFramesPlayed position is very small and the following: @@ -1297,19 +1331,21 @@ void NuPlayer::Renderer::onAudioOffloadTearDown(AudioOffloadTearDownReason reaso void NuPlayer::Renderer::startAudioOffloadPauseTimeout() { if (offloadingAudio()) { - sp<AMessage> msg = new AMessage(kWhatAudioOffloadPauseTimeout, id()); - msg->setInt32("generation", mAudioOffloadPauseTimeoutGeneration); + mWakeLock->acquire(); + sp<AMessage> msg = new AMessage(kWhatAudioOffloadPauseTimeout, this); + msg->setInt32("drainGeneration", mAudioOffloadPauseTimeoutGeneration); msg->post(kOffloadPauseMaxUs); } } void NuPlayer::Renderer::cancelAudioOffloadPauseTimeout() { if (offloadingAudio()) { + mWakeLock->release(true); ++mAudioOffloadPauseTimeoutGeneration; } } -bool NuPlayer::Renderer::onOpenAudioSink( +status_t NuPlayer::Renderer::onOpenAudioSink( const sp<AMessage> &format, bool offloadOnly, bool hasVideo, @@ -1371,8 +1407,10 @@ bool NuPlayer::Renderer::onOpenAudioSink( if (memcmp(&mCurrentOffloadInfo, &offloadInfo, sizeof(offloadInfo)) == 0) { ALOGV("openAudioSink: no change in offload mode"); // no change from previous configuration, everything ok. - return offloadingAudio(); + return OK; } + mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER; + ALOGV("openAudioSink: try to open AudioSink in offload mode"); uint32_t offloadFlags = flags; offloadFlags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; @@ -1391,6 +1429,10 @@ bool NuPlayer::Renderer::onOpenAudioSink( &offloadInfo); if (err == OK) { + if (mPlaybackRate != 1.0) { + mAudioSink->setPlaybackRatePermille( + (int32_t)(mPlaybackRate * 1000 + 0.5f)); + } // If the playback is offloaded to h/w, we pass // the HAL some metadata information. // We don't want to do this for PCM because it @@ -1414,10 +1456,24 @@ bool NuPlayer::Renderer::onOpenAudioSink( ALOGV("openAudioSink: open AudioSink in NON-offload mode"); uint32_t pcmFlags = flags; pcmFlags &= ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + + const PcmInfo info = { + (audio_channel_mask_t)channelMask, + (audio_output_flags_t)pcmFlags, + AUDIO_FORMAT_PCM_16_BIT, // TODO: change to audioFormat + numChannels, + sampleRate + }; + if (memcmp(&mCurrentPcmInfo, &info, sizeof(info)) == 0) { + ALOGV("openAudioSink: no change in pcm mode"); + // no change from previous configuration, everything ok. + return OK; + } + audioSinkChanged = true; mAudioSink->close(); mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; - CHECK_EQ(mAudioSink->open( + status_t err = mAudioSink->open( sampleRate, numChannels, (audio_channel_mask_t)channelMask, @@ -1425,20 +1481,32 @@ bool NuPlayer::Renderer::onOpenAudioSink( 8 /* bufferCount */, NULL, NULL, - (audio_output_flags_t)pcmFlags), - (status_t)OK); + (audio_output_flags_t)pcmFlags); + if (err != OK) { + ALOGW("openAudioSink: non offloaded open failed status: %d", err); + mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER; + return err; + } + mCurrentPcmInfo = info; + if (mPlaybackRate != 1.0) { + mAudioSink->setPlaybackRatePermille( + (int32_t)(mPlaybackRate * 1000 + 0.5f)); + } mAudioSink->start(); } if (audioSinkChanged) { onAudioSinkChanged(); } - - return offloadingAudio(); + if (offloadingAudio()) { + mAudioOffloadTornDown = false; + } + return OK; } void NuPlayer::Renderer::onCloseAudioSink() { mAudioSink->close(); mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; + mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER; } } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index 7b46a59..38843d5 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -23,6 +23,8 @@ namespace android { struct ABuffer; +class AWakeLock; +struct MediaClock; struct VideoFrameScheduler; struct NuPlayer::Renderer : public AHandler { @@ -46,37 +48,31 @@ struct NuPlayer::Renderer : public AHandler { void queueEOS(bool audio, status_t finalResult); - void flush(bool audio); + void setPlaybackRate(float rate); + + void flush(bool audio, bool notifyComplete); void signalTimeDiscontinuity(); void signalAudioSinkChanged(); void signalDisableOffloadAudio(); + void signalEnableOffloadAudio(); void pause(); void resume(); void setVideoFrameRate(float fps); - // Following setters and getters are protected by mTimeLock. status_t getCurrentPosition(int64_t *mediaUs); - status_t getCurrentPosition( - int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo = false); - void setHasMedia(bool audio); - void setAudioFirstAnchorTime(int64_t mediaUs); - void setAudioFirstAnchorTimeIfNeeded(int64_t mediaUs); - void setAnchorTime( - int64_t mediaUs, int64_t realUs, int64_t numFramesWritten = -1, bool resume = false); - void setVideoLateByUs(int64_t lateUs); int64_t getVideoLateByUs(); - void setPauseStartedTimeRealUs(int64_t realUs); - bool openAudioSink( + status_t openAudioSink( const sp<AMessage> &format, bool offloadOnly, bool hasVideo, - uint32_t flags); + uint32_t flags, + bool *isOffloaded); void closeAudioSink(); enum { @@ -106,14 +102,15 @@ private: kWhatPostDrainVideoQueue = 'pDVQ', kWhatQueueBuffer = 'queB', kWhatQueueEOS = 'qEOS', + kWhatSetRate = 'setR', kWhatFlush = 'flus', - kWhatAudioSinkChanged = 'auSC', kWhatPause = 'paus', kWhatResume = 'resm', kWhatOpenAudioSink = 'opnA', kWhatCloseAudioSink = 'clsA', kWhatStopAudioSink = 'stpA', kWhatDisableOffloadAudio = 'noOA', + kWhatEnableOffloadAudio = 'enOA', kWhatSetVideoFrameRate = 'sVFR', }; @@ -140,30 +137,26 @@ private: bool mDrainVideoQueuePending; int32_t mAudioQueueGeneration; int32_t mVideoQueueGeneration; + int32_t mAudioDrainGeneration; + int32_t mVideoDrainGeneration; - Mutex mTimeLock; - // |mTimeLock| protects the following 7 member vars that are related to time. - // Note: those members are only written on Renderer thread, so reading on Renderer thread - // doesn't need to be protected. Otherwise accessing those members must be protected by - // |mTimeLock|. - // TODO: move those members to a seperated media clock class. + sp<MediaClock> mMediaClock; + float mPlaybackRate; int64_t mAudioFirstAnchorTimeMediaUs; int64_t mAnchorTimeMediaUs; - int64_t mAnchorTimeRealUs; int64_t mAnchorNumFramesWritten; - int64_t mAnchorMaxMediaUs; int64_t mVideoLateByUs; bool mHasAudio; bool mHasVideo; - int64_t mPauseStartedTimeRealUs; - Mutex mFlushLock; // protects the following 2 member vars. - bool mFlushingAudio; - bool mFlushingVideo; + bool mNotifyCompleteAudio; + bool mNotifyCompleteVideo; bool mSyncQueues; + // modified on only renderer's thread. bool mPaused; + bool mVideoSampleReceived; bool mVideoRenderingStarted; int32_t mVideoRenderingStartGeneration; @@ -175,9 +168,27 @@ private: bool mAudioOffloadTornDown; audio_offload_info_t mCurrentOffloadInfo; + struct PcmInfo { + audio_channel_mask_t mChannelMask; + audio_output_flags_t mFlags; + audio_format_t mFormat; + int32_t mNumChannels; + int32_t mSampleRate; + }; + PcmInfo mCurrentPcmInfo; + static const PcmInfo AUDIO_PCMINFO_INITIALIZER; + int32_t mTotalBuffersQueued; int32_t mLastAudioBufferDrained; + sp<AWakeLock> mWakeLock; + + status_t getCurrentPositionOnLooper(int64_t *mediaUs); + status_t getCurrentPositionOnLooper( + int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo = false); + bool getCurrentPositionIfPaused_l(int64_t *mediaUs); + status_t getCurrentPositionFromAnchor( + int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo = false); size_t fillAudioBuffer(void *buffer, size_t size); @@ -186,25 +197,34 @@ private: int64_t getPlayedOutAudioDurationUs(int64_t nowUs); void postDrainAudioQueue_l(int64_t delayUs = 0); + void clearAnchorTime_l(); + void clearAudioFirstAnchorTime_l(); + void setAudioFirstAnchorTimeIfNeeded_l(int64_t mediaUs); + void setVideoLateByUs(int64_t lateUs); + void onNewAudioMediaTime(int64_t mediaTimeUs); int64_t getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs); void onDrainVideoQueue(); void postDrainVideoQueue(); - void prepareForMediaRenderingStart(); - void notifyIfMediaRenderingStarted(); + void prepareForMediaRenderingStart_l(); + void notifyIfMediaRenderingStarted_l(); void onQueueBuffer(const sp<AMessage> &msg); void onQueueEOS(const sp<AMessage> &msg); void onFlush(const sp<AMessage> &msg); void onAudioSinkChanged(); void onDisableOffloadAudio(); + void onEnableOffloadAudio(); void onPause(); void onResume(); void onSetVideoFrameRate(float fps); + int32_t getQueueGeneration(bool audio); + int32_t getDrainGeneration(bool audio); + bool getSyncQueues(); void onAudioOffloadTearDown(AudioOffloadTearDownReason reason); - bool onOpenAudioSink( + status_t onOpenAudioSink( const sp<AMessage> &format, bool offloadOnly, bool hasVideo, @@ -219,7 +239,7 @@ private: void notifyAudioOffloadTearDown(); void flushQueue(List<QueueEntry> *queue); - bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg); + bool dropBufferIfStale(bool audio, const sp<AMessage> &msg); void syncQueuesDone_l(); bool offloadingAudio() const { return (mFlags & FLAG_OFFLOAD_AUDIO) != 0; } @@ -227,6 +247,8 @@ private: void startAudioOffloadPauseTimeout(); void cancelAudioOffloadPauseTimeout(); + int64_t getDurationUsIfPlayedAtSampleRate(uint32_t numFrames); + DISALLOW_EVIL_CONSTRUCTORS(Renderer); }; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 2f06c31..5a8beb1 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -28,7 +28,6 @@ namespace android { struct ABuffer; -struct MetaData; struct MediaBuffer; struct NuPlayer::Source : public AHandler { @@ -39,6 +38,7 @@ struct NuPlayer::Source : public AHandler { FLAG_CAN_SEEK = 8, // the "seek bar" FLAG_DYNAMIC_DURATION = 16, FLAG_SECURE = 32, + FLAG_PROTECTED = 64, }; enum { @@ -48,6 +48,9 @@ struct NuPlayer::Source : public AHandler { kWhatBufferingUpdate, kWhatBufferingStart, kWhatBufferingEnd, + kWhatPauseOnBufferingStart, + kWhatResumeOnBufferingEnd, + kWhatCacheStats, kWhatSubtitleData, kWhatTimedTextData, kWhatQueueDecoderShutdown, @@ -97,7 +100,7 @@ struct NuPlayer::Source : public AHandler { return INVALID_OPERATION; } - virtual status_t selectTrack(size_t /* trackIndex */, bool /* select */) { + virtual status_t selectTrack(size_t /* trackIndex */, bool /* select */, int64_t /* timeUs*/) { return INVALID_OPERATION; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp index 885ebe4..f53afbd 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp @@ -29,9 +29,9 @@ namespace android { NuPlayer::NuPlayerStreamListener::NuPlayerStreamListener( const sp<IStreamSource> &source, - ALooper::handler_id id) + const sp<AHandler> &targetHandler) : mSource(source), - mTargetID(id), + mTargetHandler(targetHandler), mEOS(false), mSendDataNotification(true) { mSource->setListener(this); @@ -65,8 +65,8 @@ void NuPlayer::NuPlayerStreamListener::queueBuffer(size_t index, size_t size) { if (mSendDataNotification) { mSendDataNotification = false; - if (mTargetID != 0) { - (new AMessage(kWhatMoreDataQueued, mTargetID))->post(); + if (mTargetHandler != NULL) { + (new AMessage(kWhatMoreDataQueued, mTargetHandler))->post(); } } } @@ -86,8 +86,8 @@ void NuPlayer::NuPlayerStreamListener::issueCommand( if (mSendDataNotification) { mSendDataNotification = false; - if (mTargetID != 0) { - (new AMessage(kWhatMoreDataQueued, mTargetID))->post(); + if (mTargetHandler != NULL) { + (new AMessage(kWhatMoreDataQueued, mTargetHandler))->post(); } } } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h index 1874d80..2de829b 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h @@ -29,7 +29,7 @@ struct MemoryDealer; struct NuPlayer::NuPlayerStreamListener : public BnStreamListener { NuPlayerStreamListener( const sp<IStreamSource> &source, - ALooper::handler_id targetID); + const sp<AHandler> &targetHandler); virtual void queueBuffer(size_t index, size_t size); @@ -59,7 +59,7 @@ private: Mutex mLock; sp<IStreamSource> mSource; - ALooper::handler_id mTargetID; + sp<AHandler> mTargetHandler; sp<MemoryDealer> mMemoryDealer; Vector<sp<IMemory> > mBuffers; List<QueueEntry> mQueue; diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index 52ae9ee..5210fc8 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -50,7 +50,7 @@ NuPlayer::RTSPSource::RTSPSource( mState(DISCONNECTED), mFinalResult(OK), mDisconnectReplyID(0), - mBuffering(true), + mBuffering(false), mSeekGeneration(0), mEOSTimeoutAudio(0), mEOSTimeoutVideo(0) { @@ -87,7 +87,7 @@ void NuPlayer::RTSPSource::prepareAsync() { CHECK(mHandler == NULL); CHECK(mSDPLoader == NULL); - sp<AMessage> notify = new AMessage(kWhatNotify, id()); + sp<AMessage> notify = new AMessage(kWhatNotify, this); CHECK_EQ(mState, (int)DISCONNECTED); mState = CONNECTING; @@ -106,9 +106,7 @@ void NuPlayer::RTSPSource::prepareAsync() { mHandler->connect(); } - sp<AMessage> notifyStart = dupNotify(); - notifyStart->setInt32("what", kWhatBufferingStart); - notifyStart->post(); + startBufferingIfNecessary(); } void NuPlayer::RTSPSource::start() { @@ -118,7 +116,7 @@ void NuPlayer::RTSPSource::stop() { if (mLooper == NULL) { return; } - sp<AMessage> msg = new AMessage(kWhatDisconnect, id()); + sp<AMessage> msg = new AMessage(kWhatDisconnect, this); sp<AMessage> dummy; msg->postAndAwaitResponse(&dummy); @@ -144,6 +142,7 @@ void NuPlayer::RTSPSource::resume() { } status_t NuPlayer::RTSPSource::feedMoreTSData() { + Mutex::Autolock _l(mBufferingLock); return mFinalResult; } @@ -195,16 +194,8 @@ bool NuPlayer::RTSPSource::haveSufficientDataOnAllTracks() { status_t NuPlayer::RTSPSource::dequeueAccessUnit( bool audio, sp<ABuffer> *accessUnit) { - if (mBuffering) { - if (!haveSufficientDataOnAllTracks()) { - return -EWOULDBLOCK; - } - - mBuffering = false; - - sp<AMessage> notify = dupNotify(); - notify->setInt32("what", kWhatBufferingEnd); - notify->post(); + if (!stopBufferingIfNecessary()) { + return -EWOULDBLOCK; } sp<AnotherPacketSource> source = getSource(audio); @@ -246,11 +237,7 @@ status_t NuPlayer::RTSPSource::dequeueAccessUnit( if (!(otherSource != NULL && otherSource->isFinished(mediaDurationUs))) { // We should not enter buffering mode // if any of the sources already have detected EOS. - mBuffering = true; - - sp<AMessage> notify = dupNotify(); - notify->setInt32("what", kWhatBufferingStart); - notify->post(); + startBufferingIfNecessary(); } return -EWOULDBLOCK; @@ -305,7 +292,7 @@ status_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) { } status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) { - sp<AMessage> msg = new AMessage(kWhatPerformSeek, id()); + sp<AMessage> msg = new AMessage(kWhatPerformSeek, this); msg->setInt32("generation", ++mSeekGeneration); msg->setInt64("timeUs", seekTimeUs); msg->post(200000ll); @@ -324,7 +311,7 @@ void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) { void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { if (msg->what() == kWhatDisconnect) { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); mDisconnectReplyID = replyID; @@ -613,7 +600,7 @@ void NuPlayer::RTSPSource::onSDPLoaded(const sp<AMessage> &msg) { ALOGE("Unable to find url in SDP"); err = UNKNOWN_ERROR; } else { - sp<AMessage> notify = new AMessage(kWhatNotify, id()); + sp<AMessage> notify = new AMessage(kWhatNotify, this); mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID); mLooper->registerHandler(mHandler); @@ -630,7 +617,7 @@ void NuPlayer::RTSPSource::onSDPLoaded(const sp<AMessage> &msg) { } mState = DISCONNECTED; - mFinalResult = err; + setError(err); if (mDisconnectReplyID != 0) { finishDisconnectIfPossible(); @@ -657,7 +644,7 @@ void NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) { } mState = DISCONNECTED; - mFinalResult = err; + setError(err); if (mDisconnectReplyID != 0) { finishDisconnectIfPossible(); @@ -678,4 +665,40 @@ void NuPlayer::RTSPSource::finishDisconnectIfPossible() { mDisconnectReplyID = 0; } +void NuPlayer::RTSPSource::setError(status_t err) { + Mutex::Autolock _l(mBufferingLock); + mFinalResult = err; +} + +void NuPlayer::RTSPSource::startBufferingIfNecessary() { + Mutex::Autolock _l(mBufferingLock); + + if (!mBuffering) { + mBuffering = true; + + sp<AMessage> notify = dupNotify(); + notify->setInt32("what", kWhatBufferingStart); + notify->post(); + } +} + +bool NuPlayer::RTSPSource::stopBufferingIfNecessary() { + Mutex::Autolock _l(mBufferingLock); + + if (mBuffering) { + if (!haveSufficientDataOnAllTracks()) { + return false; + } + + mBuffering = false; + + sp<AMessage> notify = dupNotify(); + notify->setInt32("what", kWhatBufferingEnd); + notify->post(); + } + + return true; +} + + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h index f1cae53..5f2cf33 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.h +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h @@ -25,6 +25,7 @@ namespace android { struct ALooper; +struct AReplyToken; struct AnotherPacketSource; struct MyHandler; struct SDPLoader; @@ -96,7 +97,8 @@ private: bool mIsSDP; State mState; status_t mFinalResult; - uint32_t mDisconnectReplyID; + sp<AReplyToken> mDisconnectReplyID; + Mutex mBufferingLock; bool mBuffering; sp<ALooper> mLooper; @@ -126,6 +128,9 @@ private: bool haveSufficientDataOnAllTracks(); void setEOSTimeout(bool audio, int64_t timeout); + void setError(status_t err); + void startBufferingIfNecessary(); + bool stopBufferingIfNecessary(); DISALLOW_EVIL_CONSTRUCTORS(RTSPSource); }; diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp index 27f5159..0246b59 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp @@ -37,20 +37,33 @@ NuPlayer::StreamingSource::StreamingSource( const sp<IStreamSource> &source) : Source(notify), mSource(source), - mFinalResult(OK) { + mFinalResult(OK), + mBuffering(false) { } NuPlayer::StreamingSource::~StreamingSource() { + if (mLooper != NULL) { + mLooper->unregisterHandler(id()); + mLooper->stop(); + } } void NuPlayer::StreamingSource::prepareAsync() { + if (mLooper == NULL) { + mLooper = new ALooper; + mLooper->setName("streaming"); + mLooper->start(); + + mLooper->registerHandler(this); + } + notifyVideoSizeChanged(); notifyFlagsChanged(0); notifyPrepared(); } void NuPlayer::StreamingSource::start() { - mStreamListener = new NuPlayerStreamListener(mSource, 0); + mStreamListener = new NuPlayerStreamListener(mSource, NULL); uint32_t sourceFlags = mSource->flags(); @@ -62,13 +75,15 @@ void NuPlayer::StreamingSource::start() { mTSParser = new ATSParser(parserFlags); mStreamListener->start(); + + postReadBuffer(); } status_t NuPlayer::StreamingSource::feedMoreTSData() { - if (mFinalResult != OK) { - return mFinalResult; - } + return postReadBuffer(); +} +void NuPlayer::StreamingSource::onReadBuffer() { for (int32_t i = 0; i < 50; ++i) { char buffer[188]; sp<AMessage> extra; @@ -77,7 +92,7 @@ status_t NuPlayer::StreamingSource::feedMoreTSData() { if (n == 0) { ALOGI("input data EOS reached."); mTSParser->signalEOS(ERROR_END_OF_STREAM); - mFinalResult = ERROR_END_OF_STREAM; + setError(ERROR_END_OF_STREAM); break; } else if (n == INFO_DISCONTINUITY) { int32_t type = ATSParser::DISCONTINUITY_TIME; @@ -88,7 +103,8 @@ status_t NuPlayer::StreamingSource::feedMoreTSData() { IStreamListener::kKeyDiscontinuityMask, &mask)) { if (mask == 0) { ALOGE("Client specified an illegal discontinuity type."); - return ERROR_UNSUPPORTED; + setError(ERROR_UNSUPPORTED); + break; } type = mask; @@ -97,7 +113,6 @@ status_t NuPlayer::StreamingSource::feedMoreTSData() { mTSParser->signalDiscontinuity( (ATSParser::DiscontinuityType)type, extra); } else if (n < 0) { - CHECK_EQ(n, -EWOULDBLOCK); break; } else { if (buffer[0] == 0x00) { @@ -128,22 +143,80 @@ status_t NuPlayer::StreamingSource::feedMoreTSData() { ALOGE("TS Parser returned error %d", err); mTSParser->signalEOS(err); - mFinalResult = err; + setError(err); break; } } } } +} + +status_t NuPlayer::StreamingSource::postReadBuffer() { + { + Mutex::Autolock _l(mBufferingLock); + if (mFinalResult != OK) { + return mFinalResult; + } + if (mBuffering) { + return OK; + } + mBuffering = true; + } + (new AMessage(kWhatReadBuffer, this))->post(); return OK; } -sp<MetaData> NuPlayer::StreamingSource::getFormatMeta(bool audio) { - ATSParser::SourceType type = - audio ? ATSParser::AUDIO : ATSParser::VIDEO; +bool NuPlayer::StreamingSource::haveSufficientDataOnAllTracks() { + // We're going to buffer at least 2 secs worth data on all tracks before + // starting playback (both at startup and after a seek). - sp<AnotherPacketSource> source = - static_cast<AnotherPacketSource *>(mTSParser->getSource(type).get()); + static const int64_t kMinDurationUs = 2000000ll; + + sp<AnotherPacketSource> audioTrack = getSource(true /*audio*/); + sp<AnotherPacketSource> videoTrack = getSource(false /*audio*/); + + status_t err; + int64_t durationUs; + if (audioTrack != NULL + && (durationUs = audioTrack->getBufferedDurationUs(&err)) + < kMinDurationUs + && err == OK) { + ALOGV("audio track doesn't have enough data yet. (%.2f secs buffered)", + durationUs / 1E6); + return false; + } + + if (videoTrack != NULL + && (durationUs = videoTrack->getBufferedDurationUs(&err)) + < kMinDurationUs + && err == OK) { + ALOGV("video track doesn't have enough data yet. (%.2f secs buffered)", + durationUs / 1E6); + return false; + } + + return true; +} + +void NuPlayer::StreamingSource::setError(status_t err) { + Mutex::Autolock _l(mBufferingLock); + mFinalResult = err; +} + +sp<AnotherPacketSource> NuPlayer::StreamingSource::getSource(bool audio) { + if (mTSParser == NULL) { + return NULL; + } + + sp<MediaSource> source = mTSParser->getSource( + audio ? ATSParser::AUDIO : ATSParser::VIDEO); + + return static_cast<AnotherPacketSource *>(source.get()); +} + +sp<MetaData> NuPlayer::StreamingSource::getFormatMeta(bool audio) { + sp<AnotherPacketSource> source = getSource(audio); if (source == NULL) { return NULL; @@ -154,16 +227,16 @@ sp<MetaData> NuPlayer::StreamingSource::getFormatMeta(bool audio) { status_t NuPlayer::StreamingSource::dequeueAccessUnit( bool audio, sp<ABuffer> *accessUnit) { - ATSParser::SourceType type = - audio ? ATSParser::AUDIO : ATSParser::VIDEO; - - sp<AnotherPacketSource> source = - static_cast<AnotherPacketSource *>(mTSParser->getSource(type).get()); + sp<AnotherPacketSource> source = getSource(audio); if (source == NULL) { return -EWOULDBLOCK; } + if (!haveSufficientDataOnAllTracks()) { + postReadBuffer(); + } + status_t finalResult; if (!source->hasBufferAvailable(&finalResult)) { return finalResult == OK ? -EWOULDBLOCK : finalResult; @@ -186,5 +259,26 @@ bool NuPlayer::StreamingSource::isRealTime() const { return mSource->flags() & IStreamSource::kFlagIsRealTimeData; } +void NuPlayer::StreamingSource::onMessageReceived( + const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatReadBuffer: + { + onReadBuffer(); + + { + Mutex::Autolock _l(mBufferingLock); + mBuffering = false; + } + break; + } + default: + { + TRESPASS(); + } + } +} + + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.h b/media/libmediaplayerservice/nuplayer/StreamingSource.h index 412b6c4..1f95f3c 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.h +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.h @@ -25,6 +25,7 @@ namespace android { struct ABuffer; struct ATSParser; +struct AnotherPacketSource; struct NuPlayer::StreamingSource : public NuPlayer::Source { StreamingSource( @@ -43,14 +44,29 @@ struct NuPlayer::StreamingSource : public NuPlayer::Source { protected: virtual ~StreamingSource(); + virtual void onMessageReceived(const sp<AMessage> &msg); + virtual sp<MetaData> getFormatMeta(bool audio); private: + enum { + kWhatReadBuffer, + }; sp<IStreamSource> mSource; status_t mFinalResult; sp<NuPlayerStreamListener> mStreamListener; sp<ATSParser> mTSParser; + bool mBuffering; + Mutex mBufferingLock; + sp<ALooper> mLooper; + + void setError(status_t err); + sp<AnotherPacketSource> getSource(bool audio); + bool haveSufficientDataOnAllTracks(); + status_t postReadBuffer(); + void onReadBuffer(); + DISALLOW_EVIL_CONSTRUCTORS(StreamingSource); }; diff --git a/media/libmediaplayerservice/tests/Android.mk b/media/libmediaplayerservice/tests/Android.mk new file mode 100644 index 0000000..7bc78ff --- /dev/null +++ b/media/libmediaplayerservice/tests/Android.mk @@ -0,0 +1,24 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := DrmSessionManager_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + DrmSessionManager_test.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libmediaplayerservice \ + libutils \ + +LOCAL_C_INCLUDES := \ + frameworks/av/include \ + frameworks/av/media/libmediaplayerservice \ + +LOCAL_32_BIT_ONLY := true + +include $(BUILD_NATIVE_TEST) + diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp new file mode 100644 index 0000000..d3e760b --- /dev/null +++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DrmSessionManager_test" +#include <utils/Log.h> + +#include <gtest/gtest.h> + +#include "Drm.h" +#include "DrmSessionClientInterface.h" +#include "DrmSessionManager.h" +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/ProcessInfoInterface.h> + +namespace android { + +struct FakeProcessInfo : public ProcessInfoInterface { + FakeProcessInfo() {} + virtual ~FakeProcessInfo() {} + + virtual bool getPriority(int pid, int* priority) { + // For testing, use pid as priority. + // Lower the value higher the priority. + *priority = pid; + return true; + } + +private: + DISALLOW_EVIL_CONSTRUCTORS(FakeProcessInfo); +}; + +struct FakeDrm : public DrmSessionClientInterface { + FakeDrm() {} + virtual ~FakeDrm() {} + + virtual bool reclaimSession(const Vector<uint8_t>& sessionId) { + mReclaimedSessions.push_back(sessionId); + return true; + } + + const Vector<Vector<uint8_t> >& reclaimedSessions() const { + return mReclaimedSessions; + } + +private: + Vector<Vector<uint8_t> > mReclaimedSessions; + + DISALLOW_EVIL_CONSTRUCTORS(FakeDrm); +}; + +static const int kTestPid1 = 30; +static const int kTestPid2 = 20; +static const uint8_t kTestSessionId1[] = {1, 2, 3}; +static const uint8_t kTestSessionId2[] = {4, 5, 6, 7, 8}; +static const uint8_t kTestSessionId3[] = {9, 0}; + +class DrmSessionManagerTest : public ::testing::Test { +public: + DrmSessionManagerTest() + : mDrmSessionManager(new DrmSessionManager(new FakeProcessInfo())), + mTestDrm1(new FakeDrm()), + mTestDrm2(new FakeDrm()) { + GetSessionId(kTestSessionId1, ARRAY_SIZE(kTestSessionId1), &mSessionId1); + GetSessionId(kTestSessionId2, ARRAY_SIZE(kTestSessionId2), &mSessionId2); + GetSessionId(kTestSessionId3, ARRAY_SIZE(kTestSessionId3), &mSessionId3); + } + +protected: + static void GetSessionId(const uint8_t* ids, size_t num, Vector<uint8_t>* sessionId) { + for (size_t i = 0; i < num; ++i) { + sessionId->push_back(ids[i]); + } + } + + static void ExpectEqSessionInfo(const SessionInfo& info, sp<DrmSessionClientInterface> drm, + const Vector<uint8_t>& sessionId, int64_t timeStamp) { + EXPECT_EQ(drm, info.drm); + EXPECT_TRUE(isEqualSessionId(sessionId, info.sessionId)); + EXPECT_EQ(timeStamp, info.timeStamp); + } + + void addSession() { + mDrmSessionManager->addSession(kTestPid1, mTestDrm1, mSessionId1); + mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId2); + mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId3); + const PidSessionInfosMap& map = sessionMap(); + EXPECT_EQ(2, map.size()); + ssize_t index1 = map.indexOfKey(kTestPid1); + ASSERT_GE(index1, 0); + const SessionInfos& infos1 = map[index1]; + EXPECT_EQ(1, infos1.size()); + ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 0); + + ssize_t index2 = map.indexOfKey(kTestPid2); + ASSERT_GE(index2, 0); + const SessionInfos& infos2 = map[index2]; + EXPECT_EQ(2, infos2.size()); + ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId2, 1); + ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 2); + } + + const PidSessionInfosMap& sessionMap() { + return mDrmSessionManager->mSessionMap; + } + + void testGetLowestPriority() { + int pid; + int priority; + EXPECT_FALSE(mDrmSessionManager->getLowestPriority_l(&pid, &priority)); + + addSession(); + EXPECT_TRUE(mDrmSessionManager->getLowestPriority_l(&pid, &priority)); + + EXPECT_EQ(kTestPid1, pid); + FakeProcessInfo processInfo; + int priority1; + processInfo.getPriority(kTestPid1, &priority1); + EXPECT_EQ(priority1, priority); + } + + void testGetLeastUsedSession() { + sp<DrmSessionClientInterface> drm; + Vector<uint8_t> sessionId; + EXPECT_FALSE(mDrmSessionManager->getLeastUsedSession_l(kTestPid1, &drm, &sessionId)); + + addSession(); + + EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid1, &drm, &sessionId)); + EXPECT_EQ(mTestDrm1, drm); + EXPECT_TRUE(isEqualSessionId(mSessionId1, sessionId)); + + EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid2, &drm, &sessionId)); + EXPECT_EQ(mTestDrm2, drm); + EXPECT_TRUE(isEqualSessionId(mSessionId2, sessionId)); + + // mSessionId2 is no longer the least used session. + mDrmSessionManager->useSession(mSessionId2); + EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid2, &drm, &sessionId)); + EXPECT_EQ(mTestDrm2, drm); + EXPECT_TRUE(isEqualSessionId(mSessionId3, sessionId)); + } + + sp<DrmSessionManager> mDrmSessionManager; + sp<FakeDrm> mTestDrm1; + sp<FakeDrm> mTestDrm2; + Vector<uint8_t> mSessionId1; + Vector<uint8_t> mSessionId2; + Vector<uint8_t> mSessionId3; +}; + +TEST_F(DrmSessionManagerTest, addSession) { + addSession(); +} + +TEST_F(DrmSessionManagerTest, useSession) { + addSession(); + + mDrmSessionManager->useSession(mSessionId1); + mDrmSessionManager->useSession(mSessionId3); + + const PidSessionInfosMap& map = sessionMap(); + const SessionInfos& infos1 = map.valueFor(kTestPid1); + const SessionInfos& infos2 = map.valueFor(kTestPid2); + ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 3); + ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 4); +} + +TEST_F(DrmSessionManagerTest, removeSession) { + addSession(); + + mDrmSessionManager->removeSession(mSessionId2); + + const PidSessionInfosMap& map = sessionMap(); + EXPECT_EQ(2, map.size()); + const SessionInfos& infos1 = map.valueFor(kTestPid1); + const SessionInfos& infos2 = map.valueFor(kTestPid2); + EXPECT_EQ(1, infos1.size()); + EXPECT_EQ(1, infos2.size()); + // mSessionId2 has been removed. + ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId3, 2); +} + +TEST_F(DrmSessionManagerTest, removeDrm) { + addSession(); + + sp<FakeDrm> drm = new FakeDrm; + const uint8_t ids[] = {123}; + Vector<uint8_t> sessionId; + GetSessionId(ids, ARRAY_SIZE(ids), &sessionId); + mDrmSessionManager->addSession(kTestPid2, drm, sessionId); + + mDrmSessionManager->removeDrm(mTestDrm2); + + const PidSessionInfosMap& map = sessionMap(); + const SessionInfos& infos2 = map.valueFor(kTestPid2); + EXPECT_EQ(1, infos2.size()); + // mTestDrm2 has been removed. + ExpectEqSessionInfo(infos2[0], drm, sessionId, 3); +} + +TEST_F(DrmSessionManagerTest, reclaimSession) { + EXPECT_FALSE(mDrmSessionManager->reclaimSession(kTestPid1)); + addSession(); + + // calling pid priority is too low + EXPECT_FALSE(mDrmSessionManager->reclaimSession(50)); + + EXPECT_TRUE(mDrmSessionManager->reclaimSession(10)); + EXPECT_EQ(1, mTestDrm1->reclaimedSessions().size()); + EXPECT_TRUE(isEqualSessionId(mSessionId1, mTestDrm1->reclaimedSessions()[0])); + + mDrmSessionManager->removeSession(mSessionId1); + + // add a session from a higher priority process. + sp<FakeDrm> drm = new FakeDrm; + const uint8_t ids[] = {1, 3, 5}; + Vector<uint8_t> sessionId; + GetSessionId(ids, ARRAY_SIZE(ids), &sessionId); + mDrmSessionManager->addSession(15, drm, sessionId); + + EXPECT_TRUE(mDrmSessionManager->reclaimSession(18)); + EXPECT_EQ(1, mTestDrm2->reclaimedSessions().size()); + // mSessionId2 is reclaimed. + EXPECT_TRUE(isEqualSessionId(mSessionId2, mTestDrm2->reclaimedSessions()[0])); +} + +TEST_F(DrmSessionManagerTest, getLowestPriority) { + testGetLowestPriority(); +} + +TEST_F(DrmSessionManagerTest, getLeastUsedSession_l) { + testGetLeastUsedSession(); +} + +} // namespace android diff --git a/media/libnbaio/Android.mk b/media/libnbaio/Android.mk index 9707c4a..1353f28 100644 --- a/media/libnbaio/Android.mk +++ b/media/libnbaio/Android.mk @@ -11,7 +11,6 @@ LOCAL_SRC_FILES := \ MonoPipeReader.cpp \ Pipe.cpp \ PipeReader.cpp \ - roundup.c \ SourceAudioBufferProvider.cpp LOCAL_SRC_FILES += NBLog.cpp @@ -27,12 +26,13 @@ LOCAL_SRC_FILES += NBLog.cpp LOCAL_MODULE := libnbaio LOCAL_SHARED_LIBRARIES := \ + libaudioutils \ libbinder \ libcommon_time_client \ libcutils \ libutils \ liblog -LOCAL_STATIC_LIBRARIES += libinstantssq +LOCAL_C_INCLUDES := $(call include-path-for, audio-utils) include $(BUILD_SHARED_LIBRARY) diff --git a/media/libnbaio/MonoPipe.cpp b/media/libnbaio/MonoPipe.cpp index 0b65861..129e9ef 100644 --- a/media/libnbaio/MonoPipe.cpp +++ b/media/libnbaio/MonoPipe.cpp @@ -27,7 +27,7 @@ #include <utils/Trace.h> #include <media/AudioBufferProvider.h> #include <media/nbaio/MonoPipe.h> -#include <media/nbaio/roundup.h> +#include <audio_utils/roundup.h> namespace android { diff --git a/media/libnbaio/Pipe.cpp b/media/libnbaio/Pipe.cpp index 6e0ec8c..13f211d 100644 --- a/media/libnbaio/Pipe.cpp +++ b/media/libnbaio/Pipe.cpp @@ -21,7 +21,7 @@ #include <cutils/compiler.h> #include <utils/Log.h> #include <media/nbaio/Pipe.h> -#include <media/nbaio/roundup.h> +#include <audio_utils/roundup.h> namespace android { diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp index 353920e..9d90dbd 100644 --- a/media/libstagefright/AACWriter.cpp +++ b/media/libstagefright/AACWriter.cpp @@ -36,32 +36,19 @@ namespace android { -AACWriter::AACWriter(const char *filename) - : mFd(-1), - mInitCheck(NO_INIT), - mStarted(false), - mPaused(false), - mResumed(false), - mChannelCount(-1), - mSampleRate(-1), - mAACProfile(OMX_AUDIO_AACObjectLC) { - - ALOGV("AACWriter Constructor"); - - mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); - if (mFd >= 0) { - mInitCheck = OK; - } -} - AACWriter::AACWriter(int fd) : mFd(dup(fd)), mInitCheck(mFd < 0? NO_INIT: OK), mStarted(false), mPaused(false), mResumed(false), + mThread(0), + mEstimatedSizeBytes(0), + mEstimatedDurationUs(0), mChannelCount(-1), - mSampleRate(-1) { + mSampleRate(-1), + mAACProfile(OMX_AUDIO_AACObjectLC), + mFrameDurationUs(0) { } AACWriter::~AACWriter() { @@ -79,10 +66,6 @@ status_t AACWriter::initCheck() const { return mInitCheck; } -static int writeInt8(int fd, uint8_t x) { - return ::write(fd, &x, 1); -} - status_t AACWriter::addSource(const sp<MediaSource> &source) { if (mInitCheck != OK) { diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 4c8a199..31e10ce 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -419,6 +419,7 @@ ACodec::ACodec() mMetaDataBuffersToSubmit(0), mRepeatFrameDelayUs(-1ll), mMaxPtsGapUs(-1ll), + mMaxFps(-1), mTimePerFrameUs(-1ll), mTimePerCaptureUs(-1ll), mCreateInputBuffersSuspended(false), @@ -451,61 +452,61 @@ void ACodec::setNotificationMessage(const sp<AMessage> &msg) { void ACodec::initiateSetup(const sp<AMessage> &msg) { msg->setWhat(kWhatSetup); - msg->setTarget(id()); + msg->setTarget(this); msg->post(); } void ACodec::signalSetParameters(const sp<AMessage> ¶ms) { - sp<AMessage> msg = new AMessage(kWhatSetParameters, id()); + sp<AMessage> msg = new AMessage(kWhatSetParameters, this); msg->setMessage("params", params); msg->post(); } void ACodec::initiateAllocateComponent(const sp<AMessage> &msg) { msg->setWhat(kWhatAllocateComponent); - msg->setTarget(id()); + msg->setTarget(this); msg->post(); } void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) { msg->setWhat(kWhatConfigureComponent); - msg->setTarget(id()); + msg->setTarget(this); msg->post(); } void ACodec::initiateCreateInputSurface() { - (new AMessage(kWhatCreateInputSurface, id()))->post(); + (new AMessage(kWhatCreateInputSurface, this))->post(); } void ACodec::signalEndOfInputStream() { - (new AMessage(kWhatSignalEndOfInputStream, id()))->post(); + (new AMessage(kWhatSignalEndOfInputStream, this))->post(); } void ACodec::initiateStart() { - (new AMessage(kWhatStart, id()))->post(); + (new AMessage(kWhatStart, this))->post(); } void ACodec::signalFlush() { ALOGV("[%s] signalFlush", mComponentName.c_str()); - (new AMessage(kWhatFlush, id()))->post(); + (new AMessage(kWhatFlush, this))->post(); } void ACodec::signalResume() { - (new AMessage(kWhatResume, id()))->post(); + (new AMessage(kWhatResume, this))->post(); } void ACodec::initiateShutdown(bool keepComponentAllocated) { - sp<AMessage> msg = new AMessage(kWhatShutdown, id()); + sp<AMessage> msg = new AMessage(kWhatShutdown, this); msg->setInt32("keepComponentAllocated", keepComponentAllocated); msg->post(); if (!keepComponentAllocated) { // ensure shutdown completes in 3 seconds - (new AMessage(kWhatReleaseCodecInstance, id()))->post(3000000); + (new AMessage(kWhatReleaseCodecInstance, this))->post(3000000); } } void ACodec::signalRequestIDRFrame() { - (new AMessage(kWhatRequestIDRFrame, id()))->post(); + (new AMessage(kWhatRequestIDRFrame, this))->post(); } // *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED *** @@ -516,7 +517,7 @@ void ACodec::signalRequestIDRFrame() { void ACodec::signalSubmitOutputMetaDataBufferIfEOS_workaround() { if (mPortEOS[kPortIndexInput] && !mPortEOS[kPortIndexOutput] && mMetaDataBuffersToSubmit > 0) { - (new AMessage(kWhatSubmitOutputMetaDataBufferIfEOS, id()))->post(); + (new AMessage(kWhatSubmitOutputMetaDataBufferIfEOS, this))->post(); } } @@ -669,8 +670,9 @@ status_t ACodec::configureOutputBuffersFromNativeWindow( // XXX: Currently this error is logged, but not fatal. usage = 0; } + int omxUsage = usage; - if (mFlags & kFlagIsSecure) { + if (mFlags & kFlagIsGrallocUsageProtected) { usage |= GRALLOC_USAGE_PROTECTED; } @@ -693,6 +695,18 @@ status_t ACodec::configureOutputBuffersFromNativeWindow( } } + int consumerUsage = 0; + err = mNativeWindow->query( + mNativeWindow.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, + &consumerUsage); + if (err != 0) { + ALOGW("failed to get consumer usage bits. ignoring"); + err = 0; + } + + ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec) + %#x(Consumer) = %#x", + omxUsage, usage, consumerUsage, usage | consumerUsage); + usage |= consumerUsage; err = native_window_set_usage( mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP); @@ -922,7 +936,6 @@ status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) { ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { ANativeWindowBuffer *buf; - int fenceFd = -1; CHECK(mNativeWindow.get() != NULL); if (mTunneled) { @@ -1106,6 +1119,8 @@ status_t ACodec::setComponentRole( "video_decoder.mpeg2", "video_encoder.mpeg2" }, { MEDIA_MIMETYPE_AUDIO_AC3, "audio_decoder.ac3", "audio_encoder.ac3" }, + { MEDIA_MIMETYPE_AUDIO_EAC3, + "audio_decoder.eac3", "audio_encoder.eac3" }, }; static const size_t kNumMimeToRole = @@ -1158,7 +1173,7 @@ status_t ACodec::configureCodec( } sp<AMessage> inputFormat = new AMessage(); - sp<AMessage> outputFormat = new AMessage(); + sp<AMessage> outputFormat = mNotify->dup(); // will use this for kWhatOutputFormatChanged mIsEncoder = encoder; @@ -1245,6 +1260,10 @@ status_t ACodec::configureCodec( mMaxPtsGapUs = -1ll; } + if (!msg->findFloat("max-fps-to-encoder", &mMaxFps)) { + mMaxFps = -1; + } + if (!msg->findInt64("time-lapse", &mTimePerCaptureUs)) { mTimePerCaptureUs = -1ll; } @@ -1256,18 +1275,47 @@ status_t ACodec::configureCodec( } } + // NOTE: we only use native window for video decoders sp<RefBase> obj; - int32_t haveNativeWindow = msg->findObject("native-window", &obj) && - obj != NULL; + bool haveNativeWindow = msg->findObject("native-window", &obj) + && obj != NULL && video && !encoder; mStoreMetaDataInOutputBuffers = false; if (video && !encoder) { inputFormat->setInt32("adaptive-playback", false); + + int32_t usageProtected; + if (msg->findInt32("protected", &usageProtected) && usageProtected) { + if (!haveNativeWindow) { + ALOGE("protected output buffers must be sent to an ANativeWindow"); + return PERMISSION_DENIED; + } + mFlags |= kFlagIsGrallocUsageProtected; + mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown; + } } - if (!encoder && video && haveNativeWindow) { + if (haveNativeWindow) { sp<NativeWindowWrapper> windowWrapper( static_cast<NativeWindowWrapper *>(obj.get())); sp<ANativeWindow> nativeWindow = windowWrapper->getNativeWindow(); + // START of temporary support for automatic FRC - THIS WILL BE REMOVED + int32_t autoFrc; + if (msg->findInt32("auto-frc", &autoFrc)) { + bool enabled = autoFrc; + OMX_CONFIG_BOOLEANTYPE config; + InitOMXParams(&config); + config.bEnabled = (OMX_BOOL)enabled; + status_t temp = mOMX->setConfig( + mNode, (OMX_INDEXTYPE)OMX_IndexConfigAutoFramerateConversion, + &config, sizeof(config)); + if (temp == OK) { + outputFormat->setInt32("auto-frc", enabled); + } else if (enabled) { + ALOGI("codec does not support requested auto-frc (err %d)", temp); + } + } + // END of temporary support for automatic FRC + int32_t tunneled; if (msg->findInt32("feature-tunneled-playback", &tunneled) && tunneled != 0) { @@ -1285,11 +1333,36 @@ status_t ACodec::configureCodec( return err; } - inputFormat->setInt32("adaptive-playback", true); + int32_t maxWidth = 0, maxHeight = 0; + if (msg->findInt32("max-width", &maxWidth) && + msg->findInt32("max-height", &maxHeight)) { + + err = mOMX->prepareForAdaptivePlayback( + mNode, kPortIndexOutput, OMX_TRUE, maxWidth, maxHeight); + if (err != OK) { + ALOGW("[%s] prepareForAdaptivePlayback failed w/ err %d", + mComponentName.c_str(), err); + // allow failure + err = OK; + } else { + inputFormat->setInt32("max-width", maxWidth); + inputFormat->setInt32("max-height", maxHeight); + inputFormat->setInt32("adaptive-playback", true); + } + } } else { ALOGV("Configuring CPU controlled video playback."); mTunneled = false; + // Explicity reset the sideband handle of the window for + // non-tunneled video in case the window was previously used + // for a tunneled video playback. + err = native_window_set_sideband_stream(nativeWindow.get(), NULL); + if (err != OK) { + ALOGE("set_sideband_stream(NULL) failed! (err %d).", err); + return err; + } + // Always try to enable dynamic output buffers on native surface err = mOMX->storeMetaDataInBuffers( mNode, kPortIndexOutput, OMX_TRUE); @@ -1364,10 +1437,79 @@ status_t ACodec::configureCodec( } if (video) { + // determine need for software renderer + bool usingSwRenderer = false; + if (haveNativeWindow && mComponentName.startsWith("OMX.google.")) { + usingSwRenderer = true; + haveNativeWindow = false; + } + if (encoder) { err = setupVideoEncoder(mime, msg); } else { - err = setupVideoDecoder(mime, msg); + err = setupVideoDecoder(mime, msg, haveNativeWindow); + } + + if (err != OK) { + return err; + } + + if (haveNativeWindow) { + sp<NativeWindowWrapper> nativeWindow( + static_cast<NativeWindowWrapper *>(obj.get())); + CHECK(nativeWindow != NULL); + mNativeWindow = nativeWindow->getNativeWindow(); + + native_window_set_scaling_mode( + mNativeWindow.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + } + + // initialize native window now to get actual output format + // TODO: this is needed for some encoders even though they don't use native window + CHECK_EQ((status_t)OK, initNativeWindow()); + + // fallback for devices that do not handle flex-YUV for native buffers + if (haveNativeWindow) { + int32_t requestedColorFormat = OMX_COLOR_FormatUnused; + if (msg->findInt32("color-format", &requestedColorFormat) && + requestedColorFormat == OMX_COLOR_FormatYUV420Flexible) { + CHECK_EQ(getPortFormat(kPortIndexOutput, outputFormat), (status_t)OK); + int32_t colorFormat = OMX_COLOR_FormatUnused; + OMX_U32 flexibleEquivalent = OMX_COLOR_FormatUnused; + CHECK(outputFormat->findInt32("color-format", &colorFormat)); + ALOGD("[%s] Requested output format %#x and got %#x.", + mComponentName.c_str(), requestedColorFormat, colorFormat); + if (!isFlexibleColorFormat( + mOMX, mNode, colorFormat, haveNativeWindow, &flexibleEquivalent) + || flexibleEquivalent != (OMX_U32)requestedColorFormat) { + // device did not handle flex-YUV request for native window, fall back + // to SW renderer + ALOGI("[%s] Falling back to software renderer", mComponentName.c_str()); + mNativeWindow.clear(); + haveNativeWindow = false; + usingSwRenderer = true; + if (mStoreMetaDataInOutputBuffers) { + err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexOutput, OMX_FALSE); + mStoreMetaDataInOutputBuffers = false; + // TODO: implement adaptive-playback support for bytebuffer mode. + // This is done by SW codecs, but most HW codecs don't support it. + inputFormat->setInt32("adaptive-playback", false); + } + if (err == OK) { + err = mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_FALSE); + } + if (mFlags & kFlagIsGrallocUsageProtected) { + // fallback is not supported for protected playback + err = PERMISSION_DENIED; + } else if (err == OK) { + err = setupVideoDecoder(mime, msg, false); + } + } + } + } + + if (usingSwRenderer) { + outputFormat->setInt32("using-sw-renderer", 1); } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { int32_t numChannels, sampleRate; @@ -1498,6 +1640,15 @@ status_t ACodec::configureCodec( } else { err = setupAC3Codec(encoder, numChannels, sampleRate); } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_EAC3)) { + int32_t numChannels; + int32_t sampleRate; + if (!msg->findInt32("channel-count", &numChannels) + || !msg->findInt32("sample-rate", &sampleRate)) { + err = INVALID_OPERATION; + } else { + err = setupEAC3Codec(encoder, numChannels, sampleRate); + } } if (err != OK) { @@ -1525,6 +1676,13 @@ status_t ACodec::configureCodec( err = setMinBufferSize(kPortIndexInput, 8192); // XXX } + int32_t priority; + if (msg->findInt32("priority", &priority)) { + err = setPriority(priority); + } + + mBaseOutputFormat = outputFormat; + CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK); CHECK_EQ(getPortFormat(kPortIndexOutput, outputFormat), (status_t)OK); mInputFormat = inputFormat; @@ -1533,6 +1691,22 @@ status_t ACodec::configureCodec( return err; } +status_t ACodec::setPriority(int32_t priority) { + if (priority < 0) { + return BAD_VALUE; + } + OMX_PARAM_U32TYPE config; + InitOMXParams(&config); + config.nU32 = (OMX_U32)priority; + status_t temp = mOMX->setConfig( + mNode, (OMX_INDEXTYPE)OMX_IndexConfigPriority, + &config, sizeof(config)); + if (temp != OK) { + ALOGI("codec does not support config priority (err %d)", temp); + } + return OK; +} + status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); @@ -1781,6 +1955,44 @@ status_t ACodec::setupAC3Codec( sizeof(def)); } +status_t ACodec::setupEAC3Codec( + bool encoder, int32_t numChannels, int32_t sampleRate) { + status_t err = setupRawAudioFormat( + encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, numChannels); + + if (err != OK) { + return err; + } + + if (encoder) { + ALOGW("EAC3 encoding is not supported."); + return INVALID_OPERATION; + } + + OMX_AUDIO_PARAM_ANDROID_EAC3TYPE def; + InitOMXParams(&def); + def.nPortIndex = kPortIndexInput; + + err = mOMX->getParameter( + mNode, + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidEac3, + &def, + sizeof(def)); + + if (err != OK) { + return err; + } + + def.nChannels = numChannels; + def.nSampleRate = sampleRate; + + return mOMX->setParameter( + mNode, + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidEac3, + &def, + sizeof(def)); +} + static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate( bool isAMRWB, int32_t bps) { if (isAMRWB) { @@ -1961,7 +2173,8 @@ status_t ACodec::configureTunneledVideoPlayback( status_t ACodec::setVideoPortFormatType( OMX_U32 portIndex, OMX_VIDEO_CODINGTYPE compressionFormat, - OMX_COLOR_FORMATTYPE colorFormat) { + OMX_COLOR_FORMATTYPE colorFormat, + bool usingNativeBuffers) { OMX_VIDEO_PARAM_PORTFORMATTYPE format; InitOMXParams(&format); format.nPortIndex = portIndex; @@ -1981,10 +2194,10 @@ status_t ACodec::setVideoPortFormatType( // substitute back flexible color format to codec supported format OMX_U32 flexibleEquivalent; - if (compressionFormat == OMX_VIDEO_CodingUnused && - isFlexibleColorFormat( - mOMX, mNode, format.eColorFormat, &flexibleEquivalent) && - colorFormat == flexibleEquivalent) { + if (compressionFormat == OMX_VIDEO_CodingUnused + && isFlexibleColorFormat( + mOMX, mNode, format.eColorFormat, usingNativeBuffers, &flexibleEquivalent) + && colorFormat == flexibleEquivalent) { ALOGI("[%s] using color format %#x in place of %#x", mComponentName.c_str(), format.eColorFormat, colorFormat); colorFormat = format.eColorFormat; @@ -2028,18 +2241,66 @@ status_t ACodec::setVideoPortFormatType( return err; } -status_t ACodec::setSupportedOutputFormat() { - OMX_VIDEO_PARAM_PORTFORMATTYPE format; +// Set optimal output format. OMX component lists output formats in the order +// of preference, but this got more complicated since the introduction of flexible +// YUV formats. We support a legacy behavior for applications that do not use +// surface output, do not specify an output format, but expect a "usable" standard +// OMX format. SW readable and standard formats must be flex-YUV. +// +// Suggested preference order: +// - optimal format for texture rendering (mediaplayer behavior) +// - optimal SW readable & texture renderable format (flex-YUV support) +// - optimal SW readable non-renderable format (flex-YUV bytebuffer support) +// - legacy "usable" standard formats +// +// For legacy support, we prefer a standard format, but will settle for a SW readable +// flex-YUV format. +status_t ACodec::setSupportedOutputFormat(bool getLegacyFlexibleFormat) { + OMX_VIDEO_PARAM_PORTFORMATTYPE format, legacyFormat; InitOMXParams(&format); format.nPortIndex = kPortIndexOutput; - format.nIndex = 0; - status_t err = mOMX->getParameter( - mNode, OMX_IndexParamVideoPortFormat, - &format, sizeof(format)); - CHECK_EQ(err, (status_t)OK); - CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused); + InitOMXParams(&legacyFormat); + // this field will change when we find a suitable legacy format + legacyFormat.eColorFormat = OMX_COLOR_FormatUnused; + for (OMX_U32 index = 0; ; ++index) { + format.nIndex = index; + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoPortFormat, + &format, sizeof(format)); + if (err != OK) { + // no more formats, pick legacy format if found + if (legacyFormat.eColorFormat != OMX_COLOR_FormatUnused) { + memcpy(&format, &legacyFormat, sizeof(format)); + break; + } + return err; + } + if (format.eCompressionFormat != OMX_VIDEO_CodingUnused) { + return OMX_ErrorBadParameter; + } + if (!getLegacyFlexibleFormat) { + break; + } + // standard formats that were exposed to users before + if (format.eColorFormat == OMX_COLOR_FormatYUV420Planar + || format.eColorFormat == OMX_COLOR_FormatYUV420PackedPlanar + || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar + || format.eColorFormat == OMX_COLOR_FormatYUV420PackedSemiPlanar + || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar) { + break; + } + // find best legacy non-standard format + OMX_U32 flexibleEquivalent; + if (legacyFormat.eColorFormat == OMX_COLOR_FormatUnused + && isFlexibleColorFormat( + mOMX, mNode, format.eColorFormat, false /* usingNativeBuffers */, + &flexibleEquivalent) + && flexibleEquivalent == OMX_COLOR_FormatYUV420Flexible) { + memcpy(&legacyFormat, &format, sizeof(format)); + } + } return mOMX->setParameter( mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); @@ -2091,7 +2352,7 @@ static status_t GetMimeTypeForVideoCoding( } status_t ACodec::setupVideoDecoder( - const char *mime, const sp<AMessage> &msg) { + const char *mime, const sp<AMessage> &msg, bool haveNativeWindow) { int32_t width, height; if (!msg->findInt32("width", &width) || !msg->findInt32("height", &height)) { @@ -2117,22 +2378,31 @@ status_t ACodec::setupVideoDecoder( OMX_COLOR_FORMATTYPE colorFormat = static_cast<OMX_COLOR_FORMATTYPE>(tmp); err = setVideoPortFormatType( - kPortIndexOutput, OMX_VIDEO_CodingUnused, colorFormat); + kPortIndexOutput, OMX_VIDEO_CodingUnused, colorFormat, haveNativeWindow); if (err != OK) { ALOGW("[%s] does not support color format %d", mComponentName.c_str(), colorFormat); - err = setSupportedOutputFormat(); + err = setSupportedOutputFormat(!haveNativeWindow /* getLegacyFlexibleFormat */); } } else { - err = setSupportedOutputFormat(); + err = setSupportedOutputFormat(!haveNativeWindow /* getLegacyFlexibleFormat */); } if (err != OK) { return err; } + int32_t frameRateInt; + float frameRateFloat; + if (!msg->findFloat("frame-rate", &frameRateFloat)) { + if (!msg->findInt32("frame-rate", &frameRateInt)) { + frameRateInt = -1; + } + frameRateFloat = (float)frameRateInt; + } + err = setVideoFormatOnPort( - kPortIndexInput, width, height, compressionFormat); + kPortIndexInput, width, height, compressionFormat, frameRateFloat); if (err != OK) { return err; @@ -2896,7 +3166,8 @@ status_t ACodec::setupErrorCorrectionParameters() { status_t ACodec::setVideoFormatOnPort( OMX_U32 portIndex, - int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat) { + int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat, + float frameRate) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); def.nPortIndex = portIndex; @@ -2924,6 +3195,9 @@ status_t ACodec::setVideoFormatOnPort( if (portIndex == kPortIndexInput) { video_def->eCompressionFormat = compressionFormat; video_def->eColorFormat = OMX_COLOR_FormatUnused; + if (frameRate >= 0) { + video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f); + } } err = mOMX->setParameter( @@ -3006,7 +3280,6 @@ bool ACodec::allYourBuffersAreBelongToUs() { } void ACodec::deferMessage(const sp<AMessage> &msg) { - bool wasEmptyBefore = mDeferredQueue.empty(); mDeferredQueue.push_back(msg); } @@ -3036,7 +3309,8 @@ bool ACodec::describeDefaultColorFormat(DescribeColorFormatParams ¶ms) { if (fmt != OMX_COLOR_FormatYUV420Planar && fmt != OMX_COLOR_FormatYUV420PackedPlanar && fmt != OMX_COLOR_FormatYUV420SemiPlanar && - fmt != OMX_COLOR_FormatYUV420PackedSemiPlanar) { + fmt != OMX_COLOR_FormatYUV420PackedSemiPlanar && + fmt != HAL_PIXEL_FORMAT_YV12) { ALOGW("do not know color format 0x%x = %d", fmt, fmt); return false; } @@ -3065,8 +3339,31 @@ bool ACodec::describeDefaultColorFormat(DescribeColorFormatParams ¶ms) { image.mPlane[image.Y].mHorizSubsampling = 1; image.mPlane[image.Y].mVertSubsampling = 1; - switch (fmt) { - case OMX_COLOR_FormatYUV420Planar: // used for YV12 + switch ((int)fmt) { + case HAL_PIXEL_FORMAT_YV12: + if (params.bUsingNativeBuffers) { + size_t ystride = align(params.nStride, 16); + size_t cstride = align(params.nStride / 2, 16); + image.mPlane[image.Y].mRowInc = ystride; + + image.mPlane[image.V].mOffset = ystride * params.nSliceHeight; + image.mPlane[image.V].mColInc = 1; + image.mPlane[image.V].mRowInc = cstride; + image.mPlane[image.V].mHorizSubsampling = 2; + image.mPlane[image.V].mVertSubsampling = 2; + + image.mPlane[image.U].mOffset = image.mPlane[image.V].mOffset + + (cstride * params.nSliceHeight / 2); + image.mPlane[image.U].mColInc = 1; + image.mPlane[image.U].mRowInc = cstride; + image.mPlane[image.U].mHorizSubsampling = 2; + image.mPlane[image.U].mVertSubsampling = 2; + break; + } else { + // fall through as YV12 is used for YUV420Planar by some codecs + } + + case OMX_COLOR_FormatYUV420Planar: case OMX_COLOR_FormatYUV420PackedPlanar: image.mPlane[image.U].mOffset = params.nStride * params.nSliceHeight; image.mPlane[image.U].mColInc = 1; @@ -3126,7 +3423,7 @@ bool ACodec::describeColorFormat( // static bool ACodec::isFlexibleColorFormat( const sp<IOMX> &omx, IOMX::node_id node, - uint32_t colorFormat, OMX_U32 *flexibleEquivalent) { + uint32_t colorFormat, bool usingNativeBuffers, OMX_U32 *flexibleEquivalent) { DescribeColorFormatParams describeParams; InitOMXParams(&describeParams); describeParams.eColorFormat = (OMX_COLOR_FORMATTYPE)colorFormat; @@ -3135,6 +3432,7 @@ bool ACodec::isFlexibleColorFormat( describeParams.nFrameHeight = 128; describeParams.nStride = 128; describeParams.nSliceHeight = 128; + describeParams.bUsingNativeBuffers = (OMX_BOOL)usingNativeBuffers; CHECK(flexibleEquivalent != NULL); @@ -3192,20 +3490,30 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { notify->setInt32("slice-height", videoDef->nSliceHeight); notify->setInt32("color-format", videoDef->eColorFormat); - DescribeColorFormatParams describeParams; - InitOMXParams(&describeParams); - describeParams.eColorFormat = videoDef->eColorFormat; - describeParams.nFrameWidth = videoDef->nFrameWidth; - describeParams.nFrameHeight = videoDef->nFrameHeight; - describeParams.nStride = videoDef->nStride; - describeParams.nSliceHeight = videoDef->nSliceHeight; - - if (describeColorFormat(mOMX, mNode, describeParams)) { - notify->setBuffer( - "image-data", - ABuffer::CreateAsCopy( - &describeParams.sMediaImage, - sizeof(describeParams.sMediaImage))); + if (mNativeWindow == NULL) { + DescribeColorFormatParams describeParams; + InitOMXParams(&describeParams); + describeParams.eColorFormat = videoDef->eColorFormat; + describeParams.nFrameWidth = videoDef->nFrameWidth; + describeParams.nFrameHeight = videoDef->nFrameHeight; + describeParams.nStride = videoDef->nStride; + describeParams.nSliceHeight = videoDef->nSliceHeight; + describeParams.bUsingNativeBuffers = OMX_FALSE; + + if (describeColorFormat(mOMX, mNode, describeParams)) { + notify->setBuffer( + "image-data", + ABuffer::CreateAsCopy( + &describeParams.sMediaImage, + sizeof(describeParams.sMediaImage))); + + MediaImage *img = &describeParams.sMediaImage; + ALOGV("[%s] MediaImage { F(%zux%zu) @%zu+%zu+%zu @%zu+%zu+%zu @%zu+%zu+%zu }", + mComponentName.c_str(), img->mWidth, img->mHeight, + img->mPlane[0].mOffset, img->mPlane[0].mColInc, img->mPlane[0].mRowInc, + img->mPlane[1].mOffset, img->mPlane[1].mColInc, img->mPlane[1].mRowInc, + img->mPlane[2].mOffset, img->mPlane[2].mColInc, img->mPlane[2].mRowInc); + } } if (portIndex != kPortIndexOutput) { @@ -3302,9 +3610,12 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { break; } } - notify->setInt32("width", videoDef->nFrameWidth); notify->setInt32("height", videoDef->nFrameHeight); + ALOGV("[%s] %s format is %s", mComponentName.c_str(), + portIndex == kPortIndexInput ? "input" : "output", + notify->debugString().c_str()); + break; } @@ -3456,6 +3767,24 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { break; } + case OMX_AUDIO_CodingAndroidEAC3: + { + OMX_AUDIO_PARAM_ANDROID_EAC3TYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + CHECK_EQ((status_t)OK, mOMX->getParameter( + mNode, + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidEac3, + ¶ms, + sizeof(params))); + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_EAC3); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + case OMX_AUDIO_CodingAndroidOPUS: { OMX_AUDIO_PARAM_ANDROID_OPUSTYPE params; @@ -3500,6 +3829,23 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { break; } + case OMX_AUDIO_CodingGSMFR: + { + OMX_AUDIO_PARAM_MP3TYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + CHECK_EQ(mOMX->getParameter( + mNode, OMX_IndexParamAudioPcm, + ¶ms, sizeof(params)), + (status_t)OK); + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_MSGSM); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + default: ALOGE("UNKNOWN AUDIO CODING: %d\n", audioDef->eEncoding); TRESPASS(); @@ -3515,7 +3861,7 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { } void ACodec::sendFormatChange(const sp<AMessage> &reply) { - sp<AMessage> notify = mNotify->dup(); + sp<AMessage> notify = mBaseOutputFormat->dup(); notify->setInt32("what", kWhatOutputFormatChanged); CHECK_EQ(getPortFormat(kPortIndexOutput, notify), (status_t)OK); @@ -3641,7 +3987,6 @@ status_t ACodec::pushBlankBuffersToNativeWindow() { // on the screen and then been replaced, so an previous video frames are // guaranteed NOT to be currently displayed. for (int i = 0; i < numBufs + 1; i++) { - int fenceFd = -1; err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb); if (err != NO_ERROR) { ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)", @@ -3974,7 +4319,7 @@ void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) { info->mData->meta()->clear(); notify->setBuffer("buffer", info->mData); - sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id()); + sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec); reply->setInt32("buffer-id", info->mBufferID); notify->setMessage("reply", reply); @@ -4220,7 +4565,8 @@ bool ACodec::BaseState::onOMXFillBufferDone( case RESUBMIT_BUFFERS: { - if (rangeLength == 0 && !(flags & OMX_BUFFERFLAG_EOS)) { + if (rangeLength == 0 && (!(flags & OMX_BUFFERFLAG_EOS) + || mCodec->mPortEOS[kPortIndexOutput])) { ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID); @@ -4233,7 +4579,7 @@ bool ACodec::BaseState::onOMXFillBufferDone( } sp<AMessage> reply = - new AMessage(kWhatOutputBufferDrained, mCodec->id()); + new AMessage(kWhatOutputBufferDrained, mCodec); if (!mCodec->mSentFormat && rangeLength > 0) { mCodec->sendFormatChange(reply); @@ -4419,7 +4765,7 @@ void ACodec::UninitializedState::stateEntered() { ALOGV("Now uninitialized"); if (mDeathNotifier != NULL) { - mCodec->mOMX->asBinder()->unlinkToDeath(mDeathNotifier); + IInterface::asBinder(mCodec->mOMX)->unlinkToDeath(mDeathNotifier); mDeathNotifier.clear(); } @@ -4509,10 +4855,10 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { sp<IOMX> omx = client.interface(); - sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec->id()); + sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec); mDeathNotifier = new DeathNotifier(notify); - if (omx->asBinder()->linkToDeath(mDeathNotifier) != OK) { + if (IInterface::asBinder(omx)->linkToDeath(mDeathNotifier) != OK) { // This was a local binder, if it dies so do we, we won't care // about any notifications in the afterlife. mDeathNotifier.clear(); @@ -4584,7 +4930,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { return false; } - notify = new AMessage(kWhatOMXMessage, mCodec->id()); + notify = new AMessage(kWhatOMXMessage, mCodec); observer->setNotificationMessage(notify); mCodec->mComponentName = componentName; @@ -4592,6 +4938,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { if (componentName.endsWith(".secure")) { mCodec->mFlags |= kFlagIsSecure; + mCodec->mFlags |= kFlagIsGrallocUsageProtected; mCodec->mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown; } @@ -4630,6 +4977,7 @@ void ACodec::LoadedState::stateEntered() { mCodec->mRepeatFrameDelayUs = -1ll; mCodec->mInputFormat.clear(); mCodec->mOutputFormat.clear(); + mCodec->mBaseOutputFormat.clear(); if (mCodec->mShutdownInProgress) { bool keepComponentAllocated = mCodec->mKeepComponentAllocated; @@ -4733,20 +5081,6 @@ bool ACodec::LoadedState::onConfigureComponent( return false; } - sp<RefBase> obj; - if (msg->findObject("native-window", &obj) - && strncmp("OMX.google.", mCodec->mComponentName.c_str(), 11)) { - sp<NativeWindowWrapper> nativeWindow( - static_cast<NativeWindowWrapper *>(obj.get())); - CHECK(nativeWindow != NULL); - mCodec->mNativeWindow = nativeWindow->getNativeWindow(); - - native_window_set_scaling_mode( - mCodec->mNativeWindow.get(), - NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - } - CHECK_EQ((status_t)OK, mCodec->initNativeWindow()); - { sp<AMessage> notify = mCodec->mNotify->dup(); notify->setInt32("what", CodecBase::kWhatComponentConfigured); @@ -4802,6 +5136,21 @@ void ACodec::LoadedState::onCreateInputSurface( } } + if (err == OK && mCodec->mMaxFps > 0) { + err = mCodec->mOMX->setInternalOption( + mCodec->mNode, + kPortIndexInput, + IOMX::INTERNAL_OPTION_MAX_FPS, + &mCodec->mMaxFps, + sizeof(mCodec->mMaxFps)); + + if (err != OK) { + ALOGE("[%s] Unable to configure max fps (err %d)", + mCodec->mComponentName.c_str(), + err); + } + } + if (err == OK && mCodec->mTimePerCaptureUs > 0ll && mCodec->mTimePerFrameUs > 0ll) { int64_t timeLapse[2]; @@ -5356,6 +5705,7 @@ bool ACodec::OutputPortSettingsChangedState::onMessageReceived( case kWhatFlush: case kWhatShutdown: case kWhatResume: + case kWhatSetParameters: { if (msg->what() == kWhatResume) { ALOGV("[%s] Deferring resume", mCodec->mComponentName.c_str()); @@ -5671,7 +6021,7 @@ bool ACodec::FlushingState::onOMXEvent( case OMX_EventPortSettingsChanged: { - sp<AMessage> msg = new AMessage(kWhatOMXMessage, mCodec->id()); + sp<AMessage> msg = new AMessage(kWhatOMXMessage, mCodec); msg->setInt32("type", omx_message::EVENT); msg->setInt32("node", mCodec->mNode); msg->setInt32("event", event); diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp index 9aa7d95..f53d7f0 100644 --- a/media/libstagefright/AMRWriter.cpp +++ b/media/libstagefright/AMRWriter.cpp @@ -31,19 +31,6 @@ namespace android { -AMRWriter::AMRWriter(const char *filename) - : mFd(-1), - mInitCheck(NO_INIT), - mStarted(false), - mPaused(false), - mResumed(false) { - - mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); - if (mFd >= 0) { - mInitCheck = OK; - } -} - AMRWriter::AMRWriter(int fd) : mFd(dup(fd)), mInitCheck(mFd < 0? NO_INIT: OK), diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 7c4f92a..177293d 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -31,11 +31,13 @@ LOCAL_SRC_FILES:= \ MediaAdapter.cpp \ MediaBuffer.cpp \ MediaBufferGroup.cpp \ + MediaClock.cpp \ MediaCodec.cpp \ MediaCodecList.cpp \ MediaCodecSource.cpp \ MediaDefs.cpp \ MediaExtractor.cpp \ + MidiExtractor.cpp \ http/MediaHTTP.cpp \ MediaMuxer.cpp \ MediaSource.cpp \ @@ -45,6 +47,7 @@ LOCAL_SRC_FILES:= \ OMXClient.cpp \ OMXCodec.cpp \ OggExtractor.cpp \ + ProcessInfo.cpp \ SampleIterator.cpp \ SampleTable.cpp \ SkipCutBuffer.cpp \ @@ -68,11 +71,8 @@ LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/external/flac/include \ $(TOP)/external/tremolo \ - $(TOP)/external/openssl/include \ $(TOP)/external/libvpx/libwebm \ $(TOP)/system/netd/include \ - $(TOP)/external/icu/icu4c/source/common \ - $(TOP)/external/icu/icu4c/source/i18n \ LOCAL_SHARED_LIBRARIES := \ libbinder \ @@ -112,13 +112,13 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_id3 \ libFLAC \ libmedia_helper \ - libRScpp_static LOCAL_SHARED_LIBRARIES += \ libstagefright_enc_common \ libstagefright_avc_common \ libstagefright_foundation \ - libdl + libdl \ + libRScpp \ LOCAL_CFLAGS += -Wno-multichar diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 6a56729..87eef1e 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -878,11 +878,18 @@ void AwesomePlayer::onStreamDone() { return; } + if (mFlags & AUTO_LOOPING) { + audio_stream_type_t streamType = AUDIO_STREAM_MUSIC; + if (mAudioSink != NULL) { + streamType = mAudioSink->getAudioStreamType(); + } + if (streamType == AUDIO_STREAM_NOTIFICATION) { + ALOGW("disabling auto-loop for notification"); + modifyFlags(AUTO_LOOPING, CLEAR); + } + } if ((mFlags & LOOPING) - || ((mFlags & AUTO_LOOPING) - && (mAudioSink == NULL || mAudioSink->realtime()))) { - // Don't AUTO_LOOP if we're being recorded, since that cannot be - // turned off and recording would go on indefinitely. + || (mFlags & AUTO_LOOPING)) { seekTo_l(0); diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index c3a940a..ad12bdd 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -219,7 +219,7 @@ status_t CameraSource::isCameraAvailable( mCameraFlags |= FLAGS_HOT_CAMERA; mDeathNotifier = new DeathNotifier(); // isBinderAlive needs linkToDeath to work. - mCameraRecordingProxy->asBinder()->linkToDeath(mDeathNotifier); + IInterface::asBinder(mCameraRecordingProxy)->linkToDeath(mDeathNotifier); } mCamera->lock(); @@ -702,7 +702,7 @@ void CameraSource::releaseCamera() { { Mutex::Autolock autoLock(mLock); if (mCameraRecordingProxy != 0) { - mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier); + IInterface::asBinder(mCameraRecordingProxy)->unlinkToDeath(mDeathNotifier); mCameraRecordingProxy.clear(); } mCameraFlags = 0; @@ -825,7 +825,7 @@ status_t CameraSource::read( mFrameAvailableCondition.waitRelative(mLock, mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) { if (mCameraRecordingProxy != 0 && - !mCameraRecordingProxy->asBinder()->isBinderAlive()) { + !IInterface::asBinder(mCameraRecordingProxy)->isBinderAlive()) { ALOGW("camera recording proxy is gone"); return ERROR_END_OF_STREAM; } diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index c99db84..f7dcf35 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -22,6 +22,7 @@ #include "include/DRMExtractor.h" #include "include/FLACExtractor.h" #include "include/HTTPBase.h" +#include "include/MidiExtractor.h" #include "include/MP3Extractor.h" #include "include/MPEG2PSExtractor.h" #include "include/MPEG2TSExtractor.h" @@ -172,6 +173,7 @@ void DataSource::RegisterDefaultSniffers() { RegisterSniffer_l(SniffAAC); RegisterSniffer_l(SniffMPEG2PS); RegisterSniffer_l(SniffWVM); + RegisterSniffer_l(SniffMidi); char value[PROPERTY_VALUE_MAX]; if (property_get("drm.service.enabled", value, NULL) diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp index a7ca3da..f0db76b 100644 --- a/media/libstagefright/FileSource.cpp +++ b/media/libstagefright/FileSource.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "FileSource" +#include <utils/Log.h> + #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/FileSource.h> #include <sys/types.h> diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp index 32291c8..77a652a 100644 --- a/media/libstagefright/HTTPBase.cpp +++ b/media/libstagefright/HTTPBase.cpp @@ -36,7 +36,8 @@ HTTPBase::HTTPBase() mTotalTransferBytes(0), mPrevBandwidthMeasureTimeUs(0), mPrevEstimatedBandWidthKbps(0), - mBandWidthCollectFreqMs(5000) { + mBandWidthCollectFreqMs(5000), + mMaxBandwidthHistoryItems(100) { } void HTTPBase::addBandwidthMeasurement( @@ -50,7 +51,7 @@ void HTTPBase::addBandwidthMeasurement( mTotalTransferBytes += numBytes; mBandwidthHistory.push_back(entry); - if (++mNumBandwidthHistoryItems > 100) { + if (++mNumBandwidthHistoryItems > mMaxBandwidthHistoryItems) { BandwidthEntry *entry = &*mBandwidthHistory.begin(); mTotalTransferTimeUs -= entry->mDelayUs; mTotalTransferBytes -= entry->mNumBytes; @@ -74,7 +75,11 @@ void HTTPBase::addBandwidthMeasurement( bool HTTPBase::estimateBandwidth(int32_t *bandwidth_bps) { Mutex::Autolock autoLock(mLock); - if (mNumBandwidthHistoryItems < 2) { + // Do not do bandwidth estimation if we don't have enough samples, or + // total bytes download are too small (<64K). + // Bandwidth estimation from these samples can often shoot up and cause + // unwanted bw adaption behaviors. + if (mNumBandwidthHistoryItems < 2 || mTotalTransferBytes < 65536) { return false; } @@ -104,6 +109,10 @@ status_t HTTPBase::setBandwidthStatCollectFreq(int32_t freqMs) { return OK; } +void HTTPBase::setBandwidthHistorySize(size_t numHistoryItems) { + mMaxBandwidthHistoryItems = numHistoryItems; +} + // static void HTTPBase::RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag) { int res = qtaguid_tagSocket(sockfd, kTag, uid); diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp index 9856f92..ef07aa0 100644 --- a/media/libstagefright/MPEG2TSWriter.cpp +++ b/media/libstagefright/MPEG2TSWriter.cpp @@ -135,7 +135,7 @@ void MPEG2TSWriter::SourceInfo::start(const sp<AMessage> ¬ify) { mNotify = notify; - (new AMessage(kWhatStart, id()))->post(); + (new AMessage(kWhatStart, this))->post(); } void MPEG2TSWriter::SourceInfo::stop() { @@ -361,7 +361,7 @@ bool MPEG2TSWriter::SourceInfo::flushAACFrames() { } void MPEG2TSWriter::SourceInfo::readMore() { - (new AMessage(kWhatRead, id()))->post(); + (new AMessage(kWhatRead, this))->post(); } void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp<AMessage> &msg) { @@ -480,19 +480,6 @@ MPEG2TSWriter::MPEG2TSWriter(int fd) init(); } -MPEG2TSWriter::MPEG2TSWriter(const char *filename) - : mFile(fopen(filename, "wb")), - mWriteCookie(NULL), - mWriteFunc(NULL), - mStarted(false), - mNumSourcesDone(0), - mNumTSPacketsWritten(0), - mNumTSPacketsBeforeMeta(0), - mPATContinuityCounter(0), - mPMTContinuityCounter(0) { - init(); -} - MPEG2TSWriter::MPEG2TSWriter( void *cookie, ssize_t (*write)(void *cookie, const void *data, size_t size)) @@ -565,7 +552,7 @@ status_t MPEG2TSWriter::start(MetaData * /* param */) { for (size_t i = 0; i < mSources.size(); ++i) { sp<AMessage> notify = - new AMessage(kWhatSourceNotify, mReflector->id()); + new AMessage(kWhatSourceNotify, mReflector); notify->setInt32("source-index", i); diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index d922dc0..d0f42cc 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -265,6 +265,8 @@ status_t MPEG4DataSource::setCachedRange(off64_t offset, size_t size) { //////////////////////////////////////////////////////////////////////////////// +static const bool kUseHexDump = false; + static void hexdump(const void *_data, size_t size) { const uint8_t *data = (const uint8_t *)_data; size_t offset = 0; @@ -352,6 +354,8 @@ static bool AdjustChannelsAndRate(uint32_t fourcc, uint32_t *channels, uint32_t MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source) : mMoofOffset(0), + mMoofFound(false), + mMdatFound(false), mDataSource(source), mInitCheck(NO_INIT), mHasVideo(false), @@ -488,7 +492,9 @@ status_t MPEG4Extractor::readMetaData() { off64_t offset = 0; status_t err; - while (true) { + bool sawMoovOrSidx = false; + + while (!(sawMoovOrSidx && (mMdatFound || mMoofFound))) { off64_t orig_offset = offset; err = parseChunk(&offset, 0); @@ -500,23 +506,9 @@ status_t MPEG4Extractor::readMetaData() { ALOGE("did not advance: 0x%lld->0x%lld", orig_offset, offset); err = ERROR_MALFORMED; break; - } else if (err == OK) { - continue; - } - - uint32_t hdr[2]; - if (mDataSource->readAt(offset, hdr, 8) < 8) { - break; + } else if (err == UNKNOWN_ERROR) { + sawMoovOrSidx = true; } - uint32_t chunk_type = ntohl(hdr[1]); - 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; } if (mInitCheck == OK) { @@ -607,7 +599,6 @@ status_t MPEG4Extractor::parseDrmSINF( if (size < 0) { return ERROR_IO; } - int32_t classSize = size; data_offset += numOfBytes; while(size >= 11 ) { @@ -668,7 +659,6 @@ status_t MPEG4Extractor::parseDrmSINF( if (size < 0) { return ERROR_IO; } - classSize = size; data_offset += numOfBytes; while (size > 0) { @@ -766,7 +756,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } uint64_t chunk_size = ntohl(hdr[0]); - uint32_t chunk_type = ntohl(hdr[1]); + int32_t chunk_type = ntohl(hdr[1]); off64_t data_offset = *offset + 8; if (chunk_size == 1) { @@ -806,23 +796,23 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { MakeFourCCString(chunk_type, chunk); ALOGV("chunk: %s @ %lld, %d", chunk, *offset, depth); -#if 0 - static const char kWhitespace[] = " "; - const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth]; - printf("%sfound chunk '%s' of size %" PRIu64 "\n", indent, chunk, chunk_size); + if (kUseHexDump) { + static const char kWhitespace[] = " "; + const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth]; + printf("%sfound chunk '%s' of size %" PRIu64 "\n", indent, chunk, chunk_size); - char buffer[256]; - size_t n = chunk_size; - if (n > sizeof(buffer)) { - n = sizeof(buffer); - } - if (mDataSource->readAt(*offset, buffer, n) - < (ssize_t)n) { - return ERROR_IO; - } + char buffer[256]; + size_t n = chunk_size; + if (n > sizeof(buffer)) { + n = sizeof(buffer); + } + if (mDataSource->readAt(*offset, buffer, n) + < (ssize_t)n) { + return ERROR_IO; + } - hexdump(buffer, n); -#endif + hexdump(buffer, n); + } PathAdder autoAdder(&mPath, chunk_type); @@ -864,6 +854,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('s', 'c', 'h', 'i'): case FOURCC('e', 'd', 't', 's'): { + if (chunk_type == FOURCC('m', 'o', 'o', 'f') && !mMoofFound) { + // store the offset of the first segment + mMoofFound = true; + mMoofOffset = *offset; + } + if (chunk_type == FOURCC('s', 't', 'b', 'l')) { ALOGV("sampleTable chunk is %" PRIu64 " bytes long.", chunk_size); @@ -999,6 +995,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { int64_t duration; int32_t samplerate; + if (!mLastTrack) { + return ERROR_MALFORMED; + } if (mLastTrack->meta->findInt64(kKeyDuration, &duration) && mLastTrack->meta->findInt32(kKeySampleRate, &samplerate)) { @@ -1141,7 +1140,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { { *offset += chunk_size; - if (chunk_data_size < 4) { + if (chunk_data_size < 4 || mLastTrack == NULL) { return ERROR_MALFORMED; } @@ -1298,7 +1297,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } - uint16_t data_ref_index = U16_AT(&buffer[6]); + uint16_t data_ref_index __unused = U16_AT(&buffer[6]); uint32_t num_channels = U16_AT(&buffer[16]); uint16_t sample_size = U16_AT(&buffer[18]); @@ -1351,7 +1350,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } - uint16_t data_ref_index = U16_AT(&buffer[6]); + uint16_t data_ref_index __unused = U16_AT(&buffer[6]); uint16_t width = U16_AT(&buffer[6 + 18]); uint16_t height = U16_AT(&buffer[6 + 20]); @@ -1533,13 +1532,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - // @xyz - case FOURCC('\xA9', 'x', 'y', 'z'): + // ©xyz + case FOURCC(0xA9, '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/", + // 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, // "\0x15\xc7" is the language code = en, and "0+0" is a // location (string) value with longitude = 0 and latitude = 0. @@ -1827,6 +1826,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('m', 'd', 'a', 't'): { ALOGV("mdat chunk, drm: %d", mIsDrm); + + mMdatFound = true; + if (!mIsDrm) { *offset += chunk_size; break; @@ -1867,7 +1869,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (chunk_data_size < 24) { return ERROR_IO; } - uint32_t duration; Trex trex; if (!mDataSource->getUInt32(data_offset + 4, &trex.track_ID) || !mDataSource->getUInt32(data_offset + 8, &trex.default_sample_description_index) || @@ -2139,7 +2140,7 @@ status_t MPEG4Extractor::parseTrackHeader( return ERROR_IO; } - uint64_t ctime, mtime, duration; + uint64_t ctime __unused, mtime __unused, duration __unused; int32_t id; if (version == 1) { @@ -2161,12 +2162,13 @@ status_t MPEG4Extractor::parseTrackHeader( size_t matrixOffset = dynSize + 16; int32_t a00 = U32_AT(&buffer[matrixOffset]); int32_t a01 = U32_AT(&buffer[matrixOffset + 4]); - int32_t dx = U32_AT(&buffer[matrixOffset + 8]); int32_t a10 = U32_AT(&buffer[matrixOffset + 12]); int32_t a11 = U32_AT(&buffer[matrixOffset + 16]); - int32_t dy = U32_AT(&buffer[matrixOffset + 20]); #if 0 + int32_t dx = U32_AT(&buffer[matrixOffset + 8]); + int32_t dy = U32_AT(&buffer[matrixOffset + 20]); + ALOGI("x' = %.2f * x + %.2f * y + %.2f", a00 / 65536.0f, a01 / 65536.0f, dx / 65536.0f); ALOGI("y' = %.2f * x + %.2f * y + %.2f", @@ -2227,7 +2229,7 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) { char chunk[5]; MakeFourCCString(mPath[4], chunk); ALOGV("meta: %s @ %lld", chunk, offset); - switch (mPath[4]) { + switch ((int32_t)mPath[4]) { case FOURCC(0xa9, 'a', 'l', 'b'): { metadataKey = kKeyAlbum; @@ -2718,10 +2720,10 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( return ERROR_MALFORMED; } -#if 0 - printf("ESD of size %d\n", csd_size); - hexdump(csd, csd_size); -#endif + if (kUseHexDump) { + printf("ESD of size %d\n", csd_size); + hexdump(csd, csd_size); + } if (csd_size == 0) { // There's no further information, i.e. no codec specific data @@ -2772,7 +2774,7 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( 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; + int32_t extSampleRate __unused; if (extFreqIndex == 15) { if (csd_size < 8) { return ERROR_MALFORMED; @@ -2822,12 +2824,12 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( 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 frameLengthFlag __unused = br.getBits(1); const int32_t dependsOnCoreCoder = br.getBits(1); if (dependsOnCoreCoder ) { - const int32_t coreCoderDelay = br.getBits(14); + const int32_t coreCoderDelay __unused = br.getBits(14); } int32_t extensionFlag = -1; @@ -2856,54 +2858,54 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( 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 ElementInstanceTag __unused = br.getBits(4); + const int32_t Profile __unused = br.getBits(2); + const int32_t SamplingFrequencyIndex __unused = 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 NumAssocDataElements __unused = br.getBits(3); + const int32_t NumValidCcElements __unused = br.getBits(4); const int32_t MonoMixdownPresent = br.getBits(1); if (MonoMixdownPresent != 0) { - const int32_t MonoMixdownElementNumber = br.getBits(4); + const int32_t MonoMixdownElementNumber __unused = br.getBits(4); } const int32_t StereoMixdownPresent = br.getBits(1); if (StereoMixdownPresent != 0) { - const int32_t StereoMixdownElementNumber = br.getBits(4); + const int32_t StereoMixdownElementNumber __unused = 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); + const int32_t MatrixMixdownIndex __unused = br.getBits(2); + const int32_t PseudoSurroundEnable __unused = 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); + const int32_t FrontElementTagSelect __unused = 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); + const int32_t SideElementTagSelect __unused = 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); + const int32_t BackElementTagSelect __unused = br.getBits(4); channelsNum += BackElementIsCpe ? 2 : 1; } channelsEffectiveNum = channelsNum; for (i=0; i < NumLfeChannelElements; i++) { - const int32_t LfeElementTagSelect = br.getBits(4); + const int32_t LfeElementTagSelect __unused = br.getBits(4); channelsNum += 1; } ALOGV("mpeg4 audio channelsNum = %d", channelsNum); diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 9f20b1d..6f6e362 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -29,6 +29,7 @@ #include <utils/Log.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MPEG4Writer.h> #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MetaData.h> @@ -62,6 +63,14 @@ static const uint8_t kNalUnitTypeSeqParamSet = 0x07; static const uint8_t kNalUnitTypePicParamSet = 0x08; static const int64_t kInitialDelayTimeUs = 700000LL; +static const char kMetaKey_Model[] = "com.android.model"; +static const char kMetaKey_Version[] = "com.android.version"; +static const char kMetaKey_Build[] = "com.android.build"; +static const char kMetaKey_CaptureFps[] = "com.android.capture.fps"; + +/* uncomment to include model and build in meta */ +//#define SHOW_MODEL_BUILD 1 + class MPEG4Writer::Track { public: Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId); @@ -345,31 +354,6 @@ private: Track &operator=(const Track &); }; -MPEG4Writer::MPEG4Writer(const char *filename) - : mFd(-1), - mInitCheck(NO_INIT), - mIsRealTimeRecording(true), - mUse4ByteNalLength(true), - mUse32BitOffset(true), - mIsFileSizeLimitExplicitlyRequested(false), - mPaused(false), - mStarted(false), - mWriterThreadStarted(false), - mOffset(0), - mMdatOffset(0), - mEstimatedMoovBoxSize(0), - mInterleaveDurationUs(1000000), - mLatitudex10000(0), - mLongitudex10000(0), - mAreGeoTagsAvailable(false), - mStartTimeOffsetMs(-1) { - - mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); - if (mFd >= 0) { - mInitCheck = OK; - } -} - MPEG4Writer::MPEG4Writer(int fd) : mFd(dup(fd)), mInitCheck(mFd < 0? NO_INIT: OK), @@ -383,11 +367,14 @@ MPEG4Writer::MPEG4Writer(int fd) mOffset(0), mMdatOffset(0), mEstimatedMoovBoxSize(0), + mMoovExtraSize(0), mInterleaveDurationUs(1000000), mLatitudex10000(0), mLongitudex10000(0), mAreGeoTagsAvailable(false), + mMetaKeys(new AMessage()), mStartTimeOffsetMs(-1) { + addDeviceMeta(); } MPEG4Writer::~MPEG4Writer() { @@ -507,6 +494,34 @@ status_t MPEG4Writer::startTracks(MetaData *params) { return OK; } +void MPEG4Writer::addDeviceMeta() { + // add device info and estimate space in 'moov' + char val[PROPERTY_VALUE_MAX]; + size_t n; + // meta size is estimated by adding up the following: + // - meta header structures, which occur only once (total 66 bytes) + // - size for each key, which consists of a fixed header (32 bytes), + // plus key length and data length. + mMoovExtraSize += 66; + if (property_get("ro.build.version.release", val, NULL) + && (n = strlen(val)) > 0) { + mMetaKeys->setString(kMetaKey_Version, val, n + 1); + mMoovExtraSize += sizeof(kMetaKey_Version) + n + 32; + } +#ifdef SHOW_MODEL_BUILD + if (property_get("ro.product.model", val, NULL) + && (n = strlen(val)) > 0) { + mMetaKeys->setString(kMetaKey_Model, val, n + 1); + mMoovExtraSize += sizeof(kMetaKey_Model) + n + 32; + } + if (property_get("ro.build.display.id", val, NULL) + && (n = strlen(val)) > 0) { + mMetaKeys->setString(kMetaKey_Build, val, n + 1); + mMoovExtraSize += sizeof(kMetaKey_Build) + n + 32; + } +#endif +} + int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) { // This implementation is highly experimental/heurisitic. // @@ -560,6 +575,9 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) { size = MAX_MOOV_BOX_SIZE; } + // Account for the extra stuff (Geo, meta keys, etc.) + size += mMoovExtraSize; + ALOGI("limits: %" PRId64 "/%" PRId64 " bytes/us, bit rate: %d bps and the" " estimated moov size %" PRId64 " bytes", mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size); @@ -971,6 +989,7 @@ void MPEG4Writer::writeMoovBox(int64_t durationUs) { if (mAreGeoTagsAvailable) { writeUdtaBox(); } + writeMetaBox(); int32_t id = 1; for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it, ++id) { @@ -1140,6 +1159,14 @@ size_t MPEG4Writer::write( return bytes; } +void MPEG4Writer::beginBox(uint32_t id) { + mBoxes.push_back(mWriteMoovBoxToMemory? + mMoovBoxBufferOffset: mOffset); + + writeInt32(0); + writeInt32(id); +} + void MPEG4Writer::beginBox(const char *fourcc) { CHECK_EQ(strlen(fourcc), 4); @@ -1264,6 +1291,18 @@ status_t MPEG4Writer::setGeoData(int latitudex10000, int longitudex10000) { mLatitudex10000 = latitudex10000; mLongitudex10000 = longitudex10000; mAreGeoTagsAvailable = true; + mMoovExtraSize += 30; + return OK; +} + +status_t MPEG4Writer::setCaptureRate(float captureFps) { + if (captureFps <= 0.0f) { + return BAD_VALUE; + } + + mMetaKeys->setFloat(kMetaKey_CaptureFps, captureFps); + mMoovExtraSize += sizeof(kMetaKey_CaptureFps) + 4 + 32; + return OK; } @@ -3095,6 +3134,103 @@ void MPEG4Writer::writeUdtaBox() { endBox(); } +void MPEG4Writer::writeHdlr() { + beginBox("hdlr"); + writeInt32(0); // Version, Flags + writeInt32(0); // Predefined + writeFourcc("mdta"); + writeInt32(0); // Reserved[0] + writeInt32(0); // Reserved[1] + writeInt32(0); // Reserved[2] + writeInt8(0); // Name (empty) + endBox(); +} + +void MPEG4Writer::writeKeys() { + size_t count = mMetaKeys->countEntries(); + + beginBox("keys"); + writeInt32(0); // Version, Flags + writeInt32(count); // Entry_count + for (size_t i = 0; i < count; i++) { + AMessage::Type type; + const char *key = mMetaKeys->getEntryNameAt(i, &type); + size_t n = strlen(key); + writeInt32(n + 8); + writeFourcc("mdta"); + write(key, n); // write without the \0 + } + endBox(); +} + +void MPEG4Writer::writeIlst() { + size_t count = mMetaKeys->countEntries(); + + beginBox("ilst"); + for (size_t i = 0; i < count; i++) { + beginBox(i + 1); // key id (1-based) + beginBox("data"); + AMessage::Type type; + const char *key = mMetaKeys->getEntryNameAt(i, &type); + switch (type) { + case AMessage::kTypeString: + { + AString val; + CHECK(mMetaKeys->findString(key, &val)); + writeInt32(1); // type = UTF8 + writeInt32(0); // default country/language + write(val.c_str(), strlen(val.c_str())); // write without \0 + break; + } + + case AMessage::kTypeFloat: + { + float val; + CHECK(mMetaKeys->findFloat(key, &val)); + writeInt32(23); // type = float32 + writeInt32(0); // default country/language + writeInt32(*reinterpret_cast<int32_t *>(&val)); + break; + } + + case AMessage::kTypeInt32: + { + int32_t val; + CHECK(mMetaKeys->findInt32(key, &val)); + writeInt32(67); // type = signed int32 + writeInt32(0); // default country/language + writeInt32(val); + break; + } + + default: + { + ALOGW("Unsupported key type, writing 0 instead"); + writeInt32(77); // type = unsigned int32 + writeInt32(0); // default country/language + writeInt32(0); + break; + } + } + endBox(); // data + endBox(); // key id + } + endBox(); // ilst +} + +void MPEG4Writer::writeMetaBox() { + size_t count = mMetaKeys->countEntries(); + if (count == 0) { + return; + } + + beginBox("meta"); + writeHdlr(); + writeKeys(); + writeIlst(); + endBox(); +} + /* * Geodata is stored according to ISO-6709 standard. */ diff --git a/media/libstagefright/MediaClock.cpp b/media/libstagefright/MediaClock.cpp new file mode 100644 index 0000000..38db5e4 --- /dev/null +++ b/media/libstagefright/MediaClock.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "MediaClock" +#include <utils/Log.h> + +#include <media/stagefright/MediaClock.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALooper.h> + +namespace android { + +MediaClock::MediaClock() + : mAnchorTimeMediaUs(-1), + mAnchorTimeRealUs(-1), + mMaxTimeMediaUs(INT64_MAX), + mStartingTimeMediaUs(-1), + mPlaybackRate(1.0) { +} + +MediaClock::~MediaClock() { +} + +void MediaClock::setStartingTimeMedia(int64_t startingTimeMediaUs) { + Mutex::Autolock autoLock(mLock); + mStartingTimeMediaUs = startingTimeMediaUs; +} + +void MediaClock::clearAnchor() { + Mutex::Autolock autoLock(mLock); + mAnchorTimeMediaUs = -1; + mAnchorTimeRealUs = -1; +} + +void MediaClock::updateAnchor( + int64_t anchorTimeMediaUs, + int64_t anchorTimeRealUs, + int64_t maxTimeMediaUs) { + if (anchorTimeMediaUs < 0 || anchorTimeRealUs < 0) { + ALOGW("reject anchor time since it is negative."); + return; + } + + Mutex::Autolock autoLock(mLock); + int64_t nowUs = ALooper::GetNowUs(); + int64_t nowMediaUs = + anchorTimeMediaUs + (nowUs - anchorTimeRealUs) * (double)mPlaybackRate; + if (nowMediaUs < 0) { + ALOGW("reject anchor time since it leads to negative media time."); + return; + } + mAnchorTimeRealUs = nowUs; + mAnchorTimeMediaUs = nowMediaUs; + mMaxTimeMediaUs = maxTimeMediaUs; +} + +void MediaClock::updateMaxTimeMedia(int64_t maxTimeMediaUs) { + Mutex::Autolock autoLock(mLock); + mMaxTimeMediaUs = maxTimeMediaUs; +} + +void MediaClock::setPlaybackRate(float rate) { + CHECK_GE(rate, 0.0); + Mutex::Autolock autoLock(mLock); + if (mAnchorTimeRealUs == -1) { + mPlaybackRate = rate; + return; + } + + int64_t nowUs = ALooper::GetNowUs(); + mAnchorTimeMediaUs += (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate; + if (mAnchorTimeMediaUs < 0) { + ALOGW("setRate: anchor time should not be negative, set to 0."); + mAnchorTimeMediaUs = 0; + } + mAnchorTimeRealUs = nowUs; + mPlaybackRate = rate; +} + +status_t MediaClock::getMediaTime( + int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) { + Mutex::Autolock autoLock(mLock); + return getMediaTime_l(realUs, outMediaUs, allowPastMaxTime); +} + +status_t MediaClock::getMediaTime_l( + int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) { + if (mAnchorTimeRealUs == -1) { + return NO_INIT; + } + + int64_t mediaUs = mAnchorTimeMediaUs + + (realUs - mAnchorTimeRealUs) * (double)mPlaybackRate; + if (mediaUs > mMaxTimeMediaUs && !allowPastMaxTime) { + mediaUs = mMaxTimeMediaUs; + } + if (mediaUs < mStartingTimeMediaUs) { + mediaUs = mStartingTimeMediaUs; + } + if (mediaUs < 0) { + mediaUs = 0; + } + *outMediaUs = mediaUs; + return OK; +} + +status_t MediaClock::getRealTimeFor(int64_t targetMediaUs, int64_t *outRealUs) { + Mutex::Autolock autoLock(mLock); + if (mPlaybackRate == 0.0) { + return NO_INIT; + } + + int64_t nowUs = ALooper::GetNowUs(); + int64_t nowMediaUs; + status_t status = + getMediaTime_l(nowUs, &nowMediaUs, true /* allowPastMaxTime */); + if (status != OK) { + return status; + } + *outRealUs = (targetMediaUs - nowMediaUs) / (double)mPlaybackRate + nowUs; + return OK; +} + +} // namespace android diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 673d375..0597f1d 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -174,7 +174,7 @@ status_t MediaCodec::PostAndAwaitResponse( } // static -void MediaCodec::PostReplyWithError(int32_t replyID, int32_t err) { +void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) { sp<AMessage> response = new AMessage; response->setInt32("err", err); response->postReply(replyID); @@ -237,9 +237,9 @@ status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { mLooper->registerHandler(this); - mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, id())); + mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, this)); - sp<AMessage> msg = new AMessage(kWhatInit, id()); + sp<AMessage> msg = new AMessage(kWhatInit, this); msg->setString("name", name); msg->setInt32("nameIsType", nameIsType); @@ -252,7 +252,7 @@ status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { } status_t MediaCodec::setCallback(const sp<AMessage> &callback) { - sp<AMessage> msg = new AMessage(kWhatSetCallback, id()); + sp<AMessage> msg = new AMessage(kWhatSetCallback, this); msg->setMessage("callback", callback); sp<AMessage> response; @@ -264,7 +264,7 @@ status_t MediaCodec::configure( const sp<Surface> &nativeWindow, const sp<ICrypto> &crypto, uint32_t flags) { - sp<AMessage> msg = new AMessage(kWhatConfigure, id()); + sp<AMessage> msg = new AMessage(kWhatConfigure, this); msg->setMessage("format", format); msg->setInt32("flags", flags); @@ -298,7 +298,7 @@ status_t MediaCodec::configure( status_t MediaCodec::createInputSurface( sp<IGraphicBufferProducer>* bufferProducer) { - sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, id()); + sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, this); sp<AMessage> response; status_t err = PostAndAwaitResponse(msg, &response); @@ -317,21 +317,21 @@ status_t MediaCodec::createInputSurface( } status_t MediaCodec::start() { - sp<AMessage> msg = new AMessage(kWhatStart, id()); + sp<AMessage> msg = new AMessage(kWhatStart, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } status_t MediaCodec::stop() { - sp<AMessage> msg = new AMessage(kWhatStop, id()); + sp<AMessage> msg = new AMessage(kWhatStop, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } status_t MediaCodec::release() { - sp<AMessage> msg = new AMessage(kWhatRelease, id()); + sp<AMessage> msg = new AMessage(kWhatRelease, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); @@ -383,7 +383,7 @@ status_t MediaCodec::queueInputBuffer( errorDetailMsg->clear(); } - sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id()); + sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this); msg->setSize("index", index); msg->setSize("offset", offset); msg->setSize("size", size); @@ -410,7 +410,7 @@ status_t MediaCodec::queueSecureInputBuffer( errorDetailMsg->clear(); } - sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id()); + sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this); msg->setSize("index", index); msg->setSize("offset", offset); msg->setPointer("subSamples", (void *)subSamples); @@ -429,7 +429,7 @@ status_t MediaCodec::queueSecureInputBuffer( } status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { - sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, id()); + sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, this); msg->setInt64("timeoutUs", timeoutUs); sp<AMessage> response; @@ -450,7 +450,7 @@ status_t MediaCodec::dequeueOutputBuffer( int64_t *presentationTimeUs, uint32_t *flags, int64_t timeoutUs) { - sp<AMessage> msg = new AMessage(kWhatDequeueOutputBuffer, id()); + sp<AMessage> msg = new AMessage(kWhatDequeueOutputBuffer, this); msg->setInt64("timeoutUs", timeoutUs); sp<AMessage> response; @@ -469,7 +469,7 @@ status_t MediaCodec::dequeueOutputBuffer( } status_t MediaCodec::renderOutputBufferAndRelease(size_t index) { - sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id()); + sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, this); msg->setSize("index", index); msg->setInt32("render", true); @@ -478,7 +478,7 @@ status_t MediaCodec::renderOutputBufferAndRelease(size_t index) { } status_t MediaCodec::renderOutputBufferAndRelease(size_t index, int64_t timestampNs) { - sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id()); + sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, this); msg->setSize("index", index); msg->setInt32("render", true); msg->setInt64("timestampNs", timestampNs); @@ -488,7 +488,7 @@ status_t MediaCodec::renderOutputBufferAndRelease(size_t index, int64_t timestam } status_t MediaCodec::releaseOutputBuffer(size_t index) { - sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id()); + sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, this); msg->setSize("index", index); sp<AMessage> response; @@ -496,14 +496,14 @@ status_t MediaCodec::releaseOutputBuffer(size_t index) { } status_t MediaCodec::signalEndOfInputStream() { - sp<AMessage> msg = new AMessage(kWhatSignalEndOfInputStream, id()); + sp<AMessage> msg = new AMessage(kWhatSignalEndOfInputStream, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const { - sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, id()); + sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, this); sp<AMessage> response; status_t err; @@ -517,7 +517,7 @@ status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const { } status_t MediaCodec::getInputFormat(sp<AMessage> *format) const { - sp<AMessage> msg = new AMessage(kWhatGetInputFormat, id()); + sp<AMessage> msg = new AMessage(kWhatGetInputFormat, this); sp<AMessage> response; status_t err; @@ -531,7 +531,7 @@ status_t MediaCodec::getInputFormat(sp<AMessage> *format) const { } status_t MediaCodec::getName(AString *name) const { - sp<AMessage> msg = new AMessage(kWhatGetName, id()); + sp<AMessage> msg = new AMessage(kWhatGetName, this); sp<AMessage> response; status_t err; @@ -545,7 +545,7 @@ status_t MediaCodec::getName(AString *name) const { } status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const { - sp<AMessage> msg = new AMessage(kWhatGetBuffers, id()); + sp<AMessage> msg = new AMessage(kWhatGetBuffers, this); msg->setInt32("portIndex", kPortIndexInput); msg->setPointer("buffers", buffers); @@ -554,7 +554,7 @@ status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const { } status_t MediaCodec::getOutputBuffers(Vector<sp<ABuffer> > *buffers) const { - sp<AMessage> msg = new AMessage(kWhatGetBuffers, id()); + sp<AMessage> msg = new AMessage(kWhatGetBuffers, this); msg->setInt32("portIndex", kPortIndexOutput); msg->setPointer("buffers", buffers); @@ -612,20 +612,20 @@ status_t MediaCodec::getBufferAndFormat( } status_t MediaCodec::flush() { - sp<AMessage> msg = new AMessage(kWhatFlush, id()); + sp<AMessage> msg = new AMessage(kWhatFlush, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } status_t MediaCodec::requestIDRFrame() { - (new AMessage(kWhatRequestIDRFrame, id()))->post(); + (new AMessage(kWhatRequestIDRFrame, this))->post(); return OK; } void MediaCodec::requestActivityNotification(const sp<AMessage> ¬ify) { - sp<AMessage> msg = new AMessage(kWhatRequestActivityNotification, id()); + sp<AMessage> msg = new AMessage(kWhatRequestActivityNotification, this); msg->setMessage("notify", notify); msg->post(); } @@ -650,7 +650,7 @@ void MediaCodec::cancelPendingDequeueOperations() { } } -bool MediaCodec::handleDequeueInputBuffer(uint32_t replyID, bool newRequest) { +bool MediaCodec::handleDequeueInputBuffer(const sp<AReplyToken> &replyID, bool newRequest) { if (!isExecuting() || (mFlags & kFlagIsAsync) || (newRequest && (mFlags & kFlagDequeueInputPending))) { PostReplyWithError(replyID, INVALID_OPERATION); @@ -674,7 +674,7 @@ bool MediaCodec::handleDequeueInputBuffer(uint32_t replyID, bool newRequest) { return true; } -bool MediaCodec::handleDequeueOutputBuffer(uint32_t replyID, bool newRequest) { +bool MediaCodec::handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool newRequest) { sp<AMessage> response = new AMessage; if (!isExecuting() || (mFlags & kFlagIsAsync) @@ -879,9 +879,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->findString("componentName", &mComponentName)); if (mComponentName.startsWith("OMX.google.")) { - mFlags |= kFlagIsSoftwareCodec; + mFlags |= kFlagUsesSoftwareRenderer; } else { - mFlags &= ~kFlagIsSoftwareCodec; + mFlags &= ~kFlagUsesSoftwareRenderer; } if (mComponentName.endsWith(".secure")) { @@ -904,6 +904,11 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->findMessage("input-format", &mInputFormat)); CHECK(msg->findMessage("output-format", &mOutputFormat)); + int32_t usingSwRenderer; + if (mOutputFormat->findInt32("using-sw-renderer", &usingSwRenderer) + && usingSwRenderer) { + mFlags |= kFlagUsesSoftwareRenderer; + } setState(CONFIGURED); (new AMessage)->postReply(mReplyID); break; @@ -999,7 +1004,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { if (mSoftRenderer == NULL && mNativeWindow != NULL && - (mFlags & kFlagIsSoftwareCodec)) { + (mFlags & kFlagUsesSoftwareRenderer)) { AString mime; CHECK(msg->findString("mime", &mime)); @@ -1024,11 +1029,13 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { // Notify mCrypto of video resolution changes if (mCrypto != NULL) { - int32_t height, width; - if (mOutputFormat->findInt32("height", &height) && - mOutputFormat->findInt32("width", &width)) { - mCrypto->notifyResolution(width, height); - } + int32_t left, top, right, bottom, width, height; + if (mOutputFormat->findRect("crop", &left, &top, &right, &bottom)) { + mCrypto->notifyResolution(right - left + 1, bottom - top + 1); + } else if (mOutputFormat->findInt32("width", &width) + && mOutputFormat->findInt32("height", &height)) { + mCrypto->notifyResolution(width, height); + } } break; @@ -1191,7 +1198,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatInit: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (mState != UNINITIALIZED) { @@ -1227,7 +1234,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatSetCallback: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (mState == UNINITIALIZED @@ -1259,7 +1266,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatConfigure: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (mState != INITIALIZED) { @@ -1316,7 +1323,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatCreateInputSurface: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); // Must be configured, but can't have been started yet. @@ -1332,12 +1339,14 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatStart: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (mState == FLUSHED) { + setState(STARTED); mCodec->signalResume(); PostReplyWithError(replyID, OK); + break; } else if (mState != CONFIGURED) { PostReplyWithError(replyID, INVALID_OPERATION); break; @@ -1356,12 +1365,15 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { State targetState = (msg->what() == kWhatStop) ? INITIALIZED : UNINITIALIZED; - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); - if (!(mFlags & kFlagIsComponentAllocated) && mState != INITIALIZED + if (!((mFlags & kFlagIsComponentAllocated) && targetState == UNINITIALIZED) // See 1 + && mState != INITIALIZED && mState != CONFIGURED && !isExecuting()) { - // We may be in "UNINITIALIZED" state already and + // 1) Permit release to shut down the component if allocated. + // + // 2) We may be in "UNINITIALIZED" state already and // also shutdown the encoder/decoder without the // client being aware of this if media server died while // we were being stopped. The client would assume that @@ -1401,7 +1413,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatDequeueInputBuffer: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (mFlags & kFlagIsAsync) { @@ -1433,7 +1445,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { if (timeoutUs > 0ll) { sp<AMessage> timeoutMsg = - new AMessage(kWhatDequeueInputTimedOut, id()); + new AMessage(kWhatDequeueInputTimedOut, this); timeoutMsg->setInt32( "generation", ++mDequeueInputTimeoutGeneration); timeoutMsg->post(timeoutUs); @@ -1462,7 +1474,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatQueueInputBuffer: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (!isExecuting()) { @@ -1481,7 +1493,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatDequeueOutputBuffer: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (mFlags & kFlagIsAsync) { @@ -1507,7 +1519,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { if (timeoutUs > 0ll) { sp<AMessage> timeoutMsg = - new AMessage(kWhatDequeueOutputTimedOut, id()); + new AMessage(kWhatDequeueOutputTimedOut, this); timeoutMsg->setInt32( "generation", ++mDequeueOutputTimeoutGeneration); timeoutMsg->post(timeoutUs); @@ -1536,7 +1548,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatReleaseOutputBuffer: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (!isExecuting()) { @@ -1555,7 +1567,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatSignalEndOfInputStream: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (!isExecuting()) { @@ -1573,7 +1585,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatGetBuffers: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (!isExecuting() || (mFlags & kFlagIsAsync)) { @@ -1607,7 +1619,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatFlush: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (!isExecuting()) { @@ -1633,7 +1645,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { sp<AMessage> format = (msg->what() == kWhatGetOutputFormat ? mOutputFormat : mInputFormat); - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if ((mState != CONFIGURED && mState != STARTING && @@ -1670,7 +1682,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatGetName: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (mComponentName.empty()) { @@ -1686,7 +1698,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case kWhatSetParameters: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); sp<AMessage> params; @@ -1709,7 +1721,7 @@ void MediaCodec::extractCSD(const sp<AMessage> &format) { size_t i = 0; for (;;) { sp<ABuffer> csd; - if (!format->findBuffer(StringPrintf("csd-%u", i).c_str(), &csd)) { + if (!format->findBuffer(AStringPrintf("csd-%u", i).c_str(), &csd)) { break; } @@ -1740,7 +1752,7 @@ status_t MediaCodec::queueCSDInputBuffer(size_t bufferIndex) { AString errorDetailMsg; - sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id()); + sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this); msg->setSize("index", bufferIndex); msg->setSize("offset", 0); msg->setSize("size", csd->size()); @@ -2195,7 +2207,7 @@ void MediaCodec::postActivityNotificationIfPossible() { } status_t MediaCodec::setParameters(const sp<AMessage> ¶ms) { - sp<AMessage> msg = new AMessage(kWhatSetParameters, id()); + sp<AMessage> msg = new AMessage(kWhatSetParameters, this); msg->setMessage("params", params); sp<AMessage> response; @@ -2232,7 +2244,7 @@ status_t MediaCodec::amendOutputFormatWithCodecSpecificData( memcpy(csd->data() + 4, nalStart, nalSize); mOutputFormat->setBuffer( - StringPrintf("csd-%u", csdIndex).c_str(), csd); + AStringPrintf("csd-%u", csdIndex).c_str(), csd); ++csdIndex; } diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index 5b8be46..cf6e937 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -62,6 +62,14 @@ static Mutex sRemoteInitMutex; sp<IMediaCodecList> MediaCodecList::sRemoteList; +sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver; + +void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) { + Mutex::Autolock _l(sRemoteInitMutex); + sRemoteList.clear(); + sBinderDeathObserver.clear(); +} + // static sp<IMediaCodecList> MediaCodecList::getInstance() { Mutex::Autolock _l(sRemoteInitMutex); @@ -72,8 +80,11 @@ sp<IMediaCodecList> MediaCodecList::getInstance() { interface_cast<IMediaPlayerService>(binder); if (service.get() != NULL) { sRemoteList = service->getCodecList(); + if (sRemoteList != NULL) { + sBinderDeathObserver = new BinderDeathObserver(); + binder->linkToDeath(sBinderDeathObserver.get()); + } } - if (sRemoteList == NULL) { // if failed to get remote list, create local list sRemoteList = getLocalInstance(); diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp index 0fecda8..b6fa810 100644 --- a/media/libstagefright/MediaCodecSource.cpp +++ b/media/libstagefright/MediaCodecSource.cpp @@ -121,7 +121,7 @@ status_t MediaCodecSource::Puller::start(const sp<MetaData> &meta, mLooper->registerHandler(this); mNotify = notify; - sp<AMessage> msg = new AMessage(kWhatStart, id()); + sp<AMessage> msg = new AMessage(kWhatStart, this); msg->setObject("meta", meta); return postSynchronouslyAndReturnError(msg); } @@ -137,19 +137,19 @@ void MediaCodecSource::Puller::stop() { mSource->stop(); ALOGV("source (%s) stopped", mIsAudio ? "audio" : "video"); - (new AMessage(kWhatStop, id()))->post(); + (new AMessage(kWhatStop, this))->post(); } void MediaCodecSource::Puller::pause() { - (new AMessage(kWhatPause, id()))->post(); + (new AMessage(kWhatPause, this))->post(); } void MediaCodecSource::Puller::resume() { - (new AMessage(kWhatResume, id()))->post(); + (new AMessage(kWhatResume, this))->post(); } void MediaCodecSource::Puller::schedulePull() { - sp<AMessage> msg = new AMessage(kWhatPull, id()); + sp<AMessage> msg = new AMessage(kWhatPull, this); msg->setInt32("generation", mPullGeneration); msg->post(); } @@ -182,7 +182,7 @@ void MediaCodecSource::Puller::onMessageReceived(const sp<AMessage> &msg) { sp<AMessage> response = new AMessage; response->setInt32("err", err); - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); response->postReply(replyID); break; @@ -269,13 +269,13 @@ sp<MediaCodecSource> MediaCodecSource::Create( } status_t MediaCodecSource::start(MetaData* params) { - sp<AMessage> msg = new AMessage(kWhatStart, mReflector->id()); + sp<AMessage> msg = new AMessage(kWhatStart, mReflector); msg->setObject("meta", params); return postSynchronouslyAndReturnError(msg); } status_t MediaCodecSource::stop() { - sp<AMessage> msg = new AMessage(kWhatStop, mReflector->id()); + sp<AMessage> msg = new AMessage(kWhatStop, mReflector); status_t err = postSynchronouslyAndReturnError(msg); // mPuller->stop() needs to be done outside MediaCodecSource's looper, @@ -294,7 +294,7 @@ status_t MediaCodecSource::stop() { } status_t MediaCodecSource::pause() { - (new AMessage(kWhatPause, mReflector->id()))->post(); + (new AMessage(kWhatPause, mReflector))->post(); return OK; } @@ -422,19 +422,10 @@ status_t MediaCodecSource::initEncoder() { } } - err = mEncoder->start(); - - if (err != OK) { - return err; - } - - err = mEncoder->getInputBuffers(&mEncoderInputBuffers); + mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector); + mEncoder->setCallback(mEncoderActivityNotify); - if (err != OK) { - return err; - } - - err = mEncoder->getOutputBuffers(&mEncoderOutputBuffers); + err = mEncoder->start(); if (err != OK) { return err; @@ -461,14 +452,6 @@ void MediaCodecSource::releaseEncoder() { mbuf->release(); } } - - for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) { - sp<ABuffer> accessUnit = mEncoderInputBuffers.itemAt(i); - accessUnit->setMediaBufferBase(NULL); - } - - mEncoderInputBuffers.clear(); - mEncoderOutputBuffers.clear(); } status_t MediaCodecSource::postSynchronouslyAndReturnError( @@ -508,7 +491,7 @@ void MediaCodecSource::signalEOS(status_t err) { if (mStopping && mEncoderReachedEOS) { ALOGI("encoder (%s) stopped", mIsVideo ? "video" : "audio"); // posting reply to everyone that's waiting - List<uint32_t>::iterator it; + List<sp<AReplyToken>>::iterator it; for (it = mStopReplyIDQueue.begin(); it != mStopReplyIDQueue.end(); it++) { (new AMessage)->postReply(*it); @@ -539,20 +522,6 @@ void MediaCodecSource::resume(int64_t skipFramesBeforeUs) { } } -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()) { @@ -587,16 +556,22 @@ status_t MediaCodecSource::feedEncoderInputBuffers() { #endif // DEBUG_DRIFT_TIME } + sp<ABuffer> inbuf; + status_t err = mEncoder->getInputBuffer(bufferIndex, &inbuf); + if (err != OK || inbuf == NULL) { + mbuf->release(); + signalEOS(); + break; + } + size = mbuf->size(); - memcpy(mEncoderInputBuffers.itemAt(bufferIndex)->data(), - mbuf->data(), size); + memcpy(inbuf->data(), mbuf->data(), size); if (mIsVideo) { // video encoder will release MediaBuffer when done // with underlying data. - mEncoderInputBuffers.itemAt(bufferIndex)->setMediaBufferBase( - mbuf); + inbuf->setMediaBufferBase(mbuf); } else { mbuf->release(); } @@ -615,113 +590,6 @@ status_t MediaCodecSource::feedEncoderInputBuffers() { return OK; } -status_t MediaCodecSource::doMoreWork(int32_t numInput, int32_t numOutput) { - status_t err = OK; - - if (!(mFlags & FLAG_USE_SURFACE_INPUT)) { - while (numInput-- > 0) { - size_t bufferIndex; - err = mEncoder->dequeueInputBuffer(&bufferIndex); - - if (err != OK) { - break; - } - - mAvailEncoderInputIndices.push_back(bufferIndex); - } - - feedEncoderInputBuffers(); - } - - while (numOutput-- > 0) { - 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 %" PRId64 " us (%.2f secs), dts/pts diff %" PRId64, - 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 %" PRId64 " us (%.2f secs), drift %" PRId64, - 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"); @@ -749,11 +617,9 @@ status_t MediaCodecSource::onStart(MetaData *params) { startTimeUs = -1ll; } resume(startTimeUs); - scheduleDoMoreWork(); } else { CHECK(mPuller != NULL); - sp<AMessage> notify = new AMessage( - kWhatPullerNotify, mReflector->id()); + sp<AMessage> notify = new AMessage(kWhatPullerNotify, mReflector); err = mPuller->start(params, notify); if (err != OK) { return err; @@ -793,41 +659,114 @@ void MediaCodecSource::onMessageReceived(const sp<AMessage> &msg) { mInputBufferQueue.push_back(mbuf); feedEncoderInputBuffers(); - scheduleDoMoreWork(); break; } case kWhatEncoderActivity: { - mDoMoreWorkPending = false; - if (mEncoder == NULL) { break; } - int32_t numInput, numOutput; + int32_t cbID; + CHECK(msg->findInt32("callbackID", &cbID)); + if (cbID == MediaCodec::CB_INPUT_AVAILABLE) { + int32_t index; + CHECK(msg->findInt32("index", &index)); + + mAvailEncoderInputIndices.push_back(index); + feedEncoderInputBuffers(); + } else if (cbID == MediaCodec::CB_OUTPUT_AVAILABLE) { + int32_t index; + size_t offset; + size_t size; + int64_t timeUs; + int32_t flags; + native_handle_t* handle = NULL; + + CHECK(msg->findInt32("index", &index)); + CHECK(msg->findSize("offset", &offset)); + CHECK(msg->findSize("size", &size)); + CHECK(msg->findInt64("timeUs", &timeUs)); + CHECK(msg->findInt32("flags", &flags)); + + if (flags & MediaCodec::BUFFER_FLAG_EOS) { + mEncoder->releaseOutputBuffer(index); + signalEOS(); + break; + } - if (!msg->findInt32("input-buffers", &numInput)) { - numInput = INT32_MAX; - } - if (!msg->findInt32("output-buffers", &numOutput)) { - numOutput = INT32_MAX; - } + sp<ABuffer> outbuf; + status_t err = mEncoder->getOutputBuffer(index, &outbuf); + if (err != OK || outbuf == NULL) { + signalEOS(); + break; + } - status_t err = doMoreWork(numInput, numOutput); + MediaBuffer *mbuf = new MediaBuffer(outbuf->size()); + memcpy(mbuf->data(), outbuf->data(), outbuf->size()); - if (err == OK) { - scheduleDoMoreWork(); - } else { - // reached EOS, or error - signalEOS(err); - } + 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); - break; + ALOGV("[video] time %" PRId64 " us (%.2f secs), dts/pts diff %" PRId64, + 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 %" PRId64 " us (%.2f secs), drift %" PRId64, + 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(index); + } else if (cbID == MediaCodec::CB_ERROR) { + status_t err; + CHECK(msg->findInt32("err", &err)); + ALOGE("Encoder (%s) reported error : 0x%x", + mIsVideo ? "video" : "audio", err); + signalEOS(); + } + break; } case kWhatStart: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); sp<RefBase> obj; @@ -843,7 +782,7 @@ void MediaCodecSource::onMessageReceived(const sp<AMessage> &msg) { { ALOGI("encoder (%s) stopping", mIsVideo ? "video" : "audio"); - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (mEncoderReachedEOS) { diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index d48dd84..c48a5ae 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -34,6 +34,7 @@ const char *MEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb"; const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg"; const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I = "audio/mpeg-L1"; const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II = "audio/mpeg-L2"; +const char *MEDIA_MIMETYPE_AUDIO_MIDI = "audio/midi"; 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"; @@ -45,6 +46,7 @@ const char *MEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac"; const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts"; const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm"; const char *MEDIA_MIMETYPE_AUDIO_AC3 = "audio/ac3"; +const char *MEDIA_MIMETYPE_AUDIO_EAC3 = "audio/eac3"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4"; const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav"; diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index 9ab6611..e21fe6e 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -29,6 +29,7 @@ #include "include/WVMExtractor.h" #include "include/FLACExtractor.h" #include "include/AACExtractor.h" +#include "include/MidiExtractor.h" #include "matroska/MatroskaExtractor.h" @@ -116,6 +117,8 @@ sp<MediaExtractor> MediaExtractor::Create( ret = new AACExtractor(source, meta); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) { ret = new MPEG2PSExtractor(source); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MIDI)) { + ret = new MidiExtractor(source); } if (ret != NULL) { diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp index c7c6f34..b13877d 100644 --- a/media/libstagefright/MediaMuxer.cpp +++ b/media/libstagefright/MediaMuxer.cpp @@ -38,21 +38,6 @@ namespace android { -MediaMuxer::MediaMuxer(const char *path, OutputFormat format) - : 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) : mFormat(format), mState(UNINITIALIZED) { diff --git a/media/libstagefright/MidiExtractor.cpp b/media/libstagefright/MidiExtractor.cpp new file mode 100644 index 0000000..66fab77 --- /dev/null +++ b/media/libstagefright/MidiExtractor.cpp @@ -0,0 +1,325 @@ +/* + * 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 "MidiExtractor" +#include <utils/Log.h> + +#include "include/MidiExtractor.h" + +#include <media/MidiIoWrapper.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaSource.h> +#include <libsonivox/eas_reverb.h> + +namespace android { + +// how many Sonivox output buffers to aggregate into one MediaBuffer +static const int NUM_COMBINE_BUFFERS = 4; + +class MidiSource : public MediaSource { + +public: + MidiSource( + const sp<MidiEngine> &engine, + const sp<MetaData> &trackMetadata); + + virtual status_t start(MetaData *params); + virtual status_t stop(); + virtual sp<MetaData> getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + +protected: + virtual ~MidiSource(); + +private: + sp<MidiEngine> mEngine; + sp<MetaData> mTrackMetadata; + bool mInitCheck; + bool mStarted; + + status_t init(); + + // no copy constructor or assignment + MidiSource(const MidiSource &); + MidiSource &operator=(const MidiSource &); + +}; + + +// Midisource + +MidiSource::MidiSource( + const sp<MidiEngine> &engine, + const sp<MetaData> &trackMetadata) + : mEngine(engine), + mTrackMetadata(trackMetadata), + mInitCheck(false), + mStarted(false) +{ + ALOGV("MidiSource ctor"); + mInitCheck = init(); +} + +MidiSource::~MidiSource() +{ + ALOGV("MidiSource dtor"); + if (mStarted) { + stop(); + } +} + +status_t MidiSource::start(MetaData * /* params */) +{ + ALOGV("MidiSource::start"); + + CHECK(!mStarted); + mStarted = true; + mEngine->allocateBuffers(); + return OK; +} + +status_t MidiSource::stop() +{ + ALOGV("MidiSource::stop"); + + CHECK(mStarted); + mStarted = false; + mEngine->releaseBuffers(); + + return OK; +} + +sp<MetaData> MidiSource::getFormat() +{ + return mTrackMetadata; +} + +status_t MidiSource::read( + MediaBuffer **outBuffer, const ReadOptions *options) +{ + ALOGV("MidiSource::read"); + MediaBuffer *buffer; + // process an optional seek request + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if ((NULL != options) && options->getSeekTo(&seekTimeUs, &mode)) { + if (seekTimeUs <= 0LL) { + seekTimeUs = 0LL; + } + mEngine->seekTo(seekTimeUs); + } + buffer = mEngine->readBuffer(); + *outBuffer = buffer; + ALOGV("MidiSource::read %p done", this); + return buffer != NULL ? (status_t) OK : (status_t) ERROR_END_OF_STREAM; +} + +status_t MidiSource::init() +{ + ALOGV("MidiSource::init"); + return OK; +} + +// MidiEngine + +MidiEngine::MidiEngine(const sp<DataSource> &dataSource, + const sp<MetaData> &fileMetadata, + const sp<MetaData> &trackMetadata) : + mGroup(NULL), + mEasData(NULL), + mEasHandle(NULL), + mEasConfig(NULL), + mIsInitialized(false) { + mIoWrapper = new MidiIoWrapper(dataSource); + // spin up a new EAS engine + EAS_I32 temp; + EAS_RESULT result = EAS_Init(&mEasData); + + if (result == EAS_SUCCESS) { + result = EAS_OpenFile(mEasData, mIoWrapper->getLocator(), &mEasHandle); + } + if (result == EAS_SUCCESS) { + result = EAS_Prepare(mEasData, mEasHandle); + } + if (result == EAS_SUCCESS) { + result = EAS_ParseMetaData(mEasData, mEasHandle, &temp); + } + + if (result != EAS_SUCCESS) { + return; + } + + if (fileMetadata != NULL) { + fileMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MIDI); + } + + if (trackMetadata != NULL) { + trackMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + trackMetadata->setInt64(kKeyDuration, 1000ll * temp); // milli->micro + mEasConfig = EAS_Config(); + trackMetadata->setInt32(kKeySampleRate, mEasConfig->sampleRate); + trackMetadata->setInt32(kKeyChannelCount, mEasConfig->numChannels); + } + mIsInitialized = true; +} + +MidiEngine::~MidiEngine() { + if (mEasHandle) { + EAS_CloseFile(mEasData, mEasHandle); + } + if (mEasData) { + EAS_Shutdown(mEasData); + } + delete mGroup; + +} + +status_t MidiEngine::initCheck() { + return mIsInitialized ? OK : UNKNOWN_ERROR; +} + +status_t MidiEngine::allocateBuffers() { + // select reverb preset and enable + EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER); + EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE); + + mGroup = new MediaBufferGroup; + int bufsize = sizeof(EAS_PCM) + * mEasConfig->mixBufferSize * mEasConfig->numChannels * NUM_COMBINE_BUFFERS; + ALOGV("using %d byte buffer", bufsize); + mGroup->add_buffer(new MediaBuffer(bufsize)); + return OK; +} + +status_t MidiEngine::releaseBuffers() { + delete mGroup; + mGroup = NULL; + return OK; +} + +status_t MidiEngine::seekTo(int64_t positionUs) { + ALOGV("seekTo %lld", positionUs); + EAS_RESULT result = EAS_Locate(mEasData, mEasHandle, positionUs / 1000, false); + return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR; +} + +MediaBuffer* MidiEngine::readBuffer() { + EAS_STATE state; + EAS_State(mEasData, mEasHandle, &state); + if ((state == EAS_STATE_STOPPED) || (state == EAS_STATE_ERROR)) { + return NULL; + } + MediaBuffer *buffer; + status_t err = mGroup->acquire_buffer(&buffer); + if (err != OK) { + ALOGE("readBuffer: no buffer"); + return NULL; + } + EAS_I32 timeMs; + EAS_GetLocation(mEasData, mEasHandle, &timeMs); + int64_t timeUs = 1000ll * timeMs; + buffer->meta_data()->setInt64(kKeyTime, timeUs); + + EAS_PCM* p = (EAS_PCM*) buffer->data(); + int numBytesOutput = 0; + for (int i = 0; i < NUM_COMBINE_BUFFERS; i++) { + EAS_I32 numRendered; + EAS_RESULT result = EAS_Render(mEasData, p, mEasConfig->mixBufferSize, &numRendered); + if (result != EAS_SUCCESS) { + ALOGE("EAS_Render returned %ld", result); + break; + } + p += numRendered * mEasConfig->numChannels; + numBytesOutput += numRendered * mEasConfig->numChannels * sizeof(EAS_PCM); + } + buffer->set_range(0, numBytesOutput); + ALOGV("readBuffer: returning %zd in buffer %p", buffer->range_length(), buffer); + return buffer; +} + + +// MidiExtractor + +MidiExtractor::MidiExtractor( + const sp<DataSource> &dataSource) + : mDataSource(dataSource), + mInitCheck(false) +{ + ALOGV("MidiExtractor ctor"); + mFileMetadata = new MetaData; + mTrackMetadata = new MetaData; + mEngine = new MidiEngine(mDataSource, mFileMetadata, mTrackMetadata); + mInitCheck = mEngine->initCheck(); +} + +MidiExtractor::~MidiExtractor() +{ + ALOGV("MidiExtractor dtor"); +} + +size_t MidiExtractor::countTracks() +{ + return mInitCheck == OK ? 1 : 0; +} + +sp<MediaSource> MidiExtractor::getTrack(size_t index) +{ + if (mInitCheck != OK || index > 0) { + return NULL; + } + return new MidiSource(mEngine, mTrackMetadata); +} + +sp<MetaData> MidiExtractor::getTrackMetaData( + size_t index, uint32_t /* flags */) { + ALOGV("MidiExtractor::getTrackMetaData"); + if (mInitCheck != OK || index > 0) { + return NULL; + } + return mTrackMetadata; +} + +sp<MetaData> MidiExtractor::getMetaData() +{ + ALOGV("MidiExtractor::getMetaData"); + return mFileMetadata; +} + +// Sniffer + +bool SniffMidi( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) +{ + sp<MidiEngine> p = new MidiEngine(source, NULL, NULL); + if (p->initCheck() == OK) { + *mimeType = MEDIA_MIMETYPE_AUDIO_MIDI; + *confidence = 0.8; + ALOGV("SniffMidi: yes"); + return true; + } + ALOGV("SniffMidi: no"); + return false; + +} + +} // namespace android diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp index bd0a41d..8d70e50 100644 --- a/media/libstagefright/NuCachedSource2.cpp +++ b/media/libstagefright/NuCachedSource2.cpp @@ -226,7 +226,7 @@ NuCachedSource2::NuCachedSource2( mLooper->start(false /* runOnCallingThread */, true /* canCallJava */); Mutex::Autolock autoLock(mLock); - (new AMessage(kWhatFetchMore, mReflector->id()))->post(); + (new AMessage(kWhatFetchMore, mReflector))->post(); } NuCachedSource2::~NuCachedSource2() { @@ -354,7 +354,7 @@ void NuCachedSource2::fetchInternal() { Mutex::Autolock autoLock(mLock); if (n == 0 || mDisconnecting) { - ALOGI("ERROR_END_OF_STREAM"); + ALOGI("caching reached eos."); mNumRetriesLeft = 0; mFinalStatus = ERROR_END_OF_STREAM; @@ -433,7 +433,7 @@ void NuCachedSource2::onFetch() { delayUs = 100000ll; } - (new AMessage(kWhatFetchMore, mReflector->id()))->post(delayUs); + (new AMessage(kWhatFetchMore, mReflector))->post(delayUs); } void NuCachedSource2::onRead(const sp<AMessage> &msg) { @@ -522,7 +522,7 @@ ssize_t NuCachedSource2::readAt(off64_t offset, void *data, size_t size) { return size; } - sp<AMessage> msg = new AMessage(kWhatRead, mReflector->id()); + sp<AMessage> msg = new AMessage(kWhatRead, mReflector); msg->setInt64("offset", offset); msg->setPointer("data", data); msg->setSize("size", size); diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index ca031aa..230c1f7 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -37,7 +37,7 @@ struct MuxOMX : public IOMX { MuxOMX(const sp<IOMX> &remoteOMX); virtual ~MuxOMX(); - virtual IBinder *onAsBinder() { return mRemoteOMX->asBinder().get(); } + virtual IBinder *onAsBinder() { return IInterface::asBinder(mRemoteOMX).get(); } virtual bool livesLocally(node_id node, pid_t pid); diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 288e07a..ea19ab2 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -47,10 +47,11 @@ #include <media/stagefright/SkipCutBuffer.h> #include <utils/Vector.h> -#include <OMX_Audio.h> #include <OMX_AudioExt.h> #include <OMX_Component.h> #include <OMX_IndexExt.h> +#include <OMX_VideoExt.h> +#include <OMX_AsString.h> #include "include/avc_utils.h" @@ -100,10 +101,10 @@ static sp<MediaSource> InstantiateSoftwareEncoder( #undef FACTORY_CREATE_ENCODER #undef FACTORY_REF -#define CODEC_LOGI(x, ...) ALOGI("[%s] "x, mComponentName, ##__VA_ARGS__) -#define CODEC_LOGV(x, ...) ALOGV("[%s] "x, mComponentName, ##__VA_ARGS__) -#define CODEC_LOGW(x, ...) ALOGW("[%s] "x, mComponentName, ##__VA_ARGS__) -#define CODEC_LOGE(x, ...) ALOGE("[%s] "x, mComponentName, ##__VA_ARGS__) +#define CODEC_LOGI(x, ...) ALOGI("[%s] " x, mComponentName, ##__VA_ARGS__) +#define CODEC_LOGV(x, ...) ALOGV("[%s] " x, mComponentName, ##__VA_ARGS__) +#define CODEC_LOGW(x, ...) ALOGW("[%s] " x, mComponentName, ##__VA_ARGS__) +#define CODEC_LOGE(x, ...) ALOGE("[%s] " x, mComponentName, ##__VA_ARGS__) struct OMXCodecObserver : public BnOMXObserver { OMXCodecObserver() { @@ -450,7 +451,7 @@ status_t OMXCodec::parseAVCCodecSpecificData( // assertion, let's be lenient for now... // CHECK((ptr[4] >> 2) == 0x3f); // reserved - size_t lengthSize = 1 + (ptr[4] & 3); + size_t lengthSize __unused = 1 + (ptr[4] & 3); // commented out check below as H264_QVGA_500_NO_AUDIO.3gp // violates it... @@ -2005,7 +2006,6 @@ status_t OMXCodec::cancelBufferToNativeWindow(BufferInfo *info) { OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() { // Dequeue the next buffer from the native window. ANativeWindowBuffer* buf; - int fenceFd = -1; int err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); if (err != 0) { CODEC_LOGE("dequeueBuffer failed w/ error 0x%08x", err); @@ -2110,7 +2110,6 @@ status_t OMXCodec::pushBlankBuffersToNativeWindow() { // on the screen and then been replaced, so an previous video frames are // guaranteed NOT to be currently displayed. for (int i = 0; i < numBufs + 1; i++) { - int fenceFd = -1; err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb); if (err != NO_ERROR) { ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)", @@ -4078,220 +4077,6 @@ void OMXCodec::signalBufferReturned(MediaBuffer *buffer) { CHECK(!"should not be here."); } -static const char *imageCompressionFormatString(OMX_IMAGE_CODINGTYPE type) { - static const char *kNames[] = { - "OMX_IMAGE_CodingUnused", - "OMX_IMAGE_CodingAutoDetect", - "OMX_IMAGE_CodingJPEG", - "OMX_IMAGE_CodingJPEG2K", - "OMX_IMAGE_CodingEXIF", - "OMX_IMAGE_CodingTIFF", - "OMX_IMAGE_CodingGIF", - "OMX_IMAGE_CodingPNG", - "OMX_IMAGE_CodingLZW", - "OMX_IMAGE_CodingBMP", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *colorFormatString(OMX_COLOR_FORMATTYPE type) { - static const char *kNames[] = { - "OMX_COLOR_FormatUnused", - "OMX_COLOR_FormatMonochrome", - "OMX_COLOR_Format8bitRGB332", - "OMX_COLOR_Format12bitRGB444", - "OMX_COLOR_Format16bitARGB4444", - "OMX_COLOR_Format16bitARGB1555", - "OMX_COLOR_Format16bitRGB565", - "OMX_COLOR_Format16bitBGR565", - "OMX_COLOR_Format18bitRGB666", - "OMX_COLOR_Format18bitARGB1665", - "OMX_COLOR_Format19bitARGB1666", - "OMX_COLOR_Format24bitRGB888", - "OMX_COLOR_Format24bitBGR888", - "OMX_COLOR_Format24bitARGB1887", - "OMX_COLOR_Format25bitARGB1888", - "OMX_COLOR_Format32bitBGRA8888", - "OMX_COLOR_Format32bitARGB8888", - "OMX_COLOR_FormatYUV411Planar", - "OMX_COLOR_FormatYUV411PackedPlanar", - "OMX_COLOR_FormatYUV420Planar", - "OMX_COLOR_FormatYUV420PackedPlanar", - "OMX_COLOR_FormatYUV420SemiPlanar", - "OMX_COLOR_FormatYUV422Planar", - "OMX_COLOR_FormatYUV422PackedPlanar", - "OMX_COLOR_FormatYUV422SemiPlanar", - "OMX_COLOR_FormatYCbYCr", - "OMX_COLOR_FormatYCrYCb", - "OMX_COLOR_FormatCbYCrY", - "OMX_COLOR_FormatCrYCbY", - "OMX_COLOR_FormatYUV444Interleaved", - "OMX_COLOR_FormatRawBayer8bit", - "OMX_COLOR_FormatRawBayer10bit", - "OMX_COLOR_FormatRawBayer8bitcompressed", - "OMX_COLOR_FormatL2", - "OMX_COLOR_FormatL4", - "OMX_COLOR_FormatL8", - "OMX_COLOR_FormatL16", - "OMX_COLOR_FormatL24", - "OMX_COLOR_FormatL32", - "OMX_COLOR_FormatYUV420PackedSemiPlanar", - "OMX_COLOR_FormatYUV422PackedSemiPlanar", - "OMX_COLOR_Format18BitBGR666", - "OMX_COLOR_Format24BitARGB6666", - "OMX_COLOR_Format24BitABGR6666", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar) { - return "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar"; - } else if (type == OMX_QCOM_COLOR_FormatYVU420SemiPlanar) { - return "OMX_QCOM_COLOR_FormatYVU420SemiPlanar"; - } else if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *videoCompressionFormatString(OMX_VIDEO_CODINGTYPE type) { - static const char *kNames[] = { - "OMX_VIDEO_CodingUnused", - "OMX_VIDEO_CodingAutoDetect", - "OMX_VIDEO_CodingMPEG2", - "OMX_VIDEO_CodingH263", - "OMX_VIDEO_CodingMPEG4", - "OMX_VIDEO_CodingWMV", - "OMX_VIDEO_CodingRV", - "OMX_VIDEO_CodingAVC", - "OMX_VIDEO_CodingMJPEG", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *audioCodingTypeString(OMX_AUDIO_CODINGTYPE type) { - static const char *kNames[] = { - "OMX_AUDIO_CodingUnused", - "OMX_AUDIO_CodingAutoDetect", - "OMX_AUDIO_CodingPCM", - "OMX_AUDIO_CodingADPCM", - "OMX_AUDIO_CodingAMR", - "OMX_AUDIO_CodingGSMFR", - "OMX_AUDIO_CodingGSMEFR", - "OMX_AUDIO_CodingGSMHR", - "OMX_AUDIO_CodingPDCFR", - "OMX_AUDIO_CodingPDCEFR", - "OMX_AUDIO_CodingPDCHR", - "OMX_AUDIO_CodingTDMAFR", - "OMX_AUDIO_CodingTDMAEFR", - "OMX_AUDIO_CodingQCELP8", - "OMX_AUDIO_CodingQCELP13", - "OMX_AUDIO_CodingEVRC", - "OMX_AUDIO_CodingSMV", - "OMX_AUDIO_CodingG711", - "OMX_AUDIO_CodingG723", - "OMX_AUDIO_CodingG726", - "OMX_AUDIO_CodingG729", - "OMX_AUDIO_CodingAAC", - "OMX_AUDIO_CodingMP3", - "OMX_AUDIO_CodingSBC", - "OMX_AUDIO_CodingVORBIS", - "OMX_AUDIO_CodingOPUS", - "OMX_AUDIO_CodingWMA", - "OMX_AUDIO_CodingRA", - "OMX_AUDIO_CodingMIDI", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *audioPCMModeString(OMX_AUDIO_PCMMODETYPE type) { - static const char *kNames[] = { - "OMX_AUDIO_PCMModeLinear", - "OMX_AUDIO_PCMModeALaw", - "OMX_AUDIO_PCMModeMULaw", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *amrBandModeString(OMX_AUDIO_AMRBANDMODETYPE type) { - static const char *kNames[] = { - "OMX_AUDIO_AMRBandModeUnused", - "OMX_AUDIO_AMRBandModeNB0", - "OMX_AUDIO_AMRBandModeNB1", - "OMX_AUDIO_AMRBandModeNB2", - "OMX_AUDIO_AMRBandModeNB3", - "OMX_AUDIO_AMRBandModeNB4", - "OMX_AUDIO_AMRBandModeNB5", - "OMX_AUDIO_AMRBandModeNB6", - "OMX_AUDIO_AMRBandModeNB7", - "OMX_AUDIO_AMRBandModeWB0", - "OMX_AUDIO_AMRBandModeWB1", - "OMX_AUDIO_AMRBandModeWB2", - "OMX_AUDIO_AMRBandModeWB3", - "OMX_AUDIO_AMRBandModeWB4", - "OMX_AUDIO_AMRBandModeWB5", - "OMX_AUDIO_AMRBandModeWB6", - "OMX_AUDIO_AMRBandModeWB7", - "OMX_AUDIO_AMRBandModeWB8", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - -static const char *amrFrameFormatString(OMX_AUDIO_AMRFRAMEFORMATTYPE type) { - static const char *kNames[] = { - "OMX_AUDIO_AMRFrameFormatConformance", - "OMX_AUDIO_AMRFrameFormatIF1", - "OMX_AUDIO_AMRFrameFormatIF2", - "OMX_AUDIO_AMRFrameFormatFSF", - "OMX_AUDIO_AMRFrameFormatRTPPayload", - "OMX_AUDIO_AMRFrameFormatITU", - }; - - size_t numNames = sizeof(kNames) / sizeof(kNames[0]); - - if (type < 0 || (size_t)type >= numNames) { - return "UNKNOWN"; - } else { - return kNames[type]; - } -} - void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); @@ -4322,10 +4107,10 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { printf(" nStride = %" PRIu32 "\n", imageDef->nStride); printf(" eCompressionFormat = %s\n", - imageCompressionFormatString(imageDef->eCompressionFormat)); + asString(imageDef->eCompressionFormat)); printf(" eColorFormat = %s\n", - colorFormatString(imageDef->eColorFormat)); + asString(imageDef->eColorFormat)); break; } @@ -4341,10 +4126,10 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { printf(" nStride = %" PRIu32 "\n", videoDef->nStride); printf(" eCompressionFormat = %s\n", - videoCompressionFormatString(videoDef->eCompressionFormat)); + asString(videoDef->eCompressionFormat)); printf(" eColorFormat = %s\n", - colorFormatString(videoDef->eColorFormat)); + asString(videoDef->eColorFormat)); break; } @@ -4356,7 +4141,7 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { printf("\n"); printf(" // Audio\n"); printf(" eEncoding = %s\n", - audioCodingTypeString(audioDef->eEncoding)); + asString(audioDef->eEncoding)); if (audioDef->eEncoding == OMX_AUDIO_CodingPCM) { OMX_AUDIO_PARAM_PCMMODETYPE params; @@ -4376,7 +4161,7 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { params.eNumData == OMX_NumericalDataSigned ? "signed" : "unsigned"); - printf(" ePCMMode = %s\n", audioPCMModeString(params.ePCMMode)); + printf(" ePCMMode = %s\n", asString(params.ePCMMode)); } else if (audioDef->eEncoding == OMX_AUDIO_CodingAMR) { OMX_AUDIO_PARAM_AMRTYPE amr; InitOMXParams(&amr); @@ -4388,9 +4173,9 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { printf(" nChannels = %" PRIu32 "\n", amr.nChannels); printf(" eAMRBandMode = %s\n", - amrBandModeString(amr.eAMRBandMode)); + asString(amr.eAMRBandMode)); printf(" eAMRFrameFormat = %s\n", - amrFrameFormatString(amr.eAMRFrameFormat)); + asString(amr.eAMRFrameFormat)); } break; @@ -4699,12 +4484,7 @@ status_t QueryCodec( const char *componentName, const char *mime, bool isEncoder, CodecCapabilities *caps) { - if (strncmp(componentName, "OMX.", 4)) { - // Not an OpenMax component but a software codec. - caps->mFlags = 0; - caps->mComponentName = componentName; - return OK; - } + bool isVideo = !strncasecmp(mime, "video/", 6); sp<OMXCodecObserver> observer = new OMXCodecObserver; IOMX::node_id node; @@ -4719,59 +4499,63 @@ status_t QueryCodec( caps->mFlags = 0; caps->mComponentName = componentName; - OMX_VIDEO_PARAM_PROFILELEVELTYPE param; - InitOMXParams(¶m); + // NOTE: OMX does not provide a way to query AAC profile support + if (isVideo) { + OMX_VIDEO_PARAM_PROFILELEVELTYPE param; + InitOMXParams(¶m); - param.nPortIndex = !isEncoder ? 0 : 1; + param.nPortIndex = !isEncoder ? 0 : 1; - for (param.nProfileIndex = 0;; ++param.nProfileIndex) { - err = omx->getParameter( - node, OMX_IndexParamVideoProfileLevelQuerySupported, - ¶m, sizeof(param)); + for (param.nProfileIndex = 0;; ++param.nProfileIndex) { + err = omx->getParameter( + node, OMX_IndexParamVideoProfileLevelQuerySupported, + ¶m, sizeof(param)); - if (err != OK) { - break; - } - - CodecProfileLevel profileLevel; - profileLevel.mProfile = param.eProfile; - profileLevel.mLevel = param.eLevel; + if (err != OK) { + break; + } - caps->mProfileLevels.push(profileLevel); - } + CodecProfileLevel profileLevel; + profileLevel.mProfile = param.eProfile; + profileLevel.mLevel = param.eLevel; - // Color format query - // return colors in the order reported by the OMX component - // prefix "flexible" standard ones with the flexible equivalent - OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat; - InitOMXParams(&portFormat); - portFormat.nPortIndex = !isEncoder ? 1 : 0; - for (portFormat.nIndex = 0;; ++portFormat.nIndex) { - err = omx->getParameter( - node, OMX_IndexParamVideoPortFormat, - &portFormat, sizeof(portFormat)); - if (err != OK) { - break; + caps->mProfileLevels.push(profileLevel); } - OMX_U32 flexibleEquivalent; - if (ACodec::isFlexibleColorFormat( - omx, node, portFormat.eColorFormat, &flexibleEquivalent)) { - bool marked = false; - for (size_t i = 0; i < caps->mColorFormats.size(); i++) { - if (caps->mColorFormats.itemAt(i) == flexibleEquivalent) { - marked = true; - break; - } + // Color format query + // return colors in the order reported by the OMX component + // prefix "flexible" standard ones with the flexible equivalent + OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat; + InitOMXParams(&portFormat); + portFormat.nPortIndex = !isEncoder ? 1 : 0; + for (portFormat.nIndex = 0;; ++portFormat.nIndex) { + err = omx->getParameter( + node, OMX_IndexParamVideoPortFormat, + &portFormat, sizeof(portFormat)); + if (err != OK) { + break; } - if (!marked) { - caps->mColorFormats.push(flexibleEquivalent); + + OMX_U32 flexibleEquivalent; + if (ACodec::isFlexibleColorFormat( + omx, node, portFormat.eColorFormat, false /* usingNativeWindow */, + &flexibleEquivalent)) { + bool marked = false; + for (size_t i = 0; i < caps->mColorFormats.size(); i++) { + if (caps->mColorFormats.itemAt(i) == flexibleEquivalent) { + marked = true; + break; + } + } + if (!marked) { + caps->mColorFormats.push(flexibleEquivalent); + } } + caps->mColorFormats.push(portFormat.eColorFormat); } - caps->mColorFormats.push(portFormat.eColorFormat); } - if (!isEncoder && !strncmp(mime, "video/", 6)) { + if (isVideo && !isEncoder) { if (omx->storeMetaDataInBuffers( node, 1 /* port index */, OMX_TRUE) == OK || omx->prepareForAdaptivePlayback( diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index 821bd81..6e32494 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -38,6 +38,7 @@ extern "C" { int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb); int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb); int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb); + long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op); } namespace android { @@ -75,7 +76,7 @@ struct MyVorbisExtractor { status_t seekToTime(int64_t timeUs); status_t seekToOffset(off64_t offset); - status_t readNextPacket(MediaBuffer **buffer); + status_t readNextPacket(MediaBuffer **buffer, bool conf); status_t init(); @@ -84,6 +85,8 @@ struct MyVorbisExtractor { private: struct Page { uint64_t mGranulePosition; + int32_t mPrevPacketSize; + uint64_t mPrevPacketPos; uint32_t mSerialNo; uint32_t mPageNo; uint8_t mFlags; @@ -121,6 +124,8 @@ private: status_t verifyHeader( MediaBuffer *buffer, uint8_t type); + int32_t packetBlockSize(MediaBuffer *buffer); + void parseFileMetaData(); status_t findPrevGranulePosition(off64_t pageOffset, uint64_t *granulePos); @@ -180,7 +185,7 @@ status_t OggSource::read( } MediaBuffer *packet; - status_t err = mExtractor->mImpl->readNextPacket(&packet); + status_t err = mExtractor->mImpl->readNextPacket(&packet, /* conf = */ false); if (err != OK) { return err; @@ -373,6 +378,7 @@ status_t MyVorbisExtractor::seekToOffset(off64_t offset) { mFirstPacketInPage = true; mCurrentPageSamples = 0; mCurrentPage.mNumSegments = 0; + mCurrentPage.mPrevPacketSize = -1; mNextLaceIndex = 0; // XXX what if new page continues packet from last??? @@ -451,7 +457,7 @@ ssize_t MyVorbisExtractor::readPage(off64_t offset, Page *page) { return sizeof(header) + page->mNumSegments + totalSize; } -status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { +status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out, bool conf) { *out = NULL; MediaBuffer *buffer = NULL; @@ -489,16 +495,6 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { tmp->set_range(0, buffer->range_length()); buffer->release(); } else { - // XXX Not only is this not technically the correct time for - // this packet, we also stamp every packet in this page - // with the same time. This needs fixing later. - - if (mVi.rate) { - // Rate may not have been initialized yet if we're currently - // reading the configuration packets... - // Fortunately, the timestamp doesn't matter for those. - timeUs = mCurrentPage.mGranulePosition * 1000000ll / mVi.rate; - } tmp->set_range(0, 0); } buffer = tmp; @@ -521,16 +517,32 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { if (gotFullPacket) { // We've just read the entire packet. - if (timeUs >= 0) { - buffer->meta_data()->setInt64(kKeyTime, timeUs); - } - if (mFirstPacketInPage) { buffer->meta_data()->setInt32( kKeyValidSamples, mCurrentPageSamples); mFirstPacketInPage = false; } + // ignore timestamp for configuration packets + if (!conf) { + int32_t curBlockSize = packetBlockSize(buffer); + if (mCurrentPage.mPrevPacketSize < 0) { + mCurrentPage.mPrevPacketSize = curBlockSize; + mCurrentPage.mPrevPacketPos = + mCurrentPage.mGranulePosition - mCurrentPageSamples; + timeUs = mCurrentPage.mPrevPacketPos * 1000000ll / mVi.rate; + } else { + // The effective block size is the average of the two overlapped blocks + int32_t actualBlockSize = + (curBlockSize + mCurrentPage.mPrevPacketSize) / 2; + timeUs = mCurrentPage.mPrevPacketPos * 1000000ll / mVi.rate; + // The actual size output by the decoder will be half the effective + // size, due to the overlap + mCurrentPage.mPrevPacketPos += actualBlockSize / 2; + mCurrentPage.mPrevPacketSize = curBlockSize; + } + buffer->meta_data()->setInt64(kKeyTime, timeUs); + } *out = buffer; return OK; @@ -591,7 +603,7 @@ status_t MyVorbisExtractor::init() { MediaBuffer *packet; status_t err; - if ((err = readNextPacket(&packet)) != OK) { + if ((err = readNextPacket(&packet, /* conf = */ true)) != OK) { return err; } ALOGV("read packet of size %zu\n", packet->range_length()); @@ -602,7 +614,7 @@ status_t MyVorbisExtractor::init() { return err; } - if ((err = readNextPacket(&packet)) != OK) { + if ((err = readNextPacket(&packet, /* conf = */ true)) != OK) { return err; } ALOGV("read packet of size %zu\n", packet->range_length()); @@ -613,7 +625,7 @@ status_t MyVorbisExtractor::init() { return err; } - if ((err = readNextPacket(&packet)) != OK) { + if ((err = readNextPacket(&packet, /* conf = */ true)) != OK) { return err; } ALOGV("read packet of size %zu\n", packet->range_length()); @@ -686,6 +698,35 @@ void MyVorbisExtractor::buildTableOfContents() { } } +int32_t MyVorbisExtractor::packetBlockSize(MediaBuffer *buffer) { + const uint8_t *data = + (const uint8_t *)buffer->data() + buffer->range_offset(); + + size_t size = buffer->range_length(); + + ogg_buffer buf; + buf.data = (uint8_t *)data; + buf.size = size; + buf.refcount = 1; + buf.ptr.owner = NULL; + + ogg_reference ref; + ref.buffer = &buf; + ref.begin = 0; + ref.length = size; + ref.next = NULL; + + ogg_packet pack; + pack.packet = &ref; + pack.bytes = ref.length; + pack.b_o_s = 0; + pack.e_o_s = 0; + pack.granulepos = 0; + pack.packetno = 0; + + return vorbis_packet_blocksize(&mVi, &pack); +} + status_t MyVorbisExtractor::verifyHeader( MediaBuffer *buffer, uint8_t type) { const uint8_t *data = @@ -730,6 +771,10 @@ status_t MyVorbisExtractor::verifyHeader( ALOGV("upper-bitrate = %ld", mVi.bitrate_upper); ALOGV("nominal-bitrate = %ld", mVi.bitrate_nominal); ALOGV("window-bitrate = %ld", mVi.bitrate_window); + ALOGV("blocksizes: %d/%d", + vorbis_info_blocksize(&mVi, 0), + vorbis_info_blocksize(&mVi, 1) + ); off64_t size; if (mSource->getSize(&size) == OK) { @@ -804,6 +849,7 @@ void parseVorbisComment( { "TRACKNUMBER", kKeyCDTrackNumber }, { "DISCNUMBER", kKeyDiscNumber }, { "DATE", kKeyDate }, + { "YEAR", kKeyYear }, { "LYRICIST", kKeyWriter }, { "METADATA_BLOCK_PICTURE", kKeyAlbumArt }, { "ANDROID_LOOP", kKeyAutoLoop }, diff --git a/media/libstagefright/ProcessInfo.cpp b/media/libstagefright/ProcessInfo.cpp new file mode 100644 index 0000000..b4172b3 --- /dev/null +++ b/media/libstagefright/ProcessInfo.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ProcessInfo" +#include <utils/Log.h> + +#include <media/stagefright/ProcessInfo.h> + +#include <binder/IProcessInfoService.h> +#include <binder/IServiceManager.h> + +namespace android { + +ProcessInfo::ProcessInfo() {} + +bool ProcessInfo::getPriority(int pid, int* priority) { + sp<IBinder> binder = defaultServiceManager()->getService(String16("processinfo")); + sp<IProcessInfoService> service = interface_cast<IProcessInfoService>(binder); + + size_t length = 1; + int32_t states; + status_t err = service->getProcessStatesFromPids(length, &pid, &states); + if (err != OK) { + ALOGE("getProcessStatesFromPids failed"); + return false; + } + ALOGV("pid %d states %d", pid, states); + if (states < 0) { + return false; + } + + // Use process state as the priority. Lower the value, higher the priority. + *priority = states; + return true; +} + +ProcessInfo::~ProcessInfo() {} + +} // namespace android diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 4449d57..db33e83 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -28,9 +28,6 @@ #include <media/mediametadataretriever.h> #include <private/media/VideoFrame.h> -// Sonivox includes -#include <libsonivox/eas.h> - namespace android { StagefrightMediaScanner::StagefrightMediaScanner() {} @@ -57,54 +54,6 @@ static bool FileHasAcceptableExtension(const char *extension) { return false; } -static MediaScanResult HandleMIDI( - const char *filename, MediaScannerClient *client) { - // get the library configuration and do sanity check - const S_EAS_LIB_CONFIG* pLibConfig = EAS_Config(); - if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) { - ALOGE("EAS library/header mismatch\n"); - return MEDIA_SCAN_RESULT_ERROR; - } - EAS_I32 temp; - - // spin up a new EAS engine - EAS_DATA_HANDLE easData = NULL; - EAS_HANDLE easHandle = NULL; - EAS_RESULT result = EAS_Init(&easData); - if (result == EAS_SUCCESS) { - EAS_FILE file; - file.path = filename; - file.fd = 0; - file.offset = 0; - file.length = 0; - result = EAS_OpenFile(easData, &file, &easHandle); - } - if (result == EAS_SUCCESS) { - result = EAS_Prepare(easData, easHandle); - } - if (result == EAS_SUCCESS) { - result = EAS_ParseMetaData(easData, easHandle, &temp); - } - if (easHandle) { - EAS_CloseFile(easData, easHandle); - } - if (easData) { - EAS_Shutdown(easData); - } - - if (result != EAS_SUCCESS) { - return MEDIA_SCAN_RESULT_SKIPPED; - } - - char buffer[20]; - sprintf(buffer, "%ld", temp); - status_t status = client->addStringTag("duration", buffer); - if (status != OK) { - return MEDIA_SCAN_RESULT_ERROR; - } - return MEDIA_SCAN_RESULT_OK; -} - MediaScanResult StagefrightMediaScanner::processFile( const char *path, const char *mimeType, MediaScannerClient &client) { @@ -130,18 +79,6 @@ MediaScanResult StagefrightMediaScanner::processFileInternal( return MEDIA_SCAN_RESULT_SKIPPED; } - if (!strcasecmp(extension, ".mid") - || !strcasecmp(extension, ".smf") - || !strcasecmp(extension, ".imy") - || !strcasecmp(extension, ".midi") - || !strcasecmp(extension, ".xmf") - || !strcasecmp(extension, ".rtttl") - || !strcasecmp(extension, ".rtx") - || !strcasecmp(extension, ".ota") - || !strcasecmp(extension, ".mxmf")) { - return HandleMIDI(path, &client); - } - sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever); int fd = open(path, O_RDONLY | O_LARGEFILE); diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index 4e1c65c..e8abf48 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -26,6 +26,7 @@ #include <media/hardware/MetadataBufferType.h> #include <ui/GraphicBuffer.h> +#include <gui/BufferItem.h> #include <gui/ISurfaceComposer.h> #include <gui/IGraphicBufferAlloc.h> #include <OMX_Component.h> @@ -290,7 +291,7 @@ status_t SurfaceMediaSource::read( // TODO: mCurrentSlot can be made a bufferstate since there // can be more than one "current" slots. - BufferQueue::BufferItem item; + BufferItem item; // If the recording has started and the queue is empty, then just // wait here till the frames come in from the client side while (mStarted) { @@ -448,7 +449,7 @@ void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) { } // Part of the BufferQueue::ConsumerListener -void SurfaceMediaSource::onFrameAvailable() { +void SurfaceMediaSource::onFrameAvailable(const BufferItem& /* item */) { ALOGV("onFrameAvailable"); sp<FrameAvailableListener> listener; diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp index 1fdb244..7d15220 100644 --- a/media/libstagefright/TimedEventQueue.cpp +++ b/media/libstagefright/TimedEventQueue.cpp @@ -52,7 +52,7 @@ TimedEventQueue::TimedEventQueue() TimedEventQueue::~TimedEventQueue() { stop(); if (mPowerManager != 0) { - sp<IBinder> binder = mPowerManager->asBinder(); + sp<IBinder> binder = IInterface::asBinder(mPowerManager); binder->unlinkToDeath(mDeathRecipient); } } diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 25afc5b..c0be136 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -181,14 +181,14 @@ status_t convertMetaDataToMessage( CHECK(size >= 7); CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 - uint8_t profile = ptr[1]; - uint8_t level = ptr[3]; + uint8_t profile __unused = ptr[1]; + uint8_t level __unused = ptr[3]; // There is decodable content out there that fails the following // assertion, let's be lenient for now... // CHECK((ptr[4] >> 2) == 0x3f); // reserved - size_t lengthSize = 1 + (ptr[4] & 3); + size_t lengthSize __unused = 1 + (ptr[4] & 3); // commented out check below as H264_QVGA_500_NO_AUDIO.3gp // violates it... @@ -257,8 +257,8 @@ status_t convertMetaDataToMessage( CHECK(size >= 7); CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 - uint8_t profile = ptr[1] & 31; - uint8_t level = ptr[12]; + uint8_t profile __unused = ptr[1] & 31; + uint8_t level __unused = ptr[12]; ptr += 22; size -= 22; @@ -344,6 +344,28 @@ status_t convertMetaDataToMessage( buffer->meta()->setInt32("csd", true); buffer->meta()->setInt64("timeUs", 0); msg->setBuffer("csd-0", buffer); + + if (!meta->findData(kKeyOpusCodecDelay, &type, &data, &size)) { + return -EINVAL; + } + + buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-1", buffer); + + if (!meta->findData(kKeyOpusSeekPreRoll, &type, &data, &size)) { + return -EINVAL; + } + + buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-2", buffer); } *format = msg; diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp index a4a651d..335ac84 100644 --- a/media/libstagefright/WAVExtractor.cpp +++ b/media/libstagefright/WAVExtractor.cpp @@ -439,10 +439,6 @@ status_t WAVSource::read( maxBytesToRead = maxBytesAvailable; } - // read only integral amounts of audio unit frames. - const size_t inputUnitFrameSize = mNumChannels * mBitsPerSample / 8; - maxBytesToRead -= maxBytesToRead % inputUnitFrameSize; - if (mWaveFormat == WAVE_FORMAT_MSGSM) { // Microsoft packs 2 frames into 65 bytes, rather than using separate 33-byte frames, // so read multiples of 65, and use smaller buffers to account for ~10:1 expansion ratio @@ -450,6 +446,10 @@ status_t WAVSource::read( maxBytesToRead = 1024; } maxBytesToRead = (maxBytesToRead / 65) * 65; + } else { + // read only integral amounts of audio unit frames. + const size_t inputUnitFrameSize = mNumChannels * mBitsPerSample / 8; + maxBytesToRead -= maxBytesToRead % inputUnitFrameSize; } ssize_t n = mDataSource->readAt( diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp index 38a1f6b..8ef2dca 100644 --- a/media/libstagefright/avc_utils.cpp +++ b/media/libstagefright/avc_utils.cpp @@ -26,6 +26,7 @@ #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> +#include <utils/misc.h> namespace android { @@ -186,17 +187,31 @@ void FindAVCDimensions( if (aspect_ratio_idc == 255 /* extendedSAR */) { sar_width = br.getBits(16); sar_height = br.getBits(16); - } else if (aspect_ratio_idc > 0 && aspect_ratio_idc < 14) { - static const int32_t kFixedSARWidth[] = { - 1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160 + } else { + static const struct { unsigned width, height; } kFixedSARs[] = { + { 0, 0 }, // Invalid + { 1, 1 }, + { 12, 11 }, + { 10, 11 }, + { 16, 11 }, + { 40, 33 }, + { 24, 11 }, + { 20, 11 }, + { 32, 11 }, + { 80, 33 }, + { 18, 11 }, + { 15, 11 }, + { 64, 33 }, + { 160, 99 }, + { 4, 3 }, + { 3, 2 }, + { 2, 1 }, }; - static const int32_t kFixedSARHeight[] = { - 1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99 - }; - - sar_width = kFixedSARWidth[aspect_ratio_idc - 1]; - sar_height = kFixedSARHeight[aspect_ratio_idc - 1]; + if (aspect_ratio_idc > 0 && aspect_ratio_idc < NELEM(kFixedSARs)) { + sar_width = kFixedSARs[aspect_ratio_idc].width; + sar_height = kFixedSARs[aspect_ratio_idc].height; + } } } @@ -222,28 +237,25 @@ status_t getNextNALUnit( *nalStart = NULL; *nalSize = 0; - if (size == 0) { + if (size < 3) { return -EAGAIN; } - // Skip any number of leading 0x00. - size_t offset = 0; - while (offset < size && data[offset] == 0x00) { - ++offset; - } - - if (offset == size) { - return -EAGAIN; - } // A valid startcode consists of at least two 0x00 bytes followed by 0x01. - - if (offset < 2 || data[offset] != 0x01) { - return ERROR_MALFORMED; + for (; offset + 2 < size; ++offset) { + if (data[offset + 2] == 0x01 && data[offset] == 0x00 + && data[offset + 1] == 0x00) { + break; + } } - - ++offset; + if (offset + 2 >= size) { + *_data = &data[offset]; + *_size = 2; + return -EAGAIN; + } + offset += 3; size_t startOffset = offset; @@ -508,8 +520,8 @@ bool ExtractDimensionsFromVOLHeader( CHECK_NE(video_object_type_indication, 0x21u /* Fine Granularity Scalable */); - unsigned video_object_layer_verid; - unsigned video_object_layer_priority; + unsigned video_object_layer_verid __unused; + unsigned video_object_layer_priority __unused; if (br.getBits(1)) { video_object_layer_verid = br.getBits(4); video_object_layer_priority = br.getBits(3); @@ -571,7 +583,7 @@ bool ExtractDimensionsFromVOLHeader( unsigned video_object_layer_height = br.getBits(13); CHECK(br.getBits(1)); // marker_bit - unsigned interlaced = br.getBits(1); + unsigned interlaced __unused = br.getBits(1); *width = video_object_layer_width; *height = video_object_layer_height; @@ -617,7 +629,7 @@ bool GetMPEGAudioFrameSize( return false; } - unsigned protection = (header >> 16) & 1; + unsigned protection __unused = (header >> 16) & 1; unsigned bitrate_index = (header >> 12) & 0x0f; diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index 351ba1e..10937ec 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -623,7 +623,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { } else { int64_t currentTime = mBufferTimestamps.top(); currentTime += mStreamInfo->aacSamplesPerFrame * - 1000000ll / mStreamInfo->sampleRate; + 1000000ll / mStreamInfo->aacSampleRate; mBufferTimestamps.add(currentTime); } } else { @@ -874,9 +874,9 @@ void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { // adjust/interpolate next time stamp *currentBufLeft -= decodedSize; *nextTimeStamp += mStreamInfo->aacSamplesPerFrame * - 1000000ll / mStreamInfo->sampleRate; + 1000000ll / mStreamInfo->aacSampleRate; ALOGV("adjusted nextTimeStamp/size to %lld/%d", - *nextTimeStamp, *currentBufLeft); + (long long) *nextTimeStamp, *currentBufLeft); } else { // move to next timestamp in list if (mBufferTimestamps.size() > 0) { @@ -885,7 +885,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { mBufferSizes.removeAt(0); currentBufLeft = &mBufferSizes.editItemAt(0); ALOGV("moved to next time/size: %lld/%d", - *nextTimeStamp, *currentBufLeft); + (long long) *nextTimeStamp, *currentBufLeft); } // try to limit output buffer size to match input buffers // (e.g when an input buffer contained 4 "sub" frames, output @@ -975,6 +975,7 @@ void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { mBufferSizes.clear(); mDecodedSizes.clear(); mLastInHeader = NULL; + mEndOfInput = false; } else { int avail; while ((avail = outputDelayRingBufferSamplesAvailable()) > 0) { @@ -989,12 +990,11 @@ void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { mOutputBufferCount++; } mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos; + mEndOfOutput = false; } } void SoftAAC2::drainDecoder() { - int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels; - // flush decoder until outputDelay is compensated while (mOutputDelayCompensated > 0) { // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp index 8b5007e..bebb9dc 100644 --- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp +++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp @@ -214,8 +214,6 @@ sp<MetaData> AACEncoder::getFormat() { status_t AACEncoder::read( MediaBuffer **out, const ReadOptions *options) { - status_t err; - *out = NULL; int64_t seekTimeUs; diff --git a/media/libstagefright/codecs/aacenc/basic_op/basic_op.h b/media/libstagefright/codecs/aacenc/basic_op/basic_op.h index 5cd7e5f..bbc753b 100644 --- a/media/libstagefright/codecs/aacenc/basic_op/basic_op.h +++ b/media/libstagefright/codecs/aacenc/basic_op/basic_op.h @@ -518,8 +518,6 @@ __inline Word32 L_shl(Word32 L_var1, Word16 var2) return ASM_L_shr( L_var1, -var2); } #else - Word32 L_var_out = 0L; - if (var2 <= 0) { L_var1 = L_shr(L_var1, (Word16)-var2); @@ -540,7 +538,6 @@ __inline Word32 L_shl(Word32 L_var1, Word16 var2) } } L_var1 <<= 1; - L_var_out = L_var1; } } return (L_var1); diff --git a/media/libstagefright/codecs/aacenc/basic_op/oper_32b.c b/media/libstagefright/codecs/aacenc/basic_op/oper_32b.c index 1d029fc..4fd16a1 100644 --- a/media/libstagefright/codecs/aacenc/basic_op/oper_32b.c +++ b/media/libstagefright/codecs/aacenc/basic_op/oper_32b.c @@ -245,10 +245,9 @@ 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; + UNUSED(accuracy); if(value < 0) return 0; @@ -351,12 +350,11 @@ Word32 pow2_xy(Word32 x, Word32 y) UWord32 iPart; UWord32 fPart; Word32 res; - Word32 tmp, tmp2; - Word32 shift, shift2; + Word32 tmp; - tmp2 = -x; - iPart = tmp2 / y; - fPart = tmp2 - iPart*y; + tmp = -x; + iPart = tmp / y; + fPart = tmp - iPart*y; iPart = min(iPart,INT_BITS-1); res = pow2Table[(POW2_TABLE_SIZE*fPart)/y] >> iPart; diff --git a/media/libstagefright/codecs/aacenc/src/aacenc.c b/media/libstagefright/codecs/aacenc/src/aacenc.c index 40db92c..df17787 100644 --- a/media/libstagefright/codecs/aacenc/src/aacenc.c +++ b/media/libstagefright/codecs/aacenc/src/aacenc.c @@ -39,18 +39,20 @@ VO_U32 VO_API voAACEncInit(VO_HANDLE * phCodec,VO_AUDIO_CODINGTYPE vType, VO_CODEC_INIT_USERDATA *pUserData) { AAC_ENCODER*hAacEnc; - AACENC_CONFIG config; int error; #ifdef USE_DEAULT_MEM VO_MEM_OPERATOR voMemoprator; #endif VO_MEM_OPERATOR *pMemOP; + +#ifdef USE_DEAULT_MEM int interMem; + interMem = 0; +#endif UNUSED(vType); - interMem = 0; error = 0; /* init the memory operator */ @@ -214,7 +216,7 @@ VO_U32 VO_API voAACEncGetOutputData(VO_HANDLE hCodec, VO_CODECBUFFER * pOutput, AAC_ENCODER* hAacEnc = (AAC_ENCODER*)hCodec; Word16 numAncDataBytes=0; Word32 inbuflen; - int ret, length; + int length; if(NULL == hAacEnc) return VO_ERR_INVALID_ARG; diff --git a/media/libstagefright/codecs/aacenc/src/aacenc_core.c b/media/libstagefright/codecs/aacenc/src/aacenc_core.c index cecbc8f..de452d4 100644 --- a/media/libstagefright/codecs/aacenc/src/aacenc_core.c +++ b/media/libstagefright/codecs/aacenc/src/aacenc_core.c @@ -58,7 +58,6 @@ Word16 AacEncOpen( AAC_ENCODER* hAacEnc, /* pointer to an encoder const AACENC_CONFIG config /* pre-initialized config struct */ ) { - Word32 i; Word32 error = 0; Word16 profile = 1; diff --git a/media/libstagefright/codecs/aacenc/src/adj_thr.c b/media/libstagefright/codecs/aacenc/src/adj_thr.c index 471631c..8b8be0e 100644 --- a/media/libstagefright/codecs/aacenc/src/adj_thr.c +++ b/media/libstagefright/codecs/aacenc/src/adj_thr.c @@ -96,7 +96,7 @@ static void adaptMinSnr(PSY_OUT_CHANNEL psyOutChannel[MAX_CHANNELS], MINSNR_ADAPT_PARAM *msaParam, const Word16 nChannels) { - Word16 ch, sfb, sfbOffs, shift; + Word16 ch, sfb, sfbOffs; Word32 nSfb, avgEn; Word16 log_avgEn = 0; Word32 startRatio_x_avgEn = 0; diff --git a/media/libstagefright/codecs/aacenc/src/bitbuffer.c b/media/libstagefright/codecs/aacenc/src/bitbuffer.c index 0ce93d3..15eebd0 100644 --- a/media/libstagefright/codecs/aacenc/src/bitbuffer.c +++ b/media/libstagefright/codecs/aacenc/src/bitbuffer.c @@ -24,29 +24,6 @@ /***************************************************************************** * -* function name: updateBitBufWordPtr -* description: update Bit Buffer pointer -* -*****************************************************************************/ -static void updateBitBufWordPtr(HANDLE_BIT_BUF hBitBuf, - UWord8 **pBitBufWord, - Word16 cnt) -{ - *pBitBufWord += cnt; - - - if(*pBitBufWord > hBitBuf->pBitBufEnd) { - *pBitBufWord -= (hBitBuf->pBitBufEnd - hBitBuf->pBitBufBase + 1); - } - - if(*pBitBufWord < hBitBuf->pBitBufBase) { - *pBitBufWord += (hBitBuf->pBitBufEnd - hBitBuf->pBitBufBase + 1); - } -} - - -/***************************************************************************** -* * function name: CreateBitBuffer * description: create and init Bit Buffer Management * diff --git a/media/libstagefright/codecs/aacenc/src/bitenc.c b/media/libstagefright/codecs/aacenc/src/bitenc.c index d1fd647..9c81204 100644 --- a/media/libstagefright/codecs/aacenc/src/bitenc.c +++ b/media/libstagefright/codecs/aacenc/src/bitenc.c @@ -547,7 +547,7 @@ static void writeFillElement( const UWord8 *ancBytes, totFillBits = totFillBits - (3+4); - if ((cnt == (1<<4)-1)) { + if (cnt == (1<<4)-1) { esc_count = min( ((totFillBits >> 3) - ((1<<4)-1)), (1<<8)-1); WriteBits(hBitStream,esc_count,8); diff --git a/media/libstagefright/codecs/aacenc/src/block_switch.c b/media/libstagefright/codecs/aacenc/src/block_switch.c index c80538f..11bc7e7 100644 --- a/media/libstagefright/codecs/aacenc/src/block_switch.c +++ b/media/libstagefright/codecs/aacenc/src/block_switch.c @@ -30,9 +30,6 @@ #define ENERGY_SHIFT (8 - 1) /**************** internal function prototypes ***********/ -static Word16 -IIRFilter(const Word16 in, const Word32 coeff[], Word32 states[]); - static Word32 SrchMaxWithIndex(const Word32 *in, Word16 *index, Word16 n); @@ -280,7 +277,7 @@ Word32 CalcWindowEnergy(BLOCK_SWITCHING_CONTROL *blockSwitchingControl, Word16 chIncrement, Word16 windowLen) { - Word32 w, i, wOffset, tidx, ch; + Word32 w, i, tidx; Word32 accuUE, accuFE; Word32 tempUnfiltered; Word32 tempFiltered; @@ -329,30 +326,6 @@ Word32 CalcWindowEnergy(BLOCK_SWITCHING_CONTROL *blockSwitchingControl, } #endif -/***************************************************************************** -* -* function name: IIRFilter -* description: calculate the iir-filter for an array -* returns: the result after iir-filter -* -**********************************************************************************/ -static Word16 IIRFilter(const Word16 in, const Word32 coeff[], Word32 states[]) -{ - Word32 accu1, accu2, accu3; - Word32 out; - - accu1 = L_mpy_ls(coeff[1], in); - accu3 = accu1 - states[0]; - accu2 = fixmul( coeff[0], states[1] ); - out = accu3 - accu2; - - states[0] = accu1; - states[1] = out; - - return round16(out); -} - - static Word16 synchronizedBlockTypeTable[4][4] = { /* LONG_WINDOW START_WINDOW SHORT_WINDOW STOP_WINDOW */ /* LONG_WINDOW */{LONG_WINDOW, START_WINDOW, SHORT_WINDOW, STOP_WINDOW}, diff --git a/media/libstagefright/codecs/aacenc/src/ms_stereo.c b/media/libstagefright/codecs/aacenc/src/ms_stereo.c index 2e34f14..1e4b227 100644 --- a/media/libstagefright/codecs/aacenc/src/ms_stereo.c +++ b/media/libstagefright/codecs/aacenc/src/ms_stereo.c @@ -50,7 +50,6 @@ void MsStereoProcessing(Word32 *sfbEnergyLeft, const Word16 sfbPerGroup, const Word16 maxSfbPerGroup, const Word16 *sfbOffset) { - Word32 temp; Word32 sfb,sfboffs, j; Word32 msMaskTrueSomewhere = 0; Word32 msMaskFalseSomewhere = 0; diff --git a/media/libstagefright/codecs/aacenc/src/sf_estim.c b/media/libstagefright/codecs/aacenc/src/sf_estim.c index bc320ec..78947e1 100644 --- a/media/libstagefright/codecs/aacenc/src/sf_estim.c +++ b/media/libstagefright/codecs/aacenc/src/sf_estim.c @@ -99,7 +99,7 @@ CalcFormFactorChannel(Word16 *logSfbFormFactor, { Word32 sfbw, sfbw1; Word32 i, j; - Word32 sfbOffs, sfb, shift; + Word32 sfbOffs, sfb; sfbw = sfbw1 = 0; for (sfbOffs=0; sfbOffs<psyOutChan->sfbCnt; sfbOffs+=psyOutChan->sfbPerGroup){ diff --git a/media/libstagefright/codecs/aacenc/src/tns.c b/media/libstagefright/codecs/aacenc/src/tns.c index 5172612..27c3971 100644 --- a/media/libstagefright/codecs/aacenc/src/tns.c +++ b/media/libstagefright/codecs/aacenc/src/tns.c @@ -140,7 +140,7 @@ Word16 InitTnsConfigurationLong(Word32 bitRate, /*!< bitrate */ Word16 active) /*!< tns active flag */ { - Word32 bitratePerChannel; + Word32 bitratePerChannel __unused; tC->maxOrder = TNS_MAX_ORDER; tC->tnsStartFreq = 1275; tC->coefRes = 4; @@ -206,7 +206,7 @@ Word16 InitTnsConfigurationShort(Word32 bitRate, /*!< bitrate */ PSY_CONFIGURATION_SHORT *pC, /*!< psy config struct */ Word16 active) /*!< tns active flag */ { - Word32 bitratePerChannel; + Word32 bitratePerChannel __unused; tC->maxOrder = TNS_MAX_ORDER_SHORT; tC->tnsStartFreq = 2750; tC->coefRes = 3; @@ -497,36 +497,6 @@ Word16 TnsEncode(TNS_INFO* tnsInfo, /*!< tns info structure (modified) */ /***************************************************************************** * -* function name: m_pow2_cordic -* description: Iterative power function -* -* Calculates pow(2.0,x-1.0*(scale+1)) with INT_BITS bit precision -* using modified cordic algorithm -* returns: the result of pow2 -* -*****************************************************************************/ -static Word32 m_pow2_cordic(Word32 x, Word16 scale) -{ - Word32 k; - - Word32 accu_y = 0x40000000; - accu_y = L_shr(accu_y,scale); - - for(k=1; k<INT_BITS; k++) { - const Word32 z = m_log2_table[k]; - - while(L_sub(x,z) >= 0) { - - x = L_sub(x, z); - accu_y = L_add(accu_y, (accu_y >> k)); - } - } - return(accu_y); -} - - -/***************************************************************************** -* * function name: CalcWeightedSpectrum * description: Calculate weighted spectrum for LPC calculation * diff --git a/media/libstagefright/codecs/aacenc/src/transform.c b/media/libstagefright/codecs/aacenc/src/transform.c index a02336f..0080810 100644 --- a/media/libstagefright/codecs/aacenc/src/transform.c +++ b/media/libstagefright/codecs/aacenc/src/transform.c @@ -475,7 +475,6 @@ void Transform_Real(Word16 *mdctDelayBuffer, Word32 *winPtr; Word32 delayBufferSf,timeSignalSf,minSf; - Word32 headRoom=0; switch(blockType){ diff --git a/media/libstagefright/codecs/amrnb/common/Android.mk b/media/libstagefright/codecs/amrnb/common/Android.mk index a2b3c8f..5e632a6 100644 --- a/media/libstagefright/codecs/amrnb/common/Android.mk +++ b/media/libstagefright/codecs/amrnb/common/Android.mk @@ -67,7 +67,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include LOCAL_CFLAGS := \ - -DOSCL_UNUSED_ARG= -DOSCL_IMPORT_REF= -DOSCL_EXPORT_REF= + -D"OSCL_UNUSED_ARG(x)=(void)(x)" -DOSCL_IMPORT_REF= -DOSCL_EXPORT_REF= LOCAL_CFLAGS += -Werror diff --git a/media/libstagefright/codecs/amrnb/common/include/basic_op_c_equivalent.h b/media/libstagefright/codecs/amrnb/common/include/basic_op_c_equivalent.h index 35638e3..c4e4d4f 100644 --- a/media/libstagefright/codecs/amrnb/common/include/basic_op_c_equivalent.h +++ b/media/libstagefright/codecs/amrnb/common/include/basic_op_c_equivalent.h @@ -115,7 +115,7 @@ extern "C" Returns: L_sum = 32-bit sum of L_var1 and L_var2 (Word32) */ - static inline Word32 L_add(register Word32 L_var1, register Word32 L_var2, Flag *pOverflow) + static inline Word32 L_add(Word32 L_var1, Word32 L_var2, Flag *pOverflow) { Word32 L_sum; @@ -154,8 +154,8 @@ extern "C" Returns: L_diff = 32-bit difference of L_var1 and L_var2 (Word32) */ - static inline Word32 L_sub(register Word32 L_var1, register Word32 L_var2, - register Flag *pOverflow) + static inline Word32 L_sub(Word32 L_var1, Word32 L_var2, + Flag *pOverflow) { Word32 L_diff; @@ -246,7 +246,7 @@ extern "C" */ static inline Word32 L_mult(Word16 var1, Word16 var2, Flag *pOverflow) { - register Word32 L_product; + Word32 L_product; L_product = (Word32) var1 * var2; @@ -452,7 +452,7 @@ extern "C" */ static inline Word16 mult(Word16 var1, Word16 var2, Flag *pOverflow) { - register Word32 product; + Word32 product; product = ((Word32) var1 * var2) >> 15; diff --git a/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp b/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp index 4135f30..976b1a6 100644 --- a/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp @@ -564,10 +564,10 @@ void Az_lsp( Flag *pOverflow /* (i/o): overflow flag */ ) { - register Word16 i; - register Word16 j; - register Word16 nf; - register Word16 ip; + Word16 i; + Word16 j; + Word16 nf; + Word16 ip; Word16 xlow; Word16 ylow; Word16 xhigh; diff --git a/media/libstagefright/codecs/amrnb/common/src/div_s.cpp b/media/libstagefright/codecs/amrnb/common/src/div_s.cpp index f3bed7e..14d30c5 100644 --- a/media/libstagefright/codecs/amrnb/common/src/div_s.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/div_s.cpp @@ -207,13 +207,13 @@ Word16 div_s (Word16 var1, Word16 var2) /*---------------------------------------------------------------------------- ; FUNCTION CODE ----------------------------------------------------------------------------*/ -Word16 div_s(register Word16 var1, register Word16 var2) +Word16 div_s(Word16 var1, Word16 var2) { /*---------------------------------------------------------------------------- ; Define all local variables ----------------------------------------------------------------------------*/ Word16 var_out = 0; - register Word16 iteration; + Word16 iteration; Word32 L_num; Word32 L_denom; Word32 L_denom_by_2; diff --git a/media/libstagefright/codecs/amrnb/common/src/gc_pred.cpp b/media/libstagefright/codecs/amrnb/common/src/gc_pred.cpp index 3650f3c..1c8a700 100644 --- a/media/libstagefright/codecs/amrnb/common/src/gc_pred.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/gc_pred.cpp @@ -477,9 +477,9 @@ void gc_pred( Flag *pOverflow ) { - register Word16 i; - register Word32 L_temp1, L_temp2; - register Word32 L_tmp; + Word16 i; + Word32 L_temp1, L_temp2; + Word32 L_tmp; Word32 ener_code; Word32 ener; Word16 exp, frac; @@ -993,7 +993,7 @@ void gc_pred_average_limited( ) { Word16 av_pred_en; - register Word16 i; + Word16 i; /* do average in MR122 mode (log2() domain) */ av_pred_en = 0; diff --git a/media/libstagefright/codecs/amrnb/common/src/gmed_n.cpp b/media/libstagefright/codecs/amrnb/common/src/gmed_n.cpp index be76241..2d3b9e4 100644 --- a/media/libstagefright/codecs/amrnb/common/src/gmed_n.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/gmed_n.cpp @@ -185,9 +185,9 @@ Word16 gmed_n( /* o : the median value */ Word16 n /* i : number of inputs */ ) { - register Word16 i, j, ix = 0; - register Word16 max; - register Word16 medianIndex; + Word16 i, j, ix = 0; + Word16 max; + Word16 medianIndex; Word16 tmp[NMAX]; Word16 tmp2[NMAX]; diff --git a/media/libstagefright/codecs/amrnb/common/src/lsp_az.cpp b/media/libstagefright/codecs/amrnb/common/src/lsp_az.cpp index 6b7b471..495359f 100644 --- a/media/libstagefright/codecs/amrnb/common/src/lsp_az.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/lsp_az.cpp @@ -254,8 +254,8 @@ static void Get_lsp_pol( Word32 *f, Flag *pOverflow) { - register Word16 i; - register Word16 j; + Word16 i; + Word16 j; Word16 hi; Word16 lo; @@ -511,8 +511,8 @@ void Lsp_Az( Flag *pOverflow /* (o) : overflow flag */ ) { - register Word16 i; - register Word16 j; + Word16 i; + Word16 j; Word32 f1[6]; Word32 f2[6]; diff --git a/media/libstagefright/codecs/amrnb/common/src/mult_r.cpp b/media/libstagefright/codecs/amrnb/common/src/mult_r.cpp index 0777e68..7112b3d 100644 --- a/media/libstagefright/codecs/amrnb/common/src/mult_r.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/mult_r.cpp @@ -190,7 +190,7 @@ Word16 mult_r (Word16 var1, Word16 var2) Word16 mult_r(Word16 var1, Word16 var2, Flag *pOverflow) { - register Word32 L_product_arr; + Word32 L_product_arr; L_product_arr = ((Word32) var1) * var2; /* product */ L_product_arr += (Word32) 0x00004000L; /* round */ diff --git a/media/libstagefright/codecs/amrnb/common/src/norm_l.cpp b/media/libstagefright/codecs/amrnb/common/src/norm_l.cpp index 132fed6..d8d1259 100644 --- a/media/libstagefright/codecs/amrnb/common/src/norm_l.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/norm_l.cpp @@ -197,12 +197,12 @@ Word16 norm_l (Word32 L_var1) ; FUNCTION CODE ----------------------------------------------------------------------------*/ #if !( defined(PV_ARM_V5) || defined(PV_ARM_GCC_V5) ) -Word16 norm_l(register Word32 L_var1) +Word16 norm_l(Word32 L_var1) { /*---------------------------------------------------------------------------- ; Define all local variables ----------------------------------------------------------------------------*/ - register Word16 var_out = 0; + Word16 var_out = 0; /*---------------------------------------------------------------------------- ; Function body here diff --git a/media/libstagefright/codecs/amrnb/common/src/norm_s.cpp b/media/libstagefright/codecs/amrnb/common/src/norm_s.cpp index 8cdcdb8..6468b67 100644 --- a/media/libstagefright/codecs/amrnb/common/src/norm_s.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/norm_s.cpp @@ -194,13 +194,13 @@ Word16 norm_s (Word16 var1) ----------------------------------------------------------------------------*/ #if !( defined(PV_ARM_V5) || defined(PV_ARM_GCC_V5) ) -Word16 norm_s(register Word16 var1) +Word16 norm_s(Word16 var1) { /*---------------------------------------------------------------------------- ; Define all local variables ----------------------------------------------------------------------------*/ - register Word16 var_out = 0; + Word16 var_out = 0; /*---------------------------------------------------------------------------- ; Function body here diff --git a/media/libstagefright/codecs/amrnb/common/src/pred_lt.cpp b/media/libstagefright/codecs/amrnb/common/src/pred_lt.cpp index 9163623..8a1aa9e 100644 --- a/media/libstagefright/codecs/amrnb/common/src/pred_lt.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/pred_lt.cpp @@ -260,9 +260,9 @@ void Pred_lt_3or6( Flag *pOverflow /* output: if set, overflow occurred in this function */ ) { - register Word16 i; - register Word16 j; - register Word16 k; + Word16 i; + Word16 j; + Word16 k; Word16 *pX0; Word16 *pX2; diff --git a/media/libstagefright/codecs/amrnb/common/src/q_plsf_3.cpp b/media/libstagefright/codecs/amrnb/common/src/q_plsf_3.cpp index 2b30bf4..c70847e 100644 --- a/media/libstagefright/codecs/amrnb/common/src/q_plsf_3.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/q_plsf_3.cpp @@ -281,7 +281,7 @@ static Word16 Vq_subvec4( /* o: quantization index, Q0 */ Flag *pOverflow /* o : Flag set when overflow occurs */ ) { - register Word16 i; + Word16 i; Word16 temp; const Word16 *p_dico; Word16 index = 0; @@ -607,7 +607,7 @@ static Word16 Vq_subvec3( /* o: quantization index, Q0 */ Flag use_half, /* i: use every second entry in codebook */ Flag *pOverflow) /* o : Flag set when overflow occurs */ { - register Word16 i; + Word16 i; Word16 temp; const Word16 *p_dico; @@ -1013,7 +1013,7 @@ void Q_plsf_3( Flag *pOverflow /* o : Flag set when overflow occurs */ ) { - register Word16 i, j; + Word16 i, j; Word16 lsf1[M]; Word16 wf1[M]; Word16 lsf_p[M]; diff --git a/media/libstagefright/codecs/amrnb/common/src/residu.cpp b/media/libstagefright/codecs/amrnb/common/src/residu.cpp index b25d3be..2ad132f 100644 --- a/media/libstagefright/codecs/amrnb/common/src/residu.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/residu.cpp @@ -202,7 +202,7 @@ void Residu( { - register Word16 i, j; + Word16 i, j; Word32 s1; Word32 s2; Word32 s3; diff --git a/media/libstagefright/codecs/amrnb/common/src/shr.cpp b/media/libstagefright/codecs/amrnb/common/src/shr.cpp index 775dc69..1018d9c 100644 --- a/media/libstagefright/codecs/amrnb/common/src/shr.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/shr.cpp @@ -202,10 +202,10 @@ Word16 shr_std (Word16 var1, Word16 var2) /*---------------------------------------------------------------------------- ; FUNCTION CODE ----------------------------------------------------------------------------*/ -Word16 shr(register Word16 var1, register Word16 var2, Flag *pOverflow) +Word16 shr(Word16 var1, Word16 var2, Flag *pOverflow) { - register Word16 result; - register Word32 temp_res; + Word16 result; + Word32 temp_res; if (var2 != 0) { diff --git a/media/libstagefright/codecs/amrnb/common/src/weight_a.cpp b/media/libstagefright/codecs/amrnb/common/src/weight_a.cpp index 2e2efc4..ee821ef 100644 --- a/media/libstagefright/codecs/amrnb/common/src/weight_a.cpp +++ b/media/libstagefright/codecs/amrnb/common/src/weight_a.cpp @@ -178,7 +178,7 @@ void Weight_Ai( Word16 a_exp[] /* (o) : Spectral expanded LPC coefficients */ ) { - register Word16 i; + Word16 i; *(a_exp) = *(a); diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk index 4aa8c17..3750e2e 100644 --- a/media/libstagefright/codecs/amrnb/dec/Android.mk +++ b/media/libstagefright/codecs/amrnb/dec/Android.mk @@ -45,7 +45,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/../common/include LOCAL_CFLAGS := \ - -DOSCL_UNUSED_ARG= -DOSCL_IMPORT_REF= + -D"OSCL_UNUSED_ARG(x)=(void)(x)" -DOSCL_IMPORT_REF= LOCAL_CFLAGS += -Werror diff --git a/media/libstagefright/codecs/amrnb/dec/src/d1035pf.cpp b/media/libstagefright/codecs/amrnb/dec/src/d1035pf.cpp index 899daba..861b3e6 100644 --- a/media/libstagefright/codecs/amrnb/dec/src/d1035pf.cpp +++ b/media/libstagefright/codecs/amrnb/dec/src/d1035pf.cpp @@ -209,7 +209,7 @@ void dec_10i40_35bits( Word16 cod[] /* (o) : algebraic (fixed) codebook excitation */ ) { - register Word16 i, j, pos1, pos2; + Word16 i, j, pos1, pos2; Word16 sign, tmp; for (i = 0; i < L_CODE; i++) diff --git a/media/libstagefright/codecs/amrnb/dec/src/d_plsf_5.cpp b/media/libstagefright/codecs/amrnb/dec/src/d_plsf_5.cpp index 08b690d..7068c0a 100644 --- a/media/libstagefright/codecs/amrnb/dec/src/d_plsf_5.cpp +++ b/media/libstagefright/codecs/amrnb/dec/src/d_plsf_5.cpp @@ -308,7 +308,7 @@ void D_plsf_5( Flag *pOverflow /* o : Flag set when overflow occurs */ ) { - register Word16 i; + Word16 i; Word16 temp; Word16 sign; diff --git a/media/libstagefright/codecs/amrnb/dec/src/int_lsf.cpp b/media/libstagefright/codecs/amrnb/dec/src/int_lsf.cpp index c5aefe4..2ca30de 100644 --- a/media/libstagefright/codecs/amrnb/dec/src/int_lsf.cpp +++ b/media/libstagefright/codecs/amrnb/dec/src/int_lsf.cpp @@ -218,9 +218,9 @@ void Int_lsf( Flag *pOverflow /* o : flag set if overflow occurs */ ) { - register Word16 i; - register Word16 temp1; - register Word16 temp2; + Word16 i; + Word16 temp1; + Word16 temp2; if (i_subfr == 0) { diff --git a/media/libstagefright/codecs/amrnb/dec/src/ph_disp.cpp b/media/libstagefright/codecs/amrnb/dec/src/ph_disp.cpp index da5445b..285465f 100644 --- a/media/libstagefright/codecs/amrnb/dec/src/ph_disp.cpp +++ b/media/libstagefright/codecs/amrnb/dec/src/ph_disp.cpp @@ -207,7 +207,7 @@ int ph_disp_reset (ph_dispState *state) Word16 ph_disp_reset(ph_dispState *state) { - register Word16 i; + Word16 i; if (state == (ph_dispState *) NULL) { @@ -667,15 +667,15 @@ void ph_disp( Flag *pOverflow /* i/o : oveflow indicator */ ) { - register Word16 i, i1; - register Word16 tmp1; + Word16 i, i1; + Word16 tmp1; Word32 L_temp; Word32 L_temp2; Word16 impNr; /* indicator for amount of disp./filter used */ Word16 inno_sav[L_SUBFR]; Word16 ps_poss[L_SUBFR]; - register Word16 nze, nPulse; + Word16 nze, nPulse; Word16 ppos; const Word16 *ph_imp; /* Pointer to phase dispersion filter */ diff --git a/media/libstagefright/codecs/amrnb/dec/src/pstfilt.cpp b/media/libstagefright/codecs/amrnb/dec/src/pstfilt.cpp index 0336990..39e01a2 100644 --- a/media/libstagefright/codecs/amrnb/dec/src/pstfilt.cpp +++ b/media/libstagefright/codecs/amrnb/dec/src/pstfilt.cpp @@ -445,13 +445,13 @@ void Post_Filter( ) { Word16 Ap3[MP1]; - Word16 Ap4[MP1]; /* bandwidth expanded LP parameters */ - Word16 *Az; /* pointer to Az_4: */ + Word16 Ap4[MP1]; /* bandwidth expanded LP parameters */ + Word16 *Az; /* pointer to Az_4: */ /* LPC parameters in each subframe */ - register Word16 i_subfr; /* index for beginning of subframe */ + Word16 i_subfr; /* index for beginning of subframe */ Word16 h[L_H]; - register Word16 i; + Word16 i; Word16 temp1; Word16 temp2; Word32 L_tmp; diff --git a/media/libstagefright/codecs/amrnb/dec/test/amrnbdec_test.cpp b/media/libstagefright/codecs/amrnb/dec/test/amrnbdec_test.cpp index 521fe2b..41a9e98 100755..100644 --- a/media/libstagefright/codecs/amrnb/dec/test/amrnbdec_test.cpp +++ b/media/libstagefright/codecs/amrnb/dec/test/amrnbdec_test.cpp @@ -26,6 +26,7 @@ * SUCH DAMAGE. */ +#include <malloc.h> #include <stdio.h> #include <stdint.h> #include <string.h> diff --git a/media/libstagefright/codecs/amrnb/enc/Android.mk b/media/libstagefright/codecs/amrnb/enc/Android.mk index afc0b89..bdba8a9 100644 --- a/media/libstagefright/codecs/amrnb/enc/Android.mk +++ b/media/libstagefright/codecs/amrnb/enc/Android.mk @@ -67,7 +67,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/../common/include LOCAL_CFLAGS := \ - -DOSCL_UNUSED_ARG= + -D"OSCL_UNUSED_ARG(x)=(void)(x)" LOCAL_CFLAGS += -Werror diff --git a/media/libstagefright/codecs/amrnb/enc/src/autocorr.cpp b/media/libstagefright/codecs/amrnb/enc/src/autocorr.cpp index 0d3acac..c71811d 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/autocorr.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/autocorr.cpp @@ -306,9 +306,9 @@ Word16 Autocorr( Flag *pOverflow /* (o) : indicates overflow */ ) { - register Word16 i; - register Word16 j; - register Word16 norm; + Word16 i; + Word16 j; + Word16 norm; Word16 y[L_WINDOW]; Word32 sum; diff --git a/media/libstagefright/codecs/amrnb/enc/src/c2_9pf.cpp b/media/libstagefright/codecs/amrnb/enc/src/c2_9pf.cpp index a33cdf74..b211032 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/c2_9pf.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/c2_9pf.cpp @@ -318,7 +318,7 @@ extern "C" Word16 dn_sign[L_CODE]; Word16 rr[L_CODE][L_CODE]; - register Word16 i; + Word16 i; Word16 index; Word16 sharp; @@ -592,10 +592,10 @@ extern "C" Flag * pOverflow /* o : Flag set when overflow occurs */ ) { - register Word16 i0; - register Word16 i1; + Word16 i0; + Word16 i1; Word16 ix = 0; /* initialization only needed to keep gcc silent */ - register Word16 track1; + Word16 track1; Word16 ipos[NB_PULSE]; Word16 psk; Word16 ps0; @@ -608,7 +608,7 @@ extern "C" Word32 s; Word32 alp0; Word32 alp1; - register Word16 i; + Word16 i; Word32 L_temp; Word16 *p_codvec = &codvec[0]; @@ -993,13 +993,13 @@ extern "C" Flag *pOverflow /* o : Flag set when overflow occurs */ ) { - register Word16 i; - register Word16 j; - register Word16 k; - register Word16 track; - register Word16 first; - register Word16 index; - register Word16 rsign; + Word16 i; + Word16 j; + Word16 k; + Word16 track; + Word16 first; + Word16 index; + Word16 rsign; Word16 indx; Word16 _sign[NB_PULSE]; Word16 *p0; diff --git a/media/libstagefright/codecs/amrnb/enc/src/cl_ltp.cpp b/media/libstagefright/codecs/amrnb/enc/src/cl_ltp.cpp index 4a05327..525e57d 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/cl_ltp.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/cl_ltp.cpp @@ -638,7 +638,7 @@ void cl_ltp( Flag *pOverflow /* o : overflow indicator */ ) { - register Word16 i; + Word16 i; Word16 index; Word32 L_temp; /* temporarily variable */ Word16 resu3; /* flag for upsample resolution */ diff --git a/media/libstagefright/codecs/amrnb/enc/src/convolve.cpp b/media/libstagefright/codecs/amrnb/enc/src/convolve.cpp index e9ce7ba..5015a4a 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/convolve.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/convolve.cpp @@ -212,7 +212,7 @@ void Convolve( Word16 L /* (i) : vector size */ ) { - register Word16 i, n; + Word16 i, n; Word32 s1, s2; diff --git a/media/libstagefright/codecs/amrnb/enc/src/cor_h.cpp b/media/libstagefright/codecs/amrnb/enc/src/cor_h.cpp index e46d99f..20583c4 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/cor_h.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/cor_h.cpp @@ -272,8 +272,8 @@ void cor_h( Flag *pOverflow ) { - register Word16 i; - register Word16 dec; + Word16 i; + Word16 dec; Word16 h2[L_CODE]; Word32 s; diff --git a/media/libstagefright/codecs/amrnb/enc/src/cor_h_x.cpp b/media/libstagefright/codecs/amrnb/enc/src/cor_h_x.cpp index beb2aec..c25c026 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/cor_h_x.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/cor_h_x.cpp @@ -249,9 +249,9 @@ void cor_h_x( Flag *pOverflow /* (o): pointer to overflow flag */ ) { - register Word16 i; - register Word16 j; - register Word16 k; + Word16 i; + Word16 j; + Word16 k; Word32 s; Word32 y32[L_CODE]; diff --git a/media/libstagefright/codecs/amrnb/enc/src/cor_h_x2.cpp b/media/libstagefright/codecs/amrnb/enc/src/cor_h_x2.cpp index da60640..b4fd867 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/cor_h_x2.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/cor_h_x2.cpp @@ -236,9 +236,9 @@ void cor_h_x2( Flag *pOverflow ) { - register Word16 i; - register Word16 j; - register Word16 k; + Word16 i; + Word16 j; + Word16 k; Word32 s; Word32 y32[L_CODE]; Word32 max; diff --git a/media/libstagefright/codecs/amrnb/enc/src/dtx_enc.cpp b/media/libstagefright/codecs/amrnb/enc/src/dtx_enc.cpp index 276e590..2ccb777 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/dtx_enc.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/dtx_enc.cpp @@ -130,7 +130,7 @@ terms listed above has been obtained from the copyright holder. ; MACROS ; Define module specific macros here ----------------------------------------------------------------------------*/ -extern Word32 L_add(register Word32 L_var1, register Word32 L_var2, Flag *pOverflow); +extern Word32 L_add(Word32 L_var1, Word32 L_var2, Flag *pOverflow); /*---------------------------------------------------------------------------- ; DEFINES @@ -671,7 +671,7 @@ void dtx_enc(dtx_encState *st, /* i/o : State struct */ Flag *pOverflow /* i/o : overflow indicator */ ) { - register Word16 i, j; + Word16 i, j; Word16 temp; Word16 log_en; Word16 lsf[M]; @@ -943,7 +943,7 @@ void dtx_buffer(dtx_encState *st, /* i/o : State struct */ ) { - register Word16 i; + Word16 i; Word32 L_frame_en; Word32 L_temp; Word16 log_en_e; diff --git a/media/libstagefright/codecs/amrnb/enc/src/levinson.cpp b/media/libstagefright/codecs/amrnb/enc/src/levinson.cpp index 001897b..29cdac6 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/levinson.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/levinson.cpp @@ -638,8 +638,8 @@ Word16 Levinson( Flag *pOverflow ) { - register Word16 i; - register Word16 j; + Word16 i; + Word16 j; Word16 hi; Word16 lo; Word16 Kh; /* reflexion coefficient; hi and lo */ @@ -651,9 +651,9 @@ Word16 Levinson( Word16 Al[M + 1]; Word16 Anh[M + 1]; /* LPC coef.for next iteration in */ Word16 Anl[M + 1]; /* double prec. */ - register Word32 t0; /* temporary variable */ - register Word32 t1; /* temporary variable */ - register Word32 t2; /* temporary variable */ + Word32 t0; /* temporary variable */ + Word32 t1; /* temporary variable */ + Word32 t2; /* temporary variable */ Word16 *p_Rh; Word16 *p_Rl; diff --git a/media/libstagefright/codecs/amrnb/enc/src/pitch_ol.cpp b/media/libstagefright/codecs/amrnb/enc/src/pitch_ol.cpp index d3a2ec0..c039bb0 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/pitch_ol.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/pitch_ol.cpp @@ -320,7 +320,7 @@ static Word16 Lag_max( /* o : lag found */ ) #endif { - register Word16 i; + Word16 i; Word16 *p; Word32 max; Word32 t0; diff --git a/media/libstagefright/codecs/amrnb/enc/src/pre_proc.cpp b/media/libstagefright/codecs/amrnb/enc/src/pre_proc.cpp index fdc2440..042920e 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/pre_proc.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/pre_proc.cpp @@ -542,7 +542,7 @@ void Pre_Process( Word16 signal[], /* input/output signal */ Word16 lg) /* length of signal */ { - register Word16 i; + Word16 i; Word16 x_n_2; Word16 x_n_1; Word32 L_tmp; diff --git a/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp b/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp index d626de3..fa43f78 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp +++ b/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp @@ -248,7 +248,7 @@ void set_sign(Word16 dn[], /* i/o : correlation between target and h[] */ Word16 n /* i : # of maximum correlations in dn2[] */ ) { - register Word16 i, j, k; + Word16 i, j, k; Word16 val, min; Word16 pos = 0; /* initialization only needed to keep gcc silent */ diff --git a/media/libstagefright/codecs/amrwb/Android.mk b/media/libstagefright/codecs/amrwb/Android.mk index efdf988..686f7a3 100644 --- a/media/libstagefright/codecs/amrwb/Android.mk +++ b/media/libstagefright/codecs/amrwb/Android.mk @@ -48,7 +48,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include LOCAL_CFLAGS := \ - -DOSCL_UNUSED_ARG= -DOSCL_IMPORT_REF= + -D"OSCL_UNUSED_ARG(x)=(void)(x)" -DOSCL_IMPORT_REF= LOCAL_CFLAGS += -Werror diff --git a/media/libstagefright/codecs/amrwb/src/pvamrwb_math_op.cpp b/media/libstagefright/codecs/amrwb/src/pvamrwb_math_op.cpp index d1ec790..5872512 100644 --- a/media/libstagefright/codecs/amrwb/src/pvamrwb_math_op.cpp +++ b/media/libstagefright/codecs/amrwb/src/pvamrwb_math_op.cpp @@ -205,7 +205,7 @@ int16 div_16by16(int16 var1, int16 var2) { int16 var_out = 0; - register int16 iteration; + int16 iteration; int32 L_num; int32 L_denom; int32 L_denom_by_2; diff --git a/media/libstagefright/codecs/amrwbenc/src/q_pulse.c b/media/libstagefright/codecs/amrwbenc/src/q_pulse.c index 80a0b73..d658602 100644 --- a/media/libstagefright/codecs/amrwbenc/src/q_pulse.c +++ b/media/libstagefright/codecs/amrwbenc/src/q_pulse.c @@ -188,7 +188,7 @@ Word32 quant_4p_4N( /* (o) return 4*N bits */ Word16 pos[], /* (i) position of the pulse 1..4 */ Word16 N) /* (i) number of bits for position */ { - Word16 nb_pos, mask, n_1, tmp; + Word16 nb_pos, mask __unused, n_1, tmp; Word16 posA[4], posB[4]; Word32 i, j, k, index; diff --git a/media/libstagefright/codecs/amrwbenc/src/wb_vad.c b/media/libstagefright/codecs/amrwbenc/src/wb_vad.c index 13dd2aa..2beaefd 100644 --- a/media/libstagefright/codecs/amrwbenc/src/wb_vad.c +++ b/media/libstagefright/codecs/amrwbenc/src/wb_vad.c @@ -404,7 +404,7 @@ static void noise_estimate_update( alpha_down = ALPHA_DOWN1; } else { - if ((st->stat_count == 0)) + if (st->stat_count == 0) { alpha_up = ALPHA_UP2; alpha_down = ALPHA_DOWN2; diff --git a/media/libstagefright/codecs/avc/common/src/deblock.cpp b/media/libstagefright/codecs/avc/common/src/deblock.cpp index de2d2b6..5f8b693 100644 --- a/media/libstagefright/codecs/avc/common/src/deblock.cpp +++ b/media/libstagefright/codecs/avc/common/src/deblock.cpp @@ -1279,7 +1279,7 @@ void EdgeLoop_Luma_vertical(uint8* SrcPtr, uint8 *Strength, int Alpha, int Beta, int C0, c0, dif, AbsDelta, Strng, tmp, tmp1; int L2 = 0, L1, L0, R0, R1, R2 = 0; uint8 *ptr, *ptr1; - register uint R_in, L_in; + uint R_in, L_in; uint R_out, L_out; diff --git a/media/libstagefright/codecs/avc/enc/Android.mk b/media/libstagefright/codecs/avc/enc/Android.mk index 537ba42..2ceebc8 100644 --- a/media/libstagefright/codecs/avc/enc/Android.mk +++ b/media/libstagefright/codecs/avc/enc/Android.mk @@ -28,7 +28,7 @@ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/native/include/media/openmax LOCAL_CFLAGS := \ - -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF= + -DOSCL_IMPORT_REF= -D"OSCL_UNUSED_ARG(x)=(void)(x)" -DOSCL_EXPORT_REF= LOCAL_CFLAGS += -Werror @@ -51,7 +51,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/../common LOCAL_CFLAGS := \ - -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF= + -DOSCL_IMPORT_REF= -D"OSCL_UNUSED_ARG(x)=(void)(x)" -DOSCL_EXPORT_REF= LOCAL_STATIC_LIBRARIES := \ diff --git a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp index cfc37b7..928a74f 100644 --- a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp +++ b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "SoftAVCEncoder" #include <utils/Log.h> +#include <utils/misc.h> #include "avcenc_api.h" #include "avcenc_int.h" @@ -25,6 +26,7 @@ #include <HardwareAPI.h> #include <MetadataBufferType.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AUtils.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> @@ -51,31 +53,36 @@ static void InitOMXParams(T *params) { params->nVersion.s.nStep = 0; } +static const CodecProfileLevel kProfileLevels[] = { + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2 }, +}; + typedef struct LevelConversion { OMX_U32 omxLevel; AVCLevel avcLevel; + uint32_t maxMacroBlocks; } LevelConcersion; static LevelConversion ConversionTable[] = { - { OMX_VIDEO_AVCLevel1, AVC_LEVEL1_B }, - { OMX_VIDEO_AVCLevel1b, AVC_LEVEL1 }, - { OMX_VIDEO_AVCLevel11, AVC_LEVEL1_1 }, - { OMX_VIDEO_AVCLevel12, AVC_LEVEL1_2 }, - { OMX_VIDEO_AVCLevel13, AVC_LEVEL1_3 }, - { OMX_VIDEO_AVCLevel2, AVC_LEVEL2 }, + { OMX_VIDEO_AVCLevel1, AVC_LEVEL1_B, 99 }, + { OMX_VIDEO_AVCLevel1b, AVC_LEVEL1, 99 }, + { OMX_VIDEO_AVCLevel11, AVC_LEVEL1_1, 396 }, + { OMX_VIDEO_AVCLevel12, AVC_LEVEL1_2, 396 }, + { OMX_VIDEO_AVCLevel13, AVC_LEVEL1_3, 396 }, + { OMX_VIDEO_AVCLevel2, AVC_LEVEL2, 396 }, #if 0 - // encoding speed is very poor if video - // resolution is higher than CIF - { OMX_VIDEO_AVCLevel21, AVC_LEVEL2_1 }, - { OMX_VIDEO_AVCLevel22, AVC_LEVEL2_2 }, - { OMX_VIDEO_AVCLevel3, AVC_LEVEL3 }, - { OMX_VIDEO_AVCLevel31, AVC_LEVEL3_1 }, - { OMX_VIDEO_AVCLevel32, AVC_LEVEL3_2 }, - { OMX_VIDEO_AVCLevel4, AVC_LEVEL4 }, - { OMX_VIDEO_AVCLevel41, AVC_LEVEL4_1 }, - { OMX_VIDEO_AVCLevel42, AVC_LEVEL4_2 }, - { OMX_VIDEO_AVCLevel5, AVC_LEVEL5 }, - { OMX_VIDEO_AVCLevel51, AVC_LEVEL5_1 }, + // encoding speed is very poor if video resolution + // is higher than CIF or if level is higher than 2 + { OMX_VIDEO_AVCLevel21, AVC_LEVEL2_1, 792 }, + { OMX_VIDEO_AVCLevel22, AVC_LEVEL2_2, 1620 }, + { OMX_VIDEO_AVCLevel3, AVC_LEVEL3, 1620 }, + { OMX_VIDEO_AVCLevel31, AVC_LEVEL3_1, 3600 }, + { OMX_VIDEO_AVCLevel32, AVC_LEVEL3_2, 5120 }, + { OMX_VIDEO_AVCLevel4, AVC_LEVEL4, 8192 }, + { OMX_VIDEO_AVCLevel41, AVC_LEVEL4_1, 8192 }, + { OMX_VIDEO_AVCLevel42, AVC_LEVEL4_2, 8704 }, + { OMX_VIDEO_AVCLevel5, AVC_LEVEL5, 22080 }, + { OMX_VIDEO_AVCLevel51, AVC_LEVEL5_1, 36864 }, #endif }; @@ -148,13 +155,11 @@ SoftAVCEncoder::SoftAVCEncoder( const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) - : SoftVideoEncoderOMXComponent(name, callbacks, appData, component), - mVideoWidth(176), - mVideoHeight(144), - mVideoFrameRate(30), - mVideoBitRate(192000), - mVideoColorFormat(OMX_COLOR_FormatYUV420Planar), - mStoreMetaDataInBuffers(false), + : SoftVideoEncoderOMXComponent( + name, "video_encoder.avc", OMX_VIDEO_CodingAVC, + kProfileLevels, NELEM(kProfileLevels), + 176 /* width */, 144 /* height */, + callbacks, appData, component), mIDRFrameRefreshIntervalInSec(1), mAVCEncProfile(AVC_BASELINE), mAVCEncLevel(AVC_LEVEL2), @@ -168,7 +173,13 @@ SoftAVCEncoder::SoftAVCEncoder( mInputFrameData(NULL), mSliceGroup(NULL) { - initPorts(); + const size_t kOutputBufferSize = + 320 * ConversionTable[NELEM(ConversionTable) - 1].maxMacroBlocks; + + initPorts( + kNumBuffers, kNumBuffers, kOutputBufferSize, + MEDIA_MIMETYPE_VIDEO_AVC, 2 /* minCompressionRatio */); + ALOGI("Construct SoftAVCEncoder"); } @@ -230,30 +241,28 @@ OMX_ERRORTYPE SoftAVCEncoder::initEncParams() { mEncParams->use_overrun_buffer = AVC_OFF; - if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar - || mStoreMetaDataInBuffers) { + if (mColorFormat != OMX_COLOR_FormatYUV420Planar || mInputDataIsMeta) { // Color conversion is needed. free(mInputFrameData); mInputFrameData = - (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1); + (uint8_t *) malloc((mWidth * mHeight * 3 ) >> 1); CHECK(mInputFrameData != NULL); } // PV's AVC encoder requires the video dimension of multiple - if (mVideoWidth % 16 != 0 || mVideoHeight % 16 != 0) { + if (mWidth % 16 != 0 || mHeight % 16 != 0) { ALOGE("Video frame size %dx%d must be a multiple of 16", - mVideoWidth, mVideoHeight); + mWidth, mHeight); return OMX_ErrorBadParameter; } - mEncParams->width = mVideoWidth; - mEncParams->height = mVideoHeight; - mEncParams->bitrate = mVideoBitRate; - mEncParams->frame_rate = 1000 * mVideoFrameRate; // In frames/ms! - mEncParams->CPB_size = (uint32_t) (mVideoBitRate >> 1); + mEncParams->width = mWidth; + mEncParams->height = mHeight; + mEncParams->bitrate = mBitrate; + mEncParams->frame_rate = (1000 * mFramerate) >> 16; // In frames/ms!, mFramerate is in Q16 + mEncParams->CPB_size = (uint32_t) (mBitrate >> 1); - int32_t nMacroBlocks = ((((mVideoWidth + 15) >> 4) << 4) * - (((mVideoHeight + 15) >> 4) << 4)) >> 8; + int32_t nMacroBlocks = divUp(mWidth, 16) * divUp(mHeight, 16); CHECK(mSliceGroup == NULL); mSliceGroup = (uint32_t *) malloc(sizeof(uint32_t) * nMacroBlocks); CHECK(mSliceGroup != NULL); @@ -272,7 +281,7 @@ OMX_ERRORTYPE SoftAVCEncoder::initEncParams() { mEncParams->idr_period = 1; // All I frames } else { mEncParams->idr_period = - (mIDRFrameRefreshIntervalInSec * mVideoFrameRate); + (mIDRFrameRefreshIntervalInSec * mFramerate) >> 16; // mFramerate is in Q16 } // Set profile and level @@ -345,71 +354,9 @@ void SoftAVCEncoder::releaseOutputBuffers() { mOutputBuffers.clear(); } -void SoftAVCEncoder::initPorts() { - OMX_PARAM_PORTDEFINITIONTYPE def; - InitOMXParams(&def); - - const size_t kInputBufferSize = (mVideoWidth * mVideoHeight * 3) >> 1; - - // 31584 is PV's magic number. Not sure why. - const size_t kOutputBufferSize = - (kInputBufferSize > 31584) ? kInputBufferSize: 31584; - - def.nPortIndex = 0; - def.eDir = OMX_DirInput; - def.nBufferCountMin = kNumBuffers; - def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = kInputBufferSize; - def.bEnabled = OMX_TRUE; - def.bPopulated = OMX_FALSE; - def.eDomain = OMX_PortDomainVideo; - def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 1; - - def.format.video.cMIMEType = const_cast<char *>("video/raw"); - def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; - def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; - def.format.video.xFramerate = (mVideoFrameRate << 16); // Q16 format - def.format.video.nBitrate = mVideoBitRate; - def.format.video.nFrameWidth = mVideoWidth; - def.format.video.nFrameHeight = mVideoHeight; - def.format.video.nStride = mVideoWidth; - def.format.video.nSliceHeight = mVideoHeight; - - addPort(def); - - def.nPortIndex = 1; - def.eDir = OMX_DirOutput; - def.nBufferCountMin = kNumBuffers; - def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = kOutputBufferSize; - def.bEnabled = OMX_TRUE; - def.bPopulated = OMX_FALSE; - def.eDomain = OMX_PortDomainVideo; - def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 2; - - def.format.video.cMIMEType = const_cast<char *>("video/avc"); - def.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; - def.format.video.eColorFormat = OMX_COLOR_FormatUnused; - def.format.video.xFramerate = (0 << 16); // Q16 format - def.format.video.nBitrate = mVideoBitRate; - def.format.video.nFrameWidth = mVideoWidth; - def.format.video.nFrameHeight = mVideoHeight; - def.format.video.nStride = mVideoWidth; - def.format.video.nSliceHeight = mVideoHeight; - - addPort(def); -} - OMX_ERRORTYPE SoftAVCEncoder::internalGetParameter( OMX_INDEXTYPE index, OMX_PTR params) { switch (index) { - case OMX_IndexParamVideoErrorCorrection: - { - return OMX_ErrorNotImplemented; - } - case OMX_IndexParamVideoBitrate: { OMX_VIDEO_PARAM_BITRATETYPE *bitRate = @@ -420,37 +367,7 @@ OMX_ERRORTYPE SoftAVCEncoder::internalGetParameter( } bitRate->eControlRate = OMX_Video_ControlRateVariable; - bitRate->nTargetBitrate = mVideoBitRate; - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoPortFormat: - { - OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; - - if (formatParams->nPortIndex > 1) { - return OMX_ErrorUndefined; - } - - if (formatParams->nIndex > 2) { - return OMX_ErrorNoMore; - } - - if (formatParams->nPortIndex == 0) { - formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; - if (formatParams->nIndex == 0) { - formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; - } else if (formatParams->nIndex == 1) { - formatParams->eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar; - } else { - formatParams->eColorFormat = OMX_COLOR_FormatAndroidOpaque; - } - } else { - formatParams->eCompressionFormat = OMX_VIDEO_CodingAVC; - formatParams->eColorFormat = OMX_COLOR_FormatUnused; - } - + bitRate->nTargetBitrate = mBitrate; return OMX_ErrorNone; } @@ -487,30 +404,8 @@ OMX_ERRORTYPE SoftAVCEncoder::internalGetParameter( return OMX_ErrorNone; } - case OMX_IndexParamVideoProfileLevelQuerySupported: - { - OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = - (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)params; - - if (profileLevel->nPortIndex != 1) { - return OMX_ErrorUndefined; - } - - const size_t size = - sizeof(ConversionTable) / sizeof(ConversionTable[0]); - - if (profileLevel->nProfileIndex >= size) { - return OMX_ErrorNoMore; - } - - profileLevel->eProfile = OMX_VIDEO_AVCProfileBaseline; - profileLevel->eLevel = ConversionTable[profileLevel->nProfileIndex].omxLevel; - - return OMX_ErrorNone; - } - default: - return SimpleSoftOMXComponent::internalGetParameter(index, params); + return SoftVideoEncoderOMXComponent::internalGetParameter(index, params); } } @@ -519,11 +414,6 @@ OMX_ERRORTYPE SoftAVCEncoder::internalSetParameter( int32_t indexFull = index; switch (indexFull) { - case OMX_IndexParamVideoErrorCorrection: - { - return OMX_ErrorNotImplemented; - } - case OMX_IndexParamVideoBitrate: { OMX_VIDEO_PARAM_BITRATETYPE *bitRate = @@ -534,109 +424,7 @@ OMX_ERRORTYPE SoftAVCEncoder::internalSetParameter( return OMX_ErrorUndefined; } - mVideoBitRate = bitRate->nTargetBitrate; - return OMX_ErrorNone; - } - - case OMX_IndexParamPortDefinition: - { - OMX_PARAM_PORTDEFINITIONTYPE *def = - (OMX_PARAM_PORTDEFINITIONTYPE *)params; - if (def->nPortIndex > 1) { - return OMX_ErrorUndefined; - } - - if (def->nPortIndex == 0) { - if (def->format.video.eCompressionFormat != OMX_VIDEO_CodingUnused || - (def->format.video.eColorFormat != OMX_COLOR_FormatYUV420Planar && - def->format.video.eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar && - def->format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque)) { - return OMX_ErrorUndefined; - } - } else { - if (def->format.video.eCompressionFormat != OMX_VIDEO_CodingAVC || - (def->format.video.eColorFormat != OMX_COLOR_FormatUnused)) { - return OMX_ErrorUndefined; - } - } - - OMX_ERRORTYPE err = SimpleSoftOMXComponent::internalSetParameter(index, params); - if (OMX_ErrorNone != err) { - return err; - } - - if (def->nPortIndex == 0) { - mVideoWidth = def->format.video.nFrameWidth; - mVideoHeight = def->format.video.nFrameHeight; - mVideoFrameRate = def->format.video.xFramerate >> 16; - mVideoColorFormat = def->format.video.eColorFormat; - - OMX_PARAM_PORTDEFINITIONTYPE *portDef = - &editPortInfo(0)->mDef; - portDef->format.video.nFrameWidth = mVideoWidth; - portDef->format.video.nFrameHeight = mVideoHeight; - portDef->format.video.nStride = portDef->format.video.nFrameWidth; - portDef->format.video.nSliceHeight = portDef->format.video.nFrameHeight; - portDef->format.video.xFramerate = def->format.video.xFramerate; - portDef->format.video.eColorFormat = - (OMX_COLOR_FORMATTYPE) mVideoColorFormat; - portDef->nBufferSize = - (portDef->format.video.nStride * portDef->format.video.nSliceHeight * 3) / 2; - portDef = &editPortInfo(1)->mDef; - portDef->format.video.nFrameWidth = mVideoWidth; - portDef->format.video.nFrameHeight = mVideoHeight; - } else { - mVideoBitRate = def->format.video.nBitrate; - } - - return OMX_ErrorNone; - } - - case OMX_IndexParamStandardComponentRole: - { - const OMX_PARAM_COMPONENTROLETYPE *roleParams = - (const OMX_PARAM_COMPONENTROLETYPE *)params; - - if (strncmp((const char *)roleParams->cRole, - "video_encoder.avc", - OMX_MAX_STRINGNAME_SIZE - 1)) { - return OMX_ErrorUndefined; - } - - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoPortFormat: - { - const OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; - - if (formatParams->nPortIndex > 1) { - return OMX_ErrorUndefined; - } - - if (formatParams->nIndex > 2) { - return OMX_ErrorNoMore; - } - - if (formatParams->nPortIndex == 0) { - if (formatParams->eCompressionFormat != OMX_VIDEO_CodingUnused || - ((formatParams->nIndex == 0 && - formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) || - (formatParams->nIndex == 1 && - formatParams->eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar) || - (formatParams->nIndex == 2 && - formatParams->eColorFormat != OMX_COLOR_FormatAndroidOpaque) )) { - return OMX_ErrorUndefined; - } - mVideoColorFormat = formatParams->eColorFormat; - } else { - if (formatParams->eCompressionFormat != OMX_VIDEO_CodingAVC || - formatParams->eColorFormat != OMX_COLOR_FormatUnused) { - return OMX_ErrorUndefined; - } - } - + mBitrate = bitRate->nTargetBitrate; return OMX_ErrorNone; } @@ -673,29 +461,8 @@ OMX_ERRORTYPE SoftAVCEncoder::internalSetParameter( return OMX_ErrorNone; } - case kStoreMetaDataExtensionIndex: - { - StoreMetaDataInBuffersParams *storeParams = - (StoreMetaDataInBuffersParams*)params; - if (storeParams->nPortIndex != 0) { - ALOGE("%s: StoreMetadataInBuffersParams.nPortIndex not zero!", - __FUNCTION__); - return OMX_ErrorUndefined; - } - - mStoreMetaDataInBuffers = storeParams->bStoreMetaData; - ALOGV("StoreMetaDataInBuffers set to: %s", - mStoreMetaDataInBuffers ? " true" : "false"); - - if (mStoreMetaDataInBuffers) { - mVideoColorFormat = OMX_COLOR_FormatAndroidOpaque; - } - - return OMX_ErrorNone; - } - default: - return SimpleSoftOMXComponent::internalSetParameter(index, params); + return SoftVideoEncoderOMXComponent::internalSetParameter(index, params); } } @@ -789,16 +556,16 @@ void SoftAVCEncoder::onQueueFilled(OMX_U32 /* portIndex */) { if (inHeader->nFilledLen > 0) { AVCFrameIO videoInput; memset(&videoInput, 0, sizeof(videoInput)); - videoInput.height = ((mVideoHeight + 15) >> 4) << 4; - videoInput.pitch = ((mVideoWidth + 15) >> 4) << 4; + videoInput.height = align(mHeight, 16); + videoInput.pitch = align(mWidth, 16); videoInput.coding_timestamp = (inHeader->nTimeStamp + 500) / 1000; // in ms const uint8_t *inputData = NULL; - if (mStoreMetaDataInBuffers) { + if (mInputDataIsMeta) { inputData = extractGraphicBuffer( - mInputFrameData, (mVideoWidth * mVideoHeight * 3) >> 1, + mInputFrameData, (mWidth * mHeight * 3) >> 1, inHeader->pBuffer + inHeader->nOffset, inHeader->nFilledLen, - mVideoWidth, mVideoHeight); + mWidth, mHeight); if (inputData == NULL) { ALOGE("Unable to extract gralloc buffer in metadata mode"); mSignalledError = true; @@ -808,9 +575,9 @@ void SoftAVCEncoder::onQueueFilled(OMX_U32 /* portIndex */) { // TODO: Verify/convert pixel format enum } else { inputData = (const uint8_t *)inHeader->pBuffer + inHeader->nOffset; - if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) { + if (mColorFormat != OMX_COLOR_FormatYUV420Planar) { ConvertYUV420SemiPlanarToYUV420Planar( - inputData, mInputFrameData, mVideoWidth, mVideoHeight); + inputData, mInputFrameData, mWidth, mHeight); inputData = mInputFrameData; } } diff --git a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h index 130593f..81de109 100644 --- a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h +++ b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h @@ -26,8 +26,6 @@ namespace android { -struct MediaBuffer; - struct SoftAVCEncoder : public MediaBufferObserver, public SoftVideoEncoderOMXComponent { SoftAVCEncoder( @@ -68,12 +66,6 @@ private: int32_t mFlags; } InputBufferInfo; - int32_t mVideoWidth; - int32_t mVideoHeight; - int32_t mVideoFrameRate; - int32_t mVideoBitRate; - int32_t mVideoColorFormat; - bool mStoreMetaDataInBuffers; int32_t mIDRFrameRefreshIntervalInSec; AVCProfile mAVCEncProfile; AVCLevel mAVCEncLevel; @@ -94,7 +86,6 @@ private: Vector<MediaBuffer *> mOutputBuffers; Vector<InputBufferInfo> mInputBufferInfoVec; - void initPorts(); OMX_ERRORTYPE initEncParams(); OMX_ERRORTYPE initEncoder(); OMX_ERRORTYPE releaseEncoder(); diff --git a/media/libstagefright/codecs/avc/enc/src/findhalfpel.cpp b/media/libstagefright/codecs/avc/enc/src/findhalfpel.cpp index 38a2a15..0b8d9e2 100644 --- a/media/libstagefright/codecs/avc/enc/src/findhalfpel.cpp +++ b/media/libstagefright/codecs/avc/enc/src/findhalfpel.cpp @@ -151,8 +151,7 @@ void GenerateHalfPelPred(uint8* subpel_pred, uint8 *ncand, int lx) uint8 tmp8; int32 tmp32; int16 tmp_horz[18*22], *dst_16, *src_16; - register int a = 0, b = 0, c = 0, d = 0, e = 0, f = 0; // temp register - int msk; + int a = 0, b = 0, c = 0, d = 0, e = 0, f = 0; // temp int i, j; /* first copy full-pel to the first array */ @@ -379,7 +378,6 @@ void GenerateHalfPelPred(uint8* subpel_pred, uint8 *ncand, int lx) // one can just use the above code and change the for(i=2 to for(i=18 for (i = 16; i > 0; i -= 4) { - msk = 0; for (j = 17; j > 0; j--) { a = *((uint32*)ref); /* load 4 bytes */ diff --git a/media/libstagefright/codecs/avc/enc/src/init.cpp b/media/libstagefright/codecs/avc/enc/src/init.cpp index c258b57..6e1413a 100644 --- a/media/libstagefright/codecs/avc/enc/src/init.cpp +++ b/media/libstagefright/codecs/avc/enc/src/init.cpp @@ -177,10 +177,6 @@ AVCEnc_Status SetEncodeParam(AVCHandle* avcHandle, AVCEncParams* encParam, seqParam->offset_for_non_ref_pic = extS->offset_for_non_ref_pic; seqParam->offset_for_top_to_bottom_field = extS->offset_for_top_to_bottom_field; seqParam->num_ref_frames_in_pic_order_cnt_cycle = extS->num_ref_frames_in_pic_order_cnt_cycle; - if (extS->offset_for_ref_frame == NULL) - { - return AVCENC_ENCPARAM_MEM_FAIL; - } for (ii = 0; ii < (int) extS->num_ref_frames; ii++) { seqParam->offset_for_ref_frame[ii] = extS->offset_for_ref_frame[ii]; diff --git a/media/libstagefright/codecs/avc/enc/src/rate_control.cpp b/media/libstagefright/codecs/avc/enc/src/rate_control.cpp index aa13873..09dcc28 100644 --- a/media/libstagefright/codecs/avc/enc/src/rate_control.cpp +++ b/media/libstagefright/codecs/avc/enc/src/rate_control.cpp @@ -171,7 +171,7 @@ AVCEnc_Status InitRateControlModule(AVCHandle *avcHandle) AVCRateControl *rateCtrl = encvid->rateCtrl; double L1, L2, L3, bpp; int qp; - int i, j; + int i; rateCtrl->basicUnit = video->PicSizeInMbs; diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp index 1301060..9edffd2 100644 --- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp +++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp @@ -421,7 +421,6 @@ OMX_ERRORTYPE SoftFlacEncoder::configureEncoder() { } FLAC__bool ok = true; - FLAC__StreamEncoderInitStatus initStatus = FLAC__STREAM_ENCODER_INIT_STATUS_OK; ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mNumChannels); ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mSampleRate); ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, 16); diff --git a/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp b/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp index 4debc48..bd01a1a 100644 --- a/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp +++ b/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp @@ -34,6 +34,9 @@ static void InitOMXParams(T *params) { params->nVersion.s.nStep = 0; } +// Microsoft WAV GSM encoding packs two GSM frames into 65 bytes. +static const int kMSGSMFrameSize = 65; + SoftGSM::SoftGSM( const char *name, const OMX_CALLBACKTYPE *callbacks, @@ -64,7 +67,7 @@ void SoftGSM::initPorts() { def.eDir = OMX_DirInput; def.nBufferCountMin = kNumBuffers; def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = sizeof(gsm_frame); + def.nBufferSize = 1024 / kMSGSMFrameSize * kMSGSMFrameSize; def.bEnabled = OMX_TRUE; def.bPopulated = OMX_FALSE; def.eDomain = OMX_PortDomainAudio; @@ -207,8 +210,8 @@ void SoftGSM::onQueueFilled(OMX_U32 /* portIndex */) { mSignalledError = true; } - if(((inHeader->nFilledLen / 65) * 65) != inHeader->nFilledLen) { - ALOGE("input buffer not multiple of 65 (%d).", inHeader->nFilledLen); + if(((inHeader->nFilledLen / kMSGSMFrameSize) * kMSGSMFrameSize) != inHeader->nFilledLen) { + ALOGE("input buffer not multiple of %d (%d).", kMSGSMFrameSize, inHeader->nFilledLen); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; } @@ -258,6 +261,25 @@ int SoftGSM::DecodeGSM(gsm handle, return ret; } +void SoftGSM::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + gsm_destroy(mGsm); + mGsm = gsm_create(); + int msopt = 1; + gsm_option(mGsm, GSM_OPT_WAV49, &msopt); + } +} + +void SoftGSM::onReset() { + gsm_destroy(mGsm); + mGsm = gsm_create(); + int msopt = 1; + gsm_option(mGsm, GSM_OPT_WAV49, &msopt); + mSignalledError = false; +} + + + } // namespace android diff --git a/media/libstagefright/codecs/gsm/dec/SoftGSM.h b/media/libstagefright/codecs/gsm/dec/SoftGSM.h index 8ab6116..0303dea 100644 --- a/media/libstagefright/codecs/gsm/dec/SoftGSM.h +++ b/media/libstagefright/codecs/gsm/dec/SoftGSM.h @@ -43,6 +43,9 @@ protected: virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onReset(); + private: enum { kNumBuffers = 4, diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp index f4cba54..cddd176 100644 --- a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp +++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp @@ -26,6 +26,7 @@ #include "SoftHEVC.h" #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AUtils.h> #include <media/stagefright/MediaDefs.h> #include <OMX_VideoExt.h> @@ -75,8 +76,12 @@ SoftHEVC::SoftHEVC( mNewWidth(mWidth), mNewHeight(mHeight), mChangingResolution(false) { - initPorts(kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, - CODEC_MIME_TYPE); + const size_t kMinCompressionRatio = 4 /* compressionRatio (for Level 4+) */; + const size_t kMaxOutputBufferSize = 2048 * 2048 * 3 / 2; + // INPUT_BUF_SIZE is given by HEVC codec as minimum input size + initPorts( + kNumBuffers, max(kMaxOutputBufferSize / kMinCompressionRatio, (size_t)INPUT_BUF_SIZE), + kNumBuffers, CODEC_MIME_TYPE, kMinCompressionRatio); CHECK_EQ(initDecoder(), (status_t)OK); } @@ -644,7 +649,7 @@ void SoftHEVC::onQueueFilled(OMX_U32 portIndex) { // The decoder should be fixed so that |u4_error_code| instead of |status| returns // IHEVCD_UNSUPPORTED_DIMENSIONS. bool unsupportedDimensions = - ((IHEVCD_UNSUPPORTED_DIMENSIONS == status) + ((IHEVCD_UNSUPPORTED_DIMENSIONS == (IHEVCD_CXA_ERROR_CODES_T)status) || (IHEVCD_UNSUPPORTED_DIMENSIONS == s_dec_op.u4_error_code)); bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF)); diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index e399984..ede645c 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -21,6 +21,7 @@ #include "SoftMPEG4.h" #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AUtils.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/IOMX.h> @@ -70,7 +71,7 @@ SoftMPEG4::SoftMPEG4( mPvTime(0) { initPorts( kNumInputBuffers, - 8192 /* inputBufferSize */, + 352 * 288 * 3 / 2 /* minInputBufferSize */, kNumOutputBuffers, (mMode == MODE_MPEG4) ? MEDIA_MIMETYPE_VIDEO_MPEG4 : MEDIA_MIMETYPE_VIDEO_H263); @@ -353,14 +354,14 @@ void SoftMPEG4::onReset() { } } -void SoftMPEG4::updatePortDefinitions() { - SoftVideoDecoderOMXComponent::updatePortDefinitions(); +void SoftMPEG4::updatePortDefinitions(bool updateCrop, bool updateInputSize) { + SoftVideoDecoderOMXComponent::updatePortDefinitions(updateCrop, updateInputSize); /* We have to align our width and height - this should affect stride! */ OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; - def->nBufferSize = - (((def->format.video.nFrameWidth + 15) & -16) - * ((def->format.video.nFrameHeight + 15) & -16) * 3) / 2; + def->format.video.nStride = align(def->format.video.nStride, 16); + def->format.video.nSliceHeight = align(def->format.video.nSliceHeight, 16); + def->nBufferSize = (def->format.video.nStride * def->format.video.nSliceHeight * 3) / 2; } } // namespace android diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h index 8a06a00..4114e7d 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h @@ -66,7 +66,7 @@ private: status_t initDecoder(); - virtual void updatePortDefinitions(); + virtual void updatePortDefinitions(bool updateCrop = true, bool updateInputSize = false); bool handlePortSettingsChange(); DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4); diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/find_min_max.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/find_min_max.cpp index a357ea6..1ac88a1 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/src/find_min_max.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/src/find_min_max.cpp @@ -138,8 +138,8 @@ void FindMaxMin( /*---------------------------------------------------------------------------- ; Define all local variables ----------------------------------------------------------------------------*/ - register uint i, j; - register int min, max; + uint i, j; + int min, max; /*---------------------------------------------------------------------------- ; Function body here diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp index 844bd14..90d7c6b 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp @@ -247,10 +247,13 @@ OSCL_EXPORT_REF Bool PVInitVideoDecoder(VideoDecControls *decCtrl, uint8 *volbuf video->vol[idx]->useReverseVLC = 0; video->intra_acdcPredDisable = 1; video->vol[idx]->scalability = 0; - video->size = (int32)width * height; - video->displayWidth = video->width = width; - video->displayHeight = video->height = height; + video->displayWidth = width; + video->displayHeight = height; + video->width = (width + 15) & -16; + video->height = (height + 15) & -16; + video->size = (int32)video->width * video->height; + #ifdef PV_ANNEX_IJKT_SUPPORT video->modified_quant = 0; video->advanced_INTRA = 0; @@ -289,8 +292,10 @@ Bool PVAllocVideoData(VideoDecControls *decCtrl, int width, int height, int nLay if (video->shortVideoHeader == PV_TRUE) { - video->displayWidth = video->width = width; - video->displayHeight = video->height = height; + video->displayWidth = width; + video->displayHeight = height; + video->width = (width + 15) & -16; + video->height = (height + 15) & -16; video->nMBPerRow = video->nMBinGOB = video->width / MB_SIZE; diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp index b03ec8c..60c79a6 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp @@ -118,6 +118,10 @@ PV_STATUS DecodeVOLHeader(VideoDecData *video, int layer) { /* support SPL0-3 & SSPL0-2 */ if (tmpvar != 0x01 && tmpvar != 0x02 && tmpvar != 0x03 && tmpvar != 0x08 && + /* While not technically supported, try to decode SPL4&SPL5 files as well. */ + /* We'll fail later if the size is too large. This is to allow playback of */ + /* some <=CIF files generated by other encoders. */ + tmpvar != 0x04 && tmpvar != 0x05 && tmpvar != 0x10 && tmpvar != 0x11 && tmpvar != 0x12 && tmpvar != 0x21 && tmpvar != 0x22 && /* Core Profile Levels */ tmpvar != 0xA1 && tmpvar != 0xA2 && tmpvar != 0xA3 && diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.mk b/media/libstagefright/codecs/m4v_h263/enc/Android.mk index c9006d9..7117692 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/Android.mk +++ b/media/libstagefright/codecs/m4v_h263/enc/Android.mk @@ -25,7 +25,7 @@ LOCAL_MODULE := libstagefright_m4vh263enc LOCAL_CFLAGS := \ -DBX_RC \ - -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF= + -DOSCL_IMPORT_REF= -D"OSCL_UNUSED_ARG(x)=(void)(x)" -DOSCL_EXPORT_REF= LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/src \ @@ -55,7 +55,7 @@ LOCAL_C_INCLUDES := \ LOCAL_CFLAGS := \ -DBX_RC \ - -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF= + -DOSCL_IMPORT_REF= -D"OSCL_UNUSED_ARG(x)=(void)(x)" -DOSCL_EXPORT_REF= LOCAL_STATIC_LIBRARIES := \ diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp index 1d0a2f0..8240f83 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "SoftMPEG4Encoder" #include <utils/Log.h> +#include <utils/misc.h> #include "mp4enc_api.h" #include "OMX_Video.h" @@ -24,6 +25,7 @@ #include <HardwareAPI.h> #include <MetadataBufferType.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AUtils.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> @@ -46,19 +48,30 @@ static void InitOMXParams(T *params) { params->nVersion.s.nStep = 0; } +static const CodecProfileLevel kMPEG4ProfileLevels[] = { + { OMX_VIDEO_MPEG4ProfileCore, OMX_VIDEO_MPEG4Level2 }, +}; + +static const CodecProfileLevel kH263ProfileLevels[] = { + { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level45 }, +}; + SoftMPEG4Encoder::SoftMPEG4Encoder( const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const char *mime, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) - : SoftVideoEncoderOMXComponent(name, callbacks, appData, component), + : SoftVideoEncoderOMXComponent( + name, componentRole, codingType, + profileLevels, numProfileLevels, + 176 /* width */, 144 /* height */, + callbacks, appData, component), mEncodeMode(COMBINE_MODE_WITH_ERR_RES), - mVideoWidth(176), - mVideoHeight(144), - mVideoFrameRate(30), - mVideoBitRate(192000), - mVideoColorFormat(OMX_COLOR_FormatYUV420Planar), - mStoreMetaDataInBuffers(false), mIDRFrameRefreshIntervalInSec(1), mNumInputFrames(-1), mStarted(false), @@ -68,13 +81,15 @@ SoftMPEG4Encoder::SoftMPEG4Encoder( mEncParams(new tagvideoEncOptions), mInputFrameData(NULL) { - if (!strcmp(name, "OMX.google.h263.encoder")) { + if (codingType == OMX_VIDEO_CodingH263) { mEncodeMode = H263_MODE; - } else { - CHECK(!strcmp(name, "OMX.google.mpeg4.encoder")); } - initPorts(); + // 256 * 1024 is a magic number for PV's encoder, not sure why + const size_t kOutputBufferSize = 256 * 1024; + + initPorts(kNumBuffers, kNumBuffers, kOutputBufferSize, mime); + ALOGI("Construct SoftMPEG4Encoder"); } @@ -98,9 +113,9 @@ OMX_ERRORTYPE SoftMPEG4Encoder::initEncParams() { return OMX_ErrorUndefined; } mEncParams->encMode = mEncodeMode; - mEncParams->encWidth[0] = mVideoWidth; - mEncParams->encHeight[0] = mVideoHeight; - mEncParams->encFrameRate[0] = mVideoFrameRate; + mEncParams->encWidth[0] = mWidth; + mEncParams->encHeight[0] = mHeight; + mEncParams->encFrameRate[0] = mFramerate >> 16; // mFramerate is in Q16 format mEncParams->rcType = VBR_1; mEncParams->vbvDelay = 5.0f; @@ -111,27 +126,26 @@ OMX_ERRORTYPE SoftMPEG4Encoder::initEncParams() { mEncParams->rvlcEnable = PV_OFF; mEncParams->numLayers = 1; mEncParams->timeIncRes = 1000; - mEncParams->tickPerSrc = mEncParams->timeIncRes / mVideoFrameRate; + mEncParams->tickPerSrc = ((int64_t)mEncParams->timeIncRes << 16) / mFramerate; - mEncParams->bitRate[0] = mVideoBitRate; + mEncParams->bitRate[0] = mBitrate; mEncParams->iQuant[0] = 15; mEncParams->pQuant[0] = 12; mEncParams->quantType[0] = 0; mEncParams->noFrameSkipped = PV_OFF; - if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar - || mStoreMetaDataInBuffers) { + if (mColorFormat != OMX_COLOR_FormatYUV420Planar || mInputDataIsMeta) { // Color conversion is needed. free(mInputFrameData); mInputFrameData = - (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1); + (uint8_t *) malloc((mWidth * mHeight * 3 ) >> 1); CHECK(mInputFrameData != NULL); } // PV's MPEG4 encoder requires the video dimension of multiple - if (mVideoWidth % 16 != 0 || mVideoHeight % 16 != 0) { + if (mWidth % 16 != 0 || mHeight % 16 != 0) { ALOGE("Video frame size %dx%d must be a multiple of 16", - mVideoWidth, mVideoHeight); + mWidth, mHeight); return OMX_ErrorBadParameter; } @@ -142,7 +156,7 @@ OMX_ERRORTYPE SoftMPEG4Encoder::initEncParams() { mEncParams->intraPeriod = 1; // All I frames } else { mEncParams->intraPeriod = - (mIDRFrameRefreshIntervalInSec * mVideoFrameRate); + (mIDRFrameRefreshIntervalInSec * mFramerate) >> 16; } mEncParams->numIntraMB = 0; @@ -201,81 +215,9 @@ OMX_ERRORTYPE SoftMPEG4Encoder::releaseEncoder() { return OMX_ErrorNone; } -void SoftMPEG4Encoder::initPorts() { - OMX_PARAM_PORTDEFINITIONTYPE def; - InitOMXParams(&def); - - const size_t kInputBufferSize = (mVideoWidth * mVideoHeight * 3) >> 1; - - // 256 * 1024 is a magic number for PV's encoder, not sure why - const size_t kOutputBufferSize = - (kInputBufferSize > 256 * 1024) - ? kInputBufferSize: 256 * 1024; - - def.nPortIndex = 0; - def.eDir = OMX_DirInput; - def.nBufferCountMin = kNumBuffers; - def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = kInputBufferSize; - def.bEnabled = OMX_TRUE; - def.bPopulated = OMX_FALSE; - def.eDomain = OMX_PortDomainVideo; - def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 1; - - def.format.video.cMIMEType = const_cast<char *>("video/raw"); - - def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; - def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; - def.format.video.xFramerate = (mVideoFrameRate << 16); // Q16 format - def.format.video.nBitrate = mVideoBitRate; - def.format.video.nFrameWidth = mVideoWidth; - def.format.video.nFrameHeight = mVideoHeight; - def.format.video.nStride = mVideoWidth; - def.format.video.nSliceHeight = mVideoHeight; - - addPort(def); - - def.nPortIndex = 1; - def.eDir = OMX_DirOutput; - def.nBufferCountMin = kNumBuffers; - def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = kOutputBufferSize; - def.bEnabled = OMX_TRUE; - def.bPopulated = OMX_FALSE; - def.eDomain = OMX_PortDomainVideo; - def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 2; - - def.format.video.cMIMEType = - (mEncodeMode == COMBINE_MODE_WITH_ERR_RES) - ? const_cast<char *>(MEDIA_MIMETYPE_VIDEO_MPEG4) - : const_cast<char *>(MEDIA_MIMETYPE_VIDEO_H263); - - def.format.video.eCompressionFormat = - (mEncodeMode == COMBINE_MODE_WITH_ERR_RES) - ? OMX_VIDEO_CodingMPEG4 - : OMX_VIDEO_CodingH263; - - def.format.video.eColorFormat = OMX_COLOR_FormatUnused; - def.format.video.xFramerate = (0 << 16); // Q16 format - def.format.video.nBitrate = mVideoBitRate; - def.format.video.nFrameWidth = mVideoWidth; - def.format.video.nFrameHeight = mVideoHeight; - def.format.video.nStride = mVideoWidth; - def.format.video.nSliceHeight = mVideoHeight; - - addPort(def); -} - OMX_ERRORTYPE SoftMPEG4Encoder::internalGetParameter( OMX_INDEXTYPE index, OMX_PTR params) { switch (index) { - case OMX_IndexParamVideoErrorCorrection: - { - return OMX_ErrorNotImplemented; - } - case OMX_IndexParamVideoBitrate: { OMX_VIDEO_PARAM_BITRATETYPE *bitRate = @@ -286,41 +228,7 @@ OMX_ERRORTYPE SoftMPEG4Encoder::internalGetParameter( } bitRate->eControlRate = OMX_Video_ControlRateVariable; - bitRate->nTargetBitrate = mVideoBitRate; - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoPortFormat: - { - OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; - - if (formatParams->nPortIndex > 1) { - return OMX_ErrorUndefined; - } - - if (formatParams->nIndex > 2) { - return OMX_ErrorNoMore; - } - - if (formatParams->nPortIndex == 0) { - formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; - if (formatParams->nIndex == 0) { - formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; - } else if (formatParams->nIndex == 1) { - formatParams->eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar; - } else { - formatParams->eColorFormat = OMX_COLOR_FormatAndroidOpaque; - } - } else { - formatParams->eCompressionFormat = - (mEncodeMode == COMBINE_MODE_WITH_ERR_RES) - ? OMX_VIDEO_CodingMPEG4 - : OMX_VIDEO_CodingH263; - - formatParams->eColorFormat = OMX_COLOR_FormatUnused; - } - + bitRate->nTargetBitrate = mBitrate; return OMX_ErrorNone; } @@ -369,32 +277,8 @@ OMX_ERRORTYPE SoftMPEG4Encoder::internalGetParameter( return OMX_ErrorNone; } - case OMX_IndexParamVideoProfileLevelQuerySupported: - { - OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = - (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)params; - - if (profileLevel->nPortIndex != 1) { - return OMX_ErrorUndefined; - } - - if (profileLevel->nProfileIndex > 0) { - return OMX_ErrorNoMore; - } - - if (mEncodeMode == H263_MODE) { - profileLevel->eProfile = OMX_VIDEO_H263ProfileBaseline; - profileLevel->eLevel = OMX_VIDEO_H263Level45; - } else { - profileLevel->eProfile = OMX_VIDEO_MPEG4ProfileCore; - profileLevel->eLevel = OMX_VIDEO_MPEG4Level2; - } - - return OMX_ErrorNone; - } - default: - return SimpleSoftOMXComponent::internalGetParameter(index, params); + return SoftVideoEncoderOMXComponent::internalGetParameter(index, params); } } @@ -403,11 +287,6 @@ OMX_ERRORTYPE SoftMPEG4Encoder::internalSetParameter( int32_t indexFull = index; switch (indexFull) { - case OMX_IndexParamVideoErrorCorrection: - { - return OMX_ErrorNotImplemented; - } - case OMX_IndexParamVideoBitrate: { OMX_VIDEO_PARAM_BITRATETYPE *bitRate = @@ -418,116 +297,7 @@ OMX_ERRORTYPE SoftMPEG4Encoder::internalSetParameter( return OMX_ErrorUndefined; } - mVideoBitRate = bitRate->nTargetBitrate; - return OMX_ErrorNone; - } - - case OMX_IndexParamPortDefinition: - { - OMX_PARAM_PORTDEFINITIONTYPE *def = - (OMX_PARAM_PORTDEFINITIONTYPE *)params; - if (def->nPortIndex > 1) { - return OMX_ErrorUndefined; - } - - if (def->nPortIndex == 0) { - if (def->format.video.eCompressionFormat != OMX_VIDEO_CodingUnused || - (def->format.video.eColorFormat != OMX_COLOR_FormatYUV420Planar && - def->format.video.eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar && - def->format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque)) { - return OMX_ErrorUndefined; - } - } else { - if ((mEncodeMode == COMBINE_MODE_WITH_ERR_RES && - def->format.video.eCompressionFormat != OMX_VIDEO_CodingMPEG4) || - (mEncodeMode == H263_MODE && - def->format.video.eCompressionFormat != OMX_VIDEO_CodingH263) || - (def->format.video.eColorFormat != OMX_COLOR_FormatUnused)) { - return OMX_ErrorUndefined; - } - } - - OMX_ERRORTYPE err = SimpleSoftOMXComponent::internalSetParameter(index, params); - if (OMX_ErrorNone != err) { - return err; - } - - if (def->nPortIndex == 0) { - mVideoWidth = def->format.video.nFrameWidth; - mVideoHeight = def->format.video.nFrameHeight; - mVideoFrameRate = def->format.video.xFramerate >> 16; - mVideoColorFormat = def->format.video.eColorFormat; - - OMX_PARAM_PORTDEFINITIONTYPE *portDef = - &editPortInfo(0)->mDef; - portDef->format.video.nFrameWidth = mVideoWidth; - portDef->format.video.nFrameHeight = mVideoHeight; - portDef->format.video.nStride = portDef->format.video.nFrameWidth; - portDef->format.video.nSliceHeight = portDef->format.video.nFrameHeight; - portDef->format.video.xFramerate = def->format.video.xFramerate; - portDef->format.video.eColorFormat = - (OMX_COLOR_FORMATTYPE) mVideoColorFormat; - portDef->nBufferSize = - (portDef->format.video.nStride * portDef->format.video.nSliceHeight * 3) / 2; - portDef = &editPortInfo(1)->mDef; - portDef->format.video.nFrameWidth = mVideoWidth; - portDef->format.video.nFrameHeight = mVideoHeight; - } else { - mVideoBitRate = def->format.video.nBitrate; - } - - return OMX_ErrorNone; - } - - case OMX_IndexParamStandardComponentRole: - { - const OMX_PARAM_COMPONENTROLETYPE *roleParams = - (const OMX_PARAM_COMPONENTROLETYPE *)params; - - if (strncmp((const char *)roleParams->cRole, - (mEncodeMode == H263_MODE) - ? "video_encoder.h263": "video_encoder.mpeg4", - OMX_MAX_STRINGNAME_SIZE - 1)) { - return OMX_ErrorUndefined; - } - - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoPortFormat: - { - const OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; - - if (formatParams->nPortIndex > 1) { - return OMX_ErrorUndefined; - } - - if (formatParams->nIndex > 2) { - return OMX_ErrorNoMore; - } - - if (formatParams->nPortIndex == 0) { - if (formatParams->eCompressionFormat != OMX_VIDEO_CodingUnused || - ((formatParams->nIndex == 0 && - formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) || - (formatParams->nIndex == 1 && - formatParams->eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar) || - (formatParams->nIndex == 2 && - formatParams->eColorFormat != OMX_COLOR_FormatAndroidOpaque) )) { - return OMX_ErrorUndefined; - } - mVideoColorFormat = formatParams->eColorFormat; - } else { - if ((mEncodeMode == H263_MODE && - formatParams->eCompressionFormat != OMX_VIDEO_CodingH263) || - (mEncodeMode == COMBINE_MODE_WITH_ERR_RES && - formatParams->eCompressionFormat != OMX_VIDEO_CodingMPEG4) || - formatParams->eColorFormat != OMX_COLOR_FormatUnused) { - return OMX_ErrorUndefined; - } - } - + mBitrate = bitRate->nTargetBitrate; return OMX_ErrorNone; } @@ -578,29 +348,8 @@ OMX_ERRORTYPE SoftMPEG4Encoder::internalSetParameter( return OMX_ErrorNone; } - case kStoreMetaDataExtensionIndex: - { - StoreMetaDataInBuffersParams *storeParams = - (StoreMetaDataInBuffersParams*)params; - if (storeParams->nPortIndex != 0) { - ALOGE("%s: StoreMetadataInBuffersParams.nPortIndex not zero!", - __FUNCTION__); - return OMX_ErrorUndefined; - } - - mStoreMetaDataInBuffers = storeParams->bStoreMetaData; - ALOGV("StoreMetaDataInBuffers set to: %s", - mStoreMetaDataInBuffers ? " true" : "false"); - - if (mStoreMetaDataInBuffers) { - mVideoColorFormat = OMX_COLOR_FormatAndroidOpaque; - } - - return OMX_ErrorNone; - } - default: - return SimpleSoftOMXComponent::internalSetParameter(index, params); + return SoftVideoEncoderOMXComponent::internalSetParameter(index, params); } } @@ -663,12 +412,12 @@ void SoftMPEG4Encoder::onQueueFilled(OMX_U32 /* portIndex */) { if (inHeader->nFilledLen > 0) { const uint8_t *inputData = NULL; - if (mStoreMetaDataInBuffers) { + if (mInputDataIsMeta) { inputData = extractGraphicBuffer( - mInputFrameData, (mVideoWidth * mVideoHeight * 3) >> 1, + mInputFrameData, (mWidth * mHeight * 3) >> 1, inHeader->pBuffer + inHeader->nOffset, inHeader->nFilledLen, - mVideoWidth, mVideoHeight); + mWidth, mHeight); if (inputData == NULL) { ALOGE("Unable to extract gralloc buffer in metadata mode"); mSignalledError = true; @@ -677,9 +426,9 @@ void SoftMPEG4Encoder::onQueueFilled(OMX_U32 /* portIndex */) { } } else { inputData = (const uint8_t *)inHeader->pBuffer + inHeader->nOffset; - if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) { + if (mColorFormat != OMX_COLOR_FormatYUV420Planar) { ConvertYUV420SemiPlanarToYUV420Planar( - inputData, mInputFrameData, mVideoWidth, mVideoHeight); + inputData, mInputFrameData, mWidth, mHeight); inputData = mInputFrameData; } } @@ -689,8 +438,8 @@ void SoftMPEG4Encoder::onQueueFilled(OMX_U32 /* portIndex */) { VideoEncFrameIO vin, vout; memset(&vin, 0, sizeof(vin)); memset(&vout, 0, sizeof(vout)); - vin.height = ((mVideoHeight + 15) >> 4) << 4; - vin.pitch = ((mVideoWidth + 15) >> 4) << 4; + vin.height = align(mHeight, 16); + vin.pitch = align(mWidth, 16); vin.timestamp = (inHeader->nTimeStamp + 500) / 1000; // in ms vin.yChan = (uint8_t *)inputData; vin.uChan = vin.yChan + vin.height * vin.pitch; @@ -738,5 +487,19 @@ void SoftMPEG4Encoder::onQueueFilled(OMX_U32 /* portIndex */) { android::SoftOMXComponent *createSoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { - return new android::SoftMPEG4Encoder(name, callbacks, appData, component); + using namespace android; + if (!strcmp(name, "OMX.google.h263.encoder")) { + return new android::SoftMPEG4Encoder( + name, "video_encoder.h263", OMX_VIDEO_CodingH263, MEDIA_MIMETYPE_VIDEO_H263, + kH263ProfileLevels, NELEM(kH263ProfileLevels), + callbacks, appData, component); + } else if (!strcmp(name, "OMX.google.mpeg4.encoder")) { + return new android::SoftMPEG4Encoder( + name, "video_encoder.mpeg4", OMX_VIDEO_CodingMPEG4, MEDIA_MIMETYPE_VIDEO_MPEG4, + kMPEG4ProfileLevels, NELEM(kMPEG4ProfileLevels), + callbacks, appData, component); + } else { + CHECK(!"Unknown component"); + } + return NULL; } diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h index b0605b4..3389c37 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h @@ -25,11 +25,16 @@ namespace android { -struct MediaBuffer; +struct CodecProfileLevel; struct SoftMPEG4Encoder : public SoftVideoEncoderOMXComponent { SoftMPEG4Encoder( const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const char *mime, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component); @@ -58,12 +63,6 @@ private: } InputBufferInfo; MP4EncodingMode mEncodeMode; - int32_t mVideoWidth; - int32_t mVideoHeight; - int32_t mVideoFrameRate; - int32_t mVideoBitRate; - int32_t mVideoColorFormat; - bool mStoreMetaDataInBuffers; int32_t mIDRFrameRefreshIntervalInSec; int64_t mNumInputFrames; @@ -76,7 +75,6 @@ private: uint8_t *mInputFrameData; Vector<InputBufferInfo> mInputBufferInfoVec; - void initPorts(); OMX_ERRORTYPE initEncParams(); OMX_ERRORTYPE initEncoder(); OMX_ERRORTYPE releaseEncoder(); diff --git a/media/libstagefright/codecs/m4v_h263/enc/src/dct.cpp b/media/libstagefright/codecs/m4v_h263/enc/src/dct.cpp index fa4ae23..8d7d9f1 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/src/dct.cpp +++ b/media/libstagefright/codecs/m4v_h263/enc/src/dct.cpp @@ -267,7 +267,7 @@ extern "C" Void Block4x4DCT_AANwSub(Short *out, UChar *cur, UChar *pred, Int width) { Short *dst; - register Int k0, k1, k2, k3, k4, k5, k6, k7; + Int k0, k1, k2, k3, k4, k5, k6, k7; Int round; Int k12 = 0x022A02D4; Int k14 = 0x0188053A; @@ -473,7 +473,7 @@ extern "C" Void Block2x2DCT_AANwSub(Short *out, UChar *cur, UChar *pred, Int width) { Short *dst; - register Int k0, k1, k2, k3, k4, k5, k6, k7; + Int k0, k1, k2, k3, k4, k5, k6, k7; Int round; Int k12 = 0x022A02D4; Int k14 = 0x018803B2; @@ -863,7 +863,7 @@ extern "C" Void Block4x4DCT_AANIntra(Short *out, UChar *cur, UChar *dummy2, Int width) { Short *dst; - register Int k0, k1, k2, k3, k4, k5, k6, k7; + Int k0, k1, k2, k3, k4, k5, k6, k7; Int round; Int k12 = 0x022A02D4; Int k14 = 0x0188053A; @@ -1050,7 +1050,7 @@ extern "C" Void Block2x2DCT_AANIntra(Short *out, UChar *cur, UChar *dummy2, Int width) { Short *dst; - register Int k0, k1, k2, k3, k4, k5, k6, k7; + Int k0, k1, k2, k3, k4, k5, k6, k7; Int round; Int k12 = 0x022A02D4; Int k14 = 0x018803B2; diff --git a/media/libstagefright/codecs/m4v_h263/enc/src/vlc_encode.cpp b/media/libstagefright/codecs/m4v_h263/enc/src/vlc_encode.cpp index 7ea5dc4..2aec815 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/src/vlc_encode.cpp +++ b/media/libstagefright/codecs/m4v_h263/enc/src/vlc_encode.cpp @@ -271,7 +271,7 @@ PutCBPY(Int cbpy, Char intra, BitstreamEncVideo *bitstream) Int ind; Int length; - if ((intra == 0)) + if (intra == 0) cbpy = 15 - cbpy; ind = cbpy; diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk index 8284490..948ae29 100644 --- a/media/libstagefright/codecs/mp3dec/Android.mk +++ b/media/libstagefright/codecs/mp3dec/Android.mk @@ -48,7 +48,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include LOCAL_CFLAGS := \ - -DOSCL_UNUSED_ARG= + -D"OSCL_UNUSED_ARG(x)=(void)(x)" LOCAL_CFLAGS += -Werror diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index 87d6961..6e6a78a 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -38,19 +38,23 @@ SoftVPX::SoftVPX( NULL /* profileLevels */, 0 /* numProfileLevels */, 320 /* width */, 240 /* height */, callbacks, appData, component), mMode(codingType == OMX_VIDEO_CodingVP8 ? MODE_VP8 : MODE_VP9), + mEOSStatus(INPUT_DATA_AVAILABLE), mCtx(NULL), + mFrameParallelMode(false), + mTimeStampIdx(0), mImg(NULL) { - initPorts(kNumBuffers, 768 * 1024 /* inputBufferSize */, - kNumBuffers, - codingType == OMX_VIDEO_CodingVP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9); - + // arbitrary from avc/hevc as vpx does not specify a min compression ratio + const size_t kMinCompressionRatio = mMode == MODE_VP8 ? 2 : 4; + const char *mime = mMode == MODE_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9; + const size_t kMaxOutputBufferSize = 2048 * 2048 * 3 / 2; + initPorts( + kNumBuffers, kMaxOutputBufferSize / kMinCompressionRatio /* inputBufferSize */, + kNumBuffers, mime, kMinCompressionRatio); CHECK_EQ(initDecoder(), (status_t)OK); } SoftVPX::~SoftVPX() { - vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); - delete (vpx_codec_ctx_t *)mCtx; - mCtx = NULL; + destroyDecoder(); } static int GetCPUCoreCount() { @@ -70,12 +74,19 @@ status_t SoftVPX::initDecoder() { mCtx = new vpx_codec_ctx_t; vpx_codec_err_t vpx_err; vpx_codec_dec_cfg_t cfg; + vpx_codec_flags_t flags; memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t)); + memset(&flags, 0, sizeof(vpx_codec_flags_t)); cfg.threads = GetCPUCoreCount(); + + if (mFrameParallelMode) { + flags |= VPX_CODEC_USE_FRAME_THREADING; + } + if ((vpx_err = vpx_codec_dec_init( (vpx_codec_ctx_t *)mCtx, mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo, - &cfg, 0))) { + &cfg, flags))) { ALOGE("on2 decoder failed to initialize. (%d)", vpx_err); return UNKNOWN_ERROR; } @@ -83,86 +94,155 @@ status_t SoftVPX::initDecoder() { return OK; } +status_t SoftVPX::destroyDecoder() { + vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); + delete (vpx_codec_ctx_t *)mCtx; + mCtx = NULL; + return OK; +} + +bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *portWillReset) { + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + BufferInfo *outInfo = NULL; + OMX_BUFFERHEADERTYPE *outHeader = NULL; + vpx_codec_iter_t iter = NULL; + + if (flushDecoder && mFrameParallelMode) { + // Flush decoder by passing NULL data ptr and 0 size. + // Ideally, this should never fail. + if (vpx_codec_decode((vpx_codec_ctx_t *)mCtx, NULL, 0, NULL, 0)) { + ALOGE("Failed to flush on2 decoder."); + return false; + } + } + + if (!display) { + if (!flushDecoder) { + ALOGE("Invalid operation."); + return false; + } + // Drop all the decoded frames in decoder. + while ((mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter))) { + } + return true; + } + + while (!outQueue.empty()) { + if (mImg == NULL) { + mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); + if (mImg == NULL) { + break; + } + } + uint32_t width = mImg->d_w; + uint32_t height = mImg->d_h; + outInfo = *outQueue.begin(); + outHeader = outInfo->mHeader; + CHECK_EQ(mImg->fmt, IMG_FMT_I420); + handlePortSettingsChange(portWillReset, width, height); + if (*portWillReset) { + return true; + } + + outHeader->nOffset = 0; + outHeader->nFilledLen = (width * height * 3) / 2; + outHeader->nFlags = 0; + outHeader->nTimeStamp = *(OMX_TICKS *)mImg->user_priv; + + uint8_t *dst = outHeader->pBuffer; + const uint8_t *srcY = (const uint8_t *)mImg->planes[PLANE_Y]; + const uint8_t *srcU = (const uint8_t *)mImg->planes[PLANE_U]; + const uint8_t *srcV = (const uint8_t *)mImg->planes[PLANE_V]; + size_t srcYStride = mImg->stride[PLANE_Y]; + size_t srcUStride = mImg->stride[PLANE_U]; + size_t srcVStride = mImg->stride[PLANE_V]; + copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride); + + mImg = NULL; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + + if (!eos) { + return true; + } + + if (!outQueue.empty()) { + outInfo = *outQueue.begin(); + outQueue.erase(outQueue.begin()); + outHeader = outInfo->mHeader; + outHeader->nTimeStamp = 0; + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + mEOSStatus = OUTPUT_FRAMES_FLUSHED; + } + return true; +} + void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { - if (mOutputPortSettingsChange != NONE) { + if (mOutputPortSettingsChange != NONE || mEOSStatus == OUTPUT_FRAMES_FLUSHED) { return; } List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); bool EOSseen = false; + vpx_codec_err_t err; + bool portWillReset = false; + + while ((mEOSStatus == INPUT_EOS_SEEN || !inQueue.empty()) + && !outQueue.empty()) { + // Output the pending frames that left from last port reset or decoder flush. + if (mEOSStatus == INPUT_EOS_SEEN || mImg != NULL) { + if (!outputBuffers( + mEOSStatus == INPUT_EOS_SEEN, true /* display */, + mEOSStatus == INPUT_EOS_SEEN, &portWillReset)) { + ALOGE("on2 decoder failed to output frame."); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + if (portWillReset || mEOSStatus == OUTPUT_FRAMES_FLUSHED || + mEOSStatus == INPUT_EOS_SEEN) { + return; + } + } - while (!inQueue.empty() && !outQueue.empty()) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + mTimeStamps[mTimeStampIdx] = inHeader->nTimeStamp; BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mEOSStatus = INPUT_EOS_SEEN; EOSseen = true; - if (inHeader->nFilledLen == 0) { - 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 (mImg == NULL) { - if (vpx_codec_decode( - (vpx_codec_ctx_t *)mCtx, - inHeader->pBuffer + inHeader->nOffset, - inHeader->nFilledLen, - NULL, - 0)) { - ALOGE("on2 decoder failed to decode frame."); - - notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); - return; - } - vpx_codec_iter_t iter = NULL; - mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); + if (inHeader->nFilledLen > 0 && + vpx_codec_decode((vpx_codec_ctx_t *)mCtx, + inHeader->pBuffer + inHeader->nOffset, + inHeader->nFilledLen, + &mTimeStamps[mTimeStampIdx], 0)) { + ALOGE("on2 decoder failed to decode frame."); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; } + mTimeStampIdx = (mTimeStampIdx + 1) % kNumBuffers; - if (mImg != NULL) { - CHECK_EQ(mImg->fmt, IMG_FMT_I420); - - uint32_t width = mImg->d_w; - uint32_t height = mImg->d_h; - bool portWillReset = false; - handlePortSettingsChange(&portWillReset, width, height); - if (portWillReset) { - return; - } - - outHeader->nOffset = 0; - outHeader->nFilledLen = (width * height * 3) / 2; - outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0; - outHeader->nTimeStamp = inHeader->nTimeStamp; - - uint8_t *dst = outHeader->pBuffer; - const uint8_t *srcY = (const uint8_t *)mImg->planes[PLANE_Y]; - const uint8_t *srcU = (const uint8_t *)mImg->planes[PLANE_U]; - const uint8_t *srcV = (const uint8_t *)mImg->planes[PLANE_V]; - size_t srcYStride = mImg->stride[PLANE_Y]; - size_t srcUStride = mImg->stride[PLANE_U]; - size_t srcVStride = mImg->stride[PLANE_V]; - copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride); - - mImg = NULL; - outInfo->mOwnedByUs = false; - outQueue.erase(outQueue.begin()); - outInfo = NULL; - notifyFillBufferDone(outHeader); - outHeader = NULL; + if (!outputBuffers( + EOSseen /* flushDecoder */, true /* display */, EOSseen, &portWillReset)) { + ALOGE("on2 decoder failed to output frame."); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + if (portWillReset) { + return; } inInfo->mOwnedByUs = false; @@ -173,6 +253,30 @@ void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { } } +void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == kInputPortIndex) { + bool portWillReset = false; + if (!outputBuffers( + true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) { + ALOGE("Failed to flush decoder."); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + mEOSStatus = INPUT_DATA_AVAILABLE; + } +} + +void SoftVPX::onReset() { + bool portWillReset = false; + if (!outputBuffers( + true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) { + ALOGW("Failed to flush decoder. Try to hard reset decoder"); + destroyDecoder(); + initDecoder(); + } + mEOSStatus = INPUT_DATA_AVAILABLE; +} + } // namespace android android::SoftOMXComponent *createSoftOMXComponent( diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h index 8f68693..8ccbae2 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.h +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h @@ -38,6 +38,8 @@ protected: virtual ~SoftVPX(); virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onReset(); private: enum { @@ -49,11 +51,21 @@ private: MODE_VP9 } mMode; - void *mCtx; + enum { + INPUT_DATA_AVAILABLE, // VPX component is ready to decode data. + INPUT_EOS_SEEN, // VPX component saw EOS and is flushing On2 decoder. + OUTPUT_FRAMES_FLUSHED // VPX component finished flushing On2 decoder. + } mEOSStatus; + void *mCtx; + bool mFrameParallelMode; // Frame parallel is only supported by VP9 decoder. + OMX_TICKS mTimeStamps[kNumBuffers]; + uint8_t mTimeStampIdx; vpx_image_t *mImg; status_t initDecoder(); + status_t destroyDecoder(); + bool outputBuffers(bool flushDecoder, bool display, bool eos, bool *portWillReset); DISALLOW_EVIL_CONSTRUCTORS(SoftVPX); }; diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk index e265104..253fa04 100644 --- a/media/libstagefright/codecs/on2/enc/Android.mk +++ b/media/libstagefright/codecs/on2/enc/Android.mk @@ -6,7 +6,6 @@ LOCAL_SRC_FILES := \ LOCAL_C_INCLUDES := \ $(TOP)/external/libvpx/libvpx \ - $(TOP)/external/openssl/include \ $(TOP)/external/libvpx/libvpx/vpx_codec \ $(TOP)/external/libvpx/libvpx/vpx_ports \ frameworks/av/media/libstagefright/include \ diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index 0285feb..970acf3 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -19,6 +19,7 @@ #include "SoftVPXEncoder.h" #include <utils/Log.h> +#include <utils/misc.h> #include <media/hardware/HardwareAPI.h> #include <media/hardware/MetadataBufferType.h> @@ -50,23 +51,29 @@ static int GetCPUCoreCount() { return cpuCoreCount; } +static const CodecProfileLevel kProfileLevels[] = { + { OMX_VIDEO_VP8ProfileMain, OMX_VIDEO_VP8Level_Version0 }, + { OMX_VIDEO_VP8ProfileMain, OMX_VIDEO_VP8Level_Version1 }, + { OMX_VIDEO_VP8ProfileMain, OMX_VIDEO_VP8Level_Version2 }, + { OMX_VIDEO_VP8ProfileMain, OMX_VIDEO_VP8Level_Version3 }, +}; + SoftVPXEncoder::SoftVPXEncoder(const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) - : SoftVideoEncoderOMXComponent(name, callbacks, appData, component), + : SoftVideoEncoderOMXComponent( + name, "video_encoder.vp8", OMX_VIDEO_CodingVP8, + kProfileLevels, NELEM(kProfileLevels), + 176 /* width */, 144 /* height */, + callbacks, appData, component), mCodecContext(NULL), mCodecConfiguration(NULL), mCodecInterface(NULL), - mWidth(176), - mHeight(144), - mBitrate(192000), // in bps - mFramerate(30 << 16), // in Q16 format mBitrateUpdated(false), mBitrateControlMode(VPX_VBR), // variable bitrate mDCTPartitions(0), mErrorResilience(OMX_FALSE), - mColorFormat(OMX_COLOR_FormatYUV420Planar), mLevel(OMX_VIDEO_VP8Level_Version0), mKeyFrameInterval(0), mMinQuantizer(0), @@ -77,83 +84,22 @@ SoftVPXEncoder::SoftVPXEncoder(const char *name, mTemporalPatternIdx(0), mLastTimestamp(0x7FFFFFFFFFFFFFFFLL), mConversionBuffer(NULL), - mInputDataIsMeta(false), mKeyFrameRequested(false) { memset(mTemporalLayerBitrateRatio, 0, sizeof(mTemporalLayerBitrateRatio)); mTemporalLayerBitrateRatio[0] = 100; - initPorts(); -} + const size_t kMinOutputBufferSize = 1024 * 1024; // arbitrary -SoftVPXEncoder::~SoftVPXEncoder() { - releaseEncoder(); + initPorts( + kNumBuffers, kNumBuffers, kMinOutputBufferSize, + MEDIA_MIMETYPE_VIDEO_VP8, 2 /* minCompressionRatio */); } -void SoftVPXEncoder::initPorts() { - OMX_PARAM_PORTDEFINITIONTYPE inputPort; - OMX_PARAM_PORTDEFINITIONTYPE outputPort; - - InitOMXParams(&inputPort); - InitOMXParams(&outputPort); - - inputPort.nBufferCountMin = kNumBuffers; - inputPort.nBufferCountActual = inputPort.nBufferCountMin; - inputPort.bEnabled = OMX_TRUE; - inputPort.bPopulated = OMX_FALSE; - inputPort.eDomain = OMX_PortDomainVideo; - inputPort.bBuffersContiguous = OMX_FALSE; - inputPort.format.video.pNativeRender = NULL; - inputPort.format.video.nFrameWidth = mWidth; - inputPort.format.video.nFrameHeight = mHeight; - inputPort.format.video.nStride = inputPort.format.video.nFrameWidth; - inputPort.format.video.nSliceHeight = inputPort.format.video.nFrameHeight; - inputPort.format.video.nBitrate = 0; - // frameRate is in Q16 format. - inputPort.format.video.xFramerate = mFramerate; - inputPort.format.video.bFlagErrorConcealment = OMX_FALSE; - inputPort.nPortIndex = kInputPortIndex; - inputPort.eDir = OMX_DirInput; - inputPort.nBufferAlignment = kInputBufferAlignment; - inputPort.format.video.cMIMEType = - const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW); - inputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; - inputPort.format.video.eColorFormat = mColorFormat; - inputPort.format.video.pNativeWindow = NULL; - inputPort.nBufferSize = - (inputPort.format.video.nStride * - inputPort.format.video.nSliceHeight * 3) / 2; - - addPort(inputPort); - - outputPort.nBufferCountMin = kNumBuffers; - outputPort.nBufferCountActual = outputPort.nBufferCountMin; - outputPort.bEnabled = OMX_TRUE; - outputPort.bPopulated = OMX_FALSE; - outputPort.eDomain = OMX_PortDomainVideo; - outputPort.bBuffersContiguous = OMX_FALSE; - outputPort.format.video.pNativeRender = NULL; - outputPort.format.video.nFrameWidth = mWidth; - outputPort.format.video.nFrameHeight = mHeight; - outputPort.format.video.nStride = outputPort.format.video.nFrameWidth; - outputPort.format.video.nSliceHeight = outputPort.format.video.nFrameHeight; - outputPort.format.video.nBitrate = mBitrate; - outputPort.format.video.xFramerate = 0; - outputPort.format.video.bFlagErrorConcealment = OMX_FALSE; - outputPort.nPortIndex = kOutputPortIndex; - outputPort.eDir = OMX_DirOutput; - outputPort.nBufferAlignment = kOutputBufferAlignment; - outputPort.format.video.cMIMEType = - const_cast<char *>(MEDIA_MIMETYPE_VIDEO_VP8); - outputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingVP8; - outputPort.format.video.eColorFormat = OMX_COLOR_FormatUnused; - outputPort.format.video.pNativeWindow = NULL; - outputPort.nBufferSize = 1024 * 1024; // arbitrary - - addPort(outputPort); +SoftVPXEncoder::~SoftVPXEncoder() { + releaseEncoder(); } - status_t SoftVPXEncoder::initEncoder() { vpx_codec_err_t codec_return; @@ -409,38 +355,6 @@ OMX_ERRORTYPE SoftVPXEncoder::internalGetParameter(OMX_INDEXTYPE index, const int32_t indexFull = index; switch (indexFull) { - case OMX_IndexParamVideoPortFormat: { - OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (OMX_VIDEO_PARAM_PORTFORMATTYPE *)param; - - if (formatParams->nPortIndex == kInputPortIndex) { - if (formatParams->nIndex >= kNumberOfSupportedColorFormats) { - return OMX_ErrorNoMore; - } - - // Color formats, in order of preference - if (formatParams->nIndex == 0) { - formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; - } else if (formatParams->nIndex == 1) { - formatParams->eColorFormat = - OMX_COLOR_FormatYUV420SemiPlanar; - } else { - formatParams->eColorFormat = OMX_COLOR_FormatAndroidOpaque; - } - - formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; - formatParams->xFramerate = mFramerate; - return OMX_ErrorNone; - } else if (formatParams->nPortIndex == kOutputPortIndex) { - formatParams->eCompressionFormat = OMX_VIDEO_CodingVP8; - formatParams->eColorFormat = OMX_COLOR_FormatUnused; - formatParams->xFramerate = 0; - return OMX_ErrorNone; - } else { - return OMX_ErrorBadPortIndex; - } - } - case OMX_IndexParamVideoBitrate: { OMX_VIDEO_PARAM_BITRATETYPE *bitrate = (OMX_VIDEO_PARAM_BITRATETYPE *)param; @@ -495,54 +409,8 @@ OMX_ERRORTYPE SoftVPXEncoder::internalGetParameter(OMX_INDEXTYPE index, return OMX_ErrorNone; } - case OMX_IndexParamVideoProfileLevelQuerySupported: { - OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel = - (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param; - - if (profileAndLevel->nPortIndex != kOutputPortIndex) { - return OMX_ErrorUnsupportedIndex; - } - - switch (profileAndLevel->nProfileIndex) { - case 0: - profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version0; - break; - - case 1: - profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version1; - break; - - case 2: - profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version2; - break; - - case 3: - profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version3; - break; - - default: - return OMX_ErrorNoMore; - } - - profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain; - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoProfileLevelCurrent: { - OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel = - (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param; - - if (profileAndLevel->nPortIndex != kOutputPortIndex) { - return OMX_ErrorUnsupportedIndex; - } - - profileAndLevel->eLevel = mLevel; - profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain; - return OMX_ErrorNone; - } - default: - return SimpleSoftOMXComponent::internalGetParameter(index, param); + return SoftVideoEncoderOMXComponent::internalGetParameter(index, param); } } @@ -553,30 +421,10 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index, const int32_t indexFull = index; switch (indexFull) { - case OMX_IndexParamStandardComponentRole: - return internalSetRoleParams( - (const OMX_PARAM_COMPONENTROLETYPE *)param); - case OMX_IndexParamVideoBitrate: return internalSetBitrateParams( (const OMX_VIDEO_PARAM_BITRATETYPE *)param); - case OMX_IndexParamPortDefinition: - { - OMX_ERRORTYPE err = internalSetPortParams( - (const OMX_PARAM_PORTDEFINITIONTYPE *)param); - - if (err != OMX_ErrorNone) { - return err; - } - - return SimpleSoftOMXComponent::internalSetParameter(index, param); - } - - case OMX_IndexParamVideoPortFormat: - return internalSetFormatParams( - (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)param); - case OMX_IndexParamVideoVp8: return internalSetVp8Params( (const OMX_VIDEO_PARAM_VP8TYPE *)param); @@ -585,27 +433,8 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index, return internalSetAndroidVp8Params( (const OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *)param); - case OMX_IndexParamVideoProfileLevelCurrent: - return internalSetProfileLevel( - (const OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param); - - case kStoreMetaDataExtensionIndex: - { - // storeMetaDataInBuffers - const StoreMetaDataInBuffersParams *storeParam = - (const StoreMetaDataInBuffersParams *)param; - - if (storeParam->nPortIndex != kInputPortIndex) { - return OMX_ErrorBadPortIndex; - } - - mInputDataIsMeta = (storeParam->bStoreMetaData == OMX_TRUE); - - return OMX_ErrorNone; - } - default: - return SimpleSoftOMXComponent::internalSetParameter(index, param); + return SoftVideoEncoderOMXComponent::internalSetParameter(index, param); } } @@ -646,29 +475,6 @@ OMX_ERRORTYPE SoftVPXEncoder::setConfig( } } -OMX_ERRORTYPE SoftVPXEncoder::internalSetProfileLevel( - const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel) { - if (profileAndLevel->nPortIndex != kOutputPortIndex) { - return OMX_ErrorUnsupportedIndex; - } - - if (profileAndLevel->eProfile != OMX_VIDEO_VP8ProfileMain) { - return OMX_ErrorBadParameter; - } - - if (profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version0 || - profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version1 || - profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version2 || - profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version3) { - mLevel = (OMX_VIDEO_VP8LEVELTYPE)profileAndLevel->eLevel; - } else { - return OMX_ErrorBadParameter; - } - - return OMX_ErrorNone; -} - - OMX_ERRORTYPE SoftVPXEncoder::internalSetVp8Params( const OMX_VIDEO_PARAM_VP8TYPE* vp8Params) { if (vp8Params->nPortIndex != kOutputPortIndex) { @@ -743,95 +549,6 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetAndroidVp8Params( return OMX_ErrorNone; } -OMX_ERRORTYPE SoftVPXEncoder::internalSetFormatParams( - const OMX_VIDEO_PARAM_PORTFORMATTYPE* format) { - if (format->nPortIndex == kInputPortIndex) { - if (format->eColorFormat == OMX_COLOR_FormatYUV420Planar || - format->eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || - format->eColorFormat == OMX_COLOR_FormatAndroidOpaque) { - mColorFormat = format->eColorFormat; - - OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef; - def->format.video.eColorFormat = mColorFormat; - - return OMX_ErrorNone; - } else { - ALOGE("Unsupported color format %i", format->eColorFormat); - return OMX_ErrorUnsupportedSetting; - } - } else if (format->nPortIndex == kOutputPortIndex) { - if (format->eCompressionFormat == OMX_VIDEO_CodingVP8) { - return OMX_ErrorNone; - } else { - return OMX_ErrorUnsupportedSetting; - } - } else { - return OMX_ErrorBadPortIndex; - } -} - - -OMX_ERRORTYPE SoftVPXEncoder::internalSetRoleParams( - const OMX_PARAM_COMPONENTROLETYPE* role) { - const char* roleText = (const char*)role->cRole; - const size_t roleTextMaxSize = OMX_MAX_STRINGNAME_SIZE - 1; - - if (strncmp(roleText, "video_encoder.vp8", roleTextMaxSize)) { - ALOGE("Unsupported component role"); - return OMX_ErrorBadParameter; - } - - return OMX_ErrorNone; -} - - -OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams( - const OMX_PARAM_PORTDEFINITIONTYPE* port) { - if (port->nPortIndex == kInputPortIndex) { - mWidth = port->format.video.nFrameWidth; - mHeight = port->format.video.nFrameHeight; - - // xFramerate comes in Q16 format, in frames per second unit - mFramerate = port->format.video.xFramerate; - - if (port->format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar || - port->format.video.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || - port->format.video.eColorFormat == OMX_COLOR_FormatAndroidOpaque) { - mColorFormat = port->format.video.eColorFormat; - } else { - return OMX_ErrorUnsupportedSetting; - } - - OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; - def->format.video.nStride = def->format.video.nFrameWidth; - def->format.video.nSliceHeight = def->format.video.nFrameHeight; - def->format.video.xFramerate = mFramerate; - def->format.video.eColorFormat = mColorFormat; - def->nBufferSize = - (def->format.video.nStride * def->format.video.nSliceHeight * 3) / 2; - def = &editPortInfo(kOutputPortIndex)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; - - 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; - } -} - - OMX_ERRORTYPE SoftVPXEncoder::internalSetBitrateParams( const OMX_VIDEO_PARAM_BITRATETYPE* bitrate) { if (bitrate->nPortIndex != kOutputPortIndex) { @@ -920,7 +637,7 @@ vpx_enc_frame_flags_t SoftVPXEncoder::getEncodeFlags() { return flags; } -void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { +void SoftVPXEncoder::onQueueFilled(OMX_U32 /* portIndex */) { // Initialize encoder if not already if (mCodecContext == NULL) { if (OK != initEncoder()) { diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index f4c1564..cd0a0cf 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -155,18 +155,6 @@ private: // that specifies algorithm interface (e.g. vp8) vpx_codec_iface_t* mCodecInterface; - // Width of the input frames - int32_t mWidth; - - // Height of the input frames - int32_t mHeight; - - // 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; @@ -182,9 +170,6 @@ private: // is enabled in encoder OMX_BOOL mErrorResilience; - // Color format for the input port - OMX_COLOR_FORMATTYPE mColorFormat; - // Encoder profile corresponding to OMX level parameter // // The inconsistency in the naming is caused by @@ -229,14 +214,8 @@ private: // indeed YUV420SemiPlanar. uint8_t* mConversionBuffer; - bool mInputDataIsMeta; - bool mKeyFrameRequested; - // Initializes input and output OMX ports with sensible - // default values. - void initPorts(); - // Initializes vpx encoder with available settings. status_t initEncoder(); @@ -250,23 +229,10 @@ private: // Get current encode flags vpx_enc_frame_flags_t getEncodeFlags(); - // Handles port changes with respect to color formats - OMX_ERRORTYPE internalSetFormatParams( - const OMX_VIDEO_PARAM_PORTFORMATTYPE* format); - - // Verifies the component role tried to be set to this OMX component is - // strictly video_encoder.vp8 - OMX_ERRORTYPE internalSetRoleParams( - const OMX_PARAM_COMPONENTROLETYPE* role); - // Updates bitrate to reflect port settings. OMX_ERRORTYPE internalSetBitrateParams( const OMX_VIDEO_PARAM_BITRATETYPE* bitrate); - // Handles port definition changes. - OMX_ERRORTYPE internalSetPortParams( - const OMX_PARAM_PORTDEFINITIONTYPE* port); - // Handles vp8 specific parameters. OMX_ERRORTYPE internalSetVp8Params( const OMX_VIDEO_PARAM_VP8TYPE* vp8Params); @@ -275,10 +241,6 @@ private: OMX_ERRORTYPE internalSetAndroidVp8Params( const OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE* vp8AndroidParams); - // Updates encoder profile - OMX_ERRORTYPE internalSetProfileLevel( - const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel); - DISALLOW_EVIL_CONSTRUCTORS(SoftVPXEncoder); }; diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp index 168208f..6b8b395 100644 --- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp +++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp @@ -64,9 +64,11 @@ SoftAVC::SoftAVC( mHeadersDecoded(false), mEOSStatus(INPUT_DATA_AVAILABLE), mSignalledError(false) { + const size_t kMinCompressionRatio = 2; + const size_t kMaxOutputBufferSize = 2048 * 2048 * 3 / 2; initPorts( - kNumInputBuffers, 8192 /* inputBufferSize */, - kNumOutputBuffers, MEDIA_MIMETYPE_VIDEO_AVC); + kNumInputBuffers, kMaxOutputBufferSize / kMinCompressionRatio /* minInputBufferSize */, + kNumOutputBuffers, MEDIA_MIMETYPE_VIDEO_AVC, kMinCompressionRatio); CHECK_EQ(initDecoder(), (status_t)OK); } diff --git a/media/libstagefright/codecs/on2/h264dec/inc/H264SwDecApi.h b/media/libstagefright/codecs/on2/h264dec/inc/H264SwDecApi.h index fe112bc..fe112bc 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/inc/H264SwDecApi.h +++ b/media/libstagefright/codecs/on2/h264dec/inc/H264SwDecApi.h diff --git a/media/libstagefright/codecs/on2/h264dec/inc/basetype.h b/media/libstagefright/codecs/on2/h264dec/inc/basetype.h index 63d5653..63d5653 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/inc/basetype.h +++ b/media/libstagefright/codecs/on2/h264dec/inc/basetype.h diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM.h index 2ed86a4..fbb97e2 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_BitDec_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_BitDec_s.h index abb98fc..d5866fa 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_BitDec_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_BitDec_s.h @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armCOMM_BitDec_s.h ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -667,4 +681,4 @@ BitCount SETS "$RBitCount" MEND END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_Bitstream.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_Bitstream.h index 4f9bc3b..576b66d 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_Bitstream.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_Bitstream.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM_Bitstream.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_IDCTTable.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_IDCTTable.h index d5db32f..223684e 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_IDCTTable.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_IDCTTable.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_IDCT_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_IDCT_s.h index 03f7137..6a7d24f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_IDCT_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_IDCT_s.h @@ -1,11 +1,19 @@ ;// -;// This confidential and proprietary software may be used only as -;// authorised by a licensing agreement from ARM Limited -;// (C) COPYRIGHT 2004 ARM Limited -;// ALL RIGHTS RESERVED -;// The entire notice above must be reproduced on all authorised -;// copies and copies may only be made to the extent permitted -;// by a licensing agreement from ARM Limited. +;// Copyright (C) 2004 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// IDCT_s.s ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_MaskTable.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_MaskTable.h index b5da9dc..5246f15 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_MaskTable.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_MaskTable.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM_MaskTable.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_s.h index 2df1fc8..04735a9 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armCOMM_s.h @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armCOMM_s.h ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armOMX.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armOMX.h index f629f72..e7c0c26 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armOMX.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/armOMX.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* * * File Name: armOMX_ReleaseVersion.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/omxtypes_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/omxtypes_s.h index 8d24b65..d41a037 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/omxtypes_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/api/omxtypes_s.h @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxtypes_s.h ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/build_vc.pl b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/build_vc.pl index 1ae7005..5d672b3 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/build_vc.pl +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/build_vc.pl @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ #!/usr/bin/perl # # @@ -6,7 +22,6 @@ # Revision: 9641 # Date: Thursday, February 7, 2008 # -# (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. # # # diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM.c index e572a89..e8dbf41 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_Bitstream.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_Bitstream.c index 9ef9319..99f53ca 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_Bitstream.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_Bitstream.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM_Bitstream.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_IDCTTable.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_IDCTTable.c index 9e4679c..6f0b87f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_IDCTTable.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_IDCTTable.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM_IDCTTable.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_MaskTable.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_MaskTable.c index 3241db2..906a8e5 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_MaskTable.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/src/armCOMM_MaskTable.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/api/armVC.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/api/armVC.h index 7fa7716..6dbe8b6 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/api/armVC.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/api/armVC.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVC.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/api/armVCCOMM_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/api/armVCCOMM_s.h index 7f0a9b8..a9d4644 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/api/armVCCOMM_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/api/armVCCOMM_s.h @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCCOMM_s.h ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -69,4 +83,4 @@ ENDIF ;// ARMACCOMM_S_H - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/comm/src/omxVCCOMM_ExpandFrame_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/comm/src/omxVCCOMM_ExpandFrame_I_s.s index 02b4b08..f5d2271 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/comm/src/omxVCCOMM_ExpandFrame_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/comm/src/omxVCCOMM_ExpandFrame_I_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCCOMM_ExpandFrame_I_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -186,4 +200,4 @@ End ENDIF ;//ARM1136JS - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/api/armVCM4P10_CAVLCTables.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/api/armVCM4P10_CAVLCTables.h index 4340f2a..d43d86b 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/api/armVCM4P10_CAVLCTables.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/api/armVCM4P10_CAVLCTables.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_Average_4x_Align_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_Average_4x_Align_unsafe_s.s index b2cd9d1..198f0ac 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_Average_4x_Align_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_Average_4x_Align_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_Average_4x_Align_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -219,4 +233,4 @@ End3 ENDIF END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_CAVLCTables.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_CAVLCTables.c index 17fe518..3b84c8d 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_CAVLCTables.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_CAVLCTables.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DeblockingChroma_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DeblockingChroma_unsafe_s.s index dcbcd00..51dcb92 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DeblockingChroma_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DeblockingChroma_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_DeblockingChroma_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -17,4 +31,4 @@ - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DeblockingLuma_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DeblockingLuma_unsafe_s.s index 14b37fe..2085233 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DeblockingLuma_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DeblockingLuma_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_DeblockingLuma_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -363,4 +377,4 @@ t11 RN 9 ENDIF - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair_s.s index ac448a0..33638bf 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_DecodeCoeffsToPair_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DequantTables_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DequantTables_s.s index b16f188..afe07b5 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DequantTables_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_DequantTables_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_DequantTables_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -120,4 +134,4 @@ - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_Align_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_Align_unsafe_s.s index 82b9542..ffe123d 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_Align_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_Align_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_Align_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_Copy_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_Copy_unsafe_s.s index bc0b6ec..c9a89fd 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_Copy_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_Copy_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_Copy_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -146,4 +160,4 @@ Copy4x4End ENDIF END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.s index 66cfe5e..98b67eb 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -175,4 +189,4 @@ End2 ENDIF END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.s index 851ff6a..523eace 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.s index 2f48e13..2e7c5c7 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.s index 6690ced..81af75a 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.s index 007cd0d..906cbf3 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -182,4 +196,4 @@ End ENDIF END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_Interpolate_Chroma_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_Interpolate_Chroma_s.s index b1ad17c..35bf67c 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_Interpolate_Chroma_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_Interpolate_Chroma_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_Interpolate_Chroma_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_QuantTables_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_QuantTables_s.s index f962f70..938c719 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_QuantTables_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_QuantTables_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_QuantTables_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// Description: @@ -71,4 +85,4 @@ END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_TransformResidual4x4_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_TransformResidual4x4_s.s index 241d188..e5372e1 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_TransformResidual4x4_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_TransformResidual4x4_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_TransformResidual4x4_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -404,4 +418,4 @@ End ;// Guarding implementation by the processor name - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_UnpackBlock4x4_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_UnpackBlock4x4_s.s index ad16d9c..d02b4f3 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_UnpackBlock4x4_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/armVCM4P10_UnpackBlock4x4_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_UnpackBlock4x4_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -89,4 +103,4 @@ unpackLoop END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c index c2e6b60..34adea8 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c index 6023862..8b47dc2 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c index a19f277..2cd65ca 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c index 99bb4ce..9f9706b 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.s index 2b71486..3187f2b 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.s @@ -1,5 +1,19 @@ ;// -;// (c) Copyright 2007 ARM Limited. All Rights Reserved. +;// Copyright (C) 2007 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// Description: ;// H.264 inverse quantize and transform module diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.s index 6d960f0..d940418 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.s index 00c8354..2dc9369 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.s index 1b84080..e4fbfa4 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -328,4 +342,4 @@ ExitLoopY END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.s index 417ddc2..6adf27b 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -547,4 +561,4 @@ ExitLoopY END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c index de835bd..63d185f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_InterpolateChroma.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_InterpolateLuma_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_InterpolateLuma_s.s index cf611a3..cb3b4e2 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_InterpolateLuma_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_InterpolateLuma_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_InterpolateLuma_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -423,4 +437,4 @@ EndOfInterpolation END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8_s.s index 34fedd8..09b4cf6 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_PredictIntraChroma_8x8_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16_s.s index 1557208..0c0cba7 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_PredictIntra_16x16_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4_s.s index a90f460..112139f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_PredictIntra_4x4_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair_s.s index 53597a8..b83d7f0 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_TransformDequantChromaDCFromPair_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair_s.s index 73caec2..6974cd1 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_TransformDequantLumaDCFromPair_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -466,4 +480,4 @@ QPR5 RN 5 ENDIF ;//ARM1136JS - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h index 22115d3..359e752 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Huff_Tables_VLC.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h index d5f865c..286ba04 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_ZigZag_Tables.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Clip8_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Clip8_s.s index 7801e57..241d441 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Clip8_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Clip8_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ; /** ; * ; * File Name: armVCM4P2_Clip8_s.s @@ -5,7 +20,6 @@ ; * Revision: 9641 ; * Date: Thursday, February 7, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_AC_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_AC_unsafe_s.s index 9e30900..96f5bed 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_AC_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_AC_unsafe_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: armVCM4P2_DecodeVLCZigzag_AC_unsafe_s.s @@ -5,7 +20,6 @@ ; * Revision: 9641 ; * Date: Thursday, February 7, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c index ba4d058..04d86ed 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Huff_Tables_VLC.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Lookup_Tables.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Lookup_Tables.c index 25cf8db..04739a5 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Lookup_Tables.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Lookup_Tables.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Lookup_Tables.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_SetPredDir_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_SetPredDir_s.s index 3f92d85..d0d13d1 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_SetPredDir_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_SetPredDir_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P2_SetPredDir_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c index ed17f9b..b647559 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Zigzag_Tables.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c index b63d295..127772a 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DecodeBlockCoef_Inter.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c index c609a60..f24fc07 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DecodeBlockCoef_Intra.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP_s.s index a1861da..65a01d7 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ; ********** ; * ; * File Name: omxVCM4P2_DecodePadMV_PVOP_s.s @@ -5,7 +20,6 @@ ; * Revision: 9641 ; * Date: Thursday, February 7, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter_s.s index c43b253..5ee33d8 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: omxVCM4P2_DecodeVLCZigzag_Inter_s.s @@ -5,7 +20,6 @@ ; * Revision: 9641 ; * Date: Thursday, February 7, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC_s.s index 166729e..9d5940c 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: omxVCM4P2_DecodeVLCZigzag_IntraACVLC_s.s @@ -5,7 +20,6 @@ ; * Revision: 9641 ; * Date: Thursday, February 7, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC_s.s index d19cb13..266a62b 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: omxVCM4P2_DecodeVLCZigzag_IntraDCVLC_s.s @@ -5,7 +20,6 @@ ; * Revision: 9641 ; * Date: Thursday, February 7, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_FindMVpred_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_FindMVpred_s.s index a4bfa71..92acd51 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_FindMVpred_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_FindMVpred_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P2_FindMVpred_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -191,4 +205,4 @@ BlkEnd M_END ENDIF ;// ARM1136JS :LOR: CortexA8 - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_IDCT8x8blk_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_IDCT8x8blk_s.s index bfeb540..e4f91fb 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_IDCT8x8blk_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_IDCT8x8blk_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P2_IDCT8x8blk_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_MCReconBlock_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_MCReconBlock_s.s index 20965bf..8ac6ff9 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_MCReconBlock_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_MCReconBlock_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P2_MCReconBlock_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra_s.s index 213444a..116c81d 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ; ********** ; * ; * File Name: omxVCM4P2_PredictReconCoefIntra_s.s @@ -5,7 +20,6 @@ ; * Revision: 9641 ; * Date: Thursday, February 7, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_QuantInvInter_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_QuantInvInter_I_s.s index c9591cb..d57160f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_QuantInvInter_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_QuantInvInter_I_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: omxVCM4P2_QuantInvInter_I_s.s @@ -5,7 +20,6 @@ ; * Revision: 9641 ; * Date: Thursday, February 7, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I_s.s index 6328e01..bd82da4 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm11/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: omxVCM4P2_QuantInvIntra_I_s.s @@ -5,7 +20,6 @@ ; * Revision: 9641 ; * Date: Thursday, February 7, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM.h index 64c1958..91e38b8 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM.h @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_BitDec_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_BitDec_s.h index c738f72..56344e3 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_BitDec_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_BitDec_s.h @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armCOMM_BitDec_s.h ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -667,4 +681,4 @@ BitCount SETS "$RBitCount" MEND END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_Bitstream.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_Bitstream.h index b699034..8c0ef37 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_Bitstream.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_Bitstream.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM_Bitstream.h @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_IDCTTable.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_IDCTTable.h index e0cfdaa..d761f61 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_IDCTTable.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_IDCTTable.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * @@ -6,7 +22,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_IDCT_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_IDCT_s.h index 0baa087..9130223 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_IDCT_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_IDCT_s.h @@ -1,11 +1,19 @@ ;// -;// This confidential and proprietary software may be used only as -;// authorised by a licensing agreement from ARM Limited -;// (C) COPYRIGHT 2004 ARM Limited -;// ALL RIGHTS RESERVED -;// The entire notice above must be reproduced on all authorised -;// copies and copies may only be made to the extent permitted -;// by a licensing agreement from ARM Limited. +;// Copyright (C) 2004 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// IDCT_s.s ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_MaskTable.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_MaskTable.h index 51118fd..5ffc835 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_MaskTable.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_MaskTable.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM_MaskTable.h @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_Version.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_Version.h index 41b3e1e..41b3e1e 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_Version.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_Version.h diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_s.h index 0956bd1..321d2d3 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armCOMM_s.h @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armCOMM_s.h ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armOMX.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armOMX.h index 7a68d14..303abd9 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armOMX.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/armOMX.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* * * File Name: armOMX_ReleaseVersion.h @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/omxtypes.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/omxtypes.h index 912cb0d..912cb0d 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/omxtypes.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/omxtypes.h diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/omxtypes_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/omxtypes_s.h index 48703d1..6e742c7 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/omxtypes_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/api/omxtypes_s.h @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxtypes_s.h ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/build_vc.pl b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/build_vc.pl index 649e74c..6a206c0 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/build_vc.pl +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/build_vc.pl @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ #!/usr/bin/perl # # @@ -6,7 +22,6 @@ # Revision: 12290 # Date: Wednesday, April 9, 2008 # -# (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. # # # diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM.c index e572a89..e8dbf41 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_Bitstream.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_Bitstream.c index 9ef9319..99f53ca 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_Bitstream.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_Bitstream.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM_Bitstream.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_IDCTTable.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_IDCTTable.c index 3f5e279..85d4c67 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_IDCTTable.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_IDCTTable.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM_IDCTTable.c @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_MaskTable.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_MaskTable.c index 09f88c3..f169a16 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_MaskTable.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/src/armCOMM_MaskTable.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/armVC.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/armVC.h index 35b510b..1d37a5d 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/armVC.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/armVC.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVC.h @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/armVCCOMM_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/armVCCOMM_s.h index 32a0166..cfc2a3b 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/armVCCOMM_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/armVCCOMM_s.h @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCCOMM_s.h ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -69,4 +83,4 @@ ENDIF ;// ARMACCOMM_S_H - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/omxVC.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/omxVC.h index 7b3cc72..7b3cc72 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/omxVC.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/omxVC.h diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/omxVC_s.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/omxVC_s.h index 89f3040..89f3040 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/omxVC_s.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/api/omxVC_s.h diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/comm/src/omxVCCOMM_ExpandFrame_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/comm/src/omxVCCOMM_ExpandFrame_I_s.s index 5c5b7d8..3d6b669 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/comm/src/omxVCCOMM_ExpandFrame_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/comm/src/omxVCCOMM_ExpandFrame_I_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCCOMM_ExpandFrame_I_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -233,4 +247,4 @@ End - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/api/armVCM4P10_CAVLCTables.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/api/armVCM4P10_CAVLCTables.h index 547a2d9..7dde9a7 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/api/armVCM4P10_CAVLCTables.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/api/armVCM4P10_CAVLCTables.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_Average_4x_Align_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_Average_4x_Align_unsafe_s.s index 4f0892d..5f3eb9b 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_Average_4x_Align_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_Average_4x_Align_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_Average_4x_Align_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -219,4 +233,4 @@ End3 ENDIF END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_CAVLCTables.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_CAVLCTables.c index 137495d..bb4bd9e 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_CAVLCTables.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_CAVLCTables.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DeblockingChroma_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DeblockingChroma_unsafe_s.s index 4c3a77c..e3813d3 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DeblockingChroma_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DeblockingChroma_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_DeblockingChroma_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DeblockingLuma_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DeblockingLuma_unsafe_s.s index 0afe4fd..bcc01dd 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DeblockingLuma_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DeblockingLuma_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_DeblockingLuma_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair_s.s index 10a89e9..6e3a0d5 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_DecodeCoeffsToPair_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DequantTables_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DequantTables_s.s index 2761600..dce8c89 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DequantTables_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_DequantTables_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_DequantTables_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -120,4 +134,4 @@ - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_Align_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_Align_unsafe_s.s index 6e912d7..20b3e22 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_Align_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_Align_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_Align_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_Copy_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_Copy_unsafe_s.s index d275891..1415beb 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_Copy_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_Copy_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_Copy_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -146,4 +160,4 @@ Copy4x4End ENDIF END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.s index 4e5a39d..f5a7326 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -175,4 +189,4 @@ End2 ENDIF END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.s index d1684cb..4d86782 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.s index 7bc091f..3bc9534 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.s index babe8ad..ea1c345 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.s index 89c90aa..5414d47 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_Interpolate_Chroma_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_Interpolate_Chroma_s.s index 0f0ec78..afb9565 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_Interpolate_Chroma_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_Interpolate_Chroma_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_Interpolate_Chroma_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 9641 ;// Date: Thursday, February 7, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_QuantTables_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_QuantTables_s.s index 7e2642b..8cd33a4 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_QuantTables_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_QuantTables_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_QuantTables_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// Description: @@ -71,4 +85,4 @@ END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_TransformResidual4x4_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_TransformResidual4x4_s.s index ee9c339..9e16e49 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_TransformResidual4x4_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_TransformResidual4x4_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_TransformResidual4x4_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -183,4 +197,4 @@ End ENDIF ;//CortexA8 - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_UnpackBlock4x4_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_UnpackBlock4x4_s.s index 4c52e22..a24c7d5 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_UnpackBlock4x4_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/armVCM4P10_UnpackBlock4x4_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P10_UnpackBlock4x4_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -89,4 +103,4 @@ unpackLoop END -
\ No newline at end of file + diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c index 40d4d5e..0a6448d 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c index 619365f..7b89be7 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c index 4e871bf..950f348 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c index b29e576..5e78b4c 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.s index 485a488..4787982 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.s index 4606197..a099dcb 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.s index 18e6c1d..bf2152c 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.s index 0c3f4f2..5678670 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.s index e6fbb34..d2a134e 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c index 3ce41be..c6b3f41 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_InterpolateChroma.c @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_InterpolateLuma_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_InterpolateLuma_s.s index 942ebc6..9f8f69e 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_InterpolateLuma_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_InterpolateLuma_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_InterpolateLuma_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8_s.s index 3a60705..1ff418f 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_PredictIntraChroma_8x8_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16_s.s index e9c0eee..de331f4 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_PredictIntra_16x16_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4_s.s index 39eb8a4..b5780ef 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_PredictIntra_4x4_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair_s.s index e394339..5981795 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_TransformDequantChromaDCFromPair_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair_s.s index 2529959..d8c2431 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P10_TransformDequantLumaDCFromPair_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -261,4 +275,4 @@ QPR5 RN 5 ENDIF ;//ARM1136JS - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_Average_4x_Align_unsafe_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_Average_4x_Align_unsafe_s.S index aca2df4..46e0018 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_Average_4x_Align_unsafe_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_Average_4x_Align_unsafe_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DeblockingChroma_unsafe_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DeblockingChroma_unsafe_s.S index b9ee221..ca64a02 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DeblockingChroma_unsafe_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DeblockingChroma_unsafe_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DeblockingLuma_unsafe_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DeblockingLuma_unsafe_s.S index 47f3d44..193bc5e 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DeblockingLuma_unsafe_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DeblockingLuma_unsafe_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DecodeCoeffsToPair_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DecodeCoeffsToPair_s.S index bcc6b6b..8e0db37 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DecodeCoeffsToPair_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DecodeCoeffsToPair_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DequantTables_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DequantTables_s.S index 5bc7875..6febf2f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DequantTables_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_DequantTables_s.S @@ -1,5 +1,19 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_Align_unsafe_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_Align_unsafe_s.S index 37bc69b..7206d76 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_Align_unsafe_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_Align_unsafe_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_Copy_unsafe_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_Copy_unsafe_s.S index fe92201..e41d662 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_Copy_unsafe_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_Copy_unsafe_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.S index 544abe8..c8f5cda 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_DiagCopy_unsafe_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.S index a330972..f5868c0 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfDiagHorVer4x4_unsafe_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.S index 991c33f..065995d 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfDiagVerHor4x4_unsafe_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.S index 40e141b..1e2d16b 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfHor4x4_unsafe_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.S index 955846f..c7def2a 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_InterpolateLuma_HalfVer4x4_unsafe_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_Interpolate_Chroma_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_Interpolate_Chroma_s.S index 8599cab..2f4293f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_Interpolate_Chroma_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_Interpolate_Chroma_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_QuantTables_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_QuantTables_s.S index f5d6d1f..f4e6010 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_QuantTables_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_QuantTables_s.S @@ -1,5 +1,19 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_TransformResidual4x4_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_TransformResidual4x4_s.S index c24d717..d4cedb5 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_TransformResidual4x4_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_TransformResidual4x4_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_UnpackBlock4x4_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_UnpackBlock4x4_s.S index c552f8d..1652dc6 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_UnpackBlock4x4_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/armVCM4P10_UnpackBlock4x4_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_DeblockLuma_I.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_DeblockLuma_I.S index ba61059..90b0947 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_DeblockLuma_I.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_DeblockLuma_I.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.S index bc0f7fa..4a74594 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_DequantTransformResidualFromPairAndAdd_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.S index 79ba538..f20fb78 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingChroma_HorEdge_I_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.S index dcdddbe..003526e 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingChroma_VerEdge_I_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.S index 9755899..7ddc42e 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingLuma_HorEdge_I_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.S index 66cc32e..f71aceb 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_FilterDeblockingLuma_VerEdge_I_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_InterpolateLuma_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_InterpolateLuma_s.S index 76c3d7d..000fbeb 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_InterpolateLuma_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_InterpolateLuma_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntraChroma_8x8_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntraChroma_8x8_s.S index a896a3a..4e2cff6 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntraChroma_8x8_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntraChroma_8x8_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntra_16x16_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntra_16x16_s.S index 3944f53..c71c93b 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntra_16x16_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntra_16x16_s.S @@ -1,5 +1,19 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntra_4x4_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntra_4x4_s.S index 6646b7f..cd5d356 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntra_4x4_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_PredictIntra_4x4_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_TransformDequantChromaDCFromPair_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_TransformDequantChromaDCFromPair_s.S index 7ba3bd6..5570892 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_TransformDequantChromaDCFromPair_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_TransformDequantChromaDCFromPair_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_TransformDequantLumaDCFromPair_s.S b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_TransformDequantLumaDCFromPair_s.S index 640f096..5b6eee0 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_TransformDequantLumaDCFromPair_s.S +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p10/src_gcc/omxVCM4P10_TransformDequantLumaDCFromPair_s.S @@ -1,5 +1,20 @@ /* - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* * */ diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h index 74b5505..6cbc5ff 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Huff_Tables_VLC.h @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h index e95203a..0d64a68 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_ZigZag_Tables.h @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Clip8_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Clip8_s.s index 95fe6d2..2f830fc 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Clip8_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Clip8_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ; /** ; * ; * File Name: armVCM4P2_Clip8_s.s @@ -5,7 +20,6 @@ ; * Revision: 12290 ; * Date: Wednesday, April 9, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_AC_unsafe_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_AC_unsafe_s.s index e4a7f33..016e65b 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_AC_unsafe_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_AC_unsafe_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: armVCM4P2_DecodeVLCZigzag_AC_unsafe_s.s @@ -5,7 +20,6 @@ ; * Revision: 12290 ; * Date: Wednesday, April 9, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c index 38af975..5a77832 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Huff_Tables_VLC.c @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Lookup_Tables.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Lookup_Tables.c index 6948f80..e915d3c 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Lookup_Tables.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Lookup_Tables.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Lookup_Tables.c @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_SetPredDir_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_SetPredDir_s.s index 44f2460..bf3f363 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_SetPredDir_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_SetPredDir_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: armVCM4P2_SetPredDir_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c index 21fa715..719b434 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Zigzag_Tables.c @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c index 796ad6e..95346ad 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DecodeBlockCoef_Inter.c @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c index b28657c..91ec5d2 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DecodeBlockCoef_Intra.c @@ -5,7 +21,6 @@ * Revision: 12290 * Date: Wednesday, April 9, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP_s.s index cc16f5a..08e9538 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ; ********** ; * ; * File Name: omxVCM4P2_DecodePadMV_PVOP_s.s @@ -5,7 +20,6 @@ ; * Revision: 12290 ; * Date: Wednesday, April 9, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter_s.s index 7208c21..636dfe4 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: omxVCM4P2_DecodeVLCZigzag_Inter_s.s @@ -5,7 +20,6 @@ ; * Revision: 12290 ; * Date: Wednesday, April 9, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC_s.s index 9a37ec9..15cc5b4 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: omxVCM4P2_DecodeVLCZigzag_IntraACVLC_s.s @@ -5,7 +20,6 @@ ; * Revision: 12290 ; * Date: Wednesday, April 9, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC_s.s index 778aaf2..e9fed80 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: omxVCM4P2_DecodeVLCZigzag_IntraDCVLC_s.s @@ -5,7 +20,6 @@ ; * Revision: 12290 ; * Date: Wednesday, April 9, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_FindMVpred_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_FindMVpred_s.s index caf7121..9344120 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_FindMVpred_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_FindMVpred_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P2_FindMVpred_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// @@ -191,4 +205,4 @@ BlkEnd M_END ENDIF ;// ARM1136JS :LOR: CortexA8 - END
\ No newline at end of file + END diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_IDCT8x8blk_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_IDCT8x8blk_s.s index b5e3d0d..01b925e 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_IDCT8x8blk_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_IDCT8x8blk_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P2_IDCT8x8blk_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_MCReconBlock_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_MCReconBlock_s.s index dd00df5..3c1aec3 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_MCReconBlock_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_MCReconBlock_s.s @@ -1,11 +1,25 @@ ;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// +;// ;// ;// File Name: omxVCM4P2_MCReconBlock_s.s ;// OpenMAX DL: v1.0.2 ;// Revision: 12290 ;// Date: Wednesday, April 9, 2008 ;// -;// (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ;// ;// ;// diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra_s.s index a73f64a..6b4eb28 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ; ********** ; * ; * File Name: omxVCM4P2_PredictReconCoefIntra_s.s @@ -5,7 +20,6 @@ ; * Revision: 12290 ; * Date: Wednesday, April 9, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_QuantInvInter_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_QuantInvInter_I_s.s index bd0ad1f..744571f 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_QuantInvInter_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_QuantInvInter_I_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: omxVCM4P2_QuantInvInter_I_s.s @@ -5,7 +20,6 @@ ; * Revision: 12290 ; * Date: Wednesday, April 9, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I_s.s b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I_s.s index e00591f..61a7fd4 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I_s.s +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I_s.s @@ -1,3 +1,18 @@ +;// +;// Copyright (C) 2007-2008 ARM Limited +;// +;// Licensed under the Apache License, Version 2.0 (the "License"); +;// you may not use this file except in compliance with the License. +;// You may obtain a copy of the License at +;// +;// http://www.apache.org/licenses/LICENSE-2.0 +;// +;// Unless required by applicable law or agreed to in writing, software +;// distributed under the License is distributed on an "AS IS" BASIS, +;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;// See the License for the specific language governing permissions and +;// limitations under the License. +;// ;/** ; * ; * File Name: omxVCM4P2_QuantInvIntra_I_s.s @@ -5,7 +20,6 @@ ; * Revision: 12290 ; * Date: Wednesday, April 9, 2008 ; * -; * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. ; * ; * ; * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/src/armVC_Version.c b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/src/armVC_Version.c index 5d93681..5d93681 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/src/armVC_Version.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/arm_neon/vc/src/armVC_Version.c diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armCOMM.h b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armCOMM.h index 2ed86a4..fbb97e2 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armCOMM.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armCOMM.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armCOMM_Bitstream.h b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armCOMM_Bitstream.h index 4f9bc3b..576b66d 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armCOMM_Bitstream.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armCOMM_Bitstream.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM_Bitstream.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armOMX.h b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armOMX.h index f629f72..e7c0c26 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armOMX.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/api/armOMX.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* * * File Name: armOMX_ReleaseVersion.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/build_vc.pl b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/build_vc.pl index f0b43e0..e59cded 100755 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/build_vc.pl +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/build_vc.pl @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ #!/usr/bin/perl # # @@ -6,7 +22,6 @@ # Revision: 9641 # Date: Thursday, February 7, 2008 # -# (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. # # # diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/src/armCOMM.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/src/armCOMM.c index e572a89..e8dbf41 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/src/armCOMM.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/src/armCOMM.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/src/armCOMM_Bitstream.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/src/armCOMM_Bitstream.c index 9ef9319..99f53ca 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/src/armCOMM_Bitstream.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/src/armCOMM_Bitstream.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armCOMM_Bitstream.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/api/armVC.h b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/api/armVC.h index 7fa7716..6dbe8b6 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/api/armVC.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/api/armVC.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVC.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/armVCCOMM_Average.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/armVCCOMM_Average.c index 1e51077..b7b37bf 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/armVCCOMM_Average.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/armVCCOMM_Average.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCCOMM_Average.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/armVCCOMM_SAD.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/armVCCOMM_SAD.c index d41ac9a..05b96dc 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/armVCCOMM_SAD.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/armVCCOMM_SAD.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCCOMM_SAD.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Average_16x.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Average_16x.c index 6d1447e..175bca8 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Average_16x.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Average_16x.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCCOMM_Average_16x.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Average_8x.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Average_8x.c index 17b1326..2c14f43 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Average_8x.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Average_8x.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCCOMM_Average_8x.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ComputeTextureErrorBlock.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ComputeTextureErrorBlock.c index e559adf..a1f5240 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ComputeTextureErrorBlock.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ComputeTextureErrorBlock.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCCOMM_ComputeTextureErrorBlock.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ComputeTextureErrorBlock_SAD.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ComputeTextureErrorBlock_SAD.c index c4731aa..a7f48c9 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ComputeTextureErrorBlock_SAD.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ComputeTextureErrorBlock_SAD.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCCOMM_ComputeTextureErrorBlock_SAD.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Copy16x16.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Copy16x16.c index 4857024..8e467a4 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Copy16x16.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Copy16x16.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCCOMM_Copy16x16.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Copy8x8.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Copy8x8.c index a4f9dde..3f5969b 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Copy8x8.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_Copy8x8.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCCOMM_Copy8x8.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ExpandFrame_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ExpandFrame_I.c index 9536df7..5379fd0 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ExpandFrame_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_ExpandFrame_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCCOMM_ExpandFrame_I.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_LimitMVToRect.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_LimitMVToRect.c index af04582..9ba9093 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_LimitMVToRect.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_LimitMVToRect.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCCOMM_LimitMVToRect.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_SAD_16x.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_SAD_16x.c index 0f0cedb..83dbbd0 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_SAD_16x.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_SAD_16x.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCCOMM_SAD_16x.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_SAD_8x.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_SAD_8x.c index 1421d99..7bfd1ec 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_SAD_8x.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/comm/src/omxVCCOMM_SAD_8x.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCCOMM_SAD_8x.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/api/armVCM4P10_CAVLCTables.h b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/api/armVCM4P10_CAVLCTables.h index 8d18a8f..37241ca 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/api/armVCM4P10_CAVLCTables.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/api/armVCM4P10_CAVLCTables.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_CAVLCTables.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_CAVLCTables.c index f4e36ad..c4a3074 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_CAVLCTables.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_CAVLCTables.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_CompareMotionCostToMV.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_CompareMotionCostToMV.c index e4bedc2..6611a37 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_CompareMotionCostToMV.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_CompareMotionCostToMV.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P10_CompareMotionCostToMV.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DeBlockPixel.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DeBlockPixel.c index f4fb1d9..c6da8ab 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DeBlockPixel.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DeBlockPixel.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair.c index 7616add..831d53b 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DecodeCoeffsToPair.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DequantTables.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DequantTables.c index d9c2541..ad6cef3 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DequantTables.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_DequantTables.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_FwdTransformResidual4x4.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_FwdTransformResidual4x4.c index 93d54c3..17d6c0f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_FwdTransformResidual4x4.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_FwdTransformResidual4x4.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfDiag_Luma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfDiag_Luma.c index 8732f4f..ce9df49 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfDiag_Luma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfDiag_Luma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P10_InterpolateHalfDiag_Luma.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfHor_Luma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfHor_Luma.c index 89c0079..15462b2 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfHor_Luma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfHor_Luma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P10_InterpolateHalfHor_Luma.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfVer_Luma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfVer_Luma.c index f7ecfc5..e8adf45 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfVer_Luma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_InterpolateHalfVer_Luma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P10_InterpolateHalfVer_Luma.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_Interpolate_Chroma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_Interpolate_Chroma.c index 1507d23..26730f8 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_Interpolate_Chroma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_Interpolate_Chroma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P10_Interpolate_Chroma.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_Interpolate_Luma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_Interpolate_Luma.c index 89978dd..538d62e 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_Interpolate_Luma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_Interpolate_Luma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P10_Interpolate_Luma.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_PredictIntraDC4x4.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_PredictIntraDC4x4.c index b713073..a200d55 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_PredictIntraDC4x4.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_PredictIntraDC4x4.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_QuantTables.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_QuantTables.c index f0b5bb0..c01d4f6 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_QuantTables.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_QuantTables.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_SADQuar.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_SADQuar.c index a41e04b..6ef8af5 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_SADQuar.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_SADQuar.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P10_SADQuar.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_TransformResidual4x4.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_TransformResidual4x4.c index f9f756a..6c53731 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_TransformResidual4x4.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_TransformResidual4x4.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_UnpackBlock2x2.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_UnpackBlock2x2.c index dda49f6..fb004e5 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_UnpackBlock2x2.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_UnpackBlock2x2.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_UnpackBlock4x4.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_UnpackBlock4x4.c index 3c0dcbd..b40c933 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_UnpackBlock4x4.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/armVCM4P10_UnpackBlock4x4.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_Average_4x.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_Average_4x.c index ac0d523..638605e 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_Average_4x.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_Average_4x.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_Average_4x.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Half.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Half.c index c490e10..6cfdb64 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Half.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Half.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_BlockMatch_Half.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Integer.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Integer.c index f7764e1..050200f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Integer.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Integer.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_BlockMatch_Integer.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Quarter.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Quarter.c index 513ee25..f450d2c 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Quarter.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_BlockMatch_Quarter.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_BlockMatch_Quarter.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c index a07b1bb..9aecf3f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DeblockChroma_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c index 1f3a646..a159631 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DeblockLuma_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c index 830ddc7..f931eeb 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DecodeChromaDcCoeffsToPairCAVLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c index 7e83d1e..e8ab819 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DecodeCoeffsToPairCAVLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd.c index ed5a158..8a022ba 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_DequantTransformResidualFromPairAndAdd.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I.c index 75edee2..4f34a96 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_HorEdge_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I.c index 10b2592..70b0e87 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingChroma_VerEdge_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I.c index 30a37da..19294f8 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_HorEdge_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I.c index 8733427..53e232a 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_FilterDeblockingLuma_VerEdge_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_GetVLCInfo.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_GetVLCInfo.c index 81c59d6..c80552a 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_GetVLCInfo.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_GetVLCInfo.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_GetVLCInfo.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c index 8824de2..18824d8 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateChroma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_InterpolateChroma.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateHalfHor_Luma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateHalfHor_Luma.c index ef0befa..26c8208 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateHalfHor_Luma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateHalfHor_Luma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_InterpolateHalfHor_Luma.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateHalfVer_Luma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateHalfVer_Luma.c index 3560ff8..96c186b 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateHalfVer_Luma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateHalfVer_Luma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_InterpolateHalfVer_Luma.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateLuma.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateLuma.c index d233735..e2a8163 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateLuma.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InterpolateLuma.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_InterpolateLuma.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformDequant_ChromaDC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformDequant_ChromaDC.c index 92ba031..869e768 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformDequant_ChromaDC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformDequant_ChromaDC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_InvTransformDequant_ChromaDC.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformDequant_LumaDC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformDequant_LumaDC.c index a3b1200..75f15cf 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformDequant_LumaDC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformDequant_LumaDC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_InvTransformDequant_LumaDC.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformResidualAndAdd.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformResidualAndAdd.c index 3303997..e3e4519 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformResidualAndAdd.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_InvTransformResidualAndAdd.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_InvTransformResidualAndAdd.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MEGetBufSize.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MEGetBufSize.c index 8c3a5c3..7a245e1 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MEGetBufSize.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MEGetBufSize.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_MEGetBufSize.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MEInit.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MEInit.c index 58ecc88..e463353 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MEInit.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MEInit.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_MEInit.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MotionEstimationMB.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MotionEstimationMB.c index 33dbf3f..5264394 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MotionEstimationMB.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_MotionEstimationMB.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** x * * File Name: omxVCM4P10_MotionEstimationMB.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8.c index d6ca783..e850771 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntraChroma_8x8.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16.c index c90cb4c..ec44526 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntra_16x16.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4.c index 3fa8212..44c25f6 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_PredictIntra_4x4.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_16x.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_16x.c index c8114ee..140a785 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_16x.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_16x.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_SADQuar_16x.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_4x.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_4x.c index 4b330ba..4b60d34 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_4x.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_4x.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_SADQuar_4x.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_8x.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_8x.c index c9e9c24..6c8cdf3 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_8x.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SADQuar_8x.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_SADQuar_8x.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SAD_4x.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SAD_4x.c index 927c454..e22d8dd 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SAD_4x.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SAD_4x.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_SAD_4x.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SATD_4x4.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SATD_4x4.c index a91ae66..6f74499 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SATD_4x4.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SATD_4x4.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_SATD_4x4.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SubAndTransformQDQResidual.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SubAndTransformQDQResidual.c index 23a5662..f184d7c 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SubAndTransformQDQResidual.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_SubAndTransformQDQResidual.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_SubAndTransformQDQResidual.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair.c index 9ad0e81..dd9f5a7 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformDequantChromaDCFromPair.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair.c index 16c8be1..d333d49 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformDequantLumaDCFromPair.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /* ---------------------------------------------------------------- * * @@ -6,7 +22,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformQuant_ChromaDC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformQuant_ChromaDC.c index b5544dd..1b6a3d0 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformQuant_ChromaDC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformQuant_ChromaDC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_TransformQuant_ChromaDC.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformQuant_LumaDC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformQuant_LumaDC.c index 2ccf7f0..ea99a2d 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformQuant_LumaDC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p10/src/omxVCM4P10_TransformQuant_LumaDC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P10_TransformQuant_LumaDC.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_DCT_Table.h b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_DCT_Table.h index 3255b61..a72da13 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_DCT_Table.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_DCT_Table.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_DCT_Table.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h index 92ecc05..a88bdbc 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_Huff_Tables_VLC.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Huff_Tables_VLC.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h index c75ed89..90c163f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/api/armVCM4P2_ZigZag_Tables.h @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_ZigZag_Tables.h @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_ACDCPredict.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_ACDCPredict.c index b6a396a..c993f73 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_ACDCPredict.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_ACDCPredict.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_ACDCPredict.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_BlockMatch_Half.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_BlockMatch_Half.c index 1b69a33..4ffda10 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_BlockMatch_Half.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_BlockMatch_Half.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_BlockMatch_Half.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_BlockMatch_Integer.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_BlockMatch_Integer.c index 77fe358..2b05660 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_BlockMatch_Integer.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_BlockMatch_Integer.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_BlockMatch_Integer.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_CheckVLCEscapeMode.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_CheckVLCEscapeMode.c index 94e8639..5e510e7 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_CheckVLCEscapeMode.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_CheckVLCEscapeMode.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_CheckVLCEscapeMode.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_CompareMV.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_CompareMV.c index 3b8845e..3b621a3 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_CompareMV.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_CompareMV.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_CompareMV.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_DCT_Table.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_DCT_Table.c index a6f713e..7d055d9 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_DCT_Table.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_DCT_Table.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_DCT_Table.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_intra.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_intra.c index a2572e0..a5aa198 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_intra.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_DecodeVLCZigzag_intra.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_DecodeVLCZigzag_intra.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_EncodeVLCZigzag_intra.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_EncodeVLCZigzag_intra.c index cd6b56d..b61c547 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_EncodeVLCZigzag_intra.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_EncodeVLCZigzag_intra.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_EncodeVLCZigzag_intra.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_FillVLCBuffer.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_FillVLCBuffer.c index 93c9504..aeb7714 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_FillVLCBuffer.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_FillVLCBuffer.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_FillVLCBuffer.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_FillVLDBuffer.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_FillVLDBuffer.c index 1712c3a..f09f5d5 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_FillVLDBuffer.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_FillVLDBuffer.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_FillVLDBuffer.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_GetVLCBits.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_GetVLCBits.c index 953f597..8eb1411 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_GetVLCBits.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_GetVLCBits.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_GetVLCBits.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c index cd7e9e4..b101d48 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_Huff_Tables_VLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Huff_Tables_VLC.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_PutVLCBits.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_PutVLCBits.c index ca9efec..21d5494 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_PutVLCBits.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_PutVLCBits.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_PutVLCBits.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_SetPredDir.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_SetPredDir.c index a9cd008..61d44d4 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_SetPredDir.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_SetPredDir.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_SetPredDir.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c index a247c69..bcfc0ef 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/armVCM4P2_Zigzag_Tables.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: armVCM4P2_Zigzag_Tables.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Half_16x16.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Half_16x16.c index dcd3ce1..f23c533 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Half_16x16.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Half_16x16.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_BlockMatch_Half_16x16.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Half_8x8.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Half_8x8.c index 6996e6d..83da79f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Half_8x8.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Half_8x8.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_BlockMatch_Half_8x8.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Integer_16x16.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Integer_16x16.c index e714ef1..e224016 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Integer_16x16.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Integer_16x16.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_BlockMatch_Integer_16x16.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Integer_8x8.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Integer_8x8.c index 607e64c..73a99bd 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Integer_8x8.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_BlockMatch_Integer_8x8.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_BlockMatch_Integer_8x8.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DCT8x8blk.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DCT8x8blk.c index a077ac8..c73e24a 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DCT8x8blk.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DCT8x8blk.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DCT8x8blk.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c index 51f7bab..9c9a7f6 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Inter.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DecodeBlockCoef_Inter.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c index a0b2376..970da6c 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeBlockCoef_Intra.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DecodeBlockCoef_Intra.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP.c index 7e159b7..ae2c220 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodePadMV_PVOP.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DecodePadMV_PVOP.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter.c index 88a8d04..2d3cf6e 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_Inter.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DecodeVLCZigzag_Inter.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC.c index 96593d1..6dddaf0 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraACVLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DecodeVLCZigzag_IntraACVLC.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC.c index 95e00d7..9c76ed1 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_DecodeVLCZigzag_IntraDCVLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_DecodeVLCZigzag_IntraDCVLC.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeMV.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeMV.c index def2b6d..c04a236 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeMV.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeMV.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_EncodeMV.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_Inter.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_Inter.c index b6c73ea..2158f88 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_Inter.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_Inter.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_EncodeVLCZigzag_Inter.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_IntraACVLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_IntraACVLC.c index d047942..63b6d97 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_IntraACVLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_IntraACVLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_EncodeVLCZigzag_IntraACVLC.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_IntraDCVLC.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_IntraDCVLC.c index c57acd2..7bdda19 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_IntraDCVLC.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_EncodeVLCZigzag_IntraDCVLC.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_EncodeVLCZigzag_IntraDCVLC.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_FindMVpred.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_FindMVpred.c index a0cff48..054b486 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_FindMVpred.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_FindMVpred.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_FindMVpred.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_IDCT8x8blk.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_IDCT8x8blk.c index 1886d92..c512458 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_IDCT8x8blk.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_IDCT8x8blk.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_IDCT8x8blk.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MCReconBlock.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MCReconBlock.c index 7b3faee..33f0cf5 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MCReconBlock.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MCReconBlock.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_MCReconBlock.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * Description: diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MEGetBufSize.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MEGetBufSize.c index a8e51da..dda852e 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MEGetBufSize.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MEGetBufSize.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_MEGetBufSize.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MEInit.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MEInit.c index 419e71a..59c57c2 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MEInit.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MEInit.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_MEInit.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MotionEstimationMB.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MotionEstimationMB.c index 9549050..f9bb297 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MotionEstimationMB.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_MotionEstimationMB.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_MotionEstimationMB.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra.c index 1613f47..e091f31 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_PredictReconCoefIntra.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_PredictReconCoefIntra.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInter_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInter_I.c index 5964f73..9055b66 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInter_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInter_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_QuantInter_I.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantIntra_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantIntra_I.c index a10da68..795b802 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantIntra_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantIntra_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_QuantIntra_I.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInvInter_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInvInter_I.c index 6e0de5c..189e244 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInvInter_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInvInter_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_QuantInvInter_I.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I.c index a946d7b..2f24cc7 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_QuantInvIntra_I.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_QuantInvIntra_I.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_TransRecBlockCoef_inter.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_TransRecBlockCoef_inter.c index 6e0c59b..9615a77 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_TransRecBlockCoef_inter.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_TransRecBlockCoef_inter.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_TransRecBlockCoef_inter.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_TransRecBlockCoef_intra.c b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_TransRecBlockCoef_intra.c index dd444f9..4923e3f 100644 --- a/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_TransRecBlockCoef_intra.c +++ b/media/libstagefright/codecs/on2/h264dec/omxdl/reference/vc/m4p2/src/omxVCM4P2_TransRecBlockCoef_intra.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007-2008 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ /** * * File Name: omxVCM4P2_TransRecBlockCoef_intra.c @@ -5,7 +21,6 @@ * Revision: 9641 * Date: Thursday, February 7, 2008 * - * (c) Copyright 2007-2008 ARM Limited. All Rights Reserved. * * * diff --git a/media/libstagefright/codecs/on2/h264dec/source/DecTestBench.c b/media/libstagefright/codecs/on2/h264dec/source/DecTestBench.c index dcf2ef6..dcf2ef6 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/DecTestBench.c +++ b/media/libstagefright/codecs/on2/h264dec/source/DecTestBench.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/EvaluationTestBench.c b/media/libstagefright/codecs/on2/h264dec/source/EvaluationTestBench.c index aadc75f..aadc75f 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/EvaluationTestBench.c +++ b/media/libstagefright/codecs/on2/h264dec/source/EvaluationTestBench.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/H264SwDecApi.c b/media/libstagefright/codecs/on2/h264dec/source/H264SwDecApi.c index 524a3f0..a073dcb 100644 --- a/media/libstagefright/codecs/on2/h264dec/source/H264SwDecApi.c +++ b/media/libstagefright/codecs/on2/h264dec/source/H264SwDecApi.c @@ -36,6 +36,7 @@ 1. Include headers ------------------------------------------------------------------------------*/ #include <stdlib.h> +#include <string.h> #include "basetype.h" #include "h264bsd_container.h" #include "H264SwDecApi.h" diff --git a/media/libstagefright/codecs/on2/h264dec/source/TestBenchMultipleInstance.c b/media/libstagefright/codecs/on2/h264dec/source/TestBenchMultipleInstance.c index 42170d3..42170d3 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/TestBenchMultipleInstance.c +++ b/media/libstagefright/codecs/on2/h264dec/source/TestBenchMultipleInstance.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_byte_stream.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_byte_stream.c index db77f8c..db77f8c 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_byte_stream.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_byte_stream.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_byte_stream.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_byte_stream.h index 36aec76..36aec76 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_byte_stream.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_byte_stream.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cavlc.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cavlc.c index 91d78bd..91d78bd 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cavlc.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cavlc.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cavlc.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cavlc.h index 80353d3..80353d3 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cavlc.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cavlc.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cfg.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cfg.h index 2baba5a..2baba5a 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cfg.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_cfg.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.c index 7a262ed..7a262ed 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.h index 3134670..3134670 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_conceal.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_container.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_container.h index 99b74a0..99b74a0 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_container.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_container.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_deblocking.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_deblocking.c index f8c1f76..f8c1f76 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_deblocking.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_deblocking.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_deblocking.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_deblocking.h index 2571dda..2571dda 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_deblocking.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_deblocking.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_dpb.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_dpb.c index 9517d0a..9517d0a 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_dpb.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_dpb.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_dpb.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_dpb.h index 0e25084..0e25084 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_dpb.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_dpb.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_image.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_image.c index 7b92870..7b92870 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_image.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_image.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_image.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_image.h index ed7c18c..ed7c18c 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_image.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_image.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_inter_prediction.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_inter_prediction.c index 2a81c4a..2a81c4a 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_inter_prediction.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_inter_prediction.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_inter_prediction.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_inter_prediction.h index 94dee25..94dee25 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_inter_prediction.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_inter_prediction.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c index 52c85e5..52c85e5 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.h index 4652bd5..4652bd5 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_macroblock_layer.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_macroblock_layer.c index 2b3e7f0..2b3e7f0 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_macroblock_layer.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_macroblock_layer.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_macroblock_layer.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_macroblock_layer.h index 32bc340..32bc340 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_macroblock_layer.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_macroblock_layer.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_nal_unit.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_nal_unit.c index e44c43a..e44c43a 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_nal_unit.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_nal_unit.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_nal_unit.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_nal_unit.h index 38957bf..38957bf 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_nal_unit.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_nal_unit.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_neighbour.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_neighbour.c index ce5eeff..ce5eeff 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_neighbour.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_neighbour.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_neighbour.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_neighbour.h index fce0ad1..fce0ad1 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_neighbour.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_neighbour.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_order_cnt.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_order_cnt.c index fb23352..fb23352 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_order_cnt.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_order_cnt.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_order_cnt.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_order_cnt.h index 19741eb..19741eb 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_order_cnt.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_order_cnt.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_param_set.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_param_set.c index e04dea4..e04dea4 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_param_set.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_param_set.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_param_set.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_param_set.h index 6328638..6328638 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_param_set.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_pic_param_set.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.c index b409a06..b409a06 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.h index 5a1a140..5a1a140 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_reconstruct.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_sei.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_sei.c index 0756c47..0756c47 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_sei.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_sei.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_sei.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_sei.h index efe543a..efe543a 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_sei.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_sei.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_seq_param_set.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_seq_param_set.h index e18df94..e18df94 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_seq_param_set.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_seq_param_set.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_data.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_data.c index c288d4b..c288d4b 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_data.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_data.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_data.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_data.h index f23d49e..f23d49e 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_data.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_data.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_group_map.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_group_map.c index 7cbb534..7cbb534 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_group_map.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_group_map.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_group_map.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_group_map.h index 4bcb6f2..4bcb6f2 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_group_map.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_group_map.h 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 23401c6..23401c6 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_header.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_header.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_header.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_header.h index 198898a..198898a 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_header.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_slice_header.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_storage.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_storage.c index 3234754..3234754 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_storage.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_storage.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_storage.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_storage.h index ba3b2da..ba3b2da 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_storage.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_storage.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_stream.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_stream.c index 20d1083..20d1083 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_stream.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_stream.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_stream.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_stream.h index 4404b66..4404b66 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_stream.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_stream.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_transform.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_transform.c index 4eb6dd0..4eb6dd0 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_transform.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_transform.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_transform.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_transform.h index 4f41a23..4f41a23 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_transform.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_transform.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c index fb97a28..fb97a28 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.h index 216ad04..216ad04 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vlc.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vlc.c index 060f35e..060f35e 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vlc.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vlc.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vlc.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vlc.h index 4c16773..4c16773 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vlc.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vlc.h diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vui.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vui.c index 4a9335a..4a9335a 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vui.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vui.c diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vui.h b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vui.h index 05d52a4..05d52a4 100755..100644 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vui.h +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_vui.h diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp index b8084ae..6322dc2 100644 --- a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp +++ b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp @@ -345,9 +345,15 @@ void SoftOpus::onQueueFilled(OMX_U32 portIndex) { } uint8_t channel_mapping[kMaxChannels] = {0}; - memcpy(&channel_mapping, - kDefaultOpusChannelLayout, - kMaxChannelsWithDefaultLayout); + if (mHeader->channels <= kMaxChannelsWithDefaultLayout) { + memcpy(&channel_mapping, + kDefaultOpusChannelLayout, + kMaxChannelsWithDefaultLayout); + } else { + memcpy(&channel_mapping, + mHeader->stream_map, + mHeader->channels); + } int status = OPUS_INVALID_STATE; mDecoder = opus_multistream_decoder_create(kRate, diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp index 6474906..21da707 100644 --- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp +++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp @@ -107,6 +107,7 @@ void SoftwareRenderer::resetFormatIfChanged(const sp<AMessage> &format) { if (!runningInEmulator()) { switch (mColorFormat) { case OMX_COLOR_FormatYUV420Planar: + case OMX_COLOR_FormatYUV420SemiPlanar: case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: { halFormat = HAL_PIXEL_FORMAT_YV12; @@ -255,7 +256,8 @@ void SoftwareRenderer::render( dst_u += dst_c_stride; dst_v += dst_c_stride; } - } else if (mColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar) { + } else if (mColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar + || mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { const uint8_t *src_y = (const uint8_t *)data; const uint8_t *src_uv = (const uint8_t *)data + mWidth * (mHeight - mCropTop / 2); diff --git a/media/libstagefright/data/media_codecs_google_audio.xml b/media/libstagefright/data/media_codecs_google_audio.xml index f599004..a06684b 100644 --- a/media/libstagefright/data/media_codecs_google_audio.xml +++ b/media/libstagefright/data/media_codecs_google_audio.xml @@ -48,7 +48,7 @@ </MediaCodec> <MediaCodec name="OMX.google.vorbis.decoder" type="audio/vorbis"> <Limit name="channel-count" max="8" /> - <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000,96000" /> + <Limit name="sample-rate" ranges="8000-96000" /> <Limit name="bitrate" range="32000-500000" /> </MediaCodec> <MediaCodec name="OMX.google.opus.decoder" type="audio/opus"> @@ -65,7 +65,8 @@ <Encoders> <MediaCodec name="OMX.google.aac.encoder" type="audio/mp4a-latm"> <Limit name="channel-count" max="6" /> - <Limit name="sample-rate" ranges="11025,12000,16000,22050,24000,32000,44100,48000" /> + <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" /> + <!-- also may support 64000, 88200 and 96000 Hz --> <Limit name="bitrate" range="8000-960000" /> </MediaCodec> <MediaCodec name="OMX.google.amrnb.encoder" type="audio/3gpp"> diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml index 1cbef39..7e9fa18 100644 --- a/media/libstagefright/data/media_codecs_google_video.xml +++ b/media/libstagefright/data/media_codecs_google_video.xml @@ -73,7 +73,7 @@ <Encoders> <MediaCodec name="OMX.google.h263.encoder" type="video/3gpp"> <!-- profiles and levels: ProfileBaseline : Level45 --> - <Limit name="size" min="16x16" max="176x144" /> + <Limit name="size" min="176x144" max="176x144" /> <Limit name="alignment" value="16x16" /> <Limit name="bitrate" range="1-128000" /> </MediaCodec> diff --git a/media/libstagefright/data/media_codecs_google_video_le.xml b/media/libstagefright/data/media_codecs_google_video_le.xml new file mode 100644 index 0000000..034a038 --- /dev/null +++ b/media/libstagefright/data/media_codecs_google_video_le.xml @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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"> + <!-- profiles and levels: ProfileSimple : Level3 --> + <Limit name="size" min="2x2" max="352x288" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="blocks-per-second" range="12-11880" /> + <Limit name="bitrate" range="1-384000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + <MediaCodec name="OMX.google.h263.decoder" type="video/3gpp"> + <!-- profiles and levels: ProfileBaseline : Level30, ProfileBaseline : Level45 + ProfileISWV2 : Level30, ProfileISWV2 : Level45 --> + <Limit name="size" min="2x2" max="352x288" /> + <Limit name="alignment" value="2x2" /> + <Limit name="bitrate" range="1-384000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + <MediaCodec name="OMX.google.h264.decoder" type="video/avc"> + <!-- profiles and levels: ProfileBaseline : Level51 --> + <Limit name="size" min="2x2" max="2048x2048" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="block-count" range="1-8160" /> + <Limit name="blocks-per-second" range="1-489600" /> + <Limit name="bitrate" range="1-40000000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + <MediaCodec name="OMX.google.hevc.decoder" type="video/hevc"> + <!-- profiles and levels: ProfileMain : MainTierLevel51 --> + <Limit name="size" min="2x2" max="1280x1280" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="8x8" /> + <Limit name="block-count" range="1-139264" /> + <Limit name="blocks-per-second" range="1-432000" /> + <Limit name="bitrate" range="1-5000000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + <MediaCodec name="OMX.google.vp8.decoder" type="video/x-vnd.on2.vp8"> + <Limit name="size" min="2x2" max="2048x2048" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="block-count" range="1-8160" /> + <Limit name="blocks-per-second" range="1-500000" /> + <Limit name="bitrate" range="1-40000000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + <MediaCodec name="OMX.google.vp9.decoder" type="video/x-vnd.on2.vp9"> + <Limit name="size" min="2x2" max="1280x1280" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="block-count" range="1-3600" /> + <Limit name="blocks-per-second" range="1-108000" /> + <Limit name="bitrate" range="1-5000000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + </Decoders> + + <Encoders> + <MediaCodec name="OMX.google.h263.encoder" type="video/3gpp"> + <!-- profiles and levels: ProfileBaseline : Level45 --> + <Limit name="size" min="176x144" max="176x144" /> + <Limit name="alignment" value="16x16" /> + <Limit name="bitrate" range="1-128000" /> + </MediaCodec> + <MediaCodec name="OMX.google.h264.encoder" type="video/avc"> + <!-- profiles and levels: ProfileBaseline : Level2 --> + <Limit name="size" min="16x16" max="896x896" /> + <Limit name="alignment" value="16x16" /> + <Limit name="block-size" value="16x16" /> + <Limit name="blocks-per-second" range="1-11880" /> + <Limit name="bitrate" range="1-2000000" /> + </MediaCodec> + <MediaCodec name="OMX.google.mpeg4.encoder" type="video/mp4v-es"> + <!-- profiles and levels: ProfileCore : Level2 --> + <Limit name="size" min="16x16" max="176x144" /> + <Limit name="alignment" value="16x16" /> + <Limit name="block-size" value="16x16" /> + <Limit name="blocks-per-second" range="12-1485" /> + <Limit name="bitrate" range="1-64000" /> + </MediaCodec> + <MediaCodec name="OMX.google.vp8.encoder" type="video/x-vnd.on2.vp8"> + <!-- profiles and levels: ProfileMain : Level_Version0-3 --> + <Limit name="size" min="2x2" max="1280x1280" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-count" range="1-3600" /> + <Limit name="bitrate" range="1-20000000" /> + <Feature name="bitrate-modes" value="VBR,CBR" /> + </MediaCodec> + </Encoders> +</Included> diff --git a/media/libstagefright/filters/Android.mk b/media/libstagefright/filters/Android.mk index 5e895e1..36ab444 100644 --- a/media/libstagefright/filters/Android.mk +++ b/media/libstagefright/filters/Android.mk @@ -1,8 +1,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_NDK_STL_VARIANT := stlport_static - LOCAL_SRC_FILES := \ ColorConvert.cpp \ GraphicBufferListener.cpp \ @@ -15,9 +13,6 @@ LOCAL_SRC_FILES := \ ZeroFilter.cpp LOCAL_C_INCLUDES := \ - $(TOP)/bionic \ - $(TOP)/bionic/libstdc++/include \ - $(TOP)/external/stlport/stlport \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/frameworks/rs/cpp \ $(TOP)/frameworks/rs \ diff --git a/media/libstagefright/filters/GraphicBufferListener.cpp b/media/libstagefright/filters/GraphicBufferListener.cpp index e493137..66374ba 100644 --- a/media/libstagefright/filters/GraphicBufferListener.cpp +++ b/media/libstagefright/filters/GraphicBufferListener.cpp @@ -21,6 +21,8 @@ #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MediaErrors.h> +#include <gui/BufferItem.h> + #include "GraphicBufferListener.h" namespace android { @@ -60,7 +62,7 @@ status_t GraphicBufferListener::init( return OK; } -void GraphicBufferListener::onFrameAvailable() { +void GraphicBufferListener::onFrameAvailable(const BufferItem& /* item */) { ALOGV("onFrameAvailable() called"); { @@ -83,8 +85,8 @@ void GraphicBufferListener::onSidebandStreamChanged() { // nothing to do } -BufferQueue::BufferItem GraphicBufferListener::getBufferItem() { - BufferQueue::BufferItem item; +BufferItem GraphicBufferListener::getBufferItem() { + BufferItem item; { Mutex::Autolock autoLock(mMutex); @@ -124,8 +126,7 @@ BufferQueue::BufferItem GraphicBufferListener::getBufferItem() { return item; } -sp<GraphicBuffer> GraphicBufferListener::getBuffer( - BufferQueue::BufferItem item) { +sp<GraphicBuffer> GraphicBufferListener::getBuffer(BufferItem item) { sp<GraphicBuffer> buf; if (item.mBuf < 0 || item.mBuf >= BufferQueue::NUM_BUFFER_SLOTS) { ALOGE("getBuffer() received invalid BufferItem: mBuf==%d", item.mBuf); @@ -138,8 +139,7 @@ sp<GraphicBuffer> GraphicBufferListener::getBuffer( return buf; } -status_t GraphicBufferListener::releaseBuffer( - BufferQueue::BufferItem item) { +status_t GraphicBufferListener::releaseBuffer(BufferItem item) { if (item.mBuf < 0 || item.mBuf >= BufferQueue::NUM_BUFFER_SLOTS) { ALOGE("getBuffer() received invalid BufferItem: mBuf==%d", item.mBuf); return ERROR_OUT_OF_RANGE; diff --git a/media/libstagefright/filters/GraphicBufferListener.h b/media/libstagefright/filters/GraphicBufferListener.h index aefac0d..586bf65 100644 --- a/media/libstagefright/filters/GraphicBufferListener.h +++ b/media/libstagefright/filters/GraphicBufferListener.h @@ -31,7 +31,7 @@ public: const sp<AMessage> ¬ify, size_t bufferWidth, size_t bufferHeight, size_t bufferCount); - virtual void onFrameAvailable(); + virtual void onFrameAvailable(const BufferItem& item); virtual void onBuffersReleased(); virtual void onSidebandStreamChanged(); @@ -41,9 +41,9 @@ public: return mProducer; } - BufferQueue::BufferItem getBufferItem(); - sp<GraphicBuffer> getBuffer(BufferQueue::BufferItem item); - status_t releaseBuffer(BufferQueue::BufferItem item); + BufferItem getBufferItem(); + sp<GraphicBuffer> getBuffer(BufferItem item); + status_t releaseBuffer(BufferItem item); enum { kWhatFrameAvailable = 'frav', diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp index c5289b6..0a09575 100644 --- a/media/libstagefright/filters/MediaFilter.cpp +++ b/media/libstagefright/filters/MediaFilter.cpp @@ -31,6 +31,8 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaFilter.h> +#include <gui/BufferItem.h> + #include "ColorConvert.h" #include "GraphicBufferListener.h" #include "IntrinsicBlurFilter.h" @@ -60,36 +62,36 @@ void MediaFilter::setNotificationMessage(const sp<AMessage> &msg) { void MediaFilter::initiateAllocateComponent(const sp<AMessage> &msg) { msg->setWhat(kWhatAllocateComponent); - msg->setTarget(id()); + msg->setTarget(this); msg->post(); } void MediaFilter::initiateConfigureComponent(const sp<AMessage> &msg) { msg->setWhat(kWhatConfigureComponent); - msg->setTarget(id()); + msg->setTarget(this); msg->post(); } void MediaFilter::initiateCreateInputSurface() { - (new AMessage(kWhatCreateInputSurface, id()))->post(); + (new AMessage(kWhatCreateInputSurface, this))->post(); } void MediaFilter::initiateStart() { - (new AMessage(kWhatStart, id()))->post(); + (new AMessage(kWhatStart, this))->post(); } void MediaFilter::initiateShutdown(bool keepComponentAllocated) { - sp<AMessage> msg = new AMessage(kWhatShutdown, id()); + sp<AMessage> msg = new AMessage(kWhatShutdown, this); msg->setInt32("keepComponentAllocated", keepComponentAllocated); msg->post(); } void MediaFilter::signalFlush() { - (new AMessage(kWhatFlush, id()))->post(); + (new AMessage(kWhatFlush, this))->post(); } void MediaFilter::signalResume() { - (new AMessage(kWhatResume, id()))->post(); + (new AMessage(kWhatResume, this))->post(); } // nothing to do @@ -98,13 +100,13 @@ void MediaFilter::signalRequestIDRFrame() { } void MediaFilter::signalSetParameters(const sp<AMessage> ¶ms) { - sp<AMessage> msg = new AMessage(kWhatSetParameters, id()); + sp<AMessage> msg = new AMessage(kWhatSetParameters, this); msg->setMessage("params", params); msg->post(); } void MediaFilter::signalEndOfInputStream() { - (new AMessage(kWhatSignalEndOfInputStream, id()))->post(); + (new AMessage(kWhatSignalEndOfInputStream, this))->post(); } void MediaFilter::onMessageReceived(const sp<AMessage> &msg) { @@ -208,7 +210,7 @@ sp<ABuffer> MediaFilter::PortDescription::bufferAt(size_t index) const { //////////////////// HELPER FUNCTIONS ////////////////////////////////////////// void MediaFilter::signalProcessBuffers() { - (new AMessage(kWhatProcessBuffers, id()))->post(); + (new AMessage(kWhatProcessBuffers, this))->post(); } void MediaFilter::signalError(status_t error) { @@ -309,7 +311,7 @@ void MediaFilter::postFillThisBuffer(BufferInfo *info) { info->mData->meta()->clear(); notify->setBuffer("buffer", info->mData); - sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id()); + sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, this); reply->setInt32("buffer-id", info->mBufferID); notify->setMessage("reply", reply); @@ -329,7 +331,7 @@ void MediaFilter::postDrainThisBuffer(BufferInfo *info) { notify->setInt32("flags", info->mOutputFlags); notify->setBuffer("buffer", info->mData); - sp<AMessage> reply = new AMessage(kWhatOutputBufferDrained, id()); + sp<AMessage> reply = new AMessage(kWhatOutputBufferDrained, this); reply->setInt32("buffer-id", info->mBufferID); notify->setMessage("reply", reply); @@ -729,7 +731,7 @@ void MediaFilter::onCreateInputSurface() { mGraphicBufferListener = new GraphicBufferListener; sp<AMessage> notify = new AMessage(); - notify->setTarget(id()); + notify->setTarget(this); status_t err = mGraphicBufferListener->init( notify, mStride, mSliceHeight, kBufferCountActual); @@ -749,7 +751,7 @@ void MediaFilter::onCreateInputSurface() { } void MediaFilter::onInputFrameAvailable() { - BufferQueue::BufferItem item = mGraphicBufferListener->getBufferItem(); + BufferItem item = mGraphicBufferListener->getBufferItem(); sp<GraphicBuffer> buf = mGraphicBufferListener->getBuffer(item); // get pointer to graphic buffer diff --git a/media/libstagefright/foundation/ADebug.cpp b/media/libstagefright/foundation/ADebug.cpp new file mode 100644 index 0000000..ec4a960 --- /dev/null +++ b/media/libstagefright/foundation/ADebug.cpp @@ -0,0 +1,117 @@ +/* + * 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. + */ + +#include <errno.h> +#include <stdlib.h> +#include <ctype.h> + +#define LOG_TAG "ADebug" +#include <utils/Log.h> +#include <utils/misc.h> + +#include <cutils/properties.h> + +#include <ADebug.h> +#include <AStringUtils.h> +#include <AUtils.h> + +namespace android { + +//static +ADebug::Level ADebug::GetDebugLevelFromString( + const char *name, const char *value, ADebug::Level def) { + // split on , + const char *next = value, *current; + const unsigned long maxLevel = (unsigned long)kDebugMax; + while (next != NULL) { + current = next; + next = strchr(current, ','); + if (next != NULL) { + ++next; // pass , + } + + while (isspace(*current)) { + ++current; + } + // check for : + char *colon = strchr(current, ':'); + + // get level + char *end; + errno = 0; // strtoul does not clear errno, but it can be set for any return value + unsigned long level = strtoul(current, &end, 10); + while (isspace(*end)) { + ++end; + } + if (errno != 0 || end == current || (end != colon && *end != '\0' && end != next)) { + // invalid level - skip + continue; + } + if (colon != NULL) { + // check if pattern matches + do { // skip colon and spaces + ++colon; + } while (isspace(*colon)); + size_t globLen = (next == NULL ? strlen(colon) : (next - 1 - colon)); + while (globLen > 0 && isspace(colon[globLen - 1])) { + --globLen; // trim glob + } + + if (!AStringUtils::MatchesGlob( + colon, globLen, name, strlen(name), true /* ignoreCase */)) { + continue; + } + } + + // update debug level + def = (Level)min(level, maxLevel); + } + return def; +} + +//static +ADebug::Level ADebug::GetDebugLevelFromProperty( + const char *name, const char *propertyName, ADebug::Level def) { + char value[PROPERTY_VALUE_MAX]; + if (property_get(propertyName, value, NULL)) { + return GetDebugLevelFromString(name, value, def); + } + return def; +} + +//static +char *ADebug::GetDebugName(const char *name) { + char *debugName = strdup(name); + const char *terms[] = { "omx", "video", "audio" }; + for (size_t i = 0; i < NELEM(terms) && debugName != NULL; i++) { + const char *term = terms[i]; + const size_t len = strlen(term); + char *match = strcasestr(debugName, term); + if (match != NULL && (match == debugName || match[-1] == '.' + || match[len] == '.' || match[len] == '\0')) { + char *src = match + len; + if (match == debugName || match[-1] == '.') { + src += (*src == '.'); // remove trailing or double . + } + memmove(match, src, debugName + strlen(debugName) - src + 1); + } + } + + return debugName; +} + +} // namespace android + diff --git a/media/libstagefright/foundation/AHandler.cpp b/media/libstagefright/foundation/AHandler.cpp index bd5f7e9..7dbbe54 100644 --- a/media/libstagefright/foundation/AHandler.cpp +++ b/media/libstagefright/foundation/AHandler.cpp @@ -19,15 +19,23 @@ #include <utils/Log.h> #include <media/stagefright/foundation/AHandler.h> - -#include <media/stagefright/foundation/ALooperRoster.h> +#include <media/stagefright/foundation/AMessage.h> namespace android { -sp<ALooper> AHandler::looper() { - extern ALooperRoster gLooperRoster; +void AHandler::deliverMessage(const sp<AMessage> &msg) { + onMessageReceived(msg); + mMessageCounter++; - return gLooperRoster.findLooper(id()); + if (mVerboseStats) { + uint32_t what = msg->what(); + ssize_t idx = mMessages.indexOfKey(what); + if (idx < 0) { + mMessages.add(what, 1); + } else { + mMessages.editValueAt(idx)++; + } + } } } // namespace android diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp index 88b1c92..90b5f68 100644 --- a/media/libstagefright/foundation/ALooper.cpp +++ b/media/libstagefright/foundation/ALooper.cpp @@ -16,6 +16,9 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "ALooper" + +#include <media/stagefright/foundation/ADebug.h> + #include <utils/Log.h> #include <sys/time.h> @@ -210,7 +213,7 @@ bool ALooper::loop() { mEventQueue.erase(mEventQueue.begin()); } - gLooperRoster.deliverMessage(event.mMessage); + event.mMessage->deliver(); // NOTE: It's important to note that at this point our "ALooper" object // may no longer exist (its final reference may have gone away while @@ -220,4 +223,29 @@ bool ALooper::loop() { return true; } +// to be called by AMessage::postAndAwaitResponse only +sp<AReplyToken> ALooper::createReplyToken() { + return new AReplyToken(this); +} + +// to be called by AMessage::postAndAwaitResponse only +status_t ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response) { + // return status in case we want to handle an interrupted wait + Mutex::Autolock autoLock(mRepliesLock); + CHECK(replyToken != NULL); + while (!replyToken->retrieveReply(response)) { + mRepliesCondition.wait(mRepliesLock); + } + return OK; +} + +status_t ALooper::postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &reply) { + Mutex::Autolock autoLock(mRepliesLock); + status_t err = replyToken->setReply(reply); + if (err == OK) { + mRepliesCondition.broadcast(); + } + return err; +} + } // namespace android diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp index e0dc768..473ce1b 100644 --- a/media/libstagefright/foundation/ALooperRoster.cpp +++ b/media/libstagefright/foundation/ALooperRoster.cpp @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "ALooperRoster" #include <utils/Log.h> +#include <utils/String8.h> #include "ALooperRoster.h" @@ -26,9 +27,10 @@ namespace android { +static bool verboseStats = false; + ALooperRoster::ALooperRoster() - : mNextHandlerID(1), - mNextReplyID(1) { + : mNextHandlerID(1) { } ALooper::handler_id ALooperRoster::registerHandler( @@ -46,7 +48,7 @@ ALooper::handler_id ALooperRoster::registerHandler( ALooper::handler_id handlerID = mNextHandlerID++; mHandlers.add(handlerID, info); - handler->setID(handlerID); + handler->setID(handlerID, looper); return handlerID; } @@ -65,7 +67,7 @@ void ALooperRoster::unregisterHandler(ALooper::handler_id handlerID) { sp<AHandler> handler = info.mHandler.promote(); if (handler != NULL) { - handler->setID(0); + handler->setID(0, NULL); } mHandlers.removeItemsAt(index); @@ -97,103 +99,73 @@ void ALooperRoster::unregisterStaleHandlers() { } } -status_t ALooperRoster::postMessage( - const sp<AMessage> &msg, int64_t delayUs) { - - sp<ALooper> looper = findLooper(msg->target()); - - if (looper == NULL) { - return -ENOENT; +static void makeFourCC(uint32_t fourcc, char *s) { + s[0] = (fourcc >> 24) & 0xff; + if (s[0]) { + s[1] = (fourcc >> 16) & 0xff; + s[2] = (fourcc >> 8) & 0xff; + s[3] = fourcc & 0xff; + s[4] = 0; + } else { + sprintf(s, "%u", fourcc); } - looper->post(msg, delayUs); - return OK; } -void ALooperRoster::deliverMessage(const sp<AMessage> &msg) { - sp<AHandler> handler; - - { - Mutex::Autolock autoLock(mLock); - - ssize_t index = mHandlers.indexOfKey(msg->target()); - - if (index < 0) { - ALOGW("failed to deliver message. Target handler not registered."); - return; - } - - const HandlerInfo &info = mHandlers.valueAt(index); - handler = info.mHandler.promote(); - - if (handler == NULL) { - ALOGW("failed to deliver message. " - "Target handler %d registered, but object gone.", - msg->target()); - - mHandlers.removeItemsAt(index); - return; +void ALooperRoster::dump(int fd, const Vector<String16>& args) { + bool clear = false; + bool oldVerbose = verboseStats; + for (size_t i = 0; i < args.size(); i++) { + if (args[i] == String16("-c")) { + clear = true; + } else if (args[i] == String16("-von")) { + verboseStats = true; + } else if (args[i] == String16("-voff")) { + verboseStats = false; } } - - handler->onMessageReceived(msg); -} - -sp<ALooper> ALooperRoster::findLooper(ALooper::handler_id handlerID) { - Mutex::Autolock autoLock(mLock); - - ssize_t index = mHandlers.indexOfKey(handlerID); - - if (index < 0) { - return NULL; - } - - sp<ALooper> looper = mHandlers.valueAt(index).mLooper.promote(); - - if (looper == NULL) { - mHandlers.removeItemsAt(index); - return NULL; - } - - return looper; -} - -status_t ALooperRoster::postAndAwaitResponse( - const sp<AMessage> &msg, sp<AMessage> *response) { - sp<ALooper> looper = findLooper(msg->target()); - - if (looper == NULL) { - ALOGW("failed to post message. " - "Target handler %d still registered, but object gone.", - msg->target()); - response->clear(); - return -ENOENT; + String8 s; + if (verboseStats && !oldVerbose) { + s.append("(verbose stats collection enabled, stats will be cleared)\n"); } Mutex::Autolock autoLock(mLock); - - uint32_t replyID = mNextReplyID++; - - msg->setInt32("replyID", replyID); - - looper->post(msg, 0 /* delayUs */); - - ssize_t index; - while ((index = mReplies.indexOfKey(replyID)) < 0) { - mRepliesCondition.wait(mLock); + size_t n = mHandlers.size(); + s.appendFormat(" %zu registered handlers:\n", n); + + for (size_t i = 0; i < n; i++) { + s.appendFormat(" %d: ", mHandlers.keyAt(i)); + HandlerInfo &info = mHandlers.editValueAt(i); + sp<ALooper> looper = info.mLooper.promote(); + if (looper != NULL) { + s.append(looper->getName()); + sp<AHandler> handler = info.mHandler.promote(); + if (handler != NULL) { + handler->mVerboseStats = verboseStats; + s.appendFormat(": %u messages processed", handler->mMessageCounter); + if (verboseStats) { + for (size_t j = 0; j < handler->mMessages.size(); j++) { + char fourcc[15]; + makeFourCC(handler->mMessages.keyAt(j), fourcc); + s.appendFormat("\n %s: %u", + fourcc, + handler->mMessages.valueAt(j)); + } + } else { + handler->mMessages.clear(); + } + if (clear || (verboseStats && !oldVerbose)) { + handler->mMessageCounter = 0; + handler->mMessages.clear(); + } + } else { + s.append(": <stale handler>"); + } + } else { + s.append("<stale>"); + } + s.append("\n"); } - - *response = mReplies.valueAt(index); - mReplies.removeItemsAt(index); - - return OK; -} - -void ALooperRoster::postReply(uint32_t replyID, const sp<AMessage> &reply) { - Mutex::Autolock autoLock(mLock); - - CHECK(mReplies.indexOfKey(replyID) < 0); - mReplies.add(replyID, reply); - mRepliesCondition.broadcast(); + write(fd, s.string(), s.size()); } } // namespace android diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp index 795e8a6..e549ff6 100644 --- a/media/libstagefright/foundation/AMessage.cpp +++ b/media/libstagefright/foundation/AMessage.cpp @@ -27,6 +27,7 @@ #include "ABuffer.h" #include "ADebug.h" #include "ALooperRoster.h" +#include "AHandler.h" #include "AString.h" #include <binder/Parcel.h> @@ -36,10 +37,27 @@ namespace android { extern ALooperRoster gLooperRoster; -AMessage::AMessage(uint32_t what, ALooper::handler_id target) +status_t AReplyToken::setReply(const sp<AMessage> &reply) { + if (mReplied) { + ALOGE("trying to post a duplicate reply"); + return -EBUSY; + } + CHECK(mReply == NULL); + mReply = reply; + mReplied = true; + return OK; +} + +AMessage::AMessage(void) + : mWhat(0), + mTarget(0), + mNumItems(0) { +} + +AMessage::AMessage(uint32_t what, const sp<const AHandler> &handler) : mWhat(what), - mTarget(target), mNumItems(0) { + setTarget(handler); } AMessage::~AMessage() { @@ -54,12 +72,16 @@ uint32_t AMessage::what() const { return mWhat; } -void AMessage::setTarget(ALooper::handler_id handlerID) { - mTarget = handlerID; -} - -ALooper::handler_id AMessage::target() const { - return mTarget; +void AMessage::setTarget(const sp<const AHandler> &handler) { + if (handler == NULL) { + mTarget = 0; + mHandler.clear(); + mLooper.clear(); + } else { + mTarget = handler->id(); + mHandler = handler->getHandler(); + mLooper = handler->getLooper(); + } } void AMessage::clear() { @@ -322,33 +344,76 @@ bool AMessage::findRect( return true; } -void AMessage::post(int64_t delayUs) { - gLooperRoster.postMessage(this, delayUs); +void AMessage::deliver() { + sp<AHandler> handler = mHandler.promote(); + if (handler == NULL) { + ALOGW("failed to deliver message as target handler %d is gone.", mTarget); + return; + } + + handler->deliverMessage(this); +} + +status_t AMessage::post(int64_t delayUs) { + sp<ALooper> looper = mLooper.promote(); + if (looper == NULL) { + ALOGW("failed to post message as target looper for handler %d is gone.", mTarget); + return -ENOENT; + } + + looper->post(this, delayUs); + return OK; } status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) { - return gLooperRoster.postAndAwaitResponse(this, response); + sp<ALooper> looper = mLooper.promote(); + if (looper == NULL) { + ALOGW("failed to post message as target looper for handler %d is gone.", mTarget); + return -ENOENT; + } + + sp<AReplyToken> token = looper->createReplyToken(); + if (token == NULL) { + ALOGE("failed to create reply token"); + return -ENOMEM; + } + setObject("replyID", token); + + looper->post(this, 0 /* delayUs */); + return looper->awaitResponse(token, response); } -void AMessage::postReply(uint32_t replyID) { - gLooperRoster.postReply(replyID, this); +status_t AMessage::postReply(const sp<AReplyToken> &replyToken) { + if (replyToken == NULL) { + ALOGW("failed to post reply to a NULL token"); + return -ENOENT; + } + sp<ALooper> looper = replyToken->getLooper(); + if (looper == NULL) { + ALOGW("failed to post reply as target looper is gone."); + return -ENOENT; + } + return looper->postReply(replyToken, this); } -bool AMessage::senderAwaitsResponse(uint32_t *replyID) const { - int32_t tmp; - bool found = findInt32("replyID", &tmp); +bool AMessage::senderAwaitsResponse(sp<AReplyToken> *replyToken) { + sp<RefBase> tmp; + bool found = findObject("replyID", &tmp); if (!found) { return false; } - *replyID = static_cast<uint32_t>(tmp); + *replyToken = static_cast<AReplyToken *>(tmp.get()); + tmp.clear(); + setObject("replyID", tmp); + // TODO: delete Object instead of setting it to NULL - return true; + return *replyToken != NULL; } sp<AMessage> AMessage::dup() const { - sp<AMessage> msg = new AMessage(mWhat, mTarget); + sp<AMessage> msg = new AMessage(mWhat, mHandler.promote()); msg->mNumItems = mNumItems; #ifdef DUMP_STATS @@ -426,19 +491,19 @@ AString AMessage::debugString(int32_t indent) const { AString tmp; if (isFourcc(mWhat)) { - tmp = StringPrintf( + tmp = AStringPrintf( "'%c%c%c%c'", (char)(mWhat >> 24), (char)((mWhat >> 16) & 0xff), (char)((mWhat >> 8) & 0xff), (char)(mWhat & 0xff)); } else { - tmp = StringPrintf("0x%08x", mWhat); + tmp = AStringPrintf("0x%08x", mWhat); } s.append(tmp); if (mTarget != 0) { - tmp = StringPrintf(", target = %d", mTarget); + tmp = AStringPrintf(", target = %d", mTarget); s.append(tmp); } s.append(") = {\n"); @@ -448,37 +513,37 @@ AString AMessage::debugString(int32_t indent) const { switch (item.mType) { case kTypeInt32: - tmp = StringPrintf( + tmp = AStringPrintf( "int32_t %s = %d", item.mName, item.u.int32Value); break; case kTypeInt64: - tmp = StringPrintf( + tmp = AStringPrintf( "int64_t %s = %lld", item.mName, item.u.int64Value); break; case kTypeSize: - tmp = StringPrintf( + tmp = AStringPrintf( "size_t %s = %d", item.mName, item.u.sizeValue); break; case kTypeFloat: - tmp = StringPrintf( + tmp = AStringPrintf( "float %s = %f", item.mName, item.u.floatValue); break; case kTypeDouble: - tmp = StringPrintf( + tmp = AStringPrintf( "double %s = %f", item.mName, item.u.doubleValue); break; case kTypePointer: - tmp = StringPrintf( + tmp = AStringPrintf( "void *%s = %p", item.mName, item.u.ptrValue); break; case kTypeString: - tmp = StringPrintf( + tmp = AStringPrintf( "string %s = \"%s\"", item.mName, item.u.stringValue->c_str()); break; case kTypeObject: - tmp = StringPrintf( + tmp = AStringPrintf( "RefBase *%s = %p", item.mName, item.u.refValue); break; case kTypeBuffer: @@ -486,18 +551,18 @@ AString AMessage::debugString(int32_t indent) const { sp<ABuffer> buffer = static_cast<ABuffer *>(item.u.refValue); if (buffer != NULL && buffer->data() != NULL && buffer->size() <= 64) { - tmp = StringPrintf("Buffer %s = {\n", item.mName); + tmp = AStringPrintf("Buffer %s = {\n", item.mName); hexdump(buffer->data(), buffer->size(), indent + 4, &tmp); appendIndent(&tmp, indent + 2); tmp.append("}"); } else { - tmp = StringPrintf( + tmp = AStringPrintf( "Buffer *%s = %p", item.mName, buffer.get()); } break; } case kTypeMessage: - tmp = StringPrintf( + tmp = AStringPrintf( "AMessage %s = %s", item.mName, static_cast<AMessage *>( @@ -505,7 +570,7 @@ AString AMessage::debugString(int32_t indent) const { indent + strlen(item.mName) + 14).c_str()); break; case kTypeRect: - tmp = StringPrintf( + tmp = AStringPrintf( "Rect %s(%d, %d, %d, %d)", item.mName, item.u.rectValue.mLeft, @@ -532,7 +597,8 @@ AString AMessage::debugString(int32_t indent) const { // static sp<AMessage> AMessage::FromParcel(const Parcel &parcel) { int32_t what = parcel.readInt32(); - sp<AMessage> msg = new AMessage(what); + sp<AMessage> msg = new AMessage(); + msg->setWhat(what); msg->mNumItems = static_cast<size_t>(parcel.readInt32()); for (size_t i = 0; i < msg->mNumItems; ++i) { diff --git a/media/libstagefright/foundation/ANetworkSession.cpp b/media/libstagefright/foundation/ANetworkSession.cpp index 4504c2b..b230400 100644 --- a/media/libstagefright/foundation/ANetworkSession.cpp +++ b/media/libstagefright/foundation/ANetworkSession.cpp @@ -187,7 +187,7 @@ ANetworkSession::Session::Session( CHECK_GE(res, 0); in_addr_t addr = ntohl(localAddr.sin_addr.s_addr); - AString localAddrString = StringPrintf( + AString localAddrString = AStringPrintf( "%d.%d.%d.%d", (addr >> 24), (addr >> 16) & 0xff, @@ -195,7 +195,7 @@ ANetworkSession::Session::Session( addr & 0xff); addr = ntohl(remoteAddr.sin_addr.s_addr); - AString remoteAddrString = StringPrintf( + AString remoteAddrString = AStringPrintf( "%d.%d.%d.%d", (addr >> 24), (addr >> 16) & 0xff, @@ -301,7 +301,7 @@ status_t ANetworkSession::Session::readMore() { uint32_t ip = ntohl(remoteAddr.sin_addr.s_addr); notify->setString( "fromAddr", - StringPrintf( + AStringPrintf( "%u.%u.%u.%u", ip >> 24, (ip >> 16) & 0xff, diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp index 9835ca3..b167543 100644 --- a/media/libstagefright/foundation/AString.cpp +++ b/media/libstagefright/foundation/AString.cpp @@ -366,7 +366,7 @@ status_t AString::writeToParcel(Parcel *parcel) const { return err; } -AString StringPrintf(const char *format, ...) { +AString AStringPrintf(const char *format, ...) { va_list ap; va_start(ap, format); diff --git a/media/libstagefright/foundation/AStringUtils.cpp b/media/libstagefright/foundation/AStringUtils.cpp new file mode 100644 index 0000000..e5a846c --- /dev/null +++ b/media/libstagefright/foundation/AStringUtils.cpp @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#include <string.h> +#include <AStringUtils.h> + +namespace android { + +// static +int AStringUtils::Compare(const char *a, const char *b, size_t len, bool ignoreCase) { + // this method relies on a trailing '\0' if a or b are shorter than len + return ignoreCase ? strncasecmp(a, b, len) : strncmp(a, b, len); +} + +// static +bool AStringUtils::MatchesGlob( + const char *glob, size_t globLen, const char *str, size_t strLen, bool ignoreCase) { + // this method does not assume a trailing '\0' + size_t ix = 0, globIx = 0; + + // pattern must match until first '*' + while (globIx < globLen && glob[globIx] != '*') { + ++globIx; + } + if (strLen < globIx || Compare(str, glob, globIx /* len */, ignoreCase)) { + return false; + } + ix = globIx; + + // process by * separated sections + while (globIx < globLen) { + ++globIx; + size_t start = globIx; + while (globIx < globLen && glob[globIx] != '*') { + ++globIx; + } + size_t len = globIx - start; + const char *pattern = glob + start; + + if (globIx == globLen) { + // last pattern must match tail + if (ix + len > strLen) { + return false; + } + const char *tail = str + strLen - len; + return !Compare(tail, pattern, len, ignoreCase); + } + // progress after first occurrence of pattern + while (ix + len <= strLen && Compare(str + ix, pattern, len, ignoreCase)) { + ++ix; + } + if (ix + len > strLen) { + return false; + } + ix += len; + // we will loop around as globIx < globLen + } + + // we only get here if there were no * in the pattern + return ix == strLen; +} + +} // namespace android + diff --git a/media/libstagefright/foundation/AWakeLock.cpp b/media/libstagefright/foundation/AWakeLock.cpp new file mode 100644 index 0000000..d9277ac --- /dev/null +++ b/media/libstagefright/foundation/AWakeLock.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "AWakeLock" +#include <utils/Log.h> + +#include "ADebug.h" +#include "AWakeLock.h" + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <powermanager/PowerManager.h> + + +namespace android { + +AWakeLock::AWakeLock() : + mPowerManager(NULL), + mWakeLockToken(NULL), + mWakeLockCount(0), + mDeathRecipient(new PMDeathRecipient(this)) {} + +AWakeLock::~AWakeLock() { + if (mPowerManager != NULL) { + sp<IBinder> binder = IInterface::asBinder(mPowerManager); + binder->unlinkToDeath(mDeathRecipient); + } + clearPowerManager(); +} + +bool AWakeLock::acquire() { + if (mWakeLockCount == 0) { + CHECK(mWakeLockToken == NULL); + if (mPowerManager == NULL) { + // use checkService() to avoid blocking if power service is not up yet + sp<IBinder> binder = + defaultServiceManager()->checkService(String16("power")); + if (binder == NULL) { + ALOGW("could not get the power manager service"); + } else { + mPowerManager = interface_cast<IPowerManager>(binder); + binder->linkToDeath(mDeathRecipient); + } + } + if (mPowerManager != NULL) { + sp<IBinder> binder = new BBinder(); + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + status_t status = mPowerManager->acquireWakeLock( + POWERMANAGER_PARTIAL_WAKE_LOCK, + binder, String16("AWakeLock"), String16("media")); + IPCThreadState::self()->restoreCallingIdentity(token); + if (status == NO_ERROR) { + mWakeLockToken = binder; + mWakeLockCount++; + return true; + } + } + } else { + mWakeLockCount++; + return true; + } + return false; +} + +void AWakeLock::release(bool force) { + if (mWakeLockCount == 0) { + return; + } + if (force) { + // Force wakelock release below by setting reference count to 1. + mWakeLockCount = 1; + } + if (--mWakeLockCount == 0) { + CHECK(mWakeLockToken != NULL); + if (mPowerManager != NULL) { + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + mPowerManager->releaseWakeLock(mWakeLockToken, 0 /* flags */); + IPCThreadState::self()->restoreCallingIdentity(token); + } + mWakeLockToken.clear(); + } +} + +void AWakeLock::clearPowerManager() { + release(true); + mPowerManager.clear(); +} + +void AWakeLock::PMDeathRecipient::binderDied(const wp<IBinder>& who __unused) { + if (mWakeLock != NULL) { + mWakeLock->clearPowerManager(); + } +} + +} // namespace android diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk index 90a6a23..08355c7 100644 --- a/media/libstagefright/foundation/Android.mk +++ b/media/libstagefright/foundation/Android.mk @@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \ AAtomizer.cpp \ ABitReader.cpp \ ABuffer.cpp \ + ADebug.cpp \ AHandler.cpp \ AHierarchicalStateMachine.cpp \ ALooper.cpp \ @@ -12,6 +13,8 @@ LOCAL_SRC_FILES:= \ AMessage.cpp \ ANetworkSession.cpp \ AString.cpp \ + AStringUtils.cpp \ + AWakeLock.cpp \ ParsedMessage.cpp \ base64.cpp \ hexdump.cpp @@ -22,7 +25,9 @@ LOCAL_C_INCLUDES:= \ LOCAL_SHARED_LIBRARIES := \ libbinder \ libutils \ - liblog + libcutils \ + liblog \ + libpowermanager LOCAL_CFLAGS += -Wno-multichar -Werror diff --git a/media/libstagefright/http/MediaHTTP.cpp b/media/libstagefright/http/MediaHTTP.cpp index 2d29913..bb89567 100644 --- a/media/libstagefright/http/MediaHTTP.cpp +++ b/media/libstagefright/http/MediaHTTP.cpp @@ -129,7 +129,7 @@ status_t MediaHTTP::getSize(off64_t *size) { *size = mCachedSize; - return *size < 0 ? *size : OK; + return *size < 0 ? *size : static_cast<status_t>(OK); } uint32_t MediaHTTP::flags() { diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk index e8d558c..93b7935 100644 --- a/media/libstagefright/httplive/Android.mk +++ b/media/libstagefright/httplive/Android.mk @@ -10,8 +10,7 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/media/libstagefright \ - $(TOP)/frameworks/native/include/media/openmax \ - $(TOP)/external/openssl/include + $(TOP)/frameworks/native/include/media/openmax LOCAL_CFLAGS += -Werror diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 874c118..f5328a6 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -49,6 +49,76 @@ namespace android { +// static +// High water mark to start up switch or report prepared) +const int64_t LiveSession::kHighWaterMark = 8000000ll; +const int64_t LiveSession::kMidWaterMark = 5000000ll; +const int64_t LiveSession::kLowWaterMark = 3000000ll; + +struct LiveSession::BandwidthEstimator : public RefBase { + BandwidthEstimator(); + + void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); + bool estimateBandwidth(int32_t *bandwidth); + +private: + // Bandwidth estimation parameters + static const int32_t kMaxBandwidthHistoryItems = 20; + static const int64_t kMaxBandwidthHistoryWindowUs = 3000000ll; // 3 sec + + struct BandwidthEntry { + int64_t mDelayUs; + size_t mNumBytes; + }; + + Mutex mLock; + List<BandwidthEntry> mBandwidthHistory; + int64_t mTotalTransferTimeUs; + size_t mTotalTransferBytes; + + DISALLOW_EVIL_CONSTRUCTORS(BandwidthEstimator); +}; + +LiveSession::BandwidthEstimator::BandwidthEstimator() : + mTotalTransferTimeUs(0), + mTotalTransferBytes(0) { +} + +void LiveSession::BandwidthEstimator::addBandwidthMeasurement( + size_t numBytes, int64_t delayUs) { + AutoMutex autoLock(mLock); + + BandwidthEntry entry; + entry.mDelayUs = delayUs; + entry.mNumBytes = numBytes; + mTotalTransferTimeUs += delayUs; + mTotalTransferBytes += numBytes; + mBandwidthHistory.push_back(entry); + + // trim old samples, keeping at least kMaxBandwidthHistoryItems samples, + // and total transfer time at least kMaxBandwidthHistoryWindowUs. + while (mBandwidthHistory.size() > kMaxBandwidthHistoryItems) { + List<BandwidthEntry>::iterator it = mBandwidthHistory.begin(); + if (mTotalTransferTimeUs - it->mDelayUs < kMaxBandwidthHistoryWindowUs) { + break; + } + mTotalTransferTimeUs -= it->mDelayUs; + mTotalTransferBytes -= it->mNumBytes; + mBandwidthHistory.erase(mBandwidthHistory.begin()); + } +} + +bool LiveSession::BandwidthEstimator::estimateBandwidth(int32_t *bandwidthBps) { + AutoMutex autoLock(mLock); + + if (mBandwidthHistory.size() < 2) { + return false; + } + + *bandwidthBps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs); + return true; +} + LiveSession::LiveSession( const sp<AMessage> ¬ify, uint32_t flags, const sp<IMediaHTTPService> &httpService) @@ -58,35 +128,35 @@ LiveSession::LiveSession( mInPreparationPhase(true), mHTTPDataSource(new MediaHTTP(mHTTPService->makeHTTPConnection())), mCurBandwidthIndex(-1), + mBandwidthEstimator(new BandwidthEstimator()), mStreamMask(0), mNewStreamMask(0), mSwapMask(0), - mCheckBandwidthGeneration(0), mSwitchGeneration(0), mSubtitleGeneration(0), mLastDequeuedTimeUs(0ll), mRealTimeBaseUs(0ll), mReconfigurationInProgress(false), mSwitchInProgress(false), - mDisconnectReplyID(0), - mSeekReplyID(0), mFirstTimeUsValid(false), mFirstTimeUs(0), - mLastSeekTimeUs(0) { + mLastSeekTimeUs(0), + mPollBufferingGeneration(0) { mStreams[kAudioIndex] = StreamItem("audio"); mStreams[kVideoIndex] = StreamItem("video"); mStreams[kSubtitleIndex] = StreamItem("subtitles"); for (size_t i = 0; i < kMaxStreams; ++i) { - mDiscontinuities.add(indexToType(i), new AnotherPacketSource(NULL /* meta */)); mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */)); mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */)); - mBuffering[i] = false; } } LiveSession::~LiveSession() { + if (mFetcherLooper != NULL) { + mFetcherLooper->stop(); + } } sp<ABuffer> LiveSession::createFormatChangeBuffer(bool swap) { @@ -115,46 +185,42 @@ status_t LiveSession::dequeueAccessUnit( return -EWOULDBLOCK; } - status_t finalResult; - sp<AnotherPacketSource> discontinuityQueue = mDiscontinuities.valueFor(stream); - if (discontinuityQueue->hasBufferAvailable(&finalResult)) { - discontinuityQueue->dequeueAccessUnit(accessUnit); - // seeking, track switching - sp<AMessage> extra; - int64_t timeUs; - if ((*accessUnit)->meta()->findMessage("extra", &extra) - && extra != NULL - && extra->findInt64("timeUs", &timeUs)) { - // seeking only - mLastSeekTimeUs = timeUs; - mDiscontinuityOffsetTimesUs.clear(); - mDiscontinuityAbsStartTimesUs.clear(); - } - return INFO_DISCONTINUITY; - } - + status_t finalResult = OK; sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream); ssize_t idx = typeToIndex(stream); if (!packetSource->hasBufferAvailable(&finalResult)) { if (finalResult == OK) { - mBuffering[idx] = true; return -EAGAIN; } else { return finalResult; } } - if (mBuffering[idx]) { - if (mSwitchInProgress - || packetSource->isFinished(0) - || packetSource->getEstimatedDurationUs() > 10000000ll) { - mBuffering[idx] = false; - } + // Do not let client pull data if we don't have format yet. + // We might only have a format discontinuity queued without actual data. + // When NuPlayerDecoder dequeues the format discontinuity, it will + // immediately try to getFormat. If we return NULL, NuPlayerDecoder + // thinks it can do seamless change, so will not shutdown decoder. + // When the actual format arrives, it can't handle it and get stuck. + // TODO: We need a method to check if the packet source has any + // data packets available, dequeuing should only start then. + sp<MetaData> format = packetSource->getFormat(); + if (format == NULL) { + return -EAGAIN; + } + int32_t targetDuration = 0; + sp<AMessage> meta = packetSource->getLatestEnqueuedMeta(); + if (meta != NULL) { + meta->findInt32("targetDuration", &targetDuration); } - if (mBuffering[idx]) { - return -EAGAIN; + int64_t targetDurationUs = targetDuration * 1000000ll; + if (targetDurationUs == 0 || + targetDurationUs > PlaylistFetcher::kMinBufferedDurationUs) { + // Fetchers limit buffering to + // min(3 * targetDuration, kMinBufferedDurationUs) + targetDurationUs = PlaylistFetcher::kMinBufferedDurationUs; } // wait for counterpart @@ -220,7 +286,7 @@ status_t LiveSession::dequeueAccessUnit( Mutex::Autolock lock(mSwapMutex); if (switchGeneration == mSwitchGeneration) { swapPacketSource(stream); - sp<AMessage> msg = new AMessage(kWhatSwapped, id()); + sp<AMessage> msg = new AMessage(kWhatSwapped, this); msg->setInt32("stream", stream); msg->setInt32("switchGeneration", switchGeneration); msg->post(); @@ -325,7 +391,7 @@ status_t LiveSession::getStreamFormat(StreamType stream, sp<AMessage> *format) { void LiveSession::connectAsync( const char *url, const KeyedVector<String8, String8> *headers) { - sp<AMessage> msg = new AMessage(kWhatConnect, id()); + sp<AMessage> msg = new AMessage(kWhatConnect, this); msg->setString("url", url); if (headers != NULL) { @@ -338,7 +404,7 @@ void LiveSession::connectAsync( } status_t LiveSession::disconnect() { - sp<AMessage> msg = new AMessage(kWhatDisconnect, id()); + sp<AMessage> msg = new AMessage(kWhatDisconnect, this); sp<AMessage> response; status_t err = msg->postAndAwaitResponse(&response); @@ -347,7 +413,7 @@ status_t LiveSession::disconnect() { } status_t LiveSession::seekTo(int64_t timeUs) { - sp<AMessage> msg = new AMessage(kWhatSeek, id()); + sp<AMessage> msg = new AMessage(kWhatSeek, this); msg->setInt64("timeUs", timeUs); sp<AMessage> response; @@ -378,7 +444,7 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { case kWhatSeek: { - uint32_t seekReplyID; + sp<AReplyToken> seekReplyID; CHECK(msg->senderAwaitsResponse(&seekReplyID)); mSeekReplyID = seekReplyID; mSeekReply = new AMessage; @@ -405,11 +471,16 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { if (what == PlaylistFetcher::kWhatStopped) { AString uri; CHECK(msg->findString("uri", &uri)); - if (mFetcherInfos.removeItem(uri) < 0) { + ssize_t index = mFetcherInfos.indexOfKey(uri); + if (index < 0) { // ignore duplicated kWhatStopped messages. break; } + mFetcherLooper->unregisterHandler( + mFetcherInfos[index].mFetcher->id()); + mFetcherInfos.removeItemsAt(index); + if (mSwitchInProgress) { tryToFinishBandwidthSwitch(); } @@ -419,14 +490,6 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { CHECK_GT(mContinuationCounter, 0); if (--mContinuationCounter == 0) { mContinuation->post(); - - if (mSeekReplyID != 0) { - CHECK(mSeekReply != NULL); - mSeekReply->setInt32("err", OK); - mSeekReply->postReply(mSeekReplyID); - mSeekReplyID = 0; - mSeekReply.clear(); - } } } break; @@ -440,8 +503,11 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { int64_t durationUs; CHECK(msg->findInt64("durationUs", &durationUs)); - FetcherInfo *info = &mFetcherInfos.editValueFor(uri); - info->mDurationUs = durationUs; + ssize_t index = mFetcherInfos.indexOfKey(uri); + if (index >= 0) { + FetcherInfo *info = &mFetcherInfos.editValueFor(uri); + info->mDurationUs = durationUs; + } break; } @@ -489,34 +555,6 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { break; } - case PlaylistFetcher::kWhatTemporarilyDoneFetching: - { - AString uri; - CHECK(msg->findString("uri", &uri)); - - if (mFetcherInfos.indexOfKey(uri) < 0) { - ALOGE("couldn't find uri"); - break; - } - FetcherInfo *info = &mFetcherInfos.editValueFor(uri); - info->mIsPrepared = true; - - if (mInPreparationPhase) { - bool allFetchersPrepared = true; - for (size_t i = 0; i < mFetcherInfos.size(); ++i) { - if (!mFetcherInfos.valueAt(i).mIsPrepared) { - allFetchersPrepared = false; - break; - } - } - - if (allFetchersPrepared) { - postPrepared(OK); - } - } - break; - } - case PlaylistFetcher::kWhatStartedAt: { int32_t switchGeneration; @@ -545,19 +583,6 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { break; } - case kWhatCheckBandwidth: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mCheckBandwidthGeneration) { - break; - } - - onCheckBandwidth(msg); - break; - } - case kWhatChangeConfiguration: { onChangeConfiguration(msg); @@ -588,15 +613,13 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { break; } - case kWhatCheckSwitchDown: - { - onCheckSwitchDown(); - break; - } - - case kWhatSwitchDown: + case kWhatPollBuffering: { - onSwitchDown(); + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + if (generation == mPollBufferingGeneration) { + onPollBuffering(); + } break; } @@ -667,6 +690,14 @@ void LiveSession::onConnect(const sp<AMessage> &msg) { return; } + // create looper for fetchers + if (mFetcherLooper == NULL) { + mFetcherLooper = new ALooper(); + + mFetcherLooper->setName("Fetcher"); + mFetcherLooper->start(false, false); + } + // We trust the content provider to make a reasonable choice of preferred // initial bandwidth by listing it first in the variant playlist. // At startup we really don't have a good estimate on the available @@ -685,7 +716,6 @@ void LiveSession::onConnect(const sp<AMessage> &msg) { AString uri; mPlaylist->itemAt(i, &uri, &meta); - unsigned long bandwidth; CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth)); if (initialBandwidth == 0) { @@ -716,25 +746,26 @@ void LiveSession::onConnect(const sp<AMessage> &msg) { mPlaylist->pickRandomMediaItems(); changeConfiguration( 0ll /* timeUs */, initialBandwidthIndex, false /* pickTrack */); + + schedulePollBuffering(); } void LiveSession::finishDisconnect() { // No reconfiguration is currently pending, make sure none will trigger // during disconnection either. - cancelCheckBandwidthEvent(); // Protect mPacketSources from a swapPacketSource race condition through disconnect. // (finishDisconnect, onFinishDisconnect2) cancelBandwidthSwitch(); - // cancel switch down monitor - mSwitchDownMonitor.clear(); + // cancel buffer polling + cancelPollBuffering(); for (size_t i = 0; i < mFetcherInfos.size(); ++i) { mFetcherInfos.valueAt(i).mFetcher->stopAsync(); } - sp<AMessage> msg = new AMessage(kWhatFinishDisconnect2, id()); + sp<AMessage> msg = new AMessage(kWhatFinishDisconnect2, this); mContinuationCounter = mFetcherInfos.size(); mContinuation = msg; @@ -757,7 +788,7 @@ void LiveSession::onFinishDisconnect2() { response->setInt32("err", OK); response->postReply(mDisconnectReplyID); - mDisconnectReplyID = 0; + mDisconnectReplyID.clear(); } sp<PlaylistFetcher> LiveSession::addFetcher(const char *uri) { @@ -767,7 +798,7 @@ sp<PlaylistFetcher> LiveSession::addFetcher(const char *uri) { return NULL; } - sp<AMessage> notify = new AMessage(kWhatFetcherNotify, id()); + sp<AMessage> notify = new AMessage(kWhatFetcherNotify, this); notify->setString("uri", uri); notify->setInt32("switchGeneration", mSwitchGeneration); @@ -776,7 +807,7 @@ sp<PlaylistFetcher> LiveSession::addFetcher(const char *uri) { info.mDurationUs = -1ll; info.mIsPrepared = false; info.mToBeRemoved = false; - looper()->registerHandler(info.mFetcher); + mFetcherLooper->registerHandler(info.mFetcher); mFetcherInfos.add(uri, info); @@ -823,11 +854,11 @@ ssize_t LiveSession::fetchFile( headers.add( String8("Range"), String8( - StringPrintf( + AStringPrintf( "bytes=%lld-%s", range_offset, range_length < 0 - ? "" : StringPrintf("%lld", + ? "" : AStringPrintf("%lld", range_offset + range_length - 1).c_str()).c_str())); } status_t err = mHTTPDataSource->connect(url, &headers); @@ -859,7 +890,11 @@ ssize_t LiveSession::fetchFile( // Only resize when we don't know the size. size_t bufferRemaining = buffer->capacity() - buffer->size(); if (bufferRemaining == 0 && getSizeErr != OK) { - bufferRemaining = 32768; + size_t bufferIncrement = buffer->size() / 2; + if (bufferIncrement < 32768) { + bufferIncrement = 32768; + } + bufferRemaining = bufferIncrement; ALOGV("increasing download buffer to %zu bytes", buffer->size() + bufferRemaining); @@ -962,12 +997,21 @@ sp<M3UParser> LiveSession::fetchPlaylist( return playlist; } +#if 0 static double uniformRand() { return (double)rand() / RAND_MAX; } +#endif + +void LiveSession::addBandwidthMeasurement(size_t numBytes, int64_t delayUs) { + mBandwidthEstimator->addBandwidthMeasurement(numBytes, delayUs); +} -size_t LiveSession::getBandwidthIndex() { - if (mBandwidthItems.size() == 0) { +size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) { + if (mBandwidthItems.size() < 2) { + // shouldn't be here if we only have 1 bandwidth, check + // logic to get rid of redundant bandwidth polling + ALOGW("getBandwidthIndex() called for single bandwidth playlist!"); return 0; } @@ -985,15 +1029,6 @@ size_t LiveSession::getBandwidthIndex() { } if (index < 0) { - int32_t bandwidthBps; - if (mHTTPDataSource != NULL - && mHTTPDataSource->estimateBandwidth(&bandwidthBps)) { - ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); - } else { - ALOGV("no bandwidth estimate."); - return 0; // Pick the lowest bandwidth stream by default. - } - char value[PROPERTY_VALUE_MAX]; if (property_get("media.httplive.max-bw", value, NULL)) { char *end; @@ -1010,15 +1045,9 @@ size_t LiveSession::getBandwidthIndex() { index = mBandwidthItems.size() - 1; while (index > 0) { - // consider only 80% of the available bandwidth, but if we are switching up, - // be even more conservative (70%) to avoid overestimating and immediately - // switching back. - size_t adjustedBandwidthBps = bandwidthBps; - if (index > mCurBandwidthIndex) { - adjustedBandwidthBps = adjustedBandwidthBps * 7 / 10; - } else { - adjustedBandwidthBps = adjustedBandwidthBps * 8 / 10; - } + // be conservative (70%) to avoid overestimating and immediately + // switching down again. + size_t adjustedBandwidthBps = bandwidthBps * 7 / 10; if (mBandwidthItems.itemAt(index).mBandwidth <= adjustedBandwidthBps) { break; } @@ -1109,11 +1138,11 @@ status_t LiveSession::onSeek(const sp<AMessage> &msg) { } status_t LiveSession::getDuration(int64_t *durationUs) const { - int64_t maxDurationUs = 0ll; + int64_t maxDurationUs = -1ll; for (size_t i = 0; i < mFetcherInfos.size(); ++i) { int64_t fetcherDurationUs = mFetcherInfos.valueAt(i).mDurationUs; - if (fetcherDurationUs >= 0ll && fetcherDurationUs > maxDurationUs) { + if (fetcherDurationUs > maxDurationUs) { maxDurationUs = fetcherDurationUs; } } @@ -1156,7 +1185,7 @@ status_t LiveSession::selectTrack(size_t index, bool select) { ++mSubtitleGeneration; status_t err = mPlaylist->selectTrack(index, select); if (err == OK) { - sp<AMessage> msg = new AMessage(kWhatChangeConfiguration, id()); + sp<AMessage> msg = new AMessage(kWhatChangeConfiguration, this); msg->setInt32("bandwidthIndex", mCurBandwidthIndex); msg->setInt32("pickTrack", select); msg->post(); @@ -1164,17 +1193,12 @@ status_t LiveSession::selectTrack(size_t index, bool select) { return err; } -bool LiveSession::canSwitchUp() { - // Allow upwards bandwidth switch when a stream has buffered at least 10 seconds. - status_t err = OK; - for (size_t i = 0; i < mPacketSources.size(); ++i) { - sp<AnotherPacketSource> source = mPacketSources.valueAt(i); - int64_t dur = source->getBufferedDurationUs(&err); - if (err == OK && dur > 10000000) { - return true; - } +ssize_t LiveSession::getSelectedTrack(media_track_type type) const { + if (mPlaylist == NULL) { + return -1; + } else { + return mPlaylist->getSelectedTrack(type); } - return false; } void LiveSession::changeConfiguration( @@ -1211,34 +1235,36 @@ void LiveSession::changeConfiguration( bool discardFetcher = true; - // If we're seeking all current fetchers are discarded. if (timeUs < 0ll) { // delay fetcher removal if not picking tracks discardFetcher = pickTrack; - for (size_t j = 0; j < kMaxStreams; ++j) { - StreamType type = indexToType(j); - if ((streamMask & type) && uri == URIs[j]) { - resumeMask |= type; - streamMask &= ~type; - discardFetcher = false; - } + } + + for (size_t j = 0; j < kMaxStreams; ++j) { + StreamType type = indexToType(j); + if ((streamMask & type) && uri == URIs[j]) { + resumeMask |= type; + streamMask &= ~type; + discardFetcher = false; } } if (discardFetcher) { mFetcherInfos.valueAt(i).mFetcher->stopAsync(); } else { - mFetcherInfos.valueAt(i).mFetcher->pauseAsync(); + // if we're seeking, pause immediately (no need to finish the segment) + bool immediate = (timeUs >= 0ll); + mFetcherInfos.valueAt(i).mFetcher->pauseAsync(immediate); } } sp<AMessage> msg; if (timeUs < 0ll) { // skip onChangeConfiguration2 (decoder destruction) if not seeking. - msg = new AMessage(kWhatChangeConfiguration3, id()); + msg = new AMessage(kWhatChangeConfiguration3, this); } else { - msg = new AMessage(kWhatChangeConfiguration2, id()); + msg = new AMessage(kWhatChangeConfiguration2, this); } msg->setInt32("streamMask", streamMask); msg->setInt32("resumeMask", resumeMask); @@ -1259,14 +1285,6 @@ void LiveSession::changeConfiguration( if (mContinuationCounter == 0) { msg->post(); - - if (mSeekReplyID != 0) { - CHECK(mSeekReply != NULL); - mSeekReply->setInt32("err", OK); - mSeekReply->postReply(mSeekReplyID); - mSeekReplyID = 0; - mSeekReply.clear(); - } } } @@ -1286,13 +1304,34 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { // All fetchers are either suspended or have been removed now. + // If we're seeking, clear all packet sources before we report + // seek complete, to prevent decoder from pulling stale data. + int64_t timeUs; + CHECK(msg->findInt64("timeUs", &timeUs)); + + if (timeUs >= 0) { + mLastSeekTimeUs = timeUs; + + for (size_t i = 0; i < mPacketSources.size(); i++) { + mPacketSources.editValueAt(i)->clear(); + } + + mDiscontinuityOffsetTimesUs.clear(); + mDiscontinuityAbsStartTimesUs.clear(); + + if (mSeekReplyID != NULL) { + CHECK(mSeekReply != NULL); + mSeekReply->setInt32("err", OK); + mSeekReply->postReply(mSeekReplyID); + mSeekReplyID.clear(); + mSeekReply.clear(); + } + } + uint32_t streamMask, resumeMask; CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask)); CHECK(msg->findInt32("resumeMask", (int32_t *)&resumeMask)); - // currently onChangeConfiguration2 is only called for seeking; - // remove the following CHECK if using it else where. - CHECK_EQ(resumeMask, 0); streamMask |= resumeMask; AString URIs[kMaxStreams]; @@ -1304,17 +1343,25 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { } } - // Determine which decoders to shutdown on the player side, - // a decoder has to be shutdown if either - // 1) its streamtype was active before but now longer isn't. - // or - // 2) its streamtype was already active and still is but the URI - // has changed. uint32_t changedMask = 0; for (size_t i = 0; i < kMaxStreams && i != kSubtitleIndex; ++i) { - if (((mStreamMask & streamMask & indexToType(i)) - && !(URIs[i] == mStreams[i].mUri)) - || (mStreamMask & ~streamMask & indexToType(i))) { + // stream URI could change even if onChangeConfiguration2 is only + // used for seek. Seek could happen during a bw switch, in this + // case bw switch will be cancelled, but the seekTo position will + // fetch from the new URI. + if ((mStreamMask & streamMask & indexToType(i)) + && !mStreams[i].mUri.empty() + && !(URIs[i] == mStreams[i].mUri)) { + ALOGV("stream %zu changed: oldURI %s, newURI %s", i, + mStreams[i].mUri.c_str(), URIs[i].c_str()); + sp<AnotherPacketSource> source = mPacketSources.valueFor(indexToType(i)); + source->queueDiscontinuity( + ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true); + } + // Determine which decoders to shutdown on the player side, + // a decoder has to be shutdown if its streamtype was active + // before but now longer isn't. + if ((mStreamMask & ~streamMask & indexToType(i))) { changedMask |= indexToType(i); } } @@ -1335,7 +1382,7 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { notify->setInt32("changedMask", changedMask); msg->setWhat(kWhatChangeConfiguration3); - msg->setTarget(id()); + msg->setTarget(this); notify->setMessage("reply", msg); notify->post(); @@ -1391,24 +1438,13 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { for (size_t j = 0; j < kMaxStreams; ++j) { if ((resumeMask & indexToType(j)) && uri == mStreams[j].mUri) { sources[j] = mPacketSources.valueFor(indexToType(j)); - - if (j != kSubtitleIndex) { - ALOGV("queueing dummy discontinuity for stream type %d", indexToType(j)); - sp<AnotherPacketSource> discontinuityQueue; - discontinuityQueue = mDiscontinuities.valueFor(indexToType(j)); - discontinuityQueue->queueDiscontinuity( - ATSParser::DISCONTINUITY_NONE, - NULL, - true); - } } } - FetcherInfo &info = mFetcherInfos.editValueAt(i); if (sources[kAudioIndex] != NULL || sources[kVideoIndex] != NULL || sources[kSubtitleIndex] != NULL) { info.mFetcher->startAsync( - sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex]); + sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex], timeUs); } else { info.mToBeRemoved = true; } @@ -1433,7 +1469,6 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { sp<PlaylistFetcher> fetcher = addFetcher(uri.c_str()); CHECK(fetcher != NULL); - int32_t latestSeq = -1; int64_t startTimeUs = -1; int64_t segmentStartTimeUs = -1ll; int32_t discontinuitySeq = -1; @@ -1450,18 +1485,9 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { sources[j] = mPacketSources.valueFor(indexToType(j)); if (timeUs >= 0) { - sources[j]->clear(); startTimeUs = timeUs; - - sp<AnotherPacketSource> discontinuityQueue; - sp<AMessage> extra = new AMessage; - extra->setInt64("timeUs", timeUs); - discontinuityQueue = mDiscontinuities.valueFor(indexToType(j)); - discontinuityQueue->queueDiscontinuity( - ATSParser::DISCONTINUITY_TIME, extra, true); } else { int32_t type; - int64_t srcSegmentStartTimeUs; sp<AMessage> meta; if (pickTrack) { // selecting @@ -1473,14 +1499,15 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { if (meta != NULL && !meta->findInt32("discontinuity", &type)) { int64_t tmpUs; + int64_t tmpSegmentUs; + CHECK(meta->findInt64("timeUs", &tmpUs)); - if (startTimeUs < 0 || tmpUs < startTimeUs) { + CHECK(meta->findInt64("segmentStartTimeUs", &tmpSegmentUs)); + if (startTimeUs < 0 || tmpSegmentUs < segmentStartTimeUs) { + startTimeUs = tmpUs; + segmentStartTimeUs = tmpSegmentUs; + } else if (tmpSegmentUs == segmentStartTimeUs && tmpUs < startTimeUs) { startTimeUs = tmpUs; - } - - CHECK(meta->findInt64("segmentStartTimeUs", &tmpUs)); - if (segmentStartTimeUs < 0 || tmpUs < segmentStartTimeUs) { - segmentStartTimeUs = tmpUs; } int32_t seq; @@ -1496,9 +1523,10 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { if (j == kSubtitleIndex) { break; } - sp<AnotherPacketSource> discontinuityQueue; - discontinuityQueue = mDiscontinuities.valueFor(indexToType(j)); - discontinuityQueue->queueDiscontinuity( + + ALOGV("stream[%zu]: queue format change", j); + + sources[j]->queueDiscontinuity( ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true); } else { // adapting, queue discontinuities after resume @@ -1528,9 +1556,6 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { // All fetchers have now been started, the configuration change // has completed. - cancelCheckBandwidthEvent(); - scheduleCheckBandwidthEvent(); - ALOGV("XXX configuration change completed."); mReconfigurationInProgress = false; if (switching) { @@ -1539,7 +1564,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { mStreamMask = mNewStreamMask; } - if (mDisconnectReplyID != 0) { + if (mDisconnectReplyID != NULL) { finishDisconnect(); } } @@ -1587,42 +1612,35 @@ void LiveSession::onSwapped(const sp<AMessage> &msg) { tryToFinishBandwidthSwitch(); } -void LiveSession::onCheckSwitchDown() { - if (mSwitchDownMonitor == NULL) { - return; - } +void LiveSession::schedulePollBuffering() { + sp<AMessage> msg = new AMessage(kWhatPollBuffering, this); + msg->setInt32("generation", mPollBufferingGeneration); + msg->post(1000000ll); +} - for (size_t i = 0; i < kMaxStreams; ++i) { - int32_t targetDuration; - sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(indexToType(i)); - sp<AMessage> meta = packetSource->getLatestDequeuedMeta(); +void LiveSession::cancelPollBuffering() { + ++mPollBufferingGeneration; +} - if (meta != NULL && meta->findInt32("targetDuration", &targetDuration) ) { - int64_t bufferedDurationUs = packetSource->getEstimatedDurationUs(); - int64_t targetDurationUs = targetDuration * 1000000ll; +void LiveSession::onPollBuffering() { + ALOGV("onPollBuffering: mSwitchInProgress %d, mReconfigurationInProgress %d, " + "mInPreparationPhase %d, mCurBandwidthIndex %d, mStreamMask 0x%x", + mSwitchInProgress, mReconfigurationInProgress, + mInPreparationPhase, mCurBandwidthIndex, mStreamMask); - if (bufferedDurationUs < targetDurationUs / 3) { - (new AMessage(kWhatSwitchDown, id()))->post(); - break; - } + bool low, mid, high; + if (checkBuffering(low, mid, high)) { + if (mInPreparationPhase && mid) { + postPrepared(OK); } - } - - mSwitchDownMonitor->post(1000000ll); -} -void LiveSession::onSwitchDown() { - if (mReconfigurationInProgress || mSwitchInProgress || mCurBandwidthIndex == 0) { - return; - } - - ssize_t bandwidthIndex = getBandwidthIndex(); - if (bandwidthIndex < mCurBandwidthIndex) { - changeConfiguration(-1, bandwidthIndex, false); - return; + // don't switch before we report prepared + if (!mInPreparationPhase) { + switchBandwidthIfNeeded(high, !mid); + } } - changeConfiguration(-1, mCurBandwidthIndex - 1, false); + schedulePollBuffering(); } // Mark switch done when: @@ -1647,16 +1665,6 @@ void LiveSession::tryToFinishBandwidthSwitch() { } } -void LiveSession::scheduleCheckBandwidthEvent() { - sp<AMessage> msg = new AMessage(kWhatCheckBandwidth, id()); - msg->setInt32("generation", mCheckBandwidthGeneration); - msg->post(10000000ll); -} - -void LiveSession::cancelCheckBandwidthEvent() { - ++mCheckBandwidthGeneration; -} - void LiveSession::cancelBandwidthSwitch() { Mutex::Autolock lock(mSwapMutex); mSwitchGeneration++; @@ -1686,33 +1694,93 @@ void LiveSession::cancelBandwidthSwitch() { } } -bool LiveSession::canSwitchBandwidthTo(size_t bandwidthIndex) { - if (mReconfigurationInProgress || mSwitchInProgress) { +bool LiveSession::checkBuffering(bool &low, bool &mid, bool &high) { + low = mid = high = false; + + if (mSwitchInProgress || mReconfigurationInProgress) { + ALOGV("Switch/Reconfig in progress, defer buffer polling"); return false; } - if (mCurBandwidthIndex < 0) { - return true; + // TODO: Fine tune low/high mark. + // We also need to pause playback if buffering is too low. + // Currently during underflow, we depend on decoder to starve + // to pause, but A/V could have different buffering left, + // they're not paused together. + // TODO: Report buffering level to NuPlayer for BUFFERING_UPDATE + + // Switch down if any of the fetchers are below low mark; + // Switch up if all of the fetchers are over high mark. + size_t activeCount, lowCount, midCount, highCount; + activeCount = lowCount = midCount = highCount = 0; + for (size_t i = 0; i < mPacketSources.size(); ++i) { + // we don't check subtitles for buffering level + if (!(mStreamMask & mPacketSources.keyAt(i) + & (STREAMTYPE_AUDIO | STREAMTYPE_VIDEO))) { + continue; + } + // ignore streams that never had any packet queued. + // (it's possible that the variant only has audio or video) + sp<AMessage> meta = mPacketSources[i]->getLatestEnqueuedMeta(); + if (meta == NULL) { + continue; + } + + ++activeCount; + int64_t bufferedDurationUs = + mPacketSources[i]->getEstimatedDurationUs(); + ALOGV("source[%zu]: buffered %lld us", i, (long long)bufferedDurationUs); + if (bufferedDurationUs < kLowWaterMark) { + ++lowCount; + break; + } else if (bufferedDurationUs > kHighWaterMark) { + ++midCount; + ++highCount; + } else if (bufferedDurationUs > kMidWaterMark) { + ++midCount; + } } - if (bandwidthIndex == (size_t)mCurBandwidthIndex) { - return false; - } else if (bandwidthIndex > (size_t)mCurBandwidthIndex) { - return canSwitchUp(); - } else { + if (activeCount > 0) { + high = (highCount == activeCount); + mid = (midCount == activeCount); + low = (lowCount > 0); return true; } + + return false; } -void LiveSession::onCheckBandwidth(const sp<AMessage> &msg) { - size_t bandwidthIndex = getBandwidthIndex(); - if (canSwitchBandwidthTo(bandwidthIndex)) { - changeConfiguration(-1ll /* timeUs */, bandwidthIndex); +void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { + // no need to check bandwidth if we only have 1 bandwidth settings + if (mBandwidthItems.size() < 2) { + return; + } + + int32_t bandwidthBps; + if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps)) { + ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); } else { - // Come back and check again 10 seconds later in case there is nothing to do now. - // If we DO change configuration, once that completes it'll schedule a new - // check bandwidth event with an incremented mCheckBandwidthGeneration. - msg->post(10000000ll); + ALOGV("no bandwidth estimate."); + return; + } + + int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth; + bool bandwidthLow = bandwidthBps < (int32_t)curBandwidth * 8 / 10; + bool bandwidthHigh = bandwidthBps > (int32_t)curBandwidth * 12 / 10; + + if ((bufferHigh && bandwidthHigh) || (bufferLow && bandwidthLow)) { + ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps); + + if (bandwidthIndex == mCurBandwidthIndex + || (bufferHigh && bandwidthIndex < mCurBandwidthIndex) + || (bufferLow && bandwidthIndex > mCurBandwidthIndex)) { + return; + } + + ALOGI("#### Initiate Bandwidth Switch: %d => %d", + mCurBandwidthIndex, bandwidthIndex); + changeConfiguration(-1, bandwidthIndex, false); } } @@ -1730,10 +1798,8 @@ void LiveSession::postPrepared(status_t err) { notify->post(); mInPreparationPhase = false; - - mSwitchDownMonitor = new AMessage(kWhatCheckSwitchDown, id()); - mSwitchDownMonitor->post(); } + } // namespace android diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h index 7aacca6..685fefa 100644 --- a/media/libstagefright/httplive/LiveSession.h +++ b/media/libstagefright/httplive/LiveSession.h @@ -19,12 +19,14 @@ #define LIVE_SESSION_H_ #include <media/stagefright/foundation/AHandler.h> +#include <media/mediaplayer.h> #include <utils/String8.h> namespace android { struct ABuffer; +struct AReplyToken; struct AnotherPacketSource; struct DataSource; struct HTTPBase; @@ -32,17 +34,12 @@ struct IMediaHTTPService; struct LiveDataSource; struct M3UParser; struct PlaylistFetcher; -struct Parcel; struct LiveSession : public AHandler { enum Flags { // Don't log any URLs. kFlagIncognito = 1, }; - LiveSession( - const sp<AMessage> ¬ify, - uint32_t flags, - const sp<IMediaHTTPService> &httpService); enum StreamIndex { kAudioIndex = 0, @@ -56,6 +53,12 @@ struct LiveSession : public AHandler { STREAMTYPE_VIDEO = 1 << kVideoIndex, STREAMTYPE_SUBTITLES = 1 << kSubtitleIndex, }; + + LiveSession( + const sp<AMessage> ¬ify, + uint32_t flags, + const sp<IMediaHTTPService> &httpService); + status_t dequeueAccessUnit(StreamType stream, sp<ABuffer> *accessUnit); status_t getStreamFormat(StreamType stream, sp<AMessage> *format); @@ -73,6 +76,7 @@ struct LiveSession : public AHandler { size_t getTrackCount() const; sp<AMessage> getTrackInfo(size_t trackIndex) const; status_t selectTrack(size_t index, bool select); + ssize_t getSelectedTrack(media_track_type /* type */) const; bool isSeekable() const; bool hasDynamicDuration() const; @@ -102,16 +106,19 @@ private: kWhatDisconnect = 'disc', kWhatSeek = 'seek', kWhatFetcherNotify = 'notf', - kWhatCheckBandwidth = 'bndw', kWhatChangeConfiguration = 'chC0', kWhatChangeConfiguration2 = 'chC2', kWhatChangeConfiguration3 = 'chC3', kWhatFinishDisconnect2 = 'fin2', kWhatSwapped = 'swap', - kWhatCheckSwitchDown = 'ckSD', - kWhatSwitchDown = 'sDwn', + kWhatPollBuffering = 'poll', }; + static const int64_t kHighWaterMark; + static const int64_t kMidWaterMark; + static const int64_t kLowWaterMark; + + struct BandwidthEstimator; struct BandwidthItem { size_t mPlaylistIndex; unsigned long mBandwidth; @@ -162,9 +169,11 @@ private: Vector<BandwidthItem> mBandwidthItems; ssize_t mCurBandwidthIndex; + sp<BandwidthEstimator> mBandwidthEstimator; sp<M3UParser> mPlaylist; + sp<ALooper> mFetcherLooper; KeyedVector<AString, FetcherInfo> mFetcherInfos; uint32_t mStreamMask; @@ -177,7 +186,6 @@ private: // we use this to track reconfiguration progress. uint32_t mSwapMask; - KeyedVector<StreamType, sp<AnotherPacketSource> > mDiscontinuities; KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources; // A second set of packet sources that buffer content for the variant we're switching to. KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources2; @@ -187,7 +195,6 @@ private: // * a forced bandwidth switch termination in cancelSwitch on the live looper. Mutex mSwapMutex; - int32_t mCheckBandwidthGeneration; int32_t mSwitchGeneration; int32_t mSubtitleGeneration; @@ -200,16 +207,17 @@ private: bool mReconfigurationInProgress; bool mSwitchInProgress; - uint32_t mDisconnectReplyID; - uint32_t mSeekReplyID; + sp<AReplyToken> mDisconnectReplyID; + sp<AReplyToken> mSeekReplyID; bool mFirstTimeUsValid; int64_t mFirstTimeUs; int64_t mLastSeekTimeUs; - sp<AMessage> mSwitchDownMonitor; KeyedVector<size_t, int64_t> mDiscontinuityAbsStartTimesUs; KeyedVector<size_t, int64_t> mDiscontinuityOffsetTimesUs; + int32_t mPollBufferingGeneration; + sp<PlaylistFetcher> addFetcher(const char *uri); void onConnect(const sp<AMessage> &msg); @@ -240,7 +248,8 @@ private: sp<M3UParser> fetchPlaylist( const char *url, uint8_t *curPlaylistHash, bool *unchanged); - size_t getBandwidthIndex(); + void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); + size_t getBandwidthIndex(int32_t bandwidthBps); int64_t latestMediaSegmentStartTimeUs(); static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *); @@ -253,27 +262,24 @@ private: void onChangeConfiguration2(const sp<AMessage> &msg); void onChangeConfiguration3(const sp<AMessage> &msg); void onSwapped(const sp<AMessage> &msg); - void onCheckSwitchDown(); - void onSwitchDown(); void tryToFinishBandwidthSwitch(); - void scheduleCheckBandwidthEvent(); - void cancelCheckBandwidthEvent(); - // cancelBandwidthSwitch is atomic wrt swapPacketSource; call it to prevent packet sources // from being swapped out on stale discontinuities while manipulating // mPacketSources/mPacketSources2. void cancelBandwidthSwitch(); - bool canSwitchBandwidthTo(size_t bandwidthIndex); - void onCheckBandwidth(const sp<AMessage> &msg); + void schedulePollBuffering(); + void cancelPollBuffering(); + void onPollBuffering(); + bool checkBuffering(bool &low, bool &mid, bool &high); + void switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow); void finishDisconnect(); void postPrepared(status_t err); void swapPacketSource(StreamType stream); - bool canSwitchUp(); DISALLOW_EVIL_CONSTRUCTORS(LiveSession); }; diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 1651dee..3c5d7cf 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -35,6 +35,7 @@ struct M3UParser::MediaGroup : public RefBase { TYPE_AUDIO, TYPE_VIDEO, TYPE_SUBS, + TYPE_CC, }; enum FlagBits { @@ -66,6 +67,9 @@ protected: virtual ~MediaGroup(); private: + + friend struct M3UParser; + struct Media { AString mName; AString mURI; @@ -247,6 +251,7 @@ M3UParser::M3UParser( mIsComplete(false), mIsEvent(false), mDiscontinuitySeq(0), + mDiscontinuityCount(0), mSelectedIndex(-1) { mInitCheck = parse(data, size); } @@ -356,6 +361,38 @@ ssize_t M3UParser::getSelectedIndex() const { return mSelectedIndex; } +ssize_t M3UParser::getSelectedTrack(media_track_type type) const { + MediaGroup::Type groupType; + switch (type) { + case MEDIA_TRACK_TYPE_VIDEO: + groupType = MediaGroup::TYPE_VIDEO; + break; + + case MEDIA_TRACK_TYPE_AUDIO: + groupType = MediaGroup::TYPE_AUDIO; + break; + + case MEDIA_TRACK_TYPE_SUBTITLE: + groupType = MediaGroup::TYPE_SUBS; + break; + + default: + return -1; + } + + for (size_t i = 0, ii = 0; i < mMediaGroups.size(); ++i) { + sp<MediaGroup> group = mMediaGroups.valueAt(i); + size_t tracks = group->countTracks(); + if (groupType != group->mType) { + ii += tracks; + } else if (group->mSelectedIndex >= 0) { + return ii + group->mSelectedIndex; + } + } + + return -1; +} + bool M3UParser::getTypeURI(size_t index, const char *key, AString *uri) const { if (!mIsVariantPlaylist) { *uri = mBaseURI; @@ -546,6 +583,7 @@ status_t M3UParser::parse(const void *_data, size_t size) { itemMeta = new AMessage; } itemMeta->setInt32("discontinuity", true); + ++mDiscontinuityCount; } else if (line.startsWith("#EXT-X-STREAM-INF")) { if (mMeta != NULL) { return ERROR_MALFORMED; @@ -573,6 +611,9 @@ status_t M3UParser::parse(const void *_data, size_t size) { } else if (line.startsWith("#EXT-X-MEDIA")) { err = parseMedia(line); } else if (line.startsWith("#EXT-X-DISCONTINUITY-SEQUENCE")) { + if (mIsVariantPlaylist) { + return ERROR_MALFORMED; + } size_t seq; err = parseDiscontinuitySequence(line, &seq); if (err == OK) { @@ -592,6 +633,7 @@ status_t M3UParser::parse(const void *_data, size_t size) { || !itemMeta->findInt64("durationUs", &durationUs)) { return ERROR_MALFORMED; } + itemMeta->setInt32("discontinuity-sequence", mDiscontinuitySeq + mDiscontinuityCount); } mItems.push(); @@ -956,6 +998,8 @@ status_t M3UParser::parseMedia(const AString &line) { groupType = MediaGroup::TYPE_AUDIO; } else if (!strcasecmp("video", val.c_str())) { groupType = MediaGroup::TYPE_VIDEO; + } else if (!strcasecmp("closed-captions", val.c_str())){ + groupType = MediaGroup::TYPE_CC; } else { ALOGE("Invalid media group type '%s'", val.c_str()); return ERROR_MALFORMED; @@ -1068,6 +1112,13 @@ status_t M3UParser::parseMedia(const AString &line) { return ERROR_MALFORMED; } + if (groupType == MediaGroup::TYPE_CC) { + // TODO: ignore this for now. + // CC track will be detected by CCDecoder. But we still need to + // pass the CC track flags (lang, auto) to the app in the future. + return OK; + } + uint32_t flags = 0; if (haveGroupAutoselect && groupAutoselect) { flags |= MediaGroup::FLAG_AUTOSELECT; diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h index d588afe..d475683 100644 --- a/media/libstagefright/httplive/M3UParser.h +++ b/media/libstagefright/httplive/M3UParser.h @@ -21,6 +21,7 @@ #include <media/stagefright/foundation/ABase.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/foundation/AString.h> +#include <media/mediaplayer.h> #include <utils/Vector.h> namespace android { @@ -46,6 +47,7 @@ struct M3UParser : public RefBase { size_t getTrackCount() const; sp<AMessage> getTrackInfo(size_t index) const; ssize_t getSelectedIndex() const; + ssize_t getSelectedTrack(media_track_type /* type */) const; bool getTypeURI(size_t index, const char *key, AString *uri) const; @@ -68,6 +70,7 @@ private: bool mIsComplete; bool mIsEvent; size_t mDiscontinuitySeq; + int32_t mDiscontinuityCount; sp<AMessage> mMeta; Vector<Item> mItems; diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index 89181b5..a447010 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -49,8 +49,10 @@ namespace android { // static const int64_t PlaylistFetcher::kMinBufferedDurationUs = 10000000ll; const int64_t PlaylistFetcher::kMaxMonitorDelayUs = 3000000ll; -const int32_t PlaylistFetcher::kDownloadBlockSize = 2048; -const int32_t PlaylistFetcher::kNumSkipFrames = 10; +const int64_t PlaylistFetcher::kFetcherResumeThreshold = 100000ll; +// LCM of 188 (size of a TS packet) & 1k works well +const int32_t PlaylistFetcher::kDownloadBlockSize = 47 * 1024; +const int32_t PlaylistFetcher::kNumSkipFrames = 5; PlaylistFetcher::PlaylistFetcher( const sp<AMessage> ¬ify, @@ -58,7 +60,6 @@ PlaylistFetcher::PlaylistFetcher( const char *uri, int32_t subtitleGeneration) : mNotify(notify), - mStartTimeUsNotify(notify->dup()), mSession(session), mURI(uri), mStreamTypeMask(0), @@ -72,16 +73,16 @@ PlaylistFetcher::PlaylistFetcher( mStartup(true), mAdaptive(false), mPrepared(false), + mTimeChangeSignaled(false), mNextPTSTimeUs(-1ll), mMonitorQueueGeneration(0), mSubtitleGeneration(subtitleGeneration), + mLastDiscontinuitySeq(-1ll), + mStopping(false), mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY), mFirstPTSValid(false), - mAbsoluteTimeAnchorUs(0ll), mVideoBuffer(new AnotherPacketSource(NULL)) { memset(mPlaylistHash, 0, sizeof(mPlaylistHash)); - mStartTimeUsNotify->setInt32("what", kWhatStartedAt); - mStartTimeUsNotify->setInt32("streamMask", 0); } PlaylistFetcher::~PlaylistFetcher() { @@ -290,7 +291,6 @@ status_t PlaylistFetcher::decryptBuffer( } status_t PlaylistFetcher::checkDecryptPadding(const sp<ABuffer> &buffer) { - status_t err; AString method; CHECK(buffer->meta()->findString("cipher-method", &method)); if (method == "NONE") { @@ -325,7 +325,7 @@ void PlaylistFetcher::postMonitorQueue(int64_t delayUs, int64_t minDelayUs) { ALOGV("Need to refresh playlist in %" PRId64 , maxDelayUs); delayUs = maxDelayUs; } - sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id()); + sp<AMessage> msg = new AMessage(kWhatMonitorQueue, this); msg->setInt32("generation", mMonitorQueueGeneration); msg->post(delayUs); } @@ -334,6 +334,11 @@ void PlaylistFetcher::cancelMonitorQueue() { ++mMonitorQueueGeneration; } +void PlaylistFetcher::setStopping(bool stopping) { + AutoMutex _l(mStoppingLock); + mStopping = stopping; +} + void PlaylistFetcher::startAsync( const sp<AnotherPacketSource> &audioSource, const sp<AnotherPacketSource> &videoSource, @@ -342,7 +347,7 @@ void PlaylistFetcher::startAsync( int64_t segmentStartTimeUs, int32_t startDiscontinuitySeq, bool adaptive) { - sp<AMessage> msg = new AMessage(kWhatStart, id()); + sp<AMessage> msg = new AMessage(kWhatStart, this); uint32_t streamTypeMask = 0ul; @@ -369,18 +374,23 @@ void PlaylistFetcher::startAsync( msg->post(); } -void PlaylistFetcher::pauseAsync() { - (new AMessage(kWhatPause, id()))->post(); +void PlaylistFetcher::pauseAsync(bool immediate) { + if (immediate) { + setStopping(true); + } + (new AMessage(kWhatPause, this))->post(); } void PlaylistFetcher::stopAsync(bool clear) { - sp<AMessage> msg = new AMessage(kWhatStop, id()); + setStopping(true); + + sp<AMessage> msg = new AMessage(kWhatStop, this); msg->setInt32("clear", clear); msg->post(); } void PlaylistFetcher::resumeUntilAsync(const sp<AMessage> ¶ms) { - AMessage* msg = new AMessage(kWhatResumeUntil, id()); + AMessage* msg = new AMessage(kWhatResumeUntil, this); msg->setMessage("params", params); msg->post(); } @@ -450,6 +460,10 @@ void PlaylistFetcher::onMessageReceived(const sp<AMessage> &msg) { status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) { mPacketSources.clear(); + mStopParams.clear(); + mStartTimeUsNotify = mNotify->dup(); + mStartTimeUsNotify->setInt32("what", kWhatStartedAt); + mStartTimeUsNotify->setInt32("streamMask", 0); uint32_t streamTypeMask; CHECK(msg->findInt32("streamTypeMask", (int32_t *)&streamTypeMask)); @@ -495,12 +509,18 @@ status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) { mSegmentStartTimeUs = segmentStartTimeUs; mDiscontinuitySeq = startDiscontinuitySeq; + mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY; + if (startTimeUs >= 0) { mStartTimeUs = startTimeUs; + mFirstPTSValid = false; mSeqNumber = -1; mStartup = true; mPrepared = false; + mIDRFound = false; + mTimeChangeSignaled = false; mAdaptive = adaptive; + mVideoBuffer->clear(); } postMonitorQueue(); @@ -510,6 +530,9 @@ status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) { void PlaylistFetcher::onPause() { cancelMonitorQueue(); + mLastDiscontinuitySeq = mDiscontinuitySeq; + + setStopping(false); } void PlaylistFetcher::onStop(const sp<AMessage> &msg) { @@ -526,6 +549,8 @@ void PlaylistFetcher::onStop(const sp<AMessage> &msg) { mPacketSources.clear(); mStreamTypeMask = 0; + + setStopping(false); } // Resume until we have reached the boundary timestamps listed in `msg`; when @@ -535,12 +560,19 @@ status_t PlaylistFetcher::onResumeUntil(const sp<AMessage> &msg) { sp<AMessage> params; CHECK(msg->findMessage("params", ¶ms)); - bool stop = false; + size_t stopCount = 0; for (size_t i = 0; i < mPacketSources.size(); i++) { sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i); const char *stopKey; int streamType = mPacketSources.keyAt(i); + + if (streamType == LiveSession::STREAMTYPE_SUBTITLES) { + // the subtitle track can always be stopped + ++stopCount; + continue; + } + switch (streamType) { case LiveSession::STREAMTYPE_VIDEO: stopKey = "timeUsVideo"; @@ -550,29 +582,26 @@ status_t PlaylistFetcher::onResumeUntil(const sp<AMessage> &msg) { stopKey = "timeUsAudio"; break; - case LiveSession::STREAMTYPE_SUBTITLES: - stopKey = "timeUsSubtitle"; - break; - default: TRESPASS(); } - // Don't resume if we would stop within a resume threshold. + // check if this stream has too little data left to be resumed int32_t discontinuitySeq; int64_t latestTimeUs = 0, stopTimeUs = 0; - sp<AMessage> latestMeta = packetSource->getLatestDequeuedMeta(); + sp<AMessage> latestMeta = packetSource->getLatestEnqueuedMeta(); if (latestMeta != NULL && latestMeta->findInt32("discontinuitySeq", &discontinuitySeq) && discontinuitySeq == mDiscontinuitySeq && latestMeta->findInt64("timeUs", &latestTimeUs) && params->findInt64(stopKey, &stopTimeUs) - && stopTimeUs - latestTimeUs < resumeThreshold(latestMeta)) { - stop = true; + && stopTimeUs - latestTimeUs < kFetcherResumeThreshold) { + ++stopCount; } } - if (stop) { + // Don't resume if all streams are within a resume threshold + if (stopCount == mPacketSources.size()) { for (size_t i = 0; i < mPacketSources.size(); i++) { mPacketSources.valueAt(i)->queueAccessUnit(mSession->createFormatChangeBuffer()); } @@ -581,7 +610,7 @@ status_t PlaylistFetcher::onResumeUntil(const sp<AMessage> &msg) { } mStopParams = params; - postMonitorQueue(); + onDownloadNext(); return OK; } @@ -610,31 +639,32 @@ void PlaylistFetcher::onMonitorQueue() { int32_t targetDurationSecs; int64_t targetDurationUs = kMinBufferedDurationUs; if (mPlaylist != NULL) { - CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); + if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( + "target-duration", &targetDurationSecs)) { + ALOGE("Playlist is missing required EXT-X-TARGETDURATION tag"); + notifyError(ERROR_MALFORMED); + return; + } targetDurationUs = targetDurationSecs * 1000000ll; } - // buffer at least 3 times the target duration, or up to 10 seconds - int64_t durationToBufferUs = targetDurationUs * 3; - if (durationToBufferUs > kMinBufferedDurationUs) { - durationToBufferUs = kMinBufferedDurationUs; - } - int64_t bufferedDurationUs = 0ll; - status_t finalResult = NOT_ENOUGH_DATA; + status_t finalResult = OK; if (mStreamTypeMask == LiveSession::STREAMTYPE_SUBTITLES) { sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(LiveSession::STREAMTYPE_SUBTITLES); bufferedDurationUs = packetSource->getBufferedDurationUs(&finalResult); - finalResult = OK; } else { - // Use max stream duration to prevent us from waiting on a non-existent stream; - // when we cannot make out from the manifest what streams are included in a playlist - // we might assume extra streams. + // Use min stream duration, but ignore streams that never have any packet + // enqueued to prevent us from waiting on a non-existent stream; + // when we cannot make out from the manifest what streams are included in + // a playlist we might assume extra streams. + bufferedDurationUs = -1ll; for (size_t i = 0; i < mPacketSources.size(); ++i) { - if ((mStreamTypeMask & mPacketSources.keyAt(i)) == 0) { + if ((mStreamTypeMask & mPacketSources.keyAt(i)) == 0 + || mPacketSources[i]->getLatestEnqueuedMeta() == NULL) { continue; } @@ -642,46 +672,36 @@ void PlaylistFetcher::onMonitorQueue() { mPacketSources.valueAt(i)->getBufferedDurationUs(&finalResult); ALOGV("buffered %" PRId64 " for stream %d", bufferedStreamDurationUs, mPacketSources.keyAt(i)); - if (bufferedStreamDurationUs > bufferedDurationUs) { + if (bufferedDurationUs == -1ll + || bufferedStreamDurationUs < bufferedDurationUs) { bufferedDurationUs = bufferedStreamDurationUs; } } - } - downloadMore = (bufferedDurationUs < durationToBufferUs); - - // signal start if buffered up at least the target size - if (!mPrepared && bufferedDurationUs > targetDurationUs && downloadMore) { - mPrepared = true; - - ALOGV("prepared, buffered=%" PRId64 " > %" PRId64 "", - bufferedDurationUs, targetDurationUs); - sp<AMessage> msg = mNotify->dup(); - msg->setInt32("what", kWhatTemporarilyDoneFetching); - msg->post(); + if (bufferedDurationUs == -1ll) { + bufferedDurationUs = 0ll; + } } - if (finalResult == OK && downloadMore) { + if (finalResult == OK && bufferedDurationUs < kMinBufferedDurationUs) { ALOGV("monitoring, buffered=%" PRId64 " < %" PRId64 "", - bufferedDurationUs, durationToBufferUs); + bufferedDurationUs, kMinBufferedDurationUs); // delay the next download slightly; hopefully this gives other concurrent fetchers // a better chance to run. // onDownloadNext(); - sp<AMessage> msg = new AMessage(kWhatDownloadNext, id()); + sp<AMessage> msg = new AMessage(kWhatDownloadNext, this); msg->setInt32("generation", mMonitorQueueGeneration); msg->post(1000l); } else { - // Nothing to do yet, try again in a second. - - sp<AMessage> msg = mNotify->dup(); - msg->setInt32("what", kWhatTemporarilyDoneFetching); - msg->post(); - - int64_t delayUs = mPrepared ? kMaxMonitorDelayUs : targetDurationUs / 2; + // We'd like to maintain buffering above durationToBufferUs, so try + // again when buffer just about to go below durationToBufferUs + // (or after targetDurationUs / 2, whichever is smaller). + int64_t delayUs = bufferedDurationUs - kMinBufferedDurationUs + 1000000ll; + if (delayUs > targetDurationUs / 2) { + delayUs = targetDurationUs / 2; + } 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 - postMonitorQueue(delayUs, mPrepared ? targetDurationUs * 2 : 0); + delayUs, bufferedDurationUs, kMinBufferedDurationUs); + postMonitorQueue(delayUs); } } @@ -700,8 +720,7 @@ status_t PlaylistFetcher::refreshPlaylist() { mRefreshState = (RefreshState)(mRefreshState + 1); } } else { - ALOGE("failed to load playlist at url '%s'", mURI.c_str()); - notifyError(ERROR_IO); + ALOGE("failed to load playlist at url '%s'", uriDebugString(mURI).c_str()); return ERROR_IO; } } else { @@ -724,26 +743,25 @@ bool PlaylistFetcher::bufferStartsWithTsSyncByte(const sp<ABuffer>& buffer) { } void PlaylistFetcher::onDownloadNext() { - if (refreshPlaylist() != OK) { - return; - } - - int32_t firstSeqNumberInPlaylist; - if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( - "media-sequence", &firstSeqNumberInPlaylist)) { - firstSeqNumberInPlaylist = 0; - } - + status_t err = refreshPlaylist(); + int32_t firstSeqNumberInPlaylist = 0; + int32_t lastSeqNumberInPlaylist = 0; bool discontinuity = false; - const int32_t lastSeqNumberInPlaylist = - firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; + if (mPlaylist != NULL) { + if (mPlaylist->meta() != NULL) { + mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist); + } + + lastSeqNumberInPlaylist = + firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; - if (mDiscontinuitySeq < 0) { - mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq(); + if (mDiscontinuitySeq < 0) { + mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq(); + } } - if (mSeqNumber < 0) { + if (mPlaylist != NULL && mSeqNumber < 0) { CHECK_GE(mStartTimeUs, 0ll); if (mSegmentStartTimeUs < 0) { @@ -754,6 +772,9 @@ void PlaylistFetcher::onDownloadNext() { mSeqNumber = firstSeqNumberInPlaylist; } } else { + // When seeking mSegmentStartTimeUs is unavailable (< 0), we + // use mStartTimeUs (client supplied timestamp) to determine both start segment + // and relative position inside a segment mSeqNumber = getSeqNumberForTime(mStartTimeUs); mStartTimeUs -= getSegmentStartTimeUs(mSeqNumber); } @@ -762,6 +783,10 @@ void PlaylistFetcher::onDownloadNext() { mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); } else { + // When adapting or track switching, mSegmentStartTimeUs (relative + // to media time 0) is used to determine the start segment; mStartTimeUs (absolute + // timestamps coming from the media container) is used to determine the position + // inside a segments. mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs); if (mAdaptive) { // avoid double fetch/decode @@ -785,19 +810,26 @@ void PlaylistFetcher::onDownloadNext() { } } + // if mPlaylist is NULL then err must be non-OK; but the other way around might not be true if (mSeqNumber < firstSeqNumberInPlaylist - || mSeqNumber > lastSeqNumberInPlaylist) { - if (!mPlaylist->isComplete() && mNumRetries < kMaxNumRetries) { + || mSeqNumber > lastSeqNumberInPlaylist + || err != OK) { + if ((err != OK || !mPlaylist->isComplete()) && mNumRetries < kMaxNumRetries) { ++mNumRetries; - if (mSeqNumber > lastSeqNumberInPlaylist) { + if (mSeqNumber > lastSeqNumberInPlaylist || err != OK) { + // make sure we reach this retry logic on refresh failures + // by adding an err != OK clause to all enclosing if's. + // refresh in increasing fraction (1/2, 1/3, ...) of the // playlist's target duration or 3 seconds, whichever is less - int32_t targetDurationSecs; - CHECK(mPlaylist->meta()->findInt32( - "target-duration", &targetDurationSecs)); - int64_t delayUs = mPlaylist->size() * targetDurationSecs * - 1000000ll / (1 + mNumRetries); + int64_t delayUs = kMaxMonitorDelayUs; + if (mPlaylist != NULL && mPlaylist->meta() != NULL) { + int32_t targetDurationSecs; + CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); + delayUs = mPlaylist->size() * targetDurationSecs * + 1000000ll / (1 + mNumRetries); + } if (delayUs > kMaxMonitorDelayUs) { delayUs = kMaxMonitorDelayUs; } @@ -809,13 +841,30 @@ void PlaylistFetcher::onDownloadNext() { return; } - // we've missed the boat, let's start from the lowest sequence + if (err != OK) { + notifyError(err); + return; + } + + // we've missed the boat, let's start 3 segments prior to the latest sequence // number available and signal a discontinuity. ALOGI("We've missed the boat, restarting playback." " mStartup=%d, was looking for %d in %d-%d", mStartup, mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); + if (mStopParams != NULL) { + // we should have kept on fetching until we hit the boundaries in mStopParams, + // but since the segments we are supposed to fetch have already rolled off + // the playlist, i.e. we have already missed the boat, we inevitably have to + // skip. + for (size_t i = 0; i < mPacketSources.size(); i++) { + sp<ABuffer> formatChange = mSession->createFormatChangeBuffer(); + mPacketSources.valueAt(i)->queueAccessUnit(formatChange); + } + stopAsync(/* clear = */ false); + return; + } mSeqNumber = lastSeqNumberInPlaylist - 3; if (mSeqNumber < firstSeqNumberInPlaylist) { mSeqNumber = firstSeqNumberInPlaylist; @@ -843,11 +892,22 @@ void PlaylistFetcher::onDownloadNext() { &uri, &itemMeta)); + CHECK(itemMeta->findInt32("discontinuity-sequence", &mDiscontinuitySeq)); + int32_t val; if (itemMeta->findInt32("discontinuity", &val) && val != 0) { - mDiscontinuitySeq++; + discontinuity = true; + } else if (mLastDiscontinuitySeq >= 0 + && mDiscontinuitySeq != mLastDiscontinuitySeq) { + // Seek jumped to a new discontinuity sequence. We need to signal + // a format change to decoder. Decoder needs to shutdown and be + // created again if seamless format change is unsupported. + ALOGV("saw discontinuity: mStartup %d, mLastDiscontinuitySeq %d, " + "mDiscontinuitySeq %d, mStartTimeUs %lld", + mStartup, mLastDiscontinuitySeq, mDiscontinuitySeq, (long long)mStartTimeUs); discontinuity = true; } + mLastDiscontinuitySeq = -1; int64_t range_offset, range_length; if (!itemMeta->findInt64("range-offset", &range_offset) @@ -876,13 +936,68 @@ void PlaylistFetcher::onDownloadNext() { } } + if ((mStartup && !mTimeChangeSignaled) || discontinuity) { + // We need to signal a time discontinuity to ATSParser on the + // first segment after start, or on a discontinuity segment. + // Setting mNextPTSTimeUs informs extractAndQueueAccessUnitsXX() + // to send the time discontinuity. + if (mPlaylist->isComplete() || mPlaylist->isEvent()) { + // If this was a live event this made no sense since + // we don't have access to all the segment before the current + // one. + mNextPTSTimeUs = getSegmentStartTimeUs(mSeqNumber); + } + + // Setting mTimeChangeSignaled to true, so that if start time + // searching goes into 2nd segment (without a discontinuity), + // we don't reset time again. It causes corruption when pending + // data in ATSParser is cleared. + mTimeChangeSignaled = true; + } + + if (discontinuity) { + ALOGI("queueing discontinuity (explicit=%d)", discontinuity); + + // Signal a format discontinuity to ATSParser to clear partial data + // from previous streams. Not doing this causes bitstream corruption. + mTSParser->signalDiscontinuity( + ATSParser::DISCONTINUITY_FORMATCHANGE, NULL /* extra */); + + queueDiscontinuity( + ATSParser::DISCONTINUITY_FORMATCHANGE, + NULL /* extra */); + + if (mStartup && mStartTimeUsRelative && mFirstPTSValid) { + // This means we guessed mStartTimeUs to be in the previous + // segment (likely very close to the end), but either video or + // audio has not found start by the end of that segment. + // + // If this new segment is not a discontinuity, keep searching. + // + // If this new segment even got a discontinuity marker, just + // set mStartTimeUs=0, and take all samples from now on. + mStartTimeUs = 0; + mFirstPTSValid = false; + } + } + // block-wise download - bool startup = mStartup; ssize_t bytesRead; do { + int64_t startUs = ALooper::GetNowUs(); + bytesRead = mSession->fetchFile( uri.c_str(), &buffer, range_offset, range_length, kDownloadBlockSize, &source); + // add sample for bandwidth estimation (excluding subtitles) + if (bytesRead > 0 + && (mStreamTypeMask + & (LiveSession::STREAMTYPE_AUDIO + | LiveSession::STREAMTYPE_VIDEO))) { + int64_t delayUs = ALooper::GetNowUs() - startUs; + mSession->addBandwidthMeasurement(bytesRead, delayUs); + } + if (bytesRead < 0) { status_t err = bytesRead; ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str()); @@ -907,29 +1022,6 @@ void PlaylistFetcher::onDownloadNext() { return; } - if (startup || discontinuity) { - // Signal discontinuity. - - if (mPlaylist->isComplete() || mPlaylist->isEvent()) { - // If this was a live event this made no sense since - // we don't have access to all the segment before the current - // one. - mNextPTSTimeUs = getSegmentStartTimeUs(mSeqNumber); - } - - if (discontinuity) { - ALOGI("queueing discontinuity (explicit=%d)", discontinuity); - - queueDiscontinuity( - ATSParser::DISCONTINUITY_FORMATCHANGE, - NULL /* extra */); - - discontinuity = false; - } - - startup = false; - } - err = OK; if (bufferStartsWithTsSyncByte(buffer)) { // Incremental extraction is only supported for MPEG2 transport streams. @@ -949,6 +1041,10 @@ void PlaylistFetcher::onDownloadNext() { if (err == -EAGAIN) { // starting sequence number too low/high mTSParser.clear(); + for (size_t i = 0; i < mPacketSources.size(); i++) { + sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i); + packetSource->clear(); + } postMonitorQueue(); return; } else if (err == ERROR_OUT_OF_RANGE) { @@ -960,11 +1056,11 @@ void PlaylistFetcher::onDownloadNext() { return; } - } while (bytesRead != 0); + } while (bytesRead != 0 && !mStopping); if (bufferStartsWithTsSyncByte(buffer)) { - // If we still don't see a stream after fetching a full ts segment mark it as - // nonexistent. + // If we don't see a stream in the program table after fetching a full ts segment + // mark it as nonexistent. const size_t kNumTypes = ATSParser::NUM_SOURCE_TYPES; ATSParser::SourceType srcTypes[kNumTypes] = { ATSParser::VIDEO, ATSParser::AUDIO }; @@ -979,7 +1075,7 @@ void PlaylistFetcher::onDownloadNext() { static_cast<AnotherPacketSource *>( mTSParser->getSource(srcType).get()); - if (source == NULL) { + if (!mTSParser->hasSource(srcType)) { ALOGW("MPEG2 Transport stream does not contain %s data.", srcType == ATSParser::VIDEO ? "video" : "audio"); @@ -996,7 +1092,7 @@ void PlaylistFetcher::onDownloadNext() { return; } - status_t err = OK; + err = OK; if (tsBuffer != NULL) { AString method; CHECK(buffer->meta()->findString("cipher-method", &method)); @@ -1137,6 +1233,11 @@ const sp<ABuffer> &PlaylistFetcher::setAccessUnitProperties( accessUnit->meta()->setInt32("discard", discard); } + int32_t targetDurationSecs; + if (mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)) { + accessUnit->meta()->setInt32("targetDuration", targetDurationSecs); + } + accessUnit->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq); accessUnit->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber)); return accessUnit; @@ -1157,9 +1258,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu mTSParser->signalDiscontinuity( ATSParser::DISCONTINUITY_TIME, extra); - mAbsoluteTimeAnchorUs = mNextPTSTimeUs; mNextPTSTimeUs = -1ll; - mFirstPTSValid = false; } size_t offset = 0; @@ -1212,12 +1311,17 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu continue; } - int64_t timeUs; + const char *mime; + sp<MetaData> format = source->getFormat(); + bool isAvc = format != NULL && format->findCString(kKeyMIMEType, &mime) + && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); + sp<ABuffer> accessUnit; status_t finalResult; while (source->hasBufferAvailable(&finalResult) && source->dequeueAccessUnit(&accessUnit) == OK) { + int64_t timeUs; CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); if (mStartup) { @@ -1232,30 +1336,30 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu } } - if (timeUs < mStartTimeUs) { + if (timeUs < mStartTimeUs || (isAvc && !mIDRFound)) { // buffer up to the closest preceding IDR frame ALOGV("timeUs %" PRId64 " us < mStartTimeUs %" PRId64 " us", timeUs, mStartTimeUs); - const char *mime; - sp<MetaData> format = source->getFormat(); - bool isAvc = false; - if (format != NULL && format->findCString(kKeyMIMEType, &mime) - && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { - isAvc = true; - } - if (isAvc && IsIDR(accessUnit)) { - mVideoBuffer->clear(); - } if (isAvc) { - mVideoBuffer->queueAccessUnit(accessUnit); + if (IsIDR(accessUnit)) { + mVideoBuffer->clear(); + mIDRFound = true; + } + if (mIDRFound) { + mVideoBuffer->queueAccessUnit(accessUnit); + } } continue; } } - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - if (mStartTimeUsNotify != NULL && timeUs > mStartTimeUs) { + if (mStartTimeUsNotify != NULL) { + int32_t firstSeqNumberInPlaylist; + if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( + "media-sequence", &firstSeqNumberInPlaylist)) { + firstSeqNumberInPlaylist = 0; + } int32_t targetDurationSecs; CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); @@ -1266,6 +1370,8 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu // mStartTimeUs. // mSegmentStartTimeUs >= 0 // mSegmentStartTimeUs is non-negative when adapting or switching tracks + // mSeqNumber > firstSeqNumberInPlaylist + // don't decrement mSeqNumber if it already points to the 1st segment // timeUs - mStartTimeUs > targetDurationUs: // This and the 2 above conditions should only happen when adapting in a live // stream; the old fetcher has already fetched to mStartTimeUs; the new fetcher @@ -1275,6 +1381,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu // stop as early as possible. The definition of being "too far ahead" is // arbitrary; here we use targetDurationUs as threshold. if (mStartup && mSegmentStartTimeUs >= 0 + && mSeqNumber > firstSeqNumberInPlaylist && timeUs - mStartTimeUs > targetDurationUs) { // we just guessed a starting timestamp that is too high when adapting in a // live stream; re-adjust based on the actual timestamp extracted from the @@ -1416,8 +1523,6 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( } if (mNextPTSTimeUs >= 0ll) { - mFirstPTSValid = false; - mAbsoluteTimeAnchorUs = mNextPTSTimeUs; mNextPTSTimeUs = -1ll; } @@ -1495,7 +1600,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( CHECK_EQ(bits.getBits(12), 0xfffu); bits.skipBits(3); // ID, layer - bool protection_absent = bits.getBits(1) != 0; + bool protection_absent __unused = bits.getBits(1) != 0; unsigned profile = bits.getBits(2); CHECK_NE(profile, 3u); @@ -1518,7 +1623,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( CHECK(packetSource->getFormat()->findInt32(kKeySampleRate, &sampleRate)); int64_t timeUs = (PTS * 100ll) / 9ll; - if (!mFirstPTSValid) { + if (mStartup && !mFirstPTSValid) { mFirstPTSValid = true; mFirstTimeUs = timeUs; } @@ -1587,6 +1692,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( mStartTimeUsNotify->setInt32("streamMask", LiveSession::STREAMTYPE_AUDIO); mStartTimeUsNotify->post(); mStartTimeUsNotify.clear(); + mStartup = false; } } @@ -1635,33 +1741,4 @@ void PlaylistFetcher::updateDuration() { msg->post(); } -int64_t PlaylistFetcher::resumeThreshold(const sp<AMessage> &msg) { - int64_t durationUs, threshold; - if (msg->findInt64("durationUs", &durationUs)) { - return kNumSkipFrames * durationUs; - } - - sp<RefBase> obj; - msg->findObject("format", &obj); - MetaData *format = static_cast<MetaData *>(obj.get()); - - const char *mime; - CHECK(format->findCString(kKeyMIMEType, &mime)); - bool audio = !strncasecmp(mime, "audio/", 6); - if (audio) { - // Assumes 1000 samples per frame. - int32_t sampleRate; - CHECK(format->findInt32(kKeySampleRate, &sampleRate)); - return kNumSkipFrames /* frames */ * 1000 /* samples */ - * (1000000 / sampleRate) /* sample duration (us) */; - } else { - int32_t frameRate; - if (format->findInt32(kKeyFrameRate, &frameRate) && frameRate > 0) { - return kNumSkipFrames * (1000000 / frameRate); - } - } - - return 500000ll; -} - } // namespace android diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h index 78c358f..b82e50d 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.h +++ b/media/libstagefright/httplive/PlaylistFetcher.h @@ -31,16 +31,19 @@ struct DataSource; struct HTTPBase; struct LiveDataSource; struct M3UParser; -struct String8; +class String8; struct PlaylistFetcher : public AHandler { + static const int64_t kMinBufferedDurationUs; + static const int32_t kDownloadBlockSize; + static const int64_t kFetcherResumeThreshold; + enum { kWhatStarted, kWhatPaused, kWhatStopped, kWhatError, kWhatDurationUpdate, - kWhatTemporarilyDoneFetching, kWhatPrepared, kWhatPreparationFailed, kWhatStartedAt, @@ -64,7 +67,7 @@ struct PlaylistFetcher : public AHandler { int32_t startDiscontinuitySeq = 0, bool adaptive = false); - void pauseAsync(); + void pauseAsync(bool immediate = false); void stopAsync(bool clear = true); @@ -92,9 +95,7 @@ private: kWhatDownloadNext = 'dlnx', }; - static const int64_t kMinBufferedDurationUs; static const int64_t kMaxMonitorDelayUs; - static const int32_t kDownloadBlockSize; static const int32_t kNumSkipFrames; static bool bufferStartsWithTsSyncByte(const sp<ABuffer>& buffer); @@ -115,7 +116,7 @@ private: // adapting or switching tracks. int64_t mSegmentStartTimeUs; - ssize_t mDiscontinuitySeq; + int32_t mDiscontinuitySeq; bool mStartTimeUsRelative; sp<AMessage> mStopParams; // message containing the latest timestamps we should fetch. @@ -129,13 +130,20 @@ private: int32_t mSeqNumber; int32_t mNumRetries; bool mStartup; + bool mIDRFound; bool mAdaptive; bool mPrepared; + bool mTimeChangeSignaled; int64_t mNextPTSTimeUs; int32_t mMonitorQueueGeneration; const int32_t mSubtitleGeneration; + int32_t mLastDiscontinuitySeq; + + Mutex mStoppingLock; + bool mStopping; + enum RefreshState { INITIAL_MINIMUM_RELOAD_DELAY, FIRST_UNCHANGED_RELOAD_ATTEMPT, @@ -151,7 +159,6 @@ private: bool mFirstPTSValid; uint64_t mFirstPTS; int64_t mFirstTimeUs; - int64_t mAbsoluteTimeAnchorUs; sp<AnotherPacketSource> mVideoBuffer; // Stores the initialization vector to decrypt the next block of cipher text, which can @@ -174,6 +181,7 @@ private: void postMonitorQueue(int64_t delayUs = 0, int64_t minDelayUs = 0); void cancelMonitorQueue(); + void setStopping(bool stopping); int64_t delayUsToRefreshPlaylist() const; status_t refreshPlaylist(); @@ -211,10 +219,6 @@ private: void updateDuration(); - // Before resuming a fetcher in onResume, check the remaining duration is longer than that - // returned by resumeThreshold. - int64_t resumeThreshold(const sp<AMessage> &msg); - DISALLOW_EVIL_CONSTRUCTORS(PlaylistFetcher); }; diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index 7f221a0..d9491d6 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -630,7 +630,10 @@ void ID3::Iterator::findFrame() { | (mParent.mData[mOffset + 4] << 8) | mParent.mData[mOffset + 5]; - mFrameSize += 6; + if (mFrameSize == 0) { + return; + } + mFrameSize += 6; // add tag id and size field if (mOffset + mFrameSize > mParent.mSize) { ALOGV("partial frame at offset %zu (size = %zu, bytes-remaining = %zu)", @@ -671,7 +674,11 @@ void ID3::Iterator::findFrame() { baseSize = U32_AT(&mParent.mData[mOffset + 4]); } - mFrameSize = 10 + baseSize; + if (baseSize == 0) { + return; + } + + mFrameSize = 10 + baseSize; // add tag id, size field and flags if (mOffset + mFrameSize > mParent.mSize) { ALOGV("partial frame at offset %zu (size = %zu, bytes-remaining = %zu)", @@ -793,8 +800,8 @@ ID3::getAlbumArt(size_t *length, String8 *mime) const { mime->setTo((const char *)&data[1]); size_t mimeLen = strlen((const char *)&data[1]) + 1; - uint8_t picType = data[1 + mimeLen]; #if 0 + uint8_t picType = data[1 + mimeLen]; if (picType != 0x03) { // Front Cover Art it.next(); diff --git a/media/libstagefright/include/AACEncoder.h b/media/libstagefright/include/AACEncoder.h index 3d5fc60..52beb0e 100644 --- a/media/libstagefright/include/AACEncoder.h +++ b/media/libstagefright/include/AACEncoder.h @@ -25,7 +25,7 @@ struct VO_MEM_OPERATOR; namespace android { -struct MediaBufferGroup; +class MediaBufferGroup; class AACEncoder: public MediaSource { public: diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h index 1c3cd5e..0c66e27 100644 --- a/media/libstagefright/include/HTTPBase.h +++ b/media/libstagefright/include/HTTPBase.h @@ -48,6 +48,8 @@ struct HTTPBase : public DataSource { virtual status_t setBandwidthStatCollectFreq(int32_t freqMs); + virtual void setBandwidthHistorySize(size_t numHistoryItems); + static void RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag); static void UnRegisterSocketUserTag(int sockfd); @@ -55,7 +57,7 @@ struct HTTPBase : public DataSource { static void UnRegisterSocketUserMark(int sockfd); protected: - void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); + virtual void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); private: struct BandwidthEntry { @@ -69,6 +71,7 @@ private: size_t mNumBandwidthHistoryItems; int64_t mTotalTransferTimeUs; size_t mTotalTransferBytes; + size_t mMaxBandwidthHistoryItems; enum { kMinBandwidthCollectFreqMs = 1000, // 1 second diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h index e83f3ef..c2c4a6d 100644 --- a/media/libstagefright/include/ID3.h +++ b/media/libstagefright/include/ID3.h @@ -22,8 +22,8 @@ namespace android { -struct DataSource; -struct String8; +class DataSource; +class String8; struct ID3 { enum Version { diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h index c5e86a6..db1187d 100644 --- a/media/libstagefright/include/MPEG2TSExtractor.h +++ b/media/libstagefright/include/MPEG2TSExtractor.h @@ -28,7 +28,7 @@ namespace android { struct AMessage; struct AnotherPacketSource; struct ATSParser; -struct DataSource; +class DataSource; struct MPEG2TSSource; struct String8; diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index 1fe6fcf..8c16251 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -83,6 +83,8 @@ private: Vector<SidxEntry> mSidxEntries; off64_t mMoofOffset; + bool mMoofFound; + bool mMdatFound; Vector<PsshInfo> mPssh; diff --git a/media/libstagefright/include/MidiExtractor.h b/media/libstagefright/include/MidiExtractor.h new file mode 100644 index 0000000..9a2abc0 --- /dev/null +++ b/media/libstagefright/include/MidiExtractor.h @@ -0,0 +1,95 @@ +/* + * 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 MIDI_EXTRACTOR_H_ +#define MIDI_EXTRACTOR_H_ + +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaBufferGroup.h> +#include <media/MidiIoWrapper.h> +#include <utils/String8.h> +#include <libsonivox/eas.h> + +namespace android { + +class MidiEngine : public RefBase { +public: + MidiEngine(const sp<DataSource> &dataSource, + const sp<MetaData> &fileMetadata, + const sp<MetaData> &trackMetadata); + ~MidiEngine(); + + status_t initCheck(); + + status_t allocateBuffers(); + status_t releaseBuffers(); + status_t seekTo(int64_t positionUs); + MediaBuffer* readBuffer(); +private: + sp<MidiIoWrapper> mIoWrapper; + MediaBufferGroup *mGroup; + EAS_DATA_HANDLE mEasData; + EAS_HANDLE mEasHandle; + const S_EAS_LIB_CONFIG* mEasConfig; + bool mIsInitialized; +}; + +class MidiExtractor : public MediaExtractor { + +public: + // Extractor assumes ownership of source + MidiExtractor(const sp<DataSource> &source); + + virtual size_t countTracks(); + virtual sp<MediaSource> getTrack(size_t index); + virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); + + virtual sp<MetaData> getMetaData(); + +protected: + virtual ~MidiExtractor(); + +private: + sp<DataSource> mDataSource; + status_t mInitCheck; + sp<MetaData> mFileMetadata; + + // There is only one track + sp<MetaData> mTrackMetadata; + + sp<MidiEngine> mEngine; + + EAS_DATA_HANDLE mEasData; + EAS_HANDLE mEasHandle; + EAS_PCM* mAudioBuffer; + EAS_I32 mPlayTime; + EAS_I32 mDuration; + EAS_STATE mState; + EAS_FILE mFileLocator; + + MidiExtractor(const MidiExtractor &); + MidiExtractor &operator=(const MidiExtractor &); + +}; + +bool SniffMidi(const sp<DataSource> &source, String8 *mimeType, + float *confidence, sp<AMessage> *); + +} // namespace android + +#endif // MIDI_EXTRACTOR_H_ diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h index 24d431c..104dcfc 100644 --- a/media/libstagefright/include/OMXNodeInstance.h +++ b/media/libstagefright/include/OMXNodeInstance.h @@ -31,7 +31,7 @@ struct GraphicBufferSource; struct OMXNodeInstance { OMXNodeInstance( - OMX *owner, const sp<IOMXObserver> &observer); + OMX *owner, const sp<IOMXObserver> &observer, const char *name); void setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle); @@ -149,6 +149,18 @@ private: KeyedVector<OMX_BUFFERHEADERTYPE *, OMX::buffer_id> mBufferHeaderToBufferID; #endif + // For debug support + char *mName; + int DEBUG; + size_t mNumPortBuffers[2]; // modified under mLock, read outside for debug + Mutex mDebugLock; + // following are modified and read under mDebugLock + int DEBUG_BUMP; + SortedVector<OMX_BUFFERHEADERTYPE *> mInputBuffersWithCodec, mOutputBuffersWithCodec; + size_t mDebugLevelBumpPendingBuffers[2]; + void bumpDebugLevel_l(size_t numInputBuffers, size_t numOutputBuffers); + void unbumpDebugLevel_l(size_t portIndex); + ~OMXNodeInstance(); void addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id); @@ -186,6 +198,10 @@ private: OMX_U32 portIndex, OMX_BOOL enable, OMX_BOOL useGraphicBuffer, OMX_BOOL *usingGraphicBufferInMeta); + status_t emptyBuffer_l( + OMX_BUFFERHEADERTYPE *header, + OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr); + sp<GraphicBufferSource> getGraphicBufferSource(); void setGraphicBufferSource(const sp<GraphicBufferSource>& bufferSource); diff --git a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h index 9e97ebd..4529007 100644 --- a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h +++ b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h @@ -61,9 +61,10 @@ protected: void initPorts(OMX_U32 numInputBuffers, OMX_U32 inputBufferSize, OMX_U32 numOutputBuffers, - const char *mimeType); + const char *mimeType, + OMX_U32 minCompressionRatio = 1u); - virtual void updatePortDefinitions(bool updateCrop = true); + virtual void updatePortDefinitions(bool updateCrop = true, bool updateInputSize = false); uint32_t outputBufferWidth(); uint32_t outputBufferHeight(); @@ -99,6 +100,9 @@ protected: } mOutputPortSettingsChange; private: + uint32_t mMinInputBufferSize; + uint32_t mMinCompressionRatio; + const char *mComponentRole; OMX_VIDEO_CODINGTYPE mCodingType; const CodecProfileLevel *mProfileLevels; diff --git a/media/libstagefright/include/SoftVideoEncoderOMXComponent.h b/media/libstagefright/include/SoftVideoEncoderOMXComponent.h index b3b810d..b43635d 100644 --- a/media/libstagefright/include/SoftVideoEncoderOMXComponent.h +++ b/media/libstagefright/include/SoftVideoEncoderOMXComponent.h @@ -18,6 +18,8 @@ #define SOFT_VIDEO_ENCODER_OMX_COMPONENT_H_ +#include <media/IOMX.h> + #include "SimpleSoftOMXComponent.h" #include <system/window.h> @@ -28,11 +30,26 @@ namespace android { struct SoftVideoEncoderOMXComponent : public SimpleSoftOMXComponent { SoftVideoEncoderOMXComponent( const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, + int32_t width, + int32_t height, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component); + virtual OMX_ERRORTYPE internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR param); + virtual OMX_ERRORTYPE internalGetParameter(OMX_INDEXTYPE index, OMX_PTR params); + protected: + void initPorts( + OMX_U32 numInputBuffers, OMX_U32 numOutputBuffers, OMX_U32 outputBufferSize, + const char *mime, OMX_U32 minCompressionRatio = 1); + + static void setRawVideoSize(OMX_PARAM_PORTDEFINITIONTYPE *def); + static void ConvertFlexYUVToPlanar( uint8_t *dst, size_t dstStride, size_t dstVStride, struct android_ycbcr *ycbcr, int32_t width, int32_t height); @@ -56,9 +73,30 @@ protected: kOutputPortIndex = 1, }; + bool mInputDataIsMeta; + int32_t mWidth; // width of the input frames + int32_t mHeight; // height of the input frames + uint32_t mBitrate; // target bitrate set for the encoder, in bits per second + uint32_t mFramerate; // target framerate set for the encoder, in Q16 format + OMX_COLOR_FORMATTYPE mColorFormat; // Color format for the input port + private: + void updatePortParams(); + OMX_ERRORTYPE internalSetPortParams(const OMX_PARAM_PORTDEFINITIONTYPE* port); + + static const uint32_t kInputBufferAlignment = 1; + static const uint32_t kOutputBufferAlignment = 2; + mutable const hw_module_t *mGrallocModule; + uint32_t mMinOutputBufferSize; + uint32_t mMinCompressionRatio; + + const char *mComponentRole; + OMX_VIDEO_CODINGTYPE mCodingType; + const CodecProfileLevel *mProfileLevels; + size_t mNumProfileLevels; + DISALLOW_EVIL_CONSTRUCTORS(SoftVideoEncoderOMXComponent); }; diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h index d517320..dafa07e 100644 --- a/media/libstagefright/include/avc_utils.h +++ b/media/libstagefright/include/avc_utils.h @@ -23,7 +23,7 @@ namespace android { -struct ABitReader; +class ABitReader; enum { kAVCProfileBaseline = 0x42, @@ -36,6 +36,11 @@ enum { kAVCProfileCAVLC444Intra = 0x2c }; +struct NALPosition { + size_t nalOffset; + size_t nalSize; +}; + // Optionally returns sample aspect ratio as well. void FindAVCDimensions( const sp<ABuffer> &seqParamSet, @@ -49,7 +54,7 @@ status_t getNextNALUnit( const uint8_t **nalStart, size_t *nalSize, bool startCodeFollows = false); -struct MetaData; +class MetaData; sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit); bool IsIDR(const sp<ABuffer> &accessUnit); diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index 2587ec7..0712bf0 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -413,16 +413,16 @@ void BlockIterator::seek( const mkvparser::CuePoint* pCP; mkvparser::Tracks const *pTracks = pSegment->GetTracks(); - unsigned long int trackCount = pTracks->GetTracksCount(); while (!pCues->DoneParsing()) { pCues->LoadCuePoint(); pCP = pCues->GetLast(); CHECK(pCP); + size_t trackCount = mExtractor->mTracks.size(); for (size_t index = 0; index < trackCount; ++index) { - const mkvparser::Track *pTrack = pTracks->GetTrackByIndex(index); + MatroskaExtractor::TrackInfo& track = mExtractor->mTracks.editItemAt(index); + const mkvparser::Track *pTrack = pTracks->GetTrackByNumber(track.mTrackNum); if (pTrack && pTrack->GetType() == 1 && pCP->Find(pTrack)) { // VIDEO_TRACK - MatroskaExtractor::TrackInfo& track = mExtractor->mTracks.editItemAt(index); track.mCuePoints.push_back(pCP); } } @@ -434,12 +434,13 @@ void BlockIterator::seek( } const mkvparser::CuePoint::TrackPosition *pTP = NULL; - const mkvparser::Track *thisTrack = pTracks->GetTrackByIndex(mIndex); + const mkvparser::Track *thisTrack = pTracks->GetTrackByNumber(mTrackNum); if (thisTrack->GetType() == 1) { // video MatroskaExtractor::TrackInfo& track = mExtractor->mTracks.editItemAt(mIndex); pTP = track.find(seekTimeNs); } else { // The Cue index is built around video keyframes + unsigned long int trackCount = pTracks->GetTracksCount(); for (size_t index = 0; index < trackCount; ++index) { const mkvparser::Track *pTrack = pTracks->GetTrackByIndex(index); if (pTrack && pTrack->GetType() == 1 && pCues->Find(seekTimeNs, pTrack, pCP, pTP)) { @@ -499,17 +500,6 @@ static unsigned U24_AT(const uint8_t *ptr) { return ptr[0] << 16 | ptr[1] << 8 | ptr[2]; } -static size_t clz(uint8_t x) { - size_t numLeadingZeroes = 0; - - while (!(x & 0x80)) { - ++numLeadingZeroes; - x = x << 1; - } - - return numLeadingZeroes; -} - void MatroskaSource::clearPendingFrames() { while (!mPendingFrames.empty()) { MediaBuffer *frame = *mPendingFrames.begin(); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index eab7616..6786506 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -35,6 +35,7 @@ #include <media/stagefright/Utils.h> #include <media/IStreamSource.h> #include <utils/KeyedVector.h> +#include <utils/Vector.h> #include <inttypes.h> @@ -63,6 +64,7 @@ struct ATSParser::Program : public RefBase { void signalEOS(status_t finalResult); sp<MediaSource> getSource(SourceType type); + bool hasSource(SourceType type) const; int64_t convertPTSToTimestamp(uint64_t PTS); @@ -85,14 +87,22 @@ struct ATSParser::Program : public RefBase { } private: + struct StreamInfo { + unsigned mType; + unsigned mPID; + }; + ATSParser *mParser; unsigned mProgramNumber; unsigned mProgramMapPID; KeyedVector<unsigned, sp<Stream> > mStreams; bool mFirstPTSValid; uint64_t mFirstPTS; + int64_t mLastRecoveredPTS; status_t parseProgramMap(ABitReader *br); + int64_t recoverPTS(uint64_t PTS_33bit); + bool switchPIDs(const Vector<StreamInfo> &infos); DISALLOW_EVIL_CONSTRUCTORS(Program); }; @@ -119,6 +129,9 @@ struct ATSParser::Stream : public RefBase { sp<MediaSource> getSource(SourceType type); + bool isAudio() const; + bool isVideo() const; + protected: virtual ~Stream(); @@ -146,9 +159,6 @@ private: void extractAACFrames(const sp<ABuffer> &buffer); - bool isAudio() const; - bool isVideo() const; - DISALLOW_EVIL_CONSTRUCTORS(Stream); }; @@ -181,7 +191,8 @@ ATSParser::Program::Program( mProgramNumber(programNumber), mProgramMapPID(programMapPID), mFirstPTSValid(false), - mFirstPTS(0) { + mFirstPTS(0), + mLastRecoveredPTS(-1ll) { ALOGV("new program number %u", programNumber); } @@ -236,10 +247,71 @@ void ATSParser::Program::signalEOS(status_t finalResult) { } } -struct StreamInfo { - unsigned mType; - unsigned mPID; -}; +bool ATSParser::Program::switchPIDs(const Vector<StreamInfo> &infos) { + bool success = false; + + if (mStreams.size() == infos.size()) { + // build type->PIDs map for old and new mapping + size_t i; + KeyedVector<int32_t, Vector<int32_t> > oldType2PIDs, newType2PIDs; + for (i = 0; i < mStreams.size(); ++i) { + ssize_t index = oldType2PIDs.indexOfKey(mStreams[i]->type()); + if (index < 0) { + oldType2PIDs.add(mStreams[i]->type(), Vector<int32_t>()); + } + oldType2PIDs.editValueFor(mStreams[i]->type()).push_back(mStreams[i]->pid()); + } + for (i = 0; i < infos.size(); ++i) { + ssize_t index = newType2PIDs.indexOfKey(infos[i].mType); + if (index < 0) { + newType2PIDs.add(infos[i].mType, Vector<int32_t>()); + } + newType2PIDs.editValueFor(infos[i].mType).push_back(infos[i].mPID); + } + + // we can recover if the number of streams for each type hasn't changed + if (oldType2PIDs.size() == newType2PIDs.size()) { + success = true; + for (i = 0; i < oldType2PIDs.size(); ++i) { + // KeyedVector is sorted, we just compare key and size of each index + if (oldType2PIDs.keyAt(i) != newType2PIDs.keyAt(i) + || oldType2PIDs[i].size() != newType2PIDs[i].size()) { + success = false; + break; + } + } + } + + if (success) { + // save current streams to temp + KeyedVector<int32_t, sp<Stream> > temp; + for (i = 0; i < mStreams.size(); ++i) { + temp.add(mStreams.keyAt(i), mStreams.editValueAt(i)); + } + + mStreams.clear(); + for (i = 0; i < temp.size(); ++i) { + // The two checks below shouldn't happen, + // we already checked above the stream count matches + ssize_t index = newType2PIDs.indexOfKey(temp[i]->type()); + CHECK(index >= 0); + Vector<int32_t> &newPIDs = newType2PIDs.editValueAt(index); + CHECK(newPIDs.size() > 0); + + // get the next PID for temp[i]->type() in the new PID map + Vector<int32_t>::iterator it = newPIDs.begin(); + + // change the PID of the stream, and add it back + temp.editValueAt(i)->setPID(*it); + mStreams.add(temp[i]->pid(), temp.editValueAt(i)); + + // removed the used PID + newPIDs.erase(it); + } + } + } + return success; +} status_t ATSParser::Program::parseProgramMap(ABitReader *br) { unsigned table_id = br->getBits(8); @@ -368,39 +440,8 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { } #endif - // The only case we can recover from is if we have two streams - // and they switched PIDs. - - bool success = false; - - if (mStreams.size() == 2 && infos.size() == 2) { - const StreamInfo &info1 = infos.itemAt(0); - const StreamInfo &info2 = infos.itemAt(1); - - sp<Stream> s1 = mStreams.editValueAt(0); - sp<Stream> s2 = mStreams.editValueAt(1); - - bool caseA = - info1.mPID == s1->pid() && info1.mType == s2->type() - && info2.mPID == s2->pid() && info2.mType == s1->type(); - - bool caseB = - info1.mPID == s2->pid() && info1.mType == s1->type() - && info2.mPID == s1->pid() && info2.mType == s2->type(); - - if (caseA || caseB) { - unsigned pid1 = s1->pid(); - unsigned pid2 = s2->pid(); - s1->setPID(pid2); - s2->setPID(pid1); - - mStreams.clear(); - mStreams.add(s1->pid(), s1); - mStreams.add(s2->pid(), s2); - - success = true; - } - } + // we can recover if number of streams for each type remain the same + bool success = switchPIDs(infos); if (!success) { ALOGI("Stream PIDs changed and we cannot recover."); @@ -424,6 +465,32 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { return OK; } +int64_t ATSParser::Program::recoverPTS(uint64_t PTS_33bit) { + // We only have the lower 33-bit of the PTS. It could overflow within a + // reasonable amount of time. To handle the wrap-around, use fancy math + // to get an extended PTS that is within [-0xffffffff, 0xffffffff] + // of the latest recovered PTS. + if (mLastRecoveredPTS < 0ll) { + // Use the original 33bit number for 1st frame, the reason is that + // if 1st frame wraps to negative that's far away from 0, we could + // never start. Only start wrapping around from 2nd frame. + mLastRecoveredPTS = static_cast<int64_t>(PTS_33bit); + } else { + mLastRecoveredPTS = static_cast<int64_t>( + ((mLastRecoveredPTS - PTS_33bit + 0x100000000ll) + & 0xfffffffe00000000ull) | PTS_33bit); + // We start from 0, but recovered PTS could be slightly below 0. + // Clamp it to 0 as rest of the pipeline doesn't take negative pts. + // (eg. video is read first and starts at 0, but audio starts at 0xfffffff0) + if (mLastRecoveredPTS < 0ll) { + ALOGI("Clamping negative recovered PTS (%" PRId64 ") to 0", mLastRecoveredPTS); + mLastRecoveredPTS = 0ll; + } + } + + return mLastRecoveredPTS; +} + sp<MediaSource> ATSParser::Program::getSource(SourceType type) { size_t index = (type == AUDIO) ? 0 : 0; @@ -440,7 +507,22 @@ sp<MediaSource> ATSParser::Program::getSource(SourceType type) { return NULL; } +bool ATSParser::Program::hasSource(SourceType type) const { + for (size_t i = 0; i < mStreams.size(); ++i) { + const sp<Stream> &stream = mStreams.valueAt(i); + if (type == AUDIO && stream->isAudio()) { + return true; + } else if (type == VIDEO && stream->isVideo()) { + return true; + } + } + + return false; +} + int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) { + PTS = recoverPTS(PTS); + if (!(mParser->mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)) { if (!mFirstPTSValid) { mFirstPTSValid = true; @@ -665,7 +747,7 @@ void ATSParser::Stream::signalDiscontinuity( int64_t resumeAtMediaTimeUs = mProgram->convertPTSToTimestamp(resumeAtPTS); - extra->setInt64("resume-at-mediatimeUs", resumeAtMediaTimeUs); + extra->setInt64("resume-at-mediaTimeUs", resumeAtMediaTimeUs); } } @@ -1084,7 +1166,8 @@ status_t ATSParser::parsePID( if (payload_unit_start_indicator) { if (!section->isEmpty()) { - return ERROR_UNSUPPORTED; + ALOGW("parsePID encounters payload_unit_start_indicator when section is not empty"); + section->clear(); } unsigned skip = br->getBits(8); @@ -1278,6 +1361,17 @@ sp<MediaSource> ATSParser::getSource(SourceType type) { return NULL; } +bool ATSParser::hasSource(SourceType type) const { + for (size_t i = 0; i < mPrograms.size(); ++i) { + const sp<Program> &program = mPrograms.itemAt(i); + if (program->hasSource(type)) { + return true; + } + } + + return false; +} + bool ATSParser::PTSTimeDeltaEstablished() { if (mPrograms.isEmpty()) { return false; diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index 204934d..75d76dc 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -28,7 +28,7 @@ namespace android { -struct ABitReader; +class ABitReader; struct ABuffer; struct MediaSource; @@ -74,6 +74,7 @@ struct ATSParser : public RefBase { NUM_SOURCE_TYPES = 2 }; sp<MediaSource> getSource(SourceType type); + bool hasSource(SourceType type) const; bool PTSTimeDeltaEstablished(); diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index ed40bdd..79a9b04 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -48,7 +48,10 @@ AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta) } void AnotherPacketSource::setFormat(const sp<MetaData> &meta) { - CHECK(mFormat == NULL); + if (mFormat != NULL) { + // Only allowed to be set once. Requires explicit clear to reset. + return; + } mIsAudio = false; mIsVideo = false; @@ -91,13 +94,12 @@ sp<MetaData> AnotherPacketSource::getFormat() { while (it != mBuffers.end()) { sp<ABuffer> buffer = *it; int32_t discontinuity; - if (buffer->meta()->findInt32("discontinuity", &discontinuity)) { - break; - } - - sp<RefBase> object; - if (buffer->meta()->findObject("format", &object)) { - return mFormat = static_cast<MetaData*>(object.get()); + if (!buffer->meta()->findInt32("discontinuity", &discontinuity)) { + sp<RefBase> object; + if (buffer->meta()->findObject("format", &object)) { + setFormat(static_cast<MetaData*>(object.get())); + return mFormat; + } } ++it; @@ -131,7 +133,7 @@ status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) { sp<RefBase> object; if ((*buffer)->meta()->findObject("format", &object)) { - mFormat = static_cast<MetaData*>(object.get()); + setFormat(static_cast<MetaData*>(object.get())); } return OK; @@ -166,7 +168,7 @@ status_t AnotherPacketSource::read( sp<RefBase> object; if (buffer->meta()->findObject("format", &object)) { - mFormat = static_cast<MetaData*>(object.get()); + setFormat(static_cast<MetaData*>(object.get())); } int64_t timeUs; @@ -218,12 +220,19 @@ void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) { } if (mLatestEnqueuedMeta == NULL) { - mLatestEnqueuedMeta = buffer->meta(); + mLatestEnqueuedMeta = buffer->meta()->dup(); } else { int64_t latestTimeUs = 0; + int64_t frameDeltaUs = 0; CHECK(mLatestEnqueuedMeta->findInt64("timeUs", &latestTimeUs)); if (lastQueuedTimeUs > latestTimeUs) { - mLatestEnqueuedMeta = buffer->meta(); + mLatestEnqueuedMeta = buffer->meta()->dup(); + frameDeltaUs = lastQueuedTimeUs - latestTimeUs; + mLatestEnqueuedMeta->setInt64("durationUs", frameDeltaUs); + } else if (!mLatestEnqueuedMeta->findInt64("durationUs", &frameDeltaUs)) { + // For B frames + frameDeltaUs = latestTimeUs - lastQueuedTimeUs; + mLatestEnqueuedMeta->setInt64("durationUs", frameDeltaUs); } } } @@ -262,15 +271,15 @@ void AnotherPacketSource::queueDiscontinuity( } } + mEOSResult = OK; + mLastQueuedTimeUs = 0; + mLatestEnqueuedMeta = NULL; + if (type == ATSParser::DISCONTINUITY_NONE) { return; } - mEOSResult = OK; - mLastQueuedTimeUs = 0; - mLatestEnqueuedMeta = NULL; ++mQueuedDiscontinuityCount; - sp<ABuffer> buffer = new ABuffer(0); buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type)); buffer->meta()->setMessage("extra", extra); diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 3c8f03e..88da275 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -63,8 +63,6 @@ static unsigned parseAC3SyncFrame( const uint8_t *ptr, size_t size, sp<MetaData> *metaData) { static const unsigned channelCountTable[] = {2, 1, 2, 3, 3, 4, 4, 5}; static const unsigned samplingRateTable[] = {48000, 44100, 32000}; - static const unsigned rates[] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, - 320, 384, 448, 512, 576, 640}; static const unsigned frameSizeTable[19][3] = { { 64, 69, 96 }, @@ -89,7 +87,6 @@ static unsigned parseAC3SyncFrame( }; ABitReader bits(ptr, size); - unsigned syncStartPos = 0; // in bytes if (bits.numBitsLeft() < 16) { return 0; } @@ -121,11 +118,11 @@ static unsigned parseAC3SyncFrame( return 0; } - unsigned bsmod = bits.getBits(3); + unsigned bsmod __unused = bits.getBits(3); unsigned acmod = bits.getBits(3); - unsigned cmixlev = 0; - unsigned surmixlev = 0; - unsigned dsurmod = 0; + unsigned cmixlev __unused = 0; + unsigned surmixlev __unused = 0; + unsigned dsurmod __unused = 0; if ((acmod & 1) > 0 && acmod != 1) { if (bits.numBitsLeft() < 2) { @@ -173,8 +170,9 @@ static bool IsSeeminglyValidAC3Header(const uint8_t *ptr, size_t size) { return parseAC3SyncFrame(ptr, size, NULL) > 0; } -static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) { - if (size < 3) { +static bool IsSeeminglyValidADTSHeader( + const uint8_t *ptr, size_t size, size_t *frameLength) { + if (size < 7) { // Not enough data to verify header. return false; } @@ -197,6 +195,13 @@ static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) { return false; } + size_t frameLengthInHeader = + ((ptr[3] & 3) << 11) + (ptr[4] << 3) + ((ptr[5] >> 5) & 7); + if (frameLengthInHeader > size) { + return false; + } + + *frameLength = frameLengthInHeader; return true; } @@ -318,8 +323,10 @@ status_t ElementaryStreamQueue::appendData( } #else ssize_t startOffset = -1; + size_t frameLength; for (size_t i = 0; i < size; ++i) { - if (IsSeeminglyValidADTSHeader(&ptr[i], size - i)) { + if (IsSeeminglyValidADTSHeader( + &ptr[i], size - i, &frameLength)) { startOffset = i; break; } @@ -335,6 +342,12 @@ status_t ElementaryStreamQueue::appendData( startOffset); } + if (frameLength != size - startOffset) { + ALOGV("First ADTS AAC frame length is %zd bytes, " + "while the buffer size is %zd bytes.", + frameLength, size - startOffset); + } + data = &ptr[startOffset]; size -= startOffset; #endif @@ -540,7 +553,7 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitPCMAudio() { CHECK_EQ(bits.getBits(8), 0xa0); unsigned numAUs = bits.getBits(8); bits.skipBits(8); - unsigned quantization_word_length = bits.getBits(2); + unsigned quantization_word_length __unused = bits.getBits(2); unsigned audio_sampling_frequency = bits.getBits(3); unsigned num_channels = bits.getBits(3); @@ -616,7 +629,7 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() { CHECK_EQ(bits.getBits(12), 0xfffu); bits.skipBits(3); // ID, layer - bool protection_absent = bits.getBits(1) != 0; + bool protection_absent __unused = bits.getBits(1) != 0; if (mFormat == NULL) { unsigned profile = bits.getBits(2); @@ -665,7 +678,7 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() { return NULL; } - size_t headerSize = protection_absent ? 7 : 9; + size_t headerSize __unused = protection_absent ? 7 : 9; offset += aac_frame_length; } @@ -717,11 +730,6 @@ int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) { return timeUs; } -struct NALPosition { - size_t nalOffset; - size_t nalSize; -}; - sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() { const uint8_t *data = mBuffer->data(); @@ -729,6 +737,7 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() { Vector<NALPosition> nals; size_t totalSize = 0; + size_t seiCount = 0; status_t err; const uint8_t *nalStart; @@ -758,6 +767,9 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() { // next frame. flush = true; + } else if (nalType == 6 && nalSize > 0) { + // found non-zero sized SEI + ++seiCount; } if (flush) { @@ -766,21 +778,29 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() { size_t auSize = 4 * nals.size() + totalSize; sp<ABuffer> accessUnit = new ABuffer(auSize); + sp<ABuffer> sei; + + if (seiCount > 0) { + sei = new ABuffer(seiCount * sizeof(NALPosition)); + accessUnit->meta()->setBuffer("sei", sei); + } #if !LOG_NDEBUG AString out; #endif size_t dstOffset = 0; + size_t seiIndex = 0; for (size_t i = 0; i < nals.size(); ++i) { const NALPosition &pos = nals.itemAt(i); unsigned nalType = mBuffer->data()[pos.nalOffset] & 0x1f; - if (nalType == 6) { - sp<ABuffer> sei = new ABuffer(pos.nalSize); - memcpy(sei->data(), mBuffer->data() + pos.nalOffset, pos.nalSize); - accessUnit->meta()->setBuffer("sei", sei); + if (nalType == 6 && pos.nalSize > 0) { + CHECK_LT(seiIndex, sei->size() / sizeof(NALPosition)); + NALPosition &seiPos = ((NALPosition *)sei->data())[seiIndex++]; + seiPos.nalOffset = dstOffset + 4; + seiPos.nalSize = pos.nalSize; } #if !LOG_NDEBUG diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h index a2cca77..45b4624 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.h +++ b/media/libstagefright/mpeg2ts/ESQueue.h @@ -26,7 +26,7 @@ namespace android { struct ABuffer; -struct MetaData; +class MetaData; struct ElementaryStreamQueue { enum Mode { diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp index 35ca118..1f43d6d 100644 --- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -159,7 +159,6 @@ void MPEG2TSExtractor::init() { int numPacketsParsed = 0; while (feedMore() == OK) { - ATSParser::SourceType type; if (haveAudio && haveVideo) { break; } diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index aaa8334..07ea605 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -1,11 +1,8 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -ifeq ($(TARGET_DEVICE), manta) - LOCAL_CFLAGS += -DSURFACE_IS_BGR32 -endif - LOCAL_SRC_FILES:= \ + FrameDropper.cpp \ GraphicBufferSource.cpp \ OMX.cpp \ OMXMaster.cpp \ diff --git a/media/libstagefright/omx/FrameDropper.cpp b/media/libstagefright/omx/FrameDropper.cpp new file mode 100644 index 0000000..9fba0b7 --- /dev/null +++ b/media/libstagefright/omx/FrameDropper.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "FrameDropper" +#include <utils/Log.h> + +#include "FrameDropper.h" + +#include <media/stagefright/foundation/ADebug.h> + +namespace android { + +static const int64_t kMaxJitterUs = 2000; + +FrameDropper::FrameDropper() + : mDesiredMinTimeUs(-1), + mMinIntervalUs(0) { +} + +FrameDropper::~FrameDropper() { +} + +status_t FrameDropper::setMaxFrameRate(float maxFrameRate) { + if (maxFrameRate <= 0) { + ALOGE("framerate should be positive but got %f.", maxFrameRate); + return BAD_VALUE; + } + mMinIntervalUs = (int64_t) (1000000.0f / maxFrameRate); + return OK; +} + +bool FrameDropper::shouldDrop(int64_t timeUs) { + if (mMinIntervalUs <= 0) { + return false; + } + + if (mDesiredMinTimeUs < 0) { + mDesiredMinTimeUs = timeUs + mMinIntervalUs; + ALOGV("first frame %lld, next desired frame %lld", timeUs, mDesiredMinTimeUs); + return false; + } + + if (timeUs < (mDesiredMinTimeUs - kMaxJitterUs)) { + ALOGV("drop frame %lld, desired frame %lld, diff %lld", + timeUs, mDesiredMinTimeUs, mDesiredMinTimeUs - timeUs); + return true; + } + + int64_t n = (timeUs - mDesiredMinTimeUs + kMaxJitterUs) / mMinIntervalUs; + mDesiredMinTimeUs += (n + 1) * mMinIntervalUs; + ALOGV("keep frame %lld, next desired frame %lld, diff %lld", + timeUs, mDesiredMinTimeUs, mDesiredMinTimeUs - timeUs); + return false; +} + +} // namespace android diff --git a/media/libstagefright/omx/FrameDropper.h b/media/libstagefright/omx/FrameDropper.h new file mode 100644 index 0000000..c5a6d4b --- /dev/null +++ b/media/libstagefright/omx/FrameDropper.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FRAME_DROPPER_H_ + +#define FRAME_DROPPER_H_ + +#include <utils/Errors.h> +#include <utils/RefBase.h> + +#include <media/stagefright/foundation/ABase.h> + +namespace android { + +struct FrameDropper : public RefBase { + // No frames will be dropped until a valid max frame rate is set. + FrameDropper(); + + // maxFrameRate required to be positive. + status_t setMaxFrameRate(float maxFrameRate); + + // Returns false if max frame rate has not been set via setMaxFrameRate. + bool shouldDrop(int64_t timeUs); + +protected: + virtual ~FrameDropper(); + +private: + int64_t mDesiredMinTimeUs; + int64_t mMinIntervalUs; + + DISALLOW_EVIL_CONSTRUCTORS(FrameDropper); +}; + +} // namespace android + +#endif // FRAME_DROPPER_H_ diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index 3e70956..477cfc6 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -28,8 +28,10 @@ #include <media/hardware/MetadataBufferType.h> #include <ui/GraphicBuffer.h> +#include <gui/BufferItem.h> #include <inttypes.h> +#include "FrameDropper.h" namespace android { @@ -53,9 +55,9 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, mRepeatAfterUs(-1ll), mRepeatLastFrameGeneration(0), mRepeatLastFrameTimestamp(-1ll), - mLatestSubmittedBufferId(-1), - mLatestSubmittedBufferFrameNum(0), - mLatestSubmittedBufferUseCount(0), + mLatestBufferId(-1), + mLatestBufferFrameNum(0), + mLatestBufferUseCount(0), mRepeatBufferDeferred(false), mTimePerCaptureUs(-1ll), mTimePerFrameUs(-1ll), @@ -152,9 +154,9 @@ void GraphicBufferSource::omxExecuting() { mLooper->registerHandler(mReflector); mLooper->start(); - if (mLatestSubmittedBufferId >= 0) { + if (mLatestBufferId >= 0) { sp<AMessage> msg = - new AMessage(kWhatRepeatLastFrame, mReflector->id()); + new AMessage(kWhatRepeatLastFrame, mReflector); msg->setInt32("generation", ++mRepeatLastFrameGeneration); msg->post(mRepeatAfterUs); @@ -287,8 +289,8 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { ALOGV("cbi %d matches bq slot %d, handle=%p", cbi, id, mBufferSlot[id]->handle); - if (id == mLatestSubmittedBufferId) { - CHECK_GT(mLatestSubmittedBufferUseCount--, 0); + if (id == mLatestBufferId) { + CHECK_GT(mLatestBufferUseCount--, 0); } else { mConsumer->releaseBuffer(id, codecBuffer.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); @@ -313,11 +315,11 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { ALOGV("buffer freed, EOS pending"); submitEndOfInputStream_l(); } else if (mRepeatBufferDeferred) { - bool success = repeatLatestSubmittedBuffer_l(); + bool success = repeatLatestBuffer_l(); if (success) { - ALOGV("deferred repeatLatestSubmittedBuffer_l SUCCESS"); + ALOGV("deferred repeatLatestBuffer_l SUCCESS"); } else { - ALOGV("deferred repeatLatestSubmittedBuffer_l FAILURE"); + ALOGV("deferred repeatLatestBuffer_l FAILURE"); } mRepeatBufferDeferred = false; } @@ -359,7 +361,7 @@ void GraphicBufferSource::suspend(bool suspend) { mSuspended = true; while (mNumFramesAvailable > 0) { - BufferQueue::BufferItem item; + BufferItem item; status_t err = mConsumer->acquireBuffer(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { @@ -382,12 +384,12 @@ void GraphicBufferSource::suspend(bool suspend) { mSuspended = false; if (mExecuting && mNumFramesAvailable == 0 && mRepeatBufferDeferred) { - if (repeatLatestSubmittedBuffer_l()) { - ALOGV("suspend/deferred repeatLatestSubmittedBuffer_l SUCCESS"); + if (repeatLatestBuffer_l()) { + ALOGV("suspend/deferred repeatLatestBuffer_l SUCCESS"); mRepeatBufferDeferred = false; } else { - ALOGV("suspend/deferred repeatLatestSubmittedBuffer_l FAILURE"); + ALOGV("suspend/deferred repeatLatestBuffer_l FAILURE"); } } } @@ -409,7 +411,7 @@ bool GraphicBufferSource::fillCodecBuffer_l() { ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%zu", mNumFramesAvailable); - BufferQueue::BufferItem item; + BufferItem item; status_t err = mConsumer->acquireBuffer(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // shouldn't happen @@ -441,12 +443,22 @@ bool GraphicBufferSource::fillCodecBuffer_l() { // only submit sample if start time is unspecified, or sample // is queued after the specified start time + bool dropped = false; 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); + + int64_t timeUs = item.mTimestamp / 1000; + if (mFrameDropper != NULL && mFrameDropper->shouldDrop(timeUs)) { + ALOGV("skipping frame (%lld) to meet max framerate", static_cast<long long>(timeUs)); + // set err to OK so that the skipped frame can still be saved as the lastest frame + err = OK; + dropped = true; + } else { + err = submitBuffer_l(item, cbi); + } } if (err != OK) { @@ -455,46 +467,46 @@ bool GraphicBufferSource::fillCodecBuffer_l() { EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); } else { ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); - setLatestSubmittedBuffer_l(item); + setLatestBuffer_l(item, dropped); } return true; } -bool GraphicBufferSource::repeatLatestSubmittedBuffer_l() { +bool GraphicBufferSource::repeatLatestBuffer_l() { CHECK(mExecuting && mNumFramesAvailable == 0); - if (mLatestSubmittedBufferId < 0 || mSuspended) { + if (mLatestBufferId < 0 || mSuspended) { return false; } - if (mBufferSlot[mLatestSubmittedBufferId] == NULL) { + if (mBufferSlot[mLatestBufferId] == NULL) { // This can happen if the remote side disconnects, causing // onBuffersReleased() to NULL out our copy of the slots. The // buffer is gone, so we have nothing to show. // // To be on the safe side we try to release the buffer. - ALOGD("repeatLatestSubmittedBuffer_l: slot was NULL"); + ALOGD("repeatLatestBuffer_l: slot was NULL"); mConsumer->releaseBuffer( - mLatestSubmittedBufferId, - mLatestSubmittedBufferFrameNum, + mLatestBufferId, + mLatestBufferFrameNum, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); - mLatestSubmittedBufferId = -1; - mLatestSubmittedBufferFrameNum = 0; + mLatestBufferId = -1; + mLatestBufferFrameNum = 0; return false; } int cbi = findAvailableCodecBuffer_l(); if (cbi < 0) { // No buffers available, bail. - ALOGV("repeatLatestSubmittedBuffer_l: no codec buffers."); + ALOGV("repeatLatestBuffer_l: no codec buffers."); return false; } - BufferQueue::BufferItem item; - item.mBuf = mLatestSubmittedBufferId; - item.mFrameNumber = mLatestSubmittedBufferFrameNum; + BufferItem item; + item.mBuf = mLatestBufferId; + item.mFrameNumber = mLatestBufferFrameNum; item.mTimestamp = mRepeatLastFrameTimestamp; status_t err = submitBuffer_l(item, cbi); @@ -503,7 +515,7 @@ bool GraphicBufferSource::repeatLatestSubmittedBuffer_l() { return false; } - ++mLatestSubmittedBufferUseCount; + ++mLatestBufferUseCount; /* repeat last frame up to kRepeatLastFrameCount times. * in case of static scene, a single repeat might not get rid of encoder @@ -513,7 +525,7 @@ bool GraphicBufferSource::repeatLatestSubmittedBuffer_l() { mRepeatLastFrameTimestamp = item.mTimestamp + mRepeatAfterUs * 1000; if (mReflector != NULL) { - sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector->id()); + sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector); msg->setInt32("generation", ++mRepeatLastFrameGeneration); msg->post(mRepeatAfterUs); } @@ -522,31 +534,31 @@ bool GraphicBufferSource::repeatLatestSubmittedBuffer_l() { return true; } -void GraphicBufferSource::setLatestSubmittedBuffer_l( - const BufferQueue::BufferItem &item) { - ALOGV("setLatestSubmittedBuffer_l"); +void GraphicBufferSource::setLatestBuffer_l( + const BufferItem &item, bool dropped) { + ALOGV("setLatestBuffer_l"); - if (mLatestSubmittedBufferId >= 0) { - if (mLatestSubmittedBufferUseCount == 0) { + if (mLatestBufferId >= 0) { + if (mLatestBufferUseCount == 0) { mConsumer->releaseBuffer( - mLatestSubmittedBufferId, - mLatestSubmittedBufferFrameNum, + mLatestBufferId, + mLatestBufferFrameNum, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); } } - mLatestSubmittedBufferId = item.mBuf; - mLatestSubmittedBufferFrameNum = item.mFrameNumber; + mLatestBufferId = item.mBuf; + mLatestBufferFrameNum = item.mFrameNumber; mRepeatLastFrameTimestamp = item.mTimestamp + mRepeatAfterUs * 1000; - mLatestSubmittedBufferUseCount = 1; + mLatestBufferUseCount = dropped ? 0 : 1; mRepeatBufferDeferred = false; mRepeatLastFrameCount = kRepeatLastFrameCount; if (mReflector != NULL) { - sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector->id()); + sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector); msg->setInt32("generation", ++mRepeatLastFrameGeneration); msg->post(mRepeatAfterUs); } @@ -579,7 +591,7 @@ status_t GraphicBufferSource::signalEndOfInputStream() { return OK; } -int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) { +int64_t GraphicBufferSource::getTimestamp(const BufferItem &item) { int64_t timeUs = item.mTimestamp / 1000; if (mTimePerCaptureUs > 0ll) { @@ -640,7 +652,7 @@ int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) { } status_t GraphicBufferSource::submitBuffer_l( - const BufferQueue::BufferItem &item, int cbi) { + const BufferItem &item, int cbi) { ALOGV("submitBuffer_l cbi=%d", cbi); int64_t timeUs = getTimestamp(item); @@ -750,7 +762,7 @@ int GraphicBufferSource::findMatchingCodecBuffer_l( } // BufferQueue::ConsumerListener callback -void GraphicBufferSource::onFrameAvailable() { +void GraphicBufferSource::onFrameAvailable(const BufferItem& /*item*/) { Mutex::Autolock autoLock(mMutex); ALOGV("onFrameAvailable exec=%d avail=%zu", @@ -766,7 +778,7 @@ void GraphicBufferSource::onFrameAvailable() { ALOGV("onFrameAvailable: suspended, ignoring frame"); } - BufferQueue::BufferItem item; + BufferItem item; status_t err = mConsumer->acquireBuffer(&item, 0); if (err == OK) { // If this is the first time we're seeing this buffer, add it to our @@ -841,6 +853,23 @@ status_t GraphicBufferSource::setMaxTimestampGapUs(int64_t maxGapUs) { return OK; } +status_t GraphicBufferSource::setMaxFps(float maxFps) { + Mutex::Autolock autoLock(mMutex); + + if (mExecuting) { + return INVALID_OPERATION; + } + + mFrameDropper = new FrameDropper(); + status_t err = mFrameDropper->setMaxFrameRate(maxFps); + if (err != OK) { + mFrameDropper.clear(); + return err; + } + + return OK; +} + void GraphicBufferSource::setSkipFramesBeforeUs(int64_t skipFramesBeforeUs) { Mutex::Autolock autoLock(mMutex); @@ -879,12 +908,12 @@ void GraphicBufferSource::onMessageReceived(const sp<AMessage> &msg) { break; } - bool success = repeatLatestSubmittedBuffer_l(); + bool success = repeatLatestBuffer_l(); if (success) { - ALOGV("repeatLatestSubmittedBuffer_l SUCCESS"); + ALOGV("repeatLatestBuffer_l SUCCESS"); } else { - ALOGV("repeatLatestSubmittedBuffer_l FAILURE"); + ALOGV("repeatLatestBuffer_l FAILURE"); mRepeatBufferDeferred = true; } break; diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index c0860ab..1067472 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -30,6 +30,8 @@ namespace android { +class FrameDropper; + /* * This class is used to feed OMX codecs from a Surface via BufferQueue. * @@ -119,6 +121,9 @@ public: // of suspension on input. status_t setMaxTimestampGapUs(int64_t maxGapUs); + // When set, the max frame rate fed to the encoder will be capped at maxFps. + status_t setMaxFps(float maxFps); + // 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 @@ -137,7 +142,7 @@ protected: // into the codec buffer, and call Empty[This]Buffer. If we're not yet // executing or there's no codec buffer available, we just increment // mNumFramesAvailable and return. - virtual void onFrameAvailable(); + virtual void onFrameAvailable(const BufferItem& item); // BufferQueue::ConsumerListener interface, called when the client has // released one or more GraphicBuffers. We clear out the appropriate @@ -187,15 +192,15 @@ private: // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer // reference into the codec buffer, and submits the data to the codec. - status_t submitBuffer_l(const BufferQueue::BufferItem &item, int cbi); + status_t submitBuffer_l(const BufferItem &item, int cbi); // Submits an empty buffer, with the EOS flag set. Returns without // doing anything if we don't have a codec buffer available. void submitEndOfInputStream_l(); - void setLatestSubmittedBuffer_l(const BufferQueue::BufferItem &item); - bool repeatLatestSubmittedBuffer_l(); - int64_t getTimestamp(const BufferQueue::BufferItem &item); + void setLatestBuffer_l(const BufferItem &item, bool dropped); + bool repeatLatestBuffer_l(); + int64_t getTimestamp(const BufferItem &item); // Lock, covers all member variables. mutable Mutex mMutex; @@ -250,6 +255,8 @@ private: int64_t mPrevModifiedTimeUs; int64_t mSkipFramesBeforeNs; + sp<FrameDropper> mFrameDropper; + sp<ALooper> mLooper; sp<AHandlerReflector<GraphicBufferSource> > mReflector; @@ -258,11 +265,11 @@ private: int64_t mRepeatLastFrameTimestamp; int32_t mRepeatLastFrameCount; - int mLatestSubmittedBufferId; - uint64_t mLatestSubmittedBufferFrameNum; - int32_t mLatestSubmittedBufferUseCount; + int mLatestBufferId; + uint64_t mLatestBufferFrameNum; + int32_t mLatestBufferUseCount; - // The previously submitted buffer should've been repeated but + // The previous buffer should've been repeated but // no codec buffer was available at the time. bool mRepeatBufferDeferred; diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 41407e4..f8d38ff 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -225,7 +225,7 @@ status_t OMX::allocateNode( *node = 0; - OMXNodeInstance *instance = new OMXNodeInstance(this, observer); + OMXNodeInstance *instance = new OMXNodeInstance(this, observer, name); OMX_COMPONENTTYPE *handle; OMX_ERRORTYPE err = mMaster->makeComponentInstance( @@ -245,8 +245,8 @@ status_t OMX::allocateNode( instance->setHandle(*node, handle); - mLiveNodes.add(observer->asBinder(), instance); - observer->asBinder()->linkToDeath(this); + mLiveNodes.add(IInterface::asBinder(observer), instance); + IInterface::asBinder(observer)->linkToDeath(this); return OK; } @@ -256,7 +256,7 @@ status_t OMX::freeNode(node_id node) { { Mutex::Autolock autoLock(mLock); - ssize_t index = mLiveNodes.indexOfKey(instance->observer()->asBinder()); + ssize_t index = mLiveNodes.indexOfKey(IInterface::asBinder(instance->observer())); if (index < 0) { // This could conceivably happen if the observer dies at roughly the // same time that a client attempts to free the node explicitly. @@ -265,7 +265,7 @@ status_t OMX::freeNode(node_id node) { mLiveNodes.removeItemsAt(index); } - instance->observer()->asBinder()->unlinkToDeath(this); + IInterface::asBinder(instance->observer())->unlinkToDeath(this); status_t err = instance->freeNode(mMaster); diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index f9c84e2..4779d6a 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -18,11 +18,15 @@ #define LOG_TAG "OMXNodeInstance" #include <utils/Log.h> +#include <inttypes.h> + #include "../include/OMXNodeInstance.h" #include "OMXMaster.h" #include "GraphicBufferSource.h" #include <OMX_Component.h> +#include <OMX_IndexExt.h> +#include <OMX_AsString.h> #include <binder/IMemory.h> #include <gui/BufferQueue.h> @@ -30,7 +34,68 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/MediaErrors.h> +#include <utils/misc.h> + static const OMX_U32 kPortIndexInput = 0; +static const OMX_U32 kPortIndexOutput = 1; + +#define CLOGW(fmt, ...) ALOGW("[%x:%s] " fmt, mNodeID, mName, ##__VA_ARGS__) + +#define CLOG_ERROR_IF(cond, fn, err, fmt, ...) \ + ALOGE_IF(cond, #fn "(%x:%s, " fmt ") ERROR: %s(%#x)", \ + mNodeID, mName, ##__VA_ARGS__, asString(err), err) +#define CLOG_ERROR(fn, err, fmt, ...) CLOG_ERROR_IF(true, fn, err, fmt, ##__VA_ARGS__) +#define CLOG_IF_ERROR(fn, err, fmt, ...) \ + CLOG_ERROR_IF((err) != OMX_ErrorNone, fn, err, fmt, ##__VA_ARGS__) + +#define CLOGI_(level, fn, fmt, ...) \ + ALOGI_IF(DEBUG >= (level), #fn "(%x:%s, " fmt ")", mNodeID, mName, ##__VA_ARGS__) +#define CLOGD_(level, fn, fmt, ...) \ + ALOGD_IF(DEBUG >= (level), #fn "(%x:%s, " fmt ")", mNodeID, mName, ##__VA_ARGS__) + +#define CLOG_LIFE(fn, fmt, ...) CLOGI_(ADebug::kDebugLifeCycle, fn, fmt, ##__VA_ARGS__) +#define CLOG_STATE(fn, fmt, ...) CLOGI_(ADebug::kDebugState, fn, fmt, ##__VA_ARGS__) +#define CLOG_CONFIG(fn, fmt, ...) CLOGI_(ADebug::kDebugConfig, fn, fmt, ##__VA_ARGS__) +#define CLOG_INTERNAL(fn, fmt, ...) CLOGD_(ADebug::kDebugInternalState, fn, fmt, ##__VA_ARGS__) + +#define CLOG_DEBUG_IF(cond, fn, fmt, ...) \ + ALOGD_IF(cond, #fn "(%x, " fmt ")", mNodeID, ##__VA_ARGS__) + +#define CLOG_BUFFER(fn, fmt, ...) \ + CLOG_DEBUG_IF(DEBUG >= ADebug::kDebugAll, fn, fmt, ##__VA_ARGS__) +#define CLOG_BUMPED_BUFFER(fn, fmt, ...) \ + CLOG_DEBUG_IF(DEBUG_BUMP >= ADebug::kDebugAll, fn, fmt, ##__VA_ARGS__) + +/* buffer formatting */ +#define BUFFER_FMT(port, fmt, ...) "%s:%u " fmt, portString(port), (port), ##__VA_ARGS__ +#define NEW_BUFFER_FMT(buffer_id, port, fmt, ...) \ + BUFFER_FMT(port, fmt ") (#%zu => %#x", ##__VA_ARGS__, mActiveBuffers.size(), (buffer_id)) + +#define SIMPLE_BUFFER(port, size, data) BUFFER_FMT(port, "%zu@%p", (size), (data)) +#define SIMPLE_NEW_BUFFER(buffer_id, port, size, data) \ + NEW_BUFFER_FMT(buffer_id, port, "%zu@%p", (size), (data)) + +#define EMPTY_BUFFER(addr, header) "%#x [%u@%p]", \ + (addr), (header)->nAllocLen, (header)->pBuffer +#define FULL_BUFFER(addr, header) "%#" PRIxPTR " [%u@%p (%u..+%u) f=%x ts=%lld]", \ + (intptr_t)(addr), (header)->nAllocLen, (header)->pBuffer, \ + (header)->nOffset, (header)->nFilledLen, (header)->nFlags, (header)->nTimeStamp + +#define WITH_STATS_WRAPPER(fmt, ...) fmt " { IN=%zu/%zu OUT=%zu/%zu }", ##__VA_ARGS__, \ + mInputBuffersWithCodec.size(), mNumPortBuffers[kPortIndexInput], \ + mOutputBuffersWithCodec.size(), mNumPortBuffers[kPortIndexOutput] +// TRICKY: this is needed so formatting macros expand before substitution +#define WITH_STATS(fmt, ...) WITH_STATS_WRAPPER(fmt, ##__VA_ARGS__) + +template<class T> +static void InitOMXParams(T *params) { + memset(params, 0, sizeof(T)); + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} namespace android { @@ -56,8 +121,8 @@ struct BufferMeta { } memcpy((OMX_U8 *)mMem->pointer() + header->nOffset, - header->pBuffer + header->nOffset, - header->nFilledLen); + header->pBuffer + header->nOffset, + header->nFilledLen); } void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) { @@ -66,8 +131,8 @@ struct BufferMeta { } memcpy(header->pBuffer + header->nOffset, - (const OMX_U8 *)mMem->pointer() + header->nOffset, - header->nFilledLen); + (const OMX_U8 *)mMem->pointer() + header->nOffset, + header->nFilledLen); } void setGraphicBuffer(const sp<GraphicBuffer> &graphicBuffer) { @@ -89,8 +154,17 @@ OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = { &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone }; +static inline const char *portString(OMX_U32 portIndex) { + switch (portIndex) { + case kPortIndexInput: return "Input"; + case kPortIndexOutput: return "Output"; + case ~0U: return "All"; + default: return "port"; + } +} + OMXNodeInstance::OMXNodeInstance( - OMX *owner, const sp<IOMXObserver> &observer) + OMX *owner, const sp<IOMXObserver> &observer, const char *name) : mOwner(owner), mNodeID(0), mHandle(NULL), @@ -100,15 +174,25 @@ OMXNodeInstance::OMXNodeInstance( , mBufferIDCount(0) #endif { + mName = ADebug::GetDebugName(name); + DEBUG = ADebug::GetDebugLevelFromProperty(name, "debug.stagefright.omx-debug"); + ALOGV("debug level for %s is %d", name, DEBUG); + DEBUG_BUMP = DEBUG; + mNumPortBuffers[0] = 0; + mNumPortBuffers[1] = 0; + mDebugLevelBumpPendingBuffers[0] = 0; + mDebugLevelBumpPendingBuffers[1] = 0; } OMXNodeInstance::~OMXNodeInstance() { + free(mName); CHECK(mHandle == NULL); } void OMXNodeInstance::setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle) { - CHECK(mHandle == NULL); mNodeID = node_id; + CLOG_LIFE(allocateNode, "handle=%p", handle); + CHECK(mHandle == NULL); mHandle = handle; } @@ -120,6 +204,7 @@ sp<GraphicBufferSource> OMXNodeInstance::getGraphicBufferSource() { void OMXNodeInstance::setGraphicBufferSource( const sp<GraphicBufferSource>& bufferSource) { Mutex::Autolock autoLock(mGraphicBufferSourceLock); + CLOG_INTERNAL(setGraphicBufferSource, "%p", bufferSource.get()); mGraphicBufferSource = bufferSource; } @@ -140,6 +225,7 @@ static status_t StatusFromOMXError(OMX_ERRORTYPE err) { case OMX_ErrorNone: return OK; case OMX_ErrorUnsupportedSetting: + case OMX_ErrorUnsupportedIndex: return ERROR_UNSUPPORTED; default: return UNKNOWN_ERROR; @@ -147,6 +233,7 @@ static status_t StatusFromOMXError(OMX_ERRORTYPE err) { } status_t OMXNodeInstance::freeNode(OMXMaster *master) { + CLOG_LIFE(freeNode, "handle=%p", mHandle); static int32_t kMaxNumIterations = 10; // exit if we have already freed the node @@ -175,10 +262,11 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { OMX_ERRORTYPE err; int32_t iteration = 0; while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone - && state != OMX_StateIdle - && state != OMX_StateInvalid) { + && state != OMX_StateIdle + && state != OMX_StateInvalid) { if (++iteration > kMaxNumIterations) { - ALOGE("component failed to enter Idle state, aborting."); + CLOGW("failed to enter Idle state (now %s(%d), aborting.", + asString(state), state); state = OMX_StateInvalid; break; } @@ -204,10 +292,11 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { OMX_ERRORTYPE err; int32_t iteration = 0; while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone - && state != OMX_StateLoaded - && state != OMX_StateInvalid) { + && state != OMX_StateLoaded + && state != OMX_StateInvalid) { if (++iteration > kMaxNumIterations) { - ALOGE("component failed to enter Loaded state, aborting."); + CLOGW("failed to enter Loaded state (now %s(%d), aborting.", + asString(state), state); state = OMX_StateInvalid; break; } @@ -225,20 +314,18 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { break; default: - CHECK(!"should not be here, unknown state."); + LOG_ALWAYS_FATAL("unknown state %s(%#x).", asString(state), state); break; } - ALOGV("calling destroyComponentInstance"); + ALOGV("[%x:%s] calling destroyComponentInstance", mNodeID, mName); OMX_ERRORTYPE err = master->destroyComponentInstance( static_cast<OMX_COMPONENTTYPE *>(mHandle)); - ALOGV("destroyComponentInstance returned err %d", err); mHandle = NULL; - - if (err != OMX_ErrorNone) { - ALOGE("FreeHandle FAILED with error 0x%08x.", err); - } + CLOG_IF_ERROR(freeNode, err, ""); + free(mName); + mName = NULL; mOwner->invalidateNodeID(mNodeID); mNodeID = 0; @@ -270,7 +357,17 @@ status_t OMXNodeInstance::sendCommand( Mutex::Autolock autoLock(mLock); + // bump internal-state debug level for 2 input and output frames past a command + { + Mutex::Autolock _l(mDebugLock); + bumpDebugLevel_l(2 /* numInputBuffers */, 2 /* numOutputBuffers */); + } + + const char *paramString = + cmd == OMX_CommandStateSet ? asString((OMX_STATETYPE)param) : portString(param); + CLOG_STATE(sendCommand, "%s(%d), %s(%d)", asString(cmd), cmd, paramString, param); OMX_ERRORTYPE err = OMX_SendCommand(mHandle, cmd, param, NULL); + CLOG_IF_ERROR(sendCommand, err, "%s(%d), %s(%d)", asString(cmd), cmd, paramString, param); return StatusFromOMXError(err); } @@ -279,17 +376,23 @@ status_t OMXNodeInstance::getParameter( Mutex::Autolock autoLock(mLock); OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params); - ALOGE_IF(err != OMX_ErrorNone, "getParameter(%d) ERROR: %#x", index, err); + OMX_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index; + // some errors are expected for getParameter + if (err != OMX_ErrorNoMore) { + CLOG_IF_ERROR(getParameter, err, "%s(%#x)", asString(extIndex), index); + } return StatusFromOMXError(err); } 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_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index; + CLOG_CONFIG(setParameter, "%s(%#x), %zu@%p)", asString(extIndex), index, size, params); OMX_ERRORTYPE err = OMX_SetParameter( mHandle, index, const_cast<void *>(params)); - ALOGE_IF(err != OMX_ErrorNone, "setParameter(%d) ERROR: %#x", index, err); + CLOG_IF_ERROR(setParameter, err, "%s(%#x)", asString(extIndex), index); return StatusFromOMXError(err); } @@ -298,16 +401,23 @@ status_t OMXNodeInstance::getConfig( Mutex::Autolock autoLock(mLock); OMX_ERRORTYPE err = OMX_GetConfig(mHandle, index, params); + OMX_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index; + // some errors are expected for getConfig + if (err != OMX_ErrorNoMore) { + CLOG_IF_ERROR(getConfig, err, "%s(%#x)", asString(extIndex), index); + } return StatusFromOMXError(err); } 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_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index; + CLOG_CONFIG(setConfig, "%s(%#x), %zu@%p)", asString(extIndex), index, size, params); OMX_ERRORTYPE err = OMX_SetConfig( mHandle, index, const_cast<void *>(params)); - + CLOG_IF_ERROR(setConfig, err, "%s(%#x)", asString(extIndex), index); return StatusFromOMXError(err); } @@ -315,13 +425,14 @@ status_t OMXNodeInstance::getState(OMX_STATETYPE* state) { Mutex::Autolock autoLock(mLock); OMX_ERRORTYPE err = OMX_GetState(mHandle, state); - + CLOG_IF_ERROR(getState, err, ""); return StatusFromOMXError(err); } status_t OMXNodeInstance::enableGraphicBuffers( OMX_U32 portIndex, OMX_BOOL enable) { Mutex::Autolock autoLock(mLock); + CLOG_CONFIG(enableGraphicBuffers, "%s:%u, %d", portString(portIndex), portIndex, enable); OMX_STRING name = const_cast<OMX_STRING>( "OMX.google.android.index.enableAndroidNativeBuffers"); @@ -329,32 +440,19 @@ status_t OMXNodeInstance::enableGraphicBuffers( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - if (enable) { - ALOGE("OMX_GetExtensionIndex %s failed", name); - } - + CLOG_ERROR_IF(enable, getExtensionIndex, err, "%s", name); return StatusFromOMXError(err); } - OMX_VERSIONTYPE ver; - ver.s.nVersionMajor = 1; - ver.s.nVersionMinor = 0; - ver.s.nRevision = 0; - ver.s.nStep = 0; - EnableAndroidNativeBuffersParams params = { - sizeof(EnableAndroidNativeBuffersParams), ver, portIndex, enable, - }; + EnableAndroidNativeBuffersParams params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + params.enable = enable; err = OMX_SetParameter(mHandle, index, ¶ms); - - if (err != OMX_ErrorNone) { - ALOGE("OMX_EnableAndroidNativeBuffers failed with error %d (0x%08x)", - err, err); - - return UNKNOWN_ERROR; - } - - return OK; + CLOG_IF_ERROR(setParameter, err, "%s(%#x): %s:%u en=%d", name, index, + portString(portIndex), portIndex, enable); + return StatusFromOMXError(err); } status_t OMXNodeInstance::getGraphicBufferUsage( @@ -367,26 +465,19 @@ status_t OMXNodeInstance::getGraphicBufferUsage( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex %s failed", name); - + CLOG_ERROR(getExtensionIndex, err, "%s", name); return StatusFromOMXError(err); } - OMX_VERSIONTYPE ver; - ver.s.nVersionMajor = 1; - ver.s.nVersionMinor = 0; - ver.s.nRevision = 0; - ver.s.nStep = 0; - GetAndroidNativeBufferUsageParams params = { - sizeof(GetAndroidNativeBufferUsageParams), ver, portIndex, 0, - }; + GetAndroidNativeBufferUsageParams params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; err = OMX_GetParameter(mHandle, index, ¶ms); - if (err != OMX_ErrorNone) { - ALOGE("OMX_GetAndroidNativeBufferUsage failed with error %d (0x%08x)", - err, err); - return UNKNOWN_ERROR; + CLOG_ERROR(getParameter, err, "%s(%#x): %s:%u", name, index, + portString(portIndex), portIndex); + return StatusFromOMXError(err); } *usage = params.nUsage; @@ -398,6 +489,7 @@ status_t OMXNodeInstance::storeMetaDataInBuffers( OMX_U32 portIndex, OMX_BOOL enable) { Mutex::Autolock autolock(mLock); + CLOG_CONFIG(storeMetaDataInBuffers, "%s:%u en:%d", portString(portIndex), portIndex, enable); return storeMetaDataInBuffers_l( portIndex, enable, OMX_FALSE /* useGraphicBuffer */, NULL /* usingGraphicBufferInMetadata */); @@ -424,37 +516,42 @@ status_t OMXNodeInstance::storeMetaDataInBuffers_l( : OMX_ErrorBadParameter; if (err == OMX_ErrorNone) { *usingGraphicBufferInMetadata = OMX_TRUE; + name = graphicBufferName; } else { - *usingGraphicBufferInMetadata = OMX_FALSE; err = OMX_GetExtensionIndex(mHandle, name, &index); } - if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex %s failed", name); - return StatusFromOMXError(err); - } - - StoreMetaDataInBuffersParams params; - memset(¶ms, 0, sizeof(params)); - params.nSize = sizeof(params); + OMX_ERRORTYPE xerr = err; + if (err == OMX_ErrorNone) { + StoreMetaDataInBuffersParams params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + params.bStoreMetaData = enable; - // Version: 1.0.0.0 - params.nVersion.s.nVersionMajor = 1; + err = OMX_SetParameter(mHandle, index, ¶ms); + } - params.nPortIndex = portIndex; - params.bStoreMetaData = enable; - if ((err = OMX_SetParameter(mHandle, index, ¶ms)) != OMX_ErrorNone) { - ALOGE("OMX_SetParameter() failed for StoreMetaDataInBuffers: 0x%08x", err); + // don't log loud error if component does not support metadata mode on the output + if (err != OMX_ErrorNone) { *usingGraphicBufferInMetadata = OMX_FALSE; - return UNKNOWN_ERROR; + if (err == OMX_ErrorUnsupportedIndex && portIndex == kPortIndexOutput) { + CLOGW("component does not support metadata mode; using fallback"); + } else if (xerr != OMX_ErrorNone) { + CLOG_ERROR(getExtensionIndex, xerr, "%s", name); + } else { + CLOG_ERROR(setParameter, err, "%s(%#x): %s:%u en=%d GB=%d", name, index, + portString(portIndex), portIndex, enable, useGraphicBuffer); + } } - return err; + return StatusFromOMXError(err); } status_t OMXNodeInstance::prepareForAdaptivePlayback( OMX_U32 portIndex, OMX_BOOL enable, OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) { Mutex::Autolock autolock(mLock); + CLOG_CONFIG(prepareForAdaptivePlayback, "%s:%u en=%d max=%ux%u", + portString(portIndex), portIndex, enable, maxFrameWidth, maxFrameHeight); OMX_INDEXTYPE index; OMX_STRING name = const_cast<OMX_STRING>( @@ -462,33 +559,29 @@ status_t OMXNodeInstance::prepareForAdaptivePlayback( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGW_IF(enable, "OMX_GetExtensionIndex %s failed", name); + CLOG_ERROR_IF(enable, getExtensionIndex, err, "%s", name); return StatusFromOMXError(err); } PrepareForAdaptivePlaybackParams params; - params.nSize = sizeof(params); - params.nVersion.s.nVersionMajor = 1; - params.nVersion.s.nVersionMinor = 0; - params.nVersion.s.nRevision = 0; - params.nVersion.s.nStep = 0; - + InitOMXParams(¶ms); params.nPortIndex = portIndex; params.bEnable = enable; params.nMaxFrameWidth = maxFrameWidth; params.nMaxFrameHeight = maxFrameHeight; - if ((err = OMX_SetParameter(mHandle, index, ¶ms)) != OMX_ErrorNone) { - ALOGW("OMX_SetParameter failed for PrepareForAdaptivePlayback " - "with error %d (0x%08x)", err, err); - return UNKNOWN_ERROR; - } - return err; + + err = OMX_SetParameter(mHandle, index, ¶ms); + CLOG_IF_ERROR(setParameter, err, "%s(%#x): %s:%u en=%d max=%ux%u", name, index, + portString(portIndex), portIndex, enable, maxFrameWidth, maxFrameHeight); + return StatusFromOMXError(err); } status_t OMXNodeInstance::configureVideoTunnelMode( OMX_U32 portIndex, OMX_BOOL tunneled, OMX_U32 audioHwSync, native_handle_t **sidebandHandle) { Mutex::Autolock autolock(mLock); + CLOG_CONFIG(configureVideoTunnelMode, "%s:%u tun=%d sync=%u", + portString(portIndex), portIndex, tunneled, audioHwSync); OMX_INDEXTYPE index; OMX_STRING name = const_cast<OMX_STRING>( @@ -496,36 +589,33 @@ status_t OMXNodeInstance::configureVideoTunnelMode( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGE("configureVideoTunnelMode extension is missing!"); + CLOG_ERROR_IF(tunneled, getExtensionIndex, err, "%s", name); return StatusFromOMXError(err); } ConfigureVideoTunnelModeParams tunnelParams; - tunnelParams.nSize = sizeof(tunnelParams); - tunnelParams.nVersion.s.nVersionMajor = 1; - tunnelParams.nVersion.s.nVersionMinor = 0; - tunnelParams.nVersion.s.nRevision = 0; - tunnelParams.nVersion.s.nStep = 0; - + InitOMXParams(&tunnelParams); tunnelParams.nPortIndex = portIndex; tunnelParams.bTunneled = tunneled; tunnelParams.nAudioHwSync = audioHwSync; err = OMX_SetParameter(mHandle, index, &tunnelParams); if (err != OMX_ErrorNone) { - ALOGE("configureVideoTunnelMode failed! (err %d).", err); - return UNKNOWN_ERROR; + CLOG_ERROR(setParameter, err, "%s(%#x): %s:%u tun=%d sync=%u", name, index, + portString(portIndex), portIndex, tunneled, audioHwSync); + return StatusFromOMXError(err); } err = OMX_GetParameter(mHandle, index, &tunnelParams); if (err != OMX_ErrorNone) { - ALOGE("GetVideoTunnelWindow failed! (err %d).", err); - return UNKNOWN_ERROR; + CLOG_ERROR(getParameter, err, "%s(%#x): %s:%u tun=%d sync=%u", name, index, + portString(portIndex), portIndex, tunneled, audioHwSync); + return StatusFromOMXError(err); } if (sidebandHandle) { *sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow; } - return err; + return OK; } status_t OMXNodeInstance::useBuffer( @@ -542,14 +632,14 @@ status_t OMXNodeInstance::useBuffer( params->size(), static_cast<OMX_U8 *>(params->pointer())); if (err != OMX_ErrorNone) { - ALOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err); + CLOG_ERROR(useBuffer, err, SIMPLE_BUFFER(portIndex, params->size(), params->pointer())); delete buffer_meta; buffer_meta = NULL; *buffer = 0; - return UNKNOWN_ERROR; + return StatusFromOMXError(err); } CHECK_EQ(header->pAppPrivate, buffer_meta); @@ -563,6 +653,8 @@ status_t OMXNodeInstance::useBuffer( bufferSource->addCodecBuffer(header); } + CLOG_BUFFER(useBuffer, NEW_BUFFER_FMT( + *buffer, portIndex, "%zu@%p", params->size(), params->pointer())); return OK; } @@ -572,17 +664,14 @@ status_t OMXNodeInstance::useGraphicBuffer2_l( // port definition OMX_PARAM_PORTDEFINITIONTYPE def; - def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); - def.nVersion.s.nVersionMajor = 1; - def.nVersion.s.nVersionMinor = 0; - def.nVersion.s.nRevision = 0; - def.nVersion.s.nStep = 0; + InitOMXParams(&def); def.nPortIndex = portIndex; OMX_ERRORTYPE err = OMX_GetParameter(mHandle, OMX_IndexParamPortDefinition, &def); - if (err != OMX_ErrorNone) - { - ALOGE("%s::%d:Error getting OMX_IndexParamPortDefinition", __FUNCTION__, __LINE__); - return err; + if (err != OMX_ErrorNone) { + OMX_INDEXTYPE index = OMX_IndexParamPortDefinition; + CLOG_ERROR(getParameter, err, "%s(%#x): %s:%u", + asString(index), index, portString(portIndex), portIndex); + return UNKNOWN_ERROR; } BufferMeta *bufferMeta = new BufferMeta(graphicBuffer); @@ -600,11 +689,11 @@ status_t OMXNodeInstance::useGraphicBuffer2_l( bufferHandle); if (err != OMX_ErrorNone) { - ALOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err); + CLOG_ERROR(useBuffer, err, BUFFER_FMT(portIndex, "%u@%p", def.nBufferSize, bufferHandle)); delete bufferMeta; bufferMeta = NULL; *buffer = 0; - return UNKNOWN_ERROR; + return StatusFromOMXError(err); } CHECK_EQ(header->pBuffer, bufferHandle); @@ -613,7 +702,8 @@ status_t OMXNodeInstance::useGraphicBuffer2_l( *buffer = makeBufferID(header); addActiveBuffer(portIndex, *buffer); - + CLOG_BUFFER(useGraphicBuffer2, NEW_BUFFER_FMT( + *buffer, portIndex, "%u@%p", def.nBufferSize, bufferHandle)); return OK; } @@ -637,10 +727,8 @@ status_t OMXNodeInstance::useGraphicBuffer( OMX_STRING name = const_cast<OMX_STRING>( "OMX.google.android.index.useAndroidNativeBuffer"); OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); - if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex %s failed", name); - + CLOG_ERROR(getExtensionIndex, err, "%s", name); return StatusFromOMXError(err); } @@ -661,15 +749,15 @@ status_t OMXNodeInstance::useGraphicBuffer( err = OMX_SetParameter(mHandle, index, ¶ms); if (err != OMX_ErrorNone) { - ALOGE("OMX_UseAndroidNativeBuffer failed with error %d (0x%08x)", err, - err); + CLOG_ERROR(setParameter, err, "%s(%#x): %s:%u meta=%p GB=%p", name, index, + portString(portIndex), portIndex, bufferMeta, graphicBuffer->handle); delete bufferMeta; bufferMeta = NULL; *buffer = 0; - return UNKNOWN_ERROR; + return StatusFromOMXError(err); } CHECK_EQ(header->pAppPrivate, bufferMeta); @@ -677,12 +765,13 @@ status_t OMXNodeInstance::useGraphicBuffer( *buffer = makeBufferID(header); addActiveBuffer(portIndex, *buffer); - + CLOG_BUFFER(useGraphicBuffer, NEW_BUFFER_FMT( + *buffer, portIndex, "GB=%p", graphicBuffer->handle)); return OK; } 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); @@ -693,7 +782,8 @@ status_t OMXNodeInstance::updateGraphicBufferInMeta( bufferMeta->setGraphicBuffer(graphicBuffer); metadata->eType = kMetadataBufferTypeGrallocSource; metadata->pHandle = graphicBuffer->handle; - + CLOG_BUFFER(updateGraphicBufferInMeta, "%s:%u, %#x := %p", + portString(portIndex), portIndex, buffer, graphicBuffer->handle); return OK; } @@ -719,19 +809,21 @@ status_t OMXNodeInstance::createInputSurface( // Retrieve the width and height of the graphic buffer, set when the // codec was configured. OMX_PARAM_PORTDEFINITIONTYPE def; - def.nSize = sizeof(def); - def.nVersion.s.nVersionMajor = 1; - def.nVersion.s.nVersionMinor = 0; - def.nVersion.s.nRevision = 0; - def.nVersion.s.nStep = 0; + InitOMXParams(&def); def.nPortIndex = portIndex; OMX_ERRORTYPE oerr = OMX_GetParameter( mHandle, OMX_IndexParamPortDefinition, &def); - CHECK(oerr == OMX_ErrorNone); + if (oerr != OMX_ErrorNone) { + OMX_INDEXTYPE index = OMX_IndexParamPortDefinition; + CLOG_ERROR(getParameter, oerr, "%s(%#x): %s:%u", + asString(index), index, portString(portIndex), portIndex); + return UNKNOWN_ERROR; + } if (def.format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque) { - ALOGE("createInputSurface requires COLOR_FormatSurface " - "(AndroidOpaque) color format"); + CLOGW("createInputSurface requires COLOR_FormatSurface " + "(AndroidOpaque) color format instead of %s(%#x)", + asString(def.format.video.eColorFormat), def.format.video.eColorFormat); return INVALID_OPERATION; } @@ -754,9 +846,9 @@ status_t OMXNodeInstance::signalEndOfInputStream() { // flag set). Seems easier than doing the equivalent from here. sp<GraphicBufferSource> bufferSource(getGraphicBufferSource()); if (bufferSource == NULL) { - ALOGW("signalEndOfInputStream can only be used with Surface input"); + CLOGW("signalEndOfInputStream can only be used with Surface input"); return INVALID_OPERATION; - }; + } return bufferSource->signalEndOfInputStream(); } @@ -773,14 +865,13 @@ status_t OMXNodeInstance::allocateBuffer( mHandle, &header, portIndex, buffer_meta, size); if (err != OMX_ErrorNone) { - ALOGE("OMX_AllocateBuffer failed with error %d (0x%08x)", err, err); - + CLOG_ERROR(allocateBuffer, err, BUFFER_FMT(portIndex, "%zu@", size)); delete buffer_meta; buffer_meta = NULL; *buffer = 0; - return UNKNOWN_ERROR; + return StatusFromOMXError(err); } CHECK_EQ(header->pAppPrivate, buffer_meta); @@ -794,6 +885,7 @@ status_t OMXNodeInstance::allocateBuffer( if (bufferSource != NULL && portIndex == kPortIndexInput) { bufferSource->addCodecBuffer(header); } + CLOG_BUFFER(allocateBuffer, NEW_BUFFER_FMT(*buffer, portIndex, "%zu@%p", size, *buffer_data)); return OK; } @@ -811,14 +903,14 @@ status_t OMXNodeInstance::allocateBufferWithBackup( mHandle, &header, portIndex, buffer_meta, params->size()); if (err != OMX_ErrorNone) { - ALOGE("OMX_AllocateBuffer failed with error %d (0x%08x)", err, err); - + CLOG_ERROR(allocateBufferWithBackup, err, + SIMPLE_BUFFER(portIndex, params->size(), params->pointer())); delete buffer_meta; buffer_meta = NULL; *buffer = 0; - return UNKNOWN_ERROR; + return StatusFromOMXError(err); } CHECK_EQ(header->pAppPrivate, buffer_meta); @@ -832,12 +924,16 @@ status_t OMXNodeInstance::allocateBufferWithBackup( bufferSource->addCodecBuffer(header); } + CLOG_BUFFER(allocateBufferWithBackup, NEW_BUFFER_FMT(*buffer, portIndex, "%zu@%p :> %p", + params->size(), params->pointer(), header->pBuffer)); + return OK; } status_t OMXNodeInstance::freeBuffer( OMX_U32 portIndex, OMX::buffer_id buffer) { Mutex::Autolock autoLock(mLock); + CLOG_BUFFER(freeBuffer, "%s:%u %#x", portString(portIndex), portIndex, buffer); removeActiveBuffer(portIndex, buffer); @@ -845,6 +941,7 @@ status_t OMXNodeInstance::freeBuffer( BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate); OMX_ERRORTYPE err = OMX_FreeBuffer(mHandle, portIndex, header); + CLOG_IF_ERROR(freeBuffer, err, "%s:%u %#x", portString(portIndex), portIndex, buffer); delete buffer_meta; buffer_meta = NULL; @@ -861,8 +958,18 @@ status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) { header->nOffset = 0; header->nFlags = 0; - OMX_ERRORTYPE err = OMX_FillThisBuffer(mHandle, header); + { + Mutex::Autolock _l(mDebugLock); + mOutputBuffersWithCodec.add(header); + CLOG_BUMPED_BUFFER(fillBuffer, WITH_STATS(EMPTY_BUFFER(buffer, header))); + } + OMX_ERRORTYPE err = OMX_FillThisBuffer(mHandle, header); + if (err != OMX_ErrorNone) { + CLOG_ERROR(fillBuffer, err, EMPTY_BUFFER(buffer, header)); + Mutex::Autolock _l(mDebugLock); + mOutputBuffersWithCodec.remove(header); + } return StatusFromOMXError(err); } @@ -875,14 +982,66 @@ status_t OMXNodeInstance::emptyBuffer( OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer); header->nFilledLen = rangeLength; header->nOffset = rangeOffset; - header->nFlags = flags; - header->nTimeStamp = timestamp; BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate); buffer_meta->CopyToOMX(header); + return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer); +} + +// log queued buffer activity for the next few input and/or output frames +// if logging at internal state level +void OMXNodeInstance::bumpDebugLevel_l(size_t numInputBuffers, size_t numOutputBuffers) { + if (DEBUG == ADebug::kDebugInternalState) { + DEBUG_BUMP = ADebug::kDebugAll; + if (numInputBuffers > 0) { + mDebugLevelBumpPendingBuffers[kPortIndexInput] = numInputBuffers; + } + if (numOutputBuffers > 0) { + mDebugLevelBumpPendingBuffers[kPortIndexOutput] = numOutputBuffers; + } + } +} + +void OMXNodeInstance::unbumpDebugLevel_l(size_t portIndex) { + if (mDebugLevelBumpPendingBuffers[portIndex]) { + --mDebugLevelBumpPendingBuffers[portIndex]; + } + if (!mDebugLevelBumpPendingBuffers[0] + && !mDebugLevelBumpPendingBuffers[1]) { + DEBUG_BUMP = DEBUG; + } +} + +status_t OMXNodeInstance::emptyBuffer_l( + OMX_BUFFERHEADERTYPE *header, OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr) { + header->nFlags = flags; + header->nTimeStamp = timestamp; + + { + Mutex::Autolock _l(mDebugLock); + mInputBuffersWithCodec.add(header); + + // bump internal-state debug level for 2 input frames past a buffer with CSD + if ((flags & OMX_BUFFERFLAG_CODECCONFIG) != 0) { + bumpDebugLevel_l(2 /* numInputBuffers */, 0 /* numOutputBuffers */); + } + + CLOG_BUMPED_BUFFER(emptyBuffer, WITH_STATS(FULL_BUFFER(debugAddr, header))); + } + OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header); + CLOG_IF_ERROR(emptyBuffer, err, FULL_BUFFER(debugAddr, header)); + + { + Mutex::Autolock _l(mDebugLock); + if (err != OMX_ErrorNone) { + mInputBuffersWithCodec.remove(header); + } else if (!(flags & OMX_BUFFERFLAG_CODECCONFIG)) { + unbumpDebugLevel_l(kPortIndexInput); + } + } return StatusFromOMXError(err); } @@ -896,15 +1055,8 @@ status_t OMXNodeInstance::emptyDirectBuffer( header->nFilledLen = rangeLength; header->nOffset = rangeOffset; - header->nFlags = flags; - header->nTimeStamp = timestamp; - - OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header); - if (err != OMX_ErrorNone) { - ALOGW("emptyDirectBuffer failed, OMX err=0x%x", err); - } - return StatusFromOMXError(err); + return emptyBuffer_l(header, flags, timestamp, (intptr_t)header->pBuffer); } status_t OMXNodeInstance::getExtensionIndex( @@ -917,15 +1069,31 @@ status_t OMXNodeInstance::getExtensionIndex( return StatusFromOMXError(err); } +inline static const char *asString(IOMX::InternalOptionType i, const char *def = "??") { + switch (i) { + case IOMX::INTERNAL_OPTION_SUSPEND: return "SUSPEND"; + case IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY: + return "REPEAT_PREVIOUS_FRAME_DELAY"; + case IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP: return "MAX_TIMESTAMP_GAP"; + case IOMX::INTERNAL_OPTION_MAX_FPS: return "MAX_FPS"; + case IOMX::INTERNAL_OPTION_START_TIME: return "START_TIME"; + case IOMX::INTERNAL_OPTION_TIME_LAPSE: return "TIME_LAPSE"; + default: return def; + } +} + status_t OMXNodeInstance::setInternalOption( OMX_U32 portIndex, IOMX::InternalOptionType type, const void *data, size_t size) { + CLOG_CONFIG(setInternalOption, "%s(%d): %s:%u %zu@%p", + asString(type), type, portString(portIndex), portIndex, size, data); switch (type) { case IOMX::INTERNAL_OPTION_SUSPEND: case IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY: case IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP: + case IOMX::INTERNAL_OPTION_MAX_FPS: case IOMX::INTERNAL_OPTION_START_TIME: case IOMX::INTERNAL_OPTION_TIME_LAPSE: { @@ -933,6 +1101,7 @@ status_t OMXNodeInstance::setInternalOption( getGraphicBufferSource(); if (bufferSource == NULL || portIndex != kPortIndexInput) { + CLOGW("setInternalOption is only for Surface input"); return ERROR_UNSUPPORTED; } @@ -942,6 +1111,7 @@ status_t OMXNodeInstance::setInternalOption( } bool suspend = *(bool *)data; + CLOG_CONFIG(setInternalOption, "suspend=%d", suspend); bufferSource->suspend(suspend); } else if (type == IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY){ @@ -950,7 +1120,7 @@ status_t OMXNodeInstance::setInternalOption( } int64_t delayUs = *(int64_t *)data; - + CLOG_CONFIG(setInternalOption, "delayUs=%lld", (long long)delayUs); return bufferSource->setRepeatPreviousFrameDelayUs(delayUs); } else if (type == IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP){ @@ -959,21 +1129,34 @@ status_t OMXNodeInstance::setInternalOption( } int64_t maxGapUs = *(int64_t *)data; - + CLOG_CONFIG(setInternalOption, "gapUs=%lld", (long long)maxGapUs); return bufferSource->setMaxTimestampGapUs(maxGapUs); + } else if (type == IOMX::INTERNAL_OPTION_MAX_FPS) { + if (size != sizeof(float)) { + return INVALID_OPERATION; + } + + float maxFps = *(float *)data; + CLOG_CONFIG(setInternalOption, "maxFps=%f", maxFps); + return bufferSource->setMaxFps(maxFps); } else if (type == IOMX::INTERNAL_OPTION_START_TIME) { if (size != sizeof(int64_t)) { return INVALID_OPERATION; } int64_t skipFramesBeforeUs = *(int64_t *)data; - + CLOG_CONFIG(setInternalOption, "beforeUs=%lld", (long long)skipFramesBeforeUs); bufferSource->setSkipFramesBeforeUs(skipFramesBeforeUs); } else { // IOMX::INTERNAL_OPTION_TIME_LAPSE if (size != sizeof(int64_t) * 2) { return INVALID_OPERATION; } + int64_t timePerFrameUs = ((int64_t *)data)[0]; + int64_t timePerCaptureUs = ((int64_t *)data)[1]; + CLOG_CONFIG(setInternalOption, "perFrameUs=%lld perCaptureUs=%lld", + (long long)timePerFrameUs, (long long)timePerCaptureUs); + bufferSource->setTimeLapseUs((int64_t *)data); } @@ -992,6 +1175,16 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { OMX_BUFFERHEADERTYPE *buffer = findBufferHeader(msg.u.extended_buffer_data.buffer); + { + Mutex::Autolock _l(mDebugLock); + mOutputBuffersWithCodec.remove(buffer); + + CLOG_BUMPED_BUFFER( + FBD, WITH_STATS(FULL_BUFFER(msg.u.extended_buffer_data.buffer, buffer))); + + unbumpDebugLevel_l(kPortIndexOutput); + } + BufferMeta *buffer_meta = static_cast<BufferMeta *>(buffer->pAppPrivate); @@ -1007,16 +1200,23 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { return; } } else if (msg.type == omx_message::EMPTY_BUFFER_DONE) { + OMX_BUFFERHEADERTYPE *buffer = + findBufferHeader(msg.u.buffer_data.buffer); + + { + Mutex::Autolock _l(mDebugLock); + mInputBuffersWithCodec.remove(buffer); + + CLOG_BUMPED_BUFFER( + EBD, WITH_STATS(EMPTY_BUFFER(msg.u.buffer_data.buffer, buffer))); + } + if (bufferSource != NULL) { // This is one of the buffers used exclusively by // GraphicBufferSource. // Don't dispatch a message back to ACodec, since it doesn't // know that anyone asked to have the buffer emptied and will // be very confused. - - OMX_BUFFERHEADERTYPE *buffer = - findBufferHeader(msg.u.buffer_data.buffer); - bufferSource->codecBufferEmptied(buffer); return; } @@ -1040,6 +1240,43 @@ void OMXNodeInstance::onGetHandleFailed() { // Don't try to acquire mLock here -- in rare circumstances this will hang. void OMXNodeInstance::onEvent( OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2) { + const char *arg1String = "??"; + const char *arg2String = "??"; + ADebug::Level level = ADebug::kDebugInternalState; + + switch (event) { + case OMX_EventCmdComplete: + arg1String = asString((OMX_COMMANDTYPE)arg1); + switch (arg1) { + case OMX_CommandStateSet: + arg2String = asString((OMX_STATETYPE)arg2); + level = ADebug::kDebugState; + break; + case OMX_CommandFlush: + case OMX_CommandPortEnable: + { + // bump internal-state debug level for 2 input and output frames + Mutex::Autolock _l(mDebugLock); + bumpDebugLevel_l(2 /* numInputBuffers */, 2 /* numOutputBuffers */); + } + // fall through + default: + arg2String = portString(arg2); + } + break; + case OMX_EventError: + arg1String = asString((OMX_ERRORTYPE)arg1); + level = ADebug::kDebugLifeCycle; + break; + case OMX_EventPortSettingsChanged: + arg2String = asString((OMX_INDEXEXTTYPE)arg2); + // fall through + default: + arg1String = portString(arg1); + } + + CLOGI_(level, onEvent, "%s(%x), %s(%x), %s(%x)", + asString(event), event, arg1String, arg1, arg2String, arg2); const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource()); if (bufferSource != NULL @@ -1097,23 +1334,27 @@ void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id) { active.mPortIndex = portIndex; active.mID = id; mActiveBuffers.push(active); + + if (portIndex < NELEM(mNumPortBuffers)) { + ++mNumPortBuffers[portIndex]; + } } void OMXNodeInstance::removeActiveBuffer( OMX_U32 portIndex, OMX::buffer_id id) { - bool found = false; for (size_t i = 0; i < mActiveBuffers.size(); ++i) { if (mActiveBuffers[i].mPortIndex == portIndex - && mActiveBuffers[i].mID == id) { - found = true; + && mActiveBuffers[i].mID == id) { mActiveBuffers.removeItemsAt(i); - break; + + if (portIndex < NELEM(mNumPortBuffers)) { + --mNumPortBuffers[portIndex]; + } + return; } } - if (!found) { - ALOGW("Attempt to remove an active buffer we know nothing about..."); - } + CLOGW("Attempt to remove an active buffer [%#x] we know nothing about...", id); } void OMXNodeInstance::freeActiveBuffers() { @@ -1134,7 +1375,7 @@ OMX::buffer_id OMXNodeInstance::makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) OMX::buffer_id buffer; do { // handle the very unlikely case of ID overflow if (++mBufferIDCount == 0) { - ++mBufferIDCount; + ++mBufferIDCount; } buffer = (OMX::buffer_id)mBufferIDCount; } while (mBufferIDToBufferHeader.indexOfKey(buffer) >= 0); diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp index 4999663..801a1bd 100644 --- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp +++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp @@ -58,7 +58,7 @@ OMX_ERRORTYPE SimpleSoftOMXComponent::sendCommand( OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) { CHECK(data == NULL); - sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler->id()); + sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler); msg->setInt32("cmd", cmd); msg->setInt32("param", param); msg->post(); @@ -152,28 +152,28 @@ OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter( OMX_PARAM_PORTDEFINITIONTYPE *defParams = (OMX_PARAM_PORTDEFINITIONTYPE *)params; - if (defParams->nPortIndex >= mPorts.size() - || defParams->nSize - != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { - return OMX_ErrorUndefined; + if (defParams->nPortIndex >= mPorts.size()) { + return OMX_ErrorBadPortIndex; + } + if (defParams->nSize != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { + return OMX_ErrorUnsupportedSetting; } PortInfo *port = &mPorts.editItemAt(defParams->nPortIndex); - if (defParams->nBufferSize != port->mDef.nBufferSize) { - CHECK_GE(defParams->nBufferSize, port->mDef.nBufferSize); + // default behavior is that we only allow buffer size to increase + if (defParams->nBufferSize > port->mDef.nBufferSize) { port->mDef.nBufferSize = defParams->nBufferSize; } - if (defParams->nBufferCountActual - != port->mDef.nBufferCountActual) { - CHECK_GE(defParams->nBufferCountActual, - port->mDef.nBufferCountMin); - - port->mDef.nBufferCountActual = defParams->nBufferCountActual; + if (defParams->nBufferCountActual < port->mDef.nBufferCountMin) { + ALOGW("component requires at least %u buffers (%u requested)", + port->mDef.nBufferCountMin, defParams->nBufferCountActual); + return OMX_ErrorUnsupportedSetting; } + port->mDef.nBufferCountActual = defParams->nBufferCountActual; return OMX_ErrorNone; } @@ -307,7 +307,7 @@ OMX_ERRORTYPE SimpleSoftOMXComponent::freeBuffer( OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer( OMX_BUFFERHEADERTYPE *buffer) { - sp<AMessage> msg = new AMessage(kWhatEmptyThisBuffer, mHandler->id()); + sp<AMessage> msg = new AMessage(kWhatEmptyThisBuffer, mHandler); msg->setPointer("header", buffer); msg->post(); @@ -316,7 +316,7 @@ OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer( OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer( OMX_BUFFERHEADERTYPE *buffer) { - sp<AMessage> msg = new AMessage(kWhatFillThisBuffer, mHandler->id()); + sp<AMessage> msg = new AMessage(kWhatFillThisBuffer, mHandler); msg->setPointer("header", buffer); msg->post(); diff --git a/media/libstagefright/omx/SoftOMXComponent.cpp b/media/libstagefright/omx/SoftOMXComponent.cpp index 646cd32..df978f8 100644 --- a/media/libstagefright/omx/SoftOMXComponent.cpp +++ b/media/libstagefright/omx/SoftOMXComponent.cpp @@ -283,7 +283,7 @@ OMX_ERRORTYPE SoftOMXComponent::setConfig( OMX_ERRORTYPE SoftOMXComponent::getExtensionIndex( const char * /* name */, OMX_INDEXTYPE * /* index */) { - return OMX_ErrorUndefined; + return OMX_ErrorUnsupportedIndex; } OMX_ERRORTYPE SoftOMXComponent::useBuffer( diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp index 2f83610..4ce165b 100644 --- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp @@ -26,6 +26,7 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/AUtils.h> #include <media/stagefright/MediaDefs.h> namespace android { @@ -61,6 +62,8 @@ SoftVideoDecoderOMXComponent::SoftVideoDecoderOMXComponent( mCropWidth(width), mCropHeight(height), mOutputPortSettingsChange(NONE), + mMinInputBufferSize(384), // arbitrary, using one uncompressed macroblock + mMinCompressionRatio(1), // max input size is normally the output size mComponentRole(componentRole), mCodingType(codingType), mProfileLevels(profileLevels), @@ -71,7 +74,11 @@ void SoftVideoDecoderOMXComponent::initPorts( OMX_U32 numInputBuffers, OMX_U32 inputBufferSize, OMX_U32 numOutputBuffers, - const char *mimeType) { + const char *mimeType, + OMX_U32 minCompressionRatio) { + mMinInputBufferSize = inputBufferSize; + mMinCompressionRatio = minCompressionRatio; + OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); @@ -120,27 +127,32 @@ void SoftVideoDecoderOMXComponent::initPorts( addPort(def); - updatePortDefinitions(); + updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */); } -void SoftVideoDecoderOMXComponent::updatePortDefinitions(bool updateCrop) { - OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; - def->format.video.nStride = def->format.video.nFrameWidth; - def->format.video.nSliceHeight = def->format.video.nFrameHeight; - - def->nBufferSize = def->format.video.nFrameWidth * def->format.video.nFrameHeight * 3 / 2; - - def = &editPortInfo(kOutputPortIndex)->mDef; - def->format.video.nFrameWidth = outputBufferWidth(); - def->format.video.nFrameHeight = outputBufferHeight(); - def->format.video.nStride = def->format.video.nFrameWidth; - def->format.video.nSliceHeight = def->format.video.nFrameHeight; - - def->nBufferSize = - (def->format.video.nFrameWidth * - def->format.video.nFrameHeight * 3) / 2; +void SoftVideoDecoderOMXComponent::updatePortDefinitions(bool updateCrop, bool updateInputSize) { + OMX_PARAM_PORTDEFINITIONTYPE *outDef = &editPortInfo(kOutputPortIndex)->mDef; + outDef->format.video.nFrameWidth = outputBufferWidth(); + outDef->format.video.nFrameHeight = outputBufferHeight(); + outDef->format.video.nStride = outDef->format.video.nFrameWidth; + outDef->format.video.nSliceHeight = outDef->format.video.nFrameHeight; + + outDef->nBufferSize = + (outDef->format.video.nStride * outDef->format.video.nSliceHeight * 3) / 2; + + OMX_PARAM_PORTDEFINITIONTYPE *inDef = &editPortInfo(kInputPortIndex)->mDef; + inDef->format.video.nFrameWidth = mWidth; + inDef->format.video.nFrameHeight = mHeight; + // input port is compressed, hence it has no stride + inDef->format.video.nStride = 0; + inDef->format.video.nSliceHeight = 0; + + // when output format changes, input buffer size does not actually change + if (updateInputSize) { + inDef->nBufferSize = max( + outDef->nBufferSize / mMinCompressionRatio, + max(mMinInputBufferSize, inDef->nBufferSize)); + } if (updateCrop) { mCropLeft = 0; @@ -169,7 +181,8 @@ void SoftVideoDecoderOMXComponent::handlePortSettingsChange( bool strideChanged = false; if (fakeStride) { OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; - if (def->format.video.nStride != width || def->format.video.nSliceHeight != height) { + if (def->format.video.nStride != (OMX_S32)width + || def->format.video.nSliceHeight != (OMX_U32)height) { strideChanged = true; } } @@ -252,7 +265,7 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter( (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; if (formatParams->nPortIndex > kMaxPortIndex) { - return OMX_ErrorUndefined; + return OMX_ErrorBadPortIndex; } if (formatParams->nIndex != 0) { @@ -324,13 +337,25 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; if (formatParams->nPortIndex > kMaxPortIndex) { - return OMX_ErrorUndefined; + return OMX_ErrorBadPortIndex; } if (formatParams->nIndex != 0) { return OMX_ErrorNoMore; } + if (formatParams->nPortIndex == kInputPortIndex) { + if (formatParams->eCompressionFormat != mCodingType + || formatParams->eColorFormat != OMX_COLOR_FormatUnused) { + return OMX_ErrorUnsupportedSetting; + } + } else { + if (formatParams->eCompressionFormat != OMX_VIDEO_CodingUnused + || formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) { + return OMX_ErrorUnsupportedSetting; + } + } + return OMX_ErrorNone; } @@ -348,7 +373,7 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( mAdaptiveMaxWidth = 0; mAdaptiveMaxHeight = 0; } - updatePortDefinitions(); + updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */); return OMX_ErrorNone; } @@ -365,23 +390,21 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( uint32_t newHeight = video_def->nFrameHeight; if (newWidth != oldWidth || newHeight != oldHeight) { bool outputPort = (newParams->nPortIndex == kOutputPortIndex); - def->format.video.nFrameWidth = - (mIsAdaptive && outputPort) ? mAdaptiveMaxWidth : newWidth; - def->format.video.nFrameHeight = - (mIsAdaptive && outputPort) ? mAdaptiveMaxHeight : newHeight; - def->format.video.nStride = def->format.video.nFrameWidth; - def->format.video.nSliceHeight = def->format.video.nFrameHeight; - def->nBufferSize = - def->format.video.nFrameWidth * def->format.video.nFrameHeight * 3 / 2; if (outputPort) { + // only update (essentially crop) if size changes mWidth = newWidth; mHeight = newHeight; - mCropLeft = 0; - mCropTop = 0; - mCropWidth = newWidth; - mCropHeight = newHeight; + + updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */); + // reset buffer size based on frame size + newParams->nBufferSize = def->nBufferSize; + } else { + // For input port, we only set nFrameWidth and nFrameHeight. Buffer size + // is updated when configuring the output port using the max-frame-size, + // though client can still request a larger size. + def->format.video.nFrameWidth = newWidth; + def->format.video.nFrameHeight = newHeight; } - newParams->nBufferSize = def->nBufferSize; } return SimpleSoftOMXComponent::internalSetParameter(index, params); } diff --git a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp index 70ec6e4..d4d6217 100644 --- a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp @@ -19,6 +19,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "SoftVideoEncoderOMXComponent" #include <utils/Log.h> +#include <utils/misc.h> #include "include/SoftVideoEncoderOMXComponent.h" @@ -27,6 +28,7 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/AUtils.h> #include <media/stagefright/MediaDefs.h> #include <ui/GraphicBuffer.h> @@ -34,13 +36,316 @@ namespace android { +const static OMX_COLOR_FORMATTYPE kSupportedColorFormats[] = { + OMX_COLOR_FormatYUV420Planar, + OMX_COLOR_FormatYUV420SemiPlanar, + OMX_COLOR_FormatAndroidOpaque +}; + +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; +} + SoftVideoEncoderOMXComponent::SoftVideoEncoderOMXComponent( const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, + int32_t width, + int32_t height, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) : SimpleSoftOMXComponent(name, callbacks, appData, component), - mGrallocModule(NULL) { + mInputDataIsMeta(false), + mWidth(width), + mHeight(height), + mBitrate(192000), + mFramerate(30 << 16), // Q16 format + mColorFormat(OMX_COLOR_FormatYUV420Planar), + mGrallocModule(NULL), + mMinOutputBufferSize(384), // arbitrary, using one uncompressed macroblock + mMinCompressionRatio(1), // max output size is normally the input size + mComponentRole(componentRole), + mCodingType(codingType), + mProfileLevels(profileLevels), + mNumProfileLevels(numProfileLevels) { +} + +void SoftVideoEncoderOMXComponent::initPorts( + OMX_U32 numInputBuffers, OMX_U32 numOutputBuffers, OMX_U32 outputBufferSize, + const char *mime, OMX_U32 minCompressionRatio) { + OMX_PARAM_PORTDEFINITIONTYPE def; + + mMinOutputBufferSize = outputBufferSize; + mMinCompressionRatio = minCompressionRatio; + + InitOMXParams(&def); + + def.nPortIndex = kInputPortIndex; + def.eDir = OMX_DirInput; + def.nBufferCountMin = numInputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.format.video.pNativeRender = NULL; + def.format.video.nFrameWidth = mWidth; + def.format.video.nFrameHeight = mHeight; + def.format.video.nStride = def.format.video.nFrameWidth; + def.format.video.nSliceHeight = def.format.video.nFrameHeight; + def.format.video.nBitrate = 0; + // frameRate is in Q16 format. + def.format.video.xFramerate = mFramerate; + def.format.video.bFlagErrorConcealment = OMX_FALSE; + def.nBufferAlignment = kInputBufferAlignment; + def.format.video.cMIMEType = const_cast<char *>("video/raw"); + def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; + def.format.video.eColorFormat = mColorFormat; + def.format.video.pNativeWindow = NULL; + // buffersize set in updatePortParams + + addPort(def); + + InitOMXParams(&def); + + def.nPortIndex = kOutputPortIndex; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = numOutputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.format.video.pNativeRender = NULL; + def.format.video.nFrameWidth = mWidth; + def.format.video.nFrameHeight = mHeight; + def.format.video.nStride = 0; + def.format.video.nSliceHeight = 0; + def.format.video.nBitrate = mBitrate; + def.format.video.xFramerate = 0 << 16; + def.format.video.bFlagErrorConcealment = OMX_FALSE; + def.nBufferAlignment = kOutputBufferAlignment; + def.format.video.cMIMEType = const_cast<char *>(mime); + def.format.video.eCompressionFormat = mCodingType; + def.format.video.eColorFormat = OMX_COLOR_FormatUnused; + def.format.video.pNativeWindow = NULL; + // buffersize set in updatePortParams + + addPort(def); + + updatePortParams(); +} + +void SoftVideoEncoderOMXComponent::updatePortParams() { + OMX_PARAM_PORTDEFINITIONTYPE *inDef = &editPortInfo(kInputPortIndex)->mDef; + inDef->format.video.nFrameWidth = mWidth; + inDef->format.video.nFrameHeight = mHeight; + inDef->format.video.nStride = inDef->format.video.nFrameWidth; + inDef->format.video.nSliceHeight = inDef->format.video.nFrameHeight; + inDef->format.video.xFramerate = mFramerate; + inDef->format.video.eColorFormat = mColorFormat; + uint32_t rawBufferSize = + inDef->format.video.nStride * inDef->format.video.nSliceHeight * 3 / 2; + if (inDef->format.video.eColorFormat == OMX_COLOR_FormatAndroidOpaque) { + inDef->nBufferSize = 4 + max(sizeof(buffer_handle_t), sizeof(GraphicBuffer *)); + } else { + inDef->nBufferSize = rawBufferSize; + } + + OMX_PARAM_PORTDEFINITIONTYPE *outDef = &editPortInfo(kOutputPortIndex)->mDef; + outDef->format.video.nFrameWidth = mWidth; + outDef->format.video.nFrameHeight = mHeight; + outDef->format.video.nBitrate = mBitrate; + + outDef->nBufferSize = max(mMinOutputBufferSize, rawBufferSize / mMinCompressionRatio); +} + +OMX_ERRORTYPE SoftVideoEncoderOMXComponent::internalSetPortParams( + const OMX_PARAM_PORTDEFINITIONTYPE *port) { + if (port->nPortIndex == kInputPortIndex) { + mWidth = port->format.video.nFrameWidth; + mHeight = port->format.video.nFrameHeight; + + // xFramerate comes in Q16 format, in frames per second unit + mFramerate = port->format.video.xFramerate; + + if (port->format.video.eCompressionFormat != OMX_VIDEO_CodingUnused + || (port->format.video.eColorFormat != OMX_COLOR_FormatYUV420Planar + && port->format.video.eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar + && port->format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque)) { + return OMX_ErrorUnsupportedSetting; + } + + mColorFormat = port->format.video.eColorFormat; + } else if (port->nPortIndex == kOutputPortIndex) { + if (port->format.video.eCompressionFormat != mCodingType + || port->format.video.eColorFormat != OMX_COLOR_FormatUnused) { + return OMX_ErrorUnsupportedSetting; + } + + mBitrate = port->format.video.nBitrate; + } else { + return OMX_ErrorBadPortIndex; + } + + updatePortParams(); + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftVideoEncoderOMXComponent::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR param) { + // can include extension index OMX_INDEXEXTTYPE + const int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamVideoErrorCorrection: + { + return OMX_ErrorNotImplemented; + } + + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)param; + + if (strncmp((const char *)roleParams->cRole, + mComponentRole, + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUnsupportedSetting; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamPortDefinition: + { + OMX_ERRORTYPE err = internalSetPortParams((const OMX_PARAM_PORTDEFINITIONTYPE *)param); + + if (err != OMX_ErrorNone) { + return err; + } + + return SimpleSoftOMXComponent::internalSetParameter(index, param); + } + + case OMX_IndexParamVideoPortFormat: + { + const OMX_VIDEO_PARAM_PORTFORMATTYPE* format = + (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)param; + + if (format->nPortIndex == kInputPortIndex) { + if (format->eColorFormat == OMX_COLOR_FormatYUV420Planar || + format->eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || + format->eColorFormat == OMX_COLOR_FormatAndroidOpaque) { + mColorFormat = format->eColorFormat; + + updatePortParams(); + return OMX_ErrorNone; + } else { + ALOGE("Unsupported color format %i", format->eColorFormat); + return OMX_ErrorUnsupportedSetting; + } + } else if (format->nPortIndex == kOutputPortIndex) { + if (format->eCompressionFormat == mCodingType) { + return OMX_ErrorNone; + } else { + return OMX_ErrorUnsupportedSetting; + } + } else { + return OMX_ErrorBadPortIndex; + } + } + + case kStoreMetaDataExtensionIndex: + { + // storeMetaDataInBuffers + const StoreMetaDataInBuffersParams *storeParam = + (const StoreMetaDataInBuffersParams *)param; + + if (storeParam->nPortIndex == kOutputPortIndex) { + return storeParam->bStoreMetaData ? OMX_ErrorUnsupportedSetting : OMX_ErrorNone; + } else if (storeParam->nPortIndex != kInputPortIndex) { + return OMX_ErrorBadPortIndex; + } + + mInputDataIsMeta = (storeParam->bStoreMetaData == OMX_TRUE); + if (mInputDataIsMeta) { + mColorFormat = OMX_COLOR_FormatAndroidOpaque; + } else if (mColorFormat == OMX_COLOR_FormatAndroidOpaque) { + mColorFormat = OMX_COLOR_FormatYUV420Planar; + } + updatePortParams(); + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, param); + } +} + +OMX_ERRORTYPE SoftVideoEncoderOMXComponent::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR param) { + switch (index) { + case OMX_IndexParamVideoErrorCorrection: + { + return OMX_ErrorNotImplemented; + } + + case OMX_IndexParamVideoPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)param; + + if (formatParams->nPortIndex == kInputPortIndex) { + if (formatParams->nIndex >= NELEM(kSupportedColorFormats)) { + return OMX_ErrorNoMore; + } + + // Color formats, in order of preference + formatParams->eColorFormat = kSupportedColorFormats[formatParams->nIndex]; + formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; + formatParams->xFramerate = mFramerate; + return OMX_ErrorNone; + } else if (formatParams->nPortIndex == kOutputPortIndex) { + formatParams->eCompressionFormat = mCodingType; + formatParams->eColorFormat = OMX_COLOR_FormatUnused; + formatParams->xFramerate = 0; + return OMX_ErrorNone; + } else { + return OMX_ErrorBadPortIndex; + } + } + + case OMX_IndexParamVideoProfileLevelQuerySupported: + { + OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = + (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) param; + + if (profileLevel->nPortIndex != kOutputPortIndex) { + ALOGE("Invalid port index: %u", profileLevel->nPortIndex); + return OMX_ErrorUnsupportedIndex; + } + + if (profileLevel->nProfileIndex >= mNumProfileLevels) { + return OMX_ErrorNoMore; + } + + profileLevel->eProfile = mProfileLevels[profileLevel->nProfileIndex].mProfile; + profileLevel->eLevel = mProfileLevels[profileLevel->nProfileIndex].mLevel; + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, param); + } } // static diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk index 447b29e..9be637a 100644 --- a/media/libstagefright/omx/tests/Android.mk +++ b/media/libstagefright/omx/tests/Android.mk @@ -20,3 +20,21 @@ LOCAL_MODULE_TAGS := tests LOCAL_32_BIT_ONLY := true include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_MODULE := FrameDropper_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + FrameDropper_test.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libstagefright_omx \ + libutils \ + +LOCAL_C_INCLUDES := \ + frameworks/av/media/libstagefright/omx \ + +include $(BUILD_NATIVE_TEST) diff --git a/media/libstagefright/omx/tests/FrameDropper_test.cpp b/media/libstagefright/omx/tests/FrameDropper_test.cpp new file mode 100644 index 0000000..4ac72c4 --- /dev/null +++ b/media/libstagefright/omx/tests/FrameDropper_test.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "FrameDropper_test" +#include <utils/Log.h> + +#include <gtest/gtest.h> + +#include "FrameDropper.h" +#include <media/stagefright/foundation/ADebug.h> + +namespace android { + +struct TestFrame { + int64_t timeUs; + bool shouldDrop; +}; + +static const TestFrame testFrames20Fps[] = { + {1000000, false}, {1050000, false}, {1100000, false}, {1150000, false}, + {1200000, false}, {1250000, false}, {1300000, false}, {1350000, false}, + {1400000, false}, {1450000, false}, {1500000, false}, {1550000, false}, + {1600000, false}, {1650000, false}, {1700000, false}, {1750000, false}, + {1800000, false}, {1850000, false}, {1900000, false}, {1950000, false}, +}; + +static const TestFrame testFrames30Fps[] = { + {1000000, false}, {1033333, false}, {1066667, false}, {1100000, false}, + {1133333, false}, {1166667, false}, {1200000, false}, {1233333, false}, + {1266667, false}, {1300000, false}, {1333333, false}, {1366667, false}, + {1400000, false}, {1433333, false}, {1466667, false}, {1500000, false}, + {1533333, false}, {1566667, false}, {1600000, false}, {1633333, false}, +}; + +static const TestFrame testFrames40Fps[] = { + {1000000, false}, {1025000, true}, {1050000, false}, {1075000, false}, + {1100000, false}, {1125000, true}, {1150000, false}, {1175000, false}, + {1200000, false}, {1225000, true}, {1250000, false}, {1275000, false}, + {1300000, false}, {1325000, true}, {1350000, false}, {1375000, false}, + {1400000, false}, {1425000, true}, {1450000, false}, {1475000, false}, +}; + +static const TestFrame testFrames60Fps[] = { + {1000000, false}, {1016667, true}, {1033333, false}, {1050000, true}, + {1066667, false}, {1083333, true}, {1100000, false}, {1116667, true}, + {1133333, false}, {1150000, true}, {1166667, false}, {1183333, true}, + {1200000, false}, {1216667, true}, {1233333, false}, {1250000, true}, + {1266667, false}, {1283333, true}, {1300000, false}, {1316667, true}, +}; + +static const TestFrame testFramesVariableFps[] = { + // 40fps + {1000000, false}, {1025000, true}, {1050000, false}, {1075000, false}, + {1100000, false}, {1125000, true}, {1150000, false}, {1175000, false}, + {1200000, false}, {1225000, true}, {1250000, false}, {1275000, false}, + {1300000, false}, {1325000, true}, {1350000, false}, {1375000, false}, + {1400000, false}, {1425000, true}, {1450000, false}, {1475000, false}, + // a timestamp jump plus switch to 20fps + {2000000, false}, {2050000, false}, {2100000, false}, {2150000, false}, + {2200000, false}, {2250000, false}, {2300000, false}, {2350000, false}, + {2400000, false}, {2450000, false}, {2500000, false}, {2550000, false}, + {2600000, false}, {2650000, false}, {2700000, false}, {2750000, false}, + {2800000, false}, {2850000, false}, {2900000, false}, {2950000, false}, + // 60fps + {2966667, false}, {2983333, true}, {3000000, false}, {3016667, true}, + {3033333, false}, {3050000, true}, {3066667, false}, {3083333, true}, + {3100000, false}, {3116667, true}, {3133333, false}, {3150000, true}, + {3166667, false}, {3183333, true}, {3200000, false}, {3216667, true}, + {3233333, false}, {3250000, true}, {3266667, false}, {3283333, true}, +}; + +static const int kMaxTestJitterUs = 2000; +// return one of 1000, 0, -1000 as jitter. +static int GetJitter(size_t i) { + return (1 - (i % 3)) * (kMaxTestJitterUs / 2); +} + +class FrameDropperTest : public ::testing::Test { +public: + FrameDropperTest() : mFrameDropper(new FrameDropper()) { + EXPECT_EQ(OK, mFrameDropper->setMaxFrameRate(30.0)); + } + +protected: + void RunTest(const TestFrame* frames, size_t size) { + for (size_t i = 0; i < size; ++i) { + int jitter = GetJitter(i); + int64_t testTimeUs = frames[i].timeUs + jitter; + printf("time %lld, testTime %lld, jitter %d\n", frames[i].timeUs, testTimeUs, jitter); + EXPECT_EQ(frames[i].shouldDrop, mFrameDropper->shouldDrop(testTimeUs)); + } + } + + sp<FrameDropper> mFrameDropper; +}; + +TEST_F(FrameDropperTest, TestInvalidMaxFrameRate) { + EXPECT_NE(OK, mFrameDropper->setMaxFrameRate(-1.0)); + EXPECT_NE(OK, mFrameDropper->setMaxFrameRate(0)); +} + +TEST_F(FrameDropperTest, Test20Fps) { + RunTest(testFrames20Fps, ARRAY_SIZE(testFrames20Fps)); +} + +TEST_F(FrameDropperTest, Test30Fps) { + RunTest(testFrames30Fps, ARRAY_SIZE(testFrames30Fps)); +} + +TEST_F(FrameDropperTest, Test40Fps) { + RunTest(testFrames40Fps, ARRAY_SIZE(testFrames40Fps)); +} + +TEST_F(FrameDropperTest, Test60Fps) { + RunTest(testFrames60Fps, ARRAY_SIZE(testFrames60Fps)); +} + +TEST_F(FrameDropperTest, TestVariableFps) { + RunTest(testFramesVariableFps, ARRAY_SIZE(testFramesVariableFps)); +} + +} // namespace android diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp index f4dfd6b..67ff145 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -253,29 +253,6 @@ static sp<MediaExtractor> CreateExtractorFromURI(const char *uri) { return MediaExtractor::Create(source); } -static sp<MediaSource> MakeSource( - const char *uri, - const char *mimeType) { - sp<MediaExtractor> extractor = CreateExtractorFromURI(uri); - - if (extractor == NULL) { - return NULL; - } - - for (size_t i = 0; i < extractor->countTracks(); ++i) { - sp<MetaData> meta = extractor->getTrackMetaData(i); - - const char *trackMIME; - CHECK(meta->findCString(kKeyMIMEType, &trackMIME)); - - if (!strcasecmp(trackMIME, mimeType)) { - return extractor->getTrack(i); - } - } - - return NULL; -} - status_t Harness::testStateTransitions( const char *componentName, const char *componentRole) { if (strncmp(componentName, "OMX.", 4)) { diff --git a/media/libstagefright/rtsp/AAMRAssembler.cpp b/media/libstagefright/rtsp/AAMRAssembler.cpp index 9e8725a..bb2a238 100644 --- a/media/libstagefright/rtsp/AAMRAssembler.cpp +++ b/media/libstagefright/rtsp/AAMRAssembler.cpp @@ -143,8 +143,8 @@ ARTPAssembler::AssemblyStatus AAMRAssembler::addPacket( return MALFORMED_PACKET; } - unsigned payloadHeader = buffer->data()[0]; - unsigned CMR = payloadHeader >> 4; + unsigned payloadHeader __unused = buffer->data()[0]; + unsigned CMR __unused = payloadHeader >> 4; Vector<uint8_t> tableOfContents; diff --git a/media/libstagefright/rtsp/AMPEG2TSAssembler.h b/media/libstagefright/rtsp/AMPEG2TSAssembler.h index 712e18e..f39c2b5 100644 --- a/media/libstagefright/rtsp/AMPEG2TSAssembler.h +++ b/media/libstagefright/rtsp/AMPEG2TSAssembler.h @@ -24,7 +24,7 @@ namespace android { struct AMessage; struct AString; -struct MetaData; +class MetaData; struct AMPEG2TSAssembler : public ARTPAssembler { AMPEG2TSAssembler( diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp index aa8ffc6..1f76068 100644 --- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp @@ -108,7 +108,7 @@ static status_t parseAudioObjectType( static status_t parseGASpecificConfig( ABitReader *bits, unsigned audioObjectType, unsigned channelConfiguration) { - unsigned frameLengthFlag = bits->getBits(1); + unsigned frameLengthFlag __unused = bits->getBits(1); unsigned dependsOnCoreCoder = bits->getBits(1); if (dependsOnCoreCoder) { /* unsigned coreCoderDelay = */bits->getBits(1); @@ -217,7 +217,7 @@ static status_t parseAudioSpecificConfig(ABitReader *bits, sp<ABuffer> *asc) { // Apparently an extension is always considered an even // multiple of 8 bits long. - ALOGI("Skipping %d bits after sync extension", + ALOGI("Skipping %zu bits after sync extension", 8 - (numBitsInExtension & 7)); bits->skipBits(8 - (numBitsInExtension & 7)); @@ -422,7 +422,7 @@ sp<ABuffer> AMPEG4AudioAssembler::removeLATMFraming(const sp<ABuffer> &buffer) { } if (offset < buffer->size()) { - ALOGI("ignoring %d bytes of trailing data", buffer->size() - offset); + ALOGI("ignoring %zu bytes of trailing data", buffer->size() - offset); } CHECK_LE(offset, buffer->size()); diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp index 7eb6542..156004c 100644 --- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp @@ -360,7 +360,7 @@ ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket( } if (offset != buffer->size()) { - ALOGW("potentially malformed packet (offset %d, size %d)", + ALOGW("potentially malformed packet (offset %zu, size %zu)", offset, buffer->size()); } } diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp index 09f52bc..cfafaa7 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -279,8 +279,6 @@ sp<ABuffer> MakeAACCodecSpecificData2(const char *params) { // be encoded. CHECK_LT(20 + config->size(), 128u); - const uint8_t *data = config->data(); - static const uint8_t kStaticESDS[] = { 0x03, 22, 0x00, 0x00, // ES_ID diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp index 372fbe9..a86ab74 100644 --- a/media/libstagefright/rtsp/ARTPConnection.cpp +++ b/media/libstagefright/rtsp/ARTPConnection.cpp @@ -82,7 +82,7 @@ void ARTPConnection::addStream( size_t index, const sp<AMessage> ¬ify, bool injected) { - sp<AMessage> msg = new AMessage(kWhatAddStream, id()); + sp<AMessage> msg = new AMessage(kWhatAddStream, this); msg->setInt32("rtp-socket", rtpSocket); msg->setInt32("rtcp-socket", rtcpSocket); msg->setObject("session-desc", sessionDesc); @@ -93,7 +93,7 @@ void ARTPConnection::addStream( } void ARTPConnection::removeStream(int rtpSocket, int rtcpSocket) { - sp<AMessage> msg = new AMessage(kWhatRemoveStream, id()); + sp<AMessage> msg = new AMessage(kWhatRemoveStream, this); msg->setInt32("rtp-socket", rtpSocket); msg->setInt32("rtcp-socket", rtcpSocket); msg->post(); @@ -233,7 +233,7 @@ void ARTPConnection::postPollEvent() { return; } - sp<AMessage> msg = new AMessage(kWhatPollStreams, id()); + sp<AMessage> msg = new AMessage(kWhatPollStreams, this); msg->post(); mPollEventPending = true; @@ -639,7 +639,7 @@ sp<ARTPSource> ARTPConnection::findSource(StreamInfo *info, uint32_t srcId) { } void ARTPConnection::injectPacket(int index, const sp<ABuffer> &buffer) { - sp<AMessage> msg = new AMessage(kWhatInjectPacket, id()); + sp<AMessage> msg = new AMessage(kWhatInjectPacket, this); msg->setInt32("index", index); msg->setBuffer("buffer", buffer); msg->post(); @@ -664,11 +664,10 @@ void ARTPConnection::onInjectPacket(const sp<AMessage> &msg) { StreamInfo *s = &*it; - status_t err; if (it->mRTPSocket == index) { - err = parseRTP(s, buffer); + parseRTP(s, buffer); } else { - err = parseRTCP(s, buffer); + parseRTCP(s, buffer); } } diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp index ba4e33c..e5acb06 100644 --- a/media/libstagefright/rtsp/ARTPSession.cpp +++ b/media/libstagefright/rtsp/ARTPSession.cpp @@ -82,7 +82,7 @@ status_t ARTPSession::setup(const sp<ASessionDescription> &desc) { info->mRTPSocket = rtpSocket; info->mRTCPSocket = rtcpSocket; - sp<AMessage> notify = new AMessage(kWhatAccessUnitComplete, id()); + sp<AMessage> notify = new AMessage(kWhatAccessUnitComplete, this); notify->setSize("track-index", mTracks.size() - 1); mRTPConn->addStream( diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp index 793d116..56c4aa6 100644 --- a/media/libstagefright/rtsp/ARTPWriter.cpp +++ b/media/libstagefright/rtsp/ARTPWriter.cpp @@ -146,7 +146,7 @@ status_t ARTPWriter::start(MetaData * /* params */) { TRESPASS(); } - (new AMessage(kWhatStart, mReflector->id()))->post(); + (new AMessage(kWhatStart, mReflector))->post(); while (!(mFlags & kFlagStarted)) { mCondition.wait(mLock); @@ -161,7 +161,7 @@ status_t ARTPWriter::stop() { return OK; } - (new AMessage(kWhatStop, mReflector->id()))->post(); + (new AMessage(kWhatStop, mReflector))->post(); while (mFlags & kFlagStarted) { mCondition.wait(mLock); @@ -213,8 +213,8 @@ void ARTPWriter::onMessageReceived(const sp<AMessage> &msg) { mCondition.signal(); } - (new AMessage(kWhatRead, mReflector->id()))->post(); - (new AMessage(kWhatSendSR, mReflector->id()))->post(); + (new AMessage(kWhatRead, mReflector))->post(); + (new AMessage(kWhatSendSR, mReflector))->post(); break; } @@ -461,7 +461,7 @@ void ARTPWriter::dumpSessionDesc() { sdp.append("m=audio "); } - sdp.append(StringPrintf("%d", ntohs(mRTPAddr.sin_port))); + sdp.append(AStringPrintf("%d", ntohs(mRTPAddr.sin_port))); sdp.append( " RTP/AVP " PT_STR "\r\n" "b=AS 320000\r\n" @@ -480,7 +480,7 @@ void ARTPWriter::dumpSessionDesc() { CHECK_EQ(sampleRate, (mMode == AMR_NB) ? 8000 : 16000); sdp.append(mMode == AMR_NB ? "AMR" : "AMR-WB"); - sdp.append(StringPrintf("/%d/%d", sampleRate, numChannels)); + sdp.append(AStringPrintf("/%d/%d", sampleRate, numChannels)); } else { TRESPASS(); } @@ -543,7 +543,7 @@ void ARTPWriter::makeH264SPropParamSets(MediaBuffer *buffer) { CHECK_EQ((unsigned)data[0], 0x67u); mProfileLevel = - StringPrintf("%02X%02X%02X", data[1], data[2], data[3]); + AStringPrintf("%02X%02X%02X", data[1], data[2], data[3]); encodeBase64(data, startCodePos, &mSeqParamSet); diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index f25539c..855ffdc 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -42,7 +42,7 @@ const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll; // static const AString ARTSPConnection::sUserAgent = - StringPrintf("User-Agent: %s\r\n", MakeUserAgent().c_str()); + AStringPrintf("User-Agent: %s\r\n", MakeUserAgent().c_str()); ARTSPConnection::ARTSPConnection(bool uidValid, uid_t uid) : mUIDValid(uidValid), @@ -68,28 +68,28 @@ ARTSPConnection::~ARTSPConnection() { } void ARTSPConnection::connect(const char *url, const sp<AMessage> &reply) { - sp<AMessage> msg = new AMessage(kWhatConnect, id()); + sp<AMessage> msg = new AMessage(kWhatConnect, this); msg->setString("url", url); msg->setMessage("reply", reply); msg->post(); } void ARTSPConnection::disconnect(const sp<AMessage> &reply) { - sp<AMessage> msg = new AMessage(kWhatDisconnect, id()); + sp<AMessage> msg = new AMessage(kWhatDisconnect, this); msg->setMessage("reply", reply); msg->post(); } void ARTSPConnection::sendRequest( const char *request, const sp<AMessage> &reply) { - sp<AMessage> msg = new AMessage(kWhatSendRequest, id()); + sp<AMessage> msg = new AMessage(kWhatSendRequest, this); msg->setString("request", request); msg->setMessage("reply", reply); msg->post(); } void ARTSPConnection::observeBinaryData(const sp<AMessage> &reply) { - sp<AMessage> msg = new AMessage(kWhatObserveBinaryData, id()); + sp<AMessage> msg = new AMessage(kWhatObserveBinaryData, this); msg->setMessage("reply", reply); msg->post(); } @@ -286,7 +286,7 @@ void ARTSPConnection::onConnect(const sp<AMessage> &msg) { if (err < 0) { if (errno == EINPROGRESS) { - sp<AMessage> msg = new AMessage(kWhatCompleteConnection, id()); + sp<AMessage> msg = new AMessage(kWhatCompleteConnection, this); msg->setMessage("reply", reply); msg->setInt32("connection-id", mConnectionID); msg->post(); @@ -523,7 +523,7 @@ void ARTSPConnection::postReceiveReponseEvent() { return; } - sp<AMessage> msg = new AMessage(kWhatReceiveResponse, id()); + sp<AMessage> msg = new AMessage(kWhatReceiveResponse, this); msg->post(); mReceiveResponseEventPending = true; @@ -746,7 +746,7 @@ bool ARTSPConnection::receiveRTSPReponse() { AString request; CHECK(reply->findString("original-request", &request)); - sp<AMessage> msg = new AMessage(kWhatSendRequest, id()); + sp<AMessage> msg = new AMessage(kWhatSendRequest, this); msg->setMessage("reply", reply); msg->setString("request", request.c_str(), request.size()); diff --git a/media/libstagefright/rtsp/ARawAudioAssembler.h b/media/libstagefright/rtsp/ARawAudioAssembler.h index ed7af08..bc1dea6 100644 --- a/media/libstagefright/rtsp/ARawAudioAssembler.h +++ b/media/libstagefright/rtsp/ARawAudioAssembler.h @@ -24,7 +24,7 @@ namespace android { struct AMessage; struct AString; -struct MetaData; +class MetaData; struct ARawAudioAssembler : public ARTPAssembler { ARawAudioAssembler( diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk index d60dc2f..9fedb71 100644 --- a/media/libstagefright/rtsp/Android.mk +++ b/media/libstagefright/rtsp/Android.mk @@ -19,10 +19,11 @@ LOCAL_SRC_FILES:= \ ASessionDescription.cpp \ SDPLoader.cpp \ +LOCAL_SHARED_LIBRARIES += libcrypto + LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/media/libstagefright \ - $(TOP)/frameworks/native/include/media/openmax \ - $(TOP)/external/openssl/include + $(TOP)/frameworks/native/include/media/openmax LOCAL_MODULE:= libstagefright_rtsp diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 423a420..0642343 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -156,7 +156,7 @@ struct MyHandler : public AHandler { mSessionURL.append("rtsp://"); mSessionURL.append(host); mSessionURL.append(":"); - mSessionURL.append(StringPrintf("%u", port)); + mSessionURL.append(AStringPrintf("%u", port)); mSessionURL.append(path); ALOGV("rewritten session url: '%s'", mSessionURL.c_str()); @@ -169,10 +169,10 @@ struct MyHandler : public AHandler { looper()->registerHandler(mConn); (1 ? mNetLooper : looper())->registerHandler(mRTPConn); - sp<AMessage> notify = new AMessage('biny', id()); + sp<AMessage> notify = new AMessage('biny', this); mConn->observeBinaryData(notify); - sp<AMessage> reply = new AMessage('conn', id()); + sp<AMessage> reply = new AMessage('conn', this); mConn->connect(mOriginalSessionURL.c_str(), reply); } @@ -180,10 +180,10 @@ struct MyHandler : public AHandler { looper()->registerHandler(mConn); (1 ? mNetLooper : looper())->registerHandler(mRTPConn); - sp<AMessage> notify = new AMessage('biny', id()); + sp<AMessage> notify = new AMessage('biny', this); mConn->observeBinaryData(notify); - sp<AMessage> reply = new AMessage('sdpl', id()); + sp<AMessage> reply = new AMessage('sdpl', this); reply->setObject("description", desc); mConn->connect(mOriginalSessionURL.c_str(), reply); } @@ -210,11 +210,11 @@ struct MyHandler : public AHandler { } void disconnect() { - (new AMessage('abor', id()))->post(); + (new AMessage('abor', this))->post(); } void seek(int64_t timeUs) { - sp<AMessage> msg = new AMessage('seek', id()); + sp<AMessage> msg = new AMessage('seek', this); msg->setInt64("time", timeUs); mPauseGeneration++; msg->post(); @@ -225,14 +225,14 @@ struct MyHandler : public AHandler { } void pause() { - sp<AMessage> msg = new AMessage('paus', id()); + sp<AMessage> msg = new AMessage('paus', this); mPauseGeneration++; msg->setInt32("pausecheck", mPauseGeneration); msg->post(kPauseDelayUs); } void resume() { - sp<AMessage> msg = new AMessage('resu', id()); + sp<AMessage> msg = new AMessage('resu', this); mPauseGeneration++; msg->post(); } @@ -454,10 +454,10 @@ struct MyHandler : public AHandler { request.append("Accept: application/sdp\r\n"); request.append("\r\n"); - sp<AMessage> reply = new AMessage('desc', id()); + sp<AMessage> reply = new AMessage('desc', this); mConn->sendRequest(request.c_str(), reply); } else { - (new AMessage('disc', id()))->post(); + (new AMessage('disc', this))->post(); } break; } @@ -468,10 +468,10 @@ struct MyHandler : public AHandler { int32_t reconnect; if (msg->findInt32("reconnect", &reconnect) && reconnect) { - sp<AMessage> reply = new AMessage('conn', id()); + sp<AMessage> reply = new AMessage('conn', this); mConn->connect(mOriginalSessionURL.c_str(), reply); } else { - (new AMessage('quit', id()))->post(); + (new AMessage('quit', this))->post(); } break; } @@ -508,13 +508,13 @@ struct MyHandler : public AHandler { mSessionURL.append("rtsp://"); mSessionURL.append(host); mSessionURL.append(":"); - mSessionURL.append(StringPrintf("%u", port)); + mSessionURL.append(AStringPrintf("%u", port)); mSessionURL.append(path); ALOGI("rewritten session url: '%s'", mSessionURL.c_str()); } - sp<AMessage> reply = new AMessage('conn', id()); + sp<AMessage> reply = new AMessage('conn', this); mConn->connect(mOriginalSessionURL.c_str(), reply); break; } @@ -586,7 +586,7 @@ struct MyHandler : public AHandler { } if (result != OK) { - sp<AMessage> reply = new AMessage('disc', id()); + sp<AMessage> reply = new AMessage('disc', this); mConn->disconnect(reply); } break; @@ -631,7 +631,7 @@ struct MyHandler : public AHandler { } if (result != OK) { - sp<AMessage> reply = new AMessage('disc', id()); + sp<AMessage> reply = new AMessage('disc', this); mConn->disconnect(reply); } break; @@ -703,7 +703,7 @@ struct MyHandler : public AHandler { mSessionID.erase(i, mSessionID.size() - i); } - sp<AMessage> notify = new AMessage('accu', id()); + sp<AMessage> notify = new AMessage('accu', this); notify->setSize("track-index", trackIndex); i = response->mHeaders.indexOfKey("transport"); @@ -769,10 +769,10 @@ struct MyHandler : public AHandler { request.append("\r\n"); - sp<AMessage> reply = new AMessage('play', id()); + sp<AMessage> reply = new AMessage('play', this); mConn->sendRequest(request.c_str(), reply); } else { - sp<AMessage> reply = new AMessage('disc', id()); + sp<AMessage> reply = new AMessage('disc', this); mConn->disconnect(reply); } break; @@ -797,7 +797,7 @@ struct MyHandler : public AHandler { } else { parsePlayResponse(response); - sp<AMessage> timeout = new AMessage('tiou', id()); + sp<AMessage> timeout = new AMessage('tiou', this); mCheckTimeoutGeneration++; timeout->setInt32("tioucheck", mCheckTimeoutGeneration); timeout->post(kStartupTimeoutUs); @@ -805,7 +805,7 @@ struct MyHandler : public AHandler { } if (result != OK) { - sp<AMessage> reply = new AMessage('disc', id()); + sp<AMessage> reply = new AMessage('disc', this); mConn->disconnect(reply); } @@ -831,7 +831,7 @@ struct MyHandler : public AHandler { request.append("\r\n"); request.append("\r\n"); - sp<AMessage> reply = new AMessage('opts', id()); + sp<AMessage> reply = new AMessage('opts', this); reply->setInt32("generation", mKeepAliveGeneration); mConn->sendRequest(request.c_str(), reply); break; @@ -894,7 +894,7 @@ struct MyHandler : public AHandler { mPausing = false; mSeekable = true; - sp<AMessage> reply = new AMessage('tear', id()); + sp<AMessage> reply = new AMessage('tear', this); int32_t reconnect; if (msg->findInt32("reconnect", &reconnect) && reconnect) { @@ -926,7 +926,7 @@ struct MyHandler : public AHandler { ALOGI("TEARDOWN completed with result %d (%s)", result, strerror(-result)); - sp<AMessage> reply = new AMessage('disc', id()); + sp<AMessage> reply = new AMessage('disc', this); int32_t reconnect; if (msg->findInt32("reconnect", &reconnect) && reconnect) { @@ -958,7 +958,7 @@ struct MyHandler : public AHandler { if (mNumAccessUnitsReceived == 0) { #if 1 ALOGI("stream ended? aborting."); - (new AMessage('abor', id()))->post(); + (new AMessage('abor', this))->post(); break; #else ALOGI("haven't seen an AU in a looong time."); @@ -1077,7 +1077,7 @@ struct MyHandler : public AHandler { request.append("\r\n"); - sp<AMessage> reply = new AMessage('pau2', id()); + sp<AMessage> reply = new AMessage('pau2', this); mConn->sendRequest(request.c_str(), reply); break; } @@ -1114,7 +1114,7 @@ struct MyHandler : public AHandler { request.append("\r\n"); - sp<AMessage> reply = new AMessage('res2', id()); + sp<AMessage> reply = new AMessage('res2', this); mConn->sendRequest(request.c_str(), reply); break; } @@ -1143,7 +1143,7 @@ struct MyHandler : public AHandler { // Post new timeout in order to make sure to use // fake timestamps if no new Sender Reports arrive - sp<AMessage> timeout = new AMessage('tiou', id()); + sp<AMessage> timeout = new AMessage('tiou', this); mCheckTimeoutGeneration++; timeout->setInt32("tioucheck", mCheckTimeoutGeneration); timeout->post(kStartupTimeoutUs); @@ -1152,7 +1152,7 @@ struct MyHandler : public AHandler { if (result != OK) { ALOGE("resume failed, aborting."); - (new AMessage('abor', id()))->post(); + (new AMessage('abor', this))->post(); } mPausing = false; @@ -1180,7 +1180,7 @@ struct MyHandler : public AHandler { mCheckPending = true; ++mCheckGeneration; - sp<AMessage> reply = new AMessage('see1', id()); + sp<AMessage> reply = new AMessage('see1', this); reply->setInt64("time", timeUs); if (mPausing) { @@ -1221,7 +1221,7 @@ struct MyHandler : public AHandler { // Start new timeoutgeneration to avoid getting timeout // before PLAY response arrive - sp<AMessage> timeout = new AMessage('tiou', id()); + sp<AMessage> timeout = new AMessage('tiou', this); mCheckTimeoutGeneration++; timeout->setInt32("tioucheck", mCheckTimeoutGeneration); timeout->post(kStartupTimeoutUs); @@ -1238,12 +1238,12 @@ struct MyHandler : public AHandler { request.append("\r\n"); request.append( - StringPrintf( + AStringPrintf( "Range: npt=%lld-\r\n", timeUs / 1000000ll)); request.append("\r\n"); - sp<AMessage> reply = new AMessage('see2', id()); + sp<AMessage> reply = new AMessage('see2', this); mConn->sendRequest(request.c_str(), reply); break; } @@ -1277,7 +1277,7 @@ struct MyHandler : public AHandler { // Post new timeout in order to make sure to use // fake timestamps if no new Sender Reports arrive - sp<AMessage> timeout = new AMessage('tiou', id()); + sp<AMessage> timeout = new AMessage('tiou', this); mCheckTimeoutGeneration++; timeout->setInt32("tioucheck", mCheckTimeoutGeneration); timeout->post(kStartupTimeoutUs); @@ -1293,7 +1293,7 @@ struct MyHandler : public AHandler { if (result != OK) { ALOGE("seek failed, aborting."); - (new AMessage('abor', id()))->post(); + (new AMessage('abor', this))->post(); } mPausing = false; @@ -1343,12 +1343,12 @@ struct MyHandler : public AHandler { mTryTCPInterleaving = true; - sp<AMessage> msg = new AMessage('abor', id()); + sp<AMessage> msg = new AMessage('abor', this); msg->setInt32("reconnect", true); msg->post(); } else { ALOGW("Never received any data, disconnecting."); - (new AMessage('abor', id()))->post(); + (new AMessage('abor', this))->post(); } } else { if (!mAllTracksHaveTime) { @@ -1369,7 +1369,7 @@ struct MyHandler : public AHandler { } void postKeepAlive() { - sp<AMessage> msg = new AMessage('aliv', id()); + sp<AMessage> msg = new AMessage('aliv', this); msg->setInt32("generation", mKeepAliveGeneration); msg->post((mKeepAliveTimeoutUs * 9) / 10); } @@ -1380,7 +1380,7 @@ struct MyHandler : public AHandler { } mCheckPending = true; - sp<AMessage> check = new AMessage('chek', id()); + sp<AMessage> check = new AMessage('chek', this); check->setInt32("generation", mCheckGeneration); check->post(kAccessUnitTimeoutUs); } @@ -1566,7 +1566,7 @@ private: if (source->initCheck() != OK) { ALOGW("Unsupported format. Ignoring track #%d.", index); - sp<AMessage> reply = new AMessage('setu', id()); + sp<AMessage> reply = new AMessage('setu', this); reply->setSize("index", index); reply->setInt32("result", ERROR_UNSUPPORTED); reply->post(); @@ -1652,7 +1652,7 @@ private: request.append("\r\n"); - sp<AMessage> reply = new AMessage('setu', id()); + sp<AMessage> reply = new AMessage('setu', this); reply->setSize("index", index); reply->setSize("track-index", mTracks.size() - 1); mConn->sendRequest(request.c_str(), reply); diff --git a/media/libstagefright/rtsp/MyTransmitter.h b/media/libstagefright/rtsp/MyTransmitter.h index 009a3b1..369f276 100644 --- a/media/libstagefright/rtsp/MyTransmitter.h +++ b/media/libstagefright/rtsp/MyTransmitter.h @@ -100,7 +100,7 @@ struct MyTransmitter : public AHandler { mLooper->registerHandler(this); mLooper->registerHandler(mConn); - sp<AMessage> reply = new AMessage('conn', id()); + sp<AMessage> reply = new AMessage('conn', this); mConn->connect(mServerURL.c_str(), reply); #ifdef ANDROID @@ -229,7 +229,7 @@ struct MyTransmitter : public AHandler { request.append("\r\n"); request.append(sdp); - sp<AMessage> reply = new AMessage('anno', id()); + sp<AMessage> reply = new AMessage('anno', this); mConn->sendRequest(request.c_str(), reply); } @@ -350,7 +350,7 @@ struct MyTransmitter : public AHandler { << result << " (" << strerror(-result) << ")"; if (result != OK) { - (new AMessage('quit', id()))->post(); + (new AMessage('quit', this))->post(); break; } @@ -381,7 +381,7 @@ struct MyTransmitter : public AHandler { if (response->mStatusCode == 401) { if (mAuthType != NONE) { LOG(INFO) << "FAILED to authenticate"; - (new AMessage('quit', id()))->post(); + (new AMessage('quit', this))->post(); break; } @@ -391,14 +391,14 @@ struct MyTransmitter : public AHandler { } if (result != OK || response->mStatusCode != 200) { - (new AMessage('quit', id()))->post(); + (new AMessage('quit', this))->post(); break; } unsigned rtpPort; ARTPConnection::MakePortPair(&mRTPSocket, &mRTCPSocket, &rtpPort); - // (new AMessage('poll', id()))->post(); + // (new AMessage('poll', this))->post(); AString request; request.append("SETUP "); @@ -414,7 +414,7 @@ struct MyTransmitter : public AHandler { request.append(";mode=record\r\n"); request.append("\r\n"); - sp<AMessage> reply = new AMessage('setu', id()); + sp<AMessage> reply = new AMessage('setu', this); mConn->sendRequest(request.c_str(), reply); break; } @@ -468,7 +468,7 @@ struct MyTransmitter : public AHandler { } if (result != OK || response->mStatusCode != 200) { - (new AMessage('quit', id()))->post(); + (new AMessage('quit', this))->post(); break; } @@ -535,7 +535,7 @@ struct MyTransmitter : public AHandler { request.append("\r\n"); request.append("\r\n"); - sp<AMessage> reply = new AMessage('reco', id()); + sp<AMessage> reply = new AMessage('reco', this); mConn->sendRequest(request.c_str(), reply); break; } @@ -558,13 +558,13 @@ struct MyTransmitter : public AHandler { } if (result != OK) { - (new AMessage('quit', id()))->post(); + (new AMessage('quit', this))->post(); break; } - (new AMessage('more', id()))->post(); - (new AMessage('sr ', id()))->post(); - (new AMessage('aliv', id()))->post(30000000ll); + (new AMessage('more', this))->post(); + (new AMessage('sr ', this))->post(); + (new AMessage('aliv', this))->post(30000000ll); break; } @@ -586,7 +586,7 @@ struct MyTransmitter : public AHandler { request.append("\r\n"); request.append("\r\n"); - sp<AMessage> reply = new AMessage('opts', id()); + sp<AMessage> reply = new AMessage('opts', this); mConn->sendRequest(request.c_str(), reply); break; } @@ -603,7 +603,7 @@ struct MyTransmitter : public AHandler { break; } - (new AMessage('aliv', id()))->post(30000000ll); + (new AMessage('aliv', this))->post(30000000ll); break; } @@ -702,7 +702,7 @@ struct MyTransmitter : public AHandler { request.append("\r\n"); request.append("\r\n"); - sp<AMessage> reply = new AMessage('paus', id()); + sp<AMessage> reply = new AMessage('paus', this); mConn->sendRequest(request.c_str(), reply); } break; @@ -753,7 +753,7 @@ struct MyTransmitter : public AHandler { request.append("\r\n"); request.append("\r\n"); - sp<AMessage> reply = new AMessage('tear', id()); + sp<AMessage> reply = new AMessage('tear', this); mConn->sendRequest(request.c_str(), reply); break; } @@ -775,7 +775,7 @@ struct MyTransmitter : public AHandler { CHECK(response != NULL); } - (new AMessage('quit', id()))->post(); + (new AMessage('quit', this))->post(); break; } @@ -784,14 +784,14 @@ struct MyTransmitter : public AHandler { LOG(INFO) << "disconnect completed"; mConnected = false; - (new AMessage('quit', id()))->post(); + (new AMessage('quit', this))->post(); break; } case 'quit': { if (mConnected) { - mConn->disconnect(new AMessage('disc', id())); + mConn->disconnect(new AMessage('disc', this)); break; } diff --git a/media/libstagefright/rtsp/SDPLoader.cpp b/media/libstagefright/rtsp/SDPLoader.cpp index 424badf..0f46c83 100644 --- a/media/libstagefright/rtsp/SDPLoader.cpp +++ b/media/libstagefright/rtsp/SDPLoader.cpp @@ -51,7 +51,7 @@ SDPLoader::SDPLoader( void SDPLoader::load(const char *url, const KeyedVector<String8, String8> *headers) { mNetLooper->registerHandler(this); - sp<AMessage> msg = new AMessage(kWhatLoad, id()); + sp<AMessage> msg = new AMessage(kWhatLoad, this); msg->setString("url", url); if (headers != NULL) { @@ -105,7 +105,7 @@ void SDPLoader::onLoad(const sp<AMessage> &msg) { headers = NULL; } - off64_t sdpSize; + off64_t sdpSize = 0; if (err == OK && !mCancelled) { err = mHTTPDataSource->getSize(&sdpSize); diff --git a/media/libstagefright/rtsp/UDPPusher.cpp b/media/libstagefright/rtsp/UDPPusher.cpp index 47ea6f1..5c685a1 100644 --- a/media/libstagefright/rtsp/UDPPusher.cpp +++ b/media/libstagefright/rtsp/UDPPusher.cpp @@ -65,7 +65,7 @@ void UDPPusher::start() { mFirstTimeMs = fromlel(timeMs); mFirstTimeUs = ALooper::GetNowUs(); - (new AMessage(kWhatPush, id()))->post(); + (new AMessage(kWhatPush, this))->post(); } bool UDPPusher::onPush() { @@ -103,7 +103,7 @@ bool UDPPusher::onPush() { timeMs -= mFirstTimeMs; int64_t whenUs = mFirstTimeUs + timeMs * 1000ll; int64_t nowUs = ALooper::GetNowUs(); - (new AMessage(kWhatPush, id()))->post(whenUs - nowUs); + (new AMessage(kWhatPush, this))->post(whenUs - nowUs); return true; } diff --git a/media/libstagefright/tests/Utils_test.cpp b/media/libstagefright/tests/Utils_test.cpp index f2825dd..5c323c1 100644 --- a/media/libstagefright/tests/Utils_test.cpp +++ b/media/libstagefright/tests/Utils_test.cpp @@ -24,6 +24,7 @@ #include <unistd.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AStringUtils.h> #include <media/stagefright/foundation/AUtils.h> #include <media/stagefright/Utils.h> @@ -32,6 +33,100 @@ namespace android { class UtilsTest : public ::testing::Test { }; +TEST_F(UtilsTest, TestStringUtils) { + ASSERT_EQ(AStringUtils::Compare("Audio", "AudioExt", 5, false), 0); + ASSERT_EQ(AStringUtils::Compare("Audio", "audiOExt", 5, true), 0); + ASSERT_NE(AStringUtils::Compare("Audio", "audioExt", 5, false), 0); + ASSERT_NE(AStringUtils::Compare("Audio", "AudiOExt", 5, false), 0); + + ASSERT_LT(AStringUtils::Compare("Audio", "AudioExt", 7, false), 0); + ASSERT_LT(AStringUtils::Compare("Audio", "audiOExt", 7, true), 0); + + ASSERT_GT(AStringUtils::Compare("AudioExt", "Audio", 7, false), 0); + ASSERT_GT(AStringUtils::Compare("audiOext", "Audio", 7, true), 0); + + ASSERT_LT(AStringUtils::Compare("Audio", "Video", 5, false), 0); + ASSERT_LT(AStringUtils::Compare("Audio1", "Audio2", 6, false), 0); + ASSERT_LT(AStringUtils::Compare("audio", "VIDEO", 5, true), 0); + ASSERT_LT(AStringUtils::Compare("audio1", "AUDIO2", 6, true), 0); + + ASSERT_GT(AStringUtils::Compare("Video", "Audio", 5, false), 0); + ASSERT_GT(AStringUtils::Compare("Audio2", "Audio1", 6, false), 0); + ASSERT_GT(AStringUtils::Compare("VIDEO", "audio", 5, true), 0); + ASSERT_GT(AStringUtils::Compare("AUDIO2", "audio1", 6, true), 0); + + ASSERT_TRUE(AStringUtils::MatchesGlob("AudioA", 5, "AudioB", 5, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("AudioA", 6, "AudioA", 5, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("AudioA", 5, "AudioA", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("AudioA", 5, "audiOB", 5, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("AudioA", 5, "audiOB", 5, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("AudioA", 6, "AudioA", 5, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("AudioA", 5, "AudioA", 6, true)); + + ASSERT_TRUE(AStringUtils::MatchesGlob("*1", 1, "String8", 6, true)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*1", 1, "String8", 6, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*1", 1, "String8", 0, true)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*1", 1, "String8", 0, false)); + + ASSERT_TRUE(AStringUtils::MatchesGlob("*ring1", 5, "String8", 6, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*ring2", 5, "STRING8", 6, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*ring4", 5, "StRing8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*ring5", 5, "StrinG8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*ring8", 5, "String8", 7, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*ring8", 5, "String8", 7, true)); + + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*1", 4, "String8", 6, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*2", 4, "STRING8", 6, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*3", 4, "string8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*4", 4, "StRing8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*5", 4, "AString8", 7, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*6", 4, "AString8", 7, true)); + + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*ng1", 6, "String8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng2", 6, "string8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng3", 6, "StRing8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng4", 6, "StriNg8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng5", 6, "StrinG8", 6, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*ng6", 6, "STRING8", 6, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng8", 6, "AString8", 7, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng1", 6, "String16", 7, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*ing9", 7, "String8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ringA", 8, "String8", 6, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng8", 6, "AString8", 7, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ng1", 6, "String16", 7, true)); + ASSERT_TRUE(AStringUtils::MatchesGlob("Str*ing9", 7, "STRING8", 6, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("Str*ringA", 8, "String8", 6, true)); + + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str1", 8, "bestrestroom", 9, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str1", 8, "bestrestrestroom", 13, false)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*str*stro", 8, "bestrestrestroom", 14, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str*1", 9, "bestrestrestroom", 14, false)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str1", 8, "beSTReSTRoom", 9, true)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str1", 8, "beSTRestreSTRoom", 13, true)); + ASSERT_FALSE(AStringUtils::MatchesGlob("*str*stro", 8, "bestreSTReSTRoom", 14, true)); + ASSERT_TRUE(AStringUtils::MatchesGlob("*str*str*1", 9, "bestreSTReSTRoom", 14, true)); +} + +TEST_F(UtilsTest, TestDebug) { +#define LVL(x) (ADebug::Level)(x) + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "", LVL(5)), LVL(5)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", " \t \n ", LVL(2)), LVL(2)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3:*deo", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString( + "video", "\t\n 3 \t\n:\t\n video \t\n", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3:*deo,2:vid*", LVL(5)), LVL(2)); + ASSERT_EQ(ADebug::GetDebugLevelFromString( + "avideo", "\t\n 3 \t\n:\t\n avideo \t\n,\t\n 2 \t\n:\t\n video \t\n", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString( + "audio.omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(2)); + ASSERT_EQ(ADebug::GetDebugLevelFromString( + "video.omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(3)); + ASSERT_EQ(ADebug::GetDebugLevelFromString("omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(4)); +#undef LVL +} + TEST_F(UtilsTest, TestFourCC) { ASSERT_EQ(FOURCC('s', 't', 'm' , 'u'), 'stmu'); } @@ -77,6 +172,13 @@ TEST_F(UtilsTest, TestMathTemplates) { ASSERT_EQ(divUp(12, 4), 3); ASSERT_EQ(divUp(13, 4), 4); + ASSERT_EQ(align(11, 4), 12); + ASSERT_EQ(align(12, 4), 12); + ASSERT_EQ(align(13, 4), 16); + ASSERT_EQ(align(11, 8), 16); + ASSERT_EQ(align(11, 2), 12); + ASSERT_EQ(align(11, 1), 11); + ASSERT_EQ(abs(5L), 5L); ASSERT_EQ(abs(-25), 25); diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp index 71aa21e..55a9803 100644 --- a/media/libstagefright/timedtext/TimedTextDriver.cpp +++ b/media/libstagefright/timedtext/TimedTextDriver.cpp @@ -133,7 +133,7 @@ status_t TimedTextDriver::selectTrack(size_t index) { } mPlayer->start(); break; - defaut: + default: TRESPASS(); } return ret; @@ -181,7 +181,7 @@ status_t TimedTextDriver::seekToAsync(int64_t timeUs) { case PLAYING: mPlayer->seekToAsync(timeUs); return OK; - defaut: + default: TRESPASS(); } return UNKNOWN_ERROR; diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp index a070487..aecf666 100644 --- a/media/libstagefright/timedtext/TimedTextPlayer.cpp +++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp @@ -56,25 +56,25 @@ TimedTextPlayer::~TimedTextPlayer() { } void TimedTextPlayer::start() { - (new AMessage(kWhatStart, id()))->post(); + (new AMessage(kWhatStart, this))->post(); } void TimedTextPlayer::pause() { - (new AMessage(kWhatPause, id()))->post(); + (new AMessage(kWhatPause, this))->post(); } void TimedTextPlayer::resume() { - (new AMessage(kWhatResume, id()))->post(); + (new AMessage(kWhatResume, this))->post(); } void TimedTextPlayer::seekToAsync(int64_t timeUs) { - sp<AMessage> msg = new AMessage(kWhatSeek, id()); + sp<AMessage> msg = new AMessage(kWhatSeek, this); msg->setInt64("seekTimeUs", timeUs); msg->post(); } void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) { - sp<AMessage> msg = new AMessage(kWhatSetSource, id()); + sp<AMessage> msg = new AMessage(kWhatSetSource, this); msg->setObject("source", source); msg->post(); } @@ -231,7 +231,7 @@ void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) { status_t err = mSource->read(&startTimeUs, &endTimeUs, &(parcelEvent->parcel), options); if (err == WOULD_BLOCK) { - sp<AMessage> msg = new AMessage(kWhatRetryRead, id()); + sp<AMessage> msg = new AMessage(kWhatRetryRead, this); if (options != NULL) { int64_t seekTimeUs = kInvalidTimeUs; MediaSource::ReadOptions::SeekMode seekMode = @@ -259,7 +259,7 @@ void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) { void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) { int64_t delayUs = delayUsFromCurrentTime(timeUs); - sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id()); + sp<AMessage> msg = new AMessage(kWhatSendSubtitle, this); msg->setInt32("generation", mSendSubtitleGeneration); if (parcel != NULL) { msg->setObject("subtitle", parcel); diff --git a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp b/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp index 40e93c7..3a06d61 100644 --- a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp +++ b/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp @@ -120,26 +120,26 @@ TEST_F(TimedTextSRTSourceTest, readAll) { err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, i * kSecToMsec); - subtitle = StringPrintf("%d\n\n", i); + subtitle = AStringPrintf("%d\n\n", i); CheckDataEquals(parcel, subtitle.c_str()); } // read edge cases err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, 5500); - subtitle = StringPrintf("6\n\n"); + subtitle = AStringPrintf("6\n\n"); CheckDataEquals(parcel, subtitle.c_str()); err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, 5800); - subtitle = StringPrintf("7\n\n"); + subtitle = AStringPrintf("7\n\n"); CheckDataEquals(parcel, subtitle.c_str()); err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, 6000); - subtitle = StringPrintf("8\n\n"); + subtitle = AStringPrintf("8\n\n"); CheckDataEquals(parcel, subtitle.c_str()); err = mSource->read(&startTimeUs, &endTimeUs, &parcel); @@ -202,21 +202,21 @@ TEST_F(TimedTextSRTSourceTest, checkEdgeCase) { err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(5500 * kMsecToUsec, startTimeUs); - subtitle = StringPrintf("6\n\n"); + subtitle = AStringPrintf("6\n\n"); CheckDataEquals(parcel, subtitle.c_str()); options.setSeekTo(5800 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(5800 * kMsecToUsec, startTimeUs); - subtitle = StringPrintf("7\n\n"); + subtitle = AStringPrintf("7\n\n"); CheckDataEquals(parcel, subtitle.c_str()); options.setSeekTo(6000 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(6000 * kMsecToUsec, startTimeUs); - subtitle = StringPrintf("8\n\n"); + subtitle = AStringPrintf("8\n\n"); CheckDataEquals(parcel, subtitle.c_str()); } diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp index 03cf92a..737f144 100644 --- a/media/libstagefright/webm/WebmWriter.cpp +++ b/media/libstagefright/webm/WebmWriter.cpp @@ -80,38 +80,6 @@ WebmWriter::WebmWriter(int fd) 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; @@ -333,7 +301,6 @@ status_t WebmWriter::reset() { 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( diff --git a/media/libstagefright/webm/WebmWriter.h b/media/libstagefright/webm/WebmWriter.h index 36b6965..4ad770e 100644 --- a/media/libstagefright/webm/WebmWriter.h +++ b/media/libstagefright/webm/WebmWriter.h @@ -37,7 +37,6 @@ namespace android { class WebmWriter : public MediaWriter { public: WebmWriter(int fd); - WebmWriter(const char *filename); ~WebmWriter() { reset(); } diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index b1cdec0..6f0087f 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -121,7 +121,7 @@ status_t MediaSender::initAsync( } if (err == OK) { - sp<AMessage> notify = new AMessage(kWhatSenderNotify, id()); + sp<AMessage> notify = new AMessage(kWhatSenderNotify, this); notify->setInt32("generation", mGeneration); mTSSender = new RTPSender(mNetSession, notify); looper()->registerHandler(mTSSender); @@ -170,7 +170,7 @@ status_t MediaSender::initAsync( return INVALID_OPERATION; } - sp<AMessage> notify = new AMessage(kWhatSenderNotify, id()); + sp<AMessage> notify = new AMessage(kWhatSenderNotify, this); notify->setInt32("generation", mGeneration); notify->setSize("trackIndex", trackIndex); diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp index 04e02c1..2f4af5b 100644 --- a/media/libstagefright/wifi-display/VideoFormats.cpp +++ b/media/libstagefright/wifi-display/VideoFormats.cpp @@ -435,7 +435,7 @@ AString VideoFormats::getFormatSpec(bool forM4Message) const { // max-hres (none or 2 byte) // max-vres (none or 2 byte) - return StringPrintf( + return AStringPrintf( "%02x 00 %02x %02x %08x %08x %08x 00 0000 0000 00 none none", forM4Message ? 0x00 : ((mNativeIndex << 3) | mNativeType), mConfigs[mNativeType][mNativeIndex].profile, diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index e88a3bd..4e72533 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -95,11 +95,11 @@ status_t RTPSender::initAsync( return INVALID_OPERATION; } - sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id()); + sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, this); sp<AMessage> rtcpNotify; if (remoteRTCPPort >= 0) { - rtcpNotify = new AMessage(kWhatRTCPNotify, id()); + rtcpNotify = new AMessage(kWhatRTCPNotify, this); } CHECK_EQ(mRTPSessionID, 0); diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 2834a66..8368945 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -93,7 +93,7 @@ Converter::~Converter() { void Converter::shutdownAsync() { ALOGV("shutdown"); - (new AMessage(kWhatShutdown, id()))->post(); + (new AMessage(kWhatShutdown, this))->post(); } status_t Converter::init() { @@ -482,11 +482,11 @@ void Converter::scheduleDoMoreWork() { #if 1 if (mEncoderActivityNotify == NULL) { - mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, id()); + mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, this); } mEncoder->requestActivityNotification(mEncoderActivityNotify->dup()); #else - sp<AMessage> notify = new AMessage(kWhatEncoderActivity, id()); + sp<AMessage> notify = new AMessage(kWhatEncoderActivity, this); notify->setInt64("whenUs", ALooper::GetNowUs()); mEncoder->requestActivityNotification(notify); #endif @@ -731,8 +731,7 @@ status_t Converter::doMoreWork() { // MediaSender will post the following message when HDCP // is done, to release the output buffer back to encoder. - sp<AMessage> notify(new AMessage( - kWhatReleaseOutputBuffer, id())); + sp<AMessage> notify(new AMessage(kWhatReleaseOutputBuffer, this)); notify->setInt32("bufferIndex", bufferIndex); buffer = new ABuffer( @@ -787,18 +786,18 @@ status_t Converter::doMoreWork() { } void Converter::requestIDRFrame() { - (new AMessage(kWhatRequestIDRFrame, id()))->post(); + (new AMessage(kWhatRequestIDRFrame, this))->post(); } void Converter::dropAFrame() { // Unsupported in surface input mode. CHECK(!(mFlags & FLAG_USE_SURFACE_INPUT)); - (new AMessage(kWhatDropAFrame, id()))->post(); + (new AMessage(kWhatDropAFrame, this))->post(); } void Converter::suspendEncoding(bool suspend) { - sp<AMessage> msg = new AMessage(kWhatSuspendEncoding, id()); + sp<AMessage> msg = new AMessage(kWhatSuspendEncoding, this); msg->setInt32("suspend", suspend); msg->post(); } diff --git a/media/libstagefright/wifi-display/source/MediaPuller.cpp b/media/libstagefright/wifi-display/source/MediaPuller.cpp index 86b918f..ce07a4e 100644 --- a/media/libstagefright/wifi-display/source/MediaPuller.cpp +++ b/media/libstagefright/wifi-display/source/MediaPuller.cpp @@ -63,21 +63,21 @@ status_t MediaPuller::postSynchronouslyAndReturnError( } status_t MediaPuller::start() { - return postSynchronouslyAndReturnError(new AMessage(kWhatStart, id())); + return postSynchronouslyAndReturnError(new AMessage(kWhatStart, this)); } void MediaPuller::stopAsync(const sp<AMessage> ¬ify) { - sp<AMessage> msg = new AMessage(kWhatStop, id()); + sp<AMessage> msg = new AMessage(kWhatStop, this); msg->setMessage("notify", notify); msg->post(); } void MediaPuller::pause() { - (new AMessage(kWhatPause, id()))->post(); + (new AMessage(kWhatPause, this))->post(); } void MediaPuller::resume() { - (new AMessage(kWhatResume, id()))->post(); + (new AMessage(kWhatResume, this))->post(); } void MediaPuller::onMessageReceived(const sp<AMessage> &msg) { @@ -105,7 +105,7 @@ void MediaPuller::onMessageReceived(const sp<AMessage> &msg) { sp<AMessage> response = new AMessage; response->setInt32("err", err); - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); response->postReply(replyID); break; @@ -215,7 +215,7 @@ void MediaPuller::onMessageReceived(const sp<AMessage> &msg) { } void MediaPuller::schedulePull() { - sp<AMessage> msg = new AMessage(kWhatPull, id()); + sp<AMessage> msg = new AMessage(kWhatPull, this); msg->setInt32("generation", mPullGeneration); msg->post(); } diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 2cb4786..6080943 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -214,7 +214,7 @@ void WifiDisplaySource::PlaybackSession::Track::stopAsync() { mConverter->shutdownAsync(); } - sp<AMessage> msg = new AMessage(kWhatMediaPullerStopped, id()); + sp<AMessage> msg = new AMessage(kWhatMediaPullerStopped, this); if (mStarted && mMediaPuller != NULL) { if (mRepeaterSource != NULL) { @@ -382,7 +382,7 @@ status_t WifiDisplaySource::PlaybackSession::init( size_t videoResolutionIndex, VideoFormats::ProfileType videoProfileType, VideoFormats::LevelType videoLevelType) { - sp<AMessage> notify = new AMessage(kWhatMediaSenderNotify, id()); + sp<AMessage> notify = new AMessage(kWhatMediaSenderNotify, this); mMediaSender = new MediaSender(mNetSession, notify); looper()->registerHandler(mMediaSender); @@ -440,7 +440,7 @@ void WifiDisplaySource::PlaybackSession::updateLiveness() { status_t WifiDisplaySource::PlaybackSession::play() { updateLiveness(); - (new AMessage(kWhatResume, id()))->post(); + (new AMessage(kWhatResume, this))->post(); return OK; } @@ -460,7 +460,7 @@ status_t WifiDisplaySource::PlaybackSession::onMediaSenderInitialized() { status_t WifiDisplaySource::PlaybackSession::pause() { updateLiveness(); - (new AMessage(kWhatPause, id()))->post(); + (new AMessage(kWhatPause, this))->post(); return OK; } @@ -786,7 +786,7 @@ status_t WifiDisplaySource::PlaybackSession::setupMediaPacketizer( size_t trackIndex = mTracks.size(); - sp<AMessage> notify = new AMessage(kWhatTrackNotify, id()); + sp<AMessage> notify = new AMessage(kWhatTrackNotify, this); notify->setSize("trackIndex", trackIndex); sp<Track> track = new Track(notify, format); @@ -833,7 +833,7 @@ void WifiDisplaySource::PlaybackSession::schedulePullExtractor() { int64_t whenUs = sampleTimeUs - mFirstSampleTimeUs + mFirstSampleTimeRealUs; - sp<AMessage> msg = new AMessage(kWhatPullExtractorSample, id()); + sp<AMessage> msg = new AMessage(kWhatPullExtractorSample, this); msg->setInt32("generation", mPullExtractorGeneration); msg->post(whenUs - nowUs); @@ -857,7 +857,7 @@ void WifiDisplaySource::PlaybackSession::onPullExtractor() { size_t trackIndex; CHECK_EQ((status_t)OK, mExtractor->getSampleTrackIndex(&trackIndex)); - sp<AMessage> msg = new AMessage(kWhatConverterNotify, id()); + sp<AMessage> msg = new AMessage(kWhatConverterNotify, this); msg->setSize( "trackIndex", mExtractorTrackToInternalTrack.valueFor(trackIndex)); @@ -955,7 +955,7 @@ status_t WifiDisplaySource::PlaybackSession::addSource( ? MEDIA_MIMETYPE_AUDIO_RAW : MEDIA_MIMETYPE_AUDIO_AAC); } - notify = new AMessage(kWhatConverterNotify, id()); + notify = new AMessage(kWhatConverterNotify, this); notify->setSize("trackIndex", trackIndex); sp<Converter> converter = new Converter(notify, codecLooper, format); @@ -970,7 +970,7 @@ status_t WifiDisplaySource::PlaybackSession::addSource( return err; } - notify = new AMessage(Converter::kWhatMediaPullerNotify, converter->id()); + notify = new AMessage(Converter::kWhatMediaPullerNotify, converter); notify->setSize("trackIndex", trackIndex); sp<MediaPuller> puller = new MediaPuller(source, notify); @@ -980,7 +980,7 @@ status_t WifiDisplaySource::PlaybackSession::addSource( *numInputBuffers = converter->getInputBufferCount(); } - notify = new AMessage(kWhatTrackNotify, id()); + notify = new AMessage(kWhatTrackNotify, this); notify->setSize("trackIndex", trackIndex); sp<Track> track = new Track( diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.cpp b/media/libstagefright/wifi-display/source/RepeaterSource.cpp index 59d7e6e..af6b663 100644 --- a/media/libstagefright/wifi-display/source/RepeaterSource.cpp +++ b/media/libstagefright/wifi-display/source/RepeaterSource.cpp @@ -173,7 +173,7 @@ status_t RepeaterSource::read( } void RepeaterSource::postRead() { - (new AMessage(kWhatRead, mReflector->id()))->post(); + (new AMessage(kWhatRead, mReflector))->post(); } void RepeaterSource::onMessageReceived(const sp<AMessage> &msg) { diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index 50d317a..4c5ad17 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -106,7 +106,7 @@ void TSPacketizer::Track::extractCSDIfNecessary() { || !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) { for (size_t i = 0;; ++i) { sp<ABuffer> csd; - if (!mFormat->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) { + if (!mFormat->findBuffer(AStringPrintf("csd-%d", i).c_str(), &csd)) { break; } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 0c39ccf..14d0951 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -57,7 +57,7 @@ WifiDisplaySource::WifiDisplaySource( mNetSession(netSession), mClient(client), mSessionID(0), - mStopReplyID(0), + mStopReplyID(NULL), mChosenRTPPort(-1), mUsingPCMAudio(false), mClientSessionID(0), @@ -106,7 +106,7 @@ static status_t PostAndAwaitResponse( status_t WifiDisplaySource::start(const char *iface) { CHECK_EQ(mState, INITIALIZED); - sp<AMessage> msg = new AMessage(kWhatStart, id()); + sp<AMessage> msg = new AMessage(kWhatStart, this); msg->setString("iface", iface); sp<AMessage> response; @@ -114,21 +114,21 @@ status_t WifiDisplaySource::start(const char *iface) { } status_t WifiDisplaySource::stop() { - sp<AMessage> msg = new AMessage(kWhatStop, id()); + sp<AMessage> msg = new AMessage(kWhatStop, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } status_t WifiDisplaySource::pause() { - sp<AMessage> msg = new AMessage(kWhatPause, id()); + sp<AMessage> msg = new AMessage(kWhatPause, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } status_t WifiDisplaySource::resume() { - sp<AMessage> msg = new AMessage(kWhatResume, id()); + sp<AMessage> msg = new AMessage(kWhatResume, this); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); @@ -138,7 +138,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatStart: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); AString iface; @@ -167,7 +167,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { if (err == OK) { if (inet_aton(iface.c_str(), &mInterfaceAddr) != 0) { - sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id()); + sp<AMessage> notify = new AMessage(kWhatRTSPNotify, this); err = mNetSession->createRTSPServer( mInterfaceAddr, port, notify, &mSessionID); @@ -310,7 +310,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { if (err == OK) { mState = AWAITING_CLIENT_TEARDOWN; - (new AMessage(kWhatTeardownTriggerTimedOut, id()))->post( + (new AMessage(kWhatTeardownTriggerTimedOut, this))->post( kTeardownTriggerTimeouSecs * 1000000ll); break; @@ -325,7 +325,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { case kWhatPause: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); status_t err = OK; @@ -345,7 +345,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { case kWhatResume: { - uint32_t replyID; + sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); status_t err = OK; @@ -492,7 +492,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { if (mState == AWAITING_CLIENT_TEARDOWN) { ALOGI("TEARDOWN trigger timed out, forcing disconnection."); - CHECK_NE(mStopReplyID, 0); + CHECK(mStopReplyID != NULL); finishStop(); break; } @@ -529,7 +529,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { // HDCPObserver::notify is completely handled before // we clear the HDCP instance and unload the shared // library :( - (new AMessage(kWhatFinishStop2, id()))->post(300000ll); + (new AMessage(kWhatFinishStop2, this))->post(300000ll); break; } @@ -598,7 +598,7 @@ status_t WifiDisplaySource::sendM3(int32_t sessionID) { AppendCommonResponse(&request, mNextCSeq); request.append("Content-Type: text/parameters\r\n"); - request.append(StringPrintf("Content-Length: %d\r\n", body.size())); + request.append(AStringPrintf("Content-Length: %d\r\n", body.size())); request.append("\r\n"); request.append(body); @@ -639,26 +639,26 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { if (mSinkSupportsAudio) { body.append( - StringPrintf("wfd_audio_codecs: %s\r\n", + AStringPrintf("wfd_audio_codecs: %s\r\n", (mUsingPCMAudio ? "LPCM 00000002 00" // 2 ch PCM 48kHz : "AAC 00000001 00"))); // 2 ch AAC 48kHz } body.append( - StringPrintf( + AStringPrintf( "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n", mClientInfo.mLocalIP.c_str())); body.append( - StringPrintf( + AStringPrintf( "wfd_client_rtp_ports: %s\r\n", mWfdClientRtpPorts.c_str())); AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; AppendCommonResponse(&request, mNextCSeq); request.append("Content-Type: text/parameters\r\n"); - request.append(StringPrintf("Content-Length: %d\r\n", body.size())); + request.append(AStringPrintf("Content-Length: %d\r\n", body.size())); request.append("\r\n"); request.append(body); @@ -704,7 +704,7 @@ status_t WifiDisplaySource::sendTrigger( AppendCommonResponse(&request, mNextCSeq); request.append("Content-Type: text/parameters\r\n"); - request.append(StringPrintf("Content-Length: %d\r\n", body.size())); + request.append(AStringPrintf("Content-Length: %d\r\n", body.size())); request.append("\r\n"); request.append(body); @@ -729,7 +729,7 @@ status_t WifiDisplaySource::sendM16(int32_t sessionID) { CHECK_EQ(sessionID, mClientSessionID); request.append( - StringPrintf("Session: %d\r\n", mClientInfo.mPlaybackSessionID)); + AStringPrintf("Session: %d\r\n", mClientInfo.mPlaybackSessionID)); request.append("\r\n"); // Empty body status_t err = @@ -1027,7 +1027,7 @@ void WifiDisplaySource::scheduleReaper() { } mReaperPending = true; - (new AMessage(kWhatReapDeadClients, id()))->post(kReaperIntervalUs); + (new AMessage(kWhatReapDeadClients, this))->post(kReaperIntervalUs); } void WifiDisplaySource::scheduleKeepAlive(int32_t sessionID) { @@ -1035,7 +1035,7 @@ void WifiDisplaySource::scheduleKeepAlive(int32_t sessionID) { // expire, make sure the timeout is greater than 5 secs to begin with. CHECK_GT(kPlaybackSessionTimeoutUs, 5000000ll); - sp<AMessage> msg = new AMessage(kWhatKeepAlive, id()); + sp<AMessage> msg = new AMessage(kWhatKeepAlive, this); msg->setInt32("sessionID", sessionID); msg->post(kPlaybackSessionTimeoutUs - 5000000ll); } @@ -1239,7 +1239,7 @@ status_t WifiDisplaySource::onSetupRequest( int32_t playbackSessionID = makeUniquePlaybackSessionID(); - sp<AMessage> notify = new AMessage(kWhatPlaybackSessionNotify, id()); + sp<AMessage> notify = new AMessage(kWhatPlaybackSessionNotify, this); notify->setInt32("playbackSessionID", playbackSessionID); notify->setInt32("sessionID", sessionID); @@ -1305,7 +1305,7 @@ status_t WifiDisplaySource::onSetupRequest( if (rtpMode == RTPSender::TRANSPORT_TCP_INTERLEAVED) { response.append( - StringPrintf( + AStringPrintf( "Transport: RTP/AVP/TCP;interleaved=%d-%d;", clientRtp, clientRtcp)); } else { @@ -1318,14 +1318,14 @@ status_t WifiDisplaySource::onSetupRequest( if (clientRtcp >= 0) { response.append( - StringPrintf( + AStringPrintf( "Transport: RTP/AVP/%s;unicast;client_port=%d-%d;" "server_port=%d-%d\r\n", transportString.c_str(), clientRtp, clientRtcp, serverRtp, serverRtp + 1)); } else { response.append( - StringPrintf( + AStringPrintf( "Transport: RTP/AVP/%s;unicast;client_port=%d;" "server_port=%d\r\n", transportString.c_str(), @@ -1470,7 +1470,7 @@ status_t WifiDisplaySource::onTeardownRequest( mNetSession->sendRequest(sessionID, response.c_str()); if (mState == AWAITING_CLIENT_TEARDOWN) { - CHECK_NE(mStopReplyID, 0); + CHECK(mStopReplyID != NULL); finishStop(); } else { mClient->onDisplayError(IRemoteDisplayClient::kDisplayErrorUnknown); @@ -1585,15 +1585,15 @@ void WifiDisplaySource::AppendCommonResponse( response->append(buf); response->append("\r\n"); - response->append(StringPrintf("Server: %s\r\n", sUserAgent.c_str())); + response->append(AStringPrintf("Server: %s\r\n", sUserAgent.c_str())); if (cseq >= 0) { - response->append(StringPrintf("CSeq: %d\r\n", cseq)); + response->append(AStringPrintf("CSeq: %d\r\n", cseq)); } if (playbackSessionID >= 0ll) { response->append( - StringPrintf( + AStringPrintf( "Session: %d;timeout=%lld\r\n", playbackSessionID, kPlaybackSessionTimeoutSecs)); } @@ -1707,7 +1707,7 @@ status_t WifiDisplaySource::makeHDCP() { return ERROR_UNSUPPORTED; } - sp<AMessage> notify = new AMessage(kWhatHDCPNotify, id()); + sp<AMessage> notify = new AMessage(kWhatHDCPNotify, this); mHDCPObserver = new HDCPObserver(notify); status_t err = mHDCP->setObserver(mHDCPObserver); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 750265f..0f779e4 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -27,6 +27,7 @@ namespace android { +struct AReplyToken; struct IHDCP; struct IRemoteDisplayClient; struct ParsedMessage; @@ -121,7 +122,7 @@ private: struct in_addr mInterfaceAddr; int32_t mSessionID; - uint32_t mStopReplyID; + sp<AReplyToken> mStopReplyID; AString mWfdClientRtpPorts; int32_t mChosenRTPPort; // extracted from "wfd_client_rtp_ports" diff --git a/media/libstagefright/yuv/YUVImage.cpp b/media/libstagefright/yuv/YUVImage.cpp index bb3e2fd..c098135 100644 --- a/media/libstagefright/yuv/YUVImage.cpp +++ b/media/libstagefright/yuv/YUVImage.cpp @@ -374,13 +374,13 @@ uint8_t clamp(uint8_t v, uint8_t minValue, uint8_t maxValue) { void YUVImage::yuv2rgb(uint8_t yValue, uint8_t uValue, uint8_t vValue, uint8_t *r, uint8_t *g, uint8_t *b) const { - *r = yValue + (1.370705 * (vValue-128)); - *g = yValue - (0.698001 * (vValue-128)) - (0.337633 * (uValue-128)); - *b = yValue + (1.732446 * (uValue-128)); + int rTmp = yValue + (1.370705 * (vValue-128)); + int gTmp = yValue - (0.698001 * (vValue-128)) - (0.337633 * (uValue-128)); + int bTmp = yValue + (1.732446 * (uValue-128)); - *r = clamp(*r, 0, 255); - *g = clamp(*g, 0, 255); - *b = clamp(*b, 0, 255); + *r = clamp(rTmp, 0, 255); + *g = clamp(gTmp, 0, 255); + *b = clamp(bTmp, 0, 255); } bool YUVImage::writeToPPM(const char *filename) const { diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index 3a280f0..0ad0bf3 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -11,7 +11,7 @@ endif include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - main_mediaserver.cpp + main_mediaserver.cpp LOCAL_SHARED_LIBRARIES := \ libaudioflinger \ @@ -26,7 +26,8 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ liblog \ libbinder \ - libsoundtriggerservice + libsoundtriggerservice \ + libradioservice LOCAL_STATIC_LIBRARIES := \ libregistermsext @@ -38,7 +39,8 @@ LOCAL_C_INCLUDES := \ frameworks/av/services/audiopolicy \ frameworks/av/services/camera/libcameraservice \ $(call include-path-for, audio-utils) \ - frameworks/av/services/soundtrigger + frameworks/av/services/soundtrigger \ + frameworks/av/services/radio LOCAL_MODULE:= mediaserver LOCAL_32_BIT_ONLY := true diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp index af1c9e6..99572f8 100644 --- a/media/mediaserver/main_mediaserver.cpp +++ b/media/mediaserver/main_mediaserver.cpp @@ -33,8 +33,9 @@ #include "CameraService.h" #include "MediaLogService.h" #include "MediaPlayerService.h" -#include "AudioPolicyService.h" +#include "service/AudioPolicyService.h" #include "SoundTriggerHwService.h" +#include "RadioService.h" using namespace android; @@ -130,6 +131,7 @@ int main(int argc __unused, char** argv) CameraService::instantiate(); AudioPolicyService::instantiate(); SoundTriggerHwService::instantiate(); + RadioService::instantiate(); registerExtensions(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp index e6e19e3..052b700 100644 --- a/media/mtp/MtpDataPacket.cpp +++ b/media/mtp/MtpDataPacket.cpp @@ -51,104 +51,178 @@ void MtpDataPacket::setTransactionID(MtpTransactionID id) { MtpPacket::putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id); } -uint16_t MtpDataPacket::getUInt16() { +bool MtpDataPacket::getUInt8(uint8_t& value) { + if (mPacketSize - mOffset < sizeof(value)) + return false; + value = mBuffer[mOffset++]; + return true; +} + +bool MtpDataPacket::getUInt16(uint16_t& value) { + if (mPacketSize - mOffset < sizeof(value)) + return false; int offset = mOffset; - uint16_t result = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8); - mOffset += 2; - return result; + value = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8); + mOffset += sizeof(value); + return true; } -uint32_t MtpDataPacket::getUInt32() { +bool MtpDataPacket::getUInt32(uint32_t& value) { + if (mPacketSize - mOffset < sizeof(value)) + return false; int offset = mOffset; - uint32_t result = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) | + value = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) | ((uint32_t)mBuffer[offset + 2] << 16) | ((uint32_t)mBuffer[offset + 3] << 24); - mOffset += 4; - return result; + mOffset += sizeof(value); + return true; } -uint64_t MtpDataPacket::getUInt64() { +bool MtpDataPacket::getUInt64(uint64_t& value) { + if (mPacketSize - mOffset < sizeof(value)) + return false; int offset = mOffset; - uint64_t result = (uint64_t)mBuffer[offset] | ((uint64_t)mBuffer[offset + 1] << 8) | + value = (uint64_t)mBuffer[offset] | ((uint64_t)mBuffer[offset + 1] << 8) | ((uint64_t)mBuffer[offset + 2] << 16) | ((uint64_t)mBuffer[offset + 3] << 24) | ((uint64_t)mBuffer[offset + 4] << 32) | ((uint64_t)mBuffer[offset + 5] << 40) | ((uint64_t)mBuffer[offset + 6] << 48) | ((uint64_t)mBuffer[offset + 7] << 56); - mOffset += 8; - return result; + mOffset += sizeof(value); + return true; } -void MtpDataPacket::getUInt128(uint128_t& value) { - value[0] = getUInt32(); - value[1] = getUInt32(); - value[2] = getUInt32(); - value[3] = getUInt32(); +bool MtpDataPacket::getUInt128(uint128_t& value) { + return getUInt32(value[0]) && getUInt32(value[1]) && getUInt32(value[2]) && getUInt32(value[3]); } -void MtpDataPacket::getString(MtpStringBuffer& string) +bool MtpDataPacket::getString(MtpStringBuffer& string) { - string.readFromPacket(this); + return string.readFromPacket(this); } Int8List* MtpDataPacket::getAInt8() { + uint32_t count; + if (!getUInt32(count)) + return NULL; Int8List* result = new Int8List; - int count = getUInt32(); - for (int i = 0; i < count; i++) - result->push(getInt8()); + for (uint32_t i = 0; i < count; i++) { + int8_t value; + if (!getInt8(value)) { + delete result; + return NULL; + } + result->push(value); + } return result; } UInt8List* MtpDataPacket::getAUInt8() { + uint32_t count; + if (!getUInt32(count)) + return NULL; UInt8List* result = new UInt8List; - int count = getUInt32(); - for (int i = 0; i < count; i++) - result->push(getUInt8()); + for (uint32_t i = 0; i < count; i++) { + uint8_t value; + if (!getUInt8(value)) { + delete result; + return NULL; + } + result->push(value); + } return result; } Int16List* MtpDataPacket::getAInt16() { + uint32_t count; + if (!getUInt32(count)) + return NULL; Int16List* result = new Int16List; - int count = getUInt32(); - for (int i = 0; i < count; i++) - result->push(getInt16()); + for (uint32_t i = 0; i < count; i++) { + int16_t value; + if (!getInt16(value)) { + delete result; + return NULL; + } + result->push(value); + } return result; } UInt16List* MtpDataPacket::getAUInt16() { + uint32_t count; + if (!getUInt32(count)) + return NULL; UInt16List* result = new UInt16List; - int count = getUInt32(); - for (int i = 0; i < count; i++) - result->push(getUInt16()); + for (uint32_t i = 0; i < count; i++) { + uint16_t value; + if (!getUInt16(value)) { + delete result; + return NULL; + } + result->push(value); + } return result; } Int32List* MtpDataPacket::getAInt32() { + uint32_t count; + if (!getUInt32(count)) + return NULL; Int32List* result = new Int32List; - int count = getUInt32(); - for (int i = 0; i < count; i++) - result->push(getInt32()); + for (uint32_t i = 0; i < count; i++) { + int32_t value; + if (!getInt32(value)) { + delete result; + return NULL; + } + result->push(value); + } return result; } UInt32List* MtpDataPacket::getAUInt32() { + uint32_t count; + if (!getUInt32(count)) + return NULL; UInt32List* result = new UInt32List; - int count = getUInt32(); - for (int i = 0; i < count; i++) - result->push(getUInt32()); + for (uint32_t i = 0; i < count; i++) { + uint32_t value; + if (!getUInt32(value)) { + delete result; + return NULL; + } + result->push(value); + } return result; } Int64List* MtpDataPacket::getAInt64() { + uint32_t count; + if (!getUInt32(count)) + return NULL; Int64List* result = new Int64List; - int count = getUInt32(); - for (int i = 0; i < count; i++) - result->push(getInt64()); + for (uint32_t i = 0; i < count; i++) { + int64_t value; + if (!getInt64(value)) { + delete result; + return NULL; + } + result->push(value); + } return result; } UInt64List* MtpDataPacket::getAUInt64() { + uint32_t count; + if (!getUInt32(count)) + return NULL; UInt64List* result = new UInt64List; - int count = getUInt32(); - for (int i = 0; i < count; i++) - result->push(getUInt64()); + for (uint32_t i = 0; i < count; i++) { + uint64_t value; + if (!getUInt64(value)) { + delete result; + return NULL; + } + result->push(value); + } return result; } diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h index 2b81063..13d3bd9 100644 --- a/media/mtp/MtpDataPacket.h +++ b/media/mtp/MtpDataPacket.h @@ -30,7 +30,7 @@ class MtpStringBuffer; class MtpDataPacket : public MtpPacket { private: // current offset for get/put methods - int mOffset; + size_t mOffset; public: MtpDataPacket(); @@ -42,17 +42,18 @@ public: void setTransactionID(MtpTransactionID id); inline const uint8_t* getData() const { return mBuffer + MTP_CONTAINER_HEADER_SIZE; } - inline uint8_t getUInt8() { return (uint8_t)mBuffer[mOffset++]; } - inline int8_t getInt8() { return (int8_t)mBuffer[mOffset++]; } - uint16_t getUInt16(); - inline int16_t getInt16() { return (int16_t)getUInt16(); } - uint32_t getUInt32(); - inline int32_t getInt32() { return (int32_t)getUInt32(); } - uint64_t getUInt64(); - inline int64_t getInt64() { return (int64_t)getUInt64(); } - void getUInt128(uint128_t& value); - inline void getInt128(int128_t& value) { getUInt128((uint128_t&)value); } - void getString(MtpStringBuffer& string); + + bool getUInt8(uint8_t& value); + inline bool getInt8(int8_t& value) { return getUInt8((uint8_t&)value); } + bool getUInt16(uint16_t& value); + inline bool getInt16(int16_t& value) { return getUInt16((uint16_t&)value); } + bool getUInt32(uint32_t& value); + inline bool getInt32(int32_t& value) { return getUInt32((uint32_t&)value); } + bool getUInt64(uint64_t& value); + inline bool getInt64(int64_t& value) { return getUInt64((uint64_t&)value); } + bool getUInt128(uint128_t& value); + inline bool getInt128(int128_t& value) { return getUInt128((uint128_t&)value); } + bool getString(MtpStringBuffer& string); Int8List* getAInt8(); UInt8List* getAUInt8(); diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp index 96331b5..3eafd6f 100644 --- a/media/mtp/MtpDevice.cpp +++ b/media/mtp/MtpDevice.cpp @@ -322,8 +322,10 @@ MtpDeviceInfo* MtpDevice::getDeviceInfo() { MtpResponseCode ret = readResponse(); if (ret == MTP_RESPONSE_OK) { MtpDeviceInfo* info = new MtpDeviceInfo; - info->read(mData); - return info; + if (info->read(mData)) + return info; + else + delete info; } return NULL; } @@ -355,8 +357,10 @@ MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) { MtpResponseCode ret = readResponse(); if (ret == MTP_RESPONSE_OK) { MtpStorageInfo* info = new MtpStorageInfo(storageID); - info->read(mData); - return info; + if (info->read(mData)) + return info; + else + delete info; } return NULL; } @@ -394,8 +398,10 @@ MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) { MtpResponseCode ret = readResponse(); if (ret == MTP_RESPONSE_OK) { MtpObjectInfo* info = new MtpObjectInfo(handle); - info->read(mData); - return info; + if (info->read(mData)) + return info; + else + delete info; } return NULL; } @@ -556,8 +562,10 @@ MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) { MtpResponseCode ret = readResponse(); if (ret == MTP_RESPONSE_OK) { MtpProperty* property = new MtpProperty; - property->read(mData); - return property; + if (property->read(mData)) + return property; + else + delete property; } return NULL; } @@ -575,15 +583,17 @@ MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectForma MtpResponseCode ret = readResponse(); if (ret == MTP_RESPONSE_OK) { MtpProperty* property = new MtpProperty; - property->read(mData); - return property; + if (property->read(mData)) + return property; + else + delete property; } return NULL; } bool MtpDevice::readObject(MtpObjectHandle handle, bool (* callback)(void* data, int offset, int length, void* clientData), - int objectSize, void* clientData) { + size_t objectSize, void* clientData) { Mutex::Autolock autoLock(mMutex); bool result = false; diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h index b69203e..9b0acbf 100644 --- a/media/mtp/MtpDevice.h +++ b/media/mtp/MtpDevice.h @@ -98,7 +98,7 @@ public: bool readObject(MtpObjectHandle handle, bool (* callback)(void* data, int offset, int length, void* clientData), - int objectSize, void* clientData); + size_t objectSize, void* clientData); bool readObject(MtpObjectHandle handle, const char* destPath, int group, int perm); diff --git a/media/mtp/MtpDeviceInfo.cpp b/media/mtp/MtpDeviceInfo.cpp index 108e2b8..3e1dff7 100644 --- a/media/mtp/MtpDeviceInfo.cpp +++ b/media/mtp/MtpDeviceInfo.cpp @@ -28,7 +28,7 @@ MtpDeviceInfo::MtpDeviceInfo() mVendorExtensionID(0), mVendorExtensionVersion(0), mVendorExtensionDesc(NULL), - mFunctionalCode(0), + mFunctionalMode(0), mOperations(NULL), mEvents(NULL), mDeviceProperties(NULL), @@ -59,39 +59,46 @@ MtpDeviceInfo::~MtpDeviceInfo() { free(mSerial); } -void MtpDeviceInfo::read(MtpDataPacket& packet) { +bool MtpDeviceInfo::read(MtpDataPacket& packet) { MtpStringBuffer string; // read the device info - mStandardVersion = packet.getUInt16(); - mVendorExtensionID = packet.getUInt32(); - mVendorExtensionVersion = packet.getUInt16(); + if (!packet.getUInt16(mStandardVersion)) return false; + if (!packet.getUInt32(mVendorExtensionID)) return false; + if (!packet.getUInt16(mVendorExtensionVersion)) return false; - packet.getString(string); + if (!packet.getString(string)) return false; mVendorExtensionDesc = strdup((const char *)string); - mFunctionalCode = packet.getUInt16(); + if (!packet.getUInt16(mFunctionalMode)) return false; mOperations = packet.getAUInt16(); + if (!mOperations) return false; mEvents = packet.getAUInt16(); + if (!mEvents) return false; mDeviceProperties = packet.getAUInt16(); + if (!mDeviceProperties) return false; mCaptureFormats = packet.getAUInt16(); + if (!mCaptureFormats) return false; mPlaybackFormats = packet.getAUInt16(); + if (!mCaptureFormats) return false; - packet.getString(string); + if (!packet.getString(string)) return false; mManufacturer = strdup((const char *)string); - packet.getString(string); + if (!packet.getString(string)) return false; mModel = strdup((const char *)string); - packet.getString(string); + if (!packet.getString(string)) return false; mVersion = strdup((const char *)string); - packet.getString(string); + if (!packet.getString(string)) return false; mSerial = strdup((const char *)string); + + return true; } void MtpDeviceInfo::print() { ALOGV("Device Info:\n\tmStandardVersion: %d\n\tmVendorExtensionID: %d\n\tmVendorExtensionVersiony: %d\n", mStandardVersion, mVendorExtensionID, mVendorExtensionVersion); - ALOGV("\tmVendorExtensionDesc: %s\n\tmFunctionalCode: %d\n\tmManufacturer: %s\n\tmModel: %s\n\tmVersion: %s\n\tmSerial: %s\n", - mVendorExtensionDesc, mFunctionalCode, mManufacturer, mModel, mVersion, mSerial); + ALOGV("\tmVendorExtensionDesc: %s\n\tmFunctionalMode: %d\n\tmManufacturer: %s\n\tmModel: %s\n\tmVersion: %s\n\tmSerial: %s\n", + mVendorExtensionDesc, mFunctionalMode, mManufacturer, mModel, mVersion, mSerial); } } // namespace android diff --git a/media/mtp/MtpDeviceInfo.h b/media/mtp/MtpDeviceInfo.h index 2abaa10..bcda9a5 100644 --- a/media/mtp/MtpDeviceInfo.h +++ b/media/mtp/MtpDeviceInfo.h @@ -29,7 +29,7 @@ public: uint32_t mVendorExtensionID; uint16_t mVendorExtensionVersion; char* mVendorExtensionDesc; - uint16_t mFunctionalCode; + uint16_t mFunctionalMode; UInt16List* mOperations; UInt16List* mEvents; MtpDevicePropertyList* mDeviceProperties; @@ -44,7 +44,7 @@ public: MtpDeviceInfo(); virtual ~MtpDeviceInfo(); - void read(MtpDataPacket& packet); + bool read(MtpDataPacket& packet); void print(); }; diff --git a/media/mtp/MtpObjectInfo.cpp b/media/mtp/MtpObjectInfo.cpp index cd15343..0573104 100644 --- a/media/mtp/MtpObjectInfo.cpp +++ b/media/mtp/MtpObjectInfo.cpp @@ -55,39 +55,41 @@ MtpObjectInfo::~MtpObjectInfo() { free(mKeywords); } -void MtpObjectInfo::read(MtpDataPacket& packet) { +bool MtpObjectInfo::read(MtpDataPacket& packet) { MtpStringBuffer string; time_t time; - mStorageID = packet.getUInt32(); - mFormat = packet.getUInt16(); - mProtectionStatus = packet.getUInt16(); - mCompressedSize = packet.getUInt32(); - mThumbFormat = packet.getUInt16(); - mThumbCompressedSize = packet.getUInt32(); - mThumbPixWidth = packet.getUInt32(); - mThumbPixHeight = packet.getUInt32(); - mImagePixWidth = packet.getUInt32(); - mImagePixHeight = packet.getUInt32(); - mImagePixDepth = packet.getUInt32(); - mParent = packet.getUInt32(); - mAssociationType = packet.getUInt16(); - mAssociationDesc = packet.getUInt32(); - mSequenceNumber = packet.getUInt32(); + if (!packet.getUInt32(mStorageID)) return false; + if (!packet.getUInt16(mFormat)) return false; + if (!packet.getUInt16(mProtectionStatus)) return false; + if (!packet.getUInt32(mCompressedSize)) return false; + if (!packet.getUInt16(mThumbFormat)) return false; + if (!packet.getUInt32(mThumbCompressedSize)) return false; + if (!packet.getUInt32(mThumbPixWidth)) return false; + if (!packet.getUInt32(mThumbPixHeight)) return false; + if (!packet.getUInt32(mImagePixWidth)) return false; + if (!packet.getUInt32(mImagePixHeight)) return false; + if (!packet.getUInt32(mImagePixDepth)) return false; + if (!packet.getUInt32(mParent)) return false; + if (!packet.getUInt16(mAssociationType)) return false; + if (!packet.getUInt32(mAssociationDesc)) return false; + if (!packet.getUInt32(mSequenceNumber)) return false; - packet.getString(string); + if (!packet.getString(string)) return false; mName = strdup((const char *)string); - packet.getString(string); + if (!packet.getString(string)) return false; if (parseDateTime((const char*)string, time)) mDateCreated = time; - packet.getString(string); + if (!packet.getString(string)) return false; if (parseDateTime((const char*)string, time)) mDateModified = time; - packet.getString(string); + if (!packet.getString(string)) return false; mKeywords = strdup((const char *)string); + + return true; } void MtpObjectInfo::print() { diff --git a/media/mtp/MtpObjectInfo.h b/media/mtp/MtpObjectInfo.h index c7a449c..86780f1 100644 --- a/media/mtp/MtpObjectInfo.h +++ b/media/mtp/MtpObjectInfo.h @@ -50,7 +50,7 @@ public: MtpObjectInfo(MtpObjectHandle handle); virtual ~MtpObjectInfo(); - void read(MtpDataPacket& packet); + bool read(MtpDataPacket& packet); void print(); }; diff --git a/media/mtp/MtpPacket.cpp b/media/mtp/MtpPacket.cpp index dd07843..bab1335 100644 --- a/media/mtp/MtpPacket.cpp +++ b/media/mtp/MtpPacket.cpp @@ -52,7 +52,7 @@ void MtpPacket::reset() { memset(mBuffer, 0, mBufferSize); } -void MtpPacket::allocate(int length) { +void MtpPacket::allocate(size_t length) { if (length > mBufferSize) { int newLength = length + mAllocationIncrement; mBuffer = (uint8_t *)realloc(mBuffer, newLength); diff --git a/media/mtp/MtpPacket.h b/media/mtp/MtpPacket.h index 0ffb1d3..037722a 100644 --- a/media/mtp/MtpPacket.h +++ b/media/mtp/MtpPacket.h @@ -28,11 +28,11 @@ class MtpPacket { protected: uint8_t* mBuffer; // current size of the buffer - int mBufferSize; + size_t mBufferSize; // number of bytes to add when resizing the buffer - int mAllocationIncrement; + size_t mAllocationIncrement; // size of the data in the packet - int mPacketSize; + size_t mPacketSize; public: MtpPacket(int bufferSize); @@ -41,7 +41,7 @@ public: // sets packet size to the default container size and sets buffer to zero virtual void reset(); - void allocate(int length); + void allocate(size_t length); void dump(); void copyFrom(const MtpPacket& src); diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp index c500901..d58e2a4 100644 --- a/media/mtp/MtpProperty.cpp +++ b/media/mtp/MtpProperty.cpp @@ -106,15 +106,15 @@ MtpProperty::~MtpProperty() { free(mMinimumValue.str); free(mMaximumValue.str); if (mDefaultArrayValues) { - for (int i = 0; i < mDefaultArrayLength; i++) + for (uint32_t i = 0; i < mDefaultArrayLength; i++) free(mDefaultArrayValues[i].str); } if (mCurrentArrayValues) { - for (int i = 0; i < mCurrentArrayLength; i++) + for (uint32_t i = 0; i < mCurrentArrayLength; i++) free(mCurrentArrayValues[i].str); } if (mEnumValues) { - for (int i = 0; i < mEnumLength; i++) + for (uint16_t i = 0; i < mEnumLength; i++) free(mEnumValues[i].str); } } @@ -123,11 +123,14 @@ MtpProperty::~MtpProperty() { delete[] mEnumValues; } -void MtpProperty::read(MtpDataPacket& packet) { - mCode = packet.getUInt16(); +bool MtpProperty::read(MtpDataPacket& packet) { + uint8_t temp8; + + if (!packet.getUInt16(mCode)) return false; bool deviceProp = isDeviceProperty(); - mType = packet.getUInt16(); - mWriteable = (packet.getUInt8() == 1); + if (!packet.getUInt16(mType)) return false; + if (!packet.getUInt8(temp8)) return false; + mWriteable = (temp8 == 1); switch (mType) { case MTP_TYPE_AINT8: case MTP_TYPE_AUINT8: @@ -140,28 +143,36 @@ void MtpProperty::read(MtpDataPacket& packet) { case MTP_TYPE_AINT128: case MTP_TYPE_AUINT128: mDefaultArrayValues = readArrayValues(packet, mDefaultArrayLength); - if (deviceProp) + if (!mDefaultArrayValues) return false; + if (deviceProp) { mCurrentArrayValues = readArrayValues(packet, mCurrentArrayLength); + if (!mCurrentArrayValues) return false; + } break; default: - readValue(packet, mDefaultValue); - if (deviceProp) - readValue(packet, mCurrentValue); + if (!readValue(packet, mDefaultValue)) return false; + if (deviceProp) { + if (!readValue(packet, mCurrentValue)) return false; + } } - if (!deviceProp) - mGroupCode = packet.getUInt32(); - mFormFlag = packet.getUInt8(); + if (!deviceProp) { + if (!packet.getUInt32(mGroupCode)) return false; + } + if (!packet.getUInt8(mFormFlag)) return false; if (mFormFlag == kFormRange) { - readValue(packet, mMinimumValue); - readValue(packet, mMaximumValue); - readValue(packet, mStepSize); + if (!readValue(packet, mMinimumValue)) return false; + if (!readValue(packet, mMaximumValue)) return false; + if (!readValue(packet, mStepSize)) return false; } else if (mFormFlag == kFormEnum) { - mEnumLength = packet.getUInt16(); + if (!packet.getUInt16(mEnumLength)) return false; mEnumValues = new MtpPropertyValue[mEnumLength]; - for (int i = 0; i < mEnumLength; i++) - readValue(packet, mEnumValues[i]); + for (int i = 0; i < mEnumLength; i++) { + if (!readValue(packet, mEnumValues[i])) return false; + } } + + return true; } void MtpProperty::write(MtpDataPacket& packet) { @@ -409,57 +420,59 @@ void MtpProperty::print(MtpPropertyValue& value, MtpString& buffer) { } } -void MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) { +bool MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) { MtpStringBuffer stringBuffer; switch (mType) { case MTP_TYPE_INT8: case MTP_TYPE_AINT8: - value.u.i8 = packet.getInt8(); + if (!packet.getInt8(value.u.i8)) return false; break; case MTP_TYPE_UINT8: case MTP_TYPE_AUINT8: - value.u.u8 = packet.getUInt8(); + if (!packet.getUInt8(value.u.u8)) return false; break; case MTP_TYPE_INT16: case MTP_TYPE_AINT16: - value.u.i16 = packet.getInt16(); + if (!packet.getInt16(value.u.i16)) return false; break; case MTP_TYPE_UINT16: case MTP_TYPE_AUINT16: - value.u.u16 = packet.getUInt16(); + if (!packet.getUInt16(value.u.u16)) return false; break; case MTP_TYPE_INT32: case MTP_TYPE_AINT32: - value.u.i32 = packet.getInt32(); + if (!packet.getInt32(value.u.i32)) return false; break; case MTP_TYPE_UINT32: case MTP_TYPE_AUINT32: - value.u.u32 = packet.getUInt32(); + if (!packet.getUInt32(value.u.u32)) return false; break; case MTP_TYPE_INT64: case MTP_TYPE_AINT64: - value.u.i64 = packet.getInt64(); + if (!packet.getInt64(value.u.i64)) return false; break; case MTP_TYPE_UINT64: case MTP_TYPE_AUINT64: - value.u.u64 = packet.getUInt64(); + if (!packet.getUInt64(value.u.u64)) return false; break; case MTP_TYPE_INT128: case MTP_TYPE_AINT128: - packet.getInt128(value.u.i128); + if (!packet.getInt128(value.u.i128)) return false; break; case MTP_TYPE_UINT128: case MTP_TYPE_AUINT128: - packet.getUInt128(value.u.u128); + if (!packet.getUInt128(value.u.u128)) return false; break; case MTP_TYPE_STR: - packet.getString(stringBuffer); + if (!packet.getString(stringBuffer)) return false; value.str = strdup(stringBuffer); break; default: ALOGE("unknown type %04X in MtpProperty::readValue", mType); + return false; } + return true; } void MtpProperty::writeValue(MtpDataPacket& packet, MtpPropertyValue& value) { @@ -517,8 +530,9 @@ void MtpProperty::writeValue(MtpDataPacket& packet, MtpPropertyValue& value) { } } -MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, int& length) { - length = packet.getUInt32(); +MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, uint32_t& length) { + if (!packet.getUInt32(length)) return NULL; + // 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( @@ -528,14 +542,17 @@ MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, int& lengt return NULL; } MtpPropertyValue* result = new MtpPropertyValue[length]; - for (int i = 0; i < length; i++) - readValue(packet, result[i]); + for (uint32_t i = 0; i < length; i++) + if (!readValue(packet, result[i])) { + delete result; + return NULL; + } return result; } -void MtpProperty::writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, int length) { +void MtpProperty::writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, uint32_t length) { packet.putUInt32(length); - for (int i = 0; i < length; i++) + for (uint32_t i = 0; i < length; i++) writeValue(packet, values[i]); } diff --git a/media/mtp/MtpProperty.h b/media/mtp/MtpProperty.h index 06ca56e..2e2ead1 100644 --- a/media/mtp/MtpProperty.h +++ b/media/mtp/MtpProperty.h @@ -49,9 +49,9 @@ public: MtpPropertyValue mCurrentValue; // for array types - int mDefaultArrayLength; + uint32_t mDefaultArrayLength; MtpPropertyValue* mDefaultArrayValues; - int mCurrentArrayLength; + uint32_t mCurrentArrayLength; MtpPropertyValue* mCurrentArrayValues; enum { @@ -70,7 +70,7 @@ public: MtpPropertyValue mStepSize; // for enum form - int mEnumLength; + uint16_t mEnumLength; MtpPropertyValue* mEnumValues; public: @@ -83,7 +83,7 @@ public: inline MtpPropertyCode getPropertyCode() const { return mCode; } - void read(MtpDataPacket& packet); + bool read(MtpDataPacket& packet); void write(MtpDataPacket& packet); void setDefaultValue(const uint16_t* string); @@ -102,11 +102,11 @@ public: } private: - void readValue(MtpDataPacket& packet, MtpPropertyValue& value); + bool readValue(MtpDataPacket& packet, MtpPropertyValue& value); void writeValue(MtpDataPacket& packet, MtpPropertyValue& value); - MtpPropertyValue* readArrayValues(MtpDataPacket& packet, int& length); + MtpPropertyValue* readArrayValues(MtpDataPacket& packet, uint32_t& length); void writeArrayValues(MtpDataPacket& packet, - MtpPropertyValue* values, int length); + MtpPropertyValue* values, uint32_t length); }; }; // namespace android diff --git a/media/mtp/MtpRequestPacket.cpp b/media/mtp/MtpRequestPacket.cpp index 0e58e01..40b11b0 100644 --- a/media/mtp/MtpRequestPacket.cpp +++ b/media/mtp/MtpRequestPacket.cpp @@ -27,7 +27,8 @@ namespace android { MtpRequestPacket::MtpRequestPacket() - : MtpPacket(512) + : MtpPacket(512), + mParameterCount(0) { } @@ -37,10 +38,21 @@ MtpRequestPacket::~MtpRequestPacket() { #ifdef MTP_DEVICE int MtpRequestPacket::read(int fd) { int ret = ::read(fd, mBuffer, mBufferSize); - if (ret >= 0) + if (ret < 0) { + // file read error + return ret; + } + + // request packet should have 12 byte header followed by 0 to 5 32-bit arguments + if (ret >= MTP_CONTAINER_HEADER_SIZE + && ret <= MTP_CONTAINER_HEADER_SIZE + 5 * sizeof(uint32_t) + && ((ret - MTP_CONTAINER_HEADER_SIZE) & 3) == 0) { mPacketSize = ret; - else - mPacketSize = 0; + mParameterCount = (ret - MTP_CONTAINER_HEADER_SIZE) / sizeof(uint32_t); + } else { + ALOGE("Malformed MTP request packet"); + ret = -1; + } return ret; } #endif diff --git a/media/mtp/MtpRequestPacket.h b/media/mtp/MtpRequestPacket.h index 1201f11..79b798d 100644 --- a/media/mtp/MtpRequestPacket.h +++ b/media/mtp/MtpRequestPacket.h @@ -43,6 +43,10 @@ public: inline MtpOperationCode getOperationCode() const { return getContainerCode(); } inline void setOperationCode(MtpOperationCode code) { return setContainerCode(code); } + inline int getParameterCount() const { return mParameterCount; } + +private: + int mParameterCount; }; }; // namespace android diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index aa43967..07199e3 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -495,6 +495,9 @@ MtpResponseCode MtpServer::doOpenSession() { mResponse.setParameter(1, mSessionID); return MTP_RESPONSE_SESSION_ALREADY_OPEN; } + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; + mSessionID = mRequest.getParameter(1); mSessionOpen = true; @@ -529,6 +532,9 @@ MtpResponseCode MtpServer::doGetStorageInfo() { if (!mSessionOpen) return MTP_RESPONSE_SESSION_NOT_OPEN; + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; + MtpStorageID id = mRequest.getParameter(1); MtpStorage* storage = getStorage(id); if (!storage) @@ -550,6 +556,8 @@ MtpResponseCode MtpServer::doGetStorageInfo() { MtpResponseCode MtpServer::doGetObjectPropsSupported() { if (!mSessionOpen) return MTP_RESPONSE_SESSION_NOT_OPEN; + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectFormat format = mRequest.getParameter(1); MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format); mData.putAUInt16(properties); @@ -560,6 +568,8 @@ MtpResponseCode MtpServer::doGetObjectPropsSupported() { MtpResponseCode MtpServer::doGetObjectHandles() { if (!mSessionOpen) return MTP_RESPONSE_SESSION_NOT_OPEN; + if (mRequest.getParameterCount() < 3) + return MTP_RESPONSE_INVALID_PARAMETER; MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent @@ -577,6 +587,8 @@ MtpResponseCode MtpServer::doGetObjectHandles() { MtpResponseCode MtpServer::doGetNumObjects() { if (!mSessionOpen) return MTP_RESPONSE_SESSION_NOT_OPEN; + if (mRequest.getParameterCount() < 3) + return MTP_RESPONSE_INVALID_PARAMETER; MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent @@ -599,6 +611,8 @@ MtpResponseCode MtpServer::doGetObjectReferences() { return MTP_RESPONSE_SESSION_NOT_OPEN; if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); // FIXME - check for invalid object handle @@ -617,9 +631,13 @@ MtpResponseCode MtpServer::doSetObjectReferences() { return MTP_RESPONSE_SESSION_NOT_OPEN; if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpStorageID handle = mRequest.getParameter(1); MtpObjectHandleList* references = mData.getAUInt32(); + if (!references) + return MTP_RESPONSE_INVALID_PARAMETER; MtpResponseCode result = mDatabase->setObjectReferences(handle, references); delete references; return result; @@ -628,6 +646,8 @@ MtpResponseCode MtpServer::doSetObjectReferences() { MtpResponseCode MtpServer::doGetObjectPropValue() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + if (mRequest.getParameterCount() < 2) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); MtpObjectProperty property = mRequest.getParameter(2); ALOGV("GetObjectPropValue %d %s\n", handle, @@ -639,6 +659,8 @@ MtpResponseCode MtpServer::doGetObjectPropValue() { MtpResponseCode MtpServer::doSetObjectPropValue() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + if (mRequest.getParameterCount() < 2) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); MtpObjectProperty property = mRequest.getParameter(2); ALOGV("SetObjectPropValue %d %s\n", handle, @@ -648,6 +670,8 @@ MtpResponseCode MtpServer::doSetObjectPropValue() { } MtpResponseCode MtpServer::doGetDevicePropValue() { + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpDeviceProperty property = mRequest.getParameter(1); ALOGV("GetDevicePropValue %s\n", MtpDebug::getDevicePropCodeName(property)); @@ -656,6 +680,8 @@ MtpResponseCode MtpServer::doGetDevicePropValue() { } MtpResponseCode MtpServer::doSetDevicePropValue() { + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpDeviceProperty property = mRequest.getParameter(1); ALOGV("SetDevicePropValue %s\n", MtpDebug::getDevicePropCodeName(property)); @@ -664,6 +690,8 @@ MtpResponseCode MtpServer::doSetDevicePropValue() { } MtpResponseCode MtpServer::doResetDevicePropValue() { + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpDeviceProperty property = mRequest.getParameter(1); ALOGV("ResetDevicePropValue %s\n", MtpDebug::getDevicePropCodeName(property)); @@ -674,6 +702,8 @@ MtpResponseCode MtpServer::doResetDevicePropValue() { MtpResponseCode MtpServer::doGetObjectPropList() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + if (mRequest.getParameterCount() < 5) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); // use uint32_t so we can support 0xFFFFFFFF @@ -691,6 +721,8 @@ MtpResponseCode MtpServer::doGetObjectPropList() { MtpResponseCode MtpServer::doGetObjectInfo() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); MtpObjectInfo info(handle); MtpResponseCode result = mDatabase->getObjectInfo(handle, info); @@ -732,6 +764,8 @@ MtpResponseCode MtpServer::doGetObjectInfo() { MtpResponseCode MtpServer::doGetObject() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); MtpString pathBuf; int64_t fileLength; @@ -765,6 +799,8 @@ MtpResponseCode MtpServer::doGetObject() { } MtpResponseCode MtpServer::doGetThumb() { + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); size_t thumbSize; void* thumb = mDatabase->getThumbnail(handle, thumbSize); @@ -788,11 +824,19 @@ MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) { uint32_t length; offset = mRequest.getParameter(2); if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) { + // MTP_OPERATION_GET_PARTIAL_OBJECT_64 takes 4 arguments + if (mRequest.getParameterCount() < 4) + return MTP_RESPONSE_INVALID_PARAMETER; + // android extension with 64 bit offset uint64_t offset2 = mRequest.getParameter(3); offset = offset | (offset2 << 32); length = mRequest.getParameter(4); } else { + // MTP_OPERATION_GET_PARTIAL_OBJECT takes 3 arguments + if (mRequest.getParameterCount() < 3) + return MTP_RESPONSE_INVALID_PARAMETER; + // standard GetPartialObject length = mRequest.getParameter(3); } @@ -832,6 +876,11 @@ MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) { MtpResponseCode MtpServer::doSendObjectInfo() { MtpString path; + uint16_t temp16; + uint32_t temp32; + + if (mRequest.getParameterCount() < 2) + return MTP_RESPONSE_INVALID_PARAMETER; MtpStorageID storageID = mRequest.getParameter(1); MtpStorage* storage = getStorage(storageID); MtpObjectHandle parent = mRequest.getParameter(2); @@ -853,25 +902,29 @@ MtpResponseCode MtpServer::doSendObjectInfo() { } // read only the fields we need - mData.getUInt32(); // storage ID - MtpObjectFormat format = mData.getUInt16(); - mData.getUInt16(); // protection status - mSendObjectFileSize = mData.getUInt32(); - mData.getUInt16(); // thumb format - mData.getUInt32(); // thumb compressed size - mData.getUInt32(); // thumb pix width - mData.getUInt32(); // thumb pix height - mData.getUInt32(); // image pix width - mData.getUInt32(); // image pix height - mData.getUInt32(); // image bit depth - mData.getUInt32(); // parent - uint16_t associationType = mData.getUInt16(); - uint32_t associationDesc = mData.getUInt32(); // association desc - mData.getUInt32(); // sequence number + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // storage ID + if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER; + MtpObjectFormat format = temp16; + if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER; // protection status + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; + mSendObjectFileSize = temp32; + if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb format + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb compressed size + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb pix width + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb pix height + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // image pix width + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // image pix height + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // image bit depth + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // parent + if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER; + uint16_t associationType = temp16; + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; + uint32_t associationDesc = temp32; // association desc + if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // sequence number MtpStringBuffer name, created, modified; - mData.getString(name); // file name - mData.getString(created); // date created - mData.getString(modified); // date modified + if (!mData.getString(name)) return MTP_RESPONSE_INVALID_PARAMETER; // file name + if (!mData.getString(created)) return MTP_RESPONSE_INVALID_PARAMETER; // date created + if (!mData.getString(modified)) return MTP_RESPONSE_INVALID_PARAMETER; // date modified // keywords follow ALOGV("name: %s format: %04X\n", (const char *)name, format); @@ -1066,8 +1119,10 @@ static void deletePath(const char* path) { MtpResponseCode MtpServer::doDeleteObject() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); - MtpObjectFormat format = mRequest.getParameter(2); + MtpObjectFormat format; // FIXME - support deleting all objects if handle is 0xFFFFFFFF // FIXME - implement deleting objects by format @@ -1087,6 +1142,8 @@ MtpResponseCode MtpServer::doDeleteObject() { } MtpResponseCode MtpServer::doGetObjectPropDesc() { + if (mRequest.getParameterCount() < 2) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectProperty propCode = mRequest.getParameter(1); MtpObjectFormat format = mRequest.getParameter(2); ALOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode), @@ -1100,6 +1157,8 @@ MtpResponseCode MtpServer::doGetObjectPropDesc() { } MtpResponseCode MtpServer::doGetDevicePropDesc() { + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpDeviceProperty propCode = mRequest.getParameter(1); ALOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode)); MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode); @@ -1113,6 +1172,8 @@ MtpResponseCode MtpServer::doGetDevicePropDesc() { MtpResponseCode MtpServer::doSendPartialObject() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + if (mRequest.getParameterCount() < 4) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); uint64_t offset = mRequest.getParameter(2); uint64_t offset2 = mRequest.getParameter(3); @@ -1180,6 +1241,8 @@ MtpResponseCode MtpServer::doSendPartialObject() { } MtpResponseCode MtpServer::doTruncateObject() { + if (mRequest.getParameterCount() < 3) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); ObjectEdit* edit = getEditObject(handle); if (!edit) { @@ -1199,6 +1262,8 @@ MtpResponseCode MtpServer::doTruncateObject() { } MtpResponseCode MtpServer::doBeginEditObject() { + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); if (getEditObject(handle)) { ALOGE("object already open for edit in doBeginEditObject"); @@ -1223,6 +1288,8 @@ MtpResponseCode MtpServer::doBeginEditObject() { } MtpResponseCode MtpServer::doEndEditObject() { + if (mRequest.getParameterCount() < 1) + return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle handle = mRequest.getParameter(1); ObjectEdit* edit = getEditObject(handle); if (!edit) { diff --git a/media/mtp/MtpStorageInfo.cpp b/media/mtp/MtpStorageInfo.cpp index 2b1a9ae..5d4ebbf 100644 --- a/media/mtp/MtpStorageInfo.cpp +++ b/media/mtp/MtpStorageInfo.cpp @@ -45,21 +45,23 @@ MtpStorageInfo::~MtpStorageInfo() { free(mVolumeIdentifier); } -void MtpStorageInfo::read(MtpDataPacket& packet) { +bool MtpStorageInfo::read(MtpDataPacket& packet) { MtpStringBuffer string; // read the device info - mStorageType = packet.getUInt16(); - mFileSystemType = packet.getUInt16(); - mAccessCapability = packet.getUInt16(); - mMaxCapacity = packet.getUInt64(); - mFreeSpaceBytes = packet.getUInt64(); - mFreeSpaceObjects = packet.getUInt32(); + if (!packet.getUInt16(mStorageType)) return false; + if (!packet.getUInt16(mFileSystemType)) return false; + if (!packet.getUInt16(mAccessCapability)) return false; + if (!packet.getUInt64(mMaxCapacity)) return false; + if (!packet.getUInt64(mFreeSpaceBytes)) return false; + if (!packet.getUInt32(mFreeSpaceObjects)) return false; - packet.getString(string); + if (!packet.getString(string)) return false; mStorageDescription = strdup((const char *)string); - packet.getString(string); + if (!packet.getString(string)) return false; mVolumeIdentifier = strdup((const char *)string); + + return true; } void MtpStorageInfo::print() { diff --git a/media/mtp/MtpStorageInfo.h b/media/mtp/MtpStorageInfo.h index 2cb626e..35a8189 100644 --- a/media/mtp/MtpStorageInfo.h +++ b/media/mtp/MtpStorageInfo.h @@ -39,7 +39,7 @@ public: MtpStorageInfo(MtpStorageID id); virtual ~MtpStorageInfo(); - void read(MtpDataPacket& packet); + bool read(MtpDataPacket& packet); void print(); }; diff --git a/media/mtp/MtpStringBuffer.cpp b/media/mtp/MtpStringBuffer.cpp index f3420a4..df04694 100644 --- a/media/mtp/MtpStringBuffer.cpp +++ b/media/mtp/MtpStringBuffer.cpp @@ -123,11 +123,17 @@ void MtpStringBuffer::set(const uint16_t* src) { mByteCount = dest - mBuffer; } -void MtpStringBuffer::readFromPacket(MtpDataPacket* packet) { - int count = packet->getUInt8(); +bool MtpStringBuffer::readFromPacket(MtpDataPacket* packet) { + uint8_t count; + if (!packet->getUInt8(count)) + return false; + uint8_t* dest = mBuffer; for (int i = 0; i < count; i++) { - uint16_t ch = packet->getUInt16(); + uint16_t ch; + + if (!packet->getUInt16(ch)) + return false; if (ch >= 0x0800) { *dest++ = (uint8_t)(0xE0 | (ch >> 12)); *dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F)); @@ -142,6 +148,7 @@ void MtpStringBuffer::readFromPacket(MtpDataPacket* packet) { *dest++ = 0; mCharCount = count; mByteCount = dest - mBuffer; + return true; } void MtpStringBuffer::writeToPacket(MtpDataPacket* packet) const { diff --git a/media/mtp/MtpStringBuffer.h b/media/mtp/MtpStringBuffer.h index e5150df..85d91e8 100644 --- a/media/mtp/MtpStringBuffer.h +++ b/media/mtp/MtpStringBuffer.h @@ -46,7 +46,7 @@ public: void set(const char* src); void set(const uint16_t* src); - void readFromPacket(MtpDataPacket* packet); + bool readFromPacket(MtpDataPacket* packet); void writeToPacket(MtpDataPacket* packet) const; inline int getCharCount() const { return mCharCount; } diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp index ed00b72..80c1c2f 100644 --- a/media/ndk/NdkMediaCodec.cpp +++ b/media/ndk/NdkMediaCodec.cpp @@ -116,7 +116,7 @@ void CodecHandler::onMessageReceived(const sp<AMessage> &msg) { case kWhatStopActivityNotifications: { - uint32_t replyID; + sp<AReplyToken> replyID; msg->senderAwaitsResponse(&replyID); mCodec->mGeneration++; @@ -136,7 +136,7 @@ void CodecHandler::onMessageReceived(const sp<AMessage> &msg) { static void requestActivityNotification(AMediaCodec *codec) { - (new AMessage(kWhatRequestActivityNotifications, codec->mHandler->id()))->post(); + (new AMessage(kWhatRequestActivityNotifications, codec->mHandler))->post(); } extern "C" { @@ -219,7 +219,7 @@ media_status_t AMediaCodec_start(AMediaCodec *mData) { if (ret != OK) { return translate_error(ret); } - mData->mActivityNotification = new AMessage(kWhatActivityNotify, mData->mHandler->id()); + mData->mActivityNotification = new AMessage(kWhatActivityNotify, mData->mHandler); mData->mActivityNotification->setInt32("generation", mData->mGeneration); requestActivityNotification(mData); return AMEDIA_OK; @@ -229,7 +229,7 @@ EXPORT media_status_t AMediaCodec_stop(AMediaCodec *mData) { media_status_t ret = translate_error(mData->mCodec->stop()); - sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, mData->mHandler->id()); + sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, mData->mHandler); sp<AMessage> response; msg->postAndAwaitResponse(&response); mData->mActivityNotification.clear(); @@ -352,7 +352,8 @@ media_status_t AMediaCodec_releaseOutputBufferAtTime( } //EXPORT -media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback, void *userdata) { +media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback, + void *userdata) { mData->mCallback = callback; mData->mCallbackUserData = userdata; return AMEDIA_OK; diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp index db57d0b..0ecd64f 100644 --- a/media/ndk/NdkMediaExtractor.cpp +++ b/media/ndk/NdkMediaExtractor.cpp @@ -70,7 +70,8 @@ media_status_t AMediaExtractor_delete(AMediaExtractor *mData) { } EXPORT -media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, off64_t offset, off64_t length) { +media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, off64_t offset, + off64_t length) { ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); return translate_error(mData->mImpl->setDataSource(fd, offset, length)); } diff --git a/radio/Android.mk b/radio/Android.mk new file mode 100644 index 0000000..ecbb8fd --- /dev/null +++ b/radio/Android.mk @@ -0,0 +1,39 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + Radio.cpp \ + IRadio.cpp \ + IRadioClient.cpp \ + IRadioService.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libbinder \ + libhardware \ + libradio_metadata + +#LOCAL_C_INCLUDES += \ + system/media/camera/include \ + system/media/private/camera/include + +LOCAL_MODULE:= libradio + +include $(BUILD_SHARED_LIBRARY) diff --git a/radio/IRadio.cpp b/radio/IRadio.cpp new file mode 100644 index 0000000..242df77 --- /dev/null +++ b/radio/IRadio.cpp @@ -0,0 +1,344 @@ +/* +** +** Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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 "IRadio" +#include <utils/Log.h> +#include <utils/Errors.h> +#include <binder/IMemory.h> +#include <radio/IRadio.h> +#include <radio/IRadioService.h> +#include <radio/IRadioClient.h> +#include <system/radio.h> +#include <system/radio_metadata.h> + +namespace android { + +enum { + DETACH = IBinder::FIRST_CALL_TRANSACTION, + SET_CONFIGURATION, + GET_CONFIGURATION, + SET_MUTE, + GET_MUTE, + SCAN, + STEP, + TUNE, + CANCEL, + GET_PROGRAM_INFORMATION, + HAS_CONTROL +}; + +class BpRadio: public BpInterface<IRadio> +{ +public: + BpRadio(const sp<IBinder>& impl) + : BpInterface<IRadio>(impl) + { + } + + void detach() + { + ALOGV("detach"); + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + remote()->transact(DETACH, data, &reply); + } + + virtual status_t setConfiguration(const struct radio_band_config *config) + { + Parcel data, reply; + if (config == NULL) { + return BAD_VALUE; + } + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + data.write(config, sizeof(struct radio_band_config)); + status_t status = remote()->transact(SET_CONFIGURATION, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t getConfiguration(struct radio_band_config *config) + { + Parcel data, reply; + if (config == NULL) { + return BAD_VALUE; + } + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_CONFIGURATION, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + if (status == NO_ERROR) { + reply.read(config, sizeof(struct radio_band_config)); + } + } + return status; + } + + virtual status_t setMute(bool mute) + { + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + data.writeInt32(mute ? 1 : 0); + status_t status = remote()->transact(SET_MUTE, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t getMute(bool *mute) + { + Parcel data, reply; + if (mute == NULL) { + return BAD_VALUE; + } + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_MUTE, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + if (status == NO_ERROR) { + int muteread = reply.readInt32(); + *mute = muteread != 0; + } + } + return status; + } + + virtual status_t scan(radio_direction_t direction, bool skipSubChannel) + { + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + data.writeInt32(direction); + data.writeInt32(skipSubChannel ? 1 : 0); + status_t status = remote()->transact(SCAN, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t step(radio_direction_t direction, bool skipSubChannel) + { + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + data.writeInt32(direction); + data.writeInt32(skipSubChannel ? 1 : 0); + status_t status = remote()->transact(STEP, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t tune(unsigned int channel, unsigned int subChannel) + { + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + data.writeInt32(channel); + data.writeInt32(subChannel); + status_t status = remote()->transact(TUNE, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t cancel() + { + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + status_t status = remote()->transact(CANCEL, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t getProgramInformation(struct radio_program_info *info) + { + Parcel data, reply; + if (info == NULL) { + return BAD_VALUE; + } + radio_metadata_t *metadata = info->metadata; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_PROGRAM_INFORMATION, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + if (status == NO_ERROR) { + reply.read(info, sizeof(struct radio_program_info)); + info->metadata = metadata; + if (metadata == NULL) { + return status; + } + size_t size = (size_t)reply.readInt32(); + if (size == 0) { + return status; + } + metadata = + (radio_metadata_t *)calloc(size / sizeof(unsigned int), sizeof(unsigned int)); + if (metadata == NULL) { + return NO_MEMORY; + } + reply.read(metadata, size); + status = radio_metadata_add_metadata(&info->metadata, metadata); + free(metadata); + } + } + return status; + } + + virtual status_t hasControl(bool *hasControl) + { + Parcel data, reply; + if (hasControl == NULL) { + return BAD_VALUE; + } + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + status_t status = remote()->transact(HAS_CONTROL, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + if (status == NO_ERROR) { + *hasControl = reply.readInt32() != 0; + } + } + return status; + } +}; + +IMPLEMENT_META_INTERFACE(Radio, "android.hardware.IRadio"); + +// ---------------------------------------------------------------------- + +status_t BnRadio::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DETACH: { + ALOGV("DETACH"); + CHECK_INTERFACE(IRadio, data, reply); + detach(); + return NO_ERROR; + } break; + case SET_CONFIGURATION: { + CHECK_INTERFACE(IRadio, data, reply); + struct radio_band_config config; + data.read(&config, sizeof(struct radio_band_config)); + status_t status = setConfiguration(&config); + reply->writeInt32(status); + return NO_ERROR; + } + case GET_CONFIGURATION: { + CHECK_INTERFACE(IRadio, data, reply); + struct radio_band_config config; + status_t status = getConfiguration(&config); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->write(&config, sizeof(struct radio_band_config)); + } + return NO_ERROR; + } + case SET_MUTE: { + CHECK_INTERFACE(IRadio, data, reply); + bool mute = data.readInt32() != 0; + status_t status = setMute(mute); + reply->writeInt32(status); + return NO_ERROR; + } + case GET_MUTE: { + CHECK_INTERFACE(IRadio, data, reply); + bool mute; + status_t status = getMute(&mute); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->writeInt32(mute ? 1 : 0); + } + return NO_ERROR; + } + case SCAN: { + CHECK_INTERFACE(IRadio, data, reply); + radio_direction_t direction = (radio_direction_t)data.readInt32(); + bool skipSubChannel = data.readInt32() == 1; + status_t status = scan(direction, skipSubChannel); + reply->writeInt32(status); + return NO_ERROR; + } + case STEP: { + CHECK_INTERFACE(IRadio, data, reply); + radio_direction_t direction = (radio_direction_t)data.readInt32(); + bool skipSubChannel = data.readInt32() == 1; + status_t status = step(direction, skipSubChannel); + reply->writeInt32(status); + return NO_ERROR; + } + case TUNE: { + CHECK_INTERFACE(IRadio, data, reply); + unsigned int channel = (unsigned int)data.readInt32(); + unsigned int subChannel = (unsigned int)data.readInt32(); + status_t status = tune(channel, subChannel); + reply->writeInt32(status); + return NO_ERROR; + } + case CANCEL: { + CHECK_INTERFACE(IRadio, data, reply); + status_t status = cancel(); + reply->writeInt32(status); + return NO_ERROR; + } + case GET_PROGRAM_INFORMATION: { + CHECK_INTERFACE(IRadio, data, reply); + struct radio_program_info info; + + status_t status = radio_metadata_allocate(&info.metadata, 0, 0); + if (status != NO_ERROR) { + return status; + } + status = getProgramInformation(&info); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->write(&info, sizeof(struct radio_program_info)); + int count = radio_metadata_get_count(info.metadata); + if (count > 0) { + size_t size = radio_metadata_get_size(info.metadata); + reply->writeInt32(size); + reply->write(info.metadata, size); + } else { + reply->writeInt32(0); + } + } + radio_metadata_deallocate(info.metadata); + return NO_ERROR; + } + case HAS_CONTROL: { + CHECK_INTERFACE(IRadio, data, reply); + bool control; + status_t status = hasControl(&control); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->writeInt32(control ? 1 : 0); + } + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/radio/IRadioClient.cpp b/radio/IRadioClient.cpp new file mode 100644 index 0000000..033ca49 --- /dev/null +++ b/radio/IRadioClient.cpp @@ -0,0 +1,75 @@ +/* +** +** Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <stdint.h> +#include <sys/types.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <radio/IRadioClient.h> + +namespace android { + +enum { + ON_EVENT = IBinder::FIRST_CALL_TRANSACTION, +}; + +class BpRadioClient: public BpInterface<IRadioClient> +{ + +public: + BpRadioClient(const sp<IBinder>& impl) + : BpInterface<IRadioClient>(impl) + { + } + + virtual void onEvent(const sp<IMemory>& eventMemory) + { + Parcel data, reply; + data.writeInterfaceToken(IRadioClient::getInterfaceDescriptor()); + data.writeStrongBinder(IInterface::asBinder(eventMemory)); + remote()->transact(ON_EVENT, + data, + &reply); + } +}; + +IMPLEMENT_META_INTERFACE(RadioClient, + "android.hardware.IRadioClient"); + +// ---------------------------------------------------------------------- + +status_t BnRadioClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ON_EVENT: { + CHECK_INTERFACE(IRadioClient, data, reply); + sp<IMemory> eventMemory = interface_cast<IMemory>( + data.readStrongBinder()); + onEvent(eventMemory); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/radio/IRadioService.cpp b/radio/IRadioService.cpp new file mode 100644 index 0000000..8c2b3ef --- /dev/null +++ b/radio/IRadioService.cpp @@ -0,0 +1,181 @@ +/* +** +** Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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 "BpRadioService" +// +#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Errors.h> + +#include <stdint.h> +#include <sys/types.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <radio/IRadioService.h> +#include <radio/IRadio.h> +#include <radio/IRadioClient.h> + +namespace android { + +enum { + LIST_MODULES = IBinder::FIRST_CALL_TRANSACTION, + ATTACH, +}; + +#define MAX_ITEMS_PER_LIST 1024 + +class BpRadioService: public BpInterface<IRadioService> +{ +public: + BpRadioService(const sp<IBinder>& impl) + : BpInterface<IRadioService>(impl) + { + } + + virtual status_t listModules(struct radio_properties *properties, + uint32_t *numModules) + { + if (numModules == NULL || (*numModules != 0 && properties == NULL)) { + return BAD_VALUE; + } + Parcel data, reply; + data.writeInterfaceToken(IRadioService::getInterfaceDescriptor()); + unsigned int numModulesReq = (properties == NULL) ? 0 : *numModules; + data.writeInt32(numModulesReq); + status_t status = remote()->transact(LIST_MODULES, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + *numModules = (unsigned int)reply.readInt32(); + } + ALOGV("listModules() status %d got *numModules %d", status, *numModules); + if (status == NO_ERROR) { + if (numModulesReq > *numModules) { + numModulesReq = *numModules; + } + if (numModulesReq > 0) { + reply.read(properties, numModulesReq * sizeof(struct radio_properties)); + } + } + return status; + } + + virtual status_t attach(radio_handle_t handle, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool withAudio, + sp<IRadio>& radio) + { + Parcel data, reply; + data.writeInterfaceToken(IRadioService::getInterfaceDescriptor()); + data.writeInt32(handle); + data.writeStrongBinder(IInterface::asBinder(client)); + ALOGV("attach() config %p withAudio %d region %d type %d", config, withAudio, config->region, config->band.type); + if (config == NULL) { + data.writeInt32(0); + } else { + data.writeInt32(1); + data.write(config, sizeof(struct radio_band_config)); + } + data.writeInt32(withAudio ? 1 : 0); + status_t status = remote()->transact(ATTACH, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = reply.readInt32(); + if (reply.readInt32() != 0) { + radio = interface_cast<IRadio>(reply.readStrongBinder()); + } + return status; + } +}; + +IMPLEMENT_META_INTERFACE(RadioService, "android.hardware.IRadioService"); + +// ---------------------------------------------------------------------- + +status_t BnRadioService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case LIST_MODULES: { + CHECK_INTERFACE(IRadioService, data, reply); + unsigned int numModulesReq = data.readInt32(); + if (numModulesReq > MAX_ITEMS_PER_LIST) { + numModulesReq = MAX_ITEMS_PER_LIST; + } + unsigned int numModules = numModulesReq; + struct radio_properties *properties = + (struct radio_properties *)calloc(numModulesReq, + sizeof(struct radio_properties)); + if (properties == NULL) { + reply->writeInt32(NO_MEMORY); + reply->writeInt32(0); + return NO_ERROR; + } + + status_t status = listModules(properties, &numModules); + reply->writeInt32(status); + reply->writeInt32(numModules); + ALOGV("LIST_MODULES status %d got numModules %d", status, numModules); + + if (status == NO_ERROR) { + if (numModulesReq > numModules) { + numModulesReq = numModules; + } + reply->write(properties, + numModulesReq * sizeof(struct radio_properties)); + } + free(properties); + return NO_ERROR; + } break; + + case ATTACH: { + CHECK_INTERFACE(IRadioService, data, reply); + radio_handle_t handle = data.readInt32(); + sp<IRadioClient> client = + interface_cast<IRadioClient>(data.readStrongBinder()); + struct radio_band_config config; + struct radio_band_config *configPtr = NULL; + if (data.readInt32() != 0) { + data.read(&config, sizeof(struct radio_band_config)); + configPtr = &config; + } + bool withAudio = data.readInt32() != 0; + ALOGV("ATTACH configPtr %p withAudio %d", configPtr, withAudio); + sp<IRadio> radio; + status_t status = attach(handle, client, configPtr, withAudio, radio); + reply->writeInt32(status); + if (radio != 0) { + reply->writeInt32(1); + reply->writeStrongBinder(IInterface::asBinder(radio)); + } else { + reply->writeInt32(0); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/radio/Radio.cpp b/radio/Radio.cpp new file mode 100644 index 0000000..e3554c2 --- /dev/null +++ b/radio/Radio.cpp @@ -0,0 +1,283 @@ +/* +** +** Copyright (C) 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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 "Radio" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/threads.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/IMemory.h> + +#include <radio/Radio.h> +#include <radio/IRadio.h> +#include <radio/IRadioService.h> +#include <radio/IRadioClient.h> +#include <radio/RadioCallback.h> + +namespace android { + +namespace { + sp<IRadioService> gRadioService; + const int kRadioServicePollDelay = 500000; // 0.5s + const char* kRadioServiceName = "media.radio"; + Mutex gLock; + + class DeathNotifier : public IBinder::DeathRecipient + { + public: + DeathNotifier() { + } + + virtual void binderDied(const wp<IBinder>& who __unused) { + ALOGV("binderDied"); + Mutex::Autolock _l(gLock); + gRadioService.clear(); + ALOGW("Radio service died!"); + } + }; + + sp<DeathNotifier> gDeathNotifier; +}; // namespace anonymous + +const sp<IRadioService>& Radio::getRadioService() +{ + Mutex::Autolock _l(gLock); + if (gRadioService.get() == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16(kRadioServiceName)); + if (binder != 0) { + break; + } + ALOGW("RadioService not published, waiting..."); + usleep(kRadioServicePollDelay); + } while(true); + if (gDeathNotifier == NULL) { + gDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(gDeathNotifier); + gRadioService = interface_cast<IRadioService>(binder); + } + ALOGE_IF(gRadioService == 0, "no RadioService!?"); + return gRadioService; +} + +// Static methods +status_t Radio::listModules(struct radio_properties *properties, + uint32_t *numModules) +{ + ALOGV("listModules()"); + const sp<IRadioService>& service = getRadioService(); + if (service == 0) { + return NO_INIT; + } + return service->listModules(properties, numModules); +} + +sp<Radio> Radio::attach(radio_handle_t handle, + const struct radio_band_config *config, + bool withAudio, + const sp<RadioCallback>& callback) +{ + ALOGV("attach()"); + sp<Radio> radio; + const sp<IRadioService>& service = getRadioService(); + if (service == 0) { + return radio; + } + radio = new Radio(handle, callback); + status_t status = service->attach(handle, radio, config, withAudio, radio->mIRadio); + + if (status == NO_ERROR && radio->mIRadio != 0) { + IInterface::asBinder(radio->mIRadio)->linkToDeath(radio); + } else { + ALOGW("Error %d connecting to radio service", status); + radio.clear(); + } + return radio; +} + + + +// Radio +Radio::Radio(radio_handle_t handle, const sp<RadioCallback>& callback) + : mHandle(handle), mCallback(callback) +{ +} + +Radio::~Radio() +{ + if (mIRadio != 0) { + mIRadio->detach(); + } +} + + +void Radio::detach() { + ALOGV("detach()"); + Mutex::Autolock _l(mLock); + mCallback.clear(); + if (mIRadio != 0) { + mIRadio->detach(); + IInterface::asBinder(mIRadio)->unlinkToDeath(this); + mIRadio = 0; + } +} + +status_t Radio::setConfiguration(const struct radio_band_config *config) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->setConfiguration(config); +} + +status_t Radio::getConfiguration(struct radio_band_config *config) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->getConfiguration(config); +} + +status_t Radio::setMute(bool mute) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->setMute(mute); +} + +status_t Radio::getMute(bool *mute) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->getMute(mute); +} + +status_t Radio::scan(radio_direction_t direction, bool skipSubchannel) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->scan(direction, skipSubchannel); +} + +status_t Radio::step(radio_direction_t direction, bool skipSubchannel) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->step(direction, skipSubchannel); +} + +status_t Radio::tune(unsigned int channel, unsigned int subChannel) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->tune(channel, subChannel); +} + +status_t Radio::cancel() +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->cancel(); +} + +status_t Radio::getProgramInformation(struct radio_program_info *info) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->getProgramInformation(info); +} + +status_t Radio::hasControl(bool *hasControl) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->hasControl(hasControl); +} + + +// BpRadioClient +void Radio::onEvent(const sp<IMemory>& eventMemory) +{ + Mutex::Autolock _l(mLock); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + + struct radio_event *event = (struct radio_event *)eventMemory->pointer(); + // restore local metadata pointer from offset + switch (event->type) { + case RADIO_EVENT_TUNED: + case RADIO_EVENT_AF_SWITCH: + if (event->info.metadata != NULL) { + event->info.metadata = + (radio_metadata_t *)((char *)event + (size_t)event->info.metadata); + } + break; + case RADIO_EVENT_METADATA: + if (event->metadata != NULL) { + event->metadata = + (radio_metadata_t *)((char *)event + (size_t)event->metadata); + } + break; + default: + break; + } + + if (mCallback != 0) { + mCallback->onEvent(event); + } +} + + +//IBinder::DeathRecipient +void Radio::binderDied(const wp<IBinder>& who __unused) { + Mutex::Autolock _l(mLock); + ALOGW("Radio server binder Died "); + mIRadio = 0; + struct radio_event event; + memset(&event, 0, sizeof(struct radio_event)); + event.type = RADIO_EVENT_SERVER_DIED; + event.status = DEAD_OBJECT; + if (mCallback != 0) { + mCallback->onEvent(&event); + } +} + +}; // namespace android diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 697fb37..642ff82 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -19,10 +19,22 @@ LOCAL_SRC_FILES := \ # FIXME Move this library to frameworks/native LOCAL_MODULE := libserviceutility -include $(BUILD_STATIC_LIBRARY) +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libbinder + +include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) +# Clang++ aborts on AudioMixer.cpp, +# b/18373866, "do not know how to split this operator." +ifeq ($(filter $(TARGET_ARCH),arm arm64),$(TARGET_ARCH)) + LOCAL_CLANG := false +endif + LOCAL_SRC_FILES:= \ AudioFlinger.cpp \ Threads.cpp \ @@ -51,30 +63,31 @@ LOCAL_SHARED_LIBRARIES := \ libhardware \ libhardware_legacy \ libeffects \ - libpowermanager + libpowermanager \ + libserviceutility LOCAL_STATIC_LIBRARIES := \ libscheduling_policy \ libcpustats \ - libmedia_helper \ - libserviceutility + libmedia_helper LOCAL_MODULE:= libaudioflinger LOCAL_32_BIT_ONLY := true -LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp -LOCAL_SRC_FILES += FastThread.cpp FastThreadState.cpp -LOCAL_SRC_FILES += FastCapture.cpp FastCaptureState.cpp +LOCAL_SRC_FILES += \ + AudioWatchdog.cpp \ + FastCapture.cpp \ + FastCaptureDumpState.cpp \ + FastCaptureState.cpp \ + FastMixer.cpp \ + FastMixerDumpState.cpp \ + FastMixerState.cpp \ + FastThread.cpp \ + FastThreadDumpState.cpp \ + FastThreadState.cpp LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"' -# Define ANDROID_SMP appropriately. Used to get inline tracing fast-path. -ifeq ($(TARGET_CPU_SMP),true) - LOCAL_CFLAGS += -DANDROID_SMP=1 -else - LOCAL_CFLAGS += -DANDROID_SMP=0 -endif - LOCAL_CFLAGS += -fvisibility=hidden include $(BUILD_SHARED_LIBRARY) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 115b60c..461b5d3 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -185,7 +185,8 @@ 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", MemoryHeapBase::READ_ONLY); + mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters", + MemoryHeapBase::READ_ONLY); } #ifdef TEE_SINK @@ -825,14 +826,20 @@ bool AudioFlinger::getMicMute() const if (ret != NO_ERROR) { return false; } - + bool mute = true; bool state = AUDIO_MODE_INVALID; AutoMutex lock(mHardwareLock); - audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice(); mHardwareStatus = AUDIO_HW_GET_MIC_MUTE; - dev->get_mic_mute(dev, &state); + for (size_t i = 0; i < mAudioHwDevs.size(); i++) { + audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice(); + status_t result = dev->get_mic_mute(dev, &state); + if (result == NO_ERROR) { + mute = mute && state; + } + } mHardwareStatus = AUDIO_HW_IDLE; - return state; + + return mute; } status_t AudioFlinger::setMasterMute(bool muted) @@ -894,6 +901,21 @@ bool AudioFlinger::masterMute_l() const return mMasterMute; } +status_t AudioFlinger::checkStreamType(audio_stream_type_t stream) const +{ + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + ALOGW("setStreamVolume() invalid stream %d", stream); + return BAD_VALUE; + } + pid_t caller = IPCThreadState::self()->getCallingPid(); + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT && caller != getpid_cached) { + ALOGW("setStreamVolume() pid %d cannot use internal stream type %d", caller, stream); + return PERMISSION_DENIED; + } + + return NO_ERROR; +} + status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output) { @@ -902,10 +924,11 @@ status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, return PERMISSION_DENIED; } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { - ALOGE("setStreamVolume() invalid stream %d", stream); - return BAD_VALUE; + status_t status = checkStreamType(stream); + if (status != NO_ERROR) { + return status; } + ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to change AUDIO_STREAM_PATCH volume"); AutoMutex lock(mLock); PlaybackThread *thread = NULL; @@ -936,8 +959,13 @@ status_t AudioFlinger::setStreamMute(audio_stream_type_t stream, bool muted) return PERMISSION_DENIED; } - if (uint32_t(stream) >= AUDIO_STREAM_CNT || - uint32_t(stream) == AUDIO_STREAM_ENFORCED_AUDIBLE) { + status_t status = checkStreamType(stream); + if (status != NO_ERROR) { + return status; + } + ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to mute AUDIO_STREAM_PATCH"); + + if (uint32_t(stream) == AUDIO_STREAM_ENFORCED_AUDIBLE) { ALOGE("setStreamMute() invalid stream %d", stream); return BAD_VALUE; } @@ -952,7 +980,8 @@ status_t AudioFlinger::setStreamMute(audio_stream_type_t stream, bool muted) float AudioFlinger::streamVolume(audio_stream_type_t stream, audio_io_handle_t output) const { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + status_t status = checkStreamType(stream); + if (status != NO_ERROR) { return 0.0f; } @@ -973,7 +1002,8 @@ float AudioFlinger::streamVolume(audio_stream_type_t stream, audio_io_handle_t o bool AudioFlinger::streamMute(audio_stream_type_t stream) const { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + status_t status = checkStreamType(stream); + if (status != NO_ERROR) { return true; } @@ -1191,7 +1221,7 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) mNotificationClients.add(pid, notificationClient); - sp<IBinder> binder = client->asBinder(); + sp<IBinder> binder = IInterface::asBinder(client); binder->linkToDeath(notificationClient); clientAdded = true; } @@ -1413,13 +1443,6 @@ sp<IAudioRecord> AudioFlinger::openRecord( goto Exit; } - if (deviceRequiresCaptureAudioOutputPermission(thread->inDevice()) - && !captureAudioOutputAllowed()) { - ALOGE("openRecord() permission denied: capture not allowed"); - lStatus = PERMISSION_DENIED; - goto Exit; - } - pid_t pid = IPCThreadState::self()->getCallingPid(); client = registerPid(pid); @@ -1934,18 +1957,18 @@ status_t AudioFlinger::restoreOutput(audio_io_handle_t output) status_t AudioFlinger::openInput(audio_module_handle_t module, audio_io_handle_t *input, audio_config_t *config, - audio_devices_t *device, + audio_devices_t *devices, const String8& address, audio_source_t source, audio_input_flags_t flags) { Mutex::Autolock _l(mLock); - if (*device == AUDIO_DEVICE_NONE) { + if (*devices == AUDIO_DEVICE_NONE) { return BAD_VALUE; } - sp<RecordThread> thread = openInput_l(module, input, config, *device, address, source, flags); + sp<RecordThread> thread = openInput_l(module, input, config, *devices, address, source, flags); if (thread != 0) { // notify client processes of the new input creation @@ -1958,12 +1981,12 @@ status_t AudioFlinger::openInput(audio_module_handle_t module, sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t module, audio_io_handle_t *input, audio_config_t *config, - audio_devices_t device, + audio_devices_t devices, const String8& address, audio_source_t source, audio_input_flags_t flags) { - AudioHwDevice *inHwDev = findSuitableHwDev_l(module, device); + AudioHwDevice *inHwDev = findSuitableHwDev_l(module, devices); if (inHwDev == NULL) { *input = AUDIO_IO_HANDLE_NONE; return 0; @@ -1976,7 +1999,7 @@ sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t m audio_config_t halconfig = *config; audio_hw_device_t *inHwHal = inHwDev->hwDevice(); audio_stream_in_t *inStream = NULL; - status_t status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig, + status_t status = inHwHal->open_input_stream(inHwHal, *input, devices, &halconfig, &inStream, flags, address.string(), source); ALOGV("openInput_l() openInputStream returned input %p, SamplingRate %d" ", Format %#x, Channels %x, flags %#x, status %d addr %s", @@ -1998,7 +2021,7 @@ sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t m // FIXME describe the change proposed by HAL (save old values so we can log them here) ALOGV("openInput_l() reopening with proposed sampling rate and channel mask"); inStream = NULL; - status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig, + status = inHwHal->open_input_stream(inHwHal, *input, devices, &halconfig, &inStream, flags, address.string(), source); // FIXME log this new status; HAL should not propose any further changes } @@ -2063,7 +2086,7 @@ sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t m inputStream, *input, primaryOutputDevice_l(), - device + devices #ifdef TEE_SINK , teeSink #endif @@ -2786,13 +2809,13 @@ bool AudioFlinger::updateOrphanEffectChains(const sp<AudioFlinger::EffectModule> struct Entry { -#define MAX_NAME 32 // %Y%m%d%H%M%S_%d.wav - char mName[MAX_NAME]; +#define TEE_MAX_FILENAME 32 // %Y%m%d%H%M%S_%d.wav = 4+2+2+2+2+2+1+1+4+1 = 21 + char mFileName[TEE_MAX_FILENAME]; }; int comparEntry(const void *p1, const void *p2) { - return strcmp(((const Entry *) p1)->mName, ((const Entry *) p2)->mName); + return strcmp(((const Entry *) p1)->mFileName, ((const Entry *) p2)->mFileName); } #ifdef TEE_SINK @@ -2811,11 +2834,11 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand DIR *dir = opendir(teePath); teePath[teePathLen++] = '/'; if (dir != NULL) { -#define MAX_SORT 20 // number of entries to sort -#define MAX_KEEP 10 // number of entries to keep - struct Entry entries[MAX_SORT]; +#define TEE_MAX_SORT 20 // number of entries to sort +#define TEE_MAX_KEEP 10 // number of entries to keep + struct Entry entries[TEE_MAX_SORT]; size_t entryCount = 0; - while (entryCount < MAX_SORT) { + while (entryCount < TEE_MAX_SORT) { struct dirent de; struct dirent *result = NULL; int rc = readdir_r(dir, &de, &result); @@ -2832,17 +2855,17 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand } // ignore non .wav file entries size_t nameLen = strlen(de.d_name); - if (nameLen <= 4 || nameLen >= MAX_NAME || + if (nameLen <= 4 || nameLen >= TEE_MAX_FILENAME || strcmp(&de.d_name[nameLen - 4], ".wav")) { continue; } - strcpy(entries[entryCount++].mName, de.d_name); + strcpy(entries[entryCount++].mFileName, de.d_name); } (void) closedir(dir); - if (entryCount > MAX_KEEP) { + if (entryCount > TEE_MAX_KEEP) { qsort(entries, entryCount, sizeof(Entry), comparEntry); - for (size_t i = 0; i < entryCount - MAX_KEEP; ++i) { - strcpy(&teePath[teePathLen], entries[i].mName); + for (size_t i = 0; i < entryCount - TEE_MAX_KEEP; ++i) { + strcpy(&teePath[teePathLen], entries[i].mFileName); (void) unlink(teePath); } } @@ -2926,4 +2949,4 @@ status_t AudioFlinger::onTransact( return BnAudioFlinger::onTransact(code, data, reply, flags); } -}; // namespace android +} // namespace android diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 4fb372d..7b76185 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -745,6 +745,8 @@ private: void closeInputInternal_l(sp<RecordThread> thread); void setAudioHwSyncForSession_l(PlaybackThread *thread, audio_session_t sessionId); + status_t checkStreamType(audio_stream_type_t stream) const; + #ifdef TEE_SINK // all record threads serially share a common tee sink, which is re-created on format change sp<NBAIO_Sink> mRecordTeeSink; @@ -794,9 +796,13 @@ private: #undef INCLUDING_FROM_AUDIOFLINGER_H const char *formatToString(audio_format_t format); +String8 inputFlagsToString(audio_input_flags_t flags); +String8 outputFlagsToString(audio_output_flags_t flags); +String8 devicesToString(audio_devices_t devices); +const char *sourceToString(audio_source_t source); // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_AUDIO_FLINGER_H diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 247f728..dddca02 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -341,11 +341,46 @@ AudioMixer::RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputC ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu", this, format, inputChannelMask, outputChannelMask, mInputChannels, mOutputChannels); - // TODO: consider channel representation in index array formulation - // We ignore channel representation, and just use the bits. - memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry), - audio_channel_mask_get_bits(outputChannelMask), - audio_channel_mask_get_bits(inputChannelMask)); + + const audio_channel_representation_t inputRepresentation = + audio_channel_mask_get_representation(inputChannelMask); + const audio_channel_representation_t outputRepresentation = + audio_channel_mask_get_representation(outputChannelMask); + const uint32_t inputBits = audio_channel_mask_get_bits(inputChannelMask); + const uint32_t outputBits = audio_channel_mask_get_bits(outputChannelMask); + + switch (inputRepresentation) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + switch (outputRepresentation) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry), + outputBits, inputBits); + return; + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + // TODO: output channel index mask not currently allowed + // fall through + default: + break; + } + break; + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + switch (outputRepresentation) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + memcpy_by_index_array_initialization_src_index(mIdxAry, ARRAY_SIZE(mIdxAry), + outputBits, inputBits); + return; + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + // TODO: output channel index mask not currently allowed + // fall through + default: + break; + } + break; + default: + break; + } + LOG_ALWAYS_FATAL("invalid channel mask conversion from %#x to %#x", + inputChannelMask, outputChannelMask); } void AudioMixer::RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames) @@ -605,7 +640,10 @@ status_t AudioMixer::track_t::prepareForDownmix() && mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) { return NO_ERROR; } - if (DownmixerBufferProvider::isMultichannelCapable()) { + // DownmixerBufferProvider is only used for position masks. + if (audio_channel_mask_get_representation(channelMask) + == AUDIO_CHANNEL_REPRESENTATION_POSITION + && DownmixerBufferProvider::isMultichannelCapable()) { DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(channelMask, mMixerChannelMask, AUDIO_FORMAT_PCM_16_BIT /* TODO: use mMixerInFormat, now only PCM 16 */, @@ -2262,4 +2300,4 @@ AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, uint32_t } // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index 88e94c5..381036b 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -21,15 +21,15 @@ #include <stdint.h> #include <sys/types.h> +#include <hardware/audio_effect.h> +#include <media/AudioBufferProvider.h> +#include <media/nbaio/NBLog.h> +#include <system/audio.h> +#include <utils/Compat.h> #include <utils/threads.h> -#include <media/AudioBufferProvider.h> #include "AudioResampler.h" -#include <hardware/audio_effect.h> -#include <system/audio.h> -#include <media/nbaio/NBLog.h> - // FIXME This is actually unity gain, which might not be max in future, expressed in U.12 #define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT @@ -58,7 +58,7 @@ public: static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX; static const uint16_t UNITY_GAIN_INT = 0x1000; - static const float UNITY_GAIN_FLOAT = 1.0f; + static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f; enum { // names @@ -127,10 +127,16 @@ public: size_t getUnreleasedFrames(int name) const; static inline bool isValidPcmTrackFormat(audio_format_t format) { - return format == AUDIO_FORMAT_PCM_16_BIT || - format == AUDIO_FORMAT_PCM_24_BIT_PACKED || - format == AUDIO_FORMAT_PCM_32_BIT || - format == AUDIO_FORMAT_PCM_FLOAT; + switch (format) { + case AUDIO_FORMAT_PCM_8_BIT: + case AUDIO_FORMAT_PCM_16_BIT: + case AUDIO_FORMAT_PCM_24_BIT_PACKED: + case AUDIO_FORMAT_PCM_32_BIT: + case AUDIO_FORMAT_PCM_FLOAT: + return true; + default: + return false; + } } private: @@ -479,6 +485,6 @@ private: }; // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_AUDIO_MIXER_H diff --git a/services/audioflinger/AudioMixerOps.h b/services/audioflinger/AudioMixerOps.h index f7376a8..2678857 100644 --- a/services/audioflinger/AudioMixerOps.h +++ b/services/audioflinger/AudioMixerOps.h @@ -52,15 +52,12 @@ struct is_same<T, T> // partial specialization * * For high precision audio, only the <TO, TI, TV> = <float, float, float> * needs to be accelerated. This is perhaps the easiest form to do quickly as well. + * + * A generic version is NOT defined to catch any mistake of using it. */ template <typename TO, typename TI, typename TV> -inline TO MixMul(TI value, TV volume) { - COMPILE_TIME_ASSERT_FUNCTION_SCOPE(false); - // should not be here :-). - // To avoid mistakes, this template is always specialized. - return value * volume; -} +TO MixMul(TI value, TV volume); template <> inline int32_t MixMul<int32_t, int16_t, int16_t>(int16_t value, int16_t volume) { diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp index 1f7a613..46e3d6c 100644 --- a/services/audioflinger/AudioResampler.cpp +++ b/services/audioflinger/AudioResampler.cpp @@ -29,14 +29,11 @@ #include "AudioResamplerDyn.h" #ifdef __arm__ -#include <machine/cpu-features.h> + #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 #endif namespace android { -#ifdef __ARM_HAVE_HALFWORD_MULTIPLY // optimized asm option - #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 -#endif // __ARM_HAVE_HALFWORD_MULTIPLY // ---------------------------------------------------------------------------- class AudioResamplerOrder1 : public AudioResampler { diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h index cdc6d92..863614a 100644 --- a/services/audioflinger/AudioResampler.h +++ b/services/audioflinger/AudioResampler.h @@ -19,7 +19,9 @@ #include <stdint.h> #include <sys/types.h> + #include <cutils/compiler.h> +#include <utils/Compat.h> #include <media/AudioBufferProvider.h> #include <system/audio.h> @@ -47,7 +49,7 @@ public: DYN_HIGH_QUALITY=7, }; - static const float UNITY_GAIN_FLOAT = 1.0f; + static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f; static AudioResampler* create(audio_format_t format, int inChannelCount, int32_t sampleRate, src_quality quality=DEFAULT_QUALITY); @@ -168,7 +170,6 @@ private: }; // ---------------------------------------------------------------------------- -} -; // namespace android +} // namespace android #endif // ANDROID_AUDIO_RESAMPLER_H diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp index 8f14ff9..d3cbd1c 100644 --- a/services/audioflinger/AudioResamplerCubic.cpp +++ b/services/audioflinger/AudioResamplerCubic.cpp @@ -185,5 +185,4 @@ save_state: } // ---------------------------------------------------------------------------- -} -; // namespace android +} // namespace android diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h index b315da5..1ddc5f9 100644 --- a/services/audioflinger/AudioResamplerCubic.h +++ b/services/audioflinger/AudioResamplerCubic.h @@ -63,6 +63,6 @@ private: }; // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_CUBIC_H*/ diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp index 0eeb201..c21d4ca 100644 --- a/services/audioflinger/AudioResamplerDyn.cpp +++ b/services/audioflinger/AudioResamplerDyn.cpp @@ -618,4 +618,4 @@ template class AudioResamplerDyn<int16_t, int16_t, int32_t>; template class AudioResamplerDyn<int32_t, int16_t, int32_t>; // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/services/audioflinger/AudioResamplerDyn.h b/services/audioflinger/AudioResamplerDyn.h index e886a68..238b163 100644 --- a/services/audioflinger/AudioResamplerDyn.h +++ b/services/audioflinger/AudioResamplerDyn.h @@ -127,6 +127,6 @@ private: void* mCoefBuffer; // if a filter is created, this is not null }; -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_DYN_H*/ diff --git a/services/audioflinger/AudioResamplerFirGen.h b/services/audioflinger/AudioResamplerFirGen.h index d024b2f..ad18965 100644 --- a/services/audioflinger/AudioResamplerFirGen.h +++ b/services/audioflinger/AudioResamplerFirGen.h @@ -17,6 +17,8 @@ #ifndef ANDROID_AUDIO_RESAMPLER_FIR_GEN_H #define ANDROID_AUDIO_RESAMPLER_FIR_GEN_H +#include "utils/Compat.h" + namespace android { /* @@ -187,22 +189,23 @@ static inline int64_t toint(double x, int64_t maxval) { template <int N> struct I0Term { - static const double value = I0Term<N-1>::value / (4. * N * N); + static const CONSTEXPR double value = I0Term<N-1>::value / (4. * N * N); }; template <> struct I0Term<0> { - static const double value = 1.; + static const CONSTEXPR double value = 1.; }; template <int N> struct I0ATerm { - static const double value = I0ATerm<N-1>::value * (2.*N-1.) * (2.*N-1.) / (8. * N); + static const CONSTEXPR 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; + static const CONSTEXPR double value = + 0.398942280401432677939946059934381868475858631164934657665925; }; #if USE_HORNERS_METHOD @@ -704,6 +707,6 @@ static inline void firKaiserGen(T* coef, int L, int halfNumCoef, } } -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_FIR_GEN_H*/ diff --git a/services/audioflinger/AudioResamplerFirOps.h b/services/audioflinger/AudioResamplerFirOps.h index bf2163f..658285d 100644 --- a/services/audioflinger/AudioResamplerFirOps.h +++ b/services/audioflinger/AudioResamplerFirOps.h @@ -25,7 +25,7 @@ namespace android { #define USE_INLINE_ASSEMBLY (false) #endif -#if USE_INLINE_ASSEMBLY && defined(__ARM_NEON__) +#if defined(__aarch64__) || defined(__ARM_NEON__) #define USE_NEON (true) #include <arm_neon.h> #else @@ -158,6 +158,6 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) #endif } -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_FIR_OPS_H*/ diff --git a/services/audioflinger/AudioResamplerFirProcess.h b/services/audioflinger/AudioResamplerFirProcess.h index efc8055..176202e 100644 --- a/services/audioflinger/AudioResamplerFirProcess.h +++ b/services/audioflinger/AudioResamplerFirProcess.h @@ -174,7 +174,8 @@ struct InterpNull { * Process() calls ProcessBase() with TFUNC = InterpCompute, for interpolated phase. */ -template <int CHANNELS, int STRIDE, typename TFUNC, typename TC, typename TI, typename TO, typename TINTERP> +template <int CHANNELS, int STRIDE, typename TFUNC, typename TC, typename TI, typename TO, + typename TINTERP> static inline void ProcessBase(TO* const out, size_t count, @@ -242,6 +243,9 @@ void ProcessBase(TO* const out, } } +/* Calculates a single output frame from a polyphase resampling filter. + * See Process() for parameter details. + */ template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO> static inline void ProcessL(TO* const out, @@ -255,6 +259,39 @@ void ProcessL(TO* const out, ProcessBase<CHANNELS, STRIDE, InterpNull>(out, count, coefsP, coefsN, sP, sN, 0, volumeLR); } +/* + * Calculates a single output frame from a polyphase resampling filter, + * with filter phase interpolation. + * + * @param out should point to the output buffer with space for at least one output frame. + * + * @param count should be half the size of the total filter length (halfNumCoefs), as we + * use symmetry in filter coefficients to evaluate two dot products. + * + * @param coefsP is one phase of the polyphase filter bank of size halfNumCoefs, corresponding + * to the positive sP. + * + * @param coefsN is one phase of the polyphase filter bank of size halfNumCoefs, corresponding + * to the negative sN. + * + * @param coefsP1 is the next phase of coefsP (used for interpolation). + * + * @param coefsN1 is the next phase of coefsN (used for interpolation). + * + * @param sP is the positive half of the coefficients (as viewed by a convolution), + * starting at the original samples pointer and decrementing (by CHANNELS). + * + * @param sN is the negative half of the samples (as viewed by a convolution), + * starting at the original samples pointer + CHANNELS and incrementing (by CHANNELS). + * + * @param lerpP The fractional siting between the polyphase indices is given by the bits + * below coefShift. See fir() for details. + * + * @param volumeLR is a pointer to an array of two 32 bit volume values, one per stereo channel, + * expressed as a S32 integer or float. 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. + */ template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO, typename TINTERP> static inline void Process(TO* const out, @@ -268,11 +305,12 @@ void Process(TO* const out, TINTERP lerpP, const TO* const volumeLR) { - ProcessBase<CHANNELS, STRIDE, InterpCompute>(out, count, coefsP, coefsN, sP, sN, lerpP, volumeLR); + ProcessBase<CHANNELS, STRIDE, InterpCompute>(out, count, coefsP, coefsN, sP, sN, lerpP, + volumeLR); } /* - * Calculates a single output frame (two samples) from input sample pointer. + * Calculates a single output frame from input sample pointer. * * This sets up the params for the accelerated Process() and ProcessL() * functions to do the appropriate dot products. @@ -307,7 +345,7 @@ void Process(TO* const out, * 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. + * expressed as a S32 integer or float. 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. * @@ -396,6 +434,6 @@ void fir(TO* const out, } } -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_H*/ diff --git a/services/audioflinger/AudioResamplerFirProcessNeon.h b/services/audioflinger/AudioResamplerFirProcessNeon.h index f311cef..3de9edd 100644 --- a/services/audioflinger/AudioResamplerFirProcessNeon.h +++ b/services/audioflinger/AudioResamplerFirProcessNeon.h @@ -22,14 +22,35 @@ namespace android { // depends on AudioResamplerFirOps.h, AudioResamplerFirProcess.h #if USE_NEON + +// use intrinsics if inline arm32 assembly is not possible +#if !USE_INLINE_ASSEMBLY +#define USE_INTRINSIC +#endif + +// following intrinsics available only on ARM 64 bit ACLE +#ifndef __aarch64__ +#undef vld1q_f32_x2 +#undef vld1q_s32_x2 +#endif + +#define TO_STRING2(x) #x +#define TO_STRING(x) TO_STRING2(x) +// uncomment to print GCC version, may be relevant for intrinsic optimizations +/* #pragma message ("GCC version: " TO_STRING(__GNUC__) \ + "." TO_STRING(__GNUC_MINOR__) \ + "." TO_STRING(__GNUC_PATCHLEVEL__)) */ + // -// NEON specializations are enabled for Process() and ProcessL() +// NEON specializations are enabled for Process() and ProcessL() in AudioResamplerFirProcess.h +// +// Two variants are presented here: +// ARM NEON inline assembly which appears up to 10-15% faster than intrinsics (gcc 4.9) for arm32. +// ARM NEON intrinsics which can also be used by arm64 and x86/64 with NEON header. // -// 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. +// These are only used for inline assembly. #define ASSEMBLY_ACCUMULATE_MONO \ "vld1.s32 {d2}, [%[vLR]:64] \n"/* (1) load volumes */\ "vld1.s32 {d3}, %[out] \n"/* (2) unaligned load the output */\ @@ -49,6 +70,458 @@ namespace android { "vqadd.s32 d3, d3, d0 \n"/* (1+4d) accumulate result (saturating)*/\ "vst1.s32 {d3}, %[out] \n"/* (2+2d)store result*/ +template <int CHANNELS, int STRIDE, bool FIXED> +static inline void ProcessNeonIntrinsic(int32_t* out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* volumeLR, + uint32_t lerpP, + const int16_t* coefsP1, + const int16_t* coefsN1) +{ + ALOG_ASSERT(count > 0 && (count & 7) == 0); // multiple of 8 + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS == 1 || CHANNELS == 2); + + sP -= CHANNELS*((STRIDE>>1)-1); + coefsP = (const int16_t*)__builtin_assume_aligned(coefsP, 16); + coefsN = (const int16_t*)__builtin_assume_aligned(coefsN, 16); + + int16x4_t interp; + if (!FIXED) { + interp = vdup_n_s16(lerpP); + //interp = (int16x4_t)vset_lane_s32 ((int32x2_t)lerpP, interp, 0); + coefsP1 = (const int16_t*)__builtin_assume_aligned(coefsP1, 16); + coefsN1 = (const int16_t*)__builtin_assume_aligned(coefsN1, 16); + } + int32x4_t accum, accum2; + // warning uninitialized if we use veorq_s32 + // (alternative to below) accum = veorq_s32(accum, accum); + accum = vdupq_n_s32(0); + if (CHANNELS == 2) { + // (alternative to below) accum2 = veorq_s32(accum2, accum2); + accum2 = vdupq_n_s32(0); + } + do { + int16x8_t posCoef = vld1q_s16(coefsP); + coefsP += 8; + int16x8_t negCoef = vld1q_s16(coefsN); + coefsN += 8; + if (!FIXED) { // interpolate + int16x8_t posCoef1 = vld1q_s16(coefsP1); + coefsP1 += 8; + int16x8_t negCoef1 = vld1q_s16(coefsN1); + coefsN1 += 8; + + posCoef1 = vsubq_s16(posCoef1, posCoef); + negCoef = vsubq_s16(negCoef, negCoef1); + + posCoef1 = vqrdmulhq_lane_s16(posCoef1, interp, 0); + negCoef = vqrdmulhq_lane_s16(negCoef, interp, 0); + + posCoef = vaddq_s16(posCoef, posCoef1); + negCoef = vaddq_s16(negCoef, negCoef1); + } + switch (CHANNELS) { + case 1: { + int16x8_t posSamp = vld1q_s16(sP); + int16x8_t negSamp = vld1q_s16(sN); + sN += 8; + posSamp = vrev64q_s16(posSamp); + + // dot product + accum = vmlal_s16(accum, vget_low_s16(posSamp), vget_high_s16(posCoef)); // reversed + accum = vmlal_s16(accum, vget_high_s16(posSamp), vget_low_s16(posCoef)); // reversed + accum = vmlal_s16(accum, vget_low_s16(negSamp), vget_low_s16(negCoef)); + accum = vmlal_s16(accum, vget_high_s16(negSamp), vget_high_s16(negCoef)); + sP -= 8; + } break; + case 2: { + int16x8x2_t posSamp = vld2q_s16(sP); + int16x8x2_t negSamp = vld2q_s16(sN); + sN += 16; + posSamp.val[0] = vrev64q_s16(posSamp.val[0]); + posSamp.val[1] = vrev64q_s16(posSamp.val[1]); + + // dot product + accum = vmlal_s16(accum, vget_low_s16(posSamp.val[0]), vget_high_s16(posCoef)); // r + accum = vmlal_s16(accum, vget_high_s16(posSamp.val[0]), vget_low_s16(posCoef)); // r + accum2 = vmlal_s16(accum2, vget_low_s16(posSamp.val[1]), vget_high_s16(posCoef)); // r + accum2 = vmlal_s16(accum2, vget_high_s16(posSamp.val[1]), vget_low_s16(posCoef)); // r + accum = vmlal_s16(accum, vget_low_s16(negSamp.val[0]), vget_low_s16(negCoef)); + accum = vmlal_s16(accum, vget_high_s16(negSamp.val[0]), vget_high_s16(negCoef)); + accum2 = vmlal_s16(accum2, vget_low_s16(negSamp.val[1]), vget_low_s16(negCoef)); + accum2 = vmlal_s16(accum2, vget_high_s16(negSamp.val[1]), vget_high_s16(negCoef)); + sP -= 16; + } + } break; + } while (count -= 8); + + // multiply by volume and save + volumeLR = (const int32_t*)__builtin_assume_aligned(volumeLR, 8); + int32x2_t vLR = vld1_s32(volumeLR); + int32x2_t outSamp = vld1_s32(out); + // combine and funnel down accumulator + int32x2_t outAccum = vpadd_s32(vget_low_s32(accum), vget_high_s32(accum)); + if (CHANNELS == 1) { + // duplicate accum to both L and R + outAccum = vpadd_s32(outAccum, outAccum); + } else if (CHANNELS == 2) { + // accum2 contains R, fold in + int32x2_t outAccum2 = vpadd_s32(vget_low_s32(accum2), vget_high_s32(accum2)); + outAccum = vpadd_s32(outAccum, outAccum2); + } + outAccum = vqrdmulh_s32(outAccum, vLR); + outSamp = vqadd_s32(outSamp, outAccum); + vst1_s32(out, outSamp); +} + +template <int CHANNELS, int STRIDE, bool FIXED> +static inline void ProcessNeonIntrinsic(int32_t* out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* volumeLR, + uint32_t lerpP, + const int32_t* coefsP1, + const int32_t* coefsN1) +{ + ALOG_ASSERT(count > 0 && (count & 7) == 0); // multiple of 8 + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS == 1 || CHANNELS == 2); + + sP -= CHANNELS*((STRIDE>>1)-1); + coefsP = (const int32_t*)__builtin_assume_aligned(coefsP, 16); + coefsN = (const int32_t*)__builtin_assume_aligned(coefsN, 16); + + int32x2_t interp; + if (!FIXED) { + interp = vdup_n_s32(lerpP); + coefsP1 = (const int32_t*)__builtin_assume_aligned(coefsP1, 16); + coefsN1 = (const int32_t*)__builtin_assume_aligned(coefsN1, 16); + } + int32x4_t accum, accum2; + // warning uninitialized if we use veorq_s32 + // (alternative to below) accum = veorq_s32(accum, accum); + accum = vdupq_n_s32(0); + if (CHANNELS == 2) { + // (alternative to below) accum2 = veorq_s32(accum2, accum2); + accum2 = vdupq_n_s32(0); + } + do { +#ifdef vld1q_s32_x2 + int32x4x2_t posCoef = vld1q_s32_x2(coefsP); + coefsP += 8; + int32x4x2_t negCoef = vld1q_s32_x2(coefsN); + coefsN += 8; +#else + int32x4x2_t posCoef; + posCoef.val[0] = vld1q_s32(coefsP); + coefsP += 4; + posCoef.val[1] = vld1q_s32(coefsP); + coefsP += 4; + int32x4x2_t negCoef; + negCoef.val[0] = vld1q_s32(coefsN); + coefsN += 4; + negCoef.val[1] = vld1q_s32(coefsN); + coefsN += 4; +#endif + if (!FIXED) { // interpolate +#ifdef vld1q_s32_x2 + int32x4x2_t posCoef1 = vld1q_s32_x2(coefsP1); + coefsP1 += 8; + int32x4x2_t negCoef1 = vld1q_s32_x2(coefsN1); + coefsN1 += 8; +#else + int32x4x2_t posCoef1; + posCoef1.val[0] = vld1q_s32(coefsP1); + coefsP1 += 4; + posCoef1.val[1] = vld1q_s32(coefsP1); + coefsP1 += 4; + int32x4x2_t negCoef1; + negCoef1.val[0] = vld1q_s32(coefsN1); + coefsN1 += 4; + negCoef1.val[1] = vld1q_s32(coefsN1); + coefsN1 += 4; +#endif + + posCoef1.val[0] = vsubq_s32(posCoef1.val[0], posCoef.val[0]); + posCoef1.val[1] = vsubq_s32(posCoef1.val[1], posCoef.val[1]); + negCoef.val[0] = vsubq_s32(negCoef.val[0], negCoef1.val[0]); + negCoef.val[1] = vsubq_s32(negCoef.val[1], negCoef1.val[1]); + + posCoef1.val[0] = vqrdmulhq_lane_s32(posCoef1.val[0], interp, 0); + posCoef1.val[1] = vqrdmulhq_lane_s32(posCoef1.val[1], interp, 0); + negCoef.val[0] = vqrdmulhq_lane_s32(negCoef.val[0], interp, 0); + negCoef.val[1] = vqrdmulhq_lane_s32(negCoef.val[1], interp, 0); + + posCoef.val[0] = vaddq_s32(posCoef.val[0], posCoef1.val[0]); + posCoef.val[1] = vaddq_s32(posCoef.val[1], posCoef1.val[1]); + negCoef.val[0] = vaddq_s32(negCoef.val[0], negCoef1.val[0]); + negCoef.val[1] = vaddq_s32(negCoef.val[1], negCoef1.val[1]); + } + switch (CHANNELS) { + case 1: { + int16x8_t posSamp = vld1q_s16(sP); + int16x8_t negSamp = vld1q_s16(sN); + sN += 8; + posSamp = vrev64q_s16(posSamp); + + int32x4_t posSamp0 = vshll_n_s16(vget_low_s16(posSamp), 15); + int32x4_t posSamp1 = vshll_n_s16(vget_high_s16(posSamp), 15); + int32x4_t negSamp0 = vshll_n_s16(vget_low_s16(negSamp), 15); + int32x4_t negSamp1 = vshll_n_s16(vget_high_s16(negSamp), 15); + + // dot product + posSamp0 = vqrdmulhq_s32(posSamp0, posCoef.val[1]); // reversed + posSamp1 = vqrdmulhq_s32(posSamp1, posCoef.val[0]); // reversed + negSamp0 = vqrdmulhq_s32(negSamp0, negCoef.val[0]); + negSamp1 = vqrdmulhq_s32(negSamp1, negCoef.val[1]); + + accum = vaddq_s32(accum, posSamp0); + negSamp0 = vaddq_s32(negSamp0, negSamp1); + accum = vaddq_s32(accum, posSamp1); + accum = vaddq_s32(accum, negSamp0); + + sP -= 8; + } break; + case 2: { + int16x8x2_t posSamp = vld2q_s16(sP); + int16x8x2_t negSamp = vld2q_s16(sN); + sN += 16; + posSamp.val[0] = vrev64q_s16(posSamp.val[0]); + posSamp.val[1] = vrev64q_s16(posSamp.val[1]); + + // left + int32x4_t posSamp0 = vshll_n_s16(vget_low_s16(posSamp.val[0]), 15); + int32x4_t posSamp1 = vshll_n_s16(vget_high_s16(posSamp.val[0]), 15); + int32x4_t negSamp0 = vshll_n_s16(vget_low_s16(negSamp.val[0]), 15); + int32x4_t negSamp1 = vshll_n_s16(vget_high_s16(negSamp.val[0]), 15); + + // dot product + posSamp0 = vqrdmulhq_s32(posSamp0, posCoef.val[1]); // reversed + posSamp1 = vqrdmulhq_s32(posSamp1, posCoef.val[0]); // reversed + negSamp0 = vqrdmulhq_s32(negSamp0, negCoef.val[0]); + negSamp1 = vqrdmulhq_s32(negSamp1, negCoef.val[1]); + + accum = vaddq_s32(accum, posSamp0); + negSamp0 = vaddq_s32(negSamp0, negSamp1); + accum = vaddq_s32(accum, posSamp1); + accum = vaddq_s32(accum, negSamp0); + + // right + posSamp0 = vshll_n_s16(vget_low_s16(posSamp.val[1]), 15); + posSamp1 = vshll_n_s16(vget_high_s16(posSamp.val[1]), 15); + negSamp0 = vshll_n_s16(vget_low_s16(negSamp.val[1]), 15); + negSamp1 = vshll_n_s16(vget_high_s16(negSamp.val[1]), 15); + + // dot product + posSamp0 = vqrdmulhq_s32(posSamp0, posCoef.val[1]); // reversed + posSamp1 = vqrdmulhq_s32(posSamp1, posCoef.val[0]); // reversed + negSamp0 = vqrdmulhq_s32(negSamp0, negCoef.val[0]); + negSamp1 = vqrdmulhq_s32(negSamp1, negCoef.val[1]); + + accum2 = vaddq_s32(accum2, posSamp0); + negSamp0 = vaddq_s32(negSamp0, negSamp1); + accum2 = vaddq_s32(accum2, posSamp1); + accum2 = vaddq_s32(accum2, negSamp0); + + sP -= 16; + } break; + } + } while (count -= 8); + + // multiply by volume and save + volumeLR = (const int32_t*)__builtin_assume_aligned(volumeLR, 8); + int32x2_t vLR = vld1_s32(volumeLR); + int32x2_t outSamp = vld1_s32(out); + // combine and funnel down accumulator + int32x2_t outAccum = vpadd_s32(vget_low_s32(accum), vget_high_s32(accum)); + if (CHANNELS == 1) { + // duplicate accum to both L and R + outAccum = vpadd_s32(outAccum, outAccum); + } else if (CHANNELS == 2) { + // accum2 contains R, fold in + int32x2_t outAccum2 = vpadd_s32(vget_low_s32(accum2), vget_high_s32(accum2)); + outAccum = vpadd_s32(outAccum, outAccum2); + } + outAccum = vqrdmulh_s32(outAccum, vLR); + outSamp = vqadd_s32(outSamp, outAccum); + vst1_s32(out, outSamp); +} + +template <int CHANNELS, int STRIDE, bool FIXED> +static inline void ProcessNeonIntrinsic(float* out, + int count, + const float* coefsP, + const float* coefsN, + const float* sP, + const float* sN, + const float* volumeLR, + float lerpP, + const float* coefsP1, + const float* coefsN1) +{ + ALOG_ASSERT(count > 0 && (count & 7) == 0); // multiple of 8 + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS == 1 || CHANNELS == 2); + + sP -= CHANNELS*((STRIDE>>1)-1); + coefsP = (const float*)__builtin_assume_aligned(coefsP, 16); + coefsN = (const float*)__builtin_assume_aligned(coefsN, 16); + + float32x2_t interp; + if (!FIXED) { + interp = vdup_n_f32(lerpP); + coefsP1 = (const float*)__builtin_assume_aligned(coefsP1, 16); + coefsN1 = (const float*)__builtin_assume_aligned(coefsN1, 16); + } + float32x4_t accum, accum2; + // warning uninitialized if we use veorq_s32 + // (alternative to below) accum = veorq_s32(accum, accum); + accum = vdupq_n_f32(0); + if (CHANNELS == 2) { + // (alternative to below) accum2 = veorq_s32(accum2, accum2); + accum2 = vdupq_n_f32(0); + } + do { +#ifdef vld1q_f32_x2 + float32x4x2_t posCoef = vld1q_f32_x2(coefsP); + coefsP += 8; + float32x4x2_t negCoef = vld1q_f32_x2(coefsN); + coefsN += 8; +#else + float32x4x2_t posCoef; + posCoef.val[0] = vld1q_f32(coefsP); + coefsP += 4; + posCoef.val[1] = vld1q_f32(coefsP); + coefsP += 4; + float32x4x2_t negCoef; + negCoef.val[0] = vld1q_f32(coefsN); + coefsN += 4; + negCoef.val[1] = vld1q_f32(coefsN); + coefsN += 4; +#endif + if (!FIXED) { // interpolate +#ifdef vld1q_f32_x2 + float32x4x2_t posCoef1 = vld1q_f32_x2(coefsP1); + coefsP1 += 8; + float32x4x2_t negCoef1 = vld1q_f32_x2(coefsN1); + coefsN1 += 8; +#else + float32x4x2_t posCoef1; + posCoef1.val[0] = vld1q_f32(coefsP1); + coefsP1 += 4; + posCoef1.val[1] = vld1q_f32(coefsP1); + coefsP1 += 4; + float32x4x2_t negCoef1; + negCoef1.val[0] = vld1q_f32(coefsN1); + coefsN1 += 4; + negCoef1.val[1] = vld1q_f32(coefsN1); + coefsN1 += 4; +#endif + posCoef1.val[0] = vsubq_f32(posCoef1.val[0], posCoef.val[0]); + posCoef1.val[1] = vsubq_f32(posCoef1.val[1], posCoef.val[1]); + negCoef.val[0] = vsubq_f32(negCoef.val[0], negCoef1.val[0]); + negCoef.val[1] = vsubq_f32(negCoef.val[1], negCoef1.val[1]); + + posCoef.val[0] = vmlaq_lane_f32(posCoef.val[0], posCoef1.val[0], interp, 0); + posCoef.val[1] = vmlaq_lane_f32(posCoef.val[1], posCoef1.val[1], interp, 0); + negCoef.val[0] = vmlaq_lane_f32(negCoef1.val[0], negCoef.val[0], interp, 0); // rev + negCoef.val[1] = vmlaq_lane_f32(negCoef1.val[1], negCoef.val[1], interp, 0); // rev + } + switch (CHANNELS) { + case 1: { +#ifdef vld1q_f32_x2 + float32x4x2_t posSamp = vld1q_f32_x2(sP); + float32x4x2_t negSamp = vld1q_f32_x2(sN); + sN += 8; + sP -= 8; +#else + float32x4x2_t posSamp; + posSamp.val[0] = vld1q_f32(sP); + sP += 4; + posSamp.val[1] = vld1q_f32(sP); + sP -= 12; + float32x4x2_t negSamp; + negSamp.val[0] = vld1q_f32(sN); + sN += 4; + negSamp.val[1] = vld1q_f32(sN); + sN += 4; +#endif + // effectively we want a vrev128q_f32() + posSamp.val[0] = vrev64q_f32(posSamp.val[0]); + posSamp.val[1] = vrev64q_f32(posSamp.val[1]); + posSamp.val[0] = vcombine_f32( + vget_high_f32(posSamp.val[0]), vget_low_f32(posSamp.val[0])); + posSamp.val[1] = vcombine_f32( + vget_high_f32(posSamp.val[1]), vget_low_f32(posSamp.val[1])); + + accum = vmlaq_f32(accum, posSamp.val[0], posCoef.val[1]); + accum = vmlaq_f32(accum, posSamp.val[1], posCoef.val[0]); + accum = vmlaq_f32(accum, negSamp.val[0], negCoef.val[0]); + accum = vmlaq_f32(accum, negSamp.val[1], negCoef.val[1]); + } break; + case 2: { + float32x4x2_t posSamp0 = vld2q_f32(sP); + sP += 8; + float32x4x2_t negSamp0 = vld2q_f32(sN); + sN += 8; + posSamp0.val[0] = vrev64q_f32(posSamp0.val[0]); + posSamp0.val[1] = vrev64q_f32(posSamp0.val[1]); + posSamp0.val[0] = vcombine_f32( + vget_high_f32(posSamp0.val[0]), vget_low_f32(posSamp0.val[0])); + posSamp0.val[1] = vcombine_f32( + vget_high_f32(posSamp0.val[1]), vget_low_f32(posSamp0.val[1])); + + float32x4x2_t posSamp1 = vld2q_f32(sP); + sP -= 24; + float32x4x2_t negSamp1 = vld2q_f32(sN); + sN += 8; + posSamp1.val[0] = vrev64q_f32(posSamp1.val[0]); + posSamp1.val[1] = vrev64q_f32(posSamp1.val[1]); + posSamp1.val[0] = vcombine_f32( + vget_high_f32(posSamp1.val[0]), vget_low_f32(posSamp1.val[0])); + posSamp1.val[1] = vcombine_f32( + vget_high_f32(posSamp1.val[1]), vget_low_f32(posSamp1.val[1])); + + // Note: speed is affected by accumulation order. + // Also, speed appears slower using vmul/vadd instead of vmla for + // stereo case, comparable for mono. + + accum = vmlaq_f32(accum, negSamp0.val[0], negCoef.val[0]); + accum = vmlaq_f32(accum, negSamp1.val[0], negCoef.val[1]); + accum2 = vmlaq_f32(accum2, negSamp0.val[1], negCoef.val[0]); + accum2 = vmlaq_f32(accum2, negSamp1.val[1], negCoef.val[1]); + + accum = vmlaq_f32(accum, posSamp0.val[0], posCoef.val[1]); // reversed + accum = vmlaq_f32(accum, posSamp1.val[0], posCoef.val[0]); // reversed + accum2 = vmlaq_f32(accum2, posSamp0.val[1], posCoef.val[1]); // reversed + accum2 = vmlaq_f32(accum2, posSamp1.val[1], posCoef.val[0]); // reversed + } break; + } + } while (count -= 8); + + // multiply by volume and save + volumeLR = (const float*)__builtin_assume_aligned(volumeLR, 8); + float32x2_t vLR = vld1_f32(volumeLR); + float32x2_t outSamp = vld1_f32(out); + // combine and funnel down accumulator + float32x2_t outAccum = vpadd_f32(vget_low_f32(accum), vget_high_f32(accum)); + if (CHANNELS == 1) { + // duplicate accum to both L and R + outAccum = vpadd_f32(outAccum, outAccum); + } else if (CHANNELS == 2) { + // accum2 contains R, fold in + float32x2_t outAccum2 = vpadd_f32(vget_low_f32(accum2), vget_high_f32(accum2)); + outAccum = vpadd_f32(outAccum, outAccum2); + } + outSamp = vmla_f32(outSamp, outAccum, vLR); + vst1_f32(out, outSamp); +} + template <> inline void ProcessL<1, 16>(int32_t* const out, int count, @@ -58,6 +531,10 @@ inline void ProcessL<1, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -99,6 +576,7 @@ inline void ProcessL<1, 16>(int32_t* const out, "q0", "q1", "q2", "q3", "q8", "q10" ); +#endif } template <> @@ -110,6 +588,10 @@ inline void ProcessL<2, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -119,13 +601,13 @@ inline void ProcessL<2, 16>(int32_t* const out, "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 + "vld2.16 {q2, q3}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo frames + "vld2.16 {q5, q6}, [%[sN]]! \n"// (3) load 8 16-bits stereo frames "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 + "vrev64.16 q2, q2 \n"// (1) reverse 8 samples of positive left + "vrev64.16 q3, q3 \n"// (0 combines+) reverse positive right "vmlal.s16 q0, d4, d17 \n"// (1) multiply (reversed) samples left "vmlal.s16 q0, d5, d16 \n"// (1) multiply (reversed) samples left @@ -157,6 +639,7 @@ inline void ProcessL<2, 16>(int32_t* const out, "q4", "q5", "q6", "q8", "q10" ); +#endif } template <> @@ -171,6 +654,11 @@ inline void Process<1, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else + const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -227,6 +715,7 @@ inline void Process<1, 16>(int32_t* const out, "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11" ); +#endif } template <> @@ -241,6 +730,10 @@ inline void Process<2, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -251,8 +744,8 @@ inline void Process<2, 16>(int32_t* const out, "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 + "vld2.16 {q2, q3}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo frames + "vld2.16 {q5, q6}, [%[sN]]! \n"// (3) load 8 16-bits stereo frames "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 @@ -264,8 +757,8 @@ inline void Process<2, 16>(int32_t* const out, "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 + "vrev64.16 q2, q2 \n"// (1) reverse 8 samples of positive left + "vrev64.16 q3, q3 \n"// (1) reverse 8 samples of positive right "vadd.s16 q8, q8, q9 \n"// (1+1d) interpolate (step3) 1st set "vadd.s16 q10, q10, q11 \n"// (1+1d) interpolate (step3) 2nd set @@ -303,6 +796,7 @@ inline void Process<2, 16>(int32_t* const out, "q4", "q5", "q6", "q8", "q9", "q10", "q11" ); +#endif } template <> @@ -314,6 +808,10 @@ inline void ProcessL<1, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -327,7 +825,7 @@ inline void ProcessL<1, 16>(int32_t* const out, "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 + "vrev64.16 q2, q2 \n"// reverse 8 samples 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 @@ -335,10 +833,10 @@ inline void ProcessL<1, 16>(int32_t* const out, "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 + "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples + "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples "vadd.s32 q0, q0, q12 \n"// accumulate result "vadd.s32 q13, q13, q14 \n"// accumulate result @@ -364,6 +862,7 @@ inline void ProcessL<1, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } template <> @@ -375,6 +874,10 @@ inline void ProcessL<2, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -384,13 +887,13 @@ inline void ProcessL<2, 16>(int32_t* const out, "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 + "vld2.16 {q2, q3}, [%[sP]] \n"// load 8 16-bits stereo frames + "vld2.16 {q5, q6}, [%[sN]]! \n"// load 8 16-bits stereo frames + "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 - "vrev64.16 q3, q3 \n"// reverse 8 frames of the positive side + "vrev64.16 q2, q2 \n"// reverse 8 samples of positive left + "vrev64.16 q3, q3 \n"// reverse 8 samples of positive right "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits @@ -398,15 +901,15 @@ inline void ProcessL<2, 16>(int32_t* const out, "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 + "vqrdmulh.s32 q12, q12, q9 \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, q11 \n"// multiply samples by 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 + "vadd.s32 q0, q0, q15 \n"// accumulate result + "vadd.s32 q0, q0, q13 \n"// accumulate result "vshll.s16 q12, d6, #15 \n"// extend samples to 31 bits "vshll.s16 q13, d7, #15 \n"// extend samples to 31 bits @@ -414,15 +917,15 @@ inline void ProcessL<2, 16>(int32_t* const out, "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 + "vqrdmulh.s32 q12, q12, q9 \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, q11 \n"// multiply samples by 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 + "vadd.s32 q4, q4, q15 \n"// accumulate result + "vadd.s32 q4, q4, q13 \n"// accumulate result "subs %[count], %[count], #8 \n"// update loop counter "sub %[sP], %[sP], #32 \n"// move pointer to next set of samples @@ -444,6 +947,7 @@ inline void ProcessL<2, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } template <> @@ -458,6 +962,10 @@ inline void Process<1, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -489,7 +997,7 @@ inline void Process<1, 16>(int32_t* const out, "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 q2, q2 \n"// reverse 8 samples 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 @@ -529,6 +1037,7 @@ inline void Process<1, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } template <> @@ -543,6 +1052,10 @@ inline void Process<2, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -553,8 +1066,8 @@ inline void Process<2, 16>(int32_t* const out, "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 + "vld2.16 {q2, q3}, [%[sP]] \n"// load 8 16-bits stereo frames + "vld2.16 {q5, q6}, [%[sN]]! \n"// load 8 16-bits stereo frames "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 @@ -575,8 +1088,8 @@ inline void Process<2, 16>(int32_t* const out, "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 + "vrev64.16 q2, q2 \n"// reverse 8 samples of positive left + "vrev64.16 q3, q3 \n"// reverse 8 samples of positive right "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits @@ -591,8 +1104,8 @@ inline void Process<2, 16>(int32_t* const out, "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 + "vadd.s32 q0, q0, q15 \n"// accumulate result + "vadd.s32 q0, q0, q13 \n"// accumulate result "vshll.s16 q12, d6, #15 \n"// extend samples to 31 bits "vshll.s16 q13, d7, #15 \n"// extend samples to 31 bits @@ -607,8 +1120,8 @@ inline void Process<2, 16>(int32_t* const out, "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 + "vadd.s32 q4, q4, q15 \n"// accumulate result + "vadd.s32 q4, q4, q13 \n"// accumulate result "subs %[count], %[count], #8 \n"// update loop counter "sub %[sP], %[sP], #32 \n"// move pointer to next set of samples @@ -633,517 +1146,69 @@ inline void Process<2, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } -template <> -inline void ProcessL<1, 8>(int32_t* const out, +template<> +inline void ProcessL<1, 16>(float* 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 float* coefsP, + const float* coefsN, + const float* sP, + const float* sN, + const float* 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" - ); + ProcessNeonIntrinsic<1, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); } -template <> -inline void ProcessL<2, 8>(int32_t* const out, +template<> +inline void ProcessL<2, 16>(float* 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 float* coefsP, + const float* coefsN, + const float* sP, + const float* sN, + const float* 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" - ); + ProcessNeonIntrinsic<2, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); } -template <> -inline void Process<1, 8>(int32_t* const out, +template<> +inline void Process<1, 16>(float* 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 float* coefsP, + const float* coefsN, + const float* coefsP1, + const float* coefsN1, + const float* sP, + const float* sN, + float lerpP, + const float* 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" - ); + ProcessNeonIntrinsic<1, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); } -template <> -inline void Process<2, 8>(int32_t* const out, +template<> +inline void Process<2, 16>(float* 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 float* coefsP, + const float* coefsN, + const float* coefsP1, + const float* coefsN1, + const float* sP, + const float* sN, + float lerpP, + const float* 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" - ); + ProcessNeonIntrinsic<2, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); } #endif //USE_NEON -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_NEON_H*/ diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp index e6fb76c..ba9a356 100644 --- a/services/audioflinger/AudioResamplerSinc.cpp +++ b/services/audioflinger/AudioResamplerSinc.cpp @@ -61,135 +61,7 @@ namespace android { * cmd-line: fir -l 7 -s 48000 -c 20478 */ const uint32_t AudioResamplerSinc::mFirCoefsUp[] __attribute__ ((aligned (32))) = { - 0x6d374bc7, 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, - 0x6d35278a, 0x103e8192, 0xf36b9dfd, 0x07bdfaa5, 0xfc5102d0, 0x013d618d, 0xffc663b9, 0xfffd9592, - 0x6d2ebafe, 0x0f62811a, 0xf3b3d8ac, 0x07a9f399, 0xfc51d9a6, 0x0140bea5, 0xffc41212, 0xfffe631e, - 0x6d24069d, 0x0e8875ad, 0xf3fcb43e, 0x07953976, 0xfc53216f, 0x0143e67c, 0xffc1d373, 0xffff2b9f, - 0x6d150b35, 0x0db06a89, 0xf4462690, 0x077fd0ac, 0xfc54d8ae, 0x0146d965, 0xffbfa7d9, 0xffffef10, - 0x6d01c9e3, 0x0cda6ab5, 0xf4902587, 0x0769bdaf, 0xfc56fdda, 0x014997bb, 0xffbd8f40, 0x0000ad6e, - 0x6cea4418, 0x0c0680fe, 0xf4daa718, 0x07530501, 0xfc598f60, 0x014c21db, 0xffbb89a1, 0x000166b6, - 0x6cce7b97, 0x0b34b7f5, 0xf525a143, 0x073bab28, 0xfc5c8ba5, 0x014e782a, 0xffb996f3, 0x00021ae5, - 0x6cae7272, 0x0a6519f4, 0xf5710a17, 0x0723b4b4, 0xfc5ff105, 0x01509b14, 0xffb7b728, 0x0002c9fd, - 0x6c8a2b0f, 0x0997b116, 0xf5bcd7b1, 0x070b2639, 0xfc63bdd3, 0x01528b08, 0xffb5ea31, 0x000373fb, - 0x6c61a823, 0x08cc873c, 0xf609003f, 0x06f20453, 0xfc67f05a, 0x0154487b, 0xffb42ffc, 0x000418e2, - 0x6c34ecb5, 0x0803a60a, 0xf6557a00, 0x06d853a2, 0xfc6c86dd, 0x0155d3e8, 0xffb28876, 0x0004b8b3, - 0x6c03fc1c, 0x073d16e7, 0xf6a23b44, 0x06be18cd, 0xfc717f97, 0x01572dcf, 0xffb0f388, 0x00055371, - 0x6bced9ff, 0x0678e2fc, 0xf6ef3a6e, 0x06a3587e, 0xfc76d8bc, 0x015856b6, 0xffaf7118, 0x0005e921, - 0x6b958a54, 0x05b71332, 0xf73c6df4, 0x06881761, 0xfc7c9079, 0x01594f25, 0xffae010b, 0x000679c5, - 0x6b581163, 0x04f7b037, 0xf789cc61, 0x066c5a27, 0xfc82a4f4, 0x015a17ab, 0xffaca344, 0x00070564, - 0x6b1673c1, 0x043ac276, 0xf7d74c53, 0x06502583, 0xfc89144d, 0x015ab0db, 0xffab57a1, 0x00078c04, - 0x6ad0b652, 0x0380521c, 0xf824e480, 0x06337e2a, 0xfc8fdc9f, 0x015b1b4e, 0xffaa1e02, 0x00080dab, - 0x6a86de48, 0x02c86715, 0xf8728bb3, 0x061668d2, 0xfc96fbfc, 0x015b579e, 0xffa8f641, 0x00088a62, - 0x6a38f123, 0x0213090c, 0xf8c038d0, 0x05f8ea30, 0xfc9e7074, 0x015b666c, 0xffa7e039, 0x00090230, - 0x69e6f4b1, 0x01603f6e, 0xf90de2d1, 0x05db06fc, 0xfca63810, 0x015b485b, 0xffa6dbc0, 0x0009751e, - 0x6990ef0b, 0x00b01162, 0xf95b80cb, 0x05bcc3ed, 0xfcae50d6, 0x015afe14, 0xffa5e8ad, 0x0009e337, - 0x6936e697, 0x000285d0, 0xf9a909ea, 0x059e25b5, 0xfcb6b8c4, 0x015a8843, 0xffa506d2, 0x000a4c85, - 0x68d8e206, 0xff57a35e, 0xf9f67577, 0x057f310a, 0xfcbf6dd8, 0x0159e796, 0xffa43603, 0x000ab112, - 0x6876e855, 0xfeaf706f, 0xfa43bad2, 0x055fea9d, 0xfcc86e09, 0x01591cc0, 0xffa3760e, 0x000b10ec, - 0x681100c9, 0xfe09f323, 0xfa90d17b, 0x0540571a, 0xfcd1b74c, 0x01582878, 0xffa2c6c2, 0x000b6c1d, - 0x67a732f4, 0xfd673159, 0xfaddb10c, 0x05207b2f, 0xfcdb4793, 0x01570b77, 0xffa227ec, 0x000bc2b3, - 0x673986ac, 0xfcc730aa, 0xfb2a513b, 0x05005b82, 0xfce51ccb, 0x0155c678, 0xffa19957, 0x000c14bb, - 0x66c80413, 0xfc29f670, 0xfb76a9dd, 0x04dffcb6, 0xfcef34e1, 0x01545a3c, 0xffa11acb, 0x000c6244, - 0x6652b392, 0xfb8f87bd, 0xfbc2b2e4, 0x04bf6369, 0xfcf98dbe, 0x0152c783, 0xffa0ac11, 0x000cab5c, - 0x65d99dd5, 0xfaf7e963, 0xfc0e6461, 0x049e9433, 0xfd04254a, 0x01510f13, 0xffa04cf0, 0x000cf012, - 0x655ccbd3, 0xfa631fef, 0xfc59b685, 0x047d93a8, 0xfd0ef969, 0x014f31b2, 0xff9ffd2c, 0x000d3075, - 0x64dc46c3, 0xf9d12fab, 0xfca4a19f, 0x045c6654, 0xfd1a0801, 0x014d3029, 0xff9fbc89, 0x000d6c97, - 0x64581823, 0xf9421c9d, 0xfcef1e20, 0x043b10bd, 0xfd254ef4, 0x014b0b45, 0xff9f8ac9, 0x000da486, - 0x63d049b4, 0xf8b5ea87, 0xfd392498, 0x04199760, 0xfd30cc24, 0x0148c3d2, 0xff9f67ae, 0x000dd854, - 0x6344e578, 0xf82c9ce7, 0xfd82adba, 0x03f7feb4, 0xfd3c7d73, 0x01465a9f, 0xff9f52f7, 0x000e0812, - 0x62b5f5b2, 0xf7a636fa, 0xfdcbb25a, 0x03d64b27, 0xfd4860c2, 0x0143d07f, 0xff9f4c65, 0x000e33d3, - 0x622384e8, 0xf722bbb5, 0xfe142b6e, 0x03b4811d, 0xfd5473f3, 0x01412643, 0xff9f53b4, 0x000e5ba7, - 0x618d9ddc, 0xf6a22dcf, 0xfe5c120f, 0x0392a4f4, 0xfd60b4e7, 0x013e5cc0, 0xff9f68a1, 0x000e7fa1, - 0x60f44b91, 0xf6248fb6, 0xfea35f79, 0x0370bafc, 0xfd6d2180, 0x013b74ca, 0xff9f8ae9, 0x000e9fd5, - 0x60579947, 0xf5a9e398, 0xfeea0d0c, 0x034ec77f, 0xfd79b7a1, 0x01386f3a, 0xff9fba47, 0x000ebc54, - 0x5fb79278, 0xf5322b61, 0xff30144a, 0x032ccebb, 0xfd86752e, 0x01354ce7, 0xff9ff674, 0x000ed533, - 0x5f1442dc, 0xf4bd68b6, 0xff756edc, 0x030ad4e1, 0xfd93580d, 0x01320ea9, 0xffa03f2b, 0x000eea84, - 0x5e6db665, 0xf44b9cfe, 0xffba168d, 0x02e8de19, 0xfda05e23, 0x012eb55a, 0xffa09425, 0x000efc5c, - 0x5dc3f93c, 0xf3dcc959, 0xfffe054e, 0x02c6ee7f, 0xfdad855b, 0x012b41d3, 0xffa0f519, 0x000f0ace, - 0x5d1717c4, 0xf370eea9, 0x00413536, 0x02a50a22, 0xfdbacb9e, 0x0127b4f1, 0xffa161bf, 0x000f15ef, - 0x5c671e96, 0xf3080d8c, 0x0083a081, 0x02833506, 0xfdc82edb, 0x01240f8e, 0xffa1d9cf, 0x000f1dd2, - 0x5bb41a80, 0xf2a2265e, 0x00c54190, 0x02617321, 0xfdd5ad01, 0x01205285, 0xffa25cfe, 0x000f228d, - 0x5afe1886, 0xf23f393b, 0x010612eb, 0x023fc85c, 0xfde34403, 0x011c7eb2, 0xffa2eb04, 0x000f2434, - 0x5a4525df, 0xf1df45fd, 0x01460f41, 0x021e3891, 0xfdf0f1d6, 0x011894f0, 0xffa38395, 0x000f22dc, - 0x59894ff3, 0xf1824c3e, 0x01853165, 0x01fcc78f, 0xfdfeb475, 0x0114961b, 0xffa42668, 0x000f1e99, - 0x58caa45b, 0xf1284b58, 0x01c37452, 0x01db7914, 0xfe0c89db, 0x0110830f, 0xffa4d332, 0x000f1781, - 0x580930e1, 0xf0d14267, 0x0200d32c, 0x01ba50d2, 0xfe1a7009, 0x010c5ca6, 0xffa589a6, 0x000f0da8, - 0x5745037c, 0xf07d3043, 0x023d493c, 0x0199526b, 0xfe286505, 0x010823ba, 0xffa6497c, 0x000f0125, - 0x567e2a51, 0xf02c138a, 0x0278d1f2, 0x01788170, 0xfe3666d5, 0x0103d927, 0xffa71266, 0x000ef20b, - 0x55b4b3af, 0xefddea9a, 0x02b368e6, 0x0157e166, 0xfe447389, 0x00ff7dc4, 0xffa7e41a, 0x000ee070, - 0x54e8ae13, 0xef92b393, 0x02ed09d7, 0x013775bf, 0xfe528931, 0x00fb126b, 0xffa8be4c, 0x000ecc69, - 0x541a281e, 0xef4a6c58, 0x0325b0ad, 0x011741df, 0xfe60a5e5, 0x00f697f3, 0xffa9a0b1, 0x000eb60b, - 0x5349309e, 0xef051290, 0x035d5977, 0x00f7491a, 0xfe6ec7c0, 0x00f20f32, 0xffaa8afe, 0x000e9d6b, - 0x5275d684, 0xeec2a3a3, 0x0394006a, 0x00d78eb3, 0xfe7cece2, 0x00ed78ff, 0xffab7ce7, 0x000e829e, - 0x51a028e8, 0xee831cc3, 0x03c9a1e5, 0x00b815da, 0xfe8b1373, 0x00e8d62d, 0xffac7621, 0x000e65ba, - 0x50c83704, 0xee467ae1, 0x03fe3a6f, 0x0098e1b3, 0xfe99399f, 0x00e4278f, 0xffad7662, 0x000e46d3, - 0x4fee1037, 0xee0cbab9, 0x0431c6b5, 0x0079f54c, 0xfea75d97, 0x00df6df7, 0xffae7d5f, 0x000e25fd, - 0x4f11c3fe, 0xedd5d8ca, 0x0464438c, 0x005b53a4, 0xfeb57d92, 0x00daaa34, 0xffaf8acd, 0x000e034f, - 0x4e3361f7, 0xeda1d15c, 0x0495adf2, 0x003cffa9, 0xfec397cf, 0x00d5dd16, 0xffb09e63, 0x000ddedb, - 0x4d52f9df, 0xed70a07d, 0x04c6030d, 0x001efc35, 0xfed1aa92, 0x00d10769, 0xffb1b7d8, 0x000db8b7, - 0x4c709b8e, 0xed424205, 0x04f54029, 0x00014c12, 0xfedfb425, 0x00cc29f7, 0xffb2d6e1, 0x000d90f6, - 0x4b8c56f8, 0xed16b196, 0x052362ba, 0xffe3f1f7, 0xfeedb2da, 0x00c7458a, 0xffb3fb37, 0x000d67ae, - 0x4aa63c2c, 0xecedea99, 0x0550685d, 0xffc6f08a, 0xfefba508, 0x00c25ae8, 0xffb52490, 0x000d3cf1, - 0x49be5b50, 0xecc7e845, 0x057c4ed4, 0xffaa4a5d, 0xff09890f, 0x00bd6ad7, 0xffb652a7, 0x000d10d5, - 0x48d4c4a2, 0xeca4a59b, 0x05a7140b, 0xff8e01f1, 0xff175d53, 0x00b87619, 0xffb78533, 0x000ce36b, - 0x47e98874, 0xec841d68, 0x05d0b612, 0xff7219b3, 0xff252042, 0x00b37d70, 0xffb8bbed, 0x000cb4c8, - 0x46fcb72d, 0xec664a48, 0x05f93324, 0xff5693fe, 0xff32d04f, 0x00ae8198, 0xffb9f691, 0x000c84ff, - 0x460e6148, 0xec4b26a2, 0x0620899e, 0xff3b731b, 0xff406bf8, 0x00a9834e, 0xffbb34d8, 0x000c5422, - 0x451e9750, 0xec32acb0, 0x0646b808, 0xff20b93e, 0xff4df1be, 0x00a4834c, 0xffbc767f, 0x000c2245, - 0x442d69de, 0xec1cd677, 0x066bbd0d, 0xff066889, 0xff5b602c, 0x009f8249, 0xffbdbb42, 0x000bef79, - 0x433ae99c, 0xec099dcf, 0x068f9781, 0xfeec830d, 0xff68b5d5, 0x009a80f8, 0xffbf02dd, 0x000bbbd2, - 0x4247273f, 0xebf8fc64, 0x06b2465b, 0xfed30ac5, 0xff75f153, 0x0095800c, 0xffc04d0f, 0x000b8760, - 0x41523389, 0xebeaebaf, 0x06d3c8bb, 0xfeba0199, 0xff831148, 0x00908034, 0xffc19996, 0x000b5235, - 0x405c1f43, 0xebdf6500, 0x06f41de3, 0xfea16960, 0xff90145e, 0x008b821b, 0xffc2e832, 0x000b1c64, - 0x3f64fb40, 0xebd6617b, 0x0713453d, 0xfe8943dc, 0xff9cf947, 0x0086866b, 0xffc438a3, 0x000ae5fc, - 0x3e6cd85b, 0xebcfda19, 0x07313e56, 0xfe7192bd, 0xffa9bebe, 0x00818dcb, 0xffc58aaa, 0x000aaf0f, - 0x3d73c772, 0xebcbc7a7, 0x074e08e0, 0xfe5a579d, 0xffb66386, 0x007c98de, 0xffc6de09, 0x000a77ac, - 0x3c79d968, 0xebca22cc, 0x0769a4b2, 0xfe439407, 0xffc2e669, 0x0077a845, 0xffc83285, 0x000a3fe5, - 0x3b7f1f23, 0xebcae405, 0x078411c7, 0xfe2d496f, 0xffcf463a, 0x0072bc9d, 0xffc987e0, 0x000a07c9, - 0x3a83a989, 0xebce03aa, 0x079d503b, 0xfe177937, 0xffdb81d6, 0x006dd680, 0xffcadde1, 0x0009cf67, - 0x3987897f, 0xebd379eb, 0x07b56051, 0xfe0224b0, 0xffe79820, 0x0068f687, 0xffcc344c, 0x000996ce, - 0x388acfe9, 0xebdb3ed5, 0x07cc426c, 0xfded4d13, 0xfff38806, 0x00641d44, 0xffcd8aeb, 0x00095e0e, - 0x378d8da8, 0xebe54a4f, 0x07e1f712, 0xfdd8f38b, 0xffff507b, 0x005f4b4a, 0xffcee183, 0x00092535, - 0x368fd397, 0xebf1941f, 0x07f67eec, 0xfdc5192d, 0x000af07f, 0x005a8125, 0xffd037e0, 0x0008ec50, - 0x3591b28b, 0xec0013e8, 0x0809dac3, 0xfdb1befc, 0x00166718, 0x0055bf60, 0xffd18dcc, 0x0008b36e, - 0x34933b50, 0xec10c12c, 0x081c0b84, 0xfd9ee5e7, 0x0021b355, 0x00510682, 0xffd2e311, 0x00087a9c, - 0x33947eab, 0xec23934f, 0x082d1239, 0xfd8c8ecc, 0x002cd44d, 0x004c570f, 0xffd4377d, 0x000841e8, - 0x32958d55, 0xec388194, 0x083cf010, 0xfd7aba74, 0x0037c922, 0x0047b186, 0xffd58ade, 0x0008095d, - 0x319677fa, 0xec4f8322, 0x084ba654, 0xfd696998, 0x004290fc, 0x00431666, 0xffd6dd02, 0x0007d108, - 0x30974f3b, 0xec688f02, 0x08593671, 0xfd589cdc, 0x004d2b0e, 0x003e8628, 0xffd82dba, 0x000798f5, - 0x2f9823a8, 0xec839c22, 0x0865a1f1, 0xfd4854d3, 0x00579691, 0x003a0141, 0xffd97cd6, 0x00076130, - 0x2e9905c1, 0xeca0a156, 0x0870ea7e, 0xfd3891fd, 0x0061d2ca, 0x00358824, 0xffdaca2a, 0x000729c4, - 0x2d9a05f4, 0xecbf9558, 0x087b11de, 0xfd2954c8, 0x006bdf05, 0x00311b41, 0xffdc1588, 0x0006f2bb, - 0x2c9b349e, 0xece06ecb, 0x088419f6, 0xfd1a9d91, 0x0075ba95, 0x002cbb03, 0xffdd5ec6, 0x0006bc21, - 0x2b9ca203, 0xed032439, 0x088c04c8, 0xfd0c6ca2, 0x007f64da, 0x002867d2, 0xffdea5bb, 0x000685ff, - 0x2a9e5e57, 0xed27ac16, 0x0892d470, 0xfcfec233, 0x0088dd38, 0x00242213, 0xffdfea3c, 0x0006505f, - 0x29a079b2, 0xed4dfcc2, 0x08988b2a, 0xfcf19e6b, 0x0092231e, 0x001fea27, 0xffe12c22, 0x00061b4b, - 0x28a30416, 0xed760c88, 0x089d2b4a, 0xfce50161, 0x009b3605, 0x001bc06b, 0xffe26b48, 0x0005e6cb, - 0x27a60d6a, 0xed9fd1a2, 0x08a0b740, 0xfcd8eb17, 0x00a4156b, 0x0017a53b, 0xffe3a788, 0x0005b2e8, - 0x26a9a57b, 0xedcb4237, 0x08a33196, 0xfccd5b82, 0x00acc0da, 0x001398ec, 0xffe4e0bf, 0x00057faa, - 0x25addbf9, 0xedf8545b, 0x08a49cf0, 0xfcc25285, 0x00b537e1, 0x000f9bd2, 0xffe616c8, 0x00054d1a, - 0x24b2c075, 0xee26fe17, 0x08a4fc0d, 0xfcb7cff0, 0x00bd7a1c, 0x000bae3c, 0xffe74984, 0x00051b3e, - 0x23b86263, 0xee573562, 0x08a451c0, 0xfcadd386, 0x00c5872a, 0x0007d075, 0xffe878d3, 0x0004ea1d, - 0x22bed116, 0xee88f026, 0x08a2a0f8, 0xfca45cf7, 0x00cd5eb7, 0x000402c8, 0xffe9a494, 0x0004b9c0, - 0x21c61bc0, 0xeebc2444, 0x089fecbb, 0xfc9b6be5, 0x00d50075, 0x00004579, 0xffeaccaa, 0x00048a2b, - 0x20ce516f, 0xeef0c78d, 0x089c3824, 0xfc92ffe1, 0x00dc6c1e, 0xfffc98c9, 0xffebf0fa, 0x00045b65, - 0x1fd7810f, 0xef26cfca, 0x08978666, 0xfc8b186d, 0x00e3a175, 0xfff8fcf7, 0xffed1166, 0x00042d74, - 0x1ee1b965, 0xef5e32bd, 0x0891dac8, 0xfc83b4fc, 0x00eaa045, 0xfff5723d, 0xffee2dd7, 0x0004005e, - 0x1ded0911, 0xef96e61c, 0x088b38a9, 0xfc7cd4f0, 0x00f16861, 0xfff1f8d2, 0xffef4632, 0x0003d426, - 0x1cf97e8b, 0xefd0df9a, 0x0883a378, 0xfc76779e, 0x00f7f9a3, 0xffee90eb, 0xfff05a60, 0x0003a8d2, - 0x1c072823, 0xf00c14e1, 0x087b1ebc, 0xfc709c4d, 0x00fe53ef, 0xffeb3ab8, 0xfff16a4a, 0x00037e65, - 0x1b1613ff, 0xf0487b98, 0x0871ae0d, 0xfc6b4233, 0x0104772e, 0xffe7f666, 0xfff275db, 0x000354e5, - 0x1a26501b, 0xf0860962, 0x08675516, 0xfc66687a, 0x010a6353, 0xffe4c41e, 0xfff37d00, 0x00032c54, - 0x1937ea47, 0xf0c4b3e0, 0x085c1794, 0xfc620e3d, 0x01101858, 0xffe1a408, 0xfff47fa5, 0x000304b7, - 0x184af025, 0xf10470b0, 0x084ff957, 0xfc5e328c, 0x0115963d, 0xffde9646, 0xfff57db8, 0x0002de0e, - 0x175f6f2b, 0xf1453571, 0x0842fe3d, 0xfc5ad465, 0x011add0b, 0xffdb9af8, 0xfff67729, 0x0002b85f, - 0x1675749e, 0xf186f7c0, 0x08352a35, 0xfc57f2be, 0x011fecd3, 0xffd8b23b, 0xfff76be9, 0x000293aa, - 0x158d0d95, 0xf1c9ad40, 0x0826813e, 0xfc558c7c, 0x0124c5ab, 0xffd5dc28, 0xfff85be8, 0x00026ff2, - 0x14a646f6, 0xf20d4b92, 0x08170767, 0xfc53a07b, 0x012967b1, 0xffd318d6, 0xfff9471b, 0x00024d39, - 0x13c12d73, 0xf251c85d, 0x0806c0cb, 0xfc522d88, 0x012dd30a, 0xffd06858, 0xfffa2d74, 0x00022b7f, - 0x12ddcd8f, 0xf297194d, 0x07f5b193, 0xfc513266, 0x013207e4, 0xffcdcabe, 0xfffb0ee9, 0x00020ac7, - 0x11fc3395, 0xf2dd3411, 0x07e3ddf7, 0xfc50adcc, 0x01360670, 0xffcb4014, 0xfffbeb70, 0x0001eb10, - 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, 0x0001cc5c, +#include "AudioResamplerSincUp.h" }; /* @@ -197,135 +69,7 @@ const uint32_t AudioResamplerSinc::mFirCoefsUp[] __attribute__ ((aligned (32))) * cmd-line: fir -l 7 -s 48000 -c 17189 */ const uint32_t AudioResamplerSinc::mFirCoefsDown[] __attribute__ ((aligned (32))) = { - 0x5bacb6f4, 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, - 0x5bab6c81, 0x1d3ddccd, 0xf0421d2c, 0x03af9995, 0x01818dc9, 0xfe6bb63e, 0x0079812a, 0xfffdc37d, - 0x5ba78d37, 0x1c8f2cf9, 0xf04beb1d, 0x03c9a04a, 0x016f8aca, 0xfe70a511, 0x0079e34d, 0xfffd2545, - 0x5ba1194f, 0x1be11231, 0xf056f2c7, 0x03e309fe, 0x015d9e64, 0xfe75a79f, 0x007a36e2, 0xfffc8b86, - 0x5b981122, 0x1b3393f8, 0xf0632fb7, 0x03fbd625, 0x014bc9fa, 0xfe7abd23, 0x007a7c20, 0xfffbf639, - 0x5b8c7530, 0x1a86b9bf, 0xf0709d74, 0x04140449, 0x013a0ee9, 0xfe7fe4db, 0x007ab33d, 0xfffb655b, - 0x5b7e461a, 0x19da8ae5, 0xf07f3776, 0x042b93fd, 0x01286e86, 0xfe851e05, 0x007adc72, 0xfffad8e4, - 0x5b6d84a8, 0x192f0eb7, 0xf08ef92d, 0x044284e6, 0x0116ea22, 0xfe8a67dd, 0x007af7f6, 0xfffa50ce, - 0x5b5a31c6, 0x18844c70, 0xf09fddfe, 0x0458d6b7, 0x01058306, 0xfe8fc1a5, 0x007b0603, 0xfff9cd12, - 0x5b444e81, 0x17da4b37, 0xf0b1e143, 0x046e8933, 0x00f43a74, 0xfe952a9b, 0x007b06d4, 0xfff94da9, - 0x5b2bdc0e, 0x17311222, 0xf0c4fe50, 0x04839c29, 0x00e311a9, 0xfe9aa201, 0x007afaa1, 0xfff8d28c, - 0x5b10dbc2, 0x1688a832, 0xf0d9306d, 0x04980f79, 0x00d209db, 0xfea02719, 0x007ae1a7, 0xfff85bb1, - 0x5af34f18, 0x15e11453, 0xf0ee72db, 0x04abe310, 0x00c12439, 0xfea5b926, 0x007abc20, 0xfff7e910, - 0x5ad337af, 0x153a5d5e, 0xf104c0d2, 0x04bf16e9, 0x00b061eb, 0xfeab576d, 0x007a8a49, 0xfff77a9f, - 0x5ab09748, 0x14948a16, 0xf11c1583, 0x04d1ab0d, 0x009fc413, 0xfeb10134, 0x007a4c5d, 0xfff71057, - 0x5a8b6fc7, 0x13efa12c, 0xf1346c17, 0x04e39f93, 0x008f4bcb, 0xfeb6b5c0, 0x007a029a, 0xfff6aa2b, - 0x5a63c336, 0x134ba937, 0xf14dbfb1, 0x04f4f4a2, 0x007efa29, 0xfebc745c, 0x0079ad3d, 0xfff64812, - 0x5a3993c0, 0x12a8a8bb, 0xf1680b6e, 0x0505aa6a, 0x006ed038, 0xfec23c50, 0x00794c82, 0xfff5ea02, - 0x5a0ce3b2, 0x1206a625, 0xf1834a63, 0x0515c12d, 0x005ecf01, 0xfec80ce8, 0x0078e0a9, 0xfff58ff0, - 0x59ddb57f, 0x1165a7cc, 0xf19f77a0, 0x05253938, 0x004ef782, 0xfecde571, 0x007869ee, 0xfff539cf, - 0x59ac0bba, 0x10c5b3ef, 0xf1bc8e31, 0x053412e4, 0x003f4ab4, 0xfed3c538, 0x0077e891, 0xfff4e794, - 0x5977e919, 0x1026d0b8, 0xf1da891b, 0x05424e9b, 0x002fc98a, 0xfed9ab8f, 0x00775ccf, 0xfff49934, - 0x59415075, 0x0f890437, 0xf1f96360, 0x054feccf, 0x002074ed, 0xfedf97c6, 0x0076c6e8, 0xfff44ea3, - 0x590844c9, 0x0eec5465, 0xf21917ff, 0x055cee03, 0x00114dc3, 0xfee58932, 0x00762719, 0xfff407d2, - 0x58ccc930, 0x0e50c723, 0xf239a1ef, 0x056952c3, 0x000254e8, 0xfeeb7f27, 0x00757da3, 0xfff3c4b7, - 0x588ee0ea, 0x0db6623b, 0xf25afc29, 0x05751baa, 0xfff38b32, 0xfef178fc, 0x0074cac4, 0xfff38542, - 0x584e8f56, 0x0d1d2b5d, 0xf27d219f, 0x0580495c, 0xffe4f171, 0xfef7760c, 0x00740ebb, 0xfff34968, - 0x580bd7f4, 0x0c85281f, 0xf2a00d43, 0x058adc8d, 0xffd6886d, 0xfefd75af, 0x007349c7, 0xfff3111b, - 0x57c6be67, 0x0bee5dff, 0xf2c3ba04, 0x0594d5fa, 0xffc850e6, 0xff037744, 0x00727c27, 0xfff2dc4c, - 0x577f4670, 0x0b58d262, 0xf2e822ce, 0x059e366c, 0xffba4b98, 0xff097a29, 0x0071a61b, 0xfff2aaef, - 0x573573f2, 0x0ac48a92, 0xf30d428e, 0x05a6feb9, 0xffac7936, 0xff0f7dbf, 0x0070c7e1, 0xfff27cf3, - 0x56e94af1, 0x0a318bc1, 0xf333142f, 0x05af2fbf, 0xff9eda6d, 0xff15816a, 0x006fe1b8, 0xfff2524c, - 0x569acf90, 0x099fdb04, 0xf359929a, 0x05b6ca6b, 0xff916fe1, 0xff1b848e, 0x006ef3df, 0xfff22aea, - 0x564a0610, 0x090f7d57, 0xf380b8ba, 0x05bdcfb2, 0xff843a32, 0xff218692, 0x006dfe94, 0xfff206bf, - 0x55f6f2d3, 0x0880779d, 0xf3a88179, 0x05c44095, 0xff7739f7, 0xff2786e1, 0x006d0217, 0xfff1e5bb, - 0x55a19a5c, 0x07f2ce9b, 0xf3d0e7c2, 0x05ca1e1f, 0xff6a6fc1, 0xff2d84e5, 0x006bfea4, 0xfff1c7d0, - 0x554a0148, 0x076686fc, 0xf3f9e680, 0x05cf6965, 0xff5ddc1a, 0xff33800e, 0x006af47b, 0xfff1acef, - 0x54f02c56, 0x06dba551, 0xf42378a0, 0x05d42387, 0xff517f86, 0xff3977cb, 0x0069e3d9, 0xfff19508, - 0x54942061, 0x06522e0f, 0xf44d9912, 0x05d84daf, 0xff455a80, 0xff3f6b8f, 0x0068ccfa, 0xfff1800b, - 0x5435e263, 0x05ca258f, 0xf47842c5, 0x05dbe90f, 0xff396d7f, 0xff455acf, 0x0067b01e, 0xfff16de9, - 0x53d57774, 0x0543900d, 0xf4a370ad, 0x05def6e4, 0xff2db8f2, 0xff4b4503, 0x00668d80, 0xfff15e93, - 0x5372e4c6, 0x04be71ab, 0xf4cf1dbf, 0x05e17873, 0xff223d40, 0xff5129a3, 0x0065655d, 0xfff151f9, - 0x530e2fac, 0x043ace6e, 0xf4fb44f4, 0x05e36f0d, 0xff16faca, 0xff57082e, 0x006437f1, 0xfff1480b, - 0x52a75d90, 0x03b8aa40, 0xf527e149, 0x05e4dc08, 0xff0bf1ed, 0xff5ce021, 0x00630577, 0xfff140b9, - 0x523e73fd, 0x033808eb, 0xf554edbd, 0x05e5c0c6, 0xff0122fc, 0xff62b0fd, 0x0061ce2c, 0xfff13bf3, - 0x51d37897, 0x02b8ee22, 0xf5826555, 0x05e61eae, 0xfef68e45, 0xff687a47, 0x00609249, 0xfff139aa, - 0x5166711c, 0x023b5d76, 0xf5b0431a, 0x05e5f733, 0xfeec340f, 0xff6e3b84, 0x005f520a, 0xfff139cd, - 0x50f76368, 0x01bf5a5e, 0xf5de8218, 0x05e54bcd, 0xfee2149b, 0xff73f43d, 0x005e0da8, 0xfff13c4c, - 0x5086556f, 0x0144e834, 0xf60d1d63, 0x05e41dfe, 0xfed83023, 0xff79a3fe, 0x005cc55c, 0xfff14119, - 0x50134d3e, 0x00cc0a36, 0xf63c1012, 0x05e26f4e, 0xfece86db, 0xff7f4a54, 0x005b7961, 0xfff14821, - 0x4f9e50ff, 0x0054c382, 0xf66b5544, 0x05e0414d, 0xfec518f1, 0xff84e6d0, 0x005a29ed, 0xfff15156, - 0x4f2766f2, 0xffdf171b, 0xf69ae81d, 0x05dd9593, 0xfebbe68c, 0xff8a7905, 0x0058d738, 0xfff15ca8, - 0x4eae9571, 0xff6b07e7, 0xf6cac3c7, 0x05da6dbe, 0xfeb2efcd, 0xff900089, 0x0057817b, 0xfff16a07, - 0x4e33e2ee, 0xfef898ae, 0xf6fae373, 0x05d6cb72, 0xfeaa34d0, 0xff957cf4, 0x005628ec, 0xfff17962, - 0x4db755f3, 0xfe87cc1b, 0xf72b425b, 0x05d2b05c, 0xfea1b5a9, 0xff9aede0, 0x0054cdc0, 0xfff18aab, - 0x4d38f520, 0xfe18a4bc, 0xf75bdbbd, 0x05ce1e2d, 0xfe997268, 0xffa052ec, 0x0053702d, 0xfff19dd1, - 0x4cb8c72e, 0xfdab2501, 0xf78caae0, 0x05c9169d, 0xfe916b15, 0xffa5abb8, 0x00521068, 0xfff1b2c5, - 0x4c36d2eb, 0xfd3f4f3d, 0xf7bdab16, 0x05c39b6a, 0xfe899fb2, 0xffaaf7e6, 0x0050aea5, 0xfff1c976, - 0x4bb31f3c, 0xfcd525a5, 0xf7eed7b4, 0x05bdae57, 0xfe82103f, 0xffb0371c, 0x004f4b17, 0xfff1e1d6, - 0x4b2db31a, 0xfc6caa53, 0xf8202c1c, 0x05b7512e, 0xfe7abcb1, 0xffb56902, 0x004de5f1, 0xfff1fbd5, - 0x4aa69594, 0xfc05df40, 0xf851a3b6, 0x05b085bc, 0xfe73a4fb, 0xffba8d44, 0x004c7f66, 0xfff21764, - 0x4a1dcdce, 0xfba0c64b, 0xf88339f5, 0x05a94dd5, 0xfe6cc909, 0xffbfa38d, 0x004b17a6, 0xfff23473, - 0x499362ff, 0xfb3d6133, 0xf8b4ea55, 0x05a1ab52, 0xfe6628c1, 0xffc4ab8f, 0x0049aee3, 0xfff252f3, - 0x49075c72, 0xfadbb19a, 0xf8e6b059, 0x0599a00e, 0xfe5fc405, 0xffc9a4fc, 0x0048454b, 0xfff272d6, - 0x4879c185, 0xfa7bb908, 0xf9188793, 0x05912dea, 0xfe599aaf, 0xffce8f8a, 0x0046db0f, 0xfff2940b, - 0x47ea99a9, 0xfa1d78e3, 0xf94a6b9b, 0x058856cd, 0xfe53ac97, 0xffd36af1, 0x0045705c, 0xfff2b686, - 0x4759ec60, 0xf9c0f276, 0xf97c5815, 0x057f1c9e, 0xfe4df98e, 0xffd836eb, 0x00440561, 0xfff2da36, - 0x46c7c140, 0xf96626f0, 0xf9ae48af, 0x0575814c, 0xfe48815e, 0xffdcf336, 0x00429a4a, 0xfff2ff0d, - 0x46341fed, 0xf90d1761, 0xf9e03924, 0x056b86c6, 0xfe4343d0, 0xffe19f91, 0x00412f43, 0xfff324fd, - 0x459f101d, 0xf8b5c4be, 0xfa122537, 0x05612f00, 0xfe3e40a6, 0xffe63bc0, 0x003fc478, 0xfff34bf9, - 0x45089996, 0xf8602fdc, 0xfa4408ba, 0x05567bf1, 0xfe39779a, 0xffeac787, 0x003e5a12, 0xfff373f0, - 0x4470c42d, 0xf80c5977, 0xfa75df87, 0x054b6f92, 0xfe34e867, 0xffef42af, 0x003cf03d, 0xfff39cd7, - 0x43d797c7, 0xf7ba422b, 0xfaa7a586, 0x05400be1, 0xfe3092bf, 0xfff3ad01, 0x003b871f, 0xfff3c69f, - 0x433d1c56, 0xf769ea78, 0xfad956ab, 0x053452dc, 0xfe2c7650, 0xfff8064b, 0x003a1ee3, 0xfff3f13a, - 0x42a159dc, 0xf71b52c4, 0xfb0aeef6, 0x05284685, 0xfe2892c5, 0xfffc4e5c, 0x0038b7ae, 0xfff41c9c, - 0x42045865, 0xf6ce7b57, 0xfb3c6a73, 0x051be8dd, 0xfe24e7c3, 0x00008507, 0x003751a7, 0xfff448b7, - 0x4166200e, 0xf683645a, 0xfb6dc53c, 0x050f3bec, 0xfe2174ec, 0x0004aa1f, 0x0035ecf4, 0xfff4757e, - 0x40c6b8fd, 0xf63a0ddf, 0xfb9efb77, 0x050241b6, 0xfe1e39da, 0x0008bd7c, 0x003489b9, 0xfff4a2e5, - 0x40262b65, 0xf5f277d9, 0xfbd00956, 0x04f4fc46, 0xfe1b3628, 0x000cbef7, 0x0033281a, 0xfff4d0de, - 0x3f847f83, 0xf5aca21f, 0xfc00eb1b, 0x04e76da3, 0xfe18696a, 0x0010ae6e, 0x0031c83a, 0xfff4ff5d, - 0x3ee1bda2, 0xf5688c6d, 0xfc319d13, 0x04d997d8, 0xfe15d32f, 0x00148bbd, 0x00306a3b, 0xfff52e57, - 0x3e3dee13, 0xf5263665, 0xfc621b9a, 0x04cb7cf2, 0xfe137304, 0x001856c7, 0x002f0e3f, 0xfff55dbf, - 0x3d991932, 0xf4e59f8a, 0xfc926319, 0x04bd1efb, 0xfe114872, 0x001c0f6e, 0x002db466, 0xfff58d89, - 0x3cf34766, 0xf4a6c748, 0xfcc27008, 0x04ae8000, 0xfe0f52fc, 0x001fb599, 0x002c5cd0, 0xfff5bdaa, - 0x3c4c811c, 0xf469aced, 0xfcf23eec, 0x049fa20f, 0xfe0d9224, 0x0023492f, 0x002b079a, 0xfff5ee17, - 0x3ba4cec9, 0xf42e4faf, 0xfd21cc59, 0x04908733, 0xfe0c0567, 0x0026ca1c, 0x0029b4e4, 0xfff61ec5, - 0x3afc38eb, 0xf3f4aea6, 0xfd5114f0, 0x0481317a, 0xfe0aac3f, 0x002a384c, 0x002864c9, 0xfff64fa8, - 0x3a52c805, 0xf3bcc8d3, 0xfd801564, 0x0471a2ef, 0xfe098622, 0x002d93ae, 0x00271766, 0xfff680b5, - 0x39a884a1, 0xf3869d1a, 0xfdaeca73, 0x0461dda0, 0xfe089283, 0x0030dc34, 0x0025ccd7, 0xfff6b1e4, - 0x38fd774e, 0xf3522a49, 0xfddd30eb, 0x0451e396, 0xfe07d0d3, 0x003411d2, 0x00248535, 0xfff6e329, - 0x3851a8a2, 0xf31f6f0f, 0xfe0b45aa, 0x0441b6dd, 0xfe07407d, 0x0037347d, 0x0023409a, 0xfff7147a, - 0x37a52135, 0xf2ee6a07, 0xfe39059b, 0x0431597d, 0xfe06e0eb, 0x003a442e, 0x0021ff1f, 0xfff745cd, - 0x36f7e9a4, 0xf2bf19ae, 0xfe666dbc, 0x0420cd80, 0xfe06b184, 0x003d40e0, 0x0020c0dc, 0xfff7771a, - 0x364a0a90, 0xf2917c6d, 0xfe937b15, 0x041014eb, 0xfe06b1ac, 0x00402a8e, 0x001f85e6, 0xfff7a857, - 0x359b8c9d, 0xf265908f, 0xfec02ac2, 0x03ff31c3, 0xfe06e0c4, 0x00430137, 0x001e4e56, 0xfff7d97a, - 0x34ec786f, 0xf23b544b, 0xfeec79ec, 0x03ee260d, 0xfe073e2a, 0x0045c4dd, 0x001d1a3f, 0xfff80a7c, - 0x343cd6af, 0xf212c5be, 0xff1865cd, 0x03dcf3ca, 0xfe07c93a, 0x00487582, 0x001be9b7, 0xfff83b52, - 0x338cb004, 0xf1ebe2ec, 0xff43ebac, 0x03cb9cf9, 0xfe08814e, 0x004b132b, 0x001abcd0, 0xfff86bf6, - 0x32dc0d17, 0xf1c6a9c3, 0xff6f08e4, 0x03ba2398, 0xfe0965bc, 0x004d9dde, 0x0019939d, 0xfff89c60, - 0x322af693, 0xf1a3181a, 0xff99badb, 0x03a889a1, 0xfe0a75da, 0x005015a5, 0x00186e31, 0xfff8cc86, - 0x3179751f, 0xf1812bb0, 0xffc3ff0c, 0x0396d10c, 0xfe0bb0f9, 0x00527a8a, 0x00174c9c, 0xfff8fc62, - 0x30c79163, 0xf160e22d, 0xffedd2fd, 0x0384fbd1, 0xfe0d166b, 0x0054cc9a, 0x00162eef, 0xfff92bec, - 0x30155404, 0xf1423924, 0x00173447, 0x03730be0, 0xfe0ea57e, 0x00570be4, 0x00151538, 0xfff95b1e, - 0x2f62c5a7, 0xf1252e0f, 0x00402092, 0x0361032a, 0xfe105d7e, 0x00593877, 0x0013ff88, 0xfff989ef, - 0x2eafeeed, 0xf109be56, 0x00689598, 0x034ee39b, 0xfe123db6, 0x005b5267, 0x0012edea, 0xfff9b85b, - 0x2dfcd873, 0xf0efe748, 0x0090911f, 0x033caf1d, 0xfe144570, 0x005d59c6, 0x0011e06d, 0xfff9e65a, - 0x2d498ad3, 0xf0d7a622, 0x00b81102, 0x032a6796, 0xfe1673f2, 0x005f4eac, 0x0010d71d, 0xfffa13e5, - 0x2c960ea3, 0xf0c0f808, 0x00df1328, 0x03180ee7, 0xfe18c884, 0x0061312e, 0x000fd205, 0xfffa40f8, - 0x2be26c73, 0xf0abda0e, 0x0105958c, 0x0305a6f0, 0xfe1b4268, 0x00630167, 0x000ed130, 0xfffa6d8d, - 0x2b2eaccf, 0xf0984931, 0x012b9635, 0x02f3318a, 0xfe1de0e2, 0x0064bf71, 0x000dd4a7, 0xfffa999d, - 0x2a7ad83c, 0xf086425a, 0x0151133e, 0x02e0b08d, 0xfe20a335, 0x00666b68, 0x000cdc74, 0xfffac525, - 0x29c6f738, 0xf075c260, 0x01760ad1, 0x02ce25ca, 0xfe2388a1, 0x0068056b, 0x000be89f, 0xfffaf01e, - 0x2913123c, 0xf066c606, 0x019a7b27, 0x02bb9310, 0xfe269065, 0x00698d98, 0x000af931, 0xfffb1a84, - 0x285f31b7, 0xf05949fb, 0x01be628c, 0x02a8fa2a, 0xfe29b9c1, 0x006b0411, 0x000a0e2f, 0xfffb4453, - 0x27ab5e12, 0xf04d4ade, 0x01e1bf58, 0x02965cdb, 0xfe2d03f2, 0x006c68f8, 0x000927a0, 0xfffb6d86, - 0x26f79fab, 0xf042c539, 0x02048ff8, 0x0283bce6, 0xfe306e35, 0x006dbc71, 0x00084589, 0xfffb961a, - 0x2643feda, 0xf039b587, 0x0226d2e6, 0x02711c05, 0xfe33f7c7, 0x006efea0, 0x000767f0, 0xfffbbe09, - 0x259083eb, 0xf032182f, 0x024886ad, 0x025e7bf0, 0xfe379fe3, 0x00702fae, 0x00068ed8, 0xfffbe552, - 0x24dd3721, 0xf02be98a, 0x0269a9e9, 0x024bde5a, 0xfe3b65c4, 0x00714fc0, 0x0005ba46, 0xfffc0bef, - 0x242a20b3, 0xf02725dc, 0x028a3b44, 0x023944ee, 0xfe3f48a5, 0x00725f02, 0x0004ea3a, 0xfffc31df, - 0x237748cf, 0xf023c95d, 0x02aa397b, 0x0226b156, 0xfe4347c0, 0x00735d9c, 0x00041eb9, 0xfffc571e, - 0x22c4b795, 0xf021d031, 0x02c9a359, 0x02142533, 0xfe476250, 0x00744bba, 0x000357c2, 0xfffc7ba9, - 0x2212751a, 0xf0213671, 0x02e877b9, 0x0201a223, 0xfe4b978e, 0x0075298a, 0x00029558, 0xfffc9f7e, - 0x21608968, 0xf021f823, 0x0306b586, 0x01ef29be, 0xfe4fe6b3, 0x0075f739, 0x0001d779, 0xfffcc29a, - 0x20aefc79, 0xf0241140, 0x03245bbc, 0x01dcbd96, 0xfe544efb, 0x0076b4f5, 0x00011e26, 0xfffce4fc, - 0x1ffdd63b, 0xf0277db1, 0x03416966, 0x01ca5f37, 0xfe58cf9d, 0x007762f0, 0x0000695e, 0xfffd06a1, - 0x1f4d1e8e, 0xf02c3953, 0x035ddd9e, 0x01b81028, 0xfe5d67d4, 0x0078015a, 0xffffb91f, 0xfffd2787, - 0x1e9cdd43, 0xf0323ff5, 0x0379b790, 0x01a5d1ea, 0xfe6216db, 0x00789065, 0xffff0d66, 0xfffd47ae, - 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, 0xfffd6713, +#include "AudioResamplerSincDown.h" }; // we use 15 bits to interpolate between these samples @@ -521,7 +265,8 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, if (mConstants == &veryHighQualityConstants && readResampleCoefficients) { mFirCoefs = readResampleCoefficients( mInSampleRate <= mSampleRate ); } else { - mFirCoefs = (const int32_t *) ((mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown); + mFirCoefs = (const int32_t *) + ((mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown); } // select the appropriate resampler @@ -856,4 +601,4 @@ void AudioResamplerSinc::interpolate( } } // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h index 4691d0a..6d8e85d 100644 --- a/services/audioflinger/AudioResamplerSinc.h +++ b/services/audioflinger/AudioResamplerSinc.h @@ -95,6 +95,6 @@ private: }; // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_SINC_H*/ diff --git a/services/audioflinger/AudioResamplerSincDown.h b/services/audioflinger/AudioResamplerSincDown.h new file mode 100644 index 0000000..2d0fb86 --- /dev/null +++ b/services/audioflinger/AudioResamplerSincDown.h @@ -0,0 +1,131 @@ +// cmd-line: fir -l 7 -s48000 -c 17189 + + 0x5bacb6f4, 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, + 0x5bab6c81, 0x1d3ddccd, 0xf0421d2c, 0x03af9995, 0x01818dc9, 0xfe6bb63e, 0x0079812a, 0xfffdc37d, + 0x5ba78d37, 0x1c8f2cf9, 0xf04beb1d, 0x03c9a04a, 0x016f8aca, 0xfe70a511, 0x0079e34d, 0xfffd2545, + 0x5ba1194f, 0x1be11231, 0xf056f2c7, 0x03e309fe, 0x015d9e64, 0xfe75a79f, 0x007a36e2, 0xfffc8b86, + 0x5b981122, 0x1b3393f8, 0xf0632fb7, 0x03fbd625, 0x014bc9fa, 0xfe7abd23, 0x007a7c20, 0xfffbf639, + 0x5b8c7530, 0x1a86b9bf, 0xf0709d74, 0x04140449, 0x013a0ee9, 0xfe7fe4db, 0x007ab33d, 0xfffb655b, + 0x5b7e461a, 0x19da8ae5, 0xf07f3776, 0x042b93fd, 0x01286e86, 0xfe851e05, 0x007adc72, 0xfffad8e4, + 0x5b6d84a8, 0x192f0eb7, 0xf08ef92d, 0x044284e6, 0x0116ea22, 0xfe8a67dd, 0x007af7f6, 0xfffa50ce, + 0x5b5a31c6, 0x18844c70, 0xf09fddfe, 0x0458d6b7, 0x01058306, 0xfe8fc1a5, 0x007b0603, 0xfff9cd12, + 0x5b444e81, 0x17da4b37, 0xf0b1e143, 0x046e8933, 0x00f43a74, 0xfe952a9b, 0x007b06d4, 0xfff94da9, + 0x5b2bdc0e, 0x17311222, 0xf0c4fe50, 0x04839c29, 0x00e311a9, 0xfe9aa201, 0x007afaa1, 0xfff8d28c, + 0x5b10dbc2, 0x1688a832, 0xf0d9306d, 0x04980f79, 0x00d209db, 0xfea02719, 0x007ae1a7, 0xfff85bb1, + 0x5af34f18, 0x15e11453, 0xf0ee72db, 0x04abe310, 0x00c12439, 0xfea5b926, 0x007abc20, 0xfff7e910, + 0x5ad337af, 0x153a5d5e, 0xf104c0d2, 0x04bf16e9, 0x00b061eb, 0xfeab576d, 0x007a8a49, 0xfff77a9f, + 0x5ab09748, 0x14948a16, 0xf11c1583, 0x04d1ab0d, 0x009fc413, 0xfeb10134, 0x007a4c5d, 0xfff71057, + 0x5a8b6fc7, 0x13efa12c, 0xf1346c17, 0x04e39f93, 0x008f4bcb, 0xfeb6b5c0, 0x007a029a, 0xfff6aa2b, + 0x5a63c336, 0x134ba937, 0xf14dbfb1, 0x04f4f4a2, 0x007efa29, 0xfebc745c, 0x0079ad3d, 0xfff64812, + 0x5a3993c0, 0x12a8a8bb, 0xf1680b6e, 0x0505aa6a, 0x006ed038, 0xfec23c50, 0x00794c82, 0xfff5ea02, + 0x5a0ce3b2, 0x1206a625, 0xf1834a63, 0x0515c12d, 0x005ecf01, 0xfec80ce8, 0x0078e0a9, 0xfff58ff0, + 0x59ddb57f, 0x1165a7cc, 0xf19f77a0, 0x05253938, 0x004ef782, 0xfecde571, 0x007869ee, 0xfff539cf, + 0x59ac0bba, 0x10c5b3ef, 0xf1bc8e31, 0x053412e4, 0x003f4ab4, 0xfed3c538, 0x0077e891, 0xfff4e794, + 0x5977e919, 0x1026d0b8, 0xf1da891b, 0x05424e9b, 0x002fc98a, 0xfed9ab8f, 0x00775ccf, 0xfff49934, + 0x59415075, 0x0f890437, 0xf1f96360, 0x054feccf, 0x002074ed, 0xfedf97c6, 0x0076c6e8, 0xfff44ea3, + 0x590844c9, 0x0eec5465, 0xf21917ff, 0x055cee03, 0x00114dc3, 0xfee58932, 0x00762719, 0xfff407d2, + 0x58ccc930, 0x0e50c723, 0xf239a1ef, 0x056952c3, 0x000254e8, 0xfeeb7f27, 0x00757da3, 0xfff3c4b7, + 0x588ee0ea, 0x0db6623b, 0xf25afc29, 0x05751baa, 0xfff38b32, 0xfef178fc, 0x0074cac4, 0xfff38542, + 0x584e8f56, 0x0d1d2b5d, 0xf27d219f, 0x0580495c, 0xffe4f171, 0xfef7760c, 0x00740ebb, 0xfff34968, + 0x580bd7f4, 0x0c85281f, 0xf2a00d43, 0x058adc8d, 0xffd6886d, 0xfefd75af, 0x007349c7, 0xfff3111b, + 0x57c6be67, 0x0bee5dff, 0xf2c3ba04, 0x0594d5fa, 0xffc850e6, 0xff037744, 0x00727c27, 0xfff2dc4c, + 0x577f4670, 0x0b58d262, 0xf2e822ce, 0x059e366c, 0xffba4b98, 0xff097a29, 0x0071a61b, 0xfff2aaef, + 0x573573f2, 0x0ac48a92, 0xf30d428e, 0x05a6feb9, 0xffac7936, 0xff0f7dbf, 0x0070c7e1, 0xfff27cf3, + 0x56e94af1, 0x0a318bc1, 0xf333142f, 0x05af2fbf, 0xff9eda6d, 0xff15816a, 0x006fe1b8, 0xfff2524c, + 0x569acf90, 0x099fdb04, 0xf359929a, 0x05b6ca6b, 0xff916fe1, 0xff1b848e, 0x006ef3df, 0xfff22aea, + 0x564a0610, 0x090f7d57, 0xf380b8ba, 0x05bdcfb2, 0xff843a32, 0xff218692, 0x006dfe94, 0xfff206bf, + 0x55f6f2d3, 0x0880779d, 0xf3a88179, 0x05c44095, 0xff7739f7, 0xff2786e1, 0x006d0217, 0xfff1e5bb, + 0x55a19a5c, 0x07f2ce9b, 0xf3d0e7c2, 0x05ca1e1f, 0xff6a6fc1, 0xff2d84e5, 0x006bfea4, 0xfff1c7d0, + 0x554a0148, 0x076686fc, 0xf3f9e680, 0x05cf6965, 0xff5ddc1a, 0xff33800e, 0x006af47b, 0xfff1acef, + 0x54f02c56, 0x06dba551, 0xf42378a0, 0x05d42387, 0xff517f86, 0xff3977cb, 0x0069e3d9, 0xfff19508, + 0x54942061, 0x06522e0f, 0xf44d9912, 0x05d84daf, 0xff455a80, 0xff3f6b8f, 0x0068ccfa, 0xfff1800b, + 0x5435e263, 0x05ca258f, 0xf47842c5, 0x05dbe90f, 0xff396d7f, 0xff455acf, 0x0067b01e, 0xfff16de9, + 0x53d57774, 0x0543900d, 0xf4a370ad, 0x05def6e4, 0xff2db8f2, 0xff4b4503, 0x00668d80, 0xfff15e93, + 0x5372e4c6, 0x04be71ab, 0xf4cf1dbf, 0x05e17873, 0xff223d40, 0xff5129a3, 0x0065655d, 0xfff151f9, + 0x530e2fac, 0x043ace6e, 0xf4fb44f4, 0x05e36f0d, 0xff16faca, 0xff57082e, 0x006437f1, 0xfff1480b, + 0x52a75d90, 0x03b8aa40, 0xf527e149, 0x05e4dc08, 0xff0bf1ed, 0xff5ce021, 0x00630577, 0xfff140b9, + 0x523e73fd, 0x033808eb, 0xf554edbd, 0x05e5c0c6, 0xff0122fc, 0xff62b0fd, 0x0061ce2c, 0xfff13bf3, + 0x51d37897, 0x02b8ee22, 0xf5826555, 0x05e61eae, 0xfef68e45, 0xff687a47, 0x00609249, 0xfff139aa, + 0x5166711c, 0x023b5d76, 0xf5b0431a, 0x05e5f733, 0xfeec340f, 0xff6e3b84, 0x005f520a, 0xfff139cd, + 0x50f76368, 0x01bf5a5e, 0xf5de8218, 0x05e54bcd, 0xfee2149b, 0xff73f43d, 0x005e0da8, 0xfff13c4c, + 0x5086556f, 0x0144e834, 0xf60d1d63, 0x05e41dfe, 0xfed83023, 0xff79a3fe, 0x005cc55c, 0xfff14119, + 0x50134d3e, 0x00cc0a36, 0xf63c1012, 0x05e26f4e, 0xfece86db, 0xff7f4a54, 0x005b7961, 0xfff14821, + 0x4f9e50ff, 0x0054c382, 0xf66b5544, 0x05e0414d, 0xfec518f1, 0xff84e6d0, 0x005a29ed, 0xfff15156, + 0x4f2766f2, 0xffdf171b, 0xf69ae81d, 0x05dd9593, 0xfebbe68c, 0xff8a7905, 0x0058d738, 0xfff15ca8, + 0x4eae9571, 0xff6b07e7, 0xf6cac3c7, 0x05da6dbe, 0xfeb2efcd, 0xff900089, 0x0057817b, 0xfff16a07, + 0x4e33e2ee, 0xfef898ae, 0xf6fae373, 0x05d6cb72, 0xfeaa34d0, 0xff957cf4, 0x005628ec, 0xfff17962, + 0x4db755f3, 0xfe87cc1b, 0xf72b425b, 0x05d2b05c, 0xfea1b5a9, 0xff9aede0, 0x0054cdc0, 0xfff18aab, + 0x4d38f520, 0xfe18a4bc, 0xf75bdbbd, 0x05ce1e2d, 0xfe997268, 0xffa052ec, 0x0053702d, 0xfff19dd1, + 0x4cb8c72e, 0xfdab2501, 0xf78caae0, 0x05c9169d, 0xfe916b15, 0xffa5abb8, 0x00521068, 0xfff1b2c5, + 0x4c36d2eb, 0xfd3f4f3d, 0xf7bdab16, 0x05c39b6a, 0xfe899fb2, 0xffaaf7e6, 0x0050aea5, 0xfff1c976, + 0x4bb31f3c, 0xfcd525a5, 0xf7eed7b4, 0x05bdae57, 0xfe82103f, 0xffb0371c, 0x004f4b17, 0xfff1e1d6, + 0x4b2db31a, 0xfc6caa53, 0xf8202c1c, 0x05b7512e, 0xfe7abcb1, 0xffb56902, 0x004de5f1, 0xfff1fbd5, + 0x4aa69594, 0xfc05df40, 0xf851a3b6, 0x05b085bc, 0xfe73a4fb, 0xffba8d44, 0x004c7f66, 0xfff21764, + 0x4a1dcdce, 0xfba0c64b, 0xf88339f5, 0x05a94dd5, 0xfe6cc909, 0xffbfa38d, 0x004b17a6, 0xfff23473, + 0x499362ff, 0xfb3d6133, 0xf8b4ea55, 0x05a1ab52, 0xfe6628c1, 0xffc4ab8f, 0x0049aee3, 0xfff252f3, + 0x49075c72, 0xfadbb19a, 0xf8e6b059, 0x0599a00e, 0xfe5fc405, 0xffc9a4fc, 0x0048454b, 0xfff272d6, + 0x4879c185, 0xfa7bb908, 0xf9188793, 0x05912dea, 0xfe599aaf, 0xffce8f8a, 0x0046db0f, 0xfff2940b, + 0x47ea99a9, 0xfa1d78e3, 0xf94a6b9b, 0x058856cd, 0xfe53ac97, 0xffd36af1, 0x0045705c, 0xfff2b686, + 0x4759ec60, 0xf9c0f276, 0xf97c5815, 0x057f1c9e, 0xfe4df98e, 0xffd836eb, 0x00440561, 0xfff2da36, + 0x46c7c140, 0xf96626f0, 0xf9ae48af, 0x0575814c, 0xfe48815e, 0xffdcf336, 0x00429a4a, 0xfff2ff0d, + 0x46341fed, 0xf90d1761, 0xf9e03924, 0x056b86c6, 0xfe4343d0, 0xffe19f91, 0x00412f43, 0xfff324fd, + 0x459f101d, 0xf8b5c4be, 0xfa122537, 0x05612f00, 0xfe3e40a6, 0xffe63bc0, 0x003fc478, 0xfff34bf9, + 0x45089996, 0xf8602fdc, 0xfa4408ba, 0x05567bf1, 0xfe39779a, 0xffeac787, 0x003e5a12, 0xfff373f0, + 0x4470c42d, 0xf80c5977, 0xfa75df87, 0x054b6f92, 0xfe34e867, 0xffef42af, 0x003cf03d, 0xfff39cd7, + 0x43d797c7, 0xf7ba422b, 0xfaa7a586, 0x05400be1, 0xfe3092bf, 0xfff3ad01, 0x003b871f, 0xfff3c69f, + 0x433d1c56, 0xf769ea78, 0xfad956ab, 0x053452dc, 0xfe2c7650, 0xfff8064b, 0x003a1ee3, 0xfff3f13a, + 0x42a159dc, 0xf71b52c4, 0xfb0aeef6, 0x05284685, 0xfe2892c5, 0xfffc4e5c, 0x0038b7ae, 0xfff41c9c, + 0x42045865, 0xf6ce7b57, 0xfb3c6a73, 0x051be8dd, 0xfe24e7c3, 0x00008507, 0x003751a7, 0xfff448b7, + 0x4166200e, 0xf683645a, 0xfb6dc53c, 0x050f3bec, 0xfe2174ec, 0x0004aa1f, 0x0035ecf4, 0xfff4757e, + 0x40c6b8fd, 0xf63a0ddf, 0xfb9efb77, 0x050241b6, 0xfe1e39da, 0x0008bd7c, 0x003489b9, 0xfff4a2e5, + 0x40262b65, 0xf5f277d9, 0xfbd00956, 0x04f4fc46, 0xfe1b3628, 0x000cbef7, 0x0033281a, 0xfff4d0de, + 0x3f847f83, 0xf5aca21f, 0xfc00eb1b, 0x04e76da3, 0xfe18696a, 0x0010ae6e, 0x0031c83a, 0xfff4ff5d, + 0x3ee1bda2, 0xf5688c6d, 0xfc319d13, 0x04d997d8, 0xfe15d32f, 0x00148bbd, 0x00306a3b, 0xfff52e57, + 0x3e3dee13, 0xf5263665, 0xfc621b9a, 0x04cb7cf2, 0xfe137304, 0x001856c7, 0x002f0e3f, 0xfff55dbf, + 0x3d991932, 0xf4e59f8a, 0xfc926319, 0x04bd1efb, 0xfe114872, 0x001c0f6e, 0x002db466, 0xfff58d89, + 0x3cf34766, 0xf4a6c748, 0xfcc27008, 0x04ae8000, 0xfe0f52fc, 0x001fb599, 0x002c5cd0, 0xfff5bdaa, + 0x3c4c811c, 0xf469aced, 0xfcf23eec, 0x049fa20f, 0xfe0d9224, 0x0023492f, 0x002b079a, 0xfff5ee17, + 0x3ba4cec9, 0xf42e4faf, 0xfd21cc59, 0x04908733, 0xfe0c0567, 0x0026ca1c, 0x0029b4e4, 0xfff61ec5, + 0x3afc38eb, 0xf3f4aea6, 0xfd5114f0, 0x0481317a, 0xfe0aac3f, 0x002a384c, 0x002864c9, 0xfff64fa8, + 0x3a52c805, 0xf3bcc8d3, 0xfd801564, 0x0471a2ef, 0xfe098622, 0x002d93ae, 0x00271766, 0xfff680b5, + 0x39a884a1, 0xf3869d1a, 0xfdaeca73, 0x0461dda0, 0xfe089283, 0x0030dc34, 0x0025ccd7, 0xfff6b1e4, + 0x38fd774e, 0xf3522a49, 0xfddd30eb, 0x0451e396, 0xfe07d0d3, 0x003411d2, 0x00248535, 0xfff6e329, + 0x3851a8a2, 0xf31f6f0f, 0xfe0b45aa, 0x0441b6dd, 0xfe07407d, 0x0037347d, 0x0023409a, 0xfff7147a, + 0x37a52135, 0xf2ee6a07, 0xfe39059b, 0x0431597d, 0xfe06e0eb, 0x003a442e, 0x0021ff1f, 0xfff745cd, + 0x36f7e9a4, 0xf2bf19ae, 0xfe666dbc, 0x0420cd80, 0xfe06b184, 0x003d40e0, 0x0020c0dc, 0xfff7771a, + 0x364a0a90, 0xf2917c6d, 0xfe937b15, 0x041014eb, 0xfe06b1ac, 0x00402a8e, 0x001f85e6, 0xfff7a857, + 0x359b8c9d, 0xf265908f, 0xfec02ac2, 0x03ff31c3, 0xfe06e0c4, 0x00430137, 0x001e4e56, 0xfff7d97a, + 0x34ec786f, 0xf23b544b, 0xfeec79ec, 0x03ee260d, 0xfe073e2a, 0x0045c4dd, 0x001d1a3f, 0xfff80a7c, + 0x343cd6af, 0xf212c5be, 0xff1865cd, 0x03dcf3ca, 0xfe07c93a, 0x00487582, 0x001be9b7, 0xfff83b52, + 0x338cb004, 0xf1ebe2ec, 0xff43ebac, 0x03cb9cf9, 0xfe08814e, 0x004b132b, 0x001abcd0, 0xfff86bf6, + 0x32dc0d17, 0xf1c6a9c3, 0xff6f08e4, 0x03ba2398, 0xfe0965bc, 0x004d9dde, 0x0019939d, 0xfff89c60, + 0x322af693, 0xf1a3181a, 0xff99badb, 0x03a889a1, 0xfe0a75da, 0x005015a5, 0x00186e31, 0xfff8cc86, + 0x3179751f, 0xf1812bb0, 0xffc3ff0c, 0x0396d10c, 0xfe0bb0f9, 0x00527a8a, 0x00174c9c, 0xfff8fc62, + 0x30c79163, 0xf160e22d, 0xffedd2fd, 0x0384fbd1, 0xfe0d166b, 0x0054cc9a, 0x00162eef, 0xfff92bec, + 0x30155404, 0xf1423924, 0x00173447, 0x03730be0, 0xfe0ea57e, 0x00570be4, 0x00151538, 0xfff95b1e, + 0x2f62c5a7, 0xf1252e0f, 0x00402092, 0x0361032a, 0xfe105d7e, 0x00593877, 0x0013ff88, 0xfff989ef, + 0x2eafeeed, 0xf109be56, 0x00689598, 0x034ee39b, 0xfe123db6, 0x005b5267, 0x0012edea, 0xfff9b85b, + 0x2dfcd873, 0xf0efe748, 0x0090911f, 0x033caf1d, 0xfe144570, 0x005d59c6, 0x0011e06d, 0xfff9e65a, + 0x2d498ad3, 0xf0d7a622, 0x00b81102, 0x032a6796, 0xfe1673f2, 0x005f4eac, 0x0010d71d, 0xfffa13e5, + 0x2c960ea3, 0xf0c0f808, 0x00df1328, 0x03180ee7, 0xfe18c884, 0x0061312e, 0x000fd205, 0xfffa40f8, + 0x2be26c73, 0xf0abda0e, 0x0105958c, 0x0305a6f0, 0xfe1b4268, 0x00630167, 0x000ed130, 0xfffa6d8d, + 0x2b2eaccf, 0xf0984931, 0x012b9635, 0x02f3318a, 0xfe1de0e2, 0x0064bf71, 0x000dd4a7, 0xfffa999d, + 0x2a7ad83c, 0xf086425a, 0x0151133e, 0x02e0b08d, 0xfe20a335, 0x00666b68, 0x000cdc74, 0xfffac525, + 0x29c6f738, 0xf075c260, 0x01760ad1, 0x02ce25ca, 0xfe2388a1, 0x0068056b, 0x000be89f, 0xfffaf01e, + 0x2913123c, 0xf066c606, 0x019a7b27, 0x02bb9310, 0xfe269065, 0x00698d98, 0x000af931, 0xfffb1a84, + 0x285f31b7, 0xf05949fb, 0x01be628c, 0x02a8fa2a, 0xfe29b9c1, 0x006b0411, 0x000a0e2f, 0xfffb4453, + 0x27ab5e12, 0xf04d4ade, 0x01e1bf58, 0x02965cdb, 0xfe2d03f2, 0x006c68f8, 0x000927a0, 0xfffb6d86, + 0x26f79fab, 0xf042c539, 0x02048ff8, 0x0283bce6, 0xfe306e35, 0x006dbc71, 0x00084589, 0xfffb961a, + 0x2643feda, 0xf039b587, 0x0226d2e6, 0x02711c05, 0xfe33f7c7, 0x006efea0, 0x000767f0, 0xfffbbe09, + 0x259083eb, 0xf032182f, 0x024886ad, 0x025e7bf0, 0xfe379fe3, 0x00702fae, 0x00068ed8, 0xfffbe552, + 0x24dd3721, 0xf02be98a, 0x0269a9e9, 0x024bde5a, 0xfe3b65c4, 0x00714fc0, 0x0005ba46, 0xfffc0bef, + 0x242a20b3, 0xf02725dc, 0x028a3b44, 0x023944ee, 0xfe3f48a5, 0x00725f02, 0x0004ea3a, 0xfffc31df, + 0x237748cf, 0xf023c95d, 0x02aa397b, 0x0226b156, 0xfe4347c0, 0x00735d9c, 0x00041eb9, 0xfffc571e, + 0x22c4b795, 0xf021d031, 0x02c9a359, 0x02142533, 0xfe476250, 0x00744bba, 0x000357c2, 0xfffc7ba9, + 0x2212751a, 0xf0213671, 0x02e877b9, 0x0201a223, 0xfe4b978e, 0x0075298a, 0x00029558, 0xfffc9f7e, + 0x21608968, 0xf021f823, 0x0306b586, 0x01ef29be, 0xfe4fe6b3, 0x0075f739, 0x0001d779, 0xfffcc29a, + 0x20aefc79, 0xf0241140, 0x03245bbc, 0x01dcbd96, 0xfe544efb, 0x0076b4f5, 0x00011e26, 0xfffce4fc, + 0x1ffdd63b, 0xf0277db1, 0x03416966, 0x01ca5f37, 0xfe58cf9d, 0x007762f0, 0x0000695e, 0xfffd06a1, + 0x1f4d1e8e, 0xf02c3953, 0x035ddd9e, 0x01b81028, 0xfe5d67d4, 0x0078015a, 0xffffb91f, 0xfffd2787, + 0x1e9cdd43, 0xf0323ff5, 0x0379b790, 0x01a5d1ea, 0xfe6216db, 0x00789065, 0xffff0d66, 0xfffd47ae, + 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, 0xfffd6713, diff --git a/services/audioflinger/AudioResamplerSincUp.h b/services/audioflinger/AudioResamplerSincUp.h new file mode 100644 index 0000000..fd5367e --- /dev/null +++ b/services/audioflinger/AudioResamplerSincUp.h @@ -0,0 +1,131 @@ +// cmd-line: fir -l 7 -s48000 -c 20478 + + 0x6d374bc7, 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, + 0x6d35278a, 0x103e8192, 0xf36b9dfd, 0x07bdfaa5, 0xfc5102d0, 0x013d618d, 0xffc663b9, 0xfffd9592, + 0x6d2ebafe, 0x0f62811a, 0xf3b3d8ac, 0x07a9f399, 0xfc51d9a6, 0x0140bea5, 0xffc41212, 0xfffe631e, + 0x6d24069d, 0x0e8875ad, 0xf3fcb43e, 0x07953976, 0xfc53216f, 0x0143e67c, 0xffc1d373, 0xffff2b9f, + 0x6d150b35, 0x0db06a89, 0xf4462690, 0x077fd0ac, 0xfc54d8ae, 0x0146d965, 0xffbfa7d9, 0xffffef10, + 0x6d01c9e3, 0x0cda6ab5, 0xf4902587, 0x0769bdaf, 0xfc56fdda, 0x014997bb, 0xffbd8f40, 0x0000ad6e, + 0x6cea4418, 0x0c0680fe, 0xf4daa718, 0x07530501, 0xfc598f60, 0x014c21db, 0xffbb89a1, 0x000166b6, + 0x6cce7b97, 0x0b34b7f5, 0xf525a143, 0x073bab28, 0xfc5c8ba5, 0x014e782a, 0xffb996f3, 0x00021ae5, + 0x6cae7272, 0x0a6519f4, 0xf5710a17, 0x0723b4b4, 0xfc5ff105, 0x01509b14, 0xffb7b728, 0x0002c9fd, + 0x6c8a2b0f, 0x0997b116, 0xf5bcd7b1, 0x070b2639, 0xfc63bdd3, 0x01528b08, 0xffb5ea31, 0x000373fb, + 0x6c61a823, 0x08cc873c, 0xf609003f, 0x06f20453, 0xfc67f05a, 0x0154487b, 0xffb42ffc, 0x000418e2, + 0x6c34ecb5, 0x0803a60a, 0xf6557a00, 0x06d853a2, 0xfc6c86dd, 0x0155d3e8, 0xffb28876, 0x0004b8b3, + 0x6c03fc1c, 0x073d16e7, 0xf6a23b44, 0x06be18cd, 0xfc717f97, 0x01572dcf, 0xffb0f388, 0x00055371, + 0x6bced9ff, 0x0678e2fc, 0xf6ef3a6e, 0x06a3587e, 0xfc76d8bc, 0x015856b6, 0xffaf7118, 0x0005e921, + 0x6b958a54, 0x05b71332, 0xf73c6df4, 0x06881761, 0xfc7c9079, 0x01594f25, 0xffae010b, 0x000679c5, + 0x6b581163, 0x04f7b037, 0xf789cc61, 0x066c5a27, 0xfc82a4f4, 0x015a17ab, 0xffaca344, 0x00070564, + 0x6b1673c1, 0x043ac276, 0xf7d74c53, 0x06502583, 0xfc89144d, 0x015ab0db, 0xffab57a1, 0x00078c04, + 0x6ad0b652, 0x0380521c, 0xf824e480, 0x06337e2a, 0xfc8fdc9f, 0x015b1b4e, 0xffaa1e02, 0x00080dab, + 0x6a86de48, 0x02c86715, 0xf8728bb3, 0x061668d2, 0xfc96fbfc, 0x015b579e, 0xffa8f641, 0x00088a62, + 0x6a38f123, 0x0213090c, 0xf8c038d0, 0x05f8ea30, 0xfc9e7074, 0x015b666c, 0xffa7e039, 0x00090230, + 0x69e6f4b1, 0x01603f6e, 0xf90de2d1, 0x05db06fc, 0xfca63810, 0x015b485b, 0xffa6dbc0, 0x0009751e, + 0x6990ef0b, 0x00b01162, 0xf95b80cb, 0x05bcc3ed, 0xfcae50d6, 0x015afe14, 0xffa5e8ad, 0x0009e337, + 0x6936e697, 0x000285d0, 0xf9a909ea, 0x059e25b5, 0xfcb6b8c4, 0x015a8843, 0xffa506d2, 0x000a4c85, + 0x68d8e206, 0xff57a35e, 0xf9f67577, 0x057f310a, 0xfcbf6dd8, 0x0159e796, 0xffa43603, 0x000ab112, + 0x6876e855, 0xfeaf706f, 0xfa43bad2, 0x055fea9d, 0xfcc86e09, 0x01591cc0, 0xffa3760e, 0x000b10ec, + 0x681100c9, 0xfe09f323, 0xfa90d17b, 0x0540571a, 0xfcd1b74c, 0x01582878, 0xffa2c6c2, 0x000b6c1d, + 0x67a732f4, 0xfd673159, 0xfaddb10c, 0x05207b2f, 0xfcdb4793, 0x01570b77, 0xffa227ec, 0x000bc2b3, + 0x673986ac, 0xfcc730aa, 0xfb2a513b, 0x05005b82, 0xfce51ccb, 0x0155c678, 0xffa19957, 0x000c14bb, + 0x66c80413, 0xfc29f670, 0xfb76a9dd, 0x04dffcb6, 0xfcef34e1, 0x01545a3c, 0xffa11acb, 0x000c6244, + 0x6652b392, 0xfb8f87bd, 0xfbc2b2e4, 0x04bf6369, 0xfcf98dbe, 0x0152c783, 0xffa0ac11, 0x000cab5c, + 0x65d99dd5, 0xfaf7e963, 0xfc0e6461, 0x049e9433, 0xfd04254a, 0x01510f13, 0xffa04cf0, 0x000cf012, + 0x655ccbd3, 0xfa631fef, 0xfc59b685, 0x047d93a8, 0xfd0ef969, 0x014f31b2, 0xff9ffd2c, 0x000d3075, + 0x64dc46c3, 0xf9d12fab, 0xfca4a19f, 0x045c6654, 0xfd1a0801, 0x014d3029, 0xff9fbc89, 0x000d6c97, + 0x64581823, 0xf9421c9d, 0xfcef1e20, 0x043b10bd, 0xfd254ef4, 0x014b0b45, 0xff9f8ac9, 0x000da486, + 0x63d049b4, 0xf8b5ea87, 0xfd392498, 0x04199760, 0xfd30cc24, 0x0148c3d2, 0xff9f67ae, 0x000dd854, + 0x6344e578, 0xf82c9ce7, 0xfd82adba, 0x03f7feb4, 0xfd3c7d73, 0x01465a9f, 0xff9f52f7, 0x000e0812, + 0x62b5f5b2, 0xf7a636fa, 0xfdcbb25a, 0x03d64b27, 0xfd4860c2, 0x0143d07f, 0xff9f4c65, 0x000e33d3, + 0x622384e8, 0xf722bbb5, 0xfe142b6e, 0x03b4811d, 0xfd5473f3, 0x01412643, 0xff9f53b4, 0x000e5ba7, + 0x618d9ddc, 0xf6a22dcf, 0xfe5c120f, 0x0392a4f4, 0xfd60b4e7, 0x013e5cc0, 0xff9f68a1, 0x000e7fa1, + 0x60f44b91, 0xf6248fb6, 0xfea35f79, 0x0370bafc, 0xfd6d2180, 0x013b74ca, 0xff9f8ae9, 0x000e9fd5, + 0x60579947, 0xf5a9e398, 0xfeea0d0c, 0x034ec77f, 0xfd79b7a1, 0x01386f3a, 0xff9fba47, 0x000ebc54, + 0x5fb79278, 0xf5322b61, 0xff30144a, 0x032ccebb, 0xfd86752e, 0x01354ce7, 0xff9ff674, 0x000ed533, + 0x5f1442dc, 0xf4bd68b6, 0xff756edc, 0x030ad4e1, 0xfd93580d, 0x01320ea9, 0xffa03f2b, 0x000eea84, + 0x5e6db665, 0xf44b9cfe, 0xffba168d, 0x02e8de19, 0xfda05e23, 0x012eb55a, 0xffa09425, 0x000efc5c, + 0x5dc3f93c, 0xf3dcc959, 0xfffe054e, 0x02c6ee7f, 0xfdad855b, 0x012b41d3, 0xffa0f519, 0x000f0ace, + 0x5d1717c4, 0xf370eea9, 0x00413536, 0x02a50a22, 0xfdbacb9e, 0x0127b4f1, 0xffa161bf, 0x000f15ef, + 0x5c671e96, 0xf3080d8c, 0x0083a081, 0x02833506, 0xfdc82edb, 0x01240f8e, 0xffa1d9cf, 0x000f1dd2, + 0x5bb41a80, 0xf2a2265e, 0x00c54190, 0x02617321, 0xfdd5ad01, 0x01205285, 0xffa25cfe, 0x000f228d, + 0x5afe1886, 0xf23f393b, 0x010612eb, 0x023fc85c, 0xfde34403, 0x011c7eb2, 0xffa2eb04, 0x000f2434, + 0x5a4525df, 0xf1df45fd, 0x01460f41, 0x021e3891, 0xfdf0f1d6, 0x011894f0, 0xffa38395, 0x000f22dc, + 0x59894ff3, 0xf1824c3e, 0x01853165, 0x01fcc78f, 0xfdfeb475, 0x0114961b, 0xffa42668, 0x000f1e99, + 0x58caa45b, 0xf1284b58, 0x01c37452, 0x01db7914, 0xfe0c89db, 0x0110830f, 0xffa4d332, 0x000f1781, + 0x580930e1, 0xf0d14267, 0x0200d32c, 0x01ba50d2, 0xfe1a7009, 0x010c5ca6, 0xffa589a6, 0x000f0da8, + 0x5745037c, 0xf07d3043, 0x023d493c, 0x0199526b, 0xfe286505, 0x010823ba, 0xffa6497c, 0x000f0125, + 0x567e2a51, 0xf02c138a, 0x0278d1f2, 0x01788170, 0xfe3666d5, 0x0103d927, 0xffa71266, 0x000ef20b, + 0x55b4b3af, 0xefddea9a, 0x02b368e6, 0x0157e166, 0xfe447389, 0x00ff7dc4, 0xffa7e41a, 0x000ee070, + 0x54e8ae13, 0xef92b393, 0x02ed09d7, 0x013775bf, 0xfe528931, 0x00fb126b, 0xffa8be4c, 0x000ecc69, + 0x541a281e, 0xef4a6c58, 0x0325b0ad, 0x011741df, 0xfe60a5e5, 0x00f697f3, 0xffa9a0b1, 0x000eb60b, + 0x5349309e, 0xef051290, 0x035d5977, 0x00f7491a, 0xfe6ec7c0, 0x00f20f32, 0xffaa8afe, 0x000e9d6b, + 0x5275d684, 0xeec2a3a3, 0x0394006a, 0x00d78eb3, 0xfe7cece2, 0x00ed78ff, 0xffab7ce7, 0x000e829e, + 0x51a028e8, 0xee831cc3, 0x03c9a1e5, 0x00b815da, 0xfe8b1373, 0x00e8d62d, 0xffac7621, 0x000e65ba, + 0x50c83704, 0xee467ae1, 0x03fe3a6f, 0x0098e1b3, 0xfe99399f, 0x00e4278f, 0xffad7662, 0x000e46d3, + 0x4fee1037, 0xee0cbab9, 0x0431c6b5, 0x0079f54c, 0xfea75d97, 0x00df6df7, 0xffae7d5f, 0x000e25fd, + 0x4f11c3fe, 0xedd5d8ca, 0x0464438c, 0x005b53a4, 0xfeb57d92, 0x00daaa34, 0xffaf8acd, 0x000e034f, + 0x4e3361f7, 0xeda1d15c, 0x0495adf2, 0x003cffa9, 0xfec397cf, 0x00d5dd16, 0xffb09e63, 0x000ddedb, + 0x4d52f9df, 0xed70a07d, 0x04c6030d, 0x001efc35, 0xfed1aa92, 0x00d10769, 0xffb1b7d8, 0x000db8b7, + 0x4c709b8e, 0xed424205, 0x04f54029, 0x00014c12, 0xfedfb425, 0x00cc29f7, 0xffb2d6e1, 0x000d90f6, + 0x4b8c56f8, 0xed16b196, 0x052362ba, 0xffe3f1f7, 0xfeedb2da, 0x00c7458a, 0xffb3fb37, 0x000d67ae, + 0x4aa63c2c, 0xecedea99, 0x0550685d, 0xffc6f08a, 0xfefba508, 0x00c25ae8, 0xffb52490, 0x000d3cf1, + 0x49be5b50, 0xecc7e845, 0x057c4ed4, 0xffaa4a5d, 0xff09890f, 0x00bd6ad7, 0xffb652a7, 0x000d10d5, + 0x48d4c4a2, 0xeca4a59b, 0x05a7140b, 0xff8e01f1, 0xff175d53, 0x00b87619, 0xffb78533, 0x000ce36b, + 0x47e98874, 0xec841d68, 0x05d0b612, 0xff7219b3, 0xff252042, 0x00b37d70, 0xffb8bbed, 0x000cb4c8, + 0x46fcb72d, 0xec664a48, 0x05f93324, 0xff5693fe, 0xff32d04f, 0x00ae8198, 0xffb9f691, 0x000c84ff, + 0x460e6148, 0xec4b26a2, 0x0620899e, 0xff3b731b, 0xff406bf8, 0x00a9834e, 0xffbb34d8, 0x000c5422, + 0x451e9750, 0xec32acb0, 0x0646b808, 0xff20b93e, 0xff4df1be, 0x00a4834c, 0xffbc767f, 0x000c2245, + 0x442d69de, 0xec1cd677, 0x066bbd0d, 0xff066889, 0xff5b602c, 0x009f8249, 0xffbdbb42, 0x000bef79, + 0x433ae99c, 0xec099dcf, 0x068f9781, 0xfeec830d, 0xff68b5d5, 0x009a80f8, 0xffbf02dd, 0x000bbbd2, + 0x4247273f, 0xebf8fc64, 0x06b2465b, 0xfed30ac5, 0xff75f153, 0x0095800c, 0xffc04d0f, 0x000b8760, + 0x41523389, 0xebeaebaf, 0x06d3c8bb, 0xfeba0199, 0xff831148, 0x00908034, 0xffc19996, 0x000b5235, + 0x405c1f43, 0xebdf6500, 0x06f41de3, 0xfea16960, 0xff90145e, 0x008b821b, 0xffc2e832, 0x000b1c64, + 0x3f64fb40, 0xebd6617b, 0x0713453d, 0xfe8943dc, 0xff9cf947, 0x0086866b, 0xffc438a3, 0x000ae5fc, + 0x3e6cd85b, 0xebcfda19, 0x07313e56, 0xfe7192bd, 0xffa9bebe, 0x00818dcb, 0xffc58aaa, 0x000aaf0f, + 0x3d73c772, 0xebcbc7a7, 0x074e08e0, 0xfe5a579d, 0xffb66386, 0x007c98de, 0xffc6de09, 0x000a77ac, + 0x3c79d968, 0xebca22cc, 0x0769a4b2, 0xfe439407, 0xffc2e669, 0x0077a845, 0xffc83285, 0x000a3fe5, + 0x3b7f1f23, 0xebcae405, 0x078411c7, 0xfe2d496f, 0xffcf463a, 0x0072bc9d, 0xffc987e0, 0x000a07c9, + 0x3a83a989, 0xebce03aa, 0x079d503b, 0xfe177937, 0xffdb81d6, 0x006dd680, 0xffcadde1, 0x0009cf67, + 0x3987897f, 0xebd379eb, 0x07b56051, 0xfe0224b0, 0xffe79820, 0x0068f687, 0xffcc344c, 0x000996ce, + 0x388acfe9, 0xebdb3ed5, 0x07cc426c, 0xfded4d13, 0xfff38806, 0x00641d44, 0xffcd8aeb, 0x00095e0e, + 0x378d8da8, 0xebe54a4f, 0x07e1f712, 0xfdd8f38b, 0xffff507b, 0x005f4b4a, 0xffcee183, 0x00092535, + 0x368fd397, 0xebf1941f, 0x07f67eec, 0xfdc5192d, 0x000af07f, 0x005a8125, 0xffd037e0, 0x0008ec50, + 0x3591b28b, 0xec0013e8, 0x0809dac3, 0xfdb1befc, 0x00166718, 0x0055bf60, 0xffd18dcc, 0x0008b36e, + 0x34933b50, 0xec10c12c, 0x081c0b84, 0xfd9ee5e7, 0x0021b355, 0x00510682, 0xffd2e311, 0x00087a9c, + 0x33947eab, 0xec23934f, 0x082d1239, 0xfd8c8ecc, 0x002cd44d, 0x004c570f, 0xffd4377d, 0x000841e8, + 0x32958d55, 0xec388194, 0x083cf010, 0xfd7aba74, 0x0037c922, 0x0047b186, 0xffd58ade, 0x0008095d, + 0x319677fa, 0xec4f8322, 0x084ba654, 0xfd696998, 0x004290fc, 0x00431666, 0xffd6dd02, 0x0007d108, + 0x30974f3b, 0xec688f02, 0x08593671, 0xfd589cdc, 0x004d2b0e, 0x003e8628, 0xffd82dba, 0x000798f5, + 0x2f9823a8, 0xec839c22, 0x0865a1f1, 0xfd4854d3, 0x00579691, 0x003a0141, 0xffd97cd6, 0x00076130, + 0x2e9905c1, 0xeca0a156, 0x0870ea7e, 0xfd3891fd, 0x0061d2ca, 0x00358824, 0xffdaca2a, 0x000729c4, + 0x2d9a05f4, 0xecbf9558, 0x087b11de, 0xfd2954c8, 0x006bdf05, 0x00311b41, 0xffdc1588, 0x0006f2bb, + 0x2c9b349e, 0xece06ecb, 0x088419f6, 0xfd1a9d91, 0x0075ba95, 0x002cbb03, 0xffdd5ec6, 0x0006bc21, + 0x2b9ca203, 0xed032439, 0x088c04c8, 0xfd0c6ca2, 0x007f64da, 0x002867d2, 0xffdea5bb, 0x000685ff, + 0x2a9e5e57, 0xed27ac16, 0x0892d470, 0xfcfec233, 0x0088dd38, 0x00242213, 0xffdfea3c, 0x0006505f, + 0x29a079b2, 0xed4dfcc2, 0x08988b2a, 0xfcf19e6b, 0x0092231e, 0x001fea27, 0xffe12c22, 0x00061b4b, + 0x28a30416, 0xed760c88, 0x089d2b4a, 0xfce50161, 0x009b3605, 0x001bc06b, 0xffe26b48, 0x0005e6cb, + 0x27a60d6a, 0xed9fd1a2, 0x08a0b740, 0xfcd8eb17, 0x00a4156b, 0x0017a53b, 0xffe3a788, 0x0005b2e8, + 0x26a9a57b, 0xedcb4237, 0x08a33196, 0xfccd5b82, 0x00acc0da, 0x001398ec, 0xffe4e0bf, 0x00057faa, + 0x25addbf9, 0xedf8545b, 0x08a49cf0, 0xfcc25285, 0x00b537e1, 0x000f9bd2, 0xffe616c8, 0x00054d1a, + 0x24b2c075, 0xee26fe17, 0x08a4fc0d, 0xfcb7cff0, 0x00bd7a1c, 0x000bae3c, 0xffe74984, 0x00051b3e, + 0x23b86263, 0xee573562, 0x08a451c0, 0xfcadd386, 0x00c5872a, 0x0007d075, 0xffe878d3, 0x0004ea1d, + 0x22bed116, 0xee88f026, 0x08a2a0f8, 0xfca45cf7, 0x00cd5eb7, 0x000402c8, 0xffe9a494, 0x0004b9c0, + 0x21c61bc0, 0xeebc2444, 0x089fecbb, 0xfc9b6be5, 0x00d50075, 0x00004579, 0xffeaccaa, 0x00048a2b, + 0x20ce516f, 0xeef0c78d, 0x089c3824, 0xfc92ffe1, 0x00dc6c1e, 0xfffc98c9, 0xffebf0fa, 0x00045b65, + 0x1fd7810f, 0xef26cfca, 0x08978666, 0xfc8b186d, 0x00e3a175, 0xfff8fcf7, 0xffed1166, 0x00042d74, + 0x1ee1b965, 0xef5e32bd, 0x0891dac8, 0xfc83b4fc, 0x00eaa045, 0xfff5723d, 0xffee2dd7, 0x0004005e, + 0x1ded0911, 0xef96e61c, 0x088b38a9, 0xfc7cd4f0, 0x00f16861, 0xfff1f8d2, 0xffef4632, 0x0003d426, + 0x1cf97e8b, 0xefd0df9a, 0x0883a378, 0xfc76779e, 0x00f7f9a3, 0xffee90eb, 0xfff05a60, 0x0003a8d2, + 0x1c072823, 0xf00c14e1, 0x087b1ebc, 0xfc709c4d, 0x00fe53ef, 0xffeb3ab8, 0xfff16a4a, 0x00037e65, + 0x1b1613ff, 0xf0487b98, 0x0871ae0d, 0xfc6b4233, 0x0104772e, 0xffe7f666, 0xfff275db, 0x000354e5, + 0x1a26501b, 0xf0860962, 0x08675516, 0xfc66687a, 0x010a6353, 0xffe4c41e, 0xfff37d00, 0x00032c54, + 0x1937ea47, 0xf0c4b3e0, 0x085c1794, 0xfc620e3d, 0x01101858, 0xffe1a408, 0xfff47fa5, 0x000304b7, + 0x184af025, 0xf10470b0, 0x084ff957, 0xfc5e328c, 0x0115963d, 0xffde9646, 0xfff57db8, 0x0002de0e, + 0x175f6f2b, 0xf1453571, 0x0842fe3d, 0xfc5ad465, 0x011add0b, 0xffdb9af8, 0xfff67729, 0x0002b85f, + 0x1675749e, 0xf186f7c0, 0x08352a35, 0xfc57f2be, 0x011fecd3, 0xffd8b23b, 0xfff76be9, 0x000293aa, + 0x158d0d95, 0xf1c9ad40, 0x0826813e, 0xfc558c7c, 0x0124c5ab, 0xffd5dc28, 0xfff85be8, 0x00026ff2, + 0x14a646f6, 0xf20d4b92, 0x08170767, 0xfc53a07b, 0x012967b1, 0xffd318d6, 0xfff9471b, 0x00024d39, + 0x13c12d73, 0xf251c85d, 0x0806c0cb, 0xfc522d88, 0x012dd30a, 0xffd06858, 0xfffa2d74, 0x00022b7f, + 0x12ddcd8f, 0xf297194d, 0x07f5b193, 0xfc513266, 0x013207e4, 0xffcdcabe, 0xfffb0ee9, 0x00020ac7, + 0x11fc3395, 0xf2dd3411, 0x07e3ddf7, 0xfc50adcc, 0x01360670, 0xffcb4014, 0xfffbeb70, 0x0001eb10, + 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, 0x0001cc5c, diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h index 6a8aeb1..845697a 100644 --- a/services/audioflinger/Configuration.h +++ b/services/audioflinger/Configuration.h @@ -29,9 +29,8 @@ // uncomment to display CPU load adjusted for CPU frequency //#define CPU_FREQUENCY_STATISTICS -// uncomment to enable fast mixer to take performance samples for later statistical analysis -#define FAST_MIXER_STATISTICS -// FIXME rename to FAST_THREAD_STATISTICS +// uncomment to enable fast threads to take performance samples for later statistical analysis +#define FAST_THREAD_STATISTICS // uncomment for debugging timing problems related to StateQueue::push() //#define STATE_QUEUE_DUMP diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index bcaf8ae..8bccb47 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -1953,4 +1953,4 @@ void AudioFlinger::EffectChain::setThread(const sp<ThreadBase>& thread) } } -}; // namespace android +} // namespace android diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp index 0c9b976..9e7e8a4 100644 --- a/services/audioflinger/FastCapture.cpp +++ b/services/audioflinger/FastCapture.cpp @@ -29,18 +29,18 @@ namespace android { -/*static*/ const FastCaptureState FastCapture::initial; +/*static*/ const FastCaptureState FastCapture::sInitial; FastCapture::FastCapture() : FastThread(), - inputSource(NULL), inputSourceGen(0), pipeSink(NULL), pipeSinkGen(0), - readBuffer(NULL), readBufferState(-1), format(Format_Invalid), sampleRate(0), - // dummyDumpState - totalNativeFramesRead(0) + mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0), + mReadBuffer(NULL), mReadBufferState(-1), mFormat(Format_Invalid), mSampleRate(0), + // mDummyDumpState + mTotalNativeFramesRead(0) { - previous = &initial; - current = &initial; + mPrevious = &sInitial; + mCurrent = &sInitial; - mDummyDumpState = &dummyDumpState; + mDummyDumpState = &mDummyFastCaptureDumpState; } FastCapture::~FastCapture() @@ -63,13 +63,13 @@ void FastCapture::setLog(NBLog::Writer *logWriter __unused) void FastCapture::onIdle() { - preIdle = *(const FastCaptureState *)current; - current = &preIdle; + mPreIdle = *(const FastCaptureState *)mCurrent; + mCurrent = &mPreIdle; } void FastCapture::onExit() { - delete[] readBuffer; + free(mReadBuffer); } bool FastCapture::isSubClassCommand(FastThreadState::Command command) @@ -86,67 +86,67 @@ bool FastCapture::isSubClassCommand(FastThreadState::Command command) void FastCapture::onStateChange() { - const FastCaptureState * const current = (const FastCaptureState *) this->current; - const FastCaptureState * const previous = (const FastCaptureState *) this->previous; - FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState; + const FastCaptureState * const current = (const FastCaptureState *) mCurrent; + const FastCaptureState * const previous = (const FastCaptureState *) mPrevious; + FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; const size_t frameCount = current->mFrameCount; bool eitherChanged = false; // check for change in input HAL configuration - NBAIO_Format previousFormat = format; - if (current->mInputSourceGen != inputSourceGen) { - inputSource = current->mInputSource; - inputSourceGen = current->mInputSourceGen; - if (inputSource == NULL) { - format = Format_Invalid; - sampleRate = 0; + NBAIO_Format previousFormat = mFormat; + if (current->mInputSourceGen != mInputSourceGen) { + mInputSource = current->mInputSource; + mInputSourceGen = current->mInputSourceGen; + if (mInputSource == NULL) { + mFormat = Format_Invalid; + mSampleRate = 0; } else { - format = inputSource->format(); - sampleRate = Format_sampleRate(format); - unsigned channelCount = Format_channelCount(format); + mFormat = mInputSource->format(); + mSampleRate = Format_sampleRate(mFormat); + unsigned channelCount = Format_channelCount(mFormat); ALOG_ASSERT(channelCount == 1 || channelCount == 2); } - dumpState->mSampleRate = sampleRate; + dumpState->mSampleRate = mSampleRate; eitherChanged = true; } // check for change in pipe - if (current->mPipeSinkGen != pipeSinkGen) { - pipeSink = current->mPipeSink; - pipeSinkGen = current->mPipeSinkGen; + if (current->mPipeSinkGen != mPipeSinkGen) { + mPipeSink = current->mPipeSink; + mPipeSinkGen = current->mPipeSinkGen; eitherChanged = true; } // input source and pipe sink must be compatible - if (eitherChanged && inputSource != NULL && pipeSink != NULL) { - ALOG_ASSERT(Format_isEqual(format, pipeSink->format())); + if (eitherChanged && mInputSource != NULL && mPipeSink != NULL) { + ALOG_ASSERT(Format_isEqual(mFormat, mPipeSink->format())); } - if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { - // FIXME to avoid priority inversion, don't delete here - delete[] readBuffer; - readBuffer = NULL; - if (frameCount > 0 && sampleRate > 0) { + if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) { + // FIXME to avoid priority inversion, don't free here + free(mReadBuffer); + mReadBuffer = NULL; + if (frameCount > 0 && mSampleRate > 0) { // FIXME new may block for unbounded time at internal mutex of the heap // implementation; it would be better to have normal capture thread allocate for // us to avoid blocking here and to prevent possible priority inversion - unsigned channelCount = Format_channelCount(format); - // FIXME frameSize - readBuffer = new short[frameCount * channelCount]; - periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 - underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 - overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 - forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 - warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50 + (void)posix_memalign(&mReadBuffer, 32, frameCount * Format_frameSize(mFormat)); + mPeriodNs = (frameCount * 1000000000LL) / mSampleRate; // 1.00 + mUnderrunNs = (frameCount * 1750000000LL) / mSampleRate; // 1.75 + mOverrunNs = (frameCount * 500000000LL) / mSampleRate; // 0.50 + mForceNs = (frameCount * 950000000LL) / mSampleRate; // 0.95 + mWarmupNsMin = (frameCount * 750000000LL) / mSampleRate; // 0.75 + mWarmupNsMax = (frameCount * 1250000000LL) / mSampleRate; // 1.25 } else { - periodNs = 0; - underrunNs = 0; - overrunNs = 0; - forceNs = 0; - warmupNs = 0; + mPeriodNs = 0; + mUnderrunNs = 0; + mOverrunNs = 0; + mForceNs = 0; + mWarmupNsMin = 0; + mWarmupNsMax = LONG_MAX; } - readBufferState = -1; + mReadBufferState = -1; dumpState->mFrameCount = frameCount; } @@ -154,44 +154,43 @@ void FastCapture::onStateChange() void FastCapture::onWork() { - const FastCaptureState * const current = (const FastCaptureState *) this->current; - FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState; - const FastCaptureState::Command command = this->command; + const FastCaptureState * const current = (const FastCaptureState *) mCurrent; + FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; + const FastCaptureState::Command command = mCommand; const size_t frameCount = current->mFrameCount; if ((command & FastCaptureState::READ) /*&& isWarm*/) { - ALOG_ASSERT(inputSource != NULL); - ALOG_ASSERT(readBuffer != NULL); + ALOG_ASSERT(mInputSource != NULL); + ALOG_ASSERT(mReadBuffer != NULL); dumpState->mReadSequence++; ATRACE_BEGIN("read"); - ssize_t framesRead = inputSource->read(readBuffer, frameCount, + ssize_t framesRead = mInputSource->read(mReadBuffer, frameCount, AudioBufferProvider::kInvalidPTS); ATRACE_END(); dumpState->mReadSequence++; if (framesRead >= 0) { LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount); - totalNativeFramesRead += framesRead; - dumpState->mFramesRead = totalNativeFramesRead; - readBufferState = framesRead; + mTotalNativeFramesRead += framesRead; + dumpState->mFramesRead = mTotalNativeFramesRead; + mReadBufferState = framesRead; } else { dumpState->mReadErrors++; - readBufferState = 0; + mReadBufferState = 0; } // FIXME rename to attemptedIO - attemptedWrite = true; + mAttemptedWrite = true; } if (command & FastCaptureState::WRITE) { - ALOG_ASSERT(pipeSink != NULL); - ALOG_ASSERT(readBuffer != NULL); - if (readBufferState < 0) { - unsigned channelCount = Format_channelCount(format); - // FIXME frameSize - memset(readBuffer, 0, frameCount * channelCount * sizeof(short)); - readBufferState = frameCount; + ALOG_ASSERT(mPipeSink != NULL); + ALOG_ASSERT(mReadBuffer != NULL); + if (mReadBufferState < 0) { + unsigned channelCount = Format_channelCount(mFormat); + memset(mReadBuffer, 0, frameCount * Format_frameSize(mFormat)); + mReadBufferState = frameCount; } - if (readBufferState > 0) { - ssize_t framesWritten = pipeSink->write(readBuffer, readBufferState); + if (mReadBufferState > 0) { + ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState); // FIXME This supports at most one fast capture client. // To handle multiple clients this could be converted to an array, // or with a lot more work the control block could be shared by all clients. @@ -210,13 +209,4 @@ void FastCapture::onWork() } } -FastCaptureDumpState::FastCaptureDumpState() : FastThreadDumpState(), - mReadSequence(0), mFramesRead(0), mReadErrors(0), mSampleRate(0), mFrameCount(0) -{ -} - -FastCaptureDumpState::~FastCaptureDumpState() -{ -} - } // namespace android diff --git a/services/audioflinger/FastCapture.h b/services/audioflinger/FastCapture.h index e535b9d..e258a4d 100644 --- a/services/audioflinger/FastCapture.h +++ b/services/audioflinger/FastCapture.h @@ -20,23 +20,12 @@ #include "FastThread.h" #include "StateQueue.h" #include "FastCaptureState.h" +#include "FastCaptureDumpState.h" namespace android { typedef StateQueue<FastCaptureState> FastCaptureStateQueue; -struct FastCaptureDumpState : FastThreadDumpState { - FastCaptureDumpState(); - /*virtual*/ ~FastCaptureDumpState(); - - // FIXME by renaming, could pull up many of these to FastThreadDumpState - uint32_t mReadSequence; // incremented before and after each read() - uint32_t mFramesRead; // total number of frames read successfully - uint32_t mReadErrors; // total number of read() errors - uint32_t mSampleRate; - size_t mFrameCount; -}; - class FastCapture : public FastThread { public: @@ -57,19 +46,21 @@ private: virtual void onStateChange(); virtual void onWork(); - static const FastCaptureState initial; - FastCaptureState preIdle; // copy of state before we went into idle + static const FastCaptureState sInitial; + + FastCaptureState mPreIdle; // copy of state before we went into idle // FIXME by renaming, could pull up many of these to FastThread - NBAIO_Source *inputSource; - int inputSourceGen; - NBAIO_Sink *pipeSink; - int pipeSinkGen; - short *readBuffer; - ssize_t readBufferState; // number of initialized frames in readBuffer, or -1 to clear - NBAIO_Format format; - unsigned sampleRate; - FastCaptureDumpState dummyDumpState; - uint32_t totalNativeFramesRead; // copied to dumpState->mFramesRead + NBAIO_Source* mInputSource; + int mInputSourceGen; + NBAIO_Sink* mPipeSink; + int mPipeSinkGen; + void* mReadBuffer; + ssize_t mReadBufferState; // number of initialized frames in readBuffer, + // or -1 to clear + NBAIO_Format mFormat; + unsigned mSampleRate; + FastCaptureDumpState mDummyFastCaptureDumpState; + uint32_t mTotalNativeFramesRead; // copied to dumpState->mFramesRead }; // class FastCapture diff --git a/services/audioflinger/FastCaptureDumpState.cpp b/services/audioflinger/FastCaptureDumpState.cpp new file mode 100644 index 0000000..53eeba5 --- /dev/null +++ b/services/audioflinger/FastCaptureDumpState.cpp @@ -0,0 +1,53 @@ +/* + * 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 "FastCaptureDumpState" +//define LOG_NDEBUG 0 + +#include "Configuration.h" +#include <utils/Log.h> +#include "FastCaptureDumpState.h" +#include "FastCaptureState.h" + +namespace android { + +FastCaptureDumpState::FastCaptureDumpState() : FastThreadDumpState(), + mReadSequence(0), mFramesRead(0), mReadErrors(0), mSampleRate(0), mFrameCount(0) +{ +} + +FastCaptureDumpState::~FastCaptureDumpState() +{ +} + +void FastCaptureDumpState::dump(int fd) const +{ + if (mCommand == FastCaptureState::INITIAL) { + dprintf(fd, " FastCapture not initialized\n"); + return; + } + double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + + (mMeasuredWarmupTs.tv_nsec / 1000000.0); + double periodSec = (double) mFrameCount / mSampleRate; + dprintf(fd, " FastCapture command=%s readSequence=%u framesRead=%u\n" + " readErrors=%u sampleRate=%u frameCount=%zu\n" + " measuredWarmup=%.3g ms, warmupCycles=%u period=%.2f ms\n", + FastCaptureState::commandToString(mCommand), mReadSequence, mFramesRead, + mReadErrors, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, + periodSec * 1e3); +} + +} // android diff --git a/services/audioflinger/FastCaptureDumpState.h b/services/audioflinger/FastCaptureDumpState.h new file mode 100644 index 0000000..6f9c4c3 --- /dev/null +++ b/services/audioflinger/FastCaptureDumpState.h @@ -0,0 +1,42 @@ +/* + * 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_CAPTURE_DUMP_STATE_H +#define ANDROID_AUDIO_FAST_CAPTURE_DUMP_STATE_H + +#include <stdint.h> +#include "Configuration.h" +#include "FastThreadDumpState.h" + +namespace android { + +struct FastCaptureDumpState : FastThreadDumpState { + FastCaptureDumpState(); + /*virtual*/ ~FastCaptureDumpState(); + + void dump(int fd) const; // should only be called on a stable copy, not the original + + // FIXME by renaming, could pull up many of these to FastThreadDumpState + uint32_t mReadSequence; // incremented before and after each read() + uint32_t mFramesRead; // total number of frames read successfully + uint32_t mReadErrors; // total number of read() errors + uint32_t mSampleRate; + size_t mFrameCount; +}; + +} // android + +#endif // ANDROID_AUDIO_FAST_CAPTURE_DUMP_STATE_H diff --git a/services/audioflinger/FastCaptureState.cpp b/services/audioflinger/FastCaptureState.cpp index 1d029b7..c4d5e45 100644 --- a/services/audioflinger/FastCaptureState.cpp +++ b/services/audioflinger/FastCaptureState.cpp @@ -27,4 +27,19 @@ FastCaptureState::~FastCaptureState() { } +// static +const char *FastCaptureState::commandToString(Command command) +{ + const char *str = FastThreadState::commandToString(command); + if (str != NULL) { + return str; + } + switch (command) { + case FastCaptureState::READ: return "READ"; + case FastCaptureState::WRITE: return "WRITE"; + case FastCaptureState::READ_WRITE: return "READ_WRITE"; + } + LOG_ALWAYS_FATAL("%s", __func__); +} + } // android diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h index 29c865a..9bca2d4 100644 --- a/services/audioflinger/FastCaptureState.h +++ b/services/audioflinger/FastCaptureState.h @@ -29,21 +29,23 @@ struct FastCaptureState : FastThreadState { /*virtual*/ ~FastCaptureState(); // all pointer fields use raw pointers; objects are owned and ref-counted by RecordThread - NBAIO_Source *mInputSource; // HAL input device, must already be negotiated + NBAIO_Source* mInputSource; // HAL input device, must already be negotiated // FIXME by renaming, could pull up these fields to FastThreadState int mInputSourceGen; // increment when mInputSource is assigned - NBAIO_Sink *mPipeSink; // after reading from input source, write to this pipe sink + NBAIO_Sink* mPipeSink; // after reading from input source, write to this pipe sink int mPipeSinkGen; // increment when mPipeSink is assigned size_t mFrameCount; // number of frames per fast capture buffer - audio_track_cblk_t *mCblk; // control block for the single fast client, or NULL + audio_track_cblk_t* mCblk; // control block for the single fast client, or NULL // Extends FastThreadState::Command static const Command // The following commands also process configuration changes, and can be "or"ed: - READ = 0x8, // read from input source - WRITE = 0x10, // write to pipe sink - READ_WRITE = 0x18; // read from input source and write to pipe sink + READ = 0x8, // read from input source + WRITE = 0x10, // write to pipe sink + READ_WRITE = 0x18; // read from input source and write to pipe sink + // never returns NULL; asserts if command is invalid + static const char *commandToString(Command command); }; // struct FastCaptureState } // namespace android diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 2678cbf..f1cf0aa 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -27,10 +27,11 @@ #include "Configuration.h" #include <time.h> +#include <utils/Debug.h> #include <utils/Log.h> #include <utils/Trace.h> #include <system/audio.h> -#ifdef FAST_MIXER_STATISTICS +#ifdef FAST_THREAD_STATISTICS #include <cpustats/CentralTendencyStatistics.h> #ifdef CPU_FREQUENCY_STATISTICS #include <cpustats/ThreadCpuUsage.h> @@ -44,15 +45,15 @@ namespace android { -/*static*/ const FastMixerState FastMixer::initial; +/*static*/ const FastMixerState FastMixer::sInitial; FastMixer::FastMixer() : FastThread(), - slopNs(0), - // fastTrackNames - // generations - outputSink(NULL), - outputSinkGen(0), - mixer(NULL), + mSlopNs(0), + // mFastTrackNames + // mGenerations + mOutputSink(NULL), + mOutputSinkGen(0), + mMixer(NULL), mSinkBuffer(NULL), mSinkBufferSize(0), mSinkChannelCount(FCC_2), @@ -60,30 +61,30 @@ FastMixer::FastMixer() : FastThread(), mMixerBufferSize(0), mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT), mMixerBufferState(UNDEFINED), - format(Format_Invalid), - sampleRate(0), - fastTracksGen(0), - totalNativeFramesWritten(0), + mFormat(Format_Invalid), + mSampleRate(0), + mFastTracksGen(0), + mTotalNativeFramesWritten(0), // timestamp - nativeFramesWrittenButNotPresented(0) // the = 0 is to silence the compiler + mNativeFramesWrittenButNotPresented(0) // the = 0 is to silence the compiler { - // FIXME pass initial as parameter to base class constructor, and make it static local - previous = &initial; - current = &initial; + // FIXME pass sInitial as parameter to base class constructor, and make it static local + mPrevious = &sInitial; + mCurrent = &sInitial; - mDummyDumpState = &dummyDumpState; + mDummyDumpState = &mDummyFastMixerDumpState; // TODO: Add channel mask to NBAIO_Format. // We assume that the channel mask must be a valid positional channel mask. mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); unsigned i; for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) { - fastTrackNames[i] = -1; - generations[i] = 0; + mFastTrackNames[i] = -1; + mGenerations[i] = 0; } -#ifdef FAST_MIXER_STATISTICS - oldLoad.tv_sec = 0; - oldLoad.tv_nsec = 0; +#ifdef FAST_THREAD_STATISTICS + mOldLoad.tv_sec = 0; + mOldLoad.tv_nsec = 0; #endif } @@ -103,20 +104,20 @@ const FastThreadState *FastMixer::poll() void FastMixer::setLog(NBLog::Writer *logWriter) { - if (mixer != NULL) { - mixer->setLog(logWriter); + if (mMixer != NULL) { + mMixer->setLog(logWriter); } } void FastMixer::onIdle() { - preIdle = *(const FastMixerState *)current; - current = &preIdle; + mPreIdle = *(const FastMixerState *)mCurrent; + mCurrent = &mPreIdle; } void FastMixer::onExit() { - delete mixer; + delete mMixer; free(mMixerBuffer); free(mSinkBuffer); } @@ -135,82 +136,84 @@ bool FastMixer::isSubClassCommand(FastThreadState::Command command) void FastMixer::onStateChange() { - const FastMixerState * const current = (const FastMixerState *) this->current; - const FastMixerState * const previous = (const FastMixerState *) this->previous; - FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; + const FastMixerState * const current = (const FastMixerState *) mCurrent; + const FastMixerState * const previous = (const FastMixerState *) mPrevious; + FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState; const size_t frameCount = current->mFrameCount; // handle state change here, but since we want to diff the state, - // we're prepared for previous == &initial the first time through + // we're prepared for previous == &sInitial the first time through unsigned previousTrackMask; // check for change in output HAL configuration - NBAIO_Format previousFormat = format; - if (current->mOutputSinkGen != outputSinkGen) { - outputSink = current->mOutputSink; - outputSinkGen = current->mOutputSinkGen; - if (outputSink == NULL) { - format = Format_Invalid; - sampleRate = 0; + NBAIO_Format previousFormat = mFormat; + if (current->mOutputSinkGen != mOutputSinkGen) { + mOutputSink = current->mOutputSink; + mOutputSinkGen = current->mOutputSinkGen; + if (mOutputSink == NULL) { + mFormat = Format_Invalid; + mSampleRate = 0; mSinkChannelCount = 0; mSinkChannelMask = AUDIO_CHANNEL_NONE; } else { - format = outputSink->format(); - sampleRate = Format_sampleRate(format); - mSinkChannelCount = Format_channelCount(format); + mFormat = mOutputSink->format(); + mSampleRate = Format_sampleRate(mFormat); + mSinkChannelCount = Format_channelCount(mFormat); LOG_ALWAYS_FATAL_IF(mSinkChannelCount > AudioMixer::MAX_NUM_CHANNELS); // TODO: Add channel mask to NBAIO_Format // We assume that the channel mask must be a valid positional channel mask. mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); } - dumpState->mSampleRate = sampleRate; + dumpState->mSampleRate = mSampleRate; } - if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { + if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) { // FIXME to avoid priority inversion, don't delete here - delete mixer; - mixer = NULL; + delete mMixer; + mMixer = NULL; free(mMixerBuffer); mMixerBuffer = NULL; free(mSinkBuffer); mSinkBuffer = NULL; - if (frameCount > 0 && sampleRate > 0) { + if (frameCount > 0 && mSampleRate > 0) { // FIXME new may block for unbounded time at internal mutex of the heap // implementation; it would be better to have normal mixer allocate for us // to avoid blocking here and to prevent possible priority inversion - mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks); + mMixer = new AudioMixer(frameCount, mSampleRate, FastMixerState::kMaxFastTracks); const size_t mixerFrameSize = mSinkChannelCount * audio_bytes_per_sample(mMixerBufferFormat); mMixerBufferSize = mixerFrameSize * frameCount; (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize); const size_t sinkFrameSize = mSinkChannelCount - * audio_bytes_per_sample(format.mFormat); + * audio_bytes_per_sample(mFormat.mFormat); if (sinkFrameSize > mixerFrameSize) { // need a sink buffer mSinkBufferSize = sinkFrameSize * frameCount; (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize); } - periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 - underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 - overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 - forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 - warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50 + mPeriodNs = (frameCount * 1000000000LL) / mSampleRate; // 1.00 + mUnderrunNs = (frameCount * 1750000000LL) / mSampleRate; // 1.75 + mOverrunNs = (frameCount * 500000000LL) / mSampleRate; // 0.50 + mForceNs = (frameCount * 950000000LL) / mSampleRate; // 0.95 + mWarmupNsMin = (frameCount * 750000000LL) / mSampleRate; // 0.75 + mWarmupNsMax = (frameCount * 1250000000LL) / mSampleRate; // 1.25 } else { - periodNs = 0; - underrunNs = 0; - overrunNs = 0; - forceNs = 0; - warmupNs = 0; + mPeriodNs = 0; + mUnderrunNs = 0; + mOverrunNs = 0; + mForceNs = 0; + mWarmupNsMin = 0; + mWarmupNsMax = LONG_MAX; } mMixerBufferState = UNDEFINED; #if !LOG_NDEBUG for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) { - fastTrackNames[i] = -1; + mFastTrackNames[i] = -1; } #endif // we need to reconfigure all active tracks previousTrackMask = 0; - fastTracksGen = current->mFastTracksGen - 1; + mFastTracksGen = current->mFastTracksGen - 1; dumpState->mFrameCount = frameCount; } else { previousTrackMask = previous->mTrackMask; @@ -219,7 +222,7 @@ void FastMixer::onStateChange() // check for change in active track set const unsigned currentTrackMask = current->mTrackMask; dumpState->mTrackMask = currentTrackMask; - if (current->mFastTracksGen != fastTracksGen) { + if (current->mFastTracksGen != mFastTracksGen) { ALOG_ASSERT(mMixerBuffer != NULL); int name; @@ -230,16 +233,16 @@ void FastMixer::onStateChange() removedTracks &= ~(1 << i); const FastTrack* fastTrack = ¤t->mFastTracks[i]; ALOG_ASSERT(fastTrack->mBufferProvider == NULL); - if (mixer != NULL) { - name = fastTrackNames[i]; + if (mMixer != NULL) { + name = mFastTrackNames[i]; ALOG_ASSERT(name >= 0); - mixer->deleteTrackName(name); + mMixer->deleteTrackName(name); } #if !LOG_NDEBUG - fastTrackNames[i] = -1; + mFastTrackNames[i] = -1; #endif // don't reset track dump state, since other side is ignoring it - generations[i] = fastTrack->mGeneration; + mGenerations[i] = fastTrack->mGeneration; } // now process added tracks @@ -249,29 +252,29 @@ void FastMixer::onStateChange() addedTracks &= ~(1 << i); const FastTrack* fastTrack = ¤t->mFastTracks[i]; AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; - ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1); - if (mixer != NULL) { - name = mixer->getTrackName(fastTrack->mChannelMask, + ALOG_ASSERT(bufferProvider != NULL && mFastTrackNames[i] == -1); + if (mMixer != NULL) { + name = mMixer->getTrackName(fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX); ALOG_ASSERT(name >= 0); - fastTrackNames[i] = name; - mixer->setBufferProvider(name, bufferProvider); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, + mFastTrackNames[i] = name; + mMixer->setBufferProvider(name, bufferProvider); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer); // newly allocated track names default to full scale volume - mixer->setParameter( + mMixer->setParameter( name, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, (void *)(uintptr_t)fastTrack->mFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)fastTrack->mChannelMask); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mSinkChannelMask); - mixer->enable(name); + mMixer->enable(name); } - generations[i] = fastTrack->mGeneration; + mGenerations[i] = fastTrack->mGeneration; } // finally process (potentially) modified tracks; these use the same slot @@ -281,38 +284,38 @@ void FastMixer::onStateChange() int i = __builtin_ctz(modifiedTracks); modifiedTracks &= ~(1 << i); const FastTrack* fastTrack = ¤t->mFastTracks[i]; - if (fastTrack->mGeneration != generations[i]) { + if (fastTrack->mGeneration != mGenerations[i]) { // this track was actually modified AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; ALOG_ASSERT(bufferProvider != NULL); - if (mixer != NULL) { - name = fastTrackNames[i]; + if (mMixer != NULL) { + name = mFastTrackNames[i]; ALOG_ASSERT(name >= 0); - mixer->setBufferProvider(name, bufferProvider); + mMixer->setBufferProvider(name, bufferProvider); if (fastTrack->mVolumeProvider == NULL) { float f = AudioMixer::UNITY_GAIN_FLOAT; - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); } - mixer->setParameter(name, AudioMixer::RESAMPLE, + mMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::REMOVE, NULL); - mixer->setParameter( + mMixer->setParameter( name, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, (void *)(uintptr_t)fastTrack->mFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)fastTrack->mChannelMask); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mSinkChannelMask); // already enabled } - generations[i] = fastTrack->mGeneration; + mGenerations[i] = fastTrack->mGeneration; } } - fastTracksGen = current->mFastTracksGen; + mFastTracksGen = current->mFastTracksGen; dumpState->mNumTracks = popcount(currentTrackMask); } @@ -320,12 +323,12 @@ void FastMixer::onStateChange() void FastMixer::onWork() { - const FastMixerState * const current = (const FastMixerState *) this->current; - FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; - const FastMixerState::Command command = this->command; + const FastMixerState * const current = (const FastMixerState *) mCurrent; + FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState; + const FastMixerState::Command command = mCommand; const size_t frameCount = current->mFrameCount; - if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) { + if ((command & FastMixerState::MIX) && (mMixer != NULL) && mIsWarm) { ALOG_ASSERT(mMixerBuffer != NULL); // for each track, update volume and check for underrun unsigned currentTrackMask = current->mTrackMask; @@ -335,9 +338,9 @@ void FastMixer::onWork() const FastTrack* fastTrack = ¤t->mFastTracks[i]; // Refresh the per-track timestamp - if (timestampStatus == NO_ERROR) { + if (mTimestampStatus == NO_ERROR) { uint32_t trackFramesWrittenButNotPresented = - nativeFramesWrittenButNotPresented; + mNativeFramesWrittenButNotPresented; uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased(); // Can't provide an AudioTimestamp before first frame presented, // or during the brief 32-bit wraparound window @@ -345,20 +348,20 @@ void FastMixer::onWork() AudioTimestamp perTrackTimestamp; perTrackTimestamp.mPosition = trackFramesWritten - trackFramesWrittenButNotPresented; - perTrackTimestamp.mTime = timestamp.mTime; + perTrackTimestamp.mTime = mTimestamp.mTime; fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp); } } - int name = fastTrackNames[i]; + int name = mFastTrackNames[i]; ALOG_ASSERT(name >= 0); if (fastTrack->mVolumeProvider != NULL) { gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR(); float vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); float vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); } // FIXME The current implementation of framesReady() for fast tracks // takes a tryLock, which can block @@ -379,43 +382,44 @@ void FastMixer::onWork() if (framesReady == 0) { underruns.mBitFields.mEmpty++; underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY; - mixer->disable(name); + mMixer->disable(name); } else { // allow mixing partial buffer underruns.mBitFields.mPartial++; underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL; - mixer->enable(name); + mMixer->enable(name); } } else { underruns.mBitFields.mFull++; underruns.mBitFields.mMostRecent = UNDERRUN_FULL; - mixer->enable(name); + mMixer->enable(name); } ftDump->mUnderruns = underruns; ftDump->mFramesReady = framesReady; } int64_t pts; - if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts))) { + if (mOutputSink == NULL || (OK != mOutputSink->getNextWriteTimestamp(&pts))) { pts = AudioBufferProvider::kInvalidPTS; } // process() is CPU-bound - mixer->process(pts); + mMixer->process(pts); mMixerBufferState = MIXED; } else if (mMixerBufferState == MIXED) { mMixerBufferState = UNDEFINED; } //bool didFullWrite = false; // dumpsys could display a count of partial writes - if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) { + if ((command & FastMixerState::WRITE) && (mOutputSink != NULL) && (mMixerBuffer != NULL)) { if (mMixerBufferState == UNDEFINED) { memset(mMixerBuffer, 0, mMixerBufferSize); mMixerBufferState = ZEROED; } + // prepare the buffer used to write to sink void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer; - if (format.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format - memcpy_by_audio_format(buffer, format.mFormat, mMixerBuffer, mMixerBufferFormat, - frameCount * Format_channelCount(format)); + if (mFormat.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format + memcpy_by_audio_format(buffer, mFormat.mFormat, mMixerBuffer, mMixerBufferFormat, + frameCount * Format_channelCount(mFormat)); } // if non-NULL, then duplicate write() to this non-blocking sink NBAIO_Sink* teeSink; @@ -426,252 +430,34 @@ void FastMixer::onWork() // but this code should be modified to handle both non-blocking and blocking sinks dumpState->mWriteSequence++; ATRACE_BEGIN("write"); - ssize_t framesWritten = outputSink->write(buffer, frameCount); + ssize_t framesWritten = mOutputSink->write(buffer, frameCount); ATRACE_END(); dumpState->mWriteSequence++; if (framesWritten >= 0) { ALOG_ASSERT((size_t) framesWritten <= frameCount); - totalNativeFramesWritten += framesWritten; - dumpState->mFramesWritten = totalNativeFramesWritten; + mTotalNativeFramesWritten += framesWritten; + dumpState->mFramesWritten = mTotalNativeFramesWritten; //if ((size_t) framesWritten == frameCount) { // didFullWrite = true; //} } else { dumpState->mWriteErrors++; } - attemptedWrite = true; + mAttemptedWrite = true; // FIXME count # of writes blocked excessively, CPU usage, etc. for dump - timestampStatus = outputSink->getTimestamp(timestamp); - if (timestampStatus == NO_ERROR) { - uint32_t totalNativeFramesPresented = timestamp.mPosition; - if (totalNativeFramesPresented <= totalNativeFramesWritten) { - nativeFramesWrittenButNotPresented = - totalNativeFramesWritten - totalNativeFramesPresented; + mTimestampStatus = mOutputSink->getTimestamp(mTimestamp); + if (mTimestampStatus == NO_ERROR) { + uint32_t totalNativeFramesPresented = mTimestamp.mPosition; + if (totalNativeFramesPresented <= mTotalNativeFramesWritten) { + mNativeFramesWrittenButNotPresented = + mTotalNativeFramesWritten - totalNativeFramesPresented; } else { // HAL reported that more frames were presented than were written - timestampStatus = INVALID_OPERATION; + mTimestampStatus = INVALID_OPERATION; } } } } -FastMixerDumpState::FastMixerDumpState( -#ifdef FAST_MIXER_STATISTICS - uint32_t samplingN -#endif - ) : FastThreadDumpState(), - mWriteSequence(0), mFramesWritten(0), - mNumTracks(0), mWriteErrors(0), - mSampleRate(0), mFrameCount(0), - mTrackMask(0) -{ -#ifdef FAST_MIXER_STATISTICS - increaseSamplingN(samplingN); -#endif -} - -#ifdef FAST_MIXER_STATISTICS -void FastMixerDumpState::increaseSamplingN(uint32_t samplingN) -{ - if (samplingN <= mSamplingN || samplingN > kSamplingN || roundup(samplingN) != samplingN) { - return; - } - uint32_t additional = samplingN - mSamplingN; - // sample arrays aren't accessed atomically with respect to the bounds, - // so clearing reduces chance for dumpsys to read random uninitialized samples - memset(&mMonotonicNs[mSamplingN], 0, sizeof(mMonotonicNs[0]) * additional); - memset(&mLoadNs[mSamplingN], 0, sizeof(mLoadNs[0]) * additional); -#ifdef CPU_FREQUENCY_STATISTICS - memset(&mCpukHz[mSamplingN], 0, sizeof(mCpukHz[0]) * additional); -#endif - mSamplingN = samplingN; -} -#endif - -FastMixerDumpState::~FastMixerDumpState() -{ -} - -// helper function called by qsort() -static int compare_uint32_t(const void *pa, const void *pb) -{ - uint32_t a = *(const uint32_t *)pa; - uint32_t b = *(const uint32_t *)pb; - if (a < b) { - return -1; - } else if (a > b) { - return 1; - } else { - return 0; - } -} - -void FastMixerDumpState::dump(int fd) const -{ - if (mCommand == FastMixerState::INITIAL) { - dprintf(fd, " FastMixer not initialized\n"); - return; - } -#define COMMAND_MAX 32 - char string[COMMAND_MAX]; - switch (mCommand) { - case FastMixerState::INITIAL: - strcpy(string, "INITIAL"); - break; - case FastMixerState::HOT_IDLE: - strcpy(string, "HOT_IDLE"); - break; - case FastMixerState::COLD_IDLE: - strcpy(string, "COLD_IDLE"); - break; - case FastMixerState::EXIT: - strcpy(string, "EXIT"); - break; - case FastMixerState::MIX: - strcpy(string, "MIX"); - break; - case FastMixerState::WRITE: - strcpy(string, "WRITE"); - break; - case FastMixerState::MIX_WRITE: - strcpy(string, "MIX_WRITE"); - break; - default: - snprintf(string, COMMAND_MAX, "%d", mCommand); - break; - } - double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + - (mMeasuredWarmupTs.tv_nsec / 1000000.0); - double mixPeriodSec = (double) mFrameCount / (double) mSampleRate; - dprintf(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, - mixPeriodSec * 1e3); -#ifdef FAST_MIXER_STATISTICS - // find the interval of valid samples - uint32_t bounds = mBounds; - uint32_t newestOpen = bounds & 0xFFFF; - uint32_t oldestClosed = bounds >> 16; - uint32_t n = (newestOpen - oldestClosed) & 0xFFFF; - if (n > mSamplingN) { - ALOGE("too many samples %u", n); - n = mSamplingN; - } - // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, - // and adjusted CPU load in MHz normalized for CPU clock frequency - CentralTendencyStatistics wall, loadNs; -#ifdef CPU_FREQUENCY_STATISTICS - CentralTendencyStatistics kHz, loadMHz; - uint32_t previousCpukHz = 0; -#endif - // Assuming a normal distribution for cycle times, three standard deviations on either side of - // the mean account for 99.73% of the population. So if we take each tail to be 1/1000 of the - // sample set, we get 99.8% combined, or close to three standard deviations. - static const uint32_t kTailDenominator = 1000; - uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL; - // loop over all the samples - for (uint32_t j = 0; j < n; ++j) { - size_t i = oldestClosed++ & (mSamplingN - 1); - uint32_t wallNs = mMonotonicNs[i]; - if (tail != NULL) { - tail[j] = wallNs; - } - wall.sample(wallNs); - uint32_t sampleLoadNs = mLoadNs[i]; - loadNs.sample(sampleLoadNs); -#ifdef CPU_FREQUENCY_STATISTICS - uint32_t sampleCpukHz = mCpukHz[i]; - // skip bad kHz samples - if ((sampleCpukHz & ~0xF) != 0) { - kHz.sample(sampleCpukHz >> 4); - if (sampleCpukHz == previousCpukHz) { - double megacycles = (double) sampleLoadNs * (double) (sampleCpukHz >> 4) * 1e-12; - double adjMHz = megacycles / mixPeriodSec; // _not_ wallNs * 1e9 - loadMHz.sample(adjMHz); - } - } - previousCpukHz = sampleCpukHz; -#endif - } - if (n) { - dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", - wall.n() * mixPeriodSec); - dprintf(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); - dprintf(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 { - dprintf(fd, " No FastMixer statistics available currently\n"); - } -#ifdef CPU_FREQUENCY_STATISTICS - dprintf(fd, " CPU clock frequency in MHz:\n" - " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); - dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" - " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", - loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); -#endif - if (tail != NULL) { - qsort(tail, n, sizeof(uint32_t), compare_uint32_t); - // assume same number of tail samples on each side, left and right - uint32_t count = n / kTailDenominator; - CentralTendencyStatistics left, right; - for (uint32_t i = 0; i < count; ++i) { - left.sample(tail[i]); - right.sample(tail[n - (i + 1)]); - } - dprintf(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); - delete[] tail; - } -#endif - // The active track mask and track states are updated non-atomically. - // So if we relied on isActive to decide whether to display, - // then we might display an obsolete track or omit an active track. - // Instead we always display all tracks, with an indication - // of whether we think the track is active. - uint32_t trackMask = mTrackMask; - dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", - FastMixerState::kMaxFastTracks, trackMask); - dprintf(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]; - const FastTrackUnderruns& underruns = ftDump->mUnderruns; - const char *mostRecent; - switch (underruns.mBitFields.mMostRecent) { - case UNDERRUN_FULL: - mostRecent = "full"; - break; - case UNDERRUN_PARTIAL: - mostRecent = "partial"; - break; - case UNDERRUN_EMPTY: - mostRecent = "empty"; - break; - default: - mostRecent = "?"; - break; - } - dprintf(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, - mostRecent, ftDump->mFramesReady); - } -} - } // namespace android diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index fde8c2b..06a68fb 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -17,11 +17,7 @@ #ifndef ANDROID_AUDIO_FAST_MIXER_H #define ANDROID_AUDIO_FAST_MIXER_H -#include <linux/futex.h> -#include <sys/syscall.h> -#include <utils/Debug.h> #include "FastThread.h" -#include <utils/Thread.h> #include "StateQueue.h" #include "FastMixerState.h" #include "FastMixerDumpState.h" @@ -52,36 +48,39 @@ private: virtual void onStateChange(); virtual void onWork(); - // FIXME these former local variables need comments and to be renamed to have "m" prefix - static const FastMixerState initial; - FastMixerState preIdle; // copy of state before we went into idle - long slopNs; // accumulated time we've woken up too early (> 0) or too late (< 0) - int fastTrackNames[FastMixerState::kMaxFastTracks]; // handles used by mixer to identify tracks - int generations[FastMixerState::kMaxFastTracks]; // last observed mFastTracks[i].mGeneration - NBAIO_Sink *outputSink; - int outputSinkGen; - AudioMixer* mixer; + // FIXME these former local variables need comments + static const FastMixerState sInitial; + + FastMixerState mPreIdle; // copy of state before we went into idle + long mSlopNs; // accumulated time we've woken up too early (> 0) or too late (< 0) + int mFastTrackNames[FastMixerState::kMaxFastTracks]; + // handles used by mixer to identify tracks + int mGenerations[FastMixerState::kMaxFastTracks]; + // last observed mFastTracks[i].mGeneration + NBAIO_Sink* mOutputSink; + int mOutputSinkGen; + AudioMixer* mMixer; // mSinkBuffer audio format is stored in format.mFormat. - void* mSinkBuffer; // used for mixer output format translation + void* mSinkBuffer; // used for mixer output format translation // if sink format is different than mixer output. - size_t mSinkBufferSize; - uint32_t mSinkChannelCount; + size_t mSinkBufferSize; + uint32_t mSinkChannelCount; audio_channel_mask_t mSinkChannelMask; - void* mMixerBuffer; // mixer output buffer. - size_t mMixerBufferSize; - audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). + void* mMixerBuffer; // mixer output buffer. + size_t mMixerBufferSize; + audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState; - NBAIO_Format format; - unsigned sampleRate; - int fastTracksGen; - FastMixerDumpState dummyDumpState; - uint32_t totalNativeFramesWritten; // copied to dumpState->mFramesWritten + NBAIO_Format mFormat; + unsigned mSampleRate; + int mFastTracksGen; + FastMixerDumpState mDummyFastMixerDumpState; + uint32_t mTotalNativeFramesWritten; // copied to dumpState->mFramesWritten // next 2 fields are valid only when timestampStatus == NO_ERROR - AudioTimestamp timestamp; - uint32_t nativeFramesWrittenButNotPresented; + AudioTimestamp mTimestamp; + uint32_t mNativeFramesWrittenButNotPresented; }; // class FastMixer diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp new file mode 100644 index 0000000..b10942b --- /dev/null +++ b/services/audioflinger/FastMixerDumpState.cpp @@ -0,0 +1,199 @@ +/* + * 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 "FastMixerDumpState" +//#define LOG_NDEBUG 0 + +#include "Configuration.h" +#ifdef FAST_THREAD_STATISTICS +#include <cpustats/CentralTendencyStatistics.h> +#ifdef CPU_FREQUENCY_STATISTICS +#include <cpustats/ThreadCpuUsage.h> +#endif +#endif +#include <utils/Debug.h> +#include <utils/Log.h> +#include "FastMixerDumpState.h" + +namespace android { + +FastMixerDumpState::FastMixerDumpState() : FastThreadDumpState(), + mWriteSequence(0), mFramesWritten(0), + mNumTracks(0), mWriteErrors(0), + mSampleRate(0), mFrameCount(0), + mTrackMask(0) +{ +} + +FastMixerDumpState::~FastMixerDumpState() +{ +} + +// helper function called by qsort() +static int compare_uint32_t(const void *pa, const void *pb) +{ + uint32_t a = *(const uint32_t *)pa; + uint32_t b = *(const uint32_t *)pb; + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return 0; + } +} + +void FastMixerDumpState::dump(int fd) const +{ + if (mCommand == FastMixerState::INITIAL) { + dprintf(fd, " FastMixer not initialized\n"); + return; + } + double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + + (mMeasuredWarmupTs.tv_nsec / 1000000.0); + double mixPeriodSec = (double) mFrameCount / mSampleRate; + dprintf(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", + FastMixerState::commandToString(mCommand), mWriteSequence, mFramesWritten, + mNumTracks, mWriteErrors, mUnderruns, mOverruns, + mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, + mixPeriodSec * 1e3); +#ifdef FAST_THREAD_STATISTICS + // find the interval of valid samples + uint32_t bounds = mBounds; + uint32_t newestOpen = bounds & 0xFFFF; + uint32_t oldestClosed = bounds >> 16; + uint32_t n = (newestOpen - oldestClosed) & 0xFFFF; + if (n > mSamplingN) { + ALOGE("too many samples %u", n); + n = mSamplingN; + } + // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, + // and adjusted CPU load in MHz normalized for CPU clock frequency + CentralTendencyStatistics wall, loadNs; +#ifdef CPU_FREQUENCY_STATISTICS + CentralTendencyStatistics kHz, loadMHz; + uint32_t previousCpukHz = 0; +#endif + // Assuming a normal distribution for cycle times, three standard deviations on either side of + // the mean account for 99.73% of the population. So if we take each tail to be 1/1000 of the + // sample set, we get 99.8% combined, or close to three standard deviations. + static const uint32_t kTailDenominator = 1000; + uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL; + // loop over all the samples + for (uint32_t j = 0; j < n; ++j) { + size_t i = oldestClosed++ & (mSamplingN - 1); + uint32_t wallNs = mMonotonicNs[i]; + if (tail != NULL) { + tail[j] = wallNs; + } + wall.sample(wallNs); + uint32_t sampleLoadNs = mLoadNs[i]; + loadNs.sample(sampleLoadNs); +#ifdef CPU_FREQUENCY_STATISTICS + uint32_t sampleCpukHz = mCpukHz[i]; + // skip bad kHz samples + if ((sampleCpukHz & ~0xF) != 0) { + kHz.sample(sampleCpukHz >> 4); + if (sampleCpukHz == previousCpukHz) { + double megacycles = (double) sampleLoadNs * (double) (sampleCpukHz >> 4) * 1e-12; + double adjMHz = megacycles / mixPeriodSec; // _not_ wallNs * 1e9 + loadMHz.sample(adjMHz); + } + } + previousCpukHz = sampleCpukHz; +#endif + } + if (n) { + dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", + wall.n() * mixPeriodSec); + dprintf(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); + dprintf(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 { + dprintf(fd, " No FastMixer statistics available currently\n"); + } +#ifdef CPU_FREQUENCY_STATISTICS + dprintf(fd, " CPU clock frequency in MHz:\n" + " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", + kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); + dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" + " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", + loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); +#endif + if (tail != NULL) { + qsort(tail, n, sizeof(uint32_t), compare_uint32_t); + // assume same number of tail samples on each side, left and right + uint32_t count = n / kTailDenominator; + CentralTendencyStatistics left, right; + for (uint32_t i = 0; i < count; ++i) { + left.sample(tail[i]); + right.sample(tail[n - (i + 1)]); + } + dprintf(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); + delete[] tail; + } +#endif + // The active track mask and track states are updated non-atomically. + // So if we relied on isActive to decide whether to display, + // then we might display an obsolete track or omit an active track. + // Instead we always display all tracks, with an indication + // of whether we think the track is active. + uint32_t trackMask = mTrackMask; + dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", + FastMixerState::kMaxFastTracks, trackMask); + dprintf(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]; + const FastTrackUnderruns& underruns = ftDump->mUnderruns; + const char *mostRecent; + switch (underruns.mBitFields.mMostRecent) { + case UNDERRUN_FULL: + mostRecent = "full"; + break; + case UNDERRUN_PARTIAL: + mostRecent = "partial"; + break; + case UNDERRUN_EMPTY: + mostRecent = "empty"; + break; + default: + mostRecent = "?"; + break; + } + dprintf(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, + mostRecent, ftDump->mFramesReady); + } +} + +} // android diff --git a/services/audioflinger/FastMixerDumpState.h b/services/audioflinger/FastMixerDumpState.h index 6a1e464..ac15e7c 100644 --- a/services/audioflinger/FastMixerDumpState.h +++ b/services/audioflinger/FastMixerDumpState.h @@ -17,7 +17,10 @@ #ifndef ANDROID_AUDIO_FAST_MIXER_DUMP_STATE_H #define ANDROID_AUDIO_FAST_MIXER_DUMP_STATE_H +#include <stdint.h> #include "Configuration.h" +#include "FastThreadDumpState.h" +#include "FastMixerState.h" namespace android { @@ -52,22 +55,12 @@ private: struct FastTrackDump { FastTrackDump() : mFramesReady(0) { } /*virtual*/ ~FastTrackDump() { } - FastTrackUnderruns mUnderruns; - size_t mFramesReady; // most recent value only; no long-term statistics kept + FastTrackUnderruns mUnderruns; + size_t mFramesReady; // most recent value only; no long-term statistics kept }; -// The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys. -// Each individual native word-sized field is accessed atomically. But the -// overall structure is non-atomic, that is there may be an inconsistency between fields. -// No barriers or locks are used for either writing or reading. -// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks). -// It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer. struct FastMixerDumpState : FastThreadDumpState { - FastMixerDumpState( -#ifdef FAST_MIXER_STATISTICS - uint32_t samplingN = kSamplingNforLowRamDevice -#endif - ); + FastMixerDumpState(); /*virtual*/ ~FastMixerDumpState(); void dump(int fd) const; // should only be called on a stable copy, not the original @@ -80,14 +73,6 @@ struct FastMixerDumpState : FastThreadDumpState { size_t mFrameCount; uint32_t mTrackMask; // mask of active tracks FastTrackDump mTracks[FastMixerState::kMaxFastTracks]; - -#ifdef FAST_MIXER_STATISTICS - // Compile-time constant for a "low RAM device", must be a power of 2 <= kSamplingN. - // This value was chosen such that each array uses 1 small page (4 Kbytes). - static const uint32_t kSamplingNforLowRamDevice = 0x400; - // Increase sampling window after construction, must be a power of 2 <= kSamplingN - void increaseSamplingN(uint32_t samplingN); -#endif }; } // android diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp index 3aa8dad..a8c2634 100644 --- a/services/audioflinger/FastMixerState.cpp +++ b/services/audioflinger/FastMixerState.cpp @@ -39,4 +39,19 @@ FastMixerState::~FastMixerState() { } +// static +const char *FastMixerState::commandToString(Command command) +{ + const char *str = FastThreadState::commandToString(command); + if (str != NULL) { + return str; + } + switch (command) { + case FastMixerState::MIX: return "MIX"; + case FastMixerState::WRITE: return "WRITE"; + case FastMixerState::MIX_WRITE: return "MIX_WRITE"; + } + LOG_ALWAYS_FATAL("%s", __func__); +} + } // namespace android diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index 661c9ca..916514f 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -73,6 +73,9 @@ struct FastMixerState : FastThreadState { // This might be a one-time configuration rather than per-state NBAIO_Sink* mTeeSink; // if non-NULL, then duplicate write()s to this non-blocking sink + + // never returns NULL; asserts if command is invalid + static const char *commandToString(Command command); }; // struct FastMixerState } // namespace android diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp index 216dace..5ca579b 100644 --- a/services/audioflinger/FastThread.cpp +++ b/services/audioflinger/FastThread.cpp @@ -25,54 +25,58 @@ #include <utils/Log.h> #include <utils/Trace.h> #include "FastThread.h" +#include "FastThreadDumpState.h" #define FAST_DEFAULT_NS 999999999L // ~1 sec: default time to sleep #define FAST_HOT_IDLE_NS 1000000L // 1 ms: time to sleep while hot idling -#define MIN_WARMUP_CYCLES 2 // minimum number of loop cycles to wait for warmup +#define MIN_WARMUP_CYCLES 2 // minimum number of consecutive in-range loop cycles + // to wait for warmup #define MAX_WARMUP_CYCLES 10 // maximum number of loop cycles to wait for warmup namespace android { FastThread::FastThread() : Thread(false /*canCallJava*/), - // re-initialized to &initial by subclass constructor - previous(NULL), current(NULL), - /* oldTs({0, 0}), */ - oldTsValid(false), - sleepNs(-1), - periodNs(0), - underrunNs(0), - overrunNs(0), - forceNs(0), - warmupNs(0), - // re-initialized to &dummyDumpState by subclass constructor + // re-initialized to &sInitial by subclass constructor + mPrevious(NULL), mCurrent(NULL), + /* mOldTs({0, 0}), */ + mOldTsValid(false), + mSleepNs(-1), + mPeriodNs(0), + mUnderrunNs(0), + mOverrunNs(0), + mForceNs(0), + mWarmupNsMin(0), + mWarmupNsMax(LONG_MAX), + // re-initialized to &mDummySubclassDumpState by subclass constructor mDummyDumpState(NULL), - dumpState(NULL), - ignoreNextOverrun(true), -#ifdef FAST_MIXER_STATISTICS - // oldLoad - oldLoadValid(false), - bounds(0), - full(false), - // tcu + mDumpState(NULL), + mIgnoreNextOverrun(true), +#ifdef FAST_THREAD_STATISTICS + // mOldLoad + mOldLoadValid(false), + mBounds(0), + mFull(false), + // mTcu #endif - coldGen(0), - isWarm(false), - /* measuredWarmupTs({0, 0}), */ - warmupCycles(0), - // dummyLogWriter - logWriter(&dummyLogWriter), - timestampStatus(INVALID_OPERATION), + mColdGen(0), + mIsWarm(false), + /* mMeasuredWarmupTs({0, 0}), */ + mWarmupCycles(0), + mWarmupConsecutiveInRangeCycles(0), + // mDummyLogWriter + mLogWriter(&mDummyLogWriter), + mTimestampStatus(INVALID_OPERATION), - command(FastThreadState::INITIAL), + mCommand(FastThreadState::INITIAL), #if 0 frameCount(0), #endif - attemptedWrite(false) + mAttemptedWrite(false) { - oldTs.tv_sec = 0; - oldTs.tv_nsec = 0; - measuredWarmupTs.tv_sec = 0; - measuredWarmupTs.tv_nsec = 0; + mOldTs.tv_sec = 0; + mOldTs.tv_nsec = 0; + mMeasuredWarmupTs.tv_sec = 0; + mMeasuredWarmupTs.tv_nsec = 0; } FastThread::~FastThread() @@ -84,34 +88,34 @@ bool FastThread::threadLoop() for (;;) { // either nanosleep, sched_yield, or busy wait - if (sleepNs >= 0) { - if (sleepNs > 0) { - ALOG_ASSERT(sleepNs < 1000000000); - const struct timespec req = {0, sleepNs}; + if (mSleepNs >= 0) { + if (mSleepNs > 0) { + ALOG_ASSERT(mSleepNs < 1000000000); + const struct timespec req = {0, mSleepNs}; nanosleep(&req, NULL); } else { sched_yield(); } } // default to long sleep for next cycle - sleepNs = FAST_DEFAULT_NS; + mSleepNs = FAST_DEFAULT_NS; // poll for state change const FastThreadState *next = poll(); if (next == NULL) { // continue to use the default initial state until a real state is available - // FIXME &initial not available, should save address earlier - //ALOG_ASSERT(current == &initial && previous == &initial); - next = current; + // FIXME &sInitial not available, should save address earlier + //ALOG_ASSERT(mCurrent == &sInitial && previous == &sInitial); + next = mCurrent; } - command = next->mCommand; - if (next != current) { + mCommand = next->mCommand; + if (next != mCurrent) { // As soon as possible of learning of a new dump area, start using it - dumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState; - logWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &dummyLogWriter; - setLog(logWriter); + mDumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState; + mLogWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &mDummyLogWriter; + setLog(mLogWriter); // We want to always have a valid reference to the previous (non-idle) state. // However, the state queue only guarantees access to current and previous states. @@ -122,37 +126,38 @@ bool FastThread::threadLoop() // non-idle -> idle update previous from copy of current // idle -> idle don't update previous // idle -> non-idle don't update previous - if (!(current->mCommand & FastThreadState::IDLE)) { - if (command & FastThreadState::IDLE) { + if (!(mCurrent->mCommand & FastThreadState::IDLE)) { + if (mCommand & FastThreadState::IDLE) { onIdle(); - oldTsValid = false; -#ifdef FAST_MIXER_STATISTICS - oldLoadValid = false; + mOldTsValid = false; +#ifdef FAST_THREAD_STATISTICS + mOldLoadValid = false; #endif - ignoreNextOverrun = true; + mIgnoreNextOverrun = true; } - previous = current; + mPrevious = mCurrent; } - current = next; + mCurrent = next; } #if !LOG_NDEBUG next = NULL; // not referenced again #endif - dumpState->mCommand = command; + mDumpState->mCommand = mCommand; + // FIXME what does this comment mean? // << current, previous, command, dumpState >> - switch (command) { + switch (mCommand) { case FastThreadState::INITIAL: case FastThreadState::HOT_IDLE: - sleepNs = FAST_HOT_IDLE_NS; + mSleepNs = FAST_HOT_IDLE_NS; continue; case FastThreadState::COLD_IDLE: // only perform a cold idle command once // FIXME consider checking previous state and only perform if previous != COLD_IDLE - if (current->mColdGen != coldGen) { - int32_t *coldFutexAddr = current->mColdFutexAddr; + if (mCurrent->mColdGen != mColdGen) { + int32_t *coldFutexAddr = mCurrent->mColdFutexAddr; ALOG_ASSERT(coldFutexAddr != NULL); int32_t old = android_atomic_dec(coldFutexAddr); if (old <= 0) { @@ -164,41 +169,42 @@ bool FastThread::threadLoop() } // This may be overly conservative; there could be times that the normal mixer // requests such a brief cold idle that it doesn't require resetting this flag. - isWarm = false; - measuredWarmupTs.tv_sec = 0; - measuredWarmupTs.tv_nsec = 0; - warmupCycles = 0; - sleepNs = -1; - coldGen = current->mColdGen; -#ifdef FAST_MIXER_STATISTICS - bounds = 0; - full = false; + mIsWarm = false; + mMeasuredWarmupTs.tv_sec = 0; + mMeasuredWarmupTs.tv_nsec = 0; + mWarmupCycles = 0; + mWarmupConsecutiveInRangeCycles = 0; + mSleepNs = -1; + mColdGen = mCurrent->mColdGen; +#ifdef FAST_THREAD_STATISTICS + mBounds = 0; + mFull = false; #endif - oldTsValid = !clock_gettime(CLOCK_MONOTONIC, &oldTs); - timestampStatus = INVALID_OPERATION; + mOldTsValid = !clock_gettime(CLOCK_MONOTONIC, &mOldTs); + mTimestampStatus = INVALID_OPERATION; } else { - sleepNs = FAST_HOT_IDLE_NS; + mSleepNs = FAST_HOT_IDLE_NS; } continue; case FastThreadState::EXIT: onExit(); return false; default: - LOG_ALWAYS_FATAL_IF(!isSubClassCommand(command)); + LOG_ALWAYS_FATAL_IF(!isSubClassCommand(mCommand)); break; } // there is a non-idle state available to us; did the state change? - if (current != previous) { + if (mCurrent != mPrevious) { onStateChange(); #if 1 // FIXME shouldn't need this // only process state change once - previous = current; + mPrevious = mCurrent; #endif } // do work using current state here - attemptedWrite = false; + mAttemptedWrite = false; onWork(); // To be exactly periodic, compute the next sleep time based on current time. @@ -207,13 +213,13 @@ bool FastThread::threadLoop() struct timespec newTs; int rc = clock_gettime(CLOCK_MONOTONIC, &newTs); if (rc == 0) { - //logWriter->logTimestamp(newTs); - if (oldTsValid) { - time_t sec = newTs.tv_sec - oldTs.tv_sec; - long nsec = newTs.tv_nsec - oldTs.tv_nsec; + //mLogWriter->logTimestamp(newTs); + if (mOldTsValid) { + time_t sec = newTs.tv_sec - mOldTs.tv_sec; + long nsec = newTs.tv_nsec - mOldTs.tv_nsec; ALOGE_IF(sec < 0 || (sec == 0 && nsec < 0), "clock_gettime(CLOCK_MONOTONIC) failed: was %ld.%09ld but now %ld.%09ld", - oldTs.tv_sec, oldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec); + mOldTs.tv_sec, mOldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec); if (nsec < 0) { --sec; nsec += 1000000000; @@ -221,62 +227,70 @@ bool FastThread::threadLoop() // To avoid an initial underrun on fast tracks after exiting standby, // do not start pulling data from tracks and mixing until warmup is complete. // Warmup is considered complete after the earlier of: - // MIN_WARMUP_CYCLES write() attempts and last one blocks for at least warmupNs + // MIN_WARMUP_CYCLES consecutive in-range write() attempts, + // where "in-range" means mWarmupNsMin <= cycle time <= mWarmupNsMax // MAX_WARMUP_CYCLES write() attempts. // This is overly conservative, but to get better accuracy requires a new HAL API. - if (!isWarm && attemptedWrite) { - measuredWarmupTs.tv_sec += sec; - measuredWarmupTs.tv_nsec += nsec; - if (measuredWarmupTs.tv_nsec >= 1000000000) { - measuredWarmupTs.tv_sec++; - measuredWarmupTs.tv_nsec -= 1000000000; + if (!mIsWarm && mAttemptedWrite) { + mMeasuredWarmupTs.tv_sec += sec; + mMeasuredWarmupTs.tv_nsec += nsec; + if (mMeasuredWarmupTs.tv_nsec >= 1000000000) { + mMeasuredWarmupTs.tv_sec++; + mMeasuredWarmupTs.tv_nsec -= 1000000000; } - ++warmupCycles; - if ((nsec > warmupNs && warmupCycles >= MIN_WARMUP_CYCLES) || - (warmupCycles >= MAX_WARMUP_CYCLES)) { - isWarm = true; - dumpState->mMeasuredWarmupTs = measuredWarmupTs; - dumpState->mWarmupCycles = warmupCycles; + ++mWarmupCycles; + if (mWarmupNsMin <= nsec && nsec <= mWarmupNsMax) { + ALOGV("warmup cycle %d in range: %.03f ms", mWarmupCycles, nsec * 1e-9); + ++mWarmupConsecutiveInRangeCycles; + } else { + ALOGV("warmup cycle %d out of range: %.03f ms", mWarmupCycles, nsec * 1e-9); + mWarmupConsecutiveInRangeCycles = 0; + } + if ((mWarmupConsecutiveInRangeCycles >= MIN_WARMUP_CYCLES) || + (mWarmupCycles >= MAX_WARMUP_CYCLES)) { + mIsWarm = true; + mDumpState->mMeasuredWarmupTs = mMeasuredWarmupTs; + mDumpState->mWarmupCycles = mWarmupCycles; } } - sleepNs = -1; - if (isWarm) { - if (sec > 0 || nsec > underrunNs) { + mSleepNs = -1; + if (mIsWarm) { + if (sec > 0 || nsec > mUnderrunNs) { ATRACE_NAME("underrun"); // FIXME only log occasionally ALOGV("underrun: time since last cycle %d.%03ld sec", (int) sec, nsec / 1000000L); - dumpState->mUnderruns++; - ignoreNextOverrun = true; - } else if (nsec < overrunNs) { - if (ignoreNextOverrun) { - ignoreNextOverrun = false; + mDumpState->mUnderruns++; + mIgnoreNextOverrun = true; + } else if (nsec < mOverrunNs) { + if (mIgnoreNextOverrun) { + mIgnoreNextOverrun = false; } else { // FIXME only log occasionally ALOGV("overrun: time since last cycle %d.%03ld sec", (int) sec, nsec / 1000000L); - dumpState->mOverruns++; + mDumpState->mOverruns++; } // This forces a minimum cycle time. It: // - compensates for an audio HAL with jitter due to sample rate conversion // - works with a variable buffer depth audio HAL that never pulls at a - // rate < than overrunNs per buffer. + // rate < than mOverrunNs per buffer. // - recovers from overrun immediately after underrun // It doesn't work with a non-blocking audio HAL. - sleepNs = forceNs - nsec; + mSleepNs = mForceNs - nsec; } else { - ignoreNextOverrun = false; + mIgnoreNextOverrun = false; } } -#ifdef FAST_MIXER_STATISTICS - if (isWarm) { +#ifdef FAST_THREAD_STATISTICS + if (mIsWarm) { // advance the FIFO queue bounds - size_t i = bounds & (dumpState->mSamplingN - 1); - bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF); - if (full) { - bounds += 0x10000; - } else if (!(bounds & (dumpState->mSamplingN - 1))) { - full = true; + size_t i = mBounds & (mDumpState->mSamplingN - 1); + mBounds = (mBounds & 0xFFFF0000) | ((mBounds + 1) & 0xFFFF); + if (mFull) { + mBounds += 0x10000; + } else if (!(mBounds & (mDumpState->mSamplingN - 1))) { + mFull = true; } // compute the delta value of clock_gettime(CLOCK_MONOTONIC) uint32_t monotonicNs = nsec; @@ -288,9 +302,9 @@ bool FastThread::threadLoop() struct timespec newLoad; rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad); if (rc == 0) { - if (oldLoadValid) { - sec = newLoad.tv_sec - oldLoad.tv_sec; - nsec = newLoad.tv_nsec - oldLoad.tv_nsec; + if (mOldLoadValid) { + sec = newLoad.tv_sec - mOldLoad.tv_sec; + nsec = newLoad.tv_nsec - mOldLoad.tv_nsec; if (nsec < 0) { --sec; nsec += 1000000000; @@ -301,42 +315,42 @@ bool FastThread::threadLoop() } } else { // first time through the loop - oldLoadValid = true; + mOldLoadValid = true; } - oldLoad = newLoad; + mOldLoad = newLoad; } #ifdef CPU_FREQUENCY_STATISTICS // get the absolute value of CPU clock frequency in kHz int cpuNum = sched_getcpu(); - uint32_t kHz = tcu.getCpukHz(cpuNum); + uint32_t kHz = mTcu.getCpukHz(cpuNum); kHz = (kHz << 4) | (cpuNum & 0xF); #endif // save values in FIFO queues for dumpsys // these stores #1, #2, #3 are not atomic with respect to each other, // or with respect to store #4 below - dumpState->mMonotonicNs[i] = monotonicNs; - dumpState->mLoadNs[i] = loadNs; + mDumpState->mMonotonicNs[i] = monotonicNs; + mDumpState->mLoadNs[i] = loadNs; #ifdef CPU_FREQUENCY_STATISTICS - dumpState->mCpukHz[i] = kHz; + mDumpState->mCpukHz[i] = kHz; #endif // this store #4 is not atomic with respect to stores #1, #2, #3 above, but // the newest open & oldest closed halves are atomic with respect to each other - dumpState->mBounds = bounds; + mDumpState->mBounds = mBounds; ATRACE_INT("cycle_ms", monotonicNs / 1000000); ATRACE_INT("load_us", loadNs / 1000); } #endif } else { // first time through the loop - oldTsValid = true; - sleepNs = periodNs; - ignoreNextOverrun = true; + mOldTsValid = true; + mSleepNs = mPeriodNs; + mIgnoreNextOverrun = true; } - oldTs = newTs; + mOldTs = newTs; } else { // monotonic clock is broken - oldTsValid = false; - sleepNs = periodNs; + mOldTsValid = false; + mSleepNs = mPeriodNs; } } // for (;;) diff --git a/services/audioflinger/FastThread.h b/services/audioflinger/FastThread.h index 1330334..2efb6de 100644 --- a/services/audioflinger/FastThread.h +++ b/services/audioflinger/FastThread.h @@ -48,42 +48,45 @@ protected: virtual void onStateChange() = 0; virtual void onWork() = 0; - // FIXME these former local variables need comments and to be renamed to have an "m" prefix - const FastThreadState *previous; - const FastThreadState *current; - struct timespec oldTs; - bool oldTsValid; - long sleepNs; // -1: busy wait, 0: sched_yield, > 0: nanosleep - long periodNs; // expected period; the time required to render one mix buffer - long underrunNs; // underrun likely when write cycle is greater than this value - long overrunNs; // overrun likely when write cycle is less than this value - long forceNs; // if overrun detected, force the write cycle to take this much time - long warmupNs; // warmup complete when write cycle is greater than to this value - FastThreadDumpState *mDummyDumpState; - FastThreadDumpState *dumpState; - bool ignoreNextOverrun; // used to ignore initial overrun and first after an underrun -#ifdef FAST_MIXER_STATISTICS - struct timespec oldLoad; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) - bool oldLoadValid; // whether oldLoad is valid - uint32_t bounds; - bool full; // whether we have collected at least mSamplingN samples + // FIXME these former local variables need comments + const FastThreadState* mPrevious; + const FastThreadState* mCurrent; + struct timespec mOldTs; + bool mOldTsValid; + long mSleepNs; // -1: busy wait, 0: sched_yield, > 0: nanosleep + long mPeriodNs; // expected period; the time required to render one mix buffer + long mUnderrunNs; // underrun likely when write cycle is greater than this value + long mOverrunNs; // overrun likely when write cycle is less than this value + long mForceNs; // if overrun detected, + // force the write cycle to take this much time + long mWarmupNsMin; // warmup complete when write cycle is greater than or equal to + // this value + long mWarmupNsMax; // and less than or equal to this value + FastThreadDumpState* mDummyDumpState; + FastThreadDumpState* mDumpState; + bool mIgnoreNextOverrun; // used to ignore initial overrun and first after an + // underrun +#ifdef FAST_THREAD_STATISTICS + struct timespec mOldLoad; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) + bool mOldLoadValid; // whether oldLoad is valid + uint32_t mBounds; + bool mFull; // whether we have collected at least mSamplingN samples #ifdef CPU_FREQUENCY_STATISTICS - ThreadCpuUsage tcu; // for reading the current CPU clock frequency in kHz + ThreadCpuUsage mTcu; // for reading the current CPU clock frequency in kHz #endif #endif - unsigned coldGen; // last observed mColdGen - bool isWarm; // true means ready to mix, false means wait for warmup before mixing - struct timespec measuredWarmupTs; // how long did it take for warmup to complete - uint32_t warmupCycles; // counter of number of loop cycles required to warmup - NBLog::Writer dummyLogWriter; - NBLog::Writer *logWriter; - status_t timestampStatus; + unsigned mColdGen; // last observed mColdGen + bool mIsWarm; // true means ready to mix, + // false means wait for warmup before mixing + struct timespec mMeasuredWarmupTs; // how long did it take for warmup to complete + uint32_t mWarmupCycles; // counter of number of loop cycles during warmup phase + uint32_t mWarmupConsecutiveInRangeCycles; // number of consecutive cycles in range + NBLog::Writer mDummyLogWriter; + NBLog::Writer* mLogWriter; + status_t mTimestampStatus; - FastThreadState::Command command; -#if 0 - size_t frameCount; -#endif - bool attemptedWrite; + FastThreadState::Command mCommand; + bool mAttemptedWrite; }; // class FastThread diff --git a/services/audioflinger/FastThreadDumpState.cpp b/services/audioflinger/FastThreadDumpState.cpp new file mode 100644 index 0000000..9df5c4c --- /dev/null +++ b/services/audioflinger/FastThreadDumpState.cpp @@ -0,0 +1,58 @@ +/* + * 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 "FastThreadDumpState.h" + +namespace android { + +FastThreadDumpState::FastThreadDumpState() : + mCommand(FastThreadState::INITIAL), mUnderruns(0), mOverruns(0), + /* mMeasuredWarmupTs({0, 0}), */ + mWarmupCycles(0) +#ifdef FAST_THREAD_STATISTICS + , mSamplingN(0), mBounds(0) +#endif +{ + mMeasuredWarmupTs.tv_sec = 0; + mMeasuredWarmupTs.tv_nsec = 0; +#ifdef FAST_THREAD_STATISTICS + increaseSamplingN(1); +#endif +} + +FastThreadDumpState::~FastThreadDumpState() +{ +} + +#ifdef FAST_THREAD_STATISTICS +void FastThreadDumpState::increaseSamplingN(uint32_t samplingN) +{ + if (samplingN <= mSamplingN || samplingN > kSamplingN || roundup(samplingN) != samplingN) { + return; + } + uint32_t additional = samplingN - mSamplingN; + // sample arrays aren't accessed atomically with respect to the bounds, + // so clearing reduces chance for dumpsys to read random uninitialized samples + memset(&mMonotonicNs[mSamplingN], 0, sizeof(mMonotonicNs[0]) * additional); + memset(&mLoadNs[mSamplingN], 0, sizeof(mLoadNs[0]) * additional); +#ifdef CPU_FREQUENCY_STATISTICS + memset(&mCpukHz[mSamplingN], 0, sizeof(mCpukHz[0]) * additional); +#endif + mSamplingN = samplingN; +} +#endif + +} // android diff --git a/services/audioflinger/FastThreadDumpState.h b/services/audioflinger/FastThreadDumpState.h new file mode 100644 index 0000000..1ce0914 --- /dev/null +++ b/services/audioflinger/FastThreadDumpState.h @@ -0,0 +1,72 @@ +/* + * 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_DUMP_STATE_H +#define ANDROID_AUDIO_FAST_THREAD_DUMP_STATE_H + +#include "Configuration.h" +#include "FastThreadState.h" + +namespace android { + +// The FastThreadDumpState keeps a cache of FastThread statistics that can be logged by dumpsys. +// Each individual native word-sized field is accessed atomically. But the +// overall structure is non-atomic, that is there may be an inconsistency between fields. +// No barriers or locks are used for either writing or reading. +// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks). +// It has a different lifetime than the FastThread, and so it can't be a member of FastThread. +struct FastThreadDumpState { + FastThreadDumpState(); + /*virtual*/ ~FastThreadDumpState(); + + FastThreadState::Command mCommand; // current command + uint32_t mUnderruns; // total number of underruns + uint32_t mOverruns; // total number of overruns + struct timespec mMeasuredWarmupTs; // measured warmup time + uint32_t mWarmupCycles; // number of loop cycles required to warmup + +#ifdef FAST_THREAD_STATISTICS + // Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency. + // kSamplingN is max size of sampling frame (statistics), and must be a power of 2 <= 0x8000. + // The sample arrays are virtually allocated based on this compile-time constant, + // but are only initialized and used based on the runtime parameter mSamplingN. + static const uint32_t kSamplingN = 0x8000; + // Compile-time constant for a "low RAM device", must be a power of 2 <= kSamplingN. + // This value was chosen such that each array uses 1 small page (4 Kbytes). + static const uint32_t kSamplingNforLowRamDevice = 0x400; + // Corresponding runtime maximum size of sample arrays, must be a power of 2 <= kSamplingN. + uint32_t mSamplingN; + // The bounds define the interval of valid samples, and are represented as follows: + // newest open (excluded) endpoint = lower 16 bits of bounds, modulo N + // oldest closed (included) endpoint = upper 16 bits of bounds, modulo N + // Number of valid samples is newest - oldest. + uint32_t mBounds; // bounds for mMonotonicNs, mThreadCpuNs, and mCpukHz + // The elements in the *Ns arrays are in units of nanoseconds <= 3999999999. + uint32_t mMonotonicNs[kSamplingN]; // delta monotonic (wall clock) time + uint32_t mLoadNs[kSamplingN]; // delta CPU load in time +#ifdef CPU_FREQUENCY_STATISTICS + uint32_t mCpukHz[kSamplingN]; // absolute CPU clock frequency in kHz, bits 0-3 are CPU# +#endif + + // Increase sampling window after construction, must be a power of 2 <= kSamplingN + void increaseSamplingN(uint32_t samplingN); +#endif + +}; // struct FastThreadDumpState + +} // android + +#endif // ANDROID_AUDIO_FAST_THREAD_DUMP_STATE_H diff --git a/services/audioflinger/FastThreadState.cpp b/services/audioflinger/FastThreadState.cpp index 6994872..ad5f31f 100644 --- a/services/audioflinger/FastThreadState.cpp +++ b/services/audioflinger/FastThreadState.cpp @@ -29,21 +29,16 @@ FastThreadState::~FastThreadState() { } - -FastThreadDumpState::FastThreadDumpState() : - mCommand(FastThreadState::INITIAL), mUnderruns(0), mOverruns(0), - /* mMeasuredWarmupTs({0, 0}), */ - mWarmupCycles(0) -#ifdef FAST_MIXER_STATISTICS - , mSamplingN(1), mBounds(0) -#endif -{ - mMeasuredWarmupTs.tv_sec = 0; - mMeasuredWarmupTs.tv_nsec = 0; -} - -FastThreadDumpState::~FastThreadDumpState() +// static +const char *FastThreadState::commandToString(FastThreadState::Command command) { + switch (command) { + case FastThreadState::INITIAL: return "INITIAL"; + case FastThreadState::HOT_IDLE: return "HOT_IDLE"; + case FastThreadState::COLD_IDLE: return "COLD_IDLE"; + case FastThreadState::EXIT: return "EXIT"; + } + return NULL; } } // namespace android diff --git a/services/audioflinger/FastThreadState.h b/services/audioflinger/FastThreadState.h index 1ab8a0a..f18f846 100644 --- a/services/audioflinger/FastThreadState.h +++ b/services/audioflinger/FastThreadState.h @@ -46,43 +46,10 @@ struct FastThreadState { FastThreadDumpState* mDumpState; // if non-NULL, then update dump state periodically NBLog::Writer* mNBLogWriter; // non-blocking logger + // returns NULL if command belongs to a subclass + static const char *commandToString(Command command); }; // struct FastThreadState - -// FIXME extract common part of comment at FastMixerDumpState -struct FastThreadDumpState { - FastThreadDumpState(); - /*virtual*/ ~FastThreadDumpState(); - - FastThreadState::Command mCommand; // current command - uint32_t mUnderruns; // total number of underruns - uint32_t mOverruns; // total number of overruns - struct timespec mMeasuredWarmupTs; // measured warmup time - uint32_t mWarmupCycles; // number of loop cycles required to warmup - -#ifdef FAST_MIXER_STATISTICS - // Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency. - // kSamplingN is max size of sampling frame (statistics), and must be a power of 2 <= 0x8000. - // The sample arrays are virtually allocated based on this compile-time constant, - // but are only initialized and used based on the runtime parameter mSamplingN. - static const uint32_t kSamplingN = 0x8000; - // Corresponding runtime maximum size of sample arrays, must be a power of 2 <= kSamplingN. - uint32_t mSamplingN; - // The bounds define the interval of valid samples, and are represented as follows: - // newest open (excluded) endpoint = lower 16 bits of bounds, modulo N - // oldest closed (included) endpoint = upper 16 bits of bounds, modulo N - // Number of valid samples is newest - oldest. - uint32_t mBounds; // bounds for mMonotonicNs, mThreadCpuNs, and mCpukHz - // The elements in the *Ns arrays are in units of nanoseconds <= 3999999999. - uint32_t mMonotonicNs[kSamplingN]; // delta monotonic (wall clock) time - uint32_t mLoadNs[kSamplingN]; // delta CPU load in time -#ifdef CPU_FREQUENCY_STATISTICS - uint32_t mCpukHz[kSamplingN]; // absolute CPU clock frequency in kHz, bits 0-3 are CPU# -#endif -#endif - -}; // struct FastThreadDumpState - } // android #endif // ANDROID_AUDIO_FAST_THREAD_STATE_H diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 7544052..efbdcff 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -166,7 +166,9 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa if (*handle == mPatches[index]->mHandle) { ALOGV("createAudioPatch() removing patch handle %d", *handle); halHandle = mPatches[index]->mHalHandle; + Patch *removedPatch = mPatches[index]; mPatches.removeAt(index); + delete removedPatch; break; } } @@ -692,4 +694,4 @@ status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_co } -}; // namespace android +} // namespace android diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index ee48276..902d5e4 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -255,7 +255,7 @@ public: class Buffer : public AudioBufferProvider::Buffer { public: - int16_t *mBuffer; + void *mBuffer; }; OutputTrack(PlaybackThread *thread, @@ -271,7 +271,7 @@ public: AudioSystem::SYNC_EVENT_NONE, int triggerSession = 0); virtual void stop(); - bool write(int16_t* data, uint32_t frames); + bool write(void* data, uint32_t frames); bool bufferQueueEmpty() const { return mBufferQueue.size() == 0; } bool isActive() const { return mActive; } const wp<ThreadBase>& thread() const { return mThread; } diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp index 40d7bcd..9d4188f 100644 --- a/services/audioflinger/StateQueue.cpp +++ b/services/audioflinger/StateQueue.cpp @@ -48,7 +48,7 @@ template<typename T> StateQueue<T>::StateQueue() : , mObserverDump(&mObserverDummyDump), mMutatorDump(&mMutatorDummyDump) #endif { - atomic_init(&mNext, 0); + atomic_init(&mNext, static_cast<uintptr_t>(0)); } template<typename T> StateQueue<T>::~StateQueue() diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 6b9da83..9881764 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -23,7 +23,9 @@ #include "Configuration.h" #include <math.h> #include <fcntl.h> +#include <linux/futex.h> #include <sys/stat.h> +#include <sys/syscall.h> #include <cutils/properties.h> #include <media/AudioParameter.h> #include <media/AudioResamplerPublic.h> @@ -314,6 +316,165 @@ void CpuStats::sample(const String8 &title // ThreadBase // ---------------------------------------------------------------------------- +// static +const char *AudioFlinger::ThreadBase::threadTypeToString(AudioFlinger::ThreadBase::type_t type) +{ + switch (type) { + case MIXER: + return "MIXER"; + case DIRECT: + return "DIRECT"; + case DUPLICATING: + return "DUPLICATING"; + case RECORD: + return "RECORD"; + case OFFLOAD: + return "OFFLOAD"; + default: + return "unknown"; + } +} + +String8 devicesToString(audio_devices_t devices) +{ + static const struct mapping { + audio_devices_t mDevices; + const char * mString; + } mappingsOut[] = { + AUDIO_DEVICE_OUT_EARPIECE, "EARPIECE", + AUDIO_DEVICE_OUT_SPEAKER, "SPEAKER", + AUDIO_DEVICE_OUT_WIRED_HEADSET, "WIRED_HEADSET", + AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "WIRED_HEADPHONE", + AUDIO_DEVICE_OUT_TELEPHONY_TX, "TELEPHONY_TX", + AUDIO_DEVICE_NONE, "NONE", // must be last + }, mappingsIn[] = { + AUDIO_DEVICE_IN_BUILTIN_MIC, "BUILTIN_MIC", + AUDIO_DEVICE_IN_WIRED_HEADSET, "WIRED_HEADSET", + AUDIO_DEVICE_IN_VOICE_CALL, "VOICE_CALL", + AUDIO_DEVICE_IN_REMOTE_SUBMIX, "REMOTE_SUBMIX", + AUDIO_DEVICE_NONE, "NONE", // must be last + }; + String8 result; + audio_devices_t allDevices = AUDIO_DEVICE_NONE; + const mapping *entry; + if (devices & AUDIO_DEVICE_BIT_IN) { + devices &= ~AUDIO_DEVICE_BIT_IN; + entry = mappingsIn; + } else { + entry = mappingsOut; + } + for ( ; entry->mDevices != AUDIO_DEVICE_NONE; entry++) { + allDevices = (audio_devices_t) (allDevices | entry->mDevices); + if (devices & entry->mDevices) { + if (!result.isEmpty()) { + result.append("|"); + } + result.append(entry->mString); + } + } + if (devices & ~allDevices) { + if (!result.isEmpty()) { + result.append("|"); + } + result.appendFormat("0x%X", devices & ~allDevices); + } + if (result.isEmpty()) { + result.append(entry->mString); + } + return result; +} + +String8 inputFlagsToString(audio_input_flags_t flags) +{ + static const struct mapping { + audio_input_flags_t mFlag; + const char * mString; + } mappings[] = { + AUDIO_INPUT_FLAG_FAST, "FAST", + AUDIO_INPUT_FLAG_HW_HOTWORD, "HW_HOTWORD", + AUDIO_INPUT_FLAG_NONE, "NONE", // must be last + }; + String8 result; + audio_input_flags_t allFlags = AUDIO_INPUT_FLAG_NONE; + const mapping *entry; + for (entry = mappings; entry->mFlag != AUDIO_INPUT_FLAG_NONE; entry++) { + allFlags = (audio_input_flags_t) (allFlags | entry->mFlag); + if (flags & entry->mFlag) { + if (!result.isEmpty()) { + result.append("|"); + } + result.append(entry->mString); + } + } + if (flags & ~allFlags) { + if (!result.isEmpty()) { + result.append("|"); + } + result.appendFormat("0x%X", flags & ~allFlags); + } + if (result.isEmpty()) { + result.append(entry->mString); + } + return result; +} + +String8 outputFlagsToString(audio_output_flags_t flags) +{ + static const struct mapping { + audio_output_flags_t mFlag; + const char * mString; + } mappings[] = { + AUDIO_OUTPUT_FLAG_DIRECT, "DIRECT", + AUDIO_OUTPUT_FLAG_PRIMARY, "PRIMARY", + AUDIO_OUTPUT_FLAG_FAST, "FAST", + AUDIO_OUTPUT_FLAG_DEEP_BUFFER, "DEEP_BUFFER", + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD, "COMPRESS_OFFLOAD", + AUDIO_OUTPUT_FLAG_NON_BLOCKING, "NON_BLOCKING", + AUDIO_OUTPUT_FLAG_HW_AV_SYNC, "HW_AV_SYNC", + AUDIO_OUTPUT_FLAG_NONE, "NONE", // must be last + }; + String8 result; + audio_output_flags_t allFlags = AUDIO_OUTPUT_FLAG_NONE; + const mapping *entry; + for (entry = mappings; entry->mFlag != AUDIO_OUTPUT_FLAG_NONE; entry++) { + allFlags = (audio_output_flags_t) (allFlags | entry->mFlag); + if (flags & entry->mFlag) { + if (!result.isEmpty()) { + result.append("|"); + } + result.append(entry->mString); + } + } + if (flags & ~allFlags) { + if (!result.isEmpty()) { + result.append("|"); + } + result.appendFormat("0x%X", flags & ~allFlags); + } + if (result.isEmpty()) { + result.append(entry->mString); + } + return result; +} + +const char *sourceToString(audio_source_t source) +{ + switch (source) { + case AUDIO_SOURCE_DEFAULT: return "default"; + case AUDIO_SOURCE_MIC: return "mic"; + case AUDIO_SOURCE_VOICE_UPLINK: return "voice uplink"; + case AUDIO_SOURCE_VOICE_DOWNLINK: return "voice downlink"; + case AUDIO_SOURCE_VOICE_CALL: return "voice call"; + case AUDIO_SOURCE_CAMCORDER: return "camcorder"; + case AUDIO_SOURCE_VOICE_RECOGNITION: return "voice recognition"; + case AUDIO_SOURCE_VOICE_COMMUNICATION: return "voice communication"; + case AUDIO_SOURCE_REMOTE_SUBMIX: return "remote submix"; + case AUDIO_SOURCE_FM_TUNER: return "FM tuner"; + case AUDIO_SOURCE_HOTWORD: return "hotword"; + default: return "unknown"; + } +} + AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, audio_devices_t outDevice, audio_devices_t inDevice, type_t type) : Thread(false /*canCallJava*/), @@ -338,7 +499,7 @@ AudioFlinger::ThreadBase::~ThreadBase() // do not lock the mutex in destructor releaseWakeLock_l(); if (mPowerManager != 0) { - sp<IBinder> binder = mPowerManager->asBinder(); + sp<IBinder> binder = IInterface::asBinder(mPowerManager); binder->unlinkToDeath(mDeathRecipient); } } @@ -577,20 +738,22 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __u bool locked = AudioFlinger::dumpTryLock(mLock); if (!locked) { - dprintf(fd, "thread %p maybe dead locked\n", this); + dprintf(fd, "thread %p may be deadlocked\n", this); } + dprintf(fd, " Thread name: %s\n", mThreadName); dprintf(fd, " I/O handle: %d\n", mId); dprintf(fd, " TID: %d\n", getTid()); dprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); - dprintf(fd, " Sample rate: %u\n", mSampleRate); + dprintf(fd, " Sample rate: %u Hz\n", mSampleRate); dprintf(fd, " HAL frame count: %zu\n", mFrameCount); + dprintf(fd, " HAL format: 0x%x (%s)\n", mHALFormat, formatToString(mHALFormat)); dprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize); - dprintf(fd, " Channel Count: %u\n", mChannelCount); - dprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask, + dprintf(fd, " Channel count: %u\n", mChannelCount); + dprintf(fd, " Channel mask: 0x%08x (%s)\n", mChannelMask, channelMaskToString(mChannelMask, mType != RECORD).string()); - dprintf(fd, " Format: 0x%x (%s)\n", mHALFormat, formatToString(mHALFormat)); - dprintf(fd, " Frame size: %zu\n", mFrameSize); + dprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat)); + dprintf(fd, " Frame size: %zu bytes\n", mFrameSize); dprintf(fd, " Pending config events:"); size_t numConfig = mConfigEvents.size(); if (numConfig) { @@ -602,6 +765,9 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __u } else { dprintf(fd, " none\n"); } + dprintf(fd, " Output device: %#x (%s)\n", mOutDevice, devicesToString(mOutDevice).string()); + dprintf(fd, " Input device: %#x (%s)\n", mInDevice, devicesToString(mInDevice).string()); + dprintf(fd, " Audio source: %d (%s)\n", mAudioSource, sourceToString(mAudioSource)); if (locked) { mLock.unlock(); @@ -635,19 +801,19 @@ void AudioFlinger::ThreadBase::acquireWakeLock(int uid) String16 AudioFlinger::ThreadBase::getWakeLockTag() { switch (mType) { - case MIXER: - return String16("AudioMix"); - case DIRECT: - return String16("AudioDirectOut"); - case DUPLICATING: - return String16("AudioDup"); - case RECORD: - return String16("AudioIn"); - case OFFLOAD: - return String16("AudioOffload"); - default: - ALOG_ASSERT(false); - return String16("AudioUnknown"); + case MIXER: + return String16("AudioMix"); + case DIRECT: + return String16("AudioDirectOut"); + case DUPLICATING: + return String16("AudioDup"); + case RECORD: + return String16("AudioIn"); + case OFFLOAD: + return String16("AudioOffload"); + default: + ALOG_ASSERT(false); + return String16("AudioUnknown"); } } @@ -674,7 +840,7 @@ void AudioFlinger::ThreadBase::acquireWakeLock_l(int uid) if (status == NO_ERROR) { mWakeLockToken = binder; } - ALOGV("acquireWakeLock_l() %s status %d", mName, status); + ALOGV("acquireWakeLock_l() %s status %d", mThreadName, status); } } @@ -687,7 +853,7 @@ void AudioFlinger::ThreadBase::releaseWakeLock() void AudioFlinger::ThreadBase::releaseWakeLock_l() { if (mWakeLockToken != 0) { - ALOGV("releaseWakeLock_l() %s", mName); + ALOGV("releaseWakeLock_l() %s", mThreadName); if (mPowerManager != 0) { mPowerManager->releaseWakeLock(mWakeLockToken, 0, true /* FIXME force oneway contrary to .aidl */); @@ -708,7 +874,7 @@ void AudioFlinger::ThreadBase::getPowerManager_l() { sp<IBinder> binder = defaultServiceManager()->checkService(String16("power")); if (binder == 0) { - ALOGW("Thread %s cannot connect to the power manager service", mName); + ALOGW("Thread %s cannot connect to the power manager service", mThreadName); } else { mPowerManager = interface_cast<IPowerManager>(binder); binder->linkToDeath(mDeathRecipient); @@ -728,7 +894,7 @@ void AudioFlinger::ThreadBase::updateWakeLockUids_l(const SortedVector<int> &uid status_t status; status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array(), true /* FIXME force oneway contrary to .aidl */); - ALOGV("acquireWakeLock_l() %s status %d", mName, status); + ALOGV("acquireWakeLock_l() %s status %d", mThreadName, status); } } @@ -912,7 +1078,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( // 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); + desc->name, mThreadName); lStatus = BAD_VALUE; goto Exit; } @@ -936,7 +1102,8 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( case DUPLICATING: case RECORD: default: - ALOGW("createEffect_l() Cannot add global effect %s on thread %s", desc->name, mName); + ALOGW("createEffect_l() Cannot add global effect %s on thread %s", + desc->name, mThreadName); lStatus = BAD_VALUE; goto Exit; } @@ -1197,11 +1364,12 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge mScreenState(AudioFlinger::mScreenState), // index 0 is reserved for normal mixer's submix mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1), + mHwSupportsPause(false), mHwPaused(false), mFlushPending(false), // mLatchD, mLatchQ, mLatchDValid(false), mLatchQValid(false) { - snprintf(mName, kNameLength, "AudioOut_%X", id); - mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName); + snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id); + mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName); // Assumes constructor is called by AudioFlinger with it's mLock held, but // it would be safer to explicitly pass initial masterVolume/masterMute as @@ -1224,15 +1392,12 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge 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 + // ++ operator does not compile 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); } - // mStreamTypes[AUDIO_STREAM_CNT] exists but isn't explicitly initialized here, - // because mAudioFlinger doesn't have one to copy from } AudioFlinger::PlaybackThread::~PlaybackThread() @@ -1317,7 +1482,10 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { - dprintf(fd, "\nOutput thread %p:\n", this); + dprintf(fd, "\nOutput thread %p type %d (%s):\n", this, type(), threadTypeToString(type())); + + dumpBase(fd, args); + dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); dprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); dprintf(fd, " Total writes: %d\n", mNumWrites); @@ -1328,15 +1496,17 @@ void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& dprintf(fd, " Mixer buffer: %p\n", mMixerBuffer); dprintf(fd, " Effect buffer: %p\n", mEffectBuffer); dprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); - - dumpBase(fd, args); + AudioStreamOut *output = mOutput; + audio_output_flags_t flags = output != NULL ? output->flags : AUDIO_OUTPUT_FLAG_NONE; + String8 flagsAsString = outputFlagsToString(flags); + dprintf(fd, " AudioStreamOut: %p flags %#x (%s)\n", output, flags, flagsAsString.string()); } // Thread virtuals void AudioFlinger::PlaybackThread::onFirstRef() { - run(mName, ANDROID_PRIORITY_URGENT_AUDIO); + run(mThreadName, ANDROID_PRIORITY_URGENT_AUDIO); } // ThreadBase virtuals @@ -1380,9 +1550,10 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac ( (sharedBuffer != 0) ) || - // use case 2: callback handler and frame count is default or at least as large as HAL + // use case 2: frame count is default or at least as large as HAL ( - (tid != -1) && + // we formerly checked for a callback handler (non-0 tid), + // but that is no longer required for TRANSFER_OBTAIN mode ((frameCount == 0) || (frameCount >= mFrameCount)) ) @@ -1422,20 +1593,25 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac audio_is_linear_pcm(format), channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask); *flags &= ~IAudioFlinger::TRACK_FAST; - // For compatibility with AudioTrack calculation, buffer depth is forced - // to be at least 2 x the normal mixer frame count and cover audio hardware latency. - // This is probably too conservative, but legacy application code may depend on it. - // If you change this calculation, also review the start threshold which is related. + } + } + // For normal PCM streaming tracks, update minimum frame count. + // For compatibility with AudioTrack calculation, buffer depth is forced + // to be at least 2 x the normal mixer frame count and cover audio hardware latency. + // This is probably too conservative, but legacy application code may depend on it. + // If you change this calculation, also review the start threshold which is related. + if (!(*flags & IAudioFlinger::TRACK_FAST) + && audio_is_linear_pcm(format) && sharedBuffer == 0) { uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream); uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate); if (minBufCount < 2) { minBufCount = 2; } - size_t minFrameCount = mNormalFrameCount * minBufCount; - if (frameCount < minFrameCount) { + size_t minFrameCount = + minBufCount * sourceFramesNeeded(sampleRate, mNormalFrameCount, mSampleRate); + if (frameCount < minFrameCount) { // including frameCount == 0 frameCount = minFrameCount; } - } } *pFrameCount = frameCount; @@ -1625,13 +1801,15 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track) if (track->isExternalTrack()) { TrackBase::track_state state = track->mState; mLock.unlock(); - status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId()); + status = AudioSystem::startOutput(mId, track->streamType(), + (audio_session_t)track->sessionId()); mLock.lock(); // abort track was stopped/paused while we released the lock if (state != track->mState) { if (status == NO_ERROR) { mLock.unlock(); - AudioSystem::stopOutput(mId, track->streamType(), track->sessionId()); + AudioSystem::stopOutput(mId, track->streamType(), + (audio_session_t)track->sessionId()); mLock.lock(); } return INVALID_OPERATION; @@ -1848,6 +2026,35 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() } } + mHwSupportsPause = false; + if (mOutput->flags & AUDIO_OUTPUT_FLAG_DIRECT) { + if (mOutput->stream->pause != NULL) { + if (mOutput->stream->resume != NULL) { + mHwSupportsPause = true; + } else { + ALOGW("direct output implements pause but not resume"); + } + } else if (mOutput->stream->resume != NULL) { + ALOGW("direct output implements resume but not pause"); + } + } + + if (mType == DUPLICATING && mMixerBufferEnabled && mEffectBufferEnabled) { + // For best precision, we use float instead of the associated output + // device format (typically PCM 16 bit). + + mFormat = AUDIO_FORMAT_PCM_FLOAT; + mFrameSize = mChannelCount * audio_bytes_per_sample(mFormat); + mBufferSize = mFrameSize * mFrameCount; + + // TODO: We currently use the associated output device channel mask and sample rate. + // (1) Perhaps use the ORed channel mask of all downstream MixerThreads + // (if a valid mask) to avoid premature downmix. + // (2) Perhaps use the maximum sample rate of all downstream MixerThreads + // instead of the output device sample rate to avoid loss of high frequency information. + // This may need to be updated as MixerThread/OutputTracks are added and not here. + } + // Calculate size of normal sink buffer relative to the HAL output buffer size double multiplier = 1.0; if (mType == MIXER && (kUseFastMixer == FastMixer_Static || @@ -2060,13 +2267,15 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks( for (size_t i = 0 ; i < count ; i++) { const sp<Track>& track = tracksToRemove.itemAt(i); if (track->isExternalTrack()) { - AudioSystem::stopOutput(mId, track->streamType(), track->sessionId()); + AudioSystem::stopOutput(mId, track->streamType(), + (audio_session_t)track->sessionId()); #ifdef ADD_BATTERY_DATA // to track the speaker usage addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop); #endif if (track->isTerminated()) { - AudioSystem::releaseOutput(mId); + AudioSystem::releaseOutput(mId, track->streamType(), + (audio_session_t)track->sessionId()); } } } @@ -2180,7 +2389,13 @@ void AudioFlinger::PlaybackThread::threadLoop_drain() void AudioFlinger::PlaybackThread::threadLoop_exit() { - // Default implementation has nothing to do + { + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mTracks.size(); i++) { + sp<Track> track = mTracks[i]; + track->invalidate(); + } + } } /* @@ -2620,7 +2835,9 @@ bool AudioFlinger::PlaybackThread::threadLoop() } } else { + ATRACE_BEGIN("sleep"); usleep(sleepTime); + ATRACE_END(); } } @@ -2688,7 +2905,8 @@ status_t AudioFlinger::PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp) if (mNormalSink != 0) { return mNormalSink->getTimestamp(timestamp); } - if ((mType == OFFLOAD || mType == DIRECT) && mOutput->stream->get_presentation_position) { + if ((mType == OFFLOAD || mType == DIRECT) + && mOutput != NULL && mOutput->stream->get_presentation_position) { uint64_t position64; int ret = mOutput->stream->get_presentation_position( mOutput->stream, &position64, ×tamp.mTime); @@ -2779,6 +2997,12 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud mNormalFrameCount); mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate); + if (type == DUPLICATING) { + // The Duplicating thread uses the AudioMixer and delivers data to OutputTracks + // (downstream MixerThreads) in DuplicatingThread::threadLoop_write(). + // Do not create or use mFastMixer, mOutputSink, mPipeSink, or mNormalSink. + return; + } // create an NBAIO sink for the HAL output stream, and negotiate mOutputSink = new AudioStreamOutSink(output->stream); size_t numCounterOffers = 0; @@ -2820,6 +3044,7 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud NBAIO_Format format = mOutputSink->format(); NBAIO_Format origformat = format; // adjust format to match that of the Fast Mixer + ALOGV("format changed from %d to %d", format.mFormat, fastMixerFormat); format.mFormat = fastMixerFormat; format.mFrameSize = audio_bytes_per_sample(format.mFormat) * format.mChannelCount; @@ -2999,8 +3224,10 @@ ssize_t AudioFlinger::MixerThread::threadLoop_write() #endif } state->mCommand = FastMixerState::MIX_WRITE; +#ifdef FAST_THREAD_STATISTICS mFastMixerDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ? - FastMixerDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN); + FastThreadDumpState::kSamplingNforLowRamDevice : FastThreadDumpState::kSamplingN); +#endif sq->end(); sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED); if (kUseFastMixer == FastMixer_Dynamic) { @@ -3071,6 +3298,7 @@ void AudioFlinger::PlaybackThread::threadLoop_standby() mCallbackThread->setWriteBlocked(mWriteAckSequence); mCallbackThread->setDraining(mDrainSequence); } + mHwPaused = false; } void AudioFlinger::PlaybackThread::onAddNewTrack_l() @@ -3364,8 +3592,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac if (sr == mSampleRate) { desiredFrames = mNormalFrameCount; } else { - // +1 for rounding and +1 for additional sample needed for interpolation - desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1; + desiredFrames = sourceFramesNeeded(sr, mNormalFrameCount, mSampleRate); // add frames already consumed but not yet released by the resampler // because mAudioTrackServerProxy->framesReady() will include these frames desiredFrames += mAudioMixer->getUnreleasedFrames(track->name()); @@ -3383,6 +3610,23 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } size_t framesReady = track->framesReady(); + if (ATRACE_ENABLED()) { + // I wish we had formatted trace names + char traceName[16]; + strcpy(traceName, "nRdy"); + int name = track->name(); + if (AudioMixer::TRACK0 <= name && + name < (int) (AudioMixer::TRACK0 + AudioMixer::MAX_NUM_TRACKS)) { + name -= AudioMixer::TRACK0; + traceName[4] = (name / 10) + '0'; + traceName[5] = (name % 10) + '0'; + } else { + traceName[4] = '?'; + traceName[5] = '?'; + } + traceName[6] = '\0'; + ATRACE_INT(traceName, framesReady); + } if ((framesReady >= minFrames) && track->isReady() && !track->isPaused() && !track->isTerminated()) { @@ -3983,6 +4227,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep { size_t count = mActiveTracks.size(); mixer_state mixerStatus = MIXER_IDLE; + bool doHwPause = false; + bool doHwResume = false; + bool flushPending = false; // find out which tracks need to be processed for (size_t i = 0; i < count; i++) { @@ -4001,10 +4248,37 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep sp<Track> l = mLatestActiveTrack.promote(); bool last = l.get() == track; + if (mHwSupportsPause && track->isPausing()) { + track->setPaused(); + if (last && !mHwPaused) { + doHwPause = true; + mHwPaused = true; + } + tracksToRemove->add(track); + } else if (track->isFlushPending()) { + track->flushAck(); + if (last) { + flushPending = true; + } + } else if (mHwSupportsPause && track->isResumePending()){ + track->resumeAck(); + if (last) { + if (mHwPaused) { + doHwResume = true; + mHwPaused = false; + } + } + } + // The first time a track is added we wait - // for all its buffers to be filled before processing it + // for all its buffers to be filled before processing it. + // Allow draining the buffer in case the client + // app does not call stop() and relies on underrun to stop: + // hence the test on (track->mRetryCount > 1). + // If retryCount<=1 then track is about to underrun and be removed. uint32_t minFrames; - if ((track->sharedBuffer() == 0) && !track->isStopping_1() && !track->isPausing()) { + if ((track->sharedBuffer() == 0) && !track->isStopping_1() && !track->isPausing() + && (track->mRetryCount > 1)) { minFrames = mNormalFrameCount; } else { minFrames = 1; @@ -4019,8 +4293,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep 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 (!mHwSupportsPause) { + track->resumeAck(); } } @@ -4031,6 +4305,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep track->mRetryCount = kMaxTrackRetriesDirect; mActiveTrack = t; mixerStatus = MIXER_TRACKS_READY; + if (usesHwAvSync() && mHwPaused) { + doHwResume = true; + mHwPaused = false; + } } } else { // clear effect chain input buffer if the last active track started underruns @@ -4059,9 +4337,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep track->mState = TrackBase::STOPPED; } if (track->isStopped()) { - if (track->mState == TrackBase::FLUSHED) { - flushHw_l(); - } track->reset(); } tracksToRemove->add(track); @@ -4078,11 +4353,39 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep android_atomic_or(CBLK_DISABLED, &cblk->mFlags); } else if (last) { mixerStatus = MIXER_TRACKS_ENABLED; + if (usesHwAvSync() && !mHwPaused && !mStandby) { + doHwPause = true; + mHwPaused = true; + } } } } } + // if an active track did not command a flush, check for pending flush on stopped tracks + if (!flushPending) { + for (size_t i = 0; i < mTracks.size(); i++) { + if (mTracks[i]->isFlushPending()) { + mTracks[i]->flushAck(); + flushPending = true; + } + } + } + + // make sure the pause/flush/resume sequence is executed in the right order. + // If a flush is pending and a track is active but the HW is not paused, force a HW pause + // before flush and then resume HW. This can happen in case of pause/flush/resume + // if resume is received before pause is executed. + if (mHwSupportsPause && !mStandby && + (doHwPause || (flushPending && !mHwPaused && (count != 0)))) { + mOutput->stream->pause(mOutput->stream); + } + if (flushPending) { + flushHw_l(); + } + if (mHwSupportsPause && !mStandby && doHwResume) { + mOutput->stream->resume(mOutput->stream); + } // remove all the tracks that need to be... removeTracks_l(*tracksToRemove); @@ -4115,6 +4418,11 @@ void AudioFlinger::DirectOutputThread::threadLoop_mix() void AudioFlinger::DirectOutputThread::threadLoop_sleepTime() { + // do not write to HAL when paused + if (mHwPaused || (usesHwAvSync() && mStandby)) { + sleepTime = idleSleepTime; + return; + } if (sleepTime == 0) { if (mMixerStatus == MIXER_TRACKS_ENABLED) { sleepTime = activeSleepTime; @@ -4127,6 +4435,38 @@ void AudioFlinger::DirectOutputThread::threadLoop_sleepTime() } } +void AudioFlinger::DirectOutputThread::threadLoop_exit() +{ + { + Mutex::Autolock _l(mLock); + bool flushPending = false; + for (size_t i = 0; i < mTracks.size(); i++) { + if (mTracks[i]->isFlushPending()) { + mTracks[i]->flushAck(); + flushPending = true; + } + } + if (flushPending) { + flushHw_l(); + } + } + PlaybackThread::threadLoop_exit(); +} + +// must be called with thread mutex locked +bool AudioFlinger::DirectOutputThread::shouldStandby_l() +{ + 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(); + } + + return !mStandby && !(trackPaused || (usesHwAvSync() && mHwPaused)); +} + // getTrackName_l() must be called with ThreadBase::mLock held int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused, audio_format_t format __unused, int sessionId __unused) @@ -4236,8 +4576,10 @@ void AudioFlinger::DirectOutputThread::cacheParameters_l() void AudioFlinger::DirectOutputThread::flushHw_l() { - if (mOutput->stream->flush != NULL) + if (mOutput->stream->flush != NULL) { mOutput->stream->flush(mOutput->stream); + } + mHwPaused = false; } // ---------------------------------------------------------------------------- @@ -4346,8 +4688,6 @@ void AudioFlinger::AsyncCallbackThread::resetDraining() AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, audio_io_handle_t id, uint32_t device) : DirectOutputThread(audioFlinger, output, id, device, OFFLOAD), - mHwPaused(false), - mFlushPending(false), mPausedBytesRemaining(0) { //FIXME: mStandby should be set to true by ThreadBase constructor @@ -4584,21 +4924,6 @@ bool AudioFlinger::OffloadThread::waitingAsyncCallback_l() return false; } -// must be called with thread mutex locked -bool AudioFlinger::OffloadThread::shouldStandby_l() -{ - 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(); - } - - return !mStandby && !trackPaused; -} - - bool AudioFlinger::OffloadThread::waitingAsyncCallback() { Mutex::Autolock _l(mLock); @@ -4613,7 +4938,6 @@ 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 @@ -4661,7 +4985,11 @@ void AudioFlinger::DuplicatingThread::threadLoop_mix() if (outputsReady(outputTracks)) { mAudioMixer->process(AudioBufferProvider::kInvalidPTS); } else { - memset(mSinkBuffer, 0, mSinkBufferSize); + if (mMixerBufferValid) { + memset(mMixerBuffer, 0, mMixerBufferSize); + } else { + memset(mSinkBuffer, 0, mSinkBufferSize); + } } sleepTime = 0; writeFrames = mNormalFrameCount; @@ -4692,15 +5020,7 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime() ssize_t AudioFlinger::DuplicatingThread::threadLoop_write() { for (size_t i = 0; i < outputTracks.size(); i++) { - // 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); + outputTracks[i]->write(mSinkBuffer, writeFrames); } mStandby = false; return (ssize_t)mSinkBufferSize; @@ -4727,25 +5047,26 @@ void AudioFlinger::DuplicatingThread::clearOutputTracks() 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, + // The downstream MixerThread consumes thread->frameCount() amount of frames per mix pass. + // Adjust for thread->sampleRate() to determine minimum buffer frame count. + // Then triple buffer because Threads do not run synchronously and may not be clock locked. + const size_t frameCount = + 3 * sourceFramesNeeded(mSampleRate, thread->frameCount(), thread->sampleRate()); + // TODO: Consider asynchronous sample rate conversion to handle clock disparity + // from different OutputTracks and their associated MixerThreads (e.g. one may + // nearly empty and the other may be dropping data). + + sp<OutputTrack> outputTrack = new OutputTrack(thread, this, mSampleRate, - AUDIO_FORMAT_PCM_16_BIT, + mFormat, mChannelMask, frameCount, IPCThreadState::self()->getCallingUid()); if (outputTrack->cblk() != NULL) { - thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f); + thread->setStreamVolume(AUDIO_STREAM_PATCH, 1.0f); mOutputTracks.add(outputTrack); - ALOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread); + ALOGV("addOutputTrack() track %p, on thread %p", outputTrack.get(), thread); updateWaitTime_l(); } } @@ -4846,8 +5167,8 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, // mFastCaptureNBLogWriter , mFastTrackAvail(false) { - snprintf(mName, kNameLength, "AudioIn_%X", id); - mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName); + snprintf(mThreadName, kThreadNameLength, "AudioIn_%X", id); + mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName); readInputParameters_l(); @@ -4988,7 +5309,7 @@ AudioFlinger::RecordThread::~RecordThread() void AudioFlinger::RecordThread::onFirstRef() { - run(mName, PRIORITY_URGENT_AUDIO); + run(mThreadName, PRIORITY_URGENT_AUDIO); } bool AudioFlinger::RecordThread::threadLoop() @@ -5029,7 +5350,9 @@ reacquire_wakelock: // sleep with mutex unlocked if (sleepUs > 0) { + ATRACE_BEGIN("sleep"); usleep(sleepUs); + ATRACE_END(); sleepUs = 0; } @@ -5173,7 +5496,8 @@ reacquire_wakelock: state->mCommand = FastCaptureState::READ_WRITE; #if 0 // FIXME mFastCaptureDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ? - FastCaptureDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN); + FastThreadDumpState::kSamplingNforLowRamDevice : + FastThreadDumpState::kSamplingN); #endif didModify = true; } @@ -5321,8 +5645,8 @@ reacquire_wakelock: upmix_to_stereo_i16_from_mono_i16((int16_t *)dst, (const int16_t *)src, part1); } else { - downmix_to_mono_i16_from_stereo_i16((int16_t *)dst, (const int16_t *)src, - part1); + downmix_to_mono_i16_from_stereo_i16((int16_t *)dst, + (const int16_t *)src, part1); } dst += part1 * activeTrack->mFrameSize; front += part1; @@ -5833,15 +6157,17 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& a { dprintf(fd, "\nInput thread %p:\n", this); - if (mActiveTracks.size() > 0) { - dprintf(fd, " Buffer size: %zu bytes\n", mBufferSize); - } else { + dumpBase(fd, args); + + if (mActiveTracks.size() == 0) { dprintf(fd, " No active record clients\n"); } dprintf(fd, " Fast capture thread: %s\n", hasFastCapture() ? "yes" : "no"); dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no"); - dumpBase(fd, args); + // Make a non-atomic copy of fast capture dump state so it won't change underneath us + const FastCaptureDumpState copy(mFastCaptureDumpState); + copy.dump(fd); } void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args __unused) @@ -6306,4 +6632,4 @@ void AudioFlinger::RecordThread::getAudioPortConfig(struct audio_port_config *co config->ext.mix.usecase.source = mAudioSource; } -}; // namespace android +} // namespace android diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index bb9aa18..d600ea9 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -32,6 +32,8 @@ public: OFFLOAD // Thread class is OffloadThread }; + static const char *threadTypeToString(type_t type); + ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, audio_devices_t outDevice, audio_devices_t inDevice, type_t type); virtual ~ThreadBase(); @@ -406,6 +408,7 @@ protected: audio_channel_mask_t mChannelMask; uint32_t mChannelCount; size_t mFrameSize; + // not HAL frame size, this is for output sink (to pipe to fast mixer) audio_format_t mFormat; // Source format for Recording and // Sink format for Playback. // Sink format may be different than @@ -424,13 +427,13 @@ protected: 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) + audio_source_t mAudioSource; const audio_io_handle_t mId; Vector< sp<EffectChain> > mEffectChains; - static const int kNameLength = 16; // prctl(PR_SET_NAME) limit - char mName[kNameLength]; + static const int kThreadNameLength = 16; // prctl(PR_SET_NAME) limit + char mThreadName[kThreadNameLength]; // guaranteed NUL-terminated sp<IPowerManager> mPowerManager; sp<IBinder> mWakeLockToken; const sp<PMDeathRecipient> mDeathRecipient; @@ -710,6 +713,9 @@ protected: audio_patch_handle_t *handle); virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle); + bool usesHwAvSync() const { return (mType == DIRECT) && (mOutput != NULL) && + (mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC); } + private: friend class AudioFlinger; // for numerous @@ -727,9 +733,7 @@ private: void dumpTracks(int fd, const Vector<String16>& args); SortedVector< sp<Track> > mTracks; - // mStreamTypes[] uses 1 additional stream type internally for the OutputTrack used by - // DuplicatingThread - stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1]; + stream_type_t mStreamTypes[AUDIO_STREAM_CNT]; AudioStreamOut *mOutput; float mMasterVolume; @@ -811,7 +815,9 @@ public: protected: // accessed by both binder threads and within threadLoop(), lock on mutex needed unsigned mFastTrackAvailMask; // bit i set if fast track [i] is available - + bool mHwSupportsPause; + bool mHwPaused; + bool mFlushPending; private: // timestamp latch: // D input is written by threadLoop_write while mutex is unlocked, and read while locked @@ -912,6 +918,8 @@ protected: virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove); virtual void threadLoop_mix(); virtual void threadLoop_sleepTime(); + virtual void threadLoop_exit(); + virtual bool shouldStandby_l(); // volumes last sent to audio HAL with stream->set_volume() float mLeftVolFloat; @@ -942,12 +950,9 @@ protected: virtual bool waitingAsyncCallback(); virtual bool waitingAsyncCallback_l(); - virtual bool shouldStandby_l(); virtual void onAddNewTrack_l(); private: - bool mHwPaused; - bool mFlushPending; size_t mPausedWriteLength; // length in bytes of write interrupted by pause size_t mPausedBytesRemaining; // bytes still waiting in mixbuffer after resume wp<Track> mPreviousTrack; // used to detect track switch @@ -1165,7 +1170,8 @@ private: const sp<MemoryDealer> mReadOnlyHeap; // one-time initialization, no locks required - sp<FastCapture> mFastCapture; // non-0 if there is also a fast capture + sp<FastCapture> mFastCapture; // non-0 if there is also + // a fast capture // FIXME audio watchdog thread // contents are not guaranteed to be consistent, no locks required diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 7d8d4c8..38667b9 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -20,6 +20,7 @@ //#define LOG_NDEBUG 0 #include "Configuration.h" +#include <linux/futex.h> #include <math.h> #include <sys/syscall.h> #include <utils/Log.h> @@ -443,8 +444,6 @@ AudioFlinger::PlaybackThread::Track::Track( // 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); } } @@ -491,7 +490,7 @@ void AudioFlinger::PlaybackThread::Track::destroy() wasActive = playbackThread->destroyTrack_l(this); } if (isExternalTrack() && !wasActive) { - AudioSystem::releaseOutput(mThreadIoHandle); + AudioSystem::releaseOutput(mThreadIoHandle, mStreamType, (audio_session_t)mSessionId); } } } @@ -611,15 +610,16 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer( // ExtendedAudioBufferProvider interface -// Note that framesReady() takes a mutex on the control block using tryLock(). -// This could result in priority inversion if framesReady() is called by the normal mixer, -// as the normal mixer thread runs at lower -// priority than the client's callback thread: there is a short window within framesReady() -// during which the normal mixer could be preempted, and the client callback would block. -// Another problem can occur if framesReady() is called by the fast mixer: -// the tryLock() could block for up to 1 ms, and a sequence of these could delay fast mixer. -// FIXME Replace AudioTrackShared control block implementation by a non-blocking FIFO queue. +// framesReady() may return an approximation of the number of frames if called +// from a different thread than the one calling Proxy->obtainBuffer() and +// Proxy->releaseBuffer(). Also note there is no mutual exclusion in the +// AudioTrackServerProxy so be especially careful calling with FastTracks. size_t AudioFlinger::PlaybackThread::Track::framesReady() const { + if (mSharedBuffer != 0 && (isStopped() || isStopping())) { + // Static tracks return zero frames immediately upon stopping (for FastTracks). + // The remainder of the buffer is not drained. + return 0; + } return mAudioTrackServerProxy->framesReady(); } @@ -692,6 +692,12 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev } PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + if (isFastTrack()) { + // refresh fast track underruns on start because that field is never cleared + // by the fast mixer; furthermore, the same track can be recycled, i.e. start + // after stop. + mObservedUnderruns = playbackThread->getFastTrackUnderruns(mFastIndex); + } status = playbackThread->addTrack_l(this); if (status == INVALID_OPERATION || status == PERMISSION_DENIED) { triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE); @@ -822,12 +828,11 @@ void AudioFlinger::PlaybackThread::Track::flush() // this will be done by prepareTracks_l() when the track is stopped. // prepareTracks_l() will see mState == FLUSHED, then // remove from active track list, reset(), and trigger presentation complete + if (isDirect()) { + mFlushHwPending = true; + } if (playbackThread->mActiveTracks.indexOf(this) < 0) { reset(); - if (thread->type() == ThreadBase::DIRECT) { - DirectOutputThread *t = (DirectOutputThread *)playbackThread; - t->flushHw_l(); - } } } // Prevent flush being lost if the track is flushed and then resumed @@ -840,7 +845,7 @@ void AudioFlinger::PlaybackThread::Track::flush() // must be called with thread lock held void AudioFlinger::PlaybackThread::Track::flushAck() { - if (!isOffloaded()) + if (!isOffloaded() && !isDirect()) return; mFlushHwPending = false; @@ -1657,8 +1662,9 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( audio_channel_mask_t channelMask, size_t frameCount, int uid) - : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, - NULL, 0, 0, uid, IAudioFlinger::TRACK_DEFAULT, TYPE_OUTPUT), + : Track(playbackThread, NULL, AUDIO_STREAM_PATCH, + sampleRate, format, channelMask, frameCount, + NULL, 0, 0, uid, IAudioFlinger::TRACK_DEFAULT, TYPE_OUTPUT), mActive(false), mSourceThread(sourceThread), mClientProxy(NULL) { @@ -1709,36 +1715,18 @@ void AudioFlinger::PlaybackThread::OutputTrack::stop() mActive = false; } -bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames) +bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frames) { Buffer *pInBuffer; Buffer inBuffer; - uint32_t channelCount = mChannelCount; bool outputBufferFull = false; inBuffer.frameCount = frames; - inBuffer.i16 = data; + inBuffer.raw = data; uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs(); if (!mActive && frames != 0) { - start(); - sp<ThreadBase> thread = mThread.promote(); - if (thread != 0) { - MixerThread *mixerThread = (MixerThread *)thread.get(); - if (mFrameCount > frames) { - if (mBufferQueue.size() < kMaxOverFlowBuffers) { - uint32_t startFrames = (mFrameCount - frames); - pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[startFrames * channelCount]; - pInBuffer->frameCount = startFrames; - pInBuffer->i16 = pInBuffer->mBuffer; - memset(pInBuffer->raw, 0, startFrames * channelCount * sizeof(int16_t)); - mBufferQueue.add(pInBuffer); - } else { - ALOGW("OutputTrack::write() %p no more buffers in queue", this); - } - } - } + (void) start(); } while (waitTimeLeftMs) { @@ -1773,20 +1761,20 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount; - memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t)); + memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * mFrameSize); Proxy::Buffer buf; buf.mFrameCount = outFrames; buf.mRaw = NULL; mClientProxy->releaseBuffer(&buf); pInBuffer->frameCount -= outFrames; - pInBuffer->i16 += outFrames * channelCount; + pInBuffer->raw = (int8_t *)pInBuffer->raw + outFrames * mFrameSize; mOutBuffer.frameCount -= outFrames; - mOutBuffer.i16 += outFrames * channelCount; + mOutBuffer.raw = (int8_t *)mOutBuffer.raw + outFrames * mFrameSize; if (pInBuffer->frameCount == 0) { if (mBufferQueue.size()) { mBufferQueue.removeAt(0); - delete [] pInBuffer->mBuffer; + free(pInBuffer->mBuffer); delete pInBuffer; ALOGV("OutputTrack::write() %p thread %p released overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size()); @@ -1802,11 +1790,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr if (thread != 0 && !thread->standby()) { if (mBufferQueue.size() < kMaxOverFlowBuffers) { pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channelCount]; + pInBuffer->mBuffer = malloc(inBuffer.frameCount * mFrameSize); pInBuffer->frameCount = inBuffer.frameCount; - pInBuffer->i16 = pInBuffer->mBuffer; - memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channelCount * - sizeof(int16_t)); + pInBuffer->raw = pInBuffer->mBuffer; + memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * mFrameSize); mBufferQueue.add(pInBuffer); ALOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size()); @@ -1817,23 +1804,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr } } - // Calling write() with a 0 length buffer, means that no more data will be written: - // If no more buffers are pending, fill output track buffer to make sure it is started - // by output mixer. - if (frames == 0 && mBufferQueue.size() == 0) { - // FIXME borken, replace by getting framesReady() from proxy - size_t user = 0; // was mCblk->user - if (user < mFrameCount) { - frames = mFrameCount - user; - pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[frames * channelCount]; - pInBuffer->frameCount = frames; - pInBuffer->i16 = pInBuffer->mBuffer; - memset(pInBuffer->raw, 0, frames * channelCount * sizeof(int16_t)); - mBufferQueue.add(pInBuffer); - } else if (mActive) { - stop(); - } + // Calling write() with a 0 length buffer means that no more data will be written: + // We rely on stop() to set the appropriate flags to allow the remaining frames to play out. + if (frames == 0 && mBufferQueue.size() == 0 && mActive) { + stop(); } return outputBufferFull; @@ -1859,7 +1833,7 @@ void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue() for (size_t i = 0; i < size; i++) { Buffer *pBuffer = mBufferQueue.itemAt(i); - delete [] pBuffer->mBuffer; + free(pBuffer->mBuffer); delete pBuffer; } mBufferQueue.clear(); @@ -1873,7 +1847,8 @@ AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThr size_t frameCount, void *buffer, IAudioFlinger::track_flags_t flags) - : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, + : Track(playbackThread, NULL, AUDIO_STREAM_PATCH, + sampleRate, format, channelMask, frameCount, buffer, 0, 0, getuid(), flags, TYPE_PATCH), mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true)) { @@ -2211,4 +2186,4 @@ void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(Proxy::Buffer* buffe mProxy->releaseBuffer(buffer); } -}; // namespace android +} // namespace android diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp index 84a655a..7893778 100644 --- a/services/audioflinger/test-resample.cpp +++ b/services/audioflinger/test-resample.cpp @@ -427,6 +427,14 @@ int main(int argc, char* argv[]) { printf("quality: %d channels: %d msec: %" PRId64 " Mfrms/s: %.2lf\n", quality, channels, time/1000000, output_frames * looplimit / (time / 1e9) / 1e6); resampler->reset(); + + // TODO fix legacy bug: reset does not clear buffers. + // delete and recreate resampler here. + delete resampler; + resampler = AudioResampler::create(format, channels, + output_freq, quality); + resampler->setSampleRate(input_freq); + resampler->setVolume(AudioResampler::UNITY_GAIN_FLOAT, AudioResampler::UNITY_GAIN_FLOAT); } memset(output_vaddr, 0, output_size); diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk index 7bba05b..8604ef5 100644 --- a/services/audioflinger/tests/Android.mk +++ b/services/audioflinger/tests/Android.mk @@ -10,19 +10,10 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libutils \ libcutils \ - libstlport \ libaudioutils \ libaudioresampler -LOCAL_STATIC_LIBRARIES := \ - libgtest \ - libgtest_main - LOCAL_C_INCLUDES := \ - bionic \ - bionic/libstdc++/include \ - external/gtest/include \ - external/stlport/stlport \ $(call include-path-for, audio-utils) \ frameworks/av/services/audioflinger @@ -32,21 +23,24 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := resampler_tests LOCAL_MODULE_TAGS := tests -include $(BUILD_EXECUTABLE) +include $(BUILD_NATIVE_TEST) # # audio mixer test tool # include $(CLEAR_VARS) +# Clang++ aborts on AudioMixer.cpp, +# b/18373866, "do not know how to split this operator." +ifeq ($(filter $(TARGET_ARCH),arm arm64),$(TARGET_ARCH)) + LOCAL_CLANG := false +endif + LOCAL_SRC_FILES:= \ test-mixer.cpp \ ../AudioMixer.cpp.arm \ LOCAL_C_INCLUDES := \ - bionic \ - bionic/libstdc++/include \ - external/stlport/stlport \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) \ frameworks/av/services/audioflinger @@ -55,7 +49,6 @@ LOCAL_STATIC_LIBRARIES := \ libsndfile LOCAL_SHARED_LIBRARIES := \ - libstlport \ libeffects \ libnbaio \ libcommon_time_client \ @@ -70,4 +63,6 @@ LOCAL_MODULE:= test-mixer LOCAL_MODULE_TAGS := optional +LOCAL_CXX_STL := libc++ + include $(BUILD_EXECUTABLE) diff --git a/services/audioflinger/tests/build_and_run_all_unit_tests.sh b/services/audioflinger/tests/build_and_run_all_unit_tests.sh index 2c453b0..7f4d456 100755 --- a/services/audioflinger/tests/build_and_run_all_unit_tests.sh +++ b/services/audioflinger/tests/build_and_run_all_unit_tests.sh @@ -15,7 +15,7 @@ mm echo "waiting for device" adb root && adb wait-for-device remount adb push $OUT/system/lib/libaudioresampler.so /system/lib -adb push $OUT/system/bin/resampler_tests /system/bin +adb push $OUT/data/nativetest/resampler_tests /system/bin sh $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/tests/run_all_unit_tests.sh diff --git a/services/audioflinger/tests/mixer_to_wav_tests.sh b/services/audioflinger/tests/mixer_to_wav_tests.sh index e60e6d5..d0482a1 100755 --- a/services/audioflinger/tests/mixer_to_wav_tests.sh +++ b/services/audioflinger/tests/mixer_to_wav_tests.sh @@ -60,7 +60,7 @@ function createwav() { fi # Test: -# process__genericResampling +# process__genericResampling with mixed integer and float track input # track__Resample / track__genericResample adb shell test-mixer $1 -s 48000 \ -o /sdcard/tm48000grif.wav \ diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index 6512c38..351ed79 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -3,19 +3,19 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - AudioPolicyService.cpp \ - AudioPolicyEffects.cpp + service/AudioPolicyService.cpp \ + service/AudioPolicyEffects.cpp ifeq ($(USE_LEGACY_AUDIO_POLICY), 1) LOCAL_SRC_FILES += \ - AudioPolicyInterfaceImplLegacy.cpp \ - AudioPolicyClientImplLegacy.cpp + service/AudioPolicyInterfaceImplLegacy.cpp \ + service/AudioPolicyClientImplLegacy.cpp LOCAL_CFLAGS += -DUSE_LEGACY_AUDIO_POLICY else LOCAL_SRC_FILES += \ - AudioPolicyInterfaceImpl.cpp \ - AudioPolicyClientImpl.cpp + service/AudioPolicyInterfaceImpl.cpp \ + service/AudioPolicyClientImpl.cpp endif LOCAL_C_INCLUDES := \ @@ -30,7 +30,8 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libmedia \ libhardware \ - libhardware_legacy + libhardware_legacy \ + libserviceutility ifneq ($(USE_LEGACY_AUDIO_POLICY), 1) LOCAL_SHARED_LIBRARIES += \ @@ -38,8 +39,7 @@ LOCAL_SHARED_LIBRARIES += \ endif LOCAL_STATIC_LIBRARIES := \ - libmedia_helper \ - libserviceutility + libmedia_helper LOCAL_MODULE:= libaudiopolicyservice @@ -53,7 +53,15 @@ ifneq ($(USE_LEGACY_AUDIO_POLICY), 1) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - AudioPolicyManager.cpp + managerdefault/AudioPolicyManager.cpp \ + managerdefault/ConfigParsingUtils.cpp \ + managerdefault/Devices.cpp \ + managerdefault/Gains.cpp \ + managerdefault/HwModule.cpp \ + managerdefault/IOProfile.cpp \ + managerdefault/Ports.cpp \ + managerdefault/AudioInputDescriptor.cpp \ + managerdefault/AudioOutputDescriptor.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -73,7 +81,7 @@ ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - AudioPolicyFactory.cpp + manager/AudioPolicyFactory.cpp LOCAL_SHARED_LIBRARIES := \ libaudiopolicymanagerdefault diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 5524463..116d0d6 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -18,6 +18,7 @@ #define ANDROID_AUDIOPOLICY_INTERFACE_H #include <media/AudioSystem.h> +#include <media/AudioPolicy.h> #include <utils/String8.h> #include <hardware/audio_policy.h> @@ -56,6 +57,16 @@ class AudioPolicyInterface { public: + typedef enum { + API_INPUT_INVALID = -1, + API_INPUT_LEGACY = 0,// e.g. audio recording from a microphone + API_INPUT_MIX_CAPTURE,// used for "remote submix", capture of the media to play it remotely + API_INPUT_MIX_EXT_POLICY_REROUTE,// used for platform audio rerouting, where mixes are + // handled by external and dynamically installed + // policies which reroute audio mixes + } input_type_t; + +public: virtual ~AudioPolicyInterface() {} // // configuration functions @@ -64,7 +75,8 @@ public: // 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; + const char *device_address, + const char *device_name) = 0; // retrieve a device connection status virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address) = 0; @@ -90,30 +102,37 @@ public: audio_channel_mask_t channelMask, audio_output_flags_t flags, const audio_offload_info_t *offloadInfo) = 0; - virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) = 0; + virtual status_t getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + 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; + audio_session_t session) = 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; + audio_session_t session) = 0; // releases the output. - virtual void releaseOutput(audio_io_handle_t output) = 0; + virtual void releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) = 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_session_t session, - audio_input_flags_t flags) = 0; + virtual status_t getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags, + input_type_t *inputType) = 0; // indicates to the audio policy manager that the input starts being used. virtual status_t startInput(audio_io_handle_t input, audio_session_t session) = 0; @@ -195,6 +214,9 @@ public: audio_devices_t *device) = 0; virtual status_t releaseSoundTriggerSession(audio_session_t session) = 0; + + virtual status_t registerPolicyMixes(Vector<AudioMix> mixes) = 0; + virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes) = 0; }; diff --git a/services/audiopolicy/AudioPolicyFactory.cpp b/services/audiopolicy/manager/AudioPolicyFactory.cpp index 2ae7bc1..9910a1f 100644 --- a/services/audiopolicy/AudioPolicyFactory.cpp +++ b/services/audiopolicy/manager/AudioPolicyFactory.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "AudioPolicyManager.h" +#include "managerdefault/AudioPolicyManager.h" namespace android { diff --git a/include/media/nbaio/roundup.h b/services/audiopolicy/managerdefault/ApmImplDefinitions.h index 4c3cc25..620979b 100644 --- a/include/media/nbaio/roundup.h +++ b/services/audiopolicy/managerdefault/ApmImplDefinitions.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2015 The Android Open 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,18 +14,19 @@ * limitations under the License. */ -#ifndef ROUNDUP_H -#define ROUNDUP_H +namespace android { -#ifdef __cplusplus -extern "C" { -#endif +enum routing_strategy { + STRATEGY_MEDIA, + STRATEGY_PHONE, + STRATEGY_SONIFICATION, + STRATEGY_SONIFICATION_RESPECTFUL, + STRATEGY_DTMF, + STRATEGY_ENFORCED_AUDIBLE, + STRATEGY_TRANSMITTED_THROUGH_SPEAKER, + STRATEGY_ACCESSIBILITY, + STRATEGY_REROUTING, + NUM_STRATEGIES +}; -// Round up to the next highest power of 2 -unsigned roundup(unsigned v); - -#ifdef __cplusplus -} -#endif - -#endif // ROUNDUP_H +}; //namespace android diff --git a/services/audiopolicy/managerdefault/AudioInputDescriptor.cpp b/services/audiopolicy/managerdefault/AudioInputDescriptor.cpp new file mode 100644 index 0000000..f4054c8 --- /dev/null +++ b/services/audiopolicy/managerdefault/AudioInputDescriptor.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "APM::AudioInputDescriptor" +//#define LOG_NDEBUG 0 + +#include "AudioPolicyManager.h" + +namespace android { + +AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile) + : mId(0), mIoHandle(0), + mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), mPatchHandle(0), mRefCount(0), + mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false) +{ + if (profile != NULL) { + mSamplingRate = profile->pickSamplingRate(); + mFormat = profile->pickFormat(); + mChannelMask = profile->pickChannelMask(); + if (profile->mGains.size() > 0) { + profile->mGains[0]->getDefaultConfig(&mGain); + } + } +} + +void AudioInputDescriptor::toAudioPortConfig( + struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + ALOG_ASSERT(mProfile != 0, + "toAudioPortConfig() called on input with null profile %d", mIoHandle); + dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| + AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; + if (srcConfig != NULL) { + dstConfig->config_mask |= srcConfig->config_mask; + } + + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = AUDIO_PORT_ROLE_SINK; + dstConfig->type = AUDIO_PORT_TYPE_MIX; + dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; + dstConfig->ext.mix.handle = mIoHandle; + dstConfig->ext.mix.usecase.source = mInputSource; +} + +void AudioInputDescriptor::toAudioPort( + struct audio_port *port) const +{ + ALOG_ASSERT(mProfile != 0, "toAudioPort() called on input with null profile %d", mIoHandle); + + mProfile->toAudioPort(port); + port->id = mId; + toAudioPortConfig(&port->active_config); + port->ext.mix.hw_module = mProfile->mModule->mHandle; + port->ext.mix.handle = mIoHandle; + port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL; +} + +status_t AudioInputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " ID: %d\n", mId); + result.append(buffer); + 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); + snprintf(buffer, SIZE, " Open Ref Count %d\n", mOpenRefCount); + result.append(buffer); + + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +}; //namespace android diff --git a/services/audiopolicy/managerdefault/AudioInputDescriptor.h b/services/audiopolicy/managerdefault/AudioInputDescriptor.h new file mode 100644 index 0000000..02579e6 --- /dev/null +++ b/services/audiopolicy/managerdefault/AudioInputDescriptor.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace android { + +// 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 AudioPortConfig +{ +public: + AudioInputDescriptor(const sp<IOProfile>& profile); + + status_t dump(int fd); + + audio_port_handle_t mId; + audio_io_handle_t mIoHandle; // input handle + audio_devices_t mDevice; // current device this input is routed to + AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + audio_patch_handle_t mPatchHandle; + uint32_t mRefCount; // number of AudioRecord clients using + // this input + uint32_t mOpenRefCount; + audio_source_t mInputSource; // input source selected by application + //(mediarecorder.h) + const sp<IOProfile> mProfile; // I/O profile this output derives from + SortedVector<audio_session_t> mSessions; // audio sessions attached to this input + bool mIsSoundTrigger; // used by a soundtrigger capture + + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const; + virtual sp<AudioPort> getAudioPort() const { return mProfile; } + void toAudioPort(struct audio_port *port) const; +}; + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/AudioOutputDescriptor.cpp b/services/audiopolicy/managerdefault/AudioOutputDescriptor.cpp new file mode 100644 index 0000000..4b85972 --- /dev/null +++ b/services/audiopolicy/managerdefault/AudioOutputDescriptor.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "APM::AudioOutputDescriptor" +//#define LOG_NDEBUG 0 + +#include "AudioPolicyManager.h" + +namespace android { + +AudioOutputDescriptor::AudioOutputDescriptor( + const sp<IOProfile>& profile) + : mId(0), mIoHandle(0), mLatency(0), + mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), + mPatchHandle(0), + 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) { + mFlags = (audio_output_flags_t)profile->mFlags; + mSamplingRate = profile->pickSamplingRate(); + mFormat = profile->pickFormat(); + mChannelMask = profile->pickChannelMask(); + if (profile->mGains.size() > 0) { + profile->mGains[0]->getDefaultConfig(&mGain); + } + } +} + +audio_devices_t AudioOutputDescriptor::device() const +{ + if (isDuplicated()) { + return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice); + } else { + return mDevice; + } +} + +uint32_t AudioOutputDescriptor::latency() +{ + if (isDuplicated()) { + return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency; + } else { + return mLatency; + } +} + +bool AudioOutputDescriptor::sharesHwModuleWith( + const sp<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 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 AudioOutputDescriptor::supportedDevices() +{ + if (isDuplicated()) { + return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices()); + } else { + return mProfile->mSupportedDevices.types() ; + } +} + +bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const +{ + return isStrategyActive(NUM_STRATEGIES, inPastMs); +} + +bool 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 (i == AUDIO_STREAM_PATCH) { + continue; + } + if (((AudioPolicyManager::getStrategy((audio_stream_type_t)i) == strategy) || + (NUM_STRATEGIES == strategy)) && + isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool 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; +} + +void AudioOutputDescriptor::toAudioPortConfig( + struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle); + + dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| + AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; + if (srcConfig != NULL) { + dstConfig->config_mask |= srcConfig->config_mask; + } + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = AUDIO_PORT_ROLE_SOURCE; + dstConfig->type = AUDIO_PORT_TYPE_MIX; + dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; + dstConfig->ext.mix.handle = mIoHandle; + dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT; +} + +void AudioOutputDescriptor::toAudioPort( + struct audio_port *port) const +{ + ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle); + mProfile->toAudioPort(port); + port->id = mId; + toAudioPortConfig(&port->active_config); + port->ext.mix.hw_module = mProfile->mModule->mHandle; + port->ext.mix.handle = mIoHandle; + port->ext.mix.latency_class = + mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL; +} + +status_t AudioOutputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " ID: %d\n", mId); + result.append(buffer); + 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; +} + + + +}; //namespace android diff --git a/services/audiopolicy/managerdefault/AudioOutputDescriptor.h b/services/audiopolicy/managerdefault/AudioOutputDescriptor.h new file mode 100644 index 0000000..32f46e4 --- /dev/null +++ b/services/audiopolicy/managerdefault/AudioOutputDescriptor.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ApmImplDefinitions.h" + +namespace android { + +// 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 AudioPortConfig +{ +public: + AudioOutputDescriptor(const sp<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 sp<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; + + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const; + virtual sp<AudioPort> getAudioPort() const { return mProfile; } + void toAudioPort(struct audio_port *port) const; + + audio_port_handle_t mId; + audio_io_handle_t mIoHandle; // output handle + uint32_t mLatency; // + audio_output_flags_t mFlags; // + audio_devices_t mDevice; // current device this output is routed to + AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + audio_patch_handle_t mPatchHandle; + uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output + nsecs_t mStopTime[AUDIO_STREAM_CNT]; + sp<AudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output + sp<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 sp<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) +}; + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 584e170..53ec0f6 100644 --- a/services/audiopolicy/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "AudioPolicyManager" +#define LOG_TAG "APM::AudioPolicyManager" //#define LOG_NDEBUG 0 //#define VERY_VERBOSE_LOGGING @@ -43,6 +43,7 @@ #include <hardware/audio.h> #include <hardware/audio_effect.h> #include <media/AudioParameter.h> +#include <media/AudioPolicyHelper.h> #include <soundtrigger/SoundTrigger.h> #include "AudioPolicyManager.h" #include "audio_policy_conf.h" @@ -50,189 +51,34 @@ 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_SPEAKER_SAFE), - 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_HDMI), - 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_OUT_TELEPHONY_TX), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_LINE), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_HDMI_ARC), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPDIF), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_FM), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_LINE), - STRING_TO_ENUM(AUDIO_DEVICE_IN_AMBIENT), - 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_HDMI), - STRING_TO_ENUM(AUDIO_DEVICE_IN_TELEPHONY_RX), - 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), - STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_DEVICE), - STRING_TO_ENUM(AUDIO_DEVICE_IN_FM_TUNER), - STRING_TO_ENUM(AUDIO_DEVICE_IN_TV_TUNER), - STRING_TO_ENUM(AUDIO_DEVICE_IN_LINE), - STRING_TO_ENUM(AUDIO_DEVICE_IN_SPDIF), - STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP), - STRING_TO_ENUM(AUDIO_DEVICE_IN_LOOPBACK), -}; - -const StringToEnum sOutputFlagNameToEnumTable[] = { - 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), - STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_HW_AV_SYNC), -}; - -const StringToEnum sInputFlagNameToEnumTable[] = { - STRING_TO_ENUM(AUDIO_INPUT_FLAG_FAST), - STRING_TO_ENUM(AUDIO_INPUT_FLAG_HW_HOTWORD), -}; - -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_AAC_MAIN), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_LC), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_SSR), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_LTP), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_HE_V1), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_SCALABLE), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_ERLC), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_LD), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_HE_V2), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_ELD), - STRING_TO_ENUM(AUDIO_FORMAT_VORBIS), - STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V1), - STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V2), - STRING_TO_ENUM(AUDIO_FORMAT_OPUS), - STRING_TO_ENUM(AUDIO_FORMAT_AC3), - STRING_TO_ENUM(AUDIO_FORMAT_E_AC3), -}; - -const StringToEnum sOutChannelsNameToEnumTable[] = { - STRING_TO_ENUM(AUDIO_CHANNEL_OUT_MONO), - STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO), - STRING_TO_ENUM(AUDIO_CHANNEL_OUT_QUAD), - 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), -}; - -const StringToEnum sGainModeNameToEnumTable[] = { - STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT), - STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS), - STRING_TO_ENUM(AUDIO_GAIN_MODE_RAMP), -}; - - -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) + audio_policy_dev_state_t state, + const char *device_address, + const char *device_name) { - String8 address = (device_address == NULL) ? String8("") : String8(device_address); - // handle legacy remote submix case where the address was not always specified - if (deviceDistinguishesOnAddress(device) && (address.length() == 0)) { - address = String8("0"); - } + return setDeviceConnectionStateInt(device, state, device_address, device_name); +} - ALOGV("setDeviceConnectionState() device: %x, state %d, address %s", - device, state, address.string()); +status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address, + const char *device_name) +{ + ALOGV("setDeviceConnectionStateInt() device: 0x%X, state %d, address %s name %s", +- device, state, device_address, device_name); // connect/disconnect only 1 device at a time if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE; + sp<DeviceDescriptor> devDesc = getDeviceDescriptor(device, device_address, device_name); + // handle output devices if (audio_is_output_device(device)) { SortedVector <audio_io_handle_t> outputs; - sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device); - devDesc->mAddress = address; ssize_t index = mAvailableOutputDevices.indexOf(devDesc); // save a copy of the opened output descriptors before any output is opened or closed @@ -241,7 +87,7 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, switch (state) { // handle output device connection - case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: + case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: { if (index >= 0) { ALOGW("setDeviceConnectionState() device already connected: %x", device); return INVALID_OPERATION; @@ -258,13 +104,12 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, mAvailableOutputDevices.remove(devDesc); return INVALID_OPERATION; } - mAvailableOutputDevices[index]->mId = nextUniqueId(); - mAvailableOutputDevices[index]->mModule = module; + mAvailableOutputDevices[index]->attach(module); } else { return NO_MEMORY; } - if (checkOutputsForDevice(devDesc, state, outputs, address) != NO_ERROR) { + if (checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress) != NO_ERROR) { mAvailableOutputDevices.remove(devDesc); return INVALID_OPERATION; } @@ -273,7 +118,13 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, "checkOutputsForDevice() returned no outputs but status OK"); ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs", outputs.size()); - break; + + // Send connect to HALs + AudioParameter param = AudioParameter(devDesc->mAddress); + param.addInt(String8(AUDIO_PARAMETER_DEVICE_CONNECT), device); + mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); + + } break; // handle output device disconnection case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: { if (index < 0) { @@ -283,15 +134,15 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, ALOGV("setDeviceConnectionState() disconnecting output device %x", device); - // Set Disconnect to HALs - AudioParameter param = AudioParameter(address); + // Send Disconnect to HALs + AudioParameter param = AudioParameter(devDesc->mAddress); param.addInt(String8(AUDIO_PARAMETER_DEVICE_DISCONNECT), device); mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); // remove device from available output devices mAvailableOutputDevices.remove(devDesc); - checkOutputsForDevice(devDesc, state, outputs, address); + checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress); } break; default: @@ -348,8 +199,6 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, if (audio_is_input_device(device)) { SortedVector <audio_io_handle_t> inputs; - sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device); - devDesc->mAddress = address; ssize_t index = mAvailableInputDevices.indexOf(devDesc); switch (state) { @@ -365,17 +214,22 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, device); return INVALID_OPERATION; } - if (checkInputsForDevice(device, state, inputs, address) != NO_ERROR) { + if (checkInputsForDevice(device, state, inputs, devDesc->mAddress) != NO_ERROR) { return INVALID_OPERATION; } index = mAvailableInputDevices.add(devDesc); if (index >= 0) { - mAvailableInputDevices[index]->mId = nextUniqueId(); - mAvailableInputDevices[index]->mModule = module; + mAvailableInputDevices[index]->attach(module); } else { return NO_MEMORY; } + + // Set connect to HALs + AudioParameter param = AudioParameter(devDesc->mAddress); + param.addInt(String8(AUDIO_PARAMETER_DEVICE_CONNECT), device); + mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); + } break; // handle input device disconnection @@ -388,11 +242,11 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, ALOGV("setDeviceConnectionState() disconnecting input device %x", device); // Set Disconnect to HALs - AudioParameter param = AudioParameter(address); + AudioParameter param = AudioParameter(devDesc->mAddress); param.addInt(String8(AUDIO_PARAMETER_DEVICE_DISCONNECT), device); mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); - checkInputsForDevice(device, state, inputs, address); + checkInputsForDevice(device, state, inputs, devDesc->mAddress); mAvailableInputDevices.remove(devDesc); } break; @@ -420,14 +274,7 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, 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; - sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device); - devDesc->mAddress = (device_address == NULL) ? String8("") : String8(device_address); - // handle legacy remote submix case where the address was not always specified - if (deviceDistinguishesOnAddress(device) && (devDesc->mAddress.length() == 0)) { - devDesc->mAddress = String8("0"); - } - ssize_t index; + sp<DeviceDescriptor> devDesc = getDeviceDescriptor(device, device_address, ""); DeviceVector *deviceVector; if (audio_is_output_device(device)) { @@ -439,7 +286,7 @@ audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devi return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; } - index = deviceVector->indexOf(devDesc); + ssize_t index = deviceVector->indexOf(devDesc); if (index >= 0) { return AUDIO_POLICY_DEVICE_STATE_AVAILABLE; } else { @@ -447,6 +294,37 @@ audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devi } } +sp<DeviceDescriptor> AudioPolicyManager::getDeviceDescriptor(const audio_devices_t device, + const char *device_address, + const char *device_name) +{ + String8 address = (device_address == NULL) ? String8("") : String8(device_address); + // handle legacy remote submix case where the address was not always specified + if (deviceDistinguishesOnAddress(device) && (address.length() == 0)) { + address = String8("0"); + } + + for (size_t i = 0; i < mHwModules.size(); i++) { + if (mHwModules[i]->mHandle == 0) { + continue; + } + DeviceVector deviceList = + mHwModules[i]->mDeclaredDevices.getDevicesFromTypeAddr(device, address); + if (!deviceList.isEmpty()) { + return deviceList.itemAt(0); + } + deviceList = mHwModules[i]->mDeclaredDevices.getDevicesFromType(device); + if (!deviceList.isEmpty()) { + return deviceList.itemAt(0); + } + } + + sp<DeviceDescriptor> devDesc = + new DeviceDescriptor(String8(device_name != NULL ? device_name : ""), device); + devDesc->mAddress = address; + return devDesc; +} + void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs) { bool createTxPatch = false; @@ -457,7 +335,7 @@ void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs audio_patch_handle_t afPatchHandle; DeviceVector deviceList; - audio_devices_t txDevice = getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); + audio_devices_t txDevice = getDeviceAndMixForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); ALOGV("updateCallRouting device rxDevice %08x txDevice %08x", rxDevice, txDevice); // release existing RX patch if any @@ -584,8 +462,14 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) if (isInCall()) { ALOGV("setPhoneState() in call state management: new state is %d", state); for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + if (stream == AUDIO_STREAM_PATCH) { + continue; + } handleIncallSonification((audio_stream_type_t)stream, false, true); } + + // force reevaluating accessibility routing when call starts + mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY); } // store previous phone state for management of sonification strategy below @@ -599,18 +483,18 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) // 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++) { + for (int j = 0; j < ApmGains::DEVICE_CATEGORY_CNT; j++) { mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = - sVolumeProfiles[AUDIO_STREAM_VOICE_CALL][j]; + ApmGains::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++) { + for (int j = 0; j < ApmGains::DEVICE_CATEGORY_CNT; j++) { mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = - sVolumeProfiles[AUDIO_STREAM_DTMF][j]; + ApmGains::sVolumeProfiles[AUDIO_STREAM_DTMF][j]; } } else if (isStateInCall(state) && (state != oldState)) { ALOGV(" Switching between telephony and VoIP in setPhoneState()"); @@ -681,6 +565,9 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) if (isStateInCall(state)) { ALOGV("setPhoneState() in call state management: new state is %d", state); for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + if (stream == AUDIO_STREAM_PATCH) { + continue; + } handleIncallSonification((audio_stream_type_t)stream, true, true); } } @@ -798,7 +685,7 @@ void AudioPolicyManager::setSystemProperty(const char* property, const char* val // Find a direct output profile compatible with the parameters passed, even if the input flags do // not explicitly request a direct output -sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getProfileForDirectOutput( +sp<IOProfile> AudioPolicyManager::getProfileForDirectOutput( audio_devices_t device, uint32_t samplingRate, audio_format_t format, @@ -811,7 +698,7 @@ sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getProfileForDirectOutput( } for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) { sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j]; - bool found = profile->isCompatibleProfile(device, samplingRate, + bool found = profile->isCompatibleProfile(device, String8(""), samplingRate, NULL /*updatedSamplingRate*/, format, channelMask, flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_DIRECT); @@ -830,48 +717,113 @@ audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream, audio_output_flags_t flags, const audio_offload_info_t *offloadInfo) { - 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); - return getOutputForDevice(device, stream, samplingRate,format, channelMask, flags, - offloadInfo); -} + return getOutputForDevice(device, AUDIO_SESSION_ALLOCATE, + stream, samplingRate,format, channelMask, + flags, offloadInfo); +} + +status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + 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_attributes_t attributes; + if (attr != NULL) { + if (!isValidAttributes(attr)) { + ALOGE("getOutputForAttr() invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]", + attr->usage, attr->content_type, attr->flags, + attr->tags); + return BAD_VALUE; + } + attributes = *attr; + } else { + if (*stream < AUDIO_STREAM_MIN || *stream >= AUDIO_STREAM_PUBLIC_CNT) { + ALOGE("getOutputForAttr(): invalid stream type"); + return BAD_VALUE; + } + stream_type_to_audio_attributes(*stream, &attributes); + } -audio_io_handle_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) -{ - if (attr == NULL) { - ALOGE("getOutputForAttr() called with NULL audio attributes"); - return 0; + for (size_t i = 0; i < mPolicyMixes.size(); i++) { + sp<AudioOutputDescriptor> desc; + if (mPolicyMixes[i]->mMix.mMixType == MIX_TYPE_PLAYERS) { + for (size_t j = 0; j < mPolicyMixes[i]->mMix.mCriteria.size(); j++) { + if ((RULE_MATCH_ATTRIBUTE_USAGE == mPolicyMixes[i]->mMix.mCriteria[j].mRule && + mPolicyMixes[i]->mMix.mCriteria[j].mAttr.mUsage == attributes.usage) || + (RULE_EXCLUDE_ATTRIBUTE_USAGE == mPolicyMixes[i]->mMix.mCriteria[j].mRule && + mPolicyMixes[i]->mMix.mCriteria[j].mAttr.mUsage != attributes.usage)) { + desc = mPolicyMixes[i]->mOutput; + break; + } + if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 && + strncmp(attributes.tags + strlen("addr="), + mPolicyMixes[i]->mMix.mRegistrationId.string(), + AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { + desc = mPolicyMixes[i]->mOutput; + break; + } + } + } else if (mPolicyMixes[i]->mMix.mMixType == MIX_TYPE_RECORDERS) { + if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE && + strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 && + strncmp(attributes.tags + strlen("addr="), + mPolicyMixes[i]->mMix.mRegistrationId.string(), + AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { + desc = mPolicyMixes[i]->mOutput; + } + } + if (desc != 0) { + if (!audio_is_linear_pcm(format)) { + return BAD_VALUE; + } + desc->mPolicyMix = &mPolicyMixes[i]->mMix; + *stream = streamTypefromAttributesInt(&attributes); + *output = desc->mIoHandle; + ALOGV("getOutputForAttr() returns output %d", *output); + return NO_ERROR; + } + } + if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) { + ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE"); + return BAD_VALUE; } + ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x", - attr->usage, attr->content_type, attr->tags, attr->flags); + attributes.usage, attributes.content_type, attributes.tags, attributes.flags); - // TODO this is where filtering for custom policies (rerouting, dynamic sources) will go - routing_strategy strategy = (routing_strategy) getStrategyForAttr(attr); + routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes); audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); - if ((attr->flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { + if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); } ALOGV("getOutputForAttr() device 0x%x, samplingRate %d, format %x, channelMask %x, flags %x", device, samplingRate, format, channelMask, flags); - audio_stream_type_t stream = streamTypefromAttributesInt(attr); - return getOutputForDevice(device, stream, samplingRate, format, channelMask, flags, - offloadInfo); + *stream = streamTypefromAttributesInt(&attributes); + *output = getOutputForDevice(device, session, *stream, + samplingRate, format, channelMask, + flags, offloadInfo); + if (*output == AUDIO_IO_HANDLE_NONE) { + return INVALID_OPERATION; + } + return NO_ERROR; } audio_io_handle_t AudioPolicyManager::getOutputForDevice( audio_devices_t device, + audio_session_t session __unused, audio_stream_type_t stream, uint32_t samplingRate, audio_format_t format, @@ -934,6 +886,10 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) { flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT); } + // only allow deep buffering for music stream type + if (stream != AUDIO_STREAM_MUSIC) { + flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER); + } sp<IOProfile> profile; @@ -1013,6 +969,14 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( if (output != AUDIO_IO_HANDLE_NONE) { mpClientInterface->closeOutput(output); } + // fall back to mixer output if possible when the direct output could not be open + if (audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE) { + goto non_direct_output; + } + // fall back to mixer output if possible when the direct output could not be open + if (audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE) { + goto non_direct_output; + } return AUDIO_IO_HANDLE_NONE; } outputDesc->mSamplingRate = config.sample_rate; @@ -1118,7 +1082,7 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_h status_t AudioPolicyManager::startOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { ALOGV("startOutput() output %d, stream %d, session %d", output, stream, session); ssize_t index = mOutputs.indexOfKey(output); @@ -1149,7 +1113,13 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, outputDesc->changeRefCount(stream, 1); if (outputDesc->mRefCount[stream] == 1) { - audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/); + // starting an output being rerouted? + audio_devices_t newDevice; + if (outputDesc->mPolicyMix != NULL) { + newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + } else { + newDevice = getNewOutputDevice(output, false /*fromCache*/); + } routing_strategy strategy = getStrategy(stream); bool shouldWait = (strategy == STRATEGY_SONIFICATION) || (strategy == STRATEGY_SONIFICATION_RESPECTFUL) || @@ -1192,6 +1162,22 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, // update the outputs if starting an output with a stream that can affect notification // routing handleNotificationRoutingForStream(stream); + + // Automatically enable the remote submix input when output is started on a re routing mix + // of type MIX_TYPE_RECORDERS + if (audio_is_remote_submix_device(newDevice) && outputDesc->mPolicyMix != NULL && + outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { + setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + outputDesc->mPolicyMix->mRegistrationId, + "remote-submix"); + } + + // force reevaluating accessibility routing when ringtone or alarm starts + if (strategy == STRATEGY_SONIFICATION) { + mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY); + } + if (waitMs > muteWaitMs) { usleep((waitMs - muteWaitMs) * 2 * 1000); } @@ -1202,7 +1188,7 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { ALOGV("stopOutput() output %d, stream %d, session %d", output, stream, session); ssize_t index = mOutputs.indexOfKey(output); @@ -1226,6 +1212,17 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, outputDesc->changeRefCount(stream, -1); // store time at which the stream was stopped - see isStreamActive() if (outputDesc->mRefCount[stream] == 0) { + // Automatically disable the remote submix input when output is stopped on a + // re routing mix of type MIX_TYPE_RECORDERS + if (audio_is_remote_submix_device(outputDesc->mDevice) && + outputDesc->mPolicyMix != NULL && + outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { + setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + outputDesc->mPolicyMix->mRegistrationId, + "remote-submix"); + } + outputDesc->mStopTime[stream] = systemTime(); audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/); // delay the device switch by twice the latency because stopOutput() is executed when @@ -1260,7 +1257,9 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, } } -void AudioPolicyManager::releaseOutput(audio_io_handle_t output) +void AudioPolicyManager::releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream __unused, + audio_session_t session __unused) { ALOGV("releaseOutput() %d", output); ssize_t index = mOutputs.indexOfKey(output); @@ -1303,79 +1302,121 @@ void AudioPolicyManager::releaseOutput(audio_io_handle_t output) } -audio_io_handle_t AudioPolicyManager::getInput(audio_source_t inputSource, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_session_t session, - audio_input_flags_t flags) +status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags, + input_type_t *inputType) { - ALOGV("getInput() inputSource %d, samplingRate %d, format %d, channelMask %x, session %d, " - "flags %#x", - inputSource, samplingRate, format, channelMask, session, flags); - - audio_devices_t device = getDeviceForInputSource(inputSource); + ALOGV("getInputForAttr() source %d, samplingRate %d, format %d, channelMask %x," + "session %d, flags %#x", + attr->source, samplingRate, format, channelMask, session, flags); - if (device == AUDIO_DEVICE_NONE) { - ALOGW("getInput() could not find device for inputSource %d", inputSource); - return AUDIO_IO_HANDLE_NONE; - } + *input = AUDIO_IO_HANDLE_NONE; + *inputType = API_INPUT_INVALID; + audio_devices_t device; + // handle legacy remote submix case where the address was not always specified + String8 address = String8(""); + bool isSoundTrigger = false; + audio_source_t inputSource = attr->source; + audio_source_t halInputSource; + AudioMix *policyMix = NULL; - // 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; + if (inputSource == AUDIO_SOURCE_DEFAULT) { + inputSource = AUDIO_SOURCE_MIC; } + halInputSource = inputSource; - audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; - bool isSoundTrigger = false; - audio_source_t halInputSource = inputSource; - if (inputSource == AUDIO_SOURCE_HOTWORD) { - ssize_t index = mSoundTriggerSessions.indexOfKey(session); - if (index >= 0) { - input = mSoundTriggerSessions.valueFor(session); - isSoundTrigger = true; - flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_HW_HOTWORD); - ALOGV("SoundTrigger capture on session %d input %d", session, input); + if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX && + strncmp(attr->tags, "addr=", strlen("addr=")) == 0) { + device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + address = String8(attr->tags + strlen("addr=")); + ssize_t index = mPolicyMixes.indexOfKey(address); + if (index < 0) { + ALOGW("getInputForAttr() no policy for address %s", address.string()); + return BAD_VALUE; + } + if (mPolicyMixes[index]->mMix.mMixType != MIX_TYPE_PLAYERS) { + ALOGW("getInputForAttr() bad policy mix type for address %s", address.string()); + return BAD_VALUE; + } + policyMix = &mPolicyMixes[index]->mMix; + *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; + } else { + device = getDeviceAndMixForInputSource(inputSource, &policyMix); + if (device == AUDIO_DEVICE_NONE) { + ALOGW("getInputForAttr() could not find device for source %d", inputSource); + return BAD_VALUE; + } + if (policyMix != NULL) { + address = policyMix->mRegistrationId; + if (policyMix->mMixType == MIX_TYPE_RECORDERS) { + // there is an external policy, but this input is attached to a mix of recorders, + // meaning it receives audio injected into the framework, so the recorder doesn't + // know about it and is therefore considered "legacy" + *inputType = API_INPUT_LEGACY; + } else { + // recording a mix of players defined by an external policy, we're rerouting for + // an external policy + *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; + } + } else if (audio_is_remote_submix_device(device)) { + address = String8("0"); + *inputType = API_INPUT_MIX_CAPTURE; } else { - halInputSource = AUDIO_SOURCE_VOICE_RECOGNITION; + *inputType = API_INPUT_LEGACY; + } + // 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; + } + if (inputSource == AUDIO_SOURCE_HOTWORD) { + ssize_t index = mSoundTriggerSessions.indexOfKey(session); + if (index >= 0) { + *input = mSoundTriggerSessions.valueFor(session); + isSoundTrigger = true; + flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_HW_HOTWORD); + ALOGV("SoundTrigger capture on session %d input %d", session, *input); + } else { + halInputSource = AUDIO_SOURCE_VOICE_RECOGNITION; + } } } - sp<IOProfile> profile = getInputProfile(device, - samplingRate, - format, - channelMask, - flags); + sp<IOProfile> profile = getInputProfile(device, address, + samplingRate, format, channelMask, + flags); if (profile == 0) { //retry without flags audio_input_flags_t log_flags = flags; flags = AUDIO_INPUT_FLAG_NONE; - profile = getInputProfile(device, - samplingRate, - format, - channelMask, - flags); + profile = getInputProfile(device, address, + samplingRate, format, channelMask, + flags); if (profile == 0) { - ALOGW("getInput() could not find profile for device 0x%X, samplingRate %u, format %#x, " - "channelMask 0x%X, flags %#x", + ALOGW("getInputForAttr() could not find profile for device 0x%X, samplingRate %u," + "format %#x, channelMask 0x%X, flags %#x", device, samplingRate, format, channelMask, log_flags); - return AUDIO_IO_HANDLE_NONE; + return BAD_VALUE; } } if (profile->mModule->mHandle == 0) { - ALOGE("getInput(): HW module %s not opened", profile->mModule->mName); - return AUDIO_IO_HANDLE_NONE; + ALOGE("getInputForAttr(): HW module %s not opened", profile->mModule->mName); + return NO_INIT; } audio_config_t config = AUDIO_CONFIG_INITIALIZER; @@ -1383,11 +1424,8 @@ audio_io_handle_t AudioPolicyManager::getInput(audio_source_t inputSource, config.channel_mask = channelMask; config.format = format; - // handle legacy remote submix case where the address was not always specified - String8 address = deviceDistinguishesOnAddress(device) ? String8("0") : String8(""); - status_t status = mpClientInterface->openInput(profile->mModule->mHandle, - &input, + input, &config, &device, address, @@ -1395,16 +1433,16 @@ audio_io_handle_t AudioPolicyManager::getInput(audio_source_t inputSource, flags); // only accept input with the exact requested set of parameters - if (status != NO_ERROR || + if (status != NO_ERROR || *input == AUDIO_IO_HANDLE_NONE || (samplingRate != config.sample_rate) || (format != config.format) || (channelMask != config.channel_mask)) { - ALOGW("getInput() failed opening input: samplingRate %d, format %d, channelMask %x", + ALOGW("getInputForAttr() failed opening input: samplingRate %d, format %d, channelMask %x", samplingRate, format, channelMask); - if (input != AUDIO_IO_HANDLE_NONE) { - mpClientInterface->closeInput(input); + if (*input != AUDIO_IO_HANDLE_NONE) { + mpClientInterface->closeInput(*input); } - return AUDIO_IO_HANDLE_NONE; + return BAD_VALUE; } sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile); @@ -1417,10 +1455,13 @@ audio_io_handle_t AudioPolicyManager::getInput(audio_source_t inputSource, inputDesc->mDevice = device; inputDesc->mSessions.add(session); inputDesc->mIsSoundTrigger = isSoundTrigger; + inputDesc->mPolicyMix = policyMix; + + ALOGV("getInputForAttr() returns input type = %d", inputType); - addInput(input, inputDesc); + addInput(*input, inputDesc); mpClientInterface->onAudioPortListUpdate(); - return input; + return NO_ERROR; } status_t AudioPolicyManager::startInput(audio_io_handle_t input, @@ -1467,11 +1508,21 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, } setInputDevice(input, getNewInputDevice(input), true /* force */); - // Automatically enable the remote submix output when input is started. + // automatically enable the remote submix output when input is started if not + // used by a policy mix of type MIX_TYPE_RECORDERS // For remote submix (a virtual device), we open only one input per capture request. if (audio_is_remote_submix_device(inputDesc->mDevice)) { - setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS); + String8 address = String8(""); + if (inputDesc->mPolicyMix == NULL) { + address = String8("0"); + } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { + address = inputDesc->mPolicyMix->mRegistrationId; + } + if (address != "") { + setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + address, "remote-submix"); + } } } @@ -1506,10 +1557,20 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, inputDesc->mRefCount--; if (inputDesc->mRefCount == 0) { - // automatically disable the remote submix output when input is stopped + // automatically disable the remote submix output when input is stopped if not + // used by a policy mix of type MIX_TYPE_RECORDERS if (audio_is_remote_submix_device(inputDesc->mDevice)) { - setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS); + String8 address = String8(""); + if (inputDesc->mPolicyMix == NULL) { + address = String8("0"); + } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { + address = inputDesc->mPolicyMix->mRegistrationId; + } + if (address != "") { + setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + address, "remote-submix"); + } } resetInputDevice(input); @@ -1587,6 +1648,11 @@ void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream, } mStreams[stream].mIndexMin = indexMin; mStreams[stream].mIndexMax = indexMax; + //FIXME: AUDIO_STREAM_ACCESSIBILITY volume follows AUDIO_STREAM_MUSIC for now + if (stream == AUDIO_STREAM_MUSIC) { + mStreams[AUDIO_STREAM_ACCESSIBILITY].mIndexMin = indexMin; + mStreams[AUDIO_STREAM_ACCESSIBILITY].mIndexMax = indexMax; + } } status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, @@ -1617,19 +1683,32 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, // update volume on all outputs whose current device is also selected by the same // strategy as the device specified by the caller audio_devices_t strategyDevice = getDeviceForStrategy(getStrategy(stream), true /*fromCache*/); - if ((device != AUDIO_DEVICE_OUT_DEFAULT) && (device & strategyDevice) == 0) { + + + //FIXME: AUDIO_STREAM_ACCESSIBILITY volume follows AUDIO_STREAM_MUSIC for now + audio_devices_t accessibilityDevice = AUDIO_DEVICE_NONE; + if (stream == AUDIO_STREAM_MUSIC) { + mStreams[AUDIO_STREAM_ACCESSIBILITY].mIndexCur.add(device, index); + accessibilityDevice = getDeviceForStrategy(STRATEGY_ACCESSIBILITY, true /*fromCache*/); + } + if ((device != AUDIO_DEVICE_OUT_DEFAULT) && + (device & (strategyDevice | accessibilityDevice)) == 0) { return NO_ERROR; } status_t status = NO_ERROR; for (size_t i = 0; i < mOutputs.size(); i++) { audio_devices_t curDevice = - getDeviceForVolume(mOutputs.valueAt(i)->device()); + ApmGains::getDeviceForVolume(mOutputs.valueAt(i)->device()); if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & strategyDevice) != 0)) { status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice); if (volStatus != NO_ERROR) { status = volStatus; } } + if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & accessibilityDevice) != 0)) { + status_t volStatus = checkAndSetVolume(AUDIO_STREAM_ACCESSIBILITY, + index, mOutputs.keyAt(i), curDevice); + } } return status; } @@ -1649,7 +1728,7 @@ status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, if (device == AUDIO_DEVICE_OUT_DEFAULT) { device = getDeviceForStrategy(getStrategy(stream), true /*fromCache*/); } - device = getDeviceForVolume(device); + device = ApmGains::getDeviceForVolume(device); *index = mStreams[stream].getVolumeIndex(device); ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index); @@ -1849,7 +1928,11 @@ bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, const sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) && outputDesc->isStreamActive(stream, inPastMs, sysTime)) { - return true; + // do not consider re routing (when the output is going to a dynamic policy) + // as "remote playback" + if (outputDesc->mPolicyMix == NULL) { + return true; + } } } return false; @@ -1859,16 +1942,148 @@ bool AudioPolicyManager::isSourceActive(audio_source_t source) const { for (size_t i = 0; i < mInputs.size(); i++) { const sp<AudioInputDescriptor> inputDescriptor = mInputs.valueAt(i); - if ((inputDescriptor->mInputSource == (int)source || - (source == AUDIO_SOURCE_VOICE_RECOGNITION && - inputDescriptor->mInputSource == AUDIO_SOURCE_HOTWORD)) - && (inputDescriptor->mRefCount > 0)) { + if (inputDescriptor->mRefCount == 0) { + continue; + } + if (inputDescriptor->mInputSource == (int)source) { return true; } + // AUDIO_SOURCE_HOTWORD is equivalent to AUDIO_SOURCE_VOICE_RECOGNITION only if it + // corresponds to an active capture triggered by a hardware hotword recognition + if ((source == AUDIO_SOURCE_VOICE_RECOGNITION) && + (inputDescriptor->mInputSource == AUDIO_SOURCE_HOTWORD)) { + // FIXME: we should not assume that the first session is the active one and keep + // activity count per session. Same in startInput(). + ssize_t index = mSoundTriggerSessions.indexOfKey(inputDescriptor->mSessions.itemAt(0)); + if (index >= 0) { + return true; + } + } } return false; } +// Register a list of custom mixes with their attributes and format. +// When a mix is registered, corresponding input and output profiles are +// added to the remote submix hw module. The profile contains only the +// parameters (sampling rate, format...) specified by the mix. +// The corresponding input remote submix device is also connected. +// +// When a remote submix device is connected, the address is checked to select the +// appropriate profile and the corresponding input or output stream is opened. +// +// When capture starts, getInputForAttr() will: +// - 1 look for a mix matching the address passed in attribtutes tags if any +// - 2 if none found, getDeviceForInputSource() will: +// - 2.1 look for a mix matching the attributes source +// - 2.2 if none found, default to device selection by policy rules +// At this time, the corresponding output remote submix device is also connected +// and active playback use cases can be transferred to this mix if needed when reconnecting +// after AudioTracks are invalidated +// +// When playback starts, getOutputForAttr() will: +// - 1 look for a mix matching the address passed in attribtutes tags if any +// - 2 if none found, look for a mix matching the attributes usage +// - 3 if none found, default to device and output selection by policy rules. + +status_t AudioPolicyManager::registerPolicyMixes(Vector<AudioMix> mixes) +{ + sp<HwModule> module; + for (size_t i = 0; i < mHwModules.size(); i++) { + if (strcmp(AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX, mHwModules[i]->mName) == 0 && + mHwModules[i]->mHandle != 0) { + module = mHwModules[i]; + break; + } + } + + if (module == 0) { + return INVALID_OPERATION; + } + + ALOGV("registerPolicyMixes() num mixes %d", mixes.size()); + + for (size_t i = 0; i < mixes.size(); i++) { + String8 address = mixes[i].mRegistrationId; + ssize_t index = mPolicyMixes.indexOfKey(address); + if (index >= 0) { + ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string()); + continue; + } + audio_config_t outputConfig = mixes[i].mFormat; + audio_config_t inputConfig = mixes[i].mFormat; + // NOTE: audio flinger mixer does not support mono output: configure remote submix HAL in + // stereo and let audio flinger do the channel conversion if needed. + outputConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO; + inputConfig.channel_mask = AUDIO_CHANNEL_IN_STEREO; + module->addOutputProfile(address, &outputConfig, + AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address); + module->addInputProfile(address, &inputConfig, + AUDIO_DEVICE_IN_REMOTE_SUBMIX, address); + sp<AudioPolicyMix> policyMix = new AudioPolicyMix(); + policyMix->mMix = mixes[i]; + mPolicyMixes.add(address, policyMix); + if (mixes[i].mMixType == MIX_TYPE_PLAYERS) { + setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + address.string(), "remote-submix"); + } else { + setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + address.string(), "remote-submix"); + } + } + return NO_ERROR; +} + +status_t AudioPolicyManager::unregisterPolicyMixes(Vector<AudioMix> mixes) +{ + sp<HwModule> module; + for (size_t i = 0; i < mHwModules.size(); i++) { + if (strcmp(AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX, mHwModules[i]->mName) == 0 && + mHwModules[i]->mHandle != 0) { + module = mHwModules[i]; + break; + } + } + + if (module == 0) { + return INVALID_OPERATION; + } + + ALOGV("unregisterPolicyMixes() num mixes %d", mixes.size()); + + for (size_t i = 0; i < mixes.size(); i++) { + String8 address = mixes[i].mRegistrationId; + ssize_t index = mPolicyMixes.indexOfKey(address); + if (index < 0) { + ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string()); + continue; + } + + mPolicyMixes.removeItemsAt(index); + + if (getDeviceConnectionState(AUDIO_DEVICE_IN_REMOTE_SUBMIX, address.string()) == + AUDIO_POLICY_DEVICE_STATE_AVAILABLE) + { + setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + address.string(), "remote-submix"); + } + + if (getDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address.string()) == + AUDIO_POLICY_DEVICE_STATE_AVAILABLE) + { + setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + address.string(), "remote-submix"); + } + module->removeOutputProfile(address); + module->removeInputProfile(address); + } + return NO_ERROR; +} + status_t AudioPolicyManager::dump(int fd) { @@ -2097,7 +2312,7 @@ status_t AudioPolicyManager::getAudioPort(struct audio_port *port __unused) return NO_ERROR; } -sp<AudioPolicyManager::AudioOutputDescriptor> AudioPolicyManager::getOutputFromId( +sp<AudioOutputDescriptor> AudioPolicyManager::getOutputFromId( audio_port_handle_t id) const { sp<AudioOutputDescriptor> outputDesc = NULL; @@ -2110,7 +2325,7 @@ sp<AudioPolicyManager::AudioOutputDescriptor> AudioPolicyManager::getOutputFromI return outputDesc; } -sp<AudioPolicyManager::AudioInputDescriptor> AudioPolicyManager::getInputFromId( +sp<AudioInputDescriptor> AudioPolicyManager::getInputFromId( audio_port_handle_t id) const { sp<AudioInputDescriptor> inputDesc = NULL; @@ -2123,7 +2338,7 @@ sp<AudioPolicyManager::AudioInputDescriptor> AudioPolicyManager::getInputFromId( return inputDesc; } -sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleForDevice( +sp <HwModule> AudioPolicyManager::getModuleForDevice( audio_devices_t device) const { sp <HwModule> module; @@ -2151,7 +2366,7 @@ sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleForDevice( return module; } -sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleFromName(const char *name) const +sp <HwModule> AudioPolicyManager::getModuleFromName(const char *name) const { sp <HwModule> module; @@ -2269,6 +2484,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, } if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType, + devDesc->mAddress, patch->sources[0].sample_rate, NULL, // updatedSamplingRate patch->sources[0].format, @@ -2323,13 +2539,14 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, } if (!inputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType, - patch->sinks[0].sample_rate, - NULL, /*updatedSampleRate*/ - patch->sinks[0].format, - patch->sinks[0].channel_mask, - // FIXME for the parameter type, - // and the NONE - (audio_output_flags_t) + devDesc->mAddress, + patch->sinks[0].sample_rate, + NULL, /*updatedSampleRate*/ + patch->sinks[0].format, + patch->sinks[0].channel_mask, + // FIXME for the parameter type, + // and the NONE + (audio_output_flags_t) AUDIO_INPUT_FLAG_NONE)) { return INVALID_OPERATION; } @@ -2596,13 +2813,10 @@ status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config * void AudioPolicyManager::clearAudioPatches(uid_t uid) { - for (ssize_t i = 0; i < (ssize_t)mAudioPatches.size(); i++) { + for (ssize_t i = (ssize_t)mAudioPatches.size() - 1; i >= 0; i--) { sp<AudioPatch> patchDesc = mAudioPatches.valueAt(i); if (patchDesc->mUid == uid) { - // releaseAudioPatch() removes the patch from mAudioPatches - if (releaseAudioPatch(mAudioPatches.keyAt(i), uid) == NO_ERROR) { - i--; - } + releaseAudioPatch(mAudioPatches.keyAt(i), uid); } } } @@ -2613,7 +2827,7 @@ status_t AudioPolicyManager::acquireSoundTriggerSession(audio_session_t *session { *session = (audio_session_t)mpClientInterface->newAudioUniqueId(); *ioHandle = (audio_io_handle_t)mpClientInterface->newAudioUniqueId(); - *device = getDeviceForInputSource(AUDIO_SOURCE_HOTWORD); + *device = getDeviceAndMixForInputSource(AUDIO_SOURCE_HOTWORD); mSoundTriggerSessions.add(*session, *ioHandle); @@ -2677,6 +2891,8 @@ uint32_t AudioPolicyManager::nextAudioPortGeneration() return android_atomic_inc(&mAudioPortGeneration); } +int32_t volatile AudioPolicyManager::mNextUniqueId = 1; + AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface) : #ifdef AUDIO_POLICY_TEST @@ -2687,7 +2903,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0), mA2dpSuspended(false), - mSpeakerDrcEnabled(false), mNextUniqueId(1), + mSpeakerDrcEnabled(false), mAudioPortGeneration(1), mBeaconMuteRefCount(0), mBeaconPlayingRefCount(0), @@ -2700,7 +2916,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mForceUse[i] = AUDIO_POLICY_FORCE_NONE; } - mDefaultOutputDevice = new DeviceDescriptor(String8(""), AUDIO_DEVICE_OUT_SPEAKER); + mDefaultOutputDevice = new DeviceDescriptor(String8("Speaker"), 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"); @@ -2783,9 +2999,8 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa ssize_t index = mAvailableOutputDevices.indexOf(outProfile->mSupportedDevices[k]); // give a valid ID to an attached device once confirmed it is reachable - if ((index >= 0) && (mAvailableOutputDevices[index]->mId == 0)) { - mAvailableOutputDevices[index]->mId = nextUniqueId(); - mAvailableOutputDevices[index]->mModule = mHwModules[i]; + if (index >= 0 && !mAvailableOutputDevices[index]->isAttached()) { + mAvailableOutputDevices[index]->attach(mHwModules[i]); } } if (mPrimaryOutput == 0 && @@ -2852,9 +3067,8 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa ssize_t index = mAvailableInputDevices.indexOf(inProfile->mSupportedDevices[k]); // give a valid ID to an attached device once confirmed it is reachable - if ((index >= 0) && (mAvailableInputDevices[index]->mId == 0)) { - mAvailableInputDevices[index]->mId = nextUniqueId(); - mAvailableInputDevices[index]->mModule = mHwModules[i]; + if (index >= 0 && !mAvailableInputDevices[index]->isAttached()) { + mAvailableInputDevices[index]->attach(mHwModules[i]); } } mpClientInterface->closeInput(input); @@ -2867,7 +3081,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa } // make sure all attached devices have been allocated a unique ID for (size_t i = 0; i < mAvailableOutputDevices.size();) { - if (mAvailableOutputDevices[i]->mId == 0) { + if (!mAvailableOutputDevices[i]->isAttached()) { ALOGW("Input device %08x unreachable", mAvailableOutputDevices[i]->mDeviceType); mAvailableOutputDevices.remove(mAvailableOutputDevices[i]); continue; @@ -2875,7 +3089,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa i++; } for (size_t i = 0; i < mAvailableInputDevices.size();) { - if (mAvailableInputDevices[i]->mId == 0) { + if (!mAvailableInputDevices[i]->isAttached()) { ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->mDeviceType); mAvailableInputDevices.remove(mAvailableInputDevices[i]); continue; @@ -3115,29 +3329,15 @@ void AudioPolicyManager::addInput(audio_io_handle_t input, sp<AudioInputDescript } void AudioPolicyManager::findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/, + const audio_devices_t device /*in*/, const String8 address /*in*/, SortedVector<audio_io_handle_t>& outputs /*out*/) { - // look for a match on the given address on the addresses of the outputs: - // find the address by finding the patch that maps to this output - ssize_t patchIdx = mAudioPatches.indexOfKey(desc->mPatchHandle); - //ALOGV(" inspecting output %d (patch %d) for supported device=0x%x", - // outputIdx, patchIdx, desc->mProfile->mSupportedDevices.types()); - if (patchIdx >= 0) { - const sp<AudioPatch> patchDesc = mAudioPatches.valueAt(patchIdx); - const int numSinks = patchDesc->mPatch.num_sinks; - for (ssize_t j=0; j < numSinks; j++) { - if (patchDesc->mPatch.sinks[j].type == AUDIO_PORT_TYPE_DEVICE) { - const char* patchAddr = - patchDesc->mPatch.sinks[j].ext.device.address; - if (strncmp(patchAddr, - address.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) { - ALOGV("findIoHandlesByAddress(): adding opened output %d on same address %s", - desc->mIoHandle, patchDesc->mPatch.sinks[j].ext.device.address); - outputs.add(desc->mIoHandle); - break; - } - } - } + sp<DeviceDescriptor> devDesc = + desc->mProfile->mSupportedDevices.getDevice(device, address); + if (devDesc != 0) { + ALOGV("findIoHandlesByAddress(): adding opened output %d on same address %s", + desc->mIoHandle, address.string()); + outputs.add(desc->mIoHandle); } } @@ -3161,7 +3361,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de outputs.add(mOutputs.keyAt(i)); } else { ALOGV(" checking address match due to device 0x%x", device); - findIoHandlesByAddress(desc, address, outputs); + findIoHandlesByAddress(desc, device, address, outputs); } } } @@ -3174,9 +3374,13 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de } for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) { - if (mHwModules[i]->mOutputProfiles[j]->mSupportedDevices.types() & device) { - ALOGV("checkOutputsForDevice(): adding profile %zu from module %zu", j, i); - profiles.add(mHwModules[i]->mOutputProfiles[j]); + sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j]; + if (profile->mSupportedDevices.types() & device) { + if (!deviceDistinguishesOnAddress(device) || + address == profile->mSupportedDevices[0]->mAddress) { + profiles.add(profile); + ALOGV("checkOutputsForDevice(): adding profile %zu from module %zu", j, i); + } } } } @@ -3308,7 +3512,18 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de if (output != AUDIO_IO_HANDLE_NONE) { addOutput(output, desc); - if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) { + if (deviceDistinguishesOnAddress(device) && address != "0") { + ssize_t index = mPolicyMixes.indexOfKey(address); + if (index >= 0) { + mPolicyMixes[index]->mOutput = desc; + desc->mPolicyMix = &mPolicyMixes[index]->mMix; + } else { + ALOGE("checkOutputsForDevice() cannot find policy for address %s", + address.string()); + } + } else if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) { + // no duplicated output for direct outputs and + // outputs used by dynamic policy mixes audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE; // set initial stream volume for device @@ -3371,15 +3586,15 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de for (size_t i = 0; i < mOutputs.size(); i++) { desc = mOutputs.valueAt(i); if (!desc->isDuplicated()) { - if (!(desc->mProfile->mSupportedDevices.types() + // exact match on device + if (deviceDistinguishesOnAddress(device) && + (desc->mProfile->mSupportedDevices.types() == device)) { + findIoHandlesByAddress(desc, device, address, outputs); + } else if (!(desc->mProfile->mSupportedDevices.types() & mAvailableOutputDevices.types())) { ALOGV("checkOutputsForDevice(): disconnecting adding output %d", mOutputs.keyAt(i)); outputs.add(mOutputs.keyAt(i)); - } else if (deviceDistinguishesOnAddress(device) && - // exact match on device - (desc->mProfile->mSupportedDevices.types() == device)) { - findIoHandlesByAddress(desc, address, outputs); } } } @@ -3441,11 +3656,15 @@ status_t AudioPolicyManager::checkInputsForDevice(audio_devices_t device, profile_index < mHwModules[module_idx]->mInputProfiles.size(); profile_index++) { - if (mHwModules[module_idx]->mInputProfiles[profile_index]->mSupportedDevices.types() - & (device & ~AUDIO_DEVICE_BIT_IN)) { - ALOGV("checkInputsForDevice(): adding profile %zu from module %zu", - profile_index, module_idx); - profiles.add(mHwModules[module_idx]->mInputProfiles[profile_index]); + sp<IOProfile> profile = mHwModules[module_idx]->mInputProfiles[profile_index]; + + if (profile->mSupportedDevices.types() & (device & ~AUDIO_DEVICE_BIT_IN)) { + if (!deviceDistinguishesOnAddress(device) || + address == profile->mSupportedDevices[0]->mAddress) { + profiles.add(profile); + ALOGV("checkInputsForDevice(): adding profile %zu from module %zu", + profile_index, module_idx); + } } } } @@ -3563,7 +3782,8 @@ status_t AudioPolicyManager::checkInputsForDevice(audio_devices_t device, // check if one opened input is not needed any more after disconnecting one device for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { desc = mInputs.valueAt(input_index); - if (!(desc->mProfile->mSupportedDevices.types() & mAvailableInputDevices.types())) { + if (!(desc->mProfile->mSupportedDevices.types() & mAvailableInputDevices.types() & + ~AUDIO_DEVICE_BIT_IN)) { ALOGV("checkInputsForDevice(): disconnecting adding input %d", mInputs.keyAt(input_index)); inputs.add(mInputs.keyAt(input_index)); @@ -3578,7 +3798,7 @@ status_t AudioPolicyManager::checkInputsForDevice(audio_devices_t device, profile_index < mHwModules[module_index]->mInputProfiles.size(); profile_index++) { sp<IOProfile> profile = mHwModules[module_index]->mInputProfiles[profile_index]; - if (profile->mSupportedDevices.types() & device) { + if (profile->mSupportedDevices.types() & device & ~AUDIO_DEVICE_BIT_IN) { ALOGV("checkInputsForDevice(): clearing direct input profile %zu on module %zu", profile_index, module_index); if (profile->mSamplingRates[0] == 0) { @@ -3612,6 +3832,12 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) return; } + for (size_t i = 0; i < mPolicyMixes.size(); i++) { + if (mPolicyMixes[i]->mOutput == outputDesc) { + mPolicyMixes[i]->mOutput.clear(); + } + } + // look for duplicated outputs connected to the output being removed. for (size_t i = 0; i < mOutputs.size(); i++) { sp<AudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i); @@ -3721,6 +3947,24 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs); SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(newDevice, mOutputs); + // also take into account external policy-related changes: add all outputs which are + // associated with policies in the "before" and "after" output vectors + ALOGVV("checkOutputForStrategy(): policy related outputs"); + for (size_t i = 0 ; i < mPreviousOutputs.size() ; i++) { + const sp<AudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i); + if (desc != 0 && desc->mPolicyMix != NULL) { + srcOutputs.add(desc->mIoHandle); + ALOGVV(" previous outputs: adding %d", desc->mIoHandle); + } + } + for (size_t i = 0 ; i < mOutputs.size() ; i++) { + const sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); + if (desc != 0 && desc->mPolicyMix != NULL) { + dstOutputs.add(desc->mIoHandle); + ALOGVV(" new outputs: adding %d", desc->mIoHandle); + } + } + if (!vectorsEqual(srcOutputs,dstOutputs)) { ALOGV("checkOutputForStrategy() strategy %d, moving from output %d to output %d", strategy, srcOutputs[0], dstOutputs[0]); @@ -3754,6 +3998,9 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) } // Move tracks associated to this strategy from previous output to new output for (int i = 0; i < AUDIO_STREAM_CNT; i++) { + if (i == AUDIO_STREAM_PATCH) { + continue; + } if (getStrategy((audio_stream_type_t)i) == strategy) { mpClientInterface->invalidateStream((audio_stream_type_t)i); } @@ -3770,8 +4017,10 @@ void AudioPolicyManager::checkOutputForAllStrategies() checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE); checkOutputForStrategy(STRATEGY_SONIFICATION); checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); + checkOutputForStrategy(STRATEGY_ACCESSIBILITY); checkOutputForStrategy(STRATEGY_MEDIA); checkOutputForStrategy(STRATEGY_DTMF); + checkOutputForStrategy(STRATEGY_REROUTING); } audio_io_handle_t AudioPolicyManager::getA2dpOutput() @@ -3795,7 +4044,9 @@ void AudioPolicyManager::checkA2dpSuspend() } bool isScoConnected = - (mAvailableInputDevices.types() & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) != 0; + ((mAvailableInputDevices.types() & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET & + ~AUDIO_DEVICE_BIT_IN) != 0) || + ((mAvailableOutputDevices.types() & AUDIO_DEVICE_OUT_ALL_SCO) != 0); // suspend A2DP output if: // (NOT already suspended) && // ((SCO device is connected && @@ -3858,11 +4109,13 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, // use device for strategy sonification // 5: the strategy "respectful" sonification is active on the output: // use device for strategy "respectful" sonification - // 6: the strategy media is active on the output: + // 6: the strategy accessibility is active on the output: + // use device for strategy accessibility + // 7: the strategy media is active on the output: // use device for strategy media - // 7: the strategy DTMF is active on the output: + // 8: the strategy DTMF is active on the output: // use device for strategy DTMF - // 8: the strategy for beacon, a.k.a. "transmitted through speaker" is active on the output: + // 9: the strategy for beacon, a.k.a. "transmitted through speaker" is active on the output: // use device for strategy t-t-s if (outputDesc->isStrategyActive(STRATEGY_ENFORCED_AUDIBLE) && mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { @@ -3876,12 +4129,16 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache); } else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION_RESPECTFUL)) { device = getDeviceForStrategy(STRATEGY_SONIFICATION_RESPECTFUL, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_ACCESSIBILITY)) { + device = getDeviceForStrategy(STRATEGY_ACCESSIBILITY, fromCache); } else if (outputDesc->isStrategyActive(STRATEGY_MEDIA)) { device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache); } else if (outputDesc->isStrategyActive(STRATEGY_DTMF)) { device = getDeviceForStrategy(STRATEGY_DTMF, fromCache); } else if (outputDesc->isStrategyActive(STRATEGY_TRANSMITTED_THROUGH_SPEAKER)) { device = getDeviceForStrategy(STRATEGY_TRANSMITTED_THROUGH_SPEAKER, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_REROUTING)) { + device = getDeviceForStrategy(STRATEGY_REROUTING, fromCache); } ALOGV("getNewOutputDevice() selected device %x", device); @@ -3902,7 +4159,7 @@ audio_devices_t AudioPolicyManager::getNewInputDevice(audio_io_handle_t input) } } - audio_devices_t device = getDeviceForInputSource(inputDesc->mInputSource); + audio_devices_t device = getDeviceAndMixForInputSource(inputDesc->mInputSource); ALOGV("getNewInputDevice() selected device %x", device); return device; @@ -3916,11 +4173,11 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre // 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) { + if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_PUBLIC_CNT) { return AUDIO_DEVICE_NONE; } audio_devices_t devices; - AudioPolicyManager::routing_strategy strategy = getStrategy(stream); + routing_strategy strategy = getStrategy(stream); devices = getDeviceForStrategy(strategy, true /*fromCache*/); SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(devices, mOutputs); for (size_t i = 0; i < outputs.size(); i++) { @@ -3941,8 +4198,11 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre return devices; } -AudioPolicyManager::routing_strategy AudioPolicyManager::getStrategy( +routing_strategy AudioPolicyManager::getStrategy( audio_stream_type_t stream) { + + ALOG_ASSERT(stream != AUDIO_STREAM_PATCH,"getStrategy() called for AUDIO_STREAM_PATCH"); + // stream to strategy mapping switch (stream) { case AUDIO_STREAM_VOICE_CALL: @@ -3956,7 +4216,7 @@ AudioPolicyManager::routing_strategy AudioPolicyManager::getStrategy( case AUDIO_STREAM_DTMF: return STRATEGY_DTMF; default: - ALOGE("unknown stream type"); + ALOGE("unknown stream type %d", stream); 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 @@ -3966,6 +4226,10 @@ AudioPolicyManager::routing_strategy AudioPolicyManager::getStrategy( return STRATEGY_ENFORCED_AUDIBLE; case AUDIO_STREAM_TTS: return STRATEGY_TRANSMITTED_THROUGH_SPEAKER; + case AUDIO_STREAM_ACCESSIBILITY: + return STRATEGY_ACCESSIBILITY; + case AUDIO_STREAM_REROUTING: + return STRATEGY_REROUTING; } } @@ -3980,9 +4244,17 @@ uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) // usage to strategy mapping switch (attr->usage) { + case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: + if (isStreamActive(AUDIO_STREAM_RING) || isStreamActive(AUDIO_STREAM_ALARM)) { + return (uint32_t) STRATEGY_SONIFICATION; + } + if (isInCall()) { + return (uint32_t) STRATEGY_PHONE; + } + return (uint32_t) STRATEGY_ACCESSIBILITY; + case AUDIO_USAGE_MEDIA: case AUDIO_USAGE_GAME: - case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: case AUDIO_USAGE_ASSISTANCE_SONIFICATION: return (uint32_t) STRATEGY_MEDIA; @@ -4153,7 +4425,8 @@ audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strate // - cannot route from voice call RX OR // - audio HAL version is < 3.0 and TX device is on the primary HW module if (mPhoneState == AUDIO_MODE_IN_CALL) { - audio_devices_t txDevice = getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); + audio_devices_t txDevice = + getDeviceAndMixForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); sp<AudioOutputDescriptor> hwOutputDesc = mOutputs.valueFor(mPrimaryOutput); if (((mAvailableInputDevices.types() & AUDIO_DEVICE_IN_TELEPHONY_RX & ~AUDIO_DEVICE_BIT_IN) == 0) || @@ -4182,7 +4455,7 @@ audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strate // 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) { + (getA2dpOutput() != 0)) { device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; if (device) break; device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; @@ -4194,7 +4467,7 @@ audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strate if (device) break; device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; if (device) break; - if (mPhoneState != AUDIO_MODE_IN_CALL) { + if (!isInCall()) { device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; if (device) break; device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; @@ -4217,11 +4490,11 @@ audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strate // A2DP speaker when forcing to speaker output if (!isInCall() && (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && - (getA2dpOutput() != 0) && !mA2dpSuspended) { + (getA2dpOutput() != 0)) { device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; if (device) break; } - if (mPhoneState != AUDIO_MODE_IN_CALL) { + if (!isInCall()) { device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; if (device) break; device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; @@ -4271,15 +4544,35 @@ audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strate // The second device used for sonification is the same as the device used by media strategy // FALL THROUGH + // FIXME: STRATEGY_ACCESSIBILITY and STRATEGY_REROUTING follow STRATEGY_MEDIA for now + case STRATEGY_ACCESSIBILITY: + if (strategy == STRATEGY_ACCESSIBILITY) { + // do not route accessibility prompts to a digital output currently configured with a + // compressed format as they would likely not be mixed and dropped. + for (size_t i = 0; i < mOutputs.size(); i++) { + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); + audio_devices_t devices = desc->device() & + (AUDIO_DEVICE_OUT_HDMI | AUDIO_DEVICE_OUT_SPDIF | AUDIO_DEVICE_OUT_HDMI_ARC); + if (desc->isActive() && !audio_is_linear_pcm(desc->mFormat) && + devices != AUDIO_DEVICE_NONE) { + availableOutputDeviceTypes = availableOutputDeviceTypes & ~devices; + } + } + } + // FALL THROUGH + + case STRATEGY_REROUTING: 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 (mAvailableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0")) != 0) { + 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) { + (getA2dpOutput() != 0)) { device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; @@ -4684,7 +4977,8 @@ status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input, return status; } -sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device, +sp<IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device, + String8 address, uint32_t& samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -4702,9 +4996,10 @@ sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getInputProfile(audio_devi { sp<IOProfile> profile = mHwModules[i]->mInputProfiles[j]; // profile->log(); - if (profile->isCompatibleProfile(device, samplingRate, + if (profile->isCompatibleProfile(device, address, samplingRate, &samplingRate /*updatedSamplingRate*/, format, channelMask, (audio_output_flags_t) flags)) { + return profile; } } @@ -4712,11 +5007,42 @@ sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getInputProfile(audio_devi return NULL; } + +audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, + AudioMix **policyMix) +{ + audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & + ~AUDIO_DEVICE_BIT_IN; + + for (size_t i = 0; i < mPolicyMixes.size(); i++) { + if (mPolicyMixes[i]->mMix.mMixType != MIX_TYPE_RECORDERS) { + continue; + } + for (size_t j = 0; j < mPolicyMixes[i]->mMix.mCriteria.size(); j++) { + if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mPolicyMixes[i]->mMix.mCriteria[j].mRule && + mPolicyMixes[i]->mMix.mCriteria[j].mAttr.mSource == inputSource) || + (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mPolicyMixes[i]->mMix.mCriteria[j].mRule && + mPolicyMixes[i]->mMix.mCriteria[j].mAttr.mSource != inputSource)) { + if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { + if (policyMix != NULL) { + *policyMix = &mPolicyMixes[i]->mMix; + } + return AUDIO_DEVICE_IN_REMOTE_SUBMIX; + } + break; + } + } + } + + return getDeviceForInputSource(inputSource); +} + 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) { @@ -4729,6 +5055,9 @@ audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t input case AUDIO_SOURCE_MIC: if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP; + } else 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_USB_DEVICE) { @@ -4830,7 +5159,7 @@ bool AudioPolicyManager::isVirtualInputDevice(audio_devices_t device) } bool AudioPolicyManager::deviceDistinguishesOnAddress(audio_devices_t device) { - return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL) != 0); + return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL & ~AUDIO_DEVICE_BIT_IN) != 0); } audio_io_handle_t AudioPolicyManager::getActiveInput(bool ignoreVirtualInputs) @@ -4851,286 +5180,36 @@ uint32_t AudioPolicyManager::activeInputsCount() const for (size_t i = 0; i < mInputs.size(); i++) { const sp<AudioInputDescriptor> desc = mInputs.valueAt(i); if (desc->mRefCount > 0) { - return count++; + count++; } } return count; } -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. - // - HDMI-CEC system audio mode only output: give priority to available item in order. - if (device & AUDIO_DEVICE_OUT_SPEAKER) { - device = AUDIO_DEVICE_OUT_SPEAKER; - } else if (device & AUDIO_DEVICE_OUT_HDMI_ARC) { - device = AUDIO_DEVICE_OUT_HDMI_ARC; - } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) { - device = AUDIO_DEVICE_OUT_AUX_LINE; - } else if (device & AUDIO_DEVICE_OUT_SPDIF) { - device = AUDIO_DEVICE_OUT_SPDIF; - } else { - device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP); - } - } - - /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/ - if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE) - device = AUDIO_DEVICE_OUT_SPEAKER; - - 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_LINE: - case AUDIO_DEVICE_OUT_AUX_DIGITAL: - /*USB? Remote submix?*/ - return DEVICE_CATEGORY_EXT_MEDIA; - case AUDIO_DEVICE_OUT_SPEAKER: - case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: - case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: - 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::sExtMediaSystemVolumeCurve[AudioPolicyManager::VOLCNT] = { - {1, -58.0f}, {20, -40.0f}, {60, -21.0f}, {100, -10.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT] = { - {1, -56.0f}, {20, -34.0f}, {60, -11.0f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { - {1, -55.0f}, {20, -43.0f}, {86, -12.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::sLinearVolumeCurve[AudioPolicyManager::VOLCNT] = { - {0, -96.0f}, {33, -68.0f}, {66, -34.0f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sSilentVolumeCurve[AudioPolicyManager::VOLCNT] = { - {0, -96.0f}, {1, -96.0f}, {2, -96.0f}, {100, -96.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 - sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_SYSTEM - sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_RING - sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_MUSIC - sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_ALARM - sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_NOTIFICATION - sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_BLUETOOTH_SCO - sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_ENFORCED_AUDIBLE - sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_DTMF - sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_TTS - // "Transmitted Through Speaker": always silent except on DEVICE_CATEGORY_SPEAKER - sSilentVolumeCurve, // DEVICE_CATEGORY_HEADSET - sLinearVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sSilentVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sSilentVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, -}; - void AudioPolicyManager::initializeVolumeCurves() { for (int i = 0; i < AUDIO_STREAM_CNT; i++) { - for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + for (int j = 0; j < ApmGains::DEVICE_CATEGORY_CNT; j++) { mStreams[i].mVolumeCurve[j] = - sVolumeProfiles[i][j]; + ApmGains::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; - mStreams[AUDIO_STREAM_MUSIC].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = - sSpeakerMediaVolumeCurveDrc; + mStreams[AUDIO_STREAM_SYSTEM].mVolumeCurve[ApmGains::DEVICE_CATEGORY_SPEAKER] = + ApmGains::sDefaultSystemVolumeCurveDrc; + mStreams[AUDIO_STREAM_RING].mVolumeCurve[ApmGains::DEVICE_CATEGORY_SPEAKER] = + ApmGains::sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_ALARM].mVolumeCurve[ApmGains::DEVICE_CATEGORY_SPEAKER] = + ApmGains::sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_NOTIFICATION].mVolumeCurve[ApmGains::DEVICE_CATEGORY_SPEAKER] = + ApmGains::sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_MUSIC].mVolumeCurve[ApmGains::DEVICE_CATEGORY_SPEAKER] = + ApmGains::sSpeakerMediaVolumeCurveDrc; + mStreams[AUDIO_STREAM_ACCESSIBILITY].mVolumeCurve[ApmGains::DEVICE_CATEGORY_SPEAKER] = + ApmGains::sSpeakerMediaVolumeCurveDrc; } } @@ -5147,7 +5226,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, device = outputDesc->device(); } - volume = volIndexToAmpl(device, streamDesc, index); + volume = ApmGains::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: @@ -5214,6 +5293,18 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, } float volume = computeVolume(stream, index, output, device); + // unit gain if rerouting to external policy + if (device == AUDIO_DEVICE_OUT_REMOTE_SUBMIX) { + ssize_t index = mOutputs.indexOfKey(output); + if (index >= 0) { + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index); + if (outputDesc->mPolicyMix != NULL) { + ALOGV("max gain when rerouting for output=%d", output); + volume = 1.0f; + } + } + + } // We actually change the volume if: // - the float value returned by computeVolume() changed // - the force flag is set @@ -5256,6 +5347,9 @@ void AudioPolicyManager::applyStreamVolumes(audio_io_handle_t output, ALOGVV("applyStreamVolumes() for output %d and device %x", output, device); for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + if (stream == AUDIO_STREAM_PATCH) { + continue; + } checkAndSetVolume((audio_stream_type_t)stream, mStreams[stream].getVolumeIndex(device), output, @@ -5273,6 +5367,9 @@ void AudioPolicyManager::setStrategyMute(routing_strategy strategy, { ALOGVV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output); for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + if (stream == AUDIO_STREAM_PATCH) { + continue; + } if (getStrategy((audio_stream_type_t)stream) == strategy) { setStreamMute((audio_stream_type_t)stream, on, output, delayMs, device); } @@ -5385,315 +5482,6 @@ uint32_t AudioPolicyManager::getMaxEffectsMemory() } -// --- AudioOutputDescriptor class implementation - -AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor( - const sp<IOProfile>& profile) - : mId(0), mIoHandle(0), mLatency(0), - mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPatchHandle(0), - 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) { - mFlags = (audio_output_flags_t)profile->mFlags; - mSamplingRate = profile->pickSamplingRate(); - mFormat = profile->pickFormat(); - mChannelMask = profile->pickChannelMask(); - if (profile->mGains.size() > 0) { - profile->mGains[0]->getDefaultConfig(&mGain); - } - } -} - -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 sp<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; -} - -void AudioPolicyManager::AudioOutputDescriptor::toAudioPortConfig( - struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig) const -{ - ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle); - - dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| - AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; - if (srcConfig != NULL) { - dstConfig->config_mask |= srcConfig->config_mask; - } - AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); - - dstConfig->id = mId; - dstConfig->role = AUDIO_PORT_ROLE_SOURCE; - dstConfig->type = AUDIO_PORT_TYPE_MIX; - dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; - dstConfig->ext.mix.handle = mIoHandle; - dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT; -} - -void AudioPolicyManager::AudioOutputDescriptor::toAudioPort( - struct audio_port *port) const -{ - ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle); - mProfile->toAudioPort(port); - port->id = mId; - toAudioPortConfig(&port->active_config); - port->ext.mix.hw_module = mProfile->mModule->mHandle; - port->ext.mix.handle = mIoHandle; - port->ext.mix.latency_class = - mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL; -} - -status_t AudioPolicyManager::AudioOutputDescriptor::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, " ID: %d\n", mId); - result.append(buffer); - 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 sp<IOProfile>& profile) - : mId(0), mIoHandle(0), - mDevice(AUDIO_DEVICE_NONE), mPatchHandle(0), mRefCount(0), - mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false) -{ - if (profile != NULL) { - mSamplingRate = profile->pickSamplingRate(); - mFormat = profile->pickFormat(); - mChannelMask = profile->pickChannelMask(); - if (profile->mGains.size() > 0) { - profile->mGains[0]->getDefaultConfig(&mGain); - } - } -} - -void AudioPolicyManager::AudioInputDescriptor::toAudioPortConfig( - struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig) const -{ - ALOG_ASSERT(mProfile != 0, - "toAudioPortConfig() called on input with null profile %d", mIoHandle); - dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| - AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; - if (srcConfig != NULL) { - dstConfig->config_mask |= srcConfig->config_mask; - } - - AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); - - dstConfig->id = mId; - dstConfig->role = AUDIO_PORT_ROLE_SINK; - dstConfig->type = AUDIO_PORT_TYPE_MIX; - dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; - dstConfig->ext.mix.handle = mIoHandle; - dstConfig->ext.mix.usecase.source = mInputSource; -} - -void AudioPolicyManager::AudioInputDescriptor::toAudioPort( - struct audio_port *port) const -{ - ALOG_ASSERT(mProfile != 0, "toAudioPort() called on input with null profile %d", mIoHandle); - - mProfile->toAudioPort(port); - port->id = mId; - toAudioPortConfig(&port->active_config); - port->ext.mix.hw_module = mProfile->mModule->mHandle; - port->ext.mix.handle = mIoHandle; - port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL; -} - -status_t AudioPolicyManager::AudioInputDescriptor::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, " ID: %d\n", mId); - result.append(buffer); - 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); - snprintf(buffer, SIZE, " Open Ref Count %d\n", mOpenRefCount); - 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) @@ -5717,1512 +5505,9 @@ status_t AudioPolicyManager::EffectDescriptor::dump(int fd) return NO_ERROR; } -// --- HwModule class implementation - -AudioPolicyManager::HwModule::HwModule(const char *name) - : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), - mHalVersion(AUDIO_DEVICE_API_VERSION_MIN), mHandle(0) -{ -} - -AudioPolicyManager::HwModule::~HwModule() -{ - for (size_t i = 0; i < mOutputProfiles.size(); i++) { - mOutputProfiles[i]->mSupportedDevices.clear(); - } - for (size_t i = 0; i < mInputProfiles.size(); i++) { - mInputProfiles[i]->mSupportedDevices.clear(); - } - free((void *)mName); -} - -status_t AudioPolicyManager::HwModule::loadInput(cnode *root) -{ - cnode *node = root->first_child; - - sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this); - - while (node) { - if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { - profile->loadSamplingRates((char *)node->value); - } else if (strcmp(node->name, FORMATS_TAG) == 0) { - profile->loadFormats((char *)node->value); - } else if (strcmp(node->name, CHANNELS_TAG) == 0) { - profile->loadInChannels((char *)node->value); - } else if (strcmp(node->name, DEVICES_TAG) == 0) { - profile->mSupportedDevices.loadDevicesFromName((char *)node->value, - mDeclaredDevices); - } else if (strcmp(node->name, FLAGS_TAG) == 0) { - profile->mFlags = parseInputFlagNames((char *)node->value); - } else if (strcmp(node->name, GAINS_TAG) == 0) { - profile->loadGains(node); - } - 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()); - - mInputProfiles.add(profile); - return NO_ERROR; - } else { - return BAD_VALUE; - } -} - -status_t AudioPolicyManager::HwModule::loadOutput(cnode *root) -{ - cnode *node = root->first_child; - - sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this); - - while (node) { - if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { - profile->loadSamplingRates((char *)node->value); - } else if (strcmp(node->name, FORMATS_TAG) == 0) { - profile->loadFormats((char *)node->value); - } else if (strcmp(node->name, CHANNELS_TAG) == 0) { - profile->loadOutChannels((char *)node->value); - } else if (strcmp(node->name, DEVICES_TAG) == 0) { - profile->mSupportedDevices.loadDevicesFromName((char *)node->value, - mDeclaredDevices); - } else if (strcmp(node->name, FLAGS_TAG) == 0) { - profile->mFlags = parseOutputFlagNames((char *)node->value); - } else if (strcmp(node->name, GAINS_TAG) == 0) { - profile->loadGains(node); - } - 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); - - mOutputProfiles.add(profile); - return NO_ERROR; - } else { - return BAD_VALUE; - } -} - -status_t AudioPolicyManager::HwModule::loadDevice(cnode *root) -{ - cnode *node = root->first_child; - - audio_devices_t type = AUDIO_DEVICE_NONE; - while (node) { - if (strcmp(node->name, DEVICE_TYPE) == 0) { - type = parseDeviceNames((char *)node->value); - break; - } - node = node->next; - } - if (type == AUDIO_DEVICE_NONE || - (!audio_is_input_device(type) && !audio_is_output_device(type))) { - ALOGW("loadDevice() bad type %08x", type); - return BAD_VALUE; - } - sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type); - deviceDesc->mModule = this; - - node = root->first_child; - while (node) { - if (strcmp(node->name, DEVICE_ADDRESS) == 0) { - deviceDesc->mAddress = String8((char *)node->value); - } else if (strcmp(node->name, CHANNELS_TAG) == 0) { - if (audio_is_input_device(type)) { - deviceDesc->loadInChannels((char *)node->value); - } else { - deviceDesc->loadOutChannels((char *)node->value); - } - } else if (strcmp(node->name, GAINS_TAG) == 0) { - deviceDesc->loadGains(node); - } - node = node->next; - } - - ALOGV("loadDevice() adding device name %s type %08x address %s", - deviceDesc->mName.string(), type, deviceDesc->mAddress.string()); - - mDeclaredDevices.add(deviceDesc); - - return NO_ERROR; -} - -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); - snprintf(buffer, SIZE, " - version: %u.%u\n", mHalVersion >> 8, mHalVersion & 0xFF); - 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 %zu:\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 %zu:\n", i); - write(fd, buffer, strlen(buffer)); - mInputProfiles[i]->dump(fd); - } - } - if (mDeclaredDevices.size()) { - write(fd, " - devices:\n", strlen(" - devices:\n")); - for (size_t i = 0; i < mDeclaredDevices.size(); i++) { - mDeclaredDevices[i]->dump(fd, 4, i); - } - } -} - -// --- AudioPort class implementation - - -AudioPolicyManager::AudioPort::AudioPort(const String8& name, audio_port_type_t type, - audio_port_role_t role, const sp<HwModule>& module) : - mName(name), mType(type), mRole(role), mModule(module), mFlags(0) -{ - mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) || - ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK)); -} - -void AudioPolicyManager::AudioPort::toAudioPort(struct audio_port *port) const -{ - port->role = mRole; - port->type = mType; - unsigned int i; - for (i = 0; i < mSamplingRates.size() && i < AUDIO_PORT_MAX_SAMPLING_RATES; i++) { - if (mSamplingRates[i] != 0) { - port->sample_rates[i] = mSamplingRates[i]; - } - } - port->num_sample_rates = i; - for (i = 0; i < mChannelMasks.size() && i < AUDIO_PORT_MAX_CHANNEL_MASKS; i++) { - if (mChannelMasks[i] != 0) { - port->channel_masks[i] = mChannelMasks[i]; - } - } - port->num_channel_masks = i; - for (i = 0; i < mFormats.size() && i < AUDIO_PORT_MAX_FORMATS; i++) { - if (mFormats[i] != 0) { - port->formats[i] = mFormats[i]; - } - } - port->num_formats = i; - - ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size()); - - for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) { - port->gains[i] = mGains[i]->mGain; - } - port->num_gains = i; -} - -void AudioPolicyManager::AudioPort::importAudioPort(const sp<AudioPort> port) { - for (size_t k = 0 ; k < port->mSamplingRates.size() ; k++) { - const uint32_t rate = port->mSamplingRates.itemAt(k); - if (rate != 0) { // skip "dynamic" rates - bool hasRate = false; - for (size_t l = 0 ; l < mSamplingRates.size() ; l++) { - if (rate == mSamplingRates.itemAt(l)) { - hasRate = true; - break; - } - } - if (!hasRate) { // never import a sampling rate twice - mSamplingRates.add(rate); - } - } - } - for (size_t k = 0 ; k < port->mChannelMasks.size() ; k++) { - const audio_channel_mask_t mask = port->mChannelMasks.itemAt(k); - if (mask != 0) { // skip "dynamic" masks - bool hasMask = false; - for (size_t l = 0 ; l < mChannelMasks.size() ; l++) { - if (mask == mChannelMasks.itemAt(l)) { - hasMask = true; - break; - } - } - if (!hasMask) { // never import a channel mask twice - mChannelMasks.add(mask); - } - } - } - for (size_t k = 0 ; k < port->mFormats.size() ; k++) { - const audio_format_t format = port->mFormats.itemAt(k); - if (format != 0) { // skip "dynamic" formats - bool hasFormat = false; - for (size_t l = 0 ; l < mFormats.size() ; l++) { - if (format == mFormats.itemAt(l)) { - hasFormat = true; - break; - } - } - if (!hasFormat) { // never import a channel mask twice - mFormats.add(format); - } - } - } - for (size_t k = 0 ; k < port->mGains.size() ; k++) { - sp<AudioGain> gain = port->mGains.itemAt(k); - if (gain != 0) { - bool hasGain = false; - for (size_t l = 0 ; l < mGains.size() ; l++) { - if (gain == mGains.itemAt(l)) { - hasGain = true; - break; - } - } - if (!hasGain) { // never import a gain twice - mGains.add(gain); - } - } - } -} - -void AudioPolicyManager::AudioPort::clearCapabilities() { - mChannelMasks.clear(); - mFormats.clear(); - mSamplingRates.clear(); - mGains.clear(); -} - -void AudioPolicyManager::AudioPort::loadSamplingRates(char *name) -{ - 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) { - mSamplingRates.add(0); - return; - } - - while (str != NULL) { - uint32_t rate = atoi(str); - if (rate != 0) { - ALOGV("loadSamplingRates() adding rate %d", rate); - mSamplingRates.add(rate); - } - str = strtok(NULL, "|"); - } -} - -void AudioPolicyManager::AudioPort::loadFormats(char *name) -{ - 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) { - 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) { - mFormats.add(format); - } - str = strtok(NULL, "|"); - } -} - -void AudioPolicyManager::AudioPort::loadInChannels(char *name) -{ - const char *str = strtok(name, "|"); - - ALOGV("loadInChannels() %s", name); - - if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { - 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); - mChannelMasks.add(channelMask); - } - str = strtok(NULL, "|"); - } -} - -void AudioPolicyManager::AudioPort::loadOutChannels(char *name) -{ - 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) { - 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) { - mChannelMasks.add(channelMask); - } - str = strtok(NULL, "|"); - } - return; -} - -audio_gain_mode_t AudioPolicyManager::AudioPort::loadGainMode(char *name) -{ - const char *str = strtok(name, "|"); - - ALOGV("loadGainMode() %s", name); - audio_gain_mode_t mode = 0; - while (str != NULL) { - mode |= (audio_gain_mode_t)stringToEnum(sGainModeNameToEnumTable, - ARRAY_SIZE(sGainModeNameToEnumTable), - str); - str = strtok(NULL, "|"); - } - return mode; -} - -void AudioPolicyManager::AudioPort::loadGain(cnode *root, int index) -{ - cnode *node = root->first_child; - - sp<AudioGain> gain = new AudioGain(index, mUseInChannelMask); - - while (node) { - if (strcmp(node->name, GAIN_MODE) == 0) { - gain->mGain.mode = loadGainMode((char *)node->value); - } else if (strcmp(node->name, GAIN_CHANNELS) == 0) { - if (mUseInChannelMask) { - gain->mGain.channel_mask = - (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable, - ARRAY_SIZE(sInChannelsNameToEnumTable), - (char *)node->value); - } else { - gain->mGain.channel_mask = - (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable, - ARRAY_SIZE(sOutChannelsNameToEnumTable), - (char *)node->value); - } - } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) { - gain->mGain.min_value = atoi((char *)node->value); - } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) { - gain->mGain.max_value = atoi((char *)node->value); - } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) { - gain->mGain.default_value = atoi((char *)node->value); - } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) { - gain->mGain.step_value = atoi((char *)node->value); - } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) { - gain->mGain.min_ramp_ms = atoi((char *)node->value); - } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) { - gain->mGain.max_ramp_ms = atoi((char *)node->value); - } - node = node->next; - } - - ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d", - gain->mGain.mode, gain->mGain.channel_mask, gain->mGain.min_value, gain->mGain.max_value); - - if (gain->mGain.mode == 0) { - return; - } - mGains.add(gain); -} - -void AudioPolicyManager::AudioPort::loadGains(cnode *root) -{ - cnode *node = root->first_child; - int index = 0; - while (node) { - ALOGV("loadGains() loading gain %s", node->name); - loadGain(node, index++); - node = node->next; - } -} - -status_t AudioPolicyManager::AudioPort::checkExactSamplingRate(uint32_t samplingRate) const -{ - for (size_t i = 0; i < mSamplingRates.size(); i ++) { - if (mSamplingRates[i] == samplingRate) { - return NO_ERROR; - } - } - return BAD_VALUE; -} - -status_t AudioPolicyManager::AudioPort::checkCompatibleSamplingRate(uint32_t samplingRate, - uint32_t *updatedSamplingRate) const -{ - // Search for the closest supported sampling rate that is above (preferred) - // or below (acceptable) the desired sampling rate, within a permitted ratio. - // The sampling rates do not need to be sorted in ascending order. - ssize_t maxBelow = -1; - ssize_t minAbove = -1; - uint32_t candidate; - for (size_t i = 0; i < mSamplingRates.size(); i++) { - candidate = mSamplingRates[i]; - if (candidate == samplingRate) { - if (updatedSamplingRate != NULL) { - *updatedSamplingRate = candidate; - } - return NO_ERROR; - } - // candidate < desired - if (candidate < samplingRate) { - if (maxBelow < 0 || candidate > mSamplingRates[maxBelow]) { - maxBelow = i; - } - // candidate > desired - } else { - if (minAbove < 0 || candidate < mSamplingRates[minAbove]) { - minAbove = i; - } - } - } - // This uses hard-coded knowledge about AudioFlinger resampling ratios. - // TODO Move these assumptions out. - static const uint32_t kMaxDownSampleRatio = 6; // beyond this aliasing occurs - static const uint32_t kMaxUpSampleRatio = 256; // beyond this sample rate inaccuracies occur - // due to approximation by an int32_t of the - // phase increments - // Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum. - if (minAbove >= 0) { - candidate = mSamplingRates[minAbove]; - if (candidate / kMaxDownSampleRatio <= samplingRate) { - if (updatedSamplingRate != NULL) { - *updatedSamplingRate = candidate; - } - return NO_ERROR; - } - } - // But if we have to up-sample from a lower sampling rate, that's OK. - if (maxBelow >= 0) { - candidate = mSamplingRates[maxBelow]; - if (candidate * kMaxUpSampleRatio >= samplingRate) { - if (updatedSamplingRate != NULL) { - *updatedSamplingRate = candidate; - } - return NO_ERROR; - } - } - // leave updatedSamplingRate unmodified - return BAD_VALUE; -} - -status_t AudioPolicyManager::AudioPort::checkExactChannelMask(audio_channel_mask_t channelMask) const -{ - for (size_t i = 0; i < mChannelMasks.size(); i++) { - if (mChannelMasks[i] == channelMask) { - return NO_ERROR; - } - } - return BAD_VALUE; -} - -status_t AudioPolicyManager::AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask) - const -{ - const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; - for (size_t i = 0; i < mChannelMasks.size(); i ++) { - // FIXME Does not handle multi-channel automatic conversions yet - audio_channel_mask_t supported = mChannelMasks[i]; - if (supported == channelMask) { - return NO_ERROR; - } - if (isRecordThread) { - // This uses hard-coded knowledge that AudioFlinger can silently down-mix and up-mix. - // FIXME Abstract this out to a table. - if (((supported == AUDIO_CHANNEL_IN_FRONT_BACK || supported == AUDIO_CHANNEL_IN_STEREO) - && channelMask == AUDIO_CHANNEL_IN_MONO) || - (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK - || channelMask == AUDIO_CHANNEL_IN_STEREO))) { - return NO_ERROR; - } - } - } - return BAD_VALUE; -} - -status_t AudioPolicyManager::AudioPort::checkFormat(audio_format_t format) const -{ - for (size_t i = 0; i < mFormats.size(); i ++) { - if (mFormats[i] == format) { - return NO_ERROR; - } - } - return BAD_VALUE; -} - - -uint32_t AudioPolicyManager::AudioPort::pickSamplingRate() const -{ - // special case for uninitialized dynamic profile - if (mSamplingRates.size() == 1 && mSamplingRates[0] == 0) { - return 0; - } - - // For direct outputs, pick minimum sampling rate: this helps ensuring that the - // channel count / sampling rate combination chosen will be supported by the connected - // sink - if ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && - (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { - uint32_t samplingRate = UINT_MAX; - for (size_t i = 0; i < mSamplingRates.size(); i ++) { - if ((mSamplingRates[i] < samplingRate) && (mSamplingRates[i] > 0)) { - samplingRate = mSamplingRates[i]; - } - } - return (samplingRate == UINT_MAX) ? 0 : samplingRate; - } - - uint32_t samplingRate = 0; - uint32_t maxRate = MAX_MIXER_SAMPLING_RATE; - - // For mixed output and inputs, use max mixer sampling rates. Do not - // limit sampling rate otherwise - if (mType != AUDIO_PORT_TYPE_MIX) { - maxRate = UINT_MAX; - } - for (size_t i = 0; i < mSamplingRates.size(); i ++) { - if ((mSamplingRates[i] > samplingRate) && (mSamplingRates[i] <= maxRate)) { - samplingRate = mSamplingRates[i]; - } - } - return samplingRate; -} - -audio_channel_mask_t AudioPolicyManager::AudioPort::pickChannelMask() const -{ - // special case for uninitialized dynamic profile - if (mChannelMasks.size() == 1 && mChannelMasks[0] == 0) { - return AUDIO_CHANNEL_NONE; - } - audio_channel_mask_t channelMask = AUDIO_CHANNEL_NONE; - - // For direct outputs, pick minimum channel count: this helps ensuring that the - // channel count / sampling rate combination chosen will be supported by the connected - // sink - if ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && - (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { - uint32_t channelCount = UINT_MAX; - for (size_t i = 0; i < mChannelMasks.size(); i ++) { - uint32_t cnlCount; - if (mUseInChannelMask) { - cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); - } else { - cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); - } - if ((cnlCount < channelCount) && (cnlCount > 0)) { - channelMask = mChannelMasks[i]; - channelCount = cnlCount; - } - } - return channelMask; - } - - uint32_t channelCount = 0; - uint32_t maxCount = MAX_MIXER_CHANNEL_COUNT; - - // For mixed output and inputs, use max mixer channel count. Do not - // limit channel count otherwise - if (mType != AUDIO_PORT_TYPE_MIX) { - maxCount = UINT_MAX; - } - for (size_t i = 0; i < mChannelMasks.size(); i ++) { - uint32_t cnlCount; - if (mUseInChannelMask) { - cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); - } else { - cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); - } - if ((cnlCount > channelCount) && (cnlCount <= maxCount)) { - channelMask = mChannelMasks[i]; - channelCount = cnlCount; - } - } - return channelMask; -} - -/* format in order of increasing preference */ -const audio_format_t AudioPolicyManager::AudioPort::sPcmFormatCompareTable[] = { - AUDIO_FORMAT_DEFAULT, - AUDIO_FORMAT_PCM_16_BIT, - AUDIO_FORMAT_PCM_8_24_BIT, - AUDIO_FORMAT_PCM_24_BIT_PACKED, - AUDIO_FORMAT_PCM_32_BIT, - AUDIO_FORMAT_PCM_FLOAT, -}; - -int AudioPolicyManager::AudioPort::compareFormats(audio_format_t format1, - audio_format_t format2) -{ - // NOTE: AUDIO_FORMAT_INVALID is also considered not PCM and will be compared equal to any - // compressed format and better than any PCM format. This is by design of pickFormat() - if (!audio_is_linear_pcm(format1)) { - if (!audio_is_linear_pcm(format2)) { - return 0; - } - return 1; - } - if (!audio_is_linear_pcm(format2)) { - return -1; - } - - int index1 = -1, index2 = -1; - for (size_t i = 0; - (i < ARRAY_SIZE(sPcmFormatCompareTable)) && ((index1 == -1) || (index2 == -1)); - i ++) { - if (sPcmFormatCompareTable[i] == format1) { - index1 = i; - } - if (sPcmFormatCompareTable[i] == format2) { - index2 = i; - } - } - // format1 not found => index1 < 0 => format2 > format1 - // format2 not found => index2 < 0 => format2 < format1 - return index1 - index2; -} - -audio_format_t AudioPolicyManager::AudioPort::pickFormat() const -{ - // special case for uninitialized dynamic profile - if (mFormats.size() == 1 && mFormats[0] == 0) { - return AUDIO_FORMAT_DEFAULT; - } - - audio_format_t format = AUDIO_FORMAT_DEFAULT; - audio_format_t bestFormat = - AudioPolicyManager::AudioPort::sPcmFormatCompareTable[ - ARRAY_SIZE(AudioPolicyManager::AudioPort::sPcmFormatCompareTable) - 1]; - // For mixed output and inputs, use best mixer output format. Do not - // limit format otherwise - if ((mType != AUDIO_PORT_TYPE_MIX) || - ((mRole == AUDIO_PORT_ROLE_SOURCE) && - (((mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) != 0)))) { - bestFormat = AUDIO_FORMAT_INVALID; - } - - for (size_t i = 0; i < mFormats.size(); i ++) { - if ((compareFormats(mFormats[i], format) > 0) && - (compareFormats(mFormats[i], bestFormat) <= 0)) { - format = mFormats[i]; - } - } - return format; -} - -status_t AudioPolicyManager::AudioPort::checkGain(const struct audio_gain_config *gainConfig, - int index) const -{ - if (index < 0 || (size_t)index >= mGains.size()) { - return BAD_VALUE; - } - return mGains[index]->checkConfig(gainConfig); -} - -void AudioPolicyManager::AudioPort::dump(int fd, int spaces) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - if (mName.size() != 0) { - snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string()); - result.append(buffer); - } - - if (mSamplingRates.size() != 0) { - snprintf(buffer, SIZE, "%*s- sampling rates: ", spaces, ""); - result.append(buffer); - for (size_t i = 0; i < mSamplingRates.size(); i++) { - if (i == 0 && mSamplingRates[i] == 0) { - snprintf(buffer, SIZE, "Dynamic"); - } else { - snprintf(buffer, SIZE, "%d", mSamplingRates[i]); - } - result.append(buffer); - result.append(i == (mSamplingRates.size() - 1) ? "" : ", "); - } - result.append("\n"); - } - - if (mChannelMasks.size() != 0) { - snprintf(buffer, SIZE, "%*s- channel masks: ", spaces, ""); - result.append(buffer); - for (size_t i = 0; i < mChannelMasks.size(); i++) { - ALOGV("AudioPort::dump mChannelMasks %zu %08x", i, mChannelMasks[i]); - - if (i == 0 && mChannelMasks[i] == 0) { - snprintf(buffer, SIZE, "Dynamic"); - } else { - snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]); - } - result.append(buffer); - result.append(i == (mChannelMasks.size() - 1) ? "" : ", "); - } - result.append("\n"); - } - - if (mFormats.size() != 0) { - snprintf(buffer, SIZE, "%*s- formats: ", spaces, ""); - result.append(buffer); - for (size_t i = 0; i < mFormats.size(); i++) { - const char *formatStr = enumToString(sFormatNameToEnumTable, - ARRAY_SIZE(sFormatNameToEnumTable), - mFormats[i]); - if (i == 0 && strcmp(formatStr, "") == 0) { - snprintf(buffer, SIZE, "Dynamic"); - } else { - snprintf(buffer, SIZE, "%s", formatStr); - } - result.append(buffer); - result.append(i == (mFormats.size() - 1) ? "" : ", "); - } - result.append("\n"); - } - write(fd, result.string(), result.size()); - if (mGains.size() != 0) { - snprintf(buffer, SIZE, "%*s- gains:\n", spaces, ""); - write(fd, buffer, strlen(buffer) + 1); - result.append(buffer); - for (size_t i = 0; i < mGains.size(); i++) { - mGains[i]->dump(fd, spaces + 2, i); - } - } -} - -// --- AudioGain class implementation - -AudioPolicyManager::AudioGain::AudioGain(int index, bool useInChannelMask) -{ - mIndex = index; - mUseInChannelMask = useInChannelMask; - memset(&mGain, 0, sizeof(struct audio_gain)); -} - -void AudioPolicyManager::AudioGain::getDefaultConfig(struct audio_gain_config *config) -{ - config->index = mIndex; - config->mode = mGain.mode; - config->channel_mask = mGain.channel_mask; - if ((mGain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { - config->values[0] = mGain.default_value; - } else { - uint32_t numValues; - if (mUseInChannelMask) { - numValues = audio_channel_count_from_in_mask(mGain.channel_mask); - } else { - numValues = audio_channel_count_from_out_mask(mGain.channel_mask); - } - for (size_t i = 0; i < numValues; i++) { - config->values[i] = mGain.default_value; - } - } - if ((mGain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { - config->ramp_duration_ms = mGain.min_ramp_ms; - } -} - -status_t AudioPolicyManager::AudioGain::checkConfig(const struct audio_gain_config *config) -{ - if ((config->mode & ~mGain.mode) != 0) { - return BAD_VALUE; - } - if ((config->mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { - if ((config->values[0] < mGain.min_value) || - (config->values[0] > mGain.max_value)) { - return BAD_VALUE; - } - } else { - if ((config->channel_mask & ~mGain.channel_mask) != 0) { - return BAD_VALUE; - } - uint32_t numValues; - if (mUseInChannelMask) { - numValues = audio_channel_count_from_in_mask(config->channel_mask); - } else { - numValues = audio_channel_count_from_out_mask(config->channel_mask); - } - for (size_t i = 0; i < numValues; i++) { - if ((config->values[i] < mGain.min_value) || - (config->values[i] > mGain.max_value)) { - return BAD_VALUE; - } - } - } - if ((config->mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { - if ((config->ramp_duration_ms < mGain.min_ramp_ms) || - (config->ramp_duration_ms > mGain.max_ramp_ms)) { - return BAD_VALUE; - } - } - return NO_ERROR; -} - -void AudioPolicyManager::AudioGain::dump(int fd, int spaces, int index) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "%*sGain %d:\n", spaces, "", index+1); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- mode: %08x\n", spaces, "", mGain.mode); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- channel_mask: %08x\n", spaces, "", mGain.channel_mask); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- min_value: %d mB\n", spaces, "", mGain.min_value); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- max_value: %d mB\n", spaces, "", mGain.max_value); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- default_value: %d mB\n", spaces, "", mGain.default_value); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- step_value: %d mB\n", spaces, "", mGain.step_value); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- min_ramp_ms: %d ms\n", spaces, "", mGain.min_ramp_ms); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- max_ramp_ms: %d ms\n", spaces, "", mGain.max_ramp_ms); - result.append(buffer); - - write(fd, result.string(), result.size()); -} - -// --- AudioPortConfig class implementation - -AudioPolicyManager::AudioPortConfig::AudioPortConfig() -{ - mSamplingRate = 0; - mChannelMask = AUDIO_CHANNEL_NONE; - mFormat = AUDIO_FORMAT_INVALID; - mGain.index = -1; -} - -status_t AudioPolicyManager::AudioPortConfig::applyAudioPortConfig( - const struct audio_port_config *config, - struct audio_port_config *backupConfig) -{ - struct audio_port_config localBackupConfig; - status_t status = NO_ERROR; - - localBackupConfig.config_mask = config->config_mask; - toAudioPortConfig(&localBackupConfig); - - sp<AudioPort> audioport = getAudioPort(); - if (audioport == 0) { - status = NO_INIT; - goto exit; - } - if (config->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { - status = audioport->checkExactSamplingRate(config->sample_rate); - if (status != NO_ERROR) { - goto exit; - } - mSamplingRate = config->sample_rate; - } - if (config->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { - status = audioport->checkExactChannelMask(config->channel_mask); - if (status != NO_ERROR) { - goto exit; - } - mChannelMask = config->channel_mask; - } - if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) { - status = audioport->checkFormat(config->format); - if (status != NO_ERROR) { - goto exit; - } - mFormat = config->format; - } - if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) { - status = audioport->checkGain(&config->gain, config->gain.index); - if (status != NO_ERROR) { - goto exit; - } - mGain = config->gain; - } - -exit: - if (status != NO_ERROR) { - applyAudioPortConfig(&localBackupConfig); - } - if (backupConfig != NULL) { - *backupConfig = localBackupConfig; - } - return status; -} - -void AudioPolicyManager::AudioPortConfig::toAudioPortConfig( - struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig) const -{ - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { - dstConfig->sample_rate = mSamplingRate; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)) { - dstConfig->sample_rate = srcConfig->sample_rate; - } - } else { - dstConfig->sample_rate = 0; - } - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { - dstConfig->channel_mask = mChannelMask; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)) { - dstConfig->channel_mask = srcConfig->channel_mask; - } - } else { - dstConfig->channel_mask = AUDIO_CHANNEL_NONE; - } - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) { - dstConfig->format = mFormat; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)) { - dstConfig->format = srcConfig->format; - } - } else { - dstConfig->format = AUDIO_FORMAT_INVALID; - } - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) { - dstConfig->gain = mGain; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN)) { - dstConfig->gain = srcConfig->gain; - } - } else { - dstConfig->gain.index = -1; - } - if (dstConfig->gain.index != -1) { - dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN; - } else { - dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN; - } -} - -// --- IOProfile class implementation - -AudioPolicyManager::IOProfile::IOProfile(const String8& name, audio_port_role_t role, - const sp<HwModule>& module) - : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, 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, - uint32_t *updatedSamplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - uint32_t flags) const -{ - const bool isPlaybackThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SOURCE; - const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; - ALOG_ASSERT(isPlaybackThread != isRecordThread); - - if ((mSupportedDevices.types() & device) != device) { - return false; - } - - if (samplingRate == 0) { - return false; - } - uint32_t myUpdatedSamplingRate = samplingRate; - if (isPlaybackThread && checkExactSamplingRate(samplingRate) != NO_ERROR) { - return false; - } - if (isRecordThread && checkCompatibleSamplingRate(samplingRate, &myUpdatedSamplingRate) != - NO_ERROR) { - return false; - } - - if (!audio_is_valid_format(format) || checkFormat(format) != NO_ERROR) { - return false; - } - - if (isPlaybackThread && (!audio_is_output_channel(channelMask) || - checkExactChannelMask(channelMask) != NO_ERROR)) { - return false; - } - if (isRecordThread && (!audio_is_input_channel(channelMask) || - checkCompatibleChannelMask(channelMask) != NO_ERROR)) { - return false; - } - - if (isPlaybackThread && (mFlags & flags) != flags) { - return false; - } - // The only input flag that is allowed to be different is the fast flag. - // An existing fast stream is compatible with a normal track request. - // An existing normal stream is compatible with a fast track request, - // but the fast request will be denied by AudioFlinger and converted to normal track. - if (isRecordThread && ((mFlags ^ flags) & - ~AUDIO_INPUT_FLAG_FAST)) { - return false; - } - - if (updatedSamplingRate != NULL) { - *updatedSamplingRate = myUpdatedSamplingRate; - } - return true; -} - -void AudioPolicyManager::IOProfile::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - AudioPort::dump(fd, 4); - - snprintf(buffer, SIZE, " - flags: 0x%04x\n", mFlags); - result.append(buffer); - snprintf(buffer, SIZE, " - devices:\n"); - result.append(buffer); - write(fd, result.string(), result.size()); - for (size_t i = 0; i < mSupportedDevices.size(); i++) { - mSupportedDevices[i]->dump(fd, 6, i); - } -} - -void AudioPolicyManager::IOProfile::log() -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - ALOGV(" - sampling rates: "); - for (size_t i = 0; i < mSamplingRates.size(); i++) { - ALOGV(" %d", mSamplingRates[i]); - } - - ALOGV(" - channel masks: "); - for (size_t i = 0; i < mChannelMasks.size(); i++) { - ALOGV(" 0x%04x", mChannelMasks[i]); - } - - ALOGV(" - formats: "); - for (size_t i = 0; i < mFormats.size(); i++) { - ALOGV(" 0x%08x", mFormats[i]); - } - - ALOGV(" - devices: 0x%04x\n", mSupportedDevices.types()); - ALOGV(" - flags: 0x%04x\n", mFlags); -} - - -// --- DeviceDescriptor implementation - - -AudioPolicyManager::DeviceDescriptor::DeviceDescriptor(const String8& name, audio_devices_t type) : - AudioPort(name, AUDIO_PORT_TYPE_DEVICE, - audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : - AUDIO_PORT_ROLE_SOURCE, - NULL), - mDeviceType(type), mAddress(""), mId(0) -{ - if (mGains.size() > 0) { - mGains[0]->getDefaultConfig(&mGain); - } -} - -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 (mDeviceType == other->mDeviceType) && - (mAddress == "" || other->mAddress == "" || mAddress == other->mAddress) && - (mChannelMask == 0 || other->mChannelMask == 0 || - mChannelMask == other->mChannelMask); -} - -void AudioPolicyManager::DeviceVector::refreshTypes() -{ - mDeviceTypes = AUDIO_DEVICE_NONE; - for(size_t i = 0; i < size(); i++) { - mDeviceTypes |= itemAt(i)->mDeviceType; - } - ALOGV("DeviceVector::refreshTypes() mDeviceTypes %08x", mDeviceTypes); -} - -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->mDeviceType); - 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->mDeviceType); - } 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(String8(""), type | role_bit)); - } -} - -void AudioPolicyManager::DeviceVector::loadDevicesFromName(char *name, - const DeviceVector& declaredDevices) -{ - char *devName = strtok(name, "|"); - while (devName != NULL) { - if (strlen(devName) != 0) { - audio_devices_t type = stringToEnum(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - devName); - if (type != AUDIO_DEVICE_NONE) { - sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(""), type); - if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX) { - dev->mAddress = String8("0"); - } - add(dev); - } else { - sp<DeviceDescriptor> deviceDesc = - declaredDevices.getDeviceFromName(String8(devName)); - if (deviceDesc != 0) { - add(deviceDesc); - } - } - } - devName = strtok(NULL, "|"); - } -} - -sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDevice( - audio_devices_t type, String8 address) const -{ - sp<DeviceDescriptor> device; - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->mDeviceType == type) { - device = itemAt(i); - if (itemAt(i)->mAddress = address) { - break; - } - } - } - ALOGV("DeviceVector::getDevice() for type %d address %s found %p", - type, address.string(), device.get()); - return device; -} - -sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromId( - audio_port_handle_t id) const -{ - sp<DeviceDescriptor> device; - for (size_t i = 0; i < size(); i++) { - ALOGV("DeviceVector::getDeviceFromId(%d) itemAt(%zu)->mId %d", id, i, itemAt(i)->mId); - if (itemAt(i)->mId == id) { - device = itemAt(i); - break; - } - } - return device; -} - -AudioPolicyManager::DeviceVector AudioPolicyManager::DeviceVector::getDevicesFromType( - audio_devices_t type) const -{ - DeviceVector devices; - for (size_t i = 0; (i < size()) && (type != AUDIO_DEVICE_NONE); i++) { - if (itemAt(i)->mDeviceType & type & ~AUDIO_DEVICE_BIT_IN) { - devices.add(itemAt(i)); - type &= ~itemAt(i)->mDeviceType; - ALOGV("DeviceVector::getDevicesFromType() for type %x found %p", - itemAt(i)->mDeviceType, itemAt(i).get()); - } - } - return devices; -} - -AudioPolicyManager::DeviceVector AudioPolicyManager::DeviceVector::getDevicesFromTypeAddr( - audio_devices_t type, String8 address) const -{ - DeviceVector devices; - //ALOGV(" looking for device=%x, addr=%s", type, address.string()); - for (size_t i = 0; i < size(); i++) { - //ALOGV(" at i=%d: device=%x, addr=%s", - // i, itemAt(i)->mDeviceType, itemAt(i)->mAddress.string()); - if (itemAt(i)->mDeviceType == type) { - if (itemAt(i)->mAddress == address) { - //ALOGV(" found matching address %s", address.string()); - devices.add(itemAt(i)); - } - } - } - return devices; -} - -sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromName( - const String8& name) const -{ - sp<DeviceDescriptor> device; - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->mName == name) { - device = itemAt(i); - break; - } - } - return device; -} - -void AudioPolicyManager::DeviceDescriptor::toAudioPortConfig( - struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig) const -{ - dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK|AUDIO_PORT_CONFIG_GAIN; - if (srcConfig != NULL) { - dstConfig->config_mask |= srcConfig->config_mask; - } - - AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); - - dstConfig->id = mId; - dstConfig->role = audio_is_output_device(mDeviceType) ? - AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE; - dstConfig->type = AUDIO_PORT_TYPE_DEVICE; - dstConfig->ext.device.type = mDeviceType; - dstConfig->ext.device.hw_module = mModule->mHandle; - strncpy(dstConfig->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN); -} - -void AudioPolicyManager::DeviceDescriptor::toAudioPort(struct audio_port *port) const -{ - ALOGV("DeviceDescriptor::toAudioPort() handle %d type %x", mId, mDeviceType); - AudioPort::toAudioPort(port); - port->id = mId; - toAudioPortConfig(&port->active_config); - port->ext.device.type = mDeviceType; - port->ext.device.hw_module = mModule->mHandle; - strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN); -} - -status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces, int index) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "%*sDevice %d:\n", spaces, "", index+1); - result.append(buffer); - if (mId != 0) { - snprintf(buffer, SIZE, "%*s- id: %2d\n", spaces, "", mId); - result.append(buffer); - } - snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "", - enumToString(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - mDeviceType)); - result.append(buffer); - if (mAddress.size() != 0) { - snprintf(buffer, SIZE, "%*s- address: %-32s\n", spaces, "", mAddress.string()); - result.append(buffer); - } - write(fd, result.string(), result.size()); - AudioPort::dump(fd, spaces); - - return NO_ERROR; -} - -status_t AudioPolicyManager::AudioPatch::dump(int fd, int spaces, int index) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - - snprintf(buffer, SIZE, "%*sAudio patch %d:\n", spaces, "", index+1); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- handle: %2d\n", spaces, "", mHandle); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- audio flinger handle: %2d\n", spaces, "", mAfPatchHandle); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mUid); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- %d sources:\n", spaces, "", mPatch.num_sources); - result.append(buffer); - for (size_t i = 0; i < mPatch.num_sources; i++) { - if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) { - snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", - mPatch.sources[i].id, enumToString(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - mPatch.sources[i].ext.device.type)); - } else { - snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", - mPatch.sources[i].id, mPatch.sources[i].ext.mix.handle); - } - result.append(buffer); - } - snprintf(buffer, SIZE, "%*s- %d sinks:\n", spaces, "", mPatch.num_sinks); - result.append(buffer); - for (size_t i = 0; i < mPatch.num_sinks; i++) { - if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) { - snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", - mPatch.sinks[i].id, enumToString(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - mPatch.sinks[i].ext.device.type)); - } else { - snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", - mPatch.sinks[i].id, mPatch.sinks[i].ext.mix.handle); - } - result.append(buffer); - } - - write(fd, result.string(), result.size()); - return NO_ERROR; -} // --- audio_policy.conf file parsing - -uint32_t AudioPolicyManager::parseOutputFlagNames(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(sOutputFlagNameToEnumTable, - ARRAY_SIZE(sOutputFlagNameToEnumTable), - 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 flag; -} - -uint32_t AudioPolicyManager::parseInputFlagNames(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(sInputFlagNameToEnumTable, - ARRAY_SIZE(sInputFlagNameToEnumTable), - flagName); - } - flagName = strtok(NULL, "|"); - } - return 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; -} - +// TODO candidate to be moved to ConfigParsingUtils void AudioPolicyManager::loadHwModule(cnode *root) { status_t status = NAME_NOT_FOUND; @@ -7272,6 +5557,7 @@ void AudioPolicyManager::loadHwModule(cnode *root) } } +// TODO candidate to be moved to ConfigParsingUtils void AudioPolicyManager::loadHwModules(cnode *root) { cnode *node = config_find(root, AUDIO_HW_MODULE_TAG); @@ -7287,6 +5573,7 @@ void AudioPolicyManager::loadHwModules(cnode *root) } } +// TODO candidate to be moved to ConfigParsingUtils void AudioPolicyManager::loadGlobalConfig(cnode *root, const sp<HwModule>& module) { cnode *node = config_find(root, GLOBAL_CONFIG_TAG); @@ -7307,11 +5594,12 @@ void AudioPolicyManager::loadGlobalConfig(cnode *root, const sp<HwModule>& modul 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); + audio_devices_t device = (audio_devices_t)ConfigParsingUtils::stringToEnum( + sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + (char *)node->value); if (device != AUDIO_DEVICE_NONE) { - mDefaultOutputDevice = new DeviceDescriptor(String8(""), device); + mDefaultOutputDevice = new DeviceDescriptor(String8("default-output"), device); } else { ALOGW("loadGlobalConfig() default device not specified"); } @@ -7321,7 +5609,7 @@ void AudioPolicyManager::loadGlobalConfig(cnode *root, const sp<HwModule>& modul declaredDevices); ALOGV("loadGlobalConfig() Available InputDevices %08x", mAvailableInputDevices.types()); } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) { - mSpeakerDrcEnabled = stringToBool((char *)node->value); + mSpeakerDrcEnabled = ConfigParsingUtils::stringToBool((char *)node->value); ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", mSpeakerDrcEnabled); } else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) { uint32_t major, minor; @@ -7334,6 +5622,7 @@ void AudioPolicyManager::loadGlobalConfig(cnode *root, const sp<HwModule>& modul } } +// TODO candidate to be moved to ConfigParsingUtils status_t AudioPolicyManager::loadAudioPolicyConfig(const char *path) { cnode *root; @@ -7362,8 +5651,8 @@ void AudioPolicyManager::defaultAudioPolicyConfig(void) { sp<HwModule> module; sp<IOProfile> profile; - sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(String8(""), - AUDIO_DEVICE_IN_BUILTIN_MIC); + sp<DeviceDescriptor> defaultInputDevice = + new DeviceDescriptor(String8("builtin-mic"), AUDIO_DEVICE_IN_BUILTIN_MIC); mAvailableOutputDevices.add(mDefaultOutputDevice); mAvailableInputDevices.add(defaultInputDevice); @@ -7396,14 +5685,27 @@ audio_stream_type_t AudioPolicyManager::streamTypefromAttributesInt(const audio_ if ((attr->flags & AUDIO_FLAG_SCO) == AUDIO_FLAG_SCO) { return AUDIO_STREAM_BLUETOOTH_SCO; } + if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) { + return AUDIO_STREAM_TTS; + } // usage to stream type mapping switch (attr->usage) { case AUDIO_USAGE_MEDIA: case AUDIO_USAGE_GAME: - case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: return AUDIO_STREAM_MUSIC; + case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: + if (isStreamActive(AUDIO_STREAM_ALARM)) { + return AUDIO_STREAM_ALARM; + } + if (isStreamActive(AUDIO_STREAM_RING)) { + return AUDIO_STREAM_RING; + } + if (isInCall()) { + return AUDIO_STREAM_VOICE_CALL; + } + return AUDIO_STREAM_ACCESSIBILITY; case AUDIO_USAGE_ASSISTANCE_SONIFICATION: return AUDIO_STREAM_SYSTEM; case AUDIO_USAGE_VOICE_COMMUNICATION: @@ -7429,4 +5731,36 @@ audio_stream_type_t AudioPolicyManager::streamTypefromAttributesInt(const audio_ return AUDIO_STREAM_MUSIC; } } + +bool AudioPolicyManager::isValidAttributes(const audio_attributes_t *paa) { + // has flags that map to a strategy? + if ((paa->flags & (AUDIO_FLAG_AUDIBILITY_ENFORCED | AUDIO_FLAG_SCO | AUDIO_FLAG_BEACON)) != 0) { + return true; + } + + // has known usage? + switch (paa->usage) { + case AUDIO_USAGE_UNKNOWN: + case AUDIO_USAGE_MEDIA: + case AUDIO_USAGE_VOICE_COMMUNICATION: + case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: + case AUDIO_USAGE_ALARM: + case AUDIO_USAGE_NOTIFICATION: + case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: + case AUDIO_USAGE_NOTIFICATION_EVENT: + case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: + case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: + case AUDIO_USAGE_ASSISTANCE_SONIFICATION: + case AUDIO_USAGE_GAME: + case AUDIO_USAGE_VIRTUAL_SOURCE: + break; + default: + return false; + } + return true; +} + }; // namespace android diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 50d7831..61ea6f2 100644 --- a/services/audiopolicy/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -23,8 +23,17 @@ #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/SortedVector.h> +#include <media/AudioPolicy.h> #include "AudioPolicyInterface.h" +#include "Gains.h" +#include "Ports.h" +#include "ConfigParsingUtils.h" +#include "Devices.h" +#include "IOProfile.h" +#include "HwModule.h" +#include "AudioInputDescriptor.h" +#include "AudioOutputDescriptor.h" namespace android { @@ -72,7 +81,8 @@ public: // AudioPolicyInterface virtual status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address); + const char *device_address, + const char *device_name); virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address); virtual void setPhoneState(audio_mode_t state); @@ -87,25 +97,32 @@ public: audio_channel_mask_t channelMask, audio_output_flags_t flags, const audio_offload_info_t *offloadInfo); - virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr, - 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 getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + 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); + audio_session_t session); 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_session_t session, - audio_input_flags_t flags); + audio_session_t session); + virtual void releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); + virtual status_t getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags, + input_type_t *inputType); // indicates to the audio policy manager that the input starts being used. virtual status_t startInput(audio_io_handle_t input, @@ -148,6 +165,8 @@ public: // 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 + // For the base implementation, "remotely" means playing during screen mirroring which + // uses an output for playback with a non-empty, non "0" address. virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const; virtual bool isSourceActive(audio_source_t source) const; @@ -178,361 +197,23 @@ public: virtual status_t releaseSoundTriggerSession(audio_session_t session); -protected: - - enum routing_strategy { - STRATEGY_MEDIA, - STRATEGY_PHONE, - STRATEGY_SONIFICATION, - STRATEGY_SONIFICATION_RESPECTFUL, - STRATEGY_DTMF, - STRATEGY_ENFORCED_AUDIBLE, - STRATEGY_TRANSMITTED_THROUGH_SPEAKER, - 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_EXT_MEDIA, - DEVICE_CATEGORY_CNT - }; - - class HwModule; - - class AudioGain: public RefBase - { - public: - AudioGain(int index, bool useInChannelMask); - virtual ~AudioGain() {} - - void dump(int fd, int spaces, int index) const; - - void getDefaultConfig(struct audio_gain_config *config); - status_t checkConfig(const struct audio_gain_config *config); - int mIndex; - struct audio_gain mGain; - bool mUseInChannelMask; - }; - - class AudioPort: public virtual RefBase - { - public: - AudioPort(const String8& name, audio_port_type_t type, - audio_port_role_t role, const sp<HwModule>& module); - virtual ~AudioPort() {} - - virtual void toAudioPort(struct audio_port *port) const; - - void importAudioPort(const sp<AudioPort> port); - void clearCapabilities(); - - void loadSamplingRates(char *name); - void loadFormats(char *name); - void loadOutChannels(char *name); - void loadInChannels(char *name); - - audio_gain_mode_t loadGainMode(char *name); - void loadGain(cnode *root, int index); - void loadGains(cnode *root); - - // searches for an exact match - status_t checkExactSamplingRate(uint32_t samplingRate) const; - // searches for a compatible match, and returns the best match via updatedSamplingRate - status_t checkCompatibleSamplingRate(uint32_t samplingRate, - uint32_t *updatedSamplingRate) const; - // searches for an exact match - status_t checkExactChannelMask(audio_channel_mask_t channelMask) const; - // searches for a compatible match, currently implemented for input channel masks only - status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask) const; - status_t checkFormat(audio_format_t format) const; - status_t checkGain(const struct audio_gain_config *gainConfig, int index) const; - - uint32_t pickSamplingRate() const; - audio_channel_mask_t pickChannelMask() const; - audio_format_t pickFormat() const; - - static const audio_format_t sPcmFormatCompareTable[]; - static int compareFormats(audio_format_t format1, audio_format_t format2); - - void dump(int fd, int spaces) const; - - String8 mName; - audio_port_type_t mType; - audio_port_role_t mRole; - bool mUseInChannelMask; - // 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 - Vector < sp<AudioGain> > mGains; // gain controllers - sp<HwModule> mModule; // audio HW module exposing this I/O stream - uint32_t mFlags; // attribute flags (e.g primary output, - // direct output...). - }; - - class AudioPortConfig: public virtual RefBase - { - public: - AudioPortConfig(); - virtual ~AudioPortConfig() {} - - status_t applyAudioPortConfig(const struct audio_port_config *config, - struct audio_port_config *backupConfig = NULL); - virtual void toAudioPortConfig(struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig = NULL) const = 0; - virtual sp<AudioPort> getAudioPort() const = 0; - uint32_t mSamplingRate; - audio_format_t mFormat; - audio_channel_mask_t mChannelMask; - struct audio_gain_config mGain; - }; - - - class AudioPatch: public RefBase - { - public: - AudioPatch(audio_patch_handle_t handle, - const struct audio_patch *patch, uid_t uid) : - mHandle(handle), mPatch(*patch), mUid(uid), mAfPatchHandle(0) {} - - status_t dump(int fd, int spaces, int index) const; + virtual status_t registerPolicyMixes(Vector<AudioMix> mixes); + virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes); - audio_patch_handle_t mHandle; - struct audio_patch mPatch; - uid_t mUid; - audio_patch_handle_t mAfPatchHandle; - }; - - class DeviceDescriptor: public AudioPort, public AudioPortConfig - { - public: - DeviceDescriptor(const String8& name, audio_devices_t type); - - virtual ~DeviceDescriptor() {} - - bool equals(const sp<DeviceDescriptor>& other) const; - virtual void toAudioPortConfig(struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig = NULL) const; - virtual sp<AudioPort> getAudioPort() const { return (AudioPort*) this; } - - virtual void toAudioPort(struct audio_port *port) const; - - status_t dump(int fd, int spaces, int index) const; - - audio_devices_t mDeviceType; - String8 mAddress; - audio_port_handle_t mId; - }; - - class DeviceVector : public SortedVector< sp<DeviceDescriptor> > - { - public: - DeviceVector() : SortedVector(), mDeviceTypes(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 mDeviceTypes; } - - void loadDevicesFromType(audio_devices_t types); - void loadDevicesFromName(char *name, const DeviceVector& declaredDevices); - - sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const; - DeviceVector getDevicesFromType(audio_devices_t types) const; - sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const; - sp<DeviceDescriptor> getDeviceFromName(const String8& name) const; - DeviceVector getDevicesFromTypeAddr(audio_devices_t type, String8 address) - const; - - private: - void refreshTypes(); - audio_devices_t mDeviceTypes; - }; - - // 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 AudioPort - { - public: - IOProfile(const String8& name, audio_port_role_t role, const sp<HwModule>& module); - virtual ~IOProfile(); - - // This method is used for both output and input. - // If parameter updatedSamplingRate is non-NULL, it is assigned the actual sample rate. - // For input, flags is interpreted as audio_input_flags_t. - // TODO: merge audio_output_flags_t and audio_input_flags_t. - bool isCompatibleProfile(audio_devices_t device, - uint32_t samplingRate, - uint32_t *updatedSamplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - uint32_t flags) const; - - void dump(int fd); - void log(); - - DeviceVector mSupportedDevices; // supported devices - // (devices this output can be routed to) - }; - - class HwModule : public RefBase - { - public: - HwModule(const char *name); - ~HwModule(); - - status_t loadOutput(cnode *root); - status_t loadInput(cnode *root); - status_t loadDevice(cnode *root); - - void dump(int fd); - - const char *const mName; // base name of the audio HW module (primary, a2dp ...) - uint32_t mHalVersion; // audio HAL API version - audio_module_handle_t mHandle; - Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module - Vector < sp<IOProfile> > mInputProfiles; // input profiles exposed by this module - DeviceVector mDeclaredDevices; // devices declared in audio_policy.conf - - }; - - // default volume curve - static const VolumeCurvePoint sDefaultVolumeCurve[AudioPolicyManager::VOLCNT]; - // default volume curve for media strategy - static const VolumeCurvePoint sDefaultMediaVolumeCurve[AudioPolicyManager::VOLCNT]; - // volume curve for non-media audio on ext media outputs (HDMI, Line, etc) - static const VolumeCurvePoint sExtMediaSystemVolumeCurve[AudioPolicyManager::VOLCNT]; - // volume curve for media strategy on speakers - static const VolumeCurvePoint sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sSpeakerMediaVolumeCurveDrc[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]; - static const VolumeCurvePoint sLinearVolumeCurve[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sSilentVolumeCurve[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 AudioPortConfig - { - public: - AudioOutputDescriptor(const sp<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 sp<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; - - virtual void toAudioPortConfig(struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig = NULL) const; - virtual sp<AudioPort> getAudioPort() const { return mProfile; } - void toAudioPort(struct audio_port *port) const; - - audio_port_handle_t mId; - audio_io_handle_t mIoHandle; // output handle - uint32_t mLatency; // - audio_output_flags_t mFlags; // - audio_devices_t mDevice; // current device this output is routed to - audio_patch_handle_t mPatchHandle; - uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output - nsecs_t mStopTime[AUDIO_STREAM_CNT]; - sp<AudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output - sp<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 sp<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 AudioPortConfig - { - public: - AudioInputDescriptor(const sp<IOProfile>& profile); - - status_t dump(int fd); - - audio_port_handle_t mId; - audio_io_handle_t mIoHandle; // input handle - audio_devices_t mDevice; // current device this input is routed to - audio_patch_handle_t mPatchHandle; - uint32_t mRefCount; // number of AudioRecord clients using - // this input - uint32_t mOpenRefCount; - audio_source_t mInputSource; // input source selected by application - //(mediarecorder.h) - const sp<IOProfile> mProfile; // I/O profile this output derives from - SortedVector<audio_session_t> mSessions; // audio sessions attached to this input - bool mIsSoundTrigger; // used by a soundtrigger capture - - virtual void toAudioPortConfig(struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig = NULL) const; - virtual sp<AudioPort> getAudioPort() const { return mProfile; } - void toAudioPort(struct audio_port *port) const; - }; - - // stream descriptor used for volume control - class StreamDescriptor - { - public: - StreamDescriptor(); - - int getVolumeIndex(audio_devices_t device); - void dump(int fd); + // Audio policy configuration file parsing (audio_policy.conf) + // TODO candidates to be moved to ConfigParsingUtils + void loadHwModule(cnode *root); + void loadHwModules(cnode *root); + void loadGlobalConfig(cnode *root, const sp<HwModule>& module); + status_t loadAudioPolicyConfig(const char *path); + void defaultAudioPolicyConfig(void); - 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 + // return the strategy corresponding to a given stream type + static routing_strategy getStrategy(audio_stream_type_t stream); - const VolumeCurvePoint *mVolumeCurve[DEVICE_CATEGORY_CNT]; - }; + static uint32_t nextUniqueId(); +protected: - // stream descriptor used for volume control class EffectDescriptor : public RefBase { public: @@ -549,9 +230,6 @@ protected: void addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc); void addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc); - // 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[], @@ -568,7 +246,7 @@ protected: // 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, + virtual uint32_t setOutputDevice(audio_io_handle_t output, audio_devices_t device, bool force = false, int delayMs = 0, @@ -698,12 +376,6 @@ protected: status_t setEffectEnabled(const sp<EffectDescriptor>& effectDesc, 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, sp<AudioOutputDescriptor> > openOutputs); bool vectorsEqual(SortedVector<audio_io_handle_t>& outputs1, @@ -713,7 +385,7 @@ protected: // 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(sp<AudioOutputDescriptor> outputDesc, + virtual uint32_t checkDeviceMuteStrategies(sp<AudioOutputDescriptor> outputDesc, audio_devices_t prevDevice, uint32_t delayMs); @@ -722,10 +394,11 @@ protected: audio_format_t format); // samplingRate parameter is an in/out and so may be modified sp<IOProfile> getInputProfile(audio_devices_t device, - uint32_t& samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_input_flags_t flags); + String8 address, + uint32_t& samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags); sp<IOProfile> getProfileForDirectOutput(audio_devices_t device, uint32_t samplingRate, audio_format_t format, @@ -736,9 +409,9 @@ protected: bool isNonOffloadableEffectEnabled(); - status_t addAudioPatch(audio_patch_handle_t handle, + virtual status_t addAudioPatch(audio_patch_handle_t handle, const sp<AudioPatch>& patch); - status_t removeAudioPatch(audio_patch_handle_t handle); + virtual status_t removeAudioPatch(audio_patch_handle_t handle); sp<AudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const; sp<AudioInputDescriptor> getInputFromId(audio_port_handle_t id) const; @@ -749,25 +422,6 @@ protected: void updateCallRouting(audio_devices_t rxDevice, int delayMs = 0); - // - // 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 uint32_t parseOutputFlagNames(char *name); - static uint32_t parseInputFlagNames(char *name); - static audio_devices_t parseDeviceNames(char *name); - void loadHwModule(cnode *root); - void loadHwModules(cnode *root); - void loadGlobalConfig(cnode *root, const sp<HwModule>& module); - status_t loadAudioPolicyConfig(const char *path); - void defaultAudioPolicyConfig(void); - uid_t mUidCached; AudioPolicyClientInterface *mpClientInterface; // audio policy client interface @@ -801,7 +455,7 @@ protected: // to boost soft sounds, used to adjust volume curves accordingly Vector < sp<HwModule> > mHwModules; - volatile int32_t mNextUniqueId; + static volatile int32_t mNextUniqueId; volatile int32_t mAudioPortGeneration; DefaultKeyedVector<audio_patch_handle_t, sp<AudioPatch> > mAudioPatches; @@ -823,6 +477,17 @@ protected: uint32_t mBeaconPlayingRefCount;// ref count for the playing beacon streams bool mBeaconMuted; // has STREAM_TTS been muted + // custom mix entry in mPolicyMixes + class AudioPolicyMix : public RefBase { + public: + AudioPolicyMix() {} + + AudioMix mMix; // Audio policy mix descriptor + sp<AudioOutputDescriptor> mOutput; // Corresponding output stream + }; + DefaultKeyedVector<String8, sp<AudioPolicyMix> > mPolicyMixes; // list of registered mixes + + #ifdef AUDIO_POLICY_TEST Mutex mLock; Condition mWaitWorkCV; @@ -837,13 +502,14 @@ protected: uint32_t mTestChannels; uint32_t mTestLatencyMs; #endif //AUDIO_POLICY_TEST - static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, - int indexInUi); + + static bool isVirtualInputDevice(audio_devices_t device); + + uint32_t nextAudioPortGeneration(); private: // 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); static bool deviceDistinguishesOnAddress(audio_devices_t device); // find the outputs on a given output descriptor that have the given address. // to be called on an AudioOutputDescriptor whose supported devices (as defined @@ -851,14 +517,14 @@ private: // see deviceDistinguishesOnAddress(audio_devices_t) for whether the device type is one // where addresses are used to distinguish between one connected device and another. void findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/, + const audio_devices_t device /*in*/, const String8 address /*in*/, SortedVector<audio_io_handle_t>& outputs /*out*/); - uint32_t nextUniqueId(); - uint32_t nextAudioPortGeneration(); uint32_t curAudioPortGeneration() const { return mAudioPortGeneration; } // internal method to return the output handle for the given device and format audio_io_handle_t getOutputForDevice( audio_devices_t device, + audio_session_t session, audio_stream_type_t stream, uint32_t samplingRate, audio_format_t format, @@ -874,6 +540,21 @@ private: // the mute/unmute happened uint32_t handleEventForBeacon(int event); uint32_t setBeaconMute(bool mute); + bool isValidAttributes(const audio_attributes_t *paa); + + // select input device corresponding to requested audio source and return associated policy + // mix if any. Calls getDeviceForInputSource(). + audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, + AudioMix **policyMix = NULL); + + // Called by setDeviceConnectionState(). + status_t setDeviceConnectionStateInt(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address, + const char *device_name); + sp<DeviceDescriptor> getDeviceDescriptor(const audio_devices_t device, + const char *device_address, + const char *device_name); }; }; diff --git a/services/audiopolicy/managerdefault/ConfigParsingUtils.cpp b/services/audiopolicy/managerdefault/ConfigParsingUtils.cpp new file mode 100644 index 0000000..1afd487 --- /dev/null +++ b/services/audiopolicy/managerdefault/ConfigParsingUtils.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "APM::ConfigParsingUtils" +//#define LOG_NDEBUG 0 + +#include "AudioPolicyManager.h" + +namespace android { + +//static +uint32_t ConfigParsingUtils::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; +} + +//static +const char *ConfigParsingUtils::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 ""; +} + +//static +bool ConfigParsingUtils::stringToBool(const char *value) +{ + return ((strcasecmp("true", value) == 0) || (strcmp("1", value) == 0)); +} + + +// --- audio_policy.conf file parsing +//static +uint32_t ConfigParsingUtils::parseOutputFlagNames(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 |= ConfigParsingUtils::stringToEnum(sOutputFlagNameToEnumTable, + ARRAY_SIZE(sOutputFlagNameToEnumTable), + 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 flag; +} + +//static +uint32_t ConfigParsingUtils::parseInputFlagNames(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(sInputFlagNameToEnumTable, + ARRAY_SIZE(sInputFlagNameToEnumTable), + flagName); + } + flagName = strtok(NULL, "|"); + } + return flag; +} + +//static +audio_devices_t ConfigParsingUtils::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; +} + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/ConfigParsingUtils.h b/services/audiopolicy/managerdefault/ConfigParsingUtils.h new file mode 100644 index 0000000..b2d9763 --- /dev/null +++ b/services/audiopolicy/managerdefault/ConfigParsingUtils.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace android { + +// ---------------------------------------------------------------------------- +// Definitions for audio_policy.conf file parsing +// ---------------------------------------------------------------------------- + +struct StringToEnum { + const char *name; + uint32_t value; +}; + +#define STRING_TO_ENUM(string) { #string, string } +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +const StringToEnum sDeviceNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER_SAFE), + 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_HDMI), + 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_OUT_TELEPHONY_TX), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_LINE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_HDMI_ARC), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPDIF), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_FM), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_LINE), + STRING_TO_ENUM(AUDIO_DEVICE_IN_AMBIENT), + 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_HDMI), + STRING_TO_ENUM(AUDIO_DEVICE_IN_TELEPHONY_RX), + 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), + STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_DEVICE), + STRING_TO_ENUM(AUDIO_DEVICE_IN_FM_TUNER), + STRING_TO_ENUM(AUDIO_DEVICE_IN_TV_TUNER), + STRING_TO_ENUM(AUDIO_DEVICE_IN_LINE), + STRING_TO_ENUM(AUDIO_DEVICE_IN_SPDIF), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP), + STRING_TO_ENUM(AUDIO_DEVICE_IN_LOOPBACK), +}; + +const StringToEnum sOutputFlagNameToEnumTable[] = { + 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), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_HW_AV_SYNC), +}; + +const StringToEnum sInputFlagNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_INPUT_FLAG_FAST), + STRING_TO_ENUM(AUDIO_INPUT_FLAG_HW_HOTWORD), +}; + +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_AAC_MAIN), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_LC), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_SSR), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_LTP), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_HE_V1), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_SCALABLE), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ERLC), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_LD), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_HE_V2), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ELD), + STRING_TO_ENUM(AUDIO_FORMAT_VORBIS), + STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V1), + STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V2), + STRING_TO_ENUM(AUDIO_FORMAT_OPUS), + STRING_TO_ENUM(AUDIO_FORMAT_AC3), + STRING_TO_ENUM(AUDIO_FORMAT_E_AC3), +}; + +const StringToEnum sOutChannelsNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_MONO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_QUAD), + 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), +}; + +const StringToEnum sGainModeNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT), + STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS), + STRING_TO_ENUM(AUDIO_GAIN_MODE_RAMP), +}; + +class ConfigParsingUtils +{ +public: + 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 uint32_t parseOutputFlagNames(char *name); + static uint32_t parseInputFlagNames(char *name); + static audio_devices_t parseDeviceNames(char *name); +}; + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/Devices.cpp b/services/audiopolicy/managerdefault/Devices.cpp new file mode 100644 index 0000000..13c8bbc --- /dev/null +++ b/services/audiopolicy/managerdefault/Devices.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "APM::Devices" +//#define LOG_NDEBUG 0 + +#include "AudioPolicyManager.h" + +namespace android { + +String8 DeviceDescriptor::emptyNameStr = String8(""); + +DeviceDescriptor::DeviceDescriptor(const String8& name, audio_devices_t type) : + AudioPort(name, AUDIO_PORT_TYPE_DEVICE, + audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : + AUDIO_PORT_ROLE_SOURCE, + NULL), + mDeviceType(type), mAddress("") +{ + +} + +bool 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 (mDeviceType == other->mDeviceType) && + (mAddress == "" || other->mAddress == "" || mAddress == other->mAddress) && + (mChannelMask == 0 || other->mChannelMask == 0 || + mChannelMask == other->mChannelMask); +} + +void DeviceDescriptor::loadGains(cnode *root) +{ + AudioPort::loadGains(root); + if (mGains.size() > 0) { + mGains[0]->getDefaultConfig(&mGain); + } +} + +void DeviceVector::refreshTypes() +{ + mDeviceTypes = AUDIO_DEVICE_NONE; + for(size_t i = 0; i < size(); i++) { + mDeviceTypes |= itemAt(i)->mDeviceType; + } + ALOGV("DeviceVector::refreshTypes() mDeviceTypes %08x", mDeviceTypes); +} + +ssize_t 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 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->mDeviceType); + ret = -1; + } + return ret; +} + +ssize_t 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->mDeviceType); + } else { + ret = SortedVector::removeAt(ret); + if (ret >= 0) { + refreshTypes(); + } + } + return ret; +} + +void 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(String8("device_type"), type | role_bit)); + } +} + +void DeviceVector::loadDevicesFromName(char *name, + const DeviceVector& declaredDevices) +{ + char *devName = strtok(name, "|"); + while (devName != NULL) { + if (strlen(devName) != 0) { + audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + devName); + if (type != AUDIO_DEVICE_NONE) { + sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(name), type); + if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX || + type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) { + dev->mAddress = String8("0"); + } + add(dev); + } else { + sp<DeviceDescriptor> deviceDesc = + declaredDevices.getDeviceFromName(String8(devName)); + if (deviceDesc != 0) { + add(deviceDesc); + } + } + } + devName = strtok(NULL, "|"); + } +} + +sp<DeviceDescriptor> DeviceVector::getDevice(audio_devices_t type, String8 address) const +{ + sp<DeviceDescriptor> device; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->mDeviceType == type) { + if (address == "" || itemAt(i)->mAddress == address) { + device = itemAt(i); + if (itemAt(i)->mAddress == address) { + break; + } + } + } + } + ALOGV("DeviceVector::getDevice() for type %08x address %s found %p", + type, address.string(), device.get()); + return device; +} + +sp<DeviceDescriptor> DeviceVector::getDeviceFromId(audio_port_handle_t id) const +{ + sp<DeviceDescriptor> device; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->getHandle() == id) { + device = itemAt(i); + break; + } + } + return device; +} + +DeviceVector DeviceVector::getDevicesFromType(audio_devices_t type) const +{ + DeviceVector devices; + for (size_t i = 0; (i < size()) && (type != AUDIO_DEVICE_NONE); i++) { + if (itemAt(i)->mDeviceType & type & ~AUDIO_DEVICE_BIT_IN) { + devices.add(itemAt(i)); + type &= ~itemAt(i)->mDeviceType; + ALOGV("DeviceVector::getDevicesFromType() for type %x found %p", + itemAt(i)->mDeviceType, itemAt(i).get()); + } + } + return devices; +} + +DeviceVector DeviceVector::getDevicesFromTypeAddr( + audio_devices_t type, String8 address) const +{ + DeviceVector devices; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->mDeviceType == type) { + if (itemAt(i)->mAddress == address) { + devices.add(itemAt(i)); + } + } + } + return devices; +} + +sp<DeviceDescriptor> DeviceVector::getDeviceFromName(const String8& name) const +{ + sp<DeviceDescriptor> device; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->mName == name) { + device = itemAt(i); + break; + } + } + return device; +} + +void DeviceDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK|AUDIO_PORT_CONFIG_GAIN; + if (srcConfig != NULL) { + dstConfig->config_mask |= srcConfig->config_mask; + } + + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = audio_is_output_device(mDeviceType) ? + AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE; + dstConfig->type = AUDIO_PORT_TYPE_DEVICE; + dstConfig->ext.device.type = mDeviceType; + + //TODO Understand why this test is necessary. i.e. why at boot time does it crash + // without the test? + // This has been demonstrated to NOT be true (at start up) + // ALOG_ASSERT(mModule != NULL); + dstConfig->ext.device.hw_module = mModule != NULL ? mModule->mHandle : NULL; + strncpy(dstConfig->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN); +} + +void DeviceDescriptor::toAudioPort(struct audio_port *port) const +{ + ALOGV("DeviceDescriptor::toAudioPort() handle %d type %x", mId, mDeviceType); + AudioPort::toAudioPort(port); + port->id = mId; + toAudioPortConfig(&port->active_config); + port->ext.device.type = mDeviceType; + port->ext.device.hw_module = mModule->mHandle; + strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN); +} + +status_t DeviceDescriptor::dump(int fd, int spaces, int index) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "%*sDevice %d:\n", spaces, "", index+1); + result.append(buffer); + if (mId != 0) { + snprintf(buffer, SIZE, "%*s- id: %2d\n", spaces, "", mId); + result.append(buffer); + } + snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "", + ConfigParsingUtils::enumToString(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + mDeviceType)); + result.append(buffer); + if (mAddress.size() != 0) { + snprintf(buffer, SIZE, "%*s- address: %-32s\n", spaces, "", mAddress.string()); + result.append(buffer); + } + write(fd, result.string(), result.size()); + AudioPort::dump(fd, spaces); + + return NO_ERROR; +} + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/Devices.h b/services/audiopolicy/managerdefault/Devices.h new file mode 100644 index 0000000..af2fbda --- /dev/null +++ b/services/audiopolicy/managerdefault/Devices.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace android { + +class AudioPort; +class AudioPortConfig; + +class DeviceDescriptor: public AudioPort, public AudioPortConfig +{ +public: + DeviceDescriptor(const String8& name, audio_devices_t type); + + virtual ~DeviceDescriptor() {} + + bool equals(const sp<DeviceDescriptor>& other) const; + + // AudioPortConfig + virtual sp<AudioPort> getAudioPort() const { return (AudioPort*) this; } + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const; + + // AudioPort + virtual void loadGains(cnode *root); + virtual void toAudioPort(struct audio_port *port) const; + + status_t dump(int fd, int spaces, int index) const; + + audio_devices_t mDeviceType; + String8 mAddress; + + static String8 emptyNameStr; +}; + +class DeviceVector : public SortedVector< sp<DeviceDescriptor> > +{ +public: + DeviceVector() : SortedVector(), mDeviceTypes(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 mDeviceTypes; } + + void loadDevicesFromType(audio_devices_t types); + void loadDevicesFromName(char *name, const DeviceVector& declaredDevices); + + sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const; + DeviceVector getDevicesFromType(audio_devices_t types) const; + sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const; + sp<DeviceDescriptor> getDeviceFromName(const String8& name) const; + DeviceVector getDevicesFromTypeAddr(audio_devices_t type, String8 address) + const; + +private: + void refreshTypes(); + audio_devices_t mDeviceTypes; +}; + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/Gains.cpp b/services/audiopolicy/managerdefault/Gains.cpp new file mode 100644 index 0000000..4aca26d --- /dev/null +++ b/services/audiopolicy/managerdefault/Gains.cpp @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "APM::Gains" +//#define LOG_NDEBUG 0 + +//#define VERY_VERBOSE_LOGGING +#ifdef VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +#include "AudioPolicyManager.h" + +#include <math.h> + +namespace android { + +const VolumeCurvePoint +ApmGains::sDefaultVolumeCurve[ApmGains::VOLCNT] = { + {1, -49.5f}, {33, -33.5f}, {66, -17.0f}, {100, 0.0f} +}; + + +const VolumeCurvePoint +ApmGains::sDefaultMediaVolumeCurve[ApmGains::VOLCNT] = { + {1, -58.0f}, {20, -40.0f}, {60, -17.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +ApmGains::sExtMediaSystemVolumeCurve[ApmGains::VOLCNT] = { + {1, -58.0f}, {20, -40.0f}, {60, -21.0f}, {100, -10.0f} +}; + +const VolumeCurvePoint +ApmGains::sSpeakerMediaVolumeCurve[ApmGains::VOLCNT] = { + {1, -56.0f}, {20, -34.0f}, {60, -11.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +ApmGains::sSpeakerMediaVolumeCurveDrc[ApmGains::VOLCNT] = { + {1, -55.0f}, {20, -43.0f}, {86, -12.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +ApmGains::sSpeakerSonificationVolumeCurve[ApmGains::VOLCNT] = { + {1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f} +}; + +const VolumeCurvePoint +ApmGains::sSpeakerSonificationVolumeCurveDrc[ApmGains::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 VolumeCurvePoint +ApmGains::sDefaultSystemVolumeCurve[ApmGains::VOLCNT] = { + {1, -24.0f}, {33, -18.0f}, {66, -12.0f}, {100, -6.0f} +}; + +const VolumeCurvePoint +ApmGains::sDefaultSystemVolumeCurveDrc[ApmGains::VOLCNT] = { + {1, -34.0f}, {33, -24.0f}, {66, -15.0f}, {100, -6.0f} +}; + +const VolumeCurvePoint +ApmGains::sHeadsetSystemVolumeCurve[ApmGains::VOLCNT] = { + {1, -30.0f}, {33, -26.0f}, {66, -22.0f}, {100, -18.0f} +}; + +const VolumeCurvePoint +ApmGains::sDefaultVoiceVolumeCurve[ApmGains::VOLCNT] = { + {0, -42.0f}, {33, -28.0f}, {66, -14.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +ApmGains::sSpeakerVoiceVolumeCurve[ApmGains::VOLCNT] = { + {0, -24.0f}, {33, -16.0f}, {66, -8.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +ApmGains::sLinearVolumeCurve[ApmGains::VOLCNT] = { + {0, -96.0f}, {33, -68.0f}, {66, -34.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +ApmGains::sSilentVolumeCurve[ApmGains::VOLCNT] = { + {0, -96.0f}, {1, -96.0f}, {2, -96.0f}, {100, -96.0f} +}; + +const VolumeCurvePoint +ApmGains::sFullScaleVolumeCurve[ApmGains::VOLCNT] = { + {0, 0.0f}, {1, 0.0f}, {2, 0.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint *ApmGains::sVolumeProfiles[AUDIO_STREAM_CNT] + [ApmGains::DEVICE_CATEGORY_CNT] = { + { // AUDIO_STREAM_VOICE_CALL + ApmGains::sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_SYSTEM + ApmGains::sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_RING + ApmGains::sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_MUSIC + ApmGains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_ALARM + ApmGains::sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_NOTIFICATION + ApmGains::sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_BLUETOOTH_SCO + ApmGains::sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_ENFORCED_AUDIBLE + ApmGains::sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_DTMF + ApmGains::sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_TTS + // "Transmitted Through Speaker": always silent except on DEVICE_CATEGORY_SPEAKER + ApmGains::sSilentVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sLinearVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sSilentVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sSilentVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_ACCESSIBILITY + ApmGains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_REROUTING + ApmGains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sFullScaleVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_PATCH + ApmGains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_HEADSET + ApmGains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_SPEAKER + ApmGains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_EARPIECE + ApmGains::sFullScaleVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, +}; + +//static +audio_devices_t ApmGains::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. + // - HDMI-CEC system audio mode only output: give priority to available item in order. + if (device & AUDIO_DEVICE_OUT_SPEAKER) { + device = AUDIO_DEVICE_OUT_SPEAKER; + } else if (device & AUDIO_DEVICE_OUT_HDMI_ARC) { + device = AUDIO_DEVICE_OUT_HDMI_ARC; + } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) { + device = AUDIO_DEVICE_OUT_AUX_LINE; + } else if (device & AUDIO_DEVICE_OUT_SPDIF) { + device = AUDIO_DEVICE_OUT_SPDIF; + } else { + device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP); + } + } + + /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/ + if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE) + device = AUDIO_DEVICE_OUT_SPEAKER; + + ALOGW_IF(popcount(device) != 1, + "getDeviceForVolume() invalid device combination: %08x", + device); + + return device; +} + +//static +ApmGains::device_category ApmGains::getDeviceCategory(audio_devices_t device) +{ + switch(getDeviceForVolume(device)) { + case AUDIO_DEVICE_OUT_EARPIECE: + return ApmGains::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 ApmGains::DEVICE_CATEGORY_HEADSET; + case AUDIO_DEVICE_OUT_LINE: + case AUDIO_DEVICE_OUT_AUX_DIGITAL: + /*USB? Remote submix?*/ + return ApmGains::DEVICE_CATEGORY_EXT_MEDIA; + case AUDIO_DEVICE_OUT_SPEAKER: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: + case AUDIO_DEVICE_OUT_USB_ACCESSORY: + case AUDIO_DEVICE_OUT_USB_DEVICE: + case AUDIO_DEVICE_OUT_REMOTE_SUBMIX: + default: + return ApmGains::DEVICE_CATEGORY_SPEAKER; + } +} + +//static +float ApmGains::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi) +{ + ApmGains::device_category deviceCategory = ApmGains::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[ApmGains::VOLMAX].mIndex - + curve[ApmGains::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[ApmGains::VOLMIN].mIndex) { // out of bounds + return 0.0f; + } else if (volIdx < curve[ApmGains::VOLKNEE1].mIndex) { + segment = 0; + } else if (volIdx < curve[ApmGains::VOLKNEE2].mIndex) { + segment = 1; + } else if (volIdx <= curve[ApmGains::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; +} + + + +AudioGain::AudioGain(int index, bool useInChannelMask) +{ + mIndex = index; + mUseInChannelMask = useInChannelMask; + memset(&mGain, 0, sizeof(struct audio_gain)); +} + +void AudioGain::getDefaultConfig(struct audio_gain_config *config) +{ + config->index = mIndex; + config->mode = mGain.mode; + config->channel_mask = mGain.channel_mask; + if ((mGain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { + config->values[0] = mGain.default_value; + } else { + uint32_t numValues; + if (mUseInChannelMask) { + numValues = audio_channel_count_from_in_mask(mGain.channel_mask); + } else { + numValues = audio_channel_count_from_out_mask(mGain.channel_mask); + } + for (size_t i = 0; i < numValues; i++) { + config->values[i] = mGain.default_value; + } + } + if ((mGain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { + config->ramp_duration_ms = mGain.min_ramp_ms; + } +} + +status_t AudioGain::checkConfig(const struct audio_gain_config *config) +{ + if ((config->mode & ~mGain.mode) != 0) { + return BAD_VALUE; + } + if ((config->mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { + if ((config->values[0] < mGain.min_value) || + (config->values[0] > mGain.max_value)) { + return BAD_VALUE; + } + } else { + if ((config->channel_mask & ~mGain.channel_mask) != 0) { + return BAD_VALUE; + } + uint32_t numValues; + if (mUseInChannelMask) { + numValues = audio_channel_count_from_in_mask(config->channel_mask); + } else { + numValues = audio_channel_count_from_out_mask(config->channel_mask); + } + for (size_t i = 0; i < numValues; i++) { + if ((config->values[i] < mGain.min_value) || + (config->values[i] > mGain.max_value)) { + return BAD_VALUE; + } + } + } + if ((config->mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { + if ((config->ramp_duration_ms < mGain.min_ramp_ms) || + (config->ramp_duration_ms > mGain.max_ramp_ms)) { + return BAD_VALUE; + } + } + return NO_ERROR; +} + +void AudioGain::dump(int fd, int spaces, int index) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "%*sGain %d:\n", spaces, "", index+1); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- mode: %08x\n", spaces, "", mGain.mode); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- channel_mask: %08x\n", spaces, "", mGain.channel_mask); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- min_value: %d mB\n", spaces, "", mGain.min_value); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- max_value: %d mB\n", spaces, "", mGain.max_value); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- default_value: %d mB\n", spaces, "", mGain.default_value); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- step_value: %d mB\n", spaces, "", mGain.step_value); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- min_ramp_ms: %d ms\n", spaces, "", mGain.min_ramp_ms); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- max_ramp_ms: %d ms\n", spaces, "", mGain.max_ramp_ms); + result.append(buffer); + + write(fd, result.string(), result.size()); +} + + +// --- StreamDescriptor class implementation + +StreamDescriptor::StreamDescriptor() + : mIndexMin(0), mIndexMax(1), mCanBeMuted(true) +{ + mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT, 0); +} + +int StreamDescriptor::getVolumeIndex(audio_devices_t device) +{ + device = ApmGains::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 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()); +} + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/Gains.h b/services/audiopolicy/managerdefault/Gains.h new file mode 100644 index 0000000..b4ab129 --- /dev/null +++ b/services/audiopolicy/managerdefault/Gains.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace android { + +class VolumeCurvePoint +{ +public: + int mIndex; + float mDBAttenuation; +}; + +class StreamDescriptor; + +class ApmGains +{ +public : + // 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}; + + // device categories used for volume curve management. + enum device_category { + DEVICE_CATEGORY_HEADSET, + DEVICE_CATEGORY_SPEAKER, + DEVICE_CATEGORY_EARPIECE, + DEVICE_CATEGORY_EXT_MEDIA, + DEVICE_CATEGORY_CNT + }; + + // returns the category the device belongs to with regard to volume curve management + static ApmGains::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); + + static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi); + + // default volume curve + static const VolumeCurvePoint sDefaultVolumeCurve[ApmGains::VOLCNT]; + // default volume curve for media strategy + static const VolumeCurvePoint sDefaultMediaVolumeCurve[ApmGains::VOLCNT]; + // volume curve for non-media audio on ext media outputs (HDMI, Line, etc) + static const VolumeCurvePoint sExtMediaSystemVolumeCurve[ApmGains::VOLCNT]; + // volume curve for media strategy on speakers + static const VolumeCurvePoint sSpeakerMediaVolumeCurve[ApmGains::VOLCNT]; + static const VolumeCurvePoint sSpeakerMediaVolumeCurveDrc[ApmGains::VOLCNT]; + // volume curve for sonification strategy on speakers + static const VolumeCurvePoint sSpeakerSonificationVolumeCurve[ApmGains::VOLCNT]; + static const VolumeCurvePoint sSpeakerSonificationVolumeCurveDrc[ApmGains::VOLCNT]; + static const VolumeCurvePoint sDefaultSystemVolumeCurve[ApmGains::VOLCNT]; + static const VolumeCurvePoint sDefaultSystemVolumeCurveDrc[ApmGains::VOLCNT]; + static const VolumeCurvePoint sHeadsetSystemVolumeCurve[ApmGains::VOLCNT]; + static const VolumeCurvePoint sDefaultVoiceVolumeCurve[ApmGains::VOLCNT]; + static const VolumeCurvePoint sSpeakerVoiceVolumeCurve[ApmGains::VOLCNT]; + static const VolumeCurvePoint sLinearVolumeCurve[ApmGains::VOLCNT]; + static const VolumeCurvePoint sSilentVolumeCurve[ApmGains::VOLCNT]; + static const VolumeCurvePoint sFullScaleVolumeCurve[ApmGains::VOLCNT]; + // default volume curves per stream and device category. See initializeVolumeCurves() + static const VolumeCurvePoint *sVolumeProfiles[AUDIO_STREAM_CNT][ApmGains::DEVICE_CATEGORY_CNT]; +}; + + +class AudioGain: public RefBase +{ +public: + AudioGain(int index, bool useInChannelMask); + virtual ~AudioGain() {} + + void dump(int fd, int spaces, int index) const; + + void getDefaultConfig(struct audio_gain_config *config); + status_t checkConfig(const struct audio_gain_config *config); + int mIndex; + struct audio_gain mGain; + bool mUseInChannelMask; +}; + + +// 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[ApmGains::DEVICE_CATEGORY_CNT]; +}; + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/HwModule.cpp b/services/audiopolicy/managerdefault/HwModule.cpp new file mode 100644 index 0000000..a04bdc8 --- /dev/null +++ b/services/audiopolicy/managerdefault/HwModule.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "APM::HwModule" +//#define LOG_NDEBUG 0 + +#include "AudioPolicyManager.h" +#include "audio_policy_conf.h" +#include <hardware/audio.h> + +namespace android { + +HwModule::HwModule(const char *name) + : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), + mHalVersion(AUDIO_DEVICE_API_VERSION_MIN), mHandle(0) +{ +} + +HwModule::~HwModule() +{ + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + mOutputProfiles[i]->mSupportedDevices.clear(); + } + for (size_t i = 0; i < mInputProfiles.size(); i++) { + mInputProfiles[i]->mSupportedDevices.clear(); + } + free((void *)mName); +} + +status_t HwModule::loadInput(cnode *root) +{ + cnode *node = root->first_child; + + sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this); + + while (node) { + if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { + profile->loadSamplingRates((char *)node->value); + } else if (strcmp(node->name, FORMATS_TAG) == 0) { + profile->loadFormats((char *)node->value); + } else if (strcmp(node->name, CHANNELS_TAG) == 0) { + profile->loadInChannels((char *)node->value); + } else if (strcmp(node->name, DEVICES_TAG) == 0) { + profile->mSupportedDevices.loadDevicesFromName((char *)node->value, + mDeclaredDevices); + } else if (strcmp(node->name, FLAGS_TAG) == 0) { + profile->mFlags = ConfigParsingUtils::parseInputFlagNames((char *)node->value); + } else if (strcmp(node->name, GAINS_TAG) == 0) { + profile->loadGains(node); + } + 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()); + + mInputProfiles.add(profile); + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +status_t HwModule::loadOutput(cnode *root) +{ + cnode *node = root->first_child; + + sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this); + + while (node) { + if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { + profile->loadSamplingRates((char *)node->value); + } else if (strcmp(node->name, FORMATS_TAG) == 0) { + profile->loadFormats((char *)node->value); + } else if (strcmp(node->name, CHANNELS_TAG) == 0) { + profile->loadOutChannels((char *)node->value); + } else if (strcmp(node->name, DEVICES_TAG) == 0) { + profile->mSupportedDevices.loadDevicesFromName((char *)node->value, + mDeclaredDevices); + } else if (strcmp(node->name, FLAGS_TAG) == 0) { + profile->mFlags = ConfigParsingUtils::parseOutputFlagNames((char *)node->value); + } else if (strcmp(node->name, GAINS_TAG) == 0) { + profile->loadGains(node); + } + 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); + + mOutputProfiles.add(profile); + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +status_t HwModule::loadDevice(cnode *root) +{ + cnode *node = root->first_child; + + audio_devices_t type = AUDIO_DEVICE_NONE; + while (node) { + if (strcmp(node->name, DEVICE_TYPE) == 0) { + type = ConfigParsingUtils::parseDeviceNames((char *)node->value); + break; + } + node = node->next; + } + if (type == AUDIO_DEVICE_NONE || + (!audio_is_input_device(type) && !audio_is_output_device(type))) { + ALOGW("loadDevice() bad type %08x", type); + return BAD_VALUE; + } + sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type); + deviceDesc->mModule = this; + + node = root->first_child; + while (node) { + if (strcmp(node->name, DEVICE_ADDRESS) == 0) { + deviceDesc->mAddress = String8((char *)node->value); + } else if (strcmp(node->name, CHANNELS_TAG) == 0) { + if (audio_is_input_device(type)) { + deviceDesc->loadInChannels((char *)node->value); + } else { + deviceDesc->loadOutChannels((char *)node->value); + } + } else if (strcmp(node->name, GAINS_TAG) == 0) { + deviceDesc->loadGains(node); + } + node = node->next; + } + + ALOGV("loadDevice() adding device name %s type %08x address %s", + deviceDesc->mName.string(), type, deviceDesc->mAddress.string()); + + mDeclaredDevices.add(deviceDesc); + + return NO_ERROR; +} + +status_t HwModule::addOutputProfile(String8 name, const audio_config_t *config, + audio_devices_t device, String8 address) +{ + sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SOURCE, this); + + profile->mSamplingRates.add(config->sample_rate); + profile->mChannelMasks.add(config->channel_mask); + profile->mFormats.add(config->format); + + sp<DeviceDescriptor> devDesc = new DeviceDescriptor(name, device); + devDesc->mAddress = address; + profile->mSupportedDevices.add(devDesc); + + mOutputProfiles.add(profile); + + return NO_ERROR; +} + +status_t HwModule::removeOutputProfile(String8 name) +{ + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + if (mOutputProfiles[i]->mName == name) { + mOutputProfiles.removeAt(i); + break; + } + } + + return NO_ERROR; +} + +status_t HwModule::addInputProfile(String8 name, const audio_config_t *config, + audio_devices_t device, String8 address) +{ + sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SINK, this); + + profile->mSamplingRates.add(config->sample_rate); + profile->mChannelMasks.add(config->channel_mask); + profile->mFormats.add(config->format); + + sp<DeviceDescriptor> devDesc = new DeviceDescriptor(name, device); + devDesc->mAddress = address; + profile->mSupportedDevices.add(devDesc); + + ALOGV("addInputProfile() name %s rate %d mask 0x08", name.string(), config->sample_rate, config->channel_mask); + + mInputProfiles.add(profile); + + return NO_ERROR; +} + +status_t HwModule::removeInputProfile(String8 name) +{ + for (size_t i = 0; i < mInputProfiles.size(); i++) { + if (mInputProfiles[i]->mName == name) { + mInputProfiles.removeAt(i); + break; + } + } + + return NO_ERROR; +} + + +void 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); + snprintf(buffer, SIZE, " - version: %u.%u\n", mHalVersion >> 8, mHalVersion & 0xFF); + 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 %zu:\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 %zu:\n", i); + write(fd, buffer, strlen(buffer)); + mInputProfiles[i]->dump(fd); + } + } + if (mDeclaredDevices.size()) { + write(fd, " - devices:\n", strlen(" - devices:\n")); + for (size_t i = 0; i < mDeclaredDevices.size(); i++) { + mDeclaredDevices[i]->dump(fd, 4, i); + } + } +} + +} //namespace android diff --git a/services/audiopolicy/managerdefault/HwModule.h b/services/audiopolicy/managerdefault/HwModule.h new file mode 100644 index 0000000..f814dd9 --- /dev/null +++ b/services/audiopolicy/managerdefault/HwModule.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace android { + +class HwModule : public RefBase +{ +public: + HwModule(const char *name); + ~HwModule(); + + status_t loadOutput(cnode *root); + status_t loadInput(cnode *root); + status_t loadDevice(cnode *root); + + status_t addOutputProfile(String8 name, const audio_config_t *config, + audio_devices_t device, String8 address); + status_t removeOutputProfile(String8 name); + status_t addInputProfile(String8 name, const audio_config_t *config, + audio_devices_t device, String8 address); + status_t removeInputProfile(String8 name); + + void dump(int fd); + + const char *const mName; // base name of the audio HW module (primary, a2dp ...) + uint32_t mHalVersion; // audio HAL API version + audio_module_handle_t mHandle; + Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module + Vector < sp<IOProfile> > mInputProfiles; // input profiles exposed by this module + DeviceVector mDeclaredDevices; // devices declared in audio_policy.conf +}; + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/IOProfile.cpp b/services/audiopolicy/managerdefault/IOProfile.cpp new file mode 100644 index 0000000..538ac1a --- /dev/null +++ b/services/audiopolicy/managerdefault/IOProfile.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "APM::IOProfile" +//#define LOG_NDEBUG 0 + +#include "AudioPolicyManager.h" + +namespace android { + +IOProfile::IOProfile(const String8& name, audio_port_role_t role, + const sp<HwModule>& module) + : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module) +{ +} + +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 IOProfile::isCompatibleProfile(audio_devices_t device, + String8 address, + uint32_t samplingRate, + uint32_t *updatedSamplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + uint32_t flags) const +{ + const bool isPlaybackThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SOURCE; + const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; + ALOG_ASSERT(isPlaybackThread != isRecordThread); + + if (device != AUDIO_DEVICE_NONE && mSupportedDevices.getDevice(device, address) == 0) { + return false; + } + + if (samplingRate == 0) { + return false; + } + uint32_t myUpdatedSamplingRate = samplingRate; + if (isPlaybackThread && checkExactSamplingRate(samplingRate) != NO_ERROR) { + return false; + } + if (isRecordThread && checkCompatibleSamplingRate(samplingRate, &myUpdatedSamplingRate) != + NO_ERROR) { + return false; + } + + if (!audio_is_valid_format(format) || checkFormat(format) != NO_ERROR) { + return false; + } + + if (isPlaybackThread && (!audio_is_output_channel(channelMask) || + checkExactChannelMask(channelMask) != NO_ERROR)) { + return false; + } + if (isRecordThread && (!audio_is_input_channel(channelMask) || + checkCompatibleChannelMask(channelMask) != NO_ERROR)) { + return false; + } + + if (isPlaybackThread && (mFlags & flags) != flags) { + return false; + } + // The only input flag that is allowed to be different is the fast flag. + // An existing fast stream is compatible with a normal track request. + // An existing normal stream is compatible with a fast track request, + // but the fast request will be denied by AudioFlinger and converted to normal track. + if (isRecordThread && ((mFlags ^ flags) & + ~AUDIO_INPUT_FLAG_FAST)) { + return false; + } + + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = myUpdatedSamplingRate; + } + return true; +} + +void IOProfile::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + AudioPort::dump(fd, 4); + + snprintf(buffer, SIZE, " - flags: 0x%04x\n", mFlags); + result.append(buffer); + snprintf(buffer, SIZE, " - devices:\n"); + result.append(buffer); + write(fd, result.string(), result.size()); + for (size_t i = 0; i < mSupportedDevices.size(); i++) { + mSupportedDevices[i]->dump(fd, 6, i); + } +} + +void IOProfile::log() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + ALOGV(" - sampling rates: "); + for (size_t i = 0; i < mSamplingRates.size(); i++) { + ALOGV(" %d", mSamplingRates[i]); + } + + ALOGV(" - channel masks: "); + for (size_t i = 0; i < mChannelMasks.size(); i++) { + ALOGV(" 0x%04x", mChannelMasks[i]); + } + + ALOGV(" - formats: "); + for (size_t i = 0; i < mFormats.size(); i++) { + ALOGV(" 0x%08x", mFormats[i]); + } + + ALOGV(" - devices: 0x%04x\n", mSupportedDevices.types()); + ALOGV(" - flags: 0x%04x\n", mFlags); +} + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/IOProfile.h b/services/audiopolicy/managerdefault/IOProfile.h new file mode 100644 index 0000000..3317969 --- /dev/null +++ b/services/audiopolicy/managerdefault/IOProfile.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace android { + +class HwModule; + +// 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 AudioPort +{ +public: + IOProfile(const String8& name, audio_port_role_t role, const sp<HwModule>& module); + virtual ~IOProfile(); + + // This method is used for both output and input. + // If parameter updatedSamplingRate is non-NULL, it is assigned the actual sample rate. + // For input, flags is interpreted as audio_input_flags_t. + // TODO: merge audio_output_flags_t and audio_input_flags_t. + bool isCompatibleProfile(audio_devices_t device, + String8 address, + uint32_t samplingRate, + uint32_t *updatedSamplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + uint32_t flags) const; + + void dump(int fd); + void log(); + + DeviceVector mSupportedDevices; // supported devices + // (devices this output can be routed to) +}; + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/Ports.cpp b/services/audiopolicy/managerdefault/Ports.cpp new file mode 100644 index 0000000..3e55cee --- /dev/null +++ b/services/audiopolicy/managerdefault/Ports.cpp @@ -0,0 +1,844 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "APM::Ports" +//#define LOG_NDEBUG 0 + +#include "AudioPolicyManager.h" + +#include "audio_policy_conf.h" + +namespace android { + +// --- AudioPort class implementation + +AudioPort::AudioPort(const String8& name, audio_port_type_t type, + audio_port_role_t role, const sp<HwModule>& module) : + mName(name), mType(type), mRole(role), mModule(module), mFlags(0), mId(0) +{ + mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) || + ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK)); +} + +void AudioPort::attach(const sp<HwModule>& module) { + mId = AudioPolicyManager::nextUniqueId(); + mModule = module; +} + +void AudioPort::toAudioPort(struct audio_port *port) const +{ + port->role = mRole; + port->type = mType; + strlcpy(port->name, mName, AUDIO_PORT_MAX_NAME_LEN); + unsigned int i; + for (i = 0; i < mSamplingRates.size() && i < AUDIO_PORT_MAX_SAMPLING_RATES; i++) { + if (mSamplingRates[i] != 0) { + port->sample_rates[i] = mSamplingRates[i]; + } + } + port->num_sample_rates = i; + for (i = 0; i < mChannelMasks.size() && i < AUDIO_PORT_MAX_CHANNEL_MASKS; i++) { + if (mChannelMasks[i] != 0) { + port->channel_masks[i] = mChannelMasks[i]; + } + } + port->num_channel_masks = i; + for (i = 0; i < mFormats.size() && i < AUDIO_PORT_MAX_FORMATS; i++) { + if (mFormats[i] != 0) { + port->formats[i] = mFormats[i]; + } + } + port->num_formats = i; + + ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size()); + + for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) { + port->gains[i] = mGains[i]->mGain; + } + port->num_gains = i; +} + +void AudioPort::importAudioPort(const sp<AudioPort> port) { + for (size_t k = 0 ; k < port->mSamplingRates.size() ; k++) { + const uint32_t rate = port->mSamplingRates.itemAt(k); + if (rate != 0) { // skip "dynamic" rates + bool hasRate = false; + for (size_t l = 0 ; l < mSamplingRates.size() ; l++) { + if (rate == mSamplingRates.itemAt(l)) { + hasRate = true; + break; + } + } + if (!hasRate) { // never import a sampling rate twice + mSamplingRates.add(rate); + } + } + } + for (size_t k = 0 ; k < port->mChannelMasks.size() ; k++) { + const audio_channel_mask_t mask = port->mChannelMasks.itemAt(k); + if (mask != 0) { // skip "dynamic" masks + bool hasMask = false; + for (size_t l = 0 ; l < mChannelMasks.size() ; l++) { + if (mask == mChannelMasks.itemAt(l)) { + hasMask = true; + break; + } + } + if (!hasMask) { // never import a channel mask twice + mChannelMasks.add(mask); + } + } + } + for (size_t k = 0 ; k < port->mFormats.size() ; k++) { + const audio_format_t format = port->mFormats.itemAt(k); + if (format != 0) { // skip "dynamic" formats + bool hasFormat = false; + for (size_t l = 0 ; l < mFormats.size() ; l++) { + if (format == mFormats.itemAt(l)) { + hasFormat = true; + break; + } + } + if (!hasFormat) { // never import a channel mask twice + mFormats.add(format); + } + } + } + for (size_t k = 0 ; k < port->mGains.size() ; k++) { + sp<AudioGain> gain = port->mGains.itemAt(k); + if (gain != 0) { + bool hasGain = false; + for (size_t l = 0 ; l < mGains.size() ; l++) { + if (gain == mGains.itemAt(l)) { + hasGain = true; + break; + } + } + if (!hasGain) { // never import a gain twice + mGains.add(gain); + } + } + } +} + +void AudioPort::clearCapabilities() { + mChannelMasks.clear(); + mFormats.clear(); + mSamplingRates.clear(); + mGains.clear(); +} + +void AudioPort::loadSamplingRates(char *name) +{ + 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) { + mSamplingRates.add(0); + return; + } + + while (str != NULL) { + uint32_t rate = atoi(str); + if (rate != 0) { + ALOGV("loadSamplingRates() adding rate %d", rate); + mSamplingRates.add(rate); + } + str = strtok(NULL, "|"); + } +} + +void AudioPort::loadFormats(char *name) +{ + 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) { + mFormats.add(AUDIO_FORMAT_DEFAULT); + return; + } + + while (str != NULL) { + audio_format_t format = (audio_format_t)ConfigParsingUtils::stringToEnum(sFormatNameToEnumTable, + ARRAY_SIZE(sFormatNameToEnumTable), + str); + if (format != AUDIO_FORMAT_DEFAULT) { + mFormats.add(format); + } + str = strtok(NULL, "|"); + } +} + +void AudioPort::loadInChannels(char *name) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadInChannels() %s", name); + + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + mChannelMasks.add(0); + return; + } + + while (str != NULL) { + audio_channel_mask_t channelMask = + (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sInChannelsNameToEnumTable, + ARRAY_SIZE(sInChannelsNameToEnumTable), + str); + if (channelMask != 0) { + ALOGV("loadInChannels() adding channelMask %04x", channelMask); + mChannelMasks.add(channelMask); + } + str = strtok(NULL, "|"); + } +} + +void AudioPort::loadOutChannels(char *name) +{ + 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) { + mChannelMasks.add(0); + return; + } + + while (str != NULL) { + audio_channel_mask_t channelMask = + (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sOutChannelsNameToEnumTable, + ARRAY_SIZE(sOutChannelsNameToEnumTable), + str); + if (channelMask != 0) { + mChannelMasks.add(channelMask); + } + str = strtok(NULL, "|"); + } + return; +} + +audio_gain_mode_t AudioPort::loadGainMode(char *name) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadGainMode() %s", name); + audio_gain_mode_t mode = 0; + while (str != NULL) { + mode |= (audio_gain_mode_t)ConfigParsingUtils::stringToEnum(sGainModeNameToEnumTable, + ARRAY_SIZE(sGainModeNameToEnumTable), + str); + str = strtok(NULL, "|"); + } + return mode; +} + +void AudioPort::loadGain(cnode *root, int index) +{ + cnode *node = root->first_child; + + sp<AudioGain> gain = new AudioGain(index, mUseInChannelMask); + + while (node) { + if (strcmp(node->name, GAIN_MODE) == 0) { + gain->mGain.mode = loadGainMode((char *)node->value); + } else if (strcmp(node->name, GAIN_CHANNELS) == 0) { + if (mUseInChannelMask) { + gain->mGain.channel_mask = + (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sInChannelsNameToEnumTable, + ARRAY_SIZE(sInChannelsNameToEnumTable), + (char *)node->value); + } else { + gain->mGain.channel_mask = + (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sOutChannelsNameToEnumTable, + ARRAY_SIZE(sOutChannelsNameToEnumTable), + (char *)node->value); + } + } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) { + gain->mGain.min_value = atoi((char *)node->value); + } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) { + gain->mGain.max_value = atoi((char *)node->value); + } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) { + gain->mGain.default_value = atoi((char *)node->value); + } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) { + gain->mGain.step_value = atoi((char *)node->value); + } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) { + gain->mGain.min_ramp_ms = atoi((char *)node->value); + } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) { + gain->mGain.max_ramp_ms = atoi((char *)node->value); + } + node = node->next; + } + + ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d", + gain->mGain.mode, gain->mGain.channel_mask, gain->mGain.min_value, gain->mGain.max_value); + + if (gain->mGain.mode == 0) { + return; + } + mGains.add(gain); +} + +void AudioPort::loadGains(cnode *root) +{ + cnode *node = root->first_child; + int index = 0; + while (node) { + ALOGV("loadGains() loading gain %s", node->name); + loadGain(node, index++); + node = node->next; + } +} + +status_t AudioPort::checkExactSamplingRate(uint32_t samplingRate) const +{ + if (mSamplingRates.isEmpty()) { + return NO_ERROR; + } + + for (size_t i = 0; i < mSamplingRates.size(); i ++) { + if (mSamplingRates[i] == samplingRate) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + +status_t AudioPort::checkCompatibleSamplingRate(uint32_t samplingRate, + uint32_t *updatedSamplingRate) const +{ + if (mSamplingRates.isEmpty()) { + return NO_ERROR; + } + + // Search for the closest supported sampling rate that is above (preferred) + // or below (acceptable) the desired sampling rate, within a permitted ratio. + // The sampling rates do not need to be sorted in ascending order. + ssize_t maxBelow = -1; + ssize_t minAbove = -1; + uint32_t candidate; + for (size_t i = 0; i < mSamplingRates.size(); i++) { + candidate = mSamplingRates[i]; + if (candidate == samplingRate) { + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = candidate; + } + return NO_ERROR; + } + // candidate < desired + if (candidate < samplingRate) { + if (maxBelow < 0 || candidate > mSamplingRates[maxBelow]) { + maxBelow = i; + } + // candidate > desired + } else { + if (minAbove < 0 || candidate < mSamplingRates[minAbove]) { + minAbove = i; + } + } + } + // This uses hard-coded knowledge about AudioFlinger resampling ratios. + // TODO Move these assumptions out. + static const uint32_t kMaxDownSampleRatio = 6; // beyond this aliasing occurs + static const uint32_t kMaxUpSampleRatio = 256; // beyond this sample rate inaccuracies occur + // due to approximation by an int32_t of the + // phase increments + // Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum. + if (minAbove >= 0) { + candidate = mSamplingRates[minAbove]; + if (candidate / kMaxDownSampleRatio <= samplingRate) { + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = candidate; + } + return NO_ERROR; + } + } + // But if we have to up-sample from a lower sampling rate, that's OK. + if (maxBelow >= 0) { + candidate = mSamplingRates[maxBelow]; + if (candidate * kMaxUpSampleRatio >= samplingRate) { + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = candidate; + } + return NO_ERROR; + } + } + // leave updatedSamplingRate unmodified + return BAD_VALUE; +} + +status_t AudioPort::checkExactChannelMask(audio_channel_mask_t channelMask) const +{ + if (mChannelMasks.isEmpty()) { + return NO_ERROR; + } + + for (size_t i = 0; i < mChannelMasks.size(); i++) { + if (mChannelMasks[i] == channelMask) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + +status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask) + const +{ + if (mChannelMasks.isEmpty()) { + return NO_ERROR; + } + + const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; + for (size_t i = 0; i < mChannelMasks.size(); i ++) { + // FIXME Does not handle multi-channel automatic conversions yet + audio_channel_mask_t supported = mChannelMasks[i]; + if (supported == channelMask) { + return NO_ERROR; + } + if (isRecordThread) { + // This uses hard-coded knowledge that AudioFlinger can silently down-mix and up-mix. + // FIXME Abstract this out to a table. + if (((supported == AUDIO_CHANNEL_IN_FRONT_BACK || supported == AUDIO_CHANNEL_IN_STEREO) + && channelMask == AUDIO_CHANNEL_IN_MONO) || + (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK + || channelMask == AUDIO_CHANNEL_IN_STEREO))) { + return NO_ERROR; + } + } + } + return BAD_VALUE; +} + +status_t AudioPort::checkFormat(audio_format_t format) const +{ + if (mFormats.isEmpty()) { + return NO_ERROR; + } + + for (size_t i = 0; i < mFormats.size(); i ++) { + if (mFormats[i] == format) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + + +uint32_t AudioPort::pickSamplingRate() const +{ + // special case for uninitialized dynamic profile + if (mSamplingRates.size() == 1 && mSamplingRates[0] == 0) { + return 0; + } + + // For direct outputs, pick minimum sampling rate: this helps ensuring that the + // channel count / sampling rate combination chosen will be supported by the connected + // sink + if ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && + (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { + uint32_t samplingRate = UINT_MAX; + for (size_t i = 0; i < mSamplingRates.size(); i ++) { + if ((mSamplingRates[i] < samplingRate) && (mSamplingRates[i] > 0)) { + samplingRate = mSamplingRates[i]; + } + } + return (samplingRate == UINT_MAX) ? 0 : samplingRate; + } + + uint32_t samplingRate = 0; + uint32_t maxRate = MAX_MIXER_SAMPLING_RATE; + + // For mixed output and inputs, use max mixer sampling rates. Do not + // limit sampling rate otherwise + if (mType != AUDIO_PORT_TYPE_MIX) { + maxRate = UINT_MAX; + } + for (size_t i = 0; i < mSamplingRates.size(); i ++) { + if ((mSamplingRates[i] > samplingRate) && (mSamplingRates[i] <= maxRate)) { + samplingRate = mSamplingRates[i]; + } + } + return samplingRate; +} + +audio_channel_mask_t AudioPort::pickChannelMask() const +{ + // special case for uninitialized dynamic profile + if (mChannelMasks.size() == 1 && mChannelMasks[0] == 0) { + return AUDIO_CHANNEL_NONE; + } + audio_channel_mask_t channelMask = AUDIO_CHANNEL_NONE; + + // For direct outputs, pick minimum channel count: this helps ensuring that the + // channel count / sampling rate combination chosen will be supported by the connected + // sink + if ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && + (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { + uint32_t channelCount = UINT_MAX; + for (size_t i = 0; i < mChannelMasks.size(); i ++) { + uint32_t cnlCount; + if (mUseInChannelMask) { + cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); + } else { + cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); + } + if ((cnlCount < channelCount) && (cnlCount > 0)) { + channelMask = mChannelMasks[i]; + channelCount = cnlCount; + } + } + return channelMask; + } + + uint32_t channelCount = 0; + uint32_t maxCount = MAX_MIXER_CHANNEL_COUNT; + + // For mixed output and inputs, use max mixer channel count. Do not + // limit channel count otherwise + if (mType != AUDIO_PORT_TYPE_MIX) { + maxCount = UINT_MAX; + } + for (size_t i = 0; i < mChannelMasks.size(); i ++) { + uint32_t cnlCount; + if (mUseInChannelMask) { + cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); + } else { + cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); + } + if ((cnlCount > channelCount) && (cnlCount <= maxCount)) { + channelMask = mChannelMasks[i]; + channelCount = cnlCount; + } + } + return channelMask; +} + +/* format in order of increasing preference */ +const audio_format_t AudioPort::sPcmFormatCompareTable[] = { + AUDIO_FORMAT_DEFAULT, + AUDIO_FORMAT_PCM_16_BIT, + AUDIO_FORMAT_PCM_8_24_BIT, + AUDIO_FORMAT_PCM_24_BIT_PACKED, + AUDIO_FORMAT_PCM_32_BIT, + AUDIO_FORMAT_PCM_FLOAT, +}; + +int AudioPort::compareFormats(audio_format_t format1, + audio_format_t format2) +{ + // NOTE: AUDIO_FORMAT_INVALID is also considered not PCM and will be compared equal to any + // compressed format and better than any PCM format. This is by design of pickFormat() + if (!audio_is_linear_pcm(format1)) { + if (!audio_is_linear_pcm(format2)) { + return 0; + } + return 1; + } + if (!audio_is_linear_pcm(format2)) { + return -1; + } + + int index1 = -1, index2 = -1; + for (size_t i = 0; + (i < ARRAY_SIZE(sPcmFormatCompareTable)) && ((index1 == -1) || (index2 == -1)); + i ++) { + if (sPcmFormatCompareTable[i] == format1) { + index1 = i; + } + if (sPcmFormatCompareTable[i] == format2) { + index2 = i; + } + } + // format1 not found => index1 < 0 => format2 > format1 + // format2 not found => index2 < 0 => format2 < format1 + return index1 - index2; +} + +audio_format_t AudioPort::pickFormat() const +{ + // special case for uninitialized dynamic profile + if (mFormats.size() == 1 && mFormats[0] == 0) { + return AUDIO_FORMAT_DEFAULT; + } + + audio_format_t format = AUDIO_FORMAT_DEFAULT; + audio_format_t bestFormat = + AudioPort::sPcmFormatCompareTable[ + ARRAY_SIZE(AudioPort::sPcmFormatCompareTable) - 1]; + // For mixed output and inputs, use best mixer output format. Do not + // limit format otherwise + if ((mType != AUDIO_PORT_TYPE_MIX) || + ((mRole == AUDIO_PORT_ROLE_SOURCE) && + (((mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) != 0)))) { + bestFormat = AUDIO_FORMAT_INVALID; + } + + for (size_t i = 0; i < mFormats.size(); i ++) { + if ((compareFormats(mFormats[i], format) > 0) && + (compareFormats(mFormats[i], bestFormat) <= 0)) { + format = mFormats[i]; + } + } + return format; +} + +status_t AudioPort::checkGain(const struct audio_gain_config *gainConfig, + int index) const +{ + if (index < 0 || (size_t)index >= mGains.size()) { + return BAD_VALUE; + } + return mGains[index]->checkConfig(gainConfig); +} + +void AudioPort::dump(int fd, int spaces) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + if (mName.size() != 0) { + snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string()); + result.append(buffer); + } + + if (mSamplingRates.size() != 0) { + snprintf(buffer, SIZE, "%*s- sampling rates: ", spaces, ""); + result.append(buffer); + for (size_t i = 0; i < mSamplingRates.size(); i++) { + if (i == 0 && mSamplingRates[i] == 0) { + snprintf(buffer, SIZE, "Dynamic"); + } else { + snprintf(buffer, SIZE, "%d", mSamplingRates[i]); + } + result.append(buffer); + result.append(i == (mSamplingRates.size() - 1) ? "" : ", "); + } + result.append("\n"); + } + + if (mChannelMasks.size() != 0) { + snprintf(buffer, SIZE, "%*s- channel masks: ", spaces, ""); + result.append(buffer); + for (size_t i = 0; i < mChannelMasks.size(); i++) { + ALOGV("AudioPort::dump mChannelMasks %zu %08x", i, mChannelMasks[i]); + + if (i == 0 && mChannelMasks[i] == 0) { + snprintf(buffer, SIZE, "Dynamic"); + } else { + snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]); + } + result.append(buffer); + result.append(i == (mChannelMasks.size() - 1) ? "" : ", "); + } + result.append("\n"); + } + + if (mFormats.size() != 0) { + snprintf(buffer, SIZE, "%*s- formats: ", spaces, ""); + result.append(buffer); + for (size_t i = 0; i < mFormats.size(); i++) { + const char *formatStr = ConfigParsingUtils::enumToString(sFormatNameToEnumTable, + ARRAY_SIZE(sFormatNameToEnumTable), + mFormats[i]); + if (i == 0 && strcmp(formatStr, "") == 0) { + snprintf(buffer, SIZE, "Dynamic"); + } else { + snprintf(buffer, SIZE, "%s", formatStr); + } + result.append(buffer); + result.append(i == (mFormats.size() - 1) ? "" : ", "); + } + result.append("\n"); + } + write(fd, result.string(), result.size()); + if (mGains.size() != 0) { + snprintf(buffer, SIZE, "%*s- gains:\n", spaces, ""); + write(fd, buffer, strlen(buffer) + 1); + result.append(buffer); + for (size_t i = 0; i < mGains.size(); i++) { + mGains[i]->dump(fd, spaces + 2, i); + } + } +} + + +// --- AudioPortConfig class implementation + +AudioPortConfig::AudioPortConfig() +{ + mSamplingRate = 0; + mChannelMask = AUDIO_CHANNEL_NONE; + mFormat = AUDIO_FORMAT_INVALID; + mGain.index = -1; +} + +status_t AudioPortConfig::applyAudioPortConfig( + const struct audio_port_config *config, + struct audio_port_config *backupConfig) +{ + struct audio_port_config localBackupConfig; + status_t status = NO_ERROR; + + localBackupConfig.config_mask = config->config_mask; + toAudioPortConfig(&localBackupConfig); + + sp<AudioPort> audioport = getAudioPort(); + if (audioport == 0) { + status = NO_INIT; + goto exit; + } + if (config->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { + status = audioport->checkExactSamplingRate(config->sample_rate); + if (status != NO_ERROR) { + goto exit; + } + mSamplingRate = config->sample_rate; + } + if (config->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { + status = audioport->checkExactChannelMask(config->channel_mask); + if (status != NO_ERROR) { + goto exit; + } + mChannelMask = config->channel_mask; + } + if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) { + status = audioport->checkFormat(config->format); + if (status != NO_ERROR) { + goto exit; + } + mFormat = config->format; + } + if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) { + status = audioport->checkGain(&config->gain, config->gain.index); + if (status != NO_ERROR) { + goto exit; + } + mGain = config->gain; + } + +exit: + if (status != NO_ERROR) { + applyAudioPortConfig(&localBackupConfig); + } + if (backupConfig != NULL) { + *backupConfig = localBackupConfig; + } + return status; +} + +void AudioPortConfig::toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { + dstConfig->sample_rate = mSamplingRate; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)) { + dstConfig->sample_rate = srcConfig->sample_rate; + } + } else { + dstConfig->sample_rate = 0; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { + dstConfig->channel_mask = mChannelMask; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)) { + dstConfig->channel_mask = srcConfig->channel_mask; + } + } else { + dstConfig->channel_mask = AUDIO_CHANNEL_NONE; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) { + dstConfig->format = mFormat; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)) { + dstConfig->format = srcConfig->format; + } + } else { + dstConfig->format = AUDIO_FORMAT_INVALID; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) { + dstConfig->gain = mGain; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN)) { + dstConfig->gain = srcConfig->gain; + } + } else { + dstConfig->gain.index = -1; + } + if (dstConfig->gain.index != -1) { + dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN; + } else { + dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN; + } +} + + +// --- AudioPatch class implementation + +AudioPatch::AudioPatch(audio_patch_handle_t handle, + const struct audio_patch *patch, uid_t uid) : + mHandle(handle), mPatch(*patch), mUid(uid), mAfPatchHandle(0) +{} + +status_t AudioPatch::dump(int fd, int spaces, int index) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "%*sAudio patch %d:\n", spaces, "", index+1); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- handle: %2d\n", spaces, "", mHandle); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- audio flinger handle: %2d\n", spaces, "", mAfPatchHandle); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mUid); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- %d sources:\n", spaces, "", mPatch.num_sources); + result.append(buffer); + for (size_t i = 0; i < mPatch.num_sources; i++) { + if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) { + snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", + mPatch.sources[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + mPatch.sources[i].ext.device.type)); + } else { + snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", + mPatch.sources[i].id, mPatch.sources[i].ext.mix.handle); + } + result.append(buffer); + } + snprintf(buffer, SIZE, "%*s- %d sinks:\n", spaces, "", mPatch.num_sinks); + result.append(buffer); + for (size_t i = 0; i < mPatch.num_sinks; i++) { + if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) { + snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", + mPatch.sinks[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + mPatch.sinks[i].ext.device.type)); + } else { + snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", + mPatch.sinks[i].id, mPatch.sinks[i].ext.mix.handle); + } + result.append(buffer); + } + + write(fd, result.string(), result.size()); + return NO_ERROR; +} + + +}; // namespace android diff --git a/services/audiopolicy/managerdefault/Ports.h b/services/audiopolicy/managerdefault/Ports.h new file mode 100644 index 0000000..f6e0e93 --- /dev/null +++ b/services/audiopolicy/managerdefault/Ports.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace android { + +class HwModule; + +class AudioPort: public virtual RefBase +{ +public: + AudioPort(const String8& name, audio_port_type_t type, + audio_port_role_t role, const sp<HwModule>& module); + virtual ~AudioPort() {} + + audio_port_handle_t getHandle() { return mId; } + + void attach(const sp<HwModule>& module); + bool isAttached() { return mId != 0; } + + virtual void toAudioPort(struct audio_port *port) const; + + void importAudioPort(const sp<AudioPort> port); + void clearCapabilities(); + + void loadSamplingRates(char *name); + void loadFormats(char *name); + void loadOutChannels(char *name); + void loadInChannels(char *name); + + audio_gain_mode_t loadGainMode(char *name); + void loadGain(cnode *root, int index); + virtual void loadGains(cnode *root); + + // searches for an exact match + status_t checkExactSamplingRate(uint32_t samplingRate) const; + // searches for a compatible match, and returns the best match via updatedSamplingRate + status_t checkCompatibleSamplingRate(uint32_t samplingRate, + uint32_t *updatedSamplingRate) const; + // searches for an exact match + status_t checkExactChannelMask(audio_channel_mask_t channelMask) const; + // searches for a compatible match, currently implemented for input channel masks only + status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask) const; + status_t checkFormat(audio_format_t format) const; + status_t checkGain(const struct audio_gain_config *gainConfig, int index) const; + + uint32_t pickSamplingRate() const; + audio_channel_mask_t pickChannelMask() const; + audio_format_t pickFormat() const; + + static const audio_format_t sPcmFormatCompareTable[]; + static int compareFormats(audio_format_t format1, audio_format_t format2); + + void dump(int fd, int spaces) const; + + String8 mName; + audio_port_type_t mType; + audio_port_role_t mRole; + bool mUseInChannelMask; + // 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 + Vector < sp<AudioGain> > mGains; // gain controllers + sp<HwModule> mModule; // audio HW module exposing this I/O stream + uint32_t mFlags; // attribute flags (e.g primary output, + // direct output...). + + +protected: + //TODO - clarify the role of mId in this case, both an "attached" indicator + // and a unique ID for identifying a port to the (upcoming) selection API, + // and its relationship to the mId in AudioOutputDescriptor and AudioInputDescriptor. + audio_port_handle_t mId; +}; + +class AudioPortConfig: public virtual RefBase +{ +public: + AudioPortConfig(); + virtual ~AudioPortConfig() {} + + status_t applyAudioPortConfig(const struct audio_port_config *config, + struct audio_port_config *backupConfig = NULL); + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const = 0; + virtual sp<AudioPort> getAudioPort() const = 0; + uint32_t mSamplingRate; + audio_format_t mFormat; + audio_channel_mask_t mChannelMask; + struct audio_gain_config mGain; +}; + + +class AudioPatch: public RefBase +{ +public: + AudioPatch(audio_patch_handle_t handle, const struct audio_patch *patch, uid_t uid); + + status_t dump(int fd, int spaces, int index) const; + + audio_patch_handle_t mHandle; + struct audio_patch mPatch; + uid_t mUid; + audio_patch_handle_t mAfPatchHandle; +}; + +}; // namespace android diff --git a/services/audiopolicy/audio_policy_conf.h b/services/audiopolicy/managerdefault/audio_policy_conf.h index 2535a67..2535a67 100644 --- a/services/audiopolicy/audio_policy_conf.h +++ b/services/audiopolicy/managerdefault/audio_policy_conf.h diff --git a/services/audiopolicy/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp index 3e090e9..3e090e9 100644 --- a/services/audiopolicy/AudioPolicyClientImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp diff --git a/services/audiopolicy/AudioPolicyClientImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp index 97719da..a79f8ae 100644 --- a/services/audiopolicy/AudioPolicyClientImplLegacy.cpp +++ b/services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp @@ -188,6 +188,13 @@ static audio_io_handle_t open_input(audio_module_handle_t module, if (pSamplingRate == NULL || pFormat == NULL || pChannelMask == NULL || pDevices == NULL) { return AUDIO_IO_HANDLE_NONE; } + + if (((*pDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX) + && !captureAudioOutputAllowed()) { + ALOGE("open_input() permission denied: capture not allowed"); + return AUDIO_IO_HANDLE_NONE; + } + audio_config_t config = AUDIO_CONFIG_INITIALIZER;; config.sample_rate = *pSamplingRate; config.format = *pFormat; diff --git a/services/audiopolicy/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp index e7e1b36..e6ace20 100644 --- a/services/audiopolicy/AudioPolicyEffects.cpp +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp @@ -226,6 +226,11 @@ status_t AudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output, Mutex::Autolock _l(mLock); // create audio processors according to stream + // FIXME: should we have specific post processing settings for internal streams? + // default to media for now. + if (stream >= AUDIO_STREAM_PUBLIC_CNT) { + stream = AUDIO_STREAM_MUSIC; + } ssize_t index = mOutputStreams.indexOfKey(stream); if (index < 0) { ALOGV("addOutputSessionEffects(): no output processing needed for this stream"); @@ -335,7 +340,7 @@ void AudioPolicyEffects::EffectVector::setProcessorEnabled(bool enabled) return (audio_source_t)i; } -const char *AudioPolicyEffects::kStreamNames[AUDIO_STREAM_CNT+1] = { +const char *AudioPolicyEffects::kStreamNames[AUDIO_STREAM_PUBLIC_CNT+1] = { AUDIO_STREAM_DEFAULT_TAG, AUDIO_STREAM_VOICE_CALL_TAG, AUDIO_STREAM_SYSTEM_TAG, @@ -350,11 +355,11 @@ const char *AudioPolicyEffects::kStreamNames[AUDIO_STREAM_CNT+1] = { }; // returns the audio_stream_t enum corresponding to the output stream name or -// AUDIO_STREAM_CNT is no match found +// AUDIO_STREAM_PUBLIC_CNT is no match found audio_stream_type_t AudioPolicyEffects::streamNameToEnum(const char *name) { int i; - for (i = AUDIO_STREAM_DEFAULT; i < AUDIO_STREAM_CNT; i++) { + for (i = AUDIO_STREAM_DEFAULT; i < AUDIO_STREAM_PUBLIC_CNT; i++) { if (strcmp(name, kStreamNames[i - AUDIO_STREAM_DEFAULT]) == 0) { ALOGV("streamNameToEnum found stream %s %d", name, i); break; @@ -585,7 +590,7 @@ status_t AudioPolicyEffects::loadStreamEffectConfigurations(cnode *root, node = node->first_child; while (node) { audio_stream_type_t stream = streamNameToEnum(node->name); - if (stream == AUDIO_STREAM_CNT) { + if (stream == AUDIO_STREAM_PUBLIC_CNT) { ALOGW("loadStreamEffectConfigurations() invalid output stream %s", node->name); node = node->next; continue; @@ -653,6 +658,10 @@ status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path) loadInputEffectConfigurations(root, effects); loadStreamEffectConfigurations(root, effects); + for (size_t i = 0; i < effects.size(); i++) { + delete effects[i]; + } + config_free(root); free(root); free(data); diff --git a/services/audiopolicy/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h index 6b0d538..3dec437 100644 --- a/services/audiopolicy/AudioPolicyEffects.h +++ b/services/audiopolicy/service/AudioPolicyEffects.h @@ -151,7 +151,7 @@ private: static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1]; static audio_source_t inputSourceNameToEnum(const char *name); - static const char *kStreamNames[AUDIO_STREAM_CNT+1]; //+1 required as streams start from -1 + static const char *kStreamNames[AUDIO_STREAM_PUBLIC_CNT+1]; //+1 required as streams start from -1 audio_stream_type_t streamNameToEnum(const char *name); // Parse audio_effects.conf diff --git a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index c06ca72..e9ff838 100644 --- a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -28,7 +28,8 @@ namespace android { status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) + const char *device_address, + const char *device_name) { if (mAudioPolicyManager == NULL) { return NO_INIT; @@ -46,8 +47,8 @@ status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, ALOGV("setDeviceConnectionState()"); Mutex::Autolock _l(mLock); - return mAudioPolicyManager->setDeviceConnectionState(device, - state, device_address); + return mAudioPolicyManager->setDeviceConnectionState(device, state, + device_address, device_name); } audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( @@ -129,7 +130,7 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, audio_output_flags_t flags, const audio_offload_info_t *offloadInfo) { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return AUDIO_IO_HANDLE_NONE; } if (mAudioPolicyManager == NULL) { @@ -141,25 +142,28 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, format, channelMask, flags, offloadInfo); } -audio_io_handle_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) +status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + 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; + return NO_INIT; } ALOGV("getOutput()"); Mutex::Autolock _l(mLock); - return mAudioPolicyManager->getOutputForAttr(attr, samplingRate, + return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, samplingRate, format, channelMask, flags, offloadInfo); } status_t AudioPolicyService::startOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { if (uint32_t(stream) >= AUDIO_STREAM_CNT) { return BAD_VALUE; @@ -186,7 +190,7 @@ status_t AudioPolicyService::startOutput(audio_io_handle_t output, status_t AudioPolicyService::stopOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { if (uint32_t(stream) >= AUDIO_STREAM_CNT) { return BAD_VALUE; @@ -201,7 +205,7 @@ status_t AudioPolicyService::stopOutput(audio_io_handle_t output, status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { ALOGV("doStopOutput from tid %d", gettid()); sp<AudioPolicyEffects>audioPolicyEffects; @@ -220,63 +224,98 @@ status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, return mAudioPolicyManager->stopOutput(output, stream, session); } -void AudioPolicyService::releaseOutput(audio_io_handle_t output) +void AudioPolicyService::releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) { if (mAudioPolicyManager == NULL) { return; } ALOGV("releaseOutput()"); - mOutputCommandThread->releaseOutputCommand(output); + mOutputCommandThread->releaseOutputCommand(output, stream, session); } -void AudioPolicyService::doReleaseOutput(audio_io_handle_t output) +void AudioPolicyService::doReleaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) { ALOGV("doReleaseOutput from tid %d", gettid()); Mutex::Autolock _l(mLock); - mAudioPolicyManager->releaseOutput(output); + mAudioPolicyManager->releaseOutput(output, stream, session); } -audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - int audioSession, - audio_input_flags_t flags) +status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags) { if (mAudioPolicyManager == NULL) { - return 0; + return NO_INIT; } // already checked by client, but double-check in case the client wrapper is bypassed - if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD && - inputSource != AUDIO_SOURCE_FM_TUNER) { - return 0; + if (attr->source >= AUDIO_SOURCE_CNT && attr->source != AUDIO_SOURCE_HOTWORD && + attr->source != AUDIO_SOURCE_FM_TUNER) { + return BAD_VALUE; } - if (((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) || - ((inputSource == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) { - return 0; + if (((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) || + ((attr->source == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) { + return BAD_VALUE; } - audio_io_handle_t input; sp<AudioPolicyEffects>audioPolicyEffects; + status_t status; + AudioPolicyInterface::input_type_t inputType; { Mutex::Autolock _l(mLock); // the audio_in_acoustics_t parameter is ignored by get_input() - input = mAudioPolicyManager->getInput(inputSource, samplingRate, - format, channelMask, - (audio_session_t)audioSession, flags); + status = mAudioPolicyManager->getInputForAttr(attr, input, session, + samplingRate, format, channelMask, + flags, &inputType); audioPolicyEffects = mAudioPolicyEffects; + + if (status == NO_ERROR) { + // enforce permission (if any) required for each type of input + switch (inputType) { + case AudioPolicyInterface::API_INPUT_LEGACY: + break; + case AudioPolicyInterface::API_INPUT_MIX_CAPTURE: + if (!captureAudioOutputAllowed()) { + ALOGE("getInputForAttr() permission denied: capture not allowed"); + status = PERMISSION_DENIED; + } + break; + case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE: + if (!modifyAudioRoutingAllowed()) { + ALOGE("getInputForAttr() permission denied: modify audio routing not allowed"); + status = PERMISSION_DENIED; + } + break; + case AudioPolicyInterface::API_INPUT_INVALID: + default: + LOG_ALWAYS_FATAL("getInputForAttr() encountered an invalid input type %d", + (int)inputType); + } + } + + if (status != NO_ERROR) { + if (status == PERMISSION_DENIED) { + mAudioPolicyManager->releaseInput(*input, session); + } + return status; + } } - if (input == 0) { - return input; - } + if (audioPolicyEffects != 0) { // create audio pre processors according to input source - status_t status = audioPolicyEffects->addInputEffects(input, inputSource, audioSession); + status_t status = audioPolicyEffects->addInputEffects(*input, attr->source, session); if (status != NO_ERROR && status != ALREADY_EXISTS) { - ALOGW("Failed to add effects on input %d", input); + ALOGW("Failed to add effects on input %d", *input); } } - return input; + return NO_ERROR; } status_t AudioPolicyService::startInput(audio_io_handle_t input, @@ -332,7 +371,7 @@ status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, if (!settingsAllowed()) { return PERMISSION_DENIED; } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return BAD_VALUE; } Mutex::Autolock _l(mLock); @@ -350,7 +389,7 @@ status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, if (!settingsAllowed()) { return PERMISSION_DENIED; } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return BAD_VALUE; } Mutex::Autolock _l(mLock); @@ -366,7 +405,7 @@ status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, if (mAudioPolicyManager == NULL) { return NO_INIT; } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return BAD_VALUE; } Mutex::Autolock _l(mLock); @@ -377,7 +416,7 @@ status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return 0; } if (mAudioPolicyManager == NULL) { @@ -390,7 +429,7 @@ uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return AUDIO_DEVICE_NONE; } if (mAudioPolicyManager == NULL) { @@ -439,7 +478,7 @@ status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return false; } if (mAudioPolicyManager == NULL) { @@ -451,7 +490,7 @@ bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inP bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return false; } if (mAudioPolicyManager == NULL) { @@ -606,4 +645,20 @@ status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session) return mAudioPolicyManager->releaseSoundTriggerSession(session); } +status_t AudioPolicyService::registerPolicyMixes(Vector<AudioMix> mixes, bool registration) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (registration) { + return mAudioPolicyManager->registerPolicyMixes(mixes); + } else { + return mAudioPolicyManager->unregisterPolicyMixes(mixes); + } +} + }; // namespace android diff --git a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp index 09476c1..5a91192 100644 --- a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp @@ -33,7 +33,8 @@ namespace android { status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) + const char *device_address, + const char *device_name __unused) { if (mpAudioPolicy == NULL) { return NO_INIT; @@ -134,7 +135,7 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, audio_output_flags_t flags, const audio_offload_info_t *offloadInfo) { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return AUDIO_IO_HANDLE_NONE; } if (mpAudioPolicy == NULL) { @@ -148,9 +149,9 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, status_t AudioPolicyService::startOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return BAD_VALUE; } if (mpAudioPolicy == NULL) { @@ -176,9 +177,9 @@ status_t AudioPolicyService::startOutput(audio_io_handle_t output, status_t AudioPolicyService::stopOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return BAD_VALUE; } if (mpAudioPolicy == NULL) { @@ -191,7 +192,7 @@ status_t AudioPolicyService::stopOutput(audio_io_handle_t output, status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { ALOGV("doStopOutput from tid %d", gettid()); // release audio processors from the stream @@ -210,64 +211,75 @@ status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session); } -void AudioPolicyService::releaseOutput(audio_io_handle_t output) +void AudioPolicyService::releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) { if (mpAudioPolicy == NULL) { return; } ALOGV("releaseOutput()"); - mOutputCommandThread->releaseOutputCommand(output); + mOutputCommandThread->releaseOutputCommand(output, stream, session); } -void AudioPolicyService::doReleaseOutput(audio_io_handle_t output) +void AudioPolicyService::doReleaseOutput(audio_io_handle_t output, + audio_stream_type_t stream __unused, + audio_session_t session __unused) { 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, - audio_input_flags_t flags __unused) +status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags __unused) { if (mpAudioPolicy == NULL) { - return 0; + return NO_INIT; } + + audio_source_t inputSource = attr->source; + // already checked by client, but double-check in case the client wrapper is bypassed if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD && inputSource != AUDIO_SOURCE_FM_TUNER) { - return 0; + return BAD_VALUE; + } + + if (inputSource == AUDIO_SOURCE_DEFAULT) { + inputSource = AUDIO_SOURCE_MIC; } if (((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) || ((inputSource == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) { - return 0; + return BAD_VALUE; } - audio_io_handle_t input; sp<AudioPolicyEffects>audioPolicyEffects; { Mutex::Autolock _l(mLock); // the audio_in_acoustics_t parameter is ignored by get_input() - input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate, + *input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate, format, channelMask, (audio_in_acoustics_t) 0); audioPolicyEffects = mAudioPolicyEffects; } - if (input == 0) { - return input; + if (*input == AUDIO_IO_HANDLE_NONE) { + return INVALID_OPERATION; } if (audioPolicyEffects != 0) { // create audio pre processors according to input source - status_t status = audioPolicyEffects->addInputEffects(input, inputSource, audioSession); + status_t status = audioPolicyEffects->addInputEffects(*input, inputSource, session); if (status != NO_ERROR && status != ALREADY_EXISTS) { ALOGW("Failed to add effects on input %d", input); } } - return input; + return NO_ERROR; } status_t AudioPolicyService::startInput(audio_io_handle_t input, @@ -324,7 +336,7 @@ status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, if (!settingsAllowed()) { return PERMISSION_DENIED; } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return BAD_VALUE; } Mutex::Autolock _l(mLock); @@ -342,7 +354,7 @@ status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, if (!settingsAllowed()) { return PERMISSION_DENIED; } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return BAD_VALUE; } Mutex::Autolock _l(mLock); @@ -363,7 +375,7 @@ status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, if (mpAudioPolicy == NULL) { return NO_INIT; } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return BAD_VALUE; } Mutex::Autolock _l(mLock); @@ -379,7 +391,7 @@ status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return 0; } if (mpAudioPolicy == NULL) { @@ -392,7 +404,7 @@ uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return AUDIO_DEVICE_NONE; } if (mpAudioPolicy == NULL) { @@ -441,7 +453,7 @@ status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return false; } if (mpAudioPolicy == NULL) { @@ -453,7 +465,7 @@ bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inP bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return false; } if (mpAudioPolicy == NULL) { @@ -549,26 +561,45 @@ status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config * return INVALID_OPERATION; } -audio_io_handle_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) +status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session __unused, + 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_stream_type_t stream = audio_attributes_to_stream_type(attr); + if (attr != NULL) { + *stream = audio_attributes_to_stream_type(attr); + } else { + if (*stream == AUDIO_STREAM_DEFAULT) { + return BAD_VALUE; + } + } + *output = getOutput(*stream, samplingRate, format, channelMask, + flags, offloadInfo); + if (*output == AUDIO_IO_HANDLE_NONE) { + return INVALID_OPERATION; + } + return NO_ERROR; +} - return getOutput(stream, samplingRate, format, channelMask, flags, offloadInfo); +status_t AudioPolicyService::acquireSoundTriggerSession(audio_session_t *session __unused, + audio_io_handle_t *ioHandle __unused, + audio_devices_t *device __unused) +{ + return INVALID_OPERATION; } -status_t AudioPolicyService::acquireSoundTriggerSession(audio_session_t *session, - audio_io_handle_t *ioHandle, - audio_devices_t *device) +status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session __unused) { return INVALID_OPERATION; } -status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session) +status_t AudioPolicyService::registerPolicyMixes(Vector<AudioMix> mixes __unused, + bool registration __unused) { return INVALID_OPERATION; } diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index dd4067f..eb9116d 100644 --- a/services/audiopolicy/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -35,6 +35,7 @@ #include <hardware_legacy/power.h> #include <media/AudioEffect.h> #include <media/EffectsFactoryApi.h> +#include <media/AudioParameter.h> #include <hardware/hardware.h> #include <system/audio.h> @@ -149,7 +150,7 @@ AudioPolicyService::~AudioPolicyService() void AudioPolicyService::registerClient(const sp<IAudioPolicyServiceClient>& client) { - Mutex::Autolock _l(mLock); + Mutex::Autolock _l(mNotificationClientsLock); uid_t uid = IPCThreadState::self()->getCallingUid(); if (mNotificationClients.indexOfKey(uid) < 0) { @@ -160,7 +161,7 @@ void AudioPolicyService::registerClient(const sp<IAudioPolicyServiceClient>& cli mNotificationClients.add(uid, notificationClient); - sp<IBinder> binder = client->asBinder(); + sp<IBinder> binder = IInterface::asBinder(client); binder->linkToDeath(notificationClient); } } @@ -168,14 +169,17 @@ void AudioPolicyService::registerClient(const sp<IAudioPolicyServiceClient>& cli // removeNotificationClient() is called when the client process dies. void AudioPolicyService::removeNotificationClient(uid_t uid) { - Mutex::Autolock _l(mLock); - - mNotificationClients.removeItem(uid); - + { + Mutex::Autolock _l(mNotificationClientsLock); + mNotificationClients.removeItem(uid); + } #ifndef USE_LEGACY_AUDIO_POLICY + { + Mutex::Autolock _l(mLock); if (mAudioPolicyManager) { mAudioPolicyManager->clearAudioPatches(uid); } + } #endif } @@ -186,7 +190,7 @@ void AudioPolicyService::onAudioPortListUpdate() void AudioPolicyService::doOnAudioPortListUpdate() { - Mutex::Autolock _l(mLock); + Mutex::Autolock _l(mNotificationClientsLock); for (size_t i = 0; i < mNotificationClients.size(); i++) { mNotificationClients.valueAt(i)->onAudioPortListUpdate(); } @@ -212,7 +216,7 @@ status_t AudioPolicyService::clientReleaseAudioPatch(audio_patch_handle_t handle void AudioPolicyService::doOnAudioPatchListUpdate() { - Mutex::Autolock _l(mLock); + Mutex::Autolock _l(mNotificationClientsLock); for (size_t i = 0; i < mNotificationClients.size(); i++) { mNotificationClients.valueAt(i)->onAudioPatchListUpdate(); } @@ -454,7 +458,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() break; } mLock.unlock(); - svc->doReleaseOutput(data->mIO); + svc->doReleaseOutput(data->mIO, data->mStream, data->mSession); mLock.lock(); }break; case CREATE_AUDIO_PATCH: { @@ -651,7 +655,7 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output, audio_stream_type_t stream, - int session) + audio_session_t session) { sp<AudioCommand> command = new AudioCommand(); command->mCommand = STOP_OUTPUT; @@ -664,12 +668,16 @@ void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t sendCommand(command); } -void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output) +void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) { sp<AudioCommand> command = new AudioCommand(); command->mCommand = RELEASE_OUTPUT; sp<ReleaseOutputData> data = new ReleaseOutputData(); data->mIO = output; + data->mStream = stream; + data->mSession = session; command->mParam = data; ALOGV("AudioCommandThread() adding release output %d", output); sendCommand(command); @@ -900,8 +908,10 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(sp<AudioCommand>& c } removedCommands.clear(); - // Disable wait for status if delay is not 0 - if (delayMs != 0) { + // Disable wait for status if delay is not 0. + // Except for create audio patch command because the returned patch handle + // is needed by audio policy manager + if (delayMs != 0 && command->mCommand != CREATE_AUDIO_PATCH) { command->mWaitStatus = false; } diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 4e68ab1..0378384 100644 --- a/services/audiopolicy/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -30,9 +30,12 @@ #include <media/IAudioPolicyService.h> #include <media/ToneGenerator.h> #include <media/AudioEffect.h> +#include <media/AudioPolicy.h> +#ifdef USE_LEGACY_AUDIO_POLICY #include <hardware_legacy/AudioPolicyInterface.h> +#endif #include "AudioPolicyEffects.h" -#include "AudioPolicyManager.h" +#include "managerdefault/AudioPolicyManager.h" namespace android { @@ -58,7 +61,8 @@ public: virtual status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address); + const char *device_address, + const char *device_name); virtual audio_policy_dev_state_t getDeviceConnectionState( audio_devices_t device, const char *device_address); @@ -72,25 +76,31 @@ public: audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, const audio_offload_info_t *offloadInfo = NULL); - virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr, - uint32_t samplingRate = 0, - audio_format_t format = AUDIO_FORMAT_DEFAULT, - audio_channel_mask_t channelMask = 0, - audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, - const audio_offload_info_t *offloadInfo = NULL); + virtual status_t getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + audio_stream_type_t *stream, + uint32_t samplingRate = 0, + audio_format_t format = AUDIO_FORMAT_DEFAULT, + audio_channel_mask_t channelMask = 0, + audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, + const audio_offload_info_t *offloadInfo = NULL); virtual status_t startOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session = 0); + audio_session_t session); 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, - int audioSession, - audio_input_flags_t flags); + audio_session_t session); + virtual void releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); + virtual status_t getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags); virtual status_t startInput(audio_io_handle_t input, audio_session_t session); virtual status_t stopInput(audio_io_handle_t input, @@ -179,10 +189,14 @@ public: virtual audio_mode_t getPhoneState(); + virtual status_t registerPolicyMixes(Vector<AudioMix> mixes, bool registration); + status_t doStopOutput(audio_io_handle_t output, audio_stream_type_t stream, - int session = 0); - void doReleaseOutput(audio_io_handle_t output); + audio_session_t session); + void doReleaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); status_t clientCreateAudioPatch(const struct audio_patch *patch, audio_patch_handle_t *handle, @@ -250,8 +264,10 @@ private: status_t voiceVolumeCommand(float volume, int delayMs = 0); void stopOutputCommand(audio_io_handle_t output, audio_stream_type_t stream, - int session); - void releaseOutputCommand(audio_io_handle_t output); + audio_session_t session); + void releaseOutputCommand(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); status_t sendCommand(sp<AudioCommand>& command, int delayMs = 0); void insertCommand_l(sp<AudioCommand>& command, int delayMs = 0); status_t createAudioPatchCommand(const struct audio_patch *patch, @@ -321,12 +337,14 @@ private: public: audio_io_handle_t mIO; audio_stream_type_t mStream; - int mSession; + audio_session_t mSession; }; class ReleaseOutputData : public AudioCommandData { public: audio_io_handle_t mIO; + audio_stream_type_t mStream; + audio_session_t mSession; }; class CreateAudioPatchData : public AudioCommandData { @@ -495,7 +513,7 @@ private: AudioPolicyClient *mAudioPolicyClient; DefaultKeyedVector< uid_t, sp<NotificationClient> > mNotificationClients; - + Mutex mNotificationClientsLock; // protects mNotificationClients // Manage all effects configured in audio_effects.conf sp<AudioPolicyEffects> mAudioPolicyEffects; audio_mode_t mPhoneState; diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index e184d97..de841c8 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -23,8 +23,10 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ CameraService.cpp \ CameraDeviceFactory.cpp \ + CameraFlashlight.cpp \ common/Camera2ClientBase.cpp \ common/CameraDeviceBase.cpp \ + common/CameraModule.cpp \ common/FrameProcessorBase.cpp \ api1/CameraClient.cpp \ api1/Camera2Client.cpp \ @@ -52,6 +54,7 @@ LOCAL_SRC_FILES:= \ device3/StatusTracker.cpp \ gui/RingBufferConsumer.cpp \ utils/CameraTraces.cpp \ + utils/AutoConditionLock.cpp \ LOCAL_SHARED_LIBRARIES:= \ libui \ diff --git a/services/camera/libcameraservice/CameraDeviceFactory.cpp b/services/camera/libcameraservice/CameraDeviceFactory.cpp index bfef50e..6589e27 100644 --- a/services/camera/libcameraservice/CameraDeviceFactory.cpp +++ b/services/camera/libcameraservice/CameraDeviceFactory.cpp @@ -48,6 +48,7 @@ sp<CameraDeviceBase> CameraDeviceFactory::createDevice(int cameraId) { case CAMERA_DEVICE_API_VERSION_3_0: case CAMERA_DEVICE_API_VERSION_3_1: case CAMERA_DEVICE_API_VERSION_3_2: + case CAMERA_DEVICE_API_VERSION_3_3: device = new Camera3Device(cameraId); break; default: diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp new file mode 100644 index 0000000..6fda9b2 --- /dev/null +++ b/services/camera/libcameraservice/CameraFlashlight.cpp @@ -0,0 +1,889 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "CameraFlashlight" +#define ATRACE_TAG ATRACE_TAG_CAMERA +// #define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include <cutils/properties.h> + +#include "camera/CameraMetadata.h" +#include "CameraFlashlight.h" +#include "gui/IGraphicBufferConsumer.h" +#include "gui/BufferQueue.h" +#include "camera/camera2/CaptureRequest.h" +#include "CameraDeviceFactory.h" + + +namespace android { + +///////////////////////////////////////////////////////////////////// +// CameraFlashlight implementation begins +// used by camera service to control flashflight. +///////////////////////////////////////////////////////////////////// +CameraFlashlight::CameraFlashlight(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule), + mCallbacks(&callbacks), + mFlashlightMapInitialized(false) { +} + +CameraFlashlight::~CameraFlashlight() { +} + +status_t CameraFlashlight::createFlashlightControl(const String8& cameraId) { + ALOGV("%s: creating a flash light control for camera %s", __FUNCTION__, + cameraId.string()); + if (mFlashControl != NULL) { + return INVALID_OPERATION; + } + + status_t res = OK; + + if (mCameraModule->getRawModule()->module_api_version >= + CAMERA_MODULE_API_VERSION_2_4) { + mFlashControl = new ModuleFlashControl(*mCameraModule, *mCallbacks); + if (mFlashControl == NULL) { + ALOGV("%s: cannot create flash control for module api v2.4+", + __FUNCTION__); + return NO_MEMORY; + } + } else { + uint32_t deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; + + if (mCameraModule->getRawModule()->module_api_version >= + CAMERA_MODULE_API_VERSION_2_0) { + camera_info info; + res = mCameraModule->getCameraInfo( + atoi(String8(cameraId).string()), &info); + if (res) { + ALOGE("%s: failed to get camera info for camera %s", + __FUNCTION__, cameraId.string()); + return res; + } + deviceVersion = info.device_version; + } + + if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) { + CameraDeviceClientFlashControl *flashControl = + new CameraDeviceClientFlashControl(*mCameraModule, + *mCallbacks); + if (!flashControl) { + return NO_MEMORY; + } + + mFlashControl = flashControl; + } else { + mFlashControl = + new CameraHardwareInterfaceFlashControl(*mCameraModule, + *mCallbacks); + } + } + + return OK; +} + +status_t CameraFlashlight::setTorchMode(const String8& cameraId, bool enabled) { + if (!mFlashlightMapInitialized) { + ALOGE("%s: findFlashUnits() must be called before this method."); + return NO_INIT; + } + + ALOGV("%s: set torch mode of camera %s to %d", __FUNCTION__, + cameraId.string(), enabled); + + status_t res = OK; + Mutex::Autolock l(mLock); + + if (mOpenedCameraIds.indexOf(cameraId) != NAME_NOT_FOUND) { + // This case is needed to avoid state corruption during the following call sequence: + // CameraService::setTorchMode for camera ID 0 begins, does torch status checks + // CameraService::connect for camera ID 0 begins, calls prepareDeviceOpen, ends + // CameraService::setTorchMode for camera ID 0 continues, calls + // CameraFlashlight::setTorchMode + + // TODO: Move torch status checks and state updates behind this CameraFlashlight lock + // to avoid other similar race conditions. + ALOGE("%s: Camera device %s is in use, cannot set torch mode.", + __FUNCTION__, cameraId.string()); + return -EBUSY; + } + + if (mFlashControl == NULL) { + if (enabled == false) { + return OK; + } + + res = createFlashlightControl(cameraId); + if (res) { + return res; + } + res = mFlashControl->setTorchMode(cameraId, enabled); + return res; + } + + // if flash control already exists, turning on torch mode may fail if it's + // tied to another camera device for module v2.3 and below. + res = mFlashControl->setTorchMode(cameraId, enabled); + if (res == BAD_INDEX) { + // flash control is tied to another camera device, need to close it and + // try again. + mFlashControl.clear(); + res = createFlashlightControl(cameraId); + if (res) { + return res; + } + res = mFlashControl->setTorchMode(cameraId, enabled); + } + + return res; +} + +status_t CameraFlashlight::findFlashUnits() { + Mutex::Autolock l(mLock); + status_t res; + int32_t numCameras = mCameraModule->getNumberOfCameras(); + + mHasFlashlightMap.clear(); + mFlashlightMapInitialized = false; + + for (int32_t i = 0; i < numCameras; i++) { + bool hasFlash = false; + String8 id = String8::format("%d", i); + + res = createFlashlightControl(id); + if (res) { + ALOGE("%s: failed to create flash control for %s", __FUNCTION__, + id.string()); + } else { + res = mFlashControl->hasFlashUnit(id, &hasFlash); + if (res == -EUSERS || res == -EBUSY) { + ALOGE("%s: failed to check if camera %s has a flash unit. Some " + "camera devices may be opened", __FUNCTION__, + id.string()); + return res; + } else if (res) { + ALOGE("%s: failed to check if camera %s has a flash unit. %s" + " (%d)", __FUNCTION__, id.string(), strerror(-res), + res); + } + + mFlashControl.clear(); + } + mHasFlashlightMap.add(id, hasFlash); + } + + mFlashlightMapInitialized = true; + return OK; +} + +bool CameraFlashlight::hasFlashUnit(const String8& cameraId) { + status_t res; + + Mutex::Autolock l(mLock); + return hasFlashUnitLocked(cameraId); +} + +bool CameraFlashlight::hasFlashUnitLocked(const String8& cameraId) { + if (!mFlashlightMapInitialized) { + ALOGE("%s: findFlashUnits() must be called before this method."); + return false; + } + + ssize_t index = mHasFlashlightMap.indexOfKey(cameraId); + if (index == NAME_NOT_FOUND) { + ALOGE("%s: camera %s not present when findFlashUnits() was called", + __FUNCTION__, cameraId.string()); + return false; + } + + return mHasFlashlightMap.valueAt(index); +} + +status_t CameraFlashlight::prepareDeviceOpen(const String8& cameraId) { + ALOGV("%s: prepare for device open", __FUNCTION__); + + Mutex::Autolock l(mLock); + if (!mFlashlightMapInitialized) { + ALOGE("%s: findFlashUnits() must be called before this method."); + return NO_INIT; + } + + if (mCameraModule->getRawModule()->module_api_version < + CAMERA_MODULE_API_VERSION_2_4) { + // framework is going to open a camera device, all flash light control + // should be closed for backward compatible support. + mFlashControl.clear(); + + if (mOpenedCameraIds.size() == 0) { + // notify torch unavailable for all cameras with a flash + int numCameras = mCameraModule->getNumberOfCameras(); + for (int i = 0; i < numCameras; i++) { + if (hasFlashUnitLocked(String8::format("%d", i))) { + mCallbacks->torch_mode_status_change(mCallbacks, + String8::format("%d", i).string(), + TORCH_MODE_STATUS_NOT_AVAILABLE); + } + } + } + + // close flash control that may be opened by calling hasFlashUnitLocked. + mFlashControl.clear(); + } + + if (mOpenedCameraIds.indexOf(cameraId) == NAME_NOT_FOUND) { + mOpenedCameraIds.add(cameraId); + } + + return OK; +} + +status_t CameraFlashlight::deviceClosed(const String8& cameraId) { + ALOGV("%s: device %s is closed", __FUNCTION__, cameraId.string()); + + Mutex::Autolock l(mLock); + if (!mFlashlightMapInitialized) { + ALOGE("%s: findFlashUnits() must be called before this method."); + return NO_INIT; + } + + ssize_t index = mOpenedCameraIds.indexOf(cameraId); + if (index == NAME_NOT_FOUND) { + ALOGE("%s: couldn't find camera %s in the opened list", __FUNCTION__, + cameraId.string()); + } else { + mOpenedCameraIds.removeAt(index); + } + + // Cannot do anything until all cameras are closed. + if (mOpenedCameraIds.size() != 0) + return OK; + + if (mCameraModule->getRawModule()->module_api_version < + CAMERA_MODULE_API_VERSION_2_4) { + // notify torch available for all cameras with a flash + int numCameras = mCameraModule->getNumberOfCameras(); + for (int i = 0; i < numCameras; i++) { + if (hasFlashUnitLocked(String8::format("%d", i))) { + mCallbacks->torch_mode_status_change(mCallbacks, + String8::format("%d", i).string(), + TORCH_MODE_STATUS_AVAILABLE_OFF); + } + } + } + + return OK; +} +// CameraFlashlight implementation ends + + +FlashControlBase::~FlashControlBase() { +} + +///////////////////////////////////////////////////////////////////// +// ModuleFlashControl implementation begins +// Flash control for camera module v2.4 and above. +///////////////////////////////////////////////////////////////////// +ModuleFlashControl::ModuleFlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule) { +} + +ModuleFlashControl::~ModuleFlashControl() { +} + +status_t ModuleFlashControl::hasFlashUnit(const String8& cameraId, bool *hasFlash) { + if (!hasFlash) { + return BAD_VALUE; + } + + *hasFlash = false; + Mutex::Autolock l(mLock); + + camera_info info; + status_t res = mCameraModule->getCameraInfo(atoi(cameraId.string()), + &info); + if (res != 0) { + return res; + } + + CameraMetadata metadata; + metadata = info.static_camera_characteristics; + camera_metadata_entry flashAvailable = + metadata.find(ANDROID_FLASH_INFO_AVAILABLE); + if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) { + *hasFlash = true; + } + + return OK; +} + +status_t ModuleFlashControl::setTorchMode(const String8& cameraId, bool enabled) { + ALOGV("%s: set camera %s torch mode to %d", __FUNCTION__, + cameraId.string(), enabled); + + Mutex::Autolock l(mLock); + return mCameraModule->setTorchMode(cameraId.string(), enabled); +} +// ModuleFlashControl implementation ends + +///////////////////////////////////////////////////////////////////// +// CameraDeviceClientFlashControl implementation begins +// Flash control for camera module <= v2.3 and camera HAL v2-v3 +///////////////////////////////////////////////////////////////////// +CameraDeviceClientFlashControl::CameraDeviceClientFlashControl( + CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule), + mCallbacks(&callbacks), + mTorchEnabled(false), + mMetadata(NULL), + mStreaming(false) { +} + +CameraDeviceClientFlashControl::~CameraDeviceClientFlashControl() { + disconnectCameraDevice(); + if (mMetadata) { + delete mMetadata; + } + + mAnw.clear(); + mSurfaceTexture.clear(); + mProducer.clear(); + mConsumer.clear(); + + if (mTorchEnabled) { + if (mCallbacks) { + ALOGV("%s: notify the framework that torch was turned off", + __FUNCTION__); + mCallbacks->torch_mode_status_change(mCallbacks, + mCameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF); + } + } +} + +status_t CameraDeviceClientFlashControl::initializeSurface( + sp<CameraDeviceBase> &device, int32_t width, int32_t height) { + status_t res; + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + + mSurfaceTexture = new GLConsumer(mConsumer, 0, GLConsumer::TEXTURE_EXTERNAL, + true, true); + if (mSurfaceTexture == NULL) { + return NO_MEMORY; + } + + int32_t format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + res = mSurfaceTexture->setDefaultBufferSize(width, height); + if (res) { + return res; + } + res = mSurfaceTexture->setDefaultBufferFormat(format); + if (res) { + return res; + } + + mAnw = new Surface(mProducer, /*useAsync*/ true); + if (mAnw == NULL) { + return NO_MEMORY; + } + res = device->createStream(mAnw, width, height, format, + HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mStreamId); + if (res) { + return res; + } + + res = device->configureStreams(); + if (res) { + return res; + } + + return res; +} + +status_t CameraDeviceClientFlashControl::getSmallestSurfaceSize( + const camera_info& info, int32_t *width, int32_t *height) { + if (!width || !height) { + return BAD_VALUE; + } + + int32_t w = INT32_MAX; + int32_t h = 1; + + CameraMetadata metadata; + metadata = info.static_camera_characteristics; + camera_metadata_entry streamConfigs = + metadata.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + for (size_t i = 0; i < streamConfigs.count; i += 4) { + int32_t fmt = streamConfigs.data.i32[i]; + if (fmt == ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED) { + int32_t ww = streamConfigs.data.i32[i + 1]; + int32_t hh = streamConfigs.data.i32[i + 2]; + + if (w * h > ww * hh) { + w = ww; + h = hh; + } + } + } + + // if stream configuration is not found, try available processed sizes. + if (streamConfigs.count == 0) { + camera_metadata_entry availableProcessedSizes = + metadata.find(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES); + for (size_t i = 0; i < availableProcessedSizes.count; i += 2) { + int32_t ww = availableProcessedSizes.data.i32[i]; + int32_t hh = availableProcessedSizes.data.i32[i + 1]; + if (w * h > ww * hh) { + w = ww; + h = hh; + } + } + } + + if (w == INT32_MAX) { + return NAME_NOT_FOUND; + } + + *width = w; + *height = h; + + return OK; +} + +status_t CameraDeviceClientFlashControl::connectCameraDevice( + const String8& cameraId) { + camera_info info; + status_t res = mCameraModule->getCameraInfo(atoi(cameraId.string()), &info); + if (res != 0) { + ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__, + cameraId.string()); + return res; + } + + sp<CameraDeviceBase> device = + CameraDeviceFactory::createDevice(atoi(cameraId.string())); + if (device == NULL) { + return NO_MEMORY; + } + + res = device->initialize(mCameraModule); + if (res) { + return res; + } + + int32_t width, height; + res = getSmallestSurfaceSize(info, &width, &height); + if (res) { + return res; + } + res = initializeSurface(device, width, height); + if (res) { + return res; + } + + mCameraId = cameraId; + mStreaming = (info.device_version <= CAMERA_DEVICE_API_VERSION_3_1); + mDevice = device; + + return OK; +} + +status_t CameraDeviceClientFlashControl::disconnectCameraDevice() { + if (mDevice != NULL) { + mDevice->disconnect(); + mDevice.clear(); + } + + return OK; +} + + + +status_t CameraDeviceClientFlashControl::hasFlashUnit(const String8& cameraId, + bool *hasFlash) { + ALOGV("%s: checking if camera %s has a flash unit", __FUNCTION__, + cameraId.string()); + + Mutex::Autolock l(mLock); + return hasFlashUnitLocked(cameraId, hasFlash); + +} + +status_t CameraDeviceClientFlashControl::hasFlashUnitLocked( + const String8& cameraId, bool *hasFlash) { + if (!hasFlash) { + return BAD_VALUE; + } + + camera_info info; + status_t res = mCameraModule->getCameraInfo( + atoi(cameraId.string()), &info); + if (res != 0) { + ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__, + cameraId.string()); + return res; + } + + CameraMetadata metadata; + metadata = info.static_camera_characteristics; + camera_metadata_entry flashAvailable = + metadata.find(ANDROID_FLASH_INFO_AVAILABLE); + if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) { + *hasFlash = true; + } + + return OK; +} + +status_t CameraDeviceClientFlashControl::submitTorchEnabledRequest() { + status_t res; + + if (mMetadata == NULL) { + mMetadata = new CameraMetadata(); + if (mMetadata == NULL) { + return NO_MEMORY; + } + res = mDevice->createDefaultRequest( + CAMERA3_TEMPLATE_PREVIEW, mMetadata); + if (res) { + return res; + } + } + + uint8_t torchOn = ANDROID_FLASH_MODE_TORCH; + mMetadata->update(ANDROID_FLASH_MODE, &torchOn, 1); + mMetadata->update(ANDROID_REQUEST_OUTPUT_STREAMS, &mStreamId, 1); + + uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON; + mMetadata->update(ANDROID_CONTROL_AE_MODE, &aeMode, 1); + + int32_t requestId = 0; + mMetadata->update(ANDROID_REQUEST_ID, &requestId, 1); + + if (mStreaming) { + res = mDevice->setStreamingRequest(*mMetadata); + } else { + res = mDevice->capture(*mMetadata); + } + return res; +} + + + + +status_t CameraDeviceClientFlashControl::setTorchMode( + const String8& cameraId, bool enabled) { + bool hasFlash = false; + + Mutex::Autolock l(mLock); + status_t res = hasFlashUnitLocked(cameraId, &hasFlash); + + // pre-check + if (enabled) { + // invalid camera? + if (res) { + return -EINVAL; + } + // no flash unit? + if (!hasFlash) { + return -ENOSYS; + } + // already opened for a different device? + if (mDevice != NULL && cameraId != mCameraId) { + return BAD_INDEX; + } + } else if (mDevice == NULL || cameraId != mCameraId) { + // disabling the torch mode of an un-opened or different device. + return OK; + } else { + // disabling the torch mode of currently opened device + disconnectCameraDevice(); + mTorchEnabled = false; + mCallbacks->torch_mode_status_change(mCallbacks, + cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF); + return OK; + } + + if (mDevice == NULL) { + res = connectCameraDevice(cameraId); + if (res) { + return res; + } + } + + res = submitTorchEnabledRequest(); + if (res) { + return res; + } + + mTorchEnabled = true; + mCallbacks->torch_mode_status_change(mCallbacks, + cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_ON); + return OK; +} +// CameraDeviceClientFlashControl implementation ends + + +///////////////////////////////////////////////////////////////////// +// CameraHardwareInterfaceFlashControl implementation begins +// Flash control for camera module <= v2.3 and camera HAL v1 +///////////////////////////////////////////////////////////////////// +CameraHardwareInterfaceFlashControl::CameraHardwareInterfaceFlashControl( + CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule), + mCallbacks(&callbacks), + mTorchEnabled(false) { + +} + +CameraHardwareInterfaceFlashControl::~CameraHardwareInterfaceFlashControl() { + disconnectCameraDevice(); + + mAnw.clear(); + mSurfaceTexture.clear(); + mProducer.clear(); + mConsumer.clear(); + + if (mTorchEnabled) { + if (mCallbacks) { + ALOGV("%s: notify the framework that torch was turned off", + __FUNCTION__); + mCallbacks->torch_mode_status_change(mCallbacks, + mCameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF); + } + } +} + +status_t CameraHardwareInterfaceFlashControl::setTorchMode( + const String8& cameraId, bool enabled) { + Mutex::Autolock l(mLock); + + // pre-check + status_t res; + if (enabled) { + bool hasFlash = false; + res = hasFlashUnitLocked(cameraId, &hasFlash); + // invalid camera? + if (res) { + // hasFlashUnitLocked() returns BAD_INDEX if mDevice is connected to + // another camera device. + return res == BAD_INDEX ? BAD_INDEX : -EINVAL; + } + // no flash unit? + if (!hasFlash) { + return -ENOSYS; + } + } else if (mDevice == NULL || cameraId != mCameraId) { + // disabling the torch mode of an un-opened or different device. + return OK; + } else { + // disabling the torch mode of currently opened device + disconnectCameraDevice(); + mTorchEnabled = false; + mCallbacks->torch_mode_status_change(mCallbacks, + cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF); + return OK; + } + + res = startPreviewAndTorch(); + if (res) { + return res; + } + + mTorchEnabled = true; + mCallbacks->torch_mode_status_change(mCallbacks, + cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_ON); + return OK; +} + +status_t CameraHardwareInterfaceFlashControl::hasFlashUnit( + const String8& cameraId, bool *hasFlash) { + Mutex::Autolock l(mLock); + return hasFlashUnitLocked(cameraId, hasFlash); +} + +status_t CameraHardwareInterfaceFlashControl::hasFlashUnitLocked( + const String8& cameraId, bool *hasFlash) { + if (!hasFlash) { + return BAD_VALUE; + } + + status_t res; + if (mDevice == NULL) { + res = connectCameraDevice(cameraId); + if (res) { + return res; + } + } + + if (cameraId != mCameraId) { + return BAD_INDEX; + } + + const char *flashMode = + mParameters.get(CameraParameters::KEY_SUPPORTED_FLASH_MODES); + if (flashMode && strstr(flashMode, CameraParameters::FLASH_MODE_TORCH)) { + *hasFlash = true; + } else { + *hasFlash = false; + } + + return OK; +} + +status_t CameraHardwareInterfaceFlashControl::startPreviewAndTorch() { + status_t res = OK; + res = mDevice->startPreview(); + if (res) { + ALOGE("%s: start preview failed. %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + mParameters.set(CameraParameters::KEY_FLASH_MODE, + CameraParameters::FLASH_MODE_TORCH); + + return mDevice->setParameters(mParameters); +} + +status_t CameraHardwareInterfaceFlashControl::getSmallestSurfaceSize( + int32_t *width, int32_t *height) { + if (!width || !height) { + return BAD_VALUE; + } + + int32_t w = INT32_MAX; + int32_t h = 1; + Vector<Size> sizes; + + mParameters.getSupportedPreviewSizes(sizes); + for (size_t i = 0; i < sizes.size(); i++) { + Size s = sizes[i]; + if (w * h > s.width * s.height) { + w = s.width; + h = s.height; + } + } + + if (w == INT32_MAX) { + return NAME_NOT_FOUND; + } + + *width = w; + *height = h; + + return OK; +} + +status_t CameraHardwareInterfaceFlashControl::initializePreviewWindow( + sp<CameraHardwareInterface> device, int32_t width, int32_t height) { + status_t res; + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + + mSurfaceTexture = new GLConsumer(mConsumer, 0, GLConsumer::TEXTURE_EXTERNAL, + true, true); + if (mSurfaceTexture == NULL) { + return NO_MEMORY; + } + + int32_t format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + res = mSurfaceTexture->setDefaultBufferSize(width, height); + if (res) { + return res; + } + res = mSurfaceTexture->setDefaultBufferFormat(format); + if (res) { + return res; + } + + mAnw = new Surface(mProducer, /*useAsync*/ true); + if (mAnw == NULL) { + return NO_MEMORY; + } + + res = native_window_api_connect(mAnw.get(), NATIVE_WINDOW_API_CAMERA); + if (res) { + ALOGE("%s: Unable to connect to native window", __FUNCTION__); + return res; + } + + return device->setPreviewWindow(mAnw); +} + +status_t CameraHardwareInterfaceFlashControl::connectCameraDevice( + const String8& cameraId) { + sp<CameraHardwareInterface> device = + new CameraHardwareInterface(cameraId.string()); + + status_t res = device->initialize(mCameraModule); + if (res) { + ALOGE("%s: initializing camera %s failed", __FUNCTION__, + cameraId.string()); + return res; + } + + // need to set __get_memory in set_callbacks(). + device->setCallbacks(NULL, NULL, NULL, NULL); + + mParameters = device->getParameters(); + + int32_t width, height; + res = getSmallestSurfaceSize(&width, &height); + if (res) { + ALOGE("%s: failed to get smallest surface size for camera %s", + __FUNCTION__, cameraId.string()); + return res; + } + + res = initializePreviewWindow(device, width, height); + if (res) { + ALOGE("%s: failed to initialize preview window for camera %s", + __FUNCTION__, cameraId.string()); + return res; + } + + mCameraId = cameraId; + mDevice = device; + return OK; +} + +status_t CameraHardwareInterfaceFlashControl::disconnectCameraDevice() { + if (mDevice == NULL) { + return OK; + } + + mParameters.set(CameraParameters::KEY_FLASH_MODE, + CameraParameters::FLASH_MODE_OFF); + mDevice->setParameters(mParameters); + mDevice->stopPreview(); + status_t res = native_window_api_disconnect(mAnw.get(), + NATIVE_WINDOW_API_CAMERA); + if (res) { + ALOGW("%s: native_window_api_disconnect failed: %s (%d)", + __FUNCTION__, strerror(-res), res); + } + mDevice->setPreviewWindow(NULL); + mDevice->release(); + + return OK; +} +// CameraHardwareInterfaceFlashControl implementation ends + +} diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h new file mode 100644 index 0000000..30f01f0 --- /dev/null +++ b/services/camera/libcameraservice/CameraFlashlight.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_CAMERAFLASHLIGHT_H +#define ANDROID_SERVERS_CAMERA_CAMERAFLASHLIGHT_H + +#include "hardware/camera_common.h" +#include "utils/KeyedVector.h" +#include "utils/SortedVector.h" +#include "gui/GLConsumer.h" +#include "gui/Surface.h" +#include "common/CameraDeviceBase.h" +#include "device1/CameraHardwareInterface.h" + +namespace android { + +/** + * FlashControlBase is a base class for flash control. It defines the functions + * that a flash control for each camera module/device version should implement. + */ +class FlashControlBase : public virtual VirtualLightRefBase { + public: + virtual ~FlashControlBase(); + + // Whether a camera device has a flash unit. Calling this function may + // cause the torch mode to be turned off in HAL v1 devices. If + // previously-on torch mode is turned off, + // callbacks.torch_mode_status_change() should be invoked. + virtual status_t hasFlashUnit(const String8& cameraId, + bool *hasFlash) = 0; + + // set the torch mode to on or off. + virtual status_t setTorchMode(const String8& cameraId, + bool enabled) = 0; +}; + +/** + * CameraFlashlight can be used by camera service to control flashflight. + */ +class CameraFlashlight : public virtual VirtualLightRefBase { + public: + CameraFlashlight(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~CameraFlashlight(); + + // Find all flash units. This must be called before other methods. All + // camera devices must be closed when it's called because HAL v1 devices + // need to be opened to query available flash modes. + status_t findFlashUnits(); + + // Whether a camera device has a flash unit. Before findFlashUnits() is + // called, this function always returns false. + bool hasFlashUnit(const String8& cameraId); + + // set the torch mode to on or off. + status_t setTorchMode(const String8& cameraId, bool enabled); + + // Notify CameraFlashlight that camera service is going to open a camera + // device. CameraFlashlight will free the resources that may cause the + // camera open to fail. Camera service must call this function before + // opening a camera device. + status_t prepareDeviceOpen(const String8& cameraId); + + // Notify CameraFlashlight that camera service has closed a camera + // device. CameraFlashlight may invoke callbacks for torch mode + // available depending on the implementation. + status_t deviceClosed(const String8& cameraId); + + private: + // create flashlight control based on camera module API and camera + // device API versions. + status_t createFlashlightControl(const String8& cameraId); + + // mLock should be locked. + bool hasFlashUnitLocked(const String8& cameraId); + + sp<FlashControlBase> mFlashControl; + CameraModule *mCameraModule; + const camera_module_callbacks_t *mCallbacks; + SortedVector<String8> mOpenedCameraIds; + + // camera id -> if it has a flash unit + KeyedVector<String8, bool> mHasFlashlightMap; + bool mFlashlightMapInitialized; + + Mutex mLock; // protect CameraFlashlight API +}; + +/** + * Flash control for camera module v2.4 and above. + */ +class ModuleFlashControl : public FlashControlBase { + public: + ModuleFlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~ModuleFlashControl(); + + // FlashControlBase + status_t hasFlashUnit(const String8& cameraId, bool *hasFlash); + status_t setTorchMode(const String8& cameraId, bool enabled); + + private: + CameraModule *mCameraModule; + + Mutex mLock; +}; + +/** + * Flash control for camera module <= v2.3 and camera HAL v2-v3 + */ +class CameraDeviceClientFlashControl : public FlashControlBase { + public: + CameraDeviceClientFlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~CameraDeviceClientFlashControl(); + + // FlashControlBase + status_t setTorchMode(const String8& cameraId, bool enabled); + status_t hasFlashUnit(const String8& cameraId, bool *hasFlash); + + private: + // connect to a camera device + status_t connectCameraDevice(const String8& cameraId); + // disconnect and free mDevice + status_t disconnectCameraDevice(); + + // initialize a surface + status_t initializeSurface(sp<CameraDeviceBase>& device, int32_t width, + int32_t height); + + // submit a request to enable the torch mode + status_t submitTorchEnabledRequest(); + + // get the smallest surface size of IMPLEMENTATION_DEFINED + status_t getSmallestSurfaceSize(const camera_info& info, int32_t *width, + int32_t *height); + + // protected by mLock + status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash); + + CameraModule *mCameraModule; + const camera_module_callbacks_t *mCallbacks; + String8 mCameraId; + bool mTorchEnabled; + CameraMetadata *mMetadata; + // WORKAROUND: will be set to true for HAL v2 devices where + // setStreamingRequest() needs to be call for torch mode settings to + // take effect. + bool mStreaming; + + sp<CameraDeviceBase> mDevice; + + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; + sp<GLConsumer> mSurfaceTexture; + sp<ANativeWindow> mAnw; + int32_t mStreamId; + + Mutex mLock; +}; + +/** + * Flash control for camera module <= v2.3 and camera HAL v1 + */ +class CameraHardwareInterfaceFlashControl : public FlashControlBase { + public: + CameraHardwareInterfaceFlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~CameraHardwareInterfaceFlashControl(); + + // FlashControlBase + status_t setTorchMode(const String8& cameraId, bool enabled); + status_t hasFlashUnit(const String8& cameraId, bool *hasFlash); + + private: + // connect to a camera device + status_t connectCameraDevice(const String8& cameraId); + + // disconnect and free mDevice + status_t disconnectCameraDevice(); + + // initialize the preview window + status_t initializePreviewWindow(sp<CameraHardwareInterface> device, + int32_t width, int32_t height); + + // start preview and enable torch + status_t startPreviewAndTorch(); + + // get the smallest surface + status_t getSmallestSurfaceSize(int32_t *width, int32_t *height); + + // protected by mLock + status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash); + + CameraModule *mCameraModule; + const camera_module_callbacks_t *mCallbacks; + sp<CameraHardwareInterface> mDevice; + String8 mCameraId; + CameraParameters mParameters; + bool mTorchEnabled; + + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; + sp<GLConsumer> mSurfaceTexture; + sp<ANativeWindow> mAnw; + + Mutex mLock; +}; + +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index fd5a426..55f7a40 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -17,9 +17,14 @@ #define LOG_TAG "CameraService" //#define LOG_NDEBUG 0 +#include <algorithm> +#include <climits> #include <stdio.h> -#include <string.h> +#include <cstring> +#include <ctime> +#include <string> #include <sys/types.h> +#include <inttypes.h> #include <pthread.h> #include <binder/AppOpsManager.h> @@ -27,8 +32,10 @@ #include <binder/IServiceManager.h> #include <binder/MemoryBase.h> #include <binder/MemoryHeapBase.h> +#include <binder/ProcessInfoService.h> #include <cutils/atomic.h> #include <cutils/properties.h> +#include <cutils/multiuser.h> #include <gui/Surface.h> #include <hardware/hardware.h> #include <media/AudioSystem.h> @@ -66,25 +73,48 @@ static void setLogLevel(int level) { // ---------------------------------------------------------------------------- -static int getCallingPid() { - return IPCThreadState::self()->getCallingPid(); -} - -static int getCallingUid() { - return IPCThreadState::self()->getCallingUid(); -} - extern "C" { static void camera_device_status_change( const struct camera_module_callbacks* callbacks, int camera_id, int new_status) { sp<CameraService> cs = const_cast<CameraService*>( + static_cast<const CameraService*>(callbacks)); + + cs->onDeviceStatusChanged(static_cast<camera_device_status_t>(camera_id), + static_cast<camera_device_status_t>(new_status)); +} + +static void torch_mode_status_change( + const struct camera_module_callbacks* callbacks, + const char* camera_id, + int new_status) { + if (!callbacks || !camera_id) { + ALOGE("%s invalid parameters. callbacks %p, camera_id %p", __FUNCTION__, + callbacks, camera_id); + } + sp<CameraService> cs = const_cast<CameraService*>( static_cast<const CameraService*>(callbacks)); - cs->onDeviceStatusChanged( - camera_id, - new_status); + ICameraServiceListener::TorchStatus status; + switch (new_status) { + case TORCH_MODE_STATUS_NOT_AVAILABLE: + status = ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE; + break; + case TORCH_MODE_STATUS_AVAILABLE_OFF: + status = ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF; + break; + case TORCH_MODE_STATUS_AVAILABLE_ON: + status = ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON; + break; + default: + ALOGE("Unknown torch status %d", new_status); + return; + } + + cs->onTorchStatusChanged( + String8(camera_id), + status); } } // extern "C" @@ -95,49 +125,90 @@ static void camera_device_status_change( static CameraService *gCameraService; CameraService::CameraService() - :mSoundRef(0), mModule(0) + : mEventLog(DEFAULT_EVICTION_LOG_LENGTH), mSoundRef(0), mModule(0), mFlashlight(0) { ALOGI("CameraService started (pid=%d)", getpid()); gCameraService = this; - for (size_t i = 0; i < MAX_CAMERAS; ++i) { - mStatusList[i] = ICameraServiceListener::STATUS_PRESENT; - } - this->camera_device_status_change = android::camera_device_status_change; + this->torch_mode_status_change = android::torch_mode_status_change; + + mServiceLockWrapper = std::make_shared<WaitableMutexWrapper>(&mServiceLock); } void CameraService::onFirstRef() { - LOG1("CameraService::onFirstRef"); + ALOGI("CameraService process starting"); BnCameraService::onFirstRef(); + camera_module_t *rawModule; if (hw_get_module(CAMERA_HARDWARE_MODULE_ID, - (const hw_module_t **)&mModule) < 0) { + (const hw_module_t **)&rawModule) < 0) { ALOGE("Could not load camera HAL module"); mNumberOfCameras = 0; } else { - ALOGI("Loaded \"%s\" camera module", mModule->common.name); - mNumberOfCameras = mModule->get_number_of_cameras(); - if (mNumberOfCameras > MAX_CAMERAS) { - ALOGE("Number of cameras(%d) > MAX_CAMERAS(%d).", - mNumberOfCameras, MAX_CAMERAS); - mNumberOfCameras = MAX_CAMERAS; + mModule = new CameraModule(rawModule); + const hw_module_t *common = mModule->getRawModule(); + ALOGI("Loaded \"%s\" camera module", common->name); + mNumberOfCameras = mModule->getNumberOfCameras(); + + mFlashlight = new CameraFlashlight(*mModule, *this); + status_t res = mFlashlight->findFlashUnits(); + if (res) { + // impossible because we haven't open any camera devices. + ALOGE("Failed to find flash units."); } + for (int i = 0; i < mNumberOfCameras; i++) { - setCameraFree(i); + String8 cameraId = String8::format("%d", i); + + // Defaults to use for cost and conflicting devices + int cost = 100; + char** conflicting_devices = nullptr; + size_t conflicting_devices_length = 0; + + // If using post-2.4 module version, query the cost + conflicting devices from the HAL + if (common->module_api_version >= CAMERA_MODULE_API_VERSION_2_4) { + struct camera_info info; + status_t rc = mModule->getCameraInfo(i, &info); + if (rc == NO_ERROR) { + cost = info.resource_cost; + conflicting_devices = info.conflicting_devices; + conflicting_devices_length = info.conflicting_devices_length; + } else { + ALOGE("%s: Received error loading camera info for device %d, cost and" + " conflicting devices fields set to defaults for this device.", + __FUNCTION__, i); + } + } + + std::set<String8> conflicting; + for (size_t i = 0; i < conflicting_devices_length; i++) { + conflicting.emplace(String8(conflicting_devices[i])); + } + + // Initialize state for each camera device + { + Mutex::Autolock lock(mCameraStatesLock); + mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost, + conflicting)); + } + + if (mFlashlight->hasFlashUnit(cameraId)) { + mTorchStatusMap.add(cameraId, + ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF); + } } - if (mModule->common.module_api_version >= - CAMERA_MODULE_API_VERSION_2_1) { - mModule->set_callbacks(this); + if (common->module_api_version >= CAMERA_MODULE_API_VERSION_2_1) { + mModule->setCallbacks(this); } VendorTagDescriptor::clearGlobalVendorTagDescriptor(); - if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_2) { + if (common->module_api_version >= CAMERA_MODULE_API_VERSION_2_2) { setUpVendorTags(); } @@ -146,80 +217,111 @@ void CameraService::onFirstRef() } CameraService::~CameraService() { - for (int i = 0; i < mNumberOfCameras; i++) { - if (mBusy[i]) { - ALOGE("camera %d is still in use in destructor!", i); - } + if (mModule) { + delete mModule; + mModule = nullptr; } - VendorTagDescriptor::clearGlobalVendorTagDescriptor(); - gCameraService = NULL; + gCameraService = nullptr; } -void CameraService::onDeviceStatusChanged(int cameraId, - int newStatus) -{ +void CameraService::onDeviceStatusChanged(camera_device_status_t cameraId, + camera_device_status_t newStatus) { ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__, cameraId, newStatus); - if (cameraId < 0 || cameraId >= MAX_CAMERAS) { + String8 id = String8::format("%d", cameraId); + std::shared_ptr<CameraState> state = getCameraState(id); + + if (state == nullptr) { ALOGE("%s: Bad camera ID %d", __FUNCTION__, cameraId); return; } - if ((int)getStatus(cameraId) == newStatus) { - ALOGE("%s: State transition to the same status 0x%x not allowed", - __FUNCTION__, (uint32_t)newStatus); + ICameraServiceListener::Status oldStatus = state->getStatus(); + + if (oldStatus == static_cast<ICameraServiceListener::Status>(newStatus)) { + ALOGE("%s: State transition to the same status %#x not allowed", __FUNCTION__, newStatus); return; } - /* don't do this in updateStatus - since it is also called from connect and we could get into a deadlock */ if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) { - Vector<sp<BasicClient> > clientsToDisconnect; + sp<BasicClient> clientToDisconnect; { - Mutex::Autolock al(mServiceLock); - - /* Remove cached parameters from shim cache */ - mShimParams.removeItem(cameraId); - - /* Find all clients that we need to disconnect */ - sp<BasicClient> client = mClient[cameraId].promote(); - if (client.get() != NULL) { - clientsToDisconnect.push_back(client); - } - - int i = cameraId; - for (size_t j = 0; j < mProClientList[i].size(); ++j) { - sp<ProClient> cl = mProClientList[i][j].promote(); - if (cl != NULL) { - clientsToDisconnect.push_back(cl); - } - } - } + // Don't do this in updateStatus to avoid deadlock over mServiceLock + Mutex::Autolock lock(mServiceLock); + + // Set the device status to NOT_PRESENT, clients will no longer be able to connect + // to this device until the status changes + updateStatus(ICameraServiceListener::STATUS_NOT_PRESENT, id); + + // Remove cached shim parameters + state->setShimParams(CameraParameters()); + + // Remove the client from the list of active clients + clientToDisconnect = removeClientLocked(id); - /* now disconnect them. don't hold the lock - or we can get into a deadlock */ + // Notify the client of disconnection + clientToDisconnect->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, + CaptureResultExtras{}); + } - for (size_t i = 0; i < clientsToDisconnect.size(); ++i) { - sp<BasicClient> client = clientsToDisconnect[i]; + ALOGI("%s: Client for camera ID %s evicted due to device status change from HAL", + __FUNCTION__, id.string()); - client->disconnect(); - /** - * The remote app will no longer be able to call methods on the - * client since the client PID will be reset to 0 - */ + // Disconnect client + if (clientToDisconnect.get() != nullptr) { + // Ensure not in binder RPC so client disconnect PID checks work correctly + LOG_ALWAYS_FATAL_IF(getCallingPid() != getpid(), + "onDeviceStatusChanged must be called from the camera service process!"); + clientToDisconnect->disconnect(); } - ALOGV("%s: After unplug, disconnected %zu clients", - __FUNCTION__, clientsToDisconnect.size()); + } else { + updateStatus(static_cast<ICameraServiceListener::Status>(newStatus), id); + } + +} + +void CameraService::onTorchStatusChanged(const String8& cameraId, + ICameraServiceListener::TorchStatus newStatus) { + Mutex::Autolock al(mTorchStatusMutex); + onTorchStatusChangedLocked(cameraId, newStatus); +} + +void CameraService::onTorchStatusChangedLocked(const String8& cameraId, + ICameraServiceListener::TorchStatus newStatus) { + ALOGI("%s: Torch status changed for cameraId=%s, newStatus=%d", + __FUNCTION__, cameraId.string(), newStatus); + + ICameraServiceListener::TorchStatus status; + status_t res = getTorchStatusLocked(cameraId, &status); + if (res) { + ALOGE("%s: cannot get torch status of camera %s", cameraId.string()); + return; + } + if (status == newStatus) { + ALOGE("%s: Torch state transition to the same status 0x%x not allowed", + __FUNCTION__, (uint32_t)newStatus); + return; } - updateStatus( - static_cast<ICameraServiceListener::Status>(newStatus), cameraId); + res = setTorchStatusLocked(cameraId, newStatus); + if (res) { + ALOGE("%s: Failed to set the torch status", __FUNCTION__, + (uint32_t)newStatus); + return; + } + { + Mutex::Autolock lock(mStatusListenerLock); + for (auto& i : mListenerList) { + i->onTorchStatusChanged(newStatus, String16{cameraId}); + } + } } + int32_t CameraService::getNumberOfCameras() { return mNumberOfCameras; } @@ -236,12 +338,21 @@ status_t CameraService::getCameraInfo(int cameraId, struct camera_info info; status_t rc = filterGetInfoErrorCode( - mModule->get_camera_info(cameraId, &info)); + mModule->getCameraInfo(cameraId, &info)); cameraInfo->facing = info.facing; cameraInfo->orientation = info.orientation; return rc; } +int CameraService::cameraIdToInt(const String8& cameraId) { + errno = 0; + size_t pos = 0; + int ret = stoi(std::string{cameraId.string()}, &pos); + if (errno != 0 || pos != cameraId.size()) { + return -1; + } + return ret; +} status_t CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) { status_t ret = OK; @@ -347,7 +458,7 @@ status_t CameraService::getCameraCharacteristics(int cameraId, int facing; status_t ret = OK; - if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_0 || + if (mModule->getRawModule()->module_api_version < CAMERA_MODULE_API_VERSION_2_0 || getDeviceVersion(cameraId, &facing) <= CAMERA_DEVICE_API_VERSION_2_1 ) { /** * Backwards compatibility mode for old HALs: @@ -368,13 +479,61 @@ status_t CameraService::getCameraCharacteristics(int cameraId, * Normal HAL 2.1+ codepath. */ struct camera_info info; - ret = filterGetInfoErrorCode(mModule->get_camera_info(cameraId, &info)); + ret = filterGetInfoErrorCode(mModule->getCameraInfo(cameraId, &info)); *cameraInfo = info.static_camera_characteristics; } return ret; } +int CameraService::getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + +int CameraService::getCallingUid() { + return IPCThreadState::self()->getCallingUid(); +} + +String8 CameraService::getFormattedCurrentTime() { + time_t now = time(nullptr); + char formattedTime[64]; + strftime(formattedTime, sizeof(formattedTime), "%m-%d %H:%M:%S", localtime(&now)); + return String8(formattedTime); +} + +int CameraService::getCameraPriorityFromProcState(int procState) { + // Find the priority for the camera usage based on the process state. Higher priority clients + // win for evictions. + // Note: Unlike the ordering for ActivityManager, persistent system processes will always lose + // the camera to the top/foreground applications. + switch(procState) { + case PROCESS_STATE_TOP: // User visible + return 100; + case PROCESS_STATE_IMPORTANT_FOREGROUND: // Foreground + return 90; + case PROCESS_STATE_PERSISTENT: // Persistent system services + case PROCESS_STATE_PERSISTENT_UI: + return 80; + case PROCESS_STATE_IMPORTANT_BACKGROUND: // "Important" background processes + return 70; + case PROCESS_STATE_BACKUP: // Everything else + case PROCESS_STATE_HEAVY_WEIGHT: + case PROCESS_STATE_SERVICE: + case PROCESS_STATE_RECEIVER: + case PROCESS_STATE_HOME: + case PROCESS_STATE_LAST_ACTIVITY: + case PROCESS_STATE_CACHED_ACTIVITY: + case PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + case PROCESS_STATE_CACHED_EMPTY: + return 1; + case PROCESS_STATE_NONEXISTENT: + return -1; + default: + ALOGE("%s: Received unknown process state from ActivityManagerService!", __FUNCTION__); + return -1; + } +} + status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) { if (!mModule) { ALOGE("%s: camera hardware module doesn't exist", __FUNCTION__); @@ -387,12 +546,12 @@ status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescript int CameraService::getDeviceVersion(int cameraId, int* facing) { struct camera_info info; - if (mModule->get_camera_info(cameraId, &info) != OK) { + if (mModule->getCameraInfo(cameraId, &info) != OK) { return -1; } int deviceVersion; - if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_0) { + if (mModule->getRawModule()->module_api_version >= CAMERA_MODULE_API_VERSION_2_0) { deviceVersion = info.device_version; } else { deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; @@ -405,19 +564,6 @@ int CameraService::getDeviceVersion(int cameraId, int* facing) { return deviceVersion; } -status_t CameraService::filterOpenErrorCode(status_t err) { - switch(err) { - case NO_ERROR: - case -EBUSY: - case -EINVAL: - case -EUSERS: - return err; - default: - break; - } - return -ENODEV; -} - status_t CameraService::filterGetInfoErrorCode(status_t err) { switch(err) { case NO_ERROR: @@ -433,13 +579,13 @@ 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) { + if (!mModule->isVendorTagDefined()) { 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); + mModule->getVendorTagOps(&vOps); ATRACE_END(); // Ensure all vendor operations are present @@ -467,54 +613,90 @@ bool CameraService::setUpVendorTags() { return true; } -status_t CameraService::initializeShimMetadata(int cameraId) { - int pid = getCallingPid(); - int uid = getCallingUid(); - status_t ret = validateConnect(cameraId, uid); - if (ret != OK) { - // Error already logged by callee - return ret; - } +status_t CameraService::makeClient(const sp<CameraService>& cameraService, + const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId, + int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode, + int halVersion, int deviceVersion, apiLevel effectiveApiLevel, + /*out*/sp<BasicClient>* client) { - bool needsNewClient = false; - sp<Client> client; + // TODO: Update CameraClients + HAL interface to use strings for Camera IDs + int id = cameraIdToInt(cameraId); + if (id == -1) { + ALOGE("%s: Invalid camera ID %s, cannot convert to integer.", __FUNCTION__, + cameraId.string()); + return BAD_VALUE; + } - String16 internalPackageName("media"); - { // Scope for service lock - Mutex::Autolock lock(mServiceLock); - if (mClient[cameraId] != NULL) { - client = static_cast<Client*>(mClient[cameraId].promote().get()); - } - if (client == NULL) { - needsNewClient = true; - ret = connectHelperLocked(/*out*/client, - /*cameraClient*/NULL, // Empty binder callbacks - cameraId, - internalPackageName, - uid, - pid); - - if (ret != OK) { - // Error already logged by callee - return ret; + if (halVersion < 0 || halVersion == deviceVersion) { + // Default path: HAL version is unspecified by caller, create CameraClient + // based on device version reported by the HAL. + switch(deviceVersion) { + case CAMERA_DEVICE_API_VERSION_1_0: + if (effectiveApiLevel == API_1) { // Camera1 API route + sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get()); + *client = new CameraClient(cameraService, tmp, packageName, id, facing, + clientPid, clientUid, getpid(), legacyMode); + } else { // Camera2 API route + ALOGW("Camera using old HAL version: %d", deviceVersion); + return -EOPNOTSUPP; + } + break; + 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: + case CAMERA_DEVICE_API_VERSION_3_3: + if (effectiveApiLevel == API_1) { // Camera1 API route + sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get()); + *client = new Camera2Client(cameraService, tmp, packageName, id, facing, + clientPid, clientUid, servicePid, legacyMode); + } else { // Camera2 API route + sp<ICameraDeviceCallbacks> tmp = + static_cast<ICameraDeviceCallbacks*>(cameraCb.get()); + *client = new CameraDeviceClient(cameraService, tmp, packageName, id, + facing, clientPid, clientUid, servicePid); } + break; + default: + // Should not be reachable + ALOGE("Unknown camera device HAL version: %d", deviceVersion); + return INVALID_OPERATION; } - - if (client == NULL) { - ALOGE("%s: Could not connect to client camera device.", __FUNCTION__); - return BAD_VALUE; + } else { + // A particular HAL version is requested by caller. Create CameraClient + // based on the requested HAL version. + if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 && + halVersion == CAMERA_DEVICE_API_VERSION_1_0) { + // Only support higher HAL version device opened as HAL1.0 device. + sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get()); + *client = new CameraClient(cameraService, tmp, packageName, id, facing, + clientPid, clientUid, servicePid, legacyMode); + } else { + // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet. + ALOGE("Invalid camera HAL version %x: HAL %x device can only be" + " opened as HAL %x device", halVersion, deviceVersion, + CAMERA_DEVICE_API_VERSION_1_0); + return INVALID_OPERATION; } - - String8 rawParams = client->getParameters(); - CameraParameters params(rawParams); - mShimParams.add(cameraId, params); } + return NO_ERROR; +} + +status_t CameraService::initializeShimMetadata(int cameraId) { + int uid = getCallingUid(); - // Close client if one was opened solely for this call - if (needsNewClient) { - client->disconnect(); + String16 internalPackageName("media"); + String8 id = String8::format("%d", cameraId); + status_t ret = NO_ERROR; + sp<Client> tmp = nullptr; + if ((ret = connectHelper<ICameraClient,Client>(sp<ICameraClient>{nullptr}, id, + static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED), internalPackageName, uid, API_1, + false, true, tmp)) != NO_ERROR) { + ALOGE("%s: Error %d (%s) initializing shim metadata.", __FUNCTION__, ret, strerror(ret)); + return ret; } - return OK; + return NO_ERROR; } status_t CameraService::getLegacyParametersLazy(int cameraId, @@ -530,42 +712,54 @@ status_t CameraService::getLegacyParametersLazy(int cameraId, return BAD_VALUE; } - ssize_t index = -1; - { // Scope for service lock - Mutex::Autolock lock(mServiceLock); - index = mShimParams.indexOfKey(cameraId); - // Release service lock so initializeShimMetadata can be called correctly. + String8 id = String8::format("%d", cameraId); - if (index >= 0) { - *parameters = mShimParams[index]; + // Check if we already have parameters + { + // Scope for service lock + Mutex::Autolock lock(mServiceLock); + auto cameraState = getCameraState(id); + if (cameraState == nullptr) { + ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string()); + return BAD_VALUE; } - } - - if (index < 0) { - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - ret = initializeShimMetadata(cameraId); - IPCThreadState::self()->restoreCallingIdentity(token); - if (ret != OK) { - // Error already logged by callee - return ret; + CameraParameters p = cameraState->getShimParams(); + if (!p.isEmpty()) { + *parameters = p; + return NO_ERROR; } + } - { // Scope for service lock - Mutex::Autolock lock(mServiceLock); - index = mShimParams.indexOfKey(cameraId); - - LOG_ALWAYS_FATAL_IF(index < 0, "index should have been initialized"); + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + ret = initializeShimMetadata(cameraId); + IPCThreadState::self()->restoreCallingIdentity(token); + if (ret != NO_ERROR) { + // Error already logged by callee + return ret; + } - *parameters = mShimParams[index]; + // Check for parameters again + { + // Scope for service lock + Mutex::Autolock lock(mServiceLock); + auto cameraState = getCameraState(id); + if (cameraState == nullptr) { + ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string()); + return BAD_VALUE; + } + CameraParameters p = cameraState->getShimParams(); + if (!p.isEmpty()) { + *parameters = p; + return NO_ERROR; } } - return OK; + ALOGE("%s: Parameters were not initialized, or were empty. Device may not be present.", + __FUNCTION__); + return INVALID_OPERATION; } -status_t CameraService::validateConnect(int cameraId, - /*inout*/ - int& clientUid) const { +status_t CameraService::validateConnect(const String8& cameraId, /*inout*/int& clientUid) const { int callingPid = getCallingPid(); @@ -574,160 +768,244 @@ status_t CameraService::validateConnect(int cameraId, } else { // We only trust our own process to forward client UIDs if (callingPid != getpid()) { - ALOGE("CameraService::connect X (pid %d) rejected (don't trust clientUid)", + ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)", callingPid); return PERMISSION_DENIED; } } if (!mModule) { - ALOGE("Camera HAL module not loaded"); + ALOGE("CameraService::connect X (PID %d) rejected (camera HAL module not loaded)", + callingPid); return -ENODEV; } - if (cameraId < 0 || cameraId >= mNumberOfCameras) { - ALOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).", - callingPid, cameraId); + if (getCameraState(cameraId) == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid, + cameraId.string()); return -ENODEV; } + // Check device policy for this camera char value[PROPERTY_VALUE_MAX]; - property_get("sys.secpolicy.camera.disabled", value, "0"); + char key[PROPERTY_KEY_MAX]; + int clientUserId = multiuser_get_user_id(clientUid); + snprintf(key, PROPERTY_KEY_MAX, "sys.secpolicy.camera.off_%d", clientUserId); + property_get(key, value, "0"); if (strcmp(value, "1") == 0) { // Camera is disabled by DevicePolicyManager. - ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid); + ALOGE("CameraService::connect X (PID %d) rejected (camera %s is disabled by device " + "policy)", callingPid, cameraId.string()); return -EACCES; } - ICameraServiceListener::Status currentStatus = getStatus(cameraId); + return checkIfDeviceIsUsable(cameraId); +} + +status_t CameraService::checkIfDeviceIsUsable(const String8& cameraId) const { + auto cameraState = getCameraState(cameraId); + int callingPid = getCallingPid(); + if (cameraState == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid, + cameraId.string()); + return -ENODEV; + } + + ICameraServiceListener::Status currentStatus = cameraState->getStatus(); if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) { - ALOGI("Camera is not plugged in," - " connect X (pid %d) rejected", callingPid); + ALOGE("CameraService::connect X (PID %d) rejected (camera %s is not connected)", + callingPid, cameraId.string()); return -ENODEV; } else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) { - ALOGI("Camera is enumerating," - " connect X (pid %d) rejected", callingPid); + ALOGE("CameraService::connect X (PID %d) rejected, (camera %s is initializing)", + callingPid, cameraId.string()); return -EBUSY; } - // Else don't check for STATUS_NOT_AVAILABLE. - // -- It's done implicitly in canConnectUnsafe /w the mBusy array - return OK; + return NO_ERROR; } -bool CameraService::canConnectUnsafe(int cameraId, - const String16& clientPackageName, - const sp<IBinder>& remoteCallback, - sp<BasicClient> &client) { - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); +void CameraService::finishConnectLocked(const sp<BasicClient>& client, + const CameraService::DescriptorPtr& desc) { - if (mClient[cameraId] != 0) { - client = mClient[cameraId].promote(); - if (client != 0) { - if (remoteCallback == client->getRemote()) { - LOG1("CameraService::connect X (pid %d) (the same client)", - callingPid); - return true; - } else { - // TODOSC: need to support 1 regular client, - // multiple shared clients here - ALOGW("CameraService::connect X (pid %d) rejected" - " (existing client).", callingPid); - return false; - } + // Make a descriptor for the incoming client + auto clientDescriptor = CameraService::CameraClientManager::makeClientDescriptor(client, desc); + auto evicted = mActiveClientManager.addAndEvict(clientDescriptor); + + logConnected(desc->getKey(), static_cast<int>(desc->getOwnerId()), + String8(client->getPackageName())); + + if (evicted.size() > 0) { + // This should never happen - clients should already have been removed in disconnect + for (auto& i : evicted) { + ALOGE("%s: Invalid state: Client for camera %s was not removed in disconnect", + __FUNCTION__, i->getKey().string()); } - mClient[cameraId].clear(); - } - - /* - mBusy is set to false as the last step of the Client destructor, - after which it is guaranteed that the Client destructor has finished ( - including any inherited destructors) - - We only need this for a Client subclasses since we don't allow - multiple Clents to be opened concurrently, but multiple BasicClient - would be fine - */ - if (mBusy[cameraId]) { - ALOGW("CameraService::connect X (pid %d, \"%s\") rejected" - " (camera %d is still busy).", callingPid, - clientName8.string(), cameraId); - return false; - } - return true; + LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, clients not evicted properly", + __FUNCTION__); + } } -status_t CameraService::connectHelperLocked( +status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid, + apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName, /*out*/ - sp<Client>& client, - /*in*/ - const sp<ICameraClient>& cameraClient, - int cameraId, - const String16& clientPackageName, - int clientUid, - int callingPid, - int halVersion, - bool legacyMode) { + sp<BasicClient>* client, + std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) { - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); + status_t ret = NO_ERROR; + std::vector<sp<BasicClient>> evictedClients; + DescriptorPtr clientDescriptor; + { + if (effectiveApiLevel == API_1) { + // If we are using API1, any existing client for this camera ID with the same remote + // should be returned rather than evicted to allow MediaRecorder to work properly. + + auto current = mActiveClientManager.get(cameraId); + if (current != nullptr) { + auto clientSp = current->getValue(); + if (clientSp.get() != nullptr) { // should never be needed + if (clientSp->getRemote() == remoteCallback) { + ALOGI("CameraService::connect X (PID %d) (second call from same" + "app binder, returning the same client)", clientPid); + *client = clientSp; + return NO_ERROR; + } + } + } + } - if (halVersion < 0 || halVersion == deviceVersion) { - // Default path: HAL version is unspecified by caller, create CameraClient - // based on device version reported by the HAL. - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - client = new CameraClient(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid(), legacyMode); - break; - 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(), legacyMode); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); + // Return error if the device was unplugged or removed by the HAL for some reason + if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) { + return ret; + } + + // Get current active client PIDs + std::vector<int> ownerPids(mActiveClientManager.getAllOwners()); + ownerPids.push_back(clientPid); + + // Use the value +PROCESS_STATE_NONEXISTENT, to avoid taking + // address of PROCESS_STATE_NONEXISTENT as a reference argument + // for the vector constructor. PROCESS_STATE_NONEXISTENT does + // not have an out-of-class definition. + std::vector<int> priorities(ownerPids.size(), +PROCESS_STATE_NONEXISTENT); + + // Get priorites of all active PIDs + ProcessInfoService::getProcessStatesFromPids(ownerPids.size(), &ownerPids[0], + /*out*/&priorities[0]); + + // Update all active clients' priorities + std::map<int,int> pidToPriorityMap; + for (size_t i = 0; i < ownerPids.size() - 1; i++) { + pidToPriorityMap.emplace(ownerPids[i], getCameraPriorityFromProcState(priorities[i])); + } + mActiveClientManager.updatePriorities(pidToPriorityMap); + + // Get state for the given cameraId + auto state = getCameraState(cameraId); + if (state == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (no camera device with ID %s)", + clientPid, cameraId.string()); return BAD_VALUE; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return INVALID_OPERATION; } - } else { - // A particular HAL version is requested by caller. Create CameraClient - // based on the requested HAL version. - if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 && - halVersion == CAMERA_DEVICE_API_VERSION_1_0) { - // Only support higher HAL version device opened as HAL1.0 device. - client = new CameraClient(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid(), legacyMode); - } else { - // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet. - ALOGE("Invalid camera HAL version %x: HAL %x device can only be" - " opened as HAL %x device", halVersion, deviceVersion, - CAMERA_DEVICE_API_VERSION_1_0); - return INVALID_OPERATION; + + // Make descriptor for incoming client + clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId, + sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()), + state->getConflicting(), + getCameraPriorityFromProcState(priorities[priorities.size() - 1]), clientPid); + + // Find clients that would be evicted + auto evicted = mActiveClientManager.wouldEvict(clientDescriptor); + + // If the incoming client was 'evicted,' higher priority clients have the camera in the + // background, so we cannot do evictions + if (std::find(evicted.begin(), evicted.end(), clientDescriptor) != evicted.end()) { + ALOGE("CameraService::connect X (PID %d) rejected (existing client(s) with higher" + " priority).", clientPid); + + sp<BasicClient> clientSp = clientDescriptor->getValue(); + String8 curTime = getFormattedCurrentTime(); + auto incompatibleClients = + mActiveClientManager.getIncompatibleClients(clientDescriptor); + + String8 msg = String8::format("%s : DENIED connect device %s client for package %s " + "(PID %d, priority %d)", curTime.string(), + cameraId.string(), packageName.string(), clientPid, + getCameraPriorityFromProcState(priorities[priorities.size() - 1])); + + for (auto& i : incompatibleClients) { + msg.appendFormat("\n - Blocked by existing device %s client for package %s" + "(PID %" PRId32 ", priority %" PRId32 ")", i->getKey().string(), + String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(), + i->getPriority()); + } + + // Log the client's attempt + mEventLog.add(msg); + + return -EBUSY; + } + + for (auto& i : evicted) { + sp<BasicClient> clientSp = i->getValue(); + if (clientSp.get() == nullptr) { + ALOGE("%s: Invalid state: Null client in active client list.", __FUNCTION__); + + // TODO: Remove this + LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, null client in active list", + __FUNCTION__); + mActiveClientManager.remove(i); + continue; + } + + ALOGE("CameraService::connect evicting conflicting client for camera ID %s", + i->getKey().string()); + evictedClients.push_back(clientSp); + + String8 curTime = getFormattedCurrentTime(); + + // Log the clients evicted + mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %" + PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for " + "package %s (PID %d, priority %" PRId32 ")", curTime.string(), + i->getKey().string(), String8{clientSp->getPackageName()}.string(), + i->getOwnerId(), i->getPriority(), cameraId.string(), + packageName.string(), clientPid, + getCameraPriorityFromProcState(priorities[priorities.size() - 1]))); + + // Notify the client of disconnection + clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, + CaptureResultExtras()); } } - status_t status = connectFinishUnsafe(client, client->getRemote()); - if (status != OK) { - // this is probably not recoverable.. maybe the client can try again - return status; + // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking + // other clients from connecting in mServiceLockWrapper if held + mServiceLock.unlock(); + + // Clear caller identity temporarily so client disconnect PID checks work correctly + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + + // Destroy evicted clients + for (auto& i : evictedClients) { + // Disconnect is blocking, and should only have returned when HAL has cleaned up + i->disconnect(); // Clients will remove themselves from the active client list here } + evictedClients.clear(); - mClient[cameraId] = client; - LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, - getpid()); + IPCThreadState::self()->restoreCallingIdentity(token); - return OK; + // Once clients have been disconnected, relock + mServiceLock.lock(); + + // Check again if the device was unplugged or something while we weren't holding mServiceLock + if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) { + return ret; + } + + *partial = clientDescriptor; + return NO_ERROR; } status_t CameraService::connect( @@ -738,47 +1016,18 @@ status_t CameraService::connect( /*out*/ sp<ICamera>& device) { - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); - - LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; - } - - - sp<Client> client; - { - Mutex::Autolock lock(mServiceLock); - sp<BasicClient> clientTmp; - if (!canConnectUnsafe(cameraId, clientPackageName, - cameraClient->asBinder(), - /*out*/clientTmp)) { - return -EBUSY; - } else if (client.get() != NULL) { - device = static_cast<Client*>(clientTmp.get()); - return OK; - } - - status = connectHelperLocked(/*out*/client, - cameraClient, - cameraId, - clientPackageName, - clientUid, - callingPid); - if (status != OK) { - return status; - } + status_t ret = NO_ERROR; + String8 id = String8::format("%d", cameraId); + sp<Client> client = nullptr; + ret = connectHelper<ICameraClient,Client>(cameraClient, id, CAMERA_HAL_API_VERSION_UNSPECIFIED, + clientPackageName, clientUid, API_1, false, false, /*out*/client); + if(ret != NO_ERROR) { + return ret; } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) device = client; - return OK; + return NO_ERROR; } status_t CameraService::connectLegacy( @@ -789,8 +1038,9 @@ status_t CameraService::connectLegacy( /*out*/ sp<ICamera>& device) { + int apiVersion = mModule->getRawModule()->module_api_version; if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED && - mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_3) { + apiVersion < CAMERA_MODULE_API_VERSION_2_3) { /* * Either the HAL version is unspecified in which case this just creates * a camera client selected by the latest device version, or @@ -798,218 +1048,133 @@ status_t CameraService::connectLegacy( * the open_legacy call */ ALOGE("%s: camera HAL module version %x doesn't support connecting to legacy HAL devices!", - __FUNCTION__, mModule->common.module_api_version); + __FUNCTION__, apiVersion); return INVALID_OPERATION; } - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); - - LOG1("CameraService::connect legacy E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; - } - - sp<Client> client; - { - Mutex::Autolock lock(mServiceLock); - sp<BasicClient> clientTmp; - if (!canConnectUnsafe(cameraId, clientPackageName, - cameraClient->asBinder(), - /*out*/clientTmp)) { - return -EBUSY; - } else if (client.get() != NULL) { - device = static_cast<Client*>(clientTmp.get()); - return OK; - } - - status = connectHelperLocked(/*out*/client, - cameraClient, - cameraId, - clientPackageName, - clientUid, - callingPid, - halVersion, - /*legacyMode*/true); - if (status != OK) { - return status; - } + status_t ret = NO_ERROR; + String8 id = String8::format("%d", cameraId); + sp<Client> client = nullptr; + ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion, clientPackageName, + clientUid, API_1, true, false, /*out*/client); + if(ret != NO_ERROR) { + return ret; } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) device = client; - return OK; + return NO_ERROR; } -status_t CameraService::connectFinishUnsafe(const sp<BasicClient>& client, - const sp<IBinder>& remoteCallback) { - status_t status = client->initialize(mModule); - if (status != OK) { - return status; - } - if (remoteCallback != NULL) { - remoteCallback->linkToDeath(this); - } - - return OK; +status_t CameraService::connectPro(const sp<IProCameraCallbacks>& cameraCb, + int cameraId, + const String16& clientPackageName, + int clientUid, + /*out*/ + sp<IProCameraUser>& device) { + ALOGE("%s: Unimplemented, please use connectDevice", __FUNCTION__); + return INVALID_OPERATION; } -status_t CameraService::connectPro( - const sp<IProCameraCallbacks>& cameraCb, - int cameraId, - const String16& clientPackageName, - int clientUid, - /*out*/ - sp<IProCameraUser>& device) -{ - if (cameraCb == 0) { - ALOGE("%s: Callback must not be null", __FUNCTION__); - return BAD_VALUE; - } +status_t CameraService::connectDevice( + const sp<ICameraDeviceCallbacks>& cameraCb, + int cameraId, + const String16& clientPackageName, + int clientUid, + /*out*/ + sp<ICameraDeviceUser>& device) { - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); + status_t ret = NO_ERROR; + String8 id = String8::format("%d", cameraId); + sp<CameraDeviceClient> client = nullptr; + ret = connectHelper<ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id, + CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, API_2, false, false, + /*out*/client); - LOG1("CameraService::connectPro E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; + if(ret != NO_ERROR) { + return ret; } - sp<ProClient> client; - { - Mutex::Autolock lock(mServiceLock); - { - sp<BasicClient> client; - if (!canConnectUnsafe(cameraId, clientPackageName, - cameraCb->asBinder(), - /*out*/client)) { - return -EBUSY; - } - } - - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); - - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - ALOGE("Camera id %d uses HALv1, doesn't support ProCamera", - cameraId); - return -EOPNOTSUPP; - break; - 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, clientPackageName, - cameraId, facing, callingPid, clientUid, getpid()); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); - return BAD_VALUE; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return INVALID_OPERATION; - } - - status_t status = connectFinishUnsafe(client, client->getRemote()); - if (status != OK) { - return status; - } - - mProClientList[cameraId].push(client); - - LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId, - getpid()); - } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) device = client; - return OK; + return NO_ERROR; } -status_t CameraService::connectDevice( - const sp<ICameraDeviceCallbacks>& cameraCb, - int cameraId, - const String16& clientPackageName, - int clientUid, - /*out*/ - sp<ICameraDeviceUser>& device) -{ +status_t CameraService::setTorchMode(const String16& cameraId, bool enabled, + const sp<IBinder>& clientBinder) { + if (enabled && clientBinder == NULL) { + ALOGE("%s: torch client binder is NULL", __FUNCTION__); + return -EINVAL; + } - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); + String8 id = String8(cameraId.string()); - LOG1("CameraService::connectDevice E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); + // verify id is valid. + auto state = getCameraState(id); + if (state == nullptr) { + ALOGE("%s: camera id is invalid %s", id.string()); + return -EINVAL; + } - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; + ICameraServiceListener::Status cameraStatus = state->getStatus(); + if (cameraStatus != ICameraServiceListener::STATUS_PRESENT && + cameraStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) { + ALOGE("%s: camera id is invalid %s", id.string()); + return -EINVAL; } - sp<CameraDeviceClient> client; { - Mutex::Autolock lock(mServiceLock); - { - sp<BasicClient> client; - if (!canConnectUnsafe(cameraId, clientPackageName, - cameraCb->asBinder(), - /*out*/client)) { + Mutex::Autolock al(mTorchStatusMutex); + ICameraServiceListener::TorchStatus status; + status_t res = getTorchStatusLocked(id, &status); + if (res) { + ALOGE("%s: getting current torch status failed for camera %s", + __FUNCTION__, id.string()); + return -EINVAL; + } + + if (status == ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE) { + if (cameraStatus == ICameraServiceListener::STATUS_NOT_AVAILABLE) { + ALOGE("%s: torch mode of camera %s is not available because " + "camera is in use", __FUNCTION__, id.string()); return -EBUSY; + } else { + ALOGE("%s: torch mode of camera %s is not available due to " + "insufficient resources", __FUNCTION__, id.string()); + return -EUSERS; } } + } - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); + status_t res = mFlashlight->setTorchMode(id, enabled); + if (res) { + ALOGE("%s: setting torch mode of camera %s to %d failed. %s (%d)", + __FUNCTION__, id.string(), enabled, strerror(-res), res); + return res; + } - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - ALOGW("Camera using old HAL version: %d", deviceVersion); - return -EOPNOTSUPP; - // TODO: don't allow 2.0 Only allow 2.1 and higher - 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, clientPackageName, - cameraId, facing, callingPid, clientUid, getpid()); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); - return BAD_VALUE; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return INVALID_OPERATION; - } + { + // update the link to client's death + Mutex::Autolock al(mTorchClientMapMutex); + ssize_t index = mTorchClientMap.indexOfKey(id); + if (enabled) { + if (index == NAME_NOT_FOUND) { + mTorchClientMap.add(id, clientBinder); + } else { + const sp<IBinder> oldBinder = mTorchClientMap.valueAt(index); + oldBinder->unlinkToDeath(this); - status_t status = connectFinishUnsafe(client, client->getRemote()); - if (status != OK) { - // this is probably not recoverable.. maybe the client can try again - return status; + mTorchClientMap.replaceValueAt(index, clientBinder); + } + clientBinder->linkToDeath(this); + } else if (index != NAME_NOT_FOUND) { + sp<IBinder> oldBinder = mTorchClientMap.valueAt(index); + oldBinder->unlinkToDeath(this); } - - LOG1("CameraService::connectDevice X (id %d, this pid is %d)", cameraId, - getpid()); - - mClient[cameraId] = client; } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) - device = client; return OK; } - status_t CameraService::addListener( const sp<ICameraServiceListener>& listener) { ALOGV("%s: Add listener %p", __FUNCTION__, listener.get()); @@ -1021,30 +1186,45 @@ status_t CameraService::addListener( Mutex::Autolock lock(mServiceLock); - Vector<sp<ICameraServiceListener> >::iterator it, end; - for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { - if ((*it)->asBinder() == listener->asBinder()) { - ALOGW("%s: Tried to add listener %p which was already subscribed", - __FUNCTION__, listener.get()); - return ALREADY_EXISTS; + { + Mutex::Autolock lock(mStatusListenerLock); + for (auto& it : mListenerList) { + if (IInterface::asBinder(it) == IInterface::asBinder(listener)) { + ALOGW("%s: Tried to add listener %p which was already subscribed", + __FUNCTION__, listener.get()); + return ALREADY_EXISTS; + } } + + mListenerList.push_back(listener); } - mListenerList.push_back(listener); /* Immediately signal current status to this listener only */ { - Mutex::Autolock m(mStatusMutex) ; - int numCams = getNumberOfCameras(); - for (int i = 0; i < numCams; ++i) { - listener->onStatusChanged(mStatusList[i], i); + Mutex::Autolock lock(mCameraStatesLock); + for (auto& i : mCameraStates) { + // TODO: Update binder to use String16 for camera IDs and remove; + int id = cameraIdToInt(i.first); + if (id == -1) continue; + + listener->onStatusChanged(i.second->getStatus(), id); + } + } + + /* Immediately signal current torch status to this listener only */ + { + Mutex::Autolock al(mTorchStatusMutex); + for (size_t i = 0; i < mTorchStatusMap.size(); i++ ) { + String16 id = String16(mTorchStatusMap.keyAt(i).string()); + listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i), id); } } return OK; } -status_t CameraService::removeListener( - const sp<ICameraServiceListener>& listener) { + +status_t CameraService::removeListener(const sp<ICameraServiceListener>& listener) { ALOGV("%s: Remove listener %p", __FUNCTION__, listener.get()); if (listener == 0) { @@ -1054,11 +1234,13 @@ status_t CameraService::removeListener( Mutex::Autolock lock(mServiceLock); - Vector<sp<ICameraServiceListener> >::iterator it; - for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { - if ((*it)->asBinder() == listener->asBinder()) { - mListenerList.erase(it); - return OK; + { + Mutex::Autolock lock(mStatusListenerLock); + for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) { + if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { + mListenerList.erase(it); + return OK; + } } } @@ -1068,10 +1250,7 @@ status_t CameraService::removeListener( return BAD_VALUE; } -status_t CameraService::getLegacyParameters( - int cameraId, - /*out*/ - String16* parameters) { +status_t CameraService::getLegacyParameters(int cameraId, /*out*/String16* parameters) { ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId); if (parameters == NULL) { @@ -1126,6 +1305,7 @@ status_t CameraService::supportsCameraApi(int cameraId, int apiVersion) { return OK; } case CAMERA_DEVICE_API_VERSION_3_2: + case CAMERA_DEVICE_API_VERSION_3_3: ALOGV("%s: Camera id %d uses HAL3.2 or newer, supports api1/api2 directly", __FUNCTION__, cameraId); return OK; @@ -1140,127 +1320,107 @@ status_t CameraService::supportsCameraApi(int cameraId, int apiVersion) { return OK; } -void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) { - int callingPid = getCallingPid(); - LOG1("CameraService::removeClientByRemote E (pid %d)", callingPid); - - // Declare this before the lock to make absolutely sure the - // destructor won't be called with the lock held. +void CameraService::removeByClient(const BasicClient* client) { Mutex::Autolock lock(mServiceLock); - - int outIndex; - sp<BasicClient> client = findClientUnsafe(remoteBinder, outIndex); - - if (client != 0) { - // Found our camera, clear and leave. - LOG1("removeClient: clear camera %d", outIndex); - - sp<IBinder> remote = client->getRemote(); - if (remote != NULL) { - remote->unlinkToDeath(this); - } - - mClient[outIndex].clear(); - } else { - - sp<ProClient> clientPro = findProClientUnsafe(remoteBinder); - - if (clientPro != NULL) { - // Found our camera, clear and leave. - LOG1("removeClient: clear pro %p", clientPro.get()); - - clientPro->getRemoteCallback()->asBinder()->unlinkToDeath(this); + for (auto& i : mActiveClientManager.getAll()) { + auto clientSp = i->getValue(); + if (clientSp.get() == client) { + mActiveClientManager.remove(i); } } - - LOG1("CameraService::removeClientByRemote X (pid %d)", callingPid); } -sp<CameraService::ProClient> CameraService::findProClientUnsafe( - const wp<IBinder>& cameraCallbacksRemote) -{ - sp<ProClient> clientPro; - - for (int i = 0; i < mNumberOfCameras; ++i) { - Vector<size_t> removeIdx; +bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) { + const int callingPid = getCallingPid(); + const int servicePid = getpid(); + bool ret = false; + { + // Acquire mServiceLock and prevent other clients from connecting + std::unique_ptr<AutoConditionLock> lock = + AutoConditionLock::waitAndAcquire(mServiceLockWrapper); - for (size_t j = 0; j < mProClientList[i].size(); ++j) { - wp<ProClient> cl = mProClientList[i][j]; - sp<ProClient> clStrong = cl.promote(); - if (clStrong != NULL && clStrong->getRemote() == cameraCallbacksRemote) { - clientPro = clStrong; - break; - } else if (clStrong == NULL) { - // mark to clean up dead ptr - removeIdx.push(j); + std::vector<sp<BasicClient>> evicted; + for (auto& i : mActiveClientManager.getAll()) { + auto clientSp = i->getValue(); + if (clientSp.get() == nullptr) { + ALOGE("%s: Dead client still in mActiveClientManager.", __FUNCTION__); + mActiveClientManager.remove(i); + continue; + } + if (remote == clientSp->getRemote() && (callingPid == servicePid || + callingPid == clientSp->getClientPid())) { + mActiveClientManager.remove(i); + evicted.push_back(clientSp); + + // Notify the client of disconnection + clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, + CaptureResultExtras()); } } - // remove stale ptrs (in reverse so the indices dont change) - for (ssize_t j = (ssize_t)removeIdx.size() - 1; j >= 0; --j) { - mProClientList[i].removeAt(removeIdx[j]); - } - - } - - return clientPro; -} + // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking + // other clients from connecting in mServiceLockWrapper if held + mServiceLock.unlock(); -sp<CameraService::BasicClient> CameraService::findClientUnsafe( - const wp<IBinder>& cameraClient, int& outIndex) { - sp<BasicClient> client; + for (auto& i : evicted) { + if (i.get() != nullptr) { + i->disconnect(); + ret = true; + } + } - for (int i = 0; i < mNumberOfCameras; i++) { + // Reacquire mServiceLock + mServiceLock.lock(); - // This happens when we have already disconnected (or this is - // just another unused camera). - if (mClient[i] == 0) continue; + } // lock is destroyed, allow further connect calls - // Promote mClient. It can fail if we are called from this path: - // Client::~Client() -> disconnect() -> removeClientByRemote(). - client = mClient[i].promote(); + return ret; +} - // Clean up stale client entry - if (client == NULL) { - mClient[i].clear(); - continue; - } - if (cameraClient == client->getRemote()) { - // Found our camera - outIndex = i; - return client; +std::shared_ptr<CameraService::CameraState> CameraService::getCameraState( + const String8& cameraId) const { + std::shared_ptr<CameraState> state; + { + Mutex::Autolock lock(mCameraStatesLock); + auto iter = mCameraStates.find(cameraId); + if (iter != mCameraStates.end()) { + state = iter->second; } } - - outIndex = -1; - return NULL; + return state; } -CameraService::BasicClient* CameraService::getClientByIdUnsafe(int cameraId) { - if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL; - return mClient[cameraId].unsafe_get(); -} +sp<CameraService::BasicClient> CameraService::removeClientLocked(const String8& cameraId) { + // Remove from active clients list + auto clientDescriptorPtr = mActiveClientManager.remove(cameraId); + if (clientDescriptorPtr == nullptr) { + ALOGW("%s: Could not evict client, no client for camera ID %s", __FUNCTION__, + cameraId.string()); + return sp<BasicClient>{nullptr}; + } -Mutex* CameraService::getClientLockById(int cameraId) { - if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL; - return &mClientLock[cameraId]; + return clientDescriptorPtr->getValue(); } -sp<CameraService::BasicClient> CameraService::getClientByRemote( - const wp<IBinder>& cameraClient) { - // Declare this before the lock to make absolutely sure the - // destructor won't be called with the lock held. - sp<BasicClient> client; +void CameraService::logDisconnected(const String8& cameraId, int clientPid, + const String8& clientPackage) { - Mutex::Autolock lock(mServiceLock); + String8 curTime = getFormattedCurrentTime(); + // Log the clients evicted + mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)", + curTime.string(), cameraId.string(), clientPackage.string(), clientPid)); +} - int outIndex; - client = findClientUnsafe(cameraClient, outIndex); +void CameraService::logConnected(const String8& cameraId, int clientPid, + const String8& clientPackage) { - return client; + String8 curTime = getFormattedCurrentTime(); + // Log the clients evicted + mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)", + curTime.string(), cameraId.string(), clientPackage.string(), clientPid)); } status_t CameraService::onTransact( @@ -1289,24 +1449,6 @@ status_t CameraService::onTransact( return BnCameraService::onTransact(code, data, reply, flags); } -// The reason we need this busy bit is a new CameraService::connect() request -// may come in while the previous Client's destructor has not been run or is -// still running. If the last strong reference of the previous Client is gone -// but the destructor has not been finished, we should not allow the new Client -// to be created because we need to wait for the previous Client to tear down -// the hardware first. -void CameraService::setCameraBusy(int cameraId) { - android_atomic_write(1, &mBusy[cameraId]); - - ALOGV("setCameraBusy cameraId=%d", cameraId); -} - -void CameraService::setCameraFree(int cameraId) { - android_atomic_write(0, &mBusy[cameraId]); - - ALOGV("setCameraFree cameraId=%d", cameraId); -} - // We share the media players for shutter and recording sound for all clients. // A reference count is kept to determine when we will actually release the // media players. @@ -1363,7 +1505,8 @@ CameraService::Client::Client(const sp<CameraService>& cameraService, int cameraId, int cameraFacing, int clientPid, uid_t clientUid, int servicePid) : - CameraService::BasicClient(cameraService, cameraClient->asBinder(), + CameraService::BasicClient(cameraService, + IInterface::asBinder(cameraClient), clientPackageName, cameraId, cameraFacing, clientPid, clientUid, @@ -1374,7 +1517,6 @@ CameraService::Client::Client(const sp<CameraService>& cameraService, mRemoteCallback = cameraClient; - cameraService->setCameraBusy(cameraId); cameraService->loadSound(); LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId); @@ -1396,7 +1538,7 @@ CameraService::BasicClient::BasicClient(const sp<CameraService>& cameraService, int cameraId, int cameraFacing, int clientPid, uid_t clientUid, int servicePid): - mClientPackageName(clientPackageName) + mClientPackageName(clientPackageName), mDisconnected(false) { mCameraService = cameraService; mRemoteBinder = remoteCallback; @@ -1415,14 +1557,34 @@ CameraService::BasicClient::~BasicClient() { } void CameraService::BasicClient::disconnect() { - ALOGV("BasicClient::disconnect"); - mCameraService->removeClientByRemote(mRemoteBinder); + if (mDisconnected) return; + mDisconnected = true;; + + mCameraService->removeByClient(this); + mCameraService->logDisconnected(String8::format("%d", mCameraId), mClientPid, + String8(mClientPackageName)); + + sp<IBinder> remote = getRemote(); + if (remote != nullptr) { + remote->unlinkToDeath(mCameraService); + } finishCameraOps(); + ALOGI("%s: Disconnected client for camera %d for PID %d", __FUNCTION__, mCameraId, mClientPid); + // client shouldn't be able to call into us anymore mClientPid = 0; } +String16 CameraService::BasicClient::getPackageName() const { + return mClientPackageName; +} + + +int CameraService::BasicClient::getClientPid() const { + return mClientPid; +} + status_t CameraService::BasicClient::startCameraOps() { int32_t res; // Notify app ops that the camera is not available @@ -1448,7 +1610,7 @@ status_t CameraService::BasicClient::startCameraOps() { // Transition device availability listeners from PRESENT -> NOT_AVAILABLE mCameraService->updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, - mCameraId); + String8::format("%d", mCameraId)); return OK; } @@ -1461,22 +1623,21 @@ status_t CameraService::BasicClient::finishCameraOps() { mClientPackageName); mOpsActive = false; - // Notify device availability listeners that this camera is available - // again - - StatusVector rejectSourceStates; - rejectSourceStates.push_back(ICameraServiceListener::STATUS_NOT_PRESENT); - rejectSourceStates.push_back(ICameraServiceListener::STATUS_ENUMERATING); + auto rejected = {ICameraServiceListener::STATUS_NOT_PRESENT, + ICameraServiceListener::STATUS_ENUMERATING}; - // Transition to PRESENT if the camera is not in either of above 2 - // states + // Transition to PRESENT if the camera is not in either of the rejected states mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT, - mCameraId, - &rejectSourceStates); + String8::format("%d", mCameraId), rejected); + // Notify flashlight that a camera device is closed. + mCameraService->mFlashlight->deviceClosed( + String8::format("%d", mCameraId)); } // Always stop watching, even if no camera op is active - mAppOpsManager.stopWatchingMode(mOpsCallback); + if (mOpsCallback != NULL) { + mAppOpsManager.stopWatchingMode(mOpsCallback); + } mOpsCallback.clear(); return OK; @@ -1514,26 +1675,15 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16& packageNa // ---------------------------------------------------------------------------- -Mutex* CameraService::Client::getClientLockFromCookie(void* user) { - return gCameraService->getClientLockById((int)(intptr_t) user); -} - -// Provide client pointer for callbacks. Client lock returned from getClientLockFromCookie should -// be acquired for this to be safe -CameraService::Client* CameraService::Client::getClientFromCookie(void* user) { - BasicClient *basicClient = gCameraService->getClientByIdUnsafe((int)(intptr_t) user); - // OK: only CameraClient calls this, and they already cast anyway. - Client* client = static_cast<Client*>(basicClient); - - // This could happen if the Client is in the process of shutting down (the - // last strong reference is gone, but the destructor hasn't finished - // stopping the hardware). - if (client == NULL) return NULL; - - // destruction already started, so should not be accessed - if (client->mDestructionStarted) return NULL; - - return client; +// Provide client strong pointer for callbacks. +sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) { + String8 cameraId = String8::format("%d", (int)(intptr_t) user); + auto clientDescriptor = gCameraService->mActiveClientManager.get(cameraId); + if (clientDescriptor != nullptr) { + return sp<Client>{ + static_cast<Client*>(clientDescriptor->getValue().get())}; + } + return sp<Client>{nullptr}; } void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, @@ -1545,7 +1695,6 @@ void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode void CameraService::Client::disconnect() { ALOGV("Client::disconnect"); BasicClient::disconnect(); - mCameraService->setCameraFree(mCameraId); } CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client): @@ -1572,7 +1721,7 @@ CameraService::ProClient::ProClient(const sp<CameraService>& cameraService, int clientPid, uid_t clientUid, int servicePid) - : CameraService::BasicClient(cameraService, remoteCallback->asBinder(), + : CameraService::BasicClient(cameraService, IInterface::asBinder(remoteCallback), clientPackageName, cameraId, cameraFacing, clientPid, clientUid, servicePid) { @@ -1588,6 +1737,104 @@ void CameraService::ProClient::notifyError(ICameraDeviceCallbacks::CameraErrorCo } // ---------------------------------------------------------------------------- +// CameraState +// ---------------------------------------------------------------------------- + +CameraService::CameraState::CameraState(const String8& id, int cost, + const std::set<String8>& conflicting) : mId(id), + mStatus(ICameraServiceListener::STATUS_PRESENT), mCost(cost), mConflicting(conflicting) {} + +CameraService::CameraState::~CameraState() {} + +ICameraServiceListener::Status CameraService::CameraState::getStatus() const { + Mutex::Autolock lock(mStatusLock); + return mStatus; +} + +CameraParameters CameraService::CameraState::getShimParams() const { + return mShimParams; +} + +void CameraService::CameraState::setShimParams(const CameraParameters& params) { + mShimParams = params; +} + +int CameraService::CameraState::getCost() const { + return mCost; +} + +std::set<String8> CameraService::CameraState::getConflicting() const { + return mConflicting; +} + +String8 CameraService::CameraState::getId() const { + return mId; +} + +// ---------------------------------------------------------------------------- +// CameraClientManager +// ---------------------------------------------------------------------------- + +CameraService::CameraClientManager::~CameraClientManager() {} + +sp<CameraService::BasicClient> CameraService::CameraClientManager::getCameraClient( + const String8& id) const { + auto descriptor = get(id); + if (descriptor == nullptr) { + return sp<BasicClient>{nullptr}; + } + return descriptor->getValue(); +} + +String8 CameraService::CameraClientManager::toString() const { + auto all = getAll(); + String8 ret("["); + bool hasAny = false; + for (auto& i : all) { + hasAny = true; + String8 key = i->getKey(); + int32_t cost = i->getCost(); + int32_t pid = i->getOwnerId(); + int32_t priority = i->getPriority(); + auto conflicting = i->getConflicting(); + auto clientSp = i->getValue(); + String8 packageName; + if (clientSp.get() != nullptr) { + packageName = String8{clientSp->getPackageName()}; + } + ret.appendFormat("\n(Camera ID: %s, Cost: %" PRId32 ", PID: %" PRId32 ", Priority: %" + PRId32 ", ", key.string(), cost, pid, priority); + + if (packageName.size() != 0) { + ret.appendFormat("Client Package Name: %s", packageName.string()); + } + + ret.append(", Conflicting Client Devices: {"); + for (auto& j : conflicting) { + ret.appendFormat("%s, ", j.string()); + } + ret.append("})"); + } + if (hasAny) ret.append("\n"); + ret.append("]\n"); + return ret; +} + +CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor( + const String8& key, const sp<BasicClient>& value, int32_t cost, + const std::set<String8>& conflictingKeys, int32_t priority, int32_t ownerId) { + + return std::make_shared<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>( + key, value, cost, conflictingKeys, priority, ownerId); +} + +CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor( + const sp<BasicClient>& value, const CameraService::DescriptorPtr& partial) { + return makeClientDescriptor(partial->getKey(), value, partial->getCost(), + partial->getConflicting(), partial->getPriority(), partial->getOwnerId()); +} + +// ---------------------------------------------------------------------------- static const int kDumpLockRetries = 50; static const int kDumpLockSleep = 60000; @@ -1629,15 +1876,15 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { return NO_ERROR; } - result = String8::format("Camera module HAL API version: 0x%x\n", - mModule->common.hal_api_version); - result.appendFormat("Camera module API version: 0x%x\n", - mModule->common.module_api_version); - result.appendFormat("Camera module name: %s\n", - mModule->common.name); - result.appendFormat("Camera module author: %s\n", - mModule->common.author); - result.appendFormat("Number of camera devices: %d\n\n", mNumberOfCameras); + const hw_module_t* common = mModule->getRawModule(); + result = String8::format("Camera module HAL API version: %#x\n", common->hal_api_version); + result.appendFormat("Camera module API version: %#x\n", common->module_api_version); + result.appendFormat("Camera module name: %s\n", common->name); + result.appendFormat("Camera module author: %s\n", common->author); + result.appendFormat("Number of camera devices: %d\n", mNumberOfCameras); + String8 activeClientString = mActiveClientManager.toString(); + result.appendFormat("Active Camera Clients:\n%s", activeClientString.string()); + sp<VendorTagDescriptor> desc = VendorTagDescriptor::getGlobalVendorTagDescriptor(); if (desc == NULL) { @@ -1652,11 +1899,31 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { desc->dump(fd, /*verbosity*/2, /*indentation*/4); } - for (int i = 0; i < mNumberOfCameras; i++) { - result = String8::format("Camera %d static information:\n", i); + result = String8("Prior client events (most recent at top):\n"); + + for (const auto& msg : mEventLog) { + result.appendFormat("%s\n", msg.string()); + } + + if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) { + result.append("...\n"); + } + + write(fd, result.string(), result.size()); + + bool stateLocked = tryLock(mCameraStatesLock); + if (!stateLocked) { + result = String8::format("CameraStates in use, may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + for (auto& state : mCameraStates) { + String8 cameraId = state.first; + result = String8::format("Camera %s information:\n", cameraId.string()); camera_info info; - status_t rc = mModule->get_camera_info(i, &info); + // TODO: Change getCameraInfo + HAL to use String cameraIds + status_t rc = mModule->getCameraInfo(cameraIdToInt(cameraId), &info); if (rc != OK) { result.appendFormat(" Error reading static information!\n"); write(fd, result.string(), result.size()); @@ -1665,13 +1932,24 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { info.facing == CAMERA_FACING_BACK ? "BACK" : "FRONT"); result.appendFormat(" Orientation: %d\n", info.orientation); int deviceVersion; - if (mModule->common.module_api_version < - CAMERA_MODULE_API_VERSION_2_0) { + if (common->module_api_version < CAMERA_MODULE_API_VERSION_2_0) { deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; } else { deviceVersion = info.device_version; } - result.appendFormat(" Device version: 0x%x\n", deviceVersion); + + auto conflicting = state.second->getConflicting(); + result.appendFormat(" Resource Cost: %d\n", state.second->getCost()); + result.appendFormat(" Conflicting Devices:"); + for (auto& id : conflicting) { + result.appendFormat(" %s", cameraId.string()); + } + if (conflicting.size() == 0) { + result.appendFormat(" NONE"); + } + result.appendFormat("\n"); + + result.appendFormat(" Device version: %#x\n", deviceVersion); if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) { result.appendFormat(" Device static metadata:\n"); write(fd, result.string(), result.size()); @@ -1680,19 +1958,38 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { } else { write(fd, result.string(), result.size()); } + + CameraParameters p = state.second->getShimParams(); + if (!p.isEmpty()) { + result = String8::format(" Camera1 API shim is using parameters:\n "); + write(fd, result.string(), result.size()); + p.dump(fd, args); + } } - sp<BasicClient> client = mClient[i].promote(); - if (client == 0) { - result = String8::format(" Device is closed, no client instance\n"); + auto clientDescriptor = mActiveClientManager.get(cameraId); + if (clientDescriptor == nullptr) { + result = String8::format(" Device %s is closed, no client instance\n", + cameraId.string()); write(fd, result.string(), result.size()); continue; } hasClient = true; - result = String8::format(" Device is open. Client instance dump:\n"); + result = String8::format(" Device %s is open. Client instance dump:\n\n", + cameraId.string()); + result.appendFormat("Client priority level: %d\n", clientDescriptor->getPriority()); + result.appendFormat("Client PID: %d\n", clientDescriptor->getOwnerId()); + + auto client = clientDescriptor->getValue(); + result.appendFormat("Client package: %s\n", + String8(client->getPackageName()).string()); write(fd, result.string(), result.size()); + client->dump(fd, args); } + + if (stateLocked) mCameraStatesLock.unlock(); + if (!hasClient) { result = String8::format("\nNo active camera clients yet.\n"); write(fd, result.string(), result.size()); @@ -1716,112 +2013,119 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { write(fd, result.string(), result.size()); } } - } return NO_ERROR; } -/*virtual*/void CameraService::binderDied( - const wp<IBinder> &who) { +void CameraService::handleTorchClientBinderDied(const wp<IBinder> &who) { + Mutex::Autolock al(mTorchClientMapMutex); + for (size_t i = 0; i < mTorchClientMap.size(); i++) { + if (mTorchClientMap[i] == who) { + // turn off the torch mode that was turned on by dead client + String8 cameraId = mTorchClientMap.keyAt(i); + status_t res = mFlashlight->setTorchMode(cameraId, false); + if (res) { + ALOGE("%s: torch client died but couldn't turn off torch: " + "%s (%d)", __FUNCTION__, strerror(-res), res); + return; + } + mTorchClientMap.removeItemsAt(i); + break; + } + } +} + +/*virtual*/void CameraService::binderDied(const wp<IBinder> &who) { /** * While tempting to promote the wp<IBinder> into a sp, * it's actually not supported by the binder driver */ - ALOGV("java clients' binder died"); + // check torch client + handleTorchClientBinderDied(who); - sp<BasicClient> cameraClient = getClientByRemote(who); - - if (cameraClient == 0) { - ALOGV("java clients' binder death already cleaned up (normal case)"); + // check camera device client + if(!evictClientIdByRemote(who)) { + ALOGV("%s: Java client's binder death already cleaned up (normal case)", __FUNCTION__); return; } - ALOGW("Disconnecting camera client %p since the binder for it " - "died (this pid %d)", cameraClient.get(), getCallingPid()); - - cameraClient->disconnect(); - + ALOGE("%s: Java client's binder died, removing it from the list of active clients", + __FUNCTION__); } -void CameraService::updateStatus(ICameraServiceListener::Status status, - int32_t cameraId, - const StatusVector *rejectSourceStates) { - // do not lock mServiceLock here or can get into a deadlock from - // connect() -> ProClient::disconnect -> updateStatus - Mutex::Autolock lock(mStatusMutex); - - ICameraServiceListener::Status oldStatus = mStatusList[cameraId]; - - mStatusList[cameraId] = status; +void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId) { + updateStatus(status, cameraId, {}); +} - if (oldStatus != status) { - ALOGV("%s: Status has changed for camera ID %d from 0x%x to 0x%x", - __FUNCTION__, cameraId, (uint32_t)oldStatus, (uint32_t)status); +void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId, + std::initializer_list<ICameraServiceListener::Status> rejectSourceStates) { + // Do not lock mServiceLock here or can get into a deadlock from + // connect() -> disconnect -> updateStatus - if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT && - (status != ICameraServiceListener::STATUS_PRESENT && - status != ICameraServiceListener::STATUS_ENUMERATING)) { + auto state = getCameraState(cameraId); - ALOGW("%s: From NOT_PRESENT can only transition into PRESENT" - " or ENUMERATING", __FUNCTION__); - mStatusList[cameraId] = oldStatus; - return; - } + if (state == nullptr) { + ALOGW("%s: Could not update the status for %s, no such device exists", __FUNCTION__, + cameraId.string()); + return; + } - if (rejectSourceStates != NULL) { - const StatusVector &rejectList = *rejectSourceStates; - StatusVector::const_iterator it = rejectList.begin(); - - /** - * Sometimes we want to conditionally do a transition. - * For example if a client disconnects, we want to go to PRESENT - * only if we weren't already in NOT_PRESENT or ENUMERATING. - */ - for (; it != rejectList.end(); ++it) { - if (oldStatus == *it) { - ALOGV("%s: Rejecting status transition for Camera ID %d, " - " since the source state was was in one of the bad " - " states.", __FUNCTION__, cameraId); - mStatusList[cameraId] = oldStatus; - return; - } + // Update the status for this camera state, then send the onStatusChangedCallbacks to each + // of the listeners with both the mStatusStatus and mStatusListenerLock held + state->updateStatus(status, cameraId, rejectSourceStates, [this] + (const String8& cameraId, ICameraServiceListener::Status status) { + + // Update torch status + if (status == ICameraServiceListener::STATUS_NOT_PRESENT || + status == ICameraServiceListener::STATUS_NOT_AVAILABLE) { + // Update torch status to not available when the camera device becomes not present + // or not available. + onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE); + } else if (status == ICameraServiceListener::STATUS_PRESENT) { + // Update torch status to available when the camera device becomes present or + // available + onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF); } - } - /** - * ProClients lose their exclusive lock. - * - Done before the CameraClient can initialize the HAL device, - * since we want to be able to close it before they get to initialize - */ - if (status == ICameraServiceListener::STATUS_NOT_AVAILABLE) { - Vector<wp<ProClient> > proClients(mProClientList[cameraId]); - Vector<wp<ProClient> >::const_iterator it; - - for (it = proClients.begin(); it != proClients.end(); ++it) { - sp<ProClient> proCl = it->promote(); - if (proCl.get() != NULL) { - proCl->onExclusiveLockStolen(); - } + Mutex::Autolock lock(mStatusListenerLock); + + for (auto& listener : mListenerList) { + // TODO: Refactor status listeners to use strings for Camera IDs and remove this. + int id = cameraIdToInt(cameraId); + if (id != -1) listener->onStatusChanged(status, id); } - } + }); +} - Vector<sp<ICameraServiceListener> >::const_iterator it; - for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { - (*it)->onStatusChanged(status, cameraId); - } +status_t CameraService::getTorchStatusLocked( + const String8& cameraId, + ICameraServiceListener::TorchStatus *status) const { + if (!status) { + return BAD_VALUE; + } + ssize_t index = mTorchStatusMap.indexOfKey(cameraId); + if (index == NAME_NOT_FOUND) { + // invalid camera ID or the camera doesn't have a flash unit + return NAME_NOT_FOUND; } + + *status = mTorchStatusMap.valueAt(index); + return OK; } -ICameraServiceListener::Status CameraService::getStatus(int cameraId) const { - if (cameraId < 0 || cameraId >= MAX_CAMERAS) { - ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId); - return ICameraServiceListener::STATUS_UNKNOWN; +status_t CameraService::setTorchStatusLocked(const String8& cameraId, + ICameraServiceListener::TorchStatus status) { + ssize_t index = mTorchStatusMap.indexOfKey(cameraId); + if (index == NAME_NOT_FOUND) { + return BAD_VALUE; } + ICameraServiceListener::TorchStatus& item = + mTorchStatusMap.editValueAt(index); + item = status; - Mutex::Autolock al(mStatusMutex); - return mStatusList[cameraId]; + return OK; } }; // namespace android diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index a7328cf..53420e5 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -36,9 +36,17 @@ #include <camera/CameraParameters.h> #include <camera/ICameraServiceListener.h> +#include "CameraFlashlight.h" -/* This needs to be increased if we can have more cameras */ -#define MAX_CAMERAS 2 +#include "common/CameraModule.h" +#include "utils/AutoConditionLock.h" +#include "utils/ClientManager.h" +#include "utils/RingBuffer.h" + +#include <set> +#include <string> +#include <map> +#include <memory> namespace android { @@ -58,6 +66,34 @@ public: class Client; class BasicClient; + enum apiLevel { + API_1 = 1, + API_2 = 2 + }; + + // Process States (mirrors frameworks/base/core/java/android/app/ActivityManager.java) + static const int PROCESS_STATE_NONEXISTENT = -1; + static const int PROCESS_STATE_PERSISTENT = 0; + static const int PROCESS_STATE_PERSISTENT_UI = 1; + static const int PROCESS_STATE_TOP = 2; + static const int PROCESS_STATE_IMPORTANT_FOREGROUND = 3; + static const int PROCESS_STATE_IMPORTANT_BACKGROUND = 4; + static const int PROCESS_STATE_BACKUP = 5; + static const int PROCESS_STATE_HEAVY_WEIGHT = 6; + static const int PROCESS_STATE_SERVICE = 7; + static const int PROCESS_STATE_RECEIVER = 8; + static const int PROCESS_STATE_HOME = 9; + static const int PROCESS_STATE_LAST_ACTIVITY = 10; + static const int PROCESS_STATE_CACHED_ACTIVITY = 11; + static const int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12; + static const int PROCESS_STATE_CACHED_EMPTY = 13; + + // 3 second busy timeout when other clients are connecting + static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000; + + // Default number of messages to store in eviction log + static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50; + // Implementation of BinderService<T> static char const* getServiceName() { return "media.camera"; } @@ -66,8 +102,11 @@ public: ///////////////////////////////////////////////////////////////////// // HAL Callbacks - virtual void onDeviceStatusChanged(int cameraId, - int newStatus); + virtual void onDeviceStatusChanged(camera_device_status_t cameraId, + camera_device_status_t newStatus); + virtual void onTorchStatusChanged(const String8& cameraId, + ICameraServiceListener::TorchStatus + newStatus); ///////////////////////////////////////////////////////////////////// // ICameraService @@ -110,6 +149,9 @@ public: /*out*/ String16* parameters); + virtual status_t setTorchMode(const String16& cameraId, bool enabled, + const sp<IBinder>& clientBinder); + // OK = supports api of that version, -EOPNOTSUPP = does not support virtual status_t supportsCameraApi( int cameraId, int apiVersion); @@ -122,7 +164,6 @@ public: ///////////////////////////////////////////////////////////////////// // Client functionality - virtual void removeClientByRemote(const wp<IBinder>& remoteBinder); enum sound_kind { SOUND_SHUTTER = 0, @@ -140,20 +181,14 @@ public: ///////////////////////////////////////////////////////////////////// // Shared utilities - static status_t filterOpenErrorCode(status_t err); static status_t filterGetInfoErrorCode(status_t err); ///////////////////////////////////////////////////////////////////// // CameraClient functionality - // returns plain pointer of client. Note that mClientLock should be acquired to - // prevent the client from destruction. The result can be NULL. - virtual BasicClient* getClientByIdUnsafe(int cameraId); - virtual Mutex* getClientLockById(int cameraId); - class BasicClient : public virtual RefBase { public: - virtual status_t initialize(camera_module_t *module) = 0; + virtual status_t initialize(CameraModule *module) = 0; virtual void disconnect(); // because we can't virtually inherit IInterface, which breaks @@ -167,6 +202,15 @@ public: virtual status_t dump(int fd, const Vector<String16>& args) = 0; + // Return the package name for this client + virtual String16 getPackageName() const; + + // Notify client about a fatal error + virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras) = 0; + + // Get the PID of the application client using this + virtual int getClientPid() const; protected: BasicClient(const sp<CameraService>& cameraService, const sp<IBinder>& remoteCallback, @@ -193,6 +237,7 @@ public: pid_t mClientPid; uid_t mClientUid; // immutable after constructor pid_t mServicePid; // immutable after constructor + bool mDisconnected; // - The app-side Binder interface to receive callbacks from us sp<IBinder> mRemoteBinder; // immutable after constructor @@ -201,10 +246,6 @@ public: status_t startCameraOps(); status_t finishCameraOps(); - // Notify client about a fatal error - virtual void notifyError( - ICameraDeviceCallbacks::CameraErrorCode errorCode, - const CaptureResultExtras& resultExtras) = 0; private: AppOpsManager mAppOpsManager; @@ -273,16 +314,14 @@ public: } virtual sp<IBinder> asBinderWrapper() { - return asBinder(); + return asBinder(this); } - protected: - static Mutex* getClientLockFromCookie(void* user); - // convert client from cookie. Client lock should be acquired before getting Client. - static Client* getClientFromCookie(void* user); - virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, const CaptureResultExtras& resultExtras); + protected: + // Convert client from cookie. + static sp<CameraService::Client> getClientFromCookie(void* user); // Initialized in constructor @@ -329,54 +368,230 @@ public: // Callbacks from camera service virtual void onExclusiveLockStolen() = 0; - protected: virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, const CaptureResultExtras& resultExtras); + protected: sp<IProCameraCallbacks> mRemoteCallback; }; // class ProClient + typedef std::shared_ptr<resource_policy::ClientDescriptor<String8, + sp<CameraService::BasicClient>>> DescriptorPtr; + + /** + * A container class for managing active camera clients that are using HAL devices. Active + * clients are represented by ClientDescriptor objects that contain strong pointers to the + * actual BasicClient subclass binder interface implementation. + * + * This class manages the eviction behavior for the camera clients. See the parent class + * implementation in utils/ClientManager for the specifics of this behavior. + */ + class CameraClientManager : + public resource_policy::ClientManager<String8, sp<CameraService::BasicClient>> { + public: + virtual ~CameraClientManager(); + + /** + * Return a strong pointer to the active BasicClient for this camera ID, or an empty + * if none exists. + */ + sp<CameraService::BasicClient> getCameraClient(const String8& id) const; + + /** + * Return a string describing the current state. + */ + String8 toString() const; + + /** + * Make a ClientDescriptor object wrapping the given BasicClient strong pointer. + */ + static DescriptorPtr makeClientDescriptor(const String8& key, const sp<BasicClient>& value, + int32_t cost, const std::set<String8>& conflictingKeys, int32_t priority, + int32_t ownerId); + + /** + * Make a ClientDescriptor object wrapping the given BasicClient strong pointer with + * values intialized from a prior ClientDescriptor. + */ + static DescriptorPtr makeClientDescriptor(const sp<BasicClient>& value, + const CameraService::DescriptorPtr& partial); + + }; // class CameraClientManager + private: + /** + * Container class for the state of each logical camera device, including: ID, status, and + * dependencies on other devices. The mapping of camera ID -> state saved in mCameraStates + * represents the camera devices advertised by the HAL (and any USB devices, when we add + * those). + * + * This container does NOT represent an active camera client. These are represented using + * the ClientDescriptors stored in mActiveClientManager. + */ + class CameraState { + public: + /** + * Make a new CameraState and set the ID, cost, and conflicting devices using the values + * returned in the HAL's camera_info struct for each device. + */ + CameraState(const String8& id, int cost, const std::set<String8>& conflicting); + virtual ~CameraState(); + + /** + * Return the status for this device. + * + * This method acquires mStatusLock. + */ + ICameraServiceListener::Status getStatus() const; + + /** + * This function updates the status for this camera device, unless the given status + * is in the given list of rejected status states, and execute the function passed in + * with a signature onStatusUpdateLocked(const String8&, ICameraServiceListener::Status) + * if the status has changed. + * + * This method is idempotent, and will not result in the function passed to + * onStatusUpdateLocked being called more than once for the same arguments. + * This method aquires mStatusLock. + */ + template<class Func> + void updateStatus(ICameraServiceListener::Status status, const String8& cameraId, + std::initializer_list<ICameraServiceListener::Status> rejectSourceStates, + Func onStatusUpdatedLocked); + + /** + * Return the last set CameraParameters object generated from the information returned by + * the HAL for this device (or an empty CameraParameters object if none has been set). + */ + CameraParameters getShimParams() const; + + /** + * Set the CameraParameters for this device. + */ + void setShimParams(const CameraParameters& params); + + /** + * Return the resource_cost advertised by the HAL for this device. + */ + int getCost() const; + + /** + * Return a set of the IDs of conflicting devices advertised by the HAL for this device. + */ + std::set<String8> getConflicting() const; + + /** + * Return the ID of this camera device. + */ + String8 getId() const; + + private: + const String8 mId; + ICameraServiceListener::Status mStatus; // protected by mStatusLock + const int mCost; + std::set<String8> mConflicting; + mutable Mutex mStatusLock; + CameraParameters mShimParams; + }; // class CameraState + // Delay-load the Camera HAL module virtual void onFirstRef(); - // Step 1. Check if we can connect, before we acquire the service lock. - status_t validateConnect(int cameraId, - /*inout*/ - int& clientUid) const; + // Check if we can connect, before we acquire the service lock. + status_t validateConnect(const String8& cameraId, /*inout*/int& clientUid) const; - // Step 2. Check if we can connect, after we acquire the service lock. - bool canConnectUnsafe(int cameraId, - const String16& clientPackageName, - const sp<IBinder>& remoteCallback, - /*out*/ - sp<BasicClient> &client); + // Handle active client evictions, and update service state. + // Only call with with mServiceLock held. + status_t handleEvictionsLocked(const String8& cameraId, int clientPid, + apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName, + /*out*/ + sp<BasicClient>* client, + std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial); - // When connection is successful, initialize client and track its death - status_t connectFinishUnsafe(const sp<BasicClient>& client, - const sp<IBinder>& remoteCallback); + // Single implementation shared between the various connect calls + template<class CALLBACK, class CLIENT> + status_t connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId, int halVersion, + const String16& clientPackageName, int clientUid, apiLevel effectiveApiLevel, + bool legacyMode, bool shimUpdateOnly, /*out*/sp<CLIENT>& device); - virtual sp<BasicClient> getClientByRemote(const wp<IBinder>& cameraClient); + // Lock guarding camera service state Mutex mServiceLock; - // either a Client or CameraDeviceClient - wp<BasicClient> mClient[MAX_CAMERAS]; // protected by mServiceLock - Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks - int mNumberOfCameras; - typedef wp<ProClient> weak_pro_client_ptr; - Vector<weak_pro_client_ptr> mProClientList[MAX_CAMERAS]; + // Condition to use with mServiceLock, used to handle simultaneous connect calls from clients + std::shared_ptr<WaitableMutexWrapper> mServiceLockWrapper; + + // Return NO_ERROR if the device with a give ID can be connected to + status_t checkIfDeviceIsUsable(const String8& cameraId) const; - // needs to be called with mServiceLock held - sp<BasicClient> findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex); - sp<ProClient> findProClientUnsafe( - const wp<IBinder>& cameraCallbacksRemote); + // Container for managing currently active application-layer clients + CameraClientManager mActiveClientManager; - // atomics to record whether the hardware is allocated to some client. - volatile int32_t mBusy[MAX_CAMERAS]; - void setCameraBusy(int cameraId); - void setCameraFree(int cameraId); + // Mapping from camera ID -> state for each device, map is protected by mCameraStatesLock + std::map<String8, std::shared_ptr<CameraState>> mCameraStates; + + // Mutex guarding mCameraStates map + mutable Mutex mCameraStatesLock; + + // Circular buffer for storing event logging for dumps + RingBuffer<String8> mEventLog; + + /** + * Get the camera state for a given camera id. + * + * This acquires mCameraStatesLock. + */ + std::shared_ptr<CameraService::CameraState> getCameraState(const String8& cameraId) const; + + /** + * Evict client who's remote binder has died. Returns true if this client was in the active + * list and was disconnected. + * + * This method acquires mServiceLock. + */ + bool evictClientIdByRemote(const wp<IBinder>& cameraClient); + + /** + * Remove the given client from the active clients list; does not disconnect the client. + * + * This method acquires mServiceLock. + */ + void removeByClient(const BasicClient* client); + + /** + * Add new client to active clients list after conflicting clients have disconnected using the + * values set in the partial descriptor passed in to construct the actual client descriptor. + * This is typically called at the end of a connect call. + * + * This method must be called with mServiceLock held. + */ + void finishConnectLocked(const sp<BasicClient>& client, const DescriptorPtr& desc); + + /** + * Returns the integer corresponding to the given camera ID string, or -1 on failure. + */ + static int cameraIdToInt(const String8& cameraId); + + /** + * Remove a single client corresponding to the given camera id from the list of active clients. + * If none exists, return an empty strongpointer. + * + * This method must be called with mServiceLock held. + */ + sp<CameraService::BasicClient> removeClientLocked(const String8& cameraId); + + /** + * Add a event log message that a client has been disconnected. + */ + void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage); + + /** + * Add a event log message that a client has been connected. + */ + void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage); + + int mNumberOfCameras; // sounds MediaPlayer* newMediaPlayer(const char *file); @@ -385,45 +600,60 @@ private: sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS]; int mSoundRef; // reference count (release all MediaPlayer when 0) - camera_module_t *mModule; - - Vector<sp<ICameraServiceListener> > - mListenerList; - - // guard only mStatusList and the broadcasting of ICameraServiceListener - mutable Mutex mStatusMutex; - ICameraServiceListener::Status - mStatusList[MAX_CAMERAS]; + CameraModule* mModule; - // Read the current status (locks mStatusMutex) - ICameraServiceListener::Status - getStatus(int cameraId) const; + // Guarded by mStatusListenerMutex + std::vector<sp<ICameraServiceListener>> mListenerList; + Mutex mStatusListenerLock; - typedef Vector<ICameraServiceListener::Status> StatusVector; - // Broadcast the new status if it changed (locks the service mutex) - void updateStatus( - ICameraServiceListener::Status status, - int32_t cameraId, - const StatusVector *rejectSourceStates = NULL); + /** + * Update the status for the given camera id (if that device exists), and broadcast the + * status update to all current ICameraServiceListeners if the status has changed. Any + * statuses in rejectedSourceStates will be ignored. + * + * This method must be idempotent. + * This method acquires mStatusLock and mStatusListenerLock. + */ + void updateStatus(ICameraServiceListener::Status status, const String8& cameraId, + std::initializer_list<ICameraServiceListener::Status> rejectedSourceStates); + void updateStatus(ICameraServiceListener::Status status, const String8& cameraId); + + // flashlight control + sp<CameraFlashlight> mFlashlight; + // guard mTorchStatusMap + Mutex mTorchStatusMutex; + // guard mTorchClientMap + Mutex mTorchClientMapMutex; + // camera id -> torch status + KeyedVector<String8, ICameraServiceListener::TorchStatus> mTorchStatusMap; + // camera id -> torch client binder + // only store the last client that turns on each camera's torch mode + KeyedVector<String8, sp<IBinder> > mTorchClientMap; + + // check and handle if torch client's process has died + void handleTorchClientBinderDied(const wp<IBinder> &who); + + // handle torch mode status change and invoke callbacks. mTorchStatusMutex + // should be locked. + void onTorchStatusChangedLocked(const String8& cameraId, + ICameraServiceListener::TorchStatus newStatus); + + // get a camera's torch status. mTorchStatusMutex should be locked. + status_t getTorchStatusLocked(const String8 &cameraId, + ICameraServiceListener::TorchStatus *status) const; + + // set a camera's torch status. mTorchStatusMutex should be locked. + status_t setTorchStatusLocked(const String8 &cameraId, + ICameraServiceListener::TorchStatus status); // IBinder::DeathRecipient implementation virtual void binderDied(const wp<IBinder> &who); // Helpers - bool isValidCameraId(int cameraId); - bool setUpVendorTags(); /** - * A mapping of camera ids to CameraParameters returned by that camera device. - * - * This cache is used to generate CameraCharacteristic metadata when using - * the HAL1 shim. - */ - KeyedVector<int, CameraParameters> mShimParams; - - /** * Initialize and cache the metadata used by the HAL1 shim for a given cameraId. * * Returns OK on success, or a negative error code. @@ -446,25 +676,190 @@ private: */ status_t generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo); + static int getCallingPid(); + + static int getCallingUid(); + /** - * Connect a new camera client. This should only be used while holding the - * mutex for mServiceLock. - * - * Returns OK on success, or a negative error code. + * Get the current system time as a formatted string. */ - status_t connectHelperLocked( - /*out*/ - sp<Client>& client, - /*in*/ - const sp<ICameraClient>& cameraClient, - int cameraId, - const String16& clientPackageName, - int clientUid, - int callingPid, - int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED, - bool legacyMode = false); + static String8 getFormattedCurrentTime(); + + /** + * Get the camera eviction priority from the current process state given by ActivityManager. + */ + static int getCameraPriorityFromProcState(int procState); + + static status_t makeClient(const sp<CameraService>& cameraService, + const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId, + int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode, + int halVersion, int deviceVersion, apiLevel effectiveApiLevel, + /*out*/sp<BasicClient>* client); }; +template<class Func> +void CameraService::CameraState::updateStatus(ICameraServiceListener::Status status, + const String8& cameraId, + std::initializer_list<ICameraServiceListener::Status> rejectSourceStates, + Func onStatusUpdatedLocked) { + Mutex::Autolock lock(mStatusLock); + ICameraServiceListener::Status oldStatus = mStatus; + mStatus = status; + + if (oldStatus == status) { + return; + } + + ALOGV("%s: Status has changed for camera ID %s from %#x to %#x", __FUNCTION__, + cameraId.string(), oldStatus, status); + + if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT && + (status != ICameraServiceListener::STATUS_PRESENT && + status != ICameraServiceListener::STATUS_ENUMERATING)) { + + ALOGW("%s: From NOT_PRESENT can only transition into PRESENT or ENUMERATING", + __FUNCTION__); + mStatus = oldStatus; + return; + } + + /** + * Sometimes we want to conditionally do a transition. + * For example if a client disconnects, we want to go to PRESENT + * only if we weren't already in NOT_PRESENT or ENUMERATING. + */ + for (auto& rejectStatus : rejectSourceStates) { + if (oldStatus == rejectStatus) { + ALOGV("%s: Rejecting status transition for Camera ID %s, since the source " + "state was was in one of the bad states.", __FUNCTION__, cameraId.string()); + mStatus = oldStatus; + return; + } + } + + onStatusUpdatedLocked(cameraId, status); +} + + +template<class CALLBACK, class CLIENT> +status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId, + int halVersion, const String16& clientPackageName, int clientUid, + apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly, + /*out*/sp<CLIENT>& device) { + status_t ret = NO_ERROR; + String8 clientName8(clientPackageName); + int clientPid = getCallingPid(); + + ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and " + "Camera API version %d", clientPid, clientName8.string(), cameraId.string(), + halVersion, static_cast<int>(effectiveApiLevel)); + + // Enforce client permissions and do basic sanity checks + if((ret = validateConnect(cameraId, /*inout*/clientUid)) != NO_ERROR) { + return ret; + } + + sp<CLIENT> client = nullptr; + { + // Acquire mServiceLock and prevent other clients from connecting + std::unique_ptr<AutoConditionLock> lock = + AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS); + if (lock == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (too many other clients connecting)." + , clientPid); + return -EBUSY; + } + + // Check the shim parameters after acquiring lock, if they have already been updated and + // we were doing a shim update, return immediately + if (shimUpdateOnly) { + auto cameraState = getCameraState(cameraId); + if (cameraState != nullptr) { + if (!cameraState->getShimParams().isEmpty()) return NO_ERROR; + } + } + + sp<BasicClient> clientTmp = nullptr; + std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial; + if ((ret = handleEvictionsLocked(cameraId, clientPid, effectiveApiLevel, + IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp, + /*out*/&partial)) != NO_ERROR) { + return ret; + } + + if (clientTmp.get() != nullptr) { + // Handle special case for API1 MediaRecorder where the existing client is returned + device = static_cast<CLIENT*>(clientTmp.get()); + return NO_ERROR; + } + + // give flashlight a chance to close devices if necessary. + mFlashlight->prepareDeviceOpen(cameraId); + + // TODO: Update getDeviceVersion + HAL interface to use strings for Camera IDs + int id = cameraIdToInt(cameraId); + if (id == -1) { + ALOGE("%s: Invalid camera ID %s, cannot get device version from HAL.", __FUNCTION__, + cameraId.string()); + return BAD_VALUE; + } + + int facing = -1; + int deviceVersion = getDeviceVersion(id, /*out*/&facing); + sp<BasicClient> tmp = nullptr; + if((ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid, + clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel, + /*out*/&tmp)) != NO_ERROR) { + return ret; + } + client = static_cast<CLIENT*>(tmp.get()); + + LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state", + __FUNCTION__); + + if ((ret = client->initialize(mModule)) != OK) { + ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__); + return ret; + } + + sp<IBinder> remoteCallback = client->getRemote(); + if (remoteCallback != nullptr) { + remoteCallback->linkToDeath(this); + } + + // Update shim paremeters for legacy clients + if (effectiveApiLevel == API_1) { + // Assume we have always received a Client subclass for API1 + sp<Client> shimClient = reinterpret_cast<Client*>(client.get()); + String8 rawParams = shimClient->getParameters(); + CameraParameters params(rawParams); + + auto cameraState = getCameraState(cameraId); + if (cameraState != nullptr) { + cameraState->setShimParams(params); + } else { + ALOGE("%s: Cannot update shim parameters for camera %s, no such device exists.", + __FUNCTION__, cameraId.string()); + } + } + + if (shimUpdateOnly) { + // If only updating legacy shim parameters, immediately disconnect client + mServiceLock.unlock(); + client->disconnect(); + mServiceLock.lock(); + } else { + // Otherwise, add client to active clients list + finishConnectLocked(client, partial); + } + } // lock is destroyed, allow further connect calls + + // Important: release the mutex here so the client can call back into the service from its + // destructor (can be at the end of the call) + device = client; + return NO_ERROR; +} + } // namespace android #endif diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index f3a88a1..6f44aee 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -67,7 +67,7 @@ Camera2Client::Camera2Client(const sp<CameraService>& cameraService, mLegacyMode = legacyMode; } -status_t Camera2Client::initialize(camera_module_t *module) +status_t Camera2Client::initialize(CameraModule *module) { ATRACE_CALL(); ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId); @@ -163,10 +163,9 @@ Camera2Client::~Camera2Client() { status_t Camera2Client::dump(int fd, const Vector<String16>& args) { String8 result; - result.appendFormat("Client2[%d] (%p) Client: %s PID: %d, dump:\n", - mCameraId, - getRemoteCallback()->asBinder().get(), - String8(mClientPackageName).string(), + result.appendFormat("Client2[%d] (%p) PID: %d, dump:\n", mCameraId, + (getRemoteCallback() != NULL ? + (IInterface::asBinder(getRemoteCallback()).get()) : NULL), mClientPid); result.append(" State: "); #define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break; @@ -531,7 +530,7 @@ status_t Camera2Client::setPreviewTarget( sp<IBinder> binder; sp<ANativeWindow> window; if (bufferProducer != 0) { - binder = bufferProducer->asBinder(); + binder = IInterface::asBinder(bufferProducer); // Using controlledByApp flag to ensure that the buffer queue remains in // async mode for the old camera API, where many applications depend // on that behavior. @@ -917,6 +916,15 @@ void Camera2Client::stopPreviewL() { ALOGE("%s: Camera %d: Can't stop streaming: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); } + + // Flush all in-process captures and buffer in order to stop + // preview faster. + res = mDevice->flush(); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to flush pending requests: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + res = mDevice->waitUntilDrained(); if (res != OK) { ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)", @@ -929,13 +937,6 @@ void Camera2Client::stopPreviewL() { "stop preview: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); } - { - // Ideally we should recover the override after recording stopped, but - // right now recording stream will live until here, so we are forced to - // recover here. TODO: find a better way to handle that (b/17495165) - SharedParameters::Lock l(mParameters); - l.mParameters.recoverOverriddenJpegSize(); - } // no break case Parameters::WAITING_FOR_PREVIEW_WINDOW: { SharedParameters::Lock l(mParameters); @@ -1206,6 +1207,28 @@ void Camera2Client::stopRecording() { mCameraService->playSound(CameraService::SOUND_RECORDING); + // Remove recording stream to prevent it from slowing down takePicture later + if (!l.mParameters.recordingHint && l.mParameters.isJpegSizeOverridden()) { + res = stopStream(); + if (res != OK) { + ALOGE("%s: Camera %d: Can't stop streaming: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + res = mDevice->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + // Clean up recording stream + res = mStreamingProcessor->deleteRecordingStream(); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete recording stream before " + "stop preview: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + l.mParameters.recoverOverriddenJpegSize(); + } + res = startPreviewL(l.mParameters, true); if (res != OK) { ALOGE("%s: Camera %d: Unable to return to preview", @@ -1388,6 +1411,34 @@ status_t Camera2Client::takePicture(int msgType) { return res; } l.mParameters.state = Parameters::STILL_CAPTURE; + + // Remove recording stream to prevent video snapshot jpeg logic kicking in + if (l.mParameters.isJpegSizeOverridden() && + mStreamingProcessor->getRecordingStreamId() != NO_STREAM) { + res = mStreamingProcessor->togglePauseStream(/*pause*/true); + if (res != OK) { + ALOGE("%s: Camera %d: Can't pause streaming: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + res = mDevice->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + // Clean up recording stream + res = mStreamingProcessor->deleteRecordingStream(); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete recording stream before " + "stop preview: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + res = mStreamingProcessor->togglePauseStream(/*pause*/false); + if (res != OK) { + ALOGE("%s: Camera %d: Can't unpause streaming: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + l.mParameters.recoverOverriddenJpegSize(); + } break; case Parameters::RECORD: // Good to go for video snapshot @@ -1906,7 +1957,7 @@ size_t Camera2Client::calculateBufferSize(int width, int height, return width * height * 2; case HAL_PIXEL_FORMAT_RGBA_8888: return width * height * 4; - case HAL_PIXEL_FORMAT_RAW_SENSOR: + case HAL_PIXEL_FORMAT_RAW16: return width * height * 2; default: ALOGE("%s: Unknown preview format: %x", diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h index d68bb29..5a8241f 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.h +++ b/services/camera/libcameraservice/api1/Camera2Client.h @@ -94,7 +94,7 @@ public: virtual ~Camera2Client(); - status_t initialize(camera_module_t *module); + status_t initialize(CameraModule *module); virtual status_t dump(int fd, const Vector<String16>& args); diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp index 1a4d9a6..e552633 100644 --- a/services/camera/libcameraservice/api1/CameraClient.cpp +++ b/services/camera/libcameraservice/api1/CameraClient.cpp @@ -59,7 +59,7 @@ CameraClient::CameraClient(const sp<CameraService>& cameraService, LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId); } -status_t CameraClient::initialize(camera_module_t *module) { +status_t CameraClient::initialize(CameraModule *module) { int callingPid = getCallingPid(); status_t res; @@ -75,7 +75,7 @@ status_t CameraClient::initialize(camera_module_t *module) { snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId); mHardware = new CameraHardwareInterface(camera_device_name); - res = mHardware->initialize(&module->common); + res = mHardware->initialize(module); if (res != OK) { ALOGE("%s: Camera %d: unable to initialize device: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); @@ -99,12 +99,7 @@ status_t CameraClient::initialize(camera_module_t *module) { // tear down the client CameraClient::~CameraClient() { - // this lock should never be NULL - Mutex* lock = mCameraService->getClientLockById(mCameraId); - lock->lock(); mDestructionStarted = true; - // client will not be accessed from callback. should unlock to prevent dead-lock in disconnect - lock->unlock(); int callingPid = getCallingPid(); LOG1("CameraClient::~CameraClient E (pid %d, this %p)", callingPid, this); @@ -116,10 +111,11 @@ status_t CameraClient::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; - size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) PID: %d\n", + size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) with UID %d\n", mCameraId, - getRemoteCallback()->asBinder().get(), - mClientPid); + (getRemoteCallback() != NULL ? + IInterface::asBinder(getRemoteCallback()).get() : NULL), + mClientUid); len = (len > SIZE - 1) ? SIZE - 1 : len; write(fd, buffer, len); @@ -205,7 +201,7 @@ status_t CameraClient::connect(const sp<ICameraClient>& client) { } if (mRemoteCallback != 0 && - (client->asBinder() == mRemoteCallback->asBinder())) { + (IInterface::asBinder(client) == IInterface::asBinder(mRemoteCallback))) { LOG1("Connect to the same client"); return NO_ERROR; } @@ -328,7 +324,7 @@ status_t CameraClient::setPreviewTarget( sp<IBinder> binder; sp<ANativeWindow> window; if (bufferProducer != 0) { - binder = bufferProducer->asBinder(); + binder = IInterface::asBinder(bufferProducer); // Using controlledByApp flag to ensure that the buffer queue remains in // async mode for the old camera API, where many applications depend // on that behavior. @@ -676,6 +672,13 @@ bool CameraClient::lockIfMessageWanted(int32_t msgType) { LOG1("lockIfMessageWanted(%d): waited for %d ms", msgType, sleepCount * CHECK_MESSAGE_INTERVAL); } + + // If messages are no longer enabled after acquiring lock, release and drop message + if ((mMsgEnabled & msgType) == 0) { + mLock.unlock(); + break; + } + return true; } if (sleepCount++ == 0) { @@ -701,26 +704,13 @@ bool CameraClient::lockIfMessageWanted(int32_t msgType) { // (others) c->dataCallback // dataCallbackTimestamp // (others) c->dataCallbackTimestamp -// -// NOTE: the *Callback functions grab mLock of the client before passing -// control to handle* functions. So the handle* functions must release the -// lock before calling the ICameraClient's callbacks, so those callbacks can -// invoke methods in the Client class again (For example, the preview frame -// callback may want to releaseRecordingFrame). The handle* functions must -// release the lock after all accesses to member variables, so it must be -// handled very carefully. void CameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user) { LOG2("notifyCallback(%d)", msgType); - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - CameraClient* client = - static_cast<CameraClient*>(getClientFromCookie(user)); - if (client == NULL) return; + sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get()); + if (client.get() == nullptr) return; if (!client->lockIfMessageWanted(msgType)) return; @@ -739,13 +729,8 @@ void CameraClient::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) { LOG2("dataCallback(%d)", msgType); - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - CameraClient* client = - static_cast<CameraClient*>(getClientFromCookie(user)); - if (client == NULL) return; + sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get()); + if (client.get() == nullptr) return; if (!client->lockIfMessageWanted(msgType)) return; if (dataPtr == 0 && metadata == NULL) { @@ -777,13 +762,8 @@ void CameraClient::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user) { LOG2("dataCallbackTimestamp(%d)", msgType); - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - CameraClient* client = - static_cast<CameraClient*>(getClientFromCookie(user)); - if (client == NULL) return; + sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get()); + if (client.get() == nullptr) return; if (!client->lockIfMessageWanted(msgType)) return; diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h index 63a9d0f..95616b2 100644 --- a/services/camera/libcameraservice/api1/CameraClient.h +++ b/services/camera/libcameraservice/api1/CameraClient.h @@ -68,7 +68,7 @@ public: bool legacyMode = false); ~CameraClient(); - status_t initialize(camera_module_t *module); + status_t initialize(CameraModule *module); status_t dump(int fd, const Vector<String16>& args); diff --git a/services/camera/libcameraservice/api1/client2/BurstCapture.cpp b/services/camera/libcameraservice/api1/client2/BurstCapture.cpp index 0bfdfd4..5502dcb 100644 --- a/services/camera/libcameraservice/api1/client2/BurstCapture.cpp +++ b/services/camera/libcameraservice/api1/client2/BurstCapture.cpp @@ -44,7 +44,7 @@ status_t BurstCapture::start(Vector<CameraMetadata> &/*metadatas*/, return INVALID_OPERATION; } -void BurstCapture::onFrameAvailable() { +void BurstCapture::onFrameAvailable(const BufferItem &/*item*/) { ALOGV("%s", __FUNCTION__); Mutex::Autolock l(mInputMutex); if(!mInputChanged) { diff --git a/services/camera/libcameraservice/api1/client2/BurstCapture.h b/services/camera/libcameraservice/api1/client2/BurstCapture.h index ea321fd..c3b7722 100644 --- a/services/camera/libcameraservice/api1/client2/BurstCapture.h +++ b/services/camera/libcameraservice/api1/client2/BurstCapture.h @@ -39,7 +39,7 @@ public: BurstCapture(wp<Camera2Client> client, wp<CaptureSequencer> sequencer); virtual ~BurstCapture(); - virtual void onFrameAvailable(); + virtual void onFrameAvailable(const BufferItem& item); virtual status_t start(Vector<CameraMetadata> &metadatas, int32_t firstCaptureId); protected: diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp index bf3318e..5c8f750 100644 --- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp @@ -46,7 +46,7 @@ CallbackProcessor::~CallbackProcessor() { deleteStream(); } -void CallbackProcessor::onFrameAvailable() { +void CallbackProcessor::onFrameAvailable(const BufferItem& /*item*/) { Mutex::Autolock l(mInputMutex); if (!mCallbackAvailable) { mCallbackAvailable = true; @@ -154,8 +154,8 @@ status_t CallbackProcessor::updateStream(const Parameters ¶ms) { params.previewWidth, params.previewHeight, callbackFormat, params.previewFormat); res = device->createStream(mCallbackWindow, - params.previewWidth, params.previewHeight, - callbackFormat, &mCallbackStreamId); + params.previewWidth, params.previewHeight, callbackFormat, + HAL_DATASPACE_JFIF, CAMERA3_STREAM_ROTATION_0, &mCallbackStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for callbacks: " "%s (%d)", __FUNCTION__, mId, diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.h b/services/camera/libcameraservice/api1/client2/CallbackProcessor.h index 613f5be..7fdc329 100644 --- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.h +++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.h @@ -44,7 +44,7 @@ class CallbackProcessor: CallbackProcessor(sp<Camera2Client> client); ~CallbackProcessor(); - void onFrameAvailable(); + void onFrameAvailable(const BufferItem& item); // Set to NULL to disable the direct-to-app callback window status_t setCallbackWindow(sp<ANativeWindow> callbackWindow); diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp index 312a78c..40d53b3 100644 --- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp @@ -168,6 +168,19 @@ status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame, faceIds = entry.data.i32; } + entry = frame.find(ANDROID_SCALER_CROP_REGION); + if (entry.count < 4) { + ALOGE("%s: Camera %d: Unable to read crop region (count = %d)", + __FUNCTION__, client->getCameraId(), entry.count); + return res; + } + + Parameters::CropRegion scalerCrop = { + static_cast<float>(entry.data.i32[0]), + static_cast<float>(entry.data.i32[1]), + static_cast<float>(entry.data.i32[2]), + static_cast<float>(entry.data.i32[3])}; + faces.setCapacity(metadata.number_of_faces); size_t maxFaces = metadata.number_of_faces; @@ -183,26 +196,30 @@ status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame, camera_face_t face; - face.rect[0] = l.mParameters.arrayXToNormalized(faceRects[i*4 + 0]); - face.rect[1] = l.mParameters.arrayYToNormalized(faceRects[i*4 + 1]); - face.rect[2] = l.mParameters.arrayXToNormalized(faceRects[i*4 + 2]); - face.rect[3] = l.mParameters.arrayYToNormalized(faceRects[i*4 + 3]); + face.rect[0] = l.mParameters.arrayXToNormalizedWithCrop( + faceRects[i*4 + 0], scalerCrop); + face.rect[1] = l.mParameters.arrayYToNormalizedWithCrop( + faceRects[i*4 + 1], scalerCrop); + face.rect[2] = l.mParameters.arrayXToNormalizedWithCrop( + faceRects[i*4 + 2], scalerCrop); + face.rect[3] = l.mParameters.arrayYToNormalizedWithCrop( + faceRects[i*4 + 3], scalerCrop); face.score = faceScores[i]; if (faceDetectMode == ANDROID_STATISTICS_FACE_DETECT_MODE_FULL) { face.id = faceIds[i]; - face.left_eye[0] = - l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 0]); - face.left_eye[1] = - l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 1]); - face.right_eye[0] = - l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 2]); - face.right_eye[1] = - l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 3]); - face.mouth[0] = - l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 4]); - face.mouth[1] = - l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 5]); + face.left_eye[0] = l.mParameters.arrayXToNormalizedWithCrop( + faceLandmarks[i*6 + 0], scalerCrop); + face.left_eye[1] = l.mParameters.arrayYToNormalizedWithCrop( + faceLandmarks[i*6 + 1], scalerCrop); + face.right_eye[0] = l.mParameters.arrayXToNormalizedWithCrop( + faceLandmarks[i*6 + 2], scalerCrop); + face.right_eye[1] = l.mParameters.arrayYToNormalizedWithCrop( + faceLandmarks[i*6 + 3], scalerCrop); + face.mouth[0] = l.mParameters.arrayXToNormalizedWithCrop( + faceLandmarks[i*6 + 4], scalerCrop); + face.mouth[1] = l.mParameters.arrayYToNormalizedWithCrop( + faceLandmarks[i*6 + 5], scalerCrop); } else { face.id = 0; face.left_eye[0] = face.left_eye[1] = -2000; diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp index b433781..34798bf 100644 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp @@ -51,7 +51,7 @@ JpegProcessor::~JpegProcessor() { deleteStream(); } -void JpegProcessor::onFrameAvailable() { +void JpegProcessor::onFrameAvailable(const BufferItem& /*item*/) { Mutex::Autolock l(mInputMutex); if (!mCaptureAvailable) { mCaptureAvailable = true; @@ -145,7 +145,8 @@ status_t JpegProcessor::updateStream(const Parameters ¶ms) { // Create stream for HAL production res = device->createStream(mCaptureWindow, params.pictureWidth, params.pictureHeight, - HAL_PIXEL_FORMAT_BLOB, &mCaptureStreamId); + HAL_PIXEL_FORMAT_BLOB, HAL_DATASPACE_JFIF, + CAMERA3_STREAM_ROTATION_0, &mCaptureStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for capture: " "%s (%d)", __FUNCTION__, mId, diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.h b/services/camera/libcameraservice/api1/client2/JpegProcessor.h index b2c05df..2040b30 100644 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.h +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.h @@ -47,7 +47,7 @@ class JpegProcessor: ~JpegProcessor(); // CpuConsumer listener implementation - void onFrameAvailable(); + void onFrameAvailable(const BufferItem& item); status_t updateStream(const Parameters ¶ms); status_t deleteStream(); diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp index 42a5507..87e0132 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.cpp +++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp @@ -182,9 +182,9 @@ status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) { supportedPreviewFormats += CameraParameters::PIXEL_FORMAT_YUV420SP; break; - // Not advertizing JPEG, RAW_SENSOR, etc, for preview formats + // Not advertizing JPEG, RAW16, etc, for preview formats case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: - case HAL_PIXEL_FORMAT_RAW_SENSOR: + case HAL_PIXEL_FORMAT_RAW16: case HAL_PIXEL_FORMAT_BLOB: addComma = false; break; @@ -596,6 +596,10 @@ status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) { supportedSceneModes += CameraParameters::SCENE_MODE_BARCODE; break; + case ANDROID_CONTROL_SCENE_MODE_HDR: + supportedSceneModes += + CameraParameters::SCENE_MODE_HDR; + break; default: ALOGW("%s: Camera %d: Unknown scene mode value: %d", __FUNCTION__, cameraId, @@ -2203,6 +2207,10 @@ status_t Parameters::recoverOverriddenJpegSize() { return OK; } +bool Parameters::isJpegSizeOverridden() { + return pictureSizeOverriden; +} + const char* Parameters::getStateName(State state) { #define CASE_ENUM_TO_CHAR(x) case x: return(#x); break; switch(state) { @@ -2245,7 +2253,7 @@ const char* Parameters::formatEnumToString(int format) { case HAL_PIXEL_FORMAT_RGBA_8888: // RGBA8888 fmt = CameraParameters::PIXEL_FORMAT_RGBA8888; break; - case HAL_PIXEL_FORMAT_RAW_SENSOR: + case HAL_PIXEL_FORMAT_RAW16: ALOGW("Raw sensor preview format requested."); fmt = CameraParameters::PIXEL_FORMAT_BAYER_RGGB; break; @@ -2382,6 +2390,8 @@ int Parameters::sceneModeStringToEnum(const char *sceneMode) { ANDROID_CONTROL_SCENE_MODE_CANDLELIGHT : !strcmp(sceneMode, CameraParameters::SCENE_MODE_BARCODE) ? ANDROID_CONTROL_SCENE_MODE_BARCODE: + !strcmp(sceneMode, CameraParameters::SCENE_MODE_HDR) ? + ANDROID_CONTROL_SCENE_MODE_HDR: -1; } @@ -2619,75 +2629,71 @@ int Parameters::normalizedYToCrop(int y) const { return (y + 1000) * (previewCrop.height - 1) / 2000; } -int Parameters::arrayXToCrop(int x) const { - CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); - return x - previewCrop.left; -} - -int Parameters::arrayYToCrop(int y) const { - CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); - return y - previewCrop.top; -} +int Parameters::normalizedXToArray(int x) const { -int Parameters::cropXToNormalized(int x) const { - CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); - return x * 2000 / (previewCrop.width - 1) - 1000; -} + // Work-around for HAL pre-scaling the coordinates themselves + if (quirks.meteringCropRegion) { + return (x + 1000) * (fastInfo.arrayWidth - 1) / 2000; + } -int Parameters::cropYToNormalized(int y) const { - CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); - return y * 2000 / (previewCrop.height - 1) - 1000; + return cropXToArray(normalizedXToCrop(x)); } -int Parameters::arrayXToNormalized(int width) const { - int ret = cropXToNormalized(arrayXToCrop(width)); - - ALOG_ASSERT(ret >= -1000, "Calculated normalized value out of " - "lower bounds %d", ret); - ALOG_ASSERT(ret <= 1000, "Calculated normalized value out of " - "upper bounds %d", ret); - +int Parameters::normalizedYToArray(int y) const { // Work-around for HAL pre-scaling the coordinates themselves if (quirks.meteringCropRegion) { - return width * 2000 / (fastInfo.arrayWidth - 1) - 1000; + return (y + 1000) * (fastInfo.arrayHeight - 1) / 2000; } - return ret; + return cropYToArray(normalizedYToCrop(y)); } -int Parameters::arrayYToNormalized(int height) const { - int ret = cropYToNormalized(arrayYToCrop(height)); - ALOG_ASSERT(ret >= -1000, "Calculated normalized value out of lower bounds" - " %d", ret); - ALOG_ASSERT(ret <= 1000, "Calculated normalized value out of upper bounds" - " %d", ret); +Parameters::CropRegion Parameters::calculatePreviewCrop( + const CropRegion &scalerCrop) const { + float left, top, width, height; + float previewAspect = static_cast<float>(previewWidth) / previewHeight; + float cropAspect = scalerCrop.width / scalerCrop.height; - // Work-around for HAL pre-scaling the coordinates themselves - if (quirks.meteringCropRegion) { - return height * 2000 / (fastInfo.arrayHeight - 1) - 1000; + if (previewAspect > cropAspect) { + width = scalerCrop.width; + height = cropAspect * scalerCrop.height / previewAspect; + + left = scalerCrop.left; + top = scalerCrop.top + (scalerCrop.height - height) / 2; + } else { + width = previewAspect * scalerCrop.width / cropAspect; + height = scalerCrop.height; + + left = scalerCrop.left + (scalerCrop.width - width) / 2; + top = scalerCrop.top; } - return ret; -} + CropRegion previewCrop = {left, top, width, height}; -int Parameters::normalizedXToArray(int x) const { + return previewCrop; +} +int Parameters::arrayXToNormalizedWithCrop(int x, + const CropRegion &scalerCrop) const { // Work-around for HAL pre-scaling the coordinates themselves if (quirks.meteringCropRegion) { - return (x + 1000) * (fastInfo.arrayWidth - 1) / 2000; + return x * 2000 / (fastInfo.arrayWidth - 1) - 1000; + } else { + CropRegion previewCrop = calculatePreviewCrop(scalerCrop); + return (x - previewCrop.left) * 2000 / (previewCrop.width - 1) - 1000; } - - return cropXToArray(normalizedXToCrop(x)); } -int Parameters::normalizedYToArray(int y) const { +int Parameters::arrayYToNormalizedWithCrop(int y, + const CropRegion &scalerCrop) const { // Work-around for HAL pre-scaling the coordinates themselves if (quirks.meteringCropRegion) { - return (y + 1000) * (fastInfo.arrayHeight - 1) / 2000; + return y * 2000 / (fastInfo.arrayHeight - 1) - 1000; + } else { + CropRegion previewCrop = calculatePreviewCrop(scalerCrop); + return (y - previewCrop.top) * 2000 / (previewCrop.height - 1) - 1000; } - - return cropYToArray(normalizedYToCrop(y)); } status_t Parameters::getFilteredSizes(Size limit, Vector<Size> *sizes) { diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h index 815cc55..e628a7e 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.h +++ b/services/camera/libcameraservice/api1/client2/Parameters.h @@ -19,11 +19,13 @@ #include <system/graphics.h> +#include <utils/Compat.h> #include <utils/Errors.h> +#include <utils/KeyedVector.h> #include <utils/Mutex.h> #include <utils/String8.h> #include <utils/Vector.h> -#include <utils/KeyedVector.h> + #include <camera/CameraParameters.h> #include <camera/CameraParameters2.h> #include <camera/CameraMetadata.h> @@ -187,7 +189,7 @@ struct Parameters { static const int MAX_INITIAL_PREVIEW_WIDTH = 1920; static const int MAX_INITIAL_PREVIEW_HEIGHT = 1080; // Aspect ratio tolerance - static const float ASPECT_RATIO_TOLERANCE = 0.001; + static const CONSTEXPR float ASPECT_RATIO_TOLERANCE = 0.001; // Full static camera info, object owned by someone else, such as // Camera2Device. @@ -266,6 +268,8 @@ struct Parameters { status_t overrideJpegSizeByVideoSize(); // Recover overridden jpeg size. Called during stopRecording. status_t recoverOverriddenJpegSize(); + // if video snapshot size is currently overridden + bool isJpegSizeOverridden(); // Calculate the crop region rectangle based on current stream sizes struct CropRegion { @@ -325,13 +329,17 @@ struct Parameters { // Note that this doesn't apply to the (deprecated) single FPS value. static const int kFpsToApiScale = 1000; - // Transform between (-1000,-1000)-(1000,1000) normalized coords from camera - // API and HAL2 (0,0)-(activePixelArray.width/height) coordinates - int arrayXToNormalized(int width) const; - int arrayYToNormalized(int height) const; + // Transform from (-1000,-1000)-(1000,1000) normalized coords from camera + // API to HAL2 (0,0)-(activePixelArray.width/height) coordinates int normalizedXToArray(int x) const; int normalizedYToArray(int y) const; + // Transform from HAL3 (0,0)-(activePixelArray.width/height) coordinates to + // (-1000,-1000)-(1000,1000) normalized coordinates given a scaler crop + // region. + int arrayXToNormalizedWithCrop(int x, const CropRegion &scalerCrop) const; + int arrayYToNormalizedWithCrop(int y, const CropRegion &scalerCrop) const; + struct Range { int min; int max; @@ -341,20 +349,20 @@ struct Parameters { private: - // Convert between HAL2 sensor array coordinates and - // viewfinder crop-region relative array coordinates + // Convert from viewfinder crop-region relative array coordinates + // to HAL2 sensor array coordinates int cropXToArray(int x) const; int cropYToArray(int y) const; - int arrayXToCrop(int x) const; - int arrayYToCrop(int y) const; - // Convert between viewfinder crop-region relative array coordinates - // and camera API (-1000,1000)-(1000,1000) normalized coords - int cropXToNormalized(int x) const; - int cropYToNormalized(int y) const; + // Convert from camera API (-1000,1000)-(1000,1000) normalized coords + // to viewfinder crop-region relative array coordinates int normalizedXToCrop(int x) const; int normalizedYToCrop(int y) const; + // Given a scaler crop region, calculate preview crop region based on + // preview aspect ratio. + CropRegion calculatePreviewCrop(const CropRegion &scalerCrop) const; + Vector<Size> availablePreviewSizes; Vector<Size> availableVideoSizes; // Get size list (that are no larger than limit) from static metadata. diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp index 9e7fff8..b6071f6 100644 --- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp @@ -27,6 +27,7 @@ #include <utils/Log.h> #include <utils/Trace.h> +#include <gui/BufferItem.h> #include <gui/Surface.h> #include <media/hardware/MetadataBufferType.h> @@ -181,7 +182,8 @@ status_t StreamingProcessor::updatePreviewStream(const Parameters ¶ms) { if (mPreviewStreamId == NO_STREAM) { res = device->createStream(mPreviewWindow, params.previewWidth, params.previewHeight, - CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, &mPreviewStreamId); + CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_UNKNOWN, + CAMERA3_STREAM_ROTATION_0, &mPreviewStreamId); if (res != OK) { ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)", __FUNCTION__, mId, strerror(-res), res); @@ -420,9 +422,12 @@ status_t StreamingProcessor::updateRecordingStream(const Parameters ¶ms) { if (mRecordingStreamId == NO_STREAM) { mRecordingFrameCount = 0; + // Selecting BT.709 colorspace by default + // TODO: Wire this in from encoder side res = device->createStream(mRecordingWindow, params.videoWidth, params.videoHeight, - CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, &mRecordingStreamId); + CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_BT709, + CAMERA3_STREAM_ROTATION_0, &mRecordingStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for recording: " "%s (%d)", __FUNCTION__, mId, @@ -635,7 +640,7 @@ status_t StreamingProcessor::incrementStreamingIds() { return OK; } -void StreamingProcessor::onFrameAvailable() { +void StreamingProcessor::onFrameAvailable(const BufferItem& /*item*/) { ATRACE_CALL(); Mutex::Autolock l(mMutex); if (!mRecordingFrameAvailable) { @@ -675,7 +680,7 @@ status_t StreamingProcessor::processRecordingFrame() { sp<Camera2Client> client = mClient.promote(); if (client == 0) { // Discard frames during shutdown - BufferItemConsumer::BufferItem imgBuffer; + BufferItem imgBuffer; res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0); if (res != OK) { if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { @@ -693,7 +698,7 @@ status_t StreamingProcessor::processRecordingFrame() { with Camera2Client code calling into StreamingProcessor */ SharedParameters::Lock l(client->getParameters()); Mutex::Autolock m(mMutex); - BufferItemConsumer::BufferItem imgBuffer; + BufferItem imgBuffer; res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0); if (res != OK) { if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { @@ -819,8 +824,7 @@ void StreamingProcessor::releaseRecordingFrame(const sp<IMemory>& mem) { size_t itemIndex; for (itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) { - const BufferItemConsumer::BufferItem item = - mRecordingBuffers[itemIndex]; + const BufferItem item = mRecordingBuffers[itemIndex]; if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT && item.mGraphicBuffer->handle == imgHandle) { break; @@ -864,8 +868,7 @@ void StreamingProcessor::releaseAllRecordingFramesLocked() { size_t releasedCount = 0; for (size_t itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) { - const BufferItemConsumer::BufferItem item = - mRecordingBuffers[itemIndex]; + const BufferItem item = mRecordingBuffers[itemIndex]; if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT) { res = mRecordingConsumer->releaseBuffer(mRecordingBuffers[itemIndex]); if (res != OK) { diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h index 8466af4..2474062 100644 --- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h +++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h @@ -80,7 +80,7 @@ class StreamingProcessor: status_t incrementStreamingIds(); // Callback for new recording frames from HAL - virtual void onFrameAvailable(); + virtual void onFrameAvailable(const BufferItem& item); // Callback from stagefright which returns used recording frames void releaseRecordingFrame(const sp<IMemory>& mem); @@ -124,7 +124,7 @@ class StreamingProcessor: static const size_t kDefaultRecordingHeapCount = 8; size_t mRecordingHeapCount; - Vector<BufferItemConsumer::BufferItem> mRecordingBuffers; + Vector<BufferItem> mRecordingBuffers; size_t mRecordingHeapHead, mRecordingHeapFree; virtual bool threadLoop(); diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp index 8f78103..a03f9c7 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp @@ -66,7 +66,7 @@ ZslProcessor::~ZslProcessor() { disconnect(); } -void ZslProcessor::onFrameAvailable() { +void ZslProcessor::onFrameAvailable(const BufferItem& /*item*/) { Mutex::Autolock l(mInputMutex); if (!mZslBufferAvailable) { mZslBufferAvailable = true; @@ -185,8 +185,8 @@ status_t ZslProcessor::updateStream(const Parameters ¶ms) { (int)CAMERA2_HAL_PIXEL_FORMAT_ZSL : (int)HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; res = device->createStream(mZslWindow, - params.fastInfo.arrayWidth, params.fastInfo.arrayHeight, - streamType, &mZslStreamId); + params.fastInfo.arrayWidth, params.fastInfo.arrayHeight, streamType, + HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mZslStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for ZSL: " "%s (%d)", __FUNCTION__, mId, @@ -440,7 +440,7 @@ status_t ZslProcessor::processNewZslBuffer() { zslConsumer = mZslConsumer; } ALOGVV("Trying to get next buffer"); - BufferItemConsumer::BufferItem item; + BufferItem item; res = zslConsumer->acquireBuffer(&item, 0); if (res != OK) { if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.h b/services/camera/libcameraservice/api1/client2/ZslProcessor.h index b6533cf..5f50d7b 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor.h +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.h @@ -22,6 +22,7 @@ #include <utils/Vector.h> #include <utils/Mutex.h> #include <utils/Condition.h> +#include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> #include <camera/CameraMetadata.h> #include <camera/CaptureResult.h> @@ -53,7 +54,7 @@ class ZslProcessor: ~ZslProcessor(); // From mZslConsumer - virtual void onFrameAvailable(); + virtual void onFrameAvailable(const BufferItem& item); // From FrameProcessor virtual void onResultAvailable(const CaptureResult &result); @@ -103,7 +104,7 @@ class ZslProcessor: sp<ANativeWindow> mZslWindow; struct ZslPair { - BufferItemConsumer::BufferItem buffer; + BufferItem buffer; CameraMetadata frame; }; diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp index f110b66..470a6d6 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp @@ -592,7 +592,7 @@ nsecs_t ZslProcessor3::getCandidateTimestampLocked(size_t* metadataIdx) const { if (afState != ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED && afState != ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED && afState != ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) { - ALOGW("%s: ZSL queue frame AF state is %d is not good for capture, skip it", + ALOGVV("%s: ZSL queue frame AF state is %d is not good for capture, skip it", __FUNCTION__, afState); continue; } diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h index fc9f70c..2960478 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h @@ -22,6 +22,7 @@ #include <utils/Vector.h> #include <utils/Mutex.h> #include <utils/Condition.h> +#include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> #include <camera/CameraMetadata.h> @@ -104,7 +105,7 @@ class ZslProcessor3 : sp<camera3::Camera3ZslStream> mZslStream; struct ZslPair { - BufferItemConsumer::BufferItem buffer; + BufferItem buffer; CameraMetadata frame; }; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index e3301aa..0e2311c 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -42,8 +42,14 @@ CameraDeviceClientBase::CameraDeviceClientBase( int clientPid, uid_t clientUid, int servicePid) : - BasicClient(cameraService, remoteCallback->asBinder(), clientPackageName, - cameraId, cameraFacing, clientPid, clientUid, servicePid), + BasicClient(cameraService, + IInterface::asBinder(remoteCallback), + clientPackageName, + cameraId, + cameraFacing, + clientPid, + clientUid, + servicePid), mRemoteCallback(remoteCallback) { } @@ -65,7 +71,7 @@ CameraDeviceClient::CameraDeviceClient(const sp<CameraService>& cameraService, ALOGI("CameraDeviceClient %d: Opened", cameraId); } -status_t CameraDeviceClient::initialize(camera_module_t *module) +status_t CameraDeviceClient::initialize(CameraModule *module) { ATRACE_CALL(); status_t res; @@ -157,7 +163,7 @@ status_t CameraDeviceClient::submitRequestList(List<sp<CaptureRequest> > request if (surface == 0) continue; sp<IGraphicBufferProducer> gbp = surface->getIGraphicBufferProducer(); - int idx = mStreamMap.indexOfKey(gbp->asBinder()); + int idx = mStreamMap.indexOfKey(IInterface::asBinder(gbp)); // Trying to submit request with surface that wasn't created if (idx == NAME_NOT_FOUND) { @@ -308,17 +314,17 @@ status_t CameraDeviceClient::deleteStream(int streamId) { return res; } -status_t CameraDeviceClient::createStream(int width, int height, int format, - const sp<IGraphicBufferProducer>& bufferProducer) +status_t CameraDeviceClient::createStream(const OutputConfiguration &outputConfiguration) { ATRACE_CALL(); - ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; Mutex::Autolock icl(mBinderSerializationLock); + + sp<IGraphicBufferProducer> bufferProducer = outputConfiguration.getGraphicBufferProducer(); if (bufferProducer == NULL) { ALOGE("%s: bufferProducer must not be null", __FUNCTION__); return BAD_VALUE; @@ -327,7 +333,7 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, // Don't create multiple streams for the same target surface { - ssize_t index = mStreamMap.indexOfKey(bufferProducer->asBinder()); + ssize_t index = mStreamMap.indexOfKey(IInterface::asBinder(bufferProducer)); if (index != NAME_NOT_FOUND) { ALOGW("%s: Camera %d: Buffer producer already has a stream for it " "(ID %zd)", @@ -353,14 +359,19 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, useAsync = true; } - sp<IBinder> binder; - sp<ANativeWindow> anw; - if (bufferProducer != 0) { - binder = bufferProducer->asBinder(); - anw = new Surface(bufferProducer, useAsync); - } + int32_t disallowedFlags = GraphicBuffer::USAGE_HW_VIDEO_ENCODER | + GRALLOC_USAGE_RENDERSCRIPT; + int32_t allowedFlags = GraphicBuffer::USAGE_SW_READ_MASK | + GraphicBuffer::USAGE_HW_TEXTURE | + GraphicBuffer::USAGE_HW_COMPOSER; + bool flexibleConsumer = (consumerUsage & disallowedFlags) == 0 && + (consumerUsage & allowedFlags) != 0; + + sp<IBinder> binder = IInterface::asBinder(bufferProducer); + sp<ANativeWindow> anw = new Surface(bufferProducer, useAsync); - // TODO: remove w,h,f since we are ignoring them + int width, height, format; + android_dataspace dataSpace; if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) { ALOGE("%s: Camera %d: Failed to query Surface width", __FUNCTION__, @@ -377,25 +388,38 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, mCameraId); return res; } + if ((res = anw->query(anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE, + reinterpret_cast<int*>(&dataSpace))) != OK) { + ALOGE("%s: Camera %d: Failed to query Surface dataSpace", __FUNCTION__, + mCameraId); + return res; + } // FIXME: remove this override since the default format should be // IMPLEMENTATION_DEFINED. b/9487482 if (format >= HAL_PIXEL_FORMAT_RGBA_8888 && format <= HAL_PIXEL_FORMAT_BGRA_8888) { - ALOGW("%s: Camera %d: Overriding format 0x%x to IMPLEMENTATION_DEFINED", + ALOGW("%s: Camera %d: Overriding format %#x to IMPLEMENTATION_DEFINED", __FUNCTION__, mCameraId, format); format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; } - // TODO: add startConfigure/stopConfigure call to CameraDeviceBase - // this will make it so Camera3Device doesn't call configure_streams - // after each call, but only once we are done with all. + // Round dimensions to the nearest dimensions available for this format + if (flexibleConsumer && !CameraDeviceClient::roundBufferDimensionNearest(width, height, + format, dataSpace, mDevice->info(), /*out*/&width, /*out*/&height)) { + ALOGE("%s: No stream configurations with the format %#x defined, failed to create stream.", + __FUNCTION__, format); + return BAD_VALUE; + } int streamId = -1; - res = mDevice->createStream(anw, width, height, format, &streamId); + res = mDevice->createStream(anw, width, height, format, dataSpace, + static_cast<camera3_stream_rotation_t> + (outputConfiguration.getRotation()), + &streamId); if (res == OK) { - mStreamMap.add(bufferProducer->asBinder(), streamId); + mStreamMap.add(binder, streamId); ALOGV("%s: Camera %d: Successfully created a new stream ID %d", __FUNCTION__, mCameraId, streamId); @@ -425,6 +449,64 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, return res; } + +bool CameraDeviceClient::roundBufferDimensionNearest(int32_t width, int32_t height, + int32_t format, android_dataspace dataSpace, const CameraMetadata& info, + /*out*/int32_t* outWidth, /*out*/int32_t* outHeight) { + + camera_metadata_ro_entry streamConfigs = + (dataSpace == HAL_DATASPACE_DEPTH) ? + info.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS) : + info.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + + int32_t bestWidth = -1; + int32_t bestHeight = -1; + + // Iterate through listed stream configurations and find the one with the smallest euclidean + // distance from the given dimensions for the given format. + for (size_t i = 0; i < streamConfigs.count; i += 4) { + int32_t fmt = streamConfigs.data.i32[i]; + int32_t w = streamConfigs.data.i32[i + 1]; + int32_t h = streamConfigs.data.i32[i + 2]; + + // Ignore input/output type for now + if (fmt == format) { + if (w == width && h == height) { + bestWidth = width; + bestHeight = height; + break; + } else if (w <= ROUNDING_WIDTH_CAP && (bestWidth == -1 || + CameraDeviceClient::euclidDistSquare(w, h, width, height) < + CameraDeviceClient::euclidDistSquare(bestWidth, bestHeight, width, height))) { + bestWidth = w; + bestHeight = h; + } + } + } + + if (bestWidth == -1) { + // Return false if no configurations for this format were listed + return false; + } + + // Set the outputs to the closet width/height + if (outWidth != NULL) { + *outWidth = bestWidth; + } + if (outHeight != NULL) { + *outHeight = bestHeight; + } + + // Return true if at least one configuration for this format was listed + return true; +} + +int64_t CameraDeviceClient::euclidDistSquare(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { + int64_t d0 = x0 - x1; + int64_t d1 = y0 - y1; + return d0 * d0 + d1 * d1; +} + // Create a request object from a template. status_t CameraDeviceClient::createDefaultRequest(int templateId, /*out*/ @@ -514,10 +596,9 @@ status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) { String8 result; result.appendFormat("CameraDeviceClient[%d] (%p) dump:\n", mCameraId, - getRemoteCallback()->asBinder().get()); - result.appendFormat(" Current client: %s (PID %d, UID %u)\n", - String8(mClientPackageName).string(), - mClientPid, mClientUid); + (getRemoteCallback() != NULL ? + IInterface::asBinder(getRemoteCallback()).get() : NULL) ); + result.appendFormat(" Current client UID %u\n", mClientUid); result.append(" State:\n"); result.appendFormat(" Request ID counter: %d\n", mRequestIdCounter); diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h index 9981dfe..a3dbb90 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.h +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h @@ -19,6 +19,7 @@ #include <camera/camera2/ICameraDeviceUser.h> #include <camera/camera2/ICameraDeviceCallbacks.h> +#include <camera/camera2/OutputConfiguration.h> #include "CameraService.h" #include "common/FrameProcessorBase.h" @@ -83,11 +84,7 @@ public: // Returns -EBUSY if device is not idle virtual status_t deleteStream(int streamId); - virtual status_t createStream( - int width, - int height, - int format, - const sp<IGraphicBufferProducer>& bufferProducer); + virtual status_t createStream(const OutputConfiguration &outputConfiguration); // Create a request object from a template. virtual status_t createDefaultRequest(int templateId, @@ -119,7 +116,7 @@ public: int servicePid); virtual ~CameraDeviceClient(); - virtual status_t initialize(camera_module_t *module); + virtual status_t initialize(CameraModule *module); virtual status_t dump(int fd, const Vector<String16>& args); @@ -154,6 +151,16 @@ private: /** Utility members */ bool enforceRequestPermissions(CameraMetadata& metadata); + // Find the square of the euclidean distance between two points + static int64_t euclidDistSquare(int32_t x0, int32_t y0, int32_t x1, int32_t y1); + + // Find the closest dimensions for a given format in available stream configurations with + // a width <= ROUNDING_WIDTH_CAP + static const int32_t ROUNDING_WIDTH_CAP = 1080; + static bool roundBufferDimensionNearest(int32_t width, int32_t height, int32_t format, + android_dataspace dataSpace, const CameraMetadata& info, + /*out*/int32_t* outWidth, /*out*/int32_t* outHeight); + // IGraphicsBufferProducer binder -> Stream ID KeyedVector<sp<IBinder>, int> mStreamMap; diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp index 2ea460f..a977494 100644 --- a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp +++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp @@ -50,7 +50,7 @@ ProCamera2Client::ProCamera2Client(const sp<CameraService>& cameraService, mExclusiveLock = false; } -status_t ProCamera2Client::initialize(camera_module_t *module) +status_t ProCamera2Client::initialize(CameraModule *module) { ATRACE_CALL(); status_t res; @@ -276,11 +276,12 @@ status_t ProCamera2Client::createStream(int width, int height, int format, sp<IBinder> binder; sp<ANativeWindow> window; if (bufferProducer != 0) { - binder = bufferProducer->asBinder(); + binder = IInterface::asBinder(bufferProducer); window = new Surface(bufferProducer); } return mDevice->createStream(window, width, height, format, + HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, streamId); } @@ -334,7 +335,8 @@ status_t ProCamera2Client::dump(int fd, const Vector<String16>& args) { String8 result; result.appendFormat("ProCamera2Client[%d] (%p) PID: %d, dump:\n", mCameraId, - getRemoteCallback()->asBinder().get(), + (getRemoteCallback() != NULL ? + IInterface::asBinder(getRemoteCallback()).get() : NULL), mClientPid); result.append(" State:\n"); write(fd, result.string(), result.size()); diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.h b/services/camera/libcameraservice/api_pro/ProCamera2Client.h index 9d83122..7f5f6ac 100644 --- a/services/camera/libcameraservice/api_pro/ProCamera2Client.h +++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.h @@ -85,7 +85,7 @@ public: int servicePid); virtual ~ProCamera2Client(); - virtual status_t initialize(camera_module_t *module); + virtual status_t initialize(CameraModule *module); virtual status_t dump(int fd, const Vector<String16>& args); diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp index d6db151..0415d67 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp +++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp @@ -78,7 +78,7 @@ status_t Camera2ClientBase<TClientBase>::checkPid(const char* checkLocation) } template <typename TClientBase> -status_t Camera2ClientBase<TClientBase>::initialize(camera_module_t *module) { +status_t Camera2ClientBase<TClientBase>::initialize(CameraModule *module) { ATRACE_CALL(); ALOGV("%s: Initializing client for camera %d", __FUNCTION__, TClientBase::mCameraId); @@ -128,7 +128,8 @@ status_t Camera2ClientBase<TClientBase>::dump(int fd, String8 result; result.appendFormat("Camera2ClientBase[%d] (%p) PID: %d, dump:\n", TClientBase::mCameraId, - TClientBase::getRemoteCallback()->asBinder().get(), + (TClientBase::getRemoteCallback() != NULL ? + IInterface::asBinder(TClientBase::getRemoteCallback()).get() : NULL), TClientBase::mClientPid); result.append(" State: "); diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h index d198e4e..eb21d55 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.h +++ b/services/camera/libcameraservice/common/Camera2ClientBase.h @@ -18,6 +18,7 @@ #define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_BASE_H #include "common/CameraDeviceBase.h" +#include "common/CameraModule.h" #include "camera/CaptureResult.h" namespace android { @@ -55,7 +56,7 @@ public: int servicePid); virtual ~Camera2ClientBase(); - virtual status_t initialize(camera_module_t *module); + virtual status_t initialize(CameraModule *module); virtual status_t dump(int fd, const Vector<String16>& args); /** @@ -111,7 +112,7 @@ protected: pid_t mInitialClientPid; virtual sp<IBinder> asBinderWrapper() { - return IInterface::asBinder(); + return IInterface::asBinder(this); } virtual status_t dumpDevice(int fd, const Vector<String16>& args); diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h index d26e20c..fe55b9e 100644 --- a/services/camera/libcameraservice/common/CameraDeviceBase.h +++ b/services/camera/libcameraservice/common/CameraDeviceBase.h @@ -29,6 +29,7 @@ #include "hardware/camera3.h" #include "camera/CameraMetadata.h" #include "camera/CaptureResult.h" +#include "common/CameraModule.h" namespace android { @@ -45,7 +46,7 @@ class CameraDeviceBase : public virtual RefBase { */ virtual int getId() const = 0; - virtual status_t initialize(camera_module_t *module) = 0; + virtual status_t initialize(CameraModule *module) = 0; virtual status_t disconnect() = 0; virtual status_t dump(int fd, const Vector<String16> &args) = 0; @@ -99,17 +100,14 @@ class CameraDeviceBase : public virtual RefBase { nsecs_t timeout) = 0; /** - * Create an output stream of the requested size and format. + * Create an output stream of the requested size, format, rotation and dataspace * - * If format is CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, then the HAL device selects - * an appropriate format; it can be queried with getStreamInfo. - * - * If format is HAL_PIXEL_FORMAT_COMPRESSED, the size parameter must be - * equal to the size in bytes of the buffers to allocate for the stream. For - * other formats, the size parameter is ignored. + * For HAL_PIXEL_FORMAT_BLOB formats, the width and height should be the + * logical dimensions of the buffer, not the number of bytes. */ virtual status_t createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, int *id) = 0; + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id) = 0; /** * Create an input reprocess stream that uses buffers from an existing diff --git a/services/camera/libcameraservice/common/CameraModule.cpp b/services/camera/libcameraservice/common/CameraModule.cpp new file mode 100644 index 0000000..5f767ad --- /dev/null +++ b/services/camera/libcameraservice/common/CameraModule.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "CameraModule" +//#define LOG_NDEBUG 0 + +#include "CameraModule.h" + +namespace android { + +void CameraModule::deriveCameraCharacteristicsKeys( + uint32_t deviceVersion, CameraMetadata &chars) { + // HAL1 devices should not reach here + if (deviceVersion < CAMERA_DEVICE_API_VERSION_2_0) { + ALOGV("%s: Cannot derive keys for HAL version < 2.0"); + return; + } + + // Keys added in HAL3.3 + if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_3) { + Vector<uint8_t> controlModes; + uint8_t data = ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE; + chars.update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &data, /*count*/1); + data = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE; + chars.update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &data, /*count*/1); + controlModes.push(ANDROID_CONTROL_MODE_OFF); + controlModes.push(ANDROID_CONTROL_MODE_AUTO); + camera_metadata_entry entry = chars.find(ANDROID_CONTROL_AVAILABLE_SCENE_MODES); + if (entry.count > 1 || entry.data.u8[0] != ANDROID_CONTROL_SCENE_MODE_DISABLED) { + controlModes.push(ANDROID_CONTROL_MODE_USE_SCENE_MODE); + } + chars.update(ANDROID_CONTROL_AVAILABLE_MODES, controlModes); + } + return; +} + +CameraModule::CameraModule(camera_module_t *module) { + if (module == NULL) { + ALOGE("%s: camera hardware module must not be null", __FUNCTION__); + assert(0); + } + + mModule = module; + for (int i = 0; i < MAX_CAMERAS_PER_MODULE; i++) { + mCameraInfoCached[i] = false; + } +} + +int CameraModule::getCameraInfo(int cameraId, struct camera_info *info) { + Mutex::Autolock lock(mCameraInfoLock); + if (cameraId < 0 || cameraId >= MAX_CAMERAS_PER_MODULE) { + ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId); + return -EINVAL; + } + + // Only override static_camera_characteristics for API2 devices + int apiVersion = mModule->common.module_api_version; + if (apiVersion < CAMERA_MODULE_API_VERSION_2_0) { + return mModule->get_camera_info(cameraId, info); + } + + camera_info &wrappedInfo = mCameraInfo[cameraId]; + if (!mCameraInfoCached[cameraId]) { + camera_info rawInfo; + int ret = mModule->get_camera_info(cameraId, &rawInfo); + if (ret != 0) { + return ret; + } + CameraMetadata &m = mCameraCharacteristics[cameraId]; + m = rawInfo.static_camera_characteristics; + deriveCameraCharacteristicsKeys(rawInfo.device_version, m); + wrappedInfo = rawInfo; + wrappedInfo.static_camera_characteristics = m.getAndLock(); + mCameraInfoCached[cameraId] = true; + } + *info = wrappedInfo; + return 0; +} + +int CameraModule::open(const char* id, struct hw_device_t** device) { + return filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device)); +} + +int CameraModule::openLegacy( + const char* id, uint32_t halVersion, struct hw_device_t** device) { + return mModule->open_legacy(&mModule->common, id, halVersion, device); +} + +const hw_module_t* CameraModule::getRawModule() { + return &mModule->common; +} + +int CameraModule::getNumberOfCameras() { + return mModule->get_number_of_cameras(); +} + +int CameraModule::setCallbacks(const camera_module_callbacks_t *callbacks) { + return mModule->set_callbacks(callbacks); +} + +bool CameraModule::isVendorTagDefined() { + return mModule->get_vendor_tag_ops != NULL; +} + +void CameraModule::getVendorTagOps(vendor_tag_ops_t* ops) { + if (mModule->get_vendor_tag_ops) { + mModule->get_vendor_tag_ops(ops); + } +} + +int CameraModule::setTorchMode(const char* camera_id, bool enable) { + return mModule->set_torch_mode(camera_id, enable); +} + + +status_t CameraModule::filterOpenErrorCode(status_t err) { + switch(err) { + case NO_ERROR: + case -EBUSY: + case -EINVAL: + case -EUSERS: + return err; + default: + break; + } + return -ENODEV; +} + + +}; // namespace android + diff --git a/services/camera/libcameraservice/common/CameraModule.h b/services/camera/libcameraservice/common/CameraModule.h new file mode 100644 index 0000000..16207aa --- /dev/null +++ b/services/camera/libcameraservice/common/CameraModule.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_CAMERAMODULE_H +#define ANDROID_SERVERS_CAMERA_CAMERAMODULE_H + +#include <hardware/camera.h> +#include <camera/CameraMetadata.h> +#include <utils/Mutex.h> + +/* This needs to be increased if we can have more cameras */ +#define MAX_CAMERAS_PER_MODULE 2 + + +namespace android { +/** + * A wrapper class for HAL camera module. + * + * This class wraps camera_module_t returned from HAL to provide a wrapped + * get_camera_info implementation which CameraService generates some + * camera characteristics keys defined in newer HAL version on an older HAL. + */ +class CameraModule { +public: + CameraModule(camera_module_t *module); + + const hw_module_t* getRawModule(); + int getCameraInfo(int cameraId, struct camera_info *info); + int getNumberOfCameras(void); + int open(const char* id, struct hw_device_t** device); + int openLegacy(const char* id, uint32_t halVersion, struct hw_device_t** device); + int setCallbacks(const camera_module_callbacks_t *callbacks); + bool isVendorTagDefined(); + void getVendorTagOps(vendor_tag_ops_t* ops); + int setTorchMode(const char* camera_id, bool enable); + +private: + // Derive camera characteristics keys defined after HAL device version + static void deriveCameraCharacteristicsKeys(uint32_t deviceVersion, CameraMetadata &chars); + status_t filterOpenErrorCode(status_t err); + + camera_module_t *mModule; + CameraMetadata mCameraCharacteristics[MAX_CAMERAS_PER_MODULE]; + camera_info mCameraInfo[MAX_CAMERAS_PER_MODULE]; + bool mCameraInfoCached[MAX_CAMERAS_PER_MODULE]; + Mutex mCameraInfoLock; +}; + +} // namespace android + +#endif + diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h index 6386838..f5ebbf8 100644 --- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h +++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h @@ -89,24 +89,22 @@ public: } } - status_t initialize(hw_module_t *module) + status_t initialize(CameraModule *module) { ALOGI("Opening camera %s", mName.string()); - camera_module_t *cameraModule = reinterpret_cast<camera_module_t *>(module); camera_info info; - status_t res = cameraModule->get_camera_info(atoi(mName.string()), &info); + status_t res = module->getCameraInfo(atoi(mName.string()), &info); if (res != OK) return res; int rc = OK; - if (module->module_api_version >= CAMERA_MODULE_API_VERSION_2_3 && + if (module->getRawModule()->module_api_version >= CAMERA_MODULE_API_VERSION_2_3 && info.device_version > CAMERA_DEVICE_API_VERSION_1_0) { // Open higher version camera device as HAL1.0 device. - rc = cameraModule->open_legacy(module, mName.string(), - CAMERA_DEVICE_API_VERSION_1_0, - (hw_device_t **)&mDevice); + rc = module->openLegacy(mName.string(), + CAMERA_DEVICE_API_VERSION_1_0, + (hw_device_t **)&mDevice); } else { - rc = CameraService::filterOpenErrorCode(module->methods->open( - module, mName.string(), (hw_device_t **)&mDevice)); + rc = module->open(mName.string(), (hw_device_t **)&mDevice); } if (rc != OK) { ALOGE("Could not open camera %s: %d", mName.string(), rc); @@ -588,7 +586,7 @@ private: #ifndef container_of #define container_of(ptr, type, member) ({ \ - const typeof(((type *) 0)->member) *__mptr = (ptr); \ + const __typeof__(((type *) 0)->member) *__mptr = (ptr); \ (type *) ((char *) __mptr - (char *)(&((type *)0)->member)); }) #endif diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp index 8caadd6..878986b 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.cpp +++ b/services/camera/libcameraservice/device2/Camera2Device.cpp @@ -53,7 +53,7 @@ int Camera2Device::getId() const { return mId; } -status_t Camera2Device::initialize(camera_module_t *module) +status_t Camera2Device::initialize(CameraModule *module) { ATRACE_CALL(); ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId); @@ -68,8 +68,7 @@ status_t Camera2Device::initialize(camera_module_t *module) camera2_device_t *device; - res = CameraService::filterOpenErrorCode(module->common.methods->open( - &module->common, name, reinterpret_cast<hw_device_t**>(&device))); + res = module->open(name, reinterpret_cast<hw_device_t**>(&device)); if (res != OK) { ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__, @@ -87,7 +86,7 @@ status_t Camera2Device::initialize(camera_module_t *module) } camera_info info; - res = module->get_camera_info(mId, &info); + res = module->getCameraInfo(mId, &info); if (res != OK ) return res; if (info.device_version != device->common.version) { @@ -242,7 +241,8 @@ status_t Camera2Device::waitUntilRequestReceived(int32_t requestId, nsecs_t time } status_t Camera2Device::createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, int *id) { + uint32_t width, uint32_t height, int format, + android_dataspace /*dataSpace*/, camera3_stream_rotation_t rotation, int *id) { ATRACE_CALL(); status_t res; ALOGV("%s: E", __FUNCTION__); @@ -793,11 +793,6 @@ status_t Camera2Device::MetadataQueue::setStreamSlot(camera_metadata_t *buf) mStreamSlotCount = 0; return OK; } - camera_metadata_t *buf2 = clone_camera_metadata(buf); - if (!buf2) { - ALOGE("%s: Unable to clone metadata buffer!", __FUNCTION__); - return NO_MEMORY; - } if (mStreamSlotCount > 1) { List<camera_metadata_t*>::iterator deleter = ++mStreamSlot.begin(); @@ -806,9 +801,9 @@ status_t Camera2Device::MetadataQueue::setStreamSlot(camera_metadata_t *buf) } if (mStreamSlotCount == 1) { free_camera_metadata( *(mStreamSlot.begin()) ); - *(mStreamSlot.begin()) = buf2; + *(mStreamSlot.begin()) = buf; } else { - mStreamSlot.push_front(buf2); + mStreamSlot.push_front(buf); mStreamSlotCount = 1; } return signalConsumerLocked(); @@ -827,12 +822,7 @@ status_t Camera2Device::MetadataQueue::setStreamSlot( mStreamSlotCount = 0; for (List<camera_metadata_t*>::const_iterator r = bufs.begin(); r != bufs.end(); r++) { - camera_metadata_t *r2 = clone_camera_metadata(*r); - if (!r2) { - ALOGE("%s: Unable to clone metadata buffer!", __FUNCTION__); - return NO_MEMORY; - } - mStreamSlot.push_back(r2); + mStreamSlot.push_back(*r); mStreamSlotCount++; } return signalConsumerLocked(); diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h index 2a3f1d9..9b32fa6 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.h +++ b/services/camera/libcameraservice/device2/Camera2Device.h @@ -43,7 +43,7 @@ class Camera2Device: public CameraDeviceBase { * CameraDevice interface */ virtual int getId() const; - virtual status_t initialize(camera_module_t *module); + virtual status_t initialize(CameraModule *module); virtual status_t disconnect(); virtual status_t dump(int fd, const Vector<String16>& args); virtual const CameraMetadata& info() const; @@ -57,7 +57,8 @@ class Camera2Device: public CameraDeviceBase { virtual status_t clearStreamingRequest(int64_t *lastFrameNumber = NULL); virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout); virtual status_t createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, int *id); + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id); virtual status_t createReprocessStreamFromStream(int outputId, int *id); virtual status_t getStreamInfo(int id, uint32_t *width, uint32_t *height, uint32_t *format); @@ -124,8 +125,8 @@ class Camera2Device: public CameraDeviceBase { // Set repeating buffer(s); if the queue is empty on a dequeue call, the // queue copies the contents of the stream slot into the queue, and then - // dequeues the first new entry. The metadata buffers passed in are - // copied. + // dequeues the first new entry. The methods take the ownership of the + // metadata buffers passed in. status_t setStreamSlot(camera_metadata_t *buf); status_t setStreamSlot(const List<camera_metadata_t*> &bufs); diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 6a7f9e7..8236788 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -86,7 +86,7 @@ int Camera3Device::getId() const { * CameraDeviceBase interface */ -status_t Camera3Device::initialize(camera_module_t *module) +status_t Camera3Device::initialize(CameraModule *module) { ATRACE_CALL(); Mutex::Autolock il(mInterfaceLock); @@ -106,9 +106,8 @@ status_t Camera3Device::initialize(camera_module_t *module) camera3_device_t *device; ATRACE_BEGIN("camera3->open"); - res = CameraService::filterOpenErrorCode(module->common.methods->open( - &module->common, deviceName.string(), - reinterpret_cast<hw_device_t**>(&device))); + res = module->open(deviceName.string(), + reinterpret_cast<hw_device_t**>(&device)); ATRACE_END(); if (res != OK) { @@ -127,7 +126,7 @@ status_t Camera3Device::initialize(camera_module_t *module) } camera_info info; - res = CameraService::filterGetInfoErrorCode(module->get_camera_info( + res = CameraService::filterGetInfoErrorCode(module->getCameraInfo( mId, &info)); if (res != OK) return res; @@ -361,16 +360,15 @@ ssize_t Camera3Device::getJpegBufferSize(uint32_t width, uint32_t height) const return BAD_VALUE; } maxJpegBufferSize = jpegBufMaxSize.data.i32[0]; + assert(kMinJpegBufferSize < maxJpegBufferSize); // Calculate final jpeg buffer size for the given resolution. float scaleFactor = ((float) (width * height)) / (maxJpegResolution.width * maxJpegResolution.height); - ssize_t jpegBufferSize = scaleFactor * maxJpegBufferSize; - // Bound the buffer size to [MIN_JPEG_BUFFER_SIZE, maxJpegBufferSize]. + ssize_t jpegBufferSize = scaleFactor * (maxJpegBufferSize - kMinJpegBufferSize) + + kMinJpegBufferSize; if (jpegBufferSize > maxJpegBufferSize) { jpegBufferSize = maxJpegBufferSize; - } else if (jpegBufferSize < kMinJpegBufferSize) { - jpegBufferSize = kMinJpegBufferSize; } return jpegBufferSize; @@ -427,7 +425,7 @@ status_t Camera3Device::dump(int fd, const Vector<String16> &args) { InFlightRequest r = mInFlightMap.valueAt(i); lines.appendFormat(" Frame %d | Timestamp: %" PRId64 ", metadata" " arrived: %s, buffers left: %d\n", mInFlightMap.keyAt(i), - r.captureTimestamp, r.haveResultMetadata ? "true" : "false", + r.shutterTimestamp, r.haveResultMetadata ? "true" : "false", r.numBuffersLeft); } } @@ -803,12 +801,13 @@ status_t Camera3Device::createZslStream( } status_t Camera3Device::createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, int *id) { + uint32_t width, uint32_t height, int format, android_dataspace dataSpace, + camera3_stream_rotation_t rotation, int *id) { ATRACE_CALL(); Mutex::Autolock il(mInterfaceLock); Mutex::Autolock l(mLock); - ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d", - mId, mNextStreamId, width, height, format); + ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d", + mId, mNextStreamId, width, height, format, dataSpace, rotation); status_t res; bool wasActive = false; @@ -848,10 +847,10 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer, } newStream = new Camera3OutputStream(mNextStreamId, consumer, - width, height, jpegBufferSize, format); + width, height, jpegBufferSize, format, dataSpace, rotation); } else { newStream = new Camera3OutputStream(mNextStreamId, consumer, - width, height, format); + width, height, format, dataSpace, rotation); } newStream->setStatusTracker(mStatusTracker); @@ -1880,6 +1879,131 @@ bool Camera3Device::insert3AResult(CameraMetadata& result, int32_t tag, return true; } + +void Camera3Device::returnOutputBuffers( + const camera3_stream_buffer_t *outputBuffers, size_t numBuffers, + nsecs_t timestamp) { + for (size_t i = 0; i < numBuffers; i++) + { + Camera3Stream *stream = Camera3Stream::cast(outputBuffers[i].stream); + status_t res = stream->returnBuffer(outputBuffers[i], timestamp); + // Note: stream may be deallocated at this point, if this buffer was + // the last reference to it. + if (res != OK) { + ALOGE("Can't return buffer to its stream: %s (%d)", + strerror(-res), res); + } + } +} + + +void Camera3Device::removeInFlightRequestIfReadyLocked(int idx) { + + const InFlightRequest &request = mInFlightMap.valueAt(idx); + const uint32_t frameNumber = mInFlightMap.keyAt(idx); + + nsecs_t sensorTimestamp = request.sensorTimestamp; + nsecs_t shutterTimestamp = request.shutterTimestamp; + + // Check if it's okay to remove the request from InFlightMap: + // In the case of a successful request: + // all input and output buffers, all result metadata, shutter callback + // arrived. + // In the case of a unsuccessful request: + // all input and output buffers arrived. + if (request.numBuffersLeft == 0 && + (request.requestStatus != OK || + (request.haveResultMetadata && shutterTimestamp != 0))) { + ATRACE_ASYNC_END("frame capture", frameNumber); + + // Sanity check - if sensor timestamp matches shutter timestamp + if (request.requestStatus == OK && + sensorTimestamp != shutterTimestamp) { + SET_ERR("sensor timestamp (%" PRId64 + ") for frame %d doesn't match shutter timestamp (%" PRId64 ")", + sensorTimestamp, frameNumber, shutterTimestamp); + } + + // for an unsuccessful request, it may have pending output buffers to + // return. + assert(request.requestStatus != OK || + request.pendingOutputBuffers.size() == 0); + returnOutputBuffers(request.pendingOutputBuffers.array(), + request.pendingOutputBuffers.size(), 0); + + mInFlightMap.removeItemsAt(idx, 1); + + ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber); + } + + // Sanity check - if we have too many in-flight frames, something has + // likely gone wrong + if (mInFlightMap.size() > kInFlightWarnLimit) { + CLOGE("In-flight list too large: %zu", mInFlightMap.size()); + } +} + + +void Camera3Device::sendCaptureResult(CameraMetadata &pendingMetadata, + CaptureResultExtras &resultExtras, + CameraMetadata &collectedPartialResult, + uint32_t frameNumber) { + if (pendingMetadata.isEmpty()) + return; + + Mutex::Autolock l(mOutputLock); + + // TODO: need to track errors for tighter bounds on expected frame number + if (frameNumber < mNextResultFrameNumber) { + SET_ERR("Out-of-order capture result metadata submitted! " + "(got frame number %d, expecting %d)", + frameNumber, mNextResultFrameNumber); + return; + } + mNextResultFrameNumber = frameNumber + 1; + + CaptureResult captureResult; + captureResult.mResultExtras = resultExtras; + captureResult.mMetadata = pendingMetadata; + + if (captureResult.mMetadata.update(ANDROID_REQUEST_FRAME_COUNT, + (int32_t*)&frameNumber, 1) != OK) { + SET_ERR("Failed to set frame# in metadata (%d)", + frameNumber); + return; + } else { + ALOGVV("%s: Camera %d: Set frame# in metadata (%d)", + __FUNCTION__, mId, frameNumber); + } + + // Append any previous partials to form a complete result + if (mUsePartialResult && !collectedPartialResult.isEmpty()) { + captureResult.mMetadata.append(collectedPartialResult); + } + + captureResult.mMetadata.sort(); + + // Check that there's a timestamp in the result metadata + camera_metadata_entry entry = + captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP); + if (entry.count == 0) { + SET_ERR("No timestamp provided by HAL for frame %d!", + frameNumber); + return; + } + + // Valid result, insert into queue + List<CaptureResult>::iterator queuedResult = + mResultQueue.insert(mResultQueue.end(), CaptureResult(captureResult)); + ALOGVV("%s: result requestId = %" PRId32 ", frameNumber = %" PRId64 + ", burstId = %" PRId32, __FUNCTION__, + queuedResult->mResultExtras.requestId, + queuedResult->mResultExtras.frameNumber, + queuedResult->mResultExtras.burstId); + + mResultSignal.signal(); +} + /** * Camera HAL device callback methods */ @@ -1914,11 +2038,14 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { CaptureResultExtras resultExtras; bool hasInputBufferInRequest = false; - // Get capture timestamp and resultExtras from list of in-flight requests, - // where it was added by the shutter notification for this frame. - // Then update the in-flight status and remove the in-flight entry if - // all result data has been received. - nsecs_t timestamp = 0; + // Get shutter timestamp and resultExtras from list of in-flight requests, + // where it was added by the shutter notification for this frame. If the + // shutter timestamp isn't received yet, append the output buffers to the + // in-flight request and they will be returned when the shutter timestamp + // arrives. Update the in-flight status and remove the in-flight entry if + // all result data and shutter timestamp have been received. + nsecs_t shutterTimestamp = 0; + { Mutex::Autolock l(mInFlightLock); ssize_t idx = mInFlightMap.indexOfKey(frameNumber); @@ -1928,13 +2055,17 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { return; } InFlightRequest &request = mInFlightMap.editValueAt(idx); - ALOGVV("%s: got InFlightRequest requestId = %" PRId32 ", frameNumber = %" PRId64 - ", burstId = %" PRId32, - __FUNCTION__, request.resultExtras.requestId, request.resultExtras.frameNumber, - request.resultExtras.burstId); - // Always update the partial count to the latest one. When framework aggregates adjacent - // partial results into one, the latest partial count will be used. - request.resultExtras.partialResultCount = result->partial_result; + ALOGVV("%s: got InFlightRequest requestId = %" PRId32 + ", frameNumber = %" PRId64 ", burstId = %" PRId32 + ", partialResultCount = %d", + __FUNCTION__, request.resultExtras.requestId, + request.resultExtras.frameNumber, request.resultExtras.burstId, + result->partial_result); + // Always update the partial count to the latest one if it's not 0 + // (buffers only). When framework aggregates adjacent partial results + // into one, the latest partial count will be used. + if (result->partial_result != 0) + request.resultExtras.partialResultCount = result->partial_result; // Check if this result carries only partial metadata if (mUsePartialResult && result->result != NULL) { @@ -1978,22 +2109,9 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { } } - timestamp = request.captureTimestamp; - resultExtras = request.resultExtras; + shutterTimestamp = request.shutterTimestamp; hasInputBufferInRequest = request.hasInputBuffer; - /** - * One of the following must happen before it's legal to call process_capture_result, - * unless partial metadata is being provided: - * - CAMERA3_MSG_SHUTTER (expected during normal operation) - * - CAMERA3_MSG_ERROR (expected during flush) - */ - if (request.requestStatus == OK && timestamp == 0 && !isPartialResult) { - SET_ERR("Called before shutter notify for frame %d", - frameNumber); - return; - } - // Did we get the (final) result metadata for this capture? if (result->result != NULL && !isPartialResult) { if (request.haveResultMetadata) { @@ -2026,99 +2144,38 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { return; } - // 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); - } - - // Sanity check - if we have too many in-flight frames, something has - // likely gone wrong - if (mInFlightMap.size() > kInFlightWarnLimit) { - CLOGE("In-flight list too large: %zu", mInFlightMap.size()); + camera_metadata_ro_entry_t entry; + res = find_camera_metadata_ro_entry(result->result, + ANDROID_SENSOR_TIMESTAMP, &entry); + if (res == OK && entry.count == 1) { + request.sensorTimestamp = entry.data.i64[0]; } - } - - // Process the result metadata, if provided - bool gotResult = false; - if (result->result != NULL && !isPartialResult) { - Mutex::Autolock l(mOutputLock); - - gotResult = true; - - // TODO: need to track errors for tighter bounds on expected frame number - if (frameNumber < mNextResultFrameNumber) { - SET_ERR("Out-of-order capture result metadata submitted! " - "(got frame number %d, expecting %d)", - frameNumber, mNextResultFrameNumber); - return; - } - mNextResultFrameNumber = frameNumber + 1; - - CaptureResult captureResult; - captureResult.mResultExtras = resultExtras; - captureResult.mMetadata = result->result; - - if (captureResult.mMetadata.update(ANDROID_REQUEST_FRAME_COUNT, - (int32_t*)&frameNumber, 1) != OK) { - SET_ERR("Failed to set frame# in metadata (%d)", - frameNumber); - gotResult = false; + // If shutter event isn't received yet, append the output buffers to + // the in-flight request. Otherwise, return the output buffers to + // streams. + if (shutterTimestamp == 0) { + request.pendingOutputBuffers.appendArray(result->output_buffers, + result->num_output_buffers); } else { - ALOGVV("%s: Camera %d: Set frame# in metadata (%d)", - __FUNCTION__, mId, frameNumber); - } - - // Append any previous partials to form a complete result - if (mUsePartialResult && !collectedPartialResult.isEmpty()) { - captureResult.mMetadata.append(collectedPartialResult); - } - - captureResult.mMetadata.sort(); - - // Check that there's a timestamp in the result metadata - - camera_metadata_entry entry = - captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP); - if (entry.count == 0) { - SET_ERR("No timestamp provided by HAL for frame %d!", - frameNumber); - gotResult = false; - } else if (timestamp != entry.data.i64[0]) { - SET_ERR("Timestamp mismatch between shutter notify and result" - " metadata for frame %d (%" PRId64 " vs %" PRId64 " respectively)", - frameNumber, timestamp, entry.data.i64[0]); - gotResult = false; + returnOutputBuffers(result->output_buffers, + result->num_output_buffers, shutterTimestamp); } - if (gotResult) { - // Valid result, insert into queue - List<CaptureResult>::iterator queuedResult = - mResultQueue.insert(mResultQueue.end(), CaptureResult(captureResult)); - ALOGVV("%s: result requestId = %" PRId32 ", frameNumber = %" PRId64 - ", burstId = %" PRId32, __FUNCTION__, - queuedResult->mResultExtras.requestId, - queuedResult->mResultExtras.frameNumber, - queuedResult->mResultExtras.burstId); + if (result->result != NULL && !isPartialResult) { + if (shutterTimestamp == 0) { + request.pendingMetadata = result->result; + request.partialResult.collectedResult = collectedPartialResult; + } else { + CameraMetadata metadata; + metadata = result->result; + sendCaptureResult(metadata, request.resultExtras, + collectedPartialResult, frameNumber); + } } - } // scope for mOutputLock - // Return completed buffers to their streams with the timestamp - - for (size_t i = 0; i < result->num_output_buffers; i++) { - Camera3Stream *stream = - Camera3Stream::cast(result->output_buffers[i].stream); - res = stream->returnBuffer(result->output_buffers[i], timestamp); - // Note: stream may be deallocated at this point, if this buffer was the - // last reference to it. - if (res != OK) { - ALOGE("Can't return buffer %zu for frame %d to its stream: " - " %s (%d)", i, frameNumber, strerror(-res), res); - } - } + removeInFlightRequestIfReadyLocked(idx); + } // scope for mInFlightLock if (result->input_buffer != NULL) { if (hasInputBufferInRequest) { @@ -2138,13 +2195,6 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { __FUNCTION__); } } - - // Finally, signal any waiters for new frames - - if (gotResult) { - mResultSignal.signal(); - } - } void Camera3Device::notify(const camera3_notify_msg *msg) { @@ -2262,8 +2312,6 @@ void Camera3Device::notifyShutter(const camera3_shutter_msg_t &msg, mNextShutterFrameNumber = msg.frame_number + 1; } - CaptureResultExtras resultExtras; - // Set timestamp for the request in the in-flight tracking // and get the request ID to send upstream { @@ -2271,21 +2319,30 @@ void Camera3Device::notifyShutter(const camera3_shutter_msg_t &msg, idx = mInFlightMap.indexOfKey(msg.frame_number); if (idx >= 0) { InFlightRequest &r = mInFlightMap.editValueAt(idx); - r.captureTimestamp = msg.timestamp; - resultExtras = r.resultExtras; + + ALOGVV("Camera %d: %s: Shutter fired for frame %d (id %d) at %" PRId64, + mId, __FUNCTION__, + msg.frame_number, r.resultExtras.requestId, msg.timestamp); + // Call listener, if any + if (listener != NULL) { + listener->notifyShutter(r.resultExtras, msg.timestamp); + } + + r.shutterTimestamp = msg.timestamp; + + // send pending result and buffers + sendCaptureResult(r.pendingMetadata, r.resultExtras, + r.partialResult.collectedResult, msg.frame_number); + returnOutputBuffers(r.pendingOutputBuffers.array(), + r.pendingOutputBuffers.size(), r.shutterTimestamp); + r.pendingOutputBuffers.clear(); + + removeInFlightRequestIfReadyLocked(idx); } } if (idx < 0) { SET_ERR("Shutter notification for non-existent frame number %d", msg.frame_number); - return; - } - ALOGVV("Camera %d: %s: Shutter fired for frame %d (id %d) at %" PRId64, - mId, __FUNCTION__, - msg.frame_number, resultExtras.requestId, msg.timestamp); - // Call listener, if any - if (listener != NULL) { - listener->notifyShutter(resultExtras, msg.timestamp); } } diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index ec6bba1..a77548d 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -73,7 +73,7 @@ class Camera3Device : virtual int getId() const; // Transitions to idle state on success. - virtual status_t initialize(camera_module_t *module); + virtual status_t initialize(CameraModule *module); virtual status_t disconnect(); virtual status_t dump(int fd, const Vector<String16> &args); virtual const CameraMetadata& info() const; @@ -95,7 +95,8 @@ class Camera3Device : // If adding streams while actively capturing, will pause device before adding // stream, reconfiguring device, and unpausing. virtual status_t createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, int *id); + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id); virtual status_t createInputStream( uint32_t width, uint32_t height, int format, int *id); @@ -521,7 +522,9 @@ class Camera3Device : struct InFlightRequest { // Set by notify() SHUTTER call. - nsecs_t captureTimestamp; + nsecs_t shutterTimestamp; + // Set by process_capture_result(). + nsecs_t sensorTimestamp; int requestStatus; // Set by process_capture_result call with valid metadata bool haveResultMetadata; @@ -532,6 +535,21 @@ class Camera3Device : // If this request has any input buffer bool hasInputBuffer; + + // The last metadata that framework receives from HAL and + // not yet send out because the shutter event hasn't arrived. + // It's added by process_capture_result and sent when framework + // receives the shutter event. + CameraMetadata pendingMetadata; + + // Buffers are added by process_capture_result when output buffers + // return from HAL but framework has not yet received the shutter + // event. They will be returned to the streams when framework receives + // the shutter event. + Vector<camera3_stream_buffer_t> pendingOutputBuffers; + + + // Fields used by the partial result only struct PartialResultInFlight { // Set by process_capture_result once 3A has been sent to clients @@ -546,7 +564,8 @@ class Camera3Device : // Default constructor needed by KeyedVector InFlightRequest() : - captureTimestamp(0), + shutterTimestamp(0), + sensorTimestamp(0), requestStatus(OK), haveResultMetadata(false), numBuffersLeft(0), @@ -554,7 +573,8 @@ class Camera3Device : } InFlightRequest(int numBuffers) : - captureTimestamp(0), + shutterTimestamp(0), + sensorTimestamp(0), requestStatus(OK), haveResultMetadata(false), numBuffersLeft(numBuffers), @@ -562,7 +582,8 @@ class Camera3Device : } InFlightRequest(int numBuffers, CaptureResultExtras extras) : - captureTimestamp(0), + shutterTimestamp(0), + sensorTimestamp(0), requestStatus(OK), haveResultMetadata(false), numBuffersLeft(numBuffers), @@ -571,7 +592,8 @@ class Camera3Device : } InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput) : - captureTimestamp(0), + shutterTimestamp(0), + sensorTimestamp(0), requestStatus(OK), haveResultMetadata(false), numBuffersLeft(numBuffers), @@ -639,6 +661,24 @@ class Camera3Device : void notifyShutter(const camera3_shutter_msg_t &msg, NotificationListener *listener); + // helper function to return the output buffers to the streams. + void returnOutputBuffers(const camera3_stream_buffer_t *outputBuffers, + size_t numBuffers, nsecs_t timestamp); + + // Insert the capture result given the pending metadata, result extras, + // partial results, and the frame number to the result queue. + void sendCaptureResult(CameraMetadata &pendingMetadata, + CaptureResultExtras &resultExtras, + CameraMetadata &collectedPartialResult, uint32_t frameNumber); + + /**** Scope for mInFlightLock ****/ + + // Remove the in-flight request of the given index from mInFlightMap + // if it's no longer needed. It must only be called with mInFlightLock held. + void removeInFlightRequestIfReadyLocked(int idx); + + /**** End scope for mInFlightLock ****/ + /** * Static callback forwarding methods from HAL to instance */ diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp index 6656b09..01edfff 100644 --- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp @@ -28,7 +28,7 @@ namespace camera3 { Camera3DummyStream::Camera3DummyStream(int id) : Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, DUMMY_WIDTH, DUMMY_HEIGHT, - /*maxSize*/0, DUMMY_FORMAT) { + /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE, DUMMY_ROTATION) { } diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h index 3e42623..d023c57 100644 --- a/services/camera/libcameraservice/device3/Camera3DummyStream.h +++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h @@ -75,6 +75,8 @@ class Camera3DummyStream : static const int DUMMY_WIDTH = 320; static const int DUMMY_HEIGHT = 240; static const int DUMMY_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + static const android_dataspace DUMMY_DATASPACE = HAL_DATASPACE_UNKNOWN; + static const camera3_stream_rotation_t DUMMY_ROTATION = CAMERA3_STREAM_ROTATION_0; static const uint32_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER; /** diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp index cc66459..8696413 100644 --- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp @@ -30,9 +30,10 @@ namespace android { namespace camera3 { Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type, - uint32_t width, uint32_t height, size_t maxSize, int format) : + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation) : Camera3Stream(id, type, - width, height, maxSize, format), + width, height, maxSize, format, dataSpace, rotation), mTotalBufferCount(0), mHandoutTotalBufferCount(0), mHandoutOutputBufferCount(0), diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h index a35c290..abcf2b1 100644 --- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h @@ -33,7 +33,8 @@ class Camera3IOStreamBase : public Camera3Stream { protected: Camera3IOStreamBase(int id, camera3_stream_type_t type, - uint32_t width, uint32_t height, size_t maxSize, int format); + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation); public: diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp index 319be1d..6bf671e 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp @@ -18,6 +18,7 @@ #define ATRACE_TAG ATRACE_TAG_CAMERA //#define LOG_NDEBUG 0 +#include <gui/BufferItem.h> #include <utils/Log.h> #include <utils/Trace.h> #include "Camera3InputStream.h" @@ -28,8 +29,8 @@ namespace camera3 { Camera3InputStream::Camera3InputStream(int id, uint32_t width, uint32_t height, int format) : - Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height, - /*maxSize*/0, format) { + Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height, /*maxSize*/0, + format, HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0) { if (format == HAL_PIXEL_FORMAT_BLOB) { ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__); diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h index ae49467..fd17f4f 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.h +++ b/services/camera/libcameraservice/device3/Camera3InputStream.h @@ -48,8 +48,6 @@ class Camera3InputStream : public Camera3IOStreamBase { private: - typedef BufferItemConsumer::BufferItem BufferItem; - sp<BufferItemConsumer> mConsumer; Vector<BufferItem> mBuffersInFlight; diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp index 77ad503..96bed0d 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -33,9 +33,10 @@ namespace camera3 { Camera3OutputStream::Camera3OutputStream(int id, sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format) : + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation) : Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, - /*maxSize*/0, format), + /*maxSize*/0, format, dataSpace, rotation), mConsumer(consumer), mTransform(0), mTraceFirstBuffer(true) { @@ -48,9 +49,10 @@ Camera3OutputStream::Camera3OutputStream(int id, Camera3OutputStream::Camera3OutputStream(int id, sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, size_t maxSize, int format) : + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation) : Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, maxSize, - format), + format, dataSpace, rotation), mConsumer(consumer), mTransform(0), mTraceFirstBuffer(true) { @@ -69,10 +71,12 @@ Camera3OutputStream::Camera3OutputStream(int id, Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type, uint32_t width, uint32_t height, - int format) : + int format, + android_dataspace dataSpace, + camera3_stream_rotation_t rotation) : Camera3IOStreamBase(id, type, width, height, /*maxSize*/0, - format), + format, dataSpace, rotation), mTransform(0) { // Subclasses expected to initialize mConsumer themselves @@ -323,6 +327,14 @@ status_t Camera3OutputStream::configureQueueLocked() { return res; } + res = native_window_set_buffers_data_space(mConsumer.get(), + camera3_stream::data_space); + if (res != OK) { + ALOGE("%s: Unable to configure stream dataspace %#x for stream %d", + __FUNCTION__, camera3_stream::data_space, mId); + return res; + } + int maxConsumerBuffers; res = mConsumer->query(mConsumer.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers); diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h index be278c5..12b2ebb 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.h +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h @@ -39,14 +39,16 @@ class Camera3OutputStream : * Set up a stream for formats that have 2 dimensions, such as RAW and YUV. */ Camera3OutputStream(int id, sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format); + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation); /** * Set up a stream for formats that have a variable buffer size for the same * dimensions, such as compressed JPEG. */ Camera3OutputStream(int id, sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, size_t maxSize, int format); + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation); virtual ~Camera3OutputStream(); @@ -64,7 +66,8 @@ class Camera3OutputStream : protected: Camera3OutputStream(int id, camera3_stream_type_t type, - uint32_t width, uint32_t height, int format); + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation); /** * Note that we release the lock briefly in this function diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index 3c0e908..4acbce3 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -46,7 +46,8 @@ const Camera3Stream* Camera3Stream::cast(const camera3_stream *stream) { Camera3Stream::Camera3Stream(int id, camera3_stream_type type, - uint32_t width, uint32_t height, size_t maxSize, int format) : + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation) : camera3_stream(), mId(id), mName(String8::format("Camera3Stream[%d]", id)), @@ -58,6 +59,8 @@ Camera3Stream::Camera3Stream(int id, camera3_stream::width = width; camera3_stream::height = height; camera3_stream::format = format; + camera3_stream::data_space = dataSpace; + camera3_stream::rotation = rotation; camera3_stream::usage = 0; camera3_stream::max_buffers = 0; camera3_stream::priv = NULL; @@ -84,6 +87,10 @@ int Camera3Stream::getFormat() const { return camera3_stream::format; } +android_dataspace Camera3Stream::getDataSpace() const { + return camera3_stream::data_space; +} + camera3_stream* Camera3Stream::startConfiguration() { ATRACE_CALL(); Mutex::Autolock l(mLock); diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index d0e1337..aba27fe 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -119,9 +119,10 @@ class Camera3Stream : /** * Get the stream's dimensions and format */ - uint32_t getWidth() const; - uint32_t getHeight() const; - int getFormat() const; + uint32_t getWidth() const; + uint32_t getHeight() const; + int getFormat() const; + android_dataspace getDataSpace() const; /** * Start the stream configuration process. Returns a handle to the stream's @@ -264,7 +265,8 @@ class Camera3Stream : mutable Mutex mLock; Camera3Stream(int id, camera3_stream_type type, - uint32_t width, uint32_t height, size_t maxSize, int format); + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation); /** * Interface to be implemented by derived classes diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp index 81330ea..10d7f2e 100644 --- a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp @@ -114,7 +114,8 @@ Camera3ZslStream::Camera3ZslStream(int id, uint32_t width, uint32_t height, int bufferCount) : Camera3OutputStream(id, CAMERA3_STREAM_BIDIRECTIONAL, width, height, - HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, + HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0), mDepth(bufferCount) { sp<IGraphicBufferProducer> producer; diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp index f8562ec..8cd6800 100644 --- a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp +++ b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp @@ -268,7 +268,7 @@ status_t RingBufferConsumer::releaseOldestBufferLocked(size_t* pinnedFrames) { return OK; } -void RingBufferConsumer::onFrameAvailable() { +void RingBufferConsumer::onFrameAvailable(const BufferItem& item) { status_t err; { @@ -321,7 +321,7 @@ void RingBufferConsumer::onFrameAvailable() { item.mGraphicBuffer = mSlots[item.mBuf].mGraphicBuffer; } // end of mMutex lock - ConsumerBase::onFrameAvailable(); + ConsumerBase::onFrameAvailable(item); } void RingBufferConsumer::unpinBuffer(const BufferItem& item) { diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h index da97a11..83e7298 100644 --- a/services/camera/libcameraservice/gui/RingBufferConsumer.h +++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h @@ -17,6 +17,7 @@ #ifndef ANDROID_GUI_RINGBUFFERCONSUMER_H #define ANDROID_GUI_RINGBUFFERCONSUMER_H +#include <gui/BufferItem.h> #include <gui/ConsumerBase.h> #include <ui/GraphicBuffer.h> @@ -54,8 +55,6 @@ class RingBufferConsumer : public ConsumerBase, public: typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; - typedef BufferQueue::BufferItem BufferItem; - enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT }; enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE }; @@ -165,7 +164,7 @@ class RingBufferConsumer : public ConsumerBase, private: // Override ConsumerBase::onFrameAvailable - virtual void onFrameAvailable(); + virtual void onFrameAvailable(const BufferItem& item); void pinBufferLocked(const BufferItem& item); void unpinBuffer(const BufferItem& item); diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.cpp b/services/camera/libcameraservice/utils/AutoConditionLock.cpp new file mode 100644 index 0000000..c8ee965 --- /dev/null +++ b/services/camera/libcameraservice/utils/AutoConditionLock.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "AutoConditionLock.h" + +namespace android { + +WaitableMutexWrapper::WaitableMutexWrapper(Mutex* mutex) : mMutex{mutex}, mState{false} {} + +WaitableMutexWrapper::~WaitableMutexWrapper() {} + +// Locks manager-owned mutex +AutoConditionLock::AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager) : + mManager{manager}, mAutoLock{manager->mMutex} {} + +// Unlocks manager-owned mutex +AutoConditionLock::~AutoConditionLock() { + // Unset the condition and wake everyone up before releasing lock + mManager->mState = false; + mManager->mCondition.broadcast(); +} + +std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire( + const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime) { + + if (manager == nullptr || manager->mMutex == nullptr) { + // Bad input, return null + return std::unique_ptr<AutoConditionLock>{nullptr}; + } + + // Acquire scoped lock + std::unique_ptr<AutoConditionLock> scopedLock(new AutoConditionLock(manager)); + + // Figure out what time in the future we should hit the timeout + nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + waitTime; + + // Wait until we timeout, or success + while(manager->mState) { + status_t ret = manager->mCondition.waitRelative(*(manager->mMutex), waitTime); + if (ret != NO_ERROR) { + // Timed out or whatever, return null + return std::unique_ptr<AutoConditionLock>{nullptr}; + } + waitTime = failTime - systemTime(SYSTEM_TIME_MONOTONIC); + } + + // Set the condition and return + manager->mState = true; + return scopedLock; +} + +std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire( + const std::shared_ptr<WaitableMutexWrapper>& manager) { + + if (manager == nullptr || manager->mMutex == nullptr) { + // Bad input, return null + return std::unique_ptr<AutoConditionLock>{nullptr}; + } + + // Acquire scoped lock + std::unique_ptr<AutoConditionLock> scopedLock(new AutoConditionLock(manager)); + + // Wait until we timeout, or success + while(manager->mState) { + status_t ret = manager->mCondition.wait(*(manager->mMutex)); + if (ret != NO_ERROR) { + // Timed out or whatever, return null + return std::unique_ptr<AutoConditionLock>{nullptr}; + } + } + + // Set the condition and return + manager->mState = true; + return scopedLock; +} + +}; // namespace android diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.h b/services/camera/libcameraservice/utils/AutoConditionLock.h new file mode 100644 index 0000000..9a3eafc --- /dev/null +++ b/services/camera/libcameraservice/utils/AutoConditionLock.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_SERVICE_UTILS_SCOPED_CONDITION_H +#define ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H + +#include <utils/Timers.h> +#include <utils/Condition.h> +#include <utils/Errors.h> +#include <utils/Mutex.h> + +#include <memory> + +namespace android { + +/** + * WaitableMutexWrapper can be used with AutoConditionLock to construct scoped locks for the + * wrapped Mutex with timeouts for lock acquisition. + */ +class WaitableMutexWrapper { + friend class AutoConditionLock; +public: + /** + * Construct the ConditionManger with the given Mutex. + */ + WaitableMutexWrapper(Mutex* mutex); + + virtual ~WaitableMutexWrapper(); +private: + Mutex* mMutex; + bool mState; + Condition mCondition; +}; + +/** + * AutoConditionLock is a scoped lock similar to Mutex::Autolock, but allows timeouts to be + * specified for lock acquisition. + * + * AutoConditionLock is used with a WaitableMutexWrapper to lock/unlock the WaitableMutexWrapper's + * wrapped Mutex, and wait/set/signal the WaitableMutexWrapper's wrapped condition. To use this, + * call AutoConditionLock::waitAndAcquire to get an instance. This will: + * - Lock the given WaitableMutexWrapper's mutex. + * - Wait for the WaitableMutexWrapper's condition to become false, or timeout. + * - Set the WaitableMutexWrapper's condition to true. + * + * When the AutoConditionLock goes out of scope and is destroyed, this will: + * - Set the WaitableMutexWrapper's condition to false. + * - Signal threads waiting on this condition to wakeup. + * - Release WaitableMutexWrapper's mutex. + */ +class AutoConditionLock final { +public: + AutoConditionLock() = delete; + AutoConditionLock(const AutoConditionLock& other) = delete; + AutoConditionLock & operator=(const AutoConditionLock&) = delete; + + ~AutoConditionLock(); + + /** + * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting up to waitTime + * nanoseconds to acquire the WaitableMutexWrapper's wrapped lock. + * + * Return an empty unique_ptr if this fails, or a timeout occurs. + */ + static std::unique_ptr<AutoConditionLock> waitAndAcquire( + const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime); + + /** + * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting indefinitely to + * acquire the WaitableMutexWrapper's wrapped lock. + * + * Return an empty unique_ptr if this fails. + */ + static std::unique_ptr<AutoConditionLock> waitAndAcquire( + const std::shared_ptr<WaitableMutexWrapper>& manager); +private: + AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager); + + std::shared_ptr<WaitableMutexWrapper> mManager; + Mutex::Autolock mAutoLock; +}; + +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h new file mode 100644 index 0000000..ad5486d --- /dev/null +++ b/services/camera/libcameraservice/utils/ClientManager.h @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H +#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H + +#include <utils/Mutex.h> + +#include <algorithm> +#include <utility> +#include <vector> +#include <set> +#include <map> +#include <memory> + +namespace android { +namespace resource_policy { + +// -------------------------------------------------------------------------------- + +/** + * The ClientDescriptor class is a container for a given key/value pair identifying a shared + * resource, and the corresponding cost, priority, owner ID, and conflicting keys list used + * in determining eviction behavior. + * + * Aside from the priority, these values are immutable once the ClientDescriptor has been + * constructed. + */ +template<class KEY, class VALUE> +class ClientDescriptor final { +public: + ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost, + const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId); + ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set<KEY>&& conflictingKeys, + int32_t priority, int32_t ownerId); + + ~ClientDescriptor(); + + /** + * Return the key for this descriptor. + */ + const KEY& getKey() const; + + /** + * Return the value for this descriptor. + */ + const VALUE& getValue() const; + + /** + * Return the cost for this descriptor. + */ + int32_t getCost() const; + + /** + * Return the priority for this descriptor. + */ + int32_t getPriority() const; + + /** + * Return the owner ID for this descriptor. + */ + int32_t getOwnerId() const; + + /** + * Return true if the given key is in this descriptor's conflicting keys list. + */ + bool isConflicting(const KEY& key) const; + + /** + * Return the set of all conflicting keys for this descriptor. + */ + std::set<KEY> getConflicting() const; + + /** + * Set the proirity for this descriptor. + */ + void setPriority(int32_t priority); + + // This class is ordered by key + template<class K, class V> + friend bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b); + +private: + KEY mKey; + VALUE mValue; + int32_t mCost; + std::set<KEY> mConflicting; + int32_t mPriority; + int32_t mOwnerId; +}; // class ClientDescriptor + +template<class K, class V> +bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b) { + return a.mKey < b.mKey; +} + +template<class KEY, class VALUE> +ClientDescriptor<KEY, VALUE>::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost, + const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId) : mKey{key}, + mValue{value}, mCost{cost}, mConflicting{conflictingKeys}, mPriority{priority}, + mOwnerId{ownerId} {} + +template<class KEY, class VALUE> +ClientDescriptor<KEY, VALUE>::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, + std::set<KEY>&& conflictingKeys, int32_t priority, int32_t ownerId) : + mKey{std::forward<KEY>(key)}, mValue{std::forward<VALUE>(value)}, mCost{cost}, + mConflicting{std::forward<std::set<KEY>>(conflictingKeys)}, mPriority{priority}, + mOwnerId{ownerId} {} + +template<class KEY, class VALUE> +ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {} + +template<class KEY, class VALUE> +const KEY& ClientDescriptor<KEY, VALUE>::getKey() const { + return mKey; +} + +template<class KEY, class VALUE> +const VALUE& ClientDescriptor<KEY, VALUE>::getValue() const { + return mValue; +} + +template<class KEY, class VALUE> +int32_t ClientDescriptor<KEY, VALUE>::getCost() const { + return mCost; +} + +template<class KEY, class VALUE> +int32_t ClientDescriptor<KEY, VALUE>::getPriority() const { + return mPriority; +} + +template<class KEY, class VALUE> +int32_t ClientDescriptor<KEY, VALUE>::getOwnerId() const { + return mOwnerId; +} + +template<class KEY, class VALUE> +bool ClientDescriptor<KEY, VALUE>::isConflicting(const KEY& key) const { + if (key == mKey) return true; + for (const auto& x : mConflicting) { + if (key == x) return true; + } + return false; +} + +template<class KEY, class VALUE> +std::set<KEY> ClientDescriptor<KEY, VALUE>::getConflicting() const { + return mConflicting; +} + +template<class KEY, class VALUE> +void ClientDescriptor<KEY, VALUE>::setPriority(int32_t priority) { + mPriority = priority; +} + +// -------------------------------------------------------------------------------- + +/** + * The ClientManager class wraps an LRU-ordered list of active clients and implements eviction + * behavior for handling shared resource access. + * + * When adding a new descriptor, eviction behavior is as follows: + * - Keys are unique, adding a descriptor with the same key as an existing descriptor will + * result in the lower-priority of the two being removed. Priority ties result in the + * LRU descriptor being evicted (this means the incoming descriptor be added in this case). + * - Any descriptors with keys that are in the incoming descriptor's 'conflicting keys' list + * will be removed if they have an equal or lower priority than the incoming descriptor; + * if any have a higher priority, the incoming descriptor is removed instead. + * - If the sum of all descriptors' costs, including the incoming descriptor's, is more than + * the max cost allowed for this ClientManager, descriptors with non-zero cost, equal or lower + * priority, and a different owner will be evicted in LRU order until either the cost is less + * than the max cost, or all descriptors meeting this criteria have been evicted and the + * incoming descriptor has the highest priority. Otherwise, the incoming descriptor is + * removed instead. + */ +template<class KEY, class VALUE> +class ClientManager { +public: + // The default maximum "cost" allowed before evicting + static constexpr int32_t DEFAULT_MAX_COST = 100; + + ClientManager(); + ClientManager(int32_t totalCost); + + /** + * Add a given ClientDescriptor to the managed list. ClientDescriptors for clients that + * are evicted by this action are returned in a vector. + * + * This may return the ClientDescriptor passed in if it would be evicted. + */ + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> addAndEvict( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client); + + /** + * Given a map containing owner (pid) -> priority mappings, update the priority of each + * ClientDescriptor with an owner in this mapping. + */ + void updatePriorities(const std::map<int32_t,int32_t>& ownerPriorityList); + + /** + * Remove all ClientDescriptors. + */ + void removeAll(); + + /** + * Remove and return the ClientDescriptor with a given key. + */ + std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key); + + /** + * Remove the given ClientDescriptor. + */ + void remove(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value); + + /** + * Return a vector of the ClientDescriptors that would be evicted by adding the given + * ClientDescriptor. + * + * This may return the ClientDescriptor passed in if it would be evicted. + */ + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvict( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const; + + /** + * Return a vector of active ClientDescriptors that prevent this client from being added. + */ + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getIncompatibleClients( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const; + + /** + * Return a vector containing all currently active ClientDescriptors. + */ + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getAll() const; + + /** + * Return a vector containing all keys of currently active ClientDescriptors. + */ + std::vector<KEY> getAllKeys() const; + + /** + * Return a vector of the owner tags of all currently active ClientDescriptors (duplicates + * will be removed). + */ + std::vector<int32_t> getAllOwners() const; + + /** + * Return the ClientDescriptor corresponding to the given key, or an empty shared pointer + * if none exists. + */ + std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const; + +protected: + ~ClientManager(); + +private: + + /** + * Return a vector of the ClientDescriptors that would be evicted by adding the given + * ClientDescriptor. If returnIncompatibleClients is set to true, instead, return the + * vector of ClientDescriptors that are higher priority than the incoming client and + * either conflict with this client, or contribute to the resource cost if that would + * prevent the incoming client from being added. + * + * This may return the ClientDescriptor passed in. + */ + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvictLocked( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client, + bool returnIncompatibleClients = false) const; + + int64_t getCurrentCostLocked() const; + + mutable Mutex mLock; + int32_t mMaxCost; + // LRU ordered, most recent at end + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients; +}; // class ClientManager + +template<class KEY, class VALUE> +ClientManager<KEY, VALUE>::ClientManager() : + ClientManager(DEFAULT_MAX_COST) {} + +template<class KEY, class VALUE> +ClientManager<KEY, VALUE>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {} + +template<class KEY, class VALUE> +ClientManager<KEY, VALUE>::~ClientManager() {} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::wouldEvict( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const { + Mutex::Autolock lock(mLock); + return wouldEvictLocked(client); +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> +ClientManager<KEY, VALUE>::getIncompatibleClients( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const { + Mutex::Autolock lock(mLock); + return wouldEvictLocked(client, /*returnIncompatibleClients*/true); +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> +ClientManager<KEY, VALUE>::wouldEvictLocked( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client, + bool returnIncompatibleClients) const { + + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList; + + // Disallow null clients, return input + if (client == nullptr) { + evictList.push_back(client); + return evictList; + } + + const KEY& key = client->getKey(); + int32_t cost = client->getCost(); + int32_t priority = client->getPriority(); + int32_t owner = client->getOwnerId(); + + int64_t totalCost = getCurrentCostLocked() + cost; + + // Determine the MRU of the owners tied for having the highest priority + int32_t highestPriorityOwner = owner; + int32_t highestPriority = priority; + for (const auto& i : mClients) { + int32_t curPriority = i->getPriority(); + if (curPriority >= highestPriority) { + highestPriority = curPriority; + highestPriorityOwner = i->getOwnerId(); + } + } + + if (highestPriority == priority) { + // Switch back owner if the incoming client has the highest priority, as it is MRU + highestPriorityOwner = owner; + } + + // Build eviction list of clients to remove + for (const auto& i : mClients) { + const KEY& curKey = i->getKey(); + int32_t curCost = i->getCost(); + int32_t curPriority = i->getPriority(); + int32_t curOwner = i->getOwnerId(); + + bool conflicting = (curKey == key || i->isConflicting(key) || + client->isConflicting(curKey)); + + if (!returnIncompatibleClients) { + // Find evicted clients + + if (conflicting && curPriority > priority) { + // Pre-existing conflicting client with higher priority exists + evictList.clear(); + evictList.push_back(client); + return evictList; + } else if (conflicting || ((totalCost > mMaxCost && curCost > 0) && + (curPriority <= priority) && + !(highestPriorityOwner == owner && owner == curOwner))) { + // Add a pre-existing client to the eviction list if: + // - We are adding a client with higher priority that conflicts with this one. + // - The total cost including the incoming client's is more than the allowable + // maximum, and the client has a non-zero cost, lower priority, and a different + // owner than the incoming client when the incoming client has the + // highest priority. + evictList.push_back(i); + totalCost -= curCost; + } + } else { + // Find clients preventing the incoming client from being added + + if (curPriority > priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) { + // Pre-existing conflicting client with higher priority exists + evictList.push_back(i); + } + } + } + + // Immediately return the incompatible clients if we are calculating these instead + if (returnIncompatibleClients) { + return evictList; + } + + // If the total cost is too high, return the input unless the input has the highest priority + if (totalCost > mMaxCost && highestPriorityOwner != owner) { + evictList.clear(); + evictList.push_back(client); + return evictList; + } + + return evictList; + +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::addAndEvict( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) { + Mutex::Autolock lock(mLock); + auto evicted = wouldEvictLocked(client); + auto it = evicted.begin(); + if (it != evicted.end() && *it == client) { + return evicted; + } + + auto iter = evicted.cbegin(); + + // Remove evicted clients from list + mClients.erase(std::remove_if(mClients.begin(), mClients.end(), + [&iter] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) { + if (curClientPtr->getKey() == (*iter)->getKey()) { + iter++; + return true; + } + return false; + }), mClients.end()); + + mClients.push_back(client); + + return evicted; +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> +ClientManager<KEY, VALUE>::getAll() const { + Mutex::Autolock lock(mLock); + return mClients; +} + +template<class KEY, class VALUE> +std::vector<KEY> ClientManager<KEY, VALUE>::getAllKeys() const { + Mutex::Autolock lock(mLock); + std::vector<KEY> keys(mClients.size()); + for (const auto& i : mClients) { + keys.push_back(i->getKey()); + } + return keys; +} + +template<class KEY, class VALUE> +std::vector<int32_t> ClientManager<KEY, VALUE>::getAllOwners() const { + Mutex::Autolock lock(mLock); + std::set<int32_t> owners; + for (const auto& i : mClients) { + owners.emplace(i->getOwnerId()); + } + return std::vector<int32_t>(owners.begin(), owners.end()); +} + +template<class KEY, class VALUE> +void ClientManager<KEY, VALUE>::updatePriorities( + const std::map<int32_t,int32_t>& ownerPriorityList) { + Mutex::Autolock lock(mLock); + for (auto& i : mClients) { + auto j = ownerPriorityList.find(i->getOwnerId()); + if (j != ownerPriorityList.end()) { + i->setPriority(j->second); + } + } +} + +template<class KEY, class VALUE> +std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::get( + const KEY& key) const { + Mutex::Autolock lock(mLock); + for (const auto& i : mClients) { + if (i->getKey() == key) return i; + } + return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr); +} + +template<class KEY, class VALUE> +void ClientManager<KEY, VALUE>::removeAll() { + Mutex::Autolock lock(mLock); + mClients.clear(); +} + +template<class KEY, class VALUE> +std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::remove(const KEY& key) { + Mutex::Autolock lock(mLock); + + std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret; + + // Remove evicted clients from list + mClients.erase(std::remove_if(mClients.begin(), mClients.end(), + [&key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) { + if (curClientPtr->getKey() == key) { + ret = curClientPtr; + return true; + } + return false; + }), mClients.end()); + + return ret; +} + +template<class KEY, class VALUE> +void ClientManager<KEY, VALUE>::remove( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) { + Mutex::Autolock lock(mLock); + // Remove evicted clients from list + mClients.erase(std::remove_if(mClients.begin(), mClients.end(), + [&value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) { + if (curClientPtr == value) { + return true; + } + return false; + }), mClients.end()); +} + +template<class KEY, class VALUE> +int64_t ClientManager<KEY, VALUE>::getCurrentCostLocked() const { + int64_t totalCost = 0; + for (const auto& x : mClients) { + totalCost += x->getCost(); + } + return totalCost; +} + +// -------------------------------------------------------------------------------- + +}; // namespace resource_policy +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H diff --git a/services/camera/libcameraservice/utils/RingBuffer.h b/services/camera/libcameraservice/utils/RingBuffer.h new file mode 100644 index 0000000..df7c00e --- /dev/null +++ b/services/camera/libcameraservice/utils/RingBuffer.h @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_SERVICE_UTILS_RING_BUFFER_H +#define ANDROID_SERVICE_UTILS_RING_BUFFER_H + +#include <utils/Log.h> +#include <cutils/compiler.h> + +#include <iterator> +#include <utility> +#include <vector> + +namespace android { + +/** + * A RingBuffer class that maintains an array of objects that can grow up to a certain size. + * Elements added to the RingBuffer are inserted in the logical front of the buffer, and + * invalidate all current iterators for that RingBuffer object. + */ +template <class T> +class RingBuffer final { +public: + + /** + * Construct a RingBuffer that can grow up to the given length. + */ + RingBuffer(size_t length); + + /** + * Forward iterator to this class. Implements an std:forward_iterator. + */ + class iterator : public std::iterator<std::forward_iterator_tag, T> { + public: + iterator(T* ptr, size_t size, size_t pos, size_t ctr); + + iterator& operator++(); + + iterator operator++(int); + + bool operator==(const iterator& rhs); + + bool operator!=(const iterator& rhs); + + T& operator*(); + + T* operator->(); + + private: + T* mPtr; + size_t mSize; + size_t mPos; + size_t mCtr; + }; + + /** + * Constant forward iterator to this class. Implements an std:forward_iterator. + */ + class const_iterator : public std::iterator<std::forward_iterator_tag, T> { + public: + const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr); + + const_iterator& operator++(); + + const_iterator operator++(int); + + bool operator==(const const_iterator& rhs); + + bool operator!=(const const_iterator& rhs); + + const T& operator*(); + + const T* operator->(); + + private: + const T* mPtr; + size_t mSize; + size_t mPos; + size_t mCtr; + }; + + /** + * Adds item to the front of this RingBuffer. If the RingBuffer is at its maximum length, + * this will result in the last element being replaced (this is done using the element's + * assignment operator). + * + * All current iterators are invalidated. + */ + void add(const T& item); + + /** + * Moves item to the front of this RingBuffer. Following a call to this, item should no + * longer be used. If the RingBuffer is at its maximum length, this will result in the + * last element being replaced (this is done using the element's assignment operator). + * + * All current iterators are invalidated. + */ + void add(T&& item); + + /** + * Construct item in-place in the front of this RingBuffer using the given arguments. If + * the RingBuffer is at its maximum length, this will result in the last element being + * replaced (this is done using the element's assignment operator). + * + * All current iterators are invalidated. + */ + template <class... Args> + void emplace(Args&&... args); + + /** + * Get an iterator to the front of this RingBuffer. + */ + iterator begin(); + + /** + * Get an iterator to the end of this RingBuffer. + */ + iterator end(); + + /** + * Get a const_iterator to the front of this RingBuffer. + */ + const_iterator begin() const; + + /** + * Get a const_iterator to the end of this RingBuffer. + */ + const_iterator end() const; + + /** + * Return a reference to the element at a given index. If the index is out of range for + * this ringbuffer, [0, size), the behavior for this is undefined. + */ + T& operator[](size_t index); + + /** + * Return a const reference to the element at a given index. If the index is out of range + * for this ringbuffer, [0, size), the behavior for this is undefined. + */ + const T& operator[](size_t index) const; + + /** + * Return the current size of this RingBuffer. + */ + size_t size() const; + + /** + * Remove all elements from this RingBuffer and set the size to 0. + */ + void clear(); + +private: + size_t mFrontIdx; + size_t mMaxBufferSize; + std::vector<T> mBuffer; +}; // class RingBuffer + + +template <class T> +RingBuffer<T>::RingBuffer(size_t length) : mFrontIdx{0}, mMaxBufferSize{length} {} + +template <class T> +RingBuffer<T>::iterator::iterator(T* ptr, size_t size, size_t pos, size_t ctr) : + mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {} + +template <class T> +typename RingBuffer<T>::iterator& RingBuffer<T>::iterator::operator++() { + ++mCtr; + + if (CC_UNLIKELY(mCtr == mSize)) { + mPos = mSize; + return *this; + } + + mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1); + return *this; +} + +template <class T> +typename RingBuffer<T>::iterator RingBuffer<T>::iterator::operator++(int) { + iterator tmp{mPtr, mSize, mPos, mCtr}; + ++(*this); + return tmp; +} + +template <class T> +bool RingBuffer<T>::iterator::operator==(const iterator& rhs) { + return (mPtr + mPos) == (rhs.mPtr + rhs.mPos); +} + +template <class T> +bool RingBuffer<T>::iterator::operator!=(const iterator& rhs) { + return (mPtr + mPos) != (rhs.mPtr + rhs.mPos); +} + +template <class T> +T& RingBuffer<T>::iterator::operator*() { + return *(mPtr + mPos); +} + +template <class T> +T* RingBuffer<T>::iterator::operator->() { + return mPtr + mPos; +} + +template <class T> +RingBuffer<T>::const_iterator::const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr) : + mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {} + +template <class T> +typename RingBuffer<T>::const_iterator& RingBuffer<T>::const_iterator::operator++() { + ++mCtr; + + if (CC_UNLIKELY(mCtr == mSize)) { + mPos = mSize; + return *this; + } + + mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1); + return *this; +} + +template <class T> +typename RingBuffer<T>::const_iterator RingBuffer<T>::const_iterator::operator++(int) { + const_iterator tmp{mPtr, mSize, mPos, mCtr}; + ++(*this); + return tmp; +} + +template <class T> +bool RingBuffer<T>::const_iterator::operator==(const const_iterator& rhs) { + return (mPtr + mPos) == (rhs.mPtr + rhs.mPos); +} + +template <class T> +bool RingBuffer<T>::const_iterator::operator!=(const const_iterator& rhs) { + return (mPtr + mPos) != (rhs.mPtr + rhs.mPos); +} + +template <class T> +const T& RingBuffer<T>::const_iterator::operator*() { + return *(mPtr + mPos); +} + +template <class T> +const T* RingBuffer<T>::const_iterator::operator->() { + return mPtr + mPos; +} + +template <class T> +void RingBuffer<T>::add(const T& item) { + if (mBuffer.size() < mMaxBufferSize) { + mBuffer.push_back(item); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); + return; + } + + mBuffer[mFrontIdx] = item; + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template <class T> +void RingBuffer<T>::add(T&& item) { + if (mBuffer.size() != mMaxBufferSize) { + mBuffer.push_back(std::forward<T>(item)); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); + return; + } + + // Only works for types with move assignment operator + mBuffer[mFrontIdx] = std::forward<T>(item); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template <class T> +template <class... Args> +void RingBuffer<T>::emplace(Args&&... args) { + if (mBuffer.size() != mMaxBufferSize) { + mBuffer.emplace_back(std::forward<Args>(args)...); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); + return; + } + + // Only works for types with move assignment operator + mBuffer[mFrontIdx] = T(std::forward<Args>(args)...); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template <class T> +typename RingBuffer<T>::iterator RingBuffer<T>::begin() { + size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1; + return iterator(mBuffer.data(), mBuffer.size(), (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0); +} + +template <class T> +typename RingBuffer<T>::iterator RingBuffer<T>::end() { + size_t s = mBuffer.size(); + return iterator(mBuffer.data(), s, s, s); +} + +template <class T> +typename RingBuffer<T>::const_iterator RingBuffer<T>::begin() const { + size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1; + return const_iterator(mBuffer.data(), mBuffer.size(), + (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0); +} + +template <class T> +typename RingBuffer<T>::const_iterator RingBuffer<T>::end() const { + size_t s = mBuffer.size(); + return const_iterator(mBuffer.data(), s, s, s); +} + +template <class T> +T& RingBuffer<T>::operator[](size_t index) { + LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.", + index, mBuffer.size()); + size_t pos = (index >= mFrontIdx) ? + mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index; + return mBuffer[pos]; +} + +template <class T> +const T& RingBuffer<T>::operator[](size_t index) const { + LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.", + index, mBuffer.size()); + size_t pos = (index >= mFrontIdx) ? + mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index; + return mBuffer[pos]; +} + +template <class T> +size_t RingBuffer<T>::size() const { + return mBuffer.size(); +} + +template <class T> +void RingBuffer<T>::clear() { + mBuffer.clear(); + mFrontIdx = 0; +} + +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_RING_BUFFER_H + + diff --git a/services/medialog/Android.mk b/services/medialog/Android.mk index 95f2fef..03438bf 100644 --- a/services/medialog/Android.mk +++ b/services/medialog/Android.mk @@ -10,4 +10,6 @@ LOCAL_MODULE:= libmedialogservice LOCAL_32_BIT_ONLY := true +LOCAL_C_INCLUDES := $(call include-path-for, audio-utils) + include $(BUILD_SHARED_LIBRARY) diff --git a/services/radio/Android.mk b/services/radio/Android.mk new file mode 100644 index 0000000..9ee5666 --- /dev/null +++ b/services/radio/Android.mk @@ -0,0 +1,36 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + + +LOCAL_SRC_FILES:= \ + RadioService.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libui \ + liblog \ + libutils \ + libbinder \ + libcutils \ + libmedia \ + libhardware \ + libradio \ + libradio_metadata + +LOCAL_MODULE:= libradioservice + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/radio/RadioRegions.h b/services/radio/RadioRegions.h new file mode 100644 index 0000000..3335b8a --- /dev/null +++ b/services/radio/RadioRegions.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_RADIO_REGIONS_H +#define ANDROID_HARDWARE_RADIO_REGIONS_H + +namespace android { + +#define RADIO_BAND_LOWER_FM_ITU1 87500 +#define RADIO_BAND_UPPER_FM_ITU1 108000 +#define RADIO_BAND_SPACING_FM_ITU1 100 + +#define RADIO_BAND_LOWER_FM_ITU2 87900 +#define RADIO_BAND_UPPER_FM_ITU2 107900 +#define RADIO_BAND_SPACING_FM_ITU2 200 + +#define RADIO_BAND_LOWER_FM_JAPAN 76000 +#define RADIO_BAND_UPPER_FM_JAPAN 90000 +#define RADIO_BAND_SPACING_FM_JAPAN 100 + +#define RADIO_BAND_LOWER_FM_OIRT 65800 +#define RADIO_BAND_UPPER_FM_OIRT 74000 +#define RADIO_BAND_SPACING_FM_OIRT 10 + +#define RADIO_BAND_LOWER_LW 153 +#define RADIO_BAND_UPPER_LW 279 +#define RADIO_BAND_SPACING_LW 9 + +#define RADIO_BAND_LOWER_MW_IUT1 531 +#define RADIO_BAND_UPPER_MW_ITU1 1611 +#define RADIO_BAND_SPACING_MW_ITU1 9 + +#define RADIO_BAND_LOWER_MW_IUT2 540 +#define RADIO_BAND_UPPER_MW_ITU2 1610 +#define RADIO_BAND_SPACING_MW_ITU2 10 + +#define RADIO_BAND_LOWER_SW 2300 +#define RADIO_BAND_UPPER_SW 26100 +#define RADIO_BAND_SPACING_SW 5 + + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +const radio_band_config_t sKnownRegionConfigs[] = { + { // FM ITU 1 + RADIO_REGION_ITU_1, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_ITU1, + RADIO_BAND_UPPER_FM_ITU1, + 1, + {RADIO_BAND_SPACING_FM_ITU1}, + { + RADIO_DEEMPHASIS_50, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM Americas + RADIO_REGION_ITU_2, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_ITU2, + RADIO_BAND_UPPER_FM_ITU2, + 1, + {RADIO_BAND_SPACING_FM_ITU2}, + { + RADIO_DEEMPHASIS_75, + true, + RADIO_RDS_US, + true, + true, + } + } + }, + { // FM Japan + RADIO_REGION_JAPAN, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_JAPAN, + RADIO_BAND_UPPER_FM_JAPAN, + 1, + {RADIO_BAND_SPACING_FM_JAPAN}, + { + RADIO_DEEMPHASIS_50, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM Korea + RADIO_REGION_KOREA, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_ITU1, + RADIO_BAND_UPPER_FM_ITU1, + 1, + {RADIO_BAND_SPACING_FM_ITU1}, + { + RADIO_DEEMPHASIS_75, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM OIRT + RADIO_REGION_OIRT, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_OIRT, + RADIO_BAND_UPPER_FM_OIRT, + 1, + {RADIO_BAND_SPACING_FM_OIRT}, + { + RADIO_DEEMPHASIS_50, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM US HD radio + RADIO_REGION_ITU_2, + { + RADIO_BAND_FM_HD, + false, + RADIO_BAND_LOWER_FM_ITU2, + RADIO_BAND_UPPER_FM_ITU2, + 1, + {RADIO_BAND_SPACING_FM_ITU2}, + { + RADIO_DEEMPHASIS_75, + true, + RADIO_RDS_US, + true, + true, + } + } + }, + { // AM LW + RADIO_REGION_ITU_1, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_LW, + RADIO_BAND_UPPER_LW, + 1, + {RADIO_BAND_SPACING_LW}, + { + } + } + }, + { // AM SW + RADIO_REGION_ITU_1, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_SW, + RADIO_BAND_UPPER_SW, + 1, + {RADIO_BAND_SPACING_SW}, + { + } + } + }, + { // AM MW ITU1 + RADIO_REGION_ITU_1, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_MW_IUT1, + RADIO_BAND_UPPER_MW_ITU1, + 1, + {RADIO_BAND_SPACING_MW_ITU1}, + { + } + } + }, + { // AM MW ITU2 + RADIO_REGION_ITU_2, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_MW_IUT2, + RADIO_BAND_UPPER_MW_ITU2, + 1, + {RADIO_BAND_SPACING_MW_ITU2}, + { + } + } + } +}; + + +} // namespace android + +#endif // ANDROID_HARDWARE_RADIO_REGIONS_H diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp new file mode 100644 index 0000000..a6c2bdf --- /dev/null +++ b/services/radio/RadioService.cpp @@ -0,0 +1,901 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "RadioService" +//#define LOG_NDEBUG 0 + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <pthread.h> + +#include <system/audio.h> +#include <system/audio_policy.h> +#include <system/radio.h> +#include <system/radio_metadata.h> +#include <cutils/atomic.h> +#include <cutils/properties.h> +#include <hardware/hardware.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <binder/IServiceManager.h> +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <hardware/radio.h> +#include <media/AudioSystem.h> +#include "RadioService.h" +#include "RadioRegions.h" + +namespace android { + +static const char kRadioTunerAudioDeviceName[] = "Radio tuner source"; + +RadioService::RadioService() + : BnRadioService(), mNextUniqueId(1) +{ + ALOGI("%s", __FUNCTION__); +} + +void RadioService::onFirstRef() +{ + const hw_module_t *mod; + int rc; + struct radio_hw_device *dev; + + ALOGI("%s", __FUNCTION__); + + rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, RADIO_HARDWARE_MODULE_ID_FM, &mod); + if (rc != 0) { + ALOGE("couldn't load radio module %s.%s (%s)", + RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + rc = radio_hw_device_open(mod, &dev); + if (rc != 0) { + ALOGE("couldn't open radio hw device in %s.%s (%s)", + RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + if (dev->common.version != RADIO_DEVICE_API_VERSION_CURRENT) { + ALOGE("wrong radio hw device version %04x", dev->common.version); + return; + } + + struct radio_hal_properties halProperties; + rc = dev->get_properties(dev, &halProperties); + if (rc != 0) { + ALOGE("could not read implementation properties"); + return; + } + + radio_properties_t properties; + properties.handle = + (radio_handle_t)android_atomic_inc(&mNextUniqueId); + + ALOGI("loaded default module %s, handle %d", properties.product, properties.handle); + + convertProperties(&properties, &halProperties); + sp<Module> module = new Module(dev, properties); + mModules.add(properties.handle, module); +} + +RadioService::~RadioService() +{ + for (size_t i = 0; i < mModules.size(); i++) { + radio_hw_device_close(mModules.valueAt(i)->hwDevice()); + } +} + +status_t RadioService::listModules(struct radio_properties *properties, + uint32_t *numModules) +{ + ALOGV("listModules"); + + AutoMutex lock(mServiceLock); + if (numModules == NULL || (*numModules != 0 && properties == NULL)) { + return BAD_VALUE; + } + size_t maxModules = *numModules; + *numModules = mModules.size(); + for (size_t i = 0; i < mModules.size() && i < maxModules; i++) { + properties[i] = mModules.valueAt(i)->properties(); + } + return NO_ERROR; +} + +status_t RadioService::attach(radio_handle_t handle, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool withAudio, + sp<IRadio>& radio) +{ + ALOGV("%s %d config %p withAudio %d", __FUNCTION__, handle, config, withAudio); + + AutoMutex lock(mServiceLock); + radio.clear(); + if (client == 0) { + return BAD_VALUE; + } + ssize_t index = mModules.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + sp<Module> module = mModules.valueAt(index); + + if (config == NULL) { + config = module->getDefaultConfig(); + if (config == NULL) { + return INVALID_OPERATION; + } + } + ALOGV("%s region %d type %d", __FUNCTION__, config->region, config->band.type); + + radio = module->addClient(client, config, withAudio); + + if (radio == 0) { + NO_INIT; + } + return NO_ERROR; +} + + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 60000; + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleep); + } + return locked; +} + +status_t RadioService::dump(int fd, const Vector<String16>& args __unused) { + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + result.appendFormat("Permission Denial: can't dump RadioService"); + write(fd, result.string(), result.size()); + } else { + bool locked = tryLock(mServiceLock); + // failed to lock - RadioService is probably deadlocked + if (!locked) { + result.append("RadioService may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + if (locked) mServiceLock.unlock(); + } + return NO_ERROR; +} + +status_t RadioService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + return BnRadioService::onTransact(code, data, reply, flags); +} + + +// static +void RadioService::callback(radio_hal_event_t *halEvent, void *cookie) +{ + CallbackThread *callbackThread = (CallbackThread *)cookie; + if (callbackThread == NULL) { + return; + } + callbackThread->sendEvent(halEvent); +} + +/* static */ +void RadioService::convertProperties(radio_properties_t *properties, + const radio_hal_properties_t *halProperties) +{ + memset(properties, 0, sizeof(struct radio_properties)); + properties->class_id = halProperties->class_id; + strlcpy(properties->implementor, halProperties->implementor, + RADIO_STRING_LEN_MAX); + strlcpy(properties->product, halProperties->product, + RADIO_STRING_LEN_MAX); + strlcpy(properties->version, halProperties->version, + RADIO_STRING_LEN_MAX); + strlcpy(properties->serial, halProperties->serial, + RADIO_STRING_LEN_MAX); + properties->num_tuners = halProperties->num_tuners; + properties->num_audio_sources = halProperties->num_audio_sources; + properties->supports_capture = halProperties->supports_capture; + + for (size_t i = 0; i < ARRAY_SIZE(sKnownRegionConfigs); i++) { + const radio_hal_band_config_t *band = &sKnownRegionConfigs[i].band; + size_t j; + for (j = 0; j < halProperties->num_bands; j++) { + const radio_hal_band_config_t *halBand = &halProperties->bands[j]; + size_t k; + if (band->type != halBand->type) continue; + if (band->lower_limit < halBand->lower_limit) continue; + if (band->upper_limit > halBand->upper_limit) continue; + for (k = 0; k < halBand->num_spacings; k++) { + if (band->spacings[0] == halBand->spacings[k]) break; + } + if (k == halBand->num_spacings) continue; + if (band->type == RADIO_BAND_AM) break; + if ((band->fm.deemphasis & halBand->fm.deemphasis) == 0) continue; + if (halBand->fm.rds == 0) break; + if ((band->fm.rds & halBand->fm.rds) != 0) break; + } + if (j == halProperties->num_bands) continue; + + ALOGI("convertProperties() Adding band type %d region %d", + sKnownRegionConfigs[i].band.type , sKnownRegionConfigs[i].region); + + memcpy(&properties->bands[properties->num_bands++], + &sKnownRegionConfigs[i], + sizeof(radio_band_config_t)); + } +} + +#undef LOG_TAG +#define LOG_TAG "RadioService::CallbackThread" + +RadioService::CallbackThread::CallbackThread(const wp<ModuleClient>& moduleClient) + : mModuleClient(moduleClient), mMemoryDealer(new MemoryDealer(1024 * 1024, "RadioService")) +{ +} + +RadioService::CallbackThread::~CallbackThread() +{ + mEventQueue.clear(); +} + +void RadioService::CallbackThread::onFirstRef() +{ + run("RadioService cbk", ANDROID_PRIORITY_URGENT_AUDIO); +} + +bool RadioService::CallbackThread::threadLoop() +{ + while (!exitPending()) { + sp<IMemory> eventMemory; + sp<ModuleClient> moduleClient; + { + Mutex::Autolock _l(mCallbackLock); + while (mEventQueue.isEmpty() && !exitPending()) { + ALOGV("CallbackThread::threadLoop() sleep"); + mCallbackCond.wait(mCallbackLock); + ALOGV("CallbackThread::threadLoop() wake up"); + } + if (exitPending()) { + break; + } + eventMemory = mEventQueue[0]; + mEventQueue.removeAt(0); + moduleClient = mModuleClient.promote(); + } + if (moduleClient != 0) { + moduleClient->onCallbackEvent(eventMemory); + eventMemory.clear(); + } + } + return false; +} + +void RadioService::CallbackThread::exit() +{ + Mutex::Autolock _l(mCallbackLock); + requestExit(); + mCallbackCond.broadcast(); +} + +sp<IMemory> RadioService::CallbackThread::prepareEvent(radio_hal_event_t *halEvent) +{ + sp<IMemory> eventMemory; + + size_t headerSize = + (sizeof(struct radio_event) + sizeof(unsigned int) - 1) /sizeof(unsigned int); + size_t metadataSize = 0; + switch (halEvent->type) { + case RADIO_EVENT_TUNED: + case RADIO_EVENT_AF_SWITCH: + if (radio_metadata_check(halEvent->info.metadata) == 0) { + metadataSize = radio_metadata_get_size(halEvent->info.metadata); + } + break; + case RADIO_EVENT_METADATA: + if (radio_metadata_check(halEvent->metadata) != 0) { + return eventMemory; + } + metadataSize = radio_metadata_get_size(halEvent->metadata); + break; + default: + break; + } + size_t size = headerSize + metadataSize; + eventMemory = mMemoryDealer->allocate(size); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + eventMemory.clear(); + return eventMemory; + } + struct radio_event *event = (struct radio_event *)eventMemory->pointer(); + event->type = halEvent->type; + event->status = halEvent->status; + + switch (event->type) { + case RADIO_EVENT_CONFIG: + event->config.band = halEvent->config; + break; + case RADIO_EVENT_TUNED: + case RADIO_EVENT_AF_SWITCH: + event->info = halEvent->info; + if (metadataSize != 0) { + memcpy((char *)event + headerSize, halEvent->info.metadata, metadataSize); + // replace meta data pointer by offset while in shared memory so that receiving side + // can restore the pointer in destination process. + event->info.metadata = (radio_metadata_t *)headerSize; + } + break; + case RADIO_EVENT_TA: + case RADIO_EVENT_ANTENNA: + case RADIO_EVENT_CONTROL: + event->on = halEvent->on; + break; + case RADIO_EVENT_METADATA: + memcpy((char *)event + headerSize, halEvent->metadata, metadataSize); + // replace meta data pointer by offset while in shared memory so that receiving side + // can restore the pointer in destination process. + event->metadata = (radio_metadata_t *)headerSize; + break; + case RADIO_EVENT_HW_FAILURE: + default: + break; + } + + return eventMemory; +} + +void RadioService::CallbackThread::sendEvent(radio_hal_event_t *event) + { + sp<IMemory> eventMemory = prepareEvent(event); + if (eventMemory == 0) { + return; + } + + AutoMutex lock(mCallbackLock); + mEventQueue.add(eventMemory); + mCallbackCond.signal(); + ALOGV("%s DONE", __FUNCTION__); +} + + +#undef LOG_TAG +#define LOG_TAG "RadioService::Module" + +RadioService::Module::Module(radio_hw_device* hwDevice, radio_properties properties) + : mHwDevice(hwDevice), mProperties(properties), mMute(true) +{ +} + +RadioService::Module::~Module() { + mModuleClients.clear(); +} + +status_t RadioService::Module::dump(int fd __unused, const Vector<String16>& args __unused) { + String8 result; + return NO_ERROR; +} + +sp<RadioService::ModuleClient> RadioService::Module::addClient(const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio) +{ + ALOGV("addClient() %p config %p product %s", this, config, mProperties.product); + AutoMutex lock(mLock); + sp<ModuleClient> moduleClient; + int ret; + + for (size_t i = 0; i < mModuleClients.size(); i++) { + if (mModuleClients[i]->client() == client) { + // client already connected: reject + return moduleClient; + } + } + moduleClient = new ModuleClient(this, client, config, audio); + + struct radio_hal_band_config halConfig; + halConfig = config->band; + + // Tuner preemption logic: + // There is a limited amount of tuners and a limited amount of radio audio sources per module. + // The minimum is one tuner and one audio source. + // The numbers of tuners and sources are indicated in the module properties. + // NOTE: current framework implementation only supports one radio audio source. + // It is possible to open more than one tuner at a time but only one tuner can be connected + // to the radio audio source (AUDIO_DEVICE_IN_FM_TUNER). + // The base rule is that a newly connected tuner always wins, i.e. always gets a tuner + // and can use the audio source if requested. + // If another client is preempted, it is notified by a callback with RADIO_EVENT_CONTROL + // indicating loss of control. + // - If the newly connected client requests the audio source (audio == true): + // - if an audio source is available + // no problem + // - if not: + // the oldest client in the list using audio is preempted. + // - If the newly connected client does not request the audio source (audio == false): + // - if a tuner is available + // no problem + // - if not: + // The oldest client not using audio is preempted first and if none is found the + // the oldest client using audio is preempted. + // Each time a tuner using the audio source is opened or closed, the audio policy manager is + // notified of the connection or disconnection of AUDIO_DEVICE_IN_FM_TUNER. + + sp<ModuleClient> oldestTuner; + sp<ModuleClient> oldestAudio; + size_t allocatedTuners = 0; + size_t allocatedAudio = 0; + for (size_t i = 0; i < mModuleClients.size(); i++) { + if (mModuleClients[i]->getTuner() != NULL) { + if (mModuleClients[i]->audio()) { + if (oldestAudio == 0) { + oldestAudio = mModuleClients[i]; + } + allocatedAudio++; + } else { + if (oldestTuner == 0) { + oldestTuner = mModuleClients[i]; + } + allocatedTuners++; + } + } + } + + const struct radio_tuner *halTuner; + sp<ModuleClient> preemtedClient; + if (audio) { + if (allocatedAudio >= mProperties.num_audio_sources) { + ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch"); + preemtedClient = oldestAudio; + } + } else { + if (allocatedAudio + allocatedTuners >= mProperties.num_tuners) { + if (allocatedTuners != 0) { + ALOG_ASSERT(oldestTuner != 0, "addClient() allocatedTuners/oldestTuner mismatch"); + preemtedClient = oldestTuner; + } else { + ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch"); + preemtedClient = oldestAudio; + } + } + } + if (preemtedClient != 0) { + halTuner = preemtedClient->getTuner(); + preemtedClient->setTuner(NULL); + mHwDevice->close_tuner(mHwDevice, halTuner); + if (preemtedClient->audio()) { + notifyDeviceConnection(false, ""); + } + } + + ret = mHwDevice->open_tuner(mHwDevice, &halConfig, audio, + RadioService::callback, moduleClient->callbackThread().get(), + &halTuner); + if (ret == 0) { + ALOGV("addClient() setTuner %p", halTuner); + moduleClient->setTuner(halTuner); + mModuleClients.add(moduleClient); + if (audio) { + notifyDeviceConnection(true, ""); + } + } else { + moduleClient.clear(); + } + + + ALOGV("addClient() DONE moduleClient %p", moduleClient.get()); + + return moduleClient; +} + +void RadioService::Module::removeClient(const sp<ModuleClient>& moduleClient) { + ALOGV("removeClient()"); + AutoMutex lock(mLock); + int ret; + ssize_t index = -1; + + for (size_t i = 0; i < mModuleClients.size(); i++) { + if (mModuleClients[i] == moduleClient) { + index = i; + break; + } + } + if (index == -1) { + return; + } + + mModuleClients.removeAt(index); + const struct radio_tuner *halTuner = moduleClient->getTuner(); + if (halTuner == NULL) { + return; + } + + mHwDevice->close_tuner(mHwDevice, halTuner); + if (moduleClient->audio()) { + notifyDeviceConnection(false, ""); + } + + mMute = true; + + if (mModuleClients.isEmpty()) { + return; + } + + // Tuner reallocation logic: + // When a client is removed and was controlling a tuner, this tuner will be allocated to a + // previously preempted client. This client will be notified by a callback with + // RADIO_EVENT_CONTROL indicating gain of control. + // - If a preempted client is waiting for an audio source and one becomes available: + // Allocate the tuner to the most recently added client waiting for an audio source + // - If not: + // Allocate the tuner to the most recently added client. + // Each time a tuner using the audio source is opened or closed, the audio policy manager is + // notified of the connection or disconnection of AUDIO_DEVICE_IN_FM_TUNER. + + sp<ModuleClient> youngestClient; + sp<ModuleClient> youngestClientAudio; + size_t allocatedTuners = 0; + size_t allocatedAudio = 0; + for (ssize_t i = mModuleClients.size() - 1; i >= 0; i--) { + if (mModuleClients[i]->getTuner() == NULL) { + if (mModuleClients[i]->audio()) { + if (youngestClientAudio == 0) { + youngestClientAudio = mModuleClients[i]; + } + } else { + if (youngestClient == 0) { + youngestClient = mModuleClients[i]; + } + } + } else { + if (mModuleClients[i]->audio()) { + allocatedAudio++; + } else { + allocatedTuners++; + } + } + } + + ALOG_ASSERT(allocatedTuners + allocatedAudio < mProperties.num_tuners, + "removeClient() removed client but no tuner available"); + + ALOG_ASSERT(!moduleClient->audio() || allocatedAudio < mProperties.num_audio_sources, + "removeClient() removed audio client but no tuner with audio available"); + + if (allocatedAudio < mProperties.num_audio_sources && youngestClientAudio != 0) { + youngestClient = youngestClientAudio; + } + + ALOG_ASSERT(youngestClient != 0, "removeClient() removed client no candidate found for tuner"); + + struct radio_hal_band_config halConfig = youngestClient->halConfig(); + ret = mHwDevice->open_tuner(mHwDevice, &halConfig, youngestClient->audio(), + RadioService::callback, moduleClient->callbackThread().get(), + &halTuner); + + if (ret == 0) { + youngestClient->setTuner(halTuner); + if (youngestClient->audio()) { + notifyDeviceConnection(true, ""); + } + } +} + +status_t RadioService::Module::setMute(bool mute) +{ + Mutex::Autolock _l(mLock); + if (mute != mMute) { + mMute = mute; + //TODO notifify audio policy manager of media activity on radio audio device + } + return NO_ERROR; +} + +status_t RadioService::Module::getMute(bool *mute) +{ + Mutex::Autolock _l(mLock); + *mute = mMute; + return NO_ERROR; +} + + +const struct radio_band_config *RadioService::Module::getDefaultConfig() const +{ + if (mProperties.num_bands == 0) { + return NULL; + } + return &mProperties.bands[0]; +} + +void RadioService::Module::notifyDeviceConnection(bool connected, + const char *address) { + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_FM_TUNER, + connected ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE : + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + address, kRadioTunerAudioDeviceName); + IPCThreadState::self()->restoreCallingIdentity(token); +} + +#undef LOG_TAG +#define LOG_TAG "RadioService::ModuleClient" + +RadioService::ModuleClient::ModuleClient(const sp<Module>& module, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio) + : mModule(module), mClient(client), mConfig(*config), mAudio(audio), mTuner(NULL) +{ +} + +void RadioService::ModuleClient::onFirstRef() +{ + mCallbackThread = new CallbackThread(this); + IInterface::asBinder(mClient)->linkToDeath(this); +} + +RadioService::ModuleClient::~ModuleClient() { + if (mClient != 0) { + IInterface::asBinder(mClient)->unlinkToDeath(this); + mClient.clear(); + } + if (mCallbackThread != 0) { + mCallbackThread->exit(); + } +} + +status_t RadioService::ModuleClient::dump(int fd __unused, + const Vector<String16>& args __unused) { + String8 result; + return NO_ERROR; +} + +void RadioService::ModuleClient::detach() { + ALOGV("%s", __FUNCTION__); + sp<ModuleClient> strongMe = this; + { + AutoMutex lock(mLock); + if (mClient != 0) { + IInterface::asBinder(mClient)->unlinkToDeath(this); + mClient.clear(); + } + } + sp<Module> module = mModule.promote(); + if (module == 0) { + return; + } + module->removeClient(this); +} + +radio_hal_band_config_t RadioService::ModuleClient::halConfig() const +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + return mConfig.band; +} + +const struct radio_tuner *RadioService::ModuleClient::getTuner() const +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + return mTuner; +} + +void RadioService::ModuleClient::setTuner(const struct radio_tuner *tuner) +{ + ALOGV("%s %p", __FUNCTION__, this); + + AutoMutex lock(mLock); + mTuner = tuner; + ALOGV("%s locked", __FUNCTION__); + + radio_hal_event_t event; + event.type = RADIO_EVENT_CONTROL; + event.status = 0; + event.on = mTuner != NULL; + mCallbackThread->sendEvent(&event); + ALOGV("%s DONE", __FUNCTION__); + +} + +status_t RadioService::ModuleClient::setConfiguration(const struct radio_band_config *config) +{ + AutoMutex lock(mLock); + status_t status = NO_ERROR; + ALOGV("%s locked", __FUNCTION__); + + if (mTuner != NULL) { + struct radio_hal_band_config halConfig; + halConfig = config->band; + status = (status_t)mTuner->set_configuration(mTuner, &halConfig); + if (status == NO_ERROR) { + mConfig = *config; + } + } else { + mConfig = *config; + status == INVALID_OPERATION; + } + + return status; +} + +status_t RadioService::ModuleClient::getConfiguration(struct radio_band_config *config) +{ + AutoMutex lock(mLock); + status_t status = NO_ERROR; + ALOGV("%s locked", __FUNCTION__); + + if (mTuner != NULL) { + struct radio_hal_band_config halConfig; + status = (status_t)mTuner->get_configuration(mTuner, &halConfig); + if (status == NO_ERROR) { + mConfig.band = halConfig; + } + } + *config = mConfig; + + return status; +} + +status_t RadioService::ModuleClient::setMute(bool mute) +{ + sp<Module> module; + { + Mutex::Autolock _l(mLock); + ALOGV("%s locked", __FUNCTION__); + if (mTuner == NULL || !mAudio) { + return INVALID_OPERATION; + } + module = mModule.promote(); + if (module == 0) { + return NO_INIT; + } + } + module->setMute(mute); + return NO_ERROR; +} + +status_t RadioService::ModuleClient::getMute(bool *mute) +{ + sp<Module> module; + { + Mutex::Autolock _l(mLock); + ALOGV("%s locked", __FUNCTION__); + module = mModule.promote(); + if (module == 0) { + return NO_INIT; + } + } + return module->getMute(mute); +} + +status_t RadioService::ModuleClient::scan(radio_direction_t direction, bool skipSubChannel) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->scan(mTuner, direction, skipSubChannel); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::step(radio_direction_t direction, bool skipSubChannel) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->step(mTuner, direction, skipSubChannel); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::tune(unsigned int channel, unsigned int subChannel) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->tune(mTuner, channel, subChannel); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::cancel() +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->cancel(mTuner); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::getProgramInformation(struct radio_program_info *info) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->get_program_information(mTuner, info); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::hasControl(bool *hasControl) +{ + Mutex::Autolock lock(mLock); + ALOGV("%s locked", __FUNCTION__); + *hasControl = mTuner != NULL; + return NO_ERROR; +} + +void RadioService::ModuleClient::onCallbackEvent(const sp<IMemory>& eventMemory) +{ + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + + sp<IRadioClient> client; + { + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + radio_event_t *event = (radio_event_t *)eventMemory->pointer(); + switch (event->type) { + case RADIO_EVENT_CONFIG: + mConfig.band = event->config.band; + event->config.region = mConfig.region; + break; + default: + break; + } + + client = mClient; + } + if (client != 0) { + client->onEvent(eventMemory); + } +} + + +void RadioService::ModuleClient::binderDied( + const wp<IBinder> &who __unused) { + ALOGW("client binder died for client %p", this); + detach(); +} + +}; // namespace android diff --git a/services/radio/RadioService.h b/services/radio/RadioService.h new file mode 100644 index 0000000..49feda6 --- /dev/null +++ b/services/radio/RadioService.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_RADIO_SERVICE_H +#define ANDROID_HARDWARE_RADIO_SERVICE_H + +#include <utils/Vector.h> +//#include <binder/AppOpsManager.h> +#include <binder/MemoryDealer.h> +#include <binder/BinderService.h> +#include <binder/IAppOpsCallback.h> +#include <radio/IRadioService.h> +#include <radio/IRadio.h> +#include <radio/IRadioClient.h> +#include <system/radio.h> +#include <hardware/radio.h> + +namespace android { + +class MemoryHeapBase; + +class RadioService : + public BinderService<RadioService>, + public BnRadioService +{ + friend class BinderService<RadioService>; + +public: + class ModuleClient; + class Module; + + static char const* getServiceName() { return "media.radio"; } + + RadioService(); + virtual ~RadioService(); + + // IRadioService + virtual status_t listModules(struct radio_properties *properties, + uint32_t *numModules); + + virtual status_t attach(radio_handle_t handle, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool withAudio, + sp<IRadio>& radio); + + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + virtual status_t dump(int fd, const Vector<String16>& args); + + + class Module : public virtual RefBase { + public: + + Module(radio_hw_device* hwDevice, + struct radio_properties properties); + + virtual ~Module(); + + sp<ModuleClient> addClient(const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio); + + void removeClient(const sp<ModuleClient>& moduleClient); + + status_t setMute(bool mute); + + status_t getMute(bool *mute); + + virtual status_t dump(int fd, const Vector<String16>& args); + + const struct radio_hw_device *hwDevice() const { return mHwDevice; } + const struct radio_properties properties() const { return mProperties; } + const struct radio_band_config *getDefaultConfig() const ; + + private: + + void notifyDeviceConnection(bool connected, const char *address); + + Mutex mLock; // protects mModuleClients + const struct radio_hw_device *mHwDevice; // HAL hardware device + const struct radio_properties mProperties; // cached hardware module properties + Vector< sp<ModuleClient> > mModuleClients; // list of attached clients + bool mMute; // radio audio source state + // when unmuted, audio is routed to the + // output device selected for media use case. + }; // class Module + + class CallbackThread : public Thread { + public: + + CallbackThread(const wp<ModuleClient>& moduleClient); + + virtual ~CallbackThread(); + + + // Thread virtuals + virtual bool threadLoop(); + + // RefBase + virtual void onFirstRef(); + + void exit(); + + void sendEvent(radio_hal_event_t *halEvent); + sp<IMemory> prepareEvent(radio_hal_event_t *halEvent); + + private: + wp<ModuleClient> mModuleClient; // client module the thread belongs to + Condition mCallbackCond; // condition signaled when a new event is posted + Mutex mCallbackLock; // protects mEventQueue + Vector< sp<IMemory> > mEventQueue; // pending callback events + sp<MemoryDealer> mMemoryDealer; // shared memory for callback event + }; // class CallbackThread + + class ModuleClient : public BnRadio, + public IBinder::DeathRecipient { + public: + + ModuleClient(const sp<Module>& module, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio); + + virtual ~ModuleClient(); + + // IRadio + virtual void detach(); + + virtual status_t setConfiguration(const struct radio_band_config *config); + + virtual status_t getConfiguration(struct radio_band_config *config); + + virtual status_t setMute(bool mute); + + virtual status_t getMute(bool *mute); + + virtual status_t scan(radio_direction_t direction, bool skipSubChannel); + + virtual status_t step(radio_direction_t direction, bool skipSubChannel); + + virtual status_t tune(unsigned int channel, unsigned int subChannel); + + virtual status_t cancel(); + + virtual status_t getProgramInformation(struct radio_program_info *info); + + virtual status_t hasControl(bool *hasControl); + + virtual status_t dump(int fd, const Vector<String16>& args); + + sp<IRadioClient> client() const { return mClient; } + wp<Module> module() const { return mModule; } + radio_hal_band_config_t halConfig() const; + sp<CallbackThread> callbackThread() const { return mCallbackThread; } + void setTuner(const struct radio_tuner *tuner); + const struct radio_tuner *getTuner() const; + bool audio() const { return mAudio; } + + void onCallbackEvent(const sp<IMemory>& event); + + virtual void onFirstRef(); + + + // IBinder::DeathRecipient implementation + virtual void binderDied(const wp<IBinder> &who); + + private: + + mutable Mutex mLock; // protects mClient, mConfig and mTuner + wp<Module> mModule; // The module this client is attached to + sp<IRadioClient> mClient; // event callback binder interface + radio_band_config_t mConfig; // current band configuration + sp<CallbackThread> mCallbackThread; // event callback thread + const bool mAudio; + const struct radio_tuner *mTuner; // HAL tuner interface. NULL indicates that + // this client does not have control on any + // tuner + }; // class ModuleClient + + + static void callback(radio_hal_event_t *halEvent, void *cookie); + +private: + + virtual void onFirstRef(); + + static void convertProperties(radio_properties_t *properties, + const radio_hal_properties_t *halProperties); + Mutex mServiceLock; // protects mModules + volatile int32_t mNextUniqueId; // for module ID allocation + DefaultKeyedVector< radio_handle_t, sp<Module> > mModules; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_RADIO_SERVICE_H diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk index 572ae56..ecc49ae 100644 --- a/services/soundtrigger/Android.mk +++ b/services/soundtrigger/Android.mk @@ -32,9 +32,7 @@ LOCAL_SHARED_LIBRARIES:= \ libcutils \ libhardware \ libsoundtrigger \ - libmedia - -LOCAL_STATIC_LIBRARIES := \ + libmedia \ libserviceutility LOCAL_C_INCLUDES += \ diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp index b5aaee3..081aff7 100644 --- a/services/soundtrigger/SoundTriggerHwService.cpp +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -143,7 +143,7 @@ status_t SoundTriggerHwService::attach(const sound_trigger_module_handle_t handl sp<Module> module = mModules.valueAt(index); module->setClient(client); - client->asBinder()->linkToDeath(module); + IInterface::asBinder(client)->linkToDeath(module); moduleInterface = module; module->setCaptureState_l(mCaptureState); @@ -510,7 +510,7 @@ void SoundTriggerHwService::Module::detach() { mModels.clear(); } if (mClient != 0) { - mClient->asBinder()->unlinkToDeath(this); + IInterface::asBinder(mClient)->unlinkToDeath(this); } sp<SoundTriggerHwService> service = mService.promote(); if (service == 0) { @@ -535,6 +535,16 @@ status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& modelM (struct sound_trigger_sound_model *)modelMemory->pointer(); AutoMutex lock(mLock); + + if (mModels.size() >= mDescriptor.properties.max_sound_models) { + if (mModels.size() == 0) { + return INVALID_OPERATION; + } + ALOGW("loadSoundModel() max number of models exceeded %d making room for a new one", + mDescriptor.properties.max_sound_models); + unloadSoundModel_l(mModels.valueAt(0)->mHandle); + } + status_t status = mHwDevice->load_sound_model(mHwDevice, sound_model, SoundTriggerHwService::soundModelCallback, @@ -566,6 +576,11 @@ status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t ha } AutoMutex lock(mLock); + return unloadSoundModel_l(handle); +} + +status_t SoundTriggerHwService::Module::unloadSoundModel_l(sound_model_handle_t handle) +{ ssize_t index = mModels.indexOfKey(handle); if (index < 0) { return BAD_VALUE; @@ -574,6 +589,7 @@ status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t ha mModels.removeItem(handle); if (model->mState == Model::STATE_ACTIVE) { mHwDevice->stop_recognition(mHwDevice, model->mHandle); + model->mState = Model::STATE_IDLE; } AudioSystem::releaseSoundTriggerSession(model->mCaptureSession); return mHwDevice->unload_sound_model(mHwDevice, handle); diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h index d05dacd..2619a5f 100644 --- a/services/soundtrigger/SoundTriggerHwService.h +++ b/services/soundtrigger/SoundTriggerHwService.h @@ -141,6 +141,9 @@ public: private: + status_t unloadSoundModel_l(sound_model_handle_t handle); + + Mutex mLock; wp<SoundTriggerHwService> mService; struct sound_trigger_hw_device* mHwDevice; diff --git a/soundtrigger/ISoundTrigger.cpp b/soundtrigger/ISoundTrigger.cpp index 42280d1..eecc1ea 100644 --- a/soundtrigger/ISoundTrigger.cpp +++ b/soundtrigger/ISoundTrigger.cpp @@ -58,7 +58,7 @@ public: } Parcel data, reply; data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); - data.writeStrongBinder(modelMemory->asBinder()); + data.writeStrongBinder(IInterface::asBinder(modelMemory)); status_t status = remote()->transact(LOAD_SOUND_MODEL, data, &reply); if (status != NO_ERROR || (status = (status_t)reply.readInt32()) != NO_ERROR) { @@ -91,7 +91,7 @@ public: } else { data.writeInt32(dataMemory->size()); } - data.writeStrongBinder(dataMemory->asBinder()); + data.writeStrongBinder(IInterface::asBinder(dataMemory)); status_t status = remote()->transact(START_RECOGNITION, data, &reply); if (status != NO_ERROR) { status = (status_t)reply.readInt32(); diff --git a/soundtrigger/ISoundTriggerClient.cpp b/soundtrigger/ISoundTriggerClient.cpp index b0b4428..e0d3add 100644 --- a/soundtrigger/ISoundTriggerClient.cpp +++ b/soundtrigger/ISoundTriggerClient.cpp @@ -44,7 +44,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(ISoundTriggerClient::getInterfaceDescriptor()); - data.writeStrongBinder(eventMemory->asBinder()); + data.writeStrongBinder(IInterface::asBinder(eventMemory)); remote()->transact(ON_RECOGNITION_EVENT, data, &reply); @@ -54,7 +54,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(ISoundTriggerClient::getInterfaceDescriptor()); - data.writeStrongBinder(eventMemory->asBinder()); + data.writeStrongBinder(IInterface::asBinder(eventMemory)); remote()->transact(ON_SOUNDMODEL_EVENT, data, &reply); @@ -63,7 +63,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(ISoundTriggerClient::getInterfaceDescriptor()); - data.writeStrongBinder(eventMemory->asBinder()); + data.writeStrongBinder(IInterface::asBinder(eventMemory)); remote()->transact(ON_SERVICE_STATE_CHANGE, data, &reply); diff --git a/soundtrigger/ISoundTriggerHwService.cpp b/soundtrigger/ISoundTriggerHwService.cpp index 05728e9..75f68b8 100644 --- a/soundtrigger/ISoundTriggerHwService.cpp +++ b/soundtrigger/ISoundTriggerHwService.cpp @@ -82,7 +82,7 @@ public: Parcel data, reply; data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor()); data.write(&handle, sizeof(sound_trigger_module_handle_t)); - data.writeStrongBinder(client->asBinder()); + data.writeStrongBinder(IInterface::asBinder(client)); remote()->transact(ATTACH, data, &reply); status_t status = reply.readInt32(); if (reply.readInt32() != 0) { @@ -147,7 +147,7 @@ status_t BnSoundTriggerHwService::onTransact( reply->writeInt32(status); if (module != 0) { reply->writeInt32(1); - reply->writeStrongBinder(module->asBinder()); + reply->writeStrongBinder(IInterface::asBinder(module)); } else { reply->writeInt32(0); } diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp index 0015c30..2138cb7 100644 --- a/soundtrigger/SoundTrigger.cpp +++ b/soundtrigger/SoundTrigger.cpp @@ -104,7 +104,7 @@ sp<SoundTrigger> SoundTrigger::attach(const sound_trigger_module_handle_t module status_t status = service->attach(module, soundTrigger, soundTrigger->mISoundTrigger); if (status == NO_ERROR && soundTrigger->mISoundTrigger != 0) { - soundTrigger->mISoundTrigger->asBinder()->linkToDeath(soundTrigger); + IInterface::asBinder(soundTrigger->mISoundTrigger)->linkToDeath(soundTrigger); } else { ALOGW("Error %d connecting to sound trigger service", status); soundTrigger.clear(); @@ -144,7 +144,7 @@ void SoundTrigger::detach() { mCallback.clear(); if (mISoundTrigger != 0) { mISoundTrigger->detach(); - mISoundTrigger->asBinder()->unlinkToDeath(this); + IInterface::asBinder(mISoundTrigger)->unlinkToDeath(this); mISoundTrigger = 0; } } diff --git a/tools/resampler_tools/Android.mk b/tools/resampler_tools/Android.mk index e8cbe39..b58e4cd 100644 --- a/tools/resampler_tools/Android.mk +++ b/tools/resampler_tools/Android.mk @@ -1,6 +1,6 @@ # Copyright 2005 The Android Open Source Project # -# Android.mk for resampler_tools +# Android.mk for resampler_tools # diff --git a/tools/resampler_tools/fir.cpp b/tools/resampler_tools/fir.cpp index 62eddca..fe4d212 100644 --- a/tools/resampler_tools/fir.cpp +++ b/tools/resampler_tools/fir.cpp @@ -66,19 +66,20 @@ static double kaiser(int k, int N, double beta) { static void usage(char* name) { fprintf(stderr, - "usage: %s [-h] [-d] [-s sample_rate] [-c cut-off_frequency] [-n half_zero_crossings]" + "usage: %s [-h] [-d] [-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]" + " %s [-h] [-d] [-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" + " -D generate extra declarations\n" " -p generate poly-phase filter coefficients, with sample increment M/N\n" " -s sample rate (48000)\n" " -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" + " -f output format, can be fixed, fixed16, or float (fixed)\n" " -b kaiser window parameter beta (7.865 [-80dB])\n" " -v attenuation in dBFS (0)\n", name, name @@ -97,7 +98,8 @@ int main(int argc, char** argv) double Fs = 48000; double Fc = 20478; double atten = 1; - int format = 0; + int format = 0; // 0=fixed, 1=float + bool declarations = false; // in order to keep the errors associated with the linear // interpolation of the coefficients below the quantization error @@ -158,11 +160,14 @@ int main(int argc, char** argv) int M = 1 << 4; // number of phases for interpolation int ch; - while ((ch = getopt(argc, argv, ":hds:c:n:f:l:m:b:p:v:z:")) != -1) { + while ((ch = getopt(argc, argv, ":hds:c:n:f:l:m:b:p:v:z:D")) != -1) { switch (ch) { case 'd': debug = true; break; + case 'D': + declarations = true; + break; case 'p': if (sscanf(optarg, "%u/%u", &polyM, &polyN) != 2) { usage(argv[0]); @@ -225,24 +230,26 @@ int main(int argc, char** argv) for (int i = M-1 ; i; i>>=1, nz++); // generate the right half of the filter if (!debug) { - printf("// cmd-line: "); - for (int i=1 ; i<argc ; i++) { - printf("%s ", argv[i]); + printf("// cmd-line:"); + for (int i=0 ; i<argc ; i++) { + printf(" %s", argv[i]); } printf("\n"); - if (!polyphase) { - printf("const int32_t RESAMPLE_FIR_SIZE = %d;\n", N); - 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); - printf("const int32_t RESAMPLE_FIR_NUM_COEF = %d;\n", 2*nzc); - } - if (!format) { - printf("const int32_t RESAMPLE_FIR_COEF_BITS = %d;\n", nc); + if (declarations) { + if (!polyphase) { + printf("const int32_t RESAMPLE_FIR_SIZE = %d;\n", N); + 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); + printf("const int32_t RESAMPLE_FIR_NUM_COEF = %d;\n", 2*nzc); + } + if (!format) { + printf("const int32_t RESAMPLE_FIR_COEF_BITS = %d;\n", nc); + } + printf("\n"); + printf("static %s resampleFIR[] = {", !format ? "int32_t" : "float"); } - printf("\n"); - printf("static %s resampleFIR[] = {", !format ? "int32_t" : "float"); } if (!polyphase) { @@ -260,12 +267,15 @@ int main(int argc, char** argv) if (!format) { int64_t yi = toint(y, 1ULL<<(nc-1)); if (nc > 16) { - printf("0x%08x, ", int32_t(yi)); + printf("0x%08x,", int32_t(yi)); } else { - printf("0x%04x, ", int32_t(yi)&0xffff); + printf("0x%04x,", int32_t(yi)&0xffff); } } else { - printf("%.9g%s ", y, debug ? "," : "f,"); + printf("%.9g%s", y, debug ? "," : "f,"); + } + if (j != nzc-1) { + printf(" "); } } } @@ -283,23 +293,22 @@ int main(int argc, char** argv) if (!format) { int64_t yi = toint(y, 1ULL<<(nc-1)); if (nc > 16) { - printf("0x%08x, ", int32_t(yi)); + printf("0x%08x,", int32_t(yi)); } else { - printf("0x%04x, ", int32_t(yi)&0xffff); + printf("0x%04x,", int32_t(yi)&0xffff); } } else { - printf("%.9g%s", y, debug ? "" : "f"); + printf("%.9g%s", y, debug ? "," : "f,"); } - if (debug && (i==nzc-1)) { - } else { - printf(", "); + if (i != nzc-1) { + printf(" "); } } } } - if (!debug) { + if (!debug && declarations) { printf("\n};"); } printf("\n"); |