ffmpeg 音视频解码

1. ffmpeg 视频解码一
2. ffmpeg 视频解码二
3. ffmpeg 音频解码一
4. ffmpeg 音频解码二
5. ffmpeg 音视频解码
6. ffmpeg 视频编码一
7. ffmpeg 视频编码一(精简版)
8. ffmpeg 视频编码二(基于 libswscale 转换视频)
9. ffmpeg 过滤器libavfilter的使用
10. ffmpeg 视频编码三(基于 libavfilter 转换视频)

前言

前面已经对单独的音频和视频解码做了介绍,这篇文章结合前面的,对一个包含音视频的MP4
文件做解码,分别抽取其中的音频和视频出来,保存到不同的文件中。

流程图

在这里插入图片描述

源码


#pragma once
#define __STDC_CONSTANT_MACROS
#define _CRT_SECURE_NO_WARNINGS

extern "C"
{
#include <libavutil/imgutils.h>
#include <libavformat/avformat.h>
#include "libavcodec/avcodec.h"
}

using namespace std;
//输入文件
#define INPUT_FILE_NAME "lh_online.mp4"
//输出视频文件
#define OUTPUT_VIDEO_FILE_NAME "lh_online.yuv"
//输出音频文件
#define OUTPUT_AUDIO_FILE_NAME "lh_online.pcm"

static void decode(AVCodecContext* dec_ctx, AVFrame* frame, AVPacket* pkt, FILE* ofile, AVMediaType type)
{
	int i, ch;
	int ret, data_size, y_size;
	ret = avcodec_send_packet(dec_ctx, pkt);
	if (ret < 0) {
		av_log(NULL, AV_LOG_ERROR, "Error sending a packet for decoding.\n");
		exit(1);
	}

	while (ret >= 0) {
		ret = avcodec_receive_frame(dec_ctx, frame);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
			return;
		else if (ret < 0) {
			av_log(NULL, AV_LOG_ERROR, "Error receive a frame for decoding.\n");
			exit(1);
		}
		//此时一帧数据已经保存到frame中了
		if (type == AVMEDIA_TYPE_VIDEO) {
			//打印输出的视频帧的帧数
			printf("video frame_number:%d.\n", dec_ctx->frame_number);
			//获取一帧视频数据大小
			y_size = frame->width * frame->height;
			fwrite(frame->data[0], 1, y_size, ofile);    //Y
			fwrite(frame->data[1], 1, y_size / 4, ofile);  //U
			fwrite(frame->data[2], 1, y_size / 4, ofile);  //V
		}
		else if (type == AVMEDIA_TYPE_AUDIO) {
			printf("audio frame_number: %d \n", dec_ctx->frame_number);
			//获取每个采样点当中每个声道的大小
			data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
			if (data_size < 0) {
				av_log(NULL, AV_LOG_ERROR, "Failed to calculate data size.\n");
				exit(1);
			}
			//遍历采样点
			for (i = 0; i < frame->nb_samples; i++) {
				//遍历声道
				for (ch = 0; ch < dec_ctx->channels; ch++) {
					fwrite(frame->data[ch] + data_size * i, 1, data_size, ofile);
				}
			}
		}
	}
}

static int get_format_from_sample_fmt(const char** fmt,
	enum AVSampleFormat sample_fmt)
{
	int i;
	struct sample_fmt_entry {
		enum AVSampleFormat sample_fmt; const char* fmt_be, * fmt_le;
	} sample_fmt_entries[] = {
		{ AV_SAMPLE_FMT_U8,  "u8",    "u8"    },
		{ AV_SAMPLE_FMT_S16, "s16be", "s16le" },
		{ AV_SAMPLE_FMT_S32, "s32be", "s32le" },
		{ AV_SAMPLE_FMT_FLT, "f32be", "f32le" },
		{ AV_SAMPLE_FMT_DBL, "f64be", "f64le" },
	};
	*fmt = NULL;

	for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {
		struct sample_fmt_entry* entry = &sample_fmt_entries[i];
		if (sample_fmt == entry->sample_fmt) {
			*fmt = AV_NE(entry->fmt_be, entry->fmt_le);
			return 0;
		}
	}

	fprintf(stderr,
		"sample format %s is not supported as output format\n",
		av_get_sample_fmt_name(sample_fmt));
	return -1;
}

