从rgb24编码到mpeg4并用libavcodec从mpeg4解码到rgb24

时间:2013-11-21 16:00:31

标签: rgb libavcodec mpeg-4 avcodec

问题:

我从网络摄像头接收rgb24图像,并使用libavcodec将它们编码为mpeg4。我无法将它们解码回rgb24。 avcodec_decode_video2返回它处理了所有输入,但avcodec_decode_video2的第三个参数始终返回0.

什么有效:

在调用avcodec_encode_video2之前,rgb24图像通过sws_scale转换为yuv420p格式。这可以正常工作,因为我可以从sws_scale获取结果并成功将其转换回rgb24。我可以在屏幕上查看结果,这就是我现在这部分的工作原理。

说明

对于从avcodec_encode_video2返回的每个图像,我正在调用另一个函数,将其解码回rgb24。此方案设置用于测试目的,以确保我可以成功编码/解码。下面总结了代码中发生的事情:

/* variable declarations */

...


/* main processing */

while( true )
{
    rgb24 = getRgb24();
    sws_scale( ..., &rgb24, ..., frame->data, ... );    

    /* sws_scale is successful */

    ret = avcodec_encode_video2( ..., &pkt, frame, &got_output );    

    /* avcodec_encode_video2 returns success and got_output is 1 */

    /* copy encoded image to another buffer */
    memcpy( hugebuffer, pkt.data, pkt.size );
    memcpy( hugebuffer + pkt.size, 0, FF_INPUT_BUFFER_PADDING_SIZE );

    av_init_packet( &decodePacket );
    decodePacket.data = hugebuffer;
    decodePacket.size = pkt.size;
    len = avcodec_decode_video2( ..., decodeFrame, &got_output, &decodePacket );

    /* len = pkt.size but got_output is ALWAYS 0 */   
}

我想知道在调用解码函数之前是否还需要做其他事情???

作为参考,我提供了下面的实际代码,其中包括以下四个函数:videoEncodeInit,videoEncode,videoDecodeInit,videoDecode。之后,我发布了函数的调用方式。

/*
 * Copyright (c) 2001 Fabrice Bellard
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * @file
 * libavcodec API use example.
 *
 * Note that libavcodec only handles codecs (mpeg, mpeg4, etc...),
 * not file formats (avi, vob, mp4, mov, mkv, mxf, flv, mpegts, mpegps, etc...). See library 'libavformat' for the
 * format handling
 * @example doc/examples/decoding_encoding.c
 */

#include <math.h>

extern "C"
{
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libswscale/swscale.h>
}


#include "video.h"
#include "logmsg.h"


typedef struct
{
    AVCodec *codec;
    AVCodecContext *c;
    AVFrame *frame;
    SwsContext *yuv420p_to_rgb24_ctx;
    int frame_count;
} PrivateVideoDecodeParams;


static AVCodec *codec = 0;
static AVCodecContext *c = 0;
static AVFrame *frame = 0;
static SwsContext *rgb24_to_yuv420p_ctx = 0;

static int registered = 0;

extern int g_debug;         /* from main.cpp */

int videoEncodeInit( VideoEncodeParams *ep )
{
    int ret;

    if( registered == 0 )
    {
        avcodec_register_all();
        registered = 1;
    }

    if( c != 0 )
    {
        avcodec_close(c);
        av_free(c);

    }

    if( frame && frame->data[0] )
    {
        av_freep(&frame->data[0]);
        avcodec_free_frame(&frame);
    }

    if( rgb24_to_yuv420p_ctx )
    {
        sws_freeContext( rgb24_to_yuv420p_ctx );
    }



    /* find the mpeg1 video encoder */
    codec = avcodec_find_encoder(ep->codec_id);
    if (!codec) 
    {
        logmsg( "error - Codec=%d not found [%s:%d]\n", (int)ep->codec_id, __FILE__, __LINE__ );
        return( -1 );
    }

    c = avcodec_alloc_context3(codec);
    if (!c) 
    {
        logmsg("error - Could not allocate video codec context [%s:%d]\n", __FILE__, __LINE__ );
        return(-1);
    }

    /* put sample parameters */
    c->bit_rate = 400000;
    /* resolution must be a multiple of two */
    c->width = ep->width;   /* was hard coded at 352 */
    c->height = ep->height; /* was hard coded at 288 */
    /* frames per second */
    c->time_base.den = 1;
    c->time_base.num = ep->fps; /* was hard coded to 25 */
    c->gop_size = 10; /* emit one intra frame every ten frames */
    c->max_b_frames = 1;
    c->pix_fmt = AV_PIX_FMT_YUV420P;

    if(ep->codec_id == AV_CODEC_ID_H264)
    {
        av_opt_set(c->priv_data, "preset", "slow", 0);
    }

    /* open it */
    ret = avcodec_open2(c, codec, NULL);
    if( ret < 0)
    {
        logmsg( "error - Could not open codec [%s:%d]\n", __FILE__, __LINE__ );
        return(-1);
    }

    frame = avcodec_alloc_frame();
    if (!frame) 
    {
        logmsg("error - Could not allocate video frame [%s:%d]\n", __FILE__, __LINE__ );
        return(-1);
    }
    frame->format = c->pix_fmt;
    frame->width  = c->width;
    frame->height = c->height;

    /* the image can be allocated by any means and av_image_alloc() is
     * just the most convenient way if av_malloc() is to be used */
    ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32);
    if (ret < 0)
    {
        logmsg("error - Could not allocate raw picture buffer [%s:%d]\n", __FILE__, __LINE__ );
        return( -1 );
    }

    rgb24_to_yuv420p_ctx = sws_getContext( ep->width, ep->height, AV_PIX_FMT_RGB24,
        ep->width, ep->height, AV_PIX_FMT_YUV420P,
        SWS_FAST_BILINEAR, 0, 0, 0 );
    if( rgb24_to_yuv420p_ctx == 0 )
    {
        logmsg( "error - failed to create rb24 to yuv420p conversion scale [%s:%d]\n",
            __FILE__, __LINE__ );
        return( -1 );
    }



    return( 0 );
}

