Skip to content

Commit 11fe395

Browse files
committed
fixed issue with muxer and the time_base not being updated (because of writing of container header) BEFORE any packets are processed
1 parent 766b89e commit 11fe395

File tree

9 files changed

+155
-13
lines changed

9 files changed

+155
-13
lines changed

source/ffmpeg-cpp/demo/demo.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ void PlayDemo(int argc, char** argv)
3131
const char* containerWithAudioFile = "samples/DesiJourney.wav";
3232

3333
// hard-code the settings here, but let them be overridden by the arguments
34-
string inputAudioSource = "GENERATED"; // options are RAW, ENCODED, CONTAINER, GENERATED
34+
string inputAudioSource = "CONTAINER"; // options are RAW, ENCODED, CONTAINER, GENERATED
3535
string inputVideoSource = "ENCODED"; // options are RAW, ENCODED, CONTAINER, GENERATED
36-
string outputAudioCodec = "MP2"; // options are MP2, AAC, NONE
37-
string outputVideoCodec = "NONE"; // options are H264, H265, VP9, NONE (H264 and H265 only work on Nvidia hardware)
36+
string outputAudioCodec = "AAC"; // options are MP2, AAC, NONE
37+
string outputVideoCodec = "H264"; // options are H264, H265, VP9, NONE (H264 and H265 only work on Nvidia hardware)
3838
string outputContainerName = "out.mp4"; // container format is deduced from extension so use a known one
3939

4040
// you can use any filter string that you can use in the ffmpeg command-line here

