934 lines
24 KiB
C
Executable File
934 lines
24 KiB
C
Executable File
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <pthread.h>
|
|
#include <sys/time.h>
|
|
#include "cdx_config.h"
|
|
#include "log.h"
|
|
#include "CdxParser.h"
|
|
#include "vdecoder.h"
|
|
#include "memoryAdapter.h"
|
|
|
|
#define DEBUG_TIME_INFO 0
|
|
#define DEBUG_COST_DRAM_ENABLE 0
|
|
|
|
#define DRAM_COST_MAX_THREAD_NUM 8
|
|
#define FRAME_COUNT 64
|
|
#define DISPLAY_PICTUER_LIST_NUM 5
|
|
#define DISPLAY_HOLDING_BUFFERS 2
|
|
#define DISPLAY_HOLDING_NUM 0
|
|
#define DISPLAY_COST_TIME 2
|
|
|
|
#define DEMO_PARSER_MAX_STREAM_NUM 1024
|
|
#define DEMO_FILE_NAME_LEN (2*1024)
|
|
|
|
#define DEMO_PARSER_ERROR (1 << 0)
|
|
#define DEMO_DECODER_ERROR (1 << 1)
|
|
#define DEMO_DISPLAY_ERROR (1 << 2)
|
|
#define DEMO_DECODE_FINISH (1 << 3)
|
|
#define DEMO_PARSER_EXIT (1 << 5)
|
|
#define DEMO_DECODER_EXIT (1 << 6)
|
|
#define DEMO_DISPLAY_EXIT (1 << 7)
|
|
#define DEMO_ERROR (DEMO_PARSER_ERROR | DEMO_DECODER_ERROR | DEMO_DISPLAY_ERROR)
|
|
#define DEMO_EXIT (DEMO_ERROR | DEMO_DECODE_FINISH)
|
|
|
|
typedef struct MultiThreadCtx
|
|
{
|
|
pthread_rwlock_t rwrock;
|
|
int nEndofStream;
|
|
int loop;
|
|
int state;
|
|
}MultiThreadCtx;
|
|
|
|
typedef struct DecDemo
|
|
{
|
|
VideoDecoder *pVideoDec;
|
|
CdxParserT *parser;
|
|
CdxDataSourceT source;
|
|
CdxMediaInfoT mediaInfo;
|
|
MultiThreadCtx thread;
|
|
long long totalTime;
|
|
long long DurationTime;
|
|
int nDispFrameCount;
|
|
int nFinishNum;
|
|
int nDramCostThreadNum;
|
|
int nDecodeFrameCount;
|
|
char *pInputFile;
|
|
char *pOutputFile;
|
|
pthread_mutex_t parserMutex;
|
|
/* start to save yuv picture,
|
|
* when decoded picture order >= nSavePictureStartNumber*/
|
|
int nSavePictureStartNumber;
|
|
/* the saved picture number */
|
|
int nSavePictureNumber;
|
|
struct ScMemOpsS* memops;
|
|
}DecDemo;
|
|
|
|
typedef struct display
|
|
{
|
|
VideoPicture* picture;
|
|
int flag;
|
|
struct display *next;
|
|
}display;
|
|
|
|
typedef enum
|
|
{
|
|
INPUT,
|
|
HELP,
|
|
DECODE_FRAME_NUM,
|
|
SAVE_FRAME_START,
|
|
SAVE_FRAME_NUM,
|
|
SAVE_FRAME_FILE,
|
|
COST_DRAM_THREAD_NUM,
|
|
INVALID
|
|
}ARGUMENT_T;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
char Short[8];
|
|
char Name[128];
|
|
ARGUMENT_T argument;
|
|
char Description[512];
|
|
}argument_t;
|
|
|
|
static const argument_t ArgumentMapping[] =
|
|
{
|
|
{ "-h", "--help", HELP,
|
|
"Print this help" },
|
|
{ "-i", "--input", INPUT,
|
|
"Input file" },
|
|
{ "-n", "--decode_frame_num", DECODE_FRAME_NUM,
|
|
"After display n frames, decoder stop" },
|
|
{ "-ss", "--save_frame_start", SAVE_FRAME_START,
|
|
"After display ss frames, saving pictures begin" },
|
|
{ "-sn", "--save_frame_num", SAVE_FRAME_NUM,
|
|
"After sn frames saved, stop saving picture" },
|
|
{ "-o", "--save_frame_file", SAVE_FRAME_FILE,
|
|
"saving picture file" },
|
|
{ "-cn", "--cost_dram_thread_num", COST_DRAM_THREAD_NUM,
|
|
"create cn threads to cost dram, or disturb cpu, test decoder robust" },
|
|
};
|
|
|
|
static void PrintDemoUsage(void)
|
|
{
|
|
int i = 0;
|
|
int num = sizeof(ArgumentMapping) / sizeof(argument_t);
|
|
logd("Usage:");
|
|
while(i < num)
|
|
{
|
|
logd("%s %-32s %s", ArgumentMapping[i].Short, ArgumentMapping[i].Name,
|
|
ArgumentMapping[i].Description);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
ARGUMENT_T GetArgument(char *name)
|
|
{
|
|
int i = 0;
|
|
int num = sizeof(ArgumentMapping) / sizeof(argument_t);
|
|
while(i < num)
|
|
{
|
|
if((0 == strcmp(ArgumentMapping[i].Name, name)) ||
|
|
((0 == strcmp(ArgumentMapping[i].Short, name)) &&
|
|
(0 != strcmp(ArgumentMapping[i].Short, "--"))))
|
|
{
|
|
return ArgumentMapping[i].argument;
|
|
}
|
|
i++;
|
|
}
|
|
return INVALID;
|
|
}
|
|
|
|
|
|
void ParseArgument(DecDemo *Decoder, char *argument, char *value)
|
|
{
|
|
ARGUMENT_T arg;
|
|
// int len = strlen(value);
|
|
int len = 0;
|
|
if(len > DEMO_FILE_NAME_LEN)
|
|
return;
|
|
arg = GetArgument(argument);
|
|
switch(arg)
|
|
{
|
|
case HELP:
|
|
PrintDemoUsage();
|
|
exit(-1);
|
|
case INPUT:
|
|
sprintf(Decoder->pInputFile, "file://");
|
|
sscanf(value, "%2048s", Decoder->pInputFile + 7);
|
|
logd(" get input file: %s ", Decoder->pInputFile);
|
|
break;
|
|
case DECODE_FRAME_NUM:
|
|
sscanf(value, "%d", &Decoder->nFinishNum);
|
|
break;
|
|
case SAVE_FRAME_START:
|
|
sscanf(value, "%d", &Decoder->nSavePictureStartNumber);
|
|
break;
|
|
case SAVE_FRAME_NUM:
|
|
sscanf(value, "%d", &Decoder->nSavePictureNumber);
|
|
break;
|
|
case COST_DRAM_THREAD_NUM:
|
|
sscanf(value, "%d", &Decoder->nDramCostThreadNum);
|
|
break;
|
|
case SAVE_FRAME_FILE:
|
|
sscanf(value, "%2048s", Decoder->pOutputFile);
|
|
logd(" get output file: %s ", Decoder->pOutputFile);
|
|
break;
|
|
case INVALID:
|
|
default:
|
|
logd("unknowed argument : %s", argument);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static long long GetNowUs(void)
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
return (long long)tv.tv_sec * 1000000ll + tv.tv_usec;
|
|
}
|
|
|
|
|
|
static int initDecoder(DecDemo *Decoder)
|
|
{
|
|
int nRet, i;
|
|
int bForceExit = 0;
|
|
VConfig VideoConf;
|
|
VideoStreamInfo VideoInfo;
|
|
struct CdxProgramS *program;
|
|
CdxStreamT *stream = NULL;
|
|
|
|
memset(&VideoConf, 0, sizeof(VConfig));
|
|
memset(&VideoInfo, 0, sizeof(VideoStreamInfo));
|
|
memset(&Decoder->mediaInfo, 0, sizeof(CdxMediaInfoT));
|
|
memset(&Decoder->source, 0, sizeof(CdxDataSourceT));
|
|
|
|
Decoder->memops = MemAdapterGetOpsS();
|
|
if(Decoder->memops == NULL)
|
|
{
|
|
loge("memops is NULL");
|
|
return -1;
|
|
}
|
|
CdcMemOpen(Decoder->memops);
|
|
|
|
logv(" before strcpy(tmpUrl, url) ");
|
|
Decoder->source.uri = Decoder->pInputFile;
|
|
logv(" before CdxParserPrepare() %s", Decoder->source.uri);
|
|
pthread_mutex_init(&Decoder->parserMutex, NULL);
|
|
nRet = CdxParserPrepare(&Decoder->source, 0, &Decoder->parserMutex,
|
|
&bForceExit, &Decoder->parser, &stream, NULL, NULL);
|
|
if(nRet < 0 || Decoder->parser == NULL)
|
|
{
|
|
loge(" decoder open parser error nRet = %d, Decoder->parser: %p", nRet, Decoder->parser);
|
|
return -1;
|
|
}
|
|
logv(" before CdxParserGetMediaInfo() ");
|
|
nRet = CdxParserGetMediaInfo(Decoder->parser, &Decoder->mediaInfo);
|
|
if(nRet != 0)
|
|
{
|
|
loge(" decoder parser get media info error ");
|
|
return -1;
|
|
}
|
|
logv(" before CreateVideoDecoder() ");
|
|
Decoder->pVideoDec = CreateVideoDecoder();
|
|
if(Decoder->pVideoDec == NULL)
|
|
{
|
|
loge(" decoder demom CreateVideoDecoder() error ");
|
|
return -1;
|
|
}
|
|
logv(" before InitializeVideoDecoder() ");
|
|
program = &(Decoder->mediaInfo.program[Decoder->mediaInfo.programIndex]);
|
|
for (i = 0; i < 1; i++)
|
|
{
|
|
VideoStreamInfo *vp = &VideoInfo;
|
|
vp->eCodecFormat = program->video[i].eCodecFormat;
|
|
vp->nWidth = program->video[i].nWidth;
|
|
vp->nHeight = program->video[i].nHeight;
|
|
vp->nFrameRate = program->video[i].nFrameRate;
|
|
vp->nFrameDuration = program->video[i].nFrameDuration;
|
|
vp->nAspectRatio = program->video[i].nAspectRatio;
|
|
vp->bIs3DStream = program->video[i].bIs3DStream;
|
|
vp->nCodecSpecificDataLen = program->video[i].nCodecSpecificDataLen;
|
|
vp->pCodecSpecificData = program->video[i].pCodecSpecificData;
|
|
}
|
|
VideoConf.eOutputPixelFormat = PIXEL_FORMAT_YV12;
|
|
|
|
VideoConf.nDeInterlaceHoldingFrameBufferNum = NUM_OF_PICTURES_KEEPPED_BY_DEINTERLACE;
|
|
VideoConf.nDisplayHoldingFrameBufferNum = NUM_OF_PICTURES_KEEP_IN_LIST;
|
|
VideoConf.nRotateHoldingFrameBufferNum = NUM_OF_PICTURES_KEEPPED_BY_ROTATE;
|
|
VideoConf.nDecodeSmoothFrameBufferNum = NUM_OF_PICTURES_FOR_EXTRA_SMOOTH;
|
|
VideoConf.memops = Decoder->memops;
|
|
|
|
nRet = InitializeVideoDecoder(Decoder->pVideoDec, &VideoInfo, &VideoConf);
|
|
logv(" after InitializeVideoDecoder() ");
|
|
if(nRet != 0)
|
|
{
|
|
loge("decoder demom initialize video decoder fail.");
|
|
DestroyVideoDecoder(Decoder->pVideoDec);
|
|
Decoder->pVideoDec = NULL;
|
|
}
|
|
|
|
pthread_rwlock_init(&Decoder->thread.rwrock, NULL);
|
|
|
|
logd(" initDecoder OK ");
|
|
return 0;
|
|
}
|
|
|
|
static char copyPicture(VideoPicture* pPicture)
|
|
{
|
|
#if 1
|
|
return 0;
|
|
#else
|
|
int nWidth, nHeight;
|
|
int i, j;
|
|
char *pDst, *pSrc, ret;
|
|
ret = 0;
|
|
nHeight = pPicture->nHeight;
|
|
nWidth = pPicture->nWidth;
|
|
pDst = malloc(nWidth * nHeight);
|
|
if(pDst == NULL)
|
|
return ret;
|
|
pSrc = pPicture->pData0;
|
|
for(j = 0; j < nHeight; j++)
|
|
for( i = 0; i < nWidth; i++)
|
|
{
|
|
pDst[j + nHeight + i] = pSrc[j + nHeight + i];
|
|
pDst[i] = pSrc[j + nHeight + i] - pSrc[i];
|
|
}
|
|
|
|
pSrc = pPicture->pData1;
|
|
nHeight = pPicture->nHeight / 2;
|
|
nWidth = pPicture->nWidth / 2;
|
|
for(j = 0; j < nHeight; j++)
|
|
for( i = 0; i < nWidth; i++)
|
|
{
|
|
pDst[j + nHeight + i] = pSrc[j + nHeight + i];
|
|
pDst[i] = pSrc[j + nHeight + i] - pSrc[i];
|
|
}
|
|
|
|
pSrc = pPicture->pData2;
|
|
nHeight = pPicture->nHeight / 2;
|
|
nWidth = pPicture->nWidth / 2;
|
|
for(j = 0; j < nHeight; j++)
|
|
for( i = 0; i < nWidth; i++)
|
|
{
|
|
pDst[j + nHeight + i] = pSrc[j + nHeight + i];
|
|
pDst[i] = pSrc[j + nHeight + i] - pSrc[i];
|
|
}
|
|
ret = pDst[i];
|
|
free(pDst);
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
static void addPictureToList(VideoDecoder *pVideoDec, display *pDisList,
|
|
display **h, display **r, VideoPicture* pPicture)
|
|
{
|
|
int i;
|
|
display *node = NULL;
|
|
display *DisHeader = *h;
|
|
display *DisRear = *r;
|
|
if(pPicture == NULL || pDisList == NULL)
|
|
{
|
|
loge(" add picuter to list error");
|
|
return;
|
|
}
|
|
for(i = 0; i < DISPLAY_PICTUER_LIST_NUM; i++)
|
|
{
|
|
if(pDisList[i].flag == 0)
|
|
{
|
|
node = &pDisList[i];
|
|
node->flag = 1;
|
|
node->picture = pPicture;
|
|
break;
|
|
}
|
|
}
|
|
if(DisHeader == NULL && DisRear == NULL)
|
|
{
|
|
DisHeader = DisRear = node;
|
|
}
|
|
else
|
|
{
|
|
DisRear->next = node;
|
|
node->next = NULL;
|
|
DisRear = node;
|
|
}
|
|
|
|
i = 1;
|
|
node = DisHeader;
|
|
while(node != NULL && node->next != NULL)
|
|
{
|
|
i += 1;
|
|
node = node->next;
|
|
}
|
|
if(i >= DISPLAY_HOLDING_BUFFERS)
|
|
{
|
|
node = DisHeader;
|
|
DisHeader = DisHeader->next;
|
|
node->next = NULL;
|
|
node->flag = 0;
|
|
copyPicture(node->picture);
|
|
usleep(DISPLAY_COST_TIME * 1000); /* displaying one picture use some time */
|
|
logv(" display one picture. pts: %lld ", node->picture->nPts);
|
|
ReturnPicture(pVideoDec, node->picture);
|
|
}
|
|
*h = DisHeader;
|
|
*r = DisRear;
|
|
}
|
|
|
|
static int ReturnAllPicture(VideoDecoder *pVideoDec, display *DisHeader)
|
|
{
|
|
int num = 0;
|
|
while(DisHeader)
|
|
{
|
|
ReturnPicture(pVideoDec, DisHeader->picture);
|
|
DisHeader->flag = 0;
|
|
DisHeader = DisHeader->next;
|
|
num += 1;
|
|
}
|
|
return num;
|
|
}
|
|
|
|
void* H265DecodeThread(void* param)
|
|
{
|
|
DecDemo *pVideoDecoder;
|
|
VideoDecoder *pVideoDec;
|
|
int nRet, nStreamNum, i, state;
|
|
int nEndOfStream;
|
|
|
|
pVideoDecoder = (DecDemo *)param;
|
|
nEndOfStream = 0;
|
|
|
|
pVideoDec = pVideoDecoder->pVideoDec;
|
|
logv(" H265DecodeThread(), thread created ");
|
|
|
|
i = 0;
|
|
nStreamNum = VideoStreamFrameNum(pVideoDec, 0);
|
|
while(nStreamNum < 200)
|
|
{
|
|
usleep(2*1000);
|
|
i++;
|
|
if(i > 100)
|
|
break;
|
|
nStreamNum = VideoStreamFrameNum(pVideoDec, 0);
|
|
}
|
|
logv(" data trunk number: %d, i = %d ", nStreamNum, i);
|
|
|
|
while(1)
|
|
{
|
|
/* step 1: get stream data */
|
|
usleep(50);
|
|
pthread_rwlock_wrlock(&pVideoDecoder->thread.rwrock);
|
|
nEndOfStream = pVideoDecoder->thread.nEndofStream;
|
|
state = pVideoDecoder->thread.state;
|
|
pthread_rwlock_unlock(&pVideoDecoder->thread.rwrock);
|
|
if(state & DEMO_EXIT)
|
|
{
|
|
if(state & DEMO_ERROR)
|
|
{
|
|
loge(" decoer thread recieve an error singnal, exit..... ");
|
|
}
|
|
if(state & DEMO_DECODE_FINISH)
|
|
{
|
|
logd(" decoer thread recieve a finish singnal, exit..... ");
|
|
}
|
|
break;
|
|
}
|
|
|
|
logv(" H265DecodeThread(), DecodeVideoStream() start .... ");
|
|
nRet = DecodeVideoStream(pVideoDec, nEndOfStream /*eos*/,
|
|
0/*key frame only*/, 0/*drop b frame*/,
|
|
0/*current time*/);
|
|
// logd(" ------- decoderThread. one frame cost time: %lld ", deltaTime);
|
|
|
|
if(nEndOfStream == 1 && nRet == VDECODE_RESULT_NO_BITSTREAM)
|
|
{
|
|
logd(" decoer thread finish decoding. exit..... ");
|
|
break;
|
|
}
|
|
if(nRet == VDECODE_RESULT_KEYFRAME_DECODED ||
|
|
nRet == VDECODE_RESULT_FRAME_DECODED)
|
|
pVideoDecoder->nDecodeFrameCount++;
|
|
|
|
if(nRet < 0)
|
|
{
|
|
loge(" decoder return error. decoder exit ");
|
|
pthread_rwlock_wrlock(&pVideoDecoder->thread.rwrock);
|
|
pVideoDecoder->thread.state |= DEMO_DECODER_ERROR;
|
|
pthread_rwlock_unlock(&pVideoDecoder->thread.rwrock);
|
|
break;
|
|
}
|
|
}
|
|
|
|
pthread_rwlock_wrlock(&pVideoDecoder->thread.rwrock);
|
|
pVideoDecoder->thread.state |= DEMO_DECODER_EXIT;
|
|
pthread_rwlock_unlock(&pVideoDecoder->thread.rwrock);
|
|
|
|
logd(" decoder thread exit.... ");
|
|
pthread_exit(NULL);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void savePicture(VideoPicture* pPicture, char *file)
|
|
{
|
|
int nSizeY, nSizeUV, nSize;
|
|
FILE *fp = NULL;
|
|
char *pData = NULL;
|
|
|
|
if(pPicture == NULL)
|
|
{
|
|
loge(" demo decoder save picture error, picture pointer equals NULL ");
|
|
return;
|
|
}
|
|
fp = fopen(file, "ab");
|
|
if(fp == NULL)
|
|
{
|
|
loge(" demo decoder save picture error, open file fail ");
|
|
return;
|
|
}
|
|
nSizeY = pPicture->nWidth * pPicture->nHeight;
|
|
nSizeUV = nSizeY >> 2;
|
|
logd(" save picture to file: %s, size: %dx%d, top: %d, bottom: %d, left: %d, right: %d",
|
|
file, pPicture->nWidth, pPicture->nHeight,
|
|
pPicture->nTopOffset, pPicture->nBottomOffset, pPicture->nLeftOffset, pPicture->nRightOffset);
|
|
/* save y */
|
|
pData = pPicture->pData0;
|
|
nSize = nSizeY;
|
|
fwrite(pData, 1, nSize, fp);
|
|
|
|
/* save u */
|
|
pData = pPicture->pData0 + nSizeY + nSizeUV;
|
|
nSize = nSizeUV;
|
|
fwrite(pData, 1, nSize, fp);
|
|
|
|
/* save v */
|
|
pData = pPicture->pData0 + nSizeY;
|
|
nSize = nSizeUV;
|
|
fwrite(pData, 1, nSize, fp);
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
void *displayPictureThreadFunc(void* param)
|
|
{
|
|
DecDemo *pVideoDecoder;
|
|
VideoDecoder *pVideoDec;
|
|
VideoPicture* pPicture;
|
|
display *DisHeader, *DisRear, *pDisList;
|
|
long long nTime, DurationTime;
|
|
int state, nDispFrameCount, nValidPicNum;
|
|
|
|
pVideoDecoder = (DecDemo *)param;
|
|
|
|
DisHeader = NULL;
|
|
DisRear = NULL;
|
|
nDispFrameCount = 0;
|
|
nValidPicNum = 0;
|
|
DurationTime = 0;
|
|
pVideoDec = pVideoDecoder->pVideoDec;
|
|
|
|
pDisList = calloc(DISPLAY_PICTUER_LIST_NUM, sizeof(display));
|
|
if(pDisList == NULL)
|
|
{
|
|
pthread_rwlock_wrlock(&pVideoDecoder->thread.rwrock);
|
|
pVideoDecoder->thread.state |= DEMO_DISPLAY_ERROR;
|
|
pthread_rwlock_unlock(&pVideoDecoder->thread.rwrock);
|
|
logd(" display thread calloc memory fial ");
|
|
goto display_thread_exit;
|
|
}
|
|
logd(" display thread starting..... ");
|
|
pVideoDecoder->nDispFrameCount = 0;
|
|
nTime = GetNowUs();
|
|
while(1)
|
|
{
|
|
usleep(100);
|
|
pthread_rwlock_wrlock(&pVideoDecoder->thread.rwrock);
|
|
state = pVideoDecoder->thread.state;
|
|
pthread_rwlock_unlock(&pVideoDecoder->thread.rwrock);
|
|
if(state & DEMO_EXIT)
|
|
{
|
|
loge(" display thread recieve an error singnal, exit..... ");
|
|
break;
|
|
}
|
|
pPicture = RequestPicture(pVideoDec, 0/*the major stream*/);
|
|
if(pPicture != NULL)
|
|
{
|
|
logv(" decoder get one picture, size: %dx%d ", pPicture->nWidth, pPicture->nHeight);
|
|
logd(" picture size: %dx%d ", pPicture->nWidth, pPicture->nHeight);
|
|
nTime = GetNowUs() - nTime;
|
|
DurationTime += nTime;
|
|
nDispFrameCount += 1;
|
|
|
|
if(nDispFrameCount >= pVideoDecoder->nSavePictureStartNumber &&
|
|
nDispFrameCount < (pVideoDecoder->nSavePictureStartNumber + pVideoDecoder->nSavePictureNumber))
|
|
{
|
|
logd(" saving picture number: %d ", nDispFrameCount);
|
|
savePicture(pPicture, pVideoDecoder->pOutputFile);
|
|
}
|
|
|
|
if(nDispFrameCount >= pVideoDecoder->nFinishNum)
|
|
{
|
|
loge(" display thread get enungh frame, exit ...");
|
|
pthread_rwlock_wrlock(&pVideoDecoder->thread.rwrock);
|
|
pVideoDecoder->thread.state |= DEMO_DECODE_FINISH;
|
|
pthread_rwlock_unlock(&pVideoDecoder->thread.rwrock);
|
|
break;
|
|
}
|
|
#if DEBUG_TIME_INFO
|
|
if(nDispFrameCount >= FRAME_COUNT)
|
|
{
|
|
float fps, avg;
|
|
pVideoDecoder->nDispFrameCount += nDispFrameCount;
|
|
pVideoDecoder->DurationTime += DurationTime;
|
|
fps = (float)(DurationTime / 1000);
|
|
fps = (nDispFrameCount * 1000) / fps;
|
|
avg = (float)(pVideoDecoder->DurationTime / 1000);
|
|
avg = (pVideoDecoder->nDispFrameCount * 1000) / avg;
|
|
|
|
loge(" decoder speed info. current speed: %.2f, average speed: %.2f ", fps, avg);
|
|
DurationTime = 0;
|
|
nDispFrameCount = 0;
|
|
}
|
|
#endif
|
|
nTime = GetNowUs();
|
|
addPictureToList(pVideoDec, pDisList, &DisHeader, &DisRear, pPicture);
|
|
}
|
|
else
|
|
{
|
|
pthread_rwlock_wrlock(&pVideoDecoder->thread.rwrock);
|
|
state = pVideoDecoder->thread.state;
|
|
pthread_rwlock_unlock(&pVideoDecoder->thread.rwrock);
|
|
if(state & DEMO_DECODER_EXIT)
|
|
{
|
|
nValidPicNum = ValidPictureNum(pVideoDec, 0);
|
|
logv(" display thread find that decode thread had exit ");
|
|
if(nValidPicNum <= 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pVideoDecoder->nDispFrameCount += nDispFrameCount;
|
|
pthread_rwlock_wrlock(&pVideoDecoder->thread.rwrock);
|
|
pVideoDecoder->thread.state |= DEMO_DISPLAY_EXIT;
|
|
pthread_rwlock_unlock(&pVideoDecoder->thread.rwrock);
|
|
ReturnAllPicture(pVideoDec, DisHeader);
|
|
logd(" display thread exit....disp frame num: %d ", pVideoDecoder->nDispFrameCount);
|
|
display_thread_exit:
|
|
if(pDisList)
|
|
free(pDisList);
|
|
pthread_exit(NULL);
|
|
return 0;
|
|
}
|
|
|
|
void *parserThreadFunc(void* param)
|
|
{
|
|
DecDemo *pDec;
|
|
CdxParserT *parser;
|
|
VideoDecoder *pVideoDec;
|
|
int nRet, nStreamNum, state;
|
|
int nValidSize;
|
|
int nRequestDataSize, trytime;
|
|
unsigned char *buf;
|
|
VideoStreamDataInfo dataInfo;
|
|
CdxPacketT packet;
|
|
|
|
buf = malloc(1024*1024);
|
|
if(buf == NULL)
|
|
{
|
|
loge(" parser thread malloc error ");
|
|
goto parser_exit;
|
|
}
|
|
pDec = (DecDemo *)param;
|
|
pVideoDec = pDec->pVideoDec;
|
|
parser = pDec->parser;
|
|
memset(&packet, 0, sizeof(packet));
|
|
logv(" parserThreadFunc(), thread created ! ");
|
|
state = 0;
|
|
trytime = 0;
|
|
while (0 == CdxParserPrefetch(parser, &packet))
|
|
{
|
|
usleep(50);
|
|
nValidSize = VideoStreamBufferSize(pVideoDec, 0) - VideoStreamDataSize(pVideoDec, 0);
|
|
nRequestDataSize = packet.length;
|
|
|
|
pthread_rwlock_wrlock(&pDec->thread.rwrock);
|
|
state = pDec->thread.state;
|
|
pthread_rwlock_unlock(&pDec->thread.rwrock);
|
|
if(state & DEMO_EXIT)
|
|
{
|
|
loge(" hevc parser receive other thread error. exit flag ");
|
|
goto parser_exit;
|
|
}
|
|
if(trytime >= 2000)
|
|
{
|
|
loge(" parser thread trytime >= 2000, maybe some error happen ");
|
|
pthread_rwlock_wrlock(&pDec->thread.rwrock);
|
|
pDec->thread.state |= DEMO_PARSER_ERROR;
|
|
pthread_rwlock_unlock(&pDec->thread.rwrock);
|
|
goto parser_exit;
|
|
}
|
|
if (packet.type == CDX_MEDIA_VIDEO && ((packet.flags&MINOR_STREAM)==0))
|
|
{
|
|
if(nRequestDataSize > nValidSize)
|
|
{
|
|
usleep(50 * 1000);
|
|
trytime++;
|
|
continue;
|
|
}
|
|
|
|
nRet = RequestVideoStreamBuffer(pVideoDec,
|
|
nRequestDataSize,
|
|
(char**)&packet.buf,
|
|
&packet.buflen,
|
|
(char**)&packet.ringBuf,
|
|
&packet.ringBufLen,
|
|
0);
|
|
if(nRet != 0)
|
|
{
|
|
logw(" RequestVideoStreamBuffer fail. request size: %d, valid size: %d ",
|
|
nRequestDataSize, nValidSize);
|
|
usleep(50*1000);
|
|
continue;
|
|
}
|
|
if(packet.buflen + packet.ringBufLen < nRequestDataSize)
|
|
{
|
|
loge(" RequestVideoStreamBuffer fail, require size is too small ");
|
|
pthread_rwlock_wrlock(&pDec->thread.rwrock);
|
|
pDec->thread.state |= DEMO_PARSER_ERROR;
|
|
pthread_rwlock_unlock(&pDec->thread.rwrock);
|
|
goto parser_exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
packet.buf = buf;
|
|
packet.buflen = packet.length;
|
|
CdxParserRead(parser, &packet);
|
|
continue;
|
|
}
|
|
trytime = 0;
|
|
nStreamNum = VideoStreamFrameNum(pVideoDec, 0);
|
|
if(nStreamNum > DEMO_PARSER_MAX_STREAM_NUM)
|
|
{
|
|
usleep(50*1000);
|
|
}
|
|
nRet = CdxParserRead(parser, &packet);
|
|
if(nRet != 0)
|
|
{
|
|
loge(" parser thread read video data error ");
|
|
pthread_rwlock_wrlock(&pDec->thread.rwrock);
|
|
pDec->thread.state |= DEMO_PARSER_ERROR;
|
|
pthread_rwlock_unlock(&pDec->thread.rwrock);
|
|
goto parser_exit;
|
|
}
|
|
memset(&dataInfo, 0, sizeof(VideoStreamDataInfo));
|
|
dataInfo.pData = packet.buf;
|
|
dataInfo.nLength = packet.length;
|
|
dataInfo.nPts = packet.pts;
|
|
dataInfo.nPcr = packet.pcr;
|
|
dataInfo.bIsFirstPart = (!!(packet.flags & FIRST_PART));
|
|
dataInfo.bIsLastPart = (!!(packet.flags & LAST_PART));
|
|
nRet = SubmitVideoStreamData(pVideoDec , &dataInfo, 0);
|
|
if(nRet != 0)
|
|
{
|
|
loge(" parser thread SubmitVideoStreamData() error ");
|
|
pthread_rwlock_wrlock(&pDec->thread.rwrock);
|
|
pDec->thread.state |= DEMO_PARSER_ERROR;
|
|
pthread_rwlock_unlock(&pDec->thread.rwrock);
|
|
goto parser_exit;
|
|
}
|
|
}
|
|
|
|
pthread_rwlock_wrlock(&pDec->thread.rwrock);
|
|
pDec->thread.nEndofStream = 1;
|
|
pDec->thread.state |= DEMO_PARSER_EXIT;
|
|
pthread_rwlock_unlock(&pDec->thread.rwrock);
|
|
|
|
parser_exit:
|
|
if(buf)
|
|
free(buf);
|
|
logv(" parser exit..... ");
|
|
pthread_exit(NULL);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* DRAMcostTHread() thread make cpu do some other thing, cost DRAM bandwidth
|
|
* */
|
|
void *DRAMcostTHread(void *arg)
|
|
{
|
|
#define MEMORY_STRIDE 1920
|
|
#define MEMORY_NUM 8
|
|
#define MEMORY_BLOCK (1080*MEMORY_STRIDE)
|
|
#define MEMORY_SIZE (MEMORY_BLOCK*MEMORY_NUM)
|
|
|
|
DecDemo *pDec;
|
|
//int bExitFlag = 0;
|
|
int i, j;
|
|
int state;
|
|
char *pSrc, *pDst, *p;
|
|
|
|
pSrc = NULL;
|
|
pDst = NULL;
|
|
pDec = (DecDemo *)arg;
|
|
|
|
pSrc = (char *)malloc(MEMORY_SIZE);
|
|
if(pSrc == NULL)
|
|
{
|
|
logd(" DRAMcostTHread malloc fail ....pSrc ");
|
|
goto DRAM_cost_exit;
|
|
}
|
|
pDst = (char *)malloc(MEMORY_SIZE);
|
|
if(pDst == NULL)
|
|
{
|
|
logd(" DRAMcostTHread malloc fail ....pDst ");
|
|
goto DRAM_cost_exit;
|
|
}
|
|
logd(" DRAM memory copy thread created .... ");
|
|
while(1)
|
|
{
|
|
char *s, *d;
|
|
usleep(100);
|
|
pthread_rwlock_wrlock(&pDec->thread.rwrock);
|
|
//bExitFlag = pDec->thread.nEndofStream ;
|
|
state = pDec->thread.state;
|
|
pthread_rwlock_unlock(&pDec->thread.rwrock);
|
|
if(state & DEMO_DECODER_EXIT)
|
|
{
|
|
logd(" DRAM COST THREAD EIXT..... ");
|
|
break;
|
|
}
|
|
|
|
for(j = 0; j < MEMORY_NUM; j++)
|
|
{
|
|
s = pSrc + j*MEMORY_BLOCK;
|
|
d = pDst + j*MEMORY_BLOCK;
|
|
for(i = 0; i < 1080; i++)
|
|
{
|
|
int k;
|
|
p = s;
|
|
for(k = 0; k < MEMORY_NUM*10 && k*j < MEMORY_STRIDE; k++)
|
|
p[k*j] = rand()%128;
|
|
memcpy(d, s, MEMORY_STRIDE);
|
|
s += MEMORY_STRIDE;
|
|
d += MEMORY_STRIDE;
|
|
}
|
|
}
|
|
}
|
|
DRAM_cost_exit:
|
|
if(pSrc)
|
|
free(pSrc);
|
|
if(pDst)
|
|
free(pDst);
|
|
pthread_exit(NULL);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void DemoHelpInfo(void)
|
|
{
|
|
logd(" ==== CedarX linux decoder demo help start ===== ");
|
|
logd(" -h or --help to show the demo usage");
|
|
logd(" demo created by zouwenhuan, allwinnertech/AL3 ");
|
|
logd(" email: zouwenhuan@allwinnertech.com ");
|
|
logd(" ===== CedarX linux decoder demo help end ====== ");
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
int nRet = 0;
|
|
int i, nDramCostThreadNum;
|
|
char *pInputFile;
|
|
char *pOutputFile;
|
|
pthread_t tdecoder, tparser, tdisplay;
|
|
pthread_t dram[DRAM_COST_MAX_THREAD_NUM];
|
|
DecDemo Decoder;
|
|
long long endTime;
|
|
AddVDPlugin();
|
|
|
|
DemoHelpInfo();
|
|
|
|
pInputFile = calloc(DEMO_FILE_NAME_LEN, 1);
|
|
if(pInputFile == NULL)
|
|
{
|
|
loge(" input file. calloc memory fail. ");
|
|
return 0;
|
|
}
|
|
pOutputFile = calloc(DEMO_FILE_NAME_LEN, 1);
|
|
if(pOutputFile == NULL)
|
|
{
|
|
loge(" output file. calloc memory fail. ");
|
|
free(pInputFile);
|
|
return 0;
|
|
}
|
|
memset(&Decoder, 0, sizeof(DecDemo));
|
|
Decoder.nFinishNum = 0x7fffffff;
|
|
Decoder.nDramCostThreadNum = 0;
|
|
Decoder.pInputFile = NULL;
|
|
Decoder.nSavePictureNumber = 0;
|
|
Decoder.nSavePictureStartNumber = 0xffffff;
|
|
Decoder.pInputFile = pInputFile;
|
|
Decoder.pOutputFile = pOutputFile;
|
|
if(argc >= 2)
|
|
{
|
|
for(i = 1; i < (int)argc; i += 2)
|
|
{
|
|
ParseArgument(&Decoder, argv[i], argv[i + 1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logd(" we need more arguments ");
|
|
PrintDemoUsage();
|
|
return 0;
|
|
}
|
|
|
|
nRet = initDecoder(&Decoder);
|
|
if(nRet != 0)
|
|
{
|
|
loge(" decoder demom initDecoder error ");
|
|
return 0;
|
|
}
|
|
logd("decoder file: %s", Decoder.pInputFile);
|
|
|
|
|
|
Decoder.totalTime = GetNowUs();
|
|
|
|
nDramCostThreadNum = Decoder.nDramCostThreadNum;
|
|
|
|
pthread_create(&tparser, NULL, parserThreadFunc, (void*)(&Decoder));
|
|
pthread_create(&tdecoder, NULL, H265DecodeThread, (void*)(&Decoder));
|
|
pthread_create(&tdisplay, NULL, displayPictureThreadFunc, (void*)(&Decoder));
|
|
for(i = 0; i < nDramCostThreadNum; i++)
|
|
{
|
|
pthread_create(&dram[i], NULL, DRAMcostTHread, (void*)(&Decoder));
|
|
logd(" creat dram memory copy thread[%d] ", i);
|
|
}
|
|
|
|
pthread_join(tparser, (void**)&nRet);
|
|
pthread_join(tdecoder, (void**)&nRet);
|
|
pthread_join(tdisplay, (void**)&nRet);
|
|
for(i = 0; i < nDramCostThreadNum; i++)
|
|
pthread_join(dram[i], (void**)&nRet);
|
|
|
|
endTime = GetNowUs();
|
|
Decoder.totalTime = endTime - Decoder.totalTime;
|
|
logd(" demoDecoder finish.decode frame: %d, display frame: %d, cost %lld s ",
|
|
Decoder.nDecodeFrameCount, Decoder.nDispFrameCount, Decoder.totalTime/(1000*1000));
|
|
pthread_mutex_destroy(&Decoder.parserMutex);
|
|
CdxParserClose(Decoder.parser);
|
|
logv(" after CdxParserClose()");
|
|
DestroyVideoDecoder(Decoder.pVideoDec);
|
|
if(pInputFile != NULL)
|
|
free(pInputFile);
|
|
if(pOutputFile != NULL)
|
|
free(pOutputFile);
|
|
logd(" demo decoder exit successful");
|
|
CdcMemClose(Decoder.memops);
|
|
return 0;
|
|
}
|