| /* ------------------------------------------------------------------ |
| * Copyright (C) 2008 PacketVideo |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT 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 "VideoMIO" |
| #include <utils/Log.h> |
| #include <surfaceflinger/ISurface.h> |
| |
| #include "android_surface_output.h" |
| #include <media/PVPlayer.h> |
| |
| #include "pvlogger.h" |
| #include "pv_mime_string_utils.h" |
| #include "oscl_snprintf.h" |
| |
| #include "oscl_dll.h" |
| |
| // Define entry point for this DLL |
| OSCL_DLL_ENTRY_POINT_DEFAULT() |
| |
| //The factory functions. |
| #include "oscl_mem.h" |
| |
| using namespace android; |
| |
| OSCL_EXPORT_REF AndroidSurfaceOutput::AndroidSurfaceOutput() : |
| OsclTimerObject(OsclActiveObject::EPriorityNominal, "androidsurfaceoutput") |
| { |
| initData(); |
| |
| iColorConverter = NULL; |
| mInitialized = false; |
| mPvPlayer = NULL; |
| mEmulation = false; |
| iEosReceived = false; |
| mNumberOfFramesToHold = 2; |
| } |
| |
| status_t AndroidSurfaceOutput::set(PVPlayer* pvPlayer, const sp<ISurface>& surface, bool emulation) |
| { |
| mPvPlayer = pvPlayer; |
| mEmulation = emulation; |
| setVideoSurface(surface); |
| return NO_ERROR; |
| } |
| |
| status_t AndroidSurfaceOutput::setVideoSurface(const sp<ISurface>& surface) |
| { |
| LOGV("setVideoSurface(%p)", surface.get()); |
| // unregister buffers for the old surface |
| if (mSurface != NULL) { |
| LOGV("unregisterBuffers from old surface"); |
| mSurface->unregisterBuffers(); |
| } |
| mSurface = surface; |
| // register buffers for the new surface |
| if ((mSurface != NULL) && (mBufferHeap.heap != NULL)) { |
| LOGV("registerBuffers from old surface"); |
| mSurface->registerBuffers(mBufferHeap); |
| } |
| return NO_ERROR; |
| } |
| |
| void AndroidSurfaceOutput::initData() |
| { |
| iVideoHeight = iVideoWidth = iVideoDisplayHeight = iVideoDisplayWidth = 0; |
| iVideoFormat=PVMF_MIME_FORMAT_UNKNOWN; |
| resetVideoParameterFlags(); |
| |
| iCommandCounter=0; |
| iLogger=NULL; |
| iCommandResponseQueue.reserve(5); |
| iWriteResponseQueue.reserve(5); |
| iObserver=NULL; |
| iLogger=NULL; |
| iPeer=NULL; |
| iState=STATE_IDLE; |
| iIsMIOConfigured = false; |
| } |
| |
| void AndroidSurfaceOutput::ResetData() |
| //reset all data from this session. |
| { |
| Cleanup(); |
| |
| //reset all the received media parameters. |
| iVideoFormatString=""; |
| iVideoFormat=PVMF_MIME_FORMAT_UNKNOWN; |
| resetVideoParameterFlags(); |
| iIsMIOConfigured = false; |
| } |
| |
| void AndroidSurfaceOutput::resetVideoParameterFlags() |
| { |
| iVideoParameterFlags = VIDEO_PARAMETERS_INVALID; |
| } |
| |
| bool AndroidSurfaceOutput::checkVideoParameterFlags() |
| { |
| return (iVideoParameterFlags & VIDEO_PARAMETERS_MASK) == VIDEO_PARAMETERS_VALID; |
| } |
| |
| /* |
| * process the write response queue by sending writeComplete to the peer |
| * (nominally the decoder node). |
| * |
| * numFramesToHold is the number of frames to be held in the MIO. During |
| * playback, we hold the last frame which is used by SurfaceFlinger |
| * to composite the final output. |
| */ |
| void AndroidSurfaceOutput::processWriteResponseQueue(int numFramesToHold) |
| { |
| LOGV("processWriteResponseQueue: queued = %d, numFramesToHold = %d", |
| iWriteResponseQueue.size(), numFramesToHold); |
| while (iWriteResponseQueue.size() > numFramesToHold) { |
| if (iPeer) { |
| iPeer->writeComplete(iWriteResponseQueue[0].iStatus, |
| iWriteResponseQueue[0].iCmdId, |
| (OsclAny*)iWriteResponseQueue[0].iContext); |
| } |
| iWriteResponseQueue.erase(&iWriteResponseQueue[0]); |
| } |
| } |
| |
| void AndroidSurfaceOutput::Cleanup() |
| //cleanup all allocated memory and release resources. |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::Cleanup() In")); |
| while (!iCommandResponseQueue.empty()) |
| { |
| if (iObserver) |
| iObserver->RequestCompleted(PVMFCmdResp(iCommandResponseQueue[0].iCmdId, iCommandResponseQueue[0].iContext, iCommandResponseQueue[0].iStatus)); |
| iCommandResponseQueue.erase(&iCommandResponseQueue[0]); |
| } |
| |
| processWriteResponseQueue(0); |
| |
| // We'll close frame buf and delete here for now. |
| closeFrameBuf(); |
| |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::Cleanup() Out")); |
| } |
| |
| OSCL_EXPORT_REF AndroidSurfaceOutput::~AndroidSurfaceOutput() |
| { |
| Cleanup(); |
| } |
| |
| |
| PVMFStatus AndroidSurfaceOutput::connect(PvmiMIOSession& aSession, PvmiMIOObserver* aObserver) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::connect() called")); |
| // Each Session could have its own set of Configuration parameters |
| //in an array of structures and the session ID could be an index to that array. |
| |
| //currently supports only one session |
| if (iObserver) |
| return PVMFFailure; |
| |
| iObserver=aObserver; |
| return PVMFSuccess; |
| } |
| |
| |
| PVMFStatus AndroidSurfaceOutput::disconnect(PvmiMIOSession aSession) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::disconnect() called")); |
| //currently supports only one session |
| iObserver=NULL; |
| return PVMFSuccess; |
| } |
| |
| |
| PvmiMediaTransfer* AndroidSurfaceOutput::createMediaTransfer(PvmiMIOSession& aSession, |
| PvmiKvp* read_formats, int32 read_flags, |
| PvmiKvp* write_formats, int32 write_flags) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::createMediaTransfer() called")); |
| return (PvmiMediaTransfer*)this; |
| } |
| |
| void AndroidSurfaceOutput::QueueCommandResponse(CommandResponse& aResp) |
| { |
| //queue a command response and schedule processing. |
| |
| iCommandResponseQueue.push_back(aResp); |
| |
| //cancel any timer delay so the command response will happen ASAP. |
| if (IsBusy()) |
| Cancel(); |
| |
| RunIfNotReady(); |
| } |
| |
| PVMFCommandId AndroidSurfaceOutput::QueryUUID(const PvmfMimeString& aMimeType, |
| Oscl_Vector<PVUuid, OsclMemAllocator>& aUuids, |
| bool aExactUuidsOnly, const OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::QueryUUID() called")); |
| |
| OSCL_UNUSED_ARG(aMimeType); |
| OSCL_UNUSED_ARG(aExactUuidsOnly); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| PVMFStatus status=PVMFFailure; |
| int32 err ; |
| OSCL_TRY(err, aUuids.push_back(PVMI_CAPABILITY_AND_CONFIG_PVUUID);); |
| if (err==OsclErrNone) |
| status= PVMFSuccess; |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| |
| PVMFCommandId AndroidSurfaceOutput::QueryInterface(const PVUuid& aUuid, PVInterface*& aInterfacePtr, const OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::QueryInterface() called")); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| 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; |
| } |
| else |
| { |
| status=PVMFFailure; |
| } |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| |
| void AndroidSurfaceOutput::deleteMediaTransfer(PvmiMIOSession& aSession, PvmiMediaTransfer* media_transfer) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::deleteMediaTransfer() called")); |
| // This class is implementing the media transfer, so no cleanup is needed |
| } |
| |
| |
| PVMFCommandId AndroidSurfaceOutput:: Init(const OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::Init() called")); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| PVMFStatus status=PVMFFailure; |
| |
| switch(iState) |
| { |
| case STATE_LOGGED_ON: |
| status=PVMFSuccess; |
| iState=STATE_INITIALIZED; |
| break; |
| |
| default: |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::Invalid State")); |
| status=PVMFErrInvalidState; |
| break; |
| } |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| PVMFCommandId AndroidSurfaceOutput::Reset(const OsclAny* aContext) |
| { |
| ResetData(); |
| PVMFCommandId cmdid=iCommandCounter++; |
| CommandResponse resp(PVMFSuccess,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| |
| PVMFCommandId AndroidSurfaceOutput::Start(const OsclAny* aContext) |
| { |
| iEosReceived = false; |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::Start() called")); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| PVMFStatus status=PVMFFailure; |
| |
| switch(iState) |
| { |
| case STATE_INITIALIZED: |
| case STATE_PAUSED: |
| |
| iState=STATE_STARTED; |
| processWriteResponseQueue(0); |
| status=PVMFSuccess; |
| break; |
| |
| default: |
| status=PVMFErrInvalidState; |
| break; |
| } |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| // post the last video frame to refresh screen after pause |
| void AndroidSurfaceOutput::postLastFrame() |
| { |
| // ignore if no surface or heap |
| if ((mSurface == NULL) || (mBufferHeap.heap == NULL)) return; |
| mSurface->postBuffer(mFrameBuffers[mFrameBufferIndex]); |
| } |
| |
| PVMFCommandId AndroidSurfaceOutput::Pause(const OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::Pause() called")); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| PVMFStatus status=PVMFFailure; |
| |
| switch(iState) |
| { |
| case STATE_STARTED: |
| |
| iState=STATE_PAUSED; |
| status=PVMFSuccess; |
| |
| // post last buffer to prevent stale data |
| // if not configured, PVMFMIOConfigurationComplete is not sent |
| // there should not be any media data. |
| if(iIsMIOConfigured) { |
| postLastFrame(); |
| } |
| break; |
| |
| default: |
| status=PVMFErrInvalidState; |
| break; |
| } |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| |
| PVMFCommandId AndroidSurfaceOutput::Flush(const OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::Flush() called")); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| PVMFStatus status=PVMFFailure; |
| |
| switch(iState) |
| { |
| case STATE_STARTED: |
| |
| iState=STATE_INITIALIZED; |
| status=PVMFSuccess; |
| break; |
| |
| default: |
| status=PVMFErrInvalidState; |
| break; |
| } |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| PVMFCommandId AndroidSurfaceOutput::DiscardData(const OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::DiscardData() called")); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| //this component doesn't buffer data, so there's nothing |
| //needed here. |
| |
| PVMFStatus status=PVMFSuccess; |
| processWriteResponseQueue(0); |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| PVMFCommandId AndroidSurfaceOutput::DiscardData(PVMFTimestamp aTimestamp, const OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::DiscardData() called")); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| aTimestamp = 0; |
| |
| //this component doesn't buffer data, so there's nothing |
| //needed here. |
| |
| PVMFStatus status=PVMFSuccess; |
| processWriteResponseQueue(0); |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| PVMFCommandId AndroidSurfaceOutput::Stop(const OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::Stop() called")); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| PVMFStatus status=PVMFFailure; |
| |
| switch(iState) |
| { |
| case STATE_STARTED: |
| case STATE_PAUSED: |
| |
| #ifdef PERFORMANCE_MEASUREMENTS_ENABLED |
| // FIXME: This should be moved to OMAP library |
| PVOmapVideoProfile.MarkEndTime(); |
| PVOmapVideoProfile.PrintStats(); |
| PVOmapVideoProfile.Reset(); |
| #endif |
| |
| iState=STATE_INITIALIZED; |
| status=PVMFSuccess; |
| break; |
| |
| default: |
| status=PVMFErrInvalidState; |
| break; |
| } |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| PVMFCommandId AndroidSurfaceOutput::CancelAllCommands(const OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::CancelAllCommands() called")); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| //commands are executed immediately upon being received, so |
| //it isn't really possible to cancel them. |
| |
| PVMFStatus status=PVMFSuccess; |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| PVMFCommandId AndroidSurfaceOutput::CancelCommand(PVMFCommandId aCmdId, const OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::CancelCommand() called")); |
| |
| PVMFCommandId cmdid=iCommandCounter++; |
| |
| //commands are executed immediately upon being received, so |
| //it isn't really possible to cancel them. |
| |
| //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; |
| } |
| } |
| |
| CommandResponse resp(status,cmdid,aContext); |
| QueueCommandResponse(resp); |
| return cmdid; |
| } |
| |
| void AndroidSurfaceOutput::ThreadLogon() |
| { |
| if(iState==STATE_IDLE) |
| { |
| iLogger = PVLogger::GetLoggerObject("PVOmapVideo"); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::ThreadLogon() called")); |
| AddToScheduler(); |
| iState=STATE_LOGGED_ON; |
| } |
| } |
| |
| |
| void AndroidSurfaceOutput::ThreadLogoff() |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::ThreadLogoff() called")); |
| |
| if(iState!=STATE_IDLE) |
| { |
| RemoveFromScheduler(); |
| iLogger=NULL; |
| iState=STATE_IDLE; |
| } |
| } |
| |
| |
| void AndroidSurfaceOutput::setPeer(PvmiMediaTransfer* aPeer) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::setPeer() called")); |
| // Set the observer |
| iPeer = aPeer; |
| } |
| |
| |
| void AndroidSurfaceOutput::useMemoryAllocators(OsclMemAllocator* write_alloc) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::useMemoryAllocators() called")); |
| //not supported. |
| } |
| |
| // This routine will determine whether data can be accepted in a writeAsync |
| // call and if not, will return true; |
| bool AndroidSurfaceOutput::CheckWriteBusy(uint32 aSeqNum) |
| { |
| // for all other cases, accept data now. |
| return false; |
| } |
| |
| PVMFCommandId AndroidSurfaceOutput::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 (!iIsMIOConfigured |
| && |
| !((PVMI_MEDIAXFER_FMT_TYPE_NOTIFICATION == aFormatType) |
| && (PVMI_MEDIAXFER_FMT_INDEX_END_OF_STREAM == aFormatIndex))) |
| { |
| LOGE("data is pumped in before MIO is 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; |
| |
| if (aSeqNum < 6) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::writeAsync() seqnum %d ts %d context %d",aSeqNum,aTimestamp,aContext)); |
| |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::writeAsync() Format Type %d Format Index %d length %d",aFormatType,aFormatIndex,aDataLen)); |
| } |
| |
| PVMFStatus status=PVMFFailure; |
| |
| switch(aFormatType) |
| { |
| case PVMI_MEDIAXFER_FMT_TYPE_COMMAND : |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::writeAsync() called with Command info.")); |
| //ignore |
| status= PVMFSuccess; |
| break; |
| |
| case PVMI_MEDIAXFER_FMT_TYPE_NOTIFICATION : |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::writeAsync() called with Notification info.")); |
| switch(aFormatIndex) |
| { |
| case PVMI_MEDIAXFER_FMT_INDEX_END_OF_STREAM: |
| iEosReceived = true; |
| break; |
| default: |
| break; |
| } |
| //ignore |
| status= PVMFSuccess; |
| break; |
| |
| case PVMI_MEDIAXFER_FMT_TYPE_DATA : |
| switch(aFormatIndex) |
| { |
| case PVMI_MEDIAXFER_FMT_INDEX_FMT_SPECIFIC_INFO: |
| //format-specific info contains codec headers. |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::writeAsync() called with format-specific info.")); |
| |
| if (iState<STATE_INITIALIZED) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, |
| (0,"AndroidSurfaceOutput::writeAsync: Error - Invalid state")); |
| status=PVMFErrInvalidState; |
| } |
| else |
| { |
| status= PVMFSuccess; |
| } |
| |
| break; |
| |
| case PVMI_MEDIAXFER_FMT_INDEX_DATA: |
| //data contains the media bitstream. |
| |
| //Verify the state |
| if (iState!=STATE_STARTED) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, |
| (0,"AndroidSurfaceOutput::writeAsync: Error - Invalid state")); |
| status=PVMFErrInvalidState; |
| } |
| else |
| { |
| |
| //printf("V WriteAsync { seq=%d, ts=%d }\n", data_header_info.seq_num, data_header_info.timestamp); |
| |
| // Call playback to send data to IVA for Color Convert |
| status = writeFrameBuf(aData, aDataLen, data_header_info); |
| |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, |
| (0,"AndroidSurfaceOutput::writeAsync: Playback Progress - frame %d",iFrameNumber++)); |
| } |
| break; |
| |
| default: |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, |
| (0,"AndroidSurfaceOutput::writeAsync: Error - unrecognized format index")); |
| status= PVMFFailure; |
| break; |
| } |
| break; |
| |
| default: |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, |
| (0,"AndroidSurfaceOutput::writeAsync: Error - unrecognized format type")); |
| status= PVMFFailure; |
| break; |
| } |
| |
| //Schedule asynchronous response |
| PVMFCommandId cmdid=iCommandCounter++; |
| WriteResponse resp(status,cmdid,aContext,aTimestamp); |
| iWriteResponseQueue.push_back(resp); |
| RunIfNotReady(); |
| |
| return cmdid; |
| } |
| |
| void AndroidSurfaceOutput::writeComplete(PVMFStatus aStatus, PVMFCommandId write_cmd_id, OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::writeComplete() called")); |
| //won't be called since this component is a sink. |
| } |
| |
| |
| PVMFCommandId AndroidSurfaceOutput::readAsync(uint8* data, uint32 max_data_len, OsclAny* aContext, |
| int32* formats, uint16 num_formats) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::readAsync() called")); |
| //read not supported. |
| OsclError::Leave(OsclErrNotSupported); |
| return -1; |
| } |
| |
| |
| void AndroidSurfaceOutput::readComplete(PVMFStatus aStatus, PVMFCommandId read_cmd_id, int32 format_index, |
| const PvmiMediaXferHeader& data_header_info, OsclAny* aContext) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::readComplete() called")); |
| //won't be called since this component is a sink. |
| } |
| |
| |
| void AndroidSurfaceOutput::statusUpdate(uint32 status_flags) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::statusUpdate() called")); |
| //won't be called since this component is a sink. |
| } |
| |
| |
| void AndroidSurfaceOutput::cancelCommand(PVMFCommandId command_id) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::cancelCommand() called")); |
| |
| //the purpose of this API is to cancel a writeAsync command and report |
| //completion ASAP. |
| |
| //in this implementation, the write commands are executed immediately |
| //when received so it isn't really possible to cancel. |
| //just report completion immediately. |
| processWriteResponseQueue(0); |
| } |
| |
| void AndroidSurfaceOutput::cancelAllCommands() |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,"AndroidSurfaceOutput::cancelAllCommands() called")); |
| |
| //the purpose of this API is to cancel all writeAsync commands and report |
| //completion ASAP. |
| |
| //in this implementaiton, the write commands are executed immediately |
| //when received so it isn't really possible to cancel. |
| //just report completion immediately. |
| |
| for (uint32 i=0;i<iWriteResponseQueue.size();i++) |
| { |
| //report completion |
| if (iPeer) |
| iPeer->writeComplete(iWriteResponseQueue[i].iStatus,iWriteResponseQueue[i].iCmdId,(OsclAny*)iWriteResponseQueue[i].iContext); |
| iWriteResponseQueue.erase(&iWriteResponseQueue[i]); |
| } |
| } |
| |
| void AndroidSurfaceOutput::setObserver (PvmiConfigAndCapabilityCmdObserver* aObserver) |
| { |
| OSCL_UNUSED_ARG(aObserver); |
| //not needed since this component only supports synchronous capability & config |
| //APIs. |
| } |
| |
| PVMFStatus AndroidSurfaceOutput::getParametersSync(PvmiMIOSession aSession, PvmiKeyType aIdentifier, |
| PvmiKvp*& aParameters, int& num_parameter_elements, |
| PvmiCapabilityContext aContext) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| OSCL_UNUSED_ARG(aContext); |
| aParameters=NULL; |
| |
| // This is a query for the list of supported formats. |
| if(pv_mime_strcmp(aIdentifier, INPUT_FORMATS_CAP_QUERY) == 0) |
| { |
| aParameters=(PvmiKvp*)oscl_malloc(sizeof(PvmiKvp)); |
| if (aParameters == NULL) return PVMFErrNoMemory; |
| aParameters[num_parameter_elements++].value.pChar_value=(char*) PVMF_MIME_YUV420; |
| |
| return PVMFSuccess; |
| } |
| |
| //unrecognized key. |
| return PVMFFailure; |
| } |
| |
| PVMFStatus AndroidSurfaceOutput::releaseParameters(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements) |
| { |
| //release parameters that were allocated by this component. |
| if (aParameters) |
| { |
| oscl_free(aParameters); |
| return PVMFSuccess; |
| } |
| return PVMFFailure; |
| } |
| |
| void AndroidSurfaceOutput ::createContext(PvmiMIOSession aSession, PvmiCapabilityContext& aContext) |
| { |
| OsclError::Leave(OsclErrNotSupported); |
| } |
| |
| void AndroidSurfaceOutput::setContextParameters(PvmiMIOSession aSession, PvmiCapabilityContext& aContext, |
| PvmiKvp* aParameters, int num_parameter_elements) |
| { |
| OsclError::Leave(OsclErrNotSupported); |
| } |
| |
| void AndroidSurfaceOutput::DeleteContext(PvmiMIOSession aSession, PvmiCapabilityContext& aContext) |
| { |
| OsclError::Leave(OsclErrNotSupported); |
| } |
| |
| |
| void AndroidSurfaceOutput::setParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, |
| int num_elements, PvmiKvp * & aRet_kvp) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| |
| aRet_kvp = NULL; |
| |
| LOGV("setParametersSync"); |
| for (int32 i=0;i<num_elements;i++) |
| { |
| //Check against known video parameter keys... |
| if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_FORMAT_KEY) == 0) |
| { |
| iVideoFormatString=aParameters[i].value.pChar_value; |
| iVideoFormat=iVideoFormatString.get_str(); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::setParametersSync() Video Format Key, Value %s",iVideoFormatString.get_str())); |
| } |
| else if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_WIDTH_KEY) == 0) |
| { |
| iVideoWidth=(int32)aParameters[i].value.uint32_value; |
| iVideoParameterFlags |= VIDEO_WIDTH_VALID; |
| LOGV("iVideoWidth=%d", iVideoWidth); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::setParametersSync() Video Width Key, Value %d",iVideoWidth)); |
| } |
| else if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_HEIGHT_KEY) == 0) |
| { |
| iVideoHeight=(int32)aParameters[i].value.uint32_value; |
| iVideoParameterFlags |= VIDEO_HEIGHT_VALID; |
| LOGV("iVideoHeight=%d", iVideoHeight); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::setParametersSync() Video Height Key, Value %d",iVideoHeight)); |
| } |
| else if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_DISPLAY_HEIGHT_KEY) == 0) |
| { |
| iVideoDisplayHeight=(int32)aParameters[i].value.uint32_value; |
| iVideoParameterFlags |= DISPLAY_HEIGHT_VALID; |
| LOGV("iVideoDisplayHeight=%d", iVideoDisplayHeight); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::setParametersSync() Video Display Height Key, Value %d",iVideoDisplayHeight)); |
| } |
| else if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_DISPLAY_WIDTH_KEY) == 0) |
| { |
| iVideoDisplayWidth=(int32)aParameters[i].value.uint32_value; |
| iVideoParameterFlags |= DISPLAY_WIDTH_VALID; |
| LOGV("iVideoDisplayWidth=%d", iVideoDisplayWidth); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::setParametersSync() Video Display Width Key, Value %d",iVideoDisplayWidth)); |
| } |
| else if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_SUBFORMAT_KEY) == 0) |
| { |
| iVideoSubFormat=aParameters[i].value.pChar_value; |
| iVideoParameterFlags |= VIDEO_SUBFORMAT_VALID; |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::setParametersSync() Video SubFormat Key, Value %s",iVideoSubFormat.getMIMEStrPtr())); |
| |
| LOGV("VIDEO SUBFORMAT SET TO %s\n",iVideoSubFormat.getMIMEStrPtr()); |
| } |
| else |
| { |
| //if we get here the key is unrecognized. |
| |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, |
| (0,"AndroidSurfaceOutput::setParametersSync() Error, unrecognized key = %s", aParameters[i].key)); |
| |
| //set the return value to indicate the unrecognized key |
| //and return. |
| aRet_kvp = &aParameters[i]; |
| return; |
| } |
| } |
| uint32 mycache = iVideoParameterFlags ; |
| // if initialization is complete, update the app display info |
| if( checkVideoParameterFlags() ) |
| initCheck(); |
| iVideoParameterFlags = mycache; |
| |
| // when all necessary parameters are received, send |
| // PVMFMIOConfigurationComplete event to observer |
| if(!iIsMIOConfigured && checkVideoParameterFlags() ) |
| { |
| iIsMIOConfigured = true; |
| if(iObserver) |
| { |
| iObserver->ReportInfoEvent(PVMFMIOConfigurationComplete); |
| } |
| } |
| } |
| |
| PVMFCommandId AndroidSurfaceOutput::setParametersAsync(PvmiMIOSession aSession, PvmiKvp* aParameters, |
| int num_elements, PvmiKvp*& aRet_kvp, OsclAny* context) |
| { |
| OsclError::Leave(OsclErrNotSupported); |
| return -1; |
| } |
| |
| uint32 AndroidSurfaceOutput::getCapabilityMetric (PvmiMIOSession aSession) |
| { |
| return 0; |
| } |
| |
| PVMFStatus AndroidSurfaceOutput::verifyParametersSync (PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| |
| // 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_YUV420) == 0) { |
| return PVMFSuccess; |
| } |
| else { |
| return PVMFErrNotSupported; |
| } |
| } |
| } |
| return PVMFSuccess; |
| } |
| |
| // |
| // Private section |
| // |
| |
| void AndroidSurfaceOutput::Run() |
| { |
| //send async command responses |
| 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 |
| if (iEosReceived) { |
| LOGV("Flushing buffers after EOS"); |
| processWriteResponseQueue(0); |
| } else { |
| processWriteResponseQueue(mNumberOfFramesToHold); |
| } |
| } |
| |
| // create a frame buffer for software codecs |
| OSCL_EXPORT_REF bool AndroidSurfaceOutput::initCheck() |
| { |
| |
| // initialize only when we have all the required parameters |
| if (!checkVideoParameterFlags()) |
| return mInitialized; |
| |
| // release resources if previously initialized |
| closeFrameBuf(); |
| |
| // reset flags in case display format changes in the middle of a stream |
| resetVideoParameterFlags(); |
| |
| // copy parameters in case we need to adjust them |
| int displayWidth = iVideoDisplayWidth; |
| int displayHeight = iVideoDisplayHeight; |
| int frameWidth = iVideoWidth; |
| int frameHeight = iVideoHeight; |
| int frameSize; |
| |
| // RGB-565 frames are 2 bytes/pixel |
| displayWidth = (displayWidth + 1) & -2; |
| displayHeight = (displayHeight + 1) & -2; |
| frameWidth = (frameWidth + 1) & -2; |
| frameHeight = (frameHeight + 1) & -2; |
| frameSize = frameWidth * frameHeight * 2; |
| |
| // create frame buffer heap and register with surfaceflinger |
| sp<MemoryHeapBase> heap = new MemoryHeapBase(frameSize * kBufferCount); |
| if (heap->heapID() < 0) { |
| LOGE("Error creating frame buffer heap"); |
| return false; |
| } |
| |
| mBufferHeap = ISurface::BufferHeap(displayWidth, displayHeight, |
| frameWidth, frameHeight, PIXEL_FORMAT_RGB_565, heap); |
| mSurface->registerBuffers(mBufferHeap); |
| |
| // create frame buffers |
| for (int i = 0; i < kBufferCount; i++) { |
| mFrameBuffers[i] = i * frameSize; |
| } |
| |
| // initialize software color converter |
| iColorConverter = ColorConvert16::NewL(); |
| iColorConverter->Init(displayWidth, displayHeight, frameWidth, displayWidth, displayHeight, displayWidth, CCROTATE_NONE); |
| iColorConverter->SetMemHeight(frameHeight); |
| iColorConverter->SetMode(1); |
| |
| LOGV("video = %d x %d", displayWidth, displayHeight); |
| LOGV("frame = %d x %d", frameWidth, frameHeight); |
| LOGV("frame #bytes = %d", frameSize); |
| |
| // register frame buffers with SurfaceFlinger |
| mFrameBufferIndex = 0; |
| mInitialized = true; |
| mPvPlayer->sendEvent(MEDIA_SET_VIDEO_SIZE, iVideoDisplayWidth, iVideoDisplayHeight); |
| |
| return mInitialized; |
| } |
| |
| OSCL_EXPORT_REF PVMFStatus AndroidSurfaceOutput::writeFrameBuf(uint8* aData, uint32 aDataLen, const PvmiMediaXferHeader& data_header_info) |
| { |
| // post to SurfaceFlinger |
| if ((mSurface != NULL) && (mBufferHeap.heap != NULL)) { |
| if (++mFrameBufferIndex == kBufferCount) mFrameBufferIndex = 0; |
| iColorConverter->Convert(aData, static_cast<uint8*>(mBufferHeap.heap->base()) + mFrameBuffers[mFrameBufferIndex]); |
| mSurface->postBuffer(mFrameBuffers[mFrameBufferIndex]); |
| } |
| return PVMFSuccess; |
| } |
| |
| OSCL_EXPORT_REF void AndroidSurfaceOutput::closeFrameBuf() |
| { |
| LOGV("closeFrameBuf"); |
| if (!mInitialized) return; |
| |
| mInitialized = false; |
| if (mSurface.get()) { |
| LOGV("unregisterBuffers"); |
| mSurface->unregisterBuffers(); |
| } |
| |
| // free frame buffers |
| LOGV("free frame buffers"); |
| for (int i = 0; i < kBufferCount; i++) { |
| mFrameBuffers[i] = 0; |
| } |
| |
| // free heaps |
| LOGV("free frame heap"); |
| mBufferHeap.heap.clear(); |
| |
| // free color converter |
| if (iColorConverter != 0) |
| { |
| LOGV("free color converter"); |
| delete iColorConverter; |
| iColorConverter = 0; |
| } |
| } |
| |
| OSCL_EXPORT_REF bool AndroidSurfaceOutput::GetVideoSize(int *w, int *h) { |
| |
| *w = iVideoDisplayWidth; |
| *h = iVideoDisplayHeight; |
| return iVideoDisplayWidth != 0 && iVideoDisplayHeight != 0; |
| } |