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二次开发及直播美颜原理及应用--梅会东--清华大学出版社
===================================