diff options
Diffstat (limited to 'command/decoder.cpp')
-rw-r--r-- | command/decoder.cpp | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/command/decoder.cpp b/command/decoder.cpp new file mode 100644 index 0000000..6b01404 --- /dev/null +++ b/command/decoder.cpp @@ -0,0 +1,478 @@ +/* + * decoder.cpp: A program for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "decoder.h" + +cMarkAdDecoder::cMarkAdDecoder(bool useH264, bool useMP2, bool hasAC3) +{ + avcodec_init(); + avcodec_register_all(); + + last_qscale_table=NULL; + + cpu_set_t cpumask; + uint len = sizeof(cpumask); + int cpucount; + if (sched_getaffinity(0,len,&cpumask)<0) + { + cpucount=1; + } + else + { + cpucount=CPU_COUNT(&cpumask); + } + + int ver = avcodec_version(); + char libver[256]; + snprintf(libver,sizeof(libver),"%i.%i.%i",ver >> 16 & 0xFF,ver >> 8 & 0xFF,ver & 0xFF); + isyslog("using libavcodec.so.%s with %i threads",libver,cpucount); + + if (ver!=LIBAVCODEC_VERSION_INT) + { + esyslog("libavcodec header version %s",AV_STRINGIFY(LIBAVCODEC_VERSION)); + esyslog("header and library mismatch, decoding disabled"); + video_context=NULL; + ac3_context=NULL; + mp2_context=NULL; + audiobuf=NULL; + return; + } + + if (((ver >> 16)<52) && (useH264)) + { + esyslog("dont report bugs about H264, use libavcodec >= 52 instead!"); + } + + if (useMP2) + { + CodecID mp2_codecid=CODEC_ID_MP2; + AVCodec *mp2_codec= avcodec_find_decoder(mp2_codecid); + if (mp2_codec) + { + mp2_context = avcodec_alloc_context(); + if (mp2_context) + { + mp2_context->thread_count=cpucount; + mp2_context->codec_id = mp2_codecid; + mp2_context->codec_type = CODEC_TYPE_AUDIO; + if (avcodec_open(mp2_context, mp2_codec) < 0) + { + esyslog("could not open codec for MP2"); + av_free(mp2_context); + mp2_context=NULL; + } + } + else + { + esyslog("could not allocate mp2 context"); + } + } + else + { + esyslog("codec for MP2 not found"); + mp2_context=NULL; + } + } + else + { + mp2_context=NULL; + } + + if (hasAC3) + { + CodecID ac3_codecid=CODEC_ID_AC3; + AVCodec *ac3_codec= avcodec_find_decoder(ac3_codecid); + if (ac3_codec) + { + ac3_context = avcodec_alloc_context(); + if (ac3_context) + { + ac3_context->thread_count=cpucount; + ac3_context->codec_id = ac3_codecid; + ac3_context->codec_type = CODEC_TYPE_AUDIO; + if (avcodec_open(ac3_context, ac3_codec) < 0) + { + esyslog("could not open codec for AC3"); + av_free(ac3_context); + ac3_context=NULL; + } + } + else + { + esyslog("could not allocate ac3 context"); + } + } + else + { + esyslog("codec for AC3 not found"); + ac3_context=NULL; + } + } + else + { + ac3_context=NULL; + } + + AVCodec *video_codec=NULL; + CodecID video_codecid; + + if (useH264) + { + video_codecid=CODEC_ID_H264; + } + else + { + video_codecid=CODEC_ID_MPEG2VIDEO_XVMC; + } + + video_codec = avcodec_find_decoder(video_codecid); + if ((!video_codec) && (video_codecid==CODEC_ID_MPEG2VIDEO_XVMC)) + { + // fallback to MPEG2VIDEO + video_codecid=CODEC_ID_MPEG2VIDEO; + video_codec=avcodec_find_decoder(video_codecid); + } + + if (video_codec) + { + video_context = avcodec_alloc_context(); + if (video_context) + { + video_context->thread_count=cpucount; + if (video_codec->capabilities & CODEC_CAP_TRUNCATED) + video_context->flags|=CODEC_FLAG_TRUNCATED; // we do not send complete frames + + video_context->flags|=CODEC_FLAG_GRAY; // only decode grayscale + video_context->flags2|=CODEC_FLAG2_FAST; // really? + + video_context->skip_idct=AVDISCARD_ALL; + + if (video_codecid==CODEC_ID_H264) + { + video_context->flags2|=CODEC_FLAG2_CHUNKS; // needed for H264! + video_context->skip_loop_filter=AVDISCARD_ALL; // skip deblocking + av_log_set_level(AV_LOG_FATAL); // H264 decoder is very chatty + } + else + { + video_context->skip_frame=AVDISCARD_NONKEY; // just I-frames + } + + video_context->codec_id = video_codecid; + video_context->codec_type = CODEC_TYPE_VIDEO; + int ret=avcodec_open(video_context, video_codec); + if ((ret < 0) && (video_codecid==CODEC_ID_MPEG2VIDEO_XVMC)) + { + // fallback to MPEG2VIDEO + video_codecid=CODEC_ID_MPEG2VIDEO; + video_codec=avcodec_find_decoder(video_codecid); + if (video_codec) + { + video_context->codec_type=CODEC_TYPE_UNKNOWN; + video_context->codec_id=CODEC_ID_NONE; + video_context->codec_tag=0; + memset(video_context->codec_name,0,sizeof(video_context->codec_name)); + ret=avcodec_open(video_context, video_codec); + } + else + { + ret=-1; + } + } + if (ret < 0) + { + switch (video_codecid) + { + case CODEC_ID_H264: + esyslog("could not open codec for H264"); + break; + case CODEC_ID_MPEG2VIDEO_XVMC: + esyslog("could not open codec MPEG2 (XVMC)"); + break; + case CODEC_ID_MPEG2VIDEO: + esyslog("could not open codec MPEG2"); + break; + default: + esyslog("could not open video codec"); + break; + } + av_free(video_context); + video_context=NULL; + } + else + { + isyslog("using codec %s",video_codec->long_name); + +#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(22<<8)+2) + if (video_context->hwaccel) + { + isyslog("using hwaccel %s",video_context->hwaccel->name); + } +#endif + + video_frame = avcodec_alloc_frame(); + if (!video_frame) + { + esyslog("could not allocate frame"); + avcodec_close(video_context); + av_free(video_context); + video_context=NULL; + } + } + } + else + { + esyslog("could not allocate video context"); + } + } + else + { + switch (video_codecid) + { + case CODEC_ID_H264: + esyslog("codec for H264 not found"); + break; + case CODEC_ID_MPEG2VIDEO_XVMC: + esyslog("codec for MPEG2 (XVMC) not found"); + break; + case CODEC_ID_MPEG2VIDEO: + esyslog("codec for MPEG2 not found"); + break; + default: + esyslog("video codec not found"); + break; + } + video_context=NULL; + } + + if ((ac3_context) || (mp2_context)) + { + audiobuf=(int16_t*) malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); + } + else + { + audiobuf=NULL; + } + +} + +cMarkAdDecoder::~cMarkAdDecoder() +{ + if (video_context) + { + avcodec_close(video_context); + av_free(video_context); + av_free(video_frame); + } + + if (ac3_context) + { + avcodec_close(ac3_context); + av_free(ac3_context); + } + + if (mp2_context) + { + avcodec_close(mp2_context); + av_free(mp2_context); + } + if (audiobuf) free(audiobuf); +} + +bool cMarkAdDecoder::DecodeMP2(MarkAdContext *maContext, uchar *espkt, int eslen) +{ + if (!mp2_context) return false; + maContext->Audio.Data.Valid=false; + AVPacket avpkt; +#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(25<<8)+0) + av_init_packet(&avpkt); +#else + memset(&avpkt,0,sizeof(avpkt)); + avpkt.pts = avpkt.dts = AV_NOPTS_VALUE; + avpkt.pos = -1; +#endif + avpkt.data=espkt; + avpkt.size=eslen; + + audiobufsize=AVCODEC_MAX_AUDIO_FRAME_SIZE; + int ret=false; + int16_t Taudiobuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; + while (avpkt.size>0) + { +#if LIBAVCODEC_VERSION_INT < ((52<<16)+(25<<8)+0) + int len=avcodec_decode_audio2(mp2_context,Taudiobuf,&audiobufsize, + avpkt.data,avpkt.size); +#else + int len=avcodec_decode_audio3(mp2_context,Taudiobuf,&audiobufsize,&avpkt); +#endif + if (len<0) + { + esyslog("error decoding mp2"); + break; + } + if (audiobufsize>0) + { + memcpy(audiobuf,Taudiobuf,audiobufsize); + SetAudioInfos(maContext,mp2_context); + ret=true; + avpkt.size-=len; + avpkt.data+=len; + } + } + return ret; +} + +bool cMarkAdDecoder::SetAudioInfos(MarkAdContext *maContext, AVCodecContext *Audio_Context) +{ + if ((!maContext) || (!Audio_Context)) return false; + + maContext->Audio.Info.SampleRate = Audio_Context->sample_rate; + maContext->Audio.Info.Channels = Audio_Context->channels; + maContext->Audio.Data.SampleBuf=audiobuf; + maContext->Audio.Data.SampleBufLen=audiobufsize; + maContext->Audio.Data.Valid=true; + return true; +} + +bool cMarkAdDecoder::DecodeAC3(MarkAdContext *maContext, uchar *espkt, int eslen) +{ + if (!ac3_context) return false; + maContext->Audio.Data.Valid=false; + AVPacket avpkt; +#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(25<<8)+0) + av_init_packet(&avpkt); +#else + memset(&avpkt,0,sizeof(avpkt)); + avpkt.pts = avpkt.dts = AV_NOPTS_VALUE; + avpkt.pos = -1; +#endif + avpkt.data=espkt; + avpkt.size=eslen; + + int ret=false; + int16_t Taudiobuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; + while (avpkt.size>0) + { + audiobufsize=AVCODEC_MAX_AUDIO_FRAME_SIZE; +#if LIBAVCODEC_VERSION_INT < ((52<<16)+(25<<8)+0) + int len=avcodec_decode_audio2(ac3_context,Taudiobuf,&audiobufsize, + avpkt.data,avpkt.size); +#else + int len=avcodec_decode_audio3(ac3_context,Taudiobuf,&audiobufsize,&avpkt); +#endif + if (len<0) + { + esyslog("error decoding ac3"); + break; + } + if (audiobufsize>0) + { + memcpy(audiobuf,Taudiobuf,audiobufsize); + SetAudioInfos(maContext,ac3_context); + ret=true; + avpkt.size-=len; + avpkt.data+=len; + } + } + return ret; +} + +void cMarkAdDecoder::PAR2DAR(AVRational a, AVRational *erg) +{ + av_reduce(&erg->num,&erg->den,video_context->width*a.num, + video_context->height*a.den,1024*1024); +} + +bool cMarkAdDecoder::SetVideoInfos(MarkAdContext *maContext,AVCodecContext *Video_Context, AVFrame *Video_Frame) +{ + if ((!maContext) || (!Video_Context) || (!Video_Frame)) return false; + for (int i=0; i<4; i++) + { + if (Video_Frame->data[i]) + { + maContext->Video.Data.Plane[i]=Video_Frame->data[i]; + maContext->Video.Data.PlaneLinesize[i]=Video_Frame->linesize[i]; + maContext->Video.Data.Valid=true; + } + } + maContext->Video.Info.Height=Video_Context->height; + maContext->Video.Info.Width=Video_Context->width; + + AVRational dar; + PAR2DAR(Video_Context->sample_aspect_ratio,&dar); + + maContext->Video.Info.AspectRatio.Num=dar.num; + maContext->Video.Info.AspectRatio.Den=dar.den; + + return true; +} + +bool cMarkAdDecoder::DecodeVideo(MarkAdContext *maContext,uchar *pkt, int plen) +{ + if (!video_context) return false; + maContext->Video.Data.Valid=false; + + if ((video_context->codec_id==CODEC_ID_H264) && (!video_context->skip_frame)) + { + if (maContext->Video.Info.Pict_Type) + { + if (maContext->Video.Info.Interlaced) + { + video_context->skip_frame=AVDISCARD_BIDIR; // just P/I-frames + } + else + { + video_context->skip_frame=AVDISCARD_NONKEY; // just I-frames + } + } + } + + AVPacket avpkt; +#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(25<<8)+0) + av_init_packet(&avpkt); +#else + memset(&avpkt,0,sizeof(avpkt)); + avpkt.pts = avpkt.dts = AV_NOPTS_VALUE; + avpkt.pos = -1; +#endif + avpkt.data=pkt; + avpkt.size=plen; + + // decode video + int video_frame_ready=0; + int len,ret=false; + + while (avpkt.size>0) + { +#if LIBAVCODEC_VERSION_INT < ((52<<16)+(25<<8)+0) + len=avcodec_decode_video(video_context,video_frame,&video_frame_ready, + avpkt.data,avpkt.size); +#else + len=avcodec_decode_video2(video_context,video_frame,&video_frame_ready, + &avpkt); +#endif + if (len<0) + { + esyslog("error decoding video"); + break; + } + else + { + avpkt.size-=len; + avpkt.data+=len; + } + if (video_frame_ready) + { + if (last_qscale_table!=video_frame->qscale_table) + { + if (SetVideoInfos(maContext,video_context,video_frame)) ret=true; + last_qscale_table=video_frame->qscale_table; + } + } + } + return ret; +} |