使用する共通APIとC言語の開発:FFmpegのは、チュートリアルをはじめ


ffmpegのと接触しているプロジェクトの使用のための理由なので、命令によるトランスコードビデオを、ffmpegの呼び出すためにC#を使用しました。どのように指示使いやすいが、それは、オーディオとビデオのより複雑な二次開発に来る場合、オーディオとビデオの関連する概念のない一定の知識がない場合、それはハードコードの意味やロジックを理解するために見つけます。以来関心は最近、適切な学習のffmpegのAPIの使用を検討し始めています。

理解概念

1、マルチメディアファイルの基本的な考え方

  • これは、マルチメディアコンテナファイルであります
  • コンテナ内の多くのストリーム(ストリーム/トラック)があります。
  • 各ストリームは異なる符号器によって符号化されます
  • データが呼ばれるパケットストリームから読み出し
  • これは、1つのパケット内の1つまたは複数のフレームを含みます

図2に示すように、符号化されたオーディオの量子化

  • 信号(連続のアナログからデジタルへの変換 - >離散的、非連続的プロセスは、コンピュータによって使用されます
  • アナログ - >サンプル - >定量 - >コーディング - >デジタル・シグナル
  • サンプルサイズ:ビット記憶の数のサンプル、一般的に使用される16ビットの基本的な概念を定量化
  • サンプリングレート:であり、サンプリング周波数(サンプリング周波数1秒)、一般的なサンプルレート8kHzの、16kHzの、32kHzの、44.1kHzの、48kHzの、等、より高いサンプリング周波数、コースのより現実的な音より自然な減少、より​​大きなデータ量
  • チャネル数:音場を再生するとき、真の音を復元することができるようにするために、同時に音が音を記録しながら、周りのいくつかの異なる方向に取得するには、各方向の音はチャネルです。スピーカーまたはオーディオ録音の数に応じた数のチャネルの数が再生されている、モノ、2チャネル、マルチチャネルがあります
  • レート:また、ビットレートとして知られているが、毎秒送信されたビットの数です。BPS(ビット毎秒)の単位、より高いビットレート、毎秒以上のデータ転送、より良い音質。
码率计算公式:
码率 = 采样率 * 采样大小 * 声道数
比如采样率44.1kHz,采样大小为16bit,双声道PCM编码的WAV文件:
码率=44.1hHz*16bit*2=1411.2kbit/s。
录制1分钟的音乐的大小为(1411.2 * 1000 * 60) / 8 / 1024 / 1024 = 10.09M。

図3に示すように、時間イル

  • time_baseの時間、例えばtime_baseを測定するために使用され= {1,40}は、それが1秒の40個のセグメントに分割されることを意味し、各セグメントは、FFmpegのがav_q2d関数に、1/40秒(time_baseは)の期間を計算するために使用されます時間は、演算結果が1/40秒です。例えば、ビデオPTSあるフレームは、最初の20秒と言うことであり、それは何秒も示し800個のセグメントが存在することを意味800、PTS av_q2d(time_base)= 800(1/40)= 20S、あります演奏は、フレームのタイムベースを変換するとき。異なるフォーマットの異なるタイムベース。
  • PTSは、レンダリングとタイムスタンプです。DTSは、デコードタイムスタンプです。
    オーディオPTS:例とAACオーディオ、AACフレームは、44.1kHzのサンプリングレート(1秒の取得44,100サンプル)と、1024個のサンプルを言うことで、元のデータ及び時間の期間の1024個のサンプルを含み、 AAC 44100/1024オーディオフレームが1秒であり、各フレームの持続時間は、それによって、PTSをフレーム毎に算出することができる44100分の1024秒です。
  • 変換式
timestamp() = pts * av_q2d(st->time_base)//计算该帧在视频音频中的位置
time() = st->duration * av_q2d(st->time_base)//计算视频音频中的长度
st  为AVStream流指针
时间基转换公式
timestamp(ffmpeg内部时间戳) = AV_TIME_BASE * time()
time() = AV_TIME_BASE_Q * timestamp(ffmpeg内部时间戳)//timestamp就算是PTS/DTS

環境設定

ダウンロード

入力の公式ウェブサイトは、デベロッパーと共有アーカイブをダウンロードしました。オプションは、対応するプラットフォームをダウンロードすることに注意してください。
devが、含まれ、LIBファイルは以下のディレクトリに抽出されます。Debugディレクトリ、サブエラーが発生します共有プロジェクトにdllファイルをコピーします。
ここに画像を挿入説明

環境設定

VSで作成するC / C ++プロジェクト、財産権プロジェクト
ここに画像を挿入説明
ここに画像を挿入説明
のdllファイル、次の追加するには

avcodec.lib; avformat.lib; avutil.lib; avdevice.lib; avfilter.lib; postproc.lib; swresample.lib; swscale.lib
libavcodec  提供一系列编码器的实现
libavformat 实现在流协议,容器格式及其IO访问
libavutil 包括了hash器、解码器和各种工具函数
libavfilter 提供了各种音视频过滤器
libavdevice 提供了访问捕获设备和回放设备的接口
libswresample 实现了混音和重采样
libswscale 实现了色彩转换和缩放功能

ここに画像を挿入説明

テスト

私は開発のためのエディタとしてVS2017を使用しています。

#include<stdio.h>
#include <iostream>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
}
int main(int argc, char* argv[]) {
	printf(avcodec_configuration());
	system("pause");
	return 0;
}

