热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

FFmpeg入门详解之123:代码封装:摄像头h264/5编码并存储

7.代码封装:摄像头h2645编码并存储源码工程:S26_Test3H2645编码案例实战AVPacket,AVFrame解码:

7.代码封装:摄像头h264/5编码并存储

源码工程:S26_Test3

H264/5编码案例实战

AVPacket,   AVFrame

解码:

While(av_read_frame(..))

 avcodec_send_packet

 avcodec_receive_frame

编码:

While(read_camera(..))

 avcodec_send_frame

 avcodec_receive_packet

源码参考:

extern "C"
{
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
};
//encode one frame
static int encode(AVCodecContext *pCodecCtx, AVFrame *pFrame, AVPacket *pPkt, FILE *out_file) {int got_packet = 0;int ret = avcodec_send_frame(pCodecCtx, pFrame);if (ret < 0) {//failed to send frame for encodingreturn -1;}while (!ret) {ret = avcodec_receive_packet(pCodecCtx, pPkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return 0;}else if (ret < 0) {//error during encodingreturn -1;}printf("Write frame %d, size=%d\n", pPkt->pts, pPkt->size);fwrite(pPkt->data, 1, pPkt->size, out_file);av_packet_unref(pPkt);}return 0;
}
int main (int argc, char* argv[])
{AVFormatContext *pFormatCtx;AVOutputFormat *pOutputFmt;AVStream *pStream;AVCodecContext *pCodecCtx;AVCodec *pCodec;AVPacket *pkt;AVFrame *pFrame;FILE *in_file = NULL, *out_file = NULL;in_file = fopen("yuvtest1-352x288-yuv420p.yuv", "rb");if (in_file == NULL){printf("cannot open in file\n");return -1;}int in_w = 352, in_h = 288;int nFrameNum = 100;out_file = fopen("out.h265", "wb");if (out_file == NULL) {printf("cannot create out file\n");return -1;}uint8_t* pFrameBuf = NULL;int frame_buf_size = 0;int y_size = 0;int nEncodedFrameCount = 0;uint8_t endcode[] = { 0, 0, 1, 0xb7 };/av_register_all();pFormatCtx = avformat_alloc_context();pOutputFmt = av_guess_format(NULL, "test.h265", NULL);pFormatCtx->oformat = pOutputFmt;//除了以下方法,另外还可以使用avcodec_find_encoder_by_name()来获取AVCodecpCodec = avcodec_find_encoder(pOutputFmt->video_codec);if (!pCodec) {//cannot find encoderreturn -1;}pCodecCtx = avcodec_alloc_context3(pCodec);if (!pCodecCtx) {//failed get AVCodecContextreturn -1;}pkt = av_packet_alloc();if (!pkt){return -1;}//pCodecCtx = pStream->codec;pCodecCtx->codec_id = pOutputFmt->video_codec;pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;pCodecCtx->width = in_w;pCodecCtx->height = in_h;pCodecCtx->time_base.num = 1;pCodecCtx->time_base.den = 25;//pCodecCtx->time_base = (AVRational){ 1, 25 };pCodecCtx->bit_rate = 400000;   //pCodecCtx->gop_size = 250;      ////pCodecCtx->framerate = (AVRational){ 25, 1 };//H264//pCodecCtx->me_range = 16;     ////pCodecCtx->max_qdiff = 4;     ////pCodecCtx->qcompress = 0.6;   //pCodecCtx->qmin = 10;           //pCodecCtx->qmax = 51;           ////Optional ParampCodecCtx->max_b_frames = 3;//Set OptionAVDictionary *param = NULL;//H.264if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {///av_dict_set(¶m, "profile", "main", 0);av_dict_set(¶m, "preset", "slow", 0);av_dict_set(¶m, "tune", "zerolatency", 0);}//H.265if (pCodecCtx->codec_id == AV_CODEC_ID_HEVC) {//AV_CODEC_ID_HEVC,AV_CODEC_ID_H265av_dict_set(¶m, "profile", "main", 0);av_dict_set(¶m, "preset", "ultrafast", 0);/ note uncompatilable://av_dict_set(¶m, "tune", "zero-latency", 0);}if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0) {//failed to open codecreturn -1;}pFrame = av_frame_alloc();if (!pFrame) {fprintf(stderr, "Could not allocate the video frame data\n");return -1;}pFrame->format = pCodecCtx->pix_fmt;pFrame->width = pCodecCtx->width;pFrame->height = pCodecCtx->height;int ret = av_frame_get_buffer(pFrame, 32);if (ret < 0) {fprintf(stderr, "Could not allocate the video frame data\n");return -1;}frame_buf_size = av_image_get_buffer_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1);pFrameBuf = (uint8_t*)av_malloc(frame_buf_size);av_image_fill_arrays(pFrame->data, pFrame->linesize,pFrameBuf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1);y_size = pCodecCtx->width * pCodecCtx->height;for (int i = 0; i < nFrameNum; i++) {//Read raw YUV dataif (fread(pFrameBuf, 1, y_size * 3 / 2, in_file) <= 0) {//failed to read raw YUV datareturn -1;}else if (feof(in_file)) {break;}ret = av_frame_make_writable(pFrame);if (ret < 0) {return -1;}pFrame->data[0] = pFrameBuf;                //YpFrame->data[1] = pFrameBuf + y_size;       //UpFrame->data[2] = pFrameBuf + y_size*5/4;   //V//PTSpFrame->pts = i;//encodeencode(pCodecCtx, pFrame, pkt, out_file);}//flush the encoderencode(pCodecCtx, NULL, pkt, out_file);//add sequence end code to have a real MPEG filefwrite(endcode, 1, sizeof(endcode), out_file);avcodec_free_context(&pCodecCtx);avformat_free_context(pFormatCtx);av_frame_free(&pFrame);av_packet_free(&pkt);av_free(pFrameBuf);if (out_file)fclose(out_file);if (in_file)fclose(in_file);system("pause");return 0;
}


视音频pts计算

Pts,dts

1、概述

计算pts的时候都是转换一下时间基AV_TIMEBASE,

如果这个pts没有原来的pts做参考如何计算其值?

咋办?

time base : 时间基,也即是时间单位,举个栗子, ctx->time_base = {1, 1000}, 即,把一秒分为 1000个单位,每个单位 1/1000 秒。

fps : 帧每秒,即 每秒显示多少帧,举个栗子,25fps,每秒显示25帧,每帧持续显示 1/25 = 0.04 秒 = 40 毫秒。

sample_rate : 这个是对于 音频来讲的,即采样率,常见的比如 44.1 KHz, 即 每秒采样 44100 次。

我们知道,对于视频,我们可以根据 fps得到 每一帧的pts,即:

根据时间基,一秒钟 1000 个单位,这1000个单位分给 25 帧去使用,所以每个帧占用了 1000/25的时间,

所以,如果第一帧的pts 应该是 1000/25 *  1/1000 = 40 毫秒,即 占用的时间单位个数 * 每个时间单位的时间

同理,第二帧就是 : 40 + (1000/25 *  1/1000) = 80 毫秒,第N帧就是 :N * 40 毫秒 。

2、视频pts

视频比较好理解,就是每帧递增。

假如fps是25帧的,时间基为fps的倒数1/25,那么pts递增即可。

如下:

第一帧:pts=0

第二帧:pts=1

第三帧:pts=2

.

.

.

第n帧:pts = n - 1;

。。。以此类推

计算公式为:第n帧的pts = n * ((1 / timbase)/ fps);

3、音频pts

音频相对来说更难理解一些,因为音频的一个packet不止一帧,所以一秒到底有多少个packet就不知道,就别说如何计算pts了。

假设音频一秒有num_pkt个packet,那么这个num_pkt到底是多少?

这就需要从音频基础开始说起,我们知道音频有个采样率,就是一秒钟采用多少次,很多音频都是44100的采样率,也有8k的,那么这个采样率和num_pkt有什么关系呢?

分析发现在AVFrame中有一个比较重要的字段叫做nb_samples,这个字段名为采样数,此字段可以结合音频数据格式计算这个frame->data有多大,其实这个字段联合采样率还可以计算音频一秒有多少个packet。

计算公式如下:

num_pkt = 采样率 / nb_samples;

这样我们就知道了音频每秒的包数目(可以见到理解为音频帧),有了此数据计算pts就和视频一模一样了,

计算公式如下:

第n个包的pts = n * ((1 / timbase)/ num_pkt);

很多(不是所有的)音频时间基和采样率成倒数,那么根据公式(一般情况下)我们的音频pts就可以很简单的以nb_samples递增了,如下:

第一个包:pts = 0 * nb_samples;

第二个包:pts = 1 * nb_samples;

第三个包:pts = 2 * nb_samples;

.

.

.

第n个包:pts = (n - 1) * nb_samples;

注:以上说的timebase为AVStream里的timebase。


界面设计


摄像头封装

QtFFmpegCamera

把SDL的部分去掉

边采集边预览边录制的代码封装

T3FFmpegH2645Encoder

T3FFmpegH2645Encoder2:课程录制

独立线程

QtCameraThread

大家好,我的第一本书正式出版了,可以在京东各大店铺抢购哦。

《FFmpeg入门详解--音视频原理及应用:梅会东:清华大学出版社》

京东自营链接:https://item.jd.com/13377793.html
京东其它链接:https://search.jd.com/Search?keyword=FFmpeg%E5%85%A5%E9%97%A8%E8%AF%A6%E8%A7%A3--%E9%9F%B3%E8%A7%86%E9%A2%91%E5%8E%9F%E7%90%86%E5%8F%8A%E5%BA%94%E7%94%A8&enc=utf-8&suggest=1.his.0.0&wq=&pvid=24e80535073b4e1f98e30a3e6963fe81
 

 

出书过程非常艰辛,来回校正了好几遍,后续还有FFmpeg系列的其它图书。

第一本:FFmpeg入门详解--音视频原理及应用--梅会东--清华大学出版社

第二本:FFmpeg入门详解--流媒体直播原理及应用--梅会东--清华大学出版社

第三本:FFmpeg入门详解--命令行及音视频特效原理及应用--梅会东--清华大学出版社

第四本:FFmpeg入门详解--SDK二次开发及直播美颜原理及应用--梅会东--清华大学出版社

===================================


推荐阅读
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文介绍了基于c语言的mcs51单片机定时器计数器的应用教程,包括定时器的设置和计数方法,以及中断函数的使用。同时介绍了定时器应用的举例,包括定时器中断函数的编写和频率值的计算方法。主函数中设置了T0模式和T1计数的初值,并开启了T0和T1的中断,最后启动了CPU中断。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
author-avatar
哈哈哈阿笑
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有