Path: blob/master/modules/jpg/movie_writer_mjpeg.cpp
11352 views
/**************************************************************************/1/* movie_writer_mjpeg.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "movie_writer_mjpeg.h"31#include "core/config/project_settings.h"32#include "core/io/file_access.h"3334uint32_t MovieWriterMJPEG::get_audio_mix_rate() const {35return mix_rate;36}37AudioServer::SpeakerMode MovieWriterMJPEG::get_audio_speaker_mode() const {38return speaker_mode;39}4041bool MovieWriterMJPEG::handles_file(const String &p_path) const {42return p_path.get_extension().to_lower() == "avi";43}4445void MovieWriterMJPEG::get_supported_extensions(List<String> *r_extensions) const {46r_extensions->push_back("avi");47}4849Error 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-reference5152base_path = p_base_path.get_basename();53if (base_path.is_relative_path()) {54base_path = "res://" + base_path;55}5657base_path += ".avi";5859f = FileAccess::open(base_path, FileAccess::WRITE_READ);6061fps = p_fps;6263ERR_FAIL_COND_V(f.is_null(), ERR_CANT_OPEN);6465f->store_buffer((const uint8_t *)"RIFF", 4);66f->store_32(0); // Total length (update later)67f->store_buffer((const uint8_t *)"AVI ", 4);68f->store_buffer((const uint8_t *)"LIST", 4);69f->store_32(300); // 4 + 4 + 4 + 56 + 4 + 4 + 132 + 4 + 4 + 8470f->store_buffer((const uint8_t *)"hdrl", 4);71f->store_buffer((const uint8_t *)"avih", 4);72f->store_32(56);7374f->store_32(1000000 / p_fps); // Microsecs per frame.75f->store_32(7000); // Max bytes per second76f->store_32(0); // Padding Granularity77f->store_32(16);78total_frames_ofs = f->get_position();79f->store_32(0); // Total frames (update later)80f->store_32(0); // Initial frames81f->store_32(1); // Streams82f->store_32(0); // Suggested buffer size83f->store_32(p_movie_size.width); // Movie Width84f->store_32(p_movie_size.height); // Movie Height85for (uint32_t i = 0; i < 4; i++) {86f->store_32(0); // Reserved.87}88f->store_buffer((const uint8_t *)"LIST", 4);89f->store_32(132); // 4 + 4 + 4 + 48 + 4 + 4 + 40 + 4 + 4 + 1690f->store_buffer((const uint8_t *)"strl", 4);91f->store_buffer((const uint8_t *)"strh", 4);92f->store_32(48);93f->store_buffer((const uint8_t *)"vids", 4);94f->store_buffer((const uint8_t *)"MJPG", 4);95f->store_32(0); // Flags96f->store_16(0); // Priority97f->store_16(0); // Language98f->store_32(0); // Initial Frames99f->store_32(1); // Scale100f->store_32(p_fps); // FPS101f->store_32(0); // Start102total_frames_ofs2 = f->get_position();103f->store_32(0); // Number of frames (to be updated later)104f->store_32(0); // Suggested Buffer Size105f->store_32(0); // Quality106f->store_32(0); // Sample Size107108f->store_buffer((const uint8_t *)"strf", 4);109f->store_32(40); // Size.110f->store_32(40); // Size.111112f->store_32(p_movie_size.width); // Width113f->store_32(p_movie_size.height); // Width114f->store_16(1); // Planes115f->store_16(24); // Bitcount116f->store_buffer((const uint8_t *)"MJPG", 4); // Compression117118f->store_32(((p_movie_size.width * 24 / 8 + 3) & 0xFFFFFFFC) * p_movie_size.height); // SizeImage119f->store_32(0); // XPelsXMeter120f->store_32(0); // YPelsXMeter121f->store_32(0); // ClrUsed122f->store_32(0); // ClrImportant123124f->store_buffer((const uint8_t *)"LIST", 4);125f->store_32(16);126127f->store_buffer((const uint8_t *)"odml", 4);128f->store_buffer((const uint8_t *)"dmlh", 4);129f->store_32(4); // sizes130131total_frames_ofs3 = f->get_position();132f->store_32(0); // Number of frames (to be updated later)133134// Audio //135136const uint32_t bit_depth = 32;137uint32_t channels = 2;138switch (speaker_mode) {139case AudioServer::SPEAKER_MODE_STEREO:140channels = 2;141break;142case AudioServer::SPEAKER_SURROUND_31:143channels = 4;144break;145case AudioServer::SPEAKER_SURROUND_51:146channels = 6;147break;148case AudioServer::SPEAKER_SURROUND_71:149channels = 8;150break;151}152uint32_t blockalign = bit_depth / 8 * channels;153154f->store_buffer((const uint8_t *)"LIST", 4);155f->store_32(84); // 4 + 4 + 4 + 48 + 4 + 4 + 16156f->store_buffer((const uint8_t *)"strl", 4);157f->store_buffer((const uint8_t *)"strh", 4);158f->store_32(48);159f->store_buffer((const uint8_t *)"auds", 4);160f->store_32(0); // Handler161f->store_32(0); // Flags162f->store_16(0); // Priority163f->store_16(0); // Language164f->store_32(0); // Initial Frames165f->store_32(blockalign); // Scale166f->store_32(mix_rate * blockalign); // mix rate167f->store_32(0); // Start168total_audio_frames_ofs4 = f->get_position();169f->store_32(0); // Number of frames (to be updated later)170f->store_32(12288); // Suggested Buffer Size171f->store_32(0xFFFFFFFF); // Quality172f->store_32(blockalign); // Block Align to 32 bits173174audio_block_size = (mix_rate / fps) * blockalign;175176f->store_buffer((const uint8_t *)"strf", 4);177f->store_32(16); // Standard format, no extra fields178f->store_16(1); // Compression code, standard PCM179f->store_16(channels);180f->store_32(mix_rate); // Samples (frames) / Sec181f->store_32(mix_rate * blockalign); // Bytes / sec182f->store_16(blockalign); // Bytes / sec183f->store_16(bit_depth); // Bytes / sec184185f->store_buffer((const uint8_t *)"LIST", 4);186movi_data_ofs = f->get_position();187f->store_32(0); // Number of frames (to be updated later)188f->store_buffer((const uint8_t *)"movi", 4);189190return OK;191}192193Error MovieWriterMJPEG::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {194ERR_FAIL_COND_V(f.is_null(), ERR_UNCONFIGURED);195196Vector<uint8_t> jpg_buffer = p_image->save_jpg_to_buffer(quality);197uint32_t s = jpg_buffer.size();198199f->store_buffer((const uint8_t *)"00db", 4); // Stream 0, Video200f->store_32(jpg_buffer.size()); // sizes201f->store_buffer(jpg_buffer.ptr(), jpg_buffer.size());202if (jpg_buffer.size() & 1) {203f->store_8(0);204s++;205}206jpg_frame_sizes.push_back(s);207208f->store_buffer((const uint8_t *)"01wb", 4); // Stream 1, Audio.209f->store_32(audio_block_size);210f->store_buffer((const uint8_t *)p_audio_data, audio_block_size);211212frame_count++;213214return OK;215}216217void MovieWriterMJPEG::write_end() {218if (f.is_valid()) {219// Finalize the file (frame indices)220f->store_buffer((const uint8_t *)"idx1", 4);221f->store_32(8 * 4 * frame_count);222uint32_t ofs = 4;223uint32_t all_data_size = 0;224for (uint32_t i = 0; i < frame_count; i++) {225f->store_buffer((const uint8_t *)"00db", 4);226f->store_32(16); // AVI_KEYFRAME227f->store_32(ofs);228f->store_32(jpg_frame_sizes[i]);229230ofs += jpg_frame_sizes[i] + 8;231232f->store_buffer((const uint8_t *)"01wb", 4);233f->store_32(16); // AVI_KEYFRAME234f->store_32(ofs);235f->store_32(audio_block_size);236237ofs += audio_block_size + 8;238all_data_size += jpg_frame_sizes[i] + audio_block_size;239}240241uint32_t file_size = f->get_position();242f->seek(4);243f->store_32(file_size - 78);244f->seek(total_frames_ofs);245f->store_32(frame_count);246f->seek(total_frames_ofs2);247f->store_32(frame_count);248f->seek(total_frames_ofs3);249f->store_32(frame_count);250f->seek(total_audio_frames_ofs4);251f->store_32(frame_count * mix_rate / fps);252f->seek(movi_data_ofs);253f->store_32(all_data_size + 4 + 16 * frame_count);254255f.unref();256}257}258259MovieWriterMJPEG::MovieWriterMJPEG() {260mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate");261speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode")));262quality = GLOBAL_GET("editor/movie_writer/video_quality");263}264265266