source/ffmpeg-cpp/ffmpeg-cpp.sln

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "encode_audio", "encode_audi
1717
EndProject
1818
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "encode_video", "encode_video\encode_video.vcxproj", "{597D5EF8-04DB-48F9-A8F3-4B593B81C7C3}"
1919
EndProject
20+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "remuxing", "remuxing\remuxing.vcxproj", "{B337D322-355B-4348-A2A8-270471BE2C95}"
21+
EndProject
2022
Global
2123
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2224
Debug|x64 = Debug|x64
@@ -203,6 +205,34 @@ Global
203205
{597D5EF8-04DB-48F9-A8F3-4B593B81C7C3}.ReleaseLTO|x64.Build.0 = Release|x64
204206
{597D5EF8-04DB-48F9-A8F3-4B593B81C7C3}.ReleaseLTO|x86.ActiveCfg = Release|Win32
205207
{597D5EF8-04DB-48F9-A8F3-4B593B81C7C3}.ReleaseLTO|x86.Build.0 = Release|Win32
208+
{B337D322-355B-4348-A2A8-270471BE2C95}.Debug|x64.ActiveCfg = Debug|x64
209+
{B337D322-355B-4348-A2A8-270471BE2C95}.Debug|x64.Build.0 = Debug|x64
210+
{B337D322-355B-4348-A2A8-270471BE2C95}.Debug|x86.ActiveCfg = Debug|Win32
211+
{B337D322-355B-4348-A2A8-270471BE2C95}.Debug|x86.Build.0 = Debug|Win32
212+
{B337D322-355B-4348-A2A8-270471BE2C95}.DebugDLL|x64.ActiveCfg = Debug|x64
213+
{B337D322-355B-4348-A2A8-270471BE2C95}.DebugDLL|x64.Build.0 = Debug|x64
214+
{B337D322-355B-4348-A2A8-270471BE2C95}.DebugDLL|x86.ActiveCfg = Debug|Win32
215+
{B337D322-355B-4348-A2A8-270471BE2C95}.DebugDLL|x86.Build.0 = Debug|Win32
216+
{B337D322-355B-4348-A2A8-270471BE2C95}.DebugDLLStaticDeps|x64.ActiveCfg = Debug|x64
217+
{B337D322-355B-4348-A2A8-270471BE2C95}.DebugDLLStaticDeps|x64.Build.0 = Debug|x64
218+
{B337D322-355B-4348-A2A8-270471BE2C95}.DebugDLLStaticDeps|x86.ActiveCfg = Debug|Win32
219+
{B337D322-355B-4348-A2A8-270471BE2C95}.DebugDLLStaticDeps|x86.Build.0 = Debug|Win32
220+
{B337D322-355B-4348-A2A8-270471BE2C95}.Release|x64.ActiveCfg = Release|x64
221+
{B337D322-355B-4348-A2A8-270471BE2C95}.Release|x64.Build.0 = Release|x64
222+
{B337D322-355B-4348-A2A8-270471BE2C95}.Release|x86.ActiveCfg = Release|Win32
223+
{B337D322-355B-4348-A2A8-270471BE2C95}.Release|x86.Build.0 = Release|Win32
224+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseDLL|x64.ActiveCfg = Release|x64
225+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseDLL|x64.Build.0 = Release|x64
226+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseDLL|x86.ActiveCfg = Release|Win32
227+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseDLL|x86.Build.0 = Release|Win32
228+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseDLLStaticDeps|x64.ActiveCfg = Release|x64
229+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseDLLStaticDeps|x64.Build.0 = Release|x64
230+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseDLLStaticDeps|x86.ActiveCfg = Release|Win32
231+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseDLLStaticDeps|x86.Build.0 = Release|Win32
232+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseLTO|x64.ActiveCfg = Release|x64
233+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseLTO|x64.Build.0 = Release|x64
234+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseLTO|x86.ActiveCfg = Release|Win32
235+
{B337D322-355B-4348-A2A8-270471BE2C95}.ReleaseLTO|x86.Build.0 = Release|Win32
206236
EndGlobalSection
207237
GlobalSection(SolutionProperties) = preSolution
208238
HideSolutionNode = FALSE
@@ -212,6 +242,7 @@ Global
212242
{AAD3AB93-F831-4339-8AAD-DC956B9B9233} = {3B1FE419-D7D2-4406-9C24-5A6F6ED63E73}
213243
{9D936BE4-46AA-489F-82E7-D2CFEB157FB6} = {3B1FE419-D7D2-4406-9C24-5A6F6ED63E73}
214244
{597D5EF8-04DB-48F9-A8F3-4B593B81C7C3} = {3B1FE419-D7D2-4406-9C24-5A6F6ED63E73}
245+
{B337D322-355B-4348-A2A8-270471BE2C95} = {3B1FE419-D7D2-4406-9C24-5A6F6ED63E73}
215246
EndGlobalSection
216247
GlobalSection(ExtensibilityGlobals) = postSolution
217248
SolutionGuid = {1838F09B-B929-4D1E-ABB6-FA2A94F4A4BE}

source/ffmpeg-cpp/ffmpeg-cpp/Muxing/AudioOutputStream.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ namespace ffmpegcpp
3535
{
3636
throw FFmpegException("Could not copy codec parameters to stream", ret);
3737
}
38+
39+
codecTimeBase = openCodec->GetContext()->time_base;
3840
}
3941

4042
void AudioOutputStream::WritePacket(AVPacket *pkt, OpenCodec* openCodec)
@@ -45,8 +47,14 @@ namespace ffmpegcpp
4547
initialized = true;
4648
}
4749

50+
// Write the compressed frame to the media file.
51+
SendPacketToMuxer(pkt);
52+
}
53+
54+
void AudioOutputStream::PreparePacketForMuxer(AVPacket* pkt)
55+
{
4856
/* rescale output packet timestamp values from codec to stream timebase */
49-
AVRational* time_base = &openCodec->GetContext()->time_base;
57+
AVRational* time_base = &codecTimeBase;
5058
av_packet_rescale_ts(pkt, *time_base, stream->time_base);
5159
pkt->stream_index = stream->index;
5260

@@ -55,9 +63,6 @@ namespace ffmpegcpp
5563
{
5664
pkt->duration = stream->time_base.den / stream->time_base.num / stream->avg_frame_rate.num * stream->avg_frame_rate.den;
5765
}
58-
59-
/* Write the compressed frame to the media file. */
60-
muxer->WritePacket(pkt);
6166
}
6267

