参考的代码在
http://bbs.chinavideo.org/viewthread.php?tid=2406
http://www.ffmpeg.com.cn/index.php?title=%E9%99%A4%E4%BA%86%E7%94%A8avisynth%2C%E8%BF%98%E6%9C%89%E6%94%AF%E6%8C%81rmvb%E7%9A%84%E6%96%B9%E6%B3%95%E5%90%97%3F&oldid=325
已经编译好的ffmpeg库不用改,把这段代码写在一个c文件,加到调用ffmpeg的工程。
在调用完av_register_all()后调用register_rm_binary_codec,注册rm的解码器
不过有几点注意:
1、原代码中g_hInstance变量实际上是不需要的。GetModuleFileName的第一个参数可以为NULL
2、把GetModuleFileName改为GetModuleFileNameA,LoadLibrary改为LoadLibraryA,确保UNICODE版本的程序也可以正确运行。
3、RealVideoDecoderTransform()函数中
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;
}
以及RealVideoDecoderClose()函数中
if (rvdec->frame.data[0])
avctx->release_buffer(avctx, &rvdec->frame);
这两段代码是多余的,要删除。而且这样写会引起堆出错
代码也可以参考mplayer,它的代码还包含了Linux下解码的部分
http://svn.mplayerhq.hu/mplayer/trunk/libmpcodecs/vd_realvid.c?view=markup
/***************************************************************
* @file realvideocodec.c
* rmvideo decoder using the RealNetwork Binary DLLs
*
* Implementation of RealNetwork video interface wrapper for FFmpeg
* Created by [[email protected]][email protected][/email],2007/04/25--2007/05/02
*
* TODO:
* Deinterlace?
**************************************************************/
#ifdef WIN32
#include
#else
#include
#endif
#include "./libavcodec/avcodec.h"
#include
#include
#ifdef WIN32
typedef HRESULT (WINAPI *PRVCustomMessage)(void*, uint32_t);
typedef HRESULT (WINAPI *PRVFree)(uint32_t);
typedef HRESULT (WINAPI *PRVInit)(void*, uint32_t* dwCookie);
typedef HRESULT (WINAPI *PRVTransform)(uint8_t*, uint8_t*, void*, void*, uint32_t);
#else
typedef unsigned long (*PRVCustomMessage)(void*, uint32_t);
typedef unsigned long (*PRVFree)(uint32_t);
typedef unsigned long (*PRVInit)(void*, uint32_t* dwCookie);
typedef unsigned long (*PRVTransform)(uint8_t*, uint8_t*, void*, void*, uint32_t);
#endif
void ResizeHeight(uint8_t* pIn, uint32_t wi, uint32_t hi, uint8_t* pOut, uint32_t wo, uint32_t ho);
void ResizeRow(uint8_t* pIn, uint32_t wi, uint32_t dpi, uint8_t* pOut, uint32_t wo, uint32_t dpo);
void Resize(uint8_t* pIn, uint32_t wi, uint32_t hi, uint8_t* pOut, uint32_t wo, uint32_t ho);
void ResizeWidth(uint8_t* pIn, uint32_t wi, uint32_t hi, uint8_t* pOut, uint32_t wo, uint32_t ho);
typedef struct RealVideoDecContext
{
PRVCustomMessage RVCustomMessage;
PRVFree RVFree;
PRVInit RVInit;
PRVTransform RVTransform;
#ifdef WIN32
HMODULE m_hDrvDll;
#else
void *m_hDrvDll;
#endif
uint32_t 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(uint8_t* pIn, uint32_t wi, uint32_t hi, uint8_t* pOut, uint32_t wo, uint32_t 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(uint8_t* pIn, uint32_t wi, uint32_t hi, uint8_t* pOut, uint32_t wo, uint32_t ho)
{
uint32_t 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(uint8_t* pIn, uint32_t wi, uint32_t hi, uint8_t* pOut, uint32_t wo, uint32_t ho)
{
if(hi == ho)
{
memcpy(pOut, pIn, wo*ho);
}
else
{
uint32_t x;
for( x = 0; x < wo; x++, pIn++, pOut++)
ResizeRow(pIn, hi, wo, pOut, ho, wo);
}
}
void ResizeRow(uint8_t* pIn, uint32_t wi, uint32_t dpi, uint8_t* pOut, uint32_t wo, uint32_t dpo)
{
//ASSERT(wi < wo);
uint32_t 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];
{
uint8_t* p = &pIn[j>>16];
uint32_t jf = j&0xffff;
*pOut = (uint8_t)(((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)];
{
uint8_t* p = &pIn[dpi*(j>>16)];
uint32_t jf = j&0xffff;
*pOut = (uint8_t)(((p[0]*(0xffff-jf) + p[dpi]*jf) + 0x7fff) >> 16);
}
*pOut = pIn[dpi*(wi-1)];
}
}
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[280] = "";
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 = (unsigned int*)extra_data;
type1 = rm_swap(*pType++);
type2 = rm_swap(*pType++);
pVideoUnknown = (uint8_t*)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
#ifdef WIN32
strcpy(buff,"drvc.dll");
rvdec->m_hDrvDll = LoadLibraryA(buff);
if(!rvdec->m_hDrvDll)
{
av_log(avctx, AV_LOG_ERROR, "LoadLibraryA failed\n");
return -1;
}
rvdec->RVCustomMessage = (PRVCustomMessage)GetProcAddress(rvdec->m_hDrvDll, "RV20toYUV420CustomMessage");
rvdec->RVFree = (PRVFree)GetProcAddress(rvdec->m_hDrvDll, "RV20toYUV420Free");
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->RVInit)
rvdec->RVInit = (PRVInit)GetProcAddress(rvdec->m_hDrvDll, "RV40toYUV420Init");
if(!rvdec->RVTransform)
rvdec->RVTransform = (PRVTransform)GetProcAddress(rvdec->m_hDrvDll, "RV40toYUV420Transform");
#else
strcpy(buff, "./drvc.so");
rvdec->m_hDrvDll = dlopen (buff, RTLD_LAZY);
if(!rvdec->m_hDrvDll)
{
av_log(avctx, AV_LOG_ERROR, "Error in loading drvc.so: %s\n", dlerror());
return -1;
}
rvdec->RVCustomMessage = dlsym(rvdec->m_hDrvDll, "RV20toYUV420CustomMessage");
rvdec->RVFree = dlsym(rvdec->m_hDrvDll, "RV20toYUV420Free");
rvdec->RVInit = dlsym(rvdec->m_hDrvDll, "RV20toYUV420Init");
rvdec->RVTransform = dlsym(rvdec->m_hDrvDll, "RV20toYUV420Transform");
if(!rvdec->RVCustomMessage)
rvdec->RVCustomMessage = dlsym(rvdec->m_hDrvDll, "RV40toYUV420CustomMessage");
if(!rvdec->RVFree)
rvdec->RVFree = dlsym(rvdec->m_hDrvDll, "RV40toYUV420Free");
if(!rvdec->RVInit)
rvdec->RVInit = dlsym(rvdec->m_hDrvDll, "RV40toYUV420Init");
if(!rvdec->RVTransform)
rvdec->RVTransform = dlsym(rvdec->m_hDrvDll, "RV40toYUV420Transform");
#endif
if(!rvdec->RVCustomMessage || !rvdec->RVFree || !rvdec->RVInit || !rvdec->RVTransform)
return -1;
hr = rvdec->RVInit(&init, &rvdec->m_dwCookie);
if(hr < 0) return -1;
if(avctx->codec_tag <= 0x30335652 && 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;
}
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?
int64_t rtStart = avctx->timecode_frame_start;
#pragma pack(push, 1)
struct {uint32_t len, unk1, chunks; uint32_t* extra; uint32_t unk2, timestamp;} transform_in =
{len - offset, 0, *pDataIn, (uint32_t*)(pDataIn+1), 0, (uint32_t)(rtStart/10000)};
struct {uint32_t unk1, unk2, timestamp, w, h;} transform_out =
{0,0,0,0,0};
#pragma pack(pop)
pDataIn += offset;
hr = rvdec->RVTransform(pDataIn, (uint8_t*)rvdec->m_pI420, &transform_in, &transform_out, rvdec->m_dwCookie);
if(hr >= 0) {
uint8_t* 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){
#ifdef WIN32
FreeLibrary(rvdec->m_hDrvDll);
#else
dlclose(rvdec->m_hDrvDll);
#endif
rvdec->m_hDrvDll = 0;
}
return 0;
}
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_rv30_decoder);
register_avcodec(&binary_rv40_decoder);
realvideo_register = 1;
}