除了用avisynth,还有支持rmvb的方法吗?
From Ffmpeg工程组
除了用avisynth,还有支持rmvb的方法吗?
首先先明确一个问题,解决支持rmvb的核心在于real公司的版权限制。 以下我们将详细介绍一下另一种方法的源代码
I just use the binary DLL of realplayer to decode rmvb. eg: I export the functions from "drvc.dll","drv1.dll","drv2.dll" and register a new codec for rv30 and rv40 codec
code: /*************************************************************** * @file realvideocodec.c * rmvideo decoder using the RealNetwork Binary DLLs * * Implementation of RealNetwork video interface wrapper for FFmpeg * Created by [email=dev@fastreaming.com]dev@fastreaming.com[/email],2007/04/25--2007/05/02 * * TODO: * Deinterlace? **************************************************************/ #include <windows.h> #include "avcodec.h" #include <stdio.h> typedef HRESULT (WINAPI *PRVCustomMessage)(void*, DWORD); typedef HRESULT (WINAPI *PRVFree)(DWORD); typedef HRESULT (WINAPI *PRVHiveMessage)(void*, DWORD); typedef HRESULT (WINAPI *PRVInit)(void*, DWORD* dwCookie); typedef HRESULT (WINAPI *PRVTransform)(BYTE*, BYTE*, void*, void*, DWORD); void ResizeHeight(BYTE* pIn, DWORD wi, DWORD hi, BYTE* pOut, DWORD wo, DWORD ho); void ResizeRow(BYTE* pIn, DWORD wi, DWORD dpi, BYTE* pOut, DWORD wo, DWORD dpo); void Resize(BYTE* pIn, DWORD wi, DWORD hi, BYTE* pOut, DWORD wo, DWORD ho); void ResizeWidth(BYTE* pIn, DWORD wi, DWORD hi, BYTE* pOut, DWORD wo, DWORD ho);
typedef struct RealVideoDecContext
{
PRVCustomMessage RVCustomMessage;
PRVFree RVFree;
PRVHiveMessage RVHiveMessage;
PRVInit RVInit;
PRVTransform RVTransform;
HMODULE m_hDrvDll;
DWORD m_dwCookie;
uint8_t *m_pI420;
uint8_t *m_pI420Temp;
AVFrame frame;
}RealVideoDecContext;
//FIXME: redundant?
static uint32_t rm_swap(unsigned int x)
{
x = ((x<<8)&0xFF00FF00) | ((x>>8)&0x00FF00FF);
x = (x>>16) | (x<<16);
return x;
}
void Resize(BYTE* pIn, DWORD wi, DWORD hi, BYTE* pOut, DWORD wo, DWORD ho)
{
int si = wi*hi, so = wo*ho;
//ASSERT(((si*so)&3) == 0);
if(wi < wo)
{
ResizeWidth(pIn, wi, hi, pOut, wo, ho);
ResizeWidth(pIn + si, wi/2, hi/2, pOut + so, wo/2, ho/2);
ResizeWidth(pIn + si + si/4, wi/2, hi/2, pOut + so + so/4, wo/2, ho/2);
if(hi == ho) return;
ResizeHeight(pOut, wo, hi, pIn, wo, ho);
ResizeHeight(pOut + so, wo/2, hi/2, pIn + so, wo/2, ho/2);
ResizeHeight(pOut + so + so/4, wo/2, hi/2, pIn + so + so/4, wo/2, ho/2);
}
else if(hi < ho)
{
ResizeHeight(pIn, wi, hi, pOut, wo, ho);
ResizeHeight(pIn + si, wi/2, hi/2, pOut + so, wo/2, ho/2);
ResizeHeight(pIn + si + si/4, wi/2, hi/2, pOut + so + so/4, wo/2, ho/2);
if(wi == wo) return;
//ASSERT(0); // this is uncreachable code, but anyway... looks nice being so symmetric
ResizeWidth(pOut, wi, ho, pIn, wo, ho);
ResizeWidth(pOut + so, wi/2, ho/2, pIn + so, wo/2, ho/2);
ResizeWidth(pOut + so + so/4, wi/2, ho/2, pIn + so + so/4, wo/2, ho/2);
}
}
void ResizeWidth(BYTE* pIn, DWORD wi, DWORD hi, BYTE* pOut, DWORD wo, DWORD ho)
{
DWORD y;
for( y = 0; y < hi; y++, pIn += wi, pOut += wo)
{
if(wi == wo) memcpy(pOut, pIn, wo);
else ResizeRow(pIn, wi, 1, pOut, wo, 1);
}
}
void ResizeHeight(BYTE* pIn, DWORD wi, DWORD hi, BYTE* pOut, DWORD wo, DWORD ho)
{
if(hi == ho)
{
memcpy(pOut, pIn, wo*ho);
}
else
{
DWORD x;
for( x = 0; x < wo; x++, pIn++, pOut++)
ResizeRow(pIn, hi, wo, pOut, ho, wo);
}
}
void ResizeRow(BYTE* pIn, DWORD wi, DWORD dpi, BYTE* pOut, DWORD wo, DWORD dpo)
{
//ASSERT(wi < wo);
DWORD i,j,dj;
if(dpo == 1)
{
for( i = 0, j = 0, dj = (wi<<16)/wo; i < wo-1; i++, pOut++, j += dj)
// pOut[i] = pIn[j>>16];
{
BYTE* p = &pIn[j>>16];
DWORD jf = j&0xffff;
*pOut = ((p[0]*(0xffff-jf) + p[1]*jf) + 0x7fff) >> 16;
}
*pOut = pIn[wi-1];
}
else
{
for( i = 0, j = 0, dj = (wi<<16)/wo; i < wo-1; i++, pOut += dpo, j += dj)
// *pOut = pIn[dpi*(j>>16)];
{
BYTE* p = &pIn[dpi*(j>>16)];
DWORD jf = j&0xffff;
*pOut = ((p[0]*(0xffff-jf) + p[dpi]*jf) + 0x7fff) >> 16;
}
*pOut = pIn[dpi*(wi-1)];
}
}
extern HINSTANCE g_hInstance;
static int RealVideoDecoderInit(AVCodecContext *avctx)
{
RealVideoDecContext *rvdec = avctx->priv_data;
uint8_t *extra_data = avctx->extradata;
unsigned int type1, type2, *pType ;
uint8_t *pVideoUnknown;
char buff[MAX_PATH+1],*ptr;
int hr,size;
#pragma pack(push, 1)
struct {unsigned short unk1, w, h, unk3; unsigned long unk2, subformat, unk5, format;} init;
#pragma pack(pop)
//clean up
pType = extra_data;
type1 = rm_swap(*pType++);
type2 = rm_swap(*pType++);
pVideoUnknown = pType;
init.unk1 = 11;
init.w = avctx->width;
init.h = avctx->height;
init.unk3 = 0;
init.unk2 = 0;
init.subformat = type1;
init.unk5 = 1;
init.format = type2;
rvdec->m_dwCookie = 0;
rvdec->m_pI420 = NULL;
rvdec->m_pI420Temp = NULL;
//XXX: need probe more codecs
GetModuleFileName(g_hInstance, buff, MAX_PATH);
ptr = (char*)strrchr(buff,'\\');
if(ptr){
ptr++;
*ptr = NULL;
switch(avctx->codec_id){
case CODEC_ID_RV10:
strcat(buff,"drv1.dll");//FIXME
break;
case CODEC_ID_RV20:
strcat(buff,"drv2.dll");
break;
//case CODEC_ID_RV30:
// strcat(buff,"drv33260.dll");
// break;
default:
strcat(buff,"drvc.dll");
break;
}
rvdec->m_hDrvDll = LoadLibrary(buff);
}
if(!rvdec->m_hDrvDll)
return -1;
rvdec->RVCustomMessage = (PRVCustomMessage)GetProcAddress(rvdec->m_hDrvDll, "RV20toYUV420CustomMessage");
rvdec->RVFree = (PRVFree)GetProcAddress(rvdec->m_hDrvDll, "RV20toYUV420Free");
rvdec->RVHiveMessage = (PRVHiveMessage)GetProcAddress(rvdec->m_hDrvDll, "RV20toYUV420HiveMessage");
rvdec->RVInit = (PRVInit)GetProcAddress(rvdec->m_hDrvDll, "RV20toYUV420Init");
rvdec->RVTransform = (PRVTransform)GetProcAddress(rvdec->m_hDrvDll, "RV20toYUV420Transform");
if(!rvdec->RVCustomMessage)
rvdec->RVCustomMessage = (PRVCustomMessage)GetProcAddress(rvdec->m_hDrvDll, "RV40toYUV420CustomMessage");
if(!rvdec->RVFree)
rvdec->RVFree = (PRVFree)GetProcAddress(rvdec->m_hDrvDll, "RV40toYUV420Free");
if(!rvdec->RVHiveMessage)
rvdec->RVHiveMessage = (PRVHiveMessage)GetProcAddress(rvdec->m_hDrvDll, "RV40toYUV420HiveMessage");
if(!rvdec->RVInit)
rvdec->RVInit = (PRVInit)GetProcAddress(rvdec->m_hDrvDll, "RV40toYUV420Init");
if(!rvdec->RVTransform)
rvdec->RVTransform = (PRVTransform)GetProcAddress(rvdec->m_hDrvDll, "RV40toYUV420Transform");
if(!rvdec->RVCustomMessage || !rvdec->RVFree || !rvdec->RVHiveMessage
|| !rvdec->RVInit || !rvdec->RVTransform)
return -1;
hr = rvdec->RVInit(&init, &rvdec->m_dwCookie);
if(hr < 0) return -1;
if(avctx->codec_tag <= '03VR' && type2 >= 0x20200002){
int i;
int nWidthHeight = (1+((type1>>16)&7));
#pragma pack(push, 1)
struct {uint32_t data1; uint32_t data2; uint32_t* dimensions;} cmsg_data =
{0x24, nWidthHeight, NULL};
#pragma pack(pop)
uint32_t* pWH = av_malloc(sizeof(uint32_t)*nWidthHeight*2);
pWH[0] = avctx->width;
pWH[1] = avctx->height;
for(i = 2; i < nWidthHeight*2; i++)
pWH[i] = pVideoUnknown[i-2]*4;
cmsg_data.dimensions = pWH;
hr = rvdec->RVCustomMessage(&cmsg_data, rvdec->m_dwCookie);
av_free(pWH);
}
size = avctx->width * avctx->height;
rvdec->m_pI420 = av_malloc(size * 3/2);
rvdec->m_pI420Temp = av_malloc(size * 3/2);
if(!rvdec->m_pI420 || !rvdec->m_pI420Temp)
return -1;
memset(rvdec->m_pI420,0,size);
memset(rvdec->m_pI420 + size,128, size/2);
memset(rvdec->m_pI420Temp,0,size);
memset(rvdec->m_pI420Temp + size,128, size/2);
rvdec->frame.data[0] = NULL;
avctx->pix_fmt = PIX_FMT_YUV420P;
return hr;
}[/i][/i]
static int RealVideoDecoderTransform(AVCodecContext *avctx, void *outdata, int *outdata_size,uint8_t *buf, int buf_size)
{
RealVideoDecContext *rvdec = avctx->priv_data;
int hr;
unsigned char *pDataIn = buf;
int len = buf_size;
int offset = 1+((*pDataIn)+1)*8;
//FIXME?
unsigned int rtStart = avctx->timecode_frame_start;
#pragma pack(push, 1)
struct {DWORD len, unk1, chunks; DWORD* extra; DWORD unk2, timestamp;} transform_in =
{len - offset, 0, *pDataIn, (DWORD*)(pDataIn+1), 0, (DWORD)(rtStart/10000)};
struct {DWORD unk1, unk2, timestamp, w, h;} transform_out =
{0,0,0,0,0};
#pragma pack(pop)
pDataIn += offset;
len = avpicture_get_size(avctx->pix_fmt, avctx->width, avctx->height);
if (rvdec->frame.data[0])
avctx->release_buffer(avctx, &rvdec->frame);
if(avctx->get_buffer(avctx, &rvdec->frame) < 0){
av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
return -1;
}
hr = rvdec->RVTransform(pDataIn, (BYTE*)rvdec->m_pI420, &transform_in, &transform_out, rvdec->m_dwCookie);
if(hr >= 0) {
BYTE* pI420[3] = {rvdec->m_pI420, rvdec->m_pI420Temp, NULL};
if(transform_out.w != avctx->width || transform_out.h != avctx->height){
//TODO: use libswscale
Resize(pI420[0], transform_out.w, transform_out.h, pI420[1], avctx->width, avctx->height);
// only one of these can be true, and when it happens the result image must be in the tmp buffer
if(transform_out.w == avctx->width || transform_out.h == avctx->height)
pI420[2] = pI420[1], pI420[1] = pI420[0], pI420[0] = pI420[2];
}
avpicture_fill((AVPicture*)&rvdec->frame, pI420[0], avctx->pix_fmt, avctx->width, avctx->height);
*(AVFrame*)outdata = rvdec->frame;
*outdata_size = sizeof(AVFrame);
return buf_size;
}
return -1;
}
static int RealVideoDecoderClose(AVCodecContext *avctx)
{
RealVideoDecContext *rvdec = avctx->priv_data;
if(rvdec->m_dwCookie){
rvdec->RVFree(rvdec->m_dwCookie);
rvdec->m_dwCookie = 0;
}
if(rvdec->m_pI420){
av_free(rvdec->m_pI420);
rvdec->m_pI420 = NULL;
}
if(rvdec->m_pI420Temp){
av_free(rvdec->m_pI420Temp);
rvdec->m_pI420Temp = NULL;
}
if(rvdec->m_hDrvDll){
FreeLibrary(rvdec->m_hDrvDll);
rvdec->m_hDrvDll = 0;
}
if (rvdec->frame.data[0])
avctx->release_buffer(avctx, &rvdec->frame);
return 0;
}
AVCodec binary_rv10_decoder = {
"RealVideo10",
CODEC_TYPE_VIDEO,
CODEC_ID_RV10,
sizeof(RealVideoDecContext),
RealVideoDecoderInit,
NULL,
RealVideoDecoderClose,
RealVideoDecoderTransform,
CODEC_CAP_DR1,
};
AVCodec binary_rv20_decoder = {
"RealVideo20",
CODEC_TYPE_VIDEO,
CODEC_ID_RV20,
sizeof(RealVideoDecContext),
RealVideoDecoderInit,
NULL,
RealVideoDecoderClose,
RealVideoDecoderTransform,
CODEC_CAP_DR1,
};
AVCodec binary_rv30_decoder = {
"RealVideo30",
CODEC_TYPE_VIDEO,
CODEC_ID_RV30,
sizeof(RealVideoDecContext),
RealVideoDecoderInit,
NULL,
RealVideoDecoderClose,
RealVideoDecoderTransform,
CODEC_CAP_DR1,
};
AVCodec binary_rv40_decoder = {
"RealVideo40",
CODEC_TYPE_VIDEO,
CODEC_ID_RV40,
sizeof(RealVideoDecContext),
RealVideoDecoderInit,
NULL,
RealVideoDecoderClose,
RealVideoDecoderTransform,
CODEC_CAP_DR1,
};
void register_rm_binary_codec(void)
{
static int realvideo_register = 0;
if(realvideo_register)
return;
register_avcodec(&binary_rv10_decoder);
register_avcodec(&binary_rv20_decoder);
register_avcodec(&binary_rv30_decoder);
register_avcodec(&binary_rv40_decoder);
realvideo_register = 1;
}
//End of the file
有关该问题的讨论帖可参考ffmpeg工程组论坛中的相关讨论:
[有关除了用avisynth,还有支持rmvb的方法吗?的讨论]