6368
bool AudioOutputStream::IsPrimed()

source/ffmpeg-cpp/ffmpeg-cpp/Muxing/AudioOutputStream.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,18 @@ namespace ffmpegcpp
1919

2020
virtual bool IsPrimed();
2121

22+
protected:
23+
24+
virtual void PreparePacketForMuxer(AVPacket* pkt);
25+
2226
private:
2327

2428
void LazilyInitialize(OpenCodec* openCodec);
2529

2630
AVStream* stream;
2731

2832
bool initialized = false;
33+
34+
AVRational codecTimeBase;
2935
};
3036
}

source/ffmpeg-cpp/ffmpeg-cpp/Muxing/Muxer.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,21 @@ namespace ffmpegcpp
8282

8383
bool Muxer::IsPrimed()
8484
{
85+
if (opened) return true; // we were already opened before - always primed from now on!
8586
bool allPrimed = true;
8687
for (int i = 0; i < outputStreams.size(); ++i)
8788
{
8889
if (!outputStreams[i]->IsPrimed()) allPrimed = false;
8990
}
91+
92+
// we are finally primed - open ourselves before we continue.
93+
if (allPrimed)
94+
{
95+
// if we are all primed
96+
Open();
97+
opened = true;
98+
//printf("After %d cached packets, we can finally open the container\n", packetQueue.size());
99+
}
90100
return allPrimed;
91101
}
92102

@@ -97,14 +107,28 @@ namespace ffmpegcpp
97107
// Because of this, we need to call PreparePipeline on all input sources BEFORE
98108
// we start running the actual data through. This pipeline preparation step
99109
// pushes one frame down the pipeline so that the output can be configured properly.
110+
if (!opened)
111+
{
112+
throw FFmpegException("You cannot submit a packet to the muxer until all output streams are fully primed!");
113+
}
114+
115+
// submit this packet
116+
int ret = av_interleaved_write_frame(containerContext, pkt);
117+
if (ret < 0)
118+
{
119+
throw FFmpegException("Error while writing frame to output container", ret);
120+
}
121+
122+
return;
123+
100124
if (!opened)
101125
{
102126
// we CAN open now - all streams are primed and ready to go!
103127
if (IsPrimed())
104128
{
105129
Open();
106130
opened = true;
107-
//printf("After %d cached packets, we can finally open the container\n", packetQueue.size());
131+
printf("After %d cached packets, we can finally open the container\n", packetQueue.size());
108132

109133
// flush the queue
110134
for (int i = 0; i < packetQueue.size(); ++i)
@@ -171,6 +195,15 @@ namespace ffmpegcpp
171195

172196
void Muxer::Close()
173197
{
198+
// Make sure we drain all the output streams before we write the first packet.
199+
// We must be sure to do this because in an extreme case, one entire stream
200+
// might be queueing all its packets before we are opened, so it might not
201+
// be draining them at all.
202+
for (int i = 0; i < outputStreams.size(); ++i)
203+
{
204+
outputStreams[i]->DrainPacketQueue();
205+
}
206+
174207
// free the stream
175208
CleanUp();
176209
}

source/ffmpeg-cpp/ffmpeg-cpp/Muxing/OutputStream.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,49 @@ namespace ffmpegcpp
1010
this->muxer = muxer;
1111
this->codec = codec;
1212
}
13+
14+
void OutputStream::SendPacketToMuxer(AVPacket* pkt)
15+
{
16+
// if the muxer is primed, we submit the packet for real
17+
if (muxer->IsPrimed())
18+
{
19+
// drain the queue
20+
DrainPacketQueue();
21+
22+
// send this packet
23+
PreparePacketForMuxer(pkt);
24+
muxer->WritePacket(pkt);
25+
}
26+
27+
// otherwise, we queue the packet
28+
else
29+
{
30+
AVPacket* tmp_pkt = av_packet_alloc();
31+
if (!tmp_pkt)
32+
{
33+
throw FFmpegException("Failed to allocate packet");
34+
}
35+
av_packet_ref(tmp_pkt, pkt);
36+
packetQueue.push_back(tmp_pkt);
37+
}
38+
}
39+
40+
void OutputStream::DrainPacketQueue()
41+
{
42+
if (packetQueue.size() > 0) printf("Drain %d packets from the packet queue...", packetQueue.size());
43+
for (int i = 0; i < packetQueue.size(); ++i)
44+
{
45+
AVPacket* tmp_pkt = packetQueue[i];
46+
47+
// Write the compressed frame to the media file
48+
PreparePacketForMuxer(tmp_pkt);
49+
muxer->WritePacket(tmp_pkt);
50+
51+
// Release the packet
52+
av_packet_unref(tmp_pkt);
53+
av_packet_free(&tmp_pkt);
54+
}
55+
56+
packetQueue.clear();
57+
}
1358
}

