avcodec_send_frame和avcodec_receive_packet

编码器和解码器都维护了一个缓冲区,在刚开始输入数据时,需要多输入几帧,等缓冲区被填充满后,才会在receive端接收到编码或解码后的数据。
另一方面因为存在AVPacket中的数据不一定是一帧(比如音频的数据可能1个AVPacket包含1s的数据,帧率为25的话,就包含25帧),但存在AVFrame中的是一帧数据,所以avcodec_send_packet和avcodec_receive_frame不一定一一对应,调用一次avcodec_send_packet后,可能需要多次调用avcodec_receive_frame,另一篇文章讨论av_read_frame每次返回的视频和音频帧数

解码接口:
avcodec_send_packet和avcodec_receive_frame
内存:frame只需要分配对象本身空间就好,frame->data的空间并不需要分配。函数会判断是否已经给frame->data分配空间,如果没有分配会在函数内部为frame->data分配合适的空间。另一篇文章,AVFrame内存讨论,AVFrame相关api
。举例如下:

int re = avcodec_send_packet(codec_ctx, pkt);
if (re != 0)
{
    return;
}
while( avcodec_receive_frame(codec_ctx, frame) >= 0)
{
    //处理接收后的帧
}

avcodec_receive_frame:send端只把数据放入缓冲区,recive端才是解码并获得数据的函数,如果receive发现已有解码后的数据则直接获取,如果没有则开始解码。

编码接口
数据遗留:在最后应该向avcodec_send_frame(enc_ctx, NULL)传入NULL数据,这样编码器知道后面不会再有数据,就把放在缓冲区中的数据,全部编码并通过avcodec_receive_packet输出出来。

返回值:EAGAIN,意思是需要输入更多的数据,才会有新的数据编码后的数据返回。AVERROR_EOF在从文件中读取数据推流时,出现这个错误是因为文件内的数据读完了。

参数内存:newpkt->data并不需要分配数据空间,只需要给newpkt本身结构体分配空间就好。另一篇文章,关于AVPacket内存与avcodec_receive_packet的关系,AVPacket相关api

func()
{
	avcodec_send_frame(enc_ctx, frame);
	if(ret < 0)
	{
		//Error
	}
	while(ret >= 0)
	{
		ret = avcodec_receive_packet(enc_ctx, newpkt);
		if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)	{return;}
		else if(ret < 0){//Error}
	}

}

在ubuntu虚拟机中采集pc摄像头,并推流实测编解码的四个函数是否一一对应关系,帧率为30:
解码
avcodec_send_packet
avcodec_receive_frame
记录:s1518 r1518 s2754 r2754
s发送的次数,r成功接收的次数
第一次调用avcodec_send_packet,avcodec_receive_frame就成功接收到数据了。并且以后每次也都一一对应调用两个函数。

编码
avcodec_send_frame
avcodec_receive_packet
//s1336 r1286 s867 r817 保持50帧
avcodec_send_frame连续调用50次后,avcodec_receive_packet才成功接收到一次数据,以后每次都一一对应。

在ubuntu虚拟机中采集pc声卡,并推流实测编解码的三个函数是否一一对应关系:
解码
avcodec_send_packet
avcodec_receive_frame
av_interleaved_write_frame
实测当分别送进去128,1024,940个音频采样到avcodec_send_frame时,avcodec_receive_packet一对一返回,开始并没有几帧缓存,av_interleaved_write_frame也一对一执行,也没有一开始缓存几帧。也就是三者总是保持一对一执行,后两者都有返回,并且一开始没有缓存。音频涉及到字节的处理,见另一篇博客:libfdk_aac音频采样数和编码字节数注意

哎呦喂ヾ(✿゚▽゚)ノ~路长馆小,雪轻帘薄,酒热乎,这位爷~您ヾ(✿゚▽゚)ノ~ 里面坐~
本公众号专注分享C++,ffmpeg,opencv等相关音视频知识
webrtc,udp,tcp,rtsp,rtmp,srt/nginx+rtmp等流媒体协议和服务器
同时也会有大厂音视频技术专家不定期直播分享…
国人开发流媒体srs服务器,及yangrtc(国人版的webrtc)协议新动向
偶尔分享下程序员梦呓碎碎念(๑•॒̀ ູ॒•́๑)啦啦啦
目前刚刚开通,接受读者的优质投稿…
鉴于国内音视频圈子小,起步晚,以致分享少,门槛高,特开通分享,一起扇动这阵风吧!
微信扫描下方二维码,关注公众号,赶快进入音视频开发者社区吧!

avcodec_send_frame和avcodec_receive_packet_第1张图片 avcodec_send_frame和avcodec_receive_packet_第2张图片 avcodec_send_frame和avcodec_receive_packet_第3张图片

附录:
libavcodec\encode.c

int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
{
    av_packet_unref(avpkt);
 
    if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec))
        return AVERROR(EINVAL);
 
    if (avctx->codec->receive_packet) {
        if (avctx->internal->draining && !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY))
            return AVERROR_EOF;
        return avctx->codec->receive_packet(avctx, avpkt);
    }
 
    // Emulation via old API.
 
    if (!avctx->internal->buffer_pkt_valid) {
        int got_packet;
        int ret;
        if (!avctx->internal->draining)
            return AVERROR(EAGAIN);
        ret = do_encode(avctx, NULL, &got_packet);
        if (ret < 0)
            return ret;
        if (ret >= 0 && !got_packet)
            return AVERROR_EOF;
    }
 
    av_packet_move_ref(avpkt, avctx->internal->buffer_pkt);
    avctx->internal->buffer_pkt_valid = 0;
    return 0;
}
void av_packet_unref(AVPacket *pkt)
{
    av_packet_free_side_data(pkt);
    av_buffer_unref(&pkt->buf);
    av_init_packet(pkt);
    pkt->data = NULL;
    pkt->size = 0;
}

你可能感兴趣的:(ffmpeg,ffmpeg)