├── .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 |
[](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