当前位置: 首页 > news >正文

FFmpeg H264编码

FFmpeg进行h264的编码流程:

1、通过编码器名,查找编码器(h264编码器名字是libx264)

2、设置编码器的参数(比如码率、分辨率、帧率、时间基等)。

3、打开编码器avcodec_open2()。

4、对帧数据进行编码avcodec_send_frame()(将帧送到编码器)/avcodec_receive_packet()(从编码器获取编码后的数据包),编码完后写入文件。(在实际使用中帧数据来源于解码后的元数据或来源于摄像头,本文示例的帧数据时人工添加了模拟数据。)
在这里插入图片描述

相关API

文件操作
相关API

源码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libavcodec/avcodec.h>

#include <libavutil/opt.h>
#include <libavutil/imgutils.h>

// 对每一帧进行编码
static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
                   FILE *outfile)
{
    int ret;
    /* send the frame to the encoder */
    if (frame)
        printf("Send frame %3"PRId64"\n", frame->pts);
    ret = avcodec_send_frame(enc_ctx, frame);
    if (ret < 0) {
        fprintf(stderr, "Error sending a frame for encoding\n");
        exit(1);
    }
    while (ret >= 0) {
        ret = avcodec_receive_packet(enc_ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during encoding\n");
            exit(1);
        }
        printf("Write packet %3"PRId64" (size=%5d)\n", pkt->pts, pkt->size);
        fwrite(pkt->data, 1, pkt->size, outfile);
        av_packet_unref(pkt);
    }
}

int encode_video(const char *filename, const char *codec_name)
{
    
    //编码器
    const AVCodec *codec;
    //编码器上下文
    AVCodecContext *c= NULL;
    //got_output 用于标记一帧是否压缩成功
    int i, ret, x, y, got_output;
    FILE *f;
    //存放解码后的原始帧(未压缩的数据)
    AVFrame *frame;
    AVPacket pkt;
    uint8_t endcode[] = { 0, 0, 1, 0xb7 };

    //将所有需要的编解码,多媒体格式,以及网络,都注册要程序里
    avcodec_register_all();

    /* find the mpeg1video encoder */
    //通过编解码名找到对应的编解码器
    codec = avcodec_find_encoder_by_name(codec_name);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    // 根据编码器,创建相对应的编码器上下文
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }
    //设置相关参数

    /* put sample parameters */
    //码率,400kb
    c->bit_rate = 400000;
    /* resolution must be a multiple of two */
    c->width = 352;
    c->height = 288;
    /* frames per second */
    //时间基,每一秒25帧,每一刻度25分之1(时间基根据帧率而变化)
    c->time_base = (AVRational){1, 25};
    //帧率
    c->framerate = (AVRational){25, 1};

    /* emit one intra frame every ten frames
     * check frame pict_type before passing frame
     * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
     * then gop_size is ignored and the output of encoder
     * will always be I frame irrespective to gop_size
     */
    //多少帧产生一组关键帧
    c->gop_size = 10;
    //b帧,参考帧
    c->max_b_frames = 1;
    //编码的原始数据的YUV格式
    c->pix_fmt = AV_PIX_FMT_YUV420P;
    
    //如果编码器id 是 h264
    if (codec->id == AV_CODEC_ID_H264)
    // preset表示采用一个预先设定好的h264参数集,级别是slow,slow表示压缩速度是慢的,慢的可以保证视频质量,用快的会降低视频质量
        av_opt_set(c->priv_data, "preset", "slow", 0);

    /* open it */
    //打开编码器
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    //打开输入文件
    f = fopen(filename, "wb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }
    
    //初始化帧并设置帧的YUV格式和分辨率
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }
    frame->format = c->pix_fmt;
    frame->width  = c->width;
    frame->height = c->height;

    //为音频或视频数据分配新的缓冲区
    ret = av_frame_get_buffer(frame, 32);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate the video frame data\n");
        exit(1);
    }

    /* encode 1 second of video */
    // 这里是人工添加数据模拟生成1秒钟(25帧)的视频(真实应用中是从摄像头获取的原始数据,摄像头拿到数据后会传给编码器,然后编码器进行编码形成一帧帧数据。)
    for (i = 0; i < 25; i++) {
        //初始化packet
        av_init_packet(&pkt);
        pkt.data = NULL;    // packet data will be allocated by the encoder
        pkt.size = 0;
        // 强制输出写入文件
        fflush(stdout);

        /* make sure the frame data is writable */
        //确保帧被写入
        ret = av_frame_make_writable(frame);
        if (ret < 0)
            exit(1);

        // 下面2个循环是人工往frame里面添的数据
        /* prepare a dummy image */
        /* Y */
        for (y = 0; y < c->height; y++) {
            for (x = 0; x < c->width; x++) {
                frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
            }
        }

        /* Cb and Cr */
        for (y = 0; y < c->height/2; y++) {
            for (x = 0; x < c->width/2; x++) {
                frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
            }
        }

        frame->pts = i;

        /* encode the image */
        //开始编码
        //c : 编码器上下文
        //&pkt : 输出压缩后的数据
        //frame :输入未压缩数据
        //&got_output :判断是否压缩成功
         /* send the frame to the encoder */
        // 进行编码压缩
        encode(c,frame,&pkt,f);
    }
    // 进行编码压缩
    encode(c,frame,&pkt,f);
    /* add sequence end code to have a real MPEG file */
    fwrite(endcode, 1, sizeof(endcode), f);
    fclose(f);

    avcodec_free_context(&c);
    av_frame_free(&frame);

    return 0;
}

