/* * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. * All rights reserved. * * File : recoderdemo.c * Description : recoderdemo * History : * */ #define LOG_TAG "recoderdemo" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "memoryAdapter.h" #include "awencoder.h" #include "RecoderWriter.h" #include "csi.h" #include "disp.h" #include #define SAVE_VIDEO_FRAME (0) #define AUDIO_INPUT (0) #define VIDEO_INPUT (1) static const int STATUS_IDEL = 0; FILE* inputPCM = NULL; FILE* inputYUV = NULL; char pcm_path[128] = {0}; char yuv_path[128] = {0}; int videoEos = 0; int audioEos = 0; #define CAPTURE_FRAME_NUM 500 typedef struct DemoRecoderContext { AwEncoder* mAwEncoder; int callbackData; pthread_mutex_t mMutex; VideoEncodeConfig videoConfig; AudioEncodeConfig audioConfig; CdxMuxerT* pMuxer; int muxType; char pUrl[1024]; CdxWriterT* pStream; char* pOutUrl; unsigned char* extractDataBuff; unsigned int extractDataLength; pthread_t muxerThreadId ; pthread_t audioDataThreadId ; pthread_t videoDataThreadId ; AwPoolT *pool; CdxQueueT *dataQueue; int exitFlag ; unsigned char* pAddrPhyY; unsigned char* pAddrPhyC; int bUsed; FILE* fpSaveVideoFrame; }DemoRecoderContext; int save_frame_to_file(void *str, void *start, int length) { FILE *fp = NULL; fp = fopen(str, "ab+"); if(!fp) { printf("can not fopen (%s)\n",strerror(errno)); return -1; } if(fwrite(start, length, 1, fp)) { printf("write finish!\n"); fclose(fp); return 0; } else { printf(" can not fwrite(%s)!\n",strerror(errno)); fclose(fp); return -1; } return 0; } //* a notify callback for AwEncorder. void NotifyCallbackForAwEncorder(void* pUserData, int msg, void* param) { DemoRecoderContext* pDemoRecoder = (DemoRecoderContext*)pUserData; switch(msg) { case AWENCODER_VIDEO_ENCODER_NOTIFY_RETURN_BUFFER: { int id = *((int*)param); if(id == 0) { pDemoRecoder->bUsed = 0; } break; } default: { printf("warning: unknown callback from AwRecorder.\n"); break; } } return ; } int onVideoDataEnc(void *app,CdxMuxerPacketT *buff) { CdxMuxerPacketT *packet = NULL; DemoRecoderContext* pDemoRecoder = (DemoRecoderContext*)app; if (!buff) return 0; packet = (CdxMuxerPacketT*)malloc(sizeof(CdxMuxerPacketT)); packet->buflen = buff->buflen; packet->length = buff->length; packet->buf = malloc(buff->buflen); memcpy(packet->buf, buff->buf, packet->buflen); packet->pts = buff->pts; packet->type = buff->type; packet->streamIndex = buff->streamIndex; packet->duration = buff->duration; #if SAVE_VIDEO_FRAME if(pDemoRecoder->fpSaveVideoFrame) { fwrite(packet->buf, 1, packet->buflen, pDemoRecoder->fpSaveVideoFrame); } #endif CdxQueuePush(pDemoRecoder->dataQueue,packet); return 0; } int onAudioDataEnc(void *app,CdxMuxerPacketT *buff) { CdxMuxerPacketT *packet = NULL; DemoRecoderContext* pDemoRecoder = (DemoRecoderContext*)app; if (!buff) return 0; packet = (CdxMuxerPacketT*)malloc(sizeof(CdxMuxerPacketT)); packet->buflen = buff->buflen; packet->length = buff->length; packet->buf = malloc(buff->buflen); memcpy(packet->buf, buff->buf, packet->buflen); packet->pts = buff->pts; packet->type = buff->type; packet->streamIndex = buff->streamIndex; CdxQueuePush(pDemoRecoder->dataQueue,packet); return 0; } void* MuxerThread(void *param) { //int ret = 0; int i =0; CdxMuxerPacketT *mPacket = NULL; DemoRecoderContext *p = (DemoRecoderContext*)param; RecoderWriterT *rw = NULL; #if FS_WRITER CdxFsCacheMemInfo fs_cache_mem; int fs_mode = FSWRITEMODE_DIRECT; // int fs_cache_size = 1024 * 1024; #endif logd("MuxerThread"); if(p->pUrl) { if ((p->pStream = CdxWriterCreat(p->pUrl)) == NULL) { loge("CdxWriterCreat() failed"); return 0; } rw = (RecoderWriterT*)p->pStream; rw->file_mode = FD_FILE_MODE; strcpy(rw->file_path, p->pUrl); RWOpen(p->pStream); p->pMuxer = CdxMuxerCreate(p->muxType, p->pStream); if(p->pMuxer == NULL) { loge("CdxMuxerCreate failed"); return 0; } logd("MuxerThread init ok"); } CdxMuxerMediaInfoT mediainfo; memset(&mediainfo, 0, sizeof(CdxMuxerMediaInfoT)); switch (p->audioConfig.nType) { case AUDIO_ENCODE_PCM_TYPE: mediainfo.audio.eCodecFormat = AUDIO_ENCODER_PCM_TYPE; break; case AUDIO_ENCODE_AAC_TYPE: mediainfo.audio.eCodecFormat = AUDIO_ENCODER_AAC_TYPE; break; case AUDIO_ENCODE_MP3_TYPE: mediainfo.audio.eCodecFormat = AUDIO_ENCODER_MP3_TYPE; break; case AUDIO_ENCODE_LPCM_TYPE: mediainfo.audio.eCodecFormat = AUDIO_ENCODER_LPCM_TYPE; break; default: loge("unlown audio type(%d)", p->audioConfig.nType); break; } #if AUDIO_INPUT mediainfo.audioNum = 1; #endif #if VIDEO_INPUT mediainfo.videoNum = 1; #endif if(p->muxType == CDX_MUXER_AAC) { mediainfo.videoNum = 0; } mediainfo.audio.nAvgBitrate = p->audioConfig.nBitrate; mediainfo.audio.nBitsPerSample = p->audioConfig.nSamplerBits; mediainfo.audio.nChannelNum = p->audioConfig.nOutChan; mediainfo.audio.nMaxBitRate = p->audioConfig.nBitrate; mediainfo.audio.nSampleRate = p->audioConfig.nOutSamplerate; mediainfo.audio.nSampleCntPerFrame = 1024; // aac if(p->videoConfig.nType == VIDEO_ENCODE_H264) mediainfo.video.eCodeType = VENC_CODEC_H264; else if(p->videoConfig.nType == VIDEO_ENCODE_JPEG) mediainfo.video.eCodeType = VENC_CODEC_JPEG; else { loge("cannot suppot this video type"); return 0; } mediainfo.video.nWidth = p->videoConfig.nOutWidth; mediainfo.video.nHeight = p->videoConfig.nOutHeight; mediainfo.video.nFrameRate = p->videoConfig.nFrameRate; logd("******************* mux mediainfo *****************************"); logd("videoNum : %d", mediainfo.videoNum); logd("audioNum : %d", mediainfo.audioNum); logd("videoTYpe : %d", mediainfo.video.eCodeType); logd("framerate : %d", mediainfo.video.nFrameRate); logd("width : %d", mediainfo.video.nWidth); logd("height : %d", mediainfo.video.nHeight); logd("**************************************************************"); if(p->pMuxer) { CdxMuxerSetMediaInfo(p->pMuxer, &mediainfo); #if FS_WRITER memset(&fs_cache_mem, 0, sizeof(CdxFsCacheMemInfo)); fs_cache_mem.m_cache_size = 512 * 1024; fs_cache_mem.mp_cache = (cdx_uint8*)malloc(fs_cache_mem.m_cache_size); if (fs_cache_mem.mp_cache == NULL) { loge("fs_cache_mem.mp_cache malloc failed\n"); return NULL; } CdxMuxerControl(p->pMuxer, SET_CACHE_MEM, &fs_cache_mem); fs_mode = FSWRITEMODE_CACHETHREAD; /* fs_cache_size = 1024 * 1024; CdxMuxerControl(p->pMuxer, SET_FS_SIMPLE_CACHE_SIZE, &fs_cache_size); fs_mode = FSWRITEMODE_SIMPLECACHE; */ //fs_mode = FSWRITEMODE_DIRECT; CdxMuxerControl(p->pMuxer, SET_FS_WRITE_MODE, &fs_mode); #endif } logd("extractDataLength %d",p->extractDataLength); if(p->extractDataLength > 0 && p->pMuxer) { logd("demo WriteExtraData"); if(p->pMuxer) CdxMuxerWriteExtraData(p->pMuxer, p->extractDataBuff, p->extractDataLength, 0); } if(p->pMuxer) { logd("write head"); CdxMuxerWriteHeader(p->pMuxer); } #if AUDIO_INPUT && VIDEO_INPUT while ((audioEos==0) || (videoEos==0)) #elif VIDEO_INPUT while(videoEos==0) #elif AUDIO_INPUT while(audioEos==0) #endif { while (!CdxQueueEmpty(p->dataQueue)) { mPacket = CdxQueuePop(p->dataQueue); i++; if(p->pMuxer) { if(CdxMuxerWritePacket(p->pMuxer, mPacket) < 0) { loge("+++++++ CdxMuxerWritePacket failed"); } } free(mPacket->buf); free(mPacket); } usleep(1*1000); } if(p->pMuxer) { logd("write trailer"); CdxMuxerWriteTrailer(p->pMuxer); } logd("CdxMuxerClose"); if(p->pMuxer) { CdxMuxerClose(p->pMuxer); p->pMuxer = NULL; } if(p->pStream) { RWClose(p->pStream); CdxWriterDestroy(p->pStream); p->pStream = NULL; rw = NULL; } #if FS_WRITER if(fs_cache_mem.mp_cache) { free(fs_cache_mem.mp_cache); fs_cache_mem.mp_cache = NULL; } #endif logd("MuxerThread has finish!"); p->exitFlag = 1; return 0; } void* AudioInputThread(void *param) { int ret = 0; //int i =0; DemoRecoderContext *p = (DemoRecoderContext*)param; logd("AudioInputThread"); int num = 0; int size2 = 0; int64_t audioPts = 0; AudioInputBuffer audioInputBuffer; memset(&audioInputBuffer, 0x00, sizeof(AudioInputBuffer)); audioInputBuffer.nLen = 1024*4; //176400; audioInputBuffer.pData = (char*)malloc(audioInputBuffer.nLen); while(num<1024) { ret = -1; if(!audioEos) { if (inputPCM == NULL) { printf("before fread, inputPCM is NULL\n"); break; } size2 = fread(audioInputBuffer.pData, 1, audioInputBuffer.nLen, inputPCM); if(size2 < audioInputBuffer.nLen) { logd("read error"); audioEos = 1; } while(ret) { audioInputBuffer.nPts = audioPts; ret = AwEncoderWritePCMdata(p->mAwEncoder,&audioInputBuffer); //logd("=== WritePCMdata audioPts : %lld", audioPts); usleep(10*1000); } usleep(20*1000); audioPts += 23; } num ++; } logd("audio read data finish!"); audioEos = 1; return 0; } typedef struct { pthread_t csi_thread_id; int run_flag; DemoRecoderContext *pContext; }csi_handle; void *csi_mgr_thread_function(void *pParam) { csi_handle *hdl = (csi_handle *)pParam; unsigned long long csiV4l2CurMsec; unsigned long long csiV4l2DiffMsec; CSI_BUF csiV4l2Buf; disp_rect info; disp_rectsz rect; VideoInputBuffer videoInputBuffer; int pts = 0; int number = 0; int width32_align = 0; int height32_align = 0; char source_data_path[30]; int ret = 0; struct win_info win; unsigned int fbAddr[3]; memset(&info,0,sizeof(disp_rect)); csi_stream_on(csi_get_fd()); while(hdl->run_flag && number < CAPTURE_FRAME_NUM) { csi_buf_dequeue(&csiV4l2Buf, &csiV4l2CurMsec, &csiV4l2DiffMsec); if(csiV4l2DiffMsec == 0) { printf("get buffer diff 0!\n"); continue; } if(csiV4l2Buf.m_v4l2Buf[csiV4l2Buf.m_v4l2BufIdx].m_addr == 0) { printf("get null address!\n"); continue; } info.width = CSI_WIDTH_MAX;//disp_screenwidth_get(); info.height = CSI_HEIGHT_MAX;//disp_screenheight_get(); width32_align = (info.width + 31)/32 * 32; height32_align = (info.height + 31)/32 *32; #if 1 sprintf(source_data_path, "/mnt/UDISK/%s%d%s", "source_data", number+1, ".yuv"); ret = save_frame_to_file(source_data_path, csiV4l2Buf.m_buf_start, info.width*info.height); ret = save_frame_to_file(source_data_path, csiV4l2Buf.m_buf_start + width32_align*height32_align, info.width*info.height/2); #endif #if 1 memset(&win, 0, sizeof(struct win_info)); fbAddr[0] = (unsigned char *)csiV4l2Buf.m_v4l2Buf[csiV4l2Buf.m_v4l2BufIdx].m_addr; fbAddr[1] = (unsigned char *)(fbAddr[0]+ width32_align*height32_align); win.scn_win.nDisplayWidth = disp_screenwidth_get(); win.scn_win.nDisplayHeight = disp_screenheight_get(); win.scn_win.nLeftOff = 0; win.scn_win.nTopOff = 0; win.scn_win.nWidth = info.width; win.scn_win.nHeight = info.height; win.src_win.nWidth = info.width; win.src_win.nHeight= info.height; win.src_win.nTopOffset = 0; win.src_win.nLeftOffset = 0; win.src_win.nBottomOffset = 0; win.src_win.nRightOffset = 0; win.nScreenHeight = 480; win.nScreenWidth = 800; ret = disp_video_layer_param_set(fbAddr, VIDEO_PIXEL_FORMAT_NV21, &win); #endif videoInputBuffer.pAddrPhyY = (unsigned char *)csiV4l2Buf.m_v4l2Buf[csiV4l2Buf.m_v4l2BufIdx].m_addr; videoInputBuffer.pAddrPhyC = (unsigned char *)(videoInputBuffer.pAddrPhyY + width32_align*height32_align); videoInputBuffer.nID = 0; videoInputBuffer.nLen = width32_align*height32_align*3/2; printf("buf len:%d\n",videoInputBuffer.nLen); videoInputBuffer.nPts = pts; pts += 30; hdl->pContext->bUsed = 1; AwEncoderWriteYUVdata(hdl->pContext->mAwEncoder,&videoInputBuffer); while(hdl->pContext->bUsed == 1) { usleep(1000*2); } csi_buf_enqueue(&csiV4l2Buf); number++; } videoEos = 1; csi_stream_off(csi_get_fd()); csi_uninit(); return NULL; } unsigned int csi_thread_init(csi_handle *hdl) { int ret = 0; printf("Entering %s init ...\n", __func__); ret = csi_init(); if(ret == 0){ printf("csi init ok!\n"); }else if(ret == -1){ printf("csi not exit!\n"); ret = -1; goto errHdl; }else{ printf("csi init fail!\n"); ret = -2; goto errHdl; } ret = pthread_create(&hdl->csi_thread_id, NULL, csi_mgr_thread_function, hdl); if(ret != 0) { printf("create csi mgr pthread failed!\n"); ret = -5; goto errHdl; } errHdl: return ret; } #if 1 void disp_mgr_init() { int fdDisp; disp_init(); fdDisp = disp_get_cur_fd(); if(fdDisp < 1) { printf("open disp failed"); return; } disp_lcd_on(fdDisp); disp_open_backlight(); } void release_de(int signb) { disp_uninit(); exit(1); } #endif //* the main method. int main(int argc, char *argv[]) { DemoRecoderContext demoRecoder; EncDataCallBackOps mEncDataCallBackOps; CdxMuxerPacketT *mPacket = NULL; csi_handle *hdl = (csi_handle *)malloc(sizeof(csi_handle)); void *tret; hdl->run_flag = 1; #if 1 disp_mgr_init(); disp_video_chanel_open(); #endif mEncDataCallBackOps.onAudioDataEnc = onAudioDataEnc; mEncDataCallBackOps.onVideoDataEnc = onVideoDataEnc; printf("\n"); printf("************************************************************************\n"); printf("* This program implements a simple recoder.\n"); printf("* Inplemented by Allwinner ALD-AL3 department.\n"); printf("***********************************************************************\n"); #if 1 signal(SIGINT, release_de); #endif //* create a demoRecoder. memset(&demoRecoder, 0, sizeof(DemoRecoderContext)); demoRecoder.pool = AwPoolCreate(NULL); demoRecoder.dataQueue = CdxQueueCreate(demoRecoder.pool); #if SAVE_VIDEO_FRAME demoRecoder.fpSaveVideoFrame = fopen("/mnt/UDISK/video.dat", "wb"); if(demoRecoder.fpSaveVideoFrame == NULL) { printf("open file /mnt/UDISK/video.dat failed, errno(%d)\n", errno); } #endif //VideoEncodeConfig videoConfig; memset(&demoRecoder.videoConfig, 0x00, sizeof(VideoEncodeConfig)); demoRecoder.videoConfig.nType = VIDEO_ENCODE_H264; demoRecoder.videoConfig.nFrameRate = 30; demoRecoder.videoConfig.nOutHeight = 480;//1080; demoRecoder.videoConfig.nOutWidth = 640;//1920; demoRecoder.videoConfig.nSrcHeight = 480;//1080; demoRecoder.videoConfig.nSrcWidth = 640;//1920; demoRecoder.videoConfig.nBitRate = 3*1000*1000; demoRecoder.videoConfig.bUsePhyBuf = 1; demoRecoder.videoConfig.nInputYuvFormat = VIDEO_PIXEL_YUV420_NV21; //AudioEncodeConfig audioConfig; demoRecoder.audioConfig.nType = AUDIO_ENCODE_AAC_TYPE; demoRecoder.audioConfig.nInChan = 2; demoRecoder.audioConfig.nInSamplerate = 44100; demoRecoder.audioConfig.nOutChan = 2; demoRecoder.audioConfig.nOutSamplerate = 44100; demoRecoder.audioConfig.nSamplerBits = 16; demoRecoder.muxType = CDX_MUXER_MOV; if(demoRecoder.muxType == CDX_MUXER_TS && demoRecoder.audioConfig.nType == AUDIO_ENCODE_PCM_TYPE) { demoRecoder.audioConfig.nFrameStyle = 2; } if(demoRecoder.muxType == CDX_MUXER_TS && demoRecoder.audioConfig.nType == AUDIO_ENCODE_AAC_TYPE) { demoRecoder.audioConfig.nFrameStyle = 1; } if(demoRecoder.muxType == CDX_MUXER_AAC) { demoRecoder.audioConfig.nType = AUDIO_ENCODE_AAC_TYPE; demoRecoder.audioConfig.nFrameStyle = 0; } if(demoRecoder.muxType == CDX_MUXER_MP3) { demoRecoder.audioConfig.nType = AUDIO_ENCODE_MP3_TYPE; } pthread_mutex_init(&demoRecoder.mMutex, NULL); demoRecoder.mAwEncoder = AwEncoderCreate(&demoRecoder); if(demoRecoder.mAwEncoder == NULL) { printf("can not create AwRecorder, quit.\n"); exit(-1); } //* set callback to recoder. AwEncoderSetNotifyCallback(demoRecoder.mAwEncoder,NotifyCallbackForAwEncorder,&(demoRecoder)); if(demoRecoder.muxType == CDX_MUXER_AAC || demoRecoder.muxType == CDX_MUXER_MP3) { AwEncoderInit(demoRecoder.mAwEncoder, NULL, &demoRecoder.audioConfig,&mEncDataCallBackOps); videoEos = 1; } else { AwEncoderInit(demoRecoder.mAwEncoder, &demoRecoder.videoConfig, &demoRecoder.audioConfig,&mEncDataCallBackOps); } switch(demoRecoder.muxType) { case CDX_MUXER_AAC: sprintf(demoRecoder.pUrl, "/mnt/UDISK/save.aac"); break; case CDX_MUXER_MP3: sprintf(demoRecoder.pUrl, "/mnt/UDISK/save.mp3"); break; case CDX_MUXER_TS: sprintf(demoRecoder.pUrl, "/mnt/UDISK/save.ts"); break; default: sprintf(demoRecoder.pUrl, "/mnt/UDISK/save.mp4"); break; } AwEncoderStart(demoRecoder.mAwEncoder); AwEncoderGetExtradata(demoRecoder.mAwEncoder,&demoRecoder.extractDataBuff, &demoRecoder.extractDataLength); #if SAVE_VIDEO_FRAME if(demoRecoder.fpSaveVideoFrame) { fwrite(demoRecoder.extractDataBuff, 1, demoRecoder.extractDataLength, demoRecoder.fpSaveVideoFrame); } #endif #if AUDIO_INPUT pthread_create(&demoRecoder.audioDataThreadId, NULL, AudioInputThread, &demoRecoder); #endif #if VIDEO_INPUT hdl->pContext = &demoRecoder; csi_thread_init(hdl); #endif pthread_create(&demoRecoder.muxerThreadId, NULL, MuxerThread, &demoRecoder); while (demoRecoder.exitFlag == 0) { logd("wait MuxerThread finish!"); usleep(1000*1000); } if(demoRecoder.muxerThreadId) pthread_join(demoRecoder.muxerThreadId, NULL); #if AUDIO_INPUT if(demoRecoder.audioDataThreadId) pthread_join(demoRecoder.audioDataThreadId, NULL); #endif printf("destroy AwRecorder.\n"); while (!CdxQueueEmpty(demoRecoder.dataQueue)) { logd("free a packet"); mPacket = CdxQueuePop(demoRecoder.dataQueue); free(mPacket->buf); free(mPacket); } CdxQueueDestroy(demoRecoder.dataQueue); AwPoolDestroy(demoRecoder.pool); if(demoRecoder.mAwEncoder != NULL) { AwEncoderStop(demoRecoder.mAwEncoder); AwEncoderDestory(demoRecoder.mAwEncoder); demoRecoder.mAwEncoder = NULL; } pthread_mutex_destroy(&demoRecoder.mMutex); #if SAVE_VIDEO_FRAME if(demoRecoder.fpSaveVideoFrame) fclose(demoRecoder.fpSaveVideoFrame); #endif #if AUDIO_INPUT if (inputPCM) { fclose(inputPCM); inputPCM = NULL; } #endif hdl->run_flag = 0; pthread_join(hdl->csi_thread_id,&tret); free(hdl); #if 1 disp_video_chanel_close(); disp_uninit(); #endif printf("\n"); printf("**************************************************************\n"); printf("* Quit the program, goodbye!\n"); printf("**************************************************************\n"); printf("\n"); return 0; }