/**************************************************************************** MP4 output module Copyright (C) 2017 Krzysztof Nikiel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.****************************************************************************/ #ifdef HAVE_CONFIG_H#include "config.h"#endif #include <stdlib.h>#include <stdio.h>#ifndef WORDS_BIGENDIAN//#include <byteswap.h>#endif#include <string.h>#include <time.h>#include <unistd.h> #include "mp4write.h" enum ATOM_TYPE{ ATOM_STOP = 0 /* end of atoms */ , ATOM_NAME /* plain atom */ , ATOM_DESCENT, /* starts group of children */ ATOM_ASCENT, /* ends group */ ATOM_DATA,};typedef struct{ uint16_t opcode; void *data;} creator_t; mp4config_t mp4config = { 0 }; static FILE *g_fout = NULL; static inline uint32_t be32(uint32_t u32){#ifndef WORDS_BIGENDIAN#if defined (__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))) return __builtin_bswap32(u32);#elif defined (_MSC_VER) return _byteswap_ulong(u32);#else return (u32 << 24) | ((u32 << 8) & 0xFF0000) | ((u32 >> 8) & 0xFF00) | (u32 >> 24);#endif#else return u32;#endif} static inline uint16_t be16(uint16_t u16){#ifndef WORDS_BIGENDIAN#if defined (__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))) return __builtin_bswap16(u16);#elif defined (_MSC_VER) return _byteswap_ushort(u16);#else return (u16 << 8) | (u16 >> 8);#endif#else return u16;#endif} static int dataout(const void *data, int size){ if (fwrite(data, 1, size, g_fout) != size) { perror("mp4out"); return -1; } return size;} static int stringout(const char *txt){ return dataout(txt, strlen(txt));} static int u32out(uint32_t u32){ u32 = be32(u32); return dataout(&u32, 4);} static int u16out(uint16_t u16){ u16 = be16(u16); return dataout(&u16, 2);} static int u8out(uint8_t u8){ if (fwrite(&u8, 1, 1, g_fout) != 1) { perror("mp4 out"); return 0; } return 1;} static int ftypout(void){ int size = 0; size += stringout("M4A "); size += u32out(0); size += stringout("M4A "); size += stringout("mp42"); size += stringout("isom"); size += u32out(0); return size;} enum{ SECSINDAY = 24 * 60 * 60 };static time_t mp4time(void){ int y; time_t t; time(&t); // add some time from the start of 1904 to the start of 1970 for (y = 1904; y < 1970; y++) { t += 365 * SECSINDAY; if (!(y & 3)) t += SECSINDAY; } return t;} static int mvhdout(void){ int size = 0; int cnt; // version size += u8out(0); // flags size += u8out(0); size += u16out(0); // Creation time size += u32out(mp4time()); // Modification time size += u32out(mp4time()); // Time scale (samplerate) size += u32out(mp4config.samplerate); // Duration size += u32out(mp4config.samples); // rate size += u32out(0x00010000); // volume size += u16out(0x0100); // reserved size += u16out(0); size += u32out(0); size += u32out(0); // matrix size += u32out(0x00010000); size += u32out(0); size += u32out(0); size += u32out(0); size += u32out(0x00010000); size += u32out(0); size += u32out(0); size += u32out(0); size += u32out(0x40000000); for (cnt = 0; cnt < 6; cnt++) size += u32out(0); // Next track ID size += u32out(2); return size;}; static int tkhdout(void){ int size = 0; // version size += u8out(0); // flags // bits 8-23 size += u16out(0); // bits 0-7 size += u8out(1 /*track enabled */ ); // Creation time size += u32out(mp4time()); // Modification time size += u32out(mp4time()); // Track ID size += u32out(1); // Reserved size += u32out(0); // Duration size += u32out(mp4config.samples); // Reserved size += u32out(0); size += u32out(0); // Layer size += u16out(0); // Alternate group size += u16out(0); // Volume size += u16out(0x0100); // Reserved size += u16out(0); // matrix size += u32out(0x00010000); size += u32out(0); size += u32out(0); size += u32out(0); size += u32out(0x00010000); size += u32out(0); size += u32out(0); size += u32out(0); size += u32out(0x40000000); // Track width size += u32out(0); // Track height size += u32out(0); return size;}; static int mdhdout(void){ int size = 0; // version/flags size += u32out(0); // Creation time size += u32out(mp4time()); // Modification time size += u32out(mp4time()); // Time scale size += u32out(mp4config.samplerate); // Duration size += u32out(mp4config.samples); // Language size += u16out(0 /*0=English */ ); // pre_defined size += u16out(0); return size;}; static int hdlr1out(void){ int size = 0; // version/flags size += u32out(0); // pre_defined size += u32out(0); // Component subtype size += stringout("soun"); // reserved size += u32out(0); size += u32out(0); size += u32out(0); // name // null terminate size += u8out(0); return size;}; static int smhdout(void){ int size = 0; // version/flags size += u32out(0); // Balance size += u16out(0 /*center */ ); // Reserved size += u16out(0); return size;}; static int drefout(void){ int size = 0; // version/flags size += u32out(0); // Number of entries size += u32out(1 /*url reference */ ); return size;}; static int urlout(void){ int size = 0; size += u32out(1); return size;}; static int stsdout(void){ int size = 0; // version/flags size += u32out(0); // Number of entries(one 'mp4a') size += u32out(1); return size;}; static int mp4aout(void){ int size = 0; // Reserved (6 bytes) size += u32out(0); size += u16out(0); // Data reference index size += u16out(1); // Version size += u16out(0); // Revision level size += u16out(0); // Vendor size += u32out(0); // Number of channels size += u16out(mp4config.channels); // Sample size (bits) size += u16out(mp4config.bits); // Compression ID size += u16out(0); // Packet size size += u16out(0); // Sample rate (16.16) // rate integer part size += u16out(mp4config.samplerate); // rate reminder part size += u16out(0); return size;} static int esdsout(void){ int size = 0; // descriptor definitions: // systems/mp4_file_format/libisomediafile/src/MP4Descriptors.h // systems/mp4_file_format/libisomediafile/src/MP4Descriptors.c // // descriptor tree: // MP4ES_Descriptor // MP4DecoderConfigDescriptor // MP4DecSpecificInfoDescriptor // MP4SLConfigDescriptor struct { int es; int dc; // DecoderConfig int dsi; // DecSpecificInfo int sl; // SLConfig } dsize; enum { TAG_ES = 3, TAG_DC = 4, TAG_DSI = 5, TAG_SLC = 6 }; // calc sizes#define DESCSIZE(x) (x + 5/*.tag+.size*/) dsize.sl = 1; dsize.dsi = mp4config.asc.size; dsize.dc = 13 + DESCSIZE(dsize.dsi); dsize.es = 3 + DESCSIZE(dsize.dc) + DESCSIZE(dsize.sl); // output esds atom data // version/flags ? size += u32out(0); // mp4es size += u8out(TAG_ES); size += u8out(0x80); size += u8out(0x80); size += u8out(0x80); size += u8out(dsize.es); // ESID size += u16out(0); // flags(url(bit 6); ocr(5); streamPriority (0-4)): size += u8out(0); size += u8out(TAG_DC); size += u8out(0x80); size += u8out(0x80); size += u8out(0x80); size += u8out(dsize.dc); size += u8out(0x40 /*MPEG-4 audio */ ); size += u8out((5 << 2) /* AudioStream */ | 1 /* reserved = 1 */); // decode buffer size bytes#if 0 size += u16out(mp4config.buffersize >> 8); size += u8out(mp4config.buffersize && 0xff);#else size += u8out(0); size += u8out(0x18); size += u8out(0);#endif // bitrate size += u32out(mp4config.bitrate.max); size += u32out(mp4config.bitrate.avg); size += u8out(TAG_DSI); size += u8out(0x80); size += u8out(0x80); size += u8out(0x80); size += u8out(dsize.dsi); // AudioSpecificConfig size += dataout(mp4config.asc.data, mp4config.asc.size); size += u8out(TAG_SLC); size += u8out(0x80); size += u8out(0x80); size += u8out(0x80); size += u8out(dsize.sl); // "predefined" (no idea) size += u8out(2); return size;} static int sttsout(void){ int size = 0; // version/flags size += u32out(0); // Number of entries size += u32out(1); // only one entry // Sample count (number of frames) size += u32out(mp4config.frame.ents); // Sample duration (samples per frame) size += u32out(mp4config.framesamples); return size;} static int stszout(void){ int size = 0; int cnt; // version/flags size += u32out(0); // Sample size size += u32out(0 /*i.e. variable size */ ); // Number of entries if (!mp4config.frame.ents) return size; if (!mp4config.frame.data) return size; size += u32out(mp4config.frame.ents); for (cnt = 0; cnt < mp4config.frame.ents; cnt++) size += u32out(mp4config.frame.data[cnt]); return size;} static int stscout(void){ int size = 0; // version/flags size += u32out(0); // Number of entriesLines 1–500 of 937. Load more
/**************************************************************************** MP4 output module Copyright (C) 2017 Krzysztof Nikiel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <stdlib.h> #include <stdio.h> #ifndef WORDS_BIGENDIAN //#include <byteswap.h> #endif #include <string.h> #include <time.h> #include <unistd.h> #include "mp4write.h" enum ATOM_TYPE { ATOM_STOP = 0 /* end of atoms */ , ATOM_NAME /* plain atom */ , ATOM_DESCENT, /* starts group of children */ ATOM_ASCENT, /* ends group */ ATOM_DATA, }; typedef struct { uint16_t opcode; void *data; } creator_t; mp4config_t mp4config = { 0 }; static FILE *g_fout = NULL; static inline uint32_t be32(uint32_t u32) { #ifndef WORDS_BIGENDIAN #if defined (__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))) return __builtin_bswap32(u32); #elif defined (_MSC_VER) return _byteswap_ulong(u32); #else return (u32 << 24) | ((u32 << 8) & 0xFF0000) | ((u32 >> 8) & 0xFF00) | (u32 >> 24); #endif #else return u32; #endif } static inline uint16_t be16(uint16_t u16) { #ifndef WORDS_BIGENDIAN #if defined (__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))) return __builtin_bswap16(u16); #elif defined (_MSC_VER) return _byteswap_ushort(u16); #else return (u16 << 8) | (u16 >> 8); #endif #else return u16; #endif } static int dataout(const void *data, int size) { if (fwrite(data, 1, size, g_fout) != size) { perror("mp4out"); return -1; } return size; } static int stringout(const char *txt) { return dataout(txt, strlen(txt)); } static int u32out(uint32_t u32) { u32 = be32(u32); return dataout(&u32, 4); } static int u16out(uint16_t u16) { u16 = be16(u16); return dataout(&u16, 2); } static int u8out(uint8_t u8) { if (fwrite(&u8, 1, 1, g_fout) != 1) { perror("mp4 out"); return 0; } return 1; } static int ftypout(void) { int size = 0; size += stringout("M4A "); size += u32out(0); size += stringout("M4A "); size += stringout("mp42"); size += stringout("isom"); size += u32out(0); return size; } enum { SECSINDAY = 24 * 60 * 60 }; static time_t mp4time(void) { int y; time_t t; time(&t); // add some time from the start of 1904 to the start of 1970 for (y = 1904; y < 1970; y++) { t += 365 * SECSINDAY; if (!(y & 3)) t += SECSINDAY; } return t; } static int mvhdout(void) { int size = 0; int cnt; // version size += u8out(0); // flags size += u8out(0); size += u16out(0); // Creation time size += u32out(mp4time()); // Modification time size += u32out(mp4time()); // Time scale (samplerate) size += u32out(mp4config.samplerate); // Duration size += u32out(mp4config.samples); // rate size += u32out(0x00010000); // volume size += u16out(0x0100); // reserved size += u16out(0); size += u32out(0); size += u32out(0); // matrix size += u32out(0x00010000); size += u32out(0); size += u32out(0); size += u32out(0); size += u32out(0x00010000); size += u32out(0); size += u32out(0); size += u32out(0); size += u32out(0x40000000); for (cnt = 0; cnt < 6; cnt++) size += u32out(0); // Next track ID size += u32out(2); return size; }; static int tkhdout(void) { int size = 0; // version size += u8out(0); // flags // bits 8-23 size += u16out(0); // bits 0-7 size += u8out(1 /*track enabled */ ); // Creation time size += u32out(mp4time()); // Modification time size += u32out(mp4time()); // Track ID size += u32out(1); // Reserved size += u32out(0); // Duration size += u32out(mp4config.samples); // Reserved size += u32out(0); size += u32out(0); // Layer size += u16out(0); // Alternate group size += u16out(0); // Volume size += u16out(0x0100); // Reserved size += u16out(0); // matrix size += u32out(0x00010000); size += u32out(0); size += u32out(0); size += u32out(0); size += u32out(0x00010000); size += u32out(0); size += u32out(0); size += u32out(0); size += u32out(0x40000000); // Track width size += u32out(0); // Track height size += u32out(0); return size; }; static int mdhdout(void) { int size = 0; // version/flags size += u32out(0); // Creation time size += u32out(mp4time()); // Modification time size += u32out(mp4time()); // Time scale size += u32out(mp4config.samplerate); // Duration size += u32out(mp4config.samples); // Language size += u16out(0 /*0=English */ ); // pre_defined size += u16out(0); return size; }; static int hdlr1out(void) { int size = 0; // version/flags size += u32out(0); // pre_defined size += u32out(0); // Component subtype size += stringout("soun"); // reserved size += u32out(0); size += u32out(0); size += u32out(0); // name // null terminate size += u8out(0); return size; }; static int smhdout(void) { int size = 0; // version/flags size += u32out(0); // Balance size += u16out(0 /*center */ ); // Reserved size += u16out(0); return size; }; static int drefout(void) { int size = 0; // version/flags size += u32out(0); // Number of entries size += u32out(1 /*url reference */ ); return size; }; static int urlout(void) { int size = 0; size += u32out(1); return size; }; static int stsdout(void) { int size = 0; // version/flags size += u32out(0); // Number of entries(one 'mp4a') size += u32out(1); return size; }; static int mp4aout(void) { int size = 0; // Reserved (6 bytes) size += u32out(0); size += u16out(0); // Data reference index size += u16out(1); // Version size += u16out(0); // Revision level size += u16out(0); // Vendor size += u32out(0); // Number of channels size += u16out(mp4config.channels); // Sample size (bits) size += u16out(mp4config.bits); // Compression ID size += u16out(0); // Packet size size += u16out(0); // Sample rate (16.16) // rate integer part size += u16out(mp4config.samplerate); // rate reminder part size += u16out(0); return size; } static int esdsout(void) { int size = 0; // descriptor definitions: // systems/mp4_file_format/libisomediafile/src/MP4Descriptors.h // systems/mp4_file_format/libisomediafile/src/MP4Descriptors.c // // descriptor tree: // MP4ES_Descriptor // MP4DecoderConfigDescriptor // MP4DecSpecificInfoDescriptor // MP4SLConfigDescriptor struct { int es; int dc; // DecoderConfig int dsi; // DecSpecificInfo int sl; // SLConfig } dsize; enum { TAG_ES = 3, TAG_DC = 4, TAG_DSI = 5, TAG_SLC = 6 }; // calc sizes #define DESCSIZE(x) (x + 5/*.tag+.size*/) dsize.sl = 1; dsize.dsi = mp4config.asc.size; dsize.dc = 13 + DESCSIZE(dsize.dsi); dsize.es = 3 + DESCSIZE(dsize.dc) + DESCSIZE(dsize.sl); // output esds atom data // version/flags ? size += u32out(0); // mp4es size += u8out(TAG_ES); size += u8out(0x80); size += u8out(0x80); size += u8out(0x80); size += u8out(dsize.es); // ESID size += u16out(0); // flags(url(bit 6); ocr(5); streamPriority (0-4)): size += u8out(0); size += u8out(TAG_DC); size += u8out(0x80); size += u8out(0x80); size += u8out(0x80); size += u8out(dsize.dc); size += u8out(0x40 /*MPEG-4 audio */ ); size += u8out((5 << 2) /* AudioStream */ | 1 /* reserved = 1 */); // decode buffer size bytes #if 0 size += u16out(mp4config.buffersize >> 8); size += u8out(mp4config.buffersize && 0xff); #else size += u8out(0); size += u8out(0x18); size += u8out(0); #endif // bitrate size += u32out(mp4config.bitrate.max); size += u32out(mp4config.bitrate.avg); size += u8out(TAG_DSI); size += u8out(0x80); size += u8out(0x80); size += u8out(0x80); size += u8out(dsize.dsi); // AudioSpecificConfig size += dataout(mp4config.asc.data, mp4config.asc.size); size += u8out(TAG_SLC); size += u8out(0x80); size += u8out(0x80); size += u8out(0x80); size += u8out(dsize.sl); // "predefined" (no idea) size += u8out(2); return size; } static int sttsout(void) { int size = 0; // version/flags size += u32out(0); // Number of entries size += u32out(1); // only one entry // Sample count (number of frames) size += u32out(mp4config.frame.ents); // Sample duration (samples per frame) size += u32out(mp4config.framesamples); return size; } static int stszout(void) { int size = 0; int cnt; // version/flags size += u32out(0); // Sample size size += u32out(0 /*i.e. variable size */ ); // Number of entries if (!mp4config.frame.ents) return size; if (!mp4config.frame.data) return size; size += u32out(mp4config.frame.ents); for (cnt = 0; cnt < mp4config.frame.ents; cnt++) size += u32out(mp4config.frame.data[cnt]); return size; } static int stscout(void) { int size = 0; // version/flags size += u32out(0); // Number of entries