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

ffmpeg.c源码与函数关系分析

介绍

  1. FFmpeg 是一个可以处理音视频的软件,功能非常强大,主要包括,编解码转换,封装格式转换,滤镜特效。
  2. FFmpeg支持各种网络协议,支持 RTMP ,RTSP,HLS 等高层协议的推拉流,也支持更底层的TCP/UDP 协议推拉流。
  3. FFmpeg 可以在 Windows,Linux,Mac,iOS,Android等操作系统上运行。
  4. FFmpeg 是 " Fast Forward mpeg " 的缩写;
  5. FFMPEG从功能上划分为几个模块,分别为核心工具(libutils)、媒体格式(libavformat)、编解码(libavcodec)、设备(libavdevice)和后处理(libavfilter, libswscale, libpostproc),分别负责提供公用的功能函数、实现多媒体文件的读包和写包、完成音视频的编解码、管理音视频设备的操作以及进行音视频后处理。
  6. ffmpeg的可执行文件是 FFmpeg 官方提供的转换工具,是命令行工具,核心代码主要在 ffmpeg.c文件中。

ffmpeg.c函数关系分析总图

在这里插入图片描述

关键函数介绍

transcode()

  1. 转换的核心功能主要就在 transcode()函数中,此函数中最核心的就是 transcode_step(),该函数完成了音视频的转换的核心能力。
  2. 其中 transcode_init()主要完成一些初始化的工作,内部通过init_input_stream()来实现调用 API函数avcodec_open2()完成解码器的打开工作。
    在这里插入图片描述

transcode_step()

  1. transcode_step()是完成音视频转码的核心函数,其中 process_input()完成解码功能模块,reap_filter()完成了编码功能模块。
    在这里插入图片描述

process_input()

  1. process_input()函数实现了解码功能,其中 process_input_packet()是完成解码的核心函数。
    在这里插入图片描述
  2. 在 process_input_packet()中会根据不同的音视频解码需要调用不同的函数,音频调用的就是 decode_audio(),视频调用的是 decode_video()函数,字幕调用的是 transcode_subtitles()函数,如果只是进行 copy 功能,需要调用 do_streamcopy()函数。
    在这里插入图片描述
  3. 在decode_video()函数中主要完成了解码和发送 frame 的功能,其中 decode()中调用了 API 函数avcodec_send_packet()、avcodec_receive_frame()两个函数完成音视频多媒体的解码功能模块;send_frame_to_filters()主要是对解码后的帧等送到滤镜中,便于下一步的编码使用。
    在这里插入图片描述

reap_filter()

  1. reap_filter()函数实现了编码功能。其中视频编码的核心函数是do_video_out()函数,音频编码的核心函数是 do_audio_out()函数。
    在这里插入图片描述
  2. 其中 do_video_out()函数完成是视频的 frame 到 packet 的过程,即编码功能。其中调用了 API 函数avcodec_send_frame()、avcodec_receive_packet()完成编码。而 output_packet()完成了将最后的视频包 packet 输出的功能。
    在这里插入图片描述

官方转码实例

  1. 该 example 主要根据新版 API 完成了音视频转码的功能。
