让ffmpeg支持rmvb

 

参考的代码在
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;
}

你可能感兴趣的:(让ffmpeg支持rmvb)