int main(int argc, char* argv[])
{
	AVFormatContext* fmt_ctx = NULL;
	AVCodecContext* i_dex_ctx = NULL, * a_dex_ctx = NULL;
	AVFrame* frame;
	AVPacket* pkt;
	int ret, i;
	int i_stream_index = -1, a_stream_index = -1;

	FILE* i_ofile, * a_ofile;


	//打开输入文件,并为fmt_ctx分配空间
	if (avformat_open_input(&fmt_ctx, INPUT_FILE_NAME, NULL, NULL)) {
		av_log(NULL, AV_LOG_ERROR, "Codec not open source file.\n");
		exit(1);
	}

	//获取流信息
	if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
		av_log(NULL, AV_LOG_ERROR, "Could not find stream information.\n");
		exit(1);
	}

	for (i = 0; i < fmt_ctx->nb_streams; i++) {
		AVStream* in_stream = fmt_ctx->streams[i];
		AVCodecParameters* in_codecpar = in_stream->codecpar;

		if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
			in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
			continue;
		}
		//获取解码器
		AVCodec* codec = avcodec_find_decoder(in_codecpar->codec_id);
		if (!codec) {
			av_log(NULL, AV_LOG_ERROR, "Codec not found.\n");
			exit(1);
		}
		//分配解析器上下文
		AVCodecContext* c = avcodec_alloc_context3(codec);
		if (!c) {
			av_log(NULL, AV_LOG_ERROR, "Could not allocate video codec context.\n");
			exit(1);
		}
		//把输入流的编解码参数复制到我们的解码器上
		if (avcodec_parameters_to_context(c, in_codecpar) < 0) {
			av_log(NULL, AV_LOG_ERROR, "Failed to copy %s codec parameters to decoder context\n");
			exit(1);
		}
		//打开解码器
		if (avcodec_open2(c, codec, NULL) < 0) {
			av_log(NULL, AV_LOG_ERROR, "Could not open codec.\n");
			exit(1);
		}
		//初始化外部变量
		if (in_codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
			a_dex_ctx = c;
			a_stream_index = i;
		}
		else if (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			i_dex_ctx = c;
			i_stream_index = i;
		}

	}
	//如果音视频有一个没有,直接退出程序
	if (i_stream_index == -1 || a_stream_index == -1) {
		av_log(NULL, AV_LOG_ERROR, "video or audio stram index is not find .\n");
		exit(1);
	}

	//分配AVPacket
	pkt = av_packet_alloc();
	if (!pkt) {
		exit(1);
	}

	//分配AVFrame
	frame = av_frame_alloc();
	if (!frame) {
		exit(1);
	}

	//打开输出文件
	//视频
	i_ofile = fopen(OUTPUT_VIDEO_FILE_NAME, "wb+");
	if (!i_ofile) {
		av_log(NULL, AV_LOG_ERROR, "Could not open \s.\n", OUTPUT_VIDEO_FILE_NAME);
		exit(1);
	}
	//音频
	a_ofile = fopen(OUTPUT_AUDIO_FILE_NAME, "wb+");
	if (!a_ofile) {
		av_log(NULL, AV_LOG_ERROR, "Could not open \s.\n", OUTPUT_AUDIO_FILE_NAME);
		exit(1);
	}
	//从文件读取帧
	while (av_read_frame(fmt_ctx, pkt) >= 0) {
		//只处理视频流
		if (pkt->stream_index == i_stream_index) {
			decode(i_dex_ctx, frame, pkt, i_ofile, AVMEDIA_TYPE_VIDEO);
		}
		else if (pkt->stream_index == a_stream_index) {
			decode(a_dex_ctx, frame, pkt, a_ofile, AVMEDIA_TYPE_AUDIO);
		}
		av_packet_unref(pkt);
	}

	//flush 解码器
	decode(i_dex_ctx, frame, NULL, i_ofile, AVMEDIA_TYPE_VIDEO);
	decode(a_dex_ctx, frame, NULL, a_ofile, AVMEDIA_TYPE_AUDIO);


	printf("Demuxing succeeded.\n");

	if (i_stream_index != -1) {
		enum AVPixelFormat pix_fmt = i_dex_ctx->pix_fmt;
		printf("Play the output video file with the command:\n"
			"ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",
			av_get_pix_fmt_name(pix_fmt), i_dex_ctx->width, i_dex_ctx->height,
			OUTPUT_VIDEO_FILE_NAME);
	}

	if (a_stream_index != -1) {
		enum AVSampleFormat sfmt = a_dex_ctx->sample_fmt;
		int n_channels = a_dex_ctx->channels;
		const char* fmt;
		//如果为planar,转换为packed格式
		if (av_sample_fmt_is_planar(sfmt)) {
			const char* packed = av_get_sample_fmt_name(sfmt);
			sfmt = av_get_packed_sample_fmt(sfmt);
			n_channels = 1;
		}

		if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0) {
			exit(1);
		}

		printf("Play the output audio file with the command:\n"
			"ffplay -f %s -ac %d -ar %d %s\n",
			fmt, n_channels, a_dex_ctx->sample_rate,
			OUTPUT_AUDIO_FILE_NAME);
	}

	//资源释放
	fclose(i_ofile);
	fclose(a_ofile);

	avcodec_free_context(&i_dex_ctx);
	avcodec_free_context(&a_dex_ctx);
	av_frame_free(&frame);
	av_packet_free(&pkt);

	return 0;

}

运行该程序,可见下面如下面的打印信息:
在这里插入图片描述
其中音频共1308帧,视频510帧,其中视频是yuv420p的格式,宽高为512*288。音频为一个有1个声道,采样率为44100HZ,采样格式为fltp的文件。

看一下解码出来的文件:
在这里插入图片描述
可见生成了两个文件,一个pcm文件(lh_online.pcm),一个yuv文件(lh_online.yuv)。

下面使用ffplay播放两个文件:
首先播放视频:

ffplay -f f32le -ac 1 -ar 44100 lh_online.pcm

在这里插入图片描述
此时你应该可以看见与MP4一样的视频,可见,视频文件是正常的,证明视频解码成功了。

然后播放音频:

ffplay -f f32le -ac 1 -ar 44100 lh_online.pcm

在这里插入图片描述
此时你应该能听到和MP4文件一致的声音,可见,音频解码成功。

到此,解码部分的内容就已经说完了。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lhuann_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值