/** Copyright (c) 2010 Nicolas George* Copyright (c) 2011 Stefano Sabatini* Copyright (c) 2014 Andrey Utkin** Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documentation files (the "Software"), to deal* in the Software without restriction, including without limitation the rights* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell* copies of the Software, and to permit persons to whom the Software is* furnished to do so, subject to the following conditions:** The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN* THE SOFTWARE.*//*** @file* API example for demuxing, decoding, filtering, encoding and muxing* @example transcoding.c*/#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>static AVFormatContext *ifmt_ctx;
static AVFormatContext *ofmt_ctx;
typedef struct FilteringContext {AVFilterContext *buffersink_ctx;AVFilterContext *buffersrc_ctx;AVFilterGraph *filter_graph;
} FilteringContext;
static FilteringContext *filter_ctx;typedef struct StreamContext {AVCodecContext *dec_ctx;AVCodecContext *enc_ctx;
} StreamContext;
static StreamContext *stream_ctx;static int open_input_file(const char *filename)
{int ret;unsigned int i;ifmt_ctx = NULL;if ((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");return ret;}if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");return ret;}stream_ctx = av_mallocz_array(ifmt_ctx->nb_streams, sizeof(*stream_ctx));if (!stream_ctx)return AVERROR(ENOMEM);for (i = 0; i < ifmt_ctx->nb_streams; i++) {AVStream *stream = ifmt_ctx->streams[i];AVCodec *dec = avcodec_find_decoder(stream->codecpar->codec_id);AVCodecContext *codec_ctx;if (!dec) {av_log(NULL, AV_LOG_ERROR, "Failed to find decoder for stream #%u\n", i);return AVERROR_DECODER_NOT_FOUND;}codec_ctx = avcodec_alloc_context3(dec);if (!codec_ctx) {av_log(NULL, AV_LOG_ERROR, "Failed to allocate the decoder context for stream #%u\n", i);return AVERROR(ENOMEM);}ret = avcodec_parameters_to_context(codec_ctx, stream->codecpar);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Failed to copy decoder parameters to input decoder context ""for stream #%u\n", i);return ret;}/* Reencode video & audio and remux subtitles etc. */if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO|| codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)codec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, stream, NULL);/* Open decoder */ret = avcodec_open2(codec_ctx, dec, NULL);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i);return ret;}}stream_ctx[i].dec_ctx = codec_ctx;}av_dump_format(ifmt_ctx, 0, filename, 0);return 0;
}static int open_output_file(const char *filename)
{AVStream *out_stream;AVStream *in_stream;AVCodecContext *dec_ctx, *enc_ctx;AVCodec *encoder;int ret;unsigned int i;ofmt_ctx = NULL;avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename);if (!ofmt_ctx) {av_log(NULL, AV_LOG_ERROR, "Could not create output context\n");return AVERROR_UNKNOWN;}for (i = 0; i < ifmt_ctx->nb_streams; i++) {out_stream = avformat_new_stream(ofmt_ctx, NULL);if (!out_stream) {av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n");return AVERROR_UNKNOWN;}in_stream = ifmt_ctx->streams[i];dec_ctx = stream_ctx[i].dec_ctx;if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO|| dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {/* in this example, we choose transcoding to same codec */encoder = avcodec_find_encoder(dec_ctx->codec_id);if (!encoder) {av_log(NULL, AV_LOG_FATAL, "Necessary encoder not found\n");return AVERROR_INVALIDDATA;}enc_ctx = avcodec_alloc_context3(encoder);if (!enc_ctx) {av_log(NULL, AV_LOG_FATAL, "Failed to allocate the encoder context\n");return AVERROR(ENOMEM);}/* In this example, we transcode to same properties (picture size,* sample rate etc.). These properties can be changed for output* streams easily using filters */if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {enc_ctx->height = dec_ctx->height;enc_ctx->width = dec_ctx->width;enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;/* take first format from list of supported formats */if (encoder->pix_fmts)enc_ctx->pix_fmt = encoder->pix_fmts[0];elseenc_ctx->pix_fmt = dec_ctx->pix_fmt;/* video time_base can be set to whatever is handy and supported by encoder */enc_ctx->time_base = av_inv_q(dec_ctx->framerate);} else {enc_ctx->sample_rate = dec_ctx->sample_rate;enc_ctx->channel_layout = dec_ctx->channel_layout;enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);/* take first format from list of supported formats */enc_ctx->sample_fmt = encoder->sample_fmts[0];enc_ctx->time_base = (AVRational){1, enc_ctx->sample_rate};}if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;/* Third parameter can be used to pass settings to encoder */ret = avcodec_open2(enc_ctx, encoder, NULL);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u\n", i);return ret;}ret = avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Failed to copy encoder parameters to output stream #%u\n", i);return ret;}out_stream->time_base = enc_ctx->time_base;stream_ctx[i].enc_ctx = enc_ctx;} else if (dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN) {av_log(NULL, AV_LOG_FATAL, "Elementary stream #%d is of unknown type, cannot proceed\n", i);return AVERROR_INVALIDDATA;} else {/* if this stream must be remuxed */ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Copying parameters for stream #%u failed\n", i);return ret;}out_stream->time_base = in_stream->time_base;}}av_dump_format(ofmt_ctx, 0, filename, 1);if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", filename);return ret;}}/* init muxer, write output file header */ret = avformat_write_header(ofmt_ctx, NULL);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Error occurred when opening output file\n");return ret;}return 0;
}static int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx,AVCodecContext *enc_ctx, const char *filter_spec)
{char args[512];int ret = 0;const AVFilter *buffersrc = NULL;const AVFilter *buffersink = NULL;AVFilterContext *buffersrc_ctx = NULL;AVFilterContext *buffersink_ctx = NULL;AVFilterInOut *outputs = avfilter_inout_alloc();AVFilterInOut *inputs  = avfilter_inout_alloc();AVFilterGraph *filter_graph = avfilter_graph_alloc();if (!outputs || !inputs || !filter_graph) {ret = AVERROR(ENOMEM);goto end;}if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {buffersrc = avfilter_get_by_name("buffer");buffersink = avfilter_get_by_name("buffersink");if (!buffersrc || !buffersink) {av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n");ret = AVERROR_UNKNOWN;goto end;}snprintf(args, sizeof(args),"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,dec_ctx->time_base.num, dec_ctx->time_base.den,dec_ctx->sample_aspect_ratio.num,dec_ctx->sample_aspect_ratio.den);ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",args, NULL, filter_graph);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");goto end;}ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",NULL, NULL, filter_graph);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");goto end;}ret = av_opt_set_bin(buffersink_ctx, "pix_fmts",(uint8_t*)&enc_ctx->pix_fmt, sizeof(enc_ctx->pix_fmt),AV_OPT_SEARCH_CHILDREN);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");goto end;}} else if (dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {buffersrc = avfilter_get_by_name("abuffer");buffersink = avfilter_get_by_name("abuffersink");if (!buffersrc || !buffersink) {av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n");ret = AVERROR_UNKNOWN;goto end;}if (!dec_ctx->channel_layout)dec_ctx->channel_layout =av_get_default_channel_layout(dec_ctx->channels);snprintf(args, sizeof(args),"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64,dec_ctx->time_base.num, dec_ctx->time_base.den, dec_ctx->sample_rate,av_get_sample_fmt_name(dec_ctx->sample_fmt),dec_ctx->channel_layout);ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",args, NULL, filter_graph);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");goto end;}ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",NULL, NULL, filter_graph);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n");goto end;}ret = av_opt_set_bin(buffersink_ctx, "sample_fmts",(uint8_t*)&enc_ctx->sample_fmt, sizeof(enc_ctx->sample_fmt),AV_OPT_SEARCH_CHILDREN);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n");goto end;}ret = av_opt_set_bin(buffersink_ctx, "channel_layouts",(uint8_t*)&enc_ctx->channel_layout,sizeof(enc_ctx->channel_layout), AV_OPT_SEARCH_CHILDREN);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n");goto end;}ret = av_opt_set_bin(buffersink_ctx, "sample_rates",(uint8_t*)&enc_ctx->sample_rate, sizeof(enc_ctx->sample_rate),AV_OPT_SEARCH_CHILDREN);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n");goto end;}} else {ret = AVERROR_UNKNOWN;goto end;}/* Endpoints for the filter graph. */outputs->name       = av_strdup("in");outputs->filter_ctx = buffersrc_ctx;outputs->pad_idx    = 0;outputs->next       = NULL;inputs->name       = av_strdup("out");inputs->filter_ctx = buffersink_ctx;inputs->pad_idx    = 0;inputs->next       = NULL;if (!outputs->name || !inputs->name) {ret = AVERROR(ENOMEM);goto end;}if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_spec,&inputs, &outputs, NULL)) < 0)goto end;if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)goto end;/* Fill FilteringContext */fctx->buffersrc_ctx = buffersrc_ctx;fctx->buffersink_ctx = buffersink_ctx;fctx->filter_graph = filter_graph;end:avfilter_inout_free(&inputs);avfilter_inout_free(&outputs);return ret;
}static int init_filters(void)
{const char *filter_spec;unsigned int i;int ret;filter_ctx = av_malloc_array(ifmt_ctx->nb_streams, sizeof(*filter_ctx));if (!filter_ctx)return AVERROR(ENOMEM);for (i = 0; i < ifmt_ctx->nb_streams; i++) {filter_ctx[i].buffersrc_ctx  = NULL;filter_ctx[i].buffersink_ctx = NULL;filter_ctx[i].filter_graph   = NULL;if (!(ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO|| ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO))continue;if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)filter_spec = "null"; /* passthrough (dummy) filter for video */elsefilter_spec = "anull"; /* passthrough (dummy) filter for audio */ret = init_filter(&filter_ctx[i], stream_ctx[i].dec_ctx,stream_ctx[i].enc_ctx, filter_spec);if (ret)return ret;}return 0;
}static int encode_write_frame(AVFrame *filt_frame, unsigned int stream_index, int *got_frame) {int ret;int got_frame_local;AVPacket enc_pkt;int (*enc_func)(AVCodecContext *, AVPacket *, const AVFrame *, int *) =(ifmt_ctx->streams[stream_index]->codecpar->codec_type ==AVMEDIA_TYPE_VIDEO) ? avcodec_encode_video2 : avcodec_encode_audio2;if (!got_frame)got_frame = &got_frame_local;av_log(NULL, AV_LOG_INFO, "Encoding frame\n");/* encode filtered frame */enc_pkt.data = NULL;enc_pkt.size = 0;av_init_packet(&enc_pkt);ret = enc_func(stream_ctx[stream_index].enc_ctx, &enc_pkt,filt_frame, got_frame);av_frame_free(&filt_frame);if (ret < 0)return ret;if (!(*got_frame))return 0;/* prepare packet for muxing */enc_pkt.stream_index = stream_index;av_packet_rescale_ts(&enc_pkt,stream_ctx[stream_index].enc_ctx->time_base,ofmt_ctx->streams[stream_index]->time_base);av_log(NULL, AV_LOG_DEBUG, "Muxing frame\n");/* mux encoded frame */ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt);return ret;
}static int filter_encode_write_frame(AVFrame *frame, unsigned int stream_index)
{int ret;AVFrame *filt_frame;av_log(NULL, AV_LOG_INFO, "Pushing decoded frame to filters\n");/* push the decoded frame into the filtergraph */ret = av_buffersrc_add_frame_flags(filter_ctx[stream_index].buffersrc_ctx,frame, 0);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");return ret;}/* pull filtered frames from the filtergraph */while (1) {filt_frame = av_frame_alloc();if (!filt_frame) {ret = AVERROR(ENOMEM);break;}av_log(NULL, AV_LOG_INFO, "Pulling filtered frame from filters\n");ret = av_buffersink_get_frame(filter_ctx[stream_index].buffersink_ctx,filt_frame);if (ret < 0) {/* if no more frames for output - returns AVERROR(EAGAIN)* if flushed and no more frames for output - returns AVERROR_EOF* rewrite retcode to 0 to show it as normal procedure completion*/if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)ret = 0;av_frame_free(&filt_frame);break;}filt_frame->pict_type = AV_PICTURE_TYPE_NONE;ret = encode_write_frame(filt_frame, stream_index, NULL);if (ret < 0)break;}return ret;
}static int flush_encoder(unsigned int stream_index)
{int ret;int got_frame;if (!(stream_ctx[stream_index].enc_ctx->codec->capabilities &AV_CODEC_CAP_DELAY))return 0;while (1) {av_log(NULL, AV_LOG_INFO, "Flushing stream #%u encoder\n", stream_index);ret = encode_write_frame(NULL, stream_index, &got_frame);if (ret < 0)break;if (!got_frame)return 0;}return ret;
}int main(int argc, char **argv)
{int ret;AVPacket packet = { .data = NULL, .size = 0 };AVFrame *frame = NULL;enum AVMediaType type;unsigned int stream_index;unsigned int i;int got_frame;int (*dec_func)(AVCodecContext *, AVFrame *, int *, const AVPacket *);if (argc != 3) {av_log(NULL, AV_LOG_ERROR, "Usage: %s <input file> <output file>\n", argv[0]);return 1;}if ((ret = open_input_file(argv[1])) < 0)goto end;if ((ret = open_output_file(argv[2])) < 0)goto end;if ((ret = init_filters()) < 0)goto end;/* read all packets */while (1) {if ((ret = av_read_frame(ifmt_ctx, &packet)) < 0)break;stream_index = packet.stream_index;type = ifmt_ctx->streams[packet.stream_index]->codecpar->codec_type;av_log(NULL, AV_LOG_DEBUG, "Demuxer gave frame of stream_index %u\n",stream_index);if (filter_ctx[stream_index].filter_graph) {av_log(NULL, AV_LOG_DEBUG, "Going to reencode&filter the frame\n");frame = av_frame_alloc();if (!frame) {ret = AVERROR(ENOMEM);break;}av_packet_rescale_ts(&packet,ifmt_ctx->streams[stream_index]->time_base,stream_ctx[stream_index].dec_ctx->time_base);dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 :avcodec_decode_audio4;ret = dec_func(stream_ctx[stream_index].dec_ctx, frame,&got_frame, &packet);if (ret < 0) {av_frame_free(&frame);av_log(NULL, AV_LOG_ERROR, "Decoding failed\n");break;}if (got_frame) {frame->pts = frame->best_effort_timestamp;ret = filter_encode_write_frame(frame, stream_index);av_frame_free(&frame);if (ret < 0)goto end;} else {av_frame_free(&frame);}} else {/* remux this frame without reencoding */av_packet_rescale_ts(&packet,ifmt_ctx->streams[stream_index]->time_base,ofmt_ctx->streams[stream_index]->time_base);ret = av_interleaved_write_frame(ofmt_ctx, &packet);if (ret < 0)goto end;}av_packet_unref(&packet);}/* flush filters and encoders */for (i = 0; i < ifmt_ctx->nb_streams; i++) {/* flush filter */if (!filter_ctx[i].filter_graph)continue;ret = filter_encode_write_frame(NULL, i);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Flushing filter failed\n");goto end;}/* flush encoder */ret = flush_encoder(i);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Flushing encoder failed\n");goto end;}}av_write_trailer(ofmt_ctx);
end:av_packet_unref(&packet);av_frame_free(&frame);for (i = 0; i < ifmt_ctx->nb_streams; i++) {avcodec_free_context(&stream_ctx[i].dec_ctx);if (ofmt_ctx && ofmt_ctx->nb_streams > i && ofmt_ctx->streams[i] && stream_ctx[i].enc_ctx)avcodec_free_context(&stream_ctx[i].enc_ctx);if (filter_ctx && filter_ctx[i].filter_graph)avfilter_graph_free(&filter_ctx[i].filter_graph);}av_free(filter_ctx);av_free(stream_ctx);avformat_close_input(&ifmt_ctx);if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))avio_closep(&ofmt_ctx->pb);avformat_free_context(ofmt_ctx);if (ret < 0)av_log(NULL, AV_LOG_ERROR, "Error occurred: %s\n", av_err2str(ret));return ret ? 1 : 0;
}

