diff options
Diffstat (limited to 'liboutput/encode.c')
-rw-r--r-- | liboutput/encode.c | 432 |
1 files changed, 183 insertions, 249 deletions
diff --git a/liboutput/encode.c b/liboutput/encode.c index 351e30f..cfdf72a 100644 --- a/liboutput/encode.c +++ b/liboutput/encode.c @@ -26,175 +26,54 @@ #include <string.h> #include <stdlib.h> -#ifndef HAVE_FFMPEG_STATIC -#include <dlfcn.h> -#endif - #include "encode.h" #include <vdr/device.h> #include <vdr/tools.h> -typedef void (*f_avcodec_init)(void); -typedef void (*f_avcodec_register_all)(void); -typedef AVCodec *(*f_avcodec_find_encoder)(enum CodecID id); -typedef AVCodecContext *(*f_avcodec_alloc_context)(void); -typedef AVFrame *(*f_avcodec_alloc_frame)(void); -typedef int (*f_avcodec_open)(AVCodecContext *avctx, AVCodec *codec); -typedef int (*f_avcodec_close)(AVCodecContext *avctx); -typedef int (*f_avcodec_encode_video)(AVCodecContext *avctx, uint8_t *buf, int buf_size,const AVFrame *pict); -typedef int (*f_avpicture_fill)(AVPicture *picture, uint8_t *ptr, int pix_fmt, int width, int height); - -#if FFMPEG_VERSION_INT <= 0x000408 -typedef int (*f_img_convert)(AVPicture *dst, int dst_pix_fmt,AVPicture *src, int pix_fmt,int width, int height); -#elif FFMPEG_VERSION_INT >= 0x000409 -typedef int (*f_img_convert)(AVPicture *dst, int dst_pix_fmt,const AVPicture *src, int pix_fmt,int width, int height); -#else - #error "there is a unknow ffmpeg version or FFMPEG_VERSION_INT isnt defined" -#endif - -#ifdef HAVE_FFMPEG_STATIC - -static f_avcodec_init fn_avcodec_init=avcodec_init; -static f_avcodec_register_all fn_avcodec_register_all=avcodec_register_all; -static f_avcodec_find_encoder fn_avcodec_find_encoder=avcodec_find_encoder; -static f_avcodec_alloc_context fn_avcodec_alloc_context=avcodec_alloc_context; -static f_avcodec_alloc_frame fn_avcodec_alloc_frame=avcodec_alloc_frame; -static f_avcodec_open fn_avcodec_open=avcodec_open; -static f_avcodec_close fn_avcodec_close=avcodec_close; -static f_avcodec_encode_video fn_avcodec_encode_video=&avcodec_encode_video; -static f_avpicture_fill fn_avpicture_fill=avpicture_fill; -static f_img_convert fn_img_convert=img_convert; - -#else - -static f_avcodec_init fn_avcodec_init; -static f_avcodec_register_all fn_avcodec_register_all; -static f_avcodec_find_encoder fn_avcodec_find_encoder; -static f_avcodec_alloc_context fn_avcodec_alloc_context; -static f_avcodec_alloc_frame fn_avcodec_alloc_frame; -static f_avcodec_open fn_avcodec_open; -static f_avcodec_close fn_avcodec_close; -static f_avcodec_encode_video fn_avcodec_encode_video; -static f_avpicture_fill fn_avpicture_fill; -static f_img_convert fn_img_convert; - - -#define DLSYM(cast,sym,func) \ - sym=(cast)dlsym(m_hLibAvcodec, func); \ - bSuccess &= (sym != NULL); \ - if(sym == NULL) \ - esyslog("imageplugin: Link to function %s failed", func); - -void *cEncode::m_hLibAvcodec = NULL; - - -/******************************************************************************* - -*/ -bool cEncode::InitLibAVCodec(void){ - - bool bSuccess=false; - // Let ld.so search the libary - const char* szLibary = "libavcodec.so"; - m_hLibAvcodec=dlopen(szLibary, RTLD_LAZY); - if(!m_hLibAvcodec) - { - esyslog("imageplugin: Loading %s failed : %s", szLibary,dlerror()); - } - /* map the lib's syms to our wrapper */ - else { - - bSuccess=true; - DLSYM(f_avcodec_init,fn_avcodec_init,"avcodec_init"); - DLSYM(f_avcodec_init,fn_avcodec_register_all,"avcodec_register_all"); - DLSYM(f_avcodec_find_encoder,fn_avcodec_find_encoder,"avcodec_find_encoder"); - DLSYM(f_avcodec_alloc_context,fn_avcodec_alloc_context,"avcodec_alloc_context"); - DLSYM(f_avcodec_alloc_frame,fn_avcodec_alloc_frame,"avcodec_alloc_frame"); - DLSYM(f_avcodec_open,fn_avcodec_open,"avcodec_open"); - DLSYM(f_avcodec_close,fn_avcodec_close,"avcodec_close"); - DLSYM(f_avcodec_encode_video,fn_avcodec_encode_video,"avcodec_encode_video"); - DLSYM(f_avpicture_fill,fn_avpicture_fill,"avpicture_fill"); - DLSYM(f_img_convert,fn_img_convert,"img_convert"); - } - - return(bSuccess); -} - -/******************************************************************************* - -*/ -void cEncode::CloseLibAVCodec(void){ - - if (m_hLibAvcodec) - dlclose(m_hLibAvcodec); - m_hLibAvcodec = NULL; -} -#endif - /******************************************************************************* */ cEncode::cEncode() : m_pavCodec(NULL) -, m_pImageFilled(NULL) -, m_pImageYUV(NULL) -, m_nFrames(4) -, m_nData(0) -, m_pMPEG(NULL) -, m_pImageRGB(NULL) +, m_pImageFilled(NULL) +, m_pImageYUV(NULL) +, m_nNumberOfFramesToEncode(4) +, m_pMPEG(NULL) +, m_pImageRGB(NULL) { - m_bLoaded = false; - m_pFramesSize = new unsigned int[m_nFrames]; + m_bUsePAL = (cDevice::PrimaryDevice()->GetVideoSystem() == vsPAL); + m_nWidth = 720; + m_nHeight = m_bUsePAL ? 576 : 480; - m_bUsePAL = (cDevice::PrimaryDevice()->GetVideoSystem() == vsPAL); - m_nWidth = 720; - m_nHeight = m_bUsePAL ? 576 : 480; + m_pFrameSizes = new unsigned int[m_nNumberOfFramesToEncode]; + // Just a wild guess: 3 x output image size should be enough for the MPEG + m_nMaxMPEGSize = m_nWidth * m_nHeight * 3; - m_nMaxMPEGSize = m_nWidth*m_nHeight * 3; //It see for me 500kb are enough, therefore should the double really enough memory + AllocateBuffers(); - /* Allocate bufers */ - if(NULL == (m_pMPEG=(uint8_t *)malloc(m_nMaxMPEGSize*3)) //~1200kb - || NULL == (m_pImageRGB=(uint8_t *)malloc(m_nWidth*m_nHeight*3)) //~1200kb - || NULL == (m_pImageFilled=(uint8_t *)malloc(m_nWidth*m_nHeight*3)) //~1200kb - || NULL == (m_pImageYUV=(uint8_t *)malloc(m_nWidth*m_nHeight*3/2))) //~600kb - { - esyslog("imageplugin: Failed to alloc memory for bitmaps."); - return; - } - - m_pavCodec = fn_avcodec_find_encoder(CODEC_ID_MPEG2VIDEO); - if (!m_pavCodec) { - esyslog("imageplugin: Failed to find CODEC_ID_MPEG2VIDEO."); - return; - } - m_bLoaded = true; + m_pavCodec = avcodec_find_encoder(CODEC_ID_MPEG2VIDEO); + if (!m_pavCodec) { + esyslog("imageplugin: Failed to find CODEC_ID_MPEG2VIDEO."); + return; + } } bool cEncode::Register() { - #ifndef HAVE_FFMPEG_STATIC - if(!InitLibAVCodec()) { - esyslog("imageplugin: Failed to InitLibAVCodec."); - return false; - } - #endif - fn_avcodec_init(); - fn_avcodec_register_all(); - return true; + avcodec_init(); + avcodec_register_all(); + return true; } void cEncode::UnRegister() { -#ifndef HAVE_FFMPEG_STATIC - CloseLibAVCodec(); -#endif } void cEncode::ClearRGBMem() { - if(m_pImageRGB && m_bLoaded) - memset(m_pImageRGB,0,m_nWidth*m_nHeight*3 ); + if(m_pImageRGB) + memset(m_pImageRGB, 0, m_nWidth * m_nHeight * 3 ); } /******************************************************************************* @@ -202,18 +81,8 @@ void cEncode::ClearRGBMem() */ cEncode::~cEncode(void) { - if(m_pImageYUV) - free(m_pImageYUV); - - if(m_pImageFilled) - free(m_pImageFilled); - - if(m_pImageRGB) - free(m_pImageRGB); - - if(m_pMPEG) - free(m_pMPEG); - delete m_pFramesSize; + ReleaseBuffers(); + delete m_pFrameSizes; } /******************************************************************************* @@ -221,109 +90,174 @@ cEncode::~cEncode(void) */ bool cEncode::Encode() { - m_nData = 0; - - if (!m_bLoaded){ - dsyslog("imageplugin: libavcodec is'nt successful loaded."); - return false; - } - - bool bSuccess = false; - unsigned int i; - AVCodecContext *pAVCC = NULL; - AVFrame *pAVF = NULL; - int nSize=m_nWidth*m_nHeight; - - if(NULL == (pAVCC = fn_avcodec_alloc_context()) - || NULL == (pAVF = fn_avcodec_alloc_frame())) { - esyslog("imageplugin: Failed to alloc memory for AVCODEC and CONTEXT."); - goto encexit; - } + bool bSuccess = false; - pAVCC->bit_rate=1000000; //1000kbit - pAVCC->width = m_nWidth; - pAVCC->height = m_nHeight; - pAVCC->frame_rate=GetFrameRate(); - pAVCC->frame_rate_base=1; - pAVCC->gop_size=m_nFrames-1; //IPB //1 => Encode only I-Frames, bigger - pAVCC->max_b_frames=1; - pAVCC->flags |= CODEC_FLAG_QSCALE; - - if (fn_avcodec_open(pAVCC, m_pavCodec)<0) { - esyslog("imageplugin: Coldn't open Codec."); - goto encexit; - } - - pAVF->data[0]=m_pImageYUV; - pAVF->data[1]=m_pImageYUV+nSize; - pAVF->data[2]=pAVF->data[1]+nSize/4; - pAVF->linesize[0]=m_nWidth; - pAVF->linesize[1]=pAVF->linesize[2]=m_nWidth/2; - pAVF->quality = 1; - - // Convert RGB to YUV - if(!fn_avpicture_fill((AVPicture*)m_pImageFilled, m_pImageRGB, PIX_FMT_RGB24, - m_nWidth, m_nHeight)) { - esyslog("imageplugin: failed avpicture_fill"); - goto encexit; - } - - if(fn_img_convert((AVPicture*)pAVF->data, PIX_FMT_YUV420P, - (AVPicture*)m_pImageFilled, PIX_FMT_RGB24, m_nWidth, m_nHeight)) { - esyslog("imageplugin: failed convert RGB to YUV"); - goto encexit; - } - - // Encode Frames which defined with m_nFrames - for(i=0; i<m_nFrames && m_nData < m_nMaxMPEGSize; ++i) - { - int nFrameSize = fn_avcodec_encode_video(pAVCC, m_pMPEG + m_nData, m_nMaxMPEGSize - m_nData, pAVF); - if(nFrameSize < 0) + AVCodecContext *pAVCC = NULL; + AVFrame *pAVF = NULL; + + pAVCC = avcodec_alloc_context(); + if (! pAVCC) + { + esyslog("imageplugin: Failed to alloc memory for AVCodecContext."); + } + else + { + pAVF = avcodec_alloc_frame(); + if (! pAVF) + { + esyslog("imageplugin: Failed to alloc memory for AVFrame."); + } + else + { + SetupEncodingParameters(pAVCC); + + if (avcodec_open(pAVCC, m_pavCodec) < 0) + { + esyslog("imageplugin: Couldn't open Codec."); + } + else + { + if (ConvertImageToFrame(pAVF)) + { + bSuccess = EncodeFrames(pAVCC, pAVF); + } + avcodec_close(pAVCC); + } + av_free(pAVF); + } + av_free(pAVCC); + } + return bSuccess; +} + +void cEncode::SetupEncodingParameters(AVCodecContext *context) +{ + context->bit_rate=1000000; //1000kbit + context->width = m_nWidth; + context->height = m_nHeight; + + #if LIBAVCODEC_BUILD >= 4754 + context->time_base=(AVRational){1, GetFrameRate()}; + #else + context->frame_rate=GetFrameRate(); + context->frame_rate_base=1; + #endif + + //IPB //1 => Encode only I-Frames, bigger + context->gop_size=m_nNumberOfFramesToEncode-1; + + context->max_b_frames=1; + context->flags |= CODEC_FLAG_QSCALE; + context->pix_fmt = PIX_FMT_YUV420P; +} + +bool cEncode::ConvertImageToFrame(AVFrame *frame) +{ + int nSize = m_nWidth*m_nHeight; + + frame->data[0]=m_pImageYUV; + frame->data[1]=m_pImageYUV+nSize; + frame->data[2]=frame->data[1]+nSize/4; + frame->linesize[0]=m_nWidth; + frame->linesize[1]=frame->linesize[2]=m_nWidth/2; + frame->quality = 1; + + // Convert RGB to YUV + if(!avpicture_fill((AVPicture*)m_pImageFilled, + m_pImageRGB, + PIX_FMT_RGB24, m_nWidth, m_nHeight)) + { + esyslog("imageplugin: failed avpicture_fill"); + return false; + } + else { - esyslog("imageplugin: Failed add %d frame, insufficient memory.",i); - bSuccess = false; - break; + if(img_convert((AVPicture*)frame->data, PIX_FMT_YUV420P, + (AVPicture*)m_pImageFilled, PIX_FMT_RGB24, + m_nWidth, m_nHeight)) + { + esyslog("imageplugin: failed convert RGB to YUV"); + return false; + } } - bSuccess = true; - m_nData += nFrameSize; - *(m_pFramesSize + i) = nFrameSize; - } + return true; +} - if(bSuccess && m_nData < m_nMaxMPEGSize) // if sufficient place present - { - //add four bytes end sequnce - memcpy(m_pMPEG + m_nData,"\0x00\0x00\0x01\0xb7",4); - m_nData += 4; - *(m_pFramesSize + i - 1) += 4; - - } - else bSuccess = false; +bool cEncode::EncodeFrames(AVCodecContext *context, AVFrame *frame) +{ + unsigned int i; + + m_nMPEGSize = 0; + + // Encode m_nNumberOfFramesToEncode number of frames + for(i=0; (i < m_nNumberOfFramesToEncode) && (m_nMPEGSize < m_nMaxMPEGSize); + ++i) + { + int nFrameSize = avcodec_encode_video(context, m_pMPEG + m_nMPEGSize, + m_nMaxMPEGSize - m_nMPEGSize, frame); + if(nFrameSize < 0) + { + esyslog("imageplugin: Failed to add frame %d, insufficient memory.", + i); + return false; + } + m_nMPEGSize += nFrameSize; + *(m_pFrameSizes + i) = nFrameSize; + } - #ifdef TESTCODE - if(bSuccess) + // Add four bytes MPEG end sequence + if ((m_nMaxMPEGSize - m_nMPEGSize) >= 4) + { + memcpy(m_pMPEG + m_nMPEGSize,"\0x00\0x00\0x01\0xb7",4); + m_nMPEGSize += 4; + *(m_pFrameSizes + i - 1) += 4; + } + else + { + esyslog("imageplugin: Failed to add MPEG end sequence, " + "insufficient memory."); + return false; + } + +#ifdef TESTCODE + // Dump generate date to file Save("/tmp/imagetest.mpg"); - #endif +#endif -encexit: - if (pAVCC) - { - fn_avcodec_close(pAVCC); - free(pAVCC); - } - - if(pAVF) - { - free(pAVF); - } - return bSuccess; + return true; +} + +void cEncode::AllocateBuffers() +{ + if(NULL == (m_pMPEG=(uint8_t *)malloc(m_nMaxMPEGSize*3)) //~1200kb + || NULL == (m_pImageRGB=(uint8_t *)malloc(m_nWidth*m_nHeight*3)) //~1200kb + || NULL == (m_pImageFilled=(uint8_t *)malloc(m_nWidth*m_nHeight*3)) //~1200kb + || NULL == (m_pImageYUV=(uint8_t *)malloc(m_nWidth*m_nHeight*3/2))) //~600kb + { + esyslog("imageplugin: Failed to alloc memory for bitmaps."); + return; + } } +void cEncode::ReleaseBuffers() +{ + if(m_pImageYUV) + free(m_pImageYUV); + + if(m_pImageFilled) + free(m_pImageFilled); + + if(m_pImageRGB) + free(m_pImageRGB); + if(m_pMPEG) + free(m_pMPEG); +} #ifdef TESTCODE /******************************************************************************* - + Load a PNM Bitmap with 24bit 720x576 direct into encoder memory */ bool cEncode::Load(const char* szFileName) { @@ -340,7 +274,7 @@ bool cEncode::Load(const char* szFileName) } /******************************************************************************* - + Save encoder memory as file for diagnostics */ bool cEncode::Save(const char* szFileName) const { @@ -355,15 +289,15 @@ bool cEncode::Save(const char* szFileName) const } return false; } -#endif - -#if 0 -int main(){ +/* +// Standalone test of encoder + int main(){ cEncode e; e.Load("test.pnm"); e.Encode(); e.Save("test.mpg"); return 0; } +*/ #endif |