/* * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. * All rights reserved. * * File : demuxComponent.cpp * Description : stream control and video stream demux * History : * Author : AL3 * Date : 2015/05/05 * Comment : first version * */ //#define CONFIG_LOG_LEVEL 4 #define LOG_TAG "demuxComponent" #include #include #include #include #include "demuxComponent.h" #include "AwMessageQueue.h" #include "CdxParser.h" //* parser library in "LIBRARY/DEMUX/PARSER/include/" #include "CdxStream.h" //* parser library in "LIBRARY/DEMUX/STREAM/include/" #include "player.h" //* player library in "LIBRARY/PLAYER/" #include "cache.h" #include "cdx_log.h" #include "xplayer.h" #include "iniparserapi.h" //* demux status, same with the awplayer. static const int DEMUX_STATUS_IDLE = 0; //* the very beginning status. static const int DEMUX_STATUS_INITIALIZED = 1<<0; //* after data source set. static const int DEMUX_STATUS_PREPARING = 1<<1; //* when preparing. static const int DEMUX_STATUS_PREPARED = 1<<2; //* after parser is opened and media info get. static const int DEMUX_STATUS_STARTED = 1<<3; //* parsing and sending data. static const int DEMUX_STATUS_PAUSED = 1<<4; //* sending job paused. static const int DEMUX_STATUS_STOPPED = 1<<5; //* parser closed. static const int DEMUX_STATUS_COMPLETE = 1<<6; //* all data parsed. //* command id, same with the awplayer. static const int DEMUX_COMMAND_SET_SOURCE = 0x101; static const int DEMUX_COMMAND_PREPARE = 0x104; static const int DEMUX_COMMAND_START = 0x105; static const int DEMUX_COMMAND_PAUSE = 0x106; static const int DEMUX_COMMAND_STOP = 0x107; static const int DEMUX_COMMAND_QUIT = 0x109; static const int DEMUX_COMMAND_SEEK = 0x10a; static const int DEMUX_COMMAND_CLEAR = 0x10b; static const int DEMUX_COMMAND_CANCEL_PREPARE = 0x10c; static const int DEMUX_COMMAND_CANCEL_SEEK = 0x10d; static const int DEMUX_COMMAND_READ = 0x10e; static const int DEMUX_COMMAND_NOTIFY_FIRST_FRAME_SHOWED = 0x10f; static const int DEMUX_COMMAND_VIDEO_STREAM_CHANGE = 0x110; static const int DEMUX_COMMAND_AUDIO_STREAM_CHANGE = 0x111; static const int DEMUX_COMMAND_RESET_URL = 0x112; #define kUseSecureInputBuffers 256 //copy from wvm/ExtractorWrapper.cpp struct AwMessage { AWMESSAGE_COMMON_MEMBERS uintptr_t params[8]; }; typedef struct DemuxCompContext_t { int eStatus; int bLiveStream; //* live streaming from network. int bFileStream; //* local media file. int bVodStream; //* vod stream from network. //* data source. int nSourceType; //* url or fd or IStreamSource. CdxDataSourceT source; MediaInfo mediaInfo; pthread_t threadId; AwMessageQueue* mq; CdxParserT* pParser; CdxStreamT* pStream; Player* pPlayer; DemuxCallback callback; void* pUserData; pthread_mutex_t mutex; sem_t semSetDataSource; sem_t semStart; sem_t semStop; sem_t semQuit; sem_t semClear; sem_t semCancelPrepare; sem_t semCancelSeek; sem_t semStreamChange; int nSetDataSourceReply; int nStartReply; int nStopReply; pthread_t cacheThreadId; AwMessageQueue* mqCache; sem_t semCache; int nCacheReply; StreamCache* pCache; int bBufferring; int bEOS; int bIOError; struct ParserCacheStateS sCacheState; //* for buffering status update notify. int64_t nCacheStatReportIntervalUs; enum ECACHEPOLICY eCachePolicy; SeekModeType nSeekModeType; int bCancelPrepare; int bCancelSeek; int bSeeking; int bStopping; int bBufferStartNotified; int bNeedPausePlayerNotified; char shiftedTimeUrl[4096]; int mLivemode; int bNetDisConnectNotified; int bDisconnet; pthread_mutex_t lock; //for break blocking operation pthread_cond_t cond; int statusCode; // Todo: using atomic if necessary int nStartPlayCacheSize; //default 512*1024; int nCacheBufferSize; //default 20*1024*1024; int nCacheBufferSizeLive; //default 20*1024*1024; int nStartPlayCacheTime; //default 6000; // 6 seconds. int nMaxStartPlayChacheSize; //default 150*1024*1024; int nMaxCacheBufferSize; //default 200*1024*1024; //* for timeshift to keep the last seqNum int timeShiftLastSeqNum; //* for hls discontinuity tag int bDiscontinue; int mStreamPrepared; //* default false, true if once prepared and playbacked; struct HdcpOpsS* pHDCPOps; AwBufferingSettings* pBuffering; }DemuxCompContext; struct BufferInfo { uint32_t size; void * buffer; }; static void* DemuxThread(void* arg); static void* CacheThread(void* arg); static int setDataSourceFields(DemuxCompContext *demuxHdr, CdxDataSourceT* source, void* pHTTPServer, char* uri, CdxKeyedVectorT* pHeader); static void clearDataSourceFields(CdxDataSourceT* source); static int setMediaInfo(MediaInfo* pMediaInfo, CdxMediaInfoT* pInfoFromParser); static void clearMediaInfo(MediaInfo* pMediaInfo); static int PlayerBufferOverflow(Player* p); static int PlayerBufferUnderflow(Player* p); static int GetCacheState(DemuxCompContext* demux); static int DemuxCompAdjustCacheParamsWithBitrate(DemuxCompContext* demux, int nBitrate); static int DemuxCompUseDefaultCacheParams(DemuxCompContext* demux); static void AdjustCacheParams(DemuxCompContext* demux); static void NotifyCacheState(DemuxCompContext* demux); static void PostReadMessage(AwMessageQueue* mq); static int64_t GetSysTime(); static int ShiftTimeMode(int Shiftedms, char *buf); /* char *dirPath = (char *)((int *)param)[0]; int *pDirId = (int *)((int *)param)[1]; */ static int DemuxCBOpenDir(void *cbhdr, const char *name, void **pDirHdr) { DemuxCompContext *d = (DemuxCompContext *)cbhdr; uintptr_t msgParam[4]; int dirId = -1; msgParam[0] = (uintptr_t)name; msgParam[1] = (uintptr_t)&dirId; d->callback(d->pUserData, DEMUX_IOREQ_OPENDIR, msgParam); uintptr_t nTmpDirId = dirId; *pDirHdr = (void *)nTmpDirId; return (dirId == -1) ? -1 : 0; } /* int dirId = ((int *)param)[0]; int *pRet = (int *)((int *)param)[1]; char *buf = (char *)((int *)param)[2]; int bufLen = ((int *)param)[3]; */ static int DemuxCBReadDir(void *cbhdr, void *dirHdr, char *dname, int dnameLen) { DemuxCompContext *d = (DemuxCompContext *)cbhdr; uintptr_t msgParam[4]; int ret = -1; //int dirId = (int)dirHdr; msgParam[0] = (uintptr_t)dirHdr; msgParam[1] = (uintptr_t)&ret; msgParam[2] = (uintptr_t)dname; msgParam[3] = dnameLen; d->callback(d->pUserData, DEMUX_IOREQ_READDIR, msgParam); return ret; } /* int dirId = ((int *)param)[0]; int *pRet = (int *)((int *)param)[1]; */ static int DemuxCBCloseDir(void *cbhdr, void *dirHdr) { DemuxCompContext *d = (DemuxCompContext *)cbhdr; uintptr_t msgParam[4]; int ret; //int dirId = (int)dirHdr; msgParam[0] = (uintptr_t)dirHdr; msgParam[1] = (uintptr_t)&ret; d->callback(d->pUserData, DEMUX_IOREQ_CLOSEDIR, msgParam); return ret; } /* char *filePath = (char *)((int *)param)[0]; int *pFd = (int *)((int *)param)[1]; */ static int DemuxCBOpenFile(void *cbhdr, const char *pathname, int flags) { DemuxCompContext *d = (DemuxCompContext *)cbhdr; uintptr_t msgParam[4]; int fd = -1; CEDARX_UNUSE(flags); msgParam[0] = (uintptr_t)pathname; msgParam[1] = (uintptr_t)&fd; d->callback(d->pUserData, DEMUX_IOREQ_OPEN, msgParam); return fd; } /* char *filePath = (char *)((int *)param)[0]; int mode = ((int *)param)[1]; int *pRet = (int *)((int *)param)[2]; */ static int DemuxCBAccessFile(void *cbhdr, const char *pathname, int mode) { DemuxCompContext *d = (DemuxCompContext *)cbhdr; uintptr_t msgParam[4]; int ret = -1; msgParam[0] = (uintptr_t)pathname; msgParam[1] = mode; msgParam[2] = (uintptr_t)&ret; d->callback(d->pUserData, DEMUX_IOREQ_ACCESS, msgParam); return ret; } static struct IoOperationS demuxIoOps = { .openDir = DemuxCBOpenDir, .readDir = DemuxCBReadDir, .closeDir = DemuxCBCloseDir, .openFile = DemuxCBOpenFile, .accessFile = DemuxCBAccessFile }; static int ParserCallbackProcess(void* pUserData, int eMessageId, void* param) { DemuxCompContext *demux = (DemuxCompContext *)pUserData; switch(eMessageId) { case PARSER_NOTIFY_VIDEO_STREAM_CHANGE: { CdxMediaInfoT parserMediaInfo; memset(&parserMediaInfo,0,sizeof(CdxMediaInfoT)); CdxParserGetMediaInfo(demux->pParser, &parserMediaInfo); setMediaInfo(&demux->mediaInfo, &parserMediaInfo); demux->mediaInfo.eContainerType = (enum ECONTAINER)demux->pParser->type; //* if have cacheThread, we should make the demuxThread do callback() //* or will cause some async bug if(demux->cacheThreadId != 0) { AwMessage msg; msg.messageId = DEMUX_COMMAND_VIDEO_STREAM_CHANGE; msg.params[0] = (uintptr_t)&demux->semStreamChange; msg.params[1] = 0; msg.params[2] = ((int*)param)[0]; AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semStreamChange, -1); } else { int nMsg = DEMUX_VIDEO_STREAM_CHANGE; demux->callback(demux->pUserData, nMsg, param); } break; } case PARSER_NOTIFY_AUDIO_STREAM_CHANGE: { CdxMediaInfoT parserMediaInfo; memset(&parserMediaInfo,0,sizeof(CdxMediaInfoT)); CdxParserGetMediaInfo(demux->pParser, &parserMediaInfo); setMediaInfo(&demux->mediaInfo, &parserMediaInfo); demux->mediaInfo.eContainerType = (enum ECONTAINER)demux->pParser->type; //* if have cacheThread, we should make the demuxThread do callback() //* or will cause some async bug if(demux->cacheThreadId != 0) { AwMessage msg1; msg1.messageId = DEMUX_COMMAND_AUDIO_STREAM_CHANGE; msg1.params[0] = (uintptr_t)&demux->semStreamChange; AwMessageQueuePostMessage(demux->mq, &msg1); SemTimedWait(&demux->semStreamChange, -1); } else { int nMsg = DEMUX_AUDIO_STREAM_CHANGE; demux->callback(demux->pUserData, nMsg, param); } break; } case STREAM_EVT_DOWNLOAD_START: { DownloadObject cur; if(!param) { //this message is sent by http stream, it is the first m3u8 file; or m3u8 file. //cur.uri = demux->source.uri; //cur.seqNum = -1; //cur.statusCode = 0; //int nMsg = DEMUX_NOTIFY_HLS_DOWNLOAD_START; //demux->callback(demux->pUserData, nMsg, (void*)&cur); logd("sent by http stream"); } else { // this message is sent by hls parser int nMsg = DEMUX_NOTIFY_HLS_DOWNLOAD_START; demux->callback(demux->pUserData, nMsg, param); } break; } case STREAM_EVT_DOWNLOAD_ERROR: { DownloadObject cur; char url[1024]; if(demux->pParser) { if(CdxParserControl(demux->pParser, CDX_PSR_CMD_GET_URL, (void*)url)) { loge("get url failed"); } } cur.uri = url; cur.statusCode = *((int*)param); demux->statusCode = cur.statusCode; int nMsg = DEMUX_NOTIFY_HLS_DOWNLOAD_ERROR; demux->callback(demux->pUserData, nMsg, (void*)&cur); logd("demux: STREAM_EVT_DOWNLOAD_ERROR, statusCode(%d)", cur.statusCode); break; } case STREAM_EVT_DOWNLOAD_END: { ExtraDataContainerT *extradata = (ExtraDataContainerT *)param; if(EXTRA_DATA_HTTP == extradata->extraDataType) { //* this message is sent by http stream, it is the first m3u8 file, not callback. } else if(EXTRA_DATA_HLS == extradata->extraDataType) { int nMsg = DEMUX_NOTIFY_HLS_DOWNLOAD_END; param = (DownloadObject *)extradata->extraData; demux->callback(demux->pUserData, nMsg, param); } break; } case STREAM_EVT_NET_DISCONNECT: { int flag = *(int*)param; if(demux->bDisconnet == 0 && flag == 1) { CDX_LOGD("STREAM_EVT_NET_DISCONNECT, param = %d", flag); demux->bDisconnet = 1; } if(demux->bDisconnet == 1 && flag == 0) { CDX_LOGD("reconnect success"); demux->bDisconnet = 0; if(demux->mLivemode == 1) { StreamCacheFlushAll(demux->pCache); demux->callback(demux->pUserData, DEMUX_NOTIFY_RESET_PLAYER, NULL); } } break; } case STREAM_EVT_CMCC_LOG_RECORD: { demux->callback(demux->pUserData, DEMUX_NOTIFY_LOG_RECORDER, param); break; } case PARSER_NOTIFY_TIMESHIFT_END_INFO: { TimeShiftEndInfoT *info = (TimeShiftEndInfoT *)param; logd("PARSER_NOTIFY_TIMESHIFT_END_INFO seqnum:%d, duration:%lld ms", info->timeShiftLastSeqNum, info->timeShiftDuration); demux->timeShiftLastSeqNum = info->timeShiftLastSeqNum; cdx_int64 dur = info->timeShiftDuration; demux->callback(demux->pUserData, DEMUX_NOTIFY_TIMESHIFT_DURATION, (void*)&dur); break; } case PARSER_NOTIFY_HLS_DISCONTINUITY: { demux->bDiscontinue = 1; break; } default: { logw("ignore demux callback message, eMessageId = 0x%x.", eMessageId); return -1; } } return 0; } DemuxComp* DemuxCompCreate(void) { DemuxCompContext* d; d = (DemuxCompContext*)malloc(sizeof(DemuxCompContext)); if(d == NULL) { loge("malloc memory fail."); return NULL; } memset(d, 0, sizeof(DemuxCompContext)); d->nStartPlayCacheSize = GetConfigParamterInt("start_play_cache_size",512)*1024; d->nCacheBufferSize = GetConfigParamterInt("cache_buffer_size",20*1024)*1024; d->nCacheBufferSizeLive = GetConfigParamterInt("cache_buffer_size_live",20*1024)*1024; d->nStartPlayCacheTime = 0; // we will set it later d->nMaxStartPlayChacheSize = GetConfigParamterInt("max_start_play_chache_size",150*1024)*1024; d->nMaxCacheBufferSize = GetConfigParamterInt("max_cache_buffer_size",200*1024)*1024; logv("StartPlayCacheSize(%d)",d->nStartPlayCacheSize); logv("CacheBufferSize(%d)",d->nCacheBufferSize); logv("StartPlayCacheTime(%d)",d->nStartPlayCacheTime); logv("MaxStartPlayChacheSize(%d)",d->nMaxStartPlayChacheSize); logv("MaxCacheBufferSize(%d)",d->nMaxCacheBufferSize); d->nCacheStatReportIntervalUs = 1000000; d->mStreamPrepared = 0; pthread_mutex_init(&d->mutex, NULL); sem_init(&d->semSetDataSource, 0, 0); sem_init(&d->semStart, 0, 0); sem_init(&d->semStop, 0, 0); sem_init(&d->semQuit, 0, 0); sem_init(&d->semClear, 0, 0); sem_init(&d->semCancelPrepare, 0, 0); sem_init(&d->semCancelSeek, 0, 0); sem_init(&d->semCache, 0, 0); sem_init(&d->semStreamChange, 0, 0); pthread_mutex_init(&d->lock, NULL); pthread_cond_init(&d->cond, NULL); d->mq = AwMessageQueueCreate(64, "demuxComp"); if(d->mq == NULL) { loge("AwMessageQueueCreate() return fail."); pthread_mutex_destroy(&d->mutex); sem_destroy(&d->semSetDataSource); sem_destroy(&d->semStart); sem_destroy(&d->semStop); sem_destroy(&d->semQuit); sem_destroy(&d->semClear); sem_destroy(&d->semCancelPrepare); sem_destroy(&d->semCancelSeek); sem_destroy(&d->semCache); sem_destroy(&d->semStreamChange); free(d); return NULL; } d->mqCache = AwMessageQueueCreate(64, "DemuxComp"); if(d->mqCache == NULL) { loge("AwMessageQueueCreate() return fail."); AwMessageQueueDestroy(d->mq); pthread_mutex_destroy(&d->mutex); sem_destroy(&d->semSetDataSource); sem_destroy(&d->semStart); sem_destroy(&d->semStop); sem_destroy(&d->semQuit); sem_destroy(&d->semClear); sem_destroy(&d->semCancelPrepare); sem_destroy(&d->semCancelSeek); sem_destroy(&d->semCache); sem_destroy(&d->semStreamChange); free(d); return NULL; } if(pthread_create(&d->threadId, NULL, DemuxThread, (void*)d) != 0) { loge("can not create thread for demux component."); AwMessageQueueDestroy(d->mq); AwMessageQueueDestroy(d->mqCache); pthread_mutex_destroy(&d->mutex); sem_destroy(&d->semSetDataSource); sem_destroy(&d->semStart); sem_destroy(&d->semStop); sem_destroy(&d->semQuit); sem_destroy(&d->semClear); sem_destroy(&d->semCancelPrepare); sem_destroy(&d->semCancelSeek); sem_destroy(&d->semCache); sem_destroy(&d->semStreamChange); free(d); return NULL; } d->pCache = StreamCacheCreate(); d->pBuffering = NULL; return (DemuxComp*)d; } void DemuxCompDestroy(DemuxComp* d) { void* status; AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; //* lock mutex to sync with the message process thread when processing SEEK message. //* when processing the SEEK message, we clear the Parser's force stop flag by calling //* CdxParserClrForceStop(). if we're playing a network stream and processing //* seek message, when user cancel the seek message, we should set the parser's //* force stop flag by calling CdxParserForceStop() to //* prevend from blocking at the network io. pthread_mutex_lock(&demux->mutex); demux->bStopping = 1; pthread_mutex_unlock(&demux->mutex); //* send a quit message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_QUIT; msg.params[0] = (uintptr_t)&demux->semQuit; AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semQuit, -1); pthread_join(demux->threadId, &status); StreamCacheDestroy(demux->pCache); if(demux->mq != NULL) AwMessageQueueDestroy(demux->mq); if(demux->mqCache != NULL) AwMessageQueueDestroy(demux->mqCache); pthread_mutex_destroy(&demux->mutex); pthread_mutex_destroy(&demux->lock); pthread_cond_destroy(&demux->cond); sem_destroy(&demux->semSetDataSource); sem_destroy(&demux->semStart); sem_destroy(&demux->semStop); sem_destroy(&demux->semQuit); sem_destroy(&demux->semClear); sem_destroy(&demux->semCancelPrepare); sem_destroy(&demux->semCancelSeek); sem_destroy(&demux->semCache); sem_destroy(&demux->semStreamChange); free(demux); return; } void DemuxCompClear(DemuxComp* d) //* clear the data source, like just created. { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; //* lock mutex to sync with the message process thread when processing SEEK message. //* when processing the SEEK message, we clear the Parser's force stop flag by calling //* CdxParserClrForceStop(). if we're playing a network stream and processing //* seek message, when user cancel the seek message, we should set the parser's //* force stop flag by calling CdxParserForceStop() to //* prevend from blocking at the network io. pthread_mutex_lock(&demux->mutex); demux->bStopping = 1; pthread_mutex_unlock(&demux->mutex); //* send clear message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_CLEAR; msg.params[0] = (uintptr_t)&demux->semClear; AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semClear, -1); return; } int DemuxCompSetUrlSource(DemuxComp* d, void* pHTTPServer, const char* pUrl, const CdxKeyedVectorT* pHeaders) { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; msg.messageId = DEMUX_COMMAND_SET_SOURCE; msg.params[0] = (uintptr_t)&demux->semSetDataSource; msg.params[1] = (uintptr_t)&demux->nSetDataSourceReply; msg.params[2] = SOURCE_TYPE_URL; msg.params[3] = (uintptr_t)pUrl; msg.params[4] = (uintptr_t)pHeaders; msg.params[5] = (uintptr_t)pHTTPServer; AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semSetDataSource, -1); return demux->nSetDataSourceReply; } int DemuxCompSetFdSource(DemuxComp* d, int fd, int64_t nOffset, int64_t nLength) { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; //* send a set data source message. msg.messageId = DEMUX_COMMAND_SET_SOURCE; msg.params[0] = (uintptr_t)&demux->semSetDataSource; msg.params[1] = (uintptr_t)&demux->nSetDataSourceReply; msg.params[2] = SOURCE_TYPE_FD; msg.params[3] = fd; //* params[3] = fd. msg.params[4] = (uintptr_t)(nOffset>>32); //* params[4] = high 32 bits of offset. msg.params[5] = (uintptr_t)(nOffset & 0xffffffff); //* params[5] = low 32 bits of offset. msg.params[6] = (uintptr_t)(nLength>>32); //* params[6] = high 32 bits of length. msg.params[7] = (uintptr_t)(nLength & 0xffffffff); //* params[7] = low 32 bits of length. AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semSetDataSource, -1); return demux->nSetDataSourceReply; } int DemuxCompSetStreamSource(DemuxComp* d, const char* pUri) { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; //* send a set data source message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_SET_SOURCE; msg.params[0] = (uintptr_t)&demux->semSetDataSource; msg.params[1] = (uintptr_t)&demux->nSetDataSourceReply; msg.params[2] = SOURCE_TYPE_ISTREAMSOURCE; msg.params[3] = (uintptr_t)pUri; //* params[3] = uri of IStreamSource. AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semSetDataSource, -1); return demux->nSetDataSourceReply; } int DemuxCompSetMediaDataSource(DemuxComp* d, const char* pUri) { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; //* send a set data source message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_SET_SOURCE; msg.params[0] = (uintptr_t)&demux->semSetDataSource; msg.params[1] = (uintptr_t)&demux->nSetDataSourceReply; msg.params[2] = SOURCE_TYPE_DATASOURCE; msg.params[3] = (uintptr_t)pUri; //* params[3] = uri of MediaDataSource. AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semSetDataSource, -1); return demux->nSetDataSourceReply; } int DemuxCompSetPlayer(DemuxComp* d, Player* player) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; demux->pPlayer = player; return 0; } int DemuxCompSetHdcpOps(DemuxComp* d, struct HdcpOpsS* pHdcp) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; demux->pHDCPOps= pHdcp; return 0; } int DemuxCompSetCallback(DemuxComp* d, DemuxCallback callback, void* pUserData) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; demux->callback = callback; demux->pUserData = pUserData; return 0; } int DemuxCompPrepareAsync(DemuxComp* d) { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; //* clear this flag, you can set this flag to make the parser quit preparing. demux->bCancelPrepare = 0; demux->eStatus = DEMUX_STATUS_PREPARING; //* send a prepare message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_PREPARE; AwMessageQueuePostMessage(demux->mq, &msg); return 0; } //* should call back DEMUX_PREPARE_FINISH message. int DemuxCompCancelPrepare(DemuxComp* d) { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; demux->bCancelPrepare = 1; //* set this flag to make the parser quit preparing. pthread_mutex_lock(&demux->mutex); if(demux->pParser) { CdxParserForceStop(demux->pParser); } else if(demux->pStream) { CdxStreamForceStop(demux->pStream); } pthread_mutex_unlock(&demux->mutex); //* send a prepare. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_CANCEL_PREPARE; msg.params[0] = (uintptr_t)&demux->semCancelPrepare; msg.params[1] = 0; //* no reply. AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semCancelPrepare, -1); return 0; } int DemuxProbeH265RefPictureNumber(char* pDataBuf, int nDataLen) { return probeH265RefPictureNumber((cdx_uint8 *)pDataBuf, (cdx_uint32)nDataLen); } MediaInfo* DemuxCompGetMediaInfo(DemuxComp* d) { DemuxCompContext* demux; MediaInfo* mi; MediaInfo* myMediaInfo; int i; VideoStreamInfo* pVideoStreamInfo; AudioStreamInfo* pAudioStreamInfo; SubtitleStreamInfo* pSubtitleStreamInfo; int nCodecSpecificDataLen; char* pCodecSpecificData; demux = (DemuxCompContext*)d; myMediaInfo = &demux->mediaInfo; mi = (MediaInfo*)malloc(sizeof(MediaInfo)); if(mi == NULL) { loge("can not alloc memory for media info."); return NULL; } memset(mi, 0, sizeof(MediaInfo)); mi->nFileSize = myMediaInfo->nFileSize; mi->nDurationMs = myMediaInfo->nDurationMs; mi->nBitrate = myMediaInfo->nBitrate; mi->eContainerType = myMediaInfo->eContainerType; mi->bSeekable = myMediaInfo->bSeekable; mi->nFirstPts = myMediaInfo->nFirstPts; memcpy(mi->cRotation,myMediaInfo->cRotation,4*sizeof(unsigned char)); logv("video stream num = %d, video stream info = %p", myMediaInfo->nVideoStreamNum, myMediaInfo->pVideoStreamInfo); if(myMediaInfo->nVideoStreamNum > 0) { pVideoStreamInfo = (VideoStreamInfo*)malloc( sizeof(VideoStreamInfo)*myMediaInfo->nVideoStreamNum); if(pVideoStreamInfo == NULL) { loge("can not alloc memory for media info."); clearMediaInfo(mi); return NULL; } memset(pVideoStreamInfo, 0, sizeof(VideoStreamInfo)*myMediaInfo->nVideoStreamNum); mi->pVideoStreamInfo = pVideoStreamInfo; for(i=0; inVideoStreamNum; i++) { pVideoStreamInfo = &mi->pVideoStreamInfo[i]; memcpy(pVideoStreamInfo, &myMediaInfo->pVideoStreamInfo[i], sizeof(VideoStreamInfo)); pCodecSpecificData = pVideoStreamInfo->pCodecSpecificData; nCodecSpecificDataLen = pVideoStreamInfo->nCodecSpecificDataLen; pVideoStreamInfo->pCodecSpecificData = NULL; pVideoStreamInfo->nCodecSpecificDataLen = 0; pVideoStreamInfo->bSecureStreamFlag = myMediaInfo->pVideoStreamInfo[i].bSecureStreamFlag; if(pCodecSpecificData != NULL && nCodecSpecificDataLen > 0) { pVideoStreamInfo->pCodecSpecificData = (char*)malloc(nCodecSpecificDataLen); if(pVideoStreamInfo->pCodecSpecificData == NULL) { loge("can not alloc memory for media info."); clearMediaInfo(mi); return NULL; } memcpy(pVideoStreamInfo->pCodecSpecificData, pCodecSpecificData, nCodecSpecificDataLen); pVideoStreamInfo->nCodecSpecificDataLen = nCodecSpecificDataLen; } if (demux->pParser->type == CDX_PARSER_TS || demux->pParser->type == CDX_PARSER_BD || demux->pParser->type == CDX_PARSER_HLS) { pVideoStreamInfo->bIsFramePackage = 0; /* stream package */ } else { pVideoStreamInfo->bIsFramePackage = 1; /* frame package */ } } mi->nVideoStreamNum = myMediaInfo->nVideoStreamNum; } logv("video stream num = %d, video stream info = %p", mi->nVideoStreamNum, mi->pVideoStreamInfo); if(myMediaInfo->nAudioStreamNum > 0) { pAudioStreamInfo = (AudioStreamInfo*)malloc( sizeof(AudioStreamInfo)*myMediaInfo->nAudioStreamNum); if(pAudioStreamInfo == NULL) { loge("can not alloc memory for media info."); clearMediaInfo(mi); return NULL; } memset(pAudioStreamInfo, 0, sizeof(AudioStreamInfo)*myMediaInfo->nAudioStreamNum); mi->pAudioStreamInfo = pAudioStreamInfo; for(i=0; inAudioStreamNum; i++) { pAudioStreamInfo = &mi->pAudioStreamInfo[i]; memcpy(pAudioStreamInfo, &myMediaInfo->pAudioStreamInfo[i], sizeof(AudioStreamInfo)); pCodecSpecificData = pAudioStreamInfo->pCodecSpecificData; nCodecSpecificDataLen = pAudioStreamInfo->nCodecSpecificDataLen; pAudioStreamInfo->pCodecSpecificData = NULL; pAudioStreamInfo->nCodecSpecificDataLen = 0; if(pCodecSpecificData != NULL && nCodecSpecificDataLen > 0) { pAudioStreamInfo->pCodecSpecificData = (char*)malloc(nCodecSpecificDataLen); if(pAudioStreamInfo->pCodecSpecificData == NULL) { loge("can not alloc memory for media info."); clearMediaInfo(mi); return NULL; } memcpy(pAudioStreamInfo->pCodecSpecificData, pCodecSpecificData, nCodecSpecificDataLen); pAudioStreamInfo->nCodecSpecificDataLen = nCodecSpecificDataLen; } } mi->nAudioStreamNum = myMediaInfo->nAudioStreamNum; } if(myMediaInfo->nSubtitleStreamNum > 0) { pSubtitleStreamInfo = (SubtitleStreamInfo*)malloc (sizeof(SubtitleStreamInfo)*myMediaInfo->nSubtitleStreamNum); if(pSubtitleStreamInfo == NULL) { loge("can not alloc memory for media info."); clearMediaInfo(mi); return NULL; } memset(pSubtitleStreamInfo, 0, sizeof(SubtitleStreamInfo)*myMediaInfo->nSubtitleStreamNum); mi->pSubtitleStreamInfo = pSubtitleStreamInfo; for(i=0; inSubtitleStreamNum; i++) { pSubtitleStreamInfo = &mi->pSubtitleStreamInfo[i]; memcpy(pSubtitleStreamInfo, &myMediaInfo->pSubtitleStreamInfo[i], sizeof(SubtitleStreamInfo)); //* parser only process imbedded subtitle stream in media file. pSubtitleStreamInfo->pUrl = NULL; pSubtitleStreamInfo->fd = -1; pSubtitleStreamInfo->fdSub = -1; } mi->nSubtitleStreamNum = myMediaInfo->nSubtitleStreamNum; } return mi; } int DemuxCompStart(DemuxComp* d) { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; if(demux->eStatus == DEMUX_STATUS_STARTED && demux->bBufferring != 1) { logi("demux component already in started status."); return 0; } if(demux->eStatus == DEMUX_STATUS_COMPLETE) { logv("demux component is in complete status."); return 0; } if(pthread_equal(pthread_self(), demux->threadId) || pthread_equal(pthread_self(), demux->cacheThreadId)) { //* called from demux callback to awplayer. if(demux->bSeeking) { demux->eStatus = DEMUX_STATUS_STARTED; } return 0; } //* send a start message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_START; msg.params[0] = (uintptr_t)&demux->semStart; msg.params[1] = (uintptr_t)&demux->nStartReply; AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semStart, -1); return demux->nStartReply; } int DemuxCompStop(DemuxComp* d) //* close the data source, must call prepare again to restart. { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; //* lock mutex to sync with the message process thread when processing SEEK message. //* when processing the SEEK message, we clear the Parser's force stop flag by calling //* CdxParserClrForceStop(). if we're playing a network stream and processing //* seek message, when user cancel the seek message, we should set the parser's //* force stop flag by calling CdxParserForceStop() to //* prevend from blocking at the network io. pthread_mutex_lock(&demux->mutex); demux->bStopping = 1; pthread_mutex_unlock(&demux->mutex); if(demux->pParser != NULL) CdxParserForceStop(demux->pParser); //* quit from reading or seeking. else if(demux->pStream != NULL) CdxStreamForceStop(demux->pStream); //* send a stop message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_STOP; msg.params[0] = (uintptr_t)&demux->semStop; msg.params[1] = (uintptr_t)&demux->nStopReply; AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semStop, -1); return demux->nStopReply; } int DemuxCompPause(DemuxComp* d) //* no pause status in demux component, return OK immediately. { DemuxCompContext* demux; demux = (DemuxCompContext*)d; if(demux->eStatus != DEMUX_STATUS_STARTED) { logw("invalid pause operation, demux component not in started status."); return -1; } //* currently the demux component has no pause status, it will keep sending data until stopped. return 0; } int DemuxCompGetStatus(DemuxComp* d) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; return demux->eStatus; } int DemuxCompSeekTo(DemuxComp* d, int nSeekTimeMs, SeekModeType nSeekModeType) { AwMessage msg; DemuxCompContext* demux; logd("+++++ DemuxCompSeekTo"); demux = (DemuxCompContext*)d; demux->bCancelSeek = 0; demux->bSeeking = 1; if(demux->pParser != NULL && demux->eStatus == DEMUX_STATUS_STARTED) CdxParserForceStop(demux->pParser); //* quit from reading. demux->nSeekModeType = nSeekModeType; //* send a seek message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_SEEK; msg.params[0] = 0; msg.params[1] = 0; msg.params[2] = nSeekTimeMs; AwMessageQueuePostMessage(demux->mq, &msg); return 0; } int DemuxCompSeekToResetUrl(DemuxComp* d, int nSeekTimeMs) { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; logd("+++++ DemuxCompSeekToResetUrl"); demux->bCancelSeek = 0; demux->bSeeking = 1; if(demux->pParser != NULL && demux->eStatus == DEMUX_STATUS_STARTED) CdxParserForceStop(demux->pParser); //* quit from reading. //* send a resetUrl message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_RESET_URL; msg.params[0] = 0; msg.params[1] = 0; msg.params[2] = nSeekTimeMs; AwMessageQueuePostMessage(demux->mq, &msg); return 0; } int DemuxCompCancelSeek(DemuxComp* d) //* should not call back DEMUX_SEEK_FINISH message. { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; //* lock mutex to sync with the message process thread when processing SEEK message. //* when processing the SEEK message, we clear the Parser's force stop flag by calling //* CdxParserClrForceStop(). if we're playing a network stream and processing //* seek message, when user cancel the seek message, we should set the parser's //* force stop flag by calling CdxParserForceStop() to //* prevend from blocking at the network io. pthread_mutex_lock(&demux->mutex); demux->bCancelSeek = 1; pthread_mutex_unlock(&demux->mutex); if(demux->pParser != NULL) CdxParserForceStop(demux->pParser); //* send a prepare. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_CANCEL_SEEK; msg.params[0] = (uintptr_t)&demux->semCancelSeek; msg.params[1] = 0; //* no reply. AwMessageQueuePostMessage(demux->mq, &msg); SemTimedWait(&demux->semCancelSeek, -1); return 0; } int DemuxCompNotifyFirstFrameShowed(DemuxComp* d) //* notify video first frame showed. { AwMessage msg; DemuxCompContext* demux; demux = (DemuxCompContext*)d; //* send a start message. memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_NOTIFY_FIRST_FRAME_SHOWED; AwMessageQueuePostMessage(demux->mq, &msg); return 0; } int DemuxCompSetCacheStatReportInterval(DemuxComp* d, int ms) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; demux->nCacheStatReportIntervalUs = ms*1000; return 0; } int DemuxCompSetCachePolicy(DemuxComp* d, enum ECACHEPOLICY eCachePolicy, int nStartPlaySize, int nStartPlayTimeMs, int nMaxBufferSize) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; if(demux->eStatus == DEMUX_STATUS_IDLE || demux->eStatus == DEMUX_STATUS_INITIALIZED || demux->eStatus == DEMUX_STATUS_PREPARING) { loge("not prepared yet, can not set cache parameter."); return -1; } if(demux->bFileStream) { //* no need to set cache parameter for file stream. return -1; } if (nStartPlaySize > 0) demux->nStartPlayCacheSize = nStartPlaySize; if (nStartPlayTimeMs > 0) demux->nStartPlayCacheTime = nStartPlayTimeMs; if (nMaxBufferSize > 0 && nMaxBufferSize <= demux->nMaxCacheBufferSize) { demux->nCacheBufferSize = nMaxBufferSize; demux->nCacheBufferSizeLive = nMaxBufferSize; } switch(eCachePolicy) { case CACHE_POLICY_SMOOTH: { demux->eCachePolicy = CACHE_POLICY_SMOOTH; StreamCacheSetSize(demux->pCache, demux->nCacheBufferSize, demux->nCacheBufferSize); break; } case CACHE_POLICY_ADAPTIVE: { DemuxCompUseDefaultCacheParams(demux); demux->eCachePolicy = CACHE_POLICY_ADAPTIVE; break; } default: { loge("no such policy"); abort(); } } return 0; } int DemuxCompGetLiveMode(DemuxComp* d) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; return demux->mLivemode; } int DemuxCompSetLiveMode(DemuxComp* d, int liveMode) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; demux->mLivemode = liveMode; return 0; } int DemuxCompGetCacheSize(DemuxComp* d) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; return StreamCacheGetSize(demux->pCache); } static int DemuxCompUseDefaultCacheParams(DemuxCompContext* demux) { int nStartPlaySize; int nMaxBufferSize; int nCacheTimeMs; int nBitrate; nCacheTimeMs = demux->nStartPlayCacheTime; if(nCacheTimeMs > demux->mediaInfo.nDurationMs) nCacheTimeMs = demux->mediaInfo.nDurationMs; if(demux->mediaInfo.nFileSize <= 0) { if(demux->mediaInfo.nBitrate > 0) nBitrate = demux->mediaInfo.nBitrate; else nBitrate = 200*1024; //* default vod stream bitrate is 200 kbits. } else { if(demux->mediaInfo.nDurationMs != 0) nBitrate = (int)(demux->mediaInfo.nFileSize*8*1000/demux->mediaInfo.nDurationMs); else nBitrate = 200*1024; //* default vod stream bitrate is 200 kbits. } if(nBitrate <= 64*1024) nBitrate = 64*1024; if(nBitrate > 20*1024*1024) nBitrate = 20*1024*1024; nStartPlaySize = (int)(nBitrate * (int64_t)nCacheTimeMs / (8*1000)); if(nStartPlaySize < demux->nStartPlayCacheSize) nStartPlaySize = demux->nStartPlayCacheSize; if(nStartPlaySize > demux->nMaxStartPlayChacheSize) nStartPlaySize = demux->nMaxStartPlayChacheSize; nMaxBufferSize = nStartPlaySize*4/3; if (demux->bLiveStream) { if (nMaxBufferSize < demux->nCacheBufferSizeLive) nMaxBufferSize = demux->nCacheBufferSizeLive; } else { if (nMaxBufferSize < demux->nCacheBufferSize) nMaxBufferSize = demux->nCacheBufferSize; } if(nMaxBufferSize > demux->nMaxCacheBufferSize) nMaxBufferSize = demux->nMaxCacheBufferSize; StreamCacheSetSize(demux->pCache, nStartPlaySize, nMaxBufferSize); return 0; } int DemuxCompSetBufferingSettings(DemuxComp* d, AwBufferingSettings* pBuffering) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; if(pBuffering == NULL) { loge("Buffering is null. please check it."); return -1; } demux->pBuffering = pBuffering; //* Only BufferingSettings would assign a non-null pointer to it if (demux->bLiveStream) { if (demux->pBuffering->mRebufferingWatermarkHighKB < demux->nCacheBufferSizeLive / 1024) demux->pBuffering->mRebufferingWatermarkHighKB = demux->nCacheBufferSizeLive / 1024; } else { if (demux->pBuffering->mRebufferingWatermarkHighKB < demux->nCacheBufferSize / 1024) demux->pBuffering->mRebufferingWatermarkHighKB = demux->nCacheBufferSize / 1024; } if(demux->pBuffering->mRebufferingWatermarkHighKB > demux->nMaxCacheBufferSize / 1024) demux->pBuffering->mRebufferingWatermarkHighKB = demux->nMaxCacheBufferSize / 1024; StreamSetBufferingSettings(demux->pCache, demux->pBuffering); return 0; } int DemuxCompGetBufferingSettings(DemuxComp* d, AwBufferingSettings* pBuffering) { DemuxCompContext* demux; demux = (DemuxCompContext*)d; if(demux == NULL || pBuffering == NULL) { loge("Buffering is null. please check it."); return -1; } if(demux->pBuffering == NULL) { pBuffering->mInitialBufferingMode = AW_BUFFERING_MODE_SIZE_ONLY; pBuffering->mRebufferingMode = AW_BUFFERING_MODE_SIZE_ONLY; pBuffering->mInitialWatermarkKB = 0; pBuffering->mInitialWatermarkMs = -1; pBuffering->mRebufferingWatermarkHighKB = GetConfigParamterInt("cache_buffer_size",20*1024); pBuffering->mRebufferingWatermarkLowKB = GetConfigParamterInt("start_play_cache_size",512); pBuffering->mRebufferingWatermarkHighMs = -1; pBuffering->mRebufferingWatermarkLowMs = -1; } else { memcpy(pBuffering, demux->pBuffering, sizeof(AwBufferingSettings)); } return 0; } static void AdjustCacheParams(DemuxCompContext* demux) { int nBitrate = 0; if(demux->eCachePolicy != CACHE_POLICY_ADAPTIVE) return; nBitrate = demux->mediaInfo.nBitrate; if (nBitrate <= 0) nBitrate = PlayerGetVideoBitrate(demux->pPlayer) + PlayerGetAudioBitrate(demux->pPlayer); logd("bitrate = %d", nBitrate); if(nBitrate > 0) { if(nBitrate <= 64*1024) nBitrate = 64*1024; else if(nBitrate > 20*1024*1024) nBitrate = 20*1024*1024; DemuxCompAdjustCacheParamsWithBitrate(demux, nBitrate); } } static int DemuxCompAdjustCacheParamsWithBitrate(DemuxCompContext* demux, int nBitrate) { int nStartPlaySize; int nMaxBufferSize; int nCacheTimeMs = demux->nStartPlayCacheTime; if(demux->mediaInfo.nDurationMs > 0 && nCacheTimeMs > demux->mediaInfo.nDurationMs) nCacheTimeMs = demux->mediaInfo.nDurationMs; nStartPlaySize = (int)(nBitrate * (int64_t)nCacheTimeMs / (8*1000)); if(nStartPlaySize < demux->nStartPlayCacheSize) nStartPlaySize = demux->nStartPlayCacheSize; if(nStartPlaySize > demux->nMaxStartPlayChacheSize) nStartPlaySize = demux->nMaxStartPlayChacheSize; nMaxBufferSize = nStartPlaySize*4/3; if (demux->bLiveStream) { if (nMaxBufferSize < demux->nCacheBufferSizeLive) nMaxBufferSize = demux->nCacheBufferSizeLive; } else { if (nMaxBufferSize < demux->nCacheBufferSize) nMaxBufferSize = demux->nCacheBufferSize; } if(nMaxBufferSize > demux->nMaxCacheBufferSize) nMaxBufferSize = demux->nMaxCacheBufferSize; StreamCacheSetSize(demux->pCache, nStartPlaySize, nMaxBufferSize); return 0; } static int setVideoStreamDataInfo(MediaStreamDataInfo *streamDataInfo, struct VideoInfo* pVideoinfo) { logv("****pVideoinfo = %p, pVideoinfo->videoNum = %d",pVideoinfo, pVideoinfo->videoNum); size_t size = sizeof(VideoStreamInfo)*pVideoinfo->videoNum; VideoStreamInfo *pVideoStreamInfo = (VideoStreamInfo*)calloc(1, size); if(pVideoStreamInfo == NULL) { loge("malloc video stream info fail, size=%d.", (int)size); return -1; } int i; for(i = 0; i < pVideoinfo->videoNum; i++) { memcpy(&pVideoStreamInfo[i], &pVideoinfo->video[i], sizeof(VideoStreamInfo)); if (!pVideoinfo->video[i].pCodecSpecificData) continue; size = pVideoinfo->video[i].nCodecSpecificDataLen; pVideoStreamInfo[i].pCodecSpecificData = (char*)malloc(size); if(pVideoStreamInfo[i].pCodecSpecificData == NULL) { loge("malloc video specific data fail, size=%d", (int)size); int j; for(j = 0; j < i; j++) { if(pVideoStreamInfo[j].pCodecSpecificData) { free(pVideoStreamInfo[j].pCodecSpecificData); pVideoStreamInfo[j].pCodecSpecificData = NULL; } } free(pVideoStreamInfo); return -1; } memcpy(pVideoStreamInfo[i].pCodecSpecificData, pVideoinfo->video[i].pCodecSpecificData, size); pVideoStreamInfo[i].nCodecSpecificDataLen = size; } streamDataInfo->pStreamInfo = pVideoStreamInfo; streamDataInfo->nStreamChangeFlag = 1; streamDataInfo->nStreamChangeNum = pVideoinfo->videoNum; return 0; } static int setAudioStreamDataInfo(MediaStreamDataInfo *streamDataInfo, struct AudioInfo* pAudioinfo) { size_t size = sizeof(AudioStreamInfo)*pAudioinfo->audioNum; AudioStreamInfo *pAudioStreamInfo = (AudioStreamInfo*)calloc(1, size); if(pAudioStreamInfo == NULL) { loge("malloc audio stream info fail, size=%d", (int)size); return -1; } int i; for(i = 0; i < pAudioinfo->audioNum; i++) { memcpy(&pAudioStreamInfo[i], &pAudioinfo->audio[i], sizeof(AudioStreamInfo)); if(!pAudioinfo->audio[i].pCodecSpecificData) continue; size = pAudioinfo->audio[i].nCodecSpecificDataLen; pAudioStreamInfo[i].pCodecSpecificData = (char*)calloc(1, size); if(pAudioStreamInfo[i].pCodecSpecificData == NULL) { loge("malloc audio specific data fail, size=%d", (int)size); int j; for(j = 0; j < i; j++) { if(pAudioStreamInfo[j].pCodecSpecificData) { free(pAudioStreamInfo[j].pCodecSpecificData); pAudioStreamInfo[j].pCodecSpecificData = NULL; } } free(pAudioStreamInfo); return -1; } memcpy(pAudioStreamInfo[i].pCodecSpecificData, pAudioinfo->audio[i].pCodecSpecificData, size); pAudioStreamInfo[i].nCodecSpecificDataLen = size; } streamDataInfo->pStreamInfo = pAudioStreamInfo; streamDataInfo->nStreamChangeFlag = 1; streamDataInfo->nStreamChangeNum = pAudioinfo->audioNum; return 0; } static int setCacheNodeVideo(CdxPacketT *packet, CacheNode *node) { struct VideoInfo *info = (struct VideoInfo *)calloc(1, sizeof(*info)); if(!info) { loge("malloc fail, size=%d.", (int)sizeof(*info)); return -1; } memcpy(info, packet->info, sizeof(struct VideoInfo)); int i; for(i = 0; i < info->videoNum; i++) { size_t size = info->video[i].nCodecSpecificDataLen; if(!info->video[i].pCodecSpecificData || !size) continue; info->video[i].pCodecSpecificData = (char *)malloc(size); if(!info->video[i].pCodecSpecificData) { loge("malloc fail, size=%d.", (int)size); int j; for(j = 0; j < i; j++) { if(info->video[j].pCodecSpecificData) { free(info->video[j].pCodecSpecificData); info->video[j].pCodecSpecificData = NULL; } } free(info); return -1; } memcpy(info->video[i].pCodecSpecificData, ((struct VideoInfo *)packet->info)->video[i].pCodecSpecificData, size); } CdxAtomicSet(&info->ref, 0); CdxAtomicInc(&info->ref); node->info = info; node->infoVersion = 1; return 0; } static int setCacheNodeAudio(CdxPacketT *packet, CacheNode *node) { struct AudioInfo *info = (struct AudioInfo *)calloc(1, sizeof(*info)); if(!info) { loge("malloc fail, size=%d.", (int)sizeof(*info)); return -1; } memcpy(info, packet->info, sizeof(struct AudioInfo)); int i; for(i = 0; i < info->audioNum; i++) { size_t size = info->audio[i].nCodecSpecificDataLen; if(!info->audio[i].pCodecSpecificData || !size) continue; info->audio[i].pCodecSpecificData = (char *)malloc(size); if(!info->audio[i].pCodecSpecificData) { loge("malloc fail, size=%d.", (int)size); int j; for(j = 0; j < i; j++) { if(info->audio[j].pCodecSpecificData) { free(info->audio[j].pCodecSpecificData); info->audio[j].pCodecSpecificData = NULL; } } free(info); return -1; } memcpy(info->audio[i].pCodecSpecificData, ((struct AudioInfo *)packet->info)->audio[i].pCodecSpecificData, size); } CdxAtomicSet(&info->ref, 0); CdxAtomicInc(&info->ref); node->info = info; node->infoVersion = 1; return 0; } static void* DemuxThread(void* arg) { AwMessage msg; AwMessage newMsg; int ret; sem_t* pReplySem; int* pReplyValue; DemuxCompContext* demux; int64_t nLastCacheStateNotifyTimeUs; int64_t nLastBufferingStartTimeUs; int64_t nCacheStartPlaySizeIntervalUs; int64_t nCurTimeUs; int bVideoFirstFrameShowed; int nVideoInfoVersion = 0; int nAudioInfoVersion = 0; //* Declares below is to avoid stack overflow by goto statement. CdxPacketT packet; enum EMEDIATYPE ePlayerMediaType; MediaStreamDataInfo streamDataInfo; int nStreamIndex; demux = (DemuxCompContext*)arg; nLastCacheStateNotifyTimeUs = 0; nLastBufferingStartTimeUs = 0; nCacheStartPlaySizeIntervalUs = 2000000; //* adjust start play size every 2 seconds. bVideoFirstFrameShowed = 0; demux->timeShiftLastSeqNum =-1; while(1) { if(AwMessageQueueGetMessage(demux->mq, &msg) < 0) { loge("get message fail."); continue; } process_message: pReplySem = (sem_t*)msg.params[0]; pReplyValue = (int*)msg.params[1]; if(msg.messageId == DEMUX_COMMAND_SET_SOURCE) { logv("process message DEMUX_COMMAND_SET_SOURCE."); demux->nSourceType = (int)msg.params[2]; if(demux->nSourceType == SOURCE_TYPE_URL) { //* data source of url path. char* uri; CdxKeyedVectorT* pHeaders; uri = (char*)msg.params[3]; pHeaders = (CdxKeyedVectorT*)msg.params[4]; void* pHTTPServer; pHTTPServer = (void*)msg.params[5]; if(setDataSourceFields(demux, &demux->source, pHTTPServer, uri, pHeaders) == 0) { demux->eStatus = DEMUX_STATUS_INITIALIZED; if(pReplyValue != NULL) *pReplyValue = 0; } else { demux->eStatus = DEMUX_STATUS_IDLE; if(pReplyValue != NULL) *pReplyValue = -1; } if(pReplySem != NULL) sem_post(pReplySem); continue; } else if(demux->nSourceType == SOURCE_TYPE_FD) { //* data source is a file descriptor. int fd; long long nOffset; long long nLength; char str[128]; clearDataSourceFields(&demux->source); fd = msg.params[3]; nOffset = msg.params[4]; nOffset<<=32; nOffset |= msg.params[5]; nLength = msg.params[6]; nLength<<=32; nLength |= msg.params[7]; memset(&demux->source, 0x00, sizeof(demux->source)); sprintf(str, "fd://%d?offset=%lld&length=%lld", fd, nOffset, nLength); demux->source.uri = strdup(str); if(demux->source.uri != NULL) { demux->eStatus = DEMUX_STATUS_INITIALIZED; if(pReplyValue != NULL) *pReplyValue = 0; } else { loge("can not dump string to represent fd source."); demux->eStatus = DEMUX_STATUS_IDLE; if(pReplyValue != NULL) *pReplyValue = -1; } if(pReplySem != NULL) sem_post(pReplySem); continue; } else if(demux->nSourceType == SOURCE_TYPE_DATASOURCE) { //* data source of MediaDataSource interface. clearDataSourceFields(&demux->source); memset(&demux->source, 0x00, sizeof(demux->source)); char *uri = (char*)msg.params[3]; demux->source.uri = strdup(uri); if(demux->source.uri != NULL) { demux->eStatus = DEMUX_STATUS_INITIALIZED; if(pReplyValue != NULL) *pReplyValue = 0; } else { loge("can not dump string to represent interface."); demux->eStatus = DEMUX_STATUS_IDLE; if(pReplyValue != NULL) *pReplyValue = -1; } if(pReplySem != NULL) sem_post(pReplySem); continue; } else { //* data source of IStringSource interface. clearDataSourceFields(&demux->source); memset(&demux->source, 0x00, sizeof(demux->source)); char *uri = (char*)msg.params[3]; demux->source.uri = strdup(uri); if(demux->source.uri != NULL) { demux->eStatus = DEMUX_STATUS_INITIALIZED; if(pReplyValue != NULL) *pReplyValue = 0; } else { loge("can not dump string to represent interface."); demux->eStatus = DEMUX_STATUS_IDLE; if(pReplyValue != NULL) *pReplyValue = -1; } if(pReplySem != NULL) sem_post(pReplySem); continue; } } //* end DEMUX_COMMAND_SET_SOURCE. else if(msg.messageId == DEMUX_COMMAND_PREPARE) { int flags = 0, ret = 0; int tryTimes = 0; logd("process message DEMUX_COMMAND_PREPARE."); __prepareBegin: if(demux->pParser) { //* should not run here, pParser should be NULL under INITIALIZED or STOPPED status. logw("demux->pParser != NULL when DEMUX_COMMAND_PREPARE message received."); CdxParserClose(demux->pParser); demux->pParser = NULL; demux->pStream = NULL; } else if(demux->pStream) { CdxStreamClose(demux->pStream); demux->pStream = NULL; } if(demux->nSourceType == SOURCE_TYPE_ISTREAMSOURCE) { flags |= MIRACST; } #if AWPLAYER_CONFIG_DISABLE_SUBTITLE flags |= DISABLE_SUBTITLE; #endif #if AWPLAYER_CONFIG_DISABLE_AUDIO flags |= DISABLE_AUDIO; #endif #if AWPLAYER_CONFIG_DISABLE_VIDEO flags |= DISABLE_VIDEO; #endif #if !AWPLAYER_CONFIG_DISALBE_MULTI_AUDIO flags |= MUTIL_AUDIO; #endif struct CallBack cb; cb.callback = ParserCallbackProcess; cb.pUserData = (void *)demux; /* static struct HdcpOpsS hdcpOps; hdcpOps.init = HDCP_Init; hdcpOps.deinit = HDCP_Deinit; hdcpOps.decrypt = HDCP_Decrypt; */ ContorlTask parserContorlTask, *contorlTask; ContorlTask contorlTask1; parserContorlTask.cmd = CDX_PSR_CMD_SET_CALLBACK; parserContorlTask.param = (void *)&cb; parserContorlTask.next = NULL; contorlTask = &parserContorlTask; if(demux->pHDCPOps) { contorlTask1.cmd = CDX_PSR_CMD_SET_HDCP; contorlTask1.param = (void *)demux->pHDCPOps; contorlTask1.next = NULL; if(flags & MIRACST) { contorlTask->next = &contorlTask1; contorlTask = contorlTask->next; } } // if(/*demux->mLivemode == 1 ||*/ demux->mLivemode == 2) { flags |= CMCC_TIME_SHIFT; } logd("=== prepare msg"); ret = CdxParserPrepare(&demux->source, flags, &demux->mutex, &demux->bCancelPrepare, &demux->pParser, &demux->pStream, &parserContorlTask, &parserContorlTask); if(ret < 0) { if(demux->bCancelPrepare) { logd("cancel prepare."); goto _endTask; } if (strncasecmp(demux->source.uri, "http://", 7) != 0) { logd("source.uri(%s)", demux->source.uri); goto _endTask; } tryTimes++; if(tryTimes >= 100) { logw("try %d times, exit.", tryTimes); goto _endTask; } logw("parser prepare failed, try again."); usleep(100*1000); goto __prepareBegin; //goto _endTask; } if(CdxParserControl(demux->pParser, CDX_PSR_CMD_GET_REDIRECT_URL, (void*)demux->shiftedTimeUrl)) { //CDX_PSR_CMD_GET_REDIRECT_URL just for cmcc RESET_URL case logd("get redirect url failed"); } logd("--- demux->shiftedTimeUrl = %s", demux->shiftedTimeUrl); CdxMediaInfoT parserMediaInfo; memset(&parserMediaInfo,0,sizeof(CdxMediaInfoT)); CdxParserGetMediaInfo(demux->pParser, &parserMediaInfo); setMediaInfo(&demux->mediaInfo, &parserMediaInfo); demux->mediaInfo.eContainerType = (enum ECONTAINER)demux->pParser->type; { // record playmode, duration info. char cmccLog[4096]; memset(cmccLog, 0, 4096); if(demux->mLivemode == 1 || demux->mLivemode == 2) sprintf(cmccLog, "[info][%s %s %d]playmode: live, duration: %lldms", LOG_TAG, __FUNCTION__, __LINE__, (long long int)demux->mediaInfo.nDurationMs); else sprintf(cmccLog, "[info][%s %s %d]playmode: vod, duration: %lldms", LOG_TAG, __FUNCTION__, __LINE__, (long long int)demux->mediaInfo.nDurationMs); demux->callback(demux->pUserData, DEMUX_NOTIFY_LOG_RECORDER, (void*)cmccLog); // record width, height info. memset(cmccLog, 0, 4096); if(parserMediaInfo.program[0].videoNum > 0) { sprintf(cmccLog, "[info][%s %s %d]Video frame width: %d, height: %d", LOG_TAG, __FUNCTION__, __LINE__, demux->mediaInfo.pVideoStreamInfo[0].nWidth, demux->mediaInfo.pVideoStreamInfo[0].nHeight); demux->callback(demux->pUserData, DEMUX_NOTIFY_LOG_RECORDER, (void*)cmccLog); } // record Video codec, Audio codec info. memset(cmccLog, 0, 4096); if(parserMediaInfo.program[0].audioNum > 0 && parserMediaInfo.program[0].videoNum > 0) { sprintf(cmccLog, "[info][%s %s %d]Video codec: %d\n" " Audio codec: %d", LOG_TAG, __FUNCTION__, __LINE__, demux->mediaInfo.pVideoStreamInfo[0].eCodecFormat, demux->mediaInfo.pAudioStreamInfo[0].eCodecFormat); demux->callback(demux->pUserData, DEMUX_NOTIFY_LOG_RECORDER, (void*)cmccLog); } // record start decoder info. memset(cmccLog, 0, 4096); sprintf(cmccLog, "[info][%s %s %d]start decoder", LOG_TAG, __FUNCTION__, __LINE__); demux->callback(demux->pUserData, DEMUX_NOTIFY_LOG_RECORDER, (void*)cmccLog); } demux->bEOS = 0; demux->bIOError = 0; if(demux->nSourceType == SOURCE_TYPE_URL) { if (!strncasecmp(demux->source.uri, "file://", 7) || !strncasecmp(demux->source.uri, "bdmv://", 7) || demux->source.uri[0] == '/') demux->bFileStream = 1; else if(demux->mediaInfo.nDurationMs <= 0) demux->bLiveStream = 1; else demux->bVodStream = 1; } else if(demux->nSourceType == SOURCE_TYPE_FD) demux->bFileStream = 1; else demux->bFileStream = 1;//demux->bLiveStream = 1; //* treat IStreamSource(miracast) as a live stream.//demux->bFileStream = 1; //* if the parser is wvm , we should not start cache thread, //* because wvm need secure buffer. if(demux->bFileStream == 0 && demux->pParser->type != CDX_PARSER_WVM ) { if(pthread_create(&demux->cacheThreadId, NULL, CacheThread, (void*)demux) == 0 && demux->mediaInfo.nVideoStreamNum > 0 && !(demux->mediaInfo.pVideoStreamInfo->bSecureStreamFlag)) { //* send a fetch message to start the cache loop. memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = DEMUX_COMMAND_START; AwMessageQueuePostMessage(demux->mqCache, &newMsg); } else demux->cacheThreadId = 0; } //* set player and media format info to cache for seek processing. StreamCacheSetPlayer(demux->pCache, demux->pPlayer); if(demux->mediaInfo.nVideoStreamNum > 0) StreamCacheSetMediaFormat(demux->pCache, demux->pParser->type, (enum EVIDEOCODECFORMAT)demux->mediaInfo.pVideoStreamInfo->eCodecFormat, demux->mediaInfo.nBitrate); else StreamCacheSetMediaFormat(demux->pCache, demux->pParser->type, VIDEO_CODEC_FORMAT_UNKNOWN, demux->mediaInfo.nBitrate); demux->eStatus = DEMUX_STATUS_PREPARED; if(!demux->pBuffering && demux->bFileStream == 0) { DemuxCompSetCachePolicy((DemuxComp*)demux, CACHE_POLICY_ADAPTIVE, 0, 0, 0); } #if defined(CONF_CMCC) if(demux->cacheThreadId != 0) //* cache some data before start player. { while(1) { if(StreamCacheDataEnough(demux->pCache) || demux->bEOS || demux->bIOError) { logd("totSize(%d), startSize(%d)", demux->pCache->nDataSize, demux->pCache->nStartPlaySize); if(demux->bEOS) { //* android.media.cts wait for Buffering Update Notify, //* before MediaPlayer.start NotifyCacheState(demux); } break; } else { //* wait for 10ms if no message come. ret = AwMessageQueueTryGetMessage(demux->mq, &msg, 10); if(ret == 0) //* new message come, quit loop to process. { goto process_message; } } } } #endif if(demux->bFileStream == 0) demux->nStartPlayCacheTime = GetConfigParamterInt("start_play_cache_time",6)*1000; demux->callback(demux->pUserData, DEMUX_NOTIFY_PREPARED, 0); continue; _endTask: demux->eStatus = DEMUX_STATUS_INITIALIZED; if(demux->bCancelPrepare) { demux->callback(demux->pUserData, DEMUX_NOTIFY_PREPARED, (void*)DEMUX_ERROR_USER_CANCEL); } else { loge("DEMUX_ERROR_IO"); demux->callback(demux->pUserData, DEMUX_NOTIFY_PREPARED, (void*)DEMUX_ERROR_IO); } continue; } //* end DEMUX_COMMAND_PREPARE. else if(msg.messageId == DEMUX_COMMAND_START) { logd("process message DEMUX_COMMAND_START."); if(demux->bBufferring) { //* user press the start button when buffering. //* start to play even the data is not enough. if(demux->bBufferStartNotified == 1) { demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_END, NULL); demux->bBufferStartNotified = 0; } demux->bNeedPausePlayerNotified = 0; demux->bBufferring = 0; } if(demux->eStatus == DEMUX_STATUS_STARTED) { logi("demux already in started status."); //* send a read message to start the read loop. PostReadMessage(demux->mq); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } if(demux->eStatus != DEMUX_STATUS_PREPARED) { loge("demux not in prepared status when DEMUX_COMMAND_START message received."); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* in case of bVideoFirstFrameShowed == 0, demux will continue to send data to player //* until video first frame showed. //* here we set this flag to let the demux don't send data at buffering status. if(demux->pPlayer == NULL || PlayerHasVideo(demux->pPlayer) == 0) bVideoFirstFrameShowed = 1; demux->eStatus = DEMUX_STATUS_STARTED; //* send a read message to start the read loop. PostReadMessage(demux->mq); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end DEMUX_COMMAND_START else if(msg.messageId == DEMUX_COMMAND_STOP) { logv("process message DEMUX_COMMAND_STOP."); //* stop the cache thread. if(demux->cacheThreadId != 0) { void* status; memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = DEMUX_COMMAND_QUIT; newMsg.params[0] = (uintptr_t)&demux->semCache; AwMessageQueuePostMessage(demux->mqCache, &newMsg); SemTimedWait(&demux->semCache, -1); pthread_join(demux->cacheThreadId, &status); demux->cacheThreadId = 0; StreamCacheFlushAll(demux->pCache); if(demux->bBufferStartNotified == 1) { demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_END, NULL); demux->bBufferStartNotified = 0; demux->bNeedPausePlayerNotified = 0; } } if(demux->pParser != NULL) { CdxParserClose(demux->pParser); demux->pParser = NULL; demux->pStream = NULL; } else if(demux->pStream) { CdxStreamClose(demux->pStream); demux->pStream = NULL; } bVideoFirstFrameShowed = 0; demux->eStatus = DEMUX_STATUS_STOPPED; demux->bStopping = 0; if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end DEMUX_COMMAND_STOP. else if(msg.messageId == DEMUX_COMMAND_QUIT || msg.messageId == DEMUX_COMMAND_CLEAR) { logv("process message DEMUX_COMMAND_QUIT or DEMUX_COMMAND_CLEAR."); //* stop the cache thread if it is not stopped yet. if(demux->cacheThreadId != 0) { void* status; memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = DEMUX_COMMAND_QUIT; newMsg.params[0] = (uintptr_t)&demux->semCache; AwMessageQueuePostMessage(demux->mqCache, &newMsg); SemTimedWait(&demux->semCache, -1); pthread_join(demux->cacheThreadId, &status); demux->cacheThreadId = 0; if(demux->bBufferStartNotified == 1) { unsigned char url[1024]; if(demux->pParser) { if(CdxParserControl(demux->pParser, CDX_PSR_CMD_GET_URL, (void*)url)) { loge("get url failed"); } } demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_END, NULL); demux->bBufferStartNotified = 0; demux->bNeedPausePlayerNotified = 0; } } //mediaInfo need to free here clearMediaInfo(&demux->mediaInfo); if(demux->pParser != NULL) { CdxParserClose(demux->pParser); demux->pParser = NULL; demux->pStream = NULL; } else if(demux->pStream) { CdxStreamClose(demux->pStream); demux->pStream = NULL; } clearDataSourceFields(&demux->source); demux->eStatus = DEMUX_STATUS_IDLE; demux->bStopping = 0; bVideoFirstFrameShowed = 0; if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); if(msg.messageId == DEMUX_COMMAND_QUIT) break; //* quit the thread. continue; } //* end DEMUX_COMMAND_QUIT or DEMUX_COMMAND_CLEAR. else if(msg.messageId == DEMUX_COMMAND_SEEK) { int nSeekTimeMs; int nFinalSeekTimeMs; int params[3]; logd("process message DEMUX_COMMAND_SEEK."); bVideoFirstFrameShowed = 0; nSeekTimeMs = msg.params[2]; if(demux->pParser != NULL) { if(demux->cacheThreadId != 0) { memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = DEMUX_COMMAND_PAUSE; newMsg.params[0] = (uintptr_t)&demux->semCache; AwMessageQueuePostMessage(demux->mqCache, &newMsg); SemTimedWait(&demux->semCache, -1); int64_t nSeekTimeUs = nSeekTimeMs*1000LL; int64_t nSeekTimeFinal = StreamCacheSeekTo(demux->pCache, nSeekTimeUs); int n = 0; demux->bEOS = 0; demux->bIOError = 0; if(nSeekTimeFinal < 0) { StreamCacheFlushAll(demux->pCache); demux->nCacheReply = -1; memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = DEMUX_COMMAND_SEEK; newMsg.params[0] = (uintptr_t)&demux->semCache; newMsg.params[1] = (uintptr_t)&demux->nCacheReply; newMsg.params[2] = (uintptr_t)nSeekTimeMs; AwMessageQueuePostMessage(demux->mqCache, &newMsg); if(demux->bBufferStartNotified == 1) { demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_END, NULL); demux->bBufferStartNotified = 0; } //* wait seek complete while(demux->nCacheReply) { if(demux->bCancelSeek) { break; } ret = AwMessageQueueTryGetMessage(demux->mq, &msg, 50); if(ret == 0) //* new message come, quit loop to process. { goto process_message; } n++; if(n>=40 && !demux->bBufferStartNotified) { logd("++++++++++ send buffer Start, in demux thread"); unsigned char url[1024]; if(demux->pParser) { if(CdxParserControl(demux->pParser, CDX_PSR_CMD_GET_URL, (void*)url)) { loge("get url failed"); } } demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_START, (void*)&demux->pCache->nStartPlaySize); demux->bBufferStartNotified = 1; demux->bBufferring = 1; } } } else { if(demux->bBufferStartNotified == 1) { unsigned char url[1024]; if(demux->pParser) { if(CdxParserControl(demux->pParser, CDX_PSR_CMD_GET_URL, (void*)url)) { loge("get url failed"); } } demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_END, NULL); demux->bBufferStartNotified = 0; } //* since DemuxCompSeekTo use CdxParserForceStop, clear it here. pthread_mutex_lock(&demux->mutex); if(demux->bCancelSeek == 0 && demux->bStopping == 0) { logv("CdxParserClrForceStop"); ret = CdxParserClrForceStop(demux->pParser); if(ret < 0) { logw("CdxParserClrForceStop fail, ret(%d)", ret); } } pthread_mutex_unlock(&demux->mutex); params[0] = 0; params[1] = nSeekTimeMs; params[2] = nSeekTimeFinal/1000; demux->callback(demux->pUserData, DEMUX_NOTIFY_SEEK_FINISH, (void*)params); demux->bSeeking = 0; memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = DEMUX_COMMAND_START; newMsg.params[0] = (uintptr_t)&demux->semCache; AwMessageQueuePostMessage(demux->mqCache, &newMsg); SemTimedWait(&demux->semCache, -1); } if(demux->eStatus == DEMUX_STATUS_COMPLETE) demux->eStatus = DEMUX_STATUS_STARTED; if(demux->eStatus == DEMUX_STATUS_STARTED) PostReadMessage(demux->mq); //* send read message to start reading loop. demux->bBufferring = 0; demux->bNeedPausePlayerNotified = 0; continue; } else { //* lock the mutex to sync with the DemuxCompCancelSeek() //* operation or DEmuxCompStop() operation. //* when user are requesting to quit the seek operation, //* we should not clear the parser's force //* stop flag, other wise the parser may blocked at a network io operation. pthread_mutex_lock(&demux->mutex); if(demux->bCancelSeek == 0 && demux->bStopping == 0) { ret = CdxParserClrForceStop(demux->pParser); if(ret < 0) { logw("CdxParserClrForceStop fail, ret(%d)", ret); } } pthread_mutex_unlock(&demux->mutex); if(CdxParserSeekTo(demux->pParser, ((int64_t)nSeekTimeMs)*1000, demux->nSeekModeType) >= 0) { nFinalSeekTimeMs = nSeekTimeMs; ret = 0; } else ret = -1; if(ret == 0) { params[0] = 0; params[1] = nSeekTimeMs; params[2] = nFinalSeekTimeMs; demux->callback(demux->pUserData, DEMUX_NOTIFY_SEEK_FINISH, (void*)params); demux->bSeeking = 0; demux->bEOS = 0; demux->bIOError = 0; if(demux->eStatus == DEMUX_STATUS_COMPLETE) demux->eStatus = DEMUX_STATUS_STARTED; if(demux->eStatus == DEMUX_STATUS_STARTED) PostReadMessage(demux->mq); //* send read message to start reading loop. if(pReplyValue != NULL) *pReplyValue = 0; } else { loge("CdxParserSeekTo() return fail, demux->bCancelSeek:%d", demux->bCancelSeek); //* set to complete status to stop reading. demux->eStatus = DEMUX_STATUS_COMPLETE; demux->bSeeking = 0; if(demux->bCancelSeek == 1 || demux->bStopping == 1) params[0] = DEMUX_ERROR_USER_CANCEL; else params[0] = DEMUX_ERROR_IO; params[1] = nSeekTimeMs; demux->callback(demux->pUserData, DEMUX_NOTIFY_SEEK_FINISH, (void*)params); if(pReplyValue != NULL) *pReplyValue = -1; } demux->bBufferring = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } } else { params[0] = DEMUX_ERROR_UNKNOWN; params[1] = nSeekTimeMs; demux->bSeeking = 0; demux->callback(demux->pUserData, DEMUX_NOTIFY_SEEK_FINISH, (void*)params); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } } else if(msg.messageId == DEMUX_COMMAND_RESET_URL) { //* resetUrl in demux thread, send a message to cache thread //* and then wait the cache thread finish resetUrl message int nSeekTimeMs; int nFinalSeekTimeMs; int params[3]; int64_t startTime , nowTime; struct timeval tv; gettimeofday(&tv, NULL); startTime = tv.tv_sec * 1000000ll + tv.tv_usec; logd("process message DEMUX_COMMAND_RESET_URL."); bVideoFirstFrameShowed = 0; nSeekTimeMs = msg.params[2]; char *pLiveModeType = NULL; char *pStartime = NULL; char TimeStr[512] = {0}; pLiveModeType = strstr(demux->shiftedTimeUrl, "livemode=");//check livemode here //get the shifted time format string here if ((ShiftTimeMode(nSeekTimeMs, TimeStr)) == 2) { //* convert live to pltv char cmccLog[4096] = ""; sprintf(cmccLog, "[info][%s %s %d]convert to pltv", LOG_TAG, __FUNCTION__, __LINE__); demux->callback(demux->pUserData, DEMUX_NOTIFY_LOG_RECORDER, (void*)cmccLog); demux->mLivemode = 2; pLiveModeType[9] = '2';//get shifted event with livemode1, will change to livemode2 pStartime = strstr(demux->shiftedTimeUrl, "&starttime="); if (pStartime != NULL) { sprintf(pStartime, "&starttime=%s", TimeStr);//generate the shifted url } else { char tmpShiftedTimeUrl[4096]; snprintf(tmpShiftedTimeUrl,sizeof(tmpShiftedTimeUrl), "%s",demux->shiftedTimeUrl); snprintf(demux->shiftedTimeUrl,sizeof(demux->shiftedTimeUrl), "%s&starttime=%s",tmpShiftedTimeUrl, TimeStr); } } else { //* convert pltv to live char cmccLog[4096] = ""; sprintf(cmccLog, "[info][%s %s %d]convert to live", LOG_TAG, __FUNCTION__, __LINE__); demux->callback(demux->pUserData, DEMUX_NOTIFY_LOG_RECORDER, (void*)cmccLog); demux->mLivemode = 1; pLiveModeType[9] = '1'; pStartime = strstr(demux->shiftedTimeUrl, "&starttime="); if (pStartime != NULL) { pStartime[0] = '\0'; } } logd("ShiftedTimeUrl = %s", demux->shiftedTimeUrl); if(demux->pParser != NULL) { CdxParserClose(demux->pParser); demux->pParser = NULL; demux->pStream = NULL; } else if(demux->pStream != NULL) { CdxStreamClose(demux->pStream); demux->pStream = NULL; } //* send reset_url message to cache thread, and wait parserOpen //if(demux->cacheThreadId != 0) { memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = DEMUX_COMMAND_PAUSE; newMsg.params[0] = (uintptr_t)&demux->semCache; AwMessageQueuePostMessage(demux->mqCache, &newMsg); SemTimedWait(&demux->semCache, -1); StreamCacheFlushAll(demux->pCache); memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = DEMUX_COMMAND_RESET_URL; newMsg.params[0] = (uintptr_t)&demux->semCache; newMsg.params[1] = (uintptr_t)&demux->nCacheReply; newMsg.params[2] = (uintptr_t)nSeekTimeMs; AwMessageQueuePostMessage(demux->mqCache, &newMsg); if(demux->bBufferStartNotified == 1) { demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_END, NULL); demux->bBufferStartNotified = 0; } // blocking here, wait for parserPrepare in cache thread while(demux->pParser == NULL) { if(demux->bCancelSeek) { break; } //ret = SemTimedWait(&demux->semCache, 50); usleep(50*1000); // sleep 50ms gettimeofday(&tv, NULL); nowTime = tv.tv_sec * 1000000ll + tv.tv_usec; // after 2s, send bufferStart if((nowTime-startTime > 2000000) && !demux->bBufferStartNotified) { logd("++++++++++ send buffer Start, in demux thread"); demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_START, (void*)&demux->pCache->nStartPlaySize); demux->bBufferStartNotified = 1; demux->bBufferring = 1; } } SemTimedWait(&demux->semCache, -1); // wait DEMUX_COMMAND_RESET_URL in cache thread } nFinalSeekTimeMs = nSeekTimeMs; ret = demux->nCacheReply; logd("demux thread: ret = %d", ret); if(ret == 0) { demux->bSeeking = 0; demux->bEOS = 0; demux->bIOError = 0; //* send a start message to the cache thread. if(demux->cacheThreadId != 0) { memset(&newMsg, 0, sizeof(AwMessage)); newMsg.messageId = DEMUX_COMMAND_START; newMsg.params[0] = (uintptr_t)&demux->semCache; AwMessageQueuePostMessage(demux->mqCache, &newMsg); SemTimedWait(&demux->semCache, -1); } if(demux->eStatus == DEMUX_STATUS_COMPLETE) demux->eStatus = DEMUX_STATUS_STARTED; // *blocking here to buffering data StreamCacheSetSize(demux->pCache, demux->nStartPlayCacheSize, demux->nCacheBufferSize); while(!StreamCacheDataEnough(demux->pCache)) { usleep(20*1000); gettimeofday(&tv, NULL); nowTime = tv.tv_sec * 1000000ll + tv.tv_usec; // after 2s, send bufferStart if((nowTime-startTime > 2000000) && !demux->bBufferStartNotified) { logd("++++++++++ send buffer Start, in demux thread"); demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_START, (void*)&demux->pCache->nStartPlaySize); demux->bBufferStartNotified = 1; demux->bBufferring = 1; } if(demux->bEOS || demux->bIOError) { loge("eos in livemode2, error"); } if(demux->bCancelSeek) { break; } } if(demux->eStatus == DEMUX_STATUS_STARTED) PostReadMessage(demux->mq); //* send read message to start reading loop. params[0] = 0; params[1] = nSeekTimeMs; params[2] = nFinalSeekTimeMs; demux->callback(demux->pUserData, DEMUX_NOTIFY_SEEK_FINISH, (void*)params); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else { loge("CdxParserResetUrl() return fail, bCancelSeek = %d", demux->bCancelSeek); demux->eStatus = DEMUX_STATUS_COMPLETE; //* set to complete status to stop reading. demux->bSeeking = 0; //if(demux->bCancelSeek == 1 || demux->bStopping == 1) //* we cannot set it to DEMUX_ERROR_USER_CANCEL in cmcc, it will error out params[0] = DEMUX_ERROR_USER_CANCEL; //else // params[0] = DEMUX_ERROR_IO; params[1] = nSeekTimeMs; demux->callback(demux->pUserData, DEMUX_NOTIFY_SEEK_FINISH, (void*)params); if(pReplyValue != NULL) *pReplyValue = -1; if(pReplySem != NULL) sem_post(pReplySem); continue; } } else if(msg.messageId == DEMUX_COMMAND_CANCEL_PREPARE) { logv("process message DEMUX_COMMAND_CANCEL_PREPARE."); demux->bCancelPrepare = 0; if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else if(msg.messageId == DEMUX_COMMAND_CANCEL_SEEK) { logv("process message DEMUX_COMMAND_CANCEL_SEEK."); pthread_mutex_lock(&demux->lock); while(demux->bSeeking) { pthread_cond_wait(&demux->cond, &demux->lock); } pthread_mutex_unlock(&demux->lock); demux->bCancelSeek = 0; if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else if(msg.messageId == DEMUX_COMMAND_NOTIFY_FIRST_FRAME_SHOWED) { bVideoFirstFrameShowed = 1; if(demux->eStatus == DEMUX_STATUS_STARTED) PostReadMessage(demux->mq); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } else if(msg.messageId == DEMUX_COMMAND_VIDEO_STREAM_CHANGE) { logv("process message DEMUX_COMMAND_VIDEO_STREAM_CHANGE!"); int nMsg = DEMUX_VIDEO_STREAM_CHANGE; int params[2]; params[0] = (int)msg.params[2]; demux->callback(demux->pUserData, nMsg, (void*)params); PostReadMessage(demux->mq); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end DEMUX_COMMAND_VIDEO_STREAM_CHANGE. else if(msg.messageId == DEMUX_COMMAND_AUDIO_STREAM_CHANGE) { logv("process message DEMUX_COMMAND_AUDIO_STREAM_CHANGE!"); int nMsg = DEMUX_AUDIO_STREAM_CHANGE; demux->callback(demux->pUserData, nMsg, NULL); PostReadMessage(demux->mq); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end DEMUX_COMMAND_AUDIO_STREAM_CHANGE. else if(msg.messageId == DEMUX_COMMAND_READ) { logv("process message DEMUX_COMMAND_READ."); if(demux->eStatus != DEMUX_STATUS_STARTED) { logw("demux component not in started status, ignore read message."); continue; } if (GetConfigParamterInt("show_1th_frame_quick", 0) != 1) bVideoFirstFrameShowed = 1;//* cmcc not show first frame immediately if(demux->cacheThreadId != 0) { if(demux->bFileStream == 0) { nCurTimeUs = GetSysTime(); //************************************************************** //* notify the cache status. //************************************************************** if(nCurTimeUs >= (nLastCacheStateNotifyTimeUs + demux->nCacheStatReportIntervalUs) || nCurTimeUs < nLastCacheStateNotifyTimeUs) { NotifyCacheState(demux); nLastCacheStateNotifyTimeUs = nCurTimeUs; } } //************************************************************** //* read data from cache. //************************************************************** if(demux->bBufferring) { //* the player is paused and caching stream data. //* check whether data in cache is enough for play. if(StreamCacheDataEnough(demux->pCache) || demux->bEOS || demux->bIOError) { logd("detect data enough, notify BUFFER_END."); demux->bBufferring = 0; if(demux->bBufferStartNotified == 1) { unsigned char url[1024]; if(demux->pParser) { if(CdxParserControl(demux->pParser, CDX_PSR_CMD_GET_URL, (void*)url)) { loge("get url failed"); } } demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_END, (void*)url); demux->bBufferStartNotified = 0; } demux->callback(demux->pUserData, DEMUX_NOTIFY_RESUME_PLAYER, NULL); demux->bNeedPausePlayerNotified = 0; } else { if(demux->bBufferStartNotified == 0) { nCurTimeUs = GetSysTime(); if(nCurTimeUs > (nLastBufferingStartTimeUs + 2000000) || nCurTimeUs < nLastBufferingStartTimeUs) { //* had been paused for buffering for more than 2 seconds, //* notify buffer start message to let the application know. logd("after 2s, notify BUFFER_START."); unsigned char url[1024]; if(demux->pParser) { if(CdxParserControl(demux->pParser, CDX_PSR_CMD_GET_URL, (void*)url)) { loge("get url failed"); } } logd("-- bufferStart url = %s", url); demux->callback(demux->pUserData, DEMUX_NOTIFY_BUFFER_START, (void*)&demux->pCache->nStartPlaySize); demux->bBufferStartNotified = 1; } } if(bVideoFirstFrameShowed == 1) { //* wait some time for caching. ret = AwMessageQueueTryGetMessage(demux->mq, &msg, 10); if(ret == 0) //* new message come, quit loop to process. goto process_message; //* post a read message to continue the reading job. PostReadMessage(demux->mq); continue; } } } if(demux->pBuffering && !demux->mStreamPrepared) { int ret_caching = StreamBufferingPrepared(demux->pCache); if(ret_caching) { demux->mStreamPrepared = 1; } } if(demux->pBuffering && !demux->mStreamPrepared) { ret = AwMessageQueueTryGetMessage(demux->mq, &msg, 10); if(ret == 0) goto process_message; PostReadMessage(demux->mq); continue; } //* check whether cache underflow. if(StreamCacheUnderflow(demux->pCache)) { logv("detect cache data underflow."); //* cache underflow, if not eos, we need to notify pausing, //* otherwise we need to notify complete. if(demux->bEOS) { //* end of stream, notify complete. logi("detect eos, notify EOS."); demux->callback(demux->pUserData, DEMUX_NOTIFY_EOS, 0); demux->eStatus = DEMUX_STATUS_COMPLETE; continue; } else if(demux->bIOError) { logi("detect io error, notify IOERROR."); //* end of stream, notify complete. demux->callback(demux->pUserData, DEMUX_NOTIFY_IOERROR, 0); continue; } else { //* no data in cache, check whether player hold enough data, //* if not, we need to notify pausing to wait for caching //* more data for player. // in cmcc, we should skip the next code PlayerBufferUnderflow if(PlayerBufferUnderflow(demux->pPlayer) && demux->bNeedPausePlayerNotified == 0 && bVideoFirstFrameShowed == 1) { logd("detect player data underflow, notify PAUSE_PLAYER. "); if(demux->bDiscontinue) { demux->bDiscontinue = 0; if(CdxParserControl(demux->pParser, CDX_PSR_CMD_SET_HLS_STREAM_FORMAT_CHANGE, NULL)) { loge("***hls set stream format change fail"); } } else { demux->bBufferring = 1; nLastBufferingStartTimeUs = GetSysTime(); demux->callback(demux->pUserData, DEMUX_NOTIFY_PAUSE_PLAYER, NULL); demux->bNeedPausePlayerNotified = 1; //************************************************************** //* adjust cache params. //************************************************************** AdjustCacheParams(demux); } } //* wait some time for caching. ret = AwMessageQueueTryGetMessage(demux->mq, &msg, 10); if(ret == 0) //* new message come, quit loop to process. goto process_message; //* post a read message to continue the reading job. PostReadMessage(demux->mq); continue; } } else { //* there is some data in cache for player. //* if data in player is not too much, send it to player, //* otherwise, just keep it in the cache. if(PlayerBufferOverflow(demux->pPlayer)) { logv("detect player data overflow."); //* too much data in player, wait some time. ret = AwMessageQueueTryGetMessage(demux->mq, &msg, 200); if(ret == 0) //* new message come, quit loop to process. goto process_message; //* post a read message to continue the reading job. PostReadMessage(demux->mq); continue; } else { //************************************* //* send data from cache to player. //************************************* CacheNode* node; void* pBuf0; void* pBuf1; int nBufSize0; int nBufSize1; //******************************** //* 1. get one frame from cache. //******************************** node = StreamCacheNextFrame(demux->pCache); if(node == NULL) { loge("Cache underflow"); PostReadMessage(demux->mq); continue; } //******************************** //* 2. request buffer from player. //******************************** if(node->eMediaType == CDX_MEDIA_VIDEO) { ePlayerMediaType = MEDIA_TYPE_VIDEO; nStreamIndex = (node->nFlags&MINOR_STREAM)==0 ? 0 : 1; } else if(node->eMediaType == CDX_MEDIA_AUDIO) { ePlayerMediaType = MEDIA_TYPE_AUDIO; nStreamIndex = node->nStreamIndex; } else if(node->eMediaType == CDX_MEDIA_SUBTITLE) { ePlayerMediaType = MEDIA_TYPE_SUBTITLE; nStreamIndex = node->nStreamIndex; } else if(node->eMediaType == CDX_MEDIA_METADATA) { ePlayerMediaType = MEDIA_TYPE_METADATA; nStreamIndex = node->nStreamIndex; } else { loge("media type from parser not valid, should not run here, abort()."); abort(); } while(1) { if(ePlayerMediaType != MEDIA_TYPE_METADATA) ret = PlayerRequestStreamBuffer(demux->pPlayer, node->nLength, &pBuf0, &nBufSize0, &pBuf1, &nBufSize1, ePlayerMediaType, nStreamIndex); else { //* allocate a buffer to read uncare media data and skip it. pBuf0 = malloc(node->nLength); if(pBuf0 != NULL) { nBufSize0 = node->nLength; pBuf1 = NULL; nBufSize1 = 0; ret = 0; } else { nBufSize0 = 0; pBuf1 = NULL; nBufSize1 = 0; ret = -1; } } if ((nBufSize0 + nBufSize1) < node->nLength) ret = -1; if(ret < 0) { //logi("waiting for stream buffer."); //* no buffer, try to wait sometime. ret = AwMessageQueueTryGetMessage(demux->mq, &msg, 200); if(ret == 0) //* new message come, quit loop to process. goto process_message; /* player is paused due to cache underflow, don't wait forever */ if (demux->bNeedPausePlayerNotified == 1) break; } else break; //* get buffer ok. } /* player is paused due to cache underflow, and audio * stream is overflow. The funtion of 'show the first * video frame in paused status after seek' won't work. * Set bVideoFirstFrameShowed to one and wait until * stream cache data enough. */ if (ret < 0) { logd("don't wait for stream buffer in paused status"); bVideoFirstFrameShowed = 1; PostReadMessage(demux->mq); continue; } //********************************************** //* 3. copy data to player's buffer and submit. //********************************************** if(node->nLength > nBufSize0) { memcpy(pBuf0, node->pData, nBufSize0); memcpy(pBuf1, node->pData + nBufSize0, node->nLength-nBufSize0); } else memcpy(pBuf0, node->pData, node->nLength); if(ePlayerMediaType != MEDIA_TYPE_METADATA) { streamDataInfo.pData = (char*)pBuf0; streamDataInfo.nLength = node->nLength; streamDataInfo.nPts = node->nPts; streamDataInfo.nPcr = node->nPcr; streamDataInfo.bIsFirstPart = 1; streamDataInfo.bIsLastPart = 1; streamDataInfo.nDuration = node->nDuration; streamDataInfo.pStreamInfo = NULL; streamDataInfo.nStreamChangeFlag = 0; streamDataInfo.nStreamChangeNum = 0; if(ePlayerMediaType == MEDIA_TYPE_VIDEO && node->infoVersion == 1) { logd(" demux -- video stream info change: node->info = %p",node->info); if(node->info) { int ret; struct VideoInfo* pVideoinfo = (struct VideoInfo *)node->info; ret = setVideoStreamDataInfo(&streamDataInfo, pVideoinfo); if (ret < 0) { loge("setVideoStreamDataInfo fail."); abort(); } } nVideoInfoVersion = node->infoVersion; } else if(ePlayerMediaType == MEDIA_TYPE_AUDIO && node->infoVersion == 1) { logd(" demux -- audio stream info change: node->info = %p",node->info); if(node->info) { int ret; struct AudioInfo* pAudioinfo = (struct AudioInfo *)node->info; ret = setAudioStreamDataInfo(&streamDataInfo, pAudioinfo); if (ret < 0) { loge("setAudioStreamDataInfo fail."); abort(); } } nAudioInfoVersion = node->infoVersion; } PlayerSubmitStreamData(demux->pPlayer, &streamDataInfo, ePlayerMediaType, nStreamIndex); StreamCacheFlushOneFrame(demux->pCache); } else { //* skip the media data. unsigned int params[4]; params[0] = (unsigned int)(node->nPts >> 32);//high 32bits params[1] = (unsigned int)(node->nPts & 0xffffffff);//low 32bits params[2] = (unsigned int)node->nLength; params[3] = (unsigned int)pBuf0; demux->callback(demux->pUserData, DEMUX_NOTIFY_METADATA, (void*)params); free(pBuf0); StreamCacheFlushOneFrame(demux->pCache); } //* post a read message to continue the reading job. PostReadMessage(demux->mq); continue; } //* end if(PlayerBufferOverflow(...)){}else {} } //* end if(StreamCacheUnderflow(...)){}else {} } else { //************************************************************** //* read data directly from parser. //************************************************************** memset(&packet, 0x00, sizeof(CdxPacketT)); //* if data in player is not too much, send it to player, //* otherwise don't read. if(PlayerBufferOverflow(demux->pPlayer)) { //* too much data in player, wait some time. ret = AwMessageQueueTryGetMessage(demux->mq, &msg, 200); if(ret == 0) //* new message come, quit loop to process. goto process_message; //* post a read message to continue the reading job. PostReadMessage(demux->mq); continue; } //* 1. get data type. if(CdxParserPrefetch(demux->pParser, &packet) != 0) { if(demux->bStopping == 0 && demux->bSeeking == 0) { int err = CdxParserGetStatus(demux->pParser); if(err == PSR_IO_ERR) { demux->bIOError = 1; demux->callback(demux->pUserData, DEMUX_NOTIFY_IOERROR, 0); } else if (err == PSR_USER_CANCEL) // TODO: { /* do noting */ } else { demux->bEOS = 1; demux->callback(demux->pUserData, DEMUX_NOTIFY_EOS, 0); demux->eStatus = DEMUX_STATUS_COMPLETE; } } continue; } //* 2. request buffer from player. if(packet.type == CDX_MEDIA_VIDEO) { ePlayerMediaType = MEDIA_TYPE_VIDEO; nStreamIndex = (packet.flags&MINOR_STREAM)==0 ? 0 : 1; } else if(packet.type == CDX_MEDIA_AUDIO) { ePlayerMediaType = MEDIA_TYPE_AUDIO; nStreamIndex = packet.streamIndex; } else if(packet.type == CDX_MEDIA_SUBTITLE) { ePlayerMediaType = MEDIA_TYPE_SUBTITLE; nStreamIndex = packet.streamIndex; } else if(packet.type == CDX_MEDIA_METADATA) { ePlayerMediaType = MEDIA_TYPE_METADATA; nStreamIndex = packet.streamIndex; } else { loge("media type from parser not valid, should not run here, abort()."); abort(); } while(1) { if((!AWPLAYER_CONFIG_DISABLE_VIDEO && ePlayerMediaType == MEDIA_TYPE_VIDEO) || (!AWPLAYER_CONFIG_DISABLE_AUDIO && ePlayerMediaType == MEDIA_TYPE_AUDIO) || (!AWPLAYER_CONFIG_DISABLE_SUBTITLE && ePlayerMediaType == MEDIA_TYPE_SUBTITLE)) { ret = PlayerRequestStreamBuffer(demux->pPlayer, packet.length, &packet.buf, &packet.buflen, &packet.ringBuf, &packet.ringBufLen, ePlayerMediaType, nStreamIndex); } else { //* allocate a buffer to read uncare media data and skip it. packet.buf = malloc(packet.length); if(packet.buf != NULL) { packet.buflen = packet.length; packet.ringBuf = NULL; packet.ringBufLen = 0; ret = 0; } else { packet.buflen = 0; packet.ringBuf = NULL; packet.ringBufLen = 0; ret = -1; } } if(ret<0 || (packet.buflen+packet.ringBufLen)mq, &msg, 200); if(ret == 0) //* new message come, quit loop to process. goto process_message; } else break; //* get buffer ok. } //* 3. read data to buffer and submit. ret = CdxParserRead(demux->pParser, &packet); if(ret == 0) { if((!AWPLAYER_CONFIG_DISABLE_VIDEO && ePlayerMediaType == MEDIA_TYPE_VIDEO) || (!AWPLAYER_CONFIG_DISABLE_AUDIO && ePlayerMediaType == MEDIA_TYPE_AUDIO) || (!AWPLAYER_CONFIG_DISABLE_SUBTITLE && ePlayerMediaType == MEDIA_TYPE_SUBTITLE)) { streamDataInfo.pData = (char*)packet.buf; streamDataInfo.nLength = packet.length; streamDataInfo.nPts = packet.pts; streamDataInfo.nPcr = packet.pcr; streamDataInfo.bIsFirstPart = (!!(packet.flags & FIRST_PART)); streamDataInfo.bIsLastPart = (!!(packet.flags & LAST_PART)); streamDataInfo.nDuration = packet.duration; streamDataInfo.pStreamInfo = NULL; streamDataInfo.nStreamChangeFlag = 0; streamDataInfo.nStreamChangeNum = 0; if(ePlayerMediaType == MEDIA_TYPE_VIDEO && packet.infoVersion != nVideoInfoVersion) { if(packet.info) { int ret; struct VideoInfo* pVideoinfo = (struct VideoInfo *)packet.info; ret = setVideoStreamDataInfo(&streamDataInfo, pVideoinfo); if (ret < 0) { loge("setVideoStreamDataInfo fail."); abort(); } } nVideoInfoVersion = packet.infoVersion; } else if(ePlayerMediaType == MEDIA_TYPE_AUDIO && packet.infoVersion != nAudioInfoVersion) { if(packet.info) { int ret; struct AudioInfo* pAudioinfo = (struct AudioInfo *)packet.info; ret = setAudioStreamDataInfo(&streamDataInfo, pAudioinfo); if (ret < 0) { loge("setAudioStreamDataInfo fail."); abort(); } } nAudioInfoVersion = packet.infoVersion; } PlayerSubmitStreamData(demux->pPlayer, &streamDataInfo, ePlayerMediaType, nStreamIndex); } else { //* skip the media data. free(packet.buf); } //* post a read message to continue the reading job after message processed. PostReadMessage(demux->mq); } else { logw("read data from parser return fail."); if(demux->bStopping == 0 && demux->bSeeking == 0) { int err = CdxParserGetStatus(demux->pParser); if(err == PSR_IO_ERR) { demux->bIOError = 1; demux->callback(demux->pUserData, DEMUX_NOTIFY_IOERROR, 0); } else if (err == PSR_USER_CANCEL) // TODO: { /* do noting */ } else { demux->bEOS = 1; demux->callback(demux->pUserData, DEMUX_NOTIFY_EOS, 0); demux->eStatus = DEMUX_STATUS_COMPLETE; } } } continue; } } else { logw("unknow message with id %d, ignore.", msg.messageId); } } return NULL; } static void* CacheThread(void* arg) { AwMessage msg; int ret; sem_t* pReplySem; int* pReplyValue; DemuxCompContext* demux; int eCacheStatus; int nVideoInfoVersion = 0; int nAudioInfoVersion = 0; demux = (DemuxCompContext*)arg; eCacheStatus = DEMUX_STATUS_STOPPED; while(1) { if(AwMessageQueueGetMessage(demux->mqCache, &msg) < 0) { loge("get message fail."); continue; } cache_process_message: pReplySem = (sem_t*)msg.params[0]; pReplyValue = (int*)msg.params[1]; if(msg.messageId == DEMUX_COMMAND_START) { logv("cache thread process message DEMUX_COMMAND_START."); eCacheStatus = DEMUX_STATUS_STARTED; PostReadMessage(demux->mqCache); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end DEMUX_COMMAND_START else if(msg.messageId == DEMUX_COMMAND_PAUSE || msg.messageId == DEMUX_COMMAND_STOP) { logv("cache thread process message DEMUX_COMMAND_PAUSE or DEMUX_COMMAND_STOP."); eCacheStatus = DEMUX_STATUS_STOPPED; if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); continue; } //* end DEMUX_COMMAND_STOP. else if(msg.messageId == DEMUX_COMMAND_QUIT) { logv("cache thread process message DEMUX_COMMAND_QUIT."); if(pReplyValue != NULL) *pReplyValue = 0; if(pReplySem != NULL) sem_post(pReplySem); break; //* quit the thread. } //* end DEMUX_COMMAND_QUIT. else if(msg.messageId == DEMUX_COMMAND_SEEK) { int64_t nSeekTimeUs; int64_t nSeekTimeFinal; int params[3]; logd("cache thread process message DEMUX_COMMAND_SEEK."); nSeekTimeUs = ((int64_t)msg.params[2])*1000; //* TODO //* for ts/bd/hls parser, we should map the nSeekTimeUs to pts. //* because pts in these parsers may be loopback or may not started with zero value. pthread_mutex_lock(&demux->mutex); if(demux->bCancelSeek == 0 && demux->bStopping == 0) { ret = CdxParserClrForceStop(demux->pParser); if(ret < 0) { logw("CdxParserClrForceStop fail, ret(%d)", ret); } } pthread_mutex_unlock(&demux->mutex); logd("CdxParserSeekTo"); if(CdxParserSeekTo(demux->pParser, nSeekTimeUs, demux->nSeekModeType) >= 0) { nSeekTimeFinal = nSeekTimeUs; ret = 0; } else ret = -1; if(ret == 0) { params[0] = 0; params[1] = nSeekTimeUs/1000; params[2] = nSeekTimeFinal/1000; demux->callback(demux->pUserData, DEMUX_NOTIFY_SEEK_FINISH, (void*)params); pthread_mutex_lock(&demux->lock); demux->bSeeking = 0; pthread_cond_signal(&demux->cond); pthread_mutex_unlock(&demux->lock); demux->bEOS = 0; demux->bIOError = 0; eCacheStatus = DEMUX_STATUS_STARTED; PostReadMessage(demux->mqCache); //if(demux->eStatus == DEMUX_STATUS_STARTED) // PostReadMessage(demux->mq); //* send read message to start reading loop. if(pReplyValue != NULL) *pReplyValue = 0; } else { loge("CdxParserSeekTo() return fail, demux->bCancelSeek:%d", demux->bCancelSeek); if(demux->bCancelSeek == 1 || demux->bStopping == 1) params[0] = DEMUX_ERROR_USER_CANCEL; else params[0] = DEMUX_ERROR_IO; demux->eStatus = DEMUX_STATUS_COMPLETE; //* set to complete status to stop reading. pthread_mutex_lock(&demux->lock); demux->bSeeking = 0; pthread_cond_signal(&demux->cond); pthread_mutex_unlock(&demux->lock); params[1] = nSeekTimeUs/1000; params[2] = 0; demux->callback(demux->pUserData, DEMUX_NOTIFY_SEEK_FINISH, (void*)params); if(pReplyValue != NULL) *pReplyValue = -1; } if(pReplySem != NULL) sem_post(pReplySem); continue; } else if(msg.messageId == DEMUX_COMMAND_RESET_URL) { int64_t nSeekTimeMs; int64_t nSeekTimeFinal; logv("cache thread process message DEMUX_COMMAND_RESET_URL."); nSeekTimeMs = msg.params[2]; logd("ShiftedTimeUrl = %s", demux->shiftedTimeUrl); struct CallBack cb; cb.callback = ParserCallbackProcess; cb.pUserData = (void *)demux; ContorlTask parserContorlTask, *contorlTask; ContorlTask contorlTask1; parserContorlTask.cmd = CDX_PSR_CMD_SET_CALLBACK; parserContorlTask.param = (void *)&cb; parserContorlTask.next = NULL; contorlTask = &parserContorlTask; if(demux->timeShiftLastSeqNum >= 0) { contorlTask1.cmd = CDX_PSR_CMD_SET_TIMESHIFT_LAST_SEQNUM; contorlTask1.param = (void *)&demux->timeShiftLastSeqNum; contorlTask1.next = NULL; contorlTask->next = &contorlTask1; contorlTask = contorlTask->next; } CdxDataSourceT source; bzero(&source, sizeof(source)); source.extraData = NULL; demux->statusCode = 0; // we should reset statusCode to 0 here reopen: source.uri = demux->shiftedTimeUrl; int flags = 0; if(/*demux->mLivemode == 1 || */demux->mLivemode == 2) { flags |= CMCC_TIME_SHIFT; } do { if(demux->pParser) { CdxParserClose(demux->pParser); demux->pParser = NULL; demux->pStream = NULL; } else if(demux->pStream) { CdxStreamClose(demux->pStream); demux->pStream = NULL; } ret = CdxParserPrepare(&source, flags, &demux->mutex, &demux->bCancelSeek, &demux->pParser, &demux->pStream, &parserContorlTask, &parserContorlTask); if(ret < 0) { logd(" resetUrl failed, reconnect,demux->bCancelSeek(%d)", demux->bCancelSeek); if(demux->bCancelSeek) { break; } uintptr_t statusCode = demux->statusCode; logd("+++++ get status code: %zu", statusCode); // if the url is not connectable (http status code 400, 500), // change to livemode 1 if(statusCode >= 400) { char *pLiveModeType = NULL; char *pStartime = NULL; //check livemode here pLiveModeType = strstr(demux->shiftedTimeUrl, "livemode="); demux->mLivemode = 1; pLiveModeType[9] = '1'; pStartime = strstr(demux->shiftedTimeUrl, "&starttime="); if (pStartime != NULL) { pStartime[0] = '\0'; } goto reopen; } usleep(20000); } } while(ret < 0); logd("cache thread: ret = %d", ret); if(ret == 0) { if(pReplyValue != NULL) *pReplyValue = 0; } else { if(pReplyValue != NULL) { if(demux->bCancelSeek) *pReplyValue = -2; // user_cancel else *pReplyValue = -1; } } if(pReplySem != NULL) sem_post(pReplySem); demux->timeShiftLastSeqNum = -1; continue; } else if(msg.messageId == DEMUX_COMMAND_READ) { if(eCacheStatus != DEMUX_STATUS_STARTED) continue; logv("cache thread process message DEMUX_COMMAND_READ."); if(StreamCacheOverflow(demux->pCache)) { //* wait some time for cache buffer. ret = AwMessageQueueTryGetMessage(demux->mqCache, &msg, 200); if(ret == 0) //* new message come, quit loop to process. goto cache_process_message; //* post a read message to continue the reading job after message processed. PostReadMessage(demux->mqCache); } else { //************************************************************** //* read data directly from parser. //************************************************************** CdxPacketT packet; CacheNode node; memset(&packet, 0x00, sizeof(CdxPacketT)); //* 1. get data type. if(CdxParserPrefetch(demux->pParser, &packet) != 0) { logw("prefetch fail."); if(demux->bStopping == 0 && demux->bSeeking == 0) { int err = CdxParserGetStatus(demux->pParser); logd("err = %d", err); if(err == PSR_IO_ERR) { demux->bIOError = 1; } else if (err == PSR_USER_CANCEL) // TODO: { /* do noting */ } else { demux->bEOS = 1; NotifyCacheState(demux); } } continue; } //* 2. request cache buffer. while(1) { node.pData = (unsigned char*)malloc(packet.length); if(node.pData == NULL) { logw("allocate memory for cache node fail, waiting for memory."); //* no free memory, try to wait sometime. ret = AwMessageQueueTryGetMessage(demux->mqCache, &msg, 200); if(ret == 0) //* new message come, quit loop to process. goto cache_process_message; } else { packet.buf = node.pData; packet.buflen = packet.length; packet.ringBuf = NULL; packet.ringBufLen = 0; break; } } //* 3. read data to buffer and submit. ret = CdxParserRead(demux->pParser, &packet); if(ret == 0) { node.pNext = NULL; node.nLength = packet.length; node.eMediaType = packet.type; node.nStreamIndex = packet.streamIndex; node.nFlags = packet.flags; node.nPts = packet.pts; node.nPcr = packet.pcr; node.bIsFirstPart = 1; node.bIsLastPart = 1; node.nDuration = packet.duration; node.info = NULL; node.infoVersion = -1; if(packet.type == CDX_MEDIA_VIDEO) { if(packet.infoVersion != nVideoInfoVersion && packet.info) { logd("cache -- video stream info change: " "preInfoVersion = %d, curInfoVersion = %d", nVideoInfoVersion, packet.infoVersion); int ret; ret = setCacheNodeVideo(&packet, &node); if(ret < 0) { loge("setCacheNodeVideo fail."); abort(); } } nVideoInfoVersion = packet.infoVersion; } else if(packet.type == CDX_MEDIA_AUDIO) { if(packet.infoVersion != nAudioInfoVersion && packet.info) { logd("cache -- audio stream info change: " "preInfoVersion = %d, curInfoVersion = %d", nAudioInfoVersion, packet.infoVersion); int ret; ret = setCacheNodeAudio(&packet, &node); if(ret < 0) { loge("setCacheNodeVideo fail."); abort(); } } nAudioInfoVersion = packet.infoVersion; } StreamCacheAddOneFrame(demux->pCache, &node); //* post a read message to continue the reading job after message processed. PostReadMessage(demux->mqCache); } else { logw("read data from parser return fail."); if(node.pData != NULL) free(node.pData); if(demux->bStopping == 0 && demux->bSeeking == 0) { int err = CdxParserGetStatus(demux->pParser); if(err == PSR_IO_ERR) { demux->bIOError = 1; } else if (err == PSR_USER_CANCEL) // TODO: { /* do noting */ } else { demux->bEOS = 1; } } } continue; } //* end if(StreamCacheOverflow(demux->pCache)) {} else {} } //* end DEMUX_COMMAND_READ. } //* end while(1). return NULL; } static void clearDataSourceFields(CdxDataSourceT* source) { CdxHttpHeaderFieldsT* pHttpHeaders; int i; int nHeaderSize; if(source->uri != NULL) { free(source->uri); source->uri = NULL; } if(source->extraDataType == EXTRA_DATA_HTTP_HEADER && source->extraData != NULL) { pHttpHeaders = (CdxHttpHeaderFieldsT*)source->extraData; nHeaderSize = pHttpHeaders->num; for(i=0; ipHttpHeader[i].key != NULL) free((void*)pHttpHeaders->pHttpHeader[i].key); if(pHttpHeaders->pHttpHeader[i].val != NULL) free((void*)pHttpHeaders->pHttpHeader[i].val); } free(pHttpHeaders->pHttpHeader); free(pHttpHeaders); source->extraData = NULL; source->extraDataType = EXTRA_DATA_UNKNOWN; } else if (source->extraDataType == EXTRA_DATA_RTSP && source->extraData != NULL) { CdxKeyedVectorDestroy((CdxKeyedVectorT *)source->extraData); source->extraData = NULL; source->extraDataType = EXTRA_DATA_UNKNOWN; } return; } static int setDataSourceFields(DemuxCompContext *demuxDhr, CdxDataSourceT* source, void* pHTTPServer, char* uri, CdxKeyedVectorT* pHeaders) { CdxHttpHeaderFieldsT* pHttpHeaders; int i; int nHeaderSize; clearDataSourceFields(source); //* check whether ths uri has a scheme. if(strstr(uri, "://") != NULL) { source->uri = strdup(uri); if(source->uri == NULL) { loge("can not dump string of uri."); return -1; } } else { source->uri = (char*)malloc(strlen(uri)+8); if(source->uri == NULL) { loge("can not dump string of uri."); return -1; } sprintf(source->uri, "file://%s", uri); } source->pHTTPServer = pHTTPServer; if(pHeaders != NULL && (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8))) { const char* key; const char* value; //i = pHeaders->indexOfKey(String8("x-hide-urls-from-log")); //if(i >= 0) // pHeaders->removeItemsAt(i); nHeaderSize = pHeaders->size; if(nHeaderSize > 0) { pHttpHeaders = (CdxHttpHeaderFieldsT*)malloc(sizeof(CdxHttpHeaderFieldsT)); if(pHttpHeaders == NULL) { loge("can not malloc memory for http header."); clearDataSourceFields(source); return -1; } memset(pHttpHeaders, 0, sizeof(CdxHttpHeaderFieldsT)); pHttpHeaders->num = nHeaderSize; pHttpHeaders->pHttpHeader = (CdxHttpHeaderFieldT*)malloc(sizeof(CdxHttpHeaderFieldT)*nHeaderSize); if(pHttpHeaders->pHttpHeader == NULL) { loge("can not malloc memory for http header."); free(pHttpHeaders); clearDataSourceFields(source); return -1; } source->extraData = (void*)pHttpHeaders; source->extraDataType = EXTRA_DATA_HTTP_HEADER; for(i=0; iitem[i].key; value = pHeaders->item[i].val; if(key != NULL) { pHttpHeaders->pHttpHeader[i].key = (const char*)strdup(key); if(pHttpHeaders->pHttpHeader[i].key == NULL) { loge("can not dump string of http header."); clearDataSourceFields(source); return -1; } } else pHttpHeaders->pHttpHeader[i].key = NULL; if(value != NULL) { pHttpHeaders->pHttpHeader[i].val = (const char*)strdup(value); if(pHttpHeaders->pHttpHeader[i].val == NULL) { loge("can not dump string of http header."); clearDataSourceFields(source); return -1; } } else pHttpHeaders->pHttpHeader[i].val = NULL; } } } else if (pHeaders != NULL && strncasecmp("rtsp://", uri, 7) == 0) { /* This block should be removed after cleaning up the code above to use * CdxKeyedVectorT */ nHeaderSize = pHeaders->size; if (nHeaderSize <= 0) return 0; CdxKeyedVectorT *rtspHeader = CdxKeyedVectorCreate(nHeaderSize); if (rtspHeader == NULL) { logw("CdxKeyedVectorCreate() failed"); clearDataSourceFields(source); return -1; } source->extraData = (void *)rtspHeader; source->extraDataType = EXTRA_DATA_RTSP; char* key; char* value; for (i = 0; i < nHeaderSize; i++) { key = pHeaders->item[i].key; value = pHeaders->item[i].val; if (CdxKeyedVectorAdd(rtspHeader, key, value) != 0) { clearDataSourceFields(source); return -1; } } } else if (strncasecmp("bdmv://", uri, 7) == 0) { logw("trace..."); static struct BdmvExtraDataS bdmvED; bdmvED.ioCb = &demuxIoOps; bdmvED.cbHdr = (void *)demuxDhr; source->extraData = &bdmvED; source->extraDataType = EXTRA_DATA_BDMV; } return 0; } static int setMediaInfo(MediaInfo* pMediaInfo, CdxMediaInfoT* pInfoFromParser) { int i; int nStreamCount; VideoStreamInfo* pVideoStreamInfo = NULL; AudioStreamInfo* pAudioStreamInfo = NULL; SubtitleStreamInfo* pSubtitleStreamInfo = NULL; int nCodecSpecificDataLen; char* pCodecSpecificData = NULL; clearMediaInfo(pMediaInfo); pMediaInfo->nDurationMs = pInfoFromParser->program[0].duration; pMediaInfo->nFileSize = pInfoFromParser->fileSize; pMediaInfo->nFirstPts = pInfoFromParser->program[0].firstPts; if(pInfoFromParser->bitrate > 0) pMediaInfo->nBitrate = pInfoFromParser->bitrate; else if(pInfoFromParser->fileSize > 0 && pInfoFromParser->program[0].duration > 0) pMediaInfo->nBitrate = (int)((pInfoFromParser->fileSize*8*1000)/pInfoFromParser->program[0].duration); else pMediaInfo->nBitrate = 0; pMediaInfo->bSeekable = pInfoFromParser->bSeekable; memcpy(pMediaInfo->cRotation,pInfoFromParser->rotate,4*sizeof(unsigned char)); nStreamCount = pInfoFromParser->program[0].videoNum; logv("video stream count = %d", nStreamCount); if(nStreamCount > 0) { pVideoStreamInfo = (VideoStreamInfo*)malloc(sizeof(VideoStreamInfo)*nStreamCount); if(pVideoStreamInfo == NULL) { loge("can not alloc memory for media info."); return -1; } memset(pVideoStreamInfo, 0, sizeof(VideoStreamInfo)*nStreamCount); pMediaInfo->pVideoStreamInfo = pVideoStreamInfo; for(i=0; ipVideoStreamInfo[i]; memcpy(pVideoStreamInfo, &pInfoFromParser->program[0].video[i], sizeof(VideoStreamInfo)); pCodecSpecificData = pVideoStreamInfo->pCodecSpecificData; nCodecSpecificDataLen = pVideoStreamInfo->nCodecSpecificDataLen; pVideoStreamInfo->pCodecSpecificData = NULL; pVideoStreamInfo->nCodecSpecificDataLen = 0; if(pCodecSpecificData != NULL && nCodecSpecificDataLen > 0) { pVideoStreamInfo->pCodecSpecificData = (char*)malloc(nCodecSpecificDataLen); if(pVideoStreamInfo->pCodecSpecificData == NULL) { loge("can not alloc memory for media info."); clearMediaInfo(pMediaInfo); return -1; } memcpy(pVideoStreamInfo->pCodecSpecificData, pCodecSpecificData, nCodecSpecificDataLen); pVideoStreamInfo->nCodecSpecificDataLen = nCodecSpecificDataLen; } if(pInfoFromParser->program[0].flags & kUseSecureInputBuffers) pVideoStreamInfo->bSecureStreamFlag = 1; logv("the %dth video stream info.", i); logv(" codec: %d.", pVideoStreamInfo->eCodecFormat); logv(" width: %d.", pVideoStreamInfo->nWidth); logv(" height: %d.", pVideoStreamInfo->nHeight); logv(" frame rate: %d.", pVideoStreamInfo->nFrameRate); logv(" aspect ratio: %d.", pVideoStreamInfo->nAspectRatio); logv(" is 3D: %s.", pVideoStreamInfo->bIs3DStream ? "true" : "false"); logv(" codec specific data size: %d.", pVideoStreamInfo->nCodecSpecificDataLen); logv(" bSecureStreamFlag : %d",pVideoStreamInfo->bSecureStreamFlag); } pMediaInfo->nVideoStreamNum = nStreamCount; } //* copy audio stream info. nStreamCount = pInfoFromParser->program[0].audioNum; if(nStreamCount > 0) { pAudioStreamInfo = (AudioStreamInfo*)malloc(sizeof(AudioStreamInfo)*nStreamCount); if(pAudioStreamInfo == NULL) { clearMediaInfo(pMediaInfo); loge("can not alloc memory for media info."); return -1; } memset(pAudioStreamInfo, 0, sizeof(AudioStreamInfo)*nStreamCount); pMediaInfo->pAudioStreamInfo = pAudioStreamInfo; for(i=0; ipAudioStreamInfo[i]; memcpy(pAudioStreamInfo, &pInfoFromParser->program[0].audio[i], sizeof(AudioStreamInfo)); pCodecSpecificData = pAudioStreamInfo->pCodecSpecificData; nCodecSpecificDataLen = pAudioStreamInfo->nCodecSpecificDataLen; pAudioStreamInfo->pCodecSpecificData = NULL; pAudioStreamInfo->nCodecSpecificDataLen = 0; if(pCodecSpecificData != NULL && nCodecSpecificDataLen > 0) { pAudioStreamInfo->pCodecSpecificData = (char*)malloc(nCodecSpecificDataLen); if(pAudioStreamInfo->pCodecSpecificData == NULL) { loge("can not alloc memory for media info."); clearMediaInfo(pMediaInfo); return -1; } memcpy(pAudioStreamInfo->pCodecSpecificData, pCodecSpecificData, nCodecSpecificDataLen); pAudioStreamInfo->nCodecSpecificDataLen = nCodecSpecificDataLen; } } pMediaInfo->nAudioStreamNum = nStreamCount; } //* copy subtitle stream info. nStreamCount = pInfoFromParser->program[0].subtitleNum; if(nStreamCount > 0) { pSubtitleStreamInfo = (SubtitleStreamInfo*)malloc(sizeof(SubtitleStreamInfo)*nStreamCount); if(pSubtitleStreamInfo == NULL) { clearMediaInfo(pMediaInfo); loge("can not alloc memory for media info."); return -1; } memset(pSubtitleStreamInfo, 0, sizeof(SubtitleStreamInfo)*nStreamCount); pMediaInfo->pSubtitleStreamInfo = pSubtitleStreamInfo; for(i=0; ipSubtitleStreamInfo[i]; memcpy(pSubtitleStreamInfo, &pInfoFromParser->program[0].subtitle[i], sizeof(SubtitleStreamInfo)); pSubtitleStreamInfo->bExternal = 0; pSubtitleStreamInfo->pUrl = NULL; pSubtitleStreamInfo->fd = -1; pSubtitleStreamInfo->fdSub = -1; } pMediaInfo->nSubtitleStreamNum = nStreamCount; } return 0; } static void clearMediaInfo(MediaInfo* pMediaInfo) { int i; VideoStreamInfo* pVideoStreamInfo; AudioStreamInfo* pAudioStreamInfo; if(pMediaInfo->nVideoStreamNum > 0) { for(i=0; inVideoStreamNum; i++) { pVideoStreamInfo = &pMediaInfo->pVideoStreamInfo[i]; if(pVideoStreamInfo->pCodecSpecificData != NULL && pVideoStreamInfo->nCodecSpecificDataLen > 0) { free(pVideoStreamInfo->pCodecSpecificData); pVideoStreamInfo->pCodecSpecificData = NULL; pVideoStreamInfo->nCodecSpecificDataLen = 0; } } free(pMediaInfo->pVideoStreamInfo); pMediaInfo->pVideoStreamInfo = NULL; pMediaInfo->nVideoStreamNum = 0; } if(pMediaInfo->nAudioStreamNum > 0) { for(i=0; inAudioStreamNum; i++) { pAudioStreamInfo = &pMediaInfo->pAudioStreamInfo[i]; if(pAudioStreamInfo->pCodecSpecificData != NULL && pAudioStreamInfo->nCodecSpecificDataLen > 0) { free(pAudioStreamInfo->pCodecSpecificData); pAudioStreamInfo->pCodecSpecificData = NULL; pAudioStreamInfo->nCodecSpecificDataLen = 0; } } free(pMediaInfo->pAudioStreamInfo); pMediaInfo->pAudioStreamInfo = NULL; pMediaInfo->nAudioStreamNum = 0; } if(pMediaInfo->nSubtitleStreamNum > 0) { free(pMediaInfo->pSubtitleStreamInfo); pMediaInfo->pSubtitleStreamInfo = NULL; pMediaInfo->nSubtitleStreamNum = 0; } pMediaInfo->nFileSize = 0; pMediaInfo->nDurationMs = 0; pMediaInfo->eContainerType = CONTAINER_TYPE_UNKNOWN; pMediaInfo->bSeekable = 0; return; } static int PlayerBufferOverflow(Player* p) { int bVideoOverflow; int bAudioOverflow; int nPictureNum; int nFrameDuration; int nPcmDataSize; int nSampleRate; int nChannelCount; int nBitsPerSample; int nStreamDataSize; int nStreamBufferSize; int nBitrate; int64_t nVideoCacheTime; int64_t nAudioCacheTime; bVideoOverflow = 1; bAudioOverflow = 1; if(PlayerHasVideo(p)) { nPictureNum = PlayerGetValidPictureNum(p); nFrameDuration = PlayerGetVideoFrameDuration(p); nStreamDataSize = PlayerGetVideoStreamDataSize(p); nStreamBufferSize = PlayerGetVideoStreamBufferSize(p); nBitrate = PlayerGetVideoBitrate(p); nVideoCacheTime = nPictureNum*nFrameDuration; if(nBitrate > 0) nVideoCacheTime += ((int64_t)nStreamDataSize)*8*1000*1000/nBitrate; //* cache more than 2 seconds of data. if( nVideoCacheTime <= 2000000 || nStreamDataSize 0) nAudioCacheTime += ((int64_t)nStreamDataSize)*8*1000*1000/nBitrate; if(nAudioCacheTime <= 3000000) //* cache more than 2 seconds of data. bAudioOverflow = 0; logv("nPcmDataSize = %d, nStreamDataSize = %d, \ nBitrate = %d, nAudioCacheTime = %lld, bAudioOverflow = %d", nPcmDataSize, nStreamDataSize, nBitrate, nAudioCacheTime, bAudioOverflow); } return bVideoOverflow && bAudioOverflow; } static int PlayerBufferUnderflow(Player* p) { int bVideoUnderflow; int bAudioUnderFlow; bVideoUnderflow = 0; bAudioUnderFlow = 0; if(PlayerHasVideo(p)) { int nPictureNum; int nStreamFrameNum; nPictureNum = PlayerGetValidPictureNum(p); nStreamFrameNum = PlayerGetVideoStreamFrameNum(p); if(nPictureNum + nStreamFrameNum < 15) bVideoUnderflow = 1; logv("nPictureNum = %d, nStreamFrameNum = %d, bVideoUnderflow = %d", nPictureNum, nStreamFrameNum, bVideoUnderflow); } if(PlayerHasAudio(p)) { int nStreamDataSize; int nPcmDataSize; int nCacheTime; nStreamDataSize = PlayerGetAudioStreamDataSize(p); nPcmDataSize = PlayerGetAudioPcmDataSize(p); nCacheTime = 0; if(nCacheTime == 0 && (nPcmDataSize + nStreamDataSize < 8000)) bAudioUnderFlow = 1; logv("nStreamDataSize = %d, nPcmDataSize = %d, nCacheTime = %d, bAudioUnderFlow = %d", nStreamDataSize, nPcmDataSize, nCacheTime, bAudioUnderFlow); } return bVideoUnderflow | bAudioUnderFlow; } static int GetCacheState(DemuxCompContext* demux) { if(CdxParserControl(demux->pParser, CDX_PSR_CMD_GET_CACHESTATE, &demux->sCacheState) == 0) return 0; else return -1; } static int64_t GetSysTime() { int64_t time; struct timeval t; gettimeofday(&t, NULL); time = (int64_t)t.tv_sec * 1000000; time += t.tv_usec; return time; } static void NotifyCacheState(DemuxCompContext* demux) { int nTotalPercentage; int nBufferPercentage; int nLoadingPercentage; int param[3]; GetCacheState(demux); param[0] = nTotalPercentage = demux->sCacheState.nPercentage; param[1] = nBufferPercentage = StreamCacheGetBufferFullness(demux->pCache); param[2] = nLoadingPercentage = StreamCacheGetLoadingProgress(demux->pCache); logv("notify cache state, total percent = %d%%, \ buffer fullness = %d%%, loading progress = %d%%", nTotalPercentage, nBufferPercentage, nLoadingPercentage); demux->callback(demux->pUserData, DEMUX_NOTIFY_CACHE_STAT, (void*)param); return; } static void PostReadMessage(AwMessageQueue* mq) { if(AwMessageQueueGetCount(mq)<=0) { AwMessage msg; memset(&msg, 0, sizeof(AwMessage)); msg.messageId = DEMUX_COMMAND_READ; AwMessageQueuePostMessage(mq, &msg); } return; } static int ShiftTimeMode(int Shiftedms, char *buf) { struct tm *p; time_t timep; time(&timep); //just get seconds shifted, at least 30s for shiftedtime function if (Shiftedms > 30 * 1000) { timep -= Shiftedms / 1000; } else { return 1; } //p = localtime(&timep); timep += 8*3600; p = gmtime(&timep); sprintf(buf, "%d%02d%02dT%02d%02d%02d.00Z", 1900+p->tm_year, 1+p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec); logd("shifted formatTime str->[%s]\n", buf); return 2; }