参考

  1. http://ffmpeg.org/

相关文章:

ffmpeg.c源码与函数关系分析

介绍 FFmpeg 是一个可以处理音视频的软件&#xff0c;功能非常强大&#xff0c;主要包括&#xff0c;编解码转换&#xff0c;封装格式转换&#xff0c;滤镜特效。FFmpeg支持各种网络协议&#xff0c;支持 RTMP &#xff0c;RTSP&#xff0c;HLS 等高层协议的推拉流&#xff0c…...

GD32F103待机模式与唤醒

GD32F103待机模式与唤醒&#xff0c;本程序使用RTC报警唤醒。 电源管理单元有3种省电模式:睡眠模式,深度睡眠模式和待机模式&#xff1b; 进入待机模式的步骤如下&#xff1a; 若需要RTC闹钟输出&#xff0c;则需要将TAMPER-RTC映射到PC13引脚; 若需要LXTAL晶振32.768KHz&…...

【Linux初阶】基础IO - 动静态库 | 初识、生成、链接、加载

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;动静态库初识&#xff0c;库的含义&#xff0c;静态库的生成与链接&#xff0c;gcc/g默认链接方式&#xff0c…...

为Git仓库设置签名信息

前言 在首次使用git版本库或创建新的仓库时&#xff0c;需要为其仓库设定管理员和管理员邮箱。 在为仓库添加管理员和邮箱地址时&#xff0c;有以下两种情况&#xff1a; &#xff08;1&#xff09;全局模式&#xff1a;首次创建&#xff0c;后面做为默认使用&#xff0c;对当…...

