FFmpeg合并视频流与音频流

mux.h

 

#ifndef MUX_H
#define MUX_H

#ifdef __cplusplus
extern "C"
{
#endif

#include"common.h"
#include"encode.h"

	typedef struct AVMuxing {
		videoParm *vp;
		AVFormatContext *i_fmt_ctx_v;
		AVFormatContext *i_fmt_ctx_a;
		AVFormatContext *o_fmt_ctx;
		AVCodecContext *enc_ctx_v;
		AVCodecContext *enc_ctx_a;
		const char *in_audio;

		int audio_len;
		int videoindex_v;
		int videoindex_out;
		int audioindex_a;
		int audioindex_out;
	}AVMuxing;

	/*
	*identifier,status, percent
	*/
	typedef void(*AVMuxCallbackFunction)(void*, int, int);

	/*
	* 输入编码时返回的结构体指针,一个音频
	*/
	extern int MuxAudioVideo(videoParm *vp, const char *inAudio);

#ifdef __cplusplus
};
#endif
#endif


mux.cpp

 

 

#include "stdafx.h"

#ifdef __cplusplus
extern "C"
{
#endif

#include<tchar.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include"common.h"

#include"mux.h"
#include"logger.h"

#define CLEAN_AND_EXIT {MuxCallbackFunction((AVMuxCallbackFunction)avm->vp->notification, avm->vp, -1, /*avm->vp->progress*/0);\
						CtxAllFree(avm);\
						return -1;}

#ifdef __cplusplus
};
#endif

/*
* status, -1 for failed, 0 for encode succeed, 1 for setting stop, 2 for calback progress, 3 for mux succeed
*/

/*
* 释放结构体内存
*/
static void CtxAllFree(AVMuxing *avm);

/*
* 计算进度
*/
static int CurrentPercent(int cnt, int total);

static void MuxCallbackFunction(AVMuxCallbackFunction fcn, void *identifier, int status, int percent)
{
	if (fcn != NULL) {
		fcn(identifier, status, percent);
	}
	else {
		//LogPrint();
	}
}


OpenInputFile:

 

 

 

 

/*
* 判断输入音频是否有效
*/
static int OpenInputFile(const char *filename)
{
	FILE *fp;
	fp = fopen(filename, "rb");
	if (fp == NULL) {
		LOG_PRINT("%s....Audio fopen failed", __FUNCTION__);
		return -1;
	}
	/* fp如果为空,feek会出错,根本走不到判断audio_len那里 */
	fseek(fp, 0L, SEEK_END);
	int len = ftell(fp);
	/* 即时是局部变量,也调用fclose */
	fclose(fp);
	return len;
}


copy_file_thread:

 

 

 

 

/* 将临时文件拷贝到目的路径,用在没有音频的时候 */
void* copy_file_thread(void *arg)
{
	__try {
		int ret;
		LOG_PRINT("%s...1", __FUNCTION__);
		AVMuxing *avm = (AVMuxing*)arg;
		LOG_PRINT("%s....tmp_path: %s, path: %s", __FUNCTION__, avm->vp->tmp_path, avm->vp->path);
		bool flag = CopyFileA(avm->vp->tmp_path, avm->vp->path, FALSE);
		if (!flag) {
			TCHAR szBuf[128];
			LPVOID lpMsgBuf;
			DWORD dw = GetLastError();
			FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				NULL,
				dw,
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
				(LPTSTR)&lpMsgBuf,
				0, NULL);
			wsprintf(szBuf,
				_T("%s error message (error code = %d): %s"),
				_T("CopyFileA"), dw, lpMsgBuf);
			LocalFree(lpMsgBuf);
			LOG_PRINT("%s...%s", __FUNCTION__, szBuf);
			MuxCallbackFunction((AVMuxCallbackFunction)avm->vp->notification, avm->vp, -1, avm->vp->progress);
			CtxAllFree(avm);
			return NULL;
		}

		LOG_PRINT("%s....======copy_file_thread end ======!", __FUNCTION__);
		MuxCallbackFunction((AVMuxCallbackFunction)avm->vp->notification, avm->vp, 3, 100);
		CtxAllFree(avm);
	}
	__except (EXCEPTION_EXECUTE_HANDLER) {
		LOG_PRINT("%s error\n", __FUNCTION__);
	}
	return NULL;
}


mux_thread:

 

 

 

 

