FFmpeg4入门系列教程23:音视频mp3和h264混合(muxer)编码为mp4

索引地址:系列教程索引地址

上一篇:FFmpeg4入门系列教程22:音视频解混合(demuxer)为PCM和YUV420P

解混合的基本操作介绍完了,接下来我们来看一下混合的操作。

先看一下流程图

flow

然后看一下函数调用图

functions

首先打开输入软件,查找两条流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//打开输入视频文件
ret = avformat_open_input(&ifmtCtxVideo, inFilenameVideo, 0, 0);
if (ret < 0)
{
printf("can't open input video file\n");
goto end;
}

//查找输入流
ret = avformat_find_stream_info(ifmtCtxVideo, 0);
if (ret < 0)
{
printf("failed to retrieve input video stream information\n");
goto end;
}

//打开输入音频文件
ret = avformat_open_input(&ifmtCtxAudio, inFilenameAudio, 0, 0);
if (ret < 0)
{
printf("can't open input audio file\n");
goto end;
}

//查找输入流
ret = avformat_find_stream_info(ifmtCtxAudio, 0);
if (ret < 0)
{
printf("failed to retrieve input audio stream information\n");
goto end;
}

分配输出的上下文

1
2
3
4
5
6
7
//新建输出上下文
avformat_alloc_output_context2(&ofmtCtx, NULL, NULL, outFilename);
if (!ofmtCtx)
{
printf("can't create output context\n");
goto end;
}

将现有的流数据复制给输出的两条流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//视频输入流
for (i = 0; i < ifmtCtxVideo->nb_streams; ++i)
{
if (ifmtCtxVideo->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
AVStream *inStream = ifmtCtxVideo->streams[i];
AVStream *outStream = avformat_new_stream(ofmtCtx, NULL);
inVideoIndex = i;

if (!outStream)
{
printf("failed to allocate output stream\n");
goto end;
}

outVideoIndex = outStream->index;

if (avcodec_parameters_copy(outStream->codecpar, inStream->codecpar) < 0)
{
printf("faild to copy context from input to output stream");
goto end;
}

outStream->codecpar->codec_tag = 0;

break;
}
}

//音频输入流
for (i = 0; i < ifmtCtxAudio->nb_streams; ++i)
{
if (ifmtCtxAudio->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
AVStream *inStream = ifmtCtxAudio->streams[i];
AVStream *outStream = avformat_new_stream(ofmtCtx, NULL);
inAudioIndex = i;

if (!outStream)
{
printf("failed to allocate output stream\n");
goto end;
}

outAudioIndex = outStream->index;

if (avcodec_parameters_copy(outStream->codecpar, inStream->codecpar) < 0)
{
printf("faild to copy context from input to output stream");
goto end;
}

outStream->codecpar->codec_tag = 0;

break;
}
}

最为关键的转换的步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
AVFormatContext *ifmtCtx = NULL;
AVStream *inStream, *outStream;
int streamIndex = 0;

if (av_compare_ts(curPstVideo, ifmtCtxVideo->streams[inVideoIndex]->time_base, curPstAudio, ifmtCtxAudio->streams[inAudioIndex]->time_base) < 0)
{
ifmtCtx = ifmtCtxVideo;
streamIndex = outVideoIndex;

if (av_read_frame(ifmtCtx, &packet) >= 0)
{
do
{
inStream = ifmtCtx->streams[packet.stream_index];
outStream = ofmtCtx->streams[streamIndex];

if (packet.stream_index == inVideoIndex)
{
//Fix: No PTS(Example: Raw H.264
//Simple Write PTS
if (packet.pts == AV_NOPTS_VALUE)
{
//write PTS
AVRational timeBase1 = inStream->time_base;
//Duration between 2 frames
int64_t calcDuration = (double)AV_TIME_BASE/av_q2d(inStream->r_frame_rate);
//Parameters
packet.pts = (double)(frameIndex*calcDuration)/(double)(av_q2d(timeBase1)*AV_TIME_BASE);
packet.dts = packet.pts;
packet.duration = (double)calcDuration/(double)(av_q2d(timeBase1)*AV_TIME_BASE);
frameIndex++;
}

curPstVideo = packet.pts;
break;
}
}while(av_read_frame(ifmtCtx, &packet) >= 0);
}
else
{
break;
}
}
else
{
ifmtCtx = ifmtCtxAudio;
streamIndex = outAudioIndex;

if (av_read_frame(ifmtCtx, &packet) >= 0)
{
do
{
inStream = ifmtCtx->streams[packet.stream_index];
outStream = ofmtCtx->streams[streamIndex];

if (packet.stream_index == inAudioIndex)
{
//Fix: No PTS(Example: Raw H.264
//Simple Write PTS
if (packet.pts == AV_NOPTS_VALUE)
{
//write PTS
AVRational timeBase1 = inStream->time_base;
//Duration between 2 frames
int64_t calcDuration = (double)AV_TIME_BASE/av_q2d(inStream->r_frame_rate);
//Parameters
packet.pts = (double)(frameIndex*calcDuration)/(double)(av_q2d(timeBase1)*AV_TIME_BASE);
packet.dts = packet.pts;
packet.duration = (double)calcDuration/(double)(av_q2d(timeBase1)*AV_TIME_BASE);
frameIndex++;
}

curPstAudio = packet.pts;
break;
}
}while(av_read_frame(ifmtCtx, &packet) >= 0);
}
else
{
break;
}
}

//FIX:Bitstream Filter
#if USE_H264BSF
av_bitstream_filter_filter(h264bsfc, inStream->codec, NULL, &packet.data, &packet.size, packet.data, packet.size, 0);
#endif
#if USE_AACBSF
av_bitstream_filter_filter(aacbsfc, outStream->codec, NULL, &packet.data, &packet.size, packet.data, packet.size, 0);
#endif

//Convert PTS/DTS
packet.pts = av_rescale_q_rnd(packet.pts, inStream->time_base, outStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
packet.dts = av_rescale_q_rnd(packet.dts, inStream->time_base, outStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
packet.duration = av_rescale_q(packet.duration, inStream->time_base, outStream->time_base);
packet.pos = -1;
packet.stream_index = streamIndex;

//write
if (av_interleaved_write_frame(ofmtCtx, &packet) < 0)
{
printf("error muxing packet");
break;
}

av_packet_unref(&packet);

将第21篇输出的结果文件拿过来测试一下。

result

播放测试

result

完整代码在ffmpeg_Beginner中的23.video_muxer_mp3h2642mp4

下一篇:FFmpeg4入门系列教程24:搭建UDP/TCP/HTTP(S)/RTP/RTMP/RTSP推流服务器