1 | /**************************************************************************/ |
2 | /* movie_writer_pngwav.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_pngwav.h" |
32 | #include "core/config/project_settings.h" |
33 | #include "core/io/dir_access.h" |
34 | |
35 | uint32_t MovieWriterPNGWAV::get_audio_mix_rate() const { |
36 | return mix_rate; |
37 | } |
38 | AudioServer::SpeakerMode MovieWriterPNGWAV::get_audio_speaker_mode() const { |
39 | return speaker_mode; |
40 | } |
41 | |
42 | void MovieWriterPNGWAV::get_supported_extensions(List<String> *r_extensions) const { |
43 | r_extensions->push_back("png" ); |
44 | } |
45 | |
46 | bool MovieWriterPNGWAV::handles_file(const String &p_path) const { |
47 | return p_path.get_extension().to_lower() == "png" ; |
48 | } |
49 | |
50 | String MovieWriterPNGWAV::zeros_str(uint32_t p_index) { |
51 | char zeros[MAX_TRAILING_ZEROS + 1]; |
52 | for (uint32_t i = 0; i < MAX_TRAILING_ZEROS; i++) { |
53 | uint32_t idx = MAX_TRAILING_ZEROS - i - 1; |
54 | uint32_t digit = (p_index / uint32_t(Math::pow(double(10), double(idx)))) % 10; |
55 | zeros[i] = '0' + digit; |
56 | } |
57 | zeros[MAX_TRAILING_ZEROS] = 0; |
58 | return zeros; |
59 | } |
60 | |
61 | Error MovieWriterPNGWAV::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) { |
62 | // Quick & Dirty PNGWAV Code based on - https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference |
63 | |
64 | base_path = p_base_path.get_basename(); |
65 | if (base_path.is_relative_path()) { |
66 | base_path = "res://" + base_path; |
67 | } |
68 | |
69 | { |
70 | //Remove existing files before writing anew |
71 | uint32_t idx = 0; |
72 | Ref<DirAccess> d = DirAccess::open(base_path.get_base_dir()); |
73 | String file = base_path.get_file(); |
74 | while (true) { |
75 | String path = file + zeros_str(idx) + ".png" ; |
76 | if (d->remove(path) != OK) { |
77 | break; |
78 | } |
79 | } |
80 | } |
81 | |
82 | f_wav = FileAccess::open(base_path + ".wav" , FileAccess::WRITE_READ); |
83 | ERR_FAIL_COND_V(f_wav.is_null(), ERR_CANT_OPEN); |
84 | |
85 | fps = p_fps; |
86 | |
87 | f_wav->store_buffer((const uint8_t *)"RIFF" , 4); |
88 | int total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */; |
89 | f_wav->store_32(total_size); //will store final later |
90 | f_wav->store_buffer((const uint8_t *)"WAVE" , 4); |
91 | |
92 | /* FORMAT CHUNK */ |
93 | |
94 | f_wav->store_buffer((const uint8_t *)"fmt " , 4); |
95 | |
96 | uint32_t channels = 2; |
97 | switch (speaker_mode) { |
98 | case AudioServer::SPEAKER_MODE_STEREO: |
99 | channels = 2; |
100 | break; |
101 | case AudioServer::SPEAKER_SURROUND_31: |
102 | channels = 4; |
103 | break; |
104 | case AudioServer::SPEAKER_SURROUND_51: |
105 | channels = 6; |
106 | break; |
107 | case AudioServer::SPEAKER_SURROUND_71: |
108 | channels = 8; |
109 | break; |
110 | } |
111 | |
112 | f_wav->store_32(16); //standard format, no extra fields |
113 | f_wav->store_16(1); // compression code, standard PCM |
114 | f_wav->store_16(channels); //CHANNELS: 2 |
115 | |
116 | f_wav->store_32(mix_rate); |
117 | |
118 | /* useless stuff the format asks for */ |
119 | |
120 | int bits_per_sample = 32; |
121 | int blockalign = bits_per_sample / 8 * channels; |
122 | int bytes_per_sec = mix_rate * blockalign; |
123 | |
124 | audio_block_size = (mix_rate / fps) * blockalign; |
125 | |
126 | f_wav->store_32(bytes_per_sec); |
127 | f_wav->store_16(blockalign); // block align (unused) |
128 | f_wav->store_16(bits_per_sample); |
129 | |
130 | /* DATA CHUNK */ |
131 | |
132 | f_wav->store_buffer((const uint8_t *)"data" , 4); |
133 | |
134 | f_wav->store_32(0); //data size... wooh |
135 | wav_data_size_pos = f_wav->get_position(); |
136 | |
137 | return OK; |
138 | } |
139 | |
140 | Error MovieWriterPNGWAV::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) { |
141 | ERR_FAIL_COND_V(!f_wav.is_valid(), ERR_UNCONFIGURED); |
142 | |
143 | Vector<uint8_t> png_buffer = p_image->save_png_to_buffer(); |
144 | |
145 | Ref<FileAccess> fi = FileAccess::open(base_path + zeros_str(frame_count) + ".png" , FileAccess::WRITE); |
146 | fi->store_buffer(png_buffer.ptr(), png_buffer.size()); |
147 | f_wav->store_buffer((const uint8_t *)p_audio_data, audio_block_size); |
148 | |
149 | frame_count++; |
150 | |
151 | return OK; |
152 | } |
153 | |
154 | void MovieWriterPNGWAV::write_end() { |
155 | if (f_wav.is_valid()) { |
156 | uint32_t total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */; |
157 | uint32_t datasize = f_wav->get_position() - wav_data_size_pos; |
158 | f_wav->seek(4); |
159 | f_wav->store_32(total_size + datasize); |
160 | f_wav->seek(0x28); |
161 | f_wav->store_32(datasize); |
162 | } |
163 | } |
164 | |
165 | MovieWriterPNGWAV::MovieWriterPNGWAV() { |
166 | mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate" ); |
167 | speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode" ))); |
168 | } |
169 | |