/*
* 将音频和视频复用,判断音频包和视频包的pts,pts在前的先写入输出文件
* 当音频包没了,继续写入视频包
*/
void* mux_thread(void* avmuxing)
{
	__try {
		LOG_PRINT("%s...1", __FUNCTION__);
		AVMuxing *avm = (AVMuxing*)avmuxing;
		AVPacket pkt;
		int frame_index = 0;
		int64_t cur_pts_v = 0, cur_pts_a = 0;
		bool flag = FALSE;
		int compare_tag = -1;
		bool audio_pkt_done = FALSE;	//音频包写完标志,即使音频包写完了还要继续写视频包
		int pkt_cnt = 1;
		int i_v_nb_frames = avm->vp->fmt_ctx->streams[0]->nb_frames;

		while (1) {
			pthread_mutex_lock(&(avm->vp->pp->mutex_t));
			if (avm->vp->is_stop) {
				flag = TRUE;
			}
			pthread_mutex_unlock(&(avm->vp->pp->mutex_t));
			if (flag) {
				break;
			}
			int stream_index = 0;
			AVFormatContext *i_fmt_ctx;
			AVStream *in_stream, *out_stream;

			/* 比较音频包和视频包的pts */
			if (!audio_pkt_done) {
				if (avm->audio_len > 0) {
					compare_tag = av_compare_ts(cur_pts_v, avm->i_fmt_ctx_v->streams[avm->videoindex_v]->time_base,
						cur_pts_a, avm->i_fmt_ctx_a->streams[avm->audioindex_a]->time_base);
				}
			}
			if (compare_tag <= 0) {		/* 视频包在前,写入视频包 */
				i_fmt_ctx = avm->i_fmt_ctx_v;
				stream_index = avm->videoindex_out;

				if (av_read_frame(i_fmt_ctx, &pkt) >= 0) {
					do {
						in_stream = i_fmt_ctx->streams[pkt.stream_index];
						out_stream = avm->o_fmt_ctx->streams[stream_index];
						//out_stream->time_base = { 1, vp->fps };		//force out_stream to AVCodecContext->fps
						if (pkt.stream_index == avm->videoindex_v) {
							if (pkt.pts == AV_NOPTS_VALUE) {
								AVRational time_base1 = in_stream->time_base;
								int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
								pkt.pts = (double)(frame_index * calc_duration) / (double)(av_q2d(time_base1) * AV_TIME_BASE);
								pkt.dts = pkt.pts;
								pkt.duration = (double)calc_duration / (double)(av_q2d(time_base1) * AV_TIME_BASE);
								frame_index++;
							}
							cur_pts_v = pkt.pts;

							/* 在此回调进度 */
							avm->vp->progress = CurrentPercent(++pkt_cnt, i_v_nb_frames);
							MuxCallbackFunction((AVMuxCallbackFunction)avm->vp->notification, avm->vp, 2, avm->vp->progress);
							break;
						}

					} while (av_read_frame(i_fmt_ctx, &pkt) >= 0);
				}
				else {
					LOG_PRINT("%s....av_read_frame < 0", __FUNCTION__);
					break;
				}
			}
			else {		/* 音频包在前,写入音频包 */
				i_fmt_ctx = avm->i_fmt_ctx_a;
				stream_index = avm->audioindex_out;
				if (av_read_frame(i_fmt_ctx, &pkt) >= 0) {
					do {
						in_stream = i_fmt_ctx->streams[pkt.stream_index];
						out_stream = avm->o_fmt_ctx->streams[stream_index];
						if (pkt.stream_index == avm->audioindex_a) {
							//TODO:add audio filter here to format pkt!
							if (pkt.pts == AV_NOPTS_VALUE) {
								AVRational time_base1 = in_stream->time_base;
								int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
								pkt.pts = (double)(frame_index * calc_duration) / (double)(av_q2d(time_base1) * AV_TIME_BASE);
								pkt.dts = pkt.pts;
								pkt.duration = (double)calc_duration / (double)(av_q2d(time_base1) * AV_TIME_BASE);
								frame_index++;
							}
							cur_pts_a = pkt.pts;
							break;
						}
					} while (av_read_frame(i_fmt_ctx, &pkt) >= 0);
				}
				else {
					//break;	/* 如果音频比视频短,不break,以视频为标准 */
					/* 音频包写完后,继续写完视频包 */
					audio_pkt_done = TRUE;
					compare_tag = -1;
					continue;
				}
			}

			//Convert PTS/DTS
			pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
			pkt.pos = -1;
			pkt.stream_index = stream_index;
#ifdef DEBUG
			printf("Write 1 Packet. Size:%5d\tpts:%5lld\tdts:%5lld\n", pkt.size, pkt.pts, pkt.dts);
#endif
			//Write packet to file
			if (av_interleaved_write_frame(avm->o_fmt_ctx, &pkt) < 0) {
				LOG_PRINT("%s....av_interleaved_write_frame failed", __FUNCTION__);
				MuxCallbackFunction((AVMuxCallbackFunction)avm->vp->notification, avm->vp, -1, avm->vp->progress);
				CtxAllFree(avm);
				return NULL;
				//break;
			}
			av_free_packet(&pkt);
		}

		pthread_mutex_lock(&(avm->vp->pp->mutex_t));
		if (avm->vp->is_stop) {
			flag = TRUE;
		}
		pthread_mutex_unlock(&(avm->vp->pp->mutex_t));
		if (flag) {
			MuxCallbackFunction((AVMuxCallbackFunction)avm->vp->notification, avm->vp, 1, avm->vp->progress);
			CtxAllFree(avm);
			return NULL;
		}
		//Write file trailer
		av_write_trailer(avm->o_fmt_ctx);

#ifdef DEBUG
		printf("======muxer success ======!\n");
#endif
		LOG_PRINT("%s....======mux_thread end ======!", __FUNCTION__);
		MuxCallbackFunction((AVMuxCallbackFunction)avm->vp->notification, avm->vp, 3, 100);
		CtxAllFree(avm);
	}
	__except (EXCEPTION_EXECUTE_HANDLER) {
		LOG_PRINT("%s error\n", __FUNCTION__);
	}
	return NULL;
}


