SmartAudio/package/allwinner/tina_multimedia/libcedarx/xplayer/cache.c

887 lines
23 KiB
C
Executable File

/*
* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd.
* All rights reserved.
*
* File : cache.c
* Description : cache policy for net stream
* History :
* Author : AL3
* Date : 2015/05/05
* Comment : first version
*
*/
#include <unistd.h>
#include <stdlib.h>
#include <malloc.h>
#include "cache.h"
#include "cdx_log.h"
#include "iniparserapi.h"
// should not larger than 5 / 10 and less than 1 / 10
#define MIN_PASSED_DATA_KEPT_RATIO 3 / 10
#define MIN_PASSED_DATA_KEPT_SIZE(x) ((x)->nMaxBufferSize * MIN_PASSED_DATA_KEPT_RATIO)
static int START_PLAY_CACHE_VIDEO_FRAME_NUM = -1;
static void StreamCacheFlushPassedList(StreamCache* c);
static int StreamCacheIsKeyFrame(StreamCache* c, CacheNode* pNode);
static int64_t StreamCacheSeekByPts(StreamCache* c, int64_t nSeekTimeUs);
static int64_t StreamCacheSeekByPcr(StreamCache* c, int64_t nSeekTimeUs);
static int StreamCacheSeekByOffset(StreamCache* c, int64_t nOffset);
static int StreamCachePlayerCacheTime(Player* p);
static int StreamCacheIsMpeg12KeyFrame(CacheNode* pNode);
static int StreamCacheIsWMV3KeyFrame(CacheNode* pNode);
static int StreamCacheIsH264KeyFrame(CacheNode* pNode);
static int StreamCacheIsH265KeyFrame(CacheNode* pNode);
StreamCache* StreamCacheCreate(void)
{
StreamCache* c;
c = (StreamCache*)malloc(sizeof(StreamCache));
if(c == NULL)
return NULL;
memset(c, 0, sizeof(StreamCache));
c->eContainerFormat = CDX_PARSER_UNKNOW;
c->eVideoCodecFormat = VIDEO_CODEC_FORMAT_UNKNOWN;
c->nLastValidPts = -1;
c->nLastValidPcr = -1;
c->nFirstPts = -1;
c->nMaxBufferSize = 10 * 1024 * 1024;
c->nStartPlaySize = 1024;
pthread_mutex_init(&c->mutex, NULL);
return c;
}
static void NodeInfoDestroy(CacheNode *node)
{
if (node->info == NULL)
return;
if(node->eMediaType == CDX_MEDIA_VIDEO)
{
struct VideoInfo *pVideoInfo = (struct VideoInfo *)node->info;
int i;
for(i = 0; i < pVideoInfo->videoNum; i++)
{
if(pVideoInfo->video[i].pCodecSpecificData)
{
free(pVideoInfo->video[i].pCodecSpecificData);
pVideoInfo->video[i].pCodecSpecificData = NULL;
}
}
}
else if(node->eMediaType == CDX_MEDIA_AUDIO)
{
struct AudioInfo *pAudioInfo = (struct AudioInfo *)node->info;
int i;
for(i = 0; i < pAudioInfo->audioNum; i++)
{
if(pAudioInfo->audio[i].pCodecSpecificData)
{
free(pAudioInfo->audio[i].pCodecSpecificData);
pAudioInfo->audio[i].pCodecSpecificData = NULL;
}
}
}
free(node->info);
node->info = NULL;
return;
}
void StreamCacheDestroy(StreamCache* c)
{
CacheNode* node;
pthread_mutex_lock(&c->mutex);
node = c->pPassedHead;
while(node != NULL)
{
c->pPassedHead = node->pNext;
free(node->pData);
NodeInfoDestroy(node);
free(node);
node = c->pPassedHead;
}
pthread_mutex_unlock(&c->mutex);
free(c);
return;
}
#if (10 * MIN_PASSED_DATA_KEPT_RATIO > 5)
#error "MIN_PASSED_DATA_KEPT_RATIO too large"
#elif (10 * MIN_PASSED_DATA_KEPT_RATIO < 1)
#error "MIN_PASSED_DATA_KEPT_RATIO too little"
#endif
void StreamCacheSetSize(StreamCache* c, int nStartPlaySize, int nMaxBufferSize)
{
int n = nMaxBufferSize * MIN_PASSED_DATA_KEPT_RATIO;
if (n <= 0)
{
loge("check nMaxBufferSize");
return;
}
if ((nStartPlaySize + n) > (nMaxBufferSize * 8 / 10))
nStartPlaySize = nMaxBufferSize * 8 / 10 - n;
pthread_mutex_lock(&c->mutex);
c->nMaxBufferSize = nMaxBufferSize;
c->nStartPlaySize = nStartPlaySize;
pthread_mutex_unlock(&c->mutex);
if (unlikely(START_PLAY_CACHE_VIDEO_FRAME_NUM < 0))
START_PLAY_CACHE_VIDEO_FRAME_NUM =
GetConfigParamterInt("start_play_cache_video_frame_num", 30);
return;
}
int StreamCacheGetSize(StreamCache* c)
{
return c->nDataSize;
}
int StreamCacheUnderflow(StreamCache* c)
{
int bUnderFlow;
pthread_mutex_lock(&c->mutex);
bUnderFlow = (c->nFrameNum <= 0);
pthread_mutex_unlock(&c->mutex);
return bUnderFlow;
}
int StreamCacheOverflow(StreamCache* c)
{
int bOverFlow;
pthread_mutex_lock(&c->mutex);
bOverFlow = ((c->nDataSize + c->nPassedDataSize) >= c->nMaxBufferSize) &&
(c->nPassedDataSize <= MIN_PASSED_DATA_KEPT_SIZE(c));
pthread_mutex_unlock(&c->mutex);
return bOverFlow;
}
int StreamCacheDataEnough(StreamCache* c)
{
int bDataEnough;
pthread_mutex_lock(&c->mutex);
if(c->eVideoCodecFormat != VIDEO_CODEC_FORMAT_UNKNOWN)
{
bDataEnough = (c->nDataSize >= c->nStartPlaySize) &&
(c->nVideoFrameNum >= START_PLAY_CACHE_VIDEO_FRAME_NUM);
logv("nDataSize %d, nStartPlaySize %d, nVideoFrameNum %d, data enough %d",
c->nDataSize, c->nStartPlaySize, c->nVideoFrameNum, bDataEnough);
}
else
{
bDataEnough = (c->nDataSize >= c->nStartPlaySize) &&
(c->nFrameNum > 0);
}
pthread_mutex_unlock(&c->mutex);
return bDataEnough;
}
CacheNode* StreamCacheNextFrame(StreamCache* c)
{
CacheNode* node;
pthread_mutex_lock(&c->mutex);
node = c->pHead;
pthread_mutex_unlock(&c->mutex);
return node;
}
void StreamCacheFlushOneFrame(StreamCache* c)
{
CacheNode* node;
pthread_mutex_lock(&c->mutex);
node = c->pHead;
//if (unlikely(node == NULL))
if (node == NULL)
{
loge("This is impossible! Call StreamCacheNextFrame() first.");
pthread_mutex_unlock(&c->mutex);
return;
}
c->pHead = node->pNext;
c->nFrameNum--;
c->nDataSize -= node->nLength;
c->nPassedDataSize += node->nLength;
c->nPassedFrameNum++;
if (node->eMediaType == CDX_MEDIA_VIDEO) {
c->nVideoFrameNum--;
c->nPassedVideoFrameNum++;
}
pthread_mutex_unlock(&c->mutex);
return;
}
int StreamCacheAddOneFrame(StreamCache* c, CacheNode* node)
{
CacheNode* newNode;
newNode = (CacheNode*)malloc(sizeof(CacheNode));
if(newNode == NULL)
return -1;
*newNode = *node;
newNode->pNext = NULL;
if(newNode->nPts != -1)
{
c->nLastValidPts = node->nPts;
c->pNodeWithLastValidPts = newNode;
if (unlikely(c->nFirstPts == -1))
c->nFirstPts = node->nPts;
}
if(newNode->nPcr != -1)
{
c->nLastValidPcr = node->nPcr;
c->pNodeWithLastValidPcr = newNode;
}
pthread_mutex_lock(&c->mutex);
while ((c->nDataSize + c->nPassedDataSize) >= c->nMaxBufferSize &&
c->nPassedDataSize > MIN_PASSED_DATA_KEPT_SIZE(c))
StreamCacheFlushPassedList(c);
if (likely(c->pTail != NULL))
{
c->pTail->pNext = newNode;
c->pTail = c->pTail->pNext;
/* after call StreamCacheFlushOneFrame, pHead can be NULL */
if (c->pHead == NULL)
c->pHead = newNode;
}
else
{
c->pPassedHead = c->pHead = c->pTail = newNode;
}
c->nDataSize += newNode->nLength;
c->nFrameNum++;
if (node->eMediaType == CDX_MEDIA_VIDEO)
c->nVideoFrameNum++;
pthread_mutex_unlock(&c->mutex);
return 0;
}
void StreamCacheFlushAll(StreamCache* c)
{
CacheNode* node;
pthread_mutex_lock(&c->mutex);
node = c->pPassedHead;
while(node != NULL)
{
c->pPassedHead = node->pNext;
free(node->pData);
NodeInfoDestroy(node);
free(node);
node = c->pPassedHead;
}
c->nDataSize = 0;
c->nFrameNum = 0;
c->nVideoFrameNum = 0;
c->pHead = c->pTail = NULL;
c->nPassedDataSize = 0;
c->nPassedFrameNum = 0;
c->nPassedVideoFrameNum = 0;
c->nLastValidPts = -1;
c->pNodeWithLastValidPts = NULL;
c->nLastValidPcr = -1;
c->pNodeWithLastValidPcr = NULL;
pthread_mutex_unlock(&c->mutex);
return;
}
int StreamCacheGetBufferFullness(StreamCache* c)
{
logd("c->nPassedDataSize %dM, c->nDataSize %dM, c->nMaxBufferSize %dM",
c->nPassedDataSize / 1024 / 1024,
c->nDataSize / 1024 / 1024,
c->nMaxBufferSize / 1024 / 1024);
return (c->nDataSize * 100LL) / c->nMaxBufferSize;
}
int StreamCacheGetLoadingProgress(StreamCache* c)
{
if(c->nDataSize >= c->nStartPlaySize)
return 100;
else
return (c->nDataSize * 100LL) / c->nStartPlaySize;
}
int StreamCacheSetMediaFormat(StreamCache* c,
CdxParserTypeT eContainerFormat,
enum EVIDEOCODECFORMAT eVideoCodecFormat,
int nBitrate)
{
c->eContainerFormat = eContainerFormat;
c->eVideoCodecFormat = eVideoCodecFormat;
c->nBitrate = nBitrate;
return 0;
}
int StreamCacheSetPlayer(StreamCache* c, Player* pPlayer)
{
c->pPlayer = pPlayer;
return 0;
}
static void StreamCacheFlushPassedList(StreamCache* c)
{
CacheNode* node = c->pPassedHead;
if (node == c->pHead || node == c->pTail || node == NULL)
{
loge("Please check!");
return;
}
c->pPassedHead = node->pNext;
c->nPassedFrameNum--;
c->nPassedDataSize -= node->nLength;
if(node == c->pNodeWithLastValidPts)
{
c->nLastValidPts = -1;
c->pNodeWithLastValidPts = NULL;
}
if(node == c->pNodeWithLastValidPcr)
{
c->nLastValidPcr = -1;
c->pNodeWithLastValidPcr = NULL;
}
free(node->pData);
NodeInfoDestroy(node);
free(node);
return;
}
static int getSeekOffset(StreamCache* c, int64_t nSeekTimeUs, int64_t *pOffSet)
{
int nBitrate = c->nBitrate;
if (nBitrate <= 0)
{
nBitrate = 0;
if(PlayerHasVideo(c->pPlayer))
nBitrate += PlayerGetVideoBitrate(c->pPlayer);
if(PlayerHasAudio(c->pPlayer))
nBitrate += PlayerGetAudioBitrate(c->pPlayer);
}
if (nBitrate <= 0)
return -1;
int64_t nCurrUs = PlayerGetPosition(c->pPlayer);
if (nCurrUs < 0)
return -1;
int64_t nTimeUs = nSeekTimeUs - nCurrUs;
if (nTimeUs > 0)
nTimeUs -= StreamCachePlayerCacheTime(c->pPlayer);
*pOffSet = (nBitrate * nTimeUs / (8*1000*1000));
if (nTimeUs < 0)
{
double timeRatio = nCurrUs ? -(double)nTimeUs / nCurrUs : 0;
double offSetRatio = c->nPassedDataSize ?
-(double)*pOffSet / c->nPassedDataSize : 2;
if (timeRatio > offSetRatio)
{
logd("offset calculated from bitrate is too small, "
"timeRatio %f, offSetRatio %f, use timeRatio",
timeRatio, offSetRatio);
*pOffSet = -timeRatio * c->nPassedDataSize;
}
}
return 0;
}
static void adjustList(StreamCache *c, CacheNode *pNodeFound)
{
int nPassedDataSize = 0;
int nPassedFrameNum = 0;
int nPassedVideoFrameNum = 0;
CacheNode *pNode;
for (pNode = c->pPassedHead; pNode != pNodeFound; pNode = pNode->pNext)
{
nPassedDataSize += pNode->nLength;
nPassedFrameNum++;
if (pNode->eMediaType == CDX_MEDIA_VIDEO)
nPassedVideoFrameNum++;
}
c->pHead = pNodeFound;
c->nDataSize = c->nPassedDataSize + c->nDataSize - nPassedDataSize;
c->nFrameNum = c->nPassedFrameNum + c->nFrameNum - nPassedFrameNum;
c->nVideoFrameNum = c->nPassedVideoFrameNum + c->nVideoFrameNum - nPassedVideoFrameNum;
c->nPassedDataSize = nPassedDataSize;
c->nPassedFrameNum = nPassedFrameNum;
c->nPassedVideoFrameNum = nPassedVideoFrameNum;
}
int64_t StreamCacheSeekTo(StreamCache* c, int64_t nSeekTimeUs)
{
int64_t ret;
switch(c->eContainerFormat)
{
case CDX_PARSER_TS:
case CDX_PARSER_BD:
{
int64_t nByteOffset;
if (getSeekOffset(c, nSeekTimeUs, &nByteOffset) == -1)
return -1;
#if 0
/* Try seek by pts first */
CacheNode *pHead = c->pHead;
int nOldPassedSize = c->nPassedDataSize;
int64_t nTimeUs = nSeekTimeUs + c->nFirstPts;
ret = StreamCacheSeekByPts(c, nTimeUs);
if (ret != -1)
{
int n = c->nPassedDataSize - nOldPassedSize;
logd("offsetByPts / offsetByBitrate %llf", (double)n/nByteOffset);
if (abs(n - nByteOffset) * 4 < abs(nByteOffset))
return ret - c->nFirstPts;
logd("difference between seekByPts and seekByBitrate is too big");
adjustList(c, pHead);
}
#endif
ret = StreamCacheSeekByOffset(c, nByteOffset);
if(ret == 0)
ret = nSeekTimeUs;
break;
}
case CDX_PARSER_HLS:
ret = StreamCacheSeekByPcr(c, nSeekTimeUs);
break;
default:
ret = StreamCacheSeekByPts(c, nSeekTimeUs);
break;
}
return ret;
}
static int64_t StreamCacheSeekByPts(StreamCache* c, int64_t nSeekTimeUs)
{
if(c->nLastValidPts < nSeekTimeUs)
return -1;
CacheNode* pNode;
for (pNode = c->pPassedHead; pNode != NULL; pNode = pNode->pNext)
{
if (pNode->nPts == -1)
continue;
if (pNode->nPts > nSeekTimeUs)
return -1;
if (StreamCacheIsKeyFrame(c, pNode))
break;
}
if(pNode == NULL)
return -1;
//* find the last node with pts small than nSeekTimeUs,
//* find the first node with pts bigger than nSeekTimeUs,
//* choose the one with pts more near to nSeekTimeUs as new list head.
int64_t nLastPtsBefore = -1;
int64_t nFirstPtsAfter = 0x7fffffffffffffffLL;
int64_t nCurPts;
CacheNode *pLastNodeBefore = NULL;
CacheNode *pFirstNodeAfter = NULL;
for ( ; pNode != NULL; pNode = pNode->pNext)
{
nCurPts = pNode->nPts;
if (nCurPts == -1 || !StreamCacheIsKeyFrame(c, pNode))
continue;
if(nCurPts <= nSeekTimeUs && nCurPts > nLastPtsBefore)
{
//* update the first node before.
nLastPtsBefore = nCurPts;
pLastNodeBefore = pNode;
}
else if(nCurPts > nSeekTimeUs)
{
//* set the last node after.
nFirstPtsAfter = nCurPts;
pFirstNodeAfter = pNode;
break;
}
}
if (pFirstNodeAfter == NULL && nSeekTimeUs - nLastPtsBefore >= 1000000)
return -1;
if ((nSeekTimeUs - nLastPtsBefore) <= (nFirstPtsAfter - nSeekTimeUs))
pNode = pLastNodeBefore;
else
pNode = pFirstNodeAfter;
adjustList(c, pNode);
return c->pHead->nPts;
}
static int64_t StreamCacheSeekByPcr(StreamCache* c, int64_t nSeekTimeUs)
{
CacheNode* pNode = c->pNodeWithLastValidPcr;
if (pNode == NULL)
return -1;
if (c->nLastValidPts - pNode->nPts + c->nLastValidPcr < nSeekTimeUs)
return -1;
for (pNode = c->pPassedHead; pNode != NULL; pNode = pNode->pNext)
{
if (pNode->nPcr > nSeekTimeUs)
return -1;
else if (pNode->nPcr != -1)
break;
}
//* find the last node with pcr small than nSeekTimeUs,
//* find the first node with pcr bigger than nSeekTimeUs,
//* choose the one with pcr more near to nSeekTimeUs as new list head.
int64_t nLastPcrBefore = -1;
int64_t nFirstPcrAfter = 0x7fffffffffffffffLL;
int64_t nCurPcr;
CacheNode *pLastNodeBefore = NULL;
CacheNode *pFirstNodeAfter = NULL;
for ( ; pNode != NULL; pNode = pNode->pNext)
{
nCurPcr = pNode->nPcr;
if (nCurPcr > nLastPcrBefore && nCurPcr <= nSeekTimeUs)
{
//* update the first node before.
nLastPcrBefore = nCurPcr;
pLastNodeBefore = pNode;
}
else if (nCurPcr > nSeekTimeUs)
{
//* set the last node after.
nFirstPcrAfter = nCurPcr;
pFirstNodeAfter = pNode;
break;
}
}
int64_t nPtsBase = pLastNodeBefore->nPcr;
int64_t nPtsOffset = pLastNodeBefore->nPts;
int64_t nFoundMappedPts = nPtsBase;
CacheNode* pNodeFound = pLastNodeBefore;
pNode = pLastNodeBefore->pNext;
for ( ; pNode != pFirstNodeAfter; pNode = pNode->pNext)
{
if(pNode->nPts == -1 || !StreamCacheIsKeyFrame(c, pNode))
continue;
int64_t nMappedPts = pNode->nPts - nPtsOffset + nPtsBase;
if (nMappedPts <= nSeekTimeUs && nMappedPts > nFoundMappedPts)
{
nFoundMappedPts = nMappedPts;
pNodeFound = pNode;
}
else if (nMappedPts > nSeekTimeUs)
{
nFirstPcrAfter = nMappedPts;
pFirstNodeAfter = pNode;
break;
}
}
logv("nSeekTimeUs %lld, nFoundMappedPts %lld, nFirstPcrAfter %lld",
nSeekTimeUs, nFoundMappedPts, nFirstPcrAfter);
/* check if nFirstPcrAfter is more close to nSeekTimeUs */
if ((nFirstPcrAfter - nSeekTimeUs) < (nSeekTimeUs - nFoundMappedPts))
{
nFoundMappedPts = nFirstPcrAfter;
pNodeFound = pFirstNodeAfter;
}
/* Since HLS is not expected to support precise seekto, deviation less than
* 3 seconds is acceptable.
*/
if(pFirstNodeAfter == NULL && (nSeekTimeUs - nFoundMappedPts) > 3000000)
return -1;
adjustList(c, pNodeFound);
return nFoundMappedPts;
}
static int StreamCacheSeekByOffset(StreamCache* c, int64_t nSeekOffset)
{
/* Actually, if nSeekOffset == c->nDataSize, pNodeFound should be the next
* node after pTail. However, I prefer don't flush cache as many as
* possible.
*/
logv("nSeekOffset %lld, c->nPassedDataSize %d", nSeekOffset, c->nPassedDataSize);
if ((nSeekOffset < -c->nPassedDataSize) || (nSeekOffset > c->nDataSize))
return -1;
CacheNode* pNode;
if (nSeekOffset < 0)
{
nSeekOffset += c->nPassedDataSize;
pNode = c->pPassedHead;
}
else
pNode = c->pHead;
int offset = 0;
for ( ; pNode != NULL; pNode = pNode->pNext)
{
offset += pNode->nLength;
/* see the comment at the beginning of this function */
if (offset >= nSeekOffset)
break;
}
adjustList(c, pNode);
return 0;
}
static int StreamCacheIsKeyFrame(StreamCache* c, CacheNode* pNode)
{
if(c->eVideoCodecFormat == VIDEO_CODEC_FORMAT_UNKNOWN)
return pNode->eMediaType == CDX_MEDIA_AUDIO;
if(c->eContainerFormat == CDX_PARSER_TS ||
c->eContainerFormat == CDX_PARSER_BD ||
c->eContainerFormat == CDX_PARSER_HLS)
{
switch (c->eVideoCodecFormat)
{
case VIDEO_CODEC_FORMAT_H264:
return StreamCacheIsH264KeyFrame(pNode);
case VIDEO_CODEC_FORMAT_MPEG1:
case VIDEO_CODEC_FORMAT_MPEG2:
return StreamCacheIsMpeg12KeyFrame(pNode);
case VIDEO_CODEC_FORMAT_WMV3:
return StreamCacheIsWMV3KeyFrame(pNode);
case VIDEO_CODEC_FORMAT_H265:
return StreamCacheIsH265KeyFrame(pNode);
default:
return 0;
}
}
return pNode->nFlags & KEY_FRAME && pNode->eMediaType == CDX_MEDIA_VIDEO;
}
static int StreamCachePlayerCacheTime(Player* p)
{
int bHasVideo;
int bHasAudio;
int nPictureNum;
int nFrameDuration;
int nPcmDataSize;
int nSampleRate;
int nChannelCount;
int nBitsPerSample;
int nStreamDataSize;
int nBitrate;
int64_t nVideoCacheTime;
int64_t nAudioCacheTime;
nVideoCacheTime = 0;
nAudioCacheTime = 0;
bHasVideo = PlayerHasVideo(p);
bHasAudio = PlayerHasAudio(p);
if(bHasVideo == 0 && bHasAudio == 0)
return 0;
const int64_t unit = 8 * 1000 * 1000;
if(bHasVideo)
{
nPictureNum = PlayerGetValidPictureNum(p);
nFrameDuration = PlayerGetVideoFrameDuration(p);
nStreamDataSize = PlayerGetVideoStreamDataSize(p);
nBitrate = PlayerGetVideoBitrate(p);
nVideoCacheTime = nPictureNum*nFrameDuration;
if(nBitrate > 0)
nVideoCacheTime += nStreamDataSize * unit / nBitrate;
}
if(bHasAudio)
{
nPcmDataSize = PlayerGetAudioPcmDataSize(p);
nStreamDataSize = PlayerGetAudioStreamDataSize(p);
nBitrate = PlayerGetAudioBitrate(p);
PlayerGetAudioParam(p, &nSampleRate, &nChannelCount, &nBitsPerSample);
nAudioCacheTime = 0;
if(nSampleRate != 0 && nChannelCount != 0 && nBitsPerSample != 0)
{
nAudioCacheTime += nPcmDataSize * unit /
(nSampleRate * nChannelCount * nBitsPerSample);
}
if(nBitrate > 0)
nAudioCacheTime += nStreamDataSize * unit/nBitrate;
}
return (int)(nVideoCacheTime + nAudioCacheTime)/(bHasVideo + bHasAudio);
}
static int StreamCacheIsMpeg12KeyFrame(CacheNode* pNode)
{
unsigned int code;
unsigned int pictureType;
unsigned char* ptr;
if(pNode->nLength < 6)
return 0;
code = 0xffffffff;
for(ptr = pNode->pData; ptr <= pNode->pData + pNode->nLength - 6;)
{
code = code<<8 | *ptr++;
if (code == 0x01b3 || //* sequence header.
code == 0x01b8) //* gop header
return 1;
if(code == 0x0100) //* picture header, check picture type.
{
pictureType = (ptr[1]>>3) & 0x7;
if(pictureType == 1)
return 1;
else
return 0;
}
}
return 0;
}
static int StreamCacheIsWMV3KeyFrame(CacheNode* pNode)
{
unsigned int code;
unsigned char* ptr;
if(pNode->nLength < 16)
return 0;
code = 0xffffffff;
for (ptr = pNode->pData; ptr <= pNode->pData + pNode->nLength - 16;)
{
code = code<<8 | *ptr++;
if (code == 0x010f) //* sequence header
return 1;
}
return 0;
}
static int StreamCacheIsH264KeyFrame(CacheNode* pNode)
{
unsigned int code;
unsigned int tmp;
unsigned char* ptr;
if(pNode->nLength < 16)
return 0;
code = 0xffffffff;
for (ptr = pNode->pData; ptr <= pNode->pData + pNode->nLength - 16;)
{
code = code<<8 | *ptr++;
tmp = code & 0xffffff1f;
if (tmp == 0x0107 || //* sps
tmp == 0x0108 || //* pps
tmp == 0x0105) //* idr
return 1;
#if 0
if(tmp == 0x0101) //* slice NULU, check mbNum==0 and pictureType;
{
mbNum = ReadGolomb(...); //* Ue() not implement here.
type = ReadGolomb(...);
if(mbNum == 0 && (type == 2 || type == 7))
return 1;
}
#endif
}
return 0;
}
static int StreamCacheIsH265KeyFrame(CacheNode* pNode)
{
unsigned int code;
unsigned char* ptr;
unsigned int tmp;
if(pNode->nLength < 16)
return 0;
code = 0xffffffff;
for (ptr = pNode->pData; ptr <= pNode->pData + pNode->nLength - 16;)
{
code = code<<8 | *ptr++;
if (code == 0x0140 || //* vps
code == 0x0142 || //* sps
code == 0x0144 || //* pps
code == 0x0126 || //* key frame
code == 0x0128 || //* key frame
code == 0x012a) //* key frame
{
if(*ptr == 0x01)
return 1;
}
}
return 0;
}