83 const char*
mime()
override {
return "video/mp4"; }
178 size_t write(
const uint8_t* data,
size_t len)
override {
289 for (
size_t i = 0; i <
tracks.size(); i++)
delete tracks[i];
362 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
367 static uint16_t
readU16(
const uint8_t* p) {
return (p[0] << 8) | p[1]; }
500 if (memcmp(handler,
"vide", 4) == 0) {
503 }
else if (memcmp(handler,
"soun", 4) == 0) {
544 if (entrySize < 28)
return;
591 LOGE(
"Failed to parse esds box");
631 LOGE(
"HEVC (hev1/hvc1) video is not supported - only H.264 (avc1)");
639 const uint8_t* d = box.
data;
641 int numSps = d[5] & 0x1F;
643 auto appendUnit = [&](
const uint8_t* unit,
size_t len) {
644 static const uint8_t startCode[4] = {0, 0, 0, 1};
646 size_t off = v.
size();
647 v.resize(off + 4 + len);
648 memcpy(v.data() + off, startCode, 4);
649 memcpy(v.data() + off + 4, unit, len);
651 for (
int i = 0; i < numSps && pos + 2 <= box.
data_size; i++) {
652 uint16_t len =
readU16(d + pos);
655 appendUnit(d + pos, len);
659 int numPps = d[pos++];
660 for (
int i = 0; i < numPps && pos + 2 <= box.
data_size; i++) {
661 uint16_t len =
readU16(d + pos);
664 appendUnit(d + pos, len);
684 size_t timescaleOffset = version == 1 ? 20 : 12;
705 for (uint32_t i = 0; i < entryCount && (i + 1) * 8 <= avail; i++) {
736 if (sampleSize != 0) {
768 for (uint32_t i = 0; i < entryCount && (i + 1) * 12 <= avail; i++) {
789 size_t entrySize = is64 ? 8 : 4;
790 for (uint32_t i = 0; i < entryCount && (i + 1) * entrySize <= avail; i++) {
813 uint32_t chunk_index;
817 for (
size_t ti = 0; ti <
tracks.size(); ti++) {
828 for (
size_t i = 1; i < items.
size(); i++) {
830 long j = (long)i - 1;
831 while (j >= 0 && items[j].offset > key.offset) {
832 items[j + 1] = items[j];
837 for (
size_t i = 0; i < items.
size(); i++) {
838 Track* t = items[i].track;
840 if (samples > 0)
schedule.push_back({t, samples});
848 uint32_t chunk1 = chunkIndex0 + 1;
850 for (
size_t i = 0; i < t.
stsc.size(); i++) {
851 if (t.
stsc[i].first_chunk <= chunk1) {
852 result = t.
stsc[i].samples_per_chunk;
873 void feed(
const uint8_t* data,
size_t len) {
897 size_t take = std::min(need, len - pos);
931 uint32_t scheduledMs = (uint32_t)((ptsTicks * 1000) / t.
timescale);
932 uint32_t nowMs =
millis();
938 if (nowMs > scheduledWallMs) {
939 LOGW(
"DemuxerMP4: video frame %u ms late - skipping",
940 (
unsigned)(nowMs - scheduledWallMs));
942 }
else if (nowMs < scheduledWallMs) {
943 LOGI(
"DemuxerMP4: video frame %u ms early",
944 (
unsigned)(scheduledWallMs - nowMs));
952 while (pos + lenSize <= size) {
954 for (
size_t i = 0; i < lenSize; i++)
955 nalLen = (nalLen << 8) | data[pos + i];
957 if (pos + nalLen > size)
break;
959 static const uint8_t startCode[4] = {0, 0, 0, 1};
962 memcpy(
nal_tmp.
data() + off + 4, data + pos, nalLen);
1006 int channelCfg,
int frameLen) {
1009 adts[2] = ((aacProfile - 1) << 6) | (sampleRateIdx << 2) |
1010 ((channelCfg >> 2) & 0x1);
1011 adts[3] = ((channelCfg & 0x3) << 6) | ((frameLen + 7) >> 11);
1012 adts[4] = ((frameLen + 7) >> 3) & 0xFF;
1013 adts[5] = (((frameLen + 7) & 0x7) << 5) | 0x1F;
1032 u8((uint8_t)(v >> 8));
1036 u8((uint8_t)(v >> 16));
1037 u8((uint8_t)(v >> 8));
1041 u8((uint8_t)(v >> 24));
1042 u8((uint8_t)(v >> 16));
1043 u8((uint8_t)(v >> 8));
1058 while (*s)
u8((uint8_t)*s++);
1073 uint32_t sz = (uint32_t)(
buffer.
size() - pos);
1074 buffer[pos] = (uint8_t)(sz >> 24);
1075 buffer[pos + 1] = (uint8_t)(sz >> 16);
1076 buffer[pos + 2] = (uint8_t)(sz >> 8);
1077 buffer[pos + 3] = (uint8_t)sz;
1168 const char*
mime()
override {
return "video/mp4"; }
1207 if (
p_out ==
nullptr) {
1208 LOGE(
"output not defined");
1222 "unsupported video format: %d - MuxerMP4 writes H264, MJPEG, "
1223 "YUV422, RGB565 or I420",
1277 size_t write(
const uint8_t* data,
size_t len)
override {
1308 bool isKeyFrame =
true)
override {
1312 LOGW(
"dropping video frame: SPS/PPS not seen yet");
1317 if (nalLen == 0)
return;
1318 uint8_t nalType = nal[0] & 0x1F;
1319 if (nalType == 7 || nalType == 8)
return;
1323 p[0] = (uint8_t)(nalLen >> 24);
1324 p[1] = (uint8_t)(nalLen >> 16);
1325 p[2] = (uint8_t)(nalLen >> 8);
1326 p[3] = (uint8_t)nalLen;
1327 memcpy(p + 4, nal, nalLen);
1399 "dropping audio frame: moov not written yet (video SPS/PPS not "
1407 duration = bytesPerFrame > 0 ? (uint32_t)(len / bytesPerFrame) : 0;
1462 [
this](
const uint8_t* nal,
size_t nalLen) {
1463 if (nalLen == 0)
return;
1464 uint8_t nalType = nal[0] & 0x1F;
1468 }
else if (nalType == 8) {
1492 LOGW(
"getVideoInfo().format does not match the addXxxFrame() called");
1500 size_t expectedSize =
1502 if (expectedSize > 0 && len != expectedSize) {
1503 LOGW(
"frame size %d does not match the expected %d bytes for %d x %d",
1517 if (!
is_open || len == 0)
return 0;
1529 template <
typename F>
1532 while (i + 3 <= len) {
1534 if (i + 4 <= len && data[i] == 0 && data[i + 1] == 0 &&
1535 data[i + 2] == 0 && data[i + 3] == 1) {
1537 }
else if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
1544 size_t nalStart = i + scLen;
1545 size_t j = nalStart;
1546 size_t nextStart = len;
1547 while (j + 3 <= len) {
1548 if (data[j] == 0 && data[j + 1] == 0 &&
1549 (data[j + 2] == 1 ||
1550 (j + 3 < len && data[j + 2] == 0 && data[j + 3] == 1))) {
1556 if (nextStart > nalStart) callback(data + nalStart, nextStart - nalStart);
1562 static const uint32_t rates[13] = {96000, 88200, 64000, 48000, 44100,
1563 32000, 24000, 22050, 16000, 12000,
1565 for (
int i = 0; i < 13; i++)
1566 if (rates[i] == sampleRate)
return i;
1616 asc[0] = (uint8_t)((
audio_profile << 3) | (sampleRateIdx >> 1));
1662 static const uint32_t identity[9] = {
1663 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
1664 for (
int i = 0; i < 9; i++) b.
u32(identity[i]);
1682 b.
u16(isVideo ? 0 : 0x0100);
1684 static const uint32_t identity[9] = {
1685 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
1686 for (
int i = 0; i < 9; i++) b.
u32(identity[i]);
1716 size_t drefPos = b.
beginBox(
"dref");
1719 size_t urlPos = b.
beginBox(
"url ");
1728 size_t stsdPos = b.
beginBox(
"stsd");
1732 const char* fourcc =
"avc1";
1733 uint16_t depth = 0x0018;
1751 size_t entryPos = b.
beginBox(fourcc);
1781 size_t sttsPos = b.
beginBox(
"stts");
1785 size_t stscPos = b.
beginBox(
"stsc");
1789 size_t stszPos = b.
beginBox(
"stsz");
1794 size_t stcoPos = b.
beginBox(
"stco");
1818 size_t stsdPos = b.
beginBox(
"stsd");
1825 size_t sowtPos = b.
beginBox(
"sowt");
1829 size_t mp4aPos = b.
beginBox(
"mp4a");
1835 size_t sttsPos = b.
beginBox(
"stts");
1839 size_t stscPos = b.
beginBox(
"stsc");
1843 size_t stszPos = b.
beginBox(
"stsz");
1848 size_t stcoPos = b.
beginBox(
"stco");
1858 size_t mdiaPos = b.
beginBox(
"mdia");
1861 isVideo ?
"VideoHandler" :
"SoundHandler");
1862 size_t minfPos = b.
beginBox(
"minf");
1864 size_t vmhdPos = b.
beginBox(
"vmhd");
1869 size_t smhdPos = b.
beginBox(
"smhd");
1888 size_t trexVideoPos = b.
beginBox(
"trex");
1897 size_t trexAudioPos = b.
beginBox(
"trex");
1936 uint32_t sampleDuration,
bool isKeyFrame,
1937 uint32_t baseTime) {
1938 if (
p_out ==
nullptr)
return;
1961 uint32_t trunFlags = 0x000001 | 0x000100 | 0x000200;
1962 if (isVideo) trunFlags |= 0x000400;
1967 size_t dataOffsetFieldPos =
box.
size();
1975 box.
u32(isKeyFrame ? 0x02000000 : 0x01010000);
1981 uint32_t dataOffset = (uint32_t)(
box.
size() + 8);
1982 box.
buffer[dataOffsetFieldPos] = (uint8_t)(dataOffset >> 24);
1983 box.
buffer[dataOffsetFieldPos + 1] = (uint8_t)(dataOffset >> 16);
1984 box.
buffer[dataOffsetFieldPos + 2] = (uint8_t)(dataOffset >> 8);
1985 box.
buffer[dataOffsetFieldPos + 3] = (uint8_t)dataOffset;
1989 uint8_t mdatHeader[8];
1990 uint32_t mdatSize = (uint32_t)(len + 8);
1991 mdatHeader[0] = (uint8_t)(mdatSize >> 24);
1992 mdatHeader[1] = (uint8_t)(mdatSize >> 16);
1993 mdatHeader[2] = (uint8_t)(mdatSize >> 8);
1994 mdatHeader[3] = (uint8_t)mdatSize;
1995 mdatHeader[4] =
'm';
1996 mdatHeader[5] =
'd';
1997 mdatHeader[6] =
'a';
1998 mdatHeader[7] =
't';
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#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 Video.h:43
StreamContentType
Which track write() feeds, for muxers (MuxerAVI, MuxerMP4) that double as a plain,...
Definition Video.h:19
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:61
AudioFormat
Audio format codes used by Microsoft e.g. in avi or wav files.
Definition AudioFormat.h:21
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:348
@ Audio
Definition Video.h:19
@ Video
Definition Video.h:19
Definition ContainerMP4.h:272
uint32_t sample_count
Definition ContainerMP4.h:274
Track * track
Definition ContainerMP4.h:273
sample-to-chunk table entry (stsc)
Definition ContainerMP4.h:187
uint32_t first_chunk
Definition ContainerMP4.h:188
uint32_t samples_per_chunk
Definition ContainerMP4.h:189
Definition ContainerMP4.h:195
uint32_t sample_count
Definition ContainerMP4.h:196
uint32_t sample_delta
Definition ContainerMP4.h:197