转自:FFmpeg H264编码_ty潇潇暮雨的博客-CSDN博客_ffmpeg h264 编码 

相关文章:

  • 图像锐化是什么
  • FFmpeg原始帧处理-滤镜API用法详解
  • ffmpeg中的rtbufsize
  • 智能指针的实现
  • 什么是构造函数和析构函数?
  • CD-ROM
  • 光盘文件格式-udf、iso9660、Joliet、Romeo
  • 刻录光盘的程序步骤
  • ISO文件
  • DVD-数字通用光盘
  • VCD-影音光碟
  • CD-CompactDisk
  • 光盘的标准与格式
  • Hash-散列函数
  • 索引(index)是什么
  • hexo+github搭建个人博客
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • 【刷算法】从上往下打印二叉树
  • echarts花样作死的坑
  • HTTP请求重发
  • JavaScript实现分页效果
  • Java深入 - 深入理解Java集合
  • Js基础——数据类型之Null和Undefined
  • js数组之filter
  • MySQL主从复制读写分离及奇怪的问题
  • Python学习之路16-使用API
  • Spring Boot MyBatis配置多种数据库
  • 安装python包到指定虚拟环境
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 仿天猫超市收藏抛物线动画工具库
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 前端面试题总结
  • 学习使用ExpressJS 4.0中的新Router
  • ​flutter 代码混淆
  • ###C语言程序设计-----C语言学习(6)#
  • #if 1...#endif
  • (12)Linux 常见的三种进程状态
  • (NSDate) 时间 (time )比较
  • (多级缓存)缓存同步
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (理论篇)httpmoudle和httphandler一览
  • (一)Thymeleaf用法——Thymeleaf简介
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)Linux下编译安装log4cxx
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • ../depcomp: line 571: exec: g++: not found
  • .NET Framework 服务实现监控可观测性最佳实践
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • .pings勒索病毒的威胁:如何应对.pings勒索病毒的突袭?
  • :=
  • @RequestMapping用法详解
  • [ C++ ] STL_stack(栈)queue(队列)使用及其重要接口模拟实现
  • [ web基础篇 ] Burp Suite 爆破 Basic 认证密码
  • [17]JAVAEE-HTTP协议