int videoEncode( VideoEncodeParams *ep )
{
    AVPacket pkt;
    int ret;
    int got_output;

    av_init_packet(&pkt);
    pkt.data = 0;           // packet data will be allocated by the encoder
    pkt.size = 0;

    frame->pts = ep->pts;
    ep->pts++;

    /* convert input to that expected by the encoder */
    int srcStride = c->width * 3;
    ret = sws_scale( rgb24_to_yuv420p_ctx, &ep->rgb24, &srcStride, 0, c->height, frame->data, frame->linesize );

    /* encode the image */
    ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
    if (ret < 0) 
    {
        logmsg("error - Error encoding frame [%s:%d]\n", __FILE__, __LINE__ );
        return(-1);
    }

    if (got_output) 
    {
        if( g_debug )
        {
            logmsg("debug - Write frame %3d (size=%5d)\n", ep->pts-1, pkt.size);
        }
        ep->callback( ep, pkt.data, pkt.size );
        av_free_packet(&pkt);
    }

    return( 0 );
}


int videoDecodeInit( VideoDecodeParams *dp )
{
    PrivateVideoDecodeParams *pdp;
    int ret;

    if( registered == 0 )
    {
        avcodec_register_all();
        registered = 1;
    }

    if( dp->rgb24_out )
    {
        free( dp->rgb24_out );
    }
    dp->rgb24_out = (unsigned char*)calloc( 1, dp->width * 3 * dp->height );

    pdp = (PrivateVideoDecodeParams*)dp->priv;
    if( pdp )
    {
        if( pdp->c != 0 )
        {
            avcodec_close(pdp->c);
            av_free(pdp->c);    
        }

        if( pdp->frame && pdp->frame->data[0] )
        {
            av_freep(&pdp->frame->data[0]);
            avcodec_free_frame(&pdp->frame);
        }

        if( pdp->yuv420p_to_rgb24_ctx )
        {
            sws_freeContext( pdp->yuv420p_to_rgb24_ctx );
        }
        free( pdp );
    }

    dp->priv = calloc( 1, sizeof(*pdp) );
    pdp = (PrivateVideoDecodeParams*)dp->priv;


    /* find the video decoder */
    if( pdp->codec == 0 )
    {
        pdp->codec = avcodec_find_decoder(dp->codec_id);
        if (!pdp->codec)
        {
            logmsg("error - Codec not found=%d [%s:%d]\n", (int)dp->codec_id, __FILE__, __LINE__ );
            return( -1 );
        }
    }

    pdp->c = avcodec_alloc_context3(pdp->codec);
    if (!pdp->c) 
    {
        logmsg("error - Could not allocate video codec context [%s:%d]\n", __FILE__, __LINE__ );
        return( -1 );
    }



    if(pdp->codec->capabilities & CODEC_CAP_TRUNCATED)
    {
        pdp->c->flags |= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */
    }

    /* For some codecs, such as msmpeg4 and mpeg4, width and height
       MUST be initialized there because this information is not
       available in the bitstream. */


    /* put sample parameters */
    pdp->c->bit_rate = 400000;

    /* resolution must be a multiple of two */
    pdp->c->width = dp->width;  
    pdp->c->height = dp->height;    

    pdp->c->time_base.den = 1;
    pdp->c->time_base.num = 25; /* was hard coded to 25 */
    pdp->c->gop_size = 10; /* emit one intra frame every ten frames */
    pdp->c->max_b_frames = 1;
    pdp->c->pix_fmt = AV_PIX_FMT_YUV420P;


    if(dp->codec_id == AV_CODEC_ID_H264)
    {
        av_opt_set(pdp->c->priv_data, "preset", "slow", 0);
    }

    /* open it */
    if (avcodec_open2(pdp->c, pdp->codec, NULL) < 0) 
    {
        logmsg("error - Could not open codec [%s:%d]\n", __FILE__, __LINE__ );
        return( -1 );
    }

    pdp->frame = avcodec_alloc_frame();
    if (!pdp->frame)
    {
        logmsg("error - Could not allocate video frame [%s:%d]\n", __FILE__, __LINE__ );
        return( -1 );
    }

    pdp->frame->format = AV_PIX_FMT_YUV420P;
    pdp->frame->width  = c->width;
    pdp->frame->height = c->height;


#if 0
    /* the image can be allocated by any means and av_image_alloc() is
     * just the most convenient way if av_malloc() is to be used */
    ret = av_image_alloc(pdp->frame->data, pdp->frame->linesize, pdp->c->width, pdp->c->height, pdp->c->pix_fmt, 32);
    if (ret < 0)
    {
        logmsg("error - Could not allocate raw picture buffer [%s:%d]\n", __FILE__, __LINE__ );
        return( -1 );
    }
#endif

    pdp->yuv420p_to_rgb24_ctx = sws_getContext( dp->width, dp->height, AV_PIX_FMT_YUV420P,
        dp->width, dp->height, AV_PIX_FMT_RGB24,
        SWS_FAST_BILINEAR, 0, 0, 0 );
    if( pdp->yuv420p_to_rgb24_ctx == 0 )
    {
        logmsg( "error - failed to create yuv420p to rb24 conversion scale [%s:%d]\n",
            __FILE__, __LINE__ );
        return( -1 );
    }

    return( 0 );
}

