├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── Render ├── MP4Writer.cpp ├── MP4Writer.h ├── NvCodecRender.cpp ├── NvCodecRender.h ├── mov-file-buffer.c └── mov-file-buffer.h ├── VideoCodec ├── CudaProc │ └── ColorSpace.cu ├── Interface │ ├── cuviddec.h │ ├── nvEncodeAPI.h │ └── nvcuvid.h ├── NvDec │ ├── ColorSpace.h │ ├── FFmpegDemuxer.h │ ├── Logger.h │ ├── NvCodecUtils.h │ ├── NvDecoder.cpp │ └── NvDecoder.h └── NvEnc │ ├── NvEncoder.cpp │ ├── NvEncoder.h │ ├── NvEncoderCLIOptions.h │ ├── NvEncoderCuda.cpp │ └── NvEncoderCuda.h ├── Video_Codec_SDK_11.0.10.zip ├── libmov ├── Android.mk ├── Makefile ├── include │ ├── fmp4-writer.h │ ├── mov-atom.h │ ├── mov-box.h │ ├── mov-buffer.h │ ├── mov-format.h │ ├── mov-memory-buffer.h │ ├── mov-reader.h │ ├── mov-udta.h │ ├── mov-writer.h │ └── mp4-writer.h ├── libmov.vcxproj ├── libmov.vcxproj.filters ├── libmov.xcodeproj │ └── project.pbxproj ├── source │ ├── fmp4-reader.c │ ├── fmp4-writer.c │ ├── mov-avc1.c │ ├── mov-dinf.c │ ├── mov-elst.c │ ├── mov-esds.c │ ├── mov-ftyp.c │ ├── mov-hdlr.c │ ├── mov-hdr.c │ ├── mov-internal.h │ ├── mov-iods.c │ ├── mov-ioutil.h │ ├── mov-leva.c │ ├── mov-mdhd.c │ ├── mov-mehd.c │ ├── mov-mfhd.c │ ├── mov-minf.c │ ├── mov-mvhd.c │ ├── mov-opus.c │ ├── mov-reader.c │ ├── mov-sidx.c │ ├── mov-stco.c │ ├── mov-stsc.c │ ├── mov-stsd.c │ ├── mov-stss.c │ ├── mov-stsz.c │ ├── mov-stts.c │ ├── mov-tag.c │ ├── mov-tfdt.c │ ├── mov-tfhd.c │ ├── mov-tfra.c │ ├── mov-tkhd.c │ ├── mov-track.c │ ├── mov-trex.c │ ├── mov-trun.c │ ├── mov-tx3g.c │ ├── mov-udta.c │ ├── mov-vpcc.c │ └── mov-writer.c └── test │ ├── fmp4-writer-test.cpp │ ├── fmp4-writer-test2.cpp │ ├── mov-2-flv.cpp │ ├── mov-file-buffer.c │ ├── mov-file-buffer.h │ ├── mov-reader-test.cpp │ ├── mov-writer-adts.cpp │ ├── mov-writer-audio.cpp │ ├── mov-writer-av1.cpp │ ├── mov-writer-h264.cpp │ ├── mov-writer-h265.cpp │ ├── mov-writer-subtitle.cpp │ └── mov-writer-test.cpp └── test ├── test.cpp └── test_1280x720_v.mp4 /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project (REIDRENDER) 3 | enable_language(CUDA) 4 | set(CMAKE_CXX_STANDARD 17) # 设置 C++ 标准为 C++17 5 | find_package(CUDA REQUIRED) 6 | set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} "-std=c++17") 7 | 8 | find_package(OpenCV REQUIRED) 9 | include_directories(/usr/local/include /usr/local/cuda ${OpenCV_INCLUDE_DIRS} ${CUDA_INCLUDES}) 10 | include_directories(VideoCodec/NvDec VideoCodec/NvEnc VideoCodec/Interface Render VideoCodec/CudaProc libmov/include) 11 | 12 | aux_source_directory(VideoCodec/NvDec DEC) 13 | aux_source_directory(VideoCodec/NvEnc ENC) 14 | aux_source_directory(Render RENDER) 15 | aux_source_directory(libmov/source MOV) 16 | set(CUDA_SOURCES VideoCodec/CudaProc/ColorSpace.cu) # Add all your .cu files here 17 | 18 | link_directories(/usr/local/lib /usr/lib) 19 | 20 | set(EXECUTABLE_OUTPUT_PATH ./) 21 | add_compile_options(-g -fpermissive -std=c++17) 22 | set(CMAKE_BUILD_TYPE Release) 23 | 24 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ./) 25 | cuda_add_library(render SHARED ${DEC} ${ENC} ${RENDER} ${CUDA_SOURCES} ${MOV}) 26 | target_link_libraries(render avutil avformat avcodec swscale swresample nvcuvid nvidia-encode ${OpenCV_LIBS} ${CUDA_LIBRARIES} cuda) 27 | 28 | cuda_add_executable(demo test/test.cpp) 29 | target_link_libraries(demo render) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 sunkx 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 996.icu [![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) 2 | # Nvidia-Video-Codec 3 | * Nvidia video hard decoding, rendering, soft/hard encoding, and writing to MP4 file 4 | * Nvidia视频硬解码、渲染、软/硬编码并写入MP4文件。 5 | 6 | 7 | # 本项目基于英伟达 Video_Codec_SDK_11.0.10 API实现。 8 | * 解码:使用Video_Codec_SDK_11.0.10解码API对视频进行解码,支持H264、H265。 9 | * 渲染:使用opencv对图像进行渲染,涉及到了基础的cuda开发(不过都是比较简单的,没有什么难度)。 10 | * 编码:支持软硬编码切换,硬编码使用Video_Codec_SDK_11.0.10 API,软编码使用ffmpeg API,视频编码格式为H264,如需H265可自行修改(NvCodecRender::EncInit()/NvCodecRender.cpp)。基本nvidia的所有显卡都支持视频解码、但不是所有显卡都支持编码、所以这里实现了软硬编码切换功能。 11 | * MP4:编码后的视频写入到MP4文件中,这里使用了老陈的libmov库,项目地址:https://github.com/ireader/media-server 12 | 13 | 14 | # 环境准备 15 | * 安装驱动,注意本项目使用的是Video_Codec_SDK_11.0.10,不同SDK要求的驱动版本不一样,本项目在465.19.01和535.104.12验证过,其他版本请自行查阅官网。 16 | * 安装cuda,版本和驱动保持一致即可,不同驱动对应不同cuda版本,驱动是向下兼容的,可以先安装cuda,在安装的时候选择一起安装驱动也可以,就这样就不需要单独安装驱动了。 17 | * 安装ffmpeg,版本 >= 4.x 安装在/usr/local,否则请修改CMakeLists,把ffmpeg头文件和库路劲添加进去。 18 | * 项目测试环境cuda 11.3 + 驱动 535.104.12 + ffmpeg 4.0.5、cuda 11.3 + 驱动 465.19.01 + ffmpeg 4.0.5。 19 | 20 | 21 | # 代码支持Linux、Windows 22 | # Linux编译 23 | * export PATH=$PATH:/usr/local/cuda/bin 24 | * export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64 25 | * mkdir build 26 | * cd build 27 | * cmake .. 28 | * make -j4 29 | * 测试 ./demo ../test/test_1280x720_v.mp4 out.mp4 0 0 ,参数:输入文件、输出文件、使用哪张显卡(只有一张显卡的话就写0)、指定使用软编码还是硬件编码(0-软编码 1-硬件编码) 30 | # Windows编译 31 | * 没有测试过,但是代码是通用的。 32 | 33 | # 注意 34 | * VideoCodec目录里的文件都是从Video_Codec_SDK_11.0.10中提取的,因为Video_Codec_SDK_11.0.10中文件很多,实际使用过程中并不是所有的都需要,VideoCodec里面只提取出来本项目使用的文件,并进行分类。 35 | * 项目对FFmpegDemuxer.h进行了修改,增加了int GetParam(int &fps,int &bitrate)函数,获取视频帧率和码流,在编码中使用,这样的目的是编码时保留图像原始帧率和码流参数。 36 | 37 | 38 | # 技术交流 39 | * kxsun617@163.com 40 | 41 | -------------------------------------------------------------------------------- /Render/MP4Writer.cpp: -------------------------------------------------------------------------------- 1 | #include "MP4Writer.h" 2 | 3 | MP4Writer::MP4Writer(const char *mp4) 4 | { 5 | fp = fopen(mp4, "wb+"); 6 | mov = mov_writer_create(mov_file_buffer(), fp, MOV_FLAG_FASTSTART); 7 | } 8 | MP4Writer::~MP4Writer() 9 | { 10 | if (mov) { 11 | mov_writer_destroy(mov); 12 | mov = NULL; 13 | } 14 | if (fp) { 15 | fclose(fp); 16 | fp = NULL; 17 | } 18 | } 19 | int MP4Writer::AddVideo(int width, int height, const void *extra_data, size_t extra_data_size, MEDIATYP type) 20 | { 21 | std::unique_lock guard(mtx); 22 | if (type == H264) { 23 | s_video_track = mov_writer_add_video(mov, MOV_OBJECT_H264, width, height, extra_data, extra_data_size); 24 | } else if (type == HEVC) { 25 | s_video_track = mov_writer_add_video(mov, MOV_OBJECT_HEVC, width, height, extra_data, extra_data_size); 26 | } else { 27 | return -1; 28 | } 29 | v_object = type; 30 | return s_video_track; 31 | } 32 | int MP4Writer::AddAudio(int channel_count, int bits_per_sample, int sample_rate, const void *extra_data, size_t extra_data_size, MEDIATYP type) 33 | { 34 | std::unique_lock guard(mtx); 35 | if (type == AAC) { 36 | s_audio_track = mov_writer_add_audio(mov, MOV_OBJECT_AAC, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size); // bits_per_sample==16 37 | } else if (type == G711A) { 38 | s_audio_track = mov_writer_add_audio(mov, MOV_OBJECT_G711a, 1, 16, 8000, NULL, 0); 39 | } else if (type == G711U) { 40 | s_audio_track = mov_writer_add_audio(mov, MOV_OBJECT_G711u, 1, 16, 8000, NULL, 0); 41 | } else { 42 | return -1; 43 | } 44 | a_object = type; 45 | return s_audio_track; 46 | } 47 | int MP4Writer::WriteMedia(const void *data, size_t size, int trackId, int64_t pts, int64_t dts, bool isKey) 48 | { 49 | std::unique_lock guard(mtx); 50 | if (trackId == s_video_track) { 51 | mov_writer_write(mov, s_video_track, data, size, pts, dts, isKey ? MOV_AV_FLAG_KEYFREAME : 0); 52 | } else if (trackId == s_audio_track) { 53 | if (a_object == MOV_OBJECT_AAC) { 54 | mov_writer_write(mov, s_audio_track, data, size, pts, dts, isKey ? MOV_AV_FLAG_KEYFREAME : 0); 55 | } else if (a_object == MOV_OBJECT_G711a || a_object == MOV_OBJECT_G711u) { 56 | mov_writer_write(mov, s_audio_track, data, size, pts, dts, 0); 57 | } else { 58 | return -1; 59 | } 60 | } else { 61 | return -1; 62 | } 63 | return 0; 64 | } 65 | int h264_video_record_config(unsigned char *buffer, unsigned char *sps, int sps_len, unsigned char *pps, int pps_len) 66 | { 67 | int pos = 0; 68 | buffer[0] = 0x01; 69 | buffer[1] = sps[1]; 70 | buffer[2] = sps[2]; 71 | buffer[3] = sps[3]; 72 | 73 | buffer[4] = 0xff; 74 | buffer[5] = 0xe1; 75 | buffer[6] = sps_len >> 8; 76 | buffer[7] = (unsigned char)sps_len; 77 | memcpy(&buffer[8], sps, sps_len); 78 | pos = 8 + sps_len; 79 | 80 | buffer[pos++] = 0x01; 81 | buffer[pos++] = pps_len >> 8; 82 | buffer[pos++] = (unsigned char)pps_len; 83 | memcpy(&buffer[pos], pps, pps_len); 84 | pos += pps_len; 85 | return pos; 86 | } 87 | 88 | int hevc_video_record_config(unsigned char *buffer, unsigned char *sps, int sps_len, unsigned char *pps, int pps_len, unsigned char *vps, int vps_len) 89 | { 90 | 91 | int i = 0; 92 | // buffer[i++] = 0x1C; 93 | // buffer[i++] = 0x00; 94 | // buffer[i++] = 0x00; 95 | // buffer[i++] = 0x00; 96 | // buffer[i++] = 0x00; 97 | buffer[i++] = 0x01; 98 | 99 | // general_profile_idc 8bit 100 | buffer[i++] = 0x00; 101 | // general_profile_compatibility_flags 32 bit 102 | buffer[i++] = 0x00; 103 | buffer[i++] = 0x00; 104 | buffer[i++] = 0x00; 105 | buffer[i++] = 0x00; 106 | 107 | // 48 bit NUll nothing deal in rtmp 108 | buffer[i++] = 0x00; 109 | buffer[i++] = 0x00; 110 | buffer[i++] = 0x00; 111 | buffer[i++] = 0x00; 112 | buffer[i++] = 0x00; 113 | buffer[i++] = 0x00; 114 | 115 | // general_level_idc 116 | buffer[i++] = 0x00; 117 | 118 | // 48 bit NUll nothing deal in rtmp 119 | buffer[i++] = 0xf0; 120 | buffer[i++] = 0x00; 121 | buffer[i++] = 0xfc; 122 | buffer[i++] = 0xfc; 123 | buffer[i++] = 0xf8; 124 | buffer[i++] = 0xf8; 125 | 126 | // bit(16) avgFrameRate; 127 | buffer[i++] = 0x00; 128 | buffer[i++] = 0x00; 129 | 130 | /* bit(2) constantFrameRate; */ 131 | /* bit(3) numTemporalLayers; */ 132 | /* bit(1) temporalIdNested; */ 133 | buffer[i++] = 0x03; 134 | 135 | /* unsigned int(8) numOfArrays; 03 */ 136 | buffer[i++] = 0x03; 137 | 138 | // printf("HEVCDecoderConfigurationRecord data = %s\n", buffer); 139 | buffer[i++] = 0xa0; // vps 32 140 | buffer[i++] = (1 >> 8) & 0xff; 141 | buffer[i++] = 1 & 0xff; 142 | buffer[i++] = (vps_len >> 8) & 0xff; 143 | buffer[i++] = (vps_len) & 0xff; 144 | memcpy(&buffer[i], vps, vps_len); 145 | i += vps_len; 146 | 147 | // sps 148 | buffer[i++] = 0xa1; // sps 33 149 | buffer[i++] = (1 >> 8) & 0xff; 150 | buffer[i++] = 1 & 0xff; 151 | buffer[i++] = (sps_len >> 8) & 0xff; 152 | buffer[i++] = sps_len & 0xff; 153 | memcpy(&buffer[i], sps, sps_len); 154 | i += sps_len; 155 | 156 | // pps 157 | buffer[i++] = 0xa2; // pps 34 158 | buffer[i++] = (1 >> 8) & 0xff; 159 | buffer[i++] = 1 & 0xff; 160 | buffer[i++] = (pps_len >> 8) & 0xff; 161 | buffer[i++] = (pps_len) & 0xff; 162 | memcpy(&buffer[i], pps, pps_len); 163 | i += pps_len; 164 | return i; 165 | } 166 | int aac_audio_record_config() 167 | { 168 | return 0; 169 | } -------------------------------------------------------------------------------- /Render/MP4Writer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "mov-writer.h" 3 | #include "mov-format.h" 4 | #include "mov-file-buffer.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | enum MEDIATYP { 12 | H264 = 0, 13 | HEVC, 14 | AAC, 15 | G711A, 16 | G711U 17 | }; 18 | int h264_video_record_config(unsigned char *buffer, unsigned char *sps, int sps_len, unsigned char *pps, int pps_len); 19 | int hevc_video_record_config(unsigned char *buffer, unsigned char *sps, int sps_len, unsigned char *pps, int pps_len, unsigned char *vps, int vps_len); 20 | int aac_audio_record_config(); // TODO 21 | // 如果媒体包含多个sps pps,需要从编码器中获取所有的sps pps,然后写入到extra_data中(eg:x264调用x264_encoder_headers函数可以获取编码参数sps pps) 22 | // 关于extra_data定义参考:https://blog.csdn.net/weixin_43147845/article/details/137011573?spm=1001.2014.3001.5502 23 | class MP4Writer 24 | { 25 | public: 26 | MP4Writer(const char *mp4); 27 | ~MP4Writer(); 28 | int AddVideo(int width, int height, const void *extra_data, size_t extra_data_size, MEDIATYP type); 29 | int AddAudio(int channel_count, int bits_per_sample, int sample_rate, const void *extra_data, size_t extra_data_size, MEDIATYP type); 30 | int WriteMedia(const void *data, size_t size, int trackId, int64_t pts, int64_t dts, bool isKey); 31 | 32 | private: 33 | int s_video_track = -1; 34 | uint8_t v_object; 35 | int s_audio_track = -1; 36 | uint8_t a_object; 37 | mov_writer_t *mov = NULL; 38 | FILE *fp = NULL; 39 | std::mutex mtx; 40 | }; -------------------------------------------------------------------------------- /Render/NvCodecRender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "NvDecoder.h" 15 | #include "NvEncoderCuda.h" 16 | #include "NvEncoderCLIOptions.h" 17 | #include "NvCodecUtils.h" 18 | #include "FFmpegDemuxer.h" 19 | #include "ColorSpace.h" 20 | #include "MP4Writer.h" 21 | extern "C" { 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | } 29 | 30 | // 版本 Video_Codec_SDK_11.0.10 不同版本SDK对显卡驱动要求不同 31 | class NvCodecRender 32 | { 33 | public: 34 | NvCodecRender(const char *input, const char *output, int gpu_idx, bool use_nvenc = false); 35 | ~NvCodecRender(); 36 | int Render(); 37 | 38 | private: 39 | int Draw(unsigned char *rgba_frame, int w, int h); 40 | int EncInit(); 41 | int EncFrame(void *ptr, int size); 42 | int EncDestory(); 43 | int Write2File(uint8_t *data, int len); 44 | 45 | private: 46 | std::string in_file_; 47 | std::string out_file_; 48 | FFmpegDemuxer *demuxer_ = NULL; 49 | NvDecoder *dec_ = NULL; 50 | int gpu_idx_ = 0; 51 | int width_, height_; 52 | bool use_nvenc_ = true; 53 | // NVENC 54 | NvEncoderInitParam init_param_; 55 | NV_ENC_BUFFER_FORMAT eformat_; 56 | CUdeviceptr ptr_image_enc_ = 0; 57 | NvEncoderCuda *enc_ = NULL; 58 | // soft enc 59 | AVCodecContext *h264_codec_ctx_ = NULL; 60 | AVCodec *h264_codec_ = NULL; 61 | SwsContext *sws_context_ = NULL; 62 | AVFrame *yuv_frame_ = NULL; 63 | AVPacket enc_packet_; 64 | 65 | MP4Writer *mp4_ = NULL; 66 | unsigned char sps_buffer_[1024]; 67 | int sps_len_ = 0; 68 | unsigned char pps_buffer_[1024]; 69 | int pps_len_ = 0; 70 | int video_track_ = -1; 71 | int v_fps_ = 25; 72 | int v_bitrate_ = 0; 73 | int v_pts_ = 0; 74 | int out_fps_ = 25; // 输出帧率 75 | int64_t total_frames_ = 0; 76 | unsigned char *v_packet_ = NULL; 77 | int64_t packet_len_ = 4 * 1024 * 1024; 78 | }; -------------------------------------------------------------------------------- /Render/mov-file-buffer.c: -------------------------------------------------------------------------------- 1 | #include "mov-buffer.h" 2 | #include "mov-file-buffer.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined(_WIN32) || defined(_WIN64) 9 | #define fseek64 _fseeki64 10 | #define ftell64 _ftelli64 11 | #elif defined(__ANDROID__) 12 | #define fseek64 fseek 13 | #define ftell64 ftell 14 | #elif defined(OS_LINUX) 15 | #define fseek64 fseeko64 16 | #define ftell64 ftello64 17 | #else 18 | #define fseek64 fseek 19 | #define ftell64 ftell 20 | #endif 21 | 22 | static int mov_file_read(void* fp, void* data, uint64_t bytes) 23 | { 24 | if (bytes == fread(data, 1, bytes, (FILE*)fp)) 25 | return 0; 26 | return 0 != ferror((FILE*)fp) ? ferror((FILE*)fp) : -1 /*EOF*/; 27 | } 28 | 29 | static int mov_file_write(void* fp, const void* data, uint64_t bytes) 30 | { 31 | return bytes == fwrite(data, 1, bytes, (FILE*)fp) ? 0 : ferror((FILE*)fp); 32 | } 33 | 34 | static int mov_file_seek(void* fp, int64_t offset) 35 | { 36 | return fseek64((FILE*)fp, offset, offset >= 0 ? SEEK_SET : SEEK_END); 37 | } 38 | 39 | static int64_t mov_file_tell(void* fp) 40 | { 41 | return ftell64((FILE*)fp); 42 | } 43 | 44 | static int mov_file_cache_read(void* fp, void* data, uint64_t bytes) 45 | { 46 | uint8_t* p = (uint8_t*)data; 47 | struct mov_file_cache_t* file = (struct mov_file_cache_t*)fp; 48 | while (bytes > 0) 49 | { 50 | assert(file->off <= file->len); 51 | if (file->off >= file->len) 52 | { 53 | if (bytes >= sizeof(file->ptr)) 54 | { 55 | if (bytes == fread(p, 1, bytes, file->fp)) 56 | { 57 | file->tell += bytes; 58 | return 0; 59 | } 60 | return 0 != ferror(file->fp) ? ferror(file->fp) : -1 /*EOF*/; 61 | } 62 | else 63 | { 64 | file->off = 0; 65 | file->len = (unsigned int)fread(file->ptr, 1, sizeof(file->ptr), file->fp); 66 | if (file->len < 1) 67 | return 0 != ferror(file->fp) ? ferror(file->fp) : -1 /*EOF*/; 68 | } 69 | } 70 | 71 | if (file->off < file->len) 72 | { 73 | unsigned int n = file->len - file->off; 74 | n = n > bytes ? (unsigned int)bytes : n; 75 | memcpy(p, file->ptr + file->off, n); 76 | file->tell += n; 77 | file->off += n; 78 | bytes -= n; 79 | p += n; 80 | } 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | static int mov_file_cache_write(void* fp, const void* data, uint64_t bytes) 87 | { 88 | struct mov_file_cache_t* file = (struct mov_file_cache_t*)fp; 89 | 90 | file->tell += bytes; 91 | 92 | if (file->off + bytes < sizeof(file->ptr)) 93 | { 94 | memcpy(file->ptr + file->off, data, bytes); 95 | file->off += (unsigned int)bytes; 96 | return 0; 97 | } 98 | 99 | // write buffer 100 | if (file->off > 0) 101 | { 102 | if (file->off != fwrite(file->ptr, 1, file->off, file->fp)) 103 | return ferror(file->fp); 104 | file->off = 0; // clear buffer 105 | } 106 | 107 | // write data; 108 | return bytes == fwrite(data, 1, bytes, file->fp) ? 0 : ferror(file->fp); 109 | } 110 | 111 | static int mov_file_cache_seek(void* fp, int64_t offset) 112 | { 113 | int r; 114 | struct mov_file_cache_t* file = (struct mov_file_cache_t*)fp; 115 | if (offset != file->tell) 116 | { 117 | if (file->off > file->len) 118 | { 119 | // write bufferred data 120 | if(file->off != fwrite(file->ptr, 1, file->off, file->fp)) 121 | return ferror(file->fp); 122 | } 123 | 124 | file->off = file->len = 0; 125 | r = fseek64(file->fp, offset, offset >= 0 ? SEEK_SET : SEEK_END); 126 | file->tell = ftell64(file->fp); 127 | return r; 128 | } 129 | return 0; 130 | } 131 | 132 | static int64_t mov_file_cache_tell(void* fp) 133 | { 134 | struct mov_file_cache_t* file = (struct mov_file_cache_t*)fp; 135 | if (ftell64(file->fp) != (int64_t)(file->tell + (uint64_t)(int)(file->len - file->off))) 136 | return -1; 137 | return (int64_t)file->tell; 138 | //return ftell64(file->fp); 139 | } 140 | 141 | const struct mov_buffer_t* mov_file_buffer(void) 142 | { 143 | static struct mov_buffer_t s_io = { 144 | mov_file_read, 145 | mov_file_write, 146 | mov_file_seek, 147 | mov_file_tell, 148 | }; 149 | return &s_io; 150 | } 151 | 152 | const struct mov_buffer_t* mov_file_cache_buffer(void) 153 | { 154 | static struct mov_buffer_t s_io = { 155 | mov_file_cache_read, 156 | mov_file_cache_write, 157 | mov_file_cache_seek, 158 | mov_file_cache_tell, 159 | }; 160 | return &s_io; 161 | } 162 | -------------------------------------------------------------------------------- /Render/mov-file-buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_file_buffer_h_ 2 | #define _mov_file_buffer_h_ 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | struct mov_file_cache_t 12 | { 13 | FILE* fp; 14 | uint8_t ptr[800]; 15 | unsigned int len; 16 | unsigned int off; 17 | uint64_t tell; 18 | }; 19 | 20 | const struct mov_buffer_t* mov_file_cache_buffer(void); 21 | 22 | const struct mov_buffer_t* mov_file_buffer(void); 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | #endif /* !_mov_file_buffer_h_ */ 28 | -------------------------------------------------------------------------------- /VideoCodec/NvDec/ColorSpace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | typedef enum ColorSpaceStandard { 6 | ColorSpaceStandard_BT709 = 1, 7 | ColorSpaceStandard_Unspecified = 2, 8 | ColorSpaceStandard_Reserved = 3, 9 | ColorSpaceStandard_FCC = 4, 10 | ColorSpaceStandard_BT470 = 5, 11 | ColorSpaceStandard_BT601 = 6, 12 | ColorSpaceStandard_SMPTE240M = 7, 13 | ColorSpaceStandard_YCgCo = 8, 14 | ColorSpaceStandard_BT2020 = 9, 15 | ColorSpaceStandard_BT2020C = 10 16 | } ColorSpaceStandard; 17 | 18 | union BGRA32 { 19 | uint32_t d; 20 | uchar4 v; 21 | struct { 22 | uint8_t b, g, r, a; 23 | } c; 24 | }; 25 | 26 | union RGBA32 { 27 | uint32_t d; 28 | uchar4 v; 29 | struct { 30 | uint8_t r, g, b, a; 31 | } c; 32 | }; 33 | 34 | union BGRA64 { 35 | uint64_t d; 36 | ushort4 v; 37 | struct { 38 | uint16_t b, g, r, a; 39 | } c; 40 | }; 41 | 42 | union RGBA64 { 43 | uint64_t d; 44 | ushort4 v; 45 | struct { 46 | uint16_t r, g, b, a; 47 | } c; 48 | }; 49 | -------------------------------------------------------------------------------- /VideoCodec/NvDec/Logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2020 NVIDIA Corporation. All rights reserved. 3 | * 4 | * Please refer to the NVIDIA end user license agreement (EULA) associated 5 | * with this source code for terms and conditions that govern your use of 6 | * this software. Any use, reproduction, disclosure, or distribution of 7 | * this software and related documentation outside the terms of the EULA 8 | * is strictly prohibited. 9 | * 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #ifdef _WIN32 22 | #include 23 | #include 24 | 25 | #pragma comment(lib, "ws2_32.lib") 26 | #undef ERROR 27 | #else 28 | #include 29 | #include 30 | #include 31 | #include 32 | #define SOCKET int 33 | #define INVALID_SOCKET -1 34 | #endif 35 | 36 | enum LogLevel { 37 | TRACE, 38 | INFO, 39 | WARNING, 40 | ERROR, 41 | FATAL 42 | }; 43 | 44 | namespace simplelogger{ 45 | class Logger { 46 | public: 47 | Logger(LogLevel level, bool bPrintTimeStamp) : level(level), bPrintTimeStamp(bPrintTimeStamp) {} 48 | virtual ~Logger() {} 49 | virtual std::ostream& GetStream() = 0; 50 | virtual void FlushStream() {} 51 | bool ShouldLogFor(LogLevel l) { 52 | return l >= level; 53 | } 54 | char* GetLead(LogLevel l, const char *szFile, int nLine, const char *szFunc) { 55 | if (l < TRACE || l > FATAL) { 56 | sprintf(szLead, "[?????] "); 57 | return szLead; 58 | } 59 | const char *szLevels[] = {"TRACE", "INFO", "WARN", "ERROR", "FATAL"}; 60 | if (bPrintTimeStamp) { 61 | time_t t = time(NULL); 62 | struct tm *ptm = localtime(&t); 63 | sprintf(szLead, "[%-5s][%02d:%02d:%02d] ", 64 | szLevels[l], ptm->tm_hour, ptm->tm_min, ptm->tm_sec); 65 | } else { 66 | sprintf(szLead, "[%-5s] ", szLevels[l]); 67 | } 68 | return szLead; 69 | } 70 | void EnterCriticalSection() { 71 | mtx.lock(); 72 | } 73 | void LeaveCriticalSection() { 74 | mtx.unlock(); 75 | } 76 | private: 77 | LogLevel level; 78 | char szLead[80]; 79 | bool bPrintTimeStamp; 80 | std::mutex mtx; 81 | }; 82 | 83 | class LoggerFactory { 84 | public: 85 | static Logger* CreateFileLogger(std::string strFilePath, 86 | LogLevel level = INFO, bool bPrintTimeStamp = true) { 87 | return new FileLogger(strFilePath, level, bPrintTimeStamp); 88 | } 89 | static Logger* CreateConsoleLogger(LogLevel level = INFO, 90 | bool bPrintTimeStamp = true) { 91 | return new ConsoleLogger(level, bPrintTimeStamp); 92 | } 93 | static Logger* CreateUdpLogger(char *szHost, unsigned uPort, LogLevel level = INFO, 94 | bool bPrintTimeStamp = true) { 95 | return new UdpLogger(szHost, uPort, level, bPrintTimeStamp); 96 | } 97 | private: 98 | LoggerFactory() {} 99 | 100 | class FileLogger : public Logger { 101 | public: 102 | FileLogger(std::string strFilePath, LogLevel level, bool bPrintTimeStamp) 103 | : Logger(level, bPrintTimeStamp) { 104 | pFileOut = new std::ofstream(); 105 | pFileOut->open(strFilePath.c_str()); 106 | } 107 | ~FileLogger() { 108 | pFileOut->close(); 109 | } 110 | std::ostream& GetStream() { 111 | return *pFileOut; 112 | } 113 | private: 114 | std::ofstream *pFileOut; 115 | }; 116 | 117 | class ConsoleLogger : public Logger { 118 | public: 119 | ConsoleLogger(LogLevel level, bool bPrintTimeStamp) 120 | : Logger(level, bPrintTimeStamp) {} 121 | std::ostream& GetStream() { 122 | return std::cout; 123 | } 124 | }; 125 | 126 | class UdpLogger : public Logger { 127 | private: 128 | class UdpOstream : public std::ostream { 129 | public: 130 | UdpOstream(char *szHost, unsigned short uPort) : std::ostream(&sb), socket(INVALID_SOCKET){ 131 | #ifdef _WIN32 132 | WSADATA w; 133 | if (WSAStartup(0x0101, &w) != 0) { 134 | fprintf(stderr, "WSAStartup() failed.\n"); 135 | return; 136 | } 137 | #endif 138 | socket = ::socket(AF_INET, SOCK_DGRAM, 0); 139 | if (socket == INVALID_SOCKET) { 140 | #ifdef _WIN32 141 | WSACleanup(); 142 | #endif 143 | fprintf(stderr, "socket() failed.\n"); 144 | return; 145 | } 146 | #ifdef _WIN32 147 | unsigned int b1, b2, b3, b4; 148 | sscanf(szHost, "%u.%u.%u.%u", &b1, &b2, &b3, &b4); 149 | struct in_addr addr = {(unsigned char)b1, (unsigned char)b2, (unsigned char)b3, (unsigned char)b4}; 150 | #else 151 | struct in_addr addr = {inet_addr(szHost)}; 152 | #endif 153 | struct sockaddr_in s = {AF_INET, htons(uPort), addr}; 154 | server = s; 155 | } 156 | ~UdpOstream() throw() { 157 | if (socket == INVALID_SOCKET) { 158 | return; 159 | } 160 | #ifdef _WIN32 161 | closesocket(socket); 162 | WSACleanup(); 163 | #else 164 | close(socket); 165 | #endif 166 | } 167 | void Flush() { 168 | if (sendto(socket, sb.str().c_str(), (int)sb.str().length() + 1, 169 | 0, (struct sockaddr *)&server, (int)sizeof(sockaddr_in)) == -1) { 170 | fprintf(stderr, "sendto() failed.\n"); 171 | } 172 | sb.str(""); 173 | } 174 | 175 | private: 176 | std::stringbuf sb; 177 | SOCKET socket; 178 | struct sockaddr_in server; 179 | }; 180 | public: 181 | UdpLogger(char *szHost, unsigned uPort, LogLevel level, bool bPrintTimeStamp) 182 | : Logger(level, bPrintTimeStamp), udpOut(szHost, (unsigned short)uPort) {} 183 | UdpOstream& GetStream() { 184 | return udpOut; 185 | } 186 | virtual void FlushStream() { 187 | udpOut.Flush(); 188 | } 189 | private: 190 | UdpOstream udpOut; 191 | }; 192 | }; 193 | 194 | class LogTransaction { 195 | public: 196 | LogTransaction(Logger *pLogger, LogLevel level, const char *szFile, const int nLine, const char *szFunc) : pLogger(pLogger), level(level) { 197 | if (!pLogger) { 198 | std::cout << "[-----] "; 199 | return; 200 | } 201 | if (!pLogger->ShouldLogFor(level)) { 202 | return; 203 | } 204 | pLogger->EnterCriticalSection(); 205 | pLogger->GetStream() << pLogger->GetLead(level, szFile, nLine, szFunc); 206 | } 207 | ~LogTransaction() { 208 | if (!pLogger) { 209 | std::cout << std::endl; 210 | return; 211 | } 212 | if (!pLogger->ShouldLogFor(level)) { 213 | return; 214 | } 215 | pLogger->GetStream() << std::endl; 216 | pLogger->FlushStream(); 217 | pLogger->LeaveCriticalSection(); 218 | if (level == FATAL) { 219 | exit(1); 220 | } 221 | } 222 | std::ostream& GetStream() { 223 | if (!pLogger) { 224 | return std::cout; 225 | } 226 | if (!pLogger->ShouldLogFor(level)) { 227 | return ossNull; 228 | } 229 | return pLogger->GetStream(); 230 | } 231 | private: 232 | Logger *pLogger; 233 | LogLevel level; 234 | std::ostringstream ossNull; 235 | }; 236 | 237 | } 238 | 239 | extern simplelogger::Logger *logger; 240 | #define LOG(level) simplelogger::LogTransaction(logger, level, __FILE__, __LINE__, __FUNCTION__).GetStream() 241 | -------------------------------------------------------------------------------- /VideoCodec/NvEnc/NvEncoderCuda.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2020 NVIDIA Corporation. All rights reserved. 3 | * 4 | * Please refer to the NVIDIA end user license agreement (EULA) associated 5 | * with this source code for terms and conditions that govern your use of 6 | * this software. Any use, reproduction, disclosure, or distribution of 7 | * this software and related documentation outside the terms of the EULA 8 | * is strictly prohibited. 9 | * 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "NvEncoder.h" 19 | 20 | #define CUDA_DRVAPI_CALL( call ) \ 21 | do \ 22 | { \ 23 | CUresult err__ = call; \ 24 | if (err__ != CUDA_SUCCESS) \ 25 | { \ 26 | const char *szErrName = NULL; \ 27 | cuGetErrorName(err__, &szErrName); \ 28 | std::ostringstream errorLog; \ 29 | errorLog << "CUDA driver API error " << szErrName ; \ 30 | throw NVENCException::makeNVENCException(errorLog.str(), NV_ENC_ERR_GENERIC, __FUNCTION__, __FILE__, __LINE__); \ 31 | } \ 32 | } \ 33 | while (0) 34 | 35 | /** 36 | * @brief Encoder for CUDA device memory. 37 | */ 38 | class NvEncoderCuda : public NvEncoder 39 | { 40 | public: 41 | NvEncoderCuda(CUcontext cuContext, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat, 42 | uint32_t nExtraOutputDelay = 3, bool bMotionEstimationOnly = false, bool bOPInVideoMemory = false); 43 | virtual ~NvEncoderCuda(); 44 | 45 | /** 46 | * @brief This is a static function to copy input data from host memory to device memory. 47 | * This function assumes YUV plane is a single contiguous memory segment. 48 | */ 49 | static void CopyToDeviceFrame(CUcontext device, 50 | void* pSrcFrame, 51 | uint32_t nSrcPitch, 52 | CUdeviceptr pDstFrame, 53 | uint32_t dstPitch, 54 | int width, 55 | int height, 56 | CUmemorytype srcMemoryType, 57 | NV_ENC_BUFFER_FORMAT pixelFormat, 58 | const uint32_t dstChromaOffsets[], 59 | uint32_t numChromaPlanes, 60 | bool bUnAlignedDeviceCopy = false, 61 | CUstream stream = NULL); 62 | 63 | /** 64 | * @brief This is a static function to copy input data from host memory to device memory. 65 | * Application must pass a seperate device pointer for each YUV plane. 66 | */ 67 | static void CopyToDeviceFrame(CUcontext device, 68 | void* pSrcFrame, 69 | uint32_t nSrcPitch, 70 | CUdeviceptr pDstFrame, 71 | uint32_t dstPitch, 72 | int width, 73 | int height, 74 | CUmemorytype srcMemoryType, 75 | NV_ENC_BUFFER_FORMAT pixelFormat, 76 | CUdeviceptr dstChromaPtr[], 77 | uint32_t dstChromaPitch, 78 | uint32_t numChromaPlanes, 79 | bool bUnAlignedDeviceCopy = false); 80 | 81 | /** 82 | * @brief This function sets input and output CUDA streams 83 | */ 84 | void SetIOCudaStreams(NV_ENC_CUSTREAM_PTR inputStream, NV_ENC_CUSTREAM_PTR outputStream); 85 | 86 | protected: 87 | /** 88 | * @brief This function is used to release the input buffers allocated for encoding. 89 | * This function is an override of virtual function NvEncoder::ReleaseInputBuffers(). 90 | */ 91 | virtual void ReleaseInputBuffers() override; 92 | 93 | private: 94 | /** 95 | * @brief This function is used to allocate input buffers for encoding. 96 | * This function is an override of virtual function NvEncoder::AllocateInputBuffers(). 97 | */ 98 | virtual void AllocateInputBuffers(int32_t numInputBuffers) override; 99 | 100 | private: 101 | /** 102 | * @brief This is a private function to release CUDA device memory used for encoding. 103 | */ 104 | void ReleaseCudaResources(); 105 | 106 | protected: 107 | CUcontext m_cuContext; 108 | 109 | private: 110 | size_t m_cudaPitch = 0; 111 | }; 112 | -------------------------------------------------------------------------------- /Video_Codec_SDK_11.0.10.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BreakingY/Nvidia-Video-Codec/cbbc31cd93b8f66ba3a6e846a7a314eae62ca429/Video_Codec_SDK_11.0.10.zip -------------------------------------------------------------------------------- /libmov/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_CFLAGS += -DOS_LINUX -DOS_ANDROID 5 | LOCAL_LDLIBS += 6 | 7 | LOCAL_C_INCLUDES := $(LOCAL_PATH) 8 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/include 9 | 10 | LOCAL_SRC_FILES := $(wildcard source/*.c) 11 | LOCAL_SRC_FILES += $(wildcard source/*.cpp) 12 | 13 | LOCAL_MODULE := mov 14 | include $(BUILD_STATIC_LIBRARY) 15 | -------------------------------------------------------------------------------- /libmov/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------Output------------------------------ 2 | # OUTTYPE: 0-exe, 1-dll, 2-static 3 | #-------------------------------------------------------------------- 4 | OUTTYPE = 2 5 | OUTFILE = libmov.a 6 | 7 | #-------------------------------Include------------------------------ 8 | # 9 | # INCLUDES = $(addprefix -I,$(INCLUDES)) # add -I prefix 10 | #-------------------------------------------------------------------- 11 | INCLUDES = . ./include 12 | 13 | #-------------------------------Source------------------------------- 14 | # 15 | #-------------------------------------------------------------------- 16 | SOURCE_PATHS = source 17 | SOURCE_FILES = $(foreach dir,$(SOURCE_PATHS),$(wildcard $(dir)/*.cpp)) 18 | SOURCE_FILES += $(foreach dir,$(SOURCE_PATHS),$(wildcard $(dir)/*.c)) 19 | 20 | #-----------------------------Library-------------------------------- 21 | # 22 | # LIBPATHS = $(addprefix -L,$(LIBPATHS)) # add -L prefix 23 | #-------------------------------------------------------------------- 24 | LIBPATHS = 25 | ifdef RELEASE 26 | # relase library path 27 | LIBPATHS += 28 | else 29 | LIBPATHS += 30 | endif 31 | 32 | LIBS = 33 | 34 | STATIC_LIBS = 35 | 36 | #-----------------------------DEFINES-------------------------------- 37 | # 38 | # DEFINES := $(addprefix -D,$(DEFINES)) # add -L prefix 39 | #-------------------------------------------------------------------- 40 | DEFINES = 41 | 42 | include ../gcc.mk 43 | -------------------------------------------------------------------------------- /libmov/include/fmp4-writer.h: -------------------------------------------------------------------------------- 1 | #ifndef _fmp4_writer_h_ 2 | #define _fmp4_writer_h_ 3 | 4 | #include 5 | #include 6 | #include "mov-buffer.h" 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | typedef struct fmp4_writer_t fmp4_writer_t; 13 | 14 | /// @param[in] flags mov flags, such as: MOV_FLAG_SEGMENT, see more @mov-format.h 15 | fmp4_writer_t* fmp4_writer_create(const struct mov_buffer_t *buffer, void* param, int flags); 16 | void fmp4_writer_destroy(fmp4_writer_t* fmp4); 17 | 18 | /// @param[in] object MPEG-4 systems ObjectTypeIndication such as: MOV_OBJECT_H264, see more @mov-format.h 19 | /// @param[in] extra_data AudioSpecificConfig/AVCDecoderConfigurationRecord/HEVCDecoderConfigurationRecord 20 | /// @return >=0-track, <0-error 21 | int fmp4_writer_add_audio(fmp4_writer_t* fmp4, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size); 22 | int fmp4_writer_add_video(fmp4_writer_t* fmp4, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size); 23 | int fmp4_writer_add_subtitle(fmp4_writer_t* fmp4, uint8_t object, const void* extra_data, size_t extra_data_size); 24 | 25 | /// Write audio/video stream 26 | /// raw AAC data, don't include ADTS/AudioSpecificConfig 27 | /// H.264/H.265 MP4 format, replace start code(0x00000001) with NALU size 28 | /// @param[in] track return by mov_writer_add_audio/mov_writer_add_video 29 | /// @param[in] data audio/video frame 30 | /// @param[in] bytes buffer size 31 | /// @param[in] pts timestamp in millisecond 32 | /// @param[in] dts timestamp in millisecond 33 | /// @param[in] flags MOV_AV_FLAG_XXX, such as: MOV_AV_FLAG_KEYFREAME, see more @mov-format.h 34 | /// @return 0-ok, other-error 35 | int fmp4_writer_write(fmp4_writer_t* fmp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags); 36 | 37 | /// Save data and open next segment 38 | /// @return 0-ok, other-error 39 | int fmp4_writer_save_segment(fmp4_writer_t* fmp4); 40 | 41 | /// Get init segment data(write FTYP, MOOV only) 42 | /// WARNING: it caller duty to switch file/buffer context with fmp4_writer_write 43 | /// @return 0-ok, other-error 44 | int fmp4_writer_init_segment(fmp4_writer_t* fmp4); 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | #endif /* !_fmp4_writer_h_ */ 50 | -------------------------------------------------------------------------------- /libmov/include/mov-atom.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_atom_h_ 2 | #define _mov_atom_h_ 3 | 4 | #include 5 | #include 6 | 7 | #define N_BRAND 8 8 | 9 | struct mov_ftyp_t 10 | { 11 | uint32_t major_brand; 12 | uint32_t minor_version; 13 | 14 | uint32_t compatible_brands[N_BRAND]; 15 | int brands_count; 16 | }; 17 | 18 | // A.4 Temporal structure of the media (p148) 19 | // The movie, and each track, has a timescale. 20 | // This defines a time axis which has a number of ticks per second 21 | struct mov_mvhd_t 22 | { 23 | // FullBox 24 | uint32_t version : 8; 25 | uint32_t flags : 24; 26 | 27 | uint32_t timescale; // time-scale for the entire presentation, the number of time units that pass in one second 28 | uint64_t duration; // default UINT64_MAX(by timescale) 29 | uint64_t creation_time; // seconds sine midnight, Jan. 1, 1904, UTC 30 | uint64_t modification_time; // seconds sine midnight, Jan. 1, 1904, UTC 31 | 32 | uint32_t rate; 33 | uint16_t volume; // fixed point 8.8 number, 1.0 (0x0100) is full volume 34 | //uint16_t reserved; 35 | //uint32_t reserved2[2]; 36 | int32_t matrix[9]; // u,v,w 37 | //int32_t pre_defined[6]; 38 | uint32_t next_track_ID; 39 | }; 40 | 41 | enum 42 | { 43 | MOV_TKHD_FLAG_TRACK_ENABLE = 0x01, 44 | MOV_TKHD_FLAG_TRACK_IN_MOVIE = 0x02, 45 | MOV_TKHD_FLAG_TRACK_IN_PREVIEW = 0x04, 46 | }; 47 | 48 | struct mov_tkhd_t 49 | { 50 | // FullBox 51 | uint32_t version : 8; 52 | uint32_t flags : 24; // MOV_TKHD_FLAG_XXX 53 | 54 | uint32_t track_ID; // cannot be zero 55 | uint64_t creation_time; // seconds sine midnight, Jan. 1, 1904, UTC 56 | uint64_t modification_time; // seconds sine midnight, Jan. 1, 1904, UTC 57 | uint64_t duration; // default UINT64_MAX(by Movie Header Box timescale) 58 | //uint32_t reserved; 59 | 60 | //uint32_t reserved2[2]; 61 | int16_t layer; 62 | int16_t alternate_group; 63 | int16_t volume; // fixed point 8.8 number, 1.0 (0x0100) is full volume 64 | //uint16_t reserved; 65 | int32_t matrix[9]; // u,v,w 66 | uint32_t width; // fixed-point 16.16 values 67 | uint32_t height; // fixed-point 16.16 values 68 | }; 69 | 70 | struct mov_mdhd_t 71 | { 72 | // FullBox 73 | uint32_t version : 8; 74 | uint32_t flags : 24; 75 | 76 | uint32_t timescale; // second 77 | uint64_t duration; // default UINT64_MAX(by timescale) 78 | uint64_t creation_time; // seconds sine midnight, Jan. 1, 1904, UTC 79 | uint64_t modification_time; // seconds sine midnight, Jan. 1, 1904, UTC 80 | 81 | uint32_t pad : 1; 82 | uint32_t language : 15; 83 | uint32_t pre_defined : 16; 84 | }; 85 | 86 | struct mov_sample_entry_t 87 | { 88 | uint16_t data_reference_index; // ref [dref] Data Reference Boxes 89 | uint8_t object_type_indication; // H.264/AAC MOV_OBJECT_XXX (DecoderConfigDescriptor) 90 | uint8_t stream_type; // MP4_STREAM_XXX 91 | uint8_t* extra_data; // H.264 sps/pps 92 | int extra_data_size; 93 | 94 | union 95 | { 96 | struct mov_bitrate_t 97 | { 98 | uint32_t bufferSizeDB; 99 | uint32_t maxBitrate; 100 | uint32_t avgBitrate; 101 | } bitrate; 102 | 103 | //struct mov_uri_t 104 | //{ 105 | // char uri[256]; 106 | //} uri; 107 | 108 | // visual 109 | struct mov_visual_sample_t 110 | { 111 | uint16_t width; 112 | uint16_t height; 113 | uint32_t horizresolution; // 0x00480000 - 72dpi 114 | uint32_t vertresolution; // 0x00480000 - 72dpi 115 | uint16_t frame_count; // default 1 116 | uint16_t depth; // 0x0018 117 | 118 | struct mov_pixel_aspect_ratio_t 119 | { 120 | uint32_t h_spacing; 121 | uint32_t v_spacing; 122 | } pasp; 123 | } visual; 124 | 125 | struct mov_audio_sample_t 126 | { 127 | uint16_t channelcount; // default 2 128 | uint16_t samplesize; // default 16 129 | uint32_t samplerate; // { default samplerate of media } << 16 130 | } audio; 131 | } u; 132 | }; 133 | 134 | struct mov_stsd_t 135 | { 136 | struct mov_sample_entry_t *current; // current entry, read only 137 | struct mov_sample_entry_t *entries; 138 | uint32_t entry_count; 139 | }; 140 | 141 | struct mov_stts_t 142 | { 143 | uint32_t sample_count; 144 | uint32_t sample_delta; // in the time-scale of the media 145 | }; 146 | 147 | struct mov_stsc_t 148 | { 149 | uint32_t first_chunk; 150 | uint32_t samples_per_chunk; 151 | uint32_t sample_description_index; 152 | }; 153 | 154 | struct mov_elst_t 155 | { 156 | uint64_t segment_duration; // by Movie Header Box timescale 157 | int64_t media_time; 158 | int16_t media_rate_integer; 159 | int16_t media_rate_fraction; 160 | }; 161 | 162 | struct mov_trex_t 163 | { 164 | // uint32_t track_ID; 165 | uint32_t default_sample_description_index; 166 | uint32_t default_sample_duration; 167 | uint32_t default_sample_size; 168 | uint32_t default_sample_flags; 169 | }; 170 | 171 | struct mov_tfhd_t 172 | { 173 | uint32_t flags; 174 | // uint32_t track_ID; 175 | uint64_t base_data_offset; 176 | uint32_t sample_description_index; 177 | uint32_t default_sample_duration; 178 | uint32_t default_sample_size; 179 | uint32_t default_sample_flags; 180 | }; 181 | 182 | #endif /* !_mov_atom_h_ */ 183 | -------------------------------------------------------------------------------- /libmov/include/mov-box.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_box_h_ 2 | #define _mov_box_h_ 3 | 4 | #include 5 | #include 6 | 7 | // ISO/IEC 14496-12:2012(E) 4.2 Object Structure (16) 8 | struct mov_box_t 9 | { 10 | uint64_t size; // 0-size: box extends to end of file, 1-size: large size 11 | uint32_t type; 12 | 13 | // if 'uuid' == type 14 | //uint8_t usertype[16]; 15 | 16 | // FullBox 17 | //uint32_t version : 8; 18 | //uint32_t flags : 24; 19 | 20 | #if defined(DEBUG) || defined(_DEBUG) 21 | int level; 22 | #endif 23 | }; 24 | 25 | #endif /* !_mov_box_h_ */ 26 | -------------------------------------------------------------------------------- /libmov/include/mov-buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_buffer_h_ 2 | #define _mov_buffer_h_ 3 | 4 | #include 5 | 6 | struct mov_buffer_t 7 | { 8 | /// read data from buffer 9 | /// @param[in] param user-defined parameter 10 | /// @param[out] data user buffer 11 | /// @param[in] bytes data buffer size 12 | /// @return 0-ok, <0-error 13 | int (*read)(void* param, void* data, uint64_t bytes); 14 | 15 | /// write data to buffer 16 | /// @param[in] param user-defined parameter 17 | /// @param[in] data user buffer 18 | /// @param[in] bytes data buffer size 19 | /// @return 0-ok, <0-error 20 | int (*write)(void* param, const void* data, uint64_t bytes); 21 | 22 | /// move buffer position 23 | /// @param[in] param user-defined parameter 24 | /// @param[in] offset >=0-seek buffer read/write position to offset(from buffer begin), <0-seek from file end(SEEK_END) 25 | /// @return 0-ok, <0-error 26 | int (*seek)(void* param, int64_t offset); 27 | 28 | /// get buffer read/write position 29 | /// @return <0-error, other-current read/write position 30 | int64_t (*tell)(void* param); 31 | }; 32 | 33 | #endif /* !_mov_buffer_h_ */ 34 | -------------------------------------------------------------------------------- /libmov/include/mov-format.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_format_h_ 2 | #define _mov_format_h_ 3 | 4 | // ISO/IEC 14496-1:2010(E) 7.2.6.6 DecoderConfigDescriptor (p48) 5 | // MPEG-4 systems ObjectTypeIndication 6 | // http://www.mp4ra.org/object.html 7 | #define MOV_OBJECT_TEXT 0x08 // Text Stream 8 | #define MOV_OBJECT_MP4V 0x20 // Visual ISO/IEC 14496-2 (c) 9 | #define MOV_OBJECT_H264 0x21 // Visual ITU-T Recommendation H.264 | ISO/IEC 14496-10 10 | #define MOV_OBJECT_H265 0x23 // Visual ISO/IEC 23008-2 | ITU-T Recommendation H.265 11 | #define MOV_OBJECT_AAC 0x40 // Audio ISO/IEC 14496-3 12 | #define MOV_OBJECT_MP2V 0x60 // Visual ISO/IEC 13818-2 Simple Profile 13 | #define MOV_OBJECT_AAC_MAIN 0x66 // MPEG-2 AAC Main 14 | #define MOV_OBJECT_AAC_LOW 0x67 // MPEG-2 AAC Low 15 | #define MOV_OBJECT_AAC_SSR 0x68 // MPEG-2 AAC SSR 16 | #define MOV_OBJECT_MP3 0x69 // Audio ISO/IEC 13818-3 17 | #define MOV_OBJECT_MP1V 0x6A // Visual ISO/IEC 11172-2 18 | #define MOV_OBJECT_MP1A 0x6B // Audio ISO/IEC 11172-3 19 | #define MOV_OBJECT_JPEG 0x6C // Visual ISO/IEC 10918-1 (JPEG) 20 | #define MOV_OBJECT_PNG 0x6D // Portable Network Graphics (f) 21 | #define MOV_OBJECT_JPEG2000 0x6E // Visual ISO/IEC 15444-1 (JPEG 2000) 22 | #define MOV_OBJECT_VC1 0xA3 // SMPTE VC-1 Video 23 | #define MOV_OBJECT_DIRAC 0xA4 // Dirac Video Coder 24 | #define MOV_OBJECT_AC3 0xA5 // AC-3 25 | #define MOV_OBJECT_EAC3 0xA6 // Enhanced AC-3 26 | #define MOV_OBJECT_G719 0xA8 // ITU G.719 Audio 27 | #define MOV_OBJECT_DTS 0xA9 // Core Substream 28 | #define MOV_OBJECT_OPUS 0xAD // Opus audio https://opus-codec.org/docs/opus_in_isobmff.html 29 | #define MOV_OBJECT_VP9 0xB1 // VP9 Video 30 | #define MOV_OBJECT_FLAC 0xC1 // nonstandard from FFMPEG 31 | #define MOV_OBJECT_VP8 0xC2 // nonstandard 32 | #define MOV_OBJECT_H266 0xFC // ITU-T Recommendation H.266 33 | #define MOV_OBJECT_G711a 0xFD // ITU G.711 alaw 34 | #define MOV_OBJECT_G711u 0xFE // ITU G.711 ulaw 35 | #define MOV_OBJECT_AV1 0xFF // AV1: https://aomediacodec.github.io/av1-isobmff 36 | 37 | #define MOV_OBJECT_NONE 0x00 // unknown object id 38 | #define MOV_OBJECT_AVC MOV_OBJECT_H264 39 | #define MOV_OBJECT_HEVC MOV_OBJECT_H265 40 | #define MOV_OBJECT_VVC MOV_OBJECT_H266 41 | #define MOV_OBJECT_ALAW MOV_OBJECT_G711a 42 | #define MOV_OBJECT_ULAW MOV_OBJECT_G711u 43 | 44 | /// MOV flags 45 | #define MOV_FLAG_FASTSTART 0x00000001 46 | #define MOV_FLAG_SEGMENT 0x00000002 // fmp4_writer only 47 | 48 | /// MOV av stream flag 49 | #define MOV_AV_FLAG_KEYFREAME 0x0001 50 | #define MOV_AV_FLAG_SEGMENT_FORCE 0x8000 // exclude with MOV_AV_FLAG_SEGMENT_DISABLE, fmp4_writer only 51 | #define MOV_AV_FLAG_SEGMENT_DISABLE 0x4000 // exclude with MOV_AV_FLAG_SEGMENT_FORCE, fmp4_writer only 52 | 53 | #endif /* !_mov_format_h_ */ 54 | -------------------------------------------------------------------------------- /libmov/include/mov-memory-buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_memory_buffer_h_ 2 | #define _mov_memory_buffer_h_ 3 | 4 | #include "mov-buffer.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct mov_memory_buffer_t 12 | { 13 | uint8_t* ptr; 14 | uint64_t bytes; 15 | 16 | uint64_t off; 17 | uint64_t capacity; 18 | uint64_t maxsize; // limit 19 | }; 20 | 21 | static int mov_memory_read(void* param, void* data, uint64_t bytes) 22 | { 23 | struct mov_memory_buffer_t* ptr; 24 | ptr = (struct mov_memory_buffer_t*)param; 25 | if (ptr->off + bytes > ptr->bytes) 26 | return -E2BIG; 27 | 28 | memcpy(data, ptr->ptr + ptr->off, (size_t)bytes); 29 | ptr->off += bytes; 30 | return 0; 31 | } 32 | 33 | static int mov_memory_write(void* param, const void* data, uint64_t bytes) 34 | { 35 | void* p; 36 | uint64_t capacity; 37 | struct mov_memory_buffer_t* ptr; 38 | ptr = (struct mov_memory_buffer_t*)param; 39 | if (ptr->off + bytes > ptr->maxsize) 40 | return -E2BIG; 41 | 42 | if (ptr->off + bytes > ptr->capacity) 43 | { 44 | capacity = ptr->off + bytes + 1 * 1024 * 1024; 45 | capacity = capacity > ptr->maxsize ? ptr->maxsize : capacity; 46 | p = realloc(ptr->ptr, capacity); 47 | if (NULL == p) 48 | return -ENOMEM; 49 | ptr->ptr = (uint8_t*)p; 50 | ptr->capacity = capacity; 51 | } 52 | 53 | memcpy(ptr->ptr + ptr->off, data, bytes); 54 | ptr->off += bytes; 55 | 56 | if (ptr->off > ptr->bytes) 57 | ptr->bytes = ptr->off; 58 | return 0; 59 | } 60 | 61 | static int mov_memory_seek(void* param, int64_t offset) 62 | { 63 | struct mov_memory_buffer_t* ptr; 64 | ptr = (struct mov_memory_buffer_t*)param; 65 | if ((uint64_t)(offset >= 0 ? offset : -offset) > ptr->capacity) 66 | return -E2BIG; 67 | ptr->off = offset >= 0 ? offset : (ptr->capacity+offset); 68 | return 0; 69 | } 70 | 71 | static int64_t mov_memory_tell(void* param) 72 | { 73 | struct mov_memory_buffer_t* ptr; 74 | ptr = (struct mov_memory_buffer_t*)param; 75 | return (int64_t)ptr->off; 76 | } 77 | 78 | static inline const struct mov_buffer_t* mov_memory_buffer(void) 79 | { 80 | static struct mov_buffer_t s_io = { 81 | mov_memory_read, 82 | mov_memory_write, 83 | mov_memory_seek, 84 | mov_memory_tell, 85 | }; 86 | return &s_io; 87 | } 88 | 89 | #endif /* !_mov_memory_buffer_h_ */ 90 | -------------------------------------------------------------------------------- /libmov/include/mov-reader.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_reader_h_ 2 | #define _mov_reader_h_ 3 | 4 | #include 5 | #include 6 | #include "mov-buffer.h" 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | typedef struct mov_reader_t mov_reader_t; 13 | 14 | mov_reader_t* mov_reader_create(const struct mov_buffer_t* buffer, void* param); 15 | void mov_reader_destroy(mov_reader_t* mov); 16 | 17 | struct mov_reader_trackinfo_t 18 | { 19 | /// @param[in] object: MOV_OBJECT_H264/MOV_OBJECT_AAC, see more @mov-format.h 20 | void (*onvideo)(void* param, uint32_t track, uint8_t object, int width, int height, const void* extra, size_t bytes); 21 | void (*onaudio)(void* param, uint32_t track, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void* extra, size_t bytes); 22 | void (*onsubtitle)(void* param, uint32_t track, uint8_t object, const void* extra, size_t bytes); 23 | }; 24 | 25 | /// @return 0-OK, other-error 26 | int mov_reader_getinfo(mov_reader_t* mov, struct mov_reader_trackinfo_t *ontrack, void* param); 27 | 28 | uint64_t mov_reader_getduration(mov_reader_t* mov); 29 | 30 | /// audio: AAC raw data, don't include ADTS/AudioSpecificConfig 31 | /// video: 4-byte data length(don't include self length) + H.264 NALU(don't include 0x00000001) 32 | /// @param[in] flags MOV_AV_FLAG_xxx, such as: MOV_AV_FLAG_KEYFREAME 33 | typedef void (*mov_reader_onread)(void* param, uint32_t track, const void* buffer, size_t bytes, int64_t pts, int64_t dts, int flags); 34 | /// @return 1-read one frame, 0-EOF, <0-error 35 | int mov_reader_read(mov_reader_t* mov, void* buffer, size_t bytes, mov_reader_onread onread, void* param); 36 | 37 | /// audio: AAC raw data, don't include ADTS/AudioSpecificConfig 38 | /// video: 4-byte data length(don't include self length) + H.264 NALU(don't include 0x00000001) 39 | /// @param[in] flags MOV_AV_FLAG_xxx, such as: MOV_AV_FLAG_KEYFREAME 40 | /// @return NULL-error, other-user alloc buffer 41 | typedef void* (*mov_reader_onread2)(void* param, uint32_t track, size_t bytes, int64_t pts, int64_t dts, int flags); 42 | /// same as mov_reader_read + user alloc buffer 43 | /// NOTICE: user should free buffer on return error!!! 44 | /// @return 1-read one frame, 0-EOF, <0-error 45 | int mov_reader_read2(mov_reader_t* mov, mov_reader_onread2 onread, void* param); 46 | 47 | /// @param[in,out] timestamp input seek timestamp, output seek location timestamp 48 | /// @return 0-ok, other-error 49 | int mov_reader_seek(mov_reader_t* mov, int64_t* timestamp); 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | #endif /* !_mov_reader_h_*/ 55 | -------------------------------------------------------------------------------- /libmov/include/mov-udta.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_udta_h_ 2 | #define _mov_udta_h_ 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | 12 | // https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/Metadata/Metadata.html 13 | 14 | struct mov_udta_meta_t 15 | { 16 | //char* title; 17 | //char* artist; 18 | //char* album_artist; 19 | //char* album; 20 | //char* date; 21 | //char* comment; 22 | //char* genre; 23 | //char* copyright; 24 | //char* lyrics; 25 | //char* description; 26 | //char* synopsis; 27 | //char* show; 28 | //char* episode_id; 29 | //char* network; 30 | //char* keywords; 31 | //char* season_num; 32 | //char* media_type; 33 | //char* hd_video; 34 | //char* gapless_playback; 35 | //char* compilation; 36 | 37 | uint8_t* cover; // cover binary data, jpeg/png only 38 | int cover_size; // cover binnary data length in byte 39 | }; 40 | 41 | int mov_udta_meta_write(const struct mov_udta_meta_t* meta, void* data, int bytes); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | #endif /* !_mov_udta_h_ */ 47 | -------------------------------------------------------------------------------- /libmov/include/mov-writer.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_writer_h_ 2 | #define _mov_writer_h_ 3 | 4 | #include 5 | #include 6 | #include "mov-buffer.h" 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | typedef struct mov_writer_t mov_writer_t; 13 | 14 | /// @param[in] flags mov flags, such as: MOV_FLAG_FASTSTART, see more @mov-format.h 15 | mov_writer_t* mov_writer_create(const struct mov_buffer_t* buffer, void* param, int flags); 16 | void mov_writer_destroy(mov_writer_t* mov); 17 | 18 | /// @param[in] object MPEG-4 systems ObjectTypeIndication such as: MOV_OBJECT_H264, see more @mov-format.h 19 | /// @param[in] extra_data AudioSpecificConfig/AVCDecoderConfigurationRecord/HEVCDecoderConfigurationRecord 20 | /// @return >=0-track, <0-error 21 | int mov_writer_add_audio(mov_writer_t* mov, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size); 22 | int mov_writer_add_video(mov_writer_t* mov, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size); 23 | int mov_writer_add_subtitle(mov_writer_t* mov, uint8_t object, const void* extra_data, size_t extra_data_size); 24 | 25 | /// Write audio/video stream 26 | /// raw AAC data, don't include ADTS/AudioSpecificConfig 27 | /// H.264/H.265 MP4 format, replace start code(0x00000001) with NALU size 28 | /// @param[in] track return by mov_writer_add_audio/mov_writer_add_video 29 | /// @param[in] data audio/video frame 30 | /// @param[in] bytes buffer size 31 | /// @param[in] pts timestamp in millisecond 32 | /// @param[in] dts timestamp in millisecond 33 | /// @param[in] flags MOV_AV_FLAG_XXX, such as: MOV_AV_FLAG_KEYFREAME, see more @mov-format.h 34 | /// @return 0-ok, other-error 35 | int mov_writer_write(mov_writer_t* mov, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags); 36 | 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | #endif /* !_mov_writer_h_ */ 41 | -------------------------------------------------------------------------------- /libmov/include/mp4-writer.h: -------------------------------------------------------------------------------- 1 | #ifndef _mp4_writer_h_ 2 | #define _mp4_writer_h_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "mov-buffer.h" 9 | #include "mov-format.h" 10 | #include "mov-writer.h" 11 | #include "fmp4-writer.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | struct mp4_writer_t 18 | { 19 | mov_writer_t *mov; 20 | fmp4_writer_t* fmp4; 21 | }; 22 | 23 | /// @param[in] flags mov flags, such as: MOV_FLAG_SEGMENT, see more @mov-format.h 24 | static inline struct mp4_writer_t* mp4_writer_create(int is_fmp4, const struct mov_buffer_t* buffer, void* param, int flags) 25 | { 26 | struct mp4_writer_t* mp4; 27 | mp4 = (struct mp4_writer_t*)calloc(1, sizeof(struct mp4_writer_t)); 28 | if (!mp4) return NULL; 29 | 30 | if (!is_fmp4) { 31 | mp4->mov = mov_writer_create(buffer, param, flags); 32 | } else { 33 | mp4->fmp4 = fmp4_writer_create(buffer, param, flags); 34 | } 35 | return mp4; 36 | } 37 | 38 | static inline void mp4_writer_destroy(struct mp4_writer_t* mp4) 39 | { 40 | assert((mp4->fmp4 && !mp4->mov) || (!mp4->fmp4 && mp4->mov)); 41 | if (mp4->mov) { 42 | mov_writer_destroy(mp4->mov); 43 | } else { 44 | fmp4_writer_destroy(mp4->fmp4); 45 | } 46 | free(mp4); 47 | } 48 | 49 | /// @param[in] object MPEG-4 systems ObjectTypeIndication such as: MOV_OBJECT_AAC, see more @mov-format.h 50 | /// @param[in] extra_data AudioSpecificConfig 51 | /// @return >=0-track, <0-error 52 | static inline int mp4_writer_add_audio(struct mp4_writer_t* mp4, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size) 53 | { 54 | assert((mp4->fmp4 && !mp4->mov) || (!mp4->fmp4 && mp4->mov)); 55 | if (mp4->mov) { 56 | return mov_writer_add_audio(mp4->mov, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size); 57 | } else { 58 | return fmp4_writer_add_audio(mp4->fmp4, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size); 59 | } 60 | } 61 | 62 | /// @param[in] object MPEG-4 systems ObjectTypeIndication such as: MOV_OBJECT_H264, see more @mov-format.h 63 | /// @param[in] extra_data AVCDecoderConfigurationRecord/HEVCDecoderConfigurationRecord 64 | /// @return >=0-track, <0-error 65 | static inline int mp4_writer_add_video(struct mp4_writer_t* mp4, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size) 66 | { 67 | assert((mp4->fmp4 && !mp4->mov) || (!mp4->fmp4 && mp4->mov)); 68 | if (mp4->mov) { 69 | return mov_writer_add_video(mp4->mov, object, width, height, extra_data, extra_data_size); 70 | } else { 71 | return fmp4_writer_add_video(mp4->fmp4, object, width, height, extra_data, extra_data_size); 72 | } 73 | } 74 | 75 | static inline int mp4_writer_add_subtitle(struct mp4_writer_t* mp4, uint8_t object, const void* extra_data, size_t extra_data_size) 76 | { 77 | assert((mp4->fmp4 && !mp4->mov) || (!mp4->fmp4 && mp4->mov)); 78 | if (mp4->mov) { 79 | return mov_writer_add_subtitle(mp4->mov, object, extra_data, extra_data_size); 80 | } else { 81 | return fmp4_writer_add_subtitle(mp4->fmp4, object, extra_data, extra_data_size); 82 | } 83 | } 84 | 85 | /// Write audio/video stream 86 | /// raw AAC data, don't include ADTS/AudioSpecificConfig 87 | /// H.264/H.265 MP4 format, replace start code(0x00000001) with NALU size 88 | /// @param[in] track return by mov_writer_add_audio/mov_writer_add_video 89 | /// @param[in] data audio/video frame 90 | /// @param[in] bytes buffer size 91 | /// @param[in] pts timestamp in millisecond 92 | /// @param[in] dts timestamp in millisecond 93 | /// @param[in] flags MOV_AV_FLAG_XXX, such as: MOV_AV_FLAG_KEYFREAME, see more @mov-format.h 94 | /// @return 0-ok, other-error 95 | static inline int mp4_writer_write(struct mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags) 96 | { 97 | assert((mp4->fmp4 && !mp4->mov) || (!mp4->fmp4 && mp4->mov)); 98 | if (mp4->mov) { 99 | return mov_writer_write(mp4->mov, track, data, bytes, pts, dts, flags); 100 | } else { 101 | return fmp4_writer_write(mp4->fmp4, track, data, bytes, pts, dts, flags); 102 | } 103 | } 104 | 105 | ///////////////////// The following interfaces are only applicable to fmp4 /////////////////////////////// 106 | 107 | /// Save data and open next segment 108 | /// @return 0-ok, other-error 109 | static inline int mp4_writer_save_segment(struct mp4_writer_t* mp4) 110 | { 111 | assert((mp4->fmp4 && !mp4->mov) || (!mp4->fmp4 && mp4->mov)); 112 | if (mp4->fmp4) 113 | return fmp4_writer_save_segment(mp4->fmp4); 114 | return 0; 115 | } 116 | 117 | /// Get init segment data(write FTYP, MOOV only) 118 | /// WARNING: it caller duty to switch file/buffer context with fmp4_writer_write 119 | /// @return 0-ok, other-error 120 | static inline int mp4_writer_init_segment(struct mp4_writer_t* mp4) 121 | { 122 | assert((mp4->fmp4 && !mp4->mov) || (!mp4->fmp4 && mp4->mov)); 123 | if (mp4->fmp4) 124 | return fmp4_writer_init_segment(mp4->fmp4); 125 | return 0; 126 | } 127 | 128 | #ifdef __cplusplus 129 | } 130 | #endif 131 | #endif /* !_mp4_writer_h_ */ 132 | -------------------------------------------------------------------------------- /libmov/libmov.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {D5106098-7B37-4DE0-B8FB-233BBABC2D72} 75 | Win32Proj 76 | libmov 77 | 10.0 78 | 79 | 80 | 81 | StaticLibrary 82 | true 83 | v143 84 | Unicode 85 | 86 | 87 | StaticLibrary 88 | false 89 | v143 90 | true 91 | Unicode 92 | 93 | 94 | StaticLibrary 95 | true 96 | v143 97 | Unicode 98 | 99 | 100 | StaticLibrary 101 | false 102 | v143 103 | true 104 | Unicode 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | Level4 130 | Disabled 131 | WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 132 | .;include;%(AdditionalIncludeDirectories) 133 | 134 | 135 | Windows 136 | 137 | 138 | 139 | 140 | 141 | 142 | Level4 143 | Disabled 144 | _DEBUG;_LIB;%(PreprocessorDefinitions) 145 | .;include;%(AdditionalIncludeDirectories) 146 | 147 | 148 | Windows 149 | 150 | 151 | 152 | 153 | Level4 154 | 155 | 156 | MaxSpeed 157 | true 158 | true 159 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 160 | .;include;%(AdditionalIncludeDirectories) 161 | 162 | 163 | Windows 164 | true 165 | true 166 | 167 | 168 | 169 | 170 | Level4 171 | 172 | 173 | MaxSpeed 174 | true 175 | true 176 | NDEBUG;_LIB;%(PreprocessorDefinitions) 177 | .;include;%(AdditionalIncludeDirectories) 178 | 179 | 180 | Windows 181 | true 182 | true 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /libmov/libmov.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | Header Files 50 | 51 | 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | Source Files 76 | 77 | 78 | Source Files 79 | 80 | 81 | Source Files 82 | 83 | 84 | Source Files 85 | 86 | 87 | Source Files 88 | 89 | 90 | Source Files 91 | 92 | 93 | Source Files 94 | 95 | 96 | Source Files 97 | 98 | 99 | Source Files 100 | 101 | 102 | Source Files 103 | 104 | 105 | Source Files 106 | 107 | 108 | Source Files 109 | 110 | 111 | Source Files 112 | 113 | 114 | Source Files 115 | 116 | 117 | Source Files 118 | 119 | 120 | Source Files 121 | 122 | 123 | Source Files 124 | 125 | 126 | Source Files 127 | 128 | 129 | Source Files 130 | 131 | 132 | Source Files 133 | 134 | 135 | Source Files 136 | 137 | 138 | Source Files 139 | 140 | 141 | Source Files 142 | 143 | 144 | Source Files 145 | 146 | 147 | Source Files 148 | 149 | 150 | Source Files 151 | 152 | 153 | Source Files 154 | 155 | 156 | Source Files 157 | 158 | 159 | Source Files 160 | 161 | 162 | Source Files 163 | 164 | 165 | -------------------------------------------------------------------------------- /libmov/source/fmp4-reader.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | 5 | #define DIFF(a, b) ((a) > (b) ? ((a) - (b)) : ((b) - (a))) 6 | 7 | static int mov_fragment_seek_get_duration(struct mov_t* mov) 8 | { 9 | int i; 10 | struct mov_track_t* track; 11 | track = mov->track_count > 0 ? &mov->tracks[0] : NULL; 12 | if (track && track->frag_capacity < track->frag_count && track->mdhd.timescale) 13 | { 14 | mov_buffer_seek(&mov->io, track->frags[track->frag_count - 1].offset); 15 | mov_reader_root(mov); // moof 16 | 17 | track->mdhd.duration = track->samples[track->sample_count - 1].dts - track->samples[0].dts; 18 | mov->mvhd.duration = track->mdhd.duration * mov->mvhd.timescale / track->mdhd.timescale; 19 | 20 | // clear samples and seek to the first moof 21 | for (i = 0; i < mov->track_count; i++) 22 | { 23 | mov->tracks[i].sample_count = 0; 24 | mov->tracks[i].sample_offset = 0; 25 | } 26 | track->frag_capacity = 0; 27 | } 28 | 29 | return 0; 30 | } 31 | 32 | int mov_fragment_seek_read_mfra(struct mov_t* mov) 33 | { 34 | uint64_t pos; 35 | pos = mov_buffer_tell(&mov->io); // for fallback 36 | mov_buffer_seek(&mov->io, -16); 37 | mov_reader_root(mov); // mfro 38 | if (mov->mfro > 0) 39 | { 40 | mov_buffer_seek(&mov->io, -((int64_t)mov->mfro)); 41 | mov_reader_root(mov); // mfra 42 | mov_fragment_seek_get_duration(mov); // for get fmp4 duration 43 | } 44 | mov_buffer_seek(&mov->io, pos); 45 | return mov_buffer_error(&mov->io); 46 | } 47 | 48 | int mov_fragment_seek(struct mov_t* mov, int64_t* timestamp) 49 | { 50 | int i; 51 | uint64_t clock; 52 | size_t idx, start, end; 53 | struct mov_track_t* track; 54 | struct mov_fragment_t* frag, *prev, *next; 55 | 56 | track = mov->track_count > 0 ? &mov->tracks[0] : NULL; 57 | if (!track || track->frag_count < 1) 58 | return -1; 59 | 60 | idx = start = 0; 61 | end = track->frag_count; 62 | assert(track->frag_count > 0); 63 | clock = (uint64_t)(*timestamp) * track->mdhd.timescale / 1000; // mvhd timescale 64 | 65 | while (start < end) 66 | { 67 | idx = (start + end) / 2; 68 | frag = &track->frags[idx]; 69 | 70 | if (frag->time > clock) 71 | end = idx; 72 | else if (frag->time < clock) 73 | start = idx + 1; 74 | else 75 | break; 76 | } 77 | 78 | frag = &track->frags[idx]; 79 | prev = &track->frags[idx > 0 ? idx - 1 : idx]; 80 | next = &track->frags[idx + 1 < track->frag_count ? idx + 1 : idx]; 81 | if (DIFF(prev->time, clock) < DIFF(frag->time, clock)) 82 | frag = prev; 83 | if (DIFF(next->time, clock) < DIFF(frag->time, clock)) 84 | frag = next; 85 | 86 | *timestamp = frag->time * 1000 / track->mdhd.timescale; 87 | 88 | // clear samples and seek 89 | for (i = 0; i < mov->track_count; i++) 90 | { 91 | mov->tracks[i].sample_count = 0; 92 | mov->tracks[i].sample_offset = 0; 93 | } 94 | track->frag_capacity = (uint32_t)idx; 95 | return 0; 96 | } 97 | 98 | int mov_fragment_read_next_moof(struct mov_t* mov) 99 | { 100 | int i; 101 | struct mov_track_t* track; 102 | 103 | // clear moof samples 104 | for (i = 0; i < mov->track_count; i++) 105 | { 106 | mov->tracks[i].sample_count = 0; 107 | mov->tracks[i].sample_offset = 0; 108 | } 109 | 110 | track = mov->track_count > 0 ? &mov->tracks[0] : NULL; 111 | if (track && track->frag_capacity < track->frag_count) 112 | { 113 | mov_buffer_seek(&mov->io, track->frags[track->frag_capacity++].offset); 114 | mov_reader_root(mov); // moof 115 | return 0; 116 | } 117 | 118 | return 1; // eof 119 | } 120 | -------------------------------------------------------------------------------- /libmov/source/mov-avc1.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int mov_read_extra(struct mov_t* mov, const struct mov_box_t* box) 7 | { 8 | struct mov_track_t* track = mov->track; 9 | struct mov_sample_entry_t* entry = track->stsd.current; 10 | if (entry->extra_data_size < box->size) 11 | { 12 | void* p = realloc(entry->extra_data, (size_t)box->size); 13 | if (NULL == p) return -ENOMEM; 14 | entry->extra_data = p; 15 | } 16 | 17 | mov_buffer_read(&mov->io, entry->extra_data, box->size); 18 | entry->extra_data_size = (int)box->size; 19 | return mov_buffer_error(&mov->io); 20 | } 21 | 22 | // extra_data: ISO/IEC 14496-15 AVCDecoderConfigurationRecord 23 | size_t mov_write_avcc(const struct mov_t* mov) 24 | { 25 | const struct mov_track_t* track = mov->track; 26 | const struct mov_sample_entry_t* entry = track->stsd.current; 27 | mov_buffer_w32(&mov->io, entry->extra_data_size + 8); /* size */ 28 | mov_buffer_write(&mov->io, "avcC", 4); 29 | if (entry->extra_data_size > 0) 30 | mov_buffer_write(&mov->io, entry->extra_data, entry->extra_data_size); 31 | return entry->extra_data_size + 8; 32 | } 33 | 34 | // extra_data: ISO/IEC 14496-15:2017 HEVCDecoderConfigurationRecord 35 | size_t mov_write_hvcc(const struct mov_t* mov) 36 | { 37 | const struct mov_track_t* track = mov->track; 38 | const struct mov_sample_entry_t* entry = track->stsd.current; 39 | mov_buffer_w32(&mov->io, entry->extra_data_size + 8); /* size */ 40 | mov_buffer_write(&mov->io, "hvcC", 4); 41 | if (entry->extra_data_size > 0) 42 | mov_buffer_write(&mov->io, entry->extra_data, entry->extra_data_size); 43 | return entry->extra_data_size + 8; 44 | } 45 | 46 | // https://aomediacodec.github.io/av1-isobmff 47 | // extra data: AV1CodecConfigurationRecord 48 | 49 | size_t mov_write_av1c(const struct mov_t* mov) 50 | { 51 | const struct mov_track_t* track = mov->track; 52 | const struct mov_sample_entry_t* entry = track->stsd.current; 53 | mov_buffer_w32(&mov->io, entry->extra_data_size + 8); /* size */ 54 | mov_buffer_write(&mov->io, "av1C", 4); 55 | if (entry->extra_data_size > 0) 56 | mov_buffer_write(&mov->io, entry->extra_data, entry->extra_data_size); 57 | return entry->extra_data_size + 8; 58 | } 59 | 60 | 61 | // extra_data: ISO/IEC 14496-15 AVCDecoderConfigurationRecord 62 | /* 63 | class VvcConfigurationBox extends FullBox('vvcC',version=0,flags) { 64 | VvcDecoderConfigurationRecord() VvcConfig; 65 | } 66 | */ 67 | size_t mov_write_vvcc(const struct mov_t* mov) 68 | { 69 | const struct mov_track_t* track = mov->track; 70 | const struct mov_sample_entry_t* entry = track->stsd.current; 71 | mov_buffer_w32(&mov->io, entry->extra_data_size + 8); /* size */ 72 | mov_buffer_write(&mov->io, "vvcC", 4); 73 | if (entry->extra_data_size > 0) 74 | mov_buffer_write(&mov->io, entry->extra_data, entry->extra_data_size); 75 | return entry->extra_data_size + 8; 76 | } 77 | -------------------------------------------------------------------------------- /libmov/source/mov-dinf.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | 3 | size_t mov_write_dref(const struct mov_t* mov) 4 | { 5 | mov_buffer_w32(&mov->io, 28); /* size */ 6 | mov_buffer_write(&mov->io, "dref", 4); 7 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 8 | mov_buffer_w32(&mov->io, 1); /* entry count */ 9 | 10 | mov_buffer_w32(&mov->io, 12); /* size */ 11 | //FIXME add the alis and rsrc atom 12 | mov_buffer_write(&mov->io, "url ", 4); 13 | mov_buffer_w32(&mov->io, 1); /* version & flags */ 14 | 15 | return 28; 16 | } 17 | 18 | size_t mov_write_dinf(const struct mov_t* mov) 19 | { 20 | size_t size; 21 | uint64_t offset; 22 | 23 | size = 8 /* Box */; 24 | offset = mov_buffer_tell(&mov->io); 25 | mov_buffer_w32(&mov->io, 0); /* size */ 26 | mov_buffer_write(&mov->io, "dinf", 4); 27 | 28 | size += mov_write_dref(mov); 29 | 30 | mov_write_size(mov, offset, size); /* update size */ 31 | return size; 32 | } 33 | -------------------------------------------------------------------------------- /libmov/source/mov-elst.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // 8.6.6 Edit List Box (p53) 7 | int mov_read_elst(struct mov_t* mov, const struct mov_box_t* box) 8 | { 9 | uint32_t i, entry_count; 10 | uint32_t version; 11 | struct mov_track_t* track = mov->track; 12 | 13 | version = mov_buffer_r8(&mov->io); /* version */ 14 | mov_buffer_r24(&mov->io); /* flags */ 15 | entry_count = mov_buffer_r32(&mov->io); 16 | 17 | assert(0 == track->elst_count && NULL == track->elst); 18 | if (track->elst_count < entry_count) 19 | { 20 | void* p = realloc(track->elst, sizeof(struct mov_elst_t) * entry_count); 21 | if (NULL == p) return -ENOMEM; 22 | track->elst = (struct mov_elst_t*)p; 23 | } 24 | track->elst_count = entry_count; 25 | 26 | for (i = 0; i < entry_count; i++) 27 | { 28 | if (1 == version) 29 | { 30 | track->elst[i].segment_duration = mov_buffer_r64(&mov->io); 31 | track->elst[i].media_time = (int64_t)mov_buffer_r64(&mov->io); 32 | } 33 | else 34 | { 35 | assert(0 == version); 36 | track->elst[i].segment_duration = mov_buffer_r32(&mov->io); 37 | track->elst[i].media_time = (int32_t)mov_buffer_r32(&mov->io); 38 | } 39 | track->elst[i].media_rate_integer = (int16_t)mov_buffer_r16(&mov->io); 40 | track->elst[i].media_rate_fraction = (int16_t)mov_buffer_r16(&mov->io); 41 | } 42 | 43 | (void)box; 44 | return mov_buffer_error(&mov->io); 45 | } 46 | 47 | size_t mov_write_elst(const struct mov_t* mov) 48 | { 49 | uint32_t size; 50 | int64_t time; 51 | int64_t delay; 52 | uint8_t version; 53 | const struct mov_track_t* track = mov->track; 54 | 55 | assert(track->start_dts == track->samples[0].dts); 56 | version = track->tkhd.duration > UINT32_MAX ? 1 : 0; 57 | 58 | // in media time scale units, in composition time 59 | time = track->samples[0].pts - track->samples[0].dts; 60 | // in units of the timescale in the Movie Header Box 61 | delay = track->samples[0].pts * mov->mvhd.timescale / track->mdhd.timescale; 62 | if (delay > UINT32_MAX) 63 | version = 1; 64 | 65 | time = time < 0 ? 0 : time; 66 | size = 12/* full box */ + 4/* entry count */ + (delay > 0 ? 2 : 1) * (version ? 20 : 12); 67 | 68 | mov_buffer_w32(&mov->io, size); /* size */ 69 | mov_buffer_write(&mov->io, "elst", 4); 70 | mov_buffer_w8(&mov->io, version); /* version */ 71 | mov_buffer_w24(&mov->io, 0); /* flags */ 72 | mov_buffer_w32(&mov->io, delay > 0 ? 2 : 1); /* entry count */ 73 | 74 | if (delay > 0) 75 | { 76 | if (1 == version) 77 | { 78 | mov_buffer_w64(&mov->io, (uint64_t)delay); /* segment_duration */ 79 | mov_buffer_w64(&mov->io, (uint64_t)-1); /* media_time */ 80 | } 81 | else 82 | { 83 | mov_buffer_w32(&mov->io, (uint32_t)delay); 84 | mov_buffer_w32(&mov->io, (uint32_t)-1); 85 | } 86 | 87 | mov_buffer_w16(&mov->io, 1); /* media_rate_integer */ 88 | mov_buffer_w16(&mov->io, 0); /* media_rate_fraction */ 89 | } 90 | 91 | /* duration */ 92 | if (version == 1) 93 | { 94 | mov_buffer_w64(&mov->io, track->tkhd.duration); 95 | mov_buffer_w64(&mov->io, time); 96 | } 97 | else 98 | { 99 | mov_buffer_w32(&mov->io, (uint32_t)track->tkhd.duration); 100 | mov_buffer_w32(&mov->io, (uint32_t)time); 101 | } 102 | mov_buffer_w16(&mov->io, 1); /* media_rate_integer */ 103 | mov_buffer_w16(&mov->io, 0); /* media_rate_fraction */ 104 | 105 | return size; 106 | } 107 | 108 | void mov_apply_elst(struct mov_track_t *track) 109 | { 110 | size_t i; 111 | 112 | // edit list 113 | track->samples[0].dts = 0; 114 | track->samples[0].pts = 0; 115 | for (i = 0; i < track->elst_count; i++) 116 | { 117 | if (-1 == track->elst[i].media_time) 118 | { 119 | track->samples[0].dts = track->elst[i].segment_duration; 120 | track->samples[0].pts = track->samples[0].dts; 121 | } 122 | } 123 | } 124 | 125 | void mov_apply_elst_tfdt(struct mov_track_t *track) 126 | { 127 | size_t i; 128 | 129 | for (i = 0; i < track->elst_count; i++) 130 | { 131 | if (-1 == track->elst[i].media_time) 132 | { 133 | track->tfdt_dts += track->elst[i].segment_duration; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /libmov/source/mov-esds.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BreakingY/Nvidia-Video-Codec/cbbc31cd93b8f66ba3a6e846a7a314eae62ca429/libmov/source/mov-esds.c -------------------------------------------------------------------------------- /libmov/source/mov-ftyp.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | 4 | // 4.3 File Type Box (p17) 5 | int mov_read_ftyp(struct mov_t* mov, const struct mov_box_t* box) 6 | { 7 | if(box->size < 8) return -1; 8 | 9 | mov->ftyp.major_brand = mov_buffer_r32(&mov->io); 10 | mov->ftyp.minor_version = mov_buffer_r32(&mov->io); 11 | 12 | for(mov->ftyp.brands_count = 0; mov->ftyp.brands_count < N_BRAND && (uint64_t)mov->ftyp.brands_count * 4 < box->size - 8; ++mov->ftyp.brands_count) 13 | { 14 | mov->ftyp.compatible_brands[mov->ftyp.brands_count] = mov_buffer_r32(&mov->io); 15 | } 16 | 17 | assert(box->size == 4 * (uint64_t)mov->ftyp.brands_count + 8); 18 | mov_buffer_skip(&mov->io, box->size - 4 * (uint64_t)mov->ftyp.brands_count - 8 ); // skip compatible_brands 19 | return 0; 20 | } 21 | 22 | size_t mov_write_ftyp(const struct mov_t* mov) 23 | { 24 | int size, i; 25 | 26 | size = 8/* box */ + 8/* item */ + mov->ftyp.brands_count * 4 /* compatible brands */; 27 | 28 | mov_buffer_w32(&mov->io, size); /* size */ 29 | mov_buffer_write(&mov->io, "ftyp", 4); 30 | mov_buffer_w32(&mov->io, mov->ftyp.major_brand); 31 | mov_buffer_w32(&mov->io, mov->ftyp.minor_version); 32 | 33 | for (i = 0; i < mov->ftyp.brands_count; i++) 34 | mov_buffer_w32(&mov->io, mov->ftyp.compatible_brands[i]); 35 | 36 | return size; 37 | } 38 | 39 | size_t mov_write_styp(const struct mov_t* mov) 40 | { 41 | int size, i; 42 | 43 | size = 8/* box */ + 8/* item */ + mov->ftyp.brands_count * 4 /* compatible brands */; 44 | 45 | mov_buffer_w32(&mov->io, size); /* size */ 46 | mov_buffer_write(&mov->io, "styp", 4); 47 | mov_buffer_w32(&mov->io, mov->ftyp.major_brand); 48 | mov_buffer_w32(&mov->io, mov->ftyp.minor_version); 49 | 50 | for (i = 0; i < mov->ftyp.brands_count; i++) 51 | mov_buffer_w32(&mov->io, mov->ftyp.compatible_brands[i]); 52 | 53 | return size; 54 | } 55 | -------------------------------------------------------------------------------- /libmov/source/mov-hdlr.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | 5 | // 8.4.3 Handler Reference Box (p36) 6 | // Box Type: 'hdlr' 7 | // Container: Media Box ('mdia') or Meta Box ('meta') 8 | // Mandatory: Yes 9 | // Quantity: Exactly one 10 | int mov_read_hdlr(struct mov_t* mov, const struct mov_box_t* box) 11 | { 12 | struct mov_track_t* track = mov->track; 13 | 14 | mov_buffer_r8(&mov->io); /* version */ 15 | mov_buffer_r24(&mov->io); /* flags */ 16 | //uint32_t pre_defined = mov_buffer_r32(&mov->io); 17 | mov_buffer_skip(&mov->io, 4); 18 | track->handler_type = mov_buffer_r32(&mov->io); 19 | // const unsigned int(32)[3] reserved = 0; 20 | mov_buffer_skip(&mov->io, 12); 21 | // string name; 22 | mov_buffer_skip(&mov->io, box->size - 24); // String name 23 | return 0; 24 | } 25 | 26 | size_t mov_write_hdlr(const struct mov_t* mov) 27 | { 28 | const struct mov_track_t* track = mov->track; 29 | 30 | mov_buffer_w32(&mov->io, 33 + (uint32_t)strlen(track->handler_descr)); /* size */ 31 | mov_buffer_write(&mov->io, "hdlr", 4); 32 | mov_buffer_w32(&mov->io, 0); /* Version & flags */ 33 | 34 | mov_buffer_w32(&mov->io, 0); /* pre_defined */ 35 | mov_buffer_w32(&mov->io, track->handler_type); /* handler_type */ 36 | 37 | mov_buffer_w32(&mov->io, 0); /* reserved */ 38 | mov_buffer_w32(&mov->io, 0); /* reserved */ 39 | mov_buffer_w32(&mov->io, 0); /* reserved */ 40 | 41 | mov_buffer_write(&mov->io, track->handler_descr, (uint64_t)strlen(track->handler_descr)+1); /* name */ 42 | return 33 + strlen(track->handler_descr); 43 | } 44 | -------------------------------------------------------------------------------- /libmov/source/mov-hdr.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // https://www.webmproject.org/vp9/mp4/ 7 | 8 | int mov_read_smdm(struct mov_t* mov, const struct mov_box_t* box) 9 | { 10 | (void)box; 11 | mov_buffer_r8(&mov->io); // version 12 | mov_buffer_r24(&mov->io); // flags 13 | 14 | mov_buffer_r16(&mov->io); // primaryRChromaticity_x, 0.16 fixed-point Red X chromaticity coordinate as defined by CIE 1931 15 | mov_buffer_r16(&mov->io); // primaryRChromaticity_y 16 | mov_buffer_r16(&mov->io); // primaryGChromaticity_x 17 | mov_buffer_r16(&mov->io); // primaryGChromaticity_y 18 | mov_buffer_r16(&mov->io); // primaryBChromaticity_x 19 | mov_buffer_r16(&mov->io); // primaryBChromaticity_y 20 | mov_buffer_r16(&mov->io); // whitePointChromaticity_x 21 | mov_buffer_r16(&mov->io); // whitePointChromaticity_y 22 | mov_buffer_r32(&mov->io); // luminanceMax, 24.8 fixed point Maximum luminance, represented in candelas per square meter (cd/m²) 23 | mov_buffer_r32(&mov->io); // luminanceMin 24 | 25 | return mov_buffer_error(&mov->io); 26 | } 27 | 28 | int mov_read_coll(struct mov_t* mov, const struct mov_box_t* box) 29 | { 30 | (void)box; 31 | mov_buffer_r8(&mov->io); // version 32 | mov_buffer_r24(&mov->io); // flags 33 | 34 | mov_buffer_r16(&mov->io); // maxCLL, Maximum Content Light Level as specified in CEA-861.3, Appendix A. 35 | mov_buffer_r16(&mov->io); // maxFALL, Maximum Frame-Average Light Level as specified in CEA-861.3, Appendix A. 36 | return mov_buffer_error(&mov->io); 37 | } 38 | -------------------------------------------------------------------------------- /libmov/source/mov-iods.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // Table 1 - List of Class Tags for Descriptors (p31) 7 | /* 8 | 0x10 MP4_IOD_Tag 9 | 0x11 MP4_OD_Tag 10 | */ 11 | 12 | // 7.2.2.2 BaseDescriptor (p32) 13 | /* 14 | abstract aligned(8) expandable(2^28-1) class BaseDescriptor : bit(8) tag=0 { 15 | // empty. To be filled by classes extending this class. 16 | } 17 | */ 18 | 19 | // 7.2.6.2 ObjectDescriptorBase (p42) 20 | /* 21 | abstract class ObjectDescriptorBase extends BaseDescriptor : bit(8) 22 | tag=[ObjectDescrTag..InitialObjectDescrTag] { 23 | // empty. To be filled by classes extending this class. 24 | } 25 | class ObjectDescriptor extends ObjectDescriptorBase : bit(8) tag=ObjectDescrTag { 26 | bit(10) ObjectDescriptorID; 27 | bit(1) URL_Flag; 28 | const bit(5) reserved=0b1111.1; 29 | if (URL_Flag) { 30 | bit(8) URLlength; 31 | bit(8) URLstring[URLlength]; 32 | } else { 33 | ES_Descriptor esDescr[1 .. 255]; 34 | OCI_Descriptor ociDescr[0 .. 255]; 35 | IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255]; 36 | IPMP_Descriptor ipmpDescr [0 .. 255]; 37 | } 38 | ExtensionDescriptor extDescr[0 .. 255]; 39 | } 40 | */ 41 | 42 | // 7.2.6.4 InitialObjectDescriptor (p44) 43 | /* 44 | class InitialObjectDescriptor extends ObjectDescriptorBase : bit(8) 45 | tag=InitialObjectDescrTag { 46 | bit(10) ObjectDescriptorID; 47 | bit(1) URL_Flag; 48 | bit(1) includeInlineProfileLevelFlag; 49 | const bit(4) reserved=0b1111; 50 | if (URL_Flag) { 51 | bit(8) URLlength; 52 | bit(8) URLstring[URLlength]; 53 | } else { 54 | bit(8) ODProfileLevelIndication; 55 | bit(8) sceneProfileLevelIndication; 56 | bit(8) audioProfileLevelIndication; 57 | bit(8) visualProfileLevelIndication; 58 | bit(8) graphicsProfileLevelIndication; 59 | ES_Descriptor esDescr[1 .. 255]; 60 | OCI_Descriptor ociDescr[0 .. 255]; 61 | IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255]; 62 | IPMP_Descriptor ipmpDescr [0 .. 255]; 63 | IPMP_ToolListDescriptor toolListDescr[0 .. 1]; 64 | } 65 | ExtensionDescriptor extDescr[0 .. 255]; 66 | } 67 | */ 68 | size_t mov_write_iods(const struct mov_t* mov) 69 | { 70 | size_t size = 12 /* full box */ + 12 /* InitialObjectDescriptor */; 71 | 72 | mov_buffer_w32(&mov->io, 24); /* size */ 73 | mov_buffer_write(&mov->io, "iods", 4); 74 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 75 | 76 | mov_buffer_w8(&mov->io, 0x10); // ISO_MP4_IOD_Tag 77 | mov_buffer_w8(&mov->io, (uint8_t)(0x80 | (7 >> 21))); 78 | mov_buffer_w8(&mov->io, (uint8_t)(0x80 | (7 >> 14))); 79 | mov_buffer_w8(&mov->io, (uint8_t)(0x80 | (7 >> 7))); 80 | mov_buffer_w8(&mov->io, (uint8_t)(0x7F & 7)); 81 | 82 | mov_buffer_w16(&mov->io, 0x004f); // objectDescriptorId 1 83 | mov_buffer_w8(&mov->io, 0xff); // No OD capability required 84 | mov_buffer_w8(&mov->io, 0xff); 85 | mov_buffer_w8(&mov->io, 0xFF); 86 | mov_buffer_w8(&mov->io, 0xFF); // no visual capability required 87 | mov_buffer_w8(&mov->io, 0xff); 88 | 89 | return size; 90 | } 91 | -------------------------------------------------------------------------------- /libmov/source/mov-ioutil.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_ioutil_h_ 2 | #define _mov_ioutil_h_ 3 | 4 | #include "mov-buffer.h" 5 | 6 | struct mov_ioutil_t 7 | { 8 | struct mov_buffer_t io; 9 | void* param; 10 | int error; 11 | }; 12 | 13 | static inline int mov_buffer_error(const struct mov_ioutil_t* io) 14 | { 15 | return io->error; 16 | } 17 | 18 | static inline uint64_t mov_buffer_tell(const struct mov_ioutil_t* io) 19 | { 20 | int64_t v; 21 | v = io->io.tell(io->param); 22 | if (v < 0) 23 | ((struct mov_ioutil_t*)io)->error = -1; 24 | return v; 25 | } 26 | 27 | static inline void mov_buffer_seek(const struct mov_ioutil_t* io, int64_t offset) 28 | { 29 | // if (0 == io->error) 30 | ((struct mov_ioutil_t*)io)->error = io->io.seek(io->param, offset); 31 | } 32 | 33 | static inline void mov_buffer_skip(struct mov_ioutil_t* io, uint64_t bytes) 34 | { 35 | uint64_t offset; 36 | if (0 == io->error) 37 | { 38 | offset = io->io.tell(io->param); 39 | io->error = io->io.seek(io->param, offset + bytes); 40 | } 41 | } 42 | 43 | static inline void mov_buffer_read(struct mov_ioutil_t* io, void* data, uint64_t bytes) 44 | { 45 | if (0 == io->error) 46 | io->error = io->io.read(io->param, data, bytes); 47 | } 48 | 49 | static inline void mov_buffer_write(const struct mov_ioutil_t* io, const void* data, uint64_t bytes) 50 | { 51 | if (0 == io->error) 52 | ((struct mov_ioutil_t*)io)->error = io->io.write(io->param, data, bytes); 53 | } 54 | 55 | static inline uint8_t mov_buffer_r8(struct mov_ioutil_t* io) 56 | { 57 | uint8_t v = 0; 58 | mov_buffer_read(io, &v, 1); 59 | return v; 60 | } 61 | 62 | static inline uint16_t mov_buffer_r16(struct mov_ioutil_t* io) 63 | { 64 | uint16_t v; 65 | v = mov_buffer_r8(io); 66 | v = (v << 8) | mov_buffer_r8(io); 67 | return v; 68 | } 69 | 70 | static inline uint32_t mov_buffer_r24(struct mov_ioutil_t* io) 71 | { 72 | uint32_t v; 73 | v = mov_buffer_r8(io); 74 | v = (v << 16) | mov_buffer_r16(io); 75 | return v; 76 | } 77 | 78 | static inline uint32_t mov_buffer_r32(struct mov_ioutil_t* io) 79 | { 80 | uint32_t v; 81 | v = mov_buffer_r16(io); 82 | v = (v << 16) | mov_buffer_r16(io); 83 | return v; 84 | } 85 | 86 | static inline uint64_t mov_buffer_r64(struct mov_ioutil_t* io) 87 | { 88 | uint64_t v; 89 | v = mov_buffer_r32(io); 90 | v = (v << 32) | mov_buffer_r32(io); 91 | return v; 92 | } 93 | 94 | static inline void mov_buffer_w8(const struct mov_ioutil_t* io, uint8_t v) 95 | { 96 | mov_buffer_write(io, &v, 1); 97 | } 98 | 99 | static inline void mov_buffer_w16(const struct mov_ioutil_t* io, uint16_t v) 100 | { 101 | mov_buffer_w8(io, (uint8_t)(v >> 8)); 102 | mov_buffer_w8(io, (uint8_t)v); 103 | } 104 | 105 | static inline void mov_buffer_w24(const struct mov_ioutil_t* io, uint32_t v) 106 | { 107 | mov_buffer_w16(io, (uint16_t)(v >> 8)); 108 | mov_buffer_w8(io, (uint8_t)v); 109 | } 110 | 111 | static inline void mov_buffer_w32(const struct mov_ioutil_t* io, uint32_t v) 112 | { 113 | mov_buffer_w16(io, (uint16_t)(v >> 16)); 114 | mov_buffer_w16(io, (uint16_t)v); 115 | } 116 | 117 | static inline void mov_buffer_w64(const struct mov_ioutil_t* io, uint64_t v) 118 | { 119 | mov_buffer_w32(io, (uint32_t)(v >> 32)); 120 | mov_buffer_w32(io, (uint32_t)v); 121 | } 122 | 123 | #endif /* !_mov_ioutil_h_ */ 124 | -------------------------------------------------------------------------------- /libmov/source/mov-leva.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | 4 | // 8.8.13 Level Assignment Box (p77) 5 | int mov_read_leva(struct mov_t* mov, const struct mov_box_t* box) 6 | { 7 | unsigned int i, level_count; 8 | unsigned int assignment_type; 9 | 10 | mov_buffer_r32(&mov->io); /* version & flags */ 11 | level_count = mov_buffer_r8(&mov->io); /* level_count */ 12 | for (i = 0; i < level_count; i++) 13 | { 14 | mov_buffer_r32(&mov->io); /* track_id */ 15 | assignment_type = mov_buffer_r8(&mov->io); /* padding_flag & assignment_type */ 16 | assignment_type &= 0x7F; // 7-bits 17 | 18 | if (0 == assignment_type) 19 | { 20 | mov_buffer_r32(&mov->io); /* grouping_type */ 21 | } 22 | else if (1 == assignment_type) 23 | { 24 | mov_buffer_r32(&mov->io); /* grouping_type */ 25 | mov_buffer_r32(&mov->io); /* grouping_type_parameter */ 26 | } 27 | else if (4 == assignment_type) 28 | { 29 | mov_buffer_r32(&mov->io); /* sub_track_id */ 30 | } 31 | } 32 | 33 | (void)box; 34 | return mov_buffer_error(&mov->io); 35 | } 36 | -------------------------------------------------------------------------------- /libmov/source/mov-mdhd.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | 4 | // 8.4.2 Media Header Box (p35) 5 | // Box Type: 'mdhd' 6 | // Container: Media Box ('mdia') 7 | // Mandatory: Yes 8 | // Quantity: Exactly one 9 | 10 | /* 11 | aligned(8) class MediaHeaderBox extends FullBox('mdhd', version, 0) { 12 | if (version==1) { 13 | unsigned int(64) creation_time; 14 | unsigned int(64) modification_time; 15 | unsigned int(32) timescale; 16 | unsigned int(64) duration; 17 | } else { // version==0 18 | unsigned int(32) creation_time; 19 | unsigned int(32) modification_time; 20 | unsigned int(32) timescale; 21 | unsigned int(32) duration; 22 | } 23 | bit(1) pad = 0; 24 | unsigned int(5)[3] language; // ISO-639-2/T language code 25 | unsigned int(16) pre_defined = 0; 26 | } 27 | */ 28 | int mov_read_mdhd(struct mov_t* mov, const struct mov_box_t* box) 29 | { 30 | uint32_t val; 31 | struct mov_mdhd_t* mdhd = &mov->track->mdhd; 32 | 33 | mdhd->version = mov_buffer_r8(&mov->io); 34 | mdhd->flags = mov_buffer_r24(&mov->io); 35 | 36 | if (1 == mdhd->version) 37 | { 38 | mdhd->creation_time = mov_buffer_r64(&mov->io); 39 | mdhd->modification_time = mov_buffer_r64(&mov->io); 40 | mdhd->timescale = mov_buffer_r32(&mov->io); 41 | mdhd->duration = mov_buffer_r64(&mov->io); 42 | } 43 | else 44 | { 45 | assert(0 == mdhd->version); 46 | mdhd->creation_time = mov_buffer_r32(&mov->io); 47 | mdhd->modification_time = mov_buffer_r32(&mov->io); 48 | mdhd->timescale = mov_buffer_r32(&mov->io); 49 | mdhd->duration = mov_buffer_r32(&mov->io); 50 | } 51 | 52 | val = mov_buffer_r32(&mov->io); 53 | mdhd->language = (val >> 16) & 0x7FFF; 54 | mdhd->pre_defined = val & 0xFFFF; 55 | 56 | (void)box; 57 | return mov_buffer_error(&mov->io); 58 | } 59 | 60 | size_t mov_write_mdhd(const struct mov_t* mov) 61 | { 62 | const struct mov_mdhd_t* mdhd = &mov->track->mdhd; 63 | 64 | mov_buffer_w32(&mov->io, 32); /* size */ 65 | mov_buffer_write(&mov->io, "mdhd", 4); 66 | mov_buffer_w32(&mov->io, 0); /* version 1 & flags */ 67 | 68 | mov_buffer_w32(&mov->io, (uint32_t)mdhd->creation_time); /* creation_time */ 69 | mov_buffer_w32(&mov->io, (uint32_t)mdhd->modification_time); /* modification_time */ 70 | mov_buffer_w32(&mov->io, mdhd->timescale); /* timescale */ 71 | mov_buffer_w32(&mov->io, (uint32_t)mdhd->duration); /* duration */ 72 | 73 | mov_buffer_w16(&mov->io, (uint16_t)mdhd->language); /* ISO-639-2/T language code */ 74 | mov_buffer_w16(&mov->io, 0); /* pre_defined (quality) */ 75 | return 32; 76 | } 77 | -------------------------------------------------------------------------------- /libmov/source/mov-mehd.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // 8.8.2 Movie Extends Header Box (p68) 8 | int mov_read_mehd(struct mov_t* mov, const struct mov_box_t* box) 9 | { 10 | unsigned int version; 11 | uint64_t fragment_duration; 12 | version = mov_buffer_r8(&mov->io); /* version */ 13 | mov_buffer_r24(&mov->io); /* flags */ 14 | 15 | if (1 == version) 16 | fragment_duration = mov_buffer_r64(&mov->io); /* fragment_duration*/ 17 | else 18 | fragment_duration = mov_buffer_r32(&mov->io); /* fragment_duration*/ 19 | 20 | (void)box; 21 | //assert(fragment_duration <= mov->mvhd.duration); 22 | return mov_buffer_error(&mov->io); 23 | } 24 | 25 | size_t mov_write_mehd(const struct mov_t* mov) 26 | { 27 | mov_buffer_w32(&mov->io, 20); /* size */ 28 | mov_buffer_write(&mov->io, "mehd", 4); 29 | mov_buffer_w8(&mov->io, 1); /* version */ 30 | mov_buffer_w24(&mov->io, 0); /* flags */ 31 | mov_buffer_w64(&mov->io, mov->mvhd.duration); // 0 ? 32 | return 20; 33 | } 34 | -------------------------------------------------------------------------------- /libmov/source/mov-mfhd.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // 8.8.5 Movie Fragment Header Box (p70) 7 | int mov_read_mfhd(struct mov_t* mov, const struct mov_box_t* box) 8 | { 9 | (void)box; 10 | mov_buffer_r32(&mov->io); /* version & flags */ 11 | mov_buffer_r32(&mov->io); /* sequence_number */ 12 | return mov_buffer_error(&mov->io); 13 | } 14 | 15 | size_t mov_write_mfhd(const struct mov_t* mov, uint32_t fragment) 16 | { 17 | mov_buffer_w32(&mov->io, 16); /* size */ 18 | mov_buffer_write(&mov->io, "mfhd", 4); 19 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 20 | mov_buffer_w32(&mov->io, fragment); /* sequence_number */ 21 | return 16; 22 | } 23 | -------------------------------------------------------------------------------- /libmov/source/mov-minf.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | 4 | int mov_read_vmhd(struct mov_t* mov, const struct mov_box_t* box) 5 | { 6 | mov_buffer_r8(&mov->io); /* version */ 7 | mov_buffer_r24(&mov->io); /* flags */ 8 | mov_buffer_r16(&mov->io); /* graphicsmode */ 9 | // template unsigned int(16)[3] opcolor = {0, 0, 0}; 10 | mov_buffer_skip(&mov->io, 6); 11 | 12 | (void)box; 13 | return 0; 14 | } 15 | 16 | int mov_read_smhd(struct mov_t* mov, const struct mov_box_t* box) 17 | { 18 | mov_buffer_r8(&mov->io); /* version */ 19 | mov_buffer_r24(&mov->io); /* flags */ 20 | mov_buffer_r16(&mov->io); /* balance */ 21 | //const unsigned int(16) reserved = 0; 22 | mov_buffer_skip(&mov->io, 2); 23 | 24 | (void)box; 25 | return 0; 26 | } 27 | 28 | int mov_read_nmhd(struct mov_t* mov, const struct mov_box_t* box) 29 | { 30 | mov_buffer_r8(&mov->io); /* version */ 31 | mov_buffer_r24(&mov->io); /* flags */ 32 | (void)box; 33 | return 0; 34 | } 35 | 36 | // https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25675 37 | /* 38 | Size: A 32-bit integer that specifies the number of bytes in this base media info atom. 39 | Type: A 32-bit integer that identifies the atom type; this field must be set to 'gmin'. 40 | Version: A 1-byte specification of the version of this base media information header atom. 41 | Flags: A 3-byte space for base media information flags. Set this field to 0. 42 | Graphics mode: A 16-bit integer that specifies the transfer mode. The transfer mode specifies which Boolean operation QuickDraw should perform when drawing or transferring an image from one location to another. See Graphics Modes for more information about graphics modes supported by QuickTime. 43 | Opcolor: Three 16-bit values that specify the red, green, and blue colors for the transfer mode operation indicated in the graphics mode field. 44 | Balance: A 16-bit integer that specifies the sound balance of this media. Sound balance is the setting that controls the mix of sound between the two speakers of a computer. This field is normally set to 0. See Balance for more information about balance values. 45 | Reserved: Reserved for use by Apple. A 16-bit integer. Set this field to 0 46 | */ 47 | int mov_read_gmin(struct mov_t* mov, const struct mov_box_t* box) 48 | { 49 | mov_buffer_r8(&mov->io); /* version */ 50 | mov_buffer_r24(&mov->io); /* flags */ 51 | mov_buffer_r16(&mov->io); /* graphics mode */ 52 | mov_buffer_r16(&mov->io); /* opcolor red*/ 53 | mov_buffer_r16(&mov->io); /* opcolor green*/ 54 | mov_buffer_r16(&mov->io); /* opcolor blue*/ 55 | mov_buffer_r16(&mov->io); /* balance */ 56 | mov_buffer_r16(&mov->io); /* reserved */ 57 | 58 | (void)box; 59 | return 0; 60 | } 61 | 62 | // https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW90 63 | /* 64 | Size:A 32-bit integer that specifies the number of bytes in this text media information atom. 65 | Type:A 32-bit integer that identifies the atom type; this field must be set to 'text'. 66 | Matrix structure:A matrix structure associated with this text media 67 | */ 68 | int mov_read_text(struct mov_t* mov, const struct mov_box_t* box) 69 | { 70 | int i; 71 | // Matrix structure 72 | for (i = 0; i < 9; i++) 73 | mov_buffer_r32(&mov->io); 74 | 75 | (void)box; 76 | return 0; 77 | } 78 | 79 | size_t mov_write_vmhd(const struct mov_t* mov) 80 | { 81 | mov_buffer_w32(&mov->io, 20); /* size (always 0x14) */ 82 | mov_buffer_write(&mov->io, "vmhd", 4); 83 | mov_buffer_w32(&mov->io, 0x01); /* version & flags */ 84 | mov_buffer_w64(&mov->io, 0); /* reserved (graphics mode = copy) */ 85 | return 20; 86 | } 87 | 88 | size_t mov_write_smhd(const struct mov_t* mov) 89 | { 90 | mov_buffer_w32(&mov->io, 16); /* size */ 91 | mov_buffer_write(&mov->io, "smhd", 4); 92 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 93 | mov_buffer_w16(&mov->io, 0); /* reserved (balance, normally = 0) */ 94 | mov_buffer_w16(&mov->io, 0); /* reserved */ 95 | return 16; 96 | } 97 | 98 | size_t mov_write_nmhd(const struct mov_t* mov) 99 | { 100 | mov_buffer_w32(&mov->io, 12); /* size */ 101 | mov_buffer_write(&mov->io, "nmhd", 4); 102 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 103 | return 12; 104 | } 105 | 106 | /* 107 | ISO/IEC 14496-12:2015(E) 12.6.2 Subtitle media header (p185) 108 | aligned(8) class SubtitleMediaHeaderBox extends FullBox ('sthd', version = 0, flags = 0){ 109 | } 110 | */ 111 | size_t mov_write_sthd(const struct mov_t* mov) 112 | { 113 | mov_buffer_w32(&mov->io, 12); /* size */ 114 | mov_buffer_write(&mov->io, "sthd", 4); 115 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 116 | return 12; 117 | } 118 | -------------------------------------------------------------------------------- /libmov/source/mov-mvhd.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | 4 | // ISO/IEC 14496-12:2012(E) 5 | // 8.2.2.1 Movie Header Box (p30) 6 | // Box Type : 'mvhd' 7 | // Container : Movie Box('moov') 8 | // Mandatory : Yes 9 | // Quantity : Exactly one 10 | 11 | /* 12 | aligned(8) class MovieHeaderBox extends FullBox('mvhd', version, 0) { 13 | if (version==1) { 14 | unsigned int(64) creation_time; 15 | unsigned int(64) modification_time; 16 | unsigned int(32) timescale; 17 | unsigned int(64) duration; 18 | } else { // version==0 19 | unsigned int(32) creation_time; 20 | unsigned int(32) modification_time; 21 | unsigned int(32) timescale; 22 | unsigned int(32) duration; 23 | } 24 | template int(32) rate = 0x00010000; // typically 1.0 25 | template int(16) volume = 0x0100; // typically, full volume 26 | const bit(16) reserved = 0; 27 | const unsigned int(32)[2] reserved = 0; 28 | template int(32)[9] matrix = { 29 | 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 30 | }; // Unity matrix 31 | bit(32)[6] pre_defined = 0; 32 | unsigned int(32) next_track_ID; 33 | } 34 | */ 35 | int mov_read_mvhd(struct mov_t* mov, const struct mov_box_t* box) 36 | { 37 | int i; 38 | struct mov_mvhd_t* mvhd = &mov->mvhd; 39 | 40 | mvhd->version = mov_buffer_r8(&mov->io); 41 | mvhd->flags = mov_buffer_r24(&mov->io); 42 | 43 | if (1 == mvhd->version) 44 | { 45 | mvhd->creation_time = mov_buffer_r64(&mov->io); 46 | mvhd->modification_time = mov_buffer_r64(&mov->io); 47 | mvhd->timescale = mov_buffer_r32(&mov->io); 48 | mvhd->duration = mov_buffer_r64(&mov->io); 49 | } 50 | else 51 | { 52 | assert(0 == mvhd->version); 53 | mvhd->creation_time = mov_buffer_r32(&mov->io); 54 | mvhd->modification_time = mov_buffer_r32(&mov->io); 55 | mvhd->timescale = mov_buffer_r32(&mov->io); 56 | mvhd->duration = mov_buffer_r32(&mov->io); 57 | } 58 | 59 | mvhd->rate = mov_buffer_r32(&mov->io); 60 | mvhd->volume = (uint16_t)mov_buffer_r16(&mov->io); 61 | //mvhd->reserved = mov_buffer_r16(&mov->io); 62 | //mvhd->reserved2[0] = mov_buffer_r32(&mov->io); 63 | //mvhd->reserved2[1] = mov_buffer_r32(&mov->io); 64 | mov_buffer_skip(&mov->io, 10); 65 | for (i = 0; i < 9; i++) 66 | mvhd->matrix[i] = mov_buffer_r32(&mov->io); 67 | #if 0 68 | for (i = 0; i < 6; i++) 69 | mvhd->pre_defined[i] = mov_buffer_r32(&mov->io); 70 | #else 71 | mov_buffer_r32(&mov->io); /* preview time */ 72 | mov_buffer_r32(&mov->io); /* preview duration */ 73 | mov_buffer_r32(&mov->io); /* poster time */ 74 | mov_buffer_r32(&mov->io); /* selection time */ 75 | mov_buffer_r32(&mov->io); /* selection duration */ 76 | mov_buffer_r32(&mov->io); /* current time */ 77 | #endif 78 | mvhd->next_track_ID = mov_buffer_r32(&mov->io); 79 | 80 | (void)box; 81 | return 0; 82 | } 83 | 84 | size_t mov_write_mvhd(const struct mov_t* mov) 85 | { 86 | // int rotation = 0; // 90/180/270 87 | const struct mov_mvhd_t* mvhd = &mov->mvhd; 88 | 89 | mov_buffer_w32(&mov->io, 108); /* size */ 90 | mov_buffer_write(&mov->io, "mvhd", 4); 91 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 92 | 93 | mov_buffer_w32(&mov->io, (uint32_t)mvhd->creation_time); /* creation_time */ 94 | mov_buffer_w32(&mov->io, (uint32_t)mvhd->modification_time); /* modification_time */ 95 | mov_buffer_w32(&mov->io, mvhd->timescale); /* timescale */ 96 | mov_buffer_w32(&mov->io, (uint32_t)mvhd->duration); /* duration */ 97 | 98 | mov_buffer_w32(&mov->io, 0x00010000); /* rate 1.0 */ 99 | mov_buffer_w16(&mov->io, 0x0100); /* volume 1.0 = normal */ 100 | mov_buffer_w16(&mov->io, 0); /* reserved */ 101 | mov_buffer_w32(&mov->io, 0); /* reserved */ 102 | mov_buffer_w32(&mov->io, 0); /* reserved */ 103 | 104 | // matrix 105 | mov_buffer_w32(&mov->io, 0x00010000); /* u */ 106 | mov_buffer_w32(&mov->io, 0); 107 | mov_buffer_w32(&mov->io, 0); 108 | mov_buffer_w32(&mov->io, 0); /* v */ 109 | mov_buffer_w32(&mov->io, 0x00010000); 110 | mov_buffer_w32(&mov->io, 0); 111 | mov_buffer_w32(&mov->io, 0); /* w */ 112 | mov_buffer_w32(&mov->io, 0); 113 | mov_buffer_w32(&mov->io, 0x40000000); 114 | 115 | mov_buffer_w32(&mov->io, 0); /* reserved (preview time) */ 116 | mov_buffer_w32(&mov->io, 0); /* reserved (preview duration) */ 117 | mov_buffer_w32(&mov->io, 0); /* reserved (poster time) */ 118 | mov_buffer_w32(&mov->io, 0); /* reserved (selection time) */ 119 | mov_buffer_w32(&mov->io, 0); /* reserved (selection duration) */ 120 | mov_buffer_w32(&mov->io, 0); /* reserved (current time) */ 121 | 122 | mov_buffer_w32(&mov->io, mvhd->next_track_ID); /* Next track id */ 123 | 124 | return 108; 125 | } 126 | -------------------------------------------------------------------------------- /libmov/source/mov-opus.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // http://www.opus-codec.org/docs/opus_in_isobmff.html 8 | // 4.3.2 Opus Specific Box 9 | /* 10 | class ChannelMappingTable (unsigned int(8) OutputChannelCount){ 11 | unsigned int(8) StreamCount; 12 | unsigned int(8) CoupledCount; 13 | unsigned int(8 * OutputChannelCount) ChannelMapping; 14 | } 15 | 16 | aligned(8) class OpusSpecificBox extends Box('dOps'){ 17 | unsigned int(8) Version; 18 | unsigned int(8) OutputChannelCount; 19 | unsigned int(16) PreSkip; 20 | unsigned int(32) InputSampleRate; 21 | signed int(16) OutputGain; 22 | unsigned int(8) ChannelMappingFamily; 23 | if (ChannelMappingFamily != 0) { 24 | ChannelMappingTable(OutputChannelCount); 25 | } 26 | } 27 | */ 28 | 29 | int mov_read_dops(struct mov_t* mov, const struct mov_box_t* box) 30 | { 31 | struct mov_track_t* track = mov->track; 32 | struct mov_sample_entry_t* entry = track->stsd.current; 33 | if(box->size >= 10) 34 | { 35 | if (entry->extra_data_size < box->size + 8) 36 | { 37 | void* p = realloc(entry->extra_data, (size_t)box->size + 8); 38 | if (NULL == p) return -ENOMEM; 39 | entry->extra_data = p; 40 | } 41 | 42 | memcpy(entry->extra_data, "OpusHead", 8); 43 | entry->extra_data[8] = 1; // OpusHead version 44 | mov_buffer_r8(&mov->io); // version 0 45 | entry->extra_data[9] = mov_buffer_r8(&mov->io); // channel 46 | entry->extra_data[11] = mov_buffer_r8(&mov->io); // PreSkip (MSB -> LSB) 47 | entry->extra_data[10] = mov_buffer_r8(&mov->io); 48 | entry->extra_data[15] = mov_buffer_r8(&mov->io); // InputSampleRate (LSB -> MSB) 49 | entry->extra_data[14] = mov_buffer_r8(&mov->io); 50 | entry->extra_data[13] = mov_buffer_r8(&mov->io); 51 | entry->extra_data[12] = mov_buffer_r8(&mov->io); 52 | entry->extra_data[17] = mov_buffer_r8(&mov->io); // OutputGain (LSB -> MSB) 53 | entry->extra_data[16] = mov_buffer_r8(&mov->io); 54 | mov_buffer_read(&mov->io, entry->extra_data + 18, (size_t)box->size - 10); 55 | entry->extra_data_size = (int)box->size + 8; 56 | } 57 | return mov_buffer_error(&mov->io); 58 | } 59 | 60 | size_t mov_write_dops(const struct mov_t* mov) 61 | { 62 | const struct mov_track_t* track = mov->track; 63 | const struct mov_sample_entry_t* entry = track->stsd.current; 64 | if (entry->extra_data_size < 18) 65 | return 0; 66 | 67 | assert(0 == memcmp(entry->extra_data, "OpusHead", 8)); 68 | mov_buffer_w32(&mov->io, entry->extra_data_size); /* size */ 69 | mov_buffer_write(&mov->io, "dOps", 4); 70 | mov_buffer_w8(&mov->io, 0); // The Version field shall be set to 0. 71 | mov_buffer_w8(&mov->io, entry->extra_data[9]); // channel count 72 | mov_buffer_w16(&mov->io, (entry->extra_data[11]<<8) | entry->extra_data[10]); // PreSkip (LSB -> MSB) 73 | mov_buffer_w32(&mov->io, (entry->extra_data[15]<<8) | (entry->extra_data[14]<<8) | (entry->extra_data[13]<<8) | entry->extra_data[12]); // InputSampleRate (LSB -> MSB) 74 | mov_buffer_w16(&mov->io, (entry->extra_data[17]<<8) | entry->extra_data[16]); // OutputGain (LSB -> MSB) 75 | mov_buffer_write(&mov->io, entry->extra_data + 18, entry->extra_data_size - 18); 76 | return entry->extra_data_size; 77 | } 78 | -------------------------------------------------------------------------------- /libmov/source/mov-sidx.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | 4 | // 8.16.3 Segment Index Box (p119) 5 | int mov_read_sidx(struct mov_t* mov, const struct mov_box_t* box) 6 | { 7 | unsigned int version; 8 | unsigned int i, reference_count; 9 | 10 | version = mov_buffer_r8(&mov->io); /* version */ 11 | mov_buffer_r24(&mov->io); /* flags */ 12 | mov_buffer_r32(&mov->io); /* reference_ID */ 13 | mov_buffer_r32(&mov->io); /* timescale */ 14 | 15 | if (0 == version) 16 | { 17 | mov_buffer_r32(&mov->io); /* earliest_presentation_time */ 18 | mov_buffer_r32(&mov->io); /* first_offset */ 19 | } 20 | else 21 | { 22 | mov_buffer_r64(&mov->io); /* earliest_presentation_time */ 23 | mov_buffer_r64(&mov->io); /* first_offset */ 24 | } 25 | 26 | mov_buffer_r16(&mov->io); /* reserved */ 27 | reference_count = mov_buffer_r16(&mov->io); /* reference_count */ 28 | for (i = 0; i < reference_count; i++) 29 | { 30 | mov_buffer_r32(&mov->io); /* reference_type & referenced_size */ 31 | mov_buffer_r32(&mov->io); /* subsegment_duration */ 32 | mov_buffer_r32(&mov->io); /* starts_with_SAP & SAP_type & SAP_delta_time */ 33 | } 34 | 35 | (void)box; 36 | return mov_buffer_error(&mov->io); 37 | } 38 | 39 | size_t mov_write_sidx(const struct mov_t* mov, uint64_t offset) 40 | { 41 | uint32_t duration; 42 | uint64_t earliest_presentation_time; 43 | const struct mov_track_t* track = mov->track; 44 | 45 | if (track->sample_count > 0) 46 | { 47 | earliest_presentation_time = track->samples[0].pts; 48 | duration = (uint32_t)(track->samples[track->sample_count - 1].dts - track->samples[0].dts) + (uint32_t)track->turn_last_duration; 49 | } 50 | else 51 | { 52 | duration = 0; 53 | earliest_presentation_time = 0; 54 | } 55 | 56 | mov_buffer_w32(&mov->io, 52); /* size */ 57 | mov_buffer_write(&mov->io, "sidx", 4); 58 | mov_buffer_w8(&mov->io, 1); /* version */ 59 | mov_buffer_w24(&mov->io, 0); /* flags */ 60 | 61 | mov_buffer_w32(&mov->io, track->tkhd.track_ID); /* reference_ID */ 62 | mov_buffer_w32(&mov->io, track->mdhd.timescale); /* timescale */ 63 | mov_buffer_w64(&mov->io, earliest_presentation_time); /* earliest_presentation_time */ 64 | mov_buffer_w64(&mov->io, offset); /* first_offset */ 65 | mov_buffer_w16(&mov->io, 0); /* reserved */ 66 | mov_buffer_w16(&mov->io, 1); /* reference_count */ 67 | 68 | mov_buffer_w32(&mov->io, 0); /* reference_type & referenced_size */ 69 | mov_buffer_w32(&mov->io, duration); /* subsegment_duration */ 70 | mov_buffer_w32(&mov->io, (1U/*starts_with_SAP*/ << 31) | (1 /*SAP_type*/ << 28) | 0 /*SAP_delta_time*/); 71 | 72 | return 52; 73 | } 74 | -------------------------------------------------------------------------------- /libmov/source/mov-stco.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // 8.7.5 Chunk Offset Box (p58) 7 | /* 8 | aligned(8) class ChunkOffsetBox extends FullBox('stco', version = 0, 0) { 9 | unsigned int(32) entry_count; 10 | for (i=1; i <= entry_count; i++) { 11 | unsigned int(32) chunk_offset; 12 | } 13 | } 14 | 15 | aligned(8) class ChunkLargeOffsetBox extends FullBox('co64', version = 0, 0) { 16 | unsigned int(32) entry_count; 17 | for (i=1; i <= entry_count; i++) { 18 | unsigned int(64) chunk_offset; 19 | } 20 | } 21 | */ 22 | 23 | int mov_read_stco(struct mov_t* mov, const struct mov_box_t* box) 24 | { 25 | uint32_t i, entry_count; 26 | struct mov_stbl_t* stbl = &mov->track->stbl; 27 | 28 | mov_buffer_r8(&mov->io); /* version */ 29 | mov_buffer_r24(&mov->io); /* flags */ 30 | entry_count = mov_buffer_r32(&mov->io); 31 | 32 | assert(0 == stbl->stco_count && NULL == stbl->stco); 33 | if (stbl->stco_count < entry_count) 34 | { 35 | void* p = realloc(stbl->stco, sizeof(stbl->stco[0]) * entry_count); 36 | if (NULL == p) return -ENOMEM; 37 | stbl->stco = p; 38 | } 39 | stbl->stco_count = entry_count; 40 | 41 | if (MOV_TAG('s', 't', 'c', 'o') == box->type) 42 | { 43 | for (i = 0; i < entry_count; i++) 44 | stbl->stco[i] = mov_buffer_r32(&mov->io); // chunk_offset 45 | } 46 | else if (MOV_TAG('c', 'o', '6', '4') == box->type) 47 | { 48 | for (i = 0; i < entry_count; i++) 49 | stbl->stco[i] = mov_buffer_r64(&mov->io); // chunk_offset 50 | } 51 | else 52 | { 53 | i = 0; 54 | assert(0); 55 | } 56 | 57 | stbl->stco_count = i; 58 | return mov_buffer_error(&mov->io); 59 | } 60 | 61 | size_t mov_write_stco(const struct mov_t* mov, uint32_t count) 62 | { 63 | int co64; 64 | uint32_t size, i; 65 | const struct mov_sample_t* sample; 66 | const struct mov_track_t* track = mov->track; 67 | 68 | sample = track->sample_count > 0 ? &track->samples[track->sample_count - 1] : NULL; 69 | co64 = (sample && sample->offset + track->offset > UINT32_MAX) ? 1 : 0; 70 | size = 12/* full box */ + 4/* entry count */ + count * (co64 ? 8 : 4); 71 | 72 | mov_buffer_w32(&mov->io, size); /* size */ 73 | mov_buffer_write(&mov->io, co64 ? "co64" : "stco", 4); 74 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 75 | mov_buffer_w32(&mov->io, count); /* entry count */ 76 | 77 | for (i = 0; i < track->sample_count; i++) 78 | { 79 | sample = track->samples + i; 80 | if(0 == sample->first_chunk) 81 | continue; 82 | 83 | if(0 == co64) 84 | mov_buffer_w32(&mov->io, (uint32_t)(sample->offset + track->offset)); 85 | else 86 | mov_buffer_w64(&mov->io, sample->offset + track->offset); 87 | } 88 | 89 | return size; 90 | } 91 | 92 | size_t mov_stco_size(const struct mov_track_t* track, uint64_t offset) 93 | { 94 | size_t i, j; 95 | uint64_t co64; 96 | const struct mov_sample_t* sample; 97 | 98 | if (track->sample_count < 1) 99 | return 0; 100 | 101 | sample = &track->samples[track->sample_count - 1]; 102 | co64 = sample->offset + track->offset; 103 | if (co64 > UINT32_MAX || co64 + offset <= UINT32_MAX) 104 | return 0; 105 | 106 | for (i = 0, j = 0; i < track->sample_count; i++) 107 | { 108 | sample = track->samples + i; 109 | if (0 != sample->first_chunk) 110 | j++; 111 | } 112 | 113 | return j * 4; 114 | } 115 | 116 | uint32_t mov_build_stco(struct mov_track_t* track) 117 | { 118 | size_t i; 119 | size_t bytes = 0; 120 | uint32_t count = 0; 121 | struct mov_sample_t* sample = NULL; 122 | 123 | assert(track->stsd.entry_count > 0); 124 | for (i = 0; i < track->sample_count; i++) 125 | { 126 | if (NULL != sample 127 | && sample->offset + bytes == track->samples[i].offset 128 | && sample->sample_description_index == track->samples[i].sample_description_index) 129 | { 130 | track->samples[i].first_chunk = 0; // mark invalid value 131 | bytes += track->samples[i].bytes; 132 | ++sample->samples_per_chunk; 133 | } 134 | else 135 | { 136 | sample = &track->samples[i]; 137 | sample->first_chunk = ++count; // chunk start from 1 138 | sample->samples_per_chunk = 1; 139 | bytes = sample->bytes; 140 | } 141 | } 142 | 143 | return count; 144 | } 145 | 146 | void mov_apply_stco(struct mov_track_t* track) 147 | { 148 | uint32_t i, j, k; 149 | uint64_t n, chunk_offset; 150 | struct mov_stbl_t* stbl = &track->stbl; 151 | 152 | // sample offset 153 | assert(stbl->stsc_count > 0 && stbl->stco_count > 0); 154 | stbl->stsc[stbl->stsc_count].first_chunk = stbl->stco_count + 1; // fill stco count 155 | for (i = 0, n = 0; i < stbl->stsc_count; i++) 156 | { 157 | assert(stbl->stsc[i].first_chunk <= stbl->stco_count); 158 | for (j = stbl->stsc[i].first_chunk; j < stbl->stsc[i + 1].first_chunk; j++) 159 | { 160 | chunk_offset = stbl->stco[j - 1]; // chunk start from 1 161 | for (k = 0; k < stbl->stsc[i].samples_per_chunk; k++, n++) 162 | { 163 | track->samples[n].sample_description_index = stbl->stsc[i].sample_description_index; 164 | track->samples[n].offset = chunk_offset; 165 | track->samples[n].data = NULL; 166 | chunk_offset += track->samples[n].bytes; 167 | assert(track->samples[n].bytes > 0); 168 | assert(0 == n || track->samples[n - 1].offset + track->samples[n - 1].bytes <= track->samples[n].offset); 169 | } 170 | } 171 | } 172 | 173 | assert(n == track->sample_count); 174 | } 175 | -------------------------------------------------------------------------------- /libmov/source/mov-stsc.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // 8.7.4 Sample To Chunk Box (p57) 7 | /* 8 | aligned(8) class SampleToChunkBox extends FullBox('stsc', version = 0, 0) { 9 | unsigned int(32) entry_count; 10 | for (i=1; i <= entry_count; i++) { 11 | unsigned int(32) first_chunk; 12 | unsigned int(32) samples_per_chunk; 13 | unsigned int(32) sample_description_index; 14 | } 15 | } 16 | */ 17 | int mov_read_stsc(struct mov_t* mov, const struct mov_box_t* box) 18 | { 19 | uint32_t i, entry_count; 20 | struct mov_stbl_t* stbl = &mov->track->stbl; 21 | 22 | mov_buffer_r8(&mov->io); /* version */ 23 | mov_buffer_r24(&mov->io); /* flags */ 24 | entry_count = mov_buffer_r32(&mov->io); 25 | 26 | assert(0 == stbl->stsc_count && NULL == stbl->stsc); // duplicated STSC atom 27 | if (stbl->stsc_count < entry_count) 28 | { 29 | void* p = realloc(stbl->stsc, sizeof(struct mov_stsc_t) * (entry_count + 1/*stco count*/)); 30 | if (NULL == p) return -ENOMEM; 31 | stbl->stsc = (struct mov_stsc_t*)p; 32 | } 33 | stbl->stsc_count = entry_count; 34 | 35 | for (i = 0; i < entry_count; i++) 36 | { 37 | stbl->stsc[i].first_chunk = mov_buffer_r32(&mov->io); 38 | stbl->stsc[i].samples_per_chunk = mov_buffer_r32(&mov->io); 39 | stbl->stsc[i].sample_description_index = mov_buffer_r32(&mov->io); 40 | } 41 | 42 | (void)box; 43 | return mov_buffer_error(&mov->io); 44 | } 45 | 46 | size_t mov_write_stsc(const struct mov_t* mov) 47 | { 48 | uint64_t offset; 49 | uint64_t offset2; 50 | uint32_t size, i, entry; 51 | const struct mov_sample_t* chunk = NULL; 52 | const struct mov_sample_t* sample = NULL; 53 | const struct mov_track_t* track = mov->track; 54 | 55 | size = 12/* full box */ + 4/* entry count */; 56 | 57 | offset = mov_buffer_tell(&mov->io); 58 | mov_buffer_w32(&mov->io, 0); /* size */ 59 | mov_buffer_write(&mov->io, "stsc", 4); 60 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 61 | mov_buffer_w32(&mov->io, 0); /* entry count */ 62 | 63 | for (i = 0, entry = 0; i < track->sample_count; i++) 64 | { 65 | sample = &track->samples[i]; 66 | if (0 == sample->first_chunk || 67 | (chunk && chunk->samples_per_chunk == sample->samples_per_chunk 68 | && chunk->sample_description_index == sample->sample_description_index)) 69 | continue; 70 | 71 | ++entry; 72 | chunk = sample; 73 | mov_buffer_w32(&mov->io, sample->first_chunk); 74 | mov_buffer_w32(&mov->io, sample->samples_per_chunk); 75 | mov_buffer_w32(&mov->io, sample->sample_description_index); 76 | } 77 | 78 | size += entry * 12/* entry size*/; 79 | offset2 = mov_buffer_tell(&mov->io); 80 | mov_buffer_seek(&mov->io, offset); 81 | mov_buffer_w32(&mov->io, size); /* size */ 82 | mov_buffer_seek(&mov->io, offset + 12); 83 | mov_buffer_w32(&mov->io, entry); /* entry count */ 84 | mov_buffer_seek(&mov->io, offset2); 85 | return size; 86 | } 87 | -------------------------------------------------------------------------------- /libmov/source/mov-stss.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // 8.6.2 Sync Sample Box (p50) 7 | int mov_read_stss(struct mov_t* mov, const struct mov_box_t* box) 8 | { 9 | uint32_t i, entry_count; 10 | struct mov_stbl_t* stbl = &mov->track->stbl; 11 | 12 | mov_buffer_r8(&mov->io); /* version */ 13 | mov_buffer_r24(&mov->io); /* flags */ 14 | entry_count = mov_buffer_r32(&mov->io); 15 | 16 | assert(0 == stbl->stss_count && NULL == stbl->stss); 17 | if (stbl->stss_count < entry_count) 18 | { 19 | void* p = realloc(stbl->stss, sizeof(stbl->stss[0]) * entry_count); 20 | if (NULL == p) return -ENOMEM; 21 | stbl->stss = p; 22 | } 23 | stbl->stss_count = entry_count; 24 | 25 | for (i = 0; i < entry_count; i++) 26 | stbl->stss[i] = mov_buffer_r32(&mov->io); // uint32_t sample_number 27 | 28 | (void)box; 29 | return mov_buffer_error(&mov->io); 30 | } 31 | 32 | size_t mov_write_stss(const struct mov_t* mov) 33 | { 34 | uint64_t offset; 35 | uint64_t offset2; 36 | uint32_t size, i, j; 37 | const struct mov_sample_t* sample; 38 | const struct mov_track_t* track = mov->track; 39 | 40 | size = 12/* full box */ + 4/* entry count */; 41 | 42 | offset = mov_buffer_tell(&mov->io); 43 | mov_buffer_w32(&mov->io, 0); /* size */ 44 | mov_buffer_write(&mov->io, "stss", 4); 45 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 46 | mov_buffer_w32(&mov->io, 0); /* entry count */ 47 | 48 | for (i = 0, j = 0; i < track->sample_count; i++) 49 | { 50 | sample = &track->samples[i]; 51 | if (sample->flags & MOV_AV_FLAG_KEYFREAME) 52 | { 53 | ++j; 54 | mov_buffer_w32(&mov->io, i + 1); // start from 1 55 | } 56 | } 57 | 58 | size += j * 4/* entry */; 59 | offset2 = mov_buffer_tell(&mov->io); 60 | mov_buffer_seek(&mov->io, offset); 61 | mov_buffer_w32(&mov->io, size); /* size */ 62 | mov_buffer_seek(&mov->io, offset + 12); 63 | mov_buffer_w32(&mov->io, j); /* entry count */ 64 | mov_buffer_seek(&mov->io, offset2); 65 | return size; 66 | } 67 | 68 | void mov_apply_stss(struct mov_track_t* track) 69 | { 70 | size_t i, j; 71 | struct mov_stbl_t* stbl = &track->stbl; 72 | 73 | for (i = 0; i < stbl->stss_count; i++) 74 | { 75 | j = stbl->stss[i]; // start from 1 76 | if (j > 0 && j <= track->sample_count) 77 | track->samples[j - 1].flags |= MOV_AV_FLAG_KEYFREAME; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /libmov/source/mov-stsz.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // 8.7.3.2 Sample Size Box (p57) 8 | int mov_read_stsz(struct mov_t* mov, const struct mov_box_t* box) 9 | { 10 | uint32_t i = 0, sample_size, sample_count; 11 | struct mov_track_t* track = mov->track; 12 | 13 | mov_buffer_r8(&mov->io); /* version */ 14 | mov_buffer_r24(&mov->io); /* flags */ 15 | sample_size = mov_buffer_r32(&mov->io); 16 | sample_count = mov_buffer_r32(&mov->io); 17 | 18 | assert(0 == track->sample_count && NULL == track->samples); // duplicated STSZ atom 19 | if (track->sample_count < sample_count) 20 | { 21 | void* p = realloc(track->samples, sizeof(struct mov_sample_t) * (sample_count + 1)); 22 | if (NULL == p) return -ENOMEM; 23 | track->samples = (struct mov_sample_t*)p; 24 | memset(track->samples, 0, sizeof(struct mov_sample_t) * (sample_count + 1)); 25 | } 26 | track->sample_count = sample_count; 27 | 28 | if (0 == sample_size) 29 | { 30 | for (i = 0; i < sample_count; i++) 31 | track->samples[i].bytes = mov_buffer_r32(&mov->io); // uint32_t entry_size 32 | } 33 | else 34 | { 35 | for (i = 0; i < sample_count; i++) 36 | track->samples[i].bytes = sample_size; 37 | } 38 | 39 | (void)box; 40 | return mov_buffer_error(&mov->io); 41 | } 42 | 43 | // 8.7.3.3 Compact Sample Size Box (p57) 44 | int mov_read_stz2(struct mov_t* mov, const struct mov_box_t* box) 45 | { 46 | uint32_t i, v, field_size, sample_count; 47 | struct mov_track_t* track = mov->track; 48 | 49 | mov_buffer_r8(&mov->io); /* version */ 50 | mov_buffer_r24(&mov->io); /* flags */ 51 | // unsigned int(24) reserved = 0; 52 | mov_buffer_r24(&mov->io); /* reserved */ 53 | field_size = mov_buffer_r8(&mov->io); 54 | sample_count = mov_buffer_r32(&mov->io); 55 | 56 | assert(4 == field_size || 8 == field_size || 16 == field_size); 57 | assert(0 == track->sample_count && NULL == track->samples); // duplicated STSZ atom 58 | if (track->sample_count < sample_count) 59 | { 60 | void* p = realloc(track->samples, sizeof(struct mov_sample_t) * (sample_count + 1)); 61 | if (NULL == p) return -ENOMEM; 62 | track->samples = (struct mov_sample_t*)p; 63 | memset(track->samples, 0, sizeof(struct mov_sample_t) * (sample_count + 1)); 64 | } 65 | track->sample_count = sample_count; 66 | 67 | if (4 == field_size) 68 | { 69 | for (i = 0; i < sample_count/2; i++) 70 | { 71 | v = mov_buffer_r8(&mov->io); 72 | track->samples[i * 2].bytes = (v >> 4) & 0x0F; 73 | track->samples[i * 2 + 1].bytes = v & 0x0F; 74 | } 75 | if (sample_count % 2) 76 | { 77 | v = mov_buffer_r8(&mov->io); 78 | track->samples[i * 2].bytes = (v >> 4) & 0x0F; 79 | } 80 | } 81 | else if (8 == field_size) 82 | { 83 | for (i = 0; i < sample_count; i++) 84 | track->samples[i].bytes = mov_buffer_r8(&mov->io); 85 | } 86 | else if (16 == field_size) 87 | { 88 | for (i = 0; i < sample_count; i++) 89 | track->samples[i].bytes = mov_buffer_r16(&mov->io); 90 | } 91 | else 92 | { 93 | i = 0; 94 | assert(0); 95 | } 96 | 97 | (void)box; 98 | return mov_buffer_error(&mov->io); 99 | } 100 | 101 | size_t mov_write_stsz(const struct mov_t* mov) 102 | { 103 | uint32_t size, i; 104 | const struct mov_track_t* track = mov->track; 105 | 106 | for(i = 1; i < track->sample_count; i++) 107 | { 108 | if(track->samples[i].bytes != track->samples[i-1].bytes) 109 | break; 110 | } 111 | 112 | size = 12/* full box */ + 8 + (i < track->sample_count ? 4 * track->sample_count : 0); 113 | mov_buffer_w32(&mov->io, size); /* size */ 114 | mov_buffer_write(&mov->io, "stsz", 4); 115 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 116 | 117 | if(i < track->sample_count) 118 | { 119 | mov_buffer_w32(&mov->io, 0); 120 | mov_buffer_w32(&mov->io, track->sample_count); 121 | for(i = 0; i < track->sample_count; i++) 122 | mov_buffer_w32(&mov->io, track->samples[i].bytes); 123 | } 124 | else 125 | { 126 | mov_buffer_w32(&mov->io, track->sample_count < 1 ? 0 : track->samples[0].bytes); 127 | mov_buffer_w32(&mov->io, track->sample_count); 128 | } 129 | 130 | return size; 131 | } 132 | -------------------------------------------------------------------------------- /libmov/source/mov-stts.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // 8.6.1.2 Decoding Time to Sample Box (p47) 7 | int mov_read_stts(struct mov_t* mov, const struct mov_box_t* box) 8 | { 9 | uint32_t i, entry_count; 10 | struct mov_stbl_t* stbl = &mov->track->stbl; 11 | 12 | mov_buffer_r8(&mov->io); /* version */ 13 | mov_buffer_r24(&mov->io); /* flags */ 14 | entry_count = mov_buffer_r32(&mov->io); 15 | 16 | assert(0 == stbl->stts_count && NULL == stbl->stts); // duplicated STTS atom 17 | if (stbl->stts_count < entry_count) 18 | { 19 | void* p = realloc(stbl->stts, sizeof(struct mov_stts_t) * entry_count); 20 | if (NULL == p) return -ENOMEM; 21 | stbl->stts = (struct mov_stts_t*)p; 22 | } 23 | stbl->stts_count = entry_count; 24 | 25 | for (i = 0; i < entry_count; i++) 26 | { 27 | stbl->stts[i].sample_count = mov_buffer_r32(&mov->io); 28 | stbl->stts[i].sample_delta = mov_buffer_r32(&mov->io); 29 | } 30 | 31 | (void)box; 32 | return mov_buffer_error(&mov->io); 33 | } 34 | 35 | // 8.6.1.3 Composition Time to Sample Box (p47) 36 | int mov_read_ctts(struct mov_t* mov, const struct mov_box_t* box) 37 | { 38 | uint32_t i, entry_count; 39 | struct mov_stbl_t* stbl = &mov->track->stbl; 40 | 41 | mov_buffer_r8(&mov->io); /* version */ 42 | mov_buffer_r24(&mov->io); /* flags */ 43 | entry_count = mov_buffer_r32(&mov->io); 44 | 45 | assert(0 == stbl->ctts_count && NULL == stbl->ctts); // duplicated CTTS atom 46 | if (stbl->ctts_count < entry_count) 47 | { 48 | void* p = realloc(stbl->ctts, sizeof(struct mov_stts_t) * entry_count); 49 | if (NULL == p) return -ENOMEM; 50 | stbl->ctts = (struct mov_stts_t*)p; 51 | } 52 | stbl->ctts_count = entry_count; 53 | 54 | for (i = 0; i < entry_count; i++) 55 | { 56 | stbl->ctts[i].sample_count = mov_buffer_r32(&mov->io); 57 | stbl->ctts[i].sample_delta = mov_buffer_r32(&mov->io); // parse at int32_t 58 | } 59 | 60 | (void)box; 61 | return mov_buffer_error(&mov->io); 62 | } 63 | 64 | // 8.6.1.4 Composition to Decode Box (p53) 65 | int mov_read_cslg(struct mov_t* mov, const struct mov_box_t* box) 66 | { 67 | uint8_t version; 68 | // struct mov_stbl_t* stbl = &mov->track->stbl; 69 | 70 | version = (uint8_t)mov_buffer_r8(&mov->io); /* version */ 71 | mov_buffer_r24(&mov->io); /* flags */ 72 | 73 | if (0 == version) 74 | { 75 | mov_buffer_r32(&mov->io); /* compositionToDTSShift */ 76 | mov_buffer_r32(&mov->io); /* leastDecodeToDisplayDelta */ 77 | mov_buffer_r32(&mov->io); /* greatestDecodeToDisplayDelta */ 78 | mov_buffer_r32(&mov->io); /* compositionStartTime */ 79 | mov_buffer_r32(&mov->io); /* compositionEndTime */ 80 | } 81 | else 82 | { 83 | mov_buffer_r64(&mov->io); 84 | mov_buffer_r64(&mov->io); 85 | mov_buffer_r64(&mov->io); 86 | mov_buffer_r64(&mov->io); 87 | mov_buffer_r64(&mov->io); 88 | } 89 | 90 | (void)box; 91 | return mov_buffer_error(&mov->io); 92 | } 93 | 94 | size_t mov_write_stts(const struct mov_t* mov, uint32_t count) 95 | { 96 | uint32_t size, i; 97 | const struct mov_sample_t* sample; 98 | const struct mov_track_t* track = mov->track; 99 | 100 | size = 12/* full box */ + 4/* entry count */ + count * 8/* entry */; 101 | 102 | mov_buffer_w32(&mov->io, size); /* size */ 103 | mov_buffer_write(&mov->io, "stts", 4); 104 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 105 | mov_buffer_w32(&mov->io, count); /* entry count */ 106 | 107 | for (i = 0; i < track->sample_count; i++) 108 | { 109 | sample = &track->samples[i]; 110 | if(0 == sample->first_chunk) 111 | continue; 112 | mov_buffer_w32(&mov->io, sample->first_chunk); // count 113 | mov_buffer_w32(&mov->io, sample->samples_per_chunk); // delta * timescale / 1000 114 | } 115 | 116 | return size; 117 | } 118 | 119 | size_t mov_write_ctts(const struct mov_t* mov, uint32_t count) 120 | { 121 | uint32_t size, i; 122 | const struct mov_sample_t* sample; 123 | const struct mov_track_t* track = mov->track; 124 | 125 | size = 12/* full box */ + 4/* entry count */ + count * 8/* entry */; 126 | 127 | mov_buffer_w32(&mov->io, size); /* size */ 128 | mov_buffer_write(&mov->io, "ctts", 4); 129 | mov_buffer_w8(&mov->io, (track->flags & MOV_TRACK_FLAG_CTTS_V1) ? 1 : 0); /* version */ 130 | mov_buffer_w24(&mov->io, 0); /* flags */ 131 | mov_buffer_w32(&mov->io, count); /* entry count */ 132 | 133 | for (i = 0; i < track->sample_count; i++) 134 | { 135 | sample = &track->samples[i]; 136 | if(0 == sample->first_chunk) 137 | continue; 138 | mov_buffer_w32(&mov->io, sample->first_chunk); // count 139 | mov_buffer_w32(&mov->io, sample->samples_per_chunk); // offset * timescale / 1000 140 | } 141 | 142 | return size; 143 | } 144 | 145 | uint32_t mov_build_stts(struct mov_track_t* track) 146 | { 147 | size_t i; 148 | uint32_t delta, count = 0; 149 | struct mov_sample_t* sample = NULL; 150 | 151 | for (i = 0; i < track->sample_count; i++) 152 | { 153 | assert(track->samples[i + 1].dts >= track->samples[i].dts || i + 1 == track->sample_count); 154 | delta = (uint32_t)(i + 1 < track->sample_count && track->samples[i + 1].dts > track->samples[i].dts ? track->samples[i + 1].dts - track->samples[i].dts : 1); 155 | if (NULL != sample && delta == sample->samples_per_chunk) 156 | { 157 | track->samples[i].first_chunk = 0; 158 | assert(sample->first_chunk > 0); 159 | ++sample->first_chunk; // compress 160 | } 161 | else 162 | { 163 | sample = &track->samples[i]; 164 | sample->first_chunk = 1; 165 | sample->samples_per_chunk = delta; 166 | ++count; 167 | } 168 | } 169 | return count; 170 | } 171 | 172 | uint32_t mov_build_ctts(struct mov_track_t* track) 173 | { 174 | size_t i; 175 | uint32_t delta; 176 | uint32_t count = 0; 177 | struct mov_sample_t* sample = NULL; 178 | 179 | for (i = 0; i < track->sample_count; i++) 180 | { 181 | delta = (uint32_t)(track->samples[i].pts - track->samples[i].dts); 182 | if (i > 0 && delta == sample->samples_per_chunk) 183 | { 184 | track->samples[i].first_chunk = 0; 185 | assert(sample->first_chunk > 0); 186 | ++sample->first_chunk; // compress 187 | } 188 | else 189 | { 190 | sample = &track->samples[i]; 191 | sample->first_chunk = 1; 192 | sample->samples_per_chunk = delta; 193 | ++count; 194 | 195 | // fixed: firefox version 51 don't support version 1 196 | if (track->samples[i].pts < track->samples[i].dts) 197 | track->flags |= MOV_TRACK_FLAG_CTTS_V1; 198 | } 199 | } 200 | 201 | return count; 202 | } 203 | 204 | void mov_apply_stts(struct mov_track_t* track) 205 | { 206 | size_t i, j, n; 207 | struct mov_stbl_t* stbl = &track->stbl; 208 | 209 | for (i = 0, n = 1; i < stbl->stts_count; i++) 210 | { 211 | for (j = 0; j < stbl->stts[i].sample_count; j++, n++) 212 | { 213 | track->samples[n].dts = track->samples[n - 1].dts + stbl->stts[i].sample_delta; 214 | track->samples[n].pts = track->samples[n].dts; 215 | } 216 | } 217 | assert(n - 1 == track->sample_count); // see more mov_read_stsz 218 | } 219 | 220 | void mov_apply_ctts(struct mov_track_t* track) 221 | { 222 | size_t i, j, n; 223 | int32_t delta, dts_shift; 224 | struct mov_stbl_t* stbl = &track->stbl; 225 | 226 | // make sure pts >= dts 227 | dts_shift = 0; 228 | for (i = 0; i < stbl->ctts_count; i++) 229 | { 230 | delta = (int32_t)stbl->ctts[i].sample_delta; 231 | if (delta < 0 && dts_shift > delta && delta != -1 /* see more cslg box*/) 232 | dts_shift = delta; 233 | } 234 | assert(dts_shift <= 0); 235 | 236 | // sample cts/pts 237 | for (i = 0, n = 0; i < stbl->ctts_count; i++) 238 | { 239 | for (j = 0; j < stbl->ctts[i].sample_count; j++, n++) 240 | track->samples[n].pts += (int64_t)((int32_t)stbl->ctts[i].sample_delta - dts_shift); // always as int, fixed mp4box delta version error 241 | } 242 | assert(0 == stbl->ctts_count || n == track->sample_count); 243 | } 244 | -------------------------------------------------------------------------------- /libmov/source/mov-tag.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | 4 | struct mov_object_tag { 5 | uint8_t id; 6 | uint32_t tag; 7 | }; 8 | 9 | static struct mov_object_tag s_tags[] = { 10 | { MOV_OBJECT_H264, MOV_H264 }, // AVCSampleEntry (ISO/IEC 14496-15:2010) 11 | { MOV_OBJECT_H264, MOV_TAG('a', 'v', 'c', '2') }, // AVC2SampleEntry (ISO/IEC 14496-15:2010) 12 | { MOV_OBJECT_H264, MOV_TAG('a', 'v', 'c', '3') }, // AVCSampleEntry (ISO/IEC 14496-15:2017) 13 | { MOV_OBJECT_H264, MOV_TAG('a', 'v', 'c', '4') }, // AVC2SampleEntry (ISO/IEC 14496-15:2017) 14 | { MOV_OBJECT_H265, MOV_H265 }, // HEVCSampleEntry (ISO/IEC 14496-15:2013) 15 | { MOV_OBJECT_H265, MOV_TAG('h', 'e', 'v', '1') }, // HEVCSampleEntry (ISO/IEC 14496-15:2013) 16 | { MOV_OBJECT_H266, MOV_TAG('v', 'v', 'c', '1') }, // VVCSampleEntry (ISO/IEC 14496-15:2021) 17 | { MOV_OBJECT_MP4V, MOV_MP4V }, 18 | { MOV_OBJECT_JPEG, MOV_MP4V }, 19 | { MOV_OBJECT_PNG, MOV_MP4V }, 20 | { MOV_OBJECT_JPEG2000, MOV_MP4V }, 21 | { MOV_OBJECT_AAC, MOV_MP4A }, 22 | { MOV_OBJECT_MP3, MOV_MP4A }, // mp4_read_decoder_config_descriptor 23 | { MOV_OBJECT_MP1A, MOV_MP4A }, // mp4_read_decoder_config_descriptor 24 | { MOV_OBJECT_G711a, MOV_TAG('a', 'l', 'a', 'w') }, 25 | { MOV_OBJECT_G711u, MOV_TAG('u', 'l', 'a', 'w') }, 26 | { MOV_OBJECT_TEXT, MOV_TAG('t', 'x', '3', 'g') }, 27 | { MOV_OBJECT_TEXT, MOV_TAG('t', 'e', 'x', 't') }, 28 | { MOV_OBJECT_TEXT, MOV_TAG('c', '6', '0', '8') }, 29 | { MOV_OBJECT_OPUS, MOV_OPUS }, 30 | { MOV_OBJECT_VP8, MOV_VP8 }, 31 | { MOV_OBJECT_VP9, MOV_VP9 }, 32 | { MOV_OBJECT_AV1, MOV_AV1 }, 33 | { MOV_OBJECT_AC3, MOV_AC3 }, 34 | { MOV_OBJECT_EAC3, MOV_TAG('e', 'c', '-', '3') }, 35 | { MOV_OBJECT_DTS, MOV_DTS }, 36 | { MOV_OBJECT_VC1, MOV_VC1 }, 37 | { MOV_OBJECT_DIRAC, MOV_DIRAC }, 38 | 39 | { MOV_OBJECT_H265, MOV_TAG('d', 'v', 'h', '1') }, // Dolby Vision HEVC(H.265) dvhe 40 | }; 41 | 42 | uint32_t mov_object_to_tag(uint8_t object) 43 | { 44 | int i; 45 | for (i = 0; i < sizeof(s_tags) / sizeof(s_tags[0]); i++) 46 | { 47 | if (s_tags[i].id == object) 48 | return s_tags[i].tag; 49 | } 50 | return 0; 51 | } 52 | 53 | uint8_t mov_tag_to_object(uint32_t tag) 54 | { 55 | int i; 56 | for (i = 0; i < sizeof(s_tags) / sizeof(s_tags[0]); i++) 57 | { 58 | if (s_tags[i].tag == tag) 59 | return s_tags[i].id; 60 | } 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /libmov/source/mov-tfdt.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // 8.8.12 Track fragment decode time (p76) 8 | int mov_read_tfdt(struct mov_t* mov, const struct mov_box_t* box) 9 | { 10 | unsigned int version; 11 | version = mov_buffer_r8(&mov->io); /* version */ 12 | mov_buffer_r24(&mov->io); /* flags */ 13 | 14 | if (1 == version) 15 | mov->track->tfdt_dts = mov_buffer_r64(&mov->io); /* baseMediaDecodeTime */ 16 | else 17 | mov->track->tfdt_dts = mov_buffer_r32(&mov->io); /* baseMediaDecodeTime */ 18 | 19 | // baseMediaDecodeTime + ELST start offset 20 | mov_apply_elst_tfdt(mov->track); 21 | 22 | (void)box; 23 | return mov_buffer_error(&mov->io); 24 | } 25 | 26 | size_t mov_write_tfdt(const struct mov_t* mov) 27 | { 28 | uint8_t version; 29 | uint64_t baseMediaDecodeTime; 30 | 31 | if (mov->track->sample_count < 1) 32 | return 0; 33 | 34 | baseMediaDecodeTime = mov->track->samples[0].dts - mov->track->start_dts; 35 | version = baseMediaDecodeTime > INT32_MAX ? 1 : 0; 36 | 37 | mov_buffer_w32(&mov->io, 0 == version ? 16 : 20); /* size */ 38 | mov_buffer_write(&mov->io, "tfdt", 4); 39 | mov_buffer_w8(&mov->io, version); /* version */ 40 | mov_buffer_w24(&mov->io, 0); /* flags */ 41 | 42 | if (1 == version) 43 | mov_buffer_w64(&mov->io, baseMediaDecodeTime); /* baseMediaDecodeTime */ 44 | else 45 | mov_buffer_w32(&mov->io, (uint32_t)baseMediaDecodeTime); /* baseMediaDecodeTime */ 46 | 47 | return 0 == version ? 16 : 20; 48 | } 49 | -------------------------------------------------------------------------------- /libmov/source/mov-tfhd.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | 4 | // 8.8.7 Track Fragment Header Box (p71) 5 | int mov_read_tfhd(struct mov_t* mov, const struct mov_box_t* box) 6 | { 7 | uint32_t flags; 8 | uint32_t track_ID; 9 | 10 | mov_buffer_r8(&mov->io); /* version */ 11 | flags = mov_buffer_r24(&mov->io); /* flags */ 12 | track_ID = mov_buffer_r32(&mov->io); /* track_ID */ 13 | 14 | mov->track = mov_find_track(mov, track_ID); 15 | if (NULL == mov->track) 16 | return -1; 17 | 18 | mov->track->tfhd.flags = flags; 19 | 20 | if (MOV_TFHD_FLAG_BASE_DATA_OFFSET & flags) 21 | mov->track->tfhd.base_data_offset = mov_buffer_r64(&mov->io); /* base_data_offset*/ 22 | else if(MOV_TFHD_FLAG_DEFAULT_BASE_IS_MOOF & flags) 23 | mov->track->tfhd.base_data_offset = mov->moof_offset; /* default-base-is-moof */ 24 | else 25 | mov->track->tfhd.base_data_offset = mov->implicit_offset; 26 | 27 | if (MOV_TFHD_FLAG_SAMPLE_DESCRIPTION_INDEX & flags) 28 | mov->track->tfhd.sample_description_index = mov_buffer_r32(&mov->io); /* sample_description_index*/ 29 | else 30 | mov->track->tfhd.sample_description_index = mov->track->trex.default_sample_description_index; 31 | 32 | if (MOV_TFHD_FLAG_DEFAULT_DURATION & flags) 33 | mov->track->tfhd.default_sample_duration = mov_buffer_r32(&mov->io); /* default_sample_duration*/ 34 | else 35 | mov->track->tfhd.default_sample_duration = mov->track->trex.default_sample_duration; 36 | 37 | if (MOV_TFHD_FLAG_DEFAULT_SIZE & flags) 38 | mov->track->tfhd.default_sample_size = mov_buffer_r32(&mov->io); /* default_sample_size*/ 39 | else 40 | mov->track->tfhd.default_sample_size = mov->track->trex.default_sample_size; 41 | 42 | if (MOV_TFHD_FLAG_DEFAULT_FLAGS & flags) 43 | mov->track->tfhd.default_sample_flags = mov_buffer_r32(&mov->io); /* default_sample_flags*/ 44 | else 45 | mov->track->tfhd.default_sample_flags = mov->track->trex.default_sample_flags; 46 | 47 | if (MOV_TFHD_FLAG_DURATION_IS_EMPTY & flags) 48 | (void)box; /* duration-is-empty*/ 49 | return mov_buffer_error(&mov->io); 50 | } 51 | 52 | size_t mov_write_tfhd(const struct mov_t* mov) 53 | { 54 | size_t size; 55 | uint64_t offset; 56 | 57 | size = 12 + 4 /* track_ID */; 58 | 59 | offset = mov_buffer_tell(&mov->io); 60 | mov_buffer_w32(&mov->io, 0); /* size */ 61 | mov_buffer_write(&mov->io, "tfhd", 4); 62 | mov_buffer_w8(&mov->io, 0); /* version */ 63 | mov_buffer_w24(&mov->io, mov->track->tfhd.flags); /* flags */ 64 | mov_buffer_w32(&mov->io, mov->track->tkhd.track_ID); /* track_ID */ 65 | 66 | if (MOV_TFHD_FLAG_BASE_DATA_OFFSET & mov->track->tfhd.flags) 67 | { 68 | mov_buffer_w64(&mov->io, mov->track->tfhd.base_data_offset); /* base_data_offset*/ 69 | size += 8; 70 | } 71 | 72 | if (MOV_TFHD_FLAG_SAMPLE_DESCRIPTION_INDEX & mov->track->tfhd.flags) 73 | { 74 | mov_buffer_w32(&mov->io, mov->track->stsd.entries[0].data_reference_index); /* sample_description_index*/ 75 | size += 4; 76 | } 77 | 78 | if (MOV_TFHD_FLAG_DEFAULT_DURATION & mov->track->tfhd.flags) 79 | { 80 | mov_buffer_w32(&mov->io, mov->track->tfhd.default_sample_duration); /* default_sample_duration*/ 81 | size += 4; 82 | } 83 | 84 | if (MOV_TFHD_FLAG_DEFAULT_SIZE & mov->track->tfhd.flags) 85 | { 86 | mov_buffer_w32(&mov->io, mov->track->tfhd.default_sample_size); /* default_sample_size*/ 87 | size += 4; 88 | } 89 | 90 | if (MOV_TFHD_FLAG_DEFAULT_FLAGS & mov->track->tfhd.flags) 91 | { 92 | mov_buffer_w32(&mov->io, mov->track->tfhd.default_sample_flags); /* default_sample_flags*/ 93 | size += 4; 94 | } 95 | 96 | mov_write_size(mov, offset, size); 97 | return size; 98 | } 99 | -------------------------------------------------------------------------------- /libmov/source/mov-tfra.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // 8.8.10 Track Fragment Random Access Box (p74) 7 | int mov_read_tfra(struct mov_t* mov, const struct mov_box_t* box) 8 | { 9 | unsigned int version; 10 | uint32_t track_ID; 11 | uint32_t length_size_of; 12 | uint32_t i, j, number_of_entry; 13 | uint32_t traf_number, trun_number, sample_number; 14 | struct mov_track_t* track; 15 | 16 | version = mov_buffer_r8(&mov->io); /* version */ 17 | mov_buffer_r24(&mov->io); /* flags */ 18 | track_ID = mov_buffer_r32(&mov->io); /* track_ID */ 19 | 20 | track = mov_find_track(mov, track_ID); 21 | if (NULL == track) 22 | return -1; 23 | 24 | length_size_of = mov_buffer_r32(&mov->io); /* length_size_of XXX */ 25 | number_of_entry = mov_buffer_r32(&mov->io); /* number_of_entry */ 26 | if (number_of_entry > 0) 27 | { 28 | void* p = realloc(track->frags, sizeof(struct mov_fragment_t) * number_of_entry); 29 | if (!p) return -ENOMEM; 30 | track->frags = p; 31 | } 32 | track->frag_count = number_of_entry; 33 | 34 | for (i = 0; i < number_of_entry; i++) 35 | { 36 | if (1 == version) 37 | { 38 | track->frags[i].time = mov_buffer_r64(&mov->io); /* time */ 39 | track->frags[i].offset = mov_buffer_r64(&mov->io); /* moof_offset */ 40 | } 41 | else 42 | { 43 | track->frags[i].time = mov_buffer_r32(&mov->io); /* time */ 44 | track->frags[i].offset = mov_buffer_r32(&mov->io); /* moof_offset */ 45 | } 46 | 47 | for (traf_number = 0, j = 0; j < ((length_size_of >> 4) & 0x03) + 1; j++) 48 | traf_number = (traf_number << 8) | mov_buffer_r8(&mov->io); /* traf_number */ 49 | 50 | for (trun_number = 0, j = 0; j < ((length_size_of >> 2) & 0x03) + 1; j++) 51 | trun_number = (trun_number << 8) | mov_buffer_r8(&mov->io); /* trun_number */ 52 | 53 | for (sample_number = 0, j = 0; j < (length_size_of & 0x03) + 1; j++) 54 | sample_number = (sample_number << 8) | mov_buffer_r8(&mov->io); /* sample_number */ 55 | } 56 | 57 | (void)box; 58 | return mov_buffer_error(&mov->io); 59 | } 60 | 61 | size_t mov_write_tfra(const struct mov_t* mov) 62 | { 63 | uint32_t i, size; 64 | const struct mov_track_t* track = mov->track; 65 | 66 | size = 12/* full box */ + 12/* base */ + track->frag_count * 19/* index */; 67 | 68 | mov_buffer_w32(&mov->io, size); /* size */ 69 | mov_buffer_write(&mov->io, "tfra", 4); 70 | mov_buffer_w8(&mov->io, 1); /* version */ 71 | mov_buffer_w24(&mov->io, 0); /* flags */ 72 | 73 | mov_buffer_w32(&mov->io, track->tkhd.track_ID); /* track_ID */ 74 | mov_buffer_w32(&mov->io, 0); /* length_size_of_traf_num/trun/sample */ 75 | mov_buffer_w32(&mov->io, track->frag_count); /* number_of_entry */ 76 | 77 | for (i = 0; i < track->frag_count; i++) 78 | { 79 | mov_buffer_w64(&mov->io, track->frags[i].time); 80 | mov_buffer_w64(&mov->io, track->frags[i].offset); /* moof_offset */ 81 | mov_buffer_w8(&mov->io, 1); /* traf number */ 82 | mov_buffer_w8(&mov->io, 1); /* trun number */ 83 | mov_buffer_w8(&mov->io, 1); /* sample number */ 84 | } 85 | 86 | return size; 87 | } 88 | -------------------------------------------------------------------------------- /libmov/source/mov-tkhd.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | 4 | // ISO/IEC 14496-12:2012(E) 5 | // 8.3.2 Track Header Box (p31) 6 | // Box Type : 'tkhd' 7 | // Container : Movie Box('trak') 8 | // Mandatory : Yes 9 | // Quantity : Exactly one 10 | 11 | /* 12 | aligned(8) class TrackHeaderBox extends FullBox('tkhd', version, flags){ 13 | if (version==1) { 14 | unsigned int(64) creation_time; 15 | unsigned int(64) modification_time; 16 | unsigned int(32) track_ID; 17 | const unsigned int(32) reserved = 0; 18 | unsigned int(64) duration; 19 | } else { // version==0 20 | unsigned int(32) creation_time; 21 | unsigned int(32) modification_time; 22 | unsigned int(32) track_ID; 23 | const unsigned int(32) reserved = 0; 24 | unsigned int(32) duration; 25 | } 26 | const unsigned int(32)[2] reserved = 0; 27 | template int(16) layer = 0; 28 | template int(16) alternate_group = 0; 29 | template int(16) volume = {if track_is_audio 0x0100 else 0}; 30 | const unsigned int(16) reserved = 0; 31 | template int(32)[9] matrix= { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; // unity matrix 32 | unsigned int(32) width; 33 | unsigned int(32) height; 34 | } 35 | */ 36 | int mov_read_tkhd(struct mov_t* mov, const struct mov_box_t* box) 37 | { 38 | int i; 39 | uint8_t version; 40 | uint32_t flags; 41 | uint64_t creation_time; 42 | uint64_t modification_time; 43 | uint64_t duration; 44 | uint32_t track_ID; 45 | struct mov_tkhd_t* tkhd; 46 | struct mov_track_t* track; 47 | 48 | version = mov_buffer_r8(&mov->io); 49 | flags = mov_buffer_r24(&mov->io); 50 | 51 | if (1 == version) 52 | { 53 | creation_time = mov_buffer_r64(&mov->io); 54 | modification_time = mov_buffer_r64(&mov->io); 55 | track_ID = mov_buffer_r32(&mov->io); 56 | /*reserved = */mov_buffer_r32(&mov->io); 57 | duration = mov_buffer_r64(&mov->io); 58 | } 59 | else 60 | { 61 | assert(0 == version); 62 | creation_time = mov_buffer_r32(&mov->io); 63 | modification_time = mov_buffer_r32(&mov->io); 64 | track_ID = mov_buffer_r32(&mov->io); 65 | /*reserved = */mov_buffer_r32(&mov->io); 66 | duration = mov_buffer_r32(&mov->io); 67 | } 68 | mov_buffer_skip(&mov->io, 8); // const unsigned int(32)[2] reserved = 0; 69 | 70 | track = mov_fetch_track(mov, track_ID); 71 | if (NULL == track) return -1; 72 | 73 | mov->track = track; 74 | tkhd = &mov->track->tkhd; 75 | tkhd->version = version; 76 | tkhd->flags = flags; 77 | tkhd->duration = duration; 78 | tkhd->creation_time = creation_time; 79 | tkhd->modification_time = modification_time; 80 | 81 | tkhd->layer = (int16_t)mov_buffer_r16(&mov->io); 82 | tkhd->alternate_group = (int16_t)mov_buffer_r16(&mov->io); 83 | tkhd->volume = (int16_t)mov_buffer_r16(&mov->io); 84 | mov_buffer_skip(&mov->io, 2); // const unsigned int(16) reserved = 0; 85 | for (i = 0; i < 9; i++) 86 | tkhd->matrix[i] = mov_buffer_r32(&mov->io); 87 | tkhd->width = mov_buffer_r32(&mov->io); 88 | tkhd->height = mov_buffer_r32(&mov->io); 89 | 90 | (void)box; 91 | return 0; 92 | } 93 | 94 | size_t mov_write_tkhd(const struct mov_t* mov) 95 | { 96 | // int rotation = 0; // 90/180/270 97 | uint16_t group = 0; 98 | const struct mov_tkhd_t* tkhd = &mov->track->tkhd; 99 | 100 | mov_buffer_w32(&mov->io, 92); /* size */ 101 | mov_buffer_write(&mov->io, "tkhd", 4); 102 | mov_buffer_w8(&mov->io, 0); /* version */ 103 | mov_buffer_w24(&mov->io, tkhd->flags); /* flags */ 104 | 105 | mov_buffer_w32(&mov->io, (uint32_t)tkhd->creation_time); /* creation_time */ 106 | mov_buffer_w32(&mov->io, (uint32_t)tkhd->modification_time); /* modification_time */ 107 | mov_buffer_w32(&mov->io, tkhd->track_ID); /* track_ID */ 108 | mov_buffer_w32(&mov->io, 0); /* reserved */ 109 | mov_buffer_w32(&mov->io, (uint32_t)tkhd->duration); /* duration */ 110 | 111 | mov_buffer_w32(&mov->io, 0); /* reserved */ 112 | mov_buffer_w32(&mov->io, 0); /* reserved */ 113 | mov_buffer_w16(&mov->io, tkhd->layer); /* layer */ 114 | mov_buffer_w16(&mov->io, group); /* alternate_group */ 115 | //mov_buffer_w16(&mov->io, AVSTREAM_AUDIO == track->stream_type ? 0x0100 : 0); /* volume */ 116 | mov_buffer_w16(&mov->io, tkhd->volume); /* volume */ 117 | mov_buffer_w16(&mov->io, 0); /* reserved */ 118 | 119 | // matrix 120 | //for (i = 0; i < 9; i++) 121 | // file_reader_rb32(mov->fp, tkhd->matrix[i]); 122 | mov_buffer_w32(&mov->io, 0x00010000); /* u */ 123 | mov_buffer_w32(&mov->io, 0); 124 | mov_buffer_w32(&mov->io, 0); 125 | mov_buffer_w32(&mov->io, 0); /* v */ 126 | mov_buffer_w32(&mov->io, 0x00010000); 127 | mov_buffer_w32(&mov->io, 0); 128 | mov_buffer_w32(&mov->io, 0); /* w */ 129 | mov_buffer_w32(&mov->io, 0); 130 | mov_buffer_w32(&mov->io, 0x40000000); 131 | 132 | mov_buffer_w32(&mov->io, tkhd->width /*track->av.video.width * 0x10000U*/); /* width */ 133 | mov_buffer_w32(&mov->io, tkhd->height/*track->av.video.height * 0x10000U*/); /* height */ 134 | return 92; 135 | } 136 | -------------------------------------------------------------------------------- /libmov/source/mov-trex.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // 8.8.3 Track Extends Box (p69) 7 | int mov_read_trex(struct mov_t* mov, const struct mov_box_t* box) 8 | { 9 | uint32_t track_ID; 10 | struct mov_track_t* track; 11 | 12 | (void)box; 13 | mov_buffer_r32(&mov->io); /* version & flags */ 14 | track_ID = mov_buffer_r32(&mov->io); /* track_ID */ 15 | 16 | track = mov_fetch_track(mov, track_ID); 17 | if (NULL == track) return -1; 18 | 19 | track->trex.default_sample_description_index = mov_buffer_r32(&mov->io); /* default_sample_description_index */ 20 | track->trex.default_sample_duration = mov_buffer_r32(&mov->io); /* default_sample_duration */ 21 | track->trex.default_sample_size = mov_buffer_r32(&mov->io); /* default_sample_size */ 22 | track->trex.default_sample_flags = mov_buffer_r32(&mov->io); /* default_sample_flags */ 23 | return mov_buffer_error(&mov->io); 24 | } 25 | 26 | size_t mov_write_trex(const struct mov_t* mov) 27 | { 28 | mov_buffer_w32(&mov->io, 12 + 20); /* size */ 29 | mov_buffer_write(&mov->io, "trex", 4); 30 | mov_buffer_w32(&mov->io, 0); /* version & flags */ 31 | mov_buffer_w32(&mov->io, mov->track->tkhd.track_ID); /* track_ID */ 32 | mov_buffer_w32(&mov->io, 1); /* default_sample_description_index */ 33 | mov_buffer_w32(&mov->io, 0); /* default_sample_duration */ 34 | mov_buffer_w32(&mov->io, 0); /* default_sample_size */ 35 | mov_buffer_w32(&mov->io, 0); /* default_sample_flags */ 36 | return 32; 37 | } 38 | -------------------------------------------------------------------------------- /libmov/source/mov-trun.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // 8.8.8 Track Fragment Run Box (p72) 8 | int mov_read_trun(struct mov_t* mov, const struct mov_box_t* box) 9 | { 10 | unsigned int version; 11 | uint32_t flags; 12 | uint32_t i, sample_count; 13 | uint64_t data_offset; 14 | uint32_t first_sample_flags; 15 | uint32_t sample_duration, sample_size, sample_flags; 16 | int64_t sample_composition_time_offset; 17 | struct mov_track_t* track; 18 | struct mov_sample_t* sample; 19 | 20 | version = mov_buffer_r8(&mov->io); /* version */ 21 | flags = mov_buffer_r24(&mov->io); /* flags */ 22 | sample_count = mov_buffer_r32(&mov->io); /* sample_count */ 23 | 24 | track = mov->track; 25 | if (sample_count > 0) 26 | { 27 | void* p = realloc(track->samples, sizeof(struct mov_sample_t) * (track->sample_count + sample_count + 1)); 28 | if (NULL == p) return -ENOMEM; 29 | track->samples = (struct mov_sample_t*)p; 30 | memset(track->samples + track->sample_count, 0, sizeof(struct mov_sample_t) * (sample_count + 1)); 31 | } 32 | 33 | data_offset = track->tfhd.base_data_offset; 34 | if (MOV_TRUN_FLAG_DATA_OFFSET_PRESENT & flags) 35 | data_offset += (int32_t)mov_buffer_r32(&mov->io); /* data_offset */ 36 | 37 | if (MOV_TRUN_FLAG_FIRST_SAMPLE_FLAGS_PRESENT & flags) 38 | first_sample_flags = mov_buffer_r32(&mov->io); /* first_sample_flags */ 39 | else 40 | first_sample_flags = track->tfhd.flags; 41 | 42 | sample = track->samples + track->sample_count; 43 | for (i = 0; i < sample_count; i++) 44 | { 45 | if (MOV_TRUN_FLAG_SAMPLE_DURATION_PRESENT & flags) 46 | sample_duration = mov_buffer_r32(&mov->io); /* sample_duration*/ 47 | else 48 | sample_duration = track->tfhd.default_sample_duration; 49 | 50 | if (MOV_TRUN_FLAG_SAMPLE_SIZE_PRESENT & flags) 51 | sample_size = mov_buffer_r32(&mov->io); /* sample_size*/ 52 | else 53 | sample_size = track->tfhd.default_sample_size; 54 | 55 | if (MOV_TRUN_FLAG_SAMPLE_FLAGS_PRESENT & flags) 56 | sample_flags = mov_buffer_r32(&mov->io); /* sample_flags*/ 57 | else 58 | sample_flags = i ? track->tfhd.default_sample_flags : first_sample_flags; 59 | 60 | if (MOV_TRUN_FLAG_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT & flags) 61 | { 62 | sample_composition_time_offset = mov_buffer_r32(&mov->io); /* sample_composition_time_offset*/ 63 | if (1 == version) 64 | sample_composition_time_offset = (int32_t)sample_composition_time_offset; 65 | } 66 | else 67 | sample_composition_time_offset = 0; 68 | 69 | sample[i].offset = data_offset; 70 | sample[i].bytes = sample_size; 71 | sample[i].dts = track->tfdt_dts; 72 | sample[i].pts = sample[i].dts + sample_composition_time_offset; 73 | sample[i].flags = (sample_flags & (MOV_TREX_FLAG_SAMPLE_IS_NO_SYNC_SAMPLE | 0x01000000)) ? 0 : MOV_AV_FLAG_KEYFREAME; 74 | sample[i].sample_description_index = track->tfhd.sample_description_index; 75 | 76 | data_offset += sample_size; 77 | track->tfdt_dts += sample_duration; 78 | } 79 | track->sample_count += sample_count; 80 | mov->implicit_offset = data_offset; 81 | 82 | (void)box; 83 | return mov_buffer_error(&mov->io); 84 | } 85 | 86 | size_t mov_write_trun(const struct mov_t* mov, uint32_t from, uint32_t count, uint32_t moof) 87 | { 88 | uint32_t flags; 89 | uint32_t delta; 90 | uint64_t offset; 91 | uint32_t size, i; 92 | const struct mov_sample_t* sample; 93 | const struct mov_track_t* track = mov->track; 94 | 95 | if (count < 1) return 0; 96 | assert(from + count <= track->sample_count); 97 | flags = MOV_TRUN_FLAG_DATA_OFFSET_PRESENT; 98 | if (track->samples[from].flags & MOV_AV_FLAG_KEYFREAME) 99 | flags |= MOV_TRUN_FLAG_FIRST_SAMPLE_FLAGS_PRESENT; 100 | 101 | for (i = from; i < from + count; i++) 102 | { 103 | sample = track->samples + i; 104 | if (sample->bytes != track->tfhd.default_sample_size) 105 | flags |= MOV_TRUN_FLAG_SAMPLE_SIZE_PRESENT; 106 | if ((uint32_t)(i + 1 < track->sample_count ? track->samples[i + 1].dts - track->samples[i].dts : track->turn_last_duration) != track->tfhd.default_sample_duration) 107 | flags |= MOV_TRUN_FLAG_SAMPLE_DURATION_PRESENT; 108 | if (sample->pts != sample->dts) 109 | flags |= MOV_TRUN_FLAG_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT; 110 | } 111 | 112 | size = 12/* full box */ + 4/* sample count */; 113 | 114 | offset = mov_buffer_tell(&mov->io); 115 | mov_buffer_w32(&mov->io, 0); /* size */ 116 | mov_buffer_write(&mov->io, "trun", 4); 117 | mov_buffer_w8(&mov->io, 1); /* version */ 118 | mov_buffer_w24(&mov->io, flags); /* flags */ 119 | mov_buffer_w32(&mov->io, count); /* sample_count */ 120 | 121 | assert(flags & MOV_TRUN_FLAG_DATA_OFFSET_PRESENT); 122 | if (flags & MOV_TRUN_FLAG_DATA_OFFSET_PRESENT) 123 | { 124 | mov_buffer_w32(&mov->io, moof + (uint32_t)track->samples[from].offset); 125 | size += 4; 126 | } 127 | 128 | if (flags & MOV_TRUN_FLAG_FIRST_SAMPLE_FLAGS_PRESENT) 129 | { 130 | mov_buffer_w32(&mov->io, MOV_TREX_FLAG_SAMPLE_DEPENDS_ON_I_PICTURE); /* first_sample_flags */ 131 | size += 4; 132 | } 133 | 134 | assert(from + count <= track->sample_count); 135 | for (i = from; i < from + count; i++) 136 | { 137 | sample = track->samples + i; 138 | if (flags & MOV_TRUN_FLAG_SAMPLE_DURATION_PRESENT) 139 | { 140 | delta = (uint32_t)(i + 1 < track->sample_count ? track->samples[i + 1].dts - track->samples[i].dts : track->turn_last_duration); 141 | mov_buffer_w32(&mov->io, delta); /* sample_duration */ 142 | size += 4; 143 | } 144 | 145 | if (flags & MOV_TRUN_FLAG_SAMPLE_SIZE_PRESENT) 146 | { 147 | mov_buffer_w32(&mov->io, (uint32_t)sample->bytes); /* sample_size */ 148 | size += 4; 149 | } 150 | 151 | assert(0 == (flags & MOV_TRUN_FLAG_SAMPLE_FLAGS_PRESENT)); 152 | // mov_buffer_w32(&mov->io, 0); /* sample_flags */ 153 | 154 | if (flags & MOV_TRUN_FLAG_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT) 155 | { 156 | mov_buffer_w32(&mov->io, (int32_t)(sample->pts - sample->dts)); /* sample_composition_time_offset */ 157 | size += 4; 158 | } 159 | } 160 | 161 | mov_write_size(mov, offset, size); 162 | return size; 163 | } 164 | -------------------------------------------------------------------------------- /libmov/source/mov-tx3g.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // 3GPP TS 26.245 Release 14 12 V14.0.0 (2017-03) 7 | /* 8 | aligned(8) class StyleRecord { 9 | unsigned int(16) startChar; 10 | unsigned int(16) endChar; 11 | unsigned int(16) font-ID; 12 | unsigned int(8) face-style-flags; 13 | unsigned int(8) font-size; 14 | unsigned int(8) text-color-rgba[4]; 15 | } 16 | 17 | class FontRecord { 18 | unsigned int(16) font-ID; 19 | unsigned int(8) font-name-length; 20 | unsigned int(8) font[font-name-length]; 21 | } 22 | class FontTableBox() extends Box('ftab') { 23 | unsigned int(16) entry-count; 24 | FontRecord font-entry[entry-count]; 25 | } 26 | class DisparityBox() extends TextSampleModifierBox ('disp') { 27 | signed int(16) disparity-shift-in-16th-pel; 28 | } 29 | class BoxRecord { 30 | signed int(16) top; 31 | signed int(16) left; 32 | signed int(16) bottom; 33 | signed int(16) right; 34 | } 35 | class TextSampleEntry() extends SampleEntry ('tx3g') { 36 | unsigned int(32) displayFlags; 37 | signed int(8) horizontal-justification; 38 | signed int(8) vertical-justification; 39 | unsigned int(8) background-color-rgba[4]; 40 | BoxRecord default-text-box; 41 | StyleRecord default-style; 42 | FontTableBox font-table; 43 | DisparityBox default-disparity; 44 | } 45 | */ 46 | 47 | int mov_read_tx3g(struct mov_t* mov, const struct mov_box_t* box) 48 | { 49 | struct mov_box_t extra; 50 | //struct mov_track_t* track = mov->track; 51 | //struct mov_sample_entry_t* entry = track->stsd.current; 52 | 53 | mov_buffer_r32(&mov->io); // displayFlags 54 | mov_buffer_r8(&mov->io); // horizontal-justification 55 | mov_buffer_r8(&mov->io); // vertical-justification 56 | mov_buffer_r8(&mov->io); // background-color-rgba[4] 57 | mov_buffer_r8(&mov->io); 58 | mov_buffer_r8(&mov->io); 59 | mov_buffer_r8(&mov->io); 60 | mov_buffer_r16(&mov->io); // BoxRecord.top 61 | mov_buffer_r16(&mov->io); // BoxRecord.left 62 | mov_buffer_r16(&mov->io); // BoxRecord.bottom 63 | mov_buffer_r16(&mov->io); // BoxRecord.right 64 | mov_buffer_r16(&mov->io); // StyleRecord.startChar 65 | mov_buffer_r16(&mov->io); // StyleRecord.endChar 66 | mov_buffer_r16(&mov->io); // StyleRecord.font-ID 67 | mov_buffer_r8(&mov->io); // StyleRecord.face-style-flags 68 | mov_buffer_r8(&mov->io); // StyleRecord.font-size 69 | mov_buffer_r8(&mov->io); // StyleRecord.text-color-rgba[4] 70 | mov_buffer_r8(&mov->io); 71 | mov_buffer_r8(&mov->io); 72 | mov_buffer_r8(&mov->io); 73 | 74 | // FontTableBox 75 | extra.type = box->type; 76 | extra.size = box->size - 30; 77 | return mp4_read_extra(mov, &extra); 78 | } 79 | 80 | size_t mov_write_tx3g(const struct mov_t* mov) 81 | { 82 | //const struct mov_track_t* track = mov->track; 83 | //const struct mov_sample_entry_t* entry = track->stsd.current; 84 | 85 | mov_buffer_w32(&mov->io, 0); // displayFlags 86 | mov_buffer_w8(&mov->io, 0x01); // horizontal-justification 87 | mov_buffer_w8(&mov->io, 0xFF); // vertical-justification 88 | mov_buffer_w8(&mov->io, 0x00); // background-color-rgba[4] 89 | mov_buffer_w8(&mov->io, 0x00); 90 | mov_buffer_w8(&mov->io, 0x00); 91 | mov_buffer_w8(&mov->io, 0x00); 92 | mov_buffer_w16(&mov->io, 0x0000); // BoxRecord.top 93 | mov_buffer_w16(&mov->io, 0x0000); // BoxRecord.left 94 | mov_buffer_w16(&mov->io, 0x0000); // BoxRecord.bottom 95 | mov_buffer_w16(&mov->io, 0x0000); // BoxRecord.right 96 | mov_buffer_w16(&mov->io, 0x0000); // StyleRecord.startChar 97 | mov_buffer_w16(&mov->io, 0x0000); // StyleRecord.endChar 98 | mov_buffer_w16(&mov->io, 0x0001); // StyleRecord.font-ID 99 | mov_buffer_w8(&mov->io, 0x00); // StyleRecord.face-style-flags 100 | mov_buffer_w8(&mov->io, 0x12); // StyleRecord.font-size 101 | mov_buffer_w8(&mov->io, 0xFF); // StyleRecord.text-color-rgba[4] 102 | mov_buffer_w8(&mov->io, 0xFF); 103 | mov_buffer_w8(&mov->io, 0xFF); 104 | mov_buffer_w8(&mov->io, 0xFF); 105 | 106 | // FontTableBox 107 | mov_buffer_w32(&mov->io, 18); /* size */ 108 | mov_buffer_write(&mov->io, "ftab", 4); 109 | mov_buffer_w16(&mov->io, 1); /* entry-count */ 110 | mov_buffer_w16(&mov->io, 0x0001); /* FontRecord.font-ID */ 111 | mov_buffer_w8(&mov->io, 5); /* FontRecord.font-name-length */ 112 | mov_buffer_write(&mov->io, "Serif", 5); /* FontRecord.font[font-name-length] */ 113 | 114 | return 30 + 18; 115 | } 116 | -------------------------------------------------------------------------------- /libmov/source/mov-udta.c: -------------------------------------------------------------------------------- 1 | #include "mov-udta.h" 2 | #include "mov-ioutil.h" 3 | #include "mov-memory-buffer.h" 4 | #include "mov-internal.h" 5 | 6 | int mov_read_udta(struct mov_t* mov, const struct mov_box_t* box) 7 | { 8 | mov_buffer_skip(&mov->io, box->size); 9 | return mov_buffer_error(&mov->io); 10 | } 11 | 12 | size_t mov_write_udta(const struct mov_t* mov) 13 | { 14 | if (!mov->udta || mov->udta_size < 1) 15 | return 0; 16 | 17 | mov_buffer_w32(&mov->io, 8 + (uint32_t)mov->udta_size); 18 | mov_buffer_write(&mov->io, "udta", 4); 19 | mov_buffer_write(&mov->io, mov->udta, mov->udta_size); 20 | return 8 + (size_t)mov->udta_size; 21 | } 22 | 23 | int mov_udta_meta_write(const struct mov_udta_meta_t* meta, void* data, int bytes) 24 | { 25 | struct mov_ioutil_t w; 26 | struct mov_memory_buffer_t ptr; 27 | uint64_t pmeta, pilst, n; 28 | 29 | ptr.maxsize = bytes; 30 | ptr.capacity = bytes; 31 | ptr.off = 0; 32 | ptr.ptr = (uint8_t*)data; 33 | memset(&w, 0, sizeof(w)); 34 | memcpy(&w.io, mov_memory_buffer(), sizeof(w.io)); 35 | w.param = &ptr; 36 | 37 | pmeta = mov_buffer_tell(&w); 38 | mov_buffer_w32(&w, 0); // placeholder 39 | mov_buffer_write(&w, "meta", 4); 40 | mov_buffer_w32(&w, 0); /* version & flags */ 41 | 42 | mov_buffer_w32(&w, 33); 43 | mov_buffer_write(&w, "hdlr", 4); 44 | mov_buffer_w32(&w, 0); /* version & flags */ 45 | mov_buffer_w32(&w, 0); 46 | mov_buffer_write(&w, "mdir", 4); 47 | mov_buffer_write(&w, "appl", 4); 48 | mov_buffer_w32(&w, 0); 49 | mov_buffer_w32(&w, 0); 50 | mov_buffer_w8(&w, 0); 51 | 52 | pilst = mov_buffer_tell(&w); 53 | mov_buffer_w32(&w, 0); // placeholder 54 | mov_buffer_write(&w, "ilst", 4); 55 | 56 | // write cover 57 | mov_buffer_w32(&w, meta->cover_size + 16 + 8); 58 | mov_buffer_write(&w, "covr", 4); 59 | mov_buffer_w32(&w, meta->cover_size + 16); 60 | mov_buffer_write(&w, "data", 4); 61 | mov_buffer_w32(&w, 0); // TODO track tag 62 | mov_buffer_w32(&w, 0); 63 | mov_buffer_write(&w, meta->cover, meta->cover_size); 64 | 65 | // update box size 66 | n = mov_buffer_tell(&w); 67 | mov_buffer_seek(&w, pilst); 68 | mov_buffer_w32(&w, (uint32_t)(n - pilst)); 69 | mov_buffer_seek(&w, pmeta); 70 | mov_buffer_w32(&w, (uint32_t)(n - pmeta)); 71 | mov_buffer_seek(&w, n); // rewind 72 | 73 | return (int)ptr.bytes; 74 | } 75 | -------------------------------------------------------------------------------- /libmov/source/mov-vpcc.c: -------------------------------------------------------------------------------- 1 | #include "mov-internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // https://www.webmproject.org/vp9/mp4/ 7 | // extra data: VPCodecConfigurationBox 8 | 9 | int mov_read_vpcc(struct mov_t* mov, const struct mov_box_t* box) 10 | { 11 | struct mov_track_t* track = mov->track; 12 | struct mov_sample_entry_t* entry = track->stsd.current; 13 | if(box->size < 4) 14 | return -1; 15 | if (entry->extra_data_size < box->size-4) 16 | { 17 | void* p = realloc(entry->extra_data, (size_t)box->size-4); 18 | if (NULL == p) return -ENOMEM; 19 | entry->extra_data = p; 20 | } 21 | 22 | mov_buffer_r8(&mov->io); /* version */ 23 | mov_buffer_r24(&mov->io); /* flags */ 24 | mov_buffer_read(&mov->io, entry->extra_data, box->size-4); 25 | entry->extra_data_size = (int)box->size - 4; 26 | return mov_buffer_error(&mov->io); 27 | } 28 | 29 | size_t mov_write_vpcc(const struct mov_t* mov) 30 | { 31 | const struct mov_track_t* track = mov->track; 32 | const struct mov_sample_entry_t* entry = track->stsd.current; 33 | mov_buffer_w32(&mov->io, entry->extra_data_size + 12); /* size */ 34 | mov_buffer_write(&mov->io, "vpcC", 4); 35 | mov_buffer_w8(&mov->io, 1); /* version */ 36 | mov_buffer_w24(&mov->io, 0); /* flags */ 37 | if (entry->extra_data_size > 0) 38 | mov_buffer_write(&mov->io, entry->extra_data, entry->extra_data_size); 39 | return entry->extra_data_size + 12; 40 | } 41 | -------------------------------------------------------------------------------- /libmov/test/fmp4-writer-test.cpp: -------------------------------------------------------------------------------- 1 | #include "fmp4-writer.h" 2 | #include "mov-format.h" 3 | #include "mpeg4-aac.h" 4 | #include "opus-head.h" 5 | #include "mp3-header.h" 6 | #include "flv-proto.h" 7 | #include "flv-reader.h" 8 | #include "flv-parser.h" 9 | #include 10 | #include 11 | #include 12 | 13 | extern "C" const struct mov_buffer_t* mov_file_buffer(void); 14 | 15 | static uint8_t s_buffer[2 * 1024 * 1024]; 16 | static int s_width, s_height; 17 | 18 | static int onFLV(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags) 19 | { 20 | fmp4_writer_t* mov = (fmp4_writer_t*)param; 21 | static int s_audio_track = -1; 22 | static int s_avc_track = -1; 23 | 24 | switch (codec) 25 | { 26 | case FLV_AUDIO_AAC: 27 | case FLV_AUDIO_OPUS: 28 | return fmp4_writer_write(mov, s_audio_track, data, bytes, pts, dts, 1 == flags ? MOV_AV_FLAG_KEYFREAME : 0); 29 | 30 | case FLV_AUDIO_MP3: 31 | if (-1 == s_audio_track) 32 | { 33 | struct mp3_header_t mp3; 34 | if (0 == mp3_header_load(&mp3, data, bytes)) 35 | return -1; 36 | s_audio_track = fmp4_writer_add_audio(mov, MOV_OBJECT_MP3, mp3_get_channel(&mp3), 16, mp3_get_frequency(&mp3), NULL, 0); 37 | } 38 | 39 | if (-1 == s_audio_track) 40 | return -1; 41 | return fmp4_writer_write(mov, s_audio_track, data, bytes, pts, dts, 1 == flags ? MOV_AV_FLAG_KEYFREAME : 0); 42 | 43 | case FLV_AUDIO_G711A: 44 | case FLV_AUDIO_G711U: 45 | if (-1 == s_audio_track) 46 | s_audio_track = fmp4_writer_add_audio(mov, codec == FLV_AUDIO_G711A ? MOV_OBJECT_G711a : MOV_OBJECT_G711u, 1, 16, 8000, NULL, 0); 47 | if (-1 == s_audio_track) 48 | return -1; 49 | return fmp4_writer_write(mov, s_audio_track, data, bytes, pts, dts, 0); 50 | 51 | case FLV_VIDEO_H264: 52 | return fmp4_writer_write(mov, s_avc_track, data, bytes, pts, dts, flags); 53 | 54 | case FLV_VIDEO_AVCC: 55 | if (-1 == s_avc_track) 56 | { 57 | s_avc_track = fmp4_writer_add_video(mov, MOV_OBJECT_H264, s_width, s_height, data, bytes); 58 | } 59 | break; 60 | 61 | case FLV_AUDIO_ASC: 62 | if (-1 == s_audio_track) 63 | { 64 | struct mpeg4_aac_t aac; 65 | mpeg4_aac_audio_specific_config_load((const uint8_t*)data, bytes, &aac); 66 | int rate = mpeg4_aac_audio_frequency_to((enum mpeg4_aac_frequency)aac.sampling_frequency_index); 67 | s_audio_track = fmp4_writer_add_audio(mov, MOV_OBJECT_AAC, aac.channel_configuration, 16, rate, data, bytes); 68 | } 69 | break; 70 | 71 | case FLV_AUDIO_OPUS_HEAD: 72 | if (-1 == s_audio_track) 73 | { 74 | struct opus_head_t opus; 75 | opus_head_load((const uint8_t*)data, bytes, &opus); 76 | s_audio_track = fmp4_writer_add_audio(mov, MOV_OBJECT_OPUS, opus.channels, 16, opus.input_sample_rate, data, bytes); 77 | } 78 | break; 79 | 80 | default: 81 | // nothing to do 82 | //assert(0); 83 | break; 84 | } 85 | 86 | printf("\n"); 87 | return 0; 88 | } 89 | 90 | void fmp4_writer_test(int w, int h, const char* inflv, const char* outmp4) 91 | { 92 | int r, type; 93 | size_t taglen; 94 | uint32_t timestamp; 95 | FILE* fp = fopen(outmp4, "wb+"); 96 | void* flv = flv_reader_create(inflv); 97 | fmp4_writer_t* mov = fmp4_writer_create(mov_file_buffer(), fp, 0); 98 | 99 | s_width = w; 100 | s_height = h; 101 | while (1 == flv_reader_read(flv, &type, ×tamp, &taglen, s_buffer, sizeof(s_buffer))) 102 | { 103 | r = flv_parser_tag(type, s_buffer, taglen, timestamp, onFLV, mov); 104 | assert(r >= 0); 105 | } 106 | 107 | fmp4_writer_destroy(mov); 108 | flv_reader_destroy(flv); 109 | fclose(fp); 110 | } 111 | -------------------------------------------------------------------------------- /libmov/test/fmp4-writer-test2.cpp: -------------------------------------------------------------------------------- 1 | #include "fmp4-writer.h" 2 | #include "mov-format.h" 3 | #include "mov-reader.h" 4 | #include "mpeg4-aac.h" 5 | #include "mov-file-buffer.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static uint8_t s_buffer[2 * 1024 * 1024]; 12 | static int s_audio_track = -1; 13 | static int s_video_track = -1; 14 | 15 | static void mov_onread(void* param, uint32_t track, const void* buffer, size_t bytes, int64_t pts, int64_t dts, int flags) 16 | { 17 | fmp4_writer_t* fmp4 = (fmp4_writer_t*)param; 18 | int r = fmp4_writer_write(fmp4, track-1, buffer, bytes, pts, dts, flags); 19 | assert(0 == r); 20 | } 21 | 22 | static void mov_video_info(void* param, uint32_t track, uint8_t object, int width, int height, const void* extra, size_t bytes) 23 | { 24 | fmp4_writer_t* fmp4 = (fmp4_writer_t*)param; 25 | s_video_track = fmp4_writer_add_video(fmp4, object, width, height, extra, bytes); 26 | } 27 | 28 | static void mov_audio_info(void* param, uint32_t track, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void* extra, size_t bytes) 29 | { 30 | fmp4_writer_t* fmp4 = (fmp4_writer_t*)param; 31 | s_audio_track = fmp4_writer_add_audio(fmp4, object, channel_count, bit_per_sample, sample_rate, extra, bytes); 32 | } 33 | 34 | void fmp4_writer_test2(const char* mp4, const char* outmp4) 35 | { 36 | struct mov_file_cache_t file, wfile; 37 | memset(&file, 0, sizeof(file)); 38 | memset(&wfile, 0, sizeof(wfile)); 39 | file.fp = fopen(mp4, "rb"); 40 | wfile.fp = fopen(outmp4, "wb"); 41 | mov_reader_t* mov = mov_reader_create(mov_file_cache_buffer(), &file); 42 | fmp4_writer_t* fmp4 = fmp4_writer_create(mov_file_cache_buffer(), &wfile, MOV_FLAG_SEGMENT); 43 | 44 | struct mov_reader_trackinfo_t info = { mov_video_info, mov_audio_info }; 45 | mov_reader_getinfo(mov, &info, fmp4); 46 | fmp4_writer_init_segment(fmp4); 47 | 48 | while (mov_reader_read(mov, s_buffer, sizeof(s_buffer), mov_onread, fmp4) > 0) 49 | { 50 | } 51 | 52 | fmp4_writer_destroy(fmp4); 53 | mov_reader_destroy(mov); 54 | fclose(wfile.fp); 55 | fclose(wfile.fp); 56 | } 57 | -------------------------------------------------------------------------------- /libmov/test/mov-2-flv.cpp: -------------------------------------------------------------------------------- 1 | #include "mov-reader.h" 2 | #include "mov-format.h" 3 | #include "mpeg4-avc.h" 4 | #include "mpeg4-aac.h" 5 | #include "flv-proto.h" 6 | #include "flv-header.h" 7 | #include "flv-writer.h" 8 | #include "flv-muxer.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | extern "C" const struct mov_buffer_t* mov_file_buffer(void); 15 | 16 | static uint8_t s_packet[2 * 1024 * 1024]; 17 | static uint8_t s_buffer[4 * 1024 * 1024]; 18 | static uint32_t s_aac_track; 19 | static uint32_t s_avc_track; 20 | static uint32_t s_txt_track; 21 | static uint8_t s_video_type; 22 | static struct flv_audio_tag_header_t s_audio_tag; 23 | static struct flv_video_tag_header_t s_video_tag; 24 | 25 | static int iskeyframe(const uint8_t* data, size_t bytes) 26 | { 27 | size_t nalu_length = 4; 28 | uint32_t len; 29 | 30 | while (bytes >= nalu_length + 1) 31 | { 32 | len = 0; 33 | for (size_t i = 0; i < nalu_length; i++) 34 | len = (len << 8) | data[i]; 35 | 36 | if (len + nalu_length > bytes) 37 | return 0; // invalid 38 | 39 | uint8_t nalu_type = (FLV_VIDEO_H264 == s_video_type) ? (data[nalu_length] & 0x1f) : ((data[nalu_length] >> 1) & 0x3f); 40 | if ((FLV_VIDEO_H264 == s_video_type) ? (5 == nalu_type) : (16 <= nalu_type && nalu_type <= 23)) 41 | return 1; 42 | 43 | bytes -= nalu_length + len; 44 | data += nalu_length + len; 45 | } 46 | 47 | return 0; 48 | } 49 | 50 | static void onread(void* flv, uint32_t track, const void* buffer, size_t bytes, int64_t pts, int64_t dts, int flags) 51 | { 52 | if (s_avc_track == track) 53 | { 54 | int keyframe = (FLV_VIDEO_H264 == s_video_type || FLV_VIDEO_H265 == s_video_type) ? iskeyframe((const uint8_t*)buffer, bytes) : flags; 55 | printf("[V] pts: %08lld, dts: %08lld%s\n", pts, dts, keyframe ? " [I]" : ""); 56 | s_video_tag.keyframe = (keyframe ? 1 : 2); 57 | s_video_tag.avpacket = FLV_AVPACKET; 58 | s_video_tag.cts = (int32_t)(pts - dts); 59 | flv_video_tag_header_write(&s_video_tag, s_packet, sizeof(s_packet)); 60 | memcpy(s_packet + 5, buffer, bytes); 61 | flv_writer_input(flv, FLV_TYPE_VIDEO, s_packet, bytes + 5, (uint32_t)dts); 62 | } 63 | else if (s_aac_track == track) 64 | { 65 | printf("[A] pts: %08lld, dts: %08lld\n", pts, dts); 66 | s_audio_tag.avpacket = FLV_AVPACKET; 67 | int m = flv_audio_tag_header_write(&s_audio_tag, s_packet, sizeof(s_packet)); 68 | memcpy(s_packet + m, buffer, bytes); // AAC exclude ADTS 69 | flv_writer_input(flv, FLV_TYPE_AUDIO, s_packet, bytes + m, (uint32_t)dts); 70 | } 71 | else 72 | { 73 | assert(0); 74 | } 75 | } 76 | 77 | static void mov_video_info(void* flv, uint32_t track, uint8_t object, int /*width*/, int /*height*/, const void* extra, size_t bytes) 78 | { 79 | s_avc_track = track; 80 | assert(MOV_OBJECT_H264 == object || MOV_OBJECT_HEVC == object || MOV_OBJECT_AV1 == object || MOV_OBJECT_VP9 == object); 81 | s_video_type = MOV_OBJECT_H264 == object ? FLV_VIDEO_H264 : (MOV_OBJECT_HEVC == object ? FLV_VIDEO_H265 : FLV_VIDEO_AV1); 82 | s_video_tag.codecid = s_video_type; 83 | s_video_tag.keyframe = 1; 84 | s_video_tag.avpacket = FLV_SEQUENCE_HEADER; 85 | s_video_tag.cts = 0; 86 | flv_video_tag_header_write(&s_video_tag, s_packet, sizeof(s_packet)); 87 | memcpy(s_packet + 5, extra, bytes); 88 | flv_writer_input(flv, FLV_TYPE_VIDEO, s_packet, bytes + 5, 0); 89 | } 90 | 91 | static void mov_audio_info(void* flv, uint32_t track, uint8_t object, int channel_count, int /*bit_per_sample*/, int sample_rate, const void* extra, size_t bytes) 92 | { 93 | if (MOV_OBJECT_AAC == object || MOV_OBJECT_OPUS == object) 94 | { 95 | s_aac_track = track; 96 | s_audio_tag.codecid = MOV_OBJECT_AAC == object ? FLV_AUDIO_AAC : FLV_AUDIO_OPUS; 97 | s_audio_tag.rate = 3; // 44k-SoundRate 98 | s_audio_tag.bits = 1; // 16-bit samples 99 | s_audio_tag.channels = 1; // Stereo sound 100 | s_audio_tag.avpacket = FLV_SEQUENCE_HEADER; 101 | int m = flv_audio_tag_header_write(&s_audio_tag, s_packet, sizeof(s_packet)); 102 | 103 | memcpy(s_packet + m, extra, bytes); 104 | flv_writer_input(flv, FLV_TYPE_AUDIO, s_packet, bytes + m, 0); 105 | } 106 | } 107 | 108 | static int mov_meta_info(void* flv, int type, const void* data, size_t bytes, uint32_t timestamp) 109 | { 110 | return flv_writer_input(flv, FLV_TYPE_SCRIPT, data, bytes, 0); 111 | } 112 | 113 | void mov_2_flv_test(const char* mp4) 114 | { 115 | snprintf((char*)s_packet, sizeof(s_packet), "%s.flv", mp4); 116 | 117 | FILE* fp = fopen(mp4, "rb"); 118 | mov_reader_t* mov = mov_reader_create(mov_file_buffer(), fp); 119 | void* flv = flv_writer_create((char*)s_packet); 120 | 121 | //flv_muxer_t* muxer = flv_muxer_create(mov_meta_info, flv); 122 | //memset(&metadata, 0, sizeof(metadata)); 123 | //metadata.videocodecid = FLV_VIDEO_H264; 124 | //metadata.videodatarate = 2000; 125 | //metadata.framerate = 25.0; 126 | //metadata.width = 1280; 127 | //metadata.height = 720; 128 | //flv_muxer_metadata(muxer, &metadata); 129 | //flv_muxer_destroy(muxer); 130 | 131 | struct mov_reader_trackinfo_t info = { mov_video_info, mov_audio_info }; 132 | mov_reader_getinfo(mov, &info, flv); 133 | 134 | while (mov_reader_read(mov, s_buffer, sizeof(s_buffer), onread, flv) > 0) 135 | { 136 | } 137 | 138 | mov_reader_destroy(mov); 139 | flv_writer_destroy(flv); 140 | fclose(fp); 141 | } 142 | -------------------------------------------------------------------------------- /libmov/test/mov-file-buffer.c: -------------------------------------------------------------------------------- 1 | #include "mov-buffer.h" 2 | #include "mov-file-buffer.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined(_WIN32) || defined(_WIN64) 9 | #define fseek64 _fseeki64 10 | #define ftell64 _ftelli64 11 | #elif defined(__ANDROID__) 12 | #define fseek64 fseek 13 | #define ftell64 ftell 14 | #elif defined(OS_LINUX) 15 | #define fseek64 fseeko64 16 | #define ftell64 ftello64 17 | #else 18 | #define fseek64 fseek 19 | #define ftell64 ftell 20 | #endif 21 | 22 | static int mov_file_read(void* fp, void* data, uint64_t bytes) 23 | { 24 | if (bytes == fread(data, 1, bytes, (FILE*)fp)) 25 | return 0; 26 | return 0 != ferror((FILE*)fp) ? ferror((FILE*)fp) : -1 /*EOF*/; 27 | } 28 | 29 | static int mov_file_write(void* fp, const void* data, uint64_t bytes) 30 | { 31 | return bytes == fwrite(data, 1, bytes, (FILE*)fp) ? 0 : ferror((FILE*)fp); 32 | } 33 | 34 | static int mov_file_seek(void* fp, int64_t offset) 35 | { 36 | return fseek64((FILE*)fp, offset, offset >= 0 ? SEEK_SET : SEEK_END); 37 | } 38 | 39 | static int64_t mov_file_tell(void* fp) 40 | { 41 | return ftell64((FILE*)fp); 42 | } 43 | 44 | static int mov_file_cache_read(void* fp, void* data, uint64_t bytes) 45 | { 46 | uint8_t* p = (uint8_t*)data; 47 | struct mov_file_cache_t* file = (struct mov_file_cache_t*)fp; 48 | while (bytes > 0) 49 | { 50 | assert(file->off <= file->len); 51 | if (file->off >= file->len) 52 | { 53 | if (bytes >= sizeof(file->ptr)) 54 | { 55 | if (bytes == fread(p, 1, bytes, file->fp)) 56 | { 57 | file->tell += bytes; 58 | return 0; 59 | } 60 | return 0 != ferror(file->fp) ? ferror(file->fp) : -1 /*EOF*/; 61 | } 62 | else 63 | { 64 | file->off = 0; 65 | file->len = (unsigned int)fread(file->ptr, 1, sizeof(file->ptr), file->fp); 66 | if (file->len < 1) 67 | return 0 != ferror(file->fp) ? ferror(file->fp) : -1 /*EOF*/; 68 | } 69 | } 70 | 71 | if (file->off < file->len) 72 | { 73 | unsigned int n = file->len - file->off; 74 | n = n > bytes ? (unsigned int)bytes : n; 75 | memcpy(p, file->ptr + file->off, n); 76 | file->tell += n; 77 | file->off += n; 78 | bytes -= n; 79 | p += n; 80 | } 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | static int mov_file_cache_write(void* fp, const void* data, uint64_t bytes) 87 | { 88 | struct mov_file_cache_t* file = (struct mov_file_cache_t*)fp; 89 | 90 | file->tell += bytes; 91 | 92 | if (file->off + bytes < sizeof(file->ptr)) 93 | { 94 | memcpy(file->ptr + file->off, data, bytes); 95 | file->off += (unsigned int)bytes; 96 | return 0; 97 | } 98 | 99 | // write buffer 100 | if (file->off > 0) 101 | { 102 | if (file->off != fwrite(file->ptr, 1, file->off, file->fp)) 103 | return ferror(file->fp); 104 | file->off = 0; // clear buffer 105 | } 106 | 107 | // write data; 108 | return bytes == fwrite(data, 1, bytes, file->fp) ? 0 : ferror(file->fp); 109 | } 110 | 111 | static int mov_file_cache_seek(void* fp, int64_t offset) 112 | { 113 | int r; 114 | struct mov_file_cache_t* file = (struct mov_file_cache_t*)fp; 115 | if (offset != file->tell) 116 | { 117 | if (file->off > file->len) 118 | { 119 | // write bufferred data 120 | if(file->off != fwrite(file->ptr, 1, file->off, file->fp)) 121 | return ferror(file->fp); 122 | } 123 | 124 | file->off = file->len = 0; 125 | r = fseek64(file->fp, offset, offset >= 0 ? SEEK_SET : SEEK_END); 126 | file->tell = ftell64(file->fp); 127 | return r; 128 | } 129 | return 0; 130 | } 131 | 132 | static int64_t mov_file_cache_tell(void* fp) 133 | { 134 | struct mov_file_cache_t* file = (struct mov_file_cache_t*)fp; 135 | if (ftell64(file->fp) != (int64_t)(file->tell + (uint64_t)(int)(file->len - file->off))) 136 | return -1; 137 | return (int64_t)file->tell; 138 | //return ftell64(file->fp); 139 | } 140 | 141 | const struct mov_buffer_t* mov_file_buffer(void) 142 | { 143 | static struct mov_buffer_t s_io = { 144 | mov_file_read, 145 | mov_file_write, 146 | mov_file_seek, 147 | mov_file_tell, 148 | }; 149 | return &s_io; 150 | } 151 | 152 | const struct mov_buffer_t* mov_file_cache_buffer(void) 153 | { 154 | static struct mov_buffer_t s_io = { 155 | mov_file_cache_read, 156 | mov_file_cache_write, 157 | mov_file_cache_seek, 158 | mov_file_cache_tell, 159 | }; 160 | return &s_io; 161 | } 162 | -------------------------------------------------------------------------------- /libmov/test/mov-file-buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _mov_file_buffer_h_ 2 | #define _mov_file_buffer_h_ 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | struct mov_file_cache_t 12 | { 13 | FILE* fp; 14 | uint8_t ptr[800]; 15 | unsigned int len; 16 | unsigned int off; 17 | uint64_t tell; 18 | }; 19 | 20 | const struct mov_buffer_t* mov_file_cache_buffer(void); 21 | 22 | const struct mov_buffer_t* mov_file_buffer(void); 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | #endif /* !_mov_file_buffer_h_ */ 28 | -------------------------------------------------------------------------------- /libmov/test/mov-reader-test.cpp: -------------------------------------------------------------------------------- 1 | #include "mov-reader.h" 2 | #include "mov-format.h" 3 | #include "mpeg4-hevc.h" 4 | #include "mpeg4-avc.h" 5 | #include "mpeg4-aac.h" 6 | #include "opus-head.h" 7 | #include "webm-vpx.h" 8 | #include "aom-av1.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "mov-file-buffer.h" 14 | #include 15 | #include 16 | 17 | #define USE_NEW_MOV_READ_API 1 18 | 19 | static uint8_t s_packet[2 * 1024 * 1024]; 20 | static uint8_t s_buffer[4 * 1024 * 1024]; 21 | static FILE *s_vfp, *s_afp; 22 | static struct mpeg4_hevc_t s_hevc; 23 | static struct mpeg4_avc_t s_avc; 24 | static struct mpeg4_aac_t s_aac; 25 | static struct webm_vpx_t s_vpx; 26 | static struct opus_head_t s_opus; 27 | static struct aom_av1_t s_av1; 28 | static std::map s_tracks; 29 | 30 | #if defined(USE_NEW_MOV_READ_API) 31 | struct mov_packet_test_t 32 | { 33 | int flags; 34 | int64_t pts; 35 | int64_t dts; 36 | uint32_t track; 37 | 38 | void* ptr; 39 | size_t bytes; 40 | }; 41 | static void* onalloc(void* param, uint32_t track, size_t bytes, int64_t pts, int64_t dts, int flags) 42 | { 43 | // emulate allocation 44 | struct mov_packet_test_t* pkt = (struct mov_packet_test_t*)param; 45 | if (pkt->bytes < bytes) 46 | return NULL; 47 | pkt->flags = flags; 48 | pkt->pts = pts; 49 | pkt->dts = dts; 50 | pkt->track = track; 51 | pkt->bytes = bytes; 52 | return pkt->ptr; 53 | } 54 | #endif 55 | 56 | inline const char* ftimestamp(uint32_t t, char* buf) 57 | { 58 | sprintf(buf, "%02u:%02u:%02u.%03u", t / 3600000, (t / 60000) % 60, (t / 1000) % 60, t % 1000); 59 | return buf; 60 | } 61 | 62 | static void onread(void* flv, uint32_t track, const void* buffer, size_t bytes, int64_t pts, int64_t dts, int flags) 63 | { 64 | static char s_pts[64], s_dts[64]; 65 | static int64_t v_pts, v_dts; 66 | static int64_t a_pts, a_dts; 67 | static int64_t x_pts, x_dts; 68 | 69 | auto it = s_tracks.find(track); 70 | if (it == s_tracks.end()) 71 | { 72 | assert(0); 73 | return; 74 | } 75 | 76 | if (it->second == "H264") 77 | { 78 | printf("[H264] pts: %s, dts: %s, diff: %03d/%03d, bytes: %u%s\n", ftimestamp(pts, s_pts), ftimestamp(dts, s_dts), (int)(pts - v_pts), (int)(dts - v_dts), (unsigned int)bytes, flags ? " [I]" : ""); 79 | v_pts = pts; 80 | v_dts = dts; 81 | 82 | assert(h264_is_new_access_unit((const uint8_t*)buffer + 4, bytes - 4)); 83 | int n = h264_mp4toannexb(&s_avc, buffer, bytes, s_packet, sizeof(s_packet)); 84 | fwrite(s_packet, 1, n, s_vfp); 85 | } 86 | else if (it->second == "H265") 87 | { 88 | uint8_t nalu_type = (((const uint8_t*)buffer)[4] >> 1) & 0x3F; 89 | uint8_t irap = 16 <= nalu_type && nalu_type <= 23; 90 | 91 | printf("[H265] pts: %s, dts: %s, diff: %03d/%03d, bytes: %u%s,%d\n", ftimestamp(pts, s_pts), ftimestamp(dts, s_dts), (int)(pts - v_pts), (int)(dts - v_dts), (unsigned int)bytes, flags ? " [I]" : "", (unsigned int)nalu_type); 92 | v_pts = pts; 93 | v_dts = dts; 94 | 95 | assert(h265_is_new_access_unit((const uint8_t*)buffer+4, bytes-4)); 96 | int n = h265_mp4toannexb(&s_hevc, buffer, bytes, s_packet, sizeof(s_packet)); 97 | fwrite(s_packet, 1, n, s_vfp); 98 | } 99 | else if (it->second == "AV1") 100 | { 101 | printf("[AV1] pts: %s, dts: %s, diff: %03d/%03d, bytes: %u%s\n", ftimestamp(pts, s_pts), ftimestamp(dts, s_dts), (int)(pts - v_pts), (int)(dts - v_dts), (unsigned int)bytes, flags ? " [I]" : ""); 102 | v_pts = pts; 103 | v_dts = dts; 104 | 105 | //int n = aom_av1_codec_configuration_record_save(&s_av1, s_packet, sizeof(s_packet)); 106 | //fwrite(s_packet, 1, n, s_vfp); 107 | } 108 | else if (it->second == "VPX") 109 | { 110 | printf("[VPX] pts: %s, dts: %s, diff: %03d/%03d, bytes: %u%s\n", ftimestamp(pts, s_pts), ftimestamp(dts, s_dts), (int)(pts - v_pts), (int)(dts - v_dts), (unsigned int)bytes, flags ? " [I]" : ""); 111 | v_pts = pts; 112 | v_dts = dts; 113 | 114 | //int n = aom_av1_codec_configuration_record_save(&s_av1, s_packet, sizeof(s_packet)); 115 | //fwrite(s_packet, 1, n, s_vfp); 116 | } 117 | 118 | else if (it->second == "ACC") 119 | { 120 | printf("[AAC] pts: %s, dts: %s, diff: %03d/%03d, bytes: %u\n", ftimestamp(pts, s_pts), ftimestamp(dts, s_dts), (int)(pts - a_pts), (int)(dts - a_dts), (unsigned int)bytes); 121 | a_pts = pts; 122 | a_dts = dts; 123 | 124 | uint8_t adts[32]; 125 | int n = mpeg4_aac_adts_save(&s_aac, bytes, adts, sizeof(adts)); 126 | fwrite(adts, 1, n, s_afp); 127 | fwrite(buffer, 1, bytes, s_afp); 128 | } 129 | else if (it->second == "OPUS") 130 | { 131 | printf("[OPUS] pts: %s, dts: %s, diff: %03d/%03d, bytes: %u\n", ftimestamp(pts, s_pts), ftimestamp(dts, s_dts), (int)(pts - a_pts), (int)(dts - a_dts), (unsigned int)bytes); 132 | a_pts = pts; 133 | a_dts = dts; 134 | } 135 | else if (it->second == "MP3") 136 | { 137 | printf("[MP3] pts: %s, dts: %s, diff: %03d/%03d, bytes: %u\n", ftimestamp(pts, s_pts), ftimestamp(dts, s_dts), (int)(pts - a_pts), (int)(dts - a_dts), (unsigned int)bytes); 138 | a_pts = pts; 139 | a_dts = dts; 140 | fwrite(buffer, 1, bytes, s_afp); 141 | } 142 | else if (it->second == "G711") 143 | { 144 | static int64_t t_pts, t_dts; 145 | printf("[G711] pts: %s, dts: %s, diff: %03d/%03d, bytes: %u\n", ftimestamp(pts, s_pts), ftimestamp(dts, s_dts), (int)(pts - t_pts), (int)(dts - t_dts), (unsigned int)bytes); 146 | t_pts = pts; 147 | t_dts = dts; 148 | } 149 | else 150 | { 151 | printf("[%s] pts: %s, dts: %s, diff: %03d/%03d, bytes: %u\n", it->second.c_str(), ftimestamp(pts, s_pts), ftimestamp(dts, s_dts), (int)(pts - x_pts), (int)(dts - x_dts), (unsigned int)bytes); 152 | x_pts = pts; 153 | x_dts = dts; 154 | //assert(0); 155 | } 156 | } 157 | 158 | static void mov_video_info(void* /*param*/, uint32_t track, uint8_t object, int /*width*/, int /*height*/, const void* extra, size_t bytes) 159 | { 160 | if (MOV_OBJECT_H264 == object) 161 | { 162 | s_vfp = fopen("v.h264", "wb"); 163 | s_tracks[track] = "H264"; 164 | mpeg4_avc_decoder_configuration_record_load((const uint8_t*)extra, bytes, &s_avc); 165 | } 166 | else if (MOV_OBJECT_HEVC == object) 167 | { 168 | s_vfp = fopen("v.h265", "wb"); 169 | s_tracks[track] = "H265"; 170 | mpeg4_hevc_decoder_configuration_record_load((const uint8_t*)extra, bytes, &s_hevc); 171 | } 172 | else if (MOV_OBJECT_AV1 == object) 173 | { 174 | s_vfp = fopen("v.obus", "wb"); 175 | s_tracks[track] = "AV1"; 176 | aom_av1_codec_configuration_record_load((const uint8_t*)extra, bytes, &s_av1); 177 | } 178 | else if (MOV_OBJECT_VP9 == object) 179 | { 180 | s_vfp = fopen("v.vp9", "wb"); 181 | s_tracks[track] = "VP9"; 182 | webm_vpx_codec_configuration_record_load((const uint8_t*)extra, bytes, &s_vpx); 183 | } 184 | else 185 | { 186 | s_tracks[track] = "VIDEO"; 187 | } 188 | } 189 | 190 | static void mov_audio_info(void* /*param*/, uint32_t track, uint8_t object, int channel_count, int /*bit_per_sample*/, int sample_rate, const void* extra, size_t bytes) 191 | { 192 | if (MOV_OBJECT_AAC == object) 193 | { 194 | s_afp = fopen("a.aac", "wb"); 195 | s_tracks[track] = "AAC"; 196 | assert(bytes == mpeg4_aac_audio_specific_config_load((const uint8_t*)extra, bytes, &s_aac)); 197 | assert(channel_count == s_aac.channels); 198 | assert(MOV_OBJECT_AAC == object); 199 | s_aac.profile = MPEG4_AAC_LC; 200 | s_aac.channel_configuration = channel_count; 201 | s_aac.sampling_frequency_index = mpeg4_aac_audio_frequency_from(sample_rate); 202 | } 203 | else if (MOV_OBJECT_OPUS == object) 204 | { 205 | s_afp = fopen("a.opus", "wb"); 206 | s_tracks[track] = "OPUS"; 207 | assert(bytes == opus_head_load((const uint8_t*)extra, bytes, &s_opus)); 208 | assert(s_opus.input_sample_rate == 48000); 209 | } 210 | else if (MOV_OBJECT_MP3 == object || MOV_OBJECT_MP1A == object) 211 | { 212 | s_afp = fopen("a.mp3", "wb"); 213 | s_tracks[track] = "MP3"; 214 | } 215 | else if (MOV_OBJECT_G711a == object || MOV_OBJECT_G711u == object) 216 | { 217 | s_afp = fopen("a.raw", "wb"); 218 | s_tracks[track] = "G711"; 219 | } 220 | else 221 | { 222 | //s_aac_track = track; 223 | //s_aac.channel_configuration = channel_count; 224 | //s_aac.sampling_frequency_index = mpeg4_aac_audio_frequency_from(sample_rate); 225 | s_tracks[track] = "AUDIO"; 226 | } 227 | } 228 | 229 | static void mov_subtitle_info(void* /*param*/, uint32_t track, uint8_t object, const void* /*extra*/, size_t /*bytes*/) 230 | { 231 | s_tracks[track] = "SUBTITLE"; 232 | } 233 | 234 | void mov_reader_test(const char* mp4) 235 | { 236 | struct mov_file_cache_t file; 237 | memset(&file, 0, sizeof(file)); 238 | file.fp = fopen(mp4, "rb"); 239 | mov_reader_t* mov = mov_reader_create(mov_file_cache_buffer(), &file); 240 | uint64_t duration = mov_reader_getduration(mov); 241 | 242 | struct mov_reader_trackinfo_t info = { mov_video_info, mov_audio_info, mov_subtitle_info }; 243 | mov_reader_getinfo(mov, &info, NULL); 244 | 245 | #if !defined(USE_NEW_MOV_READ_API) 246 | while (mov_reader_read(mov, s_buffer, sizeof(s_buffer), onread, NULL) > 0) 247 | { 248 | } 249 | #else 250 | while (1) 251 | { 252 | struct mov_packet_test_t pkt; 253 | pkt.ptr = s_buffer; 254 | pkt.bytes = sizeof(s_buffer); 255 | int r = mov_reader_read2(mov, onalloc, &pkt); 256 | if (r <= 0) 257 | { 258 | // WARNNING: free(pkt.ptr) if alloc new buffer 259 | break; 260 | } 261 | onread(NULL, pkt.track, pkt.ptr, pkt.bytes, pkt.pts, pkt.dts, pkt.flags); 262 | } 263 | #endif 264 | 265 | duration /= 2; 266 | mov_reader_seek(mov, (int64_t*)&duration); 267 | 268 | mov_reader_destroy(mov); 269 | if(s_vfp) fclose(s_vfp); 270 | if(s_afp) fclose(s_afp); 271 | fclose(file.fp); 272 | } 273 | -------------------------------------------------------------------------------- /libmov/test/mov-writer-adts.cpp: -------------------------------------------------------------------------------- 1 | #include "mov-writer.h" 2 | #include "mov-format.h" 3 | #include "mpeg4-aac.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | extern "C" const struct mov_buffer_t* mov_file_buffer(void); 10 | 11 | static uint8_t s_buffer[2 * 1024 * 1024]; 12 | static uint8_t s_extra_data[64 * 1024]; 13 | 14 | struct mov_adts_test_t 15 | { 16 | mov_writer_t* mov; 17 | struct mpeg4_aac_t aac; 18 | 19 | int track; 20 | uint32_t pts, dts; 21 | const uint8_t* ptr; 22 | 23 | int vcl; 24 | }; 25 | 26 | static uint8_t* file_read(const char* file, long* size) 27 | { 28 | FILE* fp = fopen(file, "rb"); 29 | if (fp) 30 | { 31 | fseek(fp, 0, SEEK_END); 32 | *size = ftell(fp); 33 | fseek(fp, 0, SEEK_SET); 34 | 35 | uint8_t* ptr = (uint8_t*)malloc(*size); 36 | fread(ptr, 1, *size, fp); 37 | fclose(fp); 38 | 39 | return ptr; 40 | } 41 | 42 | return NULL; 43 | } 44 | 45 | static void adts_reader(struct mov_adts_test_t* ctx, const uint8_t* ptr, size_t bytes) 46 | { 47 | int64_t pts = 0; 48 | int64_t samples = 0; 49 | while(ptr && bytes > 7) 50 | { 51 | int n = mpeg4_aac_adts_frame_length(ptr, bytes); 52 | if (n < 0) 53 | break; 54 | 55 | if (n > bytes) 56 | break; 57 | 58 | if (-1 == ctx->track) 59 | { 60 | uint8_t asc[16]; 61 | assert(7 == mpeg4_aac_adts_load(ptr, bytes, &ctx->aac)); 62 | int len = mpeg4_aac_audio_specific_config_save(&ctx->aac, asc, sizeof(asc)); 63 | assert(len > 0 && len <= sizeof(asc)); 64 | ctx->track = mov_writer_add_audio(ctx->mov, MOV_OBJECT_AAC, ctx->aac.channels, 16, ctx->aac.sampling_frequency, asc, len); 65 | assert(ctx->track >= 0); 66 | assert(ctx->aac.sampling_frequency > 0); 67 | } 68 | 69 | assert(0 == mov_writer_write(ctx->mov, ctx->track, ptr + 7, n - 7, pts, pts, 0)); 70 | ptr += n; 71 | bytes -= n; 72 | samples += 1024; 73 | pts = samples * 1000 / ctx->aac.sampling_frequency; 74 | } 75 | } 76 | 77 | void mov_writer_adts_test(const char* file) 78 | { 79 | struct mov_adts_test_t ctx; 80 | memset(&ctx, 0, sizeof(ctx)); 81 | ctx.track = -1; 82 | 83 | long bytes = 0; 84 | uint8_t* ptr = file_read(file, &bytes); 85 | if (NULL == ptr) return; 86 | ctx.ptr = ptr; 87 | 88 | FILE* fp = fopen("adts.mp4", "wb+"); 89 | ctx.mov = mov_writer_create(mov_file_buffer(), fp, 0); 90 | adts_reader(&ctx, ptr, bytes); 91 | mov_writer_destroy(ctx.mov); 92 | 93 | fclose(fp); 94 | free(ptr); 95 | } 96 | -------------------------------------------------------------------------------- /libmov/test/mov-writer-audio.cpp: -------------------------------------------------------------------------------- 1 | #include "mov-writer.h" 2 | #include "mov-format.h" 3 | #include "mpeg4-aac.h" 4 | #include 5 | #include 6 | #include 7 | 8 | extern "C" const struct mov_buffer_t* mov_file_buffer(void); 9 | 10 | static uint8_t* file_read(const char* file, long* size) 11 | { 12 | FILE* fp = fopen(file, "rb"); 13 | if (fp) 14 | { 15 | fseek(fp, 0, SEEK_END); 16 | *size = ftell(fp); 17 | fseek(fp, 0, SEEK_SET); 18 | 19 | uint8_t* ptr = (uint8_t*)malloc(*size); 20 | fread(ptr, 1, *size, fp); 21 | fclose(fp); 22 | 23 | return ptr; 24 | } 25 | 26 | return NULL; 27 | } 28 | 29 | static void g711_read_frame(mov_writer_t* mov, const uint8_t* ptr, const uint8_t* end) 30 | { 31 | int track = -1; 32 | int64_t pts = 0; 33 | 34 | while (ptr < end) 35 | { 36 | if (-1 == track) 37 | { 38 | track = mov_writer_add_audio(mov, MOV_OBJECT_G711a, 1, 16, 8000, NULL, 0); 39 | if(-1 == track) continue; 40 | } 41 | 42 | int n = ptr + 320 < end ? 320 : end - ptr; 43 | mov_writer_write(mov, track, ptr, n, pts, pts, 0); 44 | pts += n / 8; // 8000Hz/8-bits/1-channel 45 | ptr += n; 46 | } 47 | } 48 | 49 | static void aac_read_frame(mov_writer_t* mov, const uint8_t* ptr, const uint8_t* end) 50 | { 51 | int rate = 1; 52 | int track = -1; 53 | int64_t pts = 0; 54 | uint64_t samples = 1024; // aac frame 55 | struct mpeg4_aac_t aac; 56 | uint8_t extra_data[64 * 1024]; 57 | 58 | while (ptr + 7 < end) 59 | { 60 | mpeg4_aac_adts_load(ptr, end - ptr, &aac); 61 | if (-1 == track) 62 | { 63 | int extra_data_size = mpeg4_aac_audio_specific_config_save(&aac, extra_data, sizeof(extra_data)); 64 | rate = mpeg4_aac_audio_frequency_to((enum mpeg4_aac_frequency)aac.sampling_frequency_index); 65 | track = mov_writer_add_audio(mov, MOV_OBJECT_AAC, aac.channel_configuration, 16, rate, extra_data, extra_data_size); 66 | if (-1 == track) continue; 67 | assert(rate != 0); 68 | } 69 | 70 | int framelen = mpeg4_aac_adts_frame_length(ptr, end - ptr); 71 | mov_writer_write(mov, track, ptr + 7, framelen - 7, pts, pts, 0); 72 | samples += 1024; 73 | pts = samples * 1000 / rate; 74 | ptr += framelen; 75 | } 76 | } 77 | 78 | void mov_writer_audio(const char* audio, int type, const char* mp4) 79 | { 80 | long bytes = 0; 81 | uint8_t* ptr = file_read(audio, &bytes); 82 | if (NULL == ptr) return; 83 | 84 | FILE* fp = fopen(mp4, "wb+"); 85 | mov_writer_t* mov = mov_writer_create(mov_file_buffer(), fp, MOV_FLAG_FASTSTART); 86 | switch (type) 87 | { 88 | case 1: 89 | aac_read_frame(mov, ptr, ptr + bytes); 90 | break; 91 | case 2: 92 | g711_read_frame(mov, ptr, ptr + bytes); 93 | break; 94 | default: 95 | assert(0); 96 | } 97 | mov_writer_destroy(mov); 98 | fclose(fp); 99 | free(ptr); 100 | } 101 | -------------------------------------------------------------------------------- /libmov/test/mov-writer-av1.cpp: -------------------------------------------------------------------------------- 1 | #include "mp4-writer.h" 2 | #include "mov-format.h" 3 | #include "aom-av1.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define MOV_WRITER_AV1_FMP4 0 10 | 11 | extern "C" const struct mov_buffer_t* mov_file_buffer(void); 12 | 13 | enum 14 | { 15 | OBU_SEQUENCE_HEADER = 1, 16 | OBU_TEMPORAL_DELIMITER = 2, 17 | OBU_FRAME = 6, 18 | }; 19 | 20 | static uint8_t s_buffer[2 * 1024 * 1024]; 21 | static uint8_t s_extra_data[64 * 1024]; 22 | 23 | struct mov_av1_test_t 24 | { 25 | mp4_writer_t* mov; 26 | struct aom_av1_t av1; 27 | 28 | int track; 29 | int width; 30 | int height; 31 | uint32_t pts, dts; 32 | uint8_t* ptr; 33 | int bytes; 34 | 35 | int vcl; 36 | int keyframe; 37 | }; 38 | 39 | static uint8_t* file_read(const char* file, long* size) 40 | { 41 | FILE* fp = fopen(file, "rb"); 42 | if (fp) 43 | { 44 | fseek(fp, 0, SEEK_END); 45 | *size = ftell(fp); 46 | fseek(fp, 0, SEEK_SET); 47 | 48 | uint8_t* ptr = (uint8_t*)malloc(*size); 49 | fread(ptr, 1, *size, fp); 50 | fclose(fp); 51 | 52 | return ptr; 53 | } 54 | 55 | return NULL; 56 | } 57 | 58 | static int av1_write(struct mov_av1_test_t* ctx, const void* data, int bytes) 59 | { 60 | if (ctx->track < 0) 61 | { 62 | int r = aom_av1_codec_configuration_record_init(&ctx->av1, data, bytes); 63 | if (ctx->av1.width < 1 || ctx->av1.height < 1) 64 | { 65 | //ctx->ptr = end; 66 | return -2; // waiting for sps/pps 67 | } 68 | 69 | int extra_data_size = aom_av1_codec_configuration_record_save(&ctx->av1, s_extra_data, sizeof(s_extra_data)); 70 | if (extra_data_size <= 0) 71 | { 72 | // invalid AVCC 73 | assert(0); 74 | return -1; 75 | } 76 | 77 | // TODO: waiting for key frame ??? 78 | ctx->track = mp4_writer_add_video(ctx->mov, MOV_OBJECT_AV1, ctx->width, ctx->height, s_extra_data, extra_data_size); 79 | if (ctx->track < 0) 80 | return -1; 81 | mp4_writer_init_segment(ctx->mov); 82 | } 83 | 84 | mp4_writer_write(ctx->mov, ctx->track, data, bytes, ctx->pts, ctx->pts, ctx->keyframe ? MOV_AV_FLAG_KEYFREAME : 0); 85 | ctx->pts += 40; 86 | ctx->dts += 40; 87 | return 0; 88 | } 89 | 90 | static int av1_handler(void* param, const uint8_t* obu, size_t bytes) 91 | { 92 | struct mov_av1_test_t* ctx = (struct mov_av1_test_t*)param; 93 | 94 | uint8_t obu_type = (obu[0] >> 3) & 0x0F; 95 | if (ctx->vcl > 0 && OBU_TEMPORAL_DELIMITER == obu_type) 96 | { 97 | int r = av1_write(ctx, ctx->ptr, ctx->bytes); 98 | if (-1 == r) 99 | return 0; // wait for more data 100 | 101 | ctx->bytes = 0; 102 | ctx->vcl = 0; 103 | ctx->keyframe = 0; 104 | } 105 | 106 | if (OBU_TEMPORAL_DELIMITER == obu_type) 107 | return 0; // ignore 108 | if (OBU_SEQUENCE_HEADER == obu_type) 109 | ctx->keyframe = 1; 110 | if (OBU_FRAME == obu_type) 111 | ++ctx->vcl; 112 | 113 | memcpy(ctx->ptr + ctx->bytes, obu, bytes); 114 | ctx->bytes += bytes; 115 | return 0; 116 | } 117 | 118 | void mov_writer_av1(const char* obu, int width, int height, const char* mp4) 119 | { 120 | struct mov_av1_test_t ctx; 121 | memset(&ctx, 0, sizeof(ctx)); 122 | ctx.track = -1; 123 | ctx.width = width; 124 | ctx.height = height; 125 | ctx.ptr = s_buffer; 126 | 127 | long bytes = 0; 128 | uint8_t* ptr = file_read(obu, &bytes); 129 | if (NULL == ptr) return; 130 | 131 | FILE* fp = fopen(mp4, "wb+"); 132 | ctx.mov = mp4_writer_create(MOV_WRITER_AV1_FMP4, mov_file_buffer(), fp, MOV_FLAG_FASTSTART | MOV_FLAG_SEGMENT); 133 | aom_av1_obu_split(ptr, bytes, av1_handler, &ctx); 134 | mp4_writer_destroy(ctx.mov); 135 | 136 | fclose(fp); 137 | free(ptr); 138 | } 139 | -------------------------------------------------------------------------------- /libmov/test/mov-writer-h264.cpp: -------------------------------------------------------------------------------- 1 | #include "mp4-writer.h" 2 | #include "mov-format.h" 3 | #include "mpeg4-avc.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define MOV_WRITER_H264_FMP4 0 10 | 11 | extern "C" const struct mov_buffer_t* mov_file_buffer(void); 12 | 13 | #define H264_NAL(v) (v & 0x1F) 14 | 15 | static uint8_t s_buffer[2 * 1024 * 1024]; 16 | static uint8_t s_extra_data[64 * 1024]; 17 | 18 | struct mov_h264_test_t 19 | { 20 | struct mp4_writer_t* mov; 21 | struct mpeg4_avc_t avc; 22 | 23 | int track; 24 | int width; 25 | int height; 26 | uint32_t pts, dts; 27 | const uint8_t* ptr; 28 | 29 | int vcl; 30 | }; 31 | 32 | static uint8_t* file_read(const char* file, long* size) 33 | { 34 | FILE* fp = fopen(file, "rb"); 35 | if (fp) 36 | { 37 | fseek(fp, 0, SEEK_END); 38 | *size = ftell(fp); 39 | fseek(fp, 0, SEEK_SET); 40 | 41 | uint8_t* ptr = (uint8_t*)malloc(*size); 42 | fread(ptr, 1, *size, fp); 43 | fclose(fp); 44 | 45 | return ptr; 46 | } 47 | 48 | return NULL; 49 | } 50 | 51 | static int h264_write(struct mov_h264_test_t* ctx, const void* data, int bytes) 52 | { 53 | int vcl = 0; 54 | int update = 0; 55 | int n = h264_annexbtomp4(&ctx->avc, data, bytes, s_buffer, sizeof(s_buffer), &vcl, &update); 56 | 57 | if (ctx->track < 0) 58 | { 59 | if (ctx->avc.nb_sps < 1 || ctx->avc.nb_pps < 1) 60 | { 61 | //ctx->ptr = end; 62 | return -2; // waiting for sps/pps 63 | } 64 | 65 | int extra_data_size = mpeg4_avc_decoder_configuration_record_save(&ctx->avc, s_extra_data, sizeof(s_extra_data)); 66 | if (extra_data_size <= 0) 67 | { 68 | // invalid AVCC 69 | assert(0); 70 | return -1; 71 | } 72 | 73 | // TODO: waiting for key frame ??? 74 | ctx->track = mp4_writer_add_video(ctx->mov, MOV_OBJECT_H264, ctx->width, ctx->height, s_extra_data, extra_data_size); 75 | if (ctx->track < 0) 76 | return -1; 77 | mp4_writer_init_segment(ctx->mov); 78 | } 79 | 80 | mp4_writer_write(ctx->mov, ctx->track, s_buffer, n, ctx->pts, ctx->pts, 1 == vcl ? MOV_AV_FLAG_KEYFREAME : 0); 81 | ctx->pts += 40; 82 | ctx->dts += 40; 83 | return 0; 84 | } 85 | 86 | static void h264_handler(void* param, const uint8_t* nalu, size_t bytes) 87 | { 88 | struct mov_h264_test_t* ctx = (struct mov_h264_test_t*)param; 89 | assert(ctx->ptr < nalu); 90 | 91 | const uint8_t* ptr = nalu - 3; 92 | // const uint8_t* end = (const uint8_t*)nalu + bytes; 93 | uint8_t nalutype = nalu[0] & 0x1f; 94 | if (ctx->vcl > 0 && h264_is_new_access_unit((const uint8_t*)nalu, bytes)) 95 | { 96 | int r = h264_write(ctx, ctx->ptr, ptr - ctx->ptr); 97 | if (-1 == r) 98 | return; // wait for more data 99 | 100 | ctx->ptr = ptr; 101 | ctx->vcl = 0; 102 | } 103 | 104 | if (1 <= nalutype && nalutype <= 5) 105 | ++ctx->vcl; 106 | } 107 | 108 | void mov_writer_h264(const char* h264, int width, int height, const char* mp4) 109 | { 110 | struct mov_h264_test_t ctx; 111 | memset(&ctx, 0, sizeof(ctx)); 112 | ctx.track = -1; 113 | ctx.width = width; 114 | ctx.height = height; 115 | 116 | long bytes = 0; 117 | uint8_t* ptr = file_read(h264, &bytes); 118 | if (NULL == ptr) return; 119 | ctx.ptr = ptr; 120 | 121 | FILE* fp = fopen(mp4, "wb+"); 122 | ctx.mov = mp4_writer_create(MOV_WRITER_H264_FMP4, mov_file_buffer(), fp, MOV_FLAG_FASTSTART | MOV_FLAG_SEGMENT); 123 | mpeg4_h264_annexb_nalu(ptr, bytes, h264_handler, &ctx); 124 | mp4_writer_destroy(ctx.mov); 125 | 126 | fclose(fp); 127 | free(ptr); 128 | } 129 | -------------------------------------------------------------------------------- /libmov/test/mov-writer-h265.cpp: -------------------------------------------------------------------------------- 1 | #include "mp4-writer.h" 2 | #include "mov-format.h" 3 | #include "mpeg4-hevc.h" 4 | #include "mpeg4-avc.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define MOV_WRITER_H265_FMP4 0 11 | 12 | extern "C" const struct mov_buffer_t* mov_file_buffer(void); 13 | #define H265_NAL(v) ((v >> 1) & 0x3F) 14 | 15 | static uint8_t s_buffer[2 * 1024 * 1024]; 16 | static uint8_t s_extra_data[64 * 1024]; 17 | 18 | struct mov_h265_test_t 19 | { 20 | mp4_writer_t* mov; 21 | struct mpeg4_hevc_t hevc; 22 | 23 | int track; 24 | int width; 25 | int height; 26 | uint32_t pts, dts; 27 | const uint8_t* ptr; 28 | 29 | uint8_t buf[1024 * 64]; 30 | int bytes; 31 | 32 | int vcl; 33 | }; 34 | 35 | static uint8_t* file_read(const char* file, long* size) 36 | { 37 | FILE* fp = fopen(file, "rb"); 38 | if (fp) 39 | { 40 | fseek(fp, 0, SEEK_END); 41 | *size = ftell(fp); 42 | fseek(fp, 0, SEEK_SET); 43 | 44 | uint8_t* ptr = (uint8_t*)malloc(*size); 45 | fread(ptr, 1, *size, fp); 46 | fclose(fp); 47 | 48 | return ptr; 49 | } 50 | 51 | return NULL; 52 | } 53 | 54 | static int h265_write(struct mov_h265_test_t* ctx, const void* data, int bytes) 55 | { 56 | int vcl = 0; 57 | int update = 0; 58 | int n = h265_annexbtomp4(&ctx->hevc, data, bytes, s_buffer, sizeof(s_buffer), &vcl, &update); 59 | 60 | if (ctx->track < 0) 61 | { 62 | if (ctx->hevc.numOfArrays < 1) 63 | { 64 | //ctx->ptr = end; 65 | return -2; // waiting for vps/sps/pps 66 | } 67 | 68 | int extra_data_size = mpeg4_hevc_decoder_configuration_record_save(&ctx->hevc, s_extra_data, sizeof(s_extra_data)); 69 | if (extra_data_size <= 0) 70 | { 71 | // invalid HVCC 72 | assert(0); 73 | return -1; 74 | } 75 | 76 | // TODO: waiting for key frame ??? 77 | ctx->track = mp4_writer_add_video(ctx->mov, MOV_OBJECT_HEVC, ctx->width, ctx->height, s_extra_data, extra_data_size); 78 | if (ctx->track < 0) 79 | return -1; 80 | mp4_writer_init_segment(ctx->mov); 81 | } 82 | 83 | mp4_writer_write(ctx->mov, ctx->track, s_buffer, n, ctx->pts, ctx->pts, 1 == vcl ? MOV_AV_FLAG_KEYFREAME : 0); 84 | ctx->pts += 40; 85 | ctx->dts += 40; 86 | return 0; 87 | } 88 | 89 | static void h265_handler(void* param, const uint8_t* nalu, size_t bytes) 90 | { 91 | static int i = 0; 92 | static int j = 0; 93 | static uint8_t startcode[] = {0x00, 0x00, 0x00, 0x01}; 94 | 95 | struct mov_h265_test_t* ctx = (struct mov_h265_test_t*)param; 96 | assert(ctx->ptr < nalu); 97 | 98 | const uint8_t* ptr = nalu - 3; 99 | // const uint8_t* end = (const uint8_t*)nalu + bytes; 100 | uint8_t nalutype = (nalu[0] >> 1) & 0x3f; 101 | if (ctx->vcl > 0 && h265_is_new_access_unit((const uint8_t*)nalu, bytes)) 102 | { 103 | //int r = h265_write(ctx, ctx->ptr, ptr - ctx->ptr); 104 | int r = h265_write(ctx, ctx->buf, ctx->bytes); 105 | if (-1 == r) 106 | return; // wait for more data 107 | 108 | if ((j++) % 25 == 0) 109 | i = (i + 1) % ctx->vcl; 110 | ctx->bytes = 0; 111 | printf("\n"); 112 | 113 | ctx->ptr = ptr; 114 | ctx->vcl = 0; 115 | } 116 | 117 | if (nalutype <= 31) 118 | { 119 | ++ctx->vcl; 120 | 121 | if (1 == ctx->vcl || ctx->vcl == i) 122 | { 123 | printf("ctx->vcl: %d ", ctx->vcl); 124 | memcpy(ctx->buf + ctx->bytes, startcode, sizeof(startcode)); 125 | ctx->bytes += sizeof(startcode); 126 | memcpy(ctx->buf + ctx->bytes, nalu, bytes); 127 | ctx->bytes += bytes; 128 | } 129 | } 130 | else 131 | { 132 | memcpy(ctx->buf + ctx->bytes, startcode, sizeof(startcode)); 133 | ctx->bytes += sizeof(startcode); 134 | memcpy(ctx->buf + ctx->bytes, nalu, bytes); 135 | ctx->bytes += bytes; 136 | } 137 | } 138 | 139 | void mov_writer_h265(const char* h265, int width, int height, const char* mp4) 140 | { 141 | struct mov_h265_test_t ctx; 142 | memset(&ctx, 0, sizeof(ctx)); 143 | ctx.track = -1; 144 | ctx.width = width; 145 | ctx.height = height; 146 | ctx.bytes = 0; 147 | 148 | long bytes = 0; 149 | uint8_t* ptr = file_read(h265, &bytes); 150 | if (NULL == ptr) return; 151 | ctx.ptr = ptr; 152 | 153 | FILE* fp = fopen(mp4, "wb+"); 154 | ctx.mov = mp4_writer_create(MOV_WRITER_H265_FMP4, mov_file_buffer(), fp, MOV_FLAG_FASTSTART | MOV_FLAG_SEGMENT); 155 | mpeg4_h264_annexb_nalu(ptr, bytes, h265_handler, &ctx); 156 | mp4_writer_destroy(ctx.mov); 157 | 158 | fclose(fp); 159 | free(ptr); 160 | } 161 | -------------------------------------------------------------------------------- /libmov/test/mov-writer-subtitle.cpp: -------------------------------------------------------------------------------- 1 | #include "mov-writer.h" 2 | #include "mov-format.h" 3 | #include "mov-reader.h" 4 | #include "mpeg4-aac.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern "C" const struct mov_buffer_t* mov_file_buffer(void); 11 | 12 | static uint8_t s_buffer[2 * 1024 * 1024]; 13 | static int s_audio_track = -1; 14 | static int s_video_track = -1; 15 | static int s_pts_last = 0; 16 | 17 | static void mov_onread(void* param, uint32_t track, const void* buffer, size_t bytes, int64_t pts, int64_t dts, int flags) 18 | { 19 | s_pts_last = pts; 20 | mov_writer_t* mov = (mov_writer_t*)param; 21 | int r = mov_writer_write(mov, track - 1, buffer, bytes, pts, dts, flags); 22 | assert(0 == r); 23 | } 24 | 25 | static void mov_video_info(void* param, uint32_t track, uint8_t object, int width, int height, const void* extra, size_t bytes) 26 | { 27 | mov_writer_t* mov = (mov_writer_t*)param; 28 | s_video_track = mov_writer_add_video(mov, object, width, height, extra, bytes); 29 | } 30 | 31 | static void mov_audio_info(void* param, uint32_t track, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void* extra, size_t bytes) 32 | { 33 | mov_writer_t* mov = (mov_writer_t*)param; 34 | s_audio_track = mov_writer_add_audio(mov, object, channel_count, bit_per_sample, sample_rate, extra, bytes); 35 | } 36 | 37 | void mov_writer_subtitle(const char* mp4, const char* outmp4) 38 | { 39 | char sbuf[128]; 40 | static const char* s_subtitles[] = { 41 | "line 1", 42 | "message 1", 43 | "line 2", 44 | "message 2", 45 | }; 46 | 47 | FILE* rfp = fopen(mp4, "rb"); 48 | FILE* wfp = fopen(outmp4, "wb"); 49 | mov_reader_t* rmov = mov_reader_create(mov_file_buffer(), rfp); 50 | mov_writer_t* wmov = mov_writer_create(mov_file_buffer(), wfp, 0); 51 | 52 | struct mov_reader_trackinfo_t info = { mov_video_info, mov_audio_info }; 53 | mov_reader_getinfo(rmov, &info, wmov); 54 | 55 | int i = 0; 56 | int track = mov_writer_add_subtitle(wmov, MOV_OBJECT_TEXT, NULL, 0); 57 | 58 | while (mov_reader_read(rmov, s_buffer, sizeof(s_buffer), mov_onread, wmov) > 0) 59 | { 60 | if (0 == (++i % 100)) 61 | { 62 | const char* t = s_subtitles[(i / 100) % (sizeof(s_subtitles)/sizeof(s_subtitles[0]))]; 63 | assert(strlen(t) < 0xFFFF); 64 | size_t n = strlen(t); 65 | sbuf[0] = (n >> 8) & 0xFF; 66 | sbuf[1] = n & 0xFF; 67 | memcpy(sbuf + 2, t, n); 68 | mov_writer_write(wmov, track, sbuf, n+2, s_pts_last, s_pts_last, 0); 69 | } 70 | } 71 | 72 | mov_writer_destroy(wmov); 73 | mov_reader_destroy(rmov); 74 | fclose(rfp); 75 | fclose(wfp); 76 | } 77 | -------------------------------------------------------------------------------- /libmov/test/mov-writer-test.cpp: -------------------------------------------------------------------------------- 1 | #include "mov-writer.h" 2 | #include "mov-format.h" 3 | #include "mov-udta.h" 4 | #include "mpeg4-aac.h" 5 | #include "opus-head.h" 6 | #include "mp3-header.h" 7 | #include "flv-proto.h" 8 | #include "flv-reader.h" 9 | #include "flv-parser.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | extern "C" const struct mov_buffer_t* mov_file_buffer(void); 16 | extern "C" int mov_writer_add_udta(mov_writer_t * mov, const void* data, size_t size); 17 | 18 | static uint8_t s_buffer[2 * 1024 * 1024]; 19 | static int s_width, s_height; 20 | 21 | static int onFLV(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags) 22 | { 23 | mov_writer_t* mov = (mov_writer_t*)param; 24 | static int s_audio_track = -1; 25 | static int s_video_track = -1; 26 | 27 | switch(codec) 28 | { 29 | case FLV_AUDIO_AAC: 30 | case FLV_AUDIO_OPUS: 31 | return mov_writer_write(mov, s_audio_track, data, bytes, pts, dts, 1 == flags ? MOV_AV_FLAG_KEYFREAME : 0); 32 | 33 | case FLV_AUDIO_MP3: 34 | if (-1 == s_audio_track) 35 | { 36 | struct mp3_header_t mp3; 37 | if (0 == mp3_header_load(&mp3, data, bytes)) 38 | return -1; 39 | s_audio_track = mov_writer_add_audio(mov, MOV_OBJECT_MP3, mp3_get_channel(&mp3), 16, mp3_get_frequency(&mp3), NULL, 0); 40 | } 41 | 42 | if (-1 == s_audio_track) 43 | return -1; 44 | return mov_writer_write(mov, s_audio_track, data, bytes, pts, dts, 1 == flags ? MOV_AV_FLAG_KEYFREAME : 0); 45 | 46 | case FLV_AUDIO_G711A: 47 | case FLV_AUDIO_G711U: 48 | if (-1 == s_audio_track) 49 | s_audio_track = mov_writer_add_audio(mov, codec==FLV_AUDIO_G711A?MOV_OBJECT_G711a:MOV_OBJECT_G711u, 1, 16, 8000, NULL, 0); 50 | if (-1 == s_audio_track) 51 | return -1; 52 | return mov_writer_write(mov, s_audio_track, data, bytes, pts, dts, 0); 53 | 54 | case FLV_VIDEO_H264: 55 | case FLV_VIDEO_H265: 56 | case FLV_VIDEO_AV1: 57 | return mov_writer_write(mov, s_video_track, data, bytes, pts, dts, flags); 58 | 59 | case FLV_VIDEO_AVCC: 60 | if (-1 == s_video_track) 61 | s_video_track = mov_writer_add_video(mov, MOV_OBJECT_H264, s_width, s_height, data, bytes); 62 | break; 63 | 64 | case FLV_VIDEO_HVCC: 65 | if (-1 == s_video_track) 66 | s_video_track = mov_writer_add_video(mov, MOV_OBJECT_HEVC, s_width, s_height, data, bytes); 67 | break; 68 | 69 | case FLV_VIDEO_AV1C: 70 | if (-1 == s_video_track) 71 | s_video_track = mov_writer_add_video(mov, MOV_OBJECT_AV1, s_width, s_height, data, bytes); 72 | break; 73 | 74 | case FLV_AUDIO_ASC: 75 | if (-1 == s_audio_track) 76 | { 77 | struct mpeg4_aac_t aac; 78 | mpeg4_aac_audio_specific_config_load((const uint8_t*)data, bytes, &aac); 79 | int rate = mpeg4_aac_audio_frequency_to((enum mpeg4_aac_frequency)aac.sampling_frequency_index); 80 | s_audio_track = mov_writer_add_audio(mov, MOV_OBJECT_AAC, aac.channel_configuration, 16, rate, data, bytes); 81 | } 82 | break; 83 | case FLV_AUDIO_OPUS_HEAD: 84 | if (-1 == s_audio_track) 85 | { 86 | struct opus_head_t opus; 87 | opus_head_load((const uint8_t*)data, bytes, &opus); 88 | s_audio_track = mov_writer_add_audio(mov, MOV_OBJECT_OPUS, opus.channels, 16, opus.input_sample_rate, data, bytes); 89 | } 90 | break; 91 | 92 | default: 93 | // nothing to do 94 | assert(FLV_SCRIPT_METADATA == codec); 95 | } 96 | 97 | printf("\n"); 98 | return 0; 99 | } 100 | 101 | static int mov_writer_add_cover(mov_writer_t* mov, const char* cover) 102 | { 103 | static uint8_t s_cover_data[2 * 1024 * 1024]; 104 | static uint8_t s_udta[2 * 1024 * 1024]; 105 | FILE* fp = fopen(cover, "rb"); 106 | if (!fp) 107 | return -1; 108 | int n = fread(s_cover_data, 1, sizeof(s_cover_data), fp); 109 | fclose(fp); 110 | 111 | assert(n < sizeof(s_cover_data)); // assert load all 112 | struct mov_udta_meta_t meta; 113 | memset(&meta, 0, sizeof(meta)); 114 | meta.cover = s_cover_data; 115 | meta.cover_size = n; 116 | n = mov_udta_meta_write(&meta, s_udta, sizeof(s_udta)); 117 | assert(n < sizeof(s_udta)); // check buffer size 118 | return mov_writer_add_udta(mov, s_udta, n); 119 | } 120 | 121 | void mov_writer_test(int w, int h, const char* inflv, const char* outmp4) 122 | { 123 | int r, type; 124 | size_t taglen; 125 | uint32_t timestamp; 126 | 127 | FILE* fp = fopen(outmp4, "wb+"); 128 | void* flv = flv_reader_create(inflv); 129 | mov_writer_t* mov = mov_writer_create(mov_file_buffer(), fp, MOV_FLAG_FASTSTART); 130 | mov_writer_add_cover(mov, "cover.jpg"); 131 | 132 | s_width = w; 133 | s_height = h; 134 | while (1 == flv_reader_read(flv, &type, ×tamp, &taglen, s_buffer, sizeof(s_buffer))) 135 | { 136 | r = flv_parser_tag(type, s_buffer, taglen, timestamp, onFLV, mov); 137 | assert(r >= 0); 138 | } 139 | 140 | mov_writer_destroy(mov); 141 | flv_reader_destroy(flv); 142 | fclose(fp); 143 | } 144 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include "NvCodecRender.h" 2 | #include 3 | #include 4 | int main(int argc, char **argv) 5 | { 6 | if (argc < 5) { 7 | printf("./demo input output gpu_idx use_nvenc(0 -not use 1- use)\n"); 8 | return -1; 9 | } 10 | ck(cuInit(0)); 11 | NvCodecRender *test = new NvCodecRender(argv[1], argv[2], atoi(argv[3]), atoi(argv[4]) == 1 ? true : false); 12 | auto start_time = std::chrono::high_resolution_clock::now(); 13 | test->Render(); 14 | delete test; 15 | auto end_time = std::chrono::high_resolution_clock::now(); 16 | auto duration = std::chrono::duration_cast(end_time - start_time); 17 | std::cout << "耗时: " << duration.count() << " 毫秒" << std::endl; 18 | return 0; 19 | } -------------------------------------------------------------------------------- /test/test_1280x720_v.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BreakingY/Nvidia-Video-Codec/cbbc31cd93b8f66ba3a6e846a7a314eae62ca429/test/test_1280x720_v.mp4 --------------------------------------------------------------------------------