基本步骤

  • 打开编码器
    在打开编码器时,要设置一些参数,例如具体使用的哪一个编码器,编H264时要使用libx264,编H265时要使用libx265。
    还需要设置GOP,码流大小、分辨率宽和高也需要设置。

  • 转换NV12到YUV420P
    NV12是mac下采集视频的格式,NV12是一个包裹的格式,分两层,第一层是Y,第二层是UVUV进行交替存储,yuv420P是编码器libx264要求的格式。

    转换时可以采用ffmpeg自身的swscale转换,还可以用google开发的libyuv,还可以自己手工写一个进行转换。

  • 准备编码数据AVFrame

    因为从设备上获取的数据是AVPacket,是一个输出的数据,不能作为编码器的输入,所以要将AVPacket中的数据导入到AVFrame中,因为AVFrame是输入数据。

  • H264编码

    前面的过程都处理完后,最后通过一个循环,就可以将数据进行编码了,拿到编码后的AVPacket存储进文件中,就可以通过播放器进行播放了。

示例代码

//
//  video_encode.c
//  FFmpegDemo
//
//  Created by qqy on 2021/7/20.
//#include "video_encode.h"
#include "time.h"#define V_WIDTH 640
#define V_HETGHT 480static int rec_status = 0;void set_status(int status){rec_status = status;
}static AVFormatContext *open_dev(void){int ret = 0;char errors[1024] = {0, };//ctxAVFormatContext *fmt_ctx = NULL;AVDictionary *options = NULL;//[[video device]:[audio device]]//0: 机器的摄像头//1: 桌面char *devicename = "0:";//register audio deviceavdevice_register_all();//get formatconst AVInputFormat *iformat = av_find_input_format("avfoundation");av_dict_set(&options, "video_size", "640x480", 0);av_dict_set(&options, "framerate", "30", 0);av_dict_set(&options, "pixel_format", "nv12", 0);//open deviceif((ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options)) < 0){av_strerror(ret, errors, 1024);fprintf(stderr, "Failed to open video device, [%d] %s\n", ret, errors);return NULL;}av_dump_format(fmt_ctx, 0, devicename, 0);return fmt_ctx;
}/**
* @brief
* @param[in] width
* param[in] height
* param[out] enc_ctx
*/
static void open_encoder(int width, int height, AVCodecContext **enc_ctx){int ret = 0;const AVCodec *codec = NULL;codec = avcodec_find_encoder_by_name("libx264");if(!codec){printf("Codec libx264 not found\n");exit(1);}*enc_ctx = avcodec_alloc_context3(codec);//第三个版本if(!(*enc_ctx)){printf("Could not allocate video codec context!\n");exit(1);}//SPS/PPS(*enc_ctx)->profile = FF_PROFILE_H264_HIGH_444;(*enc_ctx)->level = 50; //表示level是5.0//设置分辨率(*enc_ctx)->width = width;     //640(*enc_ctx)->height = height; //480//GOP(*enc_ctx)->gop_size = 30; //设置很小时,I帧就会很多,码流就会很大,设置很小时,I帧就会很少,一旦丢包就会出现异常,等到下一个I帧时才会修正。(*enc_ctx)->keyint_min = 25;//最小I帧间隔,也就是I帧最小25帧就可以插入I帧 可选项,不设置也可以//设置B帧数量(*enc_ctx)->max_b_frames = 3; //可选项,不设置也可以(*enc_ctx)->has_b_frames = 1; // 可选项,不设置也可以//参考帧数量(*enc_ctx)->refs = 3;//设置参考帧数量  可选项,不设置也可以//设置输入YUV格式 设置像素格式(*enc_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;//设置码率(*enc_ctx)->bit_rate = 1000000; // 1000kbps//设置帧率(*enc_ctx)->time_base = (AVRational){1, 25}; //帧与帧之间的间隔是time_base(*enc_ctx)->framerate = (AVRational){25, 1}; //帧率,每秒25帧ret = avcodec_open2((*enc_ctx), codec, NULL);if(ret < 0){printf("Could not open codec:%s!\n", av_err2str(ret));exit(1);}
}static AVFrame *create_frame(int width, int height){int ret = 0;AVFrame *frame = NULL;frame = av_frame_alloc();if(!frame){printf("Error, No Memory!\n");goto __ERROR;}//设置参数frame->width = width;frame->height = height;frame->format = AV_PIX_FMT_YUV420P;//alloc inner memoryret = av_frame_get_buffer(frame, 32); //按32位对齐if(ret < 0){printf("Error, Failed to alloc buffer for frame\n");goto __ERROR;}return frame;__ERROR:if(frame){av_frame_free(&frame);}return NULL;
}static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *newpkt, FILE *outfile){int ret = 0;if(frame){printf("send frame to encoder pts=%lld\n", frame->pts);}//送原始数据给编码器进行编码ret = avcodec_send_frame(enc_ctx, frame);if(ret < 0){printf("Error, Failed to send a frame for encoding\n");exit(1);}//从编码器获取编码好的数据while(ret >= 0){ret = avcodec_receive_packet(enc_ctx, newpkt); //编码器并不是一帧一帧的输出,可能是送入好几个都不输出,然后送入一个出来好几个,所以要用循环进行获取//如果编码器数据不足时,会返回EAGAIN 或者到数据尾部时会返回AVERROR_EOFif(ret == AVERROR(EAGAIN)||ret == AVERROR_EOF){return;}else if(ret < 0){printf("Error, Failled to encode!\n");exit(1);}fwrite(newpkt->data, 1, newpkt->size, outfile);av_packet_unref(newpkt);//减少packet的引用计数}}void rec_video(void){int ret = 0;//packetAVPacket pkt;AVFormatContext *fmt_ctx = NULL;AVCodecContext *enc_ctx = NULL;//set log levelav_log_set_level(AV_LOG_DEBUG);//start recordrec_status = 1000;//create filechar *yuvout = "/Users/qqy/workspace/VedioFiles/video.yuv";char *out = "/Users/qqy/workspace/VedioFiles/video.h264";FILE *yuvoutfile = fopen(yuvout, "wb+");FILE *outfile = fopen(out, "wb+");//打开设备fmt_ctx = open_dev();if(!fmt_ctx){printf("Error, Failed to open device!\n");goto __ERROR;}//打开编码器open_encoder(V_WIDTH, V_HETGHT, &enc_ctx);//创建 AVFrameAVFrame *frame = create_frame(V_WIDTH, V_HETGHT);//创建编码输出packetAVPacket *newpkt = av_packet_alloc();if(!newpkt){printf("Error, Failed to alloc avpacket!\n");goto __ERROR;}int base = 0;//read data from device
//    while((ret = av_read_frame(fmt_ctx, &pkt)) == 0 && (rec_status > 0)) {while ((ret = av_read_frame(fmt_ctx, &pkt)) == 0 || ret == -35) {if(!rec_status){break;}if(ret == -35){av_usleep(100);continue;}av_log(NULL, AV_LOG_INFO, "packet size is %d(%p)\n", pkt.size, pkt.data);// (宽 x 高) x(yuv420=1.5 / yuv422=2 / yuv444=3)
//        fwrite(pkt.data, 1, 460800, yuvoutfile); //直接将采集的数据格式NV12存储下来//此时直接从设备获取到的数据格式为 YYYYYYYYUVUV      NV12的数据格式//目标要转成的YUV420P数据格式为  YYYYYYYYUUVV      YUV420的数据格式memcpy(frame->data[0], pkt.data, V_WIDTH*V_HETGHT); //640x480=307200 Y数据//307200之后是UV数据for(int i = 0; i < V_WIDTH*V_HETGHT/4; i++){frame->data[1][i] = pkt.data[V_WIDTH*V_HETGHT+i*2];frame->data[2][i] = pkt.data[V_WIDTH*V_HETGHT+i*2+1];}//虽然已经实现了NV12转为YUV420,但是效率不算太高,可以尝试使用汇编指令优化来进行优化。这里暂时理解就可以,后面可以使用libyuv实现转换fwrite(frame->data[0], 1, V_WIDTH*V_HETGHT, yuvoutfile);fwrite(frame->data[1], 1, V_WIDTH*V_HETGHT/4, yuvoutfile);fwrite(frame->data[2], 1, V_WIDTH*V_HETGHT/4, yuvoutfile);printf("pkt, pkt.size=%d!\n", pkt.size);fflush(yuvoutfile);frame->pts = base++;encode(enc_ctx, frame, newpkt, outfile);av_packet_unref(&pkt); //release pktrec_status--;}encode(enc_ctx, NULL, newpkt, outfile); //这一步如果不做,可能送进去最后一帧时,编码器没有将全部数据输出,这个时候就有可能发生丢帧的现象__ERROR:if(yuvoutfile){fclose(yuvoutfile);}if(fmt_ctx){avformat_close_input(&fmt_ctx);}av_log(NULL, AV_LOG_DEBUG, "finish!\n");return;}

视频编码解码(H264编码实战)相关推荐

  1. 如何使用ffmpeg为Mac进行视频硬解码/硬编码(在Qt环境)

    如何使用ffmpeg为Mac进行视频硬解码/硬编码(在Qt环境) 科普 前期准备 安装ffmpeg 将ffmpeg的库文件添加到Qt项目的.pro文件中 在源文件用引入头文件 第一步:先查看本机支持哪 ...

  2. 音视频开发入门(3):视频编解码之编码基础

    前言 即时通讯应用中的实时音视频技术,几乎是IM开发中的最后一道高墙.原因在于:实时音视频技术 = 音视频处理技术 + 网络传输技术 的横向技术应用集合体,而公共互联网不是为了实时通信设计的.有关实时 ...

  3. base64 加密有空格 换行_[编码解码] Base64 编码换行和+号遍空格的处理

    Android自身带有Base64加密与解密的包,可以方便地加密密码方便传输. String base64Token = Base64.encodeToString(token.trim().getB ...

  4. 音视频开发系列-H264编码原理

    H264简介 来自百度百科的介绍: H.264是国际标准化组织(ISO)和国际电信联盟(ITU)共同提出的继MPEG4之后的新一代数字视频压缩格式. H.264是ITU-T以H.26x系列为名称命名的 ...

  5. opencv录制视频 python_Python-OpenCV录制H264编码的MP4视频

    前言 因最近项目需求涉及计算机视觉相关内容,需要实现在Python录制视频,并且录制完成后可在浏览器前端中进行视频回放的功能:特写下此篇文章以记录整体实现过程. 2019-08-02 更新 之前一直在 ...

  6. 采集音频和摄像头视频并实时H264编码及AAC编码

    0. 前言 我在前两篇文章中写了DirectShow捕获音视频然后生成avi,再进行264编码的方法.那种方法有一些局限性,不适合实时性质的应用,如:视频会议.视频聊天.视频监控等.本文所使用的技术, ...

  7. 采集音频和摄像头视频并实时H264编码及AAC编码[转]

    0. 前言 我在前两篇文章中写了DirectShow捕获音视频然后生成avi,再进行264编码的方法.那种方法有一些局限性,不适合实时性质的应用,如:视频会议.视频聊天.视频监控等.本文所使用的技术, ...

  8. 即时通讯音视频开发(三):视频编解码之编码基础

    前言 即时通讯应用中的实时音视频技术,几乎是IM开发中的最后一道高墙.原因在于:实时音视频技术 = 音视频处理技术 + 网络传输技术 的横向技术应用集合体,而公共互联网不是为了实时通信设计的. 系列文 ...

  9. V4L2视频采集与H264编码2—v4l2采集YUV数据

    在上一篇中因为是在PC机上使用的USB摄像头只能支持GPEG image格式,但是H264编码需要使用YUV数据,所以我找了个ARM开发板来做测试.本以为代码从PC机移植到开发板是很简单的一个事,谁知 ...

最新文章

  1. 如何在Ubuntu 20.04上设置Python虚拟环境
  2. mongodb高可用性架构---Replica Set
  3. [收集]美女与野兽——萨尔和吉安娜的绯闻
  4. a1在c语言里代指什么意思,A1考试宝典
  5. 物联网架构----双机热备Keepalived了解
  6. C++ Primer 第二章 学习笔记及习题答案
  7. POJ3991 HDU3351 UVALive4733 Seinfeld【水题】
  8. 计算机犀牛建人体模型步骤,Clayoo加Rhino如何建模卡通人物2
  9. iMX8MPlus和iMX8QM机器学习框架eIQ性能对比
  10. 【解决方案】校园明厨亮灶监控系统实施方案
  11. 美国克莱姆森大学计算机专业排名,美国西北大学计算机专业排名怎么样?
  12. python往word文档中写入表格、段落、标题、图片...(超级全)
  13. 71、不同灭火器的适用范围
  14. poco mysql 库_poco网络库
  15. 界门纲目科属种的英文——学生物的基础
  16. Knowledge Reasoning 复习
  17. 「数据运营」理解DataOps运营
  18. 12、Zabbix 结合Grafana
  19. html wap广告代码,手机wap底部悬浮JS广告代码
  20. 时间中常用时区的英文缩写对照中文释义

热门文章

  1. Minicom使用介绍
  2. Android数据库开发——SQLite
  3. html页面下的阴影,html5/css3文本阴影(text-shadow)详解及示例
  4. 用CrashDump定位应用错误
  5. [Quant][Note] Empirical Asset Pricing via Machine Learning
  6. PHP在线考试系统4.0版本源码电脑+手机端
  7. 浪子回头之邱关源第五版P76_3_10回路电流法multisim方正及matlab解法
  8. 物联网(X):QCustomPlot
  9. ShellExecuteEx如何关闭打开的pdf阅读器
  10. AutoJs实战教程---京东极速版