avcodec_send_frame()与avcodec_receive_packet()使用及AVPacket与AVFrame使用

1,593 阅读1分钟

技术点:

  • 大循环编码逻辑
  • 小循环(函数内),数据的编码细节
  • 哪些操作会造成AVPacket的引用计数增加?要考虑av_packet_unref()
    • avcodec_receive_packet()会
    • av_read_frame()亦会
  • encode()第二参传入nullptr会有什么后果?积极后果,flush下缓冲,剩余的帧压缩成AVPacket
static void encode(AVCodecContext* enc_ctx,
                   AVFrame* p_frame,
                   AVPacket* p_pkt,
                   FILE* out_file)
{
    int ret = 0;
    if(p_frame)
        printf("send frame to encoder, pts = %lld", p_frame->pts);  
    ret = avcodec_send_frame(enc_ctx, p_frame);
    if(ret < 0)
    {
        printf("Error, Failed to send frame for encoding! \n");
        exit(1);
    }
    
    while(ret >= 0)
    {
        // 会增加AVPacket引用计数
        ret = avcodec_receive_packet(enc_ctx, p_pkt);
        if(AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if(ret < 0)       
        {
            printf("Error, Failed to encode! \n");
            exit(1);
        }
        fwrite(p_pkt->data, 1, p_pkt->size, out_file);
        av_packet_unref(p_pkt);
    }
}
  • 下面演示是从笔记本摄像头拿到原始数据(AVPacket NV12)
// 如下,两种方式创建一个AVPacket
// 栈上分配AVPacket,无需初始化,该对象可直接用,这个用来承接摄像头的数据  
AVPacket pkt;  
// 堆上分配AVPacket,供encode()函数使用的用来保存缓=缓冲的对象,这里直接拿到其指针
AVPacket* p_buf_pkt = nullptr;
p_buf_pkt = av_packet_alloc(); 

uint base = 0;
// av_read_frame()会增加AVPacket引用计数,从文件或视频流中读入一帧AVPacket  
while(ret = av_read_frame(fmt_ctx, &pkt) == 0)
{
    // AVPacket数据经过转换,成了YUV420P的AVFrame    
    // 没有其他设置AVFrame的操作,只设置pts时间戳
    p_frame->pts = base;
    ++base;
    encode(enc_ctx, p_frame, p_buf_pkt, out_file);
    
    // 释放AVPacket  
    av_packet_unref(&pkt)
}
// 把最后的缓存刷新出来  
encode(enc_ctx, nullptr, p_buf_pkt, out_file);