source/ffmpeg-cpp/ffmpeg-cpp/Muxing/OutputStream.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,21 @@ namespace ffmpegcpp
1818

1919
virtual bool IsPrimed() = 0;
2020

21+
void DrainPacketQueue();
22+
2123
protected:
2224

23-
Muxer* muxer;
25+
virtual void PreparePacketForMuxer(AVPacket* packet) = 0;
26+
27+
void SendPacketToMuxer(AVPacket* packet);
28+
2429

2530
Codec* codec;
31+
32+
std::vector<AVPacket*> packetQueue;
33+
34+
private:
35+
36+
Muxer* muxer;
2637
};
2738
}

source/ffmpeg-cpp/ffmpeg-cpp/Muxing/VideoOutputStream.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ namespace ffmpegcpp
3535
{
3636
throw FFmpegException("Could not copy codec parameters to stream", ret);
3737
}
38+
39+
codecTimeBase = openCodec->GetContext()->time_base;
3840
}
3941

4042
void VideoOutputStream::WritePacket(AVPacket *pkt, OpenCodec* openCodec)
@@ -45,16 +47,19 @@ namespace ffmpegcpp
4547
initialized = true;
4648
}
4749

50+
SendPacketToMuxer(pkt);
51+
}
52+
53+
void VideoOutputStream::PreparePacketForMuxer(AVPacket* pkt)
54+
{
55+
4856
/* rescale output packet timestamp values from codec to stream timebase */
49-
AVRational* time_base = &openCodec->GetContext()->time_base;
57+
AVRational* time_base = &codecTimeBase;
5058
av_packet_rescale_ts(pkt, *time_base, stream->time_base);
5159
pkt->stream_index = stream->index;
5260

5361
// We NEED to fill in the duration here, otherwise the frame rate is calculated wrong in the end for certain codecs/containers (ie h264/mp4).
5462
pkt->duration = stream->time_base.den / stream->time_base.num / stream->avg_frame_rate.num * stream->avg_frame_rate.den;
55-
56-
/* Write the compressed frame to the media file. */
57-
muxer->WritePacket(pkt);
5863
}
5964

6065
bool VideoOutputStream::IsPrimed()

source/ffmpeg-cpp/ffmpeg-cpp/Muxing/VideoOutputStream.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,18 @@ namespace ffmpegcpp
1919

2020
virtual bool IsPrimed();
2121

22+
protected:
23+
24+
virtual void PreparePacketForMuxer(AVPacket* pkt);
25+
2226
private:
2327

2428
void LazilyInitialize(OpenCodec* openCodec);
2529

2630
AVStream* stream;
2731

2832
bool initialized = false;
33+
34+
AVRational codecTimeBase;
2935
};
3036
}

0 commit comments

Comments
 (0)