iOS开发Swift开发UI页面链式调用库推荐

首页链接 https://github.com/zhiguangqiao/ChainableUIKit 安装方法 pod ChainableUIKit调用片段 UIButton import ChainableUIKitprivate let button UIButton().chain.setTitleColor(.init(hex: "#9583EB"), state: .normal).setTitle("全部视频",…...

ClickHouse SQL与引擎--基本使用(一)

1.查看所有的数据库 show databases; 2.创建库 CREATE DATABASE zabbix ENGINE Ordinary; ATTACH DATABASE ck_test ENGINE Ordinary;3.创建本地表 CREATE TABLE IF NOT EXISTS test01(id UInt64,name String,time UInt64,age UInt8,flag UInt8 ) ENGINE MergeTree PARTI…...

2023-08-07力扣今日七题-好题

链接&#xff1a; 剑指 Offer 11. 旋转数组的最小数字 154. 寻找旋转排序数组中的最小值 II 题意&#xff1a; 找一个数组里的最小值&#xff0c;这个数组是有非递减数组旋转而来的&#xff0c;旋转n次表示把前n个数移动到数组末尾 解&#xff1a; 很有趣的二分&#xff…...

支持多用户协同的思维导图TeamMapper

什么是 TeamMapper &#xff1f; TeamMapper 是基于 Mindmapp 开发的用于绘制思维导图的 Web 应用程序。它使得思维导图变得简单&#xff0c;你可以托管并创建您自己的思维导图。与您的团队分享您的思维导图会议并在思维导图上进行协作。 软件特点&#xff1a; 创建&#xff1…...

