Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/jpg/movie_writer_mjpeg.cpp
10277 views
1
/**************************************************************************/
2
/* movie_writer_mjpeg.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "movie_writer_mjpeg.h"
32
#include "core/config/project_settings.h"
33
34
uint32_t MovieWriterMJPEG::get_audio_mix_rate() const {
35
return mix_rate;
36
}
37
AudioServer::SpeakerMode MovieWriterMJPEG::get_audio_speaker_mode() const {
38
return speaker_mode;
39
}
40
41
bool MovieWriterMJPEG::handles_file(const String &p_path) const {
42
return p_path.get_extension().to_lower() == "avi";
43
}
44
45
void MovieWriterMJPEG::get_supported_extensions(List<String> *r_extensions) const {
46
r_extensions->push_back("avi");
47
}
48
49
Error MovieWriterMJPEG::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
50
// Quick & Dirty MJPEG Code based on - https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference
51
52
base_path = p_base_path.get_basename();
53
if (base_path.is_relative_path()) {
54
base_path = "res://" + base_path;
55
}
56
57
base_path += ".avi";
58
59
f = FileAccess::open(base_path, FileAccess::WRITE_READ);
60
61
fps = p_fps;
62
63
ERR_FAIL_COND_V(f.is_null(), ERR_CANT_OPEN);
64
65
f->store_buffer((const uint8_t *)"RIFF", 4);
66
f->store_32(0); // Total length (update later)
67
f->store_buffer((const uint8_t *)"AVI ", 4);
68
f->store_buffer((const uint8_t *)"LIST", 4);
69
f->store_32(300); // 4 + 4 + 4 + 56 + 4 + 4 + 132 + 4 + 4 + 84
70
f->store_buffer((const uint8_t *)"hdrl", 4);
71
f->store_buffer((const uint8_t *)"avih", 4);
72
f->store_32(56);
73
74
f->store_32(1000000 / p_fps); // Microsecs per frame.
75
f->store_32(7000); // Max bytes per second
76
f->store_32(0); // Padding Granularity
77
f->store_32(16);
78
total_frames_ofs = f->get_position();
79
f->store_32(0); // Total frames (update later)
80
f->store_32(0); // Initial frames
81
f->store_32(1); // Streams
82
f->store_32(0); // Suggested buffer size
83
f->store_32(p_movie_size.width); // Movie Width
84
f->store_32(p_movie_size.height); // Movie Height
85
for (uint32_t i = 0; i < 4; i++) {
86
f->store_32(0); // Reserved.
87
}
88
f->store_buffer((const uint8_t *)"LIST", 4);
89
f->store_32(132); // 4 + 4 + 4 + 48 + 4 + 4 + 40 + 4 + 4 + 16
90
f->store_buffer((const uint8_t *)"strl", 4);
91
f->store_buffer((const uint8_t *)"strh", 4);
92
f->store_32(48);
93
f->store_buffer((const uint8_t *)"vids", 4);
94
f->store_buffer((const uint8_t *)"MJPG", 4);
95
f->store_32(0); // Flags
96
f->store_16(0); // Priority
97
f->store_16(0); // Language
98
f->store_32(0); // Initial Frames
99
f->store_32(1); // Scale
100
f->store_32(p_fps); // FPS
101
f->store_32(0); // Start
102
total_frames_ofs2 = f->get_position();
103
f->store_32(0); // Number of frames (to be updated later)
104
f->store_32(0); // Suggested Buffer Size
105
f->store_32(0); // Quality
106
f->store_32(0); // Sample Size
107
108
f->store_buffer((const uint8_t *)"strf", 4);
109
f->store_32(40); // Size.
110
f->store_32(40); // Size.
111
112
f->store_32(p_movie_size.width); // Width
113
f->store_32(p_movie_size.height); // Width
114
f->store_16(1); // Planes
115
f->store_16(24); // Bitcount
116
f->store_buffer((const uint8_t *)"MJPG", 4); // Compression
117
118
f->store_32(((p_movie_size.width * 24 / 8 + 3) & 0xFFFFFFFC) * p_movie_size.height); // SizeImage
119
f->store_32(0); // XPelsXMeter
120
f->store_32(0); // YPelsXMeter
121
f->store_32(0); // ClrUsed
122
f->store_32(0); // ClrImportant
123
124
f->store_buffer((const uint8_t *)"LIST", 4);
125
f->store_32(16);
126
127
f->store_buffer((const uint8_t *)"odml", 4);
128
f->store_buffer((const uint8_t *)"dmlh", 4);
129
f->store_32(4); // sizes
130
131
total_frames_ofs3 = f->get_position();
132
f->store_32(0); // Number of frames (to be updated later)
133
134
// Audio //
135
136
const uint32_t bit_depth = 32;
137
uint32_t channels = 2;
138
switch (speaker_mode) {
139
case AudioServer::SPEAKER_MODE_STEREO:
140
channels = 2;
141
break;
142
case AudioServer::SPEAKER_SURROUND_31:
143
channels = 4;
144
break;
145
case AudioServer::SPEAKER_SURROUND_51:
146
channels = 6;
147
break;
148
case AudioServer::SPEAKER_SURROUND_71:
149
channels = 8;
150
break;
151
}
152
uint32_t blockalign = bit_depth / 8 * channels;
153
154
f->store_buffer((const uint8_t *)"LIST", 4);
155
f->store_32(84); // 4 + 4 + 4 + 48 + 4 + 4 + 16
156
f->store_buffer((const uint8_t *)"strl", 4);
157
f->store_buffer((const uint8_t *)"strh", 4);
158
f->store_32(48);
159
f->store_buffer((const uint8_t *)"auds", 4);
160
f->store_32(0); // Handler
161
f->store_32(0); // Flags
162
f->store_16(0); // Priority
163
f->store_16(0); // Language
164
f->store_32(0); // Initial Frames
165
f->store_32(blockalign); // Scale
166
f->store_32(mix_rate * blockalign); // mix rate
167
f->store_32(0); // Start
168
total_audio_frames_ofs4 = f->get_position();
169
f->store_32(0); // Number of frames (to be updated later)
170
f->store_32(12288); // Suggested Buffer Size
171
f->store_32(0xFFFFFFFF); // Quality
172
f->store_32(blockalign); // Block Align to 32 bits
173
174
audio_block_size = (mix_rate / fps) * blockalign;
175
176
f->store_buffer((const uint8_t *)"strf", 4);
177
f->store_32(16); // Standard format, no extra fields
178
f->store_16(1); // Compression code, standard PCM
179
f->store_16(channels);
180
f->store_32(mix_rate); // Samples (frames) / Sec
181
f->store_32(mix_rate * blockalign); // Bytes / sec
182
f->store_16(blockalign); // Bytes / sec
183
f->store_16(bit_depth); // Bytes / sec
184
185
f->store_buffer((const uint8_t *)"LIST", 4);
186
movi_data_ofs = f->get_position();
187
f->store_32(0); // Number of frames (to be updated later)
188
f->store_buffer((const uint8_t *)"movi", 4);
189
190
return OK;
191
}
192
193
Error MovieWriterMJPEG::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
194
ERR_FAIL_COND_V(f.is_null(), ERR_UNCONFIGURED);
195
196
Vector<uint8_t> jpg_buffer = p_image->save_jpg_to_buffer(quality);
197
uint32_t s = jpg_buffer.size();
198
199
f->store_buffer((const uint8_t *)"00db", 4); // Stream 0, Video
200
f->store_32(jpg_buffer.size()); // sizes
201
f->store_buffer(jpg_buffer.ptr(), jpg_buffer.size());
202
if (jpg_buffer.size() & 1) {
203
f->store_8(0);
204
s++;
205
}
206
jpg_frame_sizes.push_back(s);
207
208
f->store_buffer((const uint8_t *)"01wb", 4); // Stream 1, Audio.
209
f->store_32(audio_block_size);
210
f->store_buffer((const uint8_t *)p_audio_data, audio_block_size);
211
212
frame_count++;
213
214
return OK;
215
}
216
217
void MovieWriterMJPEG::write_end() {
218
if (f.is_valid()) {
219
// Finalize the file (frame indices)
220
f->store_buffer((const uint8_t *)"idx1", 4);
221
f->store_32(8 * 4 * frame_count);
222
uint32_t ofs = 4;
223
uint32_t all_data_size = 0;
224
for (uint32_t i = 0; i < frame_count; i++) {
225
f->store_buffer((const uint8_t *)"00db", 4);
226
f->store_32(16); // AVI_KEYFRAME
227
f->store_32(ofs);
228
f->store_32(jpg_frame_sizes[i]);
229
230
ofs += jpg_frame_sizes[i] + 8;
231
232
f->store_buffer((const uint8_t *)"01wb", 4);
233
f->store_32(16); // AVI_KEYFRAME
234
f->store_32(ofs);
235
f->store_32(audio_block_size);
236
237
ofs += audio_block_size + 8;
238
all_data_size += jpg_frame_sizes[i] + audio_block_size;
239
}
240
241
uint32_t file_size = f->get_position();
242
f->seek(4);
243
f->store_32(file_size - 78);
244
f->seek(total_frames_ofs);
245
f->store_32(frame_count);
246
f->seek(total_frames_ofs2);
247
f->store_32(frame_count);
248
f->seek(total_frames_ofs3);
249
f->store_32(frame_count);
250
f->seek(total_audio_frames_ofs4);
251
f->store_32(frame_count * mix_rate / fps);
252
f->seek(movi_data_ofs);
253
f->store_32(all_data_size + 4 + 16 * frame_count);
254
255
f.unref();
256
}
257
}
258
259
MovieWriterMJPEG::MovieWriterMJPEG() {
260
mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate");
261
speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode")));
262
quality = GLOBAL_GET("editor/movie_writer/video_quality");
263
}
264
265