MuxAudioVideo:

 

 

 

 

int MuxAudioVideo(videoParm *vp, const char *inAudio)
{
	LOG_PRINT("%s....width = %d, height = %d, fps = %d, format = %s, outpath = %s", __FUNCTION__,
		vp->width, vp->height, vp->fps, vp->format, vp->path);
	LOG_PRINT("%s....1", __FUNCTION__);

	AVMuxing *avm = (AVMuxing*)malloc(sizeof(AVMuxing));
	memset(avm, 0, sizeof(AVMuxing));

	avm->vp = vp;
	avm->i_fmt_ctx_a = NULL;
	avm->i_fmt_ctx_v = NULL;
	avm->o_fmt_ctx = NULL;
	avm->enc_ctx_v = NULL;
	avm->enc_ctx_a = NULL;
	avm->in_audio = inAudio;

	/*
	* 当外部没有音频(或生成音频失败)则创建拷贝文件线程,将临时文件拷贝到目的路径
	*/
	if (!strcmp("", inAudio)) {
		pthread_create(&avm->vp->pp->tid, NULL, copy_file_thread, avm);
		/* 线程退出时自动释放资源 */
		pthread_detach(avm->vp->pp->tid);
		LOG_PRINT("%s....Create copy file thread", __FUNCTION__);
		return 0;
	}

	AVOutputFormat *o_fmt;

	int ret = -1, i = -1;
	avm->videoindex_v = -1;
	avm->videoindex_out = -1;
	avm->audioindex_a = -1;
	avm->audioindex_out = -1;
	bool flag = FALSE;
	const char *in_video = avm->vp->tmp_path;
	const char *in_audio = avm->in_audio;
	const char *out_file = avm->vp->path;

#ifdef DEBUG
	printf("=========input video: %s\n", in_video);
	printf("=========input audio: %s\n", in_audio);
#endif
	if ((avm->audio_len = OpenInputFile(in_audio)) == -1) {
		LOG_PRINT("%s....OpenInputFile failed", __FUNCTION__, ret);
		CLEAN_AND_EXIT
	}
	/* 注册所有编解码器和协议 */
	av_register_all();
	/* 打开输入音频文件, 返回AVFormatContext */
	if ((ret = avformat_open_input(&avm->i_fmt_ctx_a, in_audio, NULL, NULL)) < 0) {
		LOG_PRINT("%s....audio avformat_open_input failed,error code is: %d", __FUNCTION__, ret);
		CLEAN_AND_EXIT
	}
	/* 查找音频流信息 */
	if ((ret = avformat_find_stream_info(avm->i_fmt_ctx_a, NULL)) < 0) {
		LOG_PRINT("%s....audio avformat_find_stream_info failed,error code is: %d", __FUNCTION__, ret);
		if (avm->audio_len > 0) {
			LOG_PRINT("%s....Audio is Null", __FUNCTION__);
			CLEAN_AND_EXIT
		}
	}
	if ((ret = avformat_open_input(&avm->i_fmt_ctx_v, in_video, NULL, NULL)) < 0) {
		LOG_PRINT("%s....video avformat_open_input failed,error code is: %d", __FUNCTION__, ret);
		CLEAN_AND_EXIT
	}
	if ((ret = avformat_find_stream_info(avm->i_fmt_ctx_v, NULL)) < 0) {
		LOG_PRINT("%s....video avformat_find_stream_info failed,error code is: %d", __FUNCTION__, ret);
		CLEAN_AND_EXIT
	}
#ifdef DEBUG
	printf("========================Input Infomation==================================\n");
	av_dump_format(avm->i_fmt_ctx_v, 0, in_video, 0);
	av_dump_format(avm->i_fmt_ctx_a, 0, in_audio, 0);
	printf("==========================================================================\n");
#endif
	LOG_PRINT("%s....width = %d, height = %d, fps = %d, format = %s, outpath = %s", __FUNCTION__,
		avm->vp->width, avm->vp->height, avm->vp->fps, avm->vp->format, avm->vp->path);
	ret = avformat_alloc_output_context2(&avm->o_fmt_ctx, NULL, NULL, out_file);
	if (ret < 0) {
		LOG_PRINT("%s....avformat_alloc_output_context2 failed, error code: %d, out file is: %s", __FUNCTION__, ret, out_file);
		CLEAN_AND_EXIT
	}
	o_fmt = avm->o_fmt_ctx->oformat;

	for (i = 0; i < avm->i_fmt_ctx_v->nb_streams; i++) {
		pthread_mutex_lock(&(avm->vp->pp->mutex_t));
		if (avm->vp->is_stop) {
			flag = TRUE;
		}
		pthread_mutex_unlock(&(avm->vp->pp->mutex_t));
		if (flag) {
			break;
		}
		if (avm->i_fmt_ctx_v->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			AVStream *in_stream = avm->i_fmt_ctx_v->streams[i];
			avm->enc_ctx_v = avcodec_alloc_context3(NULL);
			if ((ret = avcodec_parameters_to_context(avm->enc_ctx_v, in_stream->codecpar)) < 0) {
				LOG_PRINT("%s....Video avcodec_parameters_to_context failed,error code is: %d", __FUNCTION__, ret);
				CLEAN_AND_EXIT
			}
			AVStream *out_stream = avformat_new_stream(avm->o_fmt_ctx, avm->enc_ctx_v->codec);
			if (!out_stream) {
				LOG_PRINT("%s....Video avformat_new_stream failed", __FUNCTION__);
				CLEAN_AND_EXIT
			}
			//out_stream->time_base = { 1, avm->vp->fps };		//force out_stream to AVCodecContext->fps
			out_stream->time_base.num = 1;
			out_stream->time_base.den = avm->vp->fps;
			avm->videoindex_v = i;
			avm->videoindex_out = out_stream->index;

			avm->enc_ctx_v->codec_tag = 0;
			if (o_fmt->flags & AVFMT_GLOBALHEADER) {
				avm->enc_ctx_v->flags |= CODEC_FLAG_GLOBAL_HEADER;
			}
			if ((ret = avcodec_parameters_from_context(out_stream->codecpar, avm->enc_ctx_v)) < 0) {
				LOG_PRINT("%s....Video avcodec_parameters_from_context,error code is: %d", __FUNCTION__, ret);
				//CLEAN_AND_EXIT
			}
			break;
		}
	}

	if (avm->audio_len > 0) {
		for (i = 0; i < avm->i_fmt_ctx_a->nb_streams; i++) {
			pthread_mutex_lock(&(avm->vp->pp->mutex_t));
			if (avm->vp->is_stop) {
				flag = TRUE;
			}
			pthread_mutex_unlock(&(avm->vp->pp->mutex_t));
			if (flag) {
				break;
			}
#ifdef DEBUG
			printf("===========streams number:%3d===========\n", avm->i_fmt_ctx_a->nb_streams);
#endif
			if (avm->i_fmt_ctx_a->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
				AVStream *in_stream = avm->i_fmt_ctx_a->streams[i];
				avm->enc_ctx_a = avcodec_alloc_context3(NULL);
				if ((ret = avcodec_parameters_to_context(avm->enc_ctx_a, in_stream->codecpar)) < 0) {
					LOG_PRINT("%s....Audio avcodec_parameters_to_context failed,error code is: %d", __FUNCTION__, ret);
					CLEAN_AND_EXIT
				}
				AVStream *out_stream = avformat_new_stream(avm->o_fmt_ctx, avm->enc_ctx_a->codec);
				if (!out_stream) {
					LOG_PRINT("%s....Audio avformat_new_stream failed", __FUNCTION__);
					CLEAN_AND_EXIT
				}
				avm->audioindex_a = i;
				avm->audioindex_out = out_stream->index;

				out_stream->codecpar->codec_tag = 0;
				if (o_fmt->flags & AVFMT_GLOBALHEADER) {
					avm->enc_ctx_a->flags |= CODEC_FLAG_GLOBAL_HEADER;
				}
				if ((ret = avcodec_parameters_from_context(out_stream->codecpar, avm->enc_ctx_a)) < 0) {
					LOG_PRINT("%s....Audio avcodec_parameters_from_context,error code is: %d", __FUNCTION__, ret);
					CLEAN_AND_EXIT
				}
				break;
			}
		}
	}
#ifdef DEBUG
	printf("========Output Infomation=======\n");
	av_dump_format(avm->o_fmt_ctx, 0, out_file, 1);
	printf("================================\n");
#endif

	//Open output file
	if (!(o_fmt->flags & AVFMT_NOFILE)) {
		if ((ret = avio_open(&avm->o_fmt_ctx->pb, out_file, AVIO_FLAG_WRITE)) < 0) {
			LOG_PRINT("%s....avio_open failed,error code is: %d", __FUNCTION__, ret);
			CLEAN_AND_EXIT
		}
	}

	if (avm->o_fmt_ctx->pb == NULL)
		LOG_PRINT("%s....avm->o_fmt_ctx->pb == NULL", __FUNCTION__);
	if (avm->o_fmt_ctx->oformat == NULL)
		LOG_PRINT("%s....avm->o_fmt_ctx->oformat == NULL", __FUNCTION__);

	//Write file header
	int header_ret = avformat_write_header(avm->o_fmt_ctx, NULL);
	if (header_ret < 0) {
		if (avm->o_fmt_ctx == NULL) {
			LOG_PRINT("%s....avm->o_fmt_ctx is NULL", __FUNCTION__);
		}
		LOG_PRINT("%s....avformat_write_header failed,error code is: %d", __FUNCTION__, header_ret);
		CLEAN_AND_EXIT
	}

	/*
	* 创建复用线程
	*/
	pthread_create(&avm->vp->pp->tid, NULL, mux_thread, avm);
	/* 线程退出时自动释放资源 */
	pthread_detach(avm->vp->pp->tid);
	LOG_PRINT("%s....Create mux thread", __FUNCTION__);
	return 0;
}


CtxAllFree:

 

 

 

 

static void CtxAllFree(AVMuxing *avm)
{
	__try {
		if (avm->enc_ctx_a) {
			avcodec_close(avm->enc_ctx_a);
		}
		if (avm->enc_ctx_v) {
			avcodec_close(avm->enc_ctx_v);
		}
		if (avm->i_fmt_ctx_a) {
			avformat_close_input(&avm->i_fmt_ctx_a);
		}
		if (avm->i_fmt_ctx_v) {
			avformat_close_input(&avm->i_fmt_ctx_v);
		}
		if (avm->o_fmt_ctx) {
			avio_close(avm->o_fmt_ctx->pb);
			avformat_free_context(avm->o_fmt_ctx);
		}
		if (avm) {
			free(avm);
		}
		/* 设为NULL,避免使用野指针 */
		avm = NULL;
	}
	__except (EXCEPTION_EXECUTE_HANDLER) {
		LOG_PRINT("%s error\n", __FUNCTION__);
	}
}


CurrentPercent:

 

 

 

 

int CurrentPercent(int cnt, int total)
{
	int ret = -1;
	ret = (int)((float)cnt / (float)total * 100.0);
	printf("Progress is %d\n", ret);

	return ret;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值