Mux Audio and Video Into MP4 With Fast Start¶
This topic describes how to use the TTranscoder class to mux AAC audio and H.264 video into an MP4 file with optional fast start (progressive download). The same sample can also remux an existing MP4 file into a fast-start MP4 file. The muxing is done with TTranscoder::run.
The code snippets in this article are from the mux_mp4_fs_file macOS sample.
Linux and Windows samples are also available:
Fast Start¶
By default the moov atom (the index of the media data) is written at the end of the MP4 file, after the mdat atom. Playback cannot start until the whole file has been downloaded.
With fast start enabled, the muxer runs a second pass that moves the moov atom before the mdat atom, so the file can be played while it is still downloading. The sample enables it by setting the Param::Muxer::MP4::FastStart parameter on the output socket. For more details see MP4 muxer parameters.
The sample can:
Mux audio and video into an MP4 file without fast start
Mux audio and video into an MP4 file with fast start
Remux an existing MP4 file into a fast-start MP4 file
Source Media¶
For source we use the big_buck_bunny_trailer.mp4, big_buck_bunny_trailer.aud.mp4 and big_buck_bunny_trailer.vid.mp4 files from the AVBlocks Assets repository. After downloading and unzipping you will find them in the mov, aud and vid subdirectories.
Code¶
The first H.264 video stream and the first AAC audio stream found in the input files are used. All other streams are ignored.
Initialize AVBlocks¶
Initialize the AVBlocks library before muxing, and shut it down when done.
int main(int argc, char* argv[])
{
Options opt;
switch(prepareOptions(opt, argc, argv))
{
case Command: return 0;
case Error: return 1;
case Parsed: break;
}
TLibrary library;
return mux(opt) ? 0 : 1;
}
Create the Transcoder¶
Delete the output file if it exists (the transcoder fails if the output exists, by design), create the transcoder and enable demo mode.
deleteFile(opt.output_file.c_str());
TTranscoder transcoder;
// Transcoder demo mode must be enabled,
// in order to use the production release for testing (without a valid license)
transcoder.allowDemoMode(true);
Configure Input Sockets¶
For each input file, open it with TMediaInfo and create an input socket from the detected streams. The first H.264 video pin and the first AAC audio pin are kept, and every other pin is disabled with PinConnection::Disabled. The same code handles a single MP4 file with both streams and separate audio and video files.
// Use the first H.264 video stream and the first AAC audio stream found in the inputs.
// All other streams are disabled.
bool videoDetected = false;
bool audioDetected = false;
for (const auto& file : opt.input_files)
{
auto input = TMediaSocket(TMediaInfo().file(file).open());
for (int32_t i = 0; i < input.pins().count(); ++i)
{
auto pin = input.pins(i);
const auto streamType = pin.streamInfo().streamType();
if (streamType == StreamType::H264 && !videoDetected)
{
videoDetected = true;
std::println("Muxing video input: {}", file);
}
else if (streamType == StreamType::AAC && !audioDetected)
{
audioDetected = true;
std::println("Muxing audio input: {}", file);
}
else
{
pin.connection(PinConnection::Disabled);
}
}
transcoder.addInput(input);
}
if (!videoDetected && !audioDetected)
{
std::println(stderr, "No H264 video or AAC audio streams found in the input files.");
return false;
}
Configure Output Socket¶
The output socket is an MP4 file. A pin with the matching stream info is added for each stream that was detected in the inputs.
// Configure output
auto output = TMediaSocket()
.file(opt.output_file)
.streamType(StreamType::MP4);
if (videoDetected)
{
output.addPin(TMediaPin()
.streamInfo(TVideoStreamInfo()
.streamType(StreamType::H264)
.streamSubType(StreamSubType::AVC1)));
}
if (audioDetected)
{
output.addPin(TMediaPin()
.streamInfo(TAudioStreamInfo()
.streamType(StreamType::AAC)
.streamSubType(StreamSubType::AAC_MP4)));
}
Enable Fast Start¶
Set the Param::Muxer::MP4::FastStart parameter on the output socket to move the moov atom to the beginning of the file.
// Fast start: run a second pass that moves the moov atom before the mdat atom,
// so that the file can be played while it is being downloaded.
if (opt.fast_start)
{
output.addParam(Param::Muxer::MP4::FastStart, 1);
}
Open the Transcoder and Run¶
Add the output socket, open the transcoder, run the muxing and close the transcoder.
transcoder.addOutput(output).transcode();
std::println("Output file: {}", opt.output_file);
return true;
Complete C++ Code¶
Here’s the complete working example from mux_mp4_fs_file.cpp:
#include <primo/avblocks/avb++.h>
#include <print>
#include <string>
#include "options.h"
#include "util.h"
using namespace primo::codecs;
using namespace primo::avblocks;
using namespace primo::avblocks::modern;
bool mux(const Options& opt)
{
deleteFile(opt.output_file.c_str());
try {
TTranscoder transcoder;
// Transcoder demo mode must be enabled,
// in order to use the production release for testing (without a valid license)
transcoder.allowDemoMode(true);
// Use the first H.264 video stream and the first AAC audio stream found in the inputs.
// All other streams are disabled.
bool videoDetected = false;
bool audioDetected = false;
for (const auto& file : opt.input_files)
{
auto input = TMediaSocket(TMediaInfo().file(file).open());
for (int32_t i = 0; i < input.pins().count(); ++i)
{
auto pin = input.pins(i);
const auto streamType = pin.streamInfo().streamType();
if (streamType == StreamType::H264 && !videoDetected)
{
videoDetected = true;
std::println("Muxing video input: {}", file);
}
else if (streamType == StreamType::AAC && !audioDetected)
{
audioDetected = true;
std::println("Muxing audio input: {}", file);
}
else
{
pin.connection(PinConnection::Disabled);
}
}
transcoder.addInput(input);
}
if (!videoDetected && !audioDetected)
{
std::println(stderr, "No H264 video or AAC audio streams found in the input files.");
return false;
}
// Configure output
auto output = TMediaSocket()
.file(opt.output_file)
.streamType(StreamType::MP4);
if (videoDetected)
{
output.addPin(TMediaPin()
.streamInfo(TVideoStreamInfo()
.streamType(StreamType::H264)
.streamSubType(StreamSubType::AVC1)));
}
if (audioDetected)
{
output.addPin(TMediaPin()
.streamInfo(TAudioStreamInfo()
.streamType(StreamType::AAC)
.streamSubType(StreamSubType::AAC_MP4)));
}
// Fast start: run a second pass that moves the moov atom before the mdat atom,
// so that the file can be played while it is being downloaded.
if (opt.fast_start)
{
output.addParam(Param::Muxer::MP4::FastStart, 1);
}
transcoder.addOutput(output).transcode();
std::println("Output file: {}", opt.output_file);
return true;
} catch (const TAVBlocksException& ex) {
std::println(stderr, "AVBlocks error: {}", ex.what());
return false;
} catch (const std::exception& ex) {
std::println(stderr, "Error: {}", ex.what());
return false;
}
}
int main(int argc, char* argv[])
{
Options opt;
switch(prepareOptions(opt, argc, argv))
{
case Command: return 0;
case Error: return 1;
case Parsed: break;
}
TLibrary library;
return mux(opt) ? 0 : 1;
}
How to Run¶
See the build instructions for macOS and the mux_mp4_fs_file example for details.
Command Line¶
./mux_mp4_fs_file --input <file>.mp4 --output <output>.mp4 [--fast_start]
./mux_mp4_fs_file --input <file>.mp4 --input <file>.mp4 --output <output>.mp4 [--fast_start]
Examples¶
List options:
./bin/x64/mux_mp4_fs_file --help
Usage: mux_mp4_fs_file --input <file1> --input <file2> --output <output.mp4> [--fast_start]
-?, --help
-f, --fast_start move the moov atom to the beginning of the file (fast
start)
-i, --input input file (AAC, H264, MP4). Can be used multiple times
-o, --output output file
Remux the MP4 file big_buck_bunny_trailer.mp4 into the fast-start MP4 file big_buck_bunny_trailer_fast_start.mp4:
mkdir -p ./output/mux_mp4_fs_file
./bin/x64/mux_mp4_fs_file \
--input ./assets/mov/big_buck_bunny_trailer.mp4 \
--output ./output/mux_mp4_fs_file/big_buck_bunny_trailer_fast_start.mp4 \
--fast_start
Mux the MP4 files big_buck_bunny_trailer.aud.mp4 and big_buck_bunny_trailer.vid.mp4 into a fast-start MP4 file:
./bin/x64/mux_mp4_fs_file \
--input ./assets/aud/big_buck_bunny_trailer.aud.mp4 \
--input ./assets/vid/big_buck_bunny_trailer.vid.mp4 \
--output ./output/mux_mp4_fs_file/big_buck_bunny_trailer_mux_fast_start.mp4 \
--fast_start
Run without arguments to use the default input and output files (fast start is enabled):
./bin/x64/mux_mp4_fs_file