问题:
我从网络摄像头接收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: