/* * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. * All rights reserved. * * File : xplayer.c * Description : xplayer * History : * Author : AL3 * Date : 2015/05/05 * Comment : first version * */ #include "cdx_log.h" #include "xplayer.h" #include "demuxComponent.h" #include #include #include #include "player.h" #include "demuxComponent.h" #include "AwMessageQueue.h" #include "layerControl.h" #include "soundControl.h" #include "subtitleControl.h" #include "deinterlace.h" #include "CdxParser.h" #include #include "CdxTime.h" // pause then start to display, the cmcc apk call seekTo, it will flush the buffering cache(livemod) // or seek some frames in cache ( vod) , it will discontinue #define PAUSE_THEN_SEEK_BUG (1) //* player status. static const int XPLAYER_STATUS_IDLE = 0; static const int XPLAYER_STATUS_INITIALIZED = 1<<0; static const int XPLAYER_STATUS_PREPARING = 1<<1; static const int XPLAYER_STATUS_PREPARED = 1<<2; static const int XPLAYER_STATUS_STARTED = 1<<3; static const int XPLAYER_STATUS_PAUSED = 1<<4; static const int XPLAYER_STATUS_STOPPED = 1<<5; static const int XPLAYER_STATUS_COMPLETE = 1<<6; static const int XPLAYER_STATUS_ERROR = 1<<7; //* command id. static const int XPLAYER_COMMAND_SET_SOURCE = 0x101; static const int XPLAYER_COMMAND_SET_SURFACE = 0x102; static const int XPLAYER_COMMAND_SET_AUDIOSINK = 0x103; static const int XPLAYER_COMMAND_PREPARE = 0x104; static const int XPLAYER_COMMAND_START = 0x105; static const int XPLAYER_COMMAND_STOP = 0x106; static const int XPLAYER_COMMAND_PAUSE = 0x107; static const int XPLAYER_COMMAND_RESET = 0x108; static const int XPLAYER_COMMAND_QUIT = 0x109; static const int XPLAYER_COMMAND_SEEK = 0x10a; static const int XPLAYER_COMMAND_RESETURL = 0x10b; static const int XPLAYER_COMMAND_SETSPEED = 0x10c; static const int XPLAYER_COMMAND_SET_SUBCTRL = 0x10d; static const int XPLAYER_COMMAND_SET_DI = 0x10e; static const int XPLAYER_COMMAND_SET_PLAYRATE = 0x10f; typedef struct PlayerContext { AwMessageQueue* mMessageQueue; Player* mPlayer; DemuxComp* mDemux; pthread_t mThreadId; int mThreadCreated; uid_t mUID; //* no use. //* data source. char* mSourceUrl; //* file path or network stream url. CdxStreamT* mSourceStream; //* outside streaming source like miracast. int mSourceFd; //* file descriptor. int64_t mSourceFdOffset; int64_t mSourceFdLength; //for setting scale down //* bSetScaleDown = 0 means do not set scale down. //* bSetScaleDown = 1 means scaling when the resolution is larger than 2k. //* here the case of the VR9 call bSetScaleDown = 1. //* bSetScaleDown = 2 means scaling at any resolution. int bSetScaleDown; //* media information. MediaInfo* mMediaInfo; //* text codec format of the subtitle, used to transform subtitle text to //* utf8 when the subtitle text codec format is unknown. char mDefaultTextFormat[32]; //* whether enable subtitle show. int mIsSubtitleDisable; //* file descriptor of .idx file of index+sub subtitle. //* we save the .idx file's fd here because application set .idx file and .sub file //* seperately, we need to wait for the .sub file's fd, see //* INVOKE_ID_ADD_EXTERNAL_SOURCE_FD command in invoke() method. int mIndexFileHasBeenSet; int mIndexFileFdOfIndexSubtitle; //* for status and synchronize control. int mStatus; pthread_mutex_t mMutexMediaInfo; //* for media info protection. //* for mStatus protection in start/stop/pause operation and complete/seek finish callback. pthread_mutex_t mMutexStatus; sem_t mSemSetDataSource; sem_t mSemPrepare; sem_t mSemStart; sem_t mSemStop; sem_t mSemPause; sem_t mSemQuit; sem_t mSemReset; sem_t mSemSeek; sem_t mSemSetSurface; sem_t mSemSetAudioSink; sem_t mSemPrepareFinish;//* for signal prepare finish, used in prepare(). sem_t mSemSetSpeed; sem_t mSemSetSubCtrl; sem_t mSemSetDeinterlace; sem_t mSemSetPlayBackSettings; //* status control. int mSetDataSourceReply; int mPrepareReply; int mStartReply; int mStopReply; int mPauseReply; int mResetReply; int mSeekReply; int mSetSurfaceReply; int mSetAudioSinkReply; int mPrepareFinishResult; //* save the prepare result for prepare(). int mSetSpeedReply; int mSetSubCtrlReply; int mSetDeinterlaceReply; int mSetPlayBackSettingsReply; int mPrepareSync; //* synchroized prarare() call, don't call back to user. int mSeeking; int mSeekByInner; //* use to check whether seek callback is for current seek operation or previous. int mSeekTime; SeekModeType mSeekModeType; int mSeekSync; //* internal seek, don't call back to user. int mLoop; int mKeepLastFrame; int mVideoSizeWidth; //* use to record videoSize which had send to app int mVideoSizeHeight; enum AwApplicationType mApplicationType; void* mHTTPService; //* record the id of subtitle which is displaying //* we set the Nums to 64 .(32 may be not enough) unsigned int mSubtitleDisplayIds[64]; int mSubtitleDisplayIdsUpdateIndex; //* save the currentSelectTrackIndex; int mCurrentSelectTrackIndex; int mRawOccupyFlag; int mLivemode; int mPauseLivemode; int mbIsDiagnose; int64_t mPauseTimeStamp; //us int64_t mShiftTimeStamp; //us int mDisplayRatio; int64_t mCurShiftTimeStamp; //us int64_t mTimeShiftDuration; //ms int mSeekTobug; // the cmcc player should change pause state when buffer start, to fix getposition bug int mDemuxNotifyPause; int64_t mDemuxPauseTimeStamp; //* int mPreSeekTimeMs; int mTimeMsBeforeStop; //softdetector int mSpeed; int mbFast; int mFastTime; int mSpeedThreadRuning; pthread_t mSpeedThreadId; int mScaledownFlag; int mWidthScaledownRatio; int mHeightScaledownRatio; XPlayerNotifyCallback mCallback; void* pUser; AwBufferingSettings* pBuffering; float gain; }PlayerContext; static void* XPlayerThread(void* arg); static int ShiftTimeMode(int Shiftedms, char *buf); static void clearMediaInfo(XPlayer* p); static int callbackProcess(void *player, int messageId, void* param); struct AwMessage { AWMESSAGE_COMMON_MEMBERS uintptr_t params[8]; }; XPlayer* XPlayerCreate() { logd("XPlayerCreate."); XPlayer* mPriData; LogVersionInfo(); mPriData = (PlayerContext*)malloc(sizeof(PlayerContext)); memset(mPriData,0x00,sizeof(PlayerContext)); mPriData->mUID = -1; mPriData->mSourceUrl = NULL; mPriData->mSourceFd = -1; mPriData->mSourceFdOffset = 0; mPriData->mSourceFdLength = 0; mPriData->mSourceStream = NULL; mPriData->mStatus = XPLAYER_STATUS_IDLE; mPriData->mSeeking = 0; mPriData->mSeekByInner = 0; mPriData->mSeekSync = 0; mPriData->mLoop = 0; mPriData->mKeepLastFrame = 0; mPriData->mMediaInfo = NULL; mPriData->mMessageQueue = NULL; mPriData->mVideoSizeWidth = 0; mPriData->mVideoSizeHeight= 0; mPriData->mScaledownFlag =0; mPriData->mWidthScaledownRatio = 0; mPriData->mHeightScaledownRatio = 0; mPriData->mCurrentSelectTrackIndex = -1; mPriData->mDemuxNotifyPause = 0; #if PAUSE_THEN_SEEK_BUG mPriData->mSeekTobug = 0; #endif mPriData->gain = DEFAULT_GAIN_IHW; pthread_mutex_init(&mPriData->mMutexMediaInfo, NULL); pthread_mutex_init(&mPriData->mMutexStatus, NULL); sem_init(&mPriData->mSemSetDataSource, 0, 0); sem_init(&mPriData->mSemPrepare, 0, 0); sem_init(&mPriData->mSemStart, 0, 0); sem_init(&mPriData->mSemStop, 0, 0); sem_init(&mPriData->mSemPause, 0, 0); sem_init(&mPriData->mSemReset, 0, 0); sem_init(&mPriData->mSemQuit, 0, 0); sem_init(&mPriData->mSemSeek, 0, 0); sem_init(&mPriData->mSemSetSurface, 0, 0); sem_init(&mPriData->mSemSetAudioSink, 0, 0); sem_init(&mPriData->mSemSetSubCtrl, 0, 0); sem_init(&mPriData->mSemSetDeinterlace, 0, 0); sem_init(&mPriData->mSemPrepareFinish, 0, 0); //* for signal prepare finish, used in prepare(). sem_init(&mPriData->mSemSetSpeed, 0, 0); sem_init(&mPriData->mSemSetPlayBackSettings, 0, 0); mPriData->mMessageQueue = AwMessageQueueCreate(64, "XPlayer"); mPriData->mPlayer = PlayerCreate(); mPriData->mDemux = DemuxCompCreate(); if(mPriData->mPlayer != NULL) PlayerSetCallback(mPriData->mPlayer, callbackProcess, (void*)mPriData); if(mPriData->mDemux != NULL) { DemuxCompSetCallback(mPriData->mDemux, callbackProcess, (void*)mPriData); DemuxCompSetPlayer(mPriData->mDemux, mPriData->mPlayer); } if(pthread_create(&mPriData->mThreadId, NULL, XPlayerThread, mPriData) == 0) mPriData->mThreadCreated = 1; else mPriData->mThreadCreated = 0; strcpy(mPriData->mDefaultTextFormat, "GBK"); mPriData->mIndexFileFdOfIndexSubtitle = -1; mPriData->mIndexFileHasBeenSet = 0; memset(mPriData->mSubtitleDisplayIds,0xff,64*sizeof(unsigned int)); mPriData->mSubtitleDisplayIdsUpdateIndex = 0; mPriData->mApplicationType = APP_DEFAULT; mPriData->mRawOccupyFlag = 0; mPriData->mSpeed = 1; mPriData->mbFast = 0; mPriData->mFastTime = 0; mPriData->mSpeedThreadRuning = 0; return mPriData; } void XPlayerDestroy(XPlayer* p) { AwMessage msg; XPlayer* mPriData; int param_occupy[3] = {1,0,0}; int param_release[3] = {0,0,0}; logw("XPlayerDestroy"); mPriData = (XPlayer*)p; if(mPriData->mThreadCreated) { void* status; XPlayerReset(p); //* stop demux and player. //* send a quit message to quit the main thread. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_QUIT; msg.params[0] = (uintptr_t)&mPriData->mSemQuit; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemQuit, -1); pthread_join(mPriData->mThreadId, &status); } if (mPriData->mSpeedThreadRuning == 1) { void* status; mPriData->mSpeedThreadRuning = 0; pthread_join(mPriData->mSpeedThreadId,&status); } if(mPriData->mDemux != NULL) DemuxCompDestroy(mPriData->mDemux); if(mPriData->mPlayer != NULL) PlayerDestroy(mPriData->mPlayer); if(mPriData->mMessageQueue != NULL) { AwMessageQueueDestroy(mPriData->mMessageQueue); mPriData->mMessageQueue = NULL; } pthread_mutex_destroy(&mPriData->mMutexMediaInfo); pthread_mutex_destroy(&mPriData->mMutexStatus); sem_destroy(&mPriData->mSemSetDataSource); sem_destroy(&mPriData->mSemPrepare); sem_destroy(&mPriData->mSemStart); sem_destroy(&mPriData->mSemStop); sem_destroy(&mPriData->mSemPause); sem_destroy(&mPriData->mSemReset); sem_destroy(&mPriData->mSemQuit); sem_destroy(&mPriData->mSemSeek); sem_destroy(&mPriData->mSemSetSurface); sem_destroy(&mPriData->mSemSetAudioSink); sem_destroy(&mPriData->mSemPrepareFinish); sem_destroy(&mPriData->mSemSetSpeed); sem_destroy(&mPriData->mSemSetSubCtrl); sem_destroy(&mPriData->mSemSetDeinterlace); sem_destroy(&mPriData->mSemSetPlayBackSettings); if(mPriData->mMediaInfo != NULL) clearMediaInfo(p); if(mPriData->mSourceUrl != NULL) free(mPriData->mSourceUrl); if(mPriData->mSourceFd != -1) close(mPriData->mSourceFd); if(mPriData->mIndexFileFdOfIndexSubtitle != -1) close(mPriData->mIndexFileFdOfIndexSubtitle); if (mPriData) { free(mPriData); mPriData = NULL; } } int XPlayerConfig(XPlayer* p, const XPlayerConfig_t *config) { p->mLivemode = config->livemode; p->mApplicationType = config->appType; return 0; } int XPlayerInitCheck(XPlayer* p) { logv("initCheck"); PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mPlayer == NULL || mPriData->mDemux == NULL || mPriData->mThreadCreated == 0) { loge("initCheck() fail, XPlayer::mplayer = %p, XPlayer::mDemux = %p", mPriData->mPlayer, mPriData->mDemux); return -1; } else return 0; } int XPlayerSetUID(XPlayer* p, int nUid) { (void)nUid; (void)p; logv("setUID(), uid = %d", nUid); //mPriData->mUID = uid; return 0; } int XPlayerSetHdcpOps(XPlayer* p, struct HdcpOpsS* pHdcp) { PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mStatus != XPLAYER_STATUS_IDLE && mPriData->mStatus != XPLAYER_STATUS_INITIALIZED) { logw("set hdcp ops incorrect. status: %d", mPriData->mStatus); return -1; } if(p->mDemux) DemuxCompSetHdcpOps(p->mDemux, pHdcp); return 0; } int XPlayerSetScaleDown(XPlayer* p,int bSetScaleDown) { if(p != NULL) { p->bSetScaleDown = bSetScaleDown; logd("XPlayer set scale down.p->bSetScaleDown:%d",p->bSetScaleDown); return 0; } else { loge("xplayer set scale down error!"); return -1; } } int XPlayerSetDataSourceUrl(XPlayer* p, const char* pUrl, void* httpService, const CdxKeyedVectorT* pHeaders) { AwMessage msg; int ret; PlayerContext* mPriData = (PlayerContext*)p; if(pUrl == NULL) { loge("setDataSource(url), url=NULL"); return -1; } logd("setDataSource(url), url='%s'", pUrl); DemuxCompSetLiveMode(mPriData->mDemux, p->mLivemode); mPriData->mHTTPService = httpService; PlayerConfigDispErrorFrame(mPriData->mPlayer, 0); //* send a set data source message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SET_SOURCE; msg.params[0] = (uintptr_t)&mPriData->mSemSetDataSource; msg.params[1] = (uintptr_t)&mPriData->mSetDataSourceReply; msg.params[2] = SOURCE_TYPE_URL; msg.params[3] = (uintptr_t)pUrl; msg.params[4] = (uintptr_t)pHeaders; msg.params[5] = (uintptr_t)(mPriData->mHTTPService); AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSetDataSource, -1); if(mPriData->mSetDataSourceReply != 0) return mPriData->mSetDataSourceReply; //* for local file, I think we should ack like file descriptor source, //* so here we call prepare(). //* local file list: 'bdmv://---', '/---' if (strncmp(pUrl, "bdmv://", 7) == 0 || strncmp(pUrl, "file://", 7) == 0 || pUrl[0] == '/') { //* for local file source set as a file descriptor, //* the application will call invoke() by command INVOKE_ID_GET_TRACK_INFO //* to get track info, so we need call prepare() here to obtain media information before //* application call prepareAsync(). //* here I think for local file source set as a url, we should ack the same as the file //* descriptor case. ret = XPlayerPrepare(p); if (ret != 0) { loge("prepare failure, ret(%d)", ret); } return ret; } else return 0; } //* Warning: The filedescriptor passed into this method will only be valid until //* the method returns, if you want to keep it, dup it! int XPlayerSetDataSourceFd(XPlayer* p, int fd, int64_t offset, int64_t length) { AwMessage msg; int ret = 0; PlayerContext* mPriData = (PlayerContext*)p; logv("setDataSource(fd), fd=%d", fd); PlayerConfigDispErrorFrame(mPriData->mPlayer, 0); //* send a set data source message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SET_SOURCE; msg.params[0] = (uintptr_t)&mPriData->mSemSetDataSource; msg.params[1] = (uintptr_t)&mPriData->mSetDataSourceReply; msg.params[2] = SOURCE_TYPE_FD; msg.params[3] = fd; msg.params[4] = (uintptr_t)(offset>>32); //* params[4] = high 32 bits of offset. msg.params[5] = (uintptr_t)(offset & 0xffffffff);//* params[5] = low 32 bits of offset. msg.params[6] = (uintptr_t)(length>>32); //* params[6] = high 32 bits of length. msg.params[7] = (uintptr_t)(length & 0xffffffff);//* params[7] = low 32 bits of length. AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSetDataSource, -1); if(mPriData->mSetDataSourceReply != 0) return mPriData->mSetDataSourceReply; #if !defined(CONF_SET_SCALEDOWN_MODE) //* for local files, the application will call invoke() by command INVOKE_ID_GET_TRACK_INFO //* to get track info, so we need call prepare() here to obtain media information before //* application call prepareAsync(). ret = XPlayerPrepare(p); if (ret != 0) { loge("prepare failure, ret(%d)", ret); } #endif return ret; } int XPlayerSetDataSourceStream(XPlayer* p, const char* streamStr) { AwMessage msg; logd("setDataSource(IStreamSource)"); PlayerContext* mPriData = (PlayerContext*)p; PlayerConfigDispErrorFrame(mPriData->mPlayer, 1); //* send a set data source message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SET_SOURCE; msg.params[0] = (uintptr_t)&mPriData->mSemSetDataSource; msg.params[1] = (uintptr_t)&mPriData->mSetDataSourceReply; msg.params[2] = SOURCE_TYPE_ISTREAMSOURCE; msg.params[3] = (uintptr_t)streamStr; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSetDataSource, -1); return mPriData->mSetDataSourceReply; } int XPlayerSetMediaDataSource(XPlayer* p, const char* streamStr) { AwMessage msg; logd("setDataSource(MediaDataSource)"); PlayerContext* mPriData = (PlayerContext*)p; PlayerConfigDispErrorFrame(mPriData->mPlayer, 1); //* send a set data source message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SET_SOURCE; msg.params[0] = (uintptr_t)&mPriData->mSemSetDataSource; msg.params[1] = (uintptr_t)&mPriData->mSetDataSourceReply; msg.params[2] = SOURCE_TYPE_DATASOURCE; msg.params[3] = (uintptr_t)streamStr; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSetDataSource, -1); return mPriData->mSetDataSourceReply; } //* set video layer control ops int XPlayerSetVideoSurfaceTexture(XPlayer* p, const LayerCtrl* surfaceTexture) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logd("setVideoSurfaceTexture, surface = %p", surfaceTexture); //* send a set surface message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SET_SURFACE; msg.params[0] = (uintptr_t)&mPriData->mSemSetSurface; msg.params[1] = (uintptr_t)&mPriData->mSetSurfaceReply; msg.params[2] = (uintptr_t)surfaceTexture; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSetSurface, -1); return mPriData->mSetSurfaceReply; } void XPlayerSetAudioSink(XPlayer* p, const SoundCtrl* audioSink) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logv("setAudioSink"); //* send a set audio sink message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SET_AUDIOSINK; msg.params[0] = (uintptr_t)&mPriData->mSemSetAudioSink; msg.params[1] = (uintptr_t)&mPriData->mSetAudioSinkReply; msg.params[2] = (uintptr_t)audioSink; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSetAudioSink, -1); return; } int XPlayerSetPlaybackSettings(XPlayer* p,const XAudioPlaybackRate *rate) { PlayerContext* mPriData = (PlayerContext*)p; //* send a set set playback message. AwMessage msg; memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SET_PLAYRATE; msg.params[0] = (uintptr_t)&mPriData->mSemSetPlayBackSettings; msg.params[1] = (uintptr_t)&mPriData->mSetPlayBackSettingsReply; msg.params[2] = (uintptr_t)rate; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSetPlayBackSettings, -1); if (rate->mSpeed == 0.f && mPriData->mStatus == XPLAYER_STATUS_STARTED) { logd("PlayerPause"); XPlayerPause(p); } else if (rate->mSpeed != 0.f && (mPriData->mStatus == XPLAYER_STATUS_PAUSED || mPriData->mStatus == XPLAYER_STATUS_PREPARED || mPriData->mStatus == XPLAYER_STATUS_COMPLETE)) { XPlayerStart(p); } return mPriData->mSetPlayBackSettingsReply; } int XPlayerGetPlaybackSettings(XPlayer* p,XAudioPlaybackRate *rate) { logd("XPlayerGetPlaybackSettings"); PlayerContext* mPriData = (PlayerContext*)p; int ret = PlayerGetPlayBackSettings(mPriData->mPlayer,rate); if (ret == 0) { if (mPriData->mStatus == XPLAYER_STATUS_PAUSED) { rate->mSpeed = 0.f; } } return ret; } void XPlayerSetSubCtrl(XPlayer* p, const SubCtrl* subctrl) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logv("setSubRender"); //* send a set audio sink message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SET_SUBCTRL; msg.params[0] = (uintptr_t)&mPriData->mSemSetSubCtrl; msg.params[1] = (uintptr_t)&mPriData->mSetSubCtrlReply; msg.params[2] = (uintptr_t)subctrl; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSetSubCtrl, -1); return; } void XPlayerSetDeinterlace(XPlayer* p, const Deinterlace* di) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logd("set deinterlace"); //* send a set audio sink message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SET_DI; msg.params[0] = (uintptr_t)&mPriData->mSemSetDeinterlace; msg.params[1] = (uintptr_t)&mPriData->mSetDeinterlaceReply; msg.params[2] = (uintptr_t)di; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSetDeinterlace, -1); return; } int XPlayerSetNotifyCallback(XPlayer* p, XPlayerNotifyCallback notifier, void* pUserData) { PlayerContext* mPriData = (PlayerContext*)p; mPriData->mCallback = notifier; mPriData->pUser = pUserData; return 0; } int XPlayerPrepareAsync(XPlayer* p) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logd("prepareAsync"); //* send a prepare. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_PREPARE; msg.params[0] = (uintptr_t)&mPriData->mSemPrepare; msg.params[1] = (uintptr_t)&mPriData->mPrepareReply; msg.params[2] = 0; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemPrepare, -1); return mPriData->mPrepareReply; } int XPlayerPrepare(XPlayer* p) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logd("prepare"); //* clear the mSemPrepareFinish semaphore. while(sem_trywait(&mPriData->mSemPrepareFinish) == 0); //* send a prepare message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_PREPARE; msg.params[0] = (uintptr_t)&mPriData->mSemPrepare; msg.params[1] = (uintptr_t)&mPriData->mPrepareReply; msg.params[2] = 1; //* params[2] = mPrepareSync. AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemPrepare, -1); if(mPriData->mPrepareReply == 0) { //* wait for the prepare finish. SemTimedWait(&mPriData->mSemPrepareFinish, -1); return mPriData->mPrepareFinishResult; } //* call DemuxCompPrepareAsync() fail, or status error. return mPriData->mPrepareReply; } int XPlayerStart(XPlayer* p) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logd("start"); // in washu cmcc for livemode 1, we should call seekTo when paused longer than 30s mPriData->mLivemode = DemuxCompGetLiveMode(mPriData->mDemux); if(mPriData->mStatus == XPLAYER_STATUS_PAUSED && mPriData->mLivemode == 1 && mPriData->mApplicationType == APP_CMCC_WASU) { struct timeval tv; gettimeofday(&tv, NULL); int shiftTimeMs = (tv.tv_sec * 1000000ll + tv.tv_usec - mPriData->mPauseTimeStamp)/1000; if(shiftTimeMs > 30*1000 && !mPriData->mSeekTobug && (mPriData->mPauseLivemode!=2)) { logd("pause time longer than 30s in livemode 1, seekTo(%d)", shiftTimeMs); XPlayerSeekTo(p, shiftTimeMs, mPriData->mSeekModeType); } } #if (PAUSE_THEN_SEEK_BUG) if(mPriData->mSeekTobug && mPriData->mApplicationType == APP_CMCC_WASU) { mPriData->mSeekTobug = 0; mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SEEK_COMPLETE, 0, 0); } #endif //* send a start message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_START; msg.params[0] = (uintptr_t)&mPriData->mSemStart; msg.params[1] = (uintptr_t)&mPriData->mStartReply; msg.params[2] = (uintptr_t)&mPriData->gain; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemStart, -1); return mPriData->mStartReply; } int XPlayerStartWithGain(XPlayer * p, float gain) { PlayerContext* mPriData = (PlayerContext*)p; mPriData->gain = gain; XPlayerStart(p); return 0; } int XPlayerStop(XPlayer* p) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logd("stop"); XPlayerGetCurrentPosition(mPriData, &mPriData->mTimeMsBeforeStop); //softdetector //* send a stop message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_STOP; msg.params[0] = (uintptr_t)&mPriData->mSemStop; msg.params[1] = (uintptr_t)&mPriData->mStopReply; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemStop, -1); logd("=== stop end"); return mPriData->mStopReply; } int XPlayerPause(XPlayer* p) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logd("pause"); mPriData->mLivemode = DemuxCompGetLiveMode(mPriData->mDemux); mPriData->mPauseLivemode = mPriData->mLivemode; if(mPriData->mLivemode == 1) { struct timeval tv; gettimeofday(&tv, NULL); mPriData->mPauseTimeStamp = tv.tv_sec * 1000000ll + tv.tv_usec; logd("livemode1, get current position inmPauseTimeStamp = %" PRId64 "", mPriData->mPauseTimeStamp); } else if(mPriData->mLivemode == 2) { int msec; XPlayerGetCurrentPosition(p, &msec); logd("get current position in pause: %d", msec); struct timeval tv; gettimeofday(&tv, NULL); mPriData->mPauseTimeStamp = tv.tv_sec * 1000000ll + tv.tv_usec - (int64_t)msec*1000; logd("get current position in pause, nowUs = %ld, mPauseTimeStamp = %" PRId64 "", tv.tv_sec * 1000000 + tv.tv_usec, mPriData->mPauseTimeStamp); } //* send a pause message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_PAUSE; msg.params[0] = (uintptr_t)&mPriData->mSemPause; msg.params[1] = (uintptr_t)&mPriData->mPauseReply; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemPause, -1); return mPriData->mPauseReply; } int XPlayerSeekTo(XPlayer* p, int nSeekTimeMs, SeekModeType nSeekModeType) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logd("seekTo [%dms]", nSeekTimeMs); mPriData->mSeekModeType = nSeekModeType; mPriData->mLivemode = DemuxCompGetLiveMode(mPriData->mDemux); #if (PAUSE_THEN_SEEK_BUG) if((mPriData->mStatus == XPLAYER_STATUS_PAUSED /*|| mPriData->mStatus == XPLAYER_STATUS_STARTED */) && mPriData->mApplicationType == APP_CMCC_WASU) { int pos; XPlayerGetCurrentPosition(p, &pos); int diff = pos - nSeekTimeMs; logd("diff = %d", diff); if(-1000 < diff && diff < 1000) { if(mPriData->mLivemode == 1 && mPriData->mStatus == XPLAYER_STATUS_PAUSED) { // if livemod1, we should change it to livemode2 struct timeval tv; gettimeofday(&tv, NULL); DemuxCompSetLiveMode(mPriData->mDemux, 2); mPriData->mShiftTimeStamp = tv.tv_sec * 1000000ll + tv.tv_usec - nSeekTimeMs*1000ll; } mPriData->mSeekTobug = 1; return XPlayerStart(p); } } #endif if(mPriData->mLivemode == 1 || mPriData->mLivemode == 2) { logd("++++++ reset url"); struct timeval tv; gettimeofday(&tv, NULL); mPriData->mShiftTimeStamp = tv.tv_sec * 1000000ll + tv.tv_usec; mPriData->mPreSeekTimeMs = nSeekTimeMs + 20000; //* for timeshift //* send a start message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_RESETURL; msg.params[0] = (uintptr_t)&mPriData->mSemSeek; msg.params[1] = (uintptr_t)&mPriData->mSeekReply; msg.params[2] = nSeekTimeMs; //* params[2] = mSeekTime. //* params[3] = mSeekSync. AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSeek, -1); } else { logd("seek"); mPriData->mSeekByInner = 0; //* send a start message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SEEK; msg.params[0] = (uintptr_t)&mPriData->mSemSeek; msg.params[1] = (uintptr_t)&mPriData->mSeekReply; msg.params[2] = nSeekTimeMs; //* params[2] = mSeekTime. AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSeek, -1); } return mPriData->mSeekReply; } int XPlayerReset(XPlayer* p) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logw("reset..."); XPlayerGetCurrentPosition(mPriData, &mPriData->mTimeMsBeforeStop); //softdetector //* send a start message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_RESET; msg.params[0] = (uintptr_t)&mPriData->mSemReset; msg.params[1] = (uintptr_t)&mPriData->mResetReply; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemReset, -1); return mPriData->mResetReply; } int XPlayerSetSpeed(XPlayer* p, int nSpeed) { AwMessage msg; PlayerContext* mPriData = (PlayerContext*)p; logd("XPlayerSetSpeed %d",nSpeed); mPriData->mSpeed = nSpeed; //PlayerSetSpeed(mPriData->mPlayer, nSpeed); if(nSpeed == 1) { mPriData->mbFast = 0; mPriData->mSpeed = 1; mPriData->mFastTime = 0; XPlayerStart(p); PlayerSetDiscardAudio(mPriData->mPlayer, 0); return 0; } //* send a start message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SETSPEED; msg.params[0] = (uintptr_t)&mPriData->mSemSetSpeed; msg.params[1] = (uintptr_t)&mPriData->mSetSpeedReply; msg.params[2] = nSpeed; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); SemTimedWait(&mPriData->mSemSetSpeed, -1); return mPriData->mSetSpeedReply; } int XPlayerIsPlaying(XPlayer* p) { logi("isPlaying"); PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mStatus == XPLAYER_STATUS_STARTED || (mPriData->mStatus == XPLAYER_STATUS_COMPLETE && mPriData->mLoop != 0)) return 1; return 0; } MediaInfo* XPlayerGetMediaInfo(XPlayer* p) { PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mStatus == XPLAYER_STATUS_PREPARING || mPriData->mStatus == XPLAYER_STATUS_INITIALIZED|| mPriData->mStatus == XPLAYER_STATUS_IDLE ) { loge("cannot get mediainfo in this status"); return NULL; } return mPriData->mMediaInfo; } int XPlayerGetCurrentPosition(XPlayer* p, int* msec) { int64_t nPositionUs; PlayerContext* mPriData = (PlayerContext*)p; logv("getCurrentPosition"); if(mPriData->mStatus == XPLAYER_STATUS_PREPARED || mPriData->mStatus == XPLAYER_STATUS_STARTED || mPriData->mStatus == XPLAYER_STATUS_PAUSED || mPriData->mStatus == XPLAYER_STATUS_COMPLETE) { if(mPriData->mSeeking != 0) { *msec = mPriData->mSeekTime; return 0; } mPriData->mLivemode = DemuxCompGetLiveMode(mPriData->mDemux); if(mPriData->mLivemode == 1) { if(mPriData->mStatus == XPLAYER_STATUS_PAUSED) { struct timeval tv; gettimeofday(&tv, NULL); *msec = (tv.tv_sec * 1000000ll + tv.tv_usec - mPriData->mPauseTimeStamp)/1000; } else if(mPriData->mDemuxNotifyPause) { struct timeval tv; gettimeofday(&tv, NULL); *msec = (tv.tv_sec * 1000000ll + tv.tv_usec - mPriData->mDemuxPauseTimeStamp)/1000; } else { *msec = 0; } if(*msec < 0) *msec = 0; return 0; } else if(mPriData->mLivemode == 2) { struct timeval tv; gettimeofday(&tv, NULL); if(mPriData->mStatus == XPLAYER_STATUS_PAUSED) { *msec = (tv.tv_sec * 1000000 + tv.tv_usec - mPriData->mPauseTimeStamp)/1000; logd("livemode2, nowUs = %ld, mPauseTimeStamp = %" PRId64 "", tv.tv_sec * 1000000 + tv.tv_usec, mPriData->mPauseTimeStamp); } else { //cmcc livemode2 only support ts nPositionUs = PlayerGetPositionCMCC(mPriData->mPlayer); logd("-- nPositionUs = %" PRId64 "", nPositionUs); *msec = (tv.tv_sec * 1000000LL + tv.tv_usec - mPriData->mShiftTimeStamp + nPositionUs)/1000; } if(*msec < 0) { logd("positon < 0 ,check it!!"); *msec = 0; } logd("livemode = 2, getCurrentPosition %d", *msec); return 0; } //* in complete status, the prepare() method maybe called and it change the media info. pthread_mutex_lock(&mPriData->mMutexMediaInfo); if(mPriData->mMediaInfo != NULL) { //* ts stream's pts is not started at 0. if(mPriData->mMediaInfo->eContainerType == CONTAINER_TYPE_TS || mPriData->mMediaInfo->eContainerType == CONTAINER_TYPE_BD || mPriData->mMediaInfo->eContainerType == CONTAINER_TYPE_HLS) nPositionUs = PlayerGetPosition(mPriData->mPlayer); else //* generally, stream pts is started at 0 except ts stream. nPositionUs = PlayerGetPts(mPriData->mPlayer); *msec = (nPositionUs + 500)/1000; pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return 0; } else { loge("getCurrentPosition() fail, mMediaInfo==NULL."); *msec = 0; pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return 0; } } else { *msec = 0; if(mPriData->mStatus == XPLAYER_COMMAND_STOP && mPriData->mTimeMsBeforeStop >= 0) *msec = mPriData->mTimeMsBeforeStop; if(mPriData->mStatus == XPLAYER_STATUS_ERROR) return -1; else return 0; } return -1; } int XPlayerGetDuration(XPlayer* p, int *msec) { logv("getDuration"); PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mStatus == XPLAYER_STATUS_PREPARED || mPriData->mStatus == XPLAYER_STATUS_STARTED || mPriData->mStatus == XPLAYER_STATUS_PAUSED || mPriData->mStatus == XPLAYER_STATUS_STOPPED || mPriData->mStatus == XPLAYER_STATUS_COMPLETE) { //* in complete status, the prepare() method maybe called and it change the media info. pthread_mutex_lock(&mPriData->mMutexMediaInfo); if(mPriData->mMediaInfo != NULL) *msec = mPriData->mMediaInfo->nDurationMs; else { loge("getCurrentPosition() fail, mPriData->mMediaInfo==NULL."); *msec = 0; } pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return 0; } loge("invalid getDuration() call, player not in valid status."); return -1; } int XPlayerSetLooping(XPlayer* p, int loop) { logd("setLooping"); PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mStatus == XPLAYER_STATUS_ERROR) return -1; mPriData->mLoop = loop; return 0; } int updateVideoInfo(XPlayer* p) { //* get media information. MediaInfo* mi; VideoStreamInfo* vi; int ret; PlayerContext* mPriData = (PlayerContext*)p; pthread_mutex_lock(&mPriData->mMutexMediaInfo); mi = DemuxCompGetMediaInfo(mPriData->mDemux); if(mi == NULL) { loge("can not get media info from demux."); pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return -1; } clearMediaInfo(p); mPriData->mMediaInfo = mi; #if !AWPLAYER_CONFIG_DISABLE_VIDEO if(mi->pVideoStreamInfo != NULL) { #if !defined(CONF_3D_ENABLE) if(mi->pVideoStreamInfo->bIs3DStream == 1) mi->pVideoStreamInfo->bIs3DStream = 0; #endif ret = PlayerSetVideoStreamInfo(mPriData->mPlayer, mi->pVideoStreamInfo); if(ret != 0) { logw("PlayerSetVideoStreamInfo() fail, video stream not supported."); } } #endif pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return 0; } int updateAudioInfo(XPlayer* p) { //* get media information. MediaInfo* mi; AudioStreamInfo* vi; int ret; PlayerContext* mPriData = (PlayerContext*)p; pthread_mutex_lock(&mPriData->mMutexMediaInfo); mi = DemuxCompGetMediaInfo(mPriData->mDemux); if(mi == NULL) { loge("can not get media info from demux."); pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return -1; } clearMediaInfo(p); mPriData->mMediaInfo = mi; #if !AWPLAYER_CONFIG_DISABLE_AUDIO if(mi->pAudioStreamInfo != NULL) { int i; int nDefaultAudioIndex; nDefaultAudioIndex = -1; for(i=0; inAudioStreamNum; i++) { if(PlayerCanSupportAudioStream(mPriData->mPlayer, &mi->pAudioStreamInfo[i])) { nDefaultAudioIndex = i; break; } } if(nDefaultAudioIndex < 0) { logw("no audio stream supported."); nDefaultAudioIndex = 0; } //* 1. stop audio PlayerStopAudio(mPriData->mPlayer); //* 2. set stream info ret = PlayerSetAudioStreamInfo(mPriData->mPlayer, mi->pAudioStreamInfo, mi->nAudioStreamNum, nDefaultAudioIndex); if(ret != 0) { logw("PlayerSetAudioStreamInfo() fail, audio stream not supported."); } } #endif pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return 0; } /* *ret 1 means need to scale down * 0 is do nothing * -1 is unknown error * */ /*A80 general 4K -> do nothing (0)*/ /*H3/H8 general 4K -> Scale down (1)*/ /*H3 H265 4K -> do nothing (0)*/ int getScaledownFlag(const MediaInfo *mi,int bSetScaleDown) { int ret = 0; /* default not scaledown */ if(bSetScaleDown == 1) { if(mi->pVideoStreamInfo->nWidth > WIDTH_1080P || mi->pVideoStreamInfo->nHeight > HEIGHT_1080P) { logd("###source file larger than 2k,scale down."); ret = 1; } } else if(bSetScaleDown == 2) { logd("set scale down in any resolution."); ret = 1; } if(mi->pVideoStreamInfo->nWidth >= WIDTH_4K || mi->pVideoStreamInfo->nHeight >= HEIGHT_4K) { if (mi->pVideoStreamInfo->eCodecFormat == VIDEO_CODEC_FORMAT_H265) { #if !defined(CONF_H265_4K_P2P) /* only H3/H5 support H.265 p2p */ ret = 1; #endif } else if (mi->pVideoStreamInfo->eCodecFormat == VIDEO_CODEC_FORMAT_H264) { #if !defined(CONF_H264_4K_P2P) /* only A80 support H.264 p2p */ ret = 1; #endif } else if (mi->pVideoStreamInfo->eCodecFormat == VIDEO_CODEC_FORMAT_VP9) { ret = 0; } else /* all other codec format should scaledown for 4K size */ { ret = 1; } } return ret; } int XPlayerSetScaleDownRatio(XPlayer* p,int widthRatio,int heightRatio){ PlayerContext* mPriData = (PlayerContext*)p; if((mPriData != NULL)&& (mPriData->mStatus==XPLAYER_STATUS_IDLE || mPriData->mStatus==XPLAYER_STATUS_INITIALIZED || mPriData->mStatus==XPLAYER_STATUS_PREPARING) ){ mPriData->mWidthScaledownRatio = widthRatio; mPriData->mHeightScaledownRatio = heightRatio; return 0; }else{ loge("XPlayerSetScaleDownRatio err,mPriData->mStatus = %d",mPriData->mStatus); return -1; } } /* *ret 0 means supported, -1 is unsupported. * */ int checkVideoSupported(const MediaInfo *mi) { int ret = 0; //default is supported /*A80/H8 H265 4K unsupported*/ /*H3/H5 H265 4K supported*/ if (mi->pVideoStreamInfo->nWidth * mi->pVideoStreamInfo->nHeight >= PIXELS_4K) { #if !defined(CONF_H265_4K) if (mi->pVideoStreamInfo->eCodecFormat == VIDEO_CODEC_FORMAT_H265) { loge("Not support H265 4K video !!"); ret = -1; } #endif if (mi->pVideoStreamInfo->eCodecFormat == VIDEO_CODEC_FORMAT_WMV3) { loge("Not support VC1(WMV3) 4K video !!"); ret = -1; } } else if (mi->pVideoStreamInfo->nWidth > WIDTH_1080P || mi->pVideoStreamInfo->nHeight > HEIGHT_1080P) { /* for all chip, WMV1/WMV2/VP6 specs unsupported, play effect too bad... */ if (mi->pVideoStreamInfo->eCodecFormat == VIDEO_CODEC_FORMAT_WMV1 || mi->pVideoStreamInfo->eCodecFormat == VIDEO_CODEC_FORMAT_WMV2 || mi->pVideoStreamInfo->eCodecFormat == VIDEO_CODEC_FORMAT_VP6) { loge("Not support WMV1/WMV2/VP6 1080P video !!"); ret = -1; } } else /* < 1080P */ { /* we support this*/ } logv("check video support ret = [%d]", ret); return ret; } static int initializePlayer(XPlayer* p) { //* get media information. MediaInfo* mi; VideoStreamInfo* vi; AudioStreamInfo* ai; SubtitleStreamInfo* si; int i; int nDefaultAudioIndex; int nDefaultSubtitleIndex; int ret; PlayerContext* mPriData = (PlayerContext*)p; pthread_mutex_lock(&mPriData->mMutexMediaInfo); mi = DemuxCompGetMediaInfo(mPriData->mDemux); if(mi == NULL) { loge("can not get media info from demux."); pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return -1; } if(mi->nBitrate > 0) PlayerSetVideoAvgByteRate(mPriData->mPlayer, mi->nBitrate/8); PlayerSetFirstPts(mPriData->mPlayer,mi->nFirstPts); mPriData->mMediaInfo = mi; if(mi->pVideoStreamInfo != NULL) { /*detect if support*/ if(checkVideoSupported(mi)) { loge("this video is outof specs, unsupported."); return -1; } if(mPriData->mWidthScaledownRatio == 0 && mPriData->mHeightScaledownRatio == 0){ /*if the app not set scaledownRatio,use the default*/ /*check if need scaledown*/ if(getScaledownFlag(mi,mPriData->bSetScaleDown)==1) /*1 means need to scale down*/ { ret = PlayerConfigVideoScaleDownRatio(mPriData->mPlayer, 1, 1); if(ret != 0) { logw("PlayerConfigVideoScaleDownRatio() fail, ret = %d",ret); } else mPriData->mScaledownFlag = 1; } }else{ ret = PlayerConfigVideoScaleDownRatio(mPriData->mPlayer,mPriData->mWidthScaledownRatio,mPriData->mHeightScaledownRatio); if(ret != 0) { logw("PlayerConfigVideoScaleDownRatio() fail, ret = %d",ret); } else mPriData->mScaledownFlag = 1; } } //* initialize the player. #if !AWPLAYER_CONFIG_DISABLE_VIDEO if(mi->pVideoStreamInfo != NULL) { #if !defined(CONF_3D_ENABLE) if(mi->pVideoStreamInfo->bIs3DStream == 1) mi->pVideoStreamInfo->bIs3DStream = 0; #endif //* set the rotation int nRotateDegree; int nRotation = atoi((const char*)mPriData->mMediaInfo->cRotation); if(nRotation == 0) nRotateDegree = 0; else if(nRotation == 90) nRotateDegree = 1; else if(nRotation == 180) nRotateDegree = 2; else if(nRotation == 270) nRotateDegree = 3; else nRotateDegree = 0; ret = PlayerConfigVideoRotateDegree(mPriData->mPlayer, nRotateDegree); if(ret != 0) { logw("PlayerConfigVideoRotateDegree() fail, ret = %d",ret); } //* set the video streamInfo ret = PlayerSetVideoStreamInfo(mPriData->mPlayer, mi->pVideoStreamInfo); if(ret != 0) { logw("PlayerSetVideoStreamInfo() fail, video stream not supported."); } ret = PlayerConfigDropDelayFrame(mPriData->mPlayer, 1); if(ret != 0) { logw("PlayerConfigDropDelayFrame() fail."); } } #endif #if !AWPLAYER_CONFIG_DISABLE_AUDIO if(mi->pAudioStreamInfo != NULL) { nDefaultAudioIndex = -1; for(i=0; inAudioStreamNum; i++) { if(PlayerCanSupportAudioStream(mPriData->mPlayer, &mi->pAudioStreamInfo[i])) { nDefaultAudioIndex = i; break; } } if(nDefaultAudioIndex < 0) { logw("no audio stream supported."); nDefaultAudioIndex = 0; } ret = PlayerSetAudioStreamInfo(mPriData->mPlayer, mi->pAudioStreamInfo, mi->nAudioStreamNum, nDefaultAudioIndex); if(ret != 0) { logw("PlayerSetAudioStreamInfo() fail, audio stream not supported."); } } #endif if(PlayerHasVideo(mPriData->mPlayer) == 0 && PlayerHasAudio(mPriData->mPlayer) == 0) { loge("neither video nor audio stream can be played."); pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return -1; } #if !AWPLAYER_CONFIG_DISABLE_SUBTITLE //* set subtitle stream to the text decoder. if(mi->pSubtitleStreamInfo != NULL) { nDefaultSubtitleIndex = -1; for(i=0; inSubtitleStreamNum; i++) { if(PlayerCanSupportSubtitleStream(mPriData->mPlayer, &mi->pSubtitleStreamInfo[i])) { nDefaultSubtitleIndex = i; break; } } if(nDefaultSubtitleIndex < 0) { logw("no subtitle stream supported."); nDefaultSubtitleIndex = 0; } ret = PlayerSetSubtitleStreamInfo(mPriData->mPlayer, mi->pSubtitleStreamInfo, mi->nSubtitleStreamNum, nDefaultSubtitleIndex); if(ret != 0) { logw("PlayerSetSubtitleStreamInfo() fail, subtitle stream not supported."); } } #endif //* report not seekable. if(mi->bSeekable == 0) mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_INFO, AW_MEDIA_INFO_NOT_SEEKABLE, 0); pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return 0; } static void clearMediaInfo(XPlayer* p) { int i; VideoStreamInfo* v; AudioStreamInfo* a; SubtitleStreamInfo* s; PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mMediaInfo != NULL) { //* free video stream info. if(mPriData->mMediaInfo->pVideoStreamInfo != NULL) { for(i=0; imMediaInfo->nVideoStreamNum; i++) { v = &mPriData->mMediaInfo->pVideoStreamInfo[i]; if(v->pCodecSpecificData != NULL && v->nCodecSpecificDataLen > 0) free(v->pCodecSpecificData); } free(mPriData->mMediaInfo->pVideoStreamInfo); mPriData->mMediaInfo->pVideoStreamInfo = NULL; } //* free audio stream info. if(mPriData->mMediaInfo->pAudioStreamInfo != NULL) { for(i=0; imMediaInfo->nAudioStreamNum; i++) { a = &mPriData->mMediaInfo->pAudioStreamInfo[i]; if(a->pCodecSpecificData != NULL && a->nCodecSpecificDataLen > 0) free(a->pCodecSpecificData); } free(mPriData->mMediaInfo->pAudioStreamInfo); mPriData->mMediaInfo->pAudioStreamInfo = NULL; } //* free subtitle stream info. if(mPriData->mMediaInfo->pSubtitleStreamInfo != NULL) { for(i=0; imMediaInfo->nSubtitleStreamNum; i++) { s = &mPriData->mMediaInfo->pSubtitleStreamInfo[i]; if(s->pUrl != NULL) { free(s->pUrl); s->pUrl = NULL; } if(s->fd >= 0) { close(s->fd); s->fd = -1; } if(s->fdSub >= 0) { close(s->fdSub); s->fdSub = -1; } } free(mPriData->mMediaInfo->pSubtitleStreamInfo); mPriData->mMediaInfo->pSubtitleStreamInfo = NULL; } //* free the media info. free(mPriData->mMediaInfo); mPriData->mMediaInfo = NULL; } return; } static void* sendSpeedCmdThread(void* arg) { PlayerContext* mPriData = (PlayerContext*)arg; mPriData->mSpeedThreadRuning = 1; int needPauseNext =0; logd("sendSpeedCmdThread start"); while(mPriData->mSpeedThreadRuning) { if (mPriData->mbFast == 1) { sleep(1); if (mPriData->mSpeedThreadRuning != 1) break; if(mPriData->mStatus == XPLAYER_STATUS_PAUSED || mPriData->mStatus == XPLAYER_STATUS_STOPPED || mPriData->mStatus == XPLAYER_STATUS_COMPLETE || mPriData->mStatus == XPLAYER_STATUS_ERROR || mPriData->mStatus == XPLAYER_STATUS_IDLE) { mPriData->mbFast = 0; continue; } if ( needPauseNext == 1 || mPriData->mSpeed == 0 ) { needPauseNext = 0; AwMessage msg; //* send a pause message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_PAUSE; msg.params[0] = (uintptr_t)&mPriData->mSemPause; msg.params[1] = (uintptr_t)&mPriData->mPauseReply; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); continue; } mPriData->mFastTime += mPriData->mSpeed*1000; if (mPriData->mFastTime < 0) { mPriData->mFastTime = 0; needPauseNext = 1; } else needPauseNext = 0; if(mPriData->mFastTime >= 0 && mPriData->mbFast == 1 ) { AwMessage msg; //* send a seek message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SEEK; msg.params[0] = (uintptr_t)&mPriData->mSemSeek; msg.params[1] = (uintptr_t)&mPriData->mSeekReply; msg.params[2] = mPriData->mFastTime; //* params[2] = mSeekTime. msg.params[3] = 0; //* params[3] = mSeekSync. logd("speed mode seek, mFastTime: %d, mSpeed: %d", mPriData->mFastTime, mPriData->mSpeed); AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); } if(mPriData->mStatus == XPLAYER_STATUS_PAUSED || mPriData->mStatus == XPLAYER_STATUS_STOPPED || mPriData->mStatus == XPLAYER_STATUS_COMPLETE || mPriData->mStatus == XPLAYER_STATUS_ERROR || mPriData->mStatus == XPLAYER_STATUS_IDLE ) { mPriData->mbFast = 0; continue; } } else { usleep(1000); } } return 0; } static void* XPlayerThread(void* arg) { AwMessage msg; int ret; sem_t* pReplySem; int* pReplyValue; PlayerContext* mPriData = (PlayerContext*)arg; while(1) { if(AwMessageQueueGetMessage(mPriData->mMessageQueue, &msg) < 0) { loge("get message fail."); continue; } pReplySem = (sem_t*)msg.params[0]; pReplyValue = (int*)msg.params[1]; if(msg.messageId == XPLAYER_COMMAND_SET_SOURCE) { logi("process message XPLAYER_COMMAND_SET_SOURCE."); //* check status. if(mPriData->mStatus != XPLAYER_STATUS_IDLE && mPriData->mStatus != XPLAYER_STATUS_INITIALIZED) { loge("invalid setDataSource() operation, player not in IDLE or INITIALIZED status"); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if((int)msg.params[2] == SOURCE_TYPE_URL) { CdxKeyedVectorT* pHeaders; //* data source is a url string. if(mPriData->mSourceUrl != NULL) free(mPriData->mSourceUrl); mPriData->mSourceUrl = strdup((char*)msg.params[3]); pHeaders = (CdxKeyedVectorT*) msg.params[4]; ret = DemuxCompSetUrlSource(mPriData->mDemux, (void*)msg.params[5], mPriData->mSourceUrl, pHeaders); if(ret == 0) { mPriData->mStatus = XPLAYER_STATUS_INITIALIZED; if(pReplyValue != NULL) *pReplyValue = 0; } else { loge("DemuxCompSetUrlSource() return fail."); mPriData->mStatus = XPLAYER_STATUS_IDLE; free(mPriData->mSourceUrl); mPriData->mSourceUrl = NULL; if(pReplyValue != NULL) *pReplyValue = -1; } } else if((int)msg.params[2] == SOURCE_TYPE_FD) { //* data source is a file descriptor. int fd; int64_t nOffset; int64_t nLength; fd = msg.params[3]; nOffset = msg.params[4]; nOffset<<=32; nOffset |= msg.params[5]; nLength = msg.params[6]; nLength<<=32; nLength |= msg.params[7]; if(mPriData->mSourceFd != -1) close(mPriData->mSourceFd); mPriData->mSourceFd = dup(fd); mPriData->mSourceFdOffset = nOffset; mPriData->mSourceFdLength = nLength; ret = DemuxCompSetFdSource(mPriData->mDemux, mPriData->mSourceFd, mPriData->mSourceFdOffset, mPriData->mSourceFdLength); if(ret == 0) { mPriData->mStatus = XPLAYER_STATUS_INITIALIZED; if(pReplyValue != NULL) *pReplyValue = (int)0; } else { loge("DemuxCompSetFdSource() return fail."); mPriData->mStatus = XPLAYER_STATUS_IDLE; close(mPriData->mSourceFd); mPriData->mSourceFd = -1; mPriData->mSourceFdOffset = 0; mPriData->mSourceFdLength = 0; if(pReplyValue != NULL) *pReplyValue = (int)-1; } } else if((int)msg.params[2] == SOURCE_TYPE_DATASOURCE) { //* data source is a mediadatasource interface. char *uri = (char *)msg.params[3]; int ret; void *handle; ret = sscanf(uri, "datasource://%p", &handle); if (ret != 1) { CDX_LOGE("sscanf failure...(%s)", uri); mPriData->mSourceStream = NULL; } else { mPriData->mSourceStream = (CdxStreamT *)handle; } ret = DemuxCompSetMediaDataSource(mPriData->mDemux, uri); if(ret == 0) { mPriData->mStatus = XPLAYER_STATUS_INITIALIZED; if(pReplyValue != NULL) *pReplyValue = (int)0; } else { loge("DemuxCompSetStreamSource() return fail."); mPriData->mStatus = XPLAYER_STATUS_IDLE; if(mPriData->mSourceStream) { CdxStreamClose(mPriData->mSourceStream); mPriData->mSourceStream = NULL; } if(pReplyValue != NULL) *pReplyValue = (int)-1; } } else { //* data source is a IStreamSource interface. char *uri = (char *)msg.params[3]; int ret; void *handle; ret = sscanf(uri, "customer://%p", &handle); if (ret != 1) { CDX_LOGE("sscanf failure...(%s)", uri); mPriData->mSourceStream = NULL; } else { mPriData->mSourceStream = (CdxStreamT *)handle; } ret = DemuxCompSetStreamSource(mPriData->mDemux, uri); if(ret == 0) { mPriData->mStatus = XPLAYER_STATUS_INITIALIZED; if(pReplyValue != NULL) *pReplyValue = (int)0; } else { loge("DemuxCompSetStreamSource() return fail."); mPriData->mStatus = XPLAYER_STATUS_IDLE; if(mPriData->mSourceStream) { CdxStreamClose(mPriData->mSourceStream); mPriData->mSourceStream = NULL; } if(pReplyValue != NULL) *pReplyValue = (int)-1; } } if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end XPLAYER_COMMAND_SET_SOURCE. else if(msg.messageId == XPLAYER_COMMAND_SET_SURFACE) { LayerCtrl* lc; logd("process message XPLAYER_COMMAND_SET_SURFACE."); //* set native window before delete the old one. //* because the player's render thread may use the old surface //* before it receive the new surface. lc = (LayerCtrl*)msg.params[2]; ret = PlayerSetWindow(mPriData->mPlayer, lc); if(ret == 0) { if(pReplyValue != NULL) *pReplyValue = (int)0; } else { loge("PlayerSetWindow() return fail."); if(pReplyValue != NULL) *pReplyValue = (int)-1; } if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end XPLAYER_COMMAND_SET_SURFACE. else if(msg.messageId == XPLAYER_COMMAND_SET_AUDIOSINK) { void* audioSink; logv("process message XPLAYER_COMMAND_SET_AUDIOSINK."); audioSink = (SoundCtrl*)msg.params[2]; PlayerSetAudioSink(mPriData->mPlayer, audioSink); //* super class MediaPlayerInterface has mAudioSink. //MediaPlayerInterface::setAudioSink(audioSink); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end XPLAYER_COMMAND_SET_AUDIOSINK. else if (msg.messageId == XPLAYER_COMMAND_SET_PLAYRATE) { XAudioPlaybackRate *rate; rate = (XAudioPlaybackRate*)msg.params[2]; //do set playback setting in playback int ret = PlayerSetPlayBackSettings(mPriData->mPlayer,rate); if(pReplyValue != NULL) *pReplyValue = ret; if(pReplySem != NULL) sem_post(pReplySem); continue; }//* end XPLAYER_COMMAND_SET_PLAYRATE. else if(msg.messageId == XPLAYER_COMMAND_SET_SUBCTRL) { SubCtrl* subctrl; logd("==== process message XPLAYER_COMMAND_SET_SUBCTRL."); subctrl = (SubCtrl*)msg.params[2]; PlayerSetSubCtrl(mPriData->mPlayer, subctrl); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end XPLAYER_COMMAND_SET_SUBCTRL. else if(msg.messageId == XPLAYER_COMMAND_SET_DI) { Deinterlace* di; logd("==== process message XPLAYER_COMMAND_SET_SUBCTRL."); di = (Deinterlace*)msg.params[2]; PlayerSetDeinterlace(mPriData->mPlayer, di); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end XPLAYER_COMMAND_SET_DI else if(msg.messageId == XPLAYER_COMMAND_PREPARE) { logd("process message XPLAYER_COMMAND_PREPARE. mPriData->mStatus: %d", mPriData->mStatus); if(mPriData->mStatus == XPLAYER_STATUS_PREPARED) { //* for data source set by fd(file descriptor), the prepare() method //* is called in setDataSource(), so the player is already in PREPARED //* status, here we just notify a prepared message. //* when app call prepareAsync(), we callback video-size to app here, if(mPriData->mMediaInfo->pVideoStreamInfo != NULL) { if(mPriData->mMediaInfo->pVideoStreamInfo->nWidth != mPriData->mVideoSizeWidth && mPriData->mMediaInfo->pVideoStreamInfo->nHeight != mPriData->mVideoSizeHeight) { int nRotation; nRotation = atoi((const char*)mPriData->mMediaInfo->cRotation); if((nRotation%180)==0)//* when the rotation is 0 and 180 { mPriData->mVideoSizeWidth = mPriData->mMediaInfo->pVideoStreamInfo->nWidth; mPriData->mVideoSizeHeight = mPriData->mMediaInfo->pVideoStreamInfo->nHeight; } else { //* when the rotation is 90 and 270, //* we should exchange nHeight and nwidth mPriData->mVideoSizeWidth = mPriData->mMediaInfo->pVideoStreamInfo->nHeight; mPriData->mVideoSizeHeight = mPriData->mMediaInfo->pVideoStreamInfo->nWidth; } logi("xxxxxxxxxx video size: width = %d, height = %d", mPriData->mVideoSizeWidth, mPriData->mVideoSizeHeight); int size[2]; size[0] = mPriData->mVideoSizeWidth; size[1] = mPriData->mVideoSizeHeight; mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SET_VIDEO_SIZE, 0, size); } } mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_PREPARED, 0, 0); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mPriData->mStatus != XPLAYER_STATUS_INITIALIZED && mPriData->mStatus != XPLAYER_STATUS_STOPPED) { logd("invalid prepareAsync() call, player not in initialized or stopped status."); if(pReplyValue != NULL) *pReplyValue = (int)-1; if(pReplySem != NULL) sem_post(pReplySem); continue; } mPriData->mStatus = XPLAYER_STATUS_PREPARING; mPriData->mPrepareSync = msg.params[2]; ret = DemuxCompPrepareAsync(mPriData->mDemux); if(ret != 0) { loge("DemuxCompPrepareAsync return fail immediately."); mPriData->mStatus = XPLAYER_STATUS_IDLE; if(pReplyValue != NULL) *pReplyValue = (int)-1; } else { if(pReplyValue != NULL) *pReplyValue = (int)0; } if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end XPLAYER_COMMAND_PREPARE. else if(msg.messageId == XPLAYER_COMMAND_SETSPEED) { logd("process message XPLAYER_COMMAND_SETSPEED."); if(mPriData->mStatus != XPLAYER_STATUS_PREPARED && mPriData->mStatus != XPLAYER_STATUS_STARTED ) { logd("invalid start() call, player not in prepared, \ started, paused or complete status."); if(pReplyValue != NULL) *pReplyValue = (int)-1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mPriData->mMediaInfo == NULL || mPriData->mMediaInfo->bSeekable == 0) { if(mPriData->mMediaInfo == NULL) { loge("setspeed fail because mMediaInfo == NULL."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } else { loge("media not seekable. cannot fast"); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } } if(mPriData->mSeeking) { DemuxCompCancelSeek(mPriData->mDemux); mPriData->mSeeking = 0; } //* protect mSeeking and mSeekTime from being changed by the seek finish callback. pthread_mutex_lock(&mPriData->mMutexStatus); int curTime; XPlayerGetCurrentPosition(mPriData, &curTime); mPriData->mSeeking = 1; mPriData->mbFast = 1; mPriData->mFastTime = curTime + mPriData->mSpeed*1000; mPriData->mSeekTime = mPriData->mFastTime; mPriData->mSeekSync = 0; pthread_mutex_unlock(&mPriData->mMutexStatus); //PlayerFastForeward(mPriData->mPlayer); PlayerSetDiscardAudio(mPriData->mPlayer, 1); if(PlayerGetStatus(mPriData->mPlayer) == PLAYER_STATUS_STOPPED) { //* if in prepared status, the player is in stopped status, //* this will make the player not record the nSeekTime at PlayerReset() operation //* called at seek finish callback. PlayerStart(mPriData->mPlayer,0,0); } PlayerPause(mPriData->mPlayer); DemuxCompSeekTo(mPriData->mDemux, mPriData->mSeekTime, mPriData->mSeekModeType); if (mPriData->mSpeedThreadRuning == 0) { int ret = pthread_create(&mPriData->mSpeedThreadId, NULL, sendSpeedCmdThread, (void*)mPriData ); if (ret != 0) { loge("create sendSpeedCmdThread error!"); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } } if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; }//* end XPLAYER_COMMAND_SETSPEED. else if(msg.messageId == XPLAYER_COMMAND_START) { logd("process message XPLAYER_COMMAND_START."); mPriData->gain = *(float*)msg.params[2]; //WK_PRINT("gain_ihw orginal:%f",gain_ihw); if(mPriData->gain > 0){ } else { mPriData->gain = DEFAULT_GAIN_IHW; } if(mPriData->mStatus != XPLAYER_STATUS_PREPARED && mPriData->mStatus != XPLAYER_STATUS_STARTED && mPriData->mStatus != XPLAYER_STATUS_PAUSED && mPriData->mStatus != XPLAYER_STATUS_COMPLETE) { logd("invalid start() call, player not in prepared, \ started, paused or complete status."); if(pReplyValue != NULL) *pReplyValue = (int)-1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mPriData->mbFast == 1) { PlayerSetDiscardAudio(mPriData->mPlayer, 0); mPriData->mbFast = 0; mPriData->mSpeed = 1; mPriData->mFastTime = 0; } //* synchronize with the seek or complete callback and the status may be changed. pthread_mutex_lock(&mPriData->mMutexStatus); if(mPriData->mStatus == XPLAYER_STATUS_STARTED) { if(PlayerGetStatus(mPriData->mPlayer) == PLAYER_STATUS_PAUSED && mPriData->mSeeking == 0) { //* player is paused for buffering, start it. //* see XPLAYER_MESSAGE_DEMUX_PAUSE_PLAYER callback message. PlayerStart(mPriData->mPlayer,0,0); DemuxCompStart(mPriData->mDemux); } pthread_mutex_unlock(&mPriData->mMutexStatus); logv("player already in started status."); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mPriData->mSeeking) { //* player and demux will be started at the seek callback. mPriData->mStatus = XPLAYER_STATUS_STARTED; pthread_mutex_unlock(&mPriData->mMutexStatus); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* for complete status, we seek to the begin of the file. if(mPriData->mStatus == XPLAYER_STATUS_COMPLETE) { AwMessage newMsg; if(mPriData->mMediaInfo->bSeekable) { if(mPriData->mSeeking) { DemuxCompCancelSeek(mPriData->mDemux); mPriData->mSeeking = 0; } mPriData->mSeeking = 1; mPriData->mSeekTime = 0; mPriData->mSeekSync = 0; mPriData->mSeekByInner = 1; logd("seekTo %.2f secs", mPriData->mSeekTime / 1E3); PlayerPause(mPriData->mPlayer); DemuxCompSeekTo(mPriData->mDemux, mPriData->mSeekTime, mPriData->mSeekModeType); //* player and demux will be started at the seek callback. mPriData->mStatus = XPLAYER_STATUS_STARTED; pthread_mutex_unlock(&mPriData->mMutexStatus); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else { //* post a stop message. memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = XPLAYER_COMMAND_STOP; AwMessageQueuePostMessage(mPriData->mMessageQueue, &newMsg); //* post a prepare message. memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = XPLAYER_COMMAND_PREPARE; newMsg.params[0] = 0; newMsg.params[1] = 0; newMsg.params[2] = 1; //* params[2] = mPrepareSync. AwMessageQueuePostMessage(mPriData->mMessageQueue, &newMsg); //* post a start message. memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = XPLAYER_COMMAND_START; newMsg.params[0] = 0; newMsg.params[1] = 0; newMsg.params[2] = (unsigned int)&mPriData->gain; AwMessageQueuePostMessage(mPriData->mMessageQueue, &newMsg); //* should I reply 0 to the user at this moment? //* or just set the semaphore and reply variable to the start message to //* make it reply when start message done? pthread_mutex_unlock(&mPriData->mMutexStatus); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } } pthread_mutex_unlock(&mPriData->mMutexStatus); mPriData->mPlayer = PlayerGainSet(mPriData->mPlayer, mPriData->gain); if(mPriData->mApplicationType == APP_STREAMING) { PlayerFast(mPriData->mPlayer, 0); } else { PlayerStart(mPriData->mPlayer,0,0); } DemuxCompStart(mPriData->mDemux); mPriData->mStatus = XPLAYER_STATUS_STARTED; if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end XPLAYER_COMMAND_START. else if(msg.messageId == XPLAYER_COMMAND_STOP) { logv("process message XPLAYER_COMMAND_STOP."); if(mPriData->mStatus != XPLAYER_STATUS_PREPARED && mPriData->mStatus != XPLAYER_STATUS_STARTED && mPriData->mStatus != XPLAYER_STATUS_PAUSED && mPriData->mStatus != XPLAYER_STATUS_COMPLETE && mPriData->mStatus != XPLAYER_STATUS_STOPPED) { logd("invalid stop() call, player not in prepared, paused, \ started, stopped or complete status."); if(pReplyValue != NULL) *pReplyValue = (int)-1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mPriData->mStatus == XPLAYER_STATUS_STOPPED) { logv("player already in stopped status."); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* the prepare callback may happen at this moment. //* so the mStatus may be changed to PREPARED asynchronizely. if(mPriData->mStatus == XPLAYER_STATUS_PREPARING) { logw("stop() called at preparing status, cancel demux prepare."); DemuxCompCancelPrepare(mPriData->mDemux); } if(mPriData->mSeeking) { DemuxCompCancelSeek(mPriData->mDemux); mPriData->mSeeking = 0; } DemuxCompStop(mPriData->mDemux); PlayerStop(mPriData->mPlayer); PlayerClear(mPriData->mPlayer); //* clear all media information in player. //*clear the mSubtitleDisplayIds memset(mPriData->mSubtitleDisplayIds,0xff,64*sizeof(unsigned int)); mPriData->mSubtitleDisplayIdsUpdateIndex = 0; mPriData->mStatus = XPLAYER_STATUS_STOPPED; if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end XPLAYER_COMMAND_STOP. else if(msg.messageId == XPLAYER_COMMAND_PAUSE) { logv("process message XPLAYER_COMMAND_PAUSE."); if(mPriData->mStatus != XPLAYER_STATUS_STARTED && mPriData->mStatus != XPLAYER_STATUS_PAUSED && mPriData->mStatus != XPLAYER_STATUS_COMPLETE) { logd("invalid pause() call, player not in started, paused or complete status."); if(pReplyValue != NULL) *pReplyValue = (int)-1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mPriData->mStatus == XPLAYER_STATUS_PAUSED) { logv("player already in paused or complete status."); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* sync with the seek, complete or pause_player/resume_player call back. pthread_mutex_lock(&mPriData->mMutexStatus); if(mPriData->mSeeking) { //* player and demux will be paused at the seek callback. mPriData->mStatus = XPLAYER_STATUS_PAUSED; pthread_mutex_unlock(&mPriData->mMutexStatus); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } PlayerPause(mPriData->mPlayer); mPriData->mStatus = XPLAYER_STATUS_PAUSED; //* sync with the seek, complete or pause_player/resume_player call back. pthread_mutex_unlock(&mPriData->mMutexStatus); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end XPLAYER_COMMAND_PAUSE. else if(msg.messageId == XPLAYER_COMMAND_RESET) { logv("process message XPLAYER_COMMAND_RESET."); //* the prepare callback may happen at this moment. //* so the mStatus may be changed to PREPARED asynchronizely. if(mPriData->mStatus == XPLAYER_STATUS_PREPARING) { logw("reset() called at preparing status, cancel demux prepare."); DemuxCompCancelPrepare(mPriData->mDemux); } if(mPriData->mSeeking) { DemuxCompCancelSeek(mPriData->mDemux); mPriData->mSeeking = 0; } //* stop and clear the demux. //* this will stop the seeking if demux is currently processing seeking message. //* it will clear the data source keep inside, this is important for the IStreamSource. DemuxCompStop(mPriData->mDemux); DemuxCompClear(mPriData->mDemux); //* stop and clear the player. PlayerStop(mPriData->mPlayer); PlayerClear(mPriData->mPlayer); //* it will clear media info config to the player. //* clear suface. if(mPriData->mKeepLastFrame == 0) { } //* clear data source. if(mPriData->mSourceUrl != NULL) { free(mPriData->mSourceUrl); mPriData->mSourceUrl = NULL; } if(mPriData->mSourceFd != -1) { close(mPriData->mSourceFd); mPriData->mSourceFd = -1; mPriData->mSourceFdOffset = 0; mPriData->mSourceFdLength = 0; } mPriData->mSourceStream = NULL; //* clear media info. clearMediaInfo(mPriData); //* clear loop setting. mPriData->mLoop = 0; //* clear the mSubtitleDisplayIds memset(mPriData->mSubtitleDisplayIds,0xff,64*sizeof(unsigned int)); mPriData->mSubtitleDisplayIdsUpdateIndex = 0; //* set status to IDLE. mPriData->mStatus = XPLAYER_STATUS_IDLE; if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else if(msg.messageId == XPLAYER_COMMAND_SEEK) { logd("process message XPLAYER_COMMAND_SEEK."); if(mPriData->mStatus != XPLAYER_STATUS_PREPARED && mPriData->mStatus != XPLAYER_STATUS_STARTED && mPriData->mStatus != XPLAYER_STATUS_PAUSED && mPriData->mStatus != XPLAYER_STATUS_COMPLETE) { logd("invalid seekTo() call, player not in prepared, \ started, paused or complete status."); if(pReplyValue != NULL) *pReplyValue = (int)-1; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* the application will call seekTo() when player is in complete status. //* after seekTo(), the player should still stay on complete status until //* application call start(). //* cts test requires this implement. if(mPriData->mStatus == XPLAYER_STATUS_COMPLETE) { //* protect mSeeking and mSeekTime from being changed by the seek finish callback. pthread_mutex_lock(&mPriData->mMutexStatus); mPriData->mSeekTime = msg.params[2]; pthread_mutex_unlock(&mPriData->mMutexStatus); mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SEEK_COMPLETE, 0, 0); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mPriData->mMediaInfo == NULL || mPriData->mMediaInfo->bSeekable == 0) { if(mPriData->mMediaInfo == NULL) { loge("seekTo fail because mMediaInfo == NULL."); mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SEEK_COMPLETE, 0, 0); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } else { loge("media not seekable."); mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SEEK_COMPLETE, 0, 0); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } } if(mPriData->mSeeking) { DemuxCompCancelSeek(mPriData->mDemux); mPriData->mSeeking = 0; } //* protect mSeeking and mSeekTime from being changed by the seek finish callback. pthread_mutex_lock(&mPriData->mMutexStatus); mPriData->mSeeking = 1; mPriData->mSeekTime = msg.params[2]; mPriData->mSeekSync = msg.params[3]; logv("seekTo %.2f secs", mPriData->mSeekTime / 1E3); pthread_mutex_unlock(&mPriData->mMutexStatus); if(PlayerGetStatus(mPriData->mPlayer) == PLAYER_STATUS_STOPPED) { //* if in prepared status, the player is in stopped status, //* this will make the player not record the nSeekTime at PlayerReset() operation //* called at seek finish callback. PlayerStart(mPriData->mPlayer,0,0); } PlayerPause(mPriData->mPlayer); DemuxCompSeekTo(mPriData->mDemux, mPriData->mSeekTime, mPriData->mSeekModeType); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end XPLAYER_COMMAND_SEEK. else if(msg.messageId == XPLAYER_COMMAND_RESETURL) { logd("process message XPLAYER_COMMAND_RESETURL."); if(mPriData->mStatus != XPLAYER_STATUS_PREPARED && mPriData->mStatus != XPLAYER_STATUS_STARTED && mPriData->mStatus != XPLAYER_STATUS_PAUSED && mPriData->mStatus != XPLAYER_STATUS_COMPLETE) { logd("invalid seekTo() call, player not in prepared, started, \ paused or complete status."); if(pReplyValue != NULL) *pReplyValue = (int)-1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mPriData->mSeeking) { DemuxCompCancelSeek(mPriData->mDemux); mPriData->mSeeking = 0; } //* protect mSeeking and mSeekTime from being changed by the seek finish callback. pthread_mutex_lock(&mPriData->mMutexStatus); mPriData->mSeeking = 1; mPriData->mSeekTime = msg.params[2]; mPriData->mSeekSync = msg.params[3]; logd("seekTo %.2f secs", mPriData->mSeekTime / 1E3); pthread_mutex_unlock(&mPriData->mMutexStatus); if(PlayerGetStatus(mPriData->mPlayer) == PLAYER_STATUS_STOPPED) { //* if in prepared status, the player is in stopped status, //* this will make the player not record the nSeekTime at PlayerReset() operation //* called at seek finish callback. PlayerStart(mPriData->mPlayer,0,0); } PlayerPause(mPriData->mPlayer); DemuxCompSeekToResetUrl(mPriData->mDemux, mPriData->mSeekTime); if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else if(msg.messageId == XPLAYER_COMMAND_QUIT) { if(pReplyValue != NULL) *pReplyValue = (int)0; if(pReplySem != NULL) sem_post(pReplySem); break; //* break the thread. } //* end XPLAYER_COMMAND_QUIT. else { logw("unknow message with id %d, ignore.", msg.messageId); } } return 0; } int XPlayerSwitchSubtitle(XPlayer* p, int nStreamIndex) { PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mPlayer == NULL) { return -1; } return PlayerSwitchSubtitle(mPriData->mPlayer, nStreamIndex); } int XPlayerSwitchAudio(XPlayer* p, int nStreamIndex) { PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mPlayer == NULL) { return -1; } return PlayerSwitchAudio(mPriData->mPlayer, nStreamIndex); } static int setSubtitleStream(PlayerContext* mPriData, int nStreamNum, SubtitleStreamInfo* pNewStreamInfo) { int i; SubtitleStreamInfo* pStreamInfo; if(pNewStreamInfo == NULL || nStreamNum == 0) { loge("PlayerProbeSubtitleStreamInfo failed!"); return -1; } pthread_mutex_lock(&mPriData->mMutexMediaInfo); //* set reference video size. if(mPriData->mMediaInfo->pVideoStreamInfo != NULL) { for(i=0; imMediaInfo->pVideoStreamInfo->nFrameRate; if(pNewStreamInfo[i].nReferenceVideoHeight == 0 || pNewStreamInfo[i].nReferenceVideoWidth == 0) { pNewStreamInfo[i].nReferenceVideoHeight = mPriData->mMediaInfo->pVideoStreamInfo->nHeight; pNewStreamInfo[i].nReferenceVideoWidth = mPriData->mMediaInfo->pVideoStreamInfo->nWidth; } } } //* add stream info to the mMediaInfo, //* put external subtitle streams ahead of the embedded subtitle streams. if(mPriData->mMediaInfo->pSubtitleStreamInfo == NULL) { mPriData->mMediaInfo->pSubtitleStreamInfo = pNewStreamInfo; mPriData->mMediaInfo->nSubtitleStreamNum = nStreamNum; pNewStreamInfo = NULL; nStreamNum = 0; } else { pStreamInfo = (SubtitleStreamInfo*)malloc(sizeof(SubtitleStreamInfo)* (mPriData->mMediaInfo->nSubtitleStreamNum + nStreamNum)); if(pStreamInfo == NULL) { loge("invode::INVOKE_ID_ADD_EXTERNAL_SOURCE fail, can not malloc memory."); for(i=0; i= 0) { close(pNewStreamInfo[i].fd); pNewStreamInfo[i].fd = -1; } if(pNewStreamInfo[i].fdSub >= 0) { close(pNewStreamInfo[i].fdSub); pNewStreamInfo[i].fdSub = -1; } } free(pNewStreamInfo); pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return -1; } //* make the internal subtitle in front of external subtitle memcpy(&pStreamInfo[mPriData->mMediaInfo->nSubtitleStreamNum], pNewStreamInfo, sizeof(SubtitleStreamInfo)*nStreamNum); memcpy(pStreamInfo, mPriData->mMediaInfo->pSubtitleStreamInfo, sizeof(SubtitleStreamInfo)*mPriData->mMediaInfo->nSubtitleStreamNum); free(mPriData->mMediaInfo->pSubtitleStreamInfo); mPriData->mMediaInfo->pSubtitleStreamInfo = pStreamInfo; mPriData->mMediaInfo->nSubtitleStreamNum += nStreamNum; free(pNewStreamInfo); pNewStreamInfo = NULL; nStreamNum = 0; } //* re-arrange the stream index. for(i=0; imMediaInfo->nSubtitleStreamNum; i++) mPriData->mMediaInfo->pSubtitleStreamInfo[i].nStreamIndex = i; //* set subtitle stream info to player again. //* here mMediaInfo != NULL, so initializePlayer() had been called. if(mPriData->mPlayer != NULL) { int nDefaultSubtitleIndex = -1; for(i=0; imMediaInfo->nSubtitleStreamNum; i++) { if(PlayerCanSupportSubtitleStream(mPriData->mPlayer, &mPriData->mMediaInfo->pSubtitleStreamInfo[i])) { nDefaultSubtitleIndex = i; break; } } if(nDefaultSubtitleIndex < 0) { logw("no subtitle stream supported."); nDefaultSubtitleIndex = 0; } if(0 != PlayerSetSubtitleStreamInfo(mPriData->mPlayer, mPriData->mMediaInfo->pSubtitleStreamInfo, mPriData->mMediaInfo->nSubtitleStreamNum, nDefaultSubtitleIndex)) { logw("PlayerSetSubtitleStreamInfo() fail, subtitle stream not supported."); } } pthread_mutex_unlock(&mPriData->mMutexMediaInfo); return 0; } int XPlayerSetExternalSubUrl(XPlayer* p, const char* fileName) { PlayerContext* mPriData = (PlayerContext*)p; SubtitleStreamInfo* pStreamInfo; SubtitleStreamInfo* pNewStreamInfo = NULL; int nStreamNum = 0; int i; if(mPriData->mStatus != XPLAYER_STATUS_PREPARED || mPriData->mMediaInfo == NULL) { loge("can not add external text source, player not in prepared status \ or there is no media stream."); return -1; } //* probe subtitle info PlayerProbeSubtitleStreamInfo(fileName, &nStreamNum, &pNewStreamInfo); if(pNewStreamInfo == NULL || nStreamNum == 0) { loge("PlayerProbeSubtitleStreamInfo failed!"); return -1; } return setSubtitleStream(mPriData, nStreamNum, pNewStreamInfo); } int XPlayerSetExternalSubFd(XPlayer* p, int fd, int64_t offset, int64_t len, int fdSub) { PlayerContext* mPriData = (PlayerContext*)p; SubtitleStreamInfo* pStreamInfo; SubtitleStreamInfo* pNewStreamInfo = NULL; int nStreamNum = 0; int i; if(mPriData->mStatus != XPLAYER_STATUS_PREPARED || mPriData->mMediaInfo == NULL) { loge("can not add external text source, player not in prepared status \ or there is no media stream."); return -1; } //* probe subtitle info PlayerProbeSubtitleStreamInfoFd(fd, offset, len, &nStreamNum, &pNewStreamInfo); if(nStreamNum > 0 && pNewStreamInfo[0].eCodecFormat == SUBTITLE_CODEC_IDXSUB) { if(fdSub >= 0) { //* for index+sub subtitle, //* we set the .sub file's descriptor to pNewStreamInfo[i].fdSub. for(i=0; i= 0) { close(pNewStreamInfo[i].fd); pNewStreamInfo[i].fd = -1; } } free(pNewStreamInfo); pNewStreamInfo = NULL; nStreamNum = 0; } } //* fdSub is the file descriptor of .sub file of a index+sub subtitle. if(fdSub >= 0) { //* close the file descriptor of .idx file, we dup it when //* INVOKE_ID_ADD_EXTERNAL_SOURCE_FD is called to set the .idx file if(fd >= 0) close(fd); } if(pNewStreamInfo == NULL || nStreamNum == 0) { loge("PlayerProbeSubtitleStreamInfo failed!"); return -1; } return setSubtitleStream(mPriData, nStreamNum, pNewStreamInfo); } int XPlayerSetSubCharset(XPlayer* p, const char* strFormat) { PlayerContext* mPriData = (PlayerContext*)p; if(strFormat != NULL && (strlen(strFormat) < 31)) { strcpy(mPriData->mDefaultTextFormat, strFormat); } else { strcpy(mPriData->mDefaultTextFormat, "UTF-8"); } return 0; } int XPlayerGetSubCharset(XPlayer* p, char *charset) { PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mPlayer == NULL) { return -1; } strcpy(charset, mPriData->mDefaultTextFormat); return 0; } int XPlayerSetSubDelay(XPlayer* p, int nTimeMs) { PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mPlayer!=NULL) return PlayerSetSubtitleShowTimeAdjustment(mPriData->mPlayer, nTimeMs); else return -1; } int XPlayerGetSubDelay(XPlayer* p) { PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mPlayer!=NULL) return PlayerGetSubtitleShowTimeAdjustment(mPriData->mPlayer); else return -1; } int XPlayerGetBitrate(XPlayer* p) { PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mPlayer!=NULL) return PlayerGetVideoBitrate(mPriData->mPlayer) + PlayerGetAudioBitrate(mPriData->mPlayer); else return -1; } int XPlayerGetCacheSize(XPlayer* p) { PlayerContext* mPriData = (PlayerContext*)p; if(mPriData->mPlayer!=NULL) return DemuxCompGetCacheSize(mPriData->mDemux); else return -1; } int XPlayerSetBufferingSettings(XPlayer* p, AwBufferingSettings* pBuffering) { if(pBuffering == NULL) { return -1; } PlayerContext* mPriData = (PlayerContext*)p; mPriData->pBuffering = pBuffering; DemuxCompSetBufferingSettings(mPriData->mDemux, mPriData->pBuffering); return 0; } int XPlayerGetBufferingSettings(XPlayer* p, AwBufferingSettings* pBuffering) { if(p == NULL || pBuffering == NULL) { logd("input is NULL. Please check it."); return -1; } PlayerContext* mPriData = (PlayerContext*)p; return DemuxCompGetBufferingSettings(mPriData->mDemux, pBuffering); } int XPlayerGetVideoDispFramerate(XPlayer* p,float* dispFramerate){ PlayerContext* mPriData = (PlayerContext*)p; return PlayerGetVideoDispFramerate(mPriData->mPlayer,dispFramerate); } int XPlayerSetHoldLastPicture(XPlayer* p,int bHoldFlag){ PlayerContext* mPriData = (PlayerContext*)p; return PlayerSetHoldLastPicture(mPriData->mPlayer,bHoldFlag); } static int callbackProcess(void *p, int messageId, void* param) { PlayerContext* mPriData = (PlayerContext*)p; switch(messageId) { case DEMUX_NOTIFY_PREPARED: { uintptr_t tmpPtr = (uintptr_t)param; int err = tmpPtr; if(err != 0) { //* demux prepare return fail. //* notify a media error event. mPriData->mStatus = XPLAYER_STATUS_ERROR; if(mPriData->mPrepareSync == 0) { if(err == DEMUX_ERROR_IO) { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_ERROR, AW_MEDIA_ERROR_IO, 0); } else if(err == DEMUX_ERROR_USER_CANCEL) { logd("preparing canceled by user"); } else { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_ERROR, AW_MEDIA_ERROR_UNKNOWN, 0); } } else { if(err == DEMUX_ERROR_IO) mPriData->mPrepareFinishResult = AW_MEDIA_ERROR_IO; else mPriData->mPrepareFinishResult = -1; sem_post(&mPriData->mSemPrepareFinish); } } else { //* demux prepare success, initialize the player. if(initializePlayer(p) == 0) { //* initialize player success, notify a prepared event. mPriData->mStatus = XPLAYER_STATUS_PREPARED; if(mPriData->mPrepareSync == 0) { if(mPriData->mMediaInfo->pVideoStreamInfo!=NULL) { if(mPriData->mMediaInfo->pVideoStreamInfo->nWidth != mPriData->mVideoSizeWidth && mPriData->mMediaInfo->pVideoStreamInfo->nHeight != mPriData->mVideoSizeHeight) { int nRotation; nRotation = atoi((const char*)mPriData->mMediaInfo->cRotation); if((nRotation%180)==0)//* when the rotation is 0 and 180 { mPriData->mVideoSizeWidth = mPriData->mMediaInfo->pVideoStreamInfo->nWidth; mPriData->mVideoSizeHeight = mPriData->mMediaInfo->pVideoStreamInfo->nHeight; } else { //* when the rotation is 90 and 270, //* we should exchange nHeight and nwidth mPriData->mVideoSizeWidth = mPriData->mMediaInfo->pVideoStreamInfo->nHeight; mPriData->mVideoSizeHeight = mPriData->mMediaInfo->pVideoStreamInfo->nWidth; } logi("xxxxxxxxxx video size: width = %d, height = %d", mPriData->mVideoSizeWidth, mPriData->mVideoSizeHeight); int size[2]; size[0] = mPriData->mVideoSizeWidth; size[1] = mPriData->mVideoSizeHeight; mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SET_VIDEO_SIZE, 0, size); } else { //此时mVideoSizeWidth必是0 logi("xxxxxxxxxx video size: width = 0, height = 0"); mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SET_VIDEO_SIZE, 0, (int []){0, 0}); } } else { //此时mVideoSizeWidth必是0 logi("xxxxxxxxxx video size: width = 0, height = 0"); mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SET_VIDEO_SIZE, 0, (int []){0, 0}); } mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_PREPARED, 0, 0); } else { mPriData->mPrepareFinishResult = 0; sem_post(&mPriData->mSemPrepareFinish); } } else { //* initialize player fail, notify a media error event. mPriData->mStatus = XPLAYER_STATUS_ERROR; if(mPriData->mPrepareSync == 0) mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_ERROR, DEMUX_ERROR_UNKNOWN, 0); else { mPriData->mPrepareFinishResult = -1; sem_post(&mPriData->mSemPrepareFinish); } } } break; } case DEMUX_NOTIFY_EOS: { if(mPriData->mLivemode == 2) //* timeshift { int seekTimeMs = 0; struct timeval tv; gettimeofday(&tv, NULL); mPriData->mCurShiftTimeStamp = tv.tv_sec * 1000000ll + tv.tv_usec; logd("mCurShiftTimeStamp=%" PRId64 " ms", mPriData->mCurShiftTimeStamp/1000); seekTimeMs = (mPriData->mCurShiftTimeStamp - mPriData->mShiftTimeStamp)/1000 - mPriData->mTimeShiftDuration + mPriData->mPreSeekTimeMs; logd("===== seekTimeMs=%d ms, mPriData->mCurShiftTimeStamp=%" PRId64 " us," "mPriData->mShiftTimeStamp=%" PRId64 " us," " mPriData->mTimeShiftDuration=%" PRId64 " ms, mPreSeekTimeMs=%d ms", seekTimeMs, mPriData->mCurShiftTimeStamp, mPriData->mShiftTimeStamp, mPriData->mTimeShiftDuration, mPriData->mPreSeekTimeMs); XPlayerSeekTo(p, seekTimeMs, mPriData->mSeekModeType); } else { logw("eos..."); PlayerSetEos(mPriData->mPlayer); } break; } case DEMUX_NOTIFY_IOERROR: { loge("io error..."); //* should we report a MEDIA_INFO event of "MEDIA_INFO_NETWORK_ERROR" and //* try reconnect for sometimes before a MEDIA_ERROR_IO event reported ? mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_ERROR, AW_MEDIA_ERROR_IO, 0); break; } case DEMUX_NOTIFY_CACHE_STAT: { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_BUFFERING_UPDATE, 0, param); break; } case DEMUX_NOTIFY_BUFFER_START: { logd("MEDIA_INFO_BUFFERING_START,mDisplayRatio = %d",mPriData->mDisplayRatio); // for live mode 1, getposition when network broken mPriData->mDemuxNotifyPause = 1; struct timeval tv; gettimeofday(&tv, NULL); mPriData->mDemuxPauseTimeStamp = tv.tv_sec * 1000000ll + tv.tv_usec; mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_INFO, AW_MEDIA_INFO_BUFFERING_START, param); break; } case DEMUX_NOTIFY_BUFFER_END: { logd("MEDIA_INFO_BUFFERING_END ..."); mPriData->mDemuxNotifyPause = 0; mPriData->mDemuxPauseTimeStamp = 0; mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_INFO, AW_MEDIA_INFO_BUFFERING_END, 0); break; } case DEMUX_NOTIFY_PAUSE_PLAYER: { logd("XPLAYER_MESSAGE_DEMUX_PAUSE_PLAYER, mStatus = %d", mPriData->mStatus); //* be careful to check whether there is any player callback lock the mMutexStatus, //* if so, the PlayerPause() call may fall into dead lock if the player //* callback is requesting mMutexStatus. //* currently we do not lock mMutexStatus in any player callback. pthread_mutex_lock(&mPriData->mMutexStatus); if(mPriData->mStatus == XPLAYER_STATUS_STARTED) PlayerPause(mPriData->mPlayer); pthread_mutex_unlock(&mPriData->mMutexStatus); break; } case DEMUX_NOTIFY_RESUME_PLAYER: { //* be careful to check whether there is any player callback lock the mMutexStatus, //* if so, the PlayerPause() call may fall into dead lock if the player //* callback is requesting mMutexStatus. //* currently we do not lock mMutexStatus in any player callback. pthread_mutex_lock(&mPriData->mMutexStatus); if (mPriData->mStatus == XPLAYER_STATUS_STARTED /*&& bBuffering == false*/) { PlayerStart(mPriData->mPlayer,0,0); } pthread_mutex_unlock(&mPriData->mMutexStatus); break; } #if !(defined(CONF_NEW_BDMV_STREAM)) case DEMUX_IOREQ_ACCESS: { mPriData->mCallback(mPriData->pUser, AWPLAYER_EXTEND_MEDIA_INFO, AW_EX_IOREQ_ACCESS, param); break; } case DEMUX_IOREQ_OPEN: { mPriData->mCallback(mPriData->pUser, AWPLAYER_EXTEND_MEDIA_INFO, AW_EX_IOREQ_OPEN, param); break; } case DEMUX_IOREQ_OPENDIR: { mPriData->mCallback(mPriData->pUser, AWPLAYER_EXTEND_MEDIA_INFO, AW_EX_IOREQ_OPENDIR, param); break; } case DEMUX_IOREQ_READDIR: { mPriData->mCallback(mPriData->pUser, AWPLAYER_EXTEND_MEDIA_INFO, AW_EX_IOREQ_READDIR, param); break; } case DEMUX_IOREQ_CLOSEDIR: { mPriData->mCallback(mPriData->pUser, AWPLAYER_EXTEND_MEDIA_INFO, AW_EX_IOREQ_CLOSEDIR, param); break; } #endif case PLAYBACK_NOTIFY_EOS: { mPriData->mStatus = XPLAYER_STATUS_COMPLETE; if(mPriData->mLoop == 0) { logd("player notify eos."); mPriData->mSeekTime = 0; //* clear the seek flag. mPriData->mbFast = 0; mPriData->mSpeed = 1; mPriData->mFastTime = 0; mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_PLAYBACK_COMPLETE, 0, 0); } else { AwMessage msg; logv("player notify eos, loop is set, send start command."); mPriData->mSeekTime = 0; //* seek to the file start and replay. //* send a start message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_START; AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); } break; } case PLAYBACK_NOTIFY_FIRST_PICTURE: { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_INFO, AW_MEDIA_INFO_RENDERING_START, 0); DemuxCompNotifyFirstFrameShowed(mPriData->mDemux); break; } case DEMUX_NOTIFY_RESET_PLAYER: { logd("DEMUX_NOTIFY_RESET_PLAYER"); mPriData->mLivemode = DemuxCompGetLiveMode(mPriData->mDemux); if(mPriData->mLivemode == 1) { mPriData->mSeekTime = 0; } pthread_mutex_lock(&mPriData->mMutexStatus); PlayerReset(mPriData->mPlayer, ((int64_t)mPriData->mSeekTime)*1000); pthread_mutex_unlock(&mPriData->mMutexStatus); break; } case DEMUX_NOTIFY_SEEK_FINISH: { logd("DEMUX_NOTIFY_SEEK_FINISH"); int seekResult; int nSeekTimeMs; int nFinalSeekTimeMs; //* be careful to check whether there is any player callback lock the mMutexStatus, //* if so, the PlayerPause() call may fall into dead lock if the player //* callback is requesting mMutexStatus. //* currently we do not lock mMutexStatus in any player callback. pthread_mutex_lock(&mPriData->mMutexStatus); seekResult = ((int*)param)[0]; nSeekTimeMs = ((int*)param)[1]; nFinalSeekTimeMs = ((int*)param)[2]; int bSeekClosest = 0; if(mPriData->mSeekModeType == AW_SEEK_CLOSEST) { bSeekClosest = 1; } if (seekResult == 0) { PlayerReset(mPriData->mPlayer, ((int64_t)nFinalSeekTimeMs)*1000); if (nSeekTimeMs == mPriData->mSeekTime) { mPriData->mSeeking = 0; if (mPriData->mStatus == XPLAYER_STATUS_STARTED) { PlayerStart(mPriData->mPlayer,bSeekClosest,nSeekTimeMs); DemuxCompStart(mPriData->mDemux); //check if seek call by xplayer inner case //inner case seek no need to callback app if (mPriData->mSeekByInner == 1) { mPriData->mSeekByInner = 0; pthread_mutex_unlock(&mPriData->mMutexStatus); break; } } } else { logv("seek time not match, there may be another seek operation happening."); } pthread_mutex_unlock(&mPriData->mMutexStatus); mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SEEK_COMPLETE, 0, 0); } else if(seekResult == DEMUX_ERROR_USER_CANCEL) { // do nothing , do not start player pthread_mutex_unlock(&mPriData->mMutexStatus); mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SEEK_COMPLETE, 0, 0); } else { pthread_mutex_unlock(&mPriData->mMutexStatus); mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_ERROR, AW_MEDIA_ERROR_IO, 0); } break; } case PLAYBACK_NOTIFY_SUBTITLE_ITEM_AVAILABLE: { //logd("subtitle available. id = %d, pSubtitleItem = %p",nSubtitleId,pSubtitleItem); break; } case PLAYBACK_NOTIFY_SUBTITLE_ITEM_EXPIRED: { logd("subtitle expired."); unsigned int nSubtitleId; int i; break; } case PLAYBACK_NOTIFY_VIDEO_SIZE: { int nWidth = ((int*)param)[0]; int nHeight = ((int*)param)[1]; logd("*************decoded nWidth = %d,nHeight = %d********",nWidth,nHeight); int decodedSize[2]; decodedSize[0] = nWidth; decodedSize[1] = nHeight; mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_DECODED_VIDEO_SIZE, 0, decodedSize); //* if had scale down, we should zoom the widht and height //* here the case of the VR9 we should not zoom the width and height if(mPriData->mScaledownFlag == 1 && mPriData->bSetScaleDown != 1) { nWidth = 2*nWidth; nHeight = 2*nHeight; } //if(nWidth != mVideoSizeWidth || nHeight != mVideoSizeHeight) { logi("xxxxxxxxxx video size : width = %d, height = %d", nWidth, nHeight); mPriData->mVideoSizeWidth = nWidth; mPriData->mVideoSizeHeight = nHeight; int size[2]; size[0] = mPriData->mVideoSizeWidth; size[1] = mPriData->mVideoSizeHeight; mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_SET_VIDEO_SIZE, 0, size); } break; } case PLAYBACK_NOTIFY_AUDIORAWPLAY: { break; } case PLAYBACK_NOTIFY_VIDEO_CROP: //* TODO break; case PLAYBACK_NOTIFY_VIDEO_UNSUPPORTED: if(mPriData->mMediaInfo->nVideoStreamNum < 2) { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_ERROR, AW_MEDIA_ERROR_UNSUPPORTED, 0); } break; case PLAYBACK_NOTIFY_AUDIO_UNSUPPORTED: if(mPriData->mMediaInfo->nVideoStreamNum == 0) { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_ERROR, AW_MEDIA_ERROR_UNSUPPORTED, 0); } //* TODO break; case PLAYBACK_NOTIFY_SUBTITLE_UNSUPPORTED: //* TODO break; case DEMUX_VIDEO_STREAM_CHANGE: logd("update video info"); int a_change = ((int*)param)[0]; if(!a_change) updateVideoInfo(p); else { updateVideoInfo(p); updateAudioInfo(p); } break; case DEMUX_AUDIO_STREAM_CHANGE: logd("update audio info"); updateAudioInfo(p); break; case PLAYBACK_NOTIFY_VIDEO_RENDER_FRAME: #if 0 if(mPriData->mbFast) { logd("==== video key frame in fast mode, mFastTime: %d, mSpeed: %d", mPriData->mFastTime, mPriData->mSpeed); if(mPriData->mSpeed == 0) { break; } int sleepTime = (mPriData->mSpeed > 0) ? 2000000/mPriData->mSpeed : (-2000000/mPriData->mSpeed); //usleep(sleepTime); mPriData->mFastTime += mPriData->mSpeed*1000; if(mPriData->mFastTime > 0 && mPriData->mbFast) { AwMessage msg; //* send a seek message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = XPLAYER_COMMAND_SEEK; msg.params[0] = (uintptr_t)&mPriData->mSemSeek; msg.params[1] = (uintptr_t)&mPriData->mSeekReply; msg.params[2] = mPriData->mFastTime; //* params[2] = mSeekTime. msg.params[3] = 0; //* params[3] = mSeekSync. AwMessageQueuePostMessage(mPriData->mMessageQueue, &msg); } else if(mPriData->mFastTime <= 0) { PlayerSetDiscardAudio(mPriData->mPlayer, 0); mPriData->mbFast = 0; } } #endif break; case DEMUX_NOTIFY_HLS_DOWNLOAD_START: { logd("--- hls message, DEMUX_NOTIFY_HLS_DOWNLOAD_START"); mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_INFO, AW_MEDIA_INFO_DOWNLOAD_START, param); break; } case DEMUX_NOTIFY_HLS_DOWNLOAD_END: { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_INFO, AW_MEDIA_INFO_DOWNLOAD_END, param); break; } case DEMUX_NOTIFY_HLS_DOWNLOAD_ERROR: { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_INFO, AW_MEDIA_INFO_DOWNLOAD_ERROR, param); break; } case DEMUX_NOTIFY_LOG_RECORDER: { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_LOG_RECORDER, 0, param); break; } case DEMUX_NOTIFY_TIMESHIFT_DURATION: { if(mPriData->mLivemode == 2) { mPriData->mTimeShiftDuration = *(cdx_int64*)param; logd("xxxxxxxxx mPriData->mTimeShiftDuration=%" PRId64 "", mPriData->mTimeShiftDuration); } break; } case DEMUX_NOTIFY_METADATA: { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_META_DATA, 0, param); break; } case PLAYBACK_RESET_BUFFER_FINISHED: { mPriData->mCallback(mPriData->pUser, AWPLAYER_MEDIA_RESET_BUFFER_FINISHED,0, NULL); break; } default: { logw("message 0x%x not handled.", messageId); break; } } return 0; }