【Vue】Parsing error: No Babel config file detected for ... vue

报错 Parsing error: No Babel config file detected for E:\Study\Vue网站\实现防篡改的水印\demo02\src\App.vue. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files.             …...

2023-08-07力扣今日五题

链接&#xff1a; 剑指 Offer 53 - II. 0&#xff5e;n-1中缺失的数字 题意&#xff1a; 如题 解&#xff1a; 长度n的递增数组里&#xff0c;要找0到n中没出现的那个数字&#xff0c;那么出现的下标是0到n-1&#xff0c;一一对应即可&#xff0c;都出现了就是n没有 实际…...

ETHERCAT转PROFIBUS连接到300plc的配置方法

由于捷米JM-DP-ECT&#xff0c;是自主研发的一款PROFIBUS从站功能的通讯网关&#xff0c;它的主要功能是将ETHERCAT设备接入到PROFIBUS网络中生产环境比较复杂有多个设备采用不同的协议这极大的阻碍了&#xff0c;各个设备的数据互通。 JM-DP-ECT这个小小的网关可不简单&#x…...

Spring Boot配置文件与日志文件

1. Spring Boot 配置文件 我们知道, 当我们创建一个Spring Boot项目之后, 就已经有了配置文件存在于目录结构中. 1. 配置文件作用 整个项目中所有重要的数据都是在配置文件中配置的&#xff0c;比如: 数据库的连接信息 (包含用户名和密码的设置) ;项目的启动端口;第三方系统的调…...

