#include "fillplugins.h" #include "xvid.h" #include "videodecoder.h" #include "videoencoder.h" #include "configfile.h" #include "plugin.h" #include "avm_output.h" #include #include #include AVM_BEGIN_NAMESPACE; #define MAX_QUALITY 6 static const char* strDebug = "XviD plugin"; PLUGIN_TEMP(xvid) class XVID_VideoDecoder: public IVideoDecoder //, public IRtConfig { void* m_pHandle; int m_iLastPPMode; int m_iMaxAuto; int m_iLastBrightness; int m_iLastContrast; int m_iLastSaturation; public: XVID_VideoDecoder(const CodecInfo& info, const BITMAPINFOHEADER& bh, int flip) :IVideoDecoder(info, bh), m_pHandle(0), m_iLastPPMode(0), m_iLastBrightness(0), m_iLastContrast(0), m_iLastSaturation(0) { m_Dest.SetSpace(fccYV12); if (flip) m_Dest.biHeight *= -1; AVM_WRITE(strDebug, "XviD linux decoder\n"); } virtual ~XVID_VideoDecoder() { Stop(); } virtual CAPS GetCapabilities() const { //return CAPS(CAP_YV12|CAP_YUY2|CAP_UYVY|CAP_I420); return CAPS(CAP_YV12); //return CAPS(CAP_NONE); } //virtual IRtConfig* GetRtConfig() { return (IRtConfig*) this; } virtual int Start() { int xerr; if (m_pHandle) return -1; XVID_INIT_PARAM init; XVID_DEC_PARAM param; init.cpu_flags = 0; //init.cpu_flags = XVID_CPU_FORCE;// | XVID_CPU_MMX; xvid_init(NULL, 0, &init, NULL); param.width = m_Dest.biWidth; param.height = labs(m_Dest.biHeight); xerr = xvid_decore(NULL, XVID_DEC_CREATE, ¶m, NULL); if (xerr != 0) { AVM_WRITE(strDebug, "XviD start failed!\n"); return -1; } m_pHandle = param.handle; return 0; } virtual int Stop() { if (!m_pHandle) return -1; xvid_decore(m_pHandle, XVID_DEC_DESTROY, 0, 0); m_pHandle = 0; return 0; } virtual int DecodeFrame(CImage* pImage, const void* src, uint_t size, int is_keyframe, bool render = true, CImage** pOut = 0) { XVID_DEC_FRAME param; if (!size || !m_pHandle) return 0; //AVM_WRITE(strDebug, "csp: 0x%x bitcount: %d height: %d\n", csp, m_Dest.biBitCount, m_Dest.biHeight); param.bitstream = (void*) src; param.length = size; if (pImage) { int csp = pImage->GetFmt()->biCompression; switch (csp) { case 0: case 3: switch (m_Dest.biBitCount) { case 16: if (csp == 3) { param.colorspace = XVID_CSP_RGB565; break; } // 16 bit with csp == 0 is 15bit /* fall through */ case 15: param.colorspace = XVID_CSP_RGB555; break; case 24: param.colorspace = XVID_CSP_RGB24; break; case 32: param.colorspace = XVID_CSP_RGB32; break; default: return -1; } if (pImage->GetFmt()->biHeight > 0) param.colorspace |= XVID_CSP_VFLIP; // RGB also doesn't seems work; param.colorspace = XVID_CSP_USER; break; case fccYV12: //param.colorspace = XVID_CSP_YV12; param.colorspace = XVID_CSP_USER; break; case fccI420: param.colorspace = XVID_CSP_I420; // untested break; case fccYUY2: param.colorspace = XVID_CSP_YUY2; break; case fccYVYU: param.colorspace = XVID_CSP_YVYU; break; case fccUYVY: param.colorspace = XVID_CSP_UYVY; break; default: return -1; } //printf("XXXXXXXXXX %d %d %d\n", size, m_Dest.biWidth, param.colorspace); param.stride = pImage->Width(); //+6; param.image = pImage->Data(); pImage->SetQuality((float) m_iLastPPMode / MAX_QUALITY); } else param.colorspace = XVID_CSP_NULL; if (xvid_decore(m_pHandle, XVID_DEC_DECODE, ¶m, NULL) != 0) return -1; if (param.colorspace == XVID_CSP_USER) { // internal format of XVID data - using for YV12 // for some reason I couldn't read YV12 with CSP setting struct dp { const uint8_t *y; const uint8_t *u; const uint8_t *v; int stride_y; int stride_uv; }; const struct dp* d = (const struct dp*) param.image; const uint8_t* p[3] = { d->y, d->v, d->u }; int s[3] = { d->stride_y, d->stride_uv, d->stride_uv }; BitmapInfo bi(m_Dest); bi.SetSpace(fccYV12); CImage ci(&bi, (const uint8_t**) p, s, false); pImage->Convert(&ci); } return size; } virtual int SetDestFmt(int bits = 24, fourcc_t csp = 0) { if (csp) bits = csp; switch (bits) { case 15: case 16: case 24: case 32: m_Dest.SetBits(bits); break; case fccYV12: case fccYUY2: m_Dest.SetSpace(bits); break; default: return -1; } if (m_pHandle) Restart(); return 0; } virtual int GetValue(const char* name, int& value) const { /* if (strcmp(name, strPostProcessing) == 0) value = m_iLastPPMode; else if (strcmp(name, strMaxAuto) == 0) value = m_iMaxAuto; else if (strcmp(name, strBrightness) == 0) value = m_iLastBrightness; else if (strcmp(name, strContrast) == 0) value = m_iLastContrast; else if (strcmp(name, strSaturation) == 0) value = m_iLastSaturation; else */ return -1; return 0; } virtual int SetValue(const char* name, int value) { return -1; } }; class XVID_VideoEncoder: public IVideoEncoder { BITMAPINFOHEADER m_bh; BITMAPINFOHEADER m_obh; unsigned int m_uiFrames; void* m_pHandle; int m_iQuant; bool m_bRtMode; int general; int motion; public: XVID_VideoEncoder(const CodecInfo& info, fourcc_t compressor, const BITMAPINFOHEADER& bh) :IVideoEncoder(info), m_bh(bh), m_obh(bh), m_pHandle(0), m_bRtMode(false) { m_obh.biCompression = fccDIVX; m_obh.biHeight = labs(m_obh.biHeight); AVM_WRITE(strDebug, "XviD linux encoder\n"); } virtual ~XVID_VideoEncoder() { Stop(); } virtual int EncodeFrame(const CImage* src, void* dest, int* is_keyframe, uint_t* size, int* lpckid=0) { int xerr; XVID_ENC_FRAME param; XVID_ENC_STATS result; param.bitstream = dest; switch (m_bh.biCompression) { case 0: param.colorspace = XVID_CSP_RGB24; break; case fccYUY2: param.colorspace = XVID_CSP_YUY2; break; case fccYV12: param.colorspace = XVID_CSP_YV12; break; case fccI420: param.colorspace = XVID_CSP_I420; break; } param.image = (void*) src->Data(); param.length = -1; // this is written by the routine param.quant = 0; param.intra = (!m_uiFrames++) ? 1 : -1; #if API_VERSION >= (2 << 16) param.general = general; param.motion = motion; #endif if (m_bRtMode) { param.quant=m_iQuant; // let encoder decide if frame is INTER/INTRA param.intra=-1; xerr = xvid_encore(m_pHandle, XVID_ENC_ENCODE, ¶m, &result); } else { xerr = xvid_encore(m_pHandle, XVID_ENC_ENCODE, ¶m, &result); } //printf("%d %d %d\n", param.intra, param.colorspace, param.length); // xframe.quant = QUANTI; // is quant != 0, use a fixed quant (and ignore bitrate) if (is_keyframe) *is_keyframe = param.intra ? 16 : 0; if (size) *size = param.length; return 0; } virtual int GetOutputSize() const { return m_bh.biWidth * labs(m_bh.biHeight) * 4; } virtual const BITMAPINFOHEADER& GetOutputFormat() const { return m_obh; } virtual int GetQuality() const { return 0;//RegReadInt(strXviDplugin, "quality2", 8500); } virtual int SetQuality(int quality) { AVM_WRITE(strDebug, "quality: %d\n", quality); //RegWriteInt(strXviDplugin, "quality2", quality); return 0; } virtual int Start() { int xerr; XVID_INIT_PARAM xinit; XVID_ENC_PARAM param; m_uiFrames = 0; xinit.cpu_flags = 0; xvid_init(NULL, 0, &xinit, NULL); memset(¶m, 0, sizeof(param)); param.width = m_bh.biWidth; param.height = labs(m_bh.biHeight); param.fincr = 100000; param.fbase = 2500000; int t; PluginGetAttrInt(m_Info, xvidstr_rc_bitrate, &t); #if API_VERSION >= ((2 << 16) | (1)) param.rc_bitrate = t; // such API change are not funny at all #else param.bitrate = t; #endif #if API_VERSION >= (2 << 16) PluginGetAttrInt(m_Info, xvidstr_rc_buffer, &t); #if API_VERSION >= ((2 << 16) | (1)) param.rc_buffer = t; #else param.rc_buffersize = t; #endif PluginGetAttrInt(m_Info, xvidstr_quant_type, &t); switch (t) { default: case 0: general = XVID_H263QUANT; break; case 1: general = XVID_MPEGQUANT; break; } #if API_VERSION >= ((2 << 16) | (1)) PluginGetAttrInt(m_Info, xvidstr_inter4v, &t); if (t) general |= XVID_INTER4V; PluginGetAttrInt(m_Info, xvidstr_diamond_search, &t); if (t) general |= PMV_HALFPELDIAMOND16 | PMV_HALFPELDIAMOND8; PluginGetAttrInt(m_Info, xvidstr_adaptive_quant, &t); if (t) general |= XVID_ADAPTIVEQUANT; #endif PluginGetAttrInt(m_Info, xvidstr_halfpel, &t); if (t) general |= XVID_HALFPEL; #ifdef XVID_INTERLACING PluginGetAttrInt(m_Info, xvidstr_interlacing, &t); if (t) general |= XVID_INTERLACING; #endif PluginGetAttrInt(m_Info, xvidstr_lum_masking, &t); if (t) general |= XVID_LUMIMASKING; // table from the xvid sample source code static const int motion_presets[7] = { 0, PMV_EARLYSTOP16, PMV_EARLYSTOP16, PMV_EARLYSTOP16 | PMV_HALFPELREFINE16, PMV_EARLYSTOP16 | PMV_HALFPELREFINE16, PMV_EARLYSTOP16 | PMV_HALFPELREFINE16 | PMV_EARLYSTOP8 | PMV_HALFPELREFINE8, PMV_EARLYSTOP16 | PMV_HALFPELREFINE16 | PMV_EXTSEARCH16 | PMV_EARLYSTOP8 | PMV_HALFPELREFINE8 | PMV_EXTSEARCH8 #ifdef XVID_INTERLACING | PMV_USESQUARES16 | PMV_USESQUARES8 #endif }; PluginGetAttrInt(m_Info, xvidstr_motion_search, &t); motion = (t >= 0 && t < 7) ? motion_presets[t] : 0; #if 0 // Motion estimation options PluginGetAttrInt(m_Info, xvidstr_me_zero, &t); if (t) motion |= XVID_ME_ZERO; PluginGetAttrInt(m_Info, xvidstr_me_logarithmic, &t); if (t) motion |= XVID_ME_LOGARITHMIC; PluginGetAttrInt(m_Info, xvidstr_me_fullsearch, &t); if (t) motion |= XVID_ME_FULLSEARCH; #endif // undef #ifdef XVID_ME_EPZS PluginGetAttrInt(m_Info, xvidstr_me_pmvfast, &t); if (t) motion |= XVID_ME_PMVFAST; PluginGetAttrInt(m_Info, xvidstr_me_epzs, &t); if (t) motion |= XVID_ME_EPZS; #endif #else PluginGetAttrInt(m_Info, xvidstr_rc_period, ¶m.rc_period); PluginGetAttrInt(m_Info, xvidstr_rc_reaction_period, ¶m.rc_reaction_period); PluginGetAttrInt(m_Info, xvidstr_rc_reaction_ratio, ¶m.rc_reaction_ratio); PluginGetAttrInt(m_Info, xvidstr_motion_search, ¶m.motion_search); #endif PluginGetAttrInt(m_Info, xvidstr_max_quantizer, ¶m.max_quantizer); PluginGetAttrInt(m_Info, xvidstr_min_quantizer, ¶m.min_quantizer); PluginGetAttrInt(m_Info, xvidstr_max_key_interval, ¶m.max_key_interval); // fast deinterlace // use bidirectional coding // flag to enable overlapped block motion compensation mode #if 0 if (param.quality == 1) { m_bRtMode=true; int quality=GetQuality(); m_iQuant=int(1+.003*(10000-quality)); AVM_WRITE(strDebug, "New quant: %d\n", m_iQuant); if(m_iQuant>31) m_iQuant=31; if(m_iQuant<1) m_iQuant=1; } else m_bRtMode=false; #endif xerr = xvid_encore(NULL, XVID_ENC_CREATE, ¶m, NULL); m_pHandle = param.handle; return xerr; } virtual int Stop() { if (!m_pHandle) return -1; int err = xvid_encore(m_pHandle, XVID_ENC_DESTROY, NULL, NULL); m_pHandle = 0; return err; } }; static IVideoEncoder* xvid_CreateVideoEncoder(const CodecInfo& info, fourcc_t compressor, const BITMAPINFOHEADER& bh) { if (bh.biCompression == 0 && bh.biBitCount != 24) { xvid_error_set("unsupported input bit depth"); return 0; } switch (bh.biCompression) { case 0: case fccYUY2: case fccYV12: case fccI420: case fccDIVX: case fccXVID: break; default: xvid_error_set("unsupported input format"); return 0; } return new XVID_VideoEncoder(info, compressor, bh); } static IVideoDecoder* xvid_CreateVideoDecoder(const CodecInfo& info, const BITMAPINFOHEADER& bh, int flip) { if (bh.biSize < 40) { xvid_error_set("unsupported biSize"); return 0; } return new XVID_VideoDecoder(info, bh, flip); } AVM_END_NAMESPACE; extern "C" const avm::codec_plugin_t avm_codec_plugin_xvid = { PLUGIN_API_VERSION, 0, 0, 0, avm::PluginGetAttrInt, avm::PluginSetAttrInt, 0, 0, // attrs avm::xvid_FillPlugins, 0, 0, // audio avm::xvid_CreateVideoDecoder, avm::xvid_CreateVideoEncoder, };