int videoDecode( VideoDecodeParams *dp )
{
    static uint8_t *inbuf = 0;
    static int inbufLen = 0;

    PrivateVideoDecodeParams *pdp;
    AVPacket avpkt;
    int len;
    int got_frame;
    int srcStride;
    int ret;

    pdp = (PrivateVideoDecodeParams*)dp->priv;

    /* Make sure we have a buffer to work with. */
    if( inbuf == 0 )
    {
        /* get a big buffer */
        inbufLen = 256000;
        inbuf = (uint8_t*)calloc( 1, inbufLen );
    }
    if( (dp->yuv420_len + FF_INPUT_BUFFER_PADDING_SIZE) > inbufLen )
    {
        if( inbuf )
        {
            free( inbuf );
        }
        inbufLen = dp->yuv420_len + FF_INPUT_BUFFER_PADDING_SIZE;
        inbuf = (uint8_t*)calloc( 1, inbufLen );
    }

    /* copy the video to our buffer */
    memcpy( inbuf, dp->yuv420_in, dp->yuv420_len );

    /* set end of buffer to 0 (this ensures that no overreading happens for damaged mpeg streams) */
    memset( inbuf + dp->yuv420_len, 0, FF_INPUT_BUFFER_PADDING_SIZE );

    av_init_packet(&avpkt);
    avpkt.size = dp->yuv420_len;
    avpkt.data = inbuf;

    len = avcodec_decode_video2(pdp->c, pdp->frame, &got_frame, &avpkt);
    if (len < 0)
    {
        logmsg("error - Error while decoding frame %d [%s:%d]\n", pdp->frame_count, __FILE__, __LINE__ );
        return len;
    }

    if (got_frame) 
    {
        logmsg( "got a frame\n" );

        /* convert to RGB24 */
        srcStride = pdp->c->width * 3;
        ret = sws_scale( pdp->yuv420p_to_rgb24_ctx, pdp->frame->data, pdp->frame->linesize, 0, pdp->c->height, &dp->rgb24_out, &srcStride );
        pdp->frame_count++;
    }
    else
    {
        logmsg( "no frame\n" );
    }
    return( got_frame );
}

以下是调用init函数的方法:

videoEncodeInit:

ep.codec_id = AV_CODEC_ID_MPEG4;
ep.width = 352;
ep.height = 288;
ep.fps = 10;
ep.rgb24 = 0;
ep.pts = 0;
ep.callback = debug_videoEncodeCallback;
if( videoEncodeInit( &ep ) == -1 )
{
    MyMessageBox( g_mainwindow, "Can't initialize video codec!", "Error", MB_OK | MB_ICONERROR );
    exit( -1 );
}

videoDecodeInit:

dp.codec_id = AV_CODEC_ID_MPEG4;
dp.width = 352;
dp.height = 288;
videoDecodeInit( &dp );

videoEncode:

0 个答案:

没有答案