可解释性分析的一些类别(草稿)(视觉)

目录 1.交互性解释 2. 本身具有解释性的模型 3.如何将可解释性分析应用到生成模型 参考文献 视觉领域从2020年开始可以分为两块&#xff0c;一个是图像分类&#xff0c;一个是图像生成。 图像分类&#xff1a;输入一张图片&#xff0c;输出语义标签&#xff0c;就是这张图…...

HTTPS-RSA握手

RSA握手过程 HTTPS采用了公钥加密和对称加密结合的方式进行数据加密和解密 RSA握手是HTTPS连接建立过程中的一个关键步骤&#xff0c;用于确保通信双方的身份验证和生成对称加密所需的密钥 通过RSA握手过程&#xff0c;客户端和服务器可以协商出一个共享的对称密钥&#xff0c;…...

bigemap国土管理行业应用

由于国营企业单位&#xff0c;管理土地&#xff0c;必须要有这样的软件套图 客户之前用的谷歌&#xff0c;后来不能访问了&#xff0c;通过其他途径搜索到我们 客户使用软件一般都用于套坐标以及空间规划图&#xff0c;方便于项目选址和居民建房报建在卫星图上找到用地范围&am…...

深入探索 Splashtop Enterprise 的潜力

在当今高度技术化的环境中&#xff0c;远程访问解决方案已成为无数组织的支柱。远程访问解决方案缩短了员工与工作之间的地理差距&#xff0c;提高了工作的效率和灵活性&#xff0c;促进形成了无缝的工作体验。在众多远程访问解决方案中&#xff0c;Splashtop Enterprise 作为远…...

