138 const char*
mimeVideo()
override {
return "video/mp4"; }
146 bool isValid(
const uint8_t* data,
size_t len)
override {
147 return len >= 8 && memcmp(data + 4,
"ftyp", 4) == 0;
253 size_t write(
const uint8_t* data,
size_t len)
override {
259 size_t feed_len = len;
260 bool truncated =
false;
266 size_t written = feed_len > 0 ?
parser.write(data, feed_len) : 0;
271 return (truncated && written == feed_len) ? len : written;
292 uint64_t pos = 0, moov_offset = 0, moov_size = 0, mdat_offset = 0,
294 bool moov_found =
false, mdat_found =
false;
295 const int max_top_level_boxes = 64;
297 for (
int i = 0; i < max_top_level_boxes; i++) {
301 uint64_t box_size =
readU32(hdr);
305 if (box_size < 8)
break;
307 if (memcmp(hdr + 4,
"moov", 4) == 0) {
309 moov_size = box_size;
311 }
else if (memcmp(hdr + 4,
"mdat", 4) == 0) {
313 mdat_size = box_size;
316 if (moov_found && mdat_found)
break;
320 if (!moov_found || !mdat_found || moov_offset < mdat_offset) {
324 LOGI(
"DemuxerMP4::quickStart: moov at %u (size %u), mdat at %u - "
325 "parsing moov directly",
326 (
unsigned)moov_offset, (
unsigned)moov_size, (
unsigned)mdat_offset);
330 parser.begin(moov_offset);
332 uint64_t remaining = moov_size;
333 uint64_t off = moov_offset;
334 while (remaining > 0) {
335 size_t chunk = (size_t)std::min<uint64_t>(remaining,
sizeof(buf));
347 parser.begin(mdat_offset);
451 for (
size_t i = 0; i <
tracks.size(); i++)
delete tracks[i];
548 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
553 static uint16_t
readU16(
const uint8_t* p) {
return (p[0] << 8) | p[1]; }
572 parser.setReference(
this);
738 const uint8_t* handler =
box_accum.data() + 8;
739 if (memcmp(handler,
"vide", 4) == 0) {
742 }
else if (memcmp(handler,
"soun", 4) == 0) {
811 if (entrySize < 28)
return;
814 info.bits_per_sample = 16;
858 LOGE(
"Failed to parse esds box");
907 LOGE(
"HEVC (hev1/hvc1) video is not supported - only H.264 (avc1)");
915 const uint8_t* d = box.
data;
917 int numSps = d[5] & 0x1F;
919 auto appendUnit = [&](
const uint8_t* unit,
size_t len) {
920 static const uint8_t startCode[4] = {0, 0, 0, 1};
922 size_t off = v.size();
923 v.resize(off + 4 + len);
924 memcpy(v.data() + off, startCode, 4);
925 memcpy(v.data() + off + 4, unit, len);
927 for (
int i = 0; i < numSps && pos + 2 <= box.
data_size; i++) {
928 uint16_t len =
readU16(d + pos);
931 appendUnit(d + pos, len);
935 int numPps = d[pos++];
936 for (
int i = 0; i < numPps && pos + 2 <= box.
data_size; i++) {
937 uint16_t len =
readU16(d + pos);
940 appendUnit(d + pos, len);
960 size_t timescaleOffset = version == 1 ? 20 : 12;
961 if (
box_accum.available() >= timescaleOffset + 4) {
1003 LOGD(
"DemuxerMP4: stts entryCount=%u", (
unsigned)entryCount);
1004 const uint8_t* p =
box_accum.data() + 8;
1005 size_t avail =
box_accum.available() - 8;
1006 for (uint32_t i = 0; i < entryCount && (i + 1) * 8 <= avail; i++) {
1014 LOGD(
"DemuxerMP4: stts parsed %u entries", (
unsigned)
current_track->stts->size());
1038 LOGD(
"DemuxerMP4: stsz sampleSize=%u sampleCount=%u", (
unsigned)sampleSize,
1039 (
unsigned)sampleCount);
1042 if (sampleSize != 0) {
1063 LOGD(
"DemuxerMP4: stsz sample_sizes progress: %u", (
unsigned)n);
1067 LOGD(
"DemuxerMP4: stsz complete, %u sample_sizes",
1083 LOGD(
"DemuxerMP4: stsc entryCount=%u", (
unsigned)entryCount);
1084 const uint8_t* p =
box_accum.data() + 8;
1085 size_t avail =
box_accum.available() - 8;
1086 for (uint32_t i = 0; i < entryCount && (i + 1) * 12 <= avail; i++) {
1094 LOGD(
"DemuxerMP4: stsc parsed %u entries", (
unsigned)
current_track->stsc->size());
1108 LOGD(
"DemuxerMP4: %s entryCount=%u", is64 ?
"co64" :
"stco",
1109 (
unsigned)entryCount);
1113 current_track->chunk_offsets->setOnDiskEntrySize(is64 ? 8 : 4);
1114 const uint8_t* p =
box_accum.data() + 8;
1115 size_t avail =
box_accum.available() - 8;
1116 size_t entrySize = is64 ? 8 : 4;
1117 for (uint32_t i = 0; i < entryCount && (i + 1) * entrySize <= avail; i++) {
1124 LOGD(
"DemuxerMP4: chunk_offsets progress: %u", (
unsigned)n);
1126 LOGD(
"DemuxerMP4: stco parsed %u chunk_offsets",
1136 LOGI(
"DemuxerMP4: mdat reached");
1147 if (t.
stsc->size() == 0)
return 0;
1148 uint32_t chunk1 = chunkIndex0 + 1;
1166 Track* next =
nullptr;
1167 uint64_t best_offset = 0;
1176 if (next ==
nullptr || off < best_offset) {
1181 if (next ==
nullptr) {
1187 if (samples == 0)
continue;
1197 void feed(
const uint8_t* data,
size_t len) {
1221 size_t take = std::min(need, len - pos);
1234 LOGI(
"DemuxerMP4: dispatched %u samples so far",
1270 bool prependConfig =
1272 if (prependConfig) {
1281 while (pos + lenSize <= size) {
1282 uint32_t nalLen = 0;
1283 for (
size_t i = 0; i < lenSize; i++)
1284 nalLen = (nalLen << 8) | data[pos + i];
1286 if (pos + nalLen > size)
break;
1288 static const uint8_t startCode[4] = {0, 0, 0, 1};
1289 nal_tmp.resize(off + 4 + nalLen);
1290 memcpy(
nal_tmp.data() + off, startCode, 4);
1291 memcpy(
nal_tmp.data() + off + 4, data + pos, nalLen);
1329 int channelCfg,
int frameLen) {
1332 adts[2] = ((aacProfile - 1) << 6) | (sampleRateIdx << 2) |
1333 ((channelCfg >> 2) & 0x1);
1334 adts[3] = ((channelCfg & 0x3) << 6) | ((frameLen + 7) >> 11);
1335 adts[4] = ((frameLen + 7) >> 3) & 0xFF;
1336 adts[5] = (((frameLen + 7) & 0x7) << 5) | 0x1F;
1355 u8((uint8_t)(v >> 8));
1359 u8((uint8_t)(v >> 16));
1360 u8((uint8_t)(v >> 8));
1364 u8((uint8_t)(v >> 24));
1365 u8((uint8_t)(v >> 16));
1366 u8((uint8_t)(v >> 8));
1371 size_t off =
buffer.size();
1372 buffer.resize(off + len);
1376 size_t off =
buffer.size();
1377 buffer.resize(off + len);
1378 memset(
buffer.data() + off, 0, len);
1381 while (*s)
u8((uint8_t)*s++);
1388 size_t pos =
buffer.size();
1396 uint32_t sz = (uint32_t)(
buffer.size() - pos);
1397 buffer[pos] = (uint8_t)(sz >> 24);
1398 buffer[pos + 1] = (uint8_t)(sz >> 16);
1399 buffer[pos + 2] = (uint8_t)(sz >> 8);
1400 buffer[pos + 3] = (uint8_t)sz;
1530 if (
p_out ==
nullptr) {
1531 LOGE(
"output not defined");
1545 "unsupported video format: %d - MuxerMP4 writes H264, MJPEG, "
1546 "YUV422, RGB565 or I420",
1600 size_t write(
const uint8_t* data,
size_t len)
override {
1651 LOGW(
"dropping video frame: SPS/PPS not seen yet");
1656 if (nalLen == 0)
return;
1657 uint8_t nalType = nal[0] & 0x1F;
1658 if (nalType == 7 || nalType == 8)
return;
1660 nal_tmp.resize(off + 4 + nalLen);
1661 uint8_t* p =
nal_tmp.data() + off;
1662 p[0] = (uint8_t)(nalLen >> 24);
1663 p[1] = (uint8_t)(nalLen >> 16);
1664 p[2] = (uint8_t)(nalLen >> 8);
1665 p[3] = (uint8_t)nalLen;
1666 memcpy(p + 4, nal, nalLen);
1668 if (
nal_tmp.size() == 0)
return 0;
1738 "dropping audio frame: moov not written yet (video SPS/PPS not "
1746 duration = bytesPerFrame > 0 ? (uint32_t)(len / bytesPerFrame) : 0;
1801 [
this](
const uint8_t* nal,
size_t nalLen) {
1802 if (nalLen == 0)
return;
1803 uint8_t nalType = nal[0] & 0x1F;
1806 memcpy(
sps_data.data(), nal, nalLen);
1807 }
else if (nalType == 8) {
1809 memcpy(
pps_data.data(), nal, nalLen);
1831 LOGW(
"getVideoInfo().format does not match the addXxxFrame() called");
1839 size_t expectedSize =
1841 if (expectedSize > 0 && len != expectedSize) {
1842 LOGW(
"frame size %d does not match the expected %d bytes for %d x %d",
1843 (
int)len, (
int)expectedSize, (
int)
video_cfg.width,
1856 if (!
is_open || len == 0)
return 0;
1868 template <
typename F>
1871 while (i + 3 <= len) {
1873 if (i + 4 <= len && data[i] == 0 && data[i + 1] == 0 &&
1874 data[i + 2] == 0 && data[i + 3] == 1) {
1876 }
else if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
1883 size_t nalStart = i + scLen;
1884 size_t j = nalStart;
1885 size_t nextStart = len;
1886 while (j + 3 <= len) {
1887 if (data[j] == 0 && data[j + 1] == 0 &&
1888 (data[j + 2] == 1 ||
1889 (j + 3 < len && data[j + 2] == 0 && data[j + 3] == 1))) {
1895 if (nextStart > nalStart) callback(data + nalStart, nextStart - nalStart);
1901 static const uint32_t rates[13] = {96000, 88200, 64000, 48000, 44100,
1902 32000, 24000, 22050, 16000, 12000,
1904 for (
int i = 0; i < 13; i++)
1905 if (rates[i] == sampleRate)
return i;
1955 asc[0] = (uint8_t)((
audio_profile << 3) | (sampleRateIdx >> 1));
1956 asc[1] = (uint8_t)(((sampleRateIdx & 1) << 7) | (
audio_info.channels << 3));
2001 static const uint32_t identity[9] = {
2002 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
2003 for (
int i = 0; i < 9; i++) b.
u32(identity[i]);
2021 b.
u16(isVideo ? 0 : 0x0100);
2023 static const uint32_t identity[9] = {
2024 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
2025 for (
int i = 0; i < 9; i++) b.
u32(identity[i]);
2026 b.
u32(isVideo ? ((uint32_t)
video_cfg.width << 16) : 0);
2027 b.
u32(isVideo ? ((uint32_t)
video_cfg.height << 16) : 0);
2055 size_t drefPos = b.
beginBox(
"dref");
2058 size_t urlPos = b.
beginBox(
"url ");
2067 size_t stsdPos = b.
beginBox(
"stsd");
2071 const char* fourcc =
"avc1";
2072 uint16_t depth = 0x0018;
2090 size_t entryPos = b.
beginBox(fourcc);
2120 size_t sttsPos = b.
beginBox(
"stts");
2124 size_t stscPos = b.
beginBox(
"stsc");
2128 size_t stszPos = b.
beginBox(
"stsz");
2133 size_t stcoPos = b.
beginBox(
"stco");
2157 size_t stsdPos = b.
beginBox(
"stsd");
2164 size_t sowtPos = b.
beginBox(
"sowt");
2168 size_t mp4aPos = b.
beginBox(
"mp4a");
2174 size_t sttsPos = b.
beginBox(
"stts");
2178 size_t stscPos = b.
beginBox(
"stsc");
2182 size_t stszPos = b.
beginBox(
"stsz");
2187 size_t stcoPos = b.
beginBox(
"stco");
2197 size_t mdiaPos = b.
beginBox(
"mdia");
2200 isVideo ?
"VideoHandler" :
"SoundHandler");
2201 size_t minfPos = b.
beginBox(
"minf");
2203 size_t vmhdPos = b.
beginBox(
"vmhd");
2208 size_t smhdPos = b.
beginBox(
"smhd");
2227 size_t trexVideoPos = b.
beginBox(
"trex");
2236 size_t trexAudioPos = b.
beginBox(
"trex");
2250 size_t ftypPos =
box.beginBox(
"ftyp");
2257 box.endBox(ftypPos);
2259 size_t moovPos =
box.beginBox(
"moov");
2264 box.endBox(moovPos);
2276 uint32_t baseTime) {
2277 if (
p_out ==
nullptr)
return;
2279 size_t moofPos =
box.beginBox(
"moof");
2280 size_t mfhdPos =
box.beginBox(
"mfhd");
2283 box.endBox(mfhdPos);
2285 size_t trafPos =
box.beginBox(
"traf");
2286 size_t tfhdPos =
box.beginBox(
"tfhd");
2289 box.endBox(tfhdPos);
2291 size_t tfdtPos =
box.beginBox(
"tfdt");
2294 box.endBox(tfdtPos);
2300 uint32_t trunFlags = 0x000001 | 0x000100 | 0x000200;
2301 if (isVideo) trunFlags |= 0x000400;
2303 size_t trunPos =
box.beginBox(
"trun");
2306 size_t dataOffsetFieldPos =
box.size();
2310 box.u32(sampleDuration);
2311 box.u32((uint32_t)len);
2316 box.endBox(trunPos);
2317 box.endBox(trafPos);
2318 box.endBox(moofPos);
2320 uint32_t dataOffset = (uint32_t)(
box.size() + 8);
2321 box.buffer[dataOffsetFieldPos] = (uint8_t)(dataOffset >> 24);
2322 box.buffer[dataOffsetFieldPos + 1] = (uint8_t)(dataOffset >> 16);
2323 box.buffer[dataOffsetFieldPos + 2] = (uint8_t)(dataOffset >> 8);
2324 box.buffer[dataOffsetFieldPos + 3] = (uint8_t)dataOffset;
2328 uint8_t mdatHeader[8];
2329 uint32_t mdatSize = (uint32_t)(len + 8);
2330 mdatHeader[0] = (uint8_t)(mdatSize >> 24);
2331 mdatHeader[1] = (uint8_t)(mdatSize >> 16);
2332 mdatHeader[2] = (uint8_t)(mdatSize >> 8);
2333 mdatHeader[3] = (uint8_t)mdatSize;
2334 mdatHeader[4] =
'm';
2335 mdatHeader[5] =
'd';
2336 mdatHeader[6] =
'a';
2337 mdatHeader[7] =
't';
2338 p_out->write(mdatHeader, 8);
2339 p_out->write(data, len);
VideoFormat
Video codec/pixel-format identifier, shared by two unrelated uses: the (single) video stream of a con...
Definition VideoOutput.h:29
StreamContentType
Which track write() feeds, for muxers (MuxerAVI, MuxerMP4) that double as a plain,...
Definition Video.h:21
size_t videoFrameSizeBytes(VideoFormat format, uint16_t width, uint16_t height)
Fixed per-frame size (bytes) for a raw/uncompressed VideoFormat at the given resolution - 0 for compr...
Definition Video.h:27
AudioFormat
Audio format codes used by Microsoft e.g. in avi or wav files.
Definition AudioFormat.h:21
const char * toMime(AudioFormat format)
Provides the mime type for a AudioFormat wav code, or nullptr if not known/mapped.
Definition AudioFormat.h:300
bool isWavFormat(AudioFormat format)
True if the wav code is handled via the WAV decoder (i.e. toMime() maps it to "audio/wav": PCM and al...
Definition AudioFormat.h:350
TrackKind
Video vs. audio track classification - lives here (rather than nested inside DemuxerMP4::Track,...
Definition SampleTableStore.h:46
@ MJPEG
Definition VideoOutput.h:31
@ RGB565
Definition VideoOutput.h:35
@ UNKNOWN
Definition VideoOutput.h:40
@ H264
Definition VideoOutput.h:30
@ I420
Definition VideoOutput.h:38
@ YUV422
Definition VideoOutput.h:34
@ Audio
Definition Video.h:21
@ Video
Definition Video.h:21
@ ALAC
Definition AudioFormat.h:250
@ PCM
Definition AudioFormat.h:23
@ AAC
Definition AudioFormat.h:251
@ UNKNOWN
Definition AudioFormat.h:22
@ AC3
Definition AudioFormat.h:115
@ Stts
Definition SampleTableStore.h:523
@ SampleSizes
Definition SampleTableStore.h:523
@ Stsc
Definition SampleTableStore.h:523
@ ChunkOffsets
Definition SampleTableStore.h:523
@ Unknown
Definition SampleTableStore.h:46
sample-to-chunk table entry (stsc)
Definition ContainerMP4.h:356
uint32_t first_chunk
Definition ContainerMP4.h:357
uint32_t samples_per_chunk
Definition ContainerMP4.h:358
Definition ContainerMP4.h:364
uint32_t sample_count
Definition ContainerMP4.h:365
uint32_t sample_delta
Definition ContainerMP4.h:366