TI的encode源程序只在第一帧的时候产生SPS/PPS和IDR帧(根据TI的产生流程,为了方便本文的IDR帧指的是不带SPS/PPS的),之后都是I帧,而且没有提供直接修改IDR帧周期的参数。不过TI提供了一个产生SPS/PPS和IDR帧的流程,如下图:
简单做一下解释:1是改变编码器的动态参数为只产生头,也就是指产生SPS/PPS。
2是调用control函数将改变的动态参数修改到编码器中。VIDENC1_process的调用参照Venc1_create函数。我这里将对VIDENC1_process的调用写成了一个函数:Venc1_control(hVe1, dynParams);可以直接在video.c中调用。
Int Venc1_control(Venc1_Handle hVe, VIDENC1_DynamicParams *dynParams)
{
//VIDENC1_Handle hEncode;
XDAS_Int32 status;
VIDENC1_Status encStatus;
encStatus.size = sizeof(VIDENC1_Status);
encStatus.data.buf = NULL;
status = VIDENC1_control(hVe->hEncode, XDM_SETPARAMS, dynParams, &encStatus);
//if (status != VIDENC1_EOK) {
// Dmai_err1("XDM_SETPARAMS failed, status=%d\n", status);
// VIDENC1_delete(hEncode);
// free(hVe);
// return NULL;
//}
printf("status=%ld\n",status);
hVe->dynParams = *dynParams;
return 0;
}
3是根据修改后的参数编写一帧数据。流程上是调用 VIDENC1_process,其实直接调用Venc1_process就可以 。
4、5、6将都态参数改成产生IDR帧并编码
7、8将动态参数改回原始状态
TI流程上没有详细说明的一点是:源程序是调用一次process编出一帧数据,然后送到writer线程中,唤醒阻塞的writer线程。现在调用两次process,如果只用一次FIFO_put(), 前一帧数据就被覆盖了。
所以,得开辟一块内存,在第一次调用process之后将编出来的帧存到buffer中,第二次调用process之后将两个buffer合并,在调用FIFO_put激活writer线程。小插曲:一开始memcpy的时候忘了包含头文件,导致拷贝不进去数据,但是编译链接只有warning没有报错。
下面上程序:
/* * video.c * * This source file has the implementations for the video thread * functions implemented for 'DVSDK encode demo' on Dm6467 platform * * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <xdc/std.h> #include <ti/sdo/ce/Engine.h> #include <stdlib.h> #include <ti/sdo/dmai/Fifo.h> #include <ti/sdo/dmai/Pause.h> #include <ti/sdo/dmai/BufTab.h> #include <ti/sdo/dmai/VideoStd.h> #include <ti/sdo/dmai/BufferGfx.h> #include <ti/sdo/dmai/Rendezvous.h> #include <ti/sdo/dmai/ce/Venc1.h> #include "video.h" #include "../demo.h" #include <pthread.h> #include <string.h> #include <unistd.h> #include <ti/sdo/dmai/Buffer.h> /* Number of buffers in the pipe to the capture thread */ #define NUM_VIDEO_BUFS 3 /****************************************************************************** * videoThrFxn ******************************************************************************/ Void *videoThrFxn(Void *arg) { VideoEnv *envp = (VideoEnv *) arg; Void *status = THREAD_SUCCESS; VIDENC1_Params defaultParams = Venc1_Params_DEFAULT; VIDENC1_DynamicParams defaultDynParams = Venc1_DynamicParams_DEFAULT; BufferGfx_Attrs gfxAttrs = BufferGfx_Attrs_DEFAULT; Venc1_Handle hVe1 = NULL; Engine_Handle hEngine = NULL; BufTab_Handle hBufTab = NULL; Int frameCnt = 0; Buffer_Handle hCapBuf, hDstBuf; VIDENC1_Params *params; VIDENC1_DynamicParams *dynParams; Int fifoRet; Int bufIdx; /* Buffer to copy buffer(hDstBuf) of IDR Frame without SPS/PPS */ //added by wanglin Int8* IDR_buffer=NULL; IDR_buffer = (unsigned char*)calloc(8000000,sizeof(char)); /* end */ /* Open the codec engine */ hEngine = Engine_open(envp->engineName, NULL, NULL); if (hEngine == NULL) { ERR("Failed to open codec engine %s\n", envp->engineName); cleanup(THREAD_FAILURE); } gblSignalHandlerInstall(); /* Use supplied params if any, otherwise use defaults */ params = envp->params ? envp->params : &defaultParams; dynParams = envp->dynParams ? envp->dynParams : &defaultDynParams; /* Set up codec parameters */ params->maxWidth = envp->imageWidth; params->maxHeight = envp->imageHeight; params->inputChromaFormat = XDM_YUV_420SP; params->reconChromaFormat = XDM_CHROMA_NA; /* Set up codec parameters depending on bit rate */ if (envp->videoBitRate < 0) { /* Variable bit rate */ params->rateControlPreset = IVIDEO_NONE; /* * If variable bit rate use a bogus bit rate value (> 0) * since it will be ignored. */ params->maxBitRate = 2000000; } else { /* Constant bit rate */ params->rateControlPreset = IVIDEO_LOW_DELAY; params->maxBitRate = 2000000;//envp->videoBitRate; } params->dataEndianness = XDM_LE_32; //dynParams->targetBitRate = params->maxBitRate; //changede by wanglin dynParams->targetBitRate = (envp->videoBitRate < 0)?2000000:envp->videoBitRate; //added by wanglin dynParams->inputWidth = params->maxWidth; dynParams->inputHeight = params->maxHeight; /* */ //dynParams->forceFrame = IVIDEO_IDR_FRAME; /* Set length of GOP*/ dynParams->intraFrameInterval=25;// added by wanglin /* Create the video encoder */ //VIDENC1_Handle hEncode; hVe1 = Venc1_create(hEngine,envp->videoEncoder, params, dynParams); if (hVe1 == NULL) { ERR("Failed to create video encoder: %s\n", envp->videoEncoder); cleanup(THREAD_FAILURE); } /* Store the output buffer size in the environment */ envp->outBufSize = Venc1_getOutBufSize(hVe1); /* Signal that the codec is created and output buffer size available */ Rendezvous_meet(envp->hRendezvousWriter); gfxAttrs.colorSpace = ColorSpace_YUV420PSEMI; gfxAttrs.dim.width = envp->imageWidth; gfxAttrs.dim.height = envp->imageHeight; gfxAttrs.dim.lineLength = BufferGfx_calcLineLength(gfxAttrs.dim.width, gfxAttrs.colorSpace); /* * Ask the codec how much input data it needs and create a table of * buffers with this size. */ hBufTab = BufTab_create(NUM_VIDEO_BUFS, Venc1_getInBufSize(hVe1), BufferGfx_getBufferAttrs(&gfxAttrs)); if (hBufTab == NULL) { ERR("Failed to allocate contiguous buffers\n"); cleanup(THREAD_FAILURE); } /* Send buffers to the capture thread to be ready for main loop */ for (bufIdx = 0; bufIdx < NUM_VIDEO_BUFS; bufIdx++) { if (Fifo_put(envp->hCaptureInFifo, BufTab_getBuf(hBufTab, bufIdx)) < 0) { ERR("Failed to send buffer to display thread\n"); cleanup(THREAD_FAILURE); } } /* Signal that initialization is done and wait for other threads */ Rendezvous_meet(envp->hRendezvousInit); //frame count Int32 Frame_count=0; while (!gblGetQuit()) { /* Pause processing? */ Pause_test(envp->hPauseProcess); /* Get a buffer to encode from the capture thread */ fifoRet = Fifo_get(envp->hCaptureOutFifo, &hCapBuf); if (fifoRet < 0) { ERR("Failed to get buffer from video thread\n"); cleanup(THREAD_FAILURE); } /* Did the capture thread flush the fifo? */ if (fifoRet == Dmai_EFLUSH) { cleanup(THREAD_SUCCESS); } /* Get a buffer to encode to from the writer thread */ //Buffer_getNumBytesUsed(hDstBuf); fifoRet = Fifo_get(envp->hWriterOutFifo, &hDstBuf); if (fifoRet < 0) { ERR("Failed to get buffer from video thread\n"); cleanup(THREAD_FAILURE); } /* Did the writer thread flush the fifo? */ if (fifoRet == Dmai_EFLUSH) { cleanup(THREAD_SUCCESS); } /* Make sure the whole buffer is used for input */ BufferGfx_resetDimensions(hCapBuf); /* Force SPS/PPS and IDR Frame*/ //added by wanglin //printf("Frame_count=%ld\n",Frame_count); //printf("Frame_count%25=%d\n",Frame_count%25); if( (Frame_count%25 == 0) && Frame_count != 0){ /* IDR */ unsigned int IDR_bits; dynParams->generateHeader = XDM_ENCODE_AU; dynParams->forceFrame = IVIDEO_IDR_FRAME; Venc1_control(hVe1, dynParams); if (Venc1_process(hVe1, hCapBuf, hDstBuf) < 0) { ERR("Failed to encode video buffer\n"); cleanup(THREAD_FAILURE); } IDR_bits = Buffer_getNumBytesUsed(hDstBuf); memcpy(IDR_buffer,Buffer_getUserPtr(hDstBuf),IDR_bits); /* SPS/PPS */ unsigned int Header_bits; dynParams->generateHeader = XDM_GENERATE_HEADER; dynParams->forceFrame = IVIDEO_NA_FRAME; Venc1_control(hVe1, dynParams); if (Venc1_process(hVe1, hCapBuf, hDstBuf) < 0) { ERR("Failed to encode video buffer\n"); cleanup(THREAD_FAILURE); } Header_bits = Buffer_getNumBytesUsed(hDstBuf); Buffer_setNumBytesUsed(hDstBuf, IDR_bits+Header_bits); memcpy(Buffer_getUserPtr(hDstBuf)+Header_bits,IDR_buffer,IDR_bits); //fwrite(Buffer_getUserPtr(hDstBuf),Buffer_getNumBytesUsed(hDstBuf), 1, stream); //printf("Buffer_getNumBytesUsed(hDstBuf)=%ld\n",Buffer_getNumBytesUsed(hDstBuf)); dynParams->generateHeader = XDM_ENCODE_AU; dynParams->forceFrame = IVIDEO_NA_FRAME; Venc1_control(hVe1, dynParams); } else{ if (Venc1_process(hVe1, hCapBuf, hDstBuf) < 0) { ERR("Failed to encode video buffer\n"); cleanup(THREAD_FAILURE); } } /* end */ //added by wanglin /* Decode the video buffer */ //if (Venc1_process(hVe1, hCapBuf, hDstBuf) < 0) { //ERR("Failed to encode video buffer\n"); //cleanup(THREAD_FAILURE); //} Frame_count++; /* Send encoded buffer to writer thread for filesystem output */ if (Fifo_put(envp->hWriterInFifo, hDstBuf) < 0) { ERR("Failed to send buffer to display thread\n"); cleanup(THREAD_FAILURE); } /* Return buffer to capture thread */ if (Fifo_put(envp->hCaptureInFifo, hCapBuf) < 0) { ERR("Failed to send buffer to display thread\n"); cleanup(THREAD_FAILURE); } /* Increment statistics for the user interface */ gblIncVideoBytesProcessed(Buffer_getNumBytesUsed(hDstBuf)); frameCnt++; } cleanup: /* Make sure the other threads aren't waiting for us */ Rendezvous_force(envp->hRendezvousInit); Rendezvous_force(envp->hRendezvousWriter); Pause_off(envp->hPauseProcess); Fifo_flush(envp->hWriterInFifo); Fifo_flush(envp->hCaptureInFifo); /* Make sure the other threads aren't waiting for init to complete */ Rendezvous_meet(envp->hRendezvousCleanup); /* Clean up the thread before exiting */ if (hBufTab) { BufTab_delete(hBufTab); } if (hVe1) { Venc1_delete(hVe1); } if (hEngine) { Engine_close(hEngine); } /* Free Buffer */ if (IDR_buffer) { free(IDR_buffer); } return status; }