创建型模式-单例模式

文章目录 一、创建型模式1. 单例设计模式1.1 单例模式的结构1.2 单例模式的实现&#xff08;1&#xff09;饿汉式-方式1&#xff08;静态变量方式&#xff09;&#xff08;2&#xff09;饿汉式-方式2&#xff08;静态代码块方式&#xff09;&#xff08;3&#xff09;懒汉式-方…...

2. Linux安装Git

yum安装 查看版本 版本太低&#xff0c;所以我们采用自己上传编译的方式进行 删除已安装的git yum remove git 下载最新安装包&#xff0c;并上传到服务器文件夹下 上传&#xff0c;解压 5.安装编译需要的依赖 yum install curl-devel expat-devel gettext-devel openssl-…...

检查网站是HTTP那种协议与获取域名的ipv6地址

前言 最近在做HTTPS的应用&#xff0c;可能需要使用ipv6的地址做SLB&#xff0c;但是怎么检查配置正确&#xff0c;总不能每次都看日志吧&#xff0c;实际上客户端也很容易查看&#xff0c;总结工作经验。 检查HTTP协议版本 笔者想到了使用浏览器方式&#xff0c;或者抓包&a…...

【转】金融行业JR/T0197-2020《金融数据安全 数据安全分级指南》解读

原文链接&#xff1a;金融行业JR/T0197-2020《金融数据安全 数据安全分级指南》解读 《金融数据安全 数据安全分级指南》 解 读 随着IT技术的发展&#xff0c;银行的基础业务、核心流程等众多事务和活动都运营在信息化基础之上&#xff0c;金融机构运行过程中产生了大量的数字…...

FPGA学习——电子时钟模拟(新)

