纯文本网站,企业名录怎么导出,南通网站设计公司,企业文化案例Qt中ffmpeg API存储和显示摄像头视频的功能需要之前写的视频ffmpegAPI的视频播放的流程。
代码源码位置#xff1a;https://download.csdn.net/download/qq_43812868/88157743?spm1001.2014.3001.5503
一、存储和显示摄像头的视频的流程 这是读取打开视频文件的流程#x…Qt中ffmpeg API存储和显示摄像头视频的功能需要之前写的视频ffmpegAPI的视频播放的流程。
代码源码位置https://download.csdn.net/download/qq_43812868/88157743?spm1001.2014.3001.5503
一、存储和显示摄像头的视频的流程 这是读取打开视频文件的流程视频文件在avformat_open_input参数中最终将数据传递到av_frame_alloc创建的AVFrame。 这个是存储的流程将第一部分的AVFrame数据传递进来之后通过av_interleaved_write_frame写入到媒体文件。
二、相关结构体介绍
在了解使用api之前还需要先了解一下ffmpeg中的相关结构体在了解了这些结构体之后可以更容易的理解代码。
AVFormatContext此结构体存储音视频封装格式中包含的信息并且这个结构体是贯穿整个播放流程的。在这个结构体中主要包含AVInputFormatAVOutputFormat、AVStream等。
struct AVInputFormat *iformat; // 输入数据的封装格式
AVIOContext *pb; // 输入数据的缓存
unsigned int nb_streams; // 音视频流的个数
AVStream **streams; // 音视频流
char filename[1024]; // 文件名
int64_t duration; // 时长单位微秒us转换为秒需要除以1000000
int bit_rate; // 比特率单位bps转换为kbps需要除以1000
AVDictionary *metadata; // 元数据**AVCodecContext**是一个描述编解码器上下文的结构体包含了众多编解码器需要的参数信息。
enum AVMediaType codec_type; // 编解码器的类型视频音频...
struct AVCodec *codec; // 采用的解码器AVCodecH.264,MPEG2...
int bit_rate; // 平均比特率
uint8_t *extradata; int extradata_size; // 针对特定编码器包含的附加信息例如对于H.264解码器来说存储SPSPPS等
AVRational time_base; // 根据该参数可以把PTS转化为实际的时间单位为秒s
int width, height; // 如果是视频的话代表宽和高
int refs; // 运动估计参考帧的个数H.264的话会有多帧MPEG2这类的一般就没有了
int sample_rate; // 采样率音频
int channels; // 声道数音频
enum AVSampleFormat sample_fmt; // 采样格式
int profile; // 型H.264里面就有其他编码标准应该也有
int level; // 级和profile差不太多AVCodec是存储编码器信息的结构体。
const char *name; // 编解码器的名字的简称
const char *long_name; // 编解码器名字的全称
enum AVMediaType type; // 指明了类型是视频音频还是字幕
enum AVCodecID id; // ID不重复
const AVRational *supported_framerates; // 支持的帧率仅视频
const enum AVPixelFormat *pix_fmts; // 支持的像素格式仅视频,如RGB24、YUV420P等。
const int *supported_samplerates; // 支持的采样率仅音频
const enum AVSampleFormat *sample_fmts; // 支持的采样格式仅音频
const uint64_t *channel_layouts; // 支持的声道数仅音频
int priv_data_size; // 私有数据的大小AVFrame该结构描述解码的原始的音频或视频数据。AVFrame必须使用av_frame_alloc进行分配。请注意这只是分配AVFrame本身必须管理数据的缓冲区通过其他方式。AVFrame必须使用av_frame_free释放。 AVPacket是存储压缩编码数据相关信息的结构体。
uint8_t *data; // 压缩编码的数据。
/* 例如对于H.264来说。1个AVPacket的data通常对应一个NAL。注意在这里只是对应而不是一模一样。他们之间有微小的差别使用FFMPEG类库分离出多媒体文件中的H.264码流。因此在使用FFMPEG进行音视频处理的时候常常可以将得到的AVPacket的data数据直接写成文件从而得到音视频的码流文件。*/
int size; // data的大小
int64_t pts; // 显示时间戳
int64_t dts; // 解码时间戳
int stream_index; // 标识该AVPacket所属的视频/音频流。三、ffmpeg函数介绍
int avformat_alloc_output_context2 (AVFormatContext **ctx, ff_const59 AVOutputFormat *oformat, const char *format_name, const char *filename);
功能为输出格式分配AVFormatContext。
参数ctx设置为创建的格式上下文或者在失败时设置为NULLoformat如果使用NULL format_name和filename则用于分配上下文的格式format_name:如果使用NULL文件名则用于分配上下文的输出格式的名称;filename用于分配上下文的文件名可以是NULL
返回值AVCodec* avcodec_find_encoder (enum AVCodecID id);
功能根据AVCodecID寻找已经注册的编码器
参数已经注册的编码器ID
返回值返回寻找到的编码器AVStream* avformat_new_stream (AVFormatContext *s, const AVCodec *c);
功能为AVFormatContext添加一个新的流
参数s媒体文件c编码器如果为空与新流相对应的AVCodecContext将被初始化以使用该编解码器。这是需要的例如要设置特定于编解码器的默认值因此如果已知则应提供编解码器
返回值新创建的流或出现错误时为NULLAVCodecContext* avcodec_alloc_context3 (const AVCodec *codec);
功能分配AVCodecContext并将其字段设置为默认值
参数如果非NULL则分配专用数据并初始化给定编解码器的默认值。然后用不同的编解码器调用avcodec_open2是非 法的。如果为NULL则编解码器特定的默认值将不会初始化这可能会导致次优的默认设置这主要对编码器很重 要例如libx264。
返回值AVCodecContext已填充默认值或失败时为NULLint avcodec_open2 (AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
功能初始化AVCodecContext以使用给定的AVCodec。在使用此函数之前必须使用avcodec_alloc_text3分配上下文。
参数avctx要初始化的上下文codec要为其打开此上下文的编解码器。如果之前已将非NULL编解码器传递给avcodec_alloc_text3或此上下文则此参数必须为NULL或等于之前传递的编解码器options一个充满AVCodecContext和编解码器专用选项的字典。返回时此对象将填充未找到的选项。可以为nullptr
返回值成功时为零错误时为负值int avcodec_parameters_from_context (AVCodecParameters *par, const AVCodecContext *codec);
功能根据提供的编解码器上下文中的值填充参数结构。par中任何分配的字段都将被释放并替换为编解码器中相应字段的副本
参数parAVCodecParamteres结构体;codec编码器上下文;
返回值成功时为0失败时为负AVERROR代码;int avio_open(AVIOContext **s, const char *url, int flags);
功能创建并初始化AVIOContext以访问url指示的资源。
参数s 用于返回指向创建的AVIOContext的指针。如果发生故障则指向的值将设置为NULL;url 要访问url的资源;flags 标志用于控制url指示的资源的方式的标志将被打开;
返回值return0如果成功则为负值对应于发生故障时的AVERROR代码av_warn_unused_result int avformat_write_header (AVFormatContext *s, AVDictionary **options);
功能:分配流专用数据并将流头写入输出媒体文件。
参数s 媒体文件句柄必须使用avformat_alloc_context进行分配。其oformat字段必须设置为所需的输出格 式其pb字段必须设置为已打开的AVIOContext;options充满AVFormatContext和muxer私有选项的AVDictionary。返回时此参数将被销毁并替换为包含未 找到的选项的dict。可能为NULL;
返回值:如果编解码器尚未在avformat_INIT中完全初始化则AVSTREAM_INIT_IN_WRITE_HEADER成功如果编解码 器已在avformat_INIT中完全启动则AVSTEAM_INIT_INIT_OUTPUT成功如果失败则为负AVERROR。void av_packet_rescale_ts (AVPacket *pkt, AVRational tb_src, AVRational tb_dst);
功能将数据包中的有效时间字段时间戳/持续时间从一个时基转换为另一个时基。具有未知值AV_NOPTS_VALUE的时间戳将被忽略。
参数pkt 将对其执行转换的pkt数据包tb_src 源时基其中表示pkt中的定时字段tb_dst 目标时基定时字段将转换为该时基
返回值int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
功能将数据包写入输出媒体文件确保正确交错
参数s 媒体文件句柄pkt 包含要写入的数据的数据包。
返回值成功时为0错误时为负AVERROR四、代码
#ifndef FFMPEGAPISAVEVIDEO_H
#define FFMPEGAPISAVEVIDEO_H#include QObject
#include QDebug
#include QThread
extern C{
#include libavutil/avassert.h
#include libavutil/channel_layout.h
#include libavutil/opt.h
#include libavutil/imgutils.h
#include libavformat/avformat.h
#include libswscale/swscale.h
#include libswresample/swresample.h
#include libavdevice/avdevice.h
#include libavcodec/avcodec.h
}#define MaxFrameNum 10class ffmpegApiSaveVideo : public QObject
{Q_OBJECT
public:struct VideoInfo{//分辨率int wideth;int hight;//码率int kbps;//图像编码AVPixelFormat pixFormat;//帧数int fps;//编码格式AVCodecID codecId;//封装格式QString packageFormat;};explicit ffmpegApiSaveVideo(QObject *parent nullptr);~ffmpegApiSaveVideo();QVectorAVFrame * m_FrameVector;void insertFrame(AVFrame *frame);void init(QString filePath,AVCodecContext *pCodecCtx,VideoInfo videoInfo);void stopWrite();
private:void initOutput(QByteArray filepath);void addStreamToftc(AVFormatContext *oc);void open_video(AVCodec *codec, AVDictionary *opt_arg);int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt);
private:AVFormatContext *m_pOFormatCtx nullptr;AVOutputFormat *m_pOutFmt nullptr; //定义一个输出的格式结构体AVCodecContext *m_pCodecCtx nullptr;AVCodecContext *m_ICodecCtx nullptr;AVCodec *m_pCodec nullptr;AVStream *m_pStream nullptr;AVFrame* m_pIFrameYUV nullptr;SwsContext* m_imgYUV_convert_ctx nullptr;int m_next_pts 0; //视频时间戳bool issave false;VideoInfo m_videoInfo;signals:public slots:void writeVideo();
};#endif // FFMPEGAPISAVEVIDEO_H
#include ffmpegapisavevideo.hffmpegApiSaveVideo::ffmpegApiSaveVideo(QObject *parent) : QObject(parent)
{}ffmpegApiSaveVideo::~ffmpegApiSaveVideo()
{}void ffmpegApiSaveVideo::insertFrame(AVFrame *frame)
{if(m_FrameVector.length()MaxFrameNum){m_FrameVector.pop_front();}m_FrameVector.append(frame);
}void ffmpegApiSaveVideo::init(QString filePath, AVCodecContext *pCodecCtx, ffmpegApiSaveVideo::VideoInfo videoInfo)
{m_videoInfo videoInfo;m_ICodecCtx pCodecCtx;initOutput(filePath.toUtf8());uint8_t *pIBuffer; //开辟存储像素点的存储地址int pixSize av_image_get_buffer_size(m_videoInfo.pixFormat,m_ICodecCtx-width, m_ICodecCtx-height,16);//创建保存空间,底层使用malloc进行内存空间的开辟。pIBuffer static_castuint8_t *(av_malloc(static_castsize_t(pixSize)));//创建图像转换之后的帧m_pIFrameYUV av_frame_alloc();av_image_fill_arrays(m_pIFrameYUV-data,m_pIFrameYUV-linesize,pIBuffer,m_videoInfo.pixFormat,1280,720,16);m_imgYUV_convert_ctx sws_getContext(m_ICodecCtx-width,m_ICodecCtx-height,m_ICodecCtx-pix_fmt,1280,720,m_videoInfo.pixFormat,SWS_BICUBIC, nullptr, nullptr, nullptr);issave true;
}void ffmpegApiSaveVideo::stopWrite()
{issave false;
}void ffmpegApiSaveVideo::writeVideo()
{while(issave){int ret;int got_packet0;AVPacket pkt;if(m_FrameVector.isEmpty()){continue;}AVFrame *pIFrame m_FrameVector.front();int length m_FrameVector.length();m_FrameVector.pop_front();if(pIFrame nullptr){continue;}sws_scale(m_imgYUV_convert_ctx, static_castuint8_t const * const *(pIFrame-data),pIFrame-linesize,0, m_ICodecCtx-height, m_pIFrameYUV-data,m_pIFrameYUV-linesize);m_pIFrameYUV-pts m_next_pts;av_init_packet(pkt);/* 编码图像*/ret avcodec_send_frame(m_pCodecCtx, m_pIFrameYUV);if (ret 0) {qDebug()Error sending the frame to the encoder;return;}ret avcodec_receive_packet(m_pCodecCtx, pkt);if (ret 0) {qDebug()Error encoding audio frame;return;}ret write_frame(m_pOFormatCtx, m_pCodecCtx-time_base, m_pStream, pkt);QThread::msleep(10);av_packet_unref(pkt);}av_write_trailer(m_pOFormatCtx);avcodec_free_context(m_pCodecCtx);/*关闭输出文件*/if (!(m_pOutFmt-flags AVFMT_NOFILE))avio_closep(m_pOFormatCtx-pb);/*释放流*/avformat_free_context(m_pOFormatCtx);}void ffmpegApiSaveVideo::initOutput(QByteArray filepath)
{int ret;//根据文件路径判断获取编码格式写入到oc中。向m_pOFormatCtx中写入数据avformat_alloc_output_context2(m_pOFormatCtx, nullptr, nullptr, filepath.data());//为输出格式分配一个AVFormatContext。if(!m_pOFormatCtx){printf(无法从文件扩展名推断出输出格式使用h264。\n);avformat_alloc_output_context2(m_pOFormatCtx, nullptr, m_videoInfo.packageFormat.toUtf8(), filepath.data());}//添加流 向m_pOutFmt和m_pCodecCtx m_pCodec写入数据。addStreamToftc(m_pOFormatCtx);/*现在所有参数都设置好了我们可以打开音频和视频编解码器并分配必要的编码缓冲器*/AVDictionary *opt nullptr;//AVDictionary是一个健值对存储工具open_video(m_pCodec,opt);av_dump_format(m_pOFormatCtx,0,filepath.data(),1);// TODO/* 打开输出文件(如果需要) */if(!(m_pOFormatCtx-flags AVFMT_NOFILE)){//创建并初始化AVIOContext以访问url指示的资源。retavio_open(m_pOFormatCtx-pb,filepath.data(),AVIO_FLAG_WRITE);if(ret0){qDebug()QString(打不开%1: %2).arg(filepath.data()).arg(av_err2str(ret));}}/* 编写流头(如果有)*/retavformat_write_header(m_pOFormatCtx, opt);if(ret0){fprintf(stderr, 打开输出文件时发生错误: %s\n,av_err2str(ret));}// 创建 缓存区}//*添加输出流。 */
void ffmpegApiSaveVideo::addStreamToftc(AVFormatContext *fct)
{int i;/* find the encoder */m_pOutFmt m_pOFormatCtx-oformat;m_pOutFmt-video_codec m_videoInfo.codecId;//设置编码格式m_pCodec avcodec_find_encoder(m_pOutFmt-video_codec);if (!(m_pCodec)){qDebug()Could not find encoder for : avcodec_get_name(m_pOutFmt-video_codec);return;}//给媒体文件添加一个流m_pStream avformat_new_stream(fct, nullptr);if (!m_pStream) {qDebug()Could not allocate stream;return;}m_pStream-id static_castint(fct-nb_streams-1);//创建 编码 上下文。m_pCodecCtx avcodec_alloc_context3(m_pCodec);av_opt_set(m_pCodecCtx-priv_data, tune, zerolatency, 0);//解决avcodec_receive_packet返回为-11的问题if (!m_pCodecCtx) {qDebug()Could not alloc an encoding context;return;}switch((m_pCodec)-type){case AVMEDIA_TYPE_AUDIO:{m_pCodecCtx-sample_fmt (m_pCodec)-sample_fmts ?(m_pCodec)-sample_fmts[0] : AV_SAMPLE_FMT_FLTP;m_pCodecCtx-bit_rate 64000;m_pCodecCtx-sample_rate 44100;if ((m_pCodec)-supported_samplerates) {m_pCodecCtx-sample_rate (m_pCodec)-supported_samplerates[0];for (i 0; (m_pCodec)-supported_samplerates[i]; i) {if ((m_pCodec)-supported_samplerates[i] 44100)m_pCodecCtx-sample_rate 44100;}}m_pCodecCtx-channels av_get_channel_layout_nb_channels(m_pCodecCtx-channel_layout);m_pCodecCtx-channel_layout AV_CH_LAYOUT_STEREO;if ((m_pCodec)-channel_layouts) {m_pCodecCtx-channel_layout (m_pCodec)-channel_layouts[0];for (i 0; (m_pCodec)-channel_layouts[i]; i) {if ((m_pCodec)-channel_layouts[i] AV_CH_LAYOUT_STEREO)m_pCodecCtx-channel_layout AV_CH_LAYOUT_STEREO;}}m_pCodecCtx-channels av_get_channel_layout_nb_channels(m_pCodecCtx-channel_layout);m_pStream-time_base (AVRational){ 1, m_pCodecCtx-sample_rate };break;}case AVMEDIA_TYPE_VIDEO:{m_pCodecCtx-codec_id m_pOutFmt-video_codec;m_pCodecCtx-bit_rate m_videoInfo.kbps; //平均比特率,例子代码默认值是400000/* 分辨率必须是2的倍数。*/m_pCodecCtx-width m_videoInfo.wideth;m_pCodecCtx-height m_videoInfo.hight;/*时基这是基本的时间单位以秒为单位表示其中的帧时间戳。 对于固定fps内容时基应为1 /framerate时间戳增量应为等于1。*/m_pStream-time_base (AVRational){1,m_videoInfo.fps}; //帧率设置 帧率为25;m_pCodecCtx-time_base m_pStream-time_base;m_pCodecCtx-gop_size 12; /* 最多每十二帧发射一帧内帧 */m_pCodecCtx-pix_fmt m_videoInfo.pixFormat;if(m_pCodecCtx-codec_id AV_CODEC_ID_MPEG2VIDEO){/* 只是为了测试我们还添加了B帧 */m_pCodecCtx-max_b_frames 2;}if(m_pCodecCtx-codec_id AV_CODEC_ID_MPEG1VIDEO){/*需要避免使用其中一些系数溢出的宏块。*普通视频不会发生这种情况因为*色度平面的运动与亮度平面不匹配。 */m_pCodecCtx-mb_decision 2;}break;}default:break;}/* 某些格式希望流头分开。 */if (fct-oformat-flags AVFMT_GLOBALHEADER)m_pCodecCtx-flags | AV_CODEC_FLAG_GLOBAL_HEADER;
}void ffmpegApiSaveVideo::open_video(AVCodec *codec, AVDictionary *opt_arg)
{int ret;AVCodecContext *c m_pCodecCtx;AVDictionary *opt nullptr;//AVDictionary用来保存音视频文件的metadataret av_dict_copy(opt, opt_arg, 0);/* open the codec */ret avcodec_open2(c, codec, opt);av_dict_free(opt);if (ret 0){qDebug()Could not open video codec:av_err2str(ret);return;}/* 将流参数复制到多路复用器*/retavcodec_parameters_from_context(m_pStream-codecpar, c);if(ret0){qDebug()Could not copy the stream parameters;return;}
}int ffmpegApiSaveVideo::write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{/* 将输出数据包时间戳值从编解码器重新调整为流时基 */av_packet_rescale_ts(pkt, *time_base, st-time_base);pkt-stream_index st-index;/*将压缩的帧写入媒体文件。*/
// log_packet(fmt_ctx, pkt);return av_interleaved_write_frame(fmt_ctx, pkt);
}