/* * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. * All rights reserved. * * File : awplayer.cpp * Description : player * History : * */ #include "awplayer.h" #include "log.h" #include #include "cdx_config.h" #include "player.h" //* player library in "LIBRARY/PLAYER/" #include "mediaInfo.h" #include "demuxComponent.h" #include "awMessageQueue.h" #include "memoryAdapter.h" #include "AwPluginManager.h" //* player status. static const int AWPLAYER_STATUS_IDLE = 0; static const int AWPLAYER_STATUS_INITIALIZED = 1<<0; static const int AWPLAYER_STATUS_PREPARING = 1<<1; static const int AWPLAYER_STATUS_PREPARED = 1<<2; static const int AWPLAYER_STATUS_STARTED = 1<<3; static const int AWPLAYER_STATUS_PAUSED = 1<<4; static const int AWPLAYER_STATUS_STOPPED = 1<<5; static const int AWPLAYER_STATUS_COMPLETE = 1<<6; static const int AWPLAYER_STATUS_ERROR = 1<<7; //* callback message id. static const int AWPLAYER_MESSAGE_DEMUX_PREPARED = 0x101; static const int AWPLAYER_MESSAGE_DEMUX_EOS = 0x102; static const int AWPLAYER_MESSAGE_DEMUX_IOERROR = 0x103; static const int AWPLAYER_MESSAGE_DEMUX_SEEK_FINISH = 0x104; static const int AWPLAYER_MESSAGE_DEMUX_CACHE_REPORT = 0x105; static const int AWPLAYER_MESSAGE_DEMUX_BUFFER_START = 0x106; static const int AWPLAYER_MESSAGE_DEMUX_BUFFER_END = 0x107; static const int AWPLAYER_MESSAGE_DEMUX_PAUSE_PLAYER = 0x108; static const int AWPLAYER_MESSAGE_DEMUX_RESUME_PLAYER = 0x109; static const int AWPLAYER_MESSAGE_DEMUX_DATA_PACKET = 0x110; static const int AWPLAYER_MESSAGE_PLAYER_EOS = 0x201; static const int AWPLAYER_MESSAGE_PLAYER_FIRST_PICTURE = 0x202; static const int AWPLAYER_MESSAGE_PLAYER_SUBTITLE_AVAILABLE = 0x203; static const int AWPLAYER_MESSAGE_PLAYER_SUBTITLE_EXPIRED = 0x204; static const int AWPLAYER_MESSAGE_PLAYER_VIDEO_PIC_DATA = 0x205; static const int AWPLAYER_MESSAGE_PLAYER_AUDIO_PCM_DATA = 0x206; static const int AWPLAYER_MESSAGE_PLAYER_VIDEO_RENDER_FRAME = 0x20d; //* command id. static const int AWPLAYER_COMMAND_SET_SOURCE = 0x101; static const int AWPLAYER_COMMAND_SET_SURFACE = 0x102; static const int AWPLAYER_COMMAND_SET_AUDIOSINK = 0x103; static const int AWPLAYER_COMMAND_PREPARE = 0x104; static const int AWPLAYER_COMMAND_START = 0x105; static const int AWPLAYER_COMMAND_STOP = 0x106; static const int AWPLAYER_COMMAND_PAUSE = 0x107; static const int AWPLAYER_COMMAND_RESET = 0x108; static const int AWPLAYER_COMMAND_QUIT = 0x109; static const int AWPLAYER_COMMAND_SEEK = 0x10a; static const int AWPLAYER_COMMAND_SET_VOLUME = 0x10b; static const int AWPLAYER_COMMAND_SETSPEED = 0x10c; static void* AwPlayerThread(void* arg); static int DemuxCallbackProcess(void* pUserData, int eMessageId, void* param); static int PlayerCallbackProcess(void* pUserData, int eMessageId, void* param); static int transformPictureMb32ToRGB(VideoPicture* pPicture, unsigned char* pData, int nWidth, int nHeight); AwPlayer::AwPlayer() { logv("awplayer construct."); AwPluginInit(); mSourceUrl = NULL; mStatus = AWPLAYER_STATUS_IDLE; mSeeking = 0; mSeekSync = 0; mLoop = 0; mMediaInfo = NULL; mMessageQueue = NULL; mVolume = 0; pthread_mutex_init(&mMutex, NULL); sem_init(&mSemSetDataSource, 0, 0); sem_init(&mSemPrepare, 0, 0); sem_init(&mSemStart, 0, 0); sem_init(&mSemStop, 0, 0); sem_init(&mSemPause, 0, 0); sem_init(&mSemReset, 0, 0); sem_init(&mSemQuit, 0, 0); sem_init(&mSemSeek, 0, 0); sem_init(&mSemSetSpeed, 0, 0); sem_init(&mSemPrepareFinish, 0, 0); //* for signal prepare finish, used in prepare(). sem_init(&mSemSetVolume, 0, 0); mMessageQueue = AwMessageQueueCreate(64); mPlayer = PlayerCreate(); mDemux = DemuxCompCreate(); if(mPlayer != NULL) PlayerSetCallback(mPlayer, PlayerCallbackProcess, (void*)this); if(mDemux != NULL) { DemuxCompSetCallback(mDemux, DemuxCallbackProcess, (void*)this); DemuxCompSetPlayer(mDemux, mPlayer); } if(pthread_create(&mThreadId, NULL, AwPlayerThread, this) == 0) mThreadCreated = 1; else mThreadCreated = 0; mSpeed = 1; mbFast = 0; mFastTime = 0; } AwPlayer::~AwPlayer() { AwMessage msg; logv("~AwPlayer"); if(mThreadCreated) { void* status; reset(); //* stop demux and player. //* send a quit message to quit the main thread. setMessage(&msg, AWPLAYER_COMMAND_QUIT, (uintptr_t)&mSemQuit); AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemQuit, -1); pthread_join(mThreadId, &status); } if(mDemux != NULL) DemuxCompDestroy(mDemux); if(mPlayer != NULL) PlayerDestroy(mPlayer); if(mMessageQueue != NULL) AwMessageQueueDestroy(mMessageQueue); pthread_mutex_destroy(&mMutex); sem_destroy(&mSemSetDataSource); sem_destroy(&mSemPrepare); sem_destroy(&mSemStart); sem_destroy(&mSemStop); sem_destroy(&mSemPause); sem_destroy(&mSemReset); sem_destroy(&mSemQuit); sem_destroy(&mSemSeek); sem_destroy(&mSemSetSpeed); sem_destroy(&mSemPrepareFinish); sem_destroy(&mSemSetVolume); if(mMediaInfo != NULL) clearMediaInfo(); if(mSourceUrl != NULL) free(mSourceUrl); } int AwPlayer::initCheck() { logv("initCheck"); if(mPlayer == NULL || mDemux == NULL || mThreadCreated == 0 || mNotifier == NULL) { loge("initCheck() fail, AwPlayer::mplayer = %p, AwPlayer::mDemux = %p", mPlayer, mDemux); return -1; } else return 0; } int AwPlayer::setVideoOutputScaleRatio(int horizonScaleDownRatio,int verticalScaleDownRatio) { if(mStatus == AWPLAYER_STATUS_IDLE || mStatus == AWPLAYER_STATUS_INITIALIZED) { return PlayerConfigVideoScaleDownRatio(mPlayer,horizonScaleDownRatio,verticalScaleDownRatio); } else { loge("not in the AWPLAYER_STATUS_IDLE or AWPLAYER_STATUS_INITIALIZED status,can not config video scale down ratio"); return -1; } } int AwPlayer::setNotifyCallback(NotifyCallback notifier, void* pUserData) { mNotifier = notifier; mUserData = pUserData; return 0; } int AwPlayer::setControlOps(LayerControlOpsT* pLayerCtlOps, SoundControlOpsT* pSoundCtlOps) { PlayerSetControlOps(mPlayer, pLayerCtlOps, pSoundCtlOps); return 0; } #if CONFIG_OS == OPTION_OS_ANDROID status_t AwPlayer::setDataSource(const char* pUrl, const KeyedVector* pHeaders) #else int AwPlayer::setDataSource(const char* pUrl, const map* pHeaders) #endif { AwMessage msg; if(pUrl == NULL) { loge("setDataSource(url), url=NULL"); return -1; } logv("setDataSource(url), url=%s", pUrl); //* send a set data source message. setMessage(&msg, AWPLAYER_COMMAND_SET_SOURCE, //* message id. (uintptr_t)&mSemSetDataSource, //* params[0] = &mSemSetDataSource. (uintptr_t)&mSetDataSourceReply, //* params[1] = &mSetDataSourceReply. SOURCE_TYPE_URL, //* params[2] = SOURCE_TYPE_URL. (uintptr_t)pUrl, //* params[3] = pUrl. (uintptr_t)pHeaders); //* params[4] = KeyedVector* pHeaders; AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemSetDataSource, -1); return mSetDataSourceReply; } int AwPlayer::prepareAsync() { AwMessage msg; logv("prepareAsync"); //* send a prepare. setMessage(&msg, AWPLAYER_COMMAND_PREPARE, //* message id. (uintptr_t)&mSemPrepare, //* params[0] = &mSemPrepare. (uintptr_t)&mPrepareReply, //* params[1] = &mPrepareReply. 0); //* params[2] = mPrepareSync. AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemPrepare, -1); return mPrepareReply; } int AwPlayer::prepare() { AwMessage msg; logv("prepare"); //* clear the mSemPrepareFinish semaphore. while(sem_trywait(&mSemPrepareFinish) == 0); //* send a prepare message. setMessage(&msg, AWPLAYER_COMMAND_PREPARE, //* message id. (uintptr_t)&mSemPrepare, //* params[0] = &mSemPrepare. (uintptr_t)&mPrepareReply, //* params[1] = &mPrepareReply. 1); //* params[2] = mPrepareSync. AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemPrepare, -1); if(mPrepareReply == 0) { //* wait for the prepare finish. SemTimedWait(&mSemPrepareFinish, -1); return mPrepareFinishResult; } else return mPrepareReply; //* call DemuxCompPrepareAsync() fail, or status error. } int AwPlayer::start() { AwMessage msg; logv("start"); //* send a start message. setMessage(&msg, AWPLAYER_COMMAND_START, //* message id. (uintptr_t)&mSemStart, //* params[0] = &mSemStart. (uintptr_t)&mStartReply); //* params[1] = &mStartReply. AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemStart, -1); return mStartReply; } MediaInfo* AwPlayer::getMediaInfo() { if(mStatus == AWPLAYER_STATUS_PREPARING || mStatus == AWPLAYER_STATUS_INITIALIZED|| mStatus == AWPLAYER_STATUS_IDLE ) { loge("cannot get mediainfo in this status"); return NULL; } return mMediaInfo; } int AwPlayer::stop() { AwMessage msg; logv("stop"); //* send a stop message. setMessage(&msg, AWPLAYER_COMMAND_STOP, //* message id. (uintptr_t)&mSemStop, //* params[0] = &mSemStop. (uintptr_t)&mStopReply); //* params[1] = &mStopReply. AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemStop, -1); return mStopReply; } int AwPlayer::pause() { AwMessage msg; logv("pause"); //* send a pause message. setMessage(&msg, AWPLAYER_COMMAND_PAUSE, //* message id. (uintptr_t)&mSemPause, //* params[0] = &mSemPause. (uintptr_t)&mPauseReply); //* params[1] = &mPauseReply. AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemPause, -1); return mPauseReply; } int AwPlayer::seekTo(int msec) { AwMessage msg; logv("seekTo"); //* send a start message. setMessage(&msg, AWPLAYER_COMMAND_SEEK, //* message id. (uintptr_t)&mSemSeek, //* params[0] = &mSemSeek. (uintptr_t)&mSeekReply, //* params[1] = &mSeekReply. msec, //* params[2] = mSeekTime. 0); //* params[3] = mSeekSync. AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemSeek, -1); return mSeekReply; } int AwPlayer::setSpeed(int nSpeed) { AwMessage msg; logw("set speed %d...", nSpeed); mSpeed = nSpeed; //PlayerSetSpeed(mPlayer, nSpeed); if(nSpeed == 1) { mbFast = 0; mSpeed = 1; mFastTime = 0; start(); PlayerSetDiscardAudio(mPlayer, 0); return 0; } //* send a start message. setMessage(&msg, AWPLAYER_COMMAND_SETSPEED, //* message id. (uintptr_t)&mSemSetSpeed, //* params[0] = &mSemReset. (uintptr_t)&mSetSpeedReply, nSpeed); //* params[2] = speed. AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemSetSpeed, -1); return mSetSpeedReply; } int AwPlayer::reset() { AwMessage msg; logv("reset"); //* send a start message. setMessage(&msg, AWPLAYER_COMMAND_RESET, //* message id. (uintptr_t)&mSemReset, //* params[0] = &mSemReset. (uintptr_t)&mResetReply); //* params[1] = &mResetReply. AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemReset, -1); return mResetReply; } int AwPlayer::isPlaying() { logv("isPlaying"); if(mStatus == AWPLAYER_STATUS_STARTED || (mStatus == AWPLAYER_STATUS_COMPLETE && mLoop != 0)) return 1; else return 0; } int AwPlayer::getCurrentPosition(int* msec) { int64_t nPositionUs; logv("getCurrentPosition"); if(mStatus == AWPLAYER_STATUS_PREPARED || mStatus == AWPLAYER_STATUS_STARTED || mStatus == AWPLAYER_STATUS_PAUSED || mStatus == AWPLAYER_STATUS_COMPLETE) { if(mSeeking != 0) { *msec = mSeekTime; return 0; } pthread_mutex_lock(&mMutex); //* in complete status, the prepare() method maybe called logd("mMediaInfo->eContainerType = %d",mMediaInfo->eContainerType); if(mMediaInfo != NULL) { nPositionUs = PlayerGetPosition(mPlayer); /* if(mMediaInfo->eContainerType == CONTAINER_TYPE_TS || mMediaInfo->eContainerType == CONTAINER_TYPE_BD || mMediaInfo->eContainerType == CONTAINER_TYPE_HLS) nPositionUs = PlayerGetPosition(mPlayer); //* ts stream's pts is not started at 0. else nPositionUs = PlayerGetPts(mPlayer); */ *msec = (nPositionUs + 500)/1000; pthread_mutex_unlock(&mMutex); return 0; } else { loge("getCurrentPosition() fail, mMediaInfo==NULL."); *msec = 0; pthread_mutex_unlock(&mMutex); return 0; } } else { *msec = 0; if(mStatus == AWPLAYER_STATUS_ERROR) return -1; else return 0; } } int AwPlayer::getDuration(int *msec) { logv("getDuration"); if(mStatus == AWPLAYER_STATUS_PREPARED || mStatus == AWPLAYER_STATUS_STARTED || mStatus == AWPLAYER_STATUS_PAUSED || mStatus == AWPLAYER_STATUS_STOPPED || mStatus == AWPLAYER_STATUS_COMPLETE) { pthread_mutex_lock(&mMutex); if(mMediaInfo != NULL) *msec = mMediaInfo->nDurationMs; else { loge("getCurrentPosition() fail, mMediaInfo==NULL."); *msec = 0; } pthread_mutex_unlock(&mMutex); return 0; } else { loge("invalid getDuration() call, player not in valid status."); return -1; } } int AwPlayer::setLooping(int loop) { logv("setLooping"); if(mStatus == AWPLAYER_STATUS_ERROR) return -1; mLoop = loop; return 0; } int AwPlayer::setVolume(int volume) { AwMessage msg; logv("setVolume"); //* send a set volume message. setMessage(&msg, AWPLAYER_COMMAND_SET_VOLUME, //* message id. (uintptr_t)&mSemSetVolume, //* params[0] = &mSemSetVolume. (uintptr_t)&mSetVolumeReply, //* params[1] = &mSetVolumeReply. volume); //* params[2] = volume. AwMessageQueuePostMessage(mMessageQueue, &msg); SemTimedWait(&mSemSetVolume, -1); return mSetVolumeReply; } int AwPlayer::initializePlayer() { //* get media information. MediaInfo* mi; int i; int nDefaultAudioIndex; int nDefaultSubtitleIndex; int ret; pthread_mutex_lock(&mMutex); mi = DemuxCompGetMediaInfo(mDemux); if(mi == NULL) { loge("can not get media info from demux."); pthread_mutex_unlock(&mMutex); return -1; } mMediaInfo = mi; //* initialize the player. #if !DEMO_CONFIG_DISABLE_VIDEO if(mi->pVideoStreamInfo != NULL) { ret = PlayerSetVideoStreamInfo(mPlayer, mi->pVideoStreamInfo); if(ret != 0) { logw("PlayerSetVideoStreamInfo() fail, video stream not supported."); } } #endif #if !DEMO_CONFIG_DISABLE_AUDIO if(mi->pAudioStreamInfo != NULL) { nDefaultAudioIndex = -1; for(i=0; inAudioStreamNum; i++) { if(PlayerCanSupportAudioStream(mPlayer, &mi->pAudioStreamInfo[i])) { nDefaultAudioIndex = i; break; } } if(nDefaultAudioIndex < 0) { logw("no audio stream supported."); nDefaultAudioIndex = 0; } ret = PlayerSetAudioStreamInfo(mPlayer, mi->pAudioStreamInfo, mi->nAudioStreamNum, nDefaultAudioIndex); if(ret != 0) { logw("PlayerSetAudioStreamInfo() fail, audio stream not supported."); } } #endif if(PlayerHasVideo(mPlayer) == 0 && PlayerHasAudio(mPlayer) == 0) { loge("neither video nor audio stream can be played."); pthread_mutex_unlock(&mMutex); return -1; } #if !DEMO_CONFIG_DISABLE_SUBTITLE //* set subtitle stream to the text decoder. if(mi->pSubtitleStreamInfo != NULL) { nDefaultSubtitleIndex = -1; for(i=0; inSubtitleStreamNum; i++) { if(PlayerCanSupportSubtitleStream(mPlayer, &mi->pSubtitleStreamInfo[i])) { nDefaultSubtitleIndex = i; break; } } if(nDefaultSubtitleIndex < 0) { logw("no subtitle stream supported."); nDefaultSubtitleIndex = 0; } ret = PlayerSetSubtitleStreamInfo(mPlayer, mi->pSubtitleStreamInfo, mi->nSubtitleStreamNum, nDefaultSubtitleIndex); if(ret != 0) { logw("PlayerSetSubtitleStreamInfo() fail, subtitle stream not supported."); } } #else void(nDefaultSubtitleIndex); #endif //* report not seekable. if(mi->bSeekable == 0) { if(mNotifier != NULL) mNotifier(mUserData, NOTIFY_NOT_SEEKABLE, 0, NULL); } #if CONFIG_OS == OPTION_OS_LINUX //* on linux, outside program do not set a video layer, //* here we let the player create a video layer by itself. PlayerSetWindow(mPlayer, NULL); #endif pthread_mutex_unlock(&mMutex); return 0; } void AwPlayer::clearMediaInfo() { int i; VideoStreamInfo* v; AudioStreamInfo* a; SubtitleStreamInfo* s; if(mMediaInfo != NULL) { //* free video stream info. if(mMediaInfo->pVideoStreamInfo != NULL) { for(i=0; inVideoStreamNum; i++) { v = &mMediaInfo->pVideoStreamInfo[i]; if(v->pCodecSpecificData != NULL && v->nCodecSpecificDataLen > 0) free(v->pCodecSpecificData); } free(mMediaInfo->pVideoStreamInfo); mMediaInfo->pVideoStreamInfo = NULL; } //* free audio stream info. if(mMediaInfo->pAudioStreamInfo != NULL) { for(i=0; inAudioStreamNum; i++) { a = &mMediaInfo->pAudioStreamInfo[i]; if(a->pCodecSpecificData != NULL && a->nCodecSpecificDataLen > 0) free(a->pCodecSpecificData); } free(mMediaInfo->pAudioStreamInfo); mMediaInfo->pAudioStreamInfo = NULL; } //* free subtitle stream info. if(mMediaInfo->pSubtitleStreamInfo != NULL) { for(i=0; inSubtitleStreamNum; i++) { s = &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(mMediaInfo->pSubtitleStreamInfo); mMediaInfo->pSubtitleStreamInfo = NULL; } //* free the media info. free(mMediaInfo); mMediaInfo = NULL; } return; } int AwPlayer::mainThread() { AwMessage msg; int ret; sem_t* pReplySem; int* pReplyValue; while(1) { if(AwMessageQueueGetMessage(mMessageQueue, &msg) < 0) { loge("get message fail."); continue; } pReplySem = (sem_t*)msg.params[0]; pReplyValue = (int*)msg.params[1]; if(msg.messageId == AWPLAYER_COMMAND_SET_SOURCE) { logv("process message AWPLAYER_COMMAND_SET_SOURCE."); //* check status. if(mStatus != AWPLAYER_STATUS_IDLE && mStatus != AWPLAYER_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) { #if CONFIG_OS == OPTION_OS_ANDROID KeyedVector* pHeaders; #else map* pHeaders; #endif //* data source is a url string. if(mSourceUrl != NULL) free(mSourceUrl); mSourceUrl = strdup((char*)msg.params[3]); #if CONFIG_OS == OPTION_OS_ANDROID pHeaders = (KeyedVector*) msg.params[4]; #else pHeaders = (map*) msg.params[4]; #endif ret = DemuxCompSetUrlSource(mDemux, mSourceUrl, pHeaders); if(ret == 0) { mStatus = AWPLAYER_STATUS_INITIALIZED; if(pReplyValue != NULL) *pReplyValue = 0; } else { loge("DemuxCompSetUrlSource() return fail."); mStatus = AWPLAYER_STATUS_IDLE; free(mSourceUrl); mSourceUrl = NULL; if(pReplyValue != NULL) *pReplyValue = -1; } } else { //* support for fd source is delete. if(pReplyValue != NULL) *pReplyValue = -1; } if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end AWPLAYER_COMMAND_SET_SOURCE. else if(msg.messageId == AWPLAYER_COMMAND_PREPARE) { logv("process message AWPLAYER_COMMAND_PREPARE."); if(mStatus != AWPLAYER_STATUS_INITIALIZED && mStatus != AWPLAYER_STATUS_STOPPED) { logv("invalid prepareAsync() call, player not in initialized or stopped status."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } mStatus = AWPLAYER_STATUS_PREPARING; mPrepareSync = msg.params[2]; ret = DemuxCompPrepareAsync(mDemux); if(ret != 0) { loge("DemuxCompPrepareAsync return fail immediately."); mStatus = AWPLAYER_STATUS_IDLE; if(pReplyValue != NULL) *pReplyValue = -1; } else { if(pReplyValue != NULL) *pReplyValue = 0; } if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end AWPLAYER_COMMAND_PREPARE. else if(msg.messageId == AWPLAYER_COMMAND_START) { logv("process message AWPLAYER_COMMAND_START."); if(mStatus != AWPLAYER_STATUS_PREPARED && mStatus != AWPLAYER_STATUS_STARTED && mStatus != AWPLAYER_STATUS_PAUSED && mStatus != AWPLAYER_STATUS_COMPLETE) { logv("invalid start() call, player not in prepared, \ started, paused or complete status."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mStatus == AWPLAYER_STATUS_STARTED) { logv("player already in started status."); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } pthread_mutex_lock(&mMutex); //* synchronize with the seek or complete callback. if(mSeeking) { mStatus = AWPLAYER_STATUS_STARTED; pthread_mutex_unlock(&mMutex); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* for complete status, we seek to the begin of the file. if(mStatus == AWPLAYER_STATUS_COMPLETE) { AwMessage newMsg; if(mMediaInfo->bSeekable) { setMessage(&newMsg, AWPLAYER_COMMAND_SEEK, //* message id. 0, //* params[0] = &mSemSeek, internal message, do not post. 0, //* params[1] = &mSeekReply, internal message, do not set reply. 0, //* params[2] = mSeekTime(ms). 1); //* params[3] = mSeekSync. AwMessageQueuePostMessage(mMessageQueue, &newMsg); pthread_mutex_unlock(&mMutex); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else { //* post a stop message. setMessage(&newMsg, AWPLAYER_COMMAND_STOP, //* message id. 0, //* params[0] = &mSemStop, internal message, do not post. 0); //* params[1] = &mStopReply, internal message, do not reply. AwMessageQueuePostMessage(mMessageQueue, &newMsg); //* post a prepare message. setMessage(&newMsg, AWPLAYER_COMMAND_PREPARE, //* message id. 0, //* params[0] = &mSemPrepare, internal message, do not post. 0, //* params[1] = &mPrepareReply, internal message, do not reply. 1); //* params[2] = mPrepareSync. AwMessageQueuePostMessage(mMessageQueue, &newMsg); //* post a start message. setMessage(&newMsg, AWPLAYER_COMMAND_START, //* message id. 0, //* params[0] = &mSemStart, internal message, do not post. 0); //* params[1] = &mStartReply, internal message, do not reply. AwMessageQueuePostMessage(mMessageQueue, &newMsg); //* should I reply OK 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(&mMutex); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } } pthread_mutex_unlock(&mMutex); PlayerStart(mPlayer); DemuxCompStart(mDemux); mStatus = AWPLAYER_STATUS_STARTED; if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end AWPLAYER_COMMAND_START. else if(msg.messageId == AWPLAYER_COMMAND_STOP) { logv("process message AWPLAYER_COMMAND_STOP."); if(mStatus != AWPLAYER_STATUS_PREPARED && mStatus != AWPLAYER_STATUS_STARTED && mStatus != AWPLAYER_STATUS_PAUSED && mStatus != AWPLAYER_STATUS_COMPLETE && mStatus != AWPLAYER_STATUS_PREPARING && mStatus != AWPLAYER_STATUS_STOPPED) { logv("invalid stop() call, player not in prepared, \ paused, started, stopped or complete status."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mStatus == AWPLAYER_STATUS_STOPPED) { logv("player already in stopped status."); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mStatus == AWPLAYER_STATUS_PREPARING) { //* so the mStatus may be changed to PREPARED asynchronizely. logw("stop() called at preparing status, cancel demux prepare."); DemuxCompCancelPrepare(mDemux); } if(mSeeking) { DemuxCompCancelSeek(mDemux); mSeeking = 0; } DemuxCompStop(mDemux); PlayerStop(mPlayer); PlayerClear(mPlayer); //* clear all media information in player. mStatus = AWPLAYER_STATUS_STOPPED; if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end AWPLAYER_COMMAND_STOP. else if(msg.messageId == AWPLAYER_COMMAND_SETSPEED) { logd("process message AWPLAYER_COMMAND_SETSPEED."); if(mStatus != AWPLAYER_STATUS_PREPARED && mStatus != AWPLAYER_STATUS_STARTED ) { logd("invalid start() call, player not in prepared, \ started, paused or complete status."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mMediaInfo == NULL || mMediaInfo->bSeekable == 0) { if(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(mSeeking) { DemuxCompCancelSeek(mDemux); mSeeking = 0; } //pthread_mutex_lock(&mMutex); int curTime; getCurrentPosition(&curTime); mSeeking = 1; mbFast = 1; mFastTime = curTime + mSpeed*1000; mSeekTime = mFastTime; mSeekSync = 0; logd("mFastTime %d ms, curTime: %d, mSpeed: %d", mFastTime, curTime, mSpeed); //pthread_mutex_unlock(&mMutex); //PlayerFastForeward(mPlayer); PlayerSetDiscardAudio(mPlayer, 1); if(PlayerGetStatus(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(mPlayer); } PlayerPause(mPlayer); DemuxCompSeekTo(mDemux, mSeekTime); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else if(msg.messageId == AWPLAYER_COMMAND_PAUSE) { logv("process message AWPLAYER_COMMAND_PAUSE."); if(mStatus != AWPLAYER_STATUS_STARTED && mStatus != AWPLAYER_STATUS_PAUSED && mStatus != AWPLAYER_STATUS_COMPLETE) { logv("invalid pause() call, player not in started, paused or complete status."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mStatus == AWPLAYER_STATUS_PAUSED || mStatus == AWPLAYER_STATUS_COMPLETE) { logv("player already in paused or complete status."); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } pthread_mutex_lock(&mMutex); //* synchronize with the seek callback. if(mSeeking) { mStatus = AWPLAYER_STATUS_PAUSED; pthread_mutex_unlock(&mMutex); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } pthread_mutex_unlock(&mMutex); //* sync with the seek or complete call back. PlayerPause(mPlayer); mStatus = AWPLAYER_STATUS_PAUSED; if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end AWPLAYER_COMMAND_PAUSE. else if(msg.messageId == AWPLAYER_COMMAND_RESET) { logd("process message AWPLAYER_COMMAND_RESET."); if(mStatus == AWPLAYER_STATUS_PREPARING) { //* so the mStatus may be changed to PREPARED asynchronizely. logw("reset() called at preparing status, \ cancel demux prepare."); DemuxCompCancelPrepare(mDemux); } if(mSeeking) { DemuxCompCancelSeek(mDemux); mSeeking = 0; } //* stop and clear the demux. DemuxCompStop(mDemux); DemuxCompClear(mDemux); //* stop and clear the player. PlayerStop(mPlayer); PlayerClear(mPlayer); //* it will clear media info config to the player. //* clear data source. if(mSourceUrl != NULL) { free(mSourceUrl); mSourceUrl = NULL; } //* clear media info. clearMediaInfo(); //* clear loop setting. mLoop = 0; //* set status to IDLE. mStatus = AWPLAYER_STATUS_IDLE; if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else if(msg.messageId == AWPLAYER_COMMAND_SEEK) { logv("process message AWPLAYER_COMMAND_SEEK."); if(mStatus != AWPLAYER_STATUS_PREPARED && mStatus != AWPLAYER_STATUS_STARTED && mStatus != AWPLAYER_STATUS_PAUSED && mStatus != AWPLAYER_STATUS_COMPLETE) { logv("invalid seekTo() call, player not in prepared,\ started, paused or complete status."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(mMediaInfo == NULL || mMediaInfo->bSeekable == 0) { if(mMediaInfo == NULL) { loge("seekTo fail because mMediaInfo == NULL."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } else { loge("media not seekable."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } } if(mSeeking) { DemuxCompCancelSeek(mDemux); mSeeking = 0; } pthread_mutex_lock(&mMutex); mSeeking = 1; mSeekTime = msg.params[2]; mSeekSync = msg.params[3]; logv("seekTo %.2f secs", mSeekTime / 1E3); pthread_mutex_unlock(&mMutex); //* if in prepared status, the player is not started yet, //* so PlayerPause() will return fail, it dosn't matter. //* player will be reset at the seek complete callback. PlayerPause(mPlayer); DemuxCompSeekTo(mDemux, mSeekTime); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end AWPLAYER_COMMAND_SEEK. else if(msg.messageId == AWPLAYER_COMMAND_QUIT) { if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); break; //* break the thread. } else if(msg.messageId == AWPLAYER_COMMAND_SET_VOLUME) { logv("process message AWPLAYER_COMMAND_SET_VOLUME."); if(mStatus != AWPLAYER_STATUS_PREPARED && mStatus != AWPLAYER_STATUS_STARTED && mStatus != AWPLAYER_STATUS_PAUSED && mStatus != AWPLAYER_STATUS_COMPLETE) { logv("invalid setVolume() call, player not in prepared, \ started, paused or complete status."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } mVolume = msg.params[2]; PlayerSetVolume(mPlayer, mVolume); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else { logw("unknow message with id %d, ignore.", msg.messageId); } } return 0; } int AwPlayer::callbackProcess(int messageId, void* param) { switch(messageId) { case AWPLAYER_MESSAGE_DEMUX_PREPARED: { uintptr_t tmpPtr = (uintptr_t)param; int err = tmpPtr; if(err != 0) { //* demux prepare return fail. //* notify a media error event. mStatus = AWPLAYER_STATUS_ERROR; if(mPrepareSync == 0) { if(err == DEMUX_ERROR_IO) mNotifier(mUserData, NOTIFY_ERROR, NOTIFY_ERROR_TYPE_IO, NULL); else mNotifier(mUserData, NOTIFY_ERROR, NOTIFY_ERROR_TYPE_UNKNOWN, NULL); } else { mPrepareFinishResult = -1; sem_post(&mSemPrepareFinish); } } else { //* demux prepare success, initialize the player. if(initializePlayer() == 0) { //* initialize player success, notify a prepared event. mStatus = AWPLAYER_STATUS_PREPARED; if(mPrepareSync == 0) mNotifier(mUserData, NOTIFY_PREPARED, 0, NULL); else { mPrepareFinishResult = 0; sem_post(&mSemPrepareFinish); } } else { //* initialize player fail, notify a media error event. mStatus = AWPLAYER_STATUS_ERROR; if(mPrepareSync == 0) mNotifier(mUserData, NOTIFY_ERROR, NOTIFY_ERROR_TYPE_UNKNOWN, NULL); else { mPrepareFinishResult = -1; sem_post(&mSemPrepareFinish); } } } break; } case AWPLAYER_MESSAGE_DEMUX_EOS: { PlayerSetEos(mPlayer); break; } case AWPLAYER_MESSAGE_DEMUX_IOERROR: { //* should we report a MEDIA_INFO event of "MEDIA_INFO_NETWORK_ERROR" and //* try reconnect for sometimes before a NOTIFY_ERROR_TYPE_IO event reported ? mNotifier(mUserData, NOTIFY_ERROR, NOTIFY_ERROR_TYPE_IO, NULL); break; } case AWPLAYER_MESSAGE_DEMUX_CACHE_REPORT: { int nTotalPercentage; int nBufferPercentage; nTotalPercentage = ((int*)param)[0]; //* read positon to total file size. nBufferPercentage = ((int*)param)[1]; //* cache data size to start play cache size. mNotifier(mUserData, NOTIFY_BUFFERRING_UPDATE, nBufferPercentage<<16 | nTotalPercentage, NULL); break; } case AWPLAYER_MESSAGE_DEMUX_BUFFER_START: { logd("llh>>>play buf is not enough,NOTIFY_BUFFER_START"); mNotifier(mUserData, NOTIFY_BUFFER_START, 0, NULL); break; } case AWPLAYER_MESSAGE_DEMUX_BUFFER_END: { logd("llh>>>play buf is enough,NOTIFY_BUFFER_END"); mNotifier(mUserData, NOTIFY_BUFFER_END, 0, NULL); break; } case AWPLAYER_MESSAGE_DEMUX_PAUSE_PLAYER: { logd("callback: DEMUX_NOTIFY_PAUSE_PLAYER,mStatus = %d",mStatus); if(mStatus == AWPLAYER_STATUS_STARTED){ logd("there is no pcm data in player,pause the player to stop the avtimer"); PlayerPause(mPlayer); } break; } case AWPLAYER_MESSAGE_DEMUX_RESUME_PLAYER: { logd("callback: DEMUX_NOTIFY_RESUME_PLAYER,mStatus = %d",mStatus); if(mStatus == AWPLAYER_STATUS_STARTED){ logd("there is enough data ,resume the player"); PlayerStart(mPlayer); } break; } case AWPLAYER_MESSAGE_PLAYER_EOS: { mStatus = AWPLAYER_STATUS_COMPLETE; if(mLoop == 0) { logv("player notify eos."); mNotifier(mUserData, NOTIFY_PLAYBACK_COMPLETE, 0, NULL); } else { AwMessage msg; logv("player notify eos, loop is set, send start command."); //* send a start message. setMessage(&msg, AWPLAYER_COMMAND_START, //* message id. 0, //* params[0] = &mSemStart, internal message, do not post message. 0); //* params[1] = &mStartReply, internal message, do not reply. AwMessageQueuePostMessage(mMessageQueue, &msg); } break; } case AWPLAYER_MESSAGE_PLAYER_FIRST_PICTURE: { mNotifier(mUserData, NOTIFY_RENDERING_START, 0, NULL); break; } case AWPLAYER_MESSAGE_DEMUX_SEEK_FINISH: { int seekResult; int seekTimeMs; //* be careful to check whether there is any player callback lock the mutex, //* if so, the PlayerPause() call may fall into dead lock if the player //* callback is requesting mMutex. //* currently we do not lock mMutex in any player callback. pthread_mutex_lock(&mMutex); seekResult = ((int*)param)[0]; seekTimeMs = ((int*)param)[1]; if(seekResult == 0) { PlayerReset(mPlayer, ((int64_t)seekTimeMs)*1000); if(seekTimeMs == mSeekTime) { mSeeking = 0; if(mStatus == AWPLAYER_STATUS_STARTED || mStatus == AWPLAYER_STATUS_COMPLETE) { PlayerStart(mPlayer); DemuxCompStart(mDemux); if(mStatus == AWPLAYER_STATUS_COMPLETE) mStatus = AWPLAYER_STATUS_STARTED; } } else { logv("seek time not match, there may be another seek operation happening."); } pthread_mutex_unlock(&mMutex); mNotifier(mUserData, NOTIFY_SEEK_COMPLETE, 0, NULL); } else if(seekResult == DEMUX_ERROR_USER_CANCEL) { pthread_mutex_unlock(&mMutex); mNotifier(mUserData, NOTIFY_SEEK_COMPLETE, 0, NULL); } else { pthread_mutex_unlock(&mMutex); mNotifier(mUserData, NOTIFY_ERROR, NOTIFY_ERROR_TYPE_IO, NULL); } break; } case AWPLAYER_MESSAGE_PLAYER_SUBTITLE_AVAILABLE: { //* skip subtitle. break; } case AWPLAYER_MESSAGE_PLAYER_SUBTITLE_EXPIRED: { //* skip subtitle. break; } case AWPLAYER_MESSAGE_DEMUX_DATA_PACKET: { CdxPacketT* packet = (CdxPacketT*)param; DemuxData data; data.nPts = packet->pts; data.nSize0 = packet->buflen; data.pData0 = (unsigned char*)packet->buf; data.nSize1 = packet->ringBufLen; data.pData1 = (unsigned char*)packet->ringBuf; if(packet->type == CDX_MEDIA_VIDEO) { mNotifier(mUserData, NOTIFY_VIDEO_PACKET, 0, &data); } else if(packet->type == CDX_MEDIA_AUDIO) { mNotifier(mUserData, NOTIFY_AUDIO_PACKET, 0, &data); } break; } case AWPLAYER_MESSAGE_PLAYER_VIDEO_PIC_DATA: { VideoPicture *pic = (VideoPicture*)param; VideoPicData data; memset(&data, 0x00, sizeof(VideoPicData)); data.nPts = pic->nPts; data.nWidth = pic->nWidth; data.nHeight = pic->nHeight; data.ePixelFormat = pic->ePixelFormat; data.nBottomOffset = pic->nBottomOffset; data.nLeftOffset = pic->nLeftOffset; data.nRightOffset = pic->nRightOffset; data.nTopOffset = pic->nTopOffset; data.pData0 = pic->pData0; data.pData1 = pic->pData1; data.pData2 = pic->pData2; data.phyCBufAddr = pic->phyCBufAddr; data.phyYBufAddr = pic->phyYBufAddr; { mNotifier(mUserData, NOTIFY_VIDEO_FRAME, 0, &data); break; } } case AWPLAYER_MESSAGE_PLAYER_AUDIO_PCM_DATA: { AudioPcmData pcmData; pcmData.pData = (unsigned char*)((uintptr_t*)param)[0]; pcmData.nSize = ((int*)param)[1]; mNotifier(mUserData, NOTIFY_AUDIO_FRAME, 0, &pcmData); break; } case AWPLAYER_MESSAGE_PLAYER_VIDEO_RENDER_FRAME: if(mbFast) { logd("==== video key frame in fast mode, mFastTime: %d, mSpeed: %d", mFastTime, mSpeed); if(mSpeed == 0) { break; } int sleepTime = (mSpeed > 0) ? 2000000/mSpeed : (-2000000/mSpeed); //usleep(sleepTime); mFastTime += mSpeed*1000; if(mFastTime > 0 && mbFast) { AwMessage msg; //* send a seek message. setMessage(&msg, AWPLAYER_COMMAND_SEEK, //* message id. (uintptr_t)&mSemSeek, //* params[0] = &mSemSeek. (uintptr_t)&mSeekReply, //* params[1] = &mSeekReply. mFastTime, //* params[2] = mSeekTime. 0); //* params[3] = mSeekSync. AwMessageQueuePostMessage(mMessageQueue, &msg); } else if(mFastTime <= 0) { PlayerSetDiscardAudio(mPlayer, 0); mbFast = 0; } } break; default: { logw("message 0x%x not handled.", messageId); break; } } return 0; } static void* AwPlayerThread(void* arg) { AwPlayer* me = (AwPlayer*)arg; me->mainThread(); return NULL; } static int DemuxCallbackProcess(void* pUserData, int eMessageId, void* param) { int msg; AwPlayer* p; switch(eMessageId) { case DEMUX_NOTIFY_PREPARED: msg = AWPLAYER_MESSAGE_DEMUX_PREPARED; break; case DEMUX_NOTIFY_EOS: msg = AWPLAYER_MESSAGE_DEMUX_EOS; break; case DEMUX_NOTIFY_IOERROR: msg = AWPLAYER_MESSAGE_DEMUX_IOERROR; break; case DEMUX_NOTIFY_SEEK_FINISH: msg = AWPLAYER_MESSAGE_DEMUX_SEEK_FINISH; break; case DEMUX_NOTIFY_CACHE_STAT: msg = AWPLAYER_MESSAGE_DEMUX_CACHE_REPORT; break; case DEMUX_NOTIFY_BUFFER_START: msg = AWPLAYER_MESSAGE_DEMUX_BUFFER_START; break; case DEMUX_NOTIFY_BUFFER_END: msg = AWPLAYER_MESSAGE_DEMUX_BUFFER_END; break; case DEMUX_NOTIFY_PAUSE_PLAYER: msg = AWPLAYER_MESSAGE_DEMUX_PAUSE_PLAYER; break; case DEMUX_NOTIFY_RESUME_PLAYER: msg = AWPLAYER_MESSAGE_DEMUX_RESUME_PLAYER; break; case DEMUX_NOTIFY_DATA_PACKET: msg = AWPLAYER_MESSAGE_DEMUX_DATA_PACKET; break; default: logw("ignore demux callback message, eMessageId = 0x%x.", eMessageId); return -1; } p = (AwPlayer*)pUserData; p->callbackProcess(msg, param); return 0; } static int PlayerCallbackProcess(void* pUserData, int eMessageId, void* param) { int msg; AwPlayer* p; switch(eMessageId) { case PLAYER_NOTIFY_EOS: msg = AWPLAYER_MESSAGE_PLAYER_EOS; break; case PLAYER_NOTIFY_FIRST_PICTURE: msg = AWPLAYER_MESSAGE_PLAYER_FIRST_PICTURE; break; case PLAYER_NOTIFY_SUBTITLE_ITEM_AVAILABLE: msg = AWPLAYER_MESSAGE_PLAYER_SUBTITLE_AVAILABLE; break; case PLAYER_NOTIFY_SUBTITLE_ITEM_EXPIRED: msg = AWPLAYER_MESSAGE_PLAYER_SUBTITLE_EXPIRED; break; case PLAYER_NOTIFY_VIDEO_PIC_DATA: msg = AWPLAYER_MESSAGE_PLAYER_VIDEO_PIC_DATA; break; case PLAYER_NOTIFY_AUDIO_PCM_DATA: msg = AWPLAYER_MESSAGE_PLAYER_AUDIO_PCM_DATA; break; case PLAYER_NOTIFY_VIDEO_RENDER_FRAME: msg = AWPLAYER_MESSAGE_PLAYER_VIDEO_RENDER_FRAME; break; case PLAYER_NOTIFY_VIDEO_SIZE: //* TODO case PLAYER_NOTIFY_VIDEO_CROP: //* TODO default: logw("ignore player callback message, eMessageId = 0x%x.", eMessageId); return -1; } p = (AwPlayer*)pUserData; p->callbackProcess(msg, param); return 0; } static int transformPictureMb32ToRGB(VideoPicture* pPicture, unsigned char* pData, int nWidth, int nHeight) { unsigned char* pClipTable; unsigned char* pClip; static const int nClipMin = -278; static const int nClipMax = 535; unsigned short* pDst = NULL; unsigned char* pSrcY = NULL; unsigned char* pSrcVU = NULL; int x = 0; int y = 0; int nMbWidth = 0; int nMbHeight = 0; int nVMb = 0; int nHMb = 0; int yPos = 0; int pos = 0; int uvPos = 0; //* initialize the clip table. pClipTable = (unsigned char*)malloc(nClipMax - nClipMin + 1); if(pClipTable == NULL) { loge("can not allocate memory for the clip table, quit."); return -1; } for(x=nClipMin; x<=nClipMax; x++) { pClipTable[x-nClipMin] = (x<0) ? 0 : (x>255) ? 255 : x; } pClip = &pClipTable[-nClipMin]; //* flush cache. // MemAdapterFlushCache(pPicture->pData0, pPicture->nWidth*pPicture->nHeight); // MemAdapterFlushCache(pPicture->pData1, pPicture->nHeight*pPicture->nHeight/2); pDst = (unsigned short*)pData; pSrcY = (unsigned char*)pPicture->pData0; pSrcVU = (unsigned char*)pPicture->pData1; nMbWidth = pPicture->nWidth/32; nMbHeight = pPicture->nHeight/32; for(nVMb=0; nVMbnWidth*32+nHMb*32; for(y=0; y<32; y++) { yPos = (nVMb*nMbWidth+nHMb)*1024+y*32; uvPos = ((nVMb/2)*nMbWidth*1024)+nHMb*1024+(y/2)*32+ (((nVMb%2)==1) ? 512 : 0); for(x=0; x<32; x+=2) { signed y1 = (signed)pSrcY[yPos+x+0] - 16; signed y2 = (signed)pSrcY[yPos+x+1] - 16; signed u = (signed)pSrcVU[uvPos+x+0] - 128; signed v = (signed)pSrcVU[uvPos+x+1] - 128; signed u_b = u * 517; signed u_g = -u * 100; signed v_g = -v * 208; signed v_r = v * 409; signed tmp1 = y1 * 298; signed b1 = (tmp1 + u_b) / 256; signed g1 = (tmp1 + v_g + u_g) / 256; signed r1 = (tmp1 + v_r) / 256; signed tmp2 = y2 * 298; signed b2 = (tmp2 + u_b) / 256; signed g2 = (tmp2 + v_g + u_g) / 256; signed r2 = (tmp2 + v_r) / 256; unsigned int rgb1 = ((pClip[r1] >> 3) << 11) | ((pClip[g1] >> 2) << 5) | (pClip[b1] >> 3); unsigned int rgb2 = ((pClip[r2] >> 3) << 11) | ((pClip[g2] >> 2) << 5) | (pClip[b2] >> 3); *(unsigned int *)(&pDst[pos]) = (rgb2 << 16) | rgb1; pos += 2; } pos += (nMbWidth-1)*32; } } } logd("pos: %d", pos); pDst = (unsigned short*)pData; for(y=0; ynTopOffset; y++) { memset(pDst+y*nWidth, 0, 2*nWidth); } for(y=pPicture->nBottomOffset; ynTopOffset; ynBottomOffset; y++) { memset(pDst+y*nWidth, 0, 2*pPicture->nLeftOffset); memset(pDst+y*nWidth+pPicture->nRightOffset, 0, 2*(nWidth-pPicture->nRightOffset)); } free(pClipTable); return 0; }