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);
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));
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]; }
739 if (memcmp(handler,
"vide", 4) == 0) {
742 }
else if (memcmp(handler,
"soun", 4) == 0) {
811 if (entrySize < 28)
return;
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;
1003 LOGD(
"DemuxerMP4: stts entryCount=%u", (
unsigned)entryCount);
1006 for (uint32_t i = 0; i < entryCount && (i + 1) * 8 <= avail; i++) {
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);
1086 for (uint32_t i = 0; i < entryCount && (i + 1) * 12 <= avail; i++) {
1108 LOGD(
"DemuxerMP4: %s entryCount=%u", is64 ?
"co64" :
"stco",
1109 (unsigned)entryCount);
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};
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));
1381 while (*s)
u8((uint8_t)*s++);
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 {
1635 LOGW(
"dropping video frame: SPS/PPS not seen yet");
1640 if (nalLen == 0)
return;
1641 uint8_t nalType = nal[0] & 0x1F;
1642 if (nalType == 7 || nalType == 8)
return;
1646 p[0] = (uint8_t)(nalLen >> 24);
1647 p[1] = (uint8_t)(nalLen >> 16);
1648 p[2] = (uint8_t)(nalLen >> 8);
1649 p[3] = (uint8_t)nalLen;
1650 memcpy(p + 4, nal, nalLen);
1722 "dropping audio frame: moov not written yet (video SPS/PPS not "
1730 duration = bytesPerFrame > 0 ? (uint32_t)(len / bytesPerFrame) : 0;
1785 [
this](
const uint8_t* nal,
size_t nalLen) {
1786 if (nalLen == 0)
return;
1787 uint8_t nalType = nal[0] & 0x1F;
1791 }
else if (nalType == 8) {
1815 LOGW(
"getVideoInfo().format does not match the addXxxFrame() called");
1823 size_t expectedSize =
1825 if (expectedSize > 0 && len != expectedSize) {
1826 LOGW(
"frame size %d does not match the expected %d bytes for %d x %d",
1840 if (!
is_open || len == 0)
return 0;
1852 template <
typename F>
1855 while (i + 3 <= len) {
1857 if (i + 4 <= len && data[i] == 0 && data[i + 1] == 0 &&
1858 data[i + 2] == 0 && data[i + 3] == 1) {
1860 }
else if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
1867 size_t nalStart = i + scLen;
1868 size_t j = nalStart;
1869 size_t nextStart = len;
1870 while (j + 3 <= len) {
1871 if (data[j] == 0 && data[j + 1] == 0 &&
1872 (data[j + 2] == 1 ||
1873 (j + 3 < len && data[j + 2] == 0 && data[j + 3] == 1))) {
1879 if (nextStart > nalStart) callback(data + nalStart, nextStart - nalStart);
1885 static const uint32_t rates[13] = {96000, 88200, 64000, 48000, 44100,
1886 32000, 24000, 22050, 16000, 12000,
1888 for (
int i = 0; i < 13; i++)
1889 if (rates[i] == sampleRate)
return i;
1939 asc[0] = (uint8_t)((
audio_profile << 3) | (sampleRateIdx >> 1));
1985 static const uint32_t identity[9] = {
1986 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
1987 for (
int i = 0; i < 9; i++) b.
u32(identity[i]);
2005 b.
u16(isVideo ? 0 : 0x0100);
2007 static const uint32_t identity[9] = {
2008 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
2009 for (
int i = 0; i < 9; i++) b.
u32(identity[i]);
2039 size_t drefPos = b.
beginBox(
"dref");
2042 size_t urlPos = b.
beginBox(
"url ");
2051 size_t stsdPos = b.
beginBox(
"stsd");
2055 const char* fourcc =
"avc1";
2056 uint16_t depth = 0x0018;
2074 size_t entryPos = b.
beginBox(fourcc);
2104 size_t sttsPos = b.
beginBox(
"stts");
2108 size_t stscPos = b.
beginBox(
"stsc");
2112 size_t stszPos = b.
beginBox(
"stsz");
2117 size_t stcoPos = b.
beginBox(
"stco");
2141 size_t stsdPos = b.
beginBox(
"stsd");
2148 size_t sowtPos = b.
beginBox(
"sowt");
2152 size_t mp4aPos = b.
beginBox(
"mp4a");
2158 size_t sttsPos = b.
beginBox(
"stts");
2162 size_t stscPos = b.
beginBox(
"stsc");
2166 size_t stszPos = b.
beginBox(
"stsz");
2171 size_t stcoPos = b.
beginBox(
"stco");
2181 size_t mdiaPos = b.
beginBox(
"mdia");
2184 isVideo ?
"VideoHandler" :
"SoundHandler");
2185 size_t minfPos = b.
beginBox(
"minf");
2187 size_t vmhdPos = b.
beginBox(
"vmhd");
2192 size_t smhdPos = b.
beginBox(
"smhd");
2211 size_t trexVideoPos = b.
beginBox(
"trex");
2220 size_t trexAudioPos = b.
beginBox(
"trex");
2260 uint32_t baseTime) {
2261 if (
p_out ==
nullptr)
return;
2284 uint32_t trunFlags = 0x000001 | 0x000100 | 0x000200;
2285 if (isVideo) trunFlags |= 0x000400;
2290 size_t dataOffsetFieldPos =
box.
size();
2304 uint32_t dataOffset = (uint32_t)(
box.
size() + 8);
2305 box.
buffer[dataOffsetFieldPos] = (uint8_t)(dataOffset >> 24);
2306 box.
buffer[dataOffsetFieldPos + 1] = (uint8_t)(dataOffset >> 16);
2307 box.
buffer[dataOffsetFieldPos + 2] = (uint8_t)(dataOffset >> 8);
2308 box.
buffer[dataOffsetFieldPos + 3] = (uint8_t)dataOffset;
2312 uint8_t mdatHeader[8];
2313 uint32_t mdatSize = (uint32_t)(len + 8);
2314 mdatHeader[0] = (uint8_t)(mdatSize >> 24);
2315 mdatHeader[1] = (uint8_t)(mdatSize >> 16);
2316 mdatHeader[2] = (uint8_t)(mdatSize >> 8);
2317 mdatHeader[3] = (uint8_t)mdatSize;
2318 mdatHeader[4] =
'm';
2319 mdatHeader[5] =
'd';
2320 mdatHeader[6] =
'a';
2321 mdatHeader[7] =
't';
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
virtual size_t write(const uint8_t *data, size_t len)
Definition Arduino.h:120
virtual void flush()
Definition Arduino.h:130
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
@ Audio
Definition Video.h:21
@ Video
Definition Video.h:21
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