ここに画像を挿入説明

開発事例

ビデオとオーディオのミックスと2本のビデオを達成するために、一致、少しコーヒーが同様の特徴を示します。

そして、APIを使用するためのロジックを処理

  • APIの登録
  • 入力、出力コンテキストを作成します。
  • 入力音声ストリームを取得し、入力ビデオストリーム
  • 出力オーディオストリーム、ビデオストリーム出力を作成します
  • 入力パラメータは、出力ストリームのフローパラメータにコピーされ
  • ファイルサイズを確認し、ファイルの長さの出力が決定しました
  • 書き込みヘッダ
  • 初期化パケット、オーディオとビデオのデータが読み込まれ、ファイルに書き込まれます
    ここに画像を挿入説明

関連するコード


#include<stdio.h>
#include <iostream>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include <libavutil/log.h>
#include <libavutil/timestamp.h>
}
#define ERROR_STR_SIZE 1024
int main(int argc, char const *argv[])
{
	int ret = -1;
	int err_code;
	char errors[ERROR_STR_SIZE];
	AVFormatContext *ifmt_ctx1 = NULL;
	AVFormatContext *ifmt_ctx2 = NULL;
	AVFormatContext *ofmt_ctx = NULL;
	AVOutputFormat *ofmt = NULL;
	AVStream *in_stream1 = NULL;
	AVStream *in_stream2 = NULL;
	AVStream *out_stream1 = NULL;
	AVStream *out_stream2 = NULL;
	int audio_stream_index = 0;
	int vedio_stream_indes = 0;
	// 文件最大时长,保证音频和视频数据长度一致
	double max_duration = 0;
	AVPacket pkt;
	int stream1 = 0, stream2 = 0;
	av_log_set_level(AV_LOG_DEBUG);
	//打开两个输入文件
	if ((err_code = avformat_open_input(&ifmt_ctx1, "C:\\Users\\haizhengzheng\\Desktop\\meta.mp4", 0, 0)) < 0) {
		av_strerror(err_code, errors, ERROR_STR_SIZE);
		av_log(NULL, AV_LOG_ERROR, "Could not open src file, %s, %d(%s)\n",
			"C:\\Users\\haizhengzheng\\Desktop\\meta.mp4", err_code, errors);
		goto END;
	}
	if ((err_code = avformat_open_input(&ifmt_ctx2, "C:\\Users\\haizhengzheng\\Desktop\\mercury.mp4", 0, 0)) < 0) {
		av_strerror(err_code, errors, ERROR_STR_SIZE);
		av_log(NULL, AV_LOG_ERROR,
			"Could not open the second src file, %s, %d(%s)\n",
			"C:\\Users\\haizhengzheng\\Desktop\\mercury.mp4", err_code, errors);
		goto END;
	}
	//创建输出上下文
	if ((err_code = avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, "C:\\Users\\haizhengzheng\\Desktop\\amv.mp4")) < 0) {
		av_strerror(err_code, errors, ERROR_STR_SIZE);
		av_log(NULL, AV_LOG_ERROR, "Failed to create an context of outfile , %d(%s) \n",
			err_code, errors);
	}
	ofmt = ofmt_ctx->oformat;//获得输出文件的格式信息
	// 找到第一个参数里最好的音频流和第二个文件中的视频流下标
	audio_stream_index = av_find_best_stream(ifmt_ctx1, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);//获取音频流下标
	vedio_stream_indes = av_find_best_stream(ifmt_ctx2, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);//获取视频流下标
	// 获取第一个文件中的音频流
	in_stream1 = ifmt_ctx1->streams[audio_stream_index];
	stream1 = 0;
	// 创建音频输出流
	out_stream1 = avformat_new_stream(ofmt_ctx, NULL);
	if (!out_stream1) {
		av_log(NULL, AV_LOG_ERROR, "Failed to alloc out stream!\n");
		goto END;
	}
	// 拷贝流参数
	if ((err_code = avcodec_parameters_copy(out_stream1->codecpar, in_stream1->codecpar)) < 0) {
		av_strerror(err_code, errors, ERROR_STR_SIZE);
		av_log(NULL, AV_LOG_ERROR,
			"Failed to copy codec parameter, %d(%s)\n",
			err_code, errors);
	}
	out_stream1->codecpar->codec_tag = 0;
	// 获取第二个文件中的视频流
	in_stream2 = ifmt_ctx2->streams[vedio_stream_indes];
	stream2 = 1;

	// 创建视频输出流
	out_stream2 = avformat_new_stream(ofmt_ctx, NULL);
	if (!out_stream2) {
		av_log(NULL, AV_LOG_ERROR, "Failed to alloc out stream!\n");
		goto END;
	}
	// 拷贝流参数
	if ((err_code = avcodec_parameters_copy(out_stream2->codecpar, in_stream2->codecpar)) < 0) {
		av_strerror(err_code, errors, ERROR_STR_SIZE);
		av_log(NULL, AV_LOG_ERROR,
			"Failed to copy codec parameter, %d(%s)\n",
			err_code, errors);
		goto END;
	}
	out_stream2->codecpar->codec_tag = 0;
	//输出流信息
	av_dump_format(ofmt_ctx, 0, "C:\\Users\\haizhengzheng\\Desktop\\amv.mp4", 1);

	// 判断两个流的长度,确定最终文件的长度    time(秒) = st->duration * av_q2d(st->time_base)   duration 就是dts\pts     av_q2d()就是倒数
	if (in_stream1->duration * av_q2d(in_stream1->time_base) > in_stream2->duration * av_q2d(in_stream2->time_base)) {
		max_duration = in_stream2->duration * av_q2d(in_stream2->time_base);
	}
	else {
		max_duration = in_stream1->duration * av_q2d(in_stream1->time_base);
	}
	//打开输出文件
	if (!(ofmt->flags & AVFMT_NOFILE)) {
		if ((err_code = avio_open(&ofmt_ctx->pb, "C:\\Users\\haizhengzheng\\Desktop\\amv.mp4", AVIO_FLAG_WRITE)) < 0) {
			av_strerror(err_code, errors, ERROR_STR_SIZE);
			av_log(NULL, AV_LOG_ERROR,
				"Could not open output file, %s, %d(%s)\n",
				"C:\\Users\\haizhengzheng\\Desktop\\amv.mp4", err_code, errors);
			goto END;
		}
	}
	//写头信息
	avformat_write_header(ofmt_ctx, NULL);
	av_init_packet(&pkt);
	// 读取音频数据并写入输出文件中
	while (av_read_frame(ifmt_ctx1, &pkt) >= 0) {
		// 如果读取的时间超过了最长时间表示不需要该帧,跳过
		if (pkt.pts * av_q2d(in_stream1->time_base) > max_duration) {
			av_packet_unref(&pkt);
			continue;
		}
		// 如果是我们需要的音频流,转换时间基后写入文件  av_rescale_q_rnd()时间基转换函数
		if (pkt.stream_index == audio_stream_index) {
			pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream1->time_base, out_stream1->time_base,//获取包的PTS\DTS\duration
				(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
			pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream1->time_base, out_stream1->time_base,
				(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			pkt.duration = av_rescale_q(max_duration, in_stream1->time_base, out_stream1->time_base);
			pkt.pos = -1;
			pkt.stream_index = stream1;
			av_interleaved_write_frame(ofmt_ctx, &pkt);
			av_packet_unref(&pkt);
		}
	}


	// 读取视频数据并写入输出文件中
	while (av_read_frame(ifmt_ctx2, &pkt) >= 0) {

		// 如果读取的时间超过了最长时间表示不需要该帧,跳过
		if (pkt.pts * av_q2d(in_stream2->time_base) > max_duration) {
			av_packet_unref(&pkt);
			continue;
		}
		// 如果是我们需要的视频流,转换时间基后写入文件
		if (pkt.stream_index == vedio_stream_indes) {
			pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream2->time_base, out_stream2->time_base,
				(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream2->time_base, out_stream2->time_base,
				(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			pkt.duration = av_rescale_q(max_duration, in_stream2->time_base, out_stream2->time_base);
			pkt.pos = -1;
			pkt.stream_index = stream2;
			av_interleaved_write_frame(ofmt_ctx, &pkt);
			av_packet_unref(&pkt);
		}
	}
	//写尾信息
	av_write_trailer(ofmt_ctx);
		ret = 0;
END:
	// 释放内存
	if (ifmt_ctx1) {
		avformat_close_input(&ifmt_ctx1);
	}

	if (ifmt_ctx2) {
		avformat_close_input(&ifmt_ctx2);
	}

	if (ofmt_ctx) {
		if (!(ofmt->flags & AVFMT_NOFILE)) {
			avio_closep(&ofmt_ctx->pb);
		}
		avformat_free_context(ofmt_ctx);
	}
}

参考記事

オーディオの基本の
コード参照

公開された31元の記事 ウォン称賛29 ビュー8725

おすすめ

転載: blog.csdn.net/m0_38051293/article/details/104703396