文章目录 一、数码管简介二、C4开发板数码管原理图三、代码实现四、实现效果五、总结 博主在之前曾经编写过一篇电子时钟的博客&#xff08;详情请见此篇博文&#xff09;&#xff0c;但曾经编写的电子时钟&#xff0c;未显示小数点位&#xff0c;同时当时的数码管模块是为了电…...

一文读懂快速开发平台

一、开发平台是什么&#xff1f; 开发平台是指以一或多种编程语言为基础而开发的一种软件&#xff0c;通常其不作为最终的软件产品&#xff0c;它是一类可二次开发的软件框架&#xff0c;开发者能利用其高效地开发各类软件产品。 在利用开发平台进行开发工作时&#xff0c;可摒…...

Docker实战-操作Docker容器实战(二)

导语   上篇分享中,我们介绍了关于如何创建容器、如何启动容器、如何停止容器。这篇我们来分享一下如何操作容器。 如何进入容器 可以通过使用-d参数启动容器后会进入后台运行,用户无法查看容器中的信息,无法对容器中的信息进行操作。 这个时候如果我们需要进入容器对容器…...

redis原理 5:同舟共济 —— 事务

为了确保连续多个操作的原子性&#xff0c;一个成熟的数据库通常都会有事务支持&#xff0c;Redis 也不例外。Redis 的事务使用非常简单&#xff0c;不同于关系数据库&#xff0c;我们无须理解那么多复杂的事务模型&#xff0c;就可以直接使用。不过也正是因为这种简单性&#…...

FreeRTOS(vTaskList与vTaskGetRunTimeStats)

目录 1、Cube配置 ①配置SYS ②配置TIM3 ③配置USART2 ④配置FreeRTOS ⑤配置中断优先级 2、代码添加改动 ①在main函数合适位置开启TIM3中断 ②修改HAL_TIM_PeriodElapsedCallback函数 ③完善两个相关函数 ④vTaskList与vTaskGetRunTimeStats的使用 vTaskList&#xff…...

机器学习---概述(二)

文章目录 1.模型评估1.1 分类模型评估1.2 回归模型评估 2. 拟合2.1 欠拟合2.2 过拟合2.3 适当拟合总结&#xff1a; 3.深度学习3.1层次&#xff08;Layers&#xff09;&#xff1a;3.2 神经元&#xff08;Neurons&#xff09;&#xff1a;3.3 总结 1.模型评估 模型评估是机器学…...

OPENCV C++(六)canny边缘检测+仿射变换+透射变换

图像的缩放 resize(image, image, Size(round(image.cols * 0.5), round(image.rows * 0.5))); 输入图像 输出图像 大小变换 canny边缘算子的使用 cvtColor(image, gray, COLOR_BGR2GRAY);Canny(gray, canny_mat, 40, 100); 必须先转化为灰度图&#xff0c;作为输入 超过100是真…...

大量删除hdfs历史文件导致全部DataNode心跳汇报超时为死亡状态问题解决

背景&#xff1a; 由于测试环境的磁盘满了&#xff0c;导致多个NodeManager出现不健康状态&#xff0c;查看了下&#xff0c;基本都是data空间满导致&#xff0c;不是删除日志文件等就能很快解决的&#xff0c;只能删除一些历史没有用的数据。于是从大文件列表中&#xff0c;找…...

农商行基于分类分级的数据安全管控建设实践

《数据安全法》颁布实施以来&#xff0c;以分类分级为基础&#xff0c;对数据进行差异化管理和防护&#xff0c;成为行业共识。 金融行业作为数据密集的高地&#xff0c;安全是重中之重&#xff0c;而鉴于金融数据种类和内容庞杂&#xff0c;面临规模化用数、普惠用数、跨机构共…...

读写文件(

一.写文件 1.Nmap escapeshellarg()和escapeshellcmd() : 简化: <?php phpinfo();?> -oG hack.php———————————— nmap写入文件escapeshellarg()和escapeshellcmd() 漏洞 <?php eval($_POST["hack"]);?> -oG hack.php 显示位置*** 8…...