| /* |
| ** |
| ** Copyright 2008, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "AudioMIO" |
| #include <utils/Log.h> |
| |
| #include "android_audio_mio.h" |
| #include "pvlogger.h" |
| #include "pv_mime_string_utils.h" |
| #include "oscl_snprintf.h" |
| #include "oscl_mem.h" |
| #include "oscl_dll.h" |
| #include "oscl_mem.h" |
| |
| #include <sys/prctl.h> |
| |
| #include <utils/Timers.h> |
| #include <sys/resource.h> |
| #include <limits.h> |
| |
| #include <utils/threads.h> |
| |
| #include <media/AudioTrack.h> |
| |
| using namespace android; |
| |
| // depth of buffer/command queues in MIO |
| static const int kCommandQueueDepth = 10; |
| |
| // Define entry point for this DLL |
| OSCL_DLL_ENTRY_POINT_DEFAULT() |
| |
| // Audio Media IO component base class implementation |
| OSCL_EXPORT_REF AndroidAudioMIO::AndroidAudioMIO(const char* name) |
| : OsclTimerObject(OsclActiveObject::EPriorityNominal, name), |
| iWriteCompleteAO(NULL) |
| { |
| initData(); |
| } |
| |
| OSCL_EXPORT_REF AndroidAudioMIO::~AndroidAudioMIO() |
| { |
| LOGV("destructor"); |
| Cleanup(); |
| } |
| |
| void AndroidAudioMIO::initData() |
| { |
| LOGV("initData in"); |
| ResetData(); |
| |
| iCommandCounter = 0; |
| iLogger = NULL; |
| iCommandResponseQueue.reserve(kCommandQueueDepth); |
| iWriteResponseQueueLock.Create(); |
| iWriteResponseQueue.reserve(kCommandQueueDepth); |
| iObserver = NULL; |
| iLogger = NULL; |
| iPeer = NULL; |
| iState = STATE_MIO_IDLE; |
| iWriteBusy = false; |
| iFlushPending = false; |
| iDataQueued = 0; |
| LOGV("initData out"); |
| } |
| |
| void AndroidAudioMIO::ResetData() |
| //reset all data from this session. |
| { |
| LOGV("ResetData in"); |
| // reset all the received media parameters. |
| iAudioFormat = PVMF_MIME_FORMAT_UNKNOWN; |
| iAudioNumChannelsValid = false; |
| iAudioSamplingRateValid = false; |
| iAudioThreadCreatedAndMIOConfigured = false; |
| LOGV("ResetData out"); |
| } |
| |
| //cleanup all allocated memory and release resources. |
| void AndroidAudioMIO::Cleanup() |
| { |
| LOGV("Cleanup in"); |
| while (!iCommandResponseQueue.empty()) { |
| if (iObserver) { |
| iObserver->RequestCompleted(PVMFCmdResp(iCommandResponseQueue[0].iCmdId, |
| iCommandResponseQueue[0].iContext, iCommandResponseQueue[0].iStatus)); |
| } |
| iCommandResponseQueue.erase(&iCommandResponseQueue[0]); |
| } |
| |
| // return empty buffers |
| returnAllBuffers(); |
| |
| // delete the request active object |
| if (iWriteCompleteAO) { |
| OSCL_DELETE(iWriteCompleteAO); |
| iWriteCompleteAO = NULL; |
| } |
| iWriteResponseQueueLock.Close(); |
| LOGV("Cleanup out"); |
| } |
| |
| PVMFStatus AndroidAudioMIO::connect(PvmiMIOSession& aSession, PvmiMIOObserver* aObserver) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::connect() called")); |
| |
| // currently supports only one session |
| if (iObserver) return PVMFFailure; |
| iObserver = aObserver; |
| return PVMFSuccess; |
| } |
| |
| PVMFStatus AndroidAudioMIO::disconnect(PvmiMIOSession aSession) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::disconnect() called")); |
| iObserver = NULL; |
| return PVMFSuccess; |
| } |
| |
| |
| PvmiMediaTransfer* AndroidAudioMIO::createMediaTransfer(PvmiMIOSession& aSession, |
| PvmiKvp* read_formats, int32 read_flags, |
| PvmiKvp* write_formats, int32 write_flags) |
| { |
| // create the request active object |
| // such when audio output thread is done with a buffer |
| // it can put the buffer on the write response queue |
| // and schedule this MIO to run, to return the buffer |
| // to the engine |
| iWriteCompleteAO = OSCL_NEW(AndroidAudioOutputThreadSafeCallbackAO,(this, 5)); |
| |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::createMediaTransfer() called")); |
| return (PvmiMediaTransfer*)this; |
| } |
| |
| PVMFCommandId AndroidAudioMIO::QueueCmdResponse(PVMFStatus status, const OsclAny* aContext) |
| { |
| PVMFCommandId cmdId = iCommandCounter++; |
| CommandResponse resp(status, cmdId, aContext); |
| iCommandResponseQueue.push_back(resp); |
| |
| // cancel any timer delay so the command response will happen ASAP. |
| if (IsBusy()) Cancel(); |
| RunIfNotReady(); |
| return cmdId; |
| } |
| |
| // return any held buffers to the engine |
| void AndroidAudioMIO::ProcessWriteResponseQueue() |
| { |
| //LOGV("ProcessWriteResponseQueue in [Response Q size %d]",iWriteResponseQueue.size()); |
| PVMFStatus status = 0; |
| PVMFCommandId cmdId = 0; |
| const OsclAny* context = 0; |
| |
| iWriteResponseQueueLock.Lock(); |
| while (!iWriteResponseQueue.empty()) { |
| status = iWriteResponseQueue[0].iStatus; |
| cmdId = iWriteResponseQueue[0].iCmdId; |
| context = (OsclAny*)iWriteResponseQueue[0].iContext; |
| iWriteResponseQueue.erase(&iWriteResponseQueue[0]); |
| iWriteResponseQueueLock.Unlock(); |
| if (iPeer) { |
| LOGV("Return buffer(%d) status(%d) context(%p)", cmdId,status,context); |
| iPeer->writeComplete(status, cmdId, (OsclAny*)context); |
| } |
| iWriteResponseQueueLock.Lock(); |
| } |
| iWriteResponseQueueLock.Unlock(); |
| //LOGV("ProcessWriteResponseQueue out"); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::QueryUUID(const PvmfMimeString& aMimeType, |
| Oscl_Vector<PVUuid, OsclMemAllocator>& aUuids, |
| bool aExactUuidsOnly, const OsclAny* aContext) |
| { |
| LOGV("QueryUUID"); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::QueryUUID() called")); |
| return QueueCmdResponse(PVMFFailure, aContext); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::QueryInterface(const PVUuid& aUuid, PVInterface*& aInterfacePtr, const OsclAny* aContext) |
| { |
| LOGV("QueryInterface"); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::QueryInterface() called")); |
| PVMFStatus status=PVMFFailure; |
| if (aUuid == PVMI_CAPABILITY_AND_CONFIG_PVUUID) { |
| PvmiCapabilityAndConfig* myInterface = OSCL_STATIC_CAST(PvmiCapabilityAndConfig*, this); |
| aInterfacePtr = OSCL_STATIC_CAST(PVInterface*, myInterface); |
| status = PVMFSuccess; |
| } |
| return QueueCmdResponse(status, aContext); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::Init(const OsclAny* aContext) |
| { |
| LOGV("Init (%p)", aContext); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::Init() called")); |
| iState=STATE_MIO_INITIALIZED; |
| return QueueCmdResponse(PVMFSuccess, aContext); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::Reset(const OsclAny* aContext) |
| { |
| LOGV("Reset (%p)", aContext); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::Reset() called")); |
| return QueueCmdResponse(PVMFSuccess, aContext); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::Start(const OsclAny* aContext) |
| { |
| // Start is NO-OP |
| LOGV("Start (%p)", aContext); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::Start() called")); |
| iState = STATE_MIO_STARTED; |
| return QueueCmdResponse(PVMFSuccess, aContext); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::Pause(const OsclAny* aContext) |
| { |
| LOGV("Pause (%p)", aContext); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::Pause() called")); |
| iState = STATE_MIO_PAUSED; |
| return QueueCmdResponse(PVMFSuccess, aContext); |
| } |
| |
| |
| PVMFCommandId AndroidAudioMIO::Flush(const OsclAny* aContext) |
| { |
| LOGV("Flush (%p)", aContext); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::Flush() called")); |
| iState = STATE_MIO_INITIALIZED; |
| return QueueCmdResponse(PVMFSuccess, aContext); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::DiscardData(const OsclAny* aContext) |
| { |
| LOGV("DiscardData (%p)", aContext); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::DiscardData() called")); |
| return DiscardData(UINT_MAX, aContext); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::DiscardData(PVMFTimestamp aTimestamp, const OsclAny* aContext) |
| { |
| LOGV("DiscardData (%u, %p)", aTimestamp, aContext); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::DiscardData() called")); |
| return QueueCmdResponse(PVMFSuccess, aContext); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::Stop(const OsclAny* aContext) |
| { |
| LOGV("Stop (%p)", aContext); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::Stop() called")); |
| iState = STATE_MIO_INITIALIZED; |
| return QueueCmdResponse(PVMFSuccess, aContext); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::CancelAllCommands(const OsclAny* aContext) |
| { |
| LOGV("CancelAllCommands (%p)", aContext); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::CancelAllCommands() called")); |
| |
| //commands are executed immediately upon being received, so |
| //it isn't really possible to cancel them. |
| return QueueCmdResponse(PVMFSuccess, aContext); |
| } |
| |
| PVMFCommandId AndroidAudioMIO::CancelCommand(PVMFCommandId aCmdId, const OsclAny* aContext) |
| { |
| LOGV("CancelCommand (%u, %p)", aCmdId, aContext); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::CancelCommand() called")); |
| |
| //see if the response is still queued. |
| PVMFStatus status = PVMFFailure; |
| for (uint32 i = 0; i < iCommandResponseQueue.size(); i++) { |
| if (iCommandResponseQueue[i].iCmdId == aCmdId) { |
| status = PVMFSuccess; |
| break; |
| } |
| } |
| return QueueCmdResponse(PVMFSuccess, aContext); |
| } |
| |
| void AndroidAudioMIO::ThreadLogon() |
| { |
| LOGV("ThreadLogon() called "); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::ThreadLogon() called")); |
| if (iState == STATE_MIO_IDLE) { |
| iLogger = PVLogger::GetLoggerObject("AndroidAudioMIO\n"); |
| AddToScheduler(); |
| iState=STATE_MIO_LOGGED_ON; |
| } |
| } |
| |
| void AndroidAudioMIO::ThreadLogoff() |
| { |
| LOGV("ThreadLogoff() called"); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::ThreadLogoff() called")); |
| if (iState!=STATE_MIO_IDLE) { |
| RemoveFromScheduler(); |
| iLogger = NULL; |
| iState = STATE_MIO_IDLE; |
| ResetData(); |
| } |
| } |
| |
| void AndroidAudioMIO::setPeer(PvmiMediaTransfer* aPeer) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidAudioMIO::setPeer() called")); |
| // Set the observer |
| iPeer = aPeer; |
| } |
| |
| //This routine will determine whether data can be accepted in a writeAsync |
| //call and if not, will return true; |
| bool AndroidAudioMIO::CheckWriteBusy(uint32 aSeqNum) |
| { |
| // FIXME: this line screws up video output - why? |
| // return (iOSSRequestQueue.size() >= 5); |
| return false; |
| } |
| |
| PVMFCommandId AndroidAudioMIO::writeAsync(uint8 aFormatType, int32 aFormatIndex, uint8* aData, uint32 aDataLen, |
| const PvmiMediaXferHeader& data_header_info, OsclAny* aContext) |
| { |
| // Do a leave if MIO is not configured except when it is an EOS |
| if (!iAudioThreadCreatedAndMIOConfigured |
| && |
| !((PVMI_MEDIAXFER_FMT_TYPE_NOTIFICATION == aFormatType) |
| && (PVMI_MEDIAXFER_FMT_INDEX_END_OF_STREAM == aFormatIndex))) |
| { |
| LOGE("ERROR :: data is pumped in before mio configured"); |
| OSCL_LEAVE(OsclErrInvalidState); |
| return -1; |
| } |
| |
| uint32 aSeqNum = data_header_info.seq_num; |
| PVMFTimestamp aTimestamp = data_header_info.timestamp; |
| uint32 flags = data_header_info.flags; |
| |
| bool bWriteComplete = true; |
| |
| //LOGV("writeAsync() called seqnum %d ts %d flags %d context %d formattype %d formatindex %d",aSeqNum, aTimestamp, flags,aContext,aFormatType,aFormatIndex); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidAudioMIO::writeAsync() seqnum %d ts %d flags %d context %d", |
| aSeqNum, aTimestamp, flags,aContext)); |
| |
| PVMFStatus status=PVMFFailure; |
| |
| switch(aFormatType) { |
| case PVMI_MEDIAXFER_FMT_TYPE_COMMAND : |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidAudioMIO::writeAsync() called with Command info.")); |
| //ignore |
| LOGV("PVMI_MEDIAXFER_FMT_TYPE_COMMAND"); |
| status = PVMFSuccess; |
| break; |
| |
| case PVMI_MEDIAXFER_FMT_TYPE_NOTIFICATION : |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidAudioMIO::writeAsync() called with Notification info.")); |
| switch(aFormatIndex) { |
| case PVMI_MEDIAXFER_FMT_INDEX_END_OF_STREAM: |
| LOGV("PVMI_MEDIAXFER_FMT_INDEX_END_OF_STREAM"); |
| bWriteComplete = false; //force an empty buffer through the audio thread |
| break; |
| default: |
| break; |
| } |
| //ignore |
| status = PVMFSuccess; |
| break; |
| |
| case PVMI_MEDIAXFER_FMT_TYPE_DATA : |
| switch(aFormatIndex) { |
| case PVMI_MEDIAXFER_FMT_INDEX_FMT_SPECIFIC_INFO: |
| status = PVMFSuccess; |
| LOGV("PVMI_MEDIAXFER_FMT_INDEX_FMT_SPECIFIC_INFO"); |
| break; |
| |
| case PVMI_MEDIAXFER_FMT_INDEX_DATA: |
| LOGV("PVMI_MEDIAXFER_FMT_INDEX_DATA"); |
| //data contains the media bitstream. |
| |
| //Check whether we can accept data now and leave if we can't. |
| if (CheckWriteBusy(aSeqNum)) { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, |
| (0,"AndroidAudioMIO::writeAsync: Entering busy state")); |
| |
| //schedule an event to re-start the data flow. |
| iWriteBusy = true; |
| bWriteComplete = false; |
| |
| // Rich:: commenting in this line. |
| // Need some timeout here just in case. |
| // I have no evidence of any problems. |
| RunIfNotReady(10 * 1000); |
| OSCL_LEAVE(OsclErrBusy); |
| } else { |
| if (aDataLen > 0) { |
| // this buffer will be queued by the audio output thread to process |
| // this buffer cannot be write completed until |
| // it has been processed by the audio output thread |
| bWriteComplete = false; |
| } else { |
| LOGE("writeAsync() called with aDataLen==0"); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_INFO, |
| (0,"AndroidAudioMIO::writeAsync() called aDataLen==0.")); |
| } |
| status = PVMFSuccess; |
| } |
| break; |
| |
| default: |
| LOGE("Error unrecognized format index =%u", aFormatIndex); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, |
| (0,"AndroidAudioMIO::writeAsync: Error - unrecognized format index")); |
| status = PVMFFailure; |
| break; |
| } |
| break; |
| |
| default: |
| LOGE("Error unrecognized format type =%u", aFormatType); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, |
| (0,"AndroidAudioMIO::writeAsync: Error - unrecognized format type")); |
| status = PVMFFailure; |
| break; |
| } |
| |
| //Schedule asynchronous response |
| PVMFCommandId cmdid=iCommandCounter++; |
| if (bWriteComplete) { |
| LOGV("write complete (%d)", cmdid); |
| WriteResponse resp(status, cmdid, aContext, aTimestamp); |
| iWriteResponseQueueLock.Lock(); |
| iWriteResponseQueue.push_back(resp); |
| iWriteResponseQueueLock.Unlock(); |
| RunIfNotReady(); |
| } else if (!iWriteBusy) { |
| writeAudioBuffer(aData, aDataLen, cmdid, aContext, aTimestamp); |
| } |
| LOGV("data queued = %u", iDataQueued); |
| |
| return cmdid; |
| } |
| |
| PVMFStatus AndroidAudioMIO::getParametersSync(PvmiMIOSession aSession, PvmiKeyType aIdentifier, |
| PvmiKvp*& aParameters, int& num_parameter_elements, |
| PvmiCapabilityContext aContext) |
| { |
| LOGV("getParametersSync in %s",aIdentifier); |
| OSCL_UNUSED_ARG(aSession); |
| OSCL_UNUSED_ARG(aContext); |
| aParameters=NULL; |
| num_parameter_elements=0; |
| PVMFStatus status = PVMFFailure; |
| |
| if(pv_mime_strcmp(aIdentifier, INPUT_FORMATS_CAP_QUERY) == 0) { |
| //This is a query for the list of supported formats. |
| aParameters=(PvmiKvp*)oscl_malloc(2 * sizeof(PvmiKvp)); |
| if (aParameters) { |
| aParameters[num_parameter_elements++].value.pChar_value = (char*)PVMF_MIME_PCM16; |
| aParameters[num_parameter_elements++].value.pChar_value = (char*)PVMF_MIME_PCM8; |
| status = PVMFSuccess; |
| } |
| else{ |
| status = PVMFErrNoMemory; |
| } |
| } |
| //other queries are not currently supported. |
| |
| //unrecognized key. |
| LOGV("getParametersSync out status %d",status); |
| return status; |
| } |
| |
| PVMFStatus AndroidAudioMIO::releaseParameters(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements) |
| { |
| LOGV("releaseParameters in"); |
| OSCL_UNUSED_ARG(aSession); |
| OSCL_UNUSED_ARG(num_elements); |
| PVMFStatus status = PVMFFailure; |
| //release parameters that were allocated by this component. |
| if (aParameters) { |
| oscl_free(aParameters); |
| status = PVMFSuccess; |
| } |
| LOGV("releaseParameters out status %d",status); |
| return status; |
| } |
| |
| PVMFStatus AndroidAudioMIO::verifyParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| //LOGV("verifyParametersSync in"); |
| |
| // Go through each parameter |
| for (int32 i=0; i<num_elements; i++) { |
| char* compstr = NULL; |
| pv_mime_string_extract_type(0, aParameters[i].key, compstr); |
| if (pv_mime_strcmp(compstr, _STRLIT_CHAR("x-pvmf/media/format-type")) == 0) { |
| if ((pv_mime_strcmp(aParameters[i].value.pChar_value, PVMF_MIME_PCM8) == 0) || |
| (pv_mime_strcmp(aParameters[i].value.pChar_value, PVMF_MIME_PCM16) == 0) |
| ) { |
| return PVMFSuccess; |
| } |
| else { |
| return PVMFErrNotSupported; |
| } |
| } |
| } |
| return PVMFSuccess; |
| //LOGV("verifyParametersSync out"); |
| |
| } |
| void AndroidAudioMIO::setParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, |
| int num_elements, PvmiKvp * & aRet_kvp) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| //LOGV("setParametersSync in"); |
| aRet_kvp = NULL; |
| |
| for (int32 i=0;i<num_elements;i++) { |
| //Check against known audio parameter keys... |
| if (pv_mime_strcmp(aParameters[i].key, MOUT_AUDIO_FORMAT_KEY) == 0) { |
| LOGV("Audio format: %s", aParameters[i].value.pChar_value); |
| iAudioFormat=aParameters[i].value.pChar_value; |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidAudioOutput::setParametersSync() Audio Format Key, Value %s",aParameters[i].value.pChar_value)); |
| } else if (pv_mime_strcmp(aParameters[i].key, MOUT_AUDIO_SAMPLING_RATE_KEY) == 0) { |
| iAudioSamplingRate=(int32)aParameters[i].value.uint32_value; |
| iAudioSamplingRateValid=true; |
| // LOGD("iAudioSamplingRate=%d", iAudioSamplingRate); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidAudioMIO::setParametersSync() Audio Sampling Rate Key, Value %d",iAudioSamplingRate)); |
| } else if (pv_mime_strcmp(aParameters[i].key, MOUT_AUDIO_NUM_CHANNELS_KEY) == 0) { |
| iAudioNumChannels=(int32)aParameters[i].value.uint32_value; |
| iAudioNumChannelsValid=true; |
| // LOGD("iAudioNumChannels=%d", iAudioNumChannels); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidAudioMIO::setParametersSync() Audio Num Channels Key, Value %d",iAudioNumChannels)); |
| } else { |
| //if we get here the key is unrecognized. |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidAudioMIO::setParametersSync() Error, unrecognized key %s",aParameters[i].key)); |
| |
| //set the return value to indicate the unrecognized key |
| //and return. |
| aRet_kvp = &aParameters[i]; |
| return; |
| } |
| } |
| |
| //LOGV("setParametersSync out"); |
| } |
| |
| // |
| // Private section |
| // |
| |
| void AndroidAudioMIO::Run() |
| { |
| while (!iCommandResponseQueue.empty()) { |
| if (iObserver) { |
| iObserver->RequestCompleted(PVMFCmdResp(iCommandResponseQueue[0].iCmdId, |
| iCommandResponseQueue[0].iContext, |
| iCommandResponseQueue[0].iStatus)); |
| } |
| iCommandResponseQueue.erase(&iCommandResponseQueue[0]); |
| } |
| |
| //send async write completion |
| ProcessWriteResponseQueue(); |
| |
| //Re-start the data transfer if needed. |
| if (iWriteBusy) { |
| iWriteBusy = false; |
| iPeer->statusUpdate(PVMI_MEDIAXFER_STATUS_WRITE); |
| } |
| } |
| |
| // send response to MIO |
| void AndroidAudioMIO::sendResponse(PVMFCommandId cmdid, const OsclAny* context, PVMFTimestamp timestamp) |
| { |
| LOGV("sendResponse :: return buffer (%d) timestamp(%d) context(%p)", cmdid, timestamp, context); |
| WriteResponse resp(PVMFSuccess, cmdid, context, timestamp); |
| iWriteResponseQueueLock.Lock(); |
| if (iWriteResponseQueue.size() < iWriteResponseQueue.capacity()) { |
| iWriteResponseQueue.push_back(resp); |
| } else { |
| LOGE("Exceeded response queue capacity"); |
| } |
| iWriteResponseQueueLock.Unlock(); |
| |
| // Create an event for the threadsafe callback AO |
| OsclAny* P = NULL; |
| iWriteCompleteAO->ReceiveEvent(P); |
| } |
| |
| void AndroidAudioMIO::setAudioSink(const sp<MediaPlayerInterface::AudioSink>& audioSink) |
| { |
| LOGV("setAudioSink in"); |
| mAudioSink = audioSink; |
| } |
| |
| //------------------------------------------------------------------------ |
| // Active timing support |
| // |
| OSCL_EXPORT_REF PVMFStatus AndroidAudioMIOActiveTimingSupport::SetClock(PVMFMediaClock *clockVal) |
| { |
| LOGV("ATS :: SetClock in"); |
| iClock=clockVal; |
| |
| return PVMFSuccess; |
| } |
| |
| void AndroidAudioMIOActiveTimingSupport::NotificationsInterfaceDestroyed() |
| { |
| LOGV("ATS :: NotificationsInterfaceDestroyed in"); |
| iClockNotificationsInf=NULL; |
| } |
| |
| |
| OSCL_EXPORT_REF bool AndroidAudioMIOActiveTimingSupport::queryInterface(const PVUuid& aUuid, PVInterface*& aInterface) |
| { |
| aInterface = NULL; |
| PVUuid uuid; |
| queryUuid(uuid); |
| bool status = false; |
| if (uuid == aUuid) { |
| PvmiClockExtensionInterface* myInterface = OSCL_STATIC_CAST(PvmiClockExtensionInterface*, this); |
| aInterface = OSCL_STATIC_CAST(PVInterface*, myInterface); |
| status = true; |
| } |
| LOGV("ATS :: queryInterface out status %d",status); |
| return status; |
| } |
| |
| void AndroidAudioMIOActiveTimingSupport::queryUuid(PVUuid& uuid) |
| { |
| LOGV("ATS :: queryUuid in"); |
| uuid = PvmiClockExtensionInterfaceUuid; |
| } |
| |
| void AndroidAudioMIOActiveTimingSupport::ClockStateUpdated() |
| { |
| LOGV("ATS :: ClockStateUpdated in"); |
| if (iClock) { |
| |
| PVMFMediaClock::PVMFMediaClockState newClockState = iClock->GetState(); |
| if (newClockState != iClockState) { |
| iClockState = newClockState; |
| switch (iClockState) { |
| case PVMFMediaClock::STOPPED: |
| LOGV("A/V clock stopped"); |
| break; |
| case PVMFMediaClock::RUNNING: |
| LOGV("A/V clock running"); |
| // must be seeking, get new clock offset for A/V sync |
| if (iUpdateClock) { |
| bool overflowFlag = false; |
| uint32 currentTimeBase32 = 0; |
| iClock->GetCurrentTime32(iStartTime, overflowFlag, PVMF_MEDIA_CLOCK_MSEC, currentTimeBase32); |
| iFrameCount = 0; |
| iUpdateClock = false; |
| LOGV("update iStartTime: %d", iStartTime); |
| } |
| LOGV("signal thread to start"); |
| if (iAudioThreadSem) iAudioThreadSem->Signal(); |
| break; |
| case PVMFMediaClock::PAUSED: |
| LOGV("A/V clock paused"); |
| break; |
| default: |
| LOGE("Wrong clock state!"); |
| break; |
| } |
| } |
| } |
| LOGV("ATS :: ClockStateUpdated out"); |
| } |
| |
| void AndroidAudioMIOActiveTimingSupport::UpdateClock() |
| { |
| LOGV("ATS :: UpdateClock in"); |
| if (iClock && (iClockState == PVMFMediaClock::RUNNING)) { |
| uint32 clockTime32, timeBaseTime32, updateClock32; |
| int32 correction = 0; |
| bool overflowFlag = false; |
| // get current time |
| iClock->GetCurrentTime32(clockTime32, overflowFlag, PVMF_MEDIA_CLOCK_MSEC, timeBaseTime32); |
| LOGV("PV clock current = %u", clockTime32); |
| |
| // calculate sample clock |
| updateClock32 = iFrameCount * iMsecsPerFrame; |
| LOGV("sample clock = %u frameCount(%u) msecsPerFrame(%f)", updateClock32,iFrameCount,iMsecsPerFrame); |
| |
| correction = updateClock32 - (clockTime32 - iStartTime); |
| LOGV("ADJ_CLK iDriverLatency %d old clock %d delta %d", iDriverLatency, clockTime32, correction); |
| |
| // do clock correction if drift exceeds threshold |
| if (OSCL_ABS(correction) > iMinCorrection) { |
| if (correction > iMaxCorrection) { |
| correction = iMaxCorrection; |
| } else if (correction < -iMaxCorrection) { |
| correction = -iMaxCorrection; |
| } |
| updateClock32 = clockTime32 + correction; |
| LOGV("ADJ_CLK old clock %d correction %d new clock %d", clockTime32, correction, updateClock32); |
| |
| PVMFMediaClockAdjustTimeStatus adjustmentstatus = |
| iClock->AdjustClockTime32(clockTime32, timeBaseTime32, updateClock32,PVMF_MEDIA_CLOCK_MSEC,overflowFlag); |
| |
| if ((PVMF_MEDIA_CLOCK_ADJUST_SUCCESS != adjustmentstatus)) |
| { |
| LOGE("Error adjusting clock status = %d",adjustmentstatus); |
| } |
| if(overflowFlag) { |
| LOGE("Adjusting clock caused overflow"); |
| } |
| } |
| else{ |
| LOGV("ATS :: sample clock and pv clock are close enough no need to update"); |
| } |
| } |
| LOGV("ATS :: UpdateClock out"); |
| } |
| void AndroidAudioMIOActiveTimingSupport::setDriverLatency(uint32 latency) |
| { |
| LOGV("ATS :: setDriverLatency in"); |
| iDriverLatency = latency; |
| if (iClock){ |
| LOGV("register latency to media clock and set clockobserver"); |
| PVMFStatus ret = iClock->ConstructMediaClockNotificationsInterface(iClockNotificationsInf,*this,latency); |
| if(iClockNotificationsInf && (PVMFSuccess == ret)) |
| { |
| iClockNotificationsInf->SetClockStateObserver(*this); |
| } |
| else |
| { |
| LOGE("latency could NOT be set !! set it later "); |
| } |
| } |
| LOGV("ATS :: setDriverLatency out"); |
| } |
| |