From 156b799011bf8d6f1bee9db7dd6a3301e482ebac Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 9 Apr 2017 14:16:45 +0800 Subject: [PATCH 01/14] Remove STK headers in recorder and use c++11 thread library --- src/graphics/irr_driver.cpp | 68 ++- src/recorder/capture_library.cpp | 298 +++++++++++++ src/recorder/capture_library.hpp | 118 ++++++ src/recorder/jpg_writer.cpp | 84 ---- src/recorder/mjpeg_writer.cpp | 69 +++ .../{jpg_writer.hpp => mjpeg_writer.hpp} | 9 +- src/recorder/mkv_writer.cpp | 75 ++-- src/recorder/mkv_writer.hpp | 1 + src/recorder/openglrecorder.h | 211 +++++++++ src/recorder/pulseaudio_recorder.cpp | 153 +++---- src/recorder/pulseaudio_recorder.hpp | 5 +- src/recorder/recorder.cpp | 265 ++++++++++++ src/recorder/recorder_common.cpp | 401 ------------------ src/recorder/recorder_common.hpp | 60 --- src/recorder/recorder_private.hpp | 17 + src/recorder/vorbis_encoder.cpp | 62 ++- src/recorder/vorbis_encoder.hpp | 18 +- src/recorder/vpx_encoder.cpp | 158 ++----- src/recorder/vpx_encoder.hpp | 6 +- src/recorder/wasapi_recorder.cpp | 135 +++--- src/recorder/wasapi_recorder.hpp | 5 +- 21 files changed, 1288 insertions(+), 930 deletions(-) create mode 100644 src/recorder/capture_library.cpp create mode 100644 src/recorder/capture_library.hpp delete mode 100644 src/recorder/jpg_writer.cpp create mode 100644 src/recorder/mjpeg_writer.cpp rename src/recorder/{jpg_writer.hpp => mjpeg_writer.hpp} (87%) create mode 100644 src/recorder/openglrecorder.h create mode 100644 src/recorder/recorder.cpp delete mode 100644 src/recorder/recorder_common.cpp delete mode 100644 src/recorder/recorder_common.hpp create mode 100644 src/recorder/recorder_private.hpp diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index c88f2f034..a01acb61b 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -57,7 +57,7 @@ #include "modes/profile_world.hpp" #include "modes/world.hpp" #include "physics/physics.hpp" -#include "recorder/recorder_common.hpp" +#include "recorder/openglrecorder.h" #include "scriptengine/property_animator.hpp" #include "states_screens/dialogs/confirm_resolution_dialog.hpp" #include "states_screens/state_manager.hpp" @@ -170,7 +170,7 @@ IrrDriver::~IrrDriver() delete m_wind; delete m_renderer; #ifdef ENABLE_RECORDER - Recorder::destroyRecorder(); + ogrDestroy(); #endif } // ~IrrDriver @@ -603,6 +603,46 @@ void IrrDriver::initDevice() sml->drop(); m_actual_screen_size = m_video_driver->getCurrentRenderTargetSize(); + +#ifdef ENABLE_RECORDER + RecorderConfig cfg; + cfg.m_triple_buffering = 1; + cfg.m_record_audio = 1; + cfg.m_width = m_actual_screen_size.Width; + cfg.m_height = m_actual_screen_size.Height; + int vf = UserConfigParams::m_record_format; + cfg.m_video_format = (VideoFormat)vf; + cfg.m_audio_format = REC_AF_VORBIS; + cfg.m_audio_bitrate = 112000; + cfg.m_video_bitrate = UserConfigParams::m_vp_bitrate; + cfg.m_record_fps = UserConfigParams::m_record_fps; + cfg.m_record_jpg_quality = UserConfigParams::m_recorder_jpg_quality; + ogrInitConfig(&cfg); + + ogrRegGeneralCallback(REC_CBT_START_RECORDING, + [] (void* user_data) { MessageQueue::add + (MessageQueue::MT_GENERIC, _("Video recording started.")); }, NULL); + ogrRegGeneralCallback(REC_CBT_ERROR_RECORDING, + [] (void* user_data) { MessageQueue::add + (MessageQueue::MT_ERROR, _("Error when saving video.")); }, NULL); + ogrRegGeneralCallback(REC_CBT_SLOW_RECORDING, + [] (void* user_data) { MessageQueue::add + (MessageQueue::MT_ERROR, _("Encoding is too slow, dropping frames.")); + }, NULL); + ogrRegGeneralCallback(REC_CBT_WAIT_RECORDING, + [] (void* user_data) { MessageQueue::add + (MessageQueue::MT_GENERIC, _("Please wait while encoding is finished." + )); }, NULL); + ogrRegStringCallback(REC_CBT_SAVED_RECORDING, + [] (const char* s, void* user_data) { MessageQueue::add + (MessageQueue::MT_GENERIC, _("Video saved in \"%s\".", s)); + }, NULL); + ogrRegIntCallback(REC_CBT_PROGRESS_RECORDING, + [] (const int i, void* user_data) + { Log::info("Recorder", "%d%% of video encoding finished", i);}, NULL); + +#endif + #ifndef SERVER_ONLY if(CVS->isGLSL()) m_renderer = new ShaderBasedRenderer(); @@ -927,7 +967,7 @@ void IrrDriver::applyResolutionSettings() VAOManager::getInstance()->kill(); STKTexManager::getInstance()->kill(); #ifdef ENABLE_RECORDER - Recorder::destroyRecorder(); + ogrDestroy(); m_recording = false; #endif // initDevice will drop the current device. @@ -1896,7 +1936,7 @@ void IrrDriver::update(float dt) // printRenderStats(); #ifdef ENABLE_RECORDER if (m_recording) - Recorder::captureFrameBufferImage(); + ogrCapture(); #endif } // update @@ -1913,21 +1953,27 @@ void IrrDriver::setRecording(bool val) return; if (val == true) { - if (!Recorder::isRecording()) + if (ogrCapturing() > 0) return; m_recording = val; std::string track_name = World::getWorld() != NULL ? race_manager->getTrackName() : "menu"; - Recorder::setRecordingName(file_manager->getScreenshotDir() + - track_name); - Recorder::prepareCapture(); - MessageQueue::add(MessageQueue::MT_GENERIC, - _("Video recording started.")); + time_t rawtime; + time(&rawtime); + tm* timeInfo = localtime(&rawtime); + char time_buffer[256]; + sprintf(time_buffer, "%i.%02i.%02i_%02i.%02i.%02i", + timeInfo->tm_year + 1900, timeInfo->tm_mon + 1, + timeInfo->tm_mday, timeInfo->tm_hour, + timeInfo->tm_min, timeInfo->tm_sec); + ogrSetSavedName((file_manager->getScreenshotDir() + + track_name + "_" + time_buffer).c_str()); + ogrPrepareCapture(); } else { m_recording = val; - Recorder::stopRecording(); + ogrStopCapture(); } #endif } // setRecording diff --git a/src/recorder/capture_library.cpp b/src/recorder/capture_library.cpp new file mode 100644 index 000000000..110cfead9 --- /dev/null +++ b/src/recorder/capture_library.cpp @@ -0,0 +1,298 @@ +#ifdef ENABLE_RECORDER +#include "capture_library.hpp" + +#include "mjpeg_writer.hpp" +#include "mkv_writer.hpp" +#include "recorder_private.hpp" +#include "pulseaudio_recorder.hpp" +#include "vpx_encoder.hpp" +#include "wasapi_recorder.hpp" + +extern "C" +{ +#include +} + +const uint32_t E_GL_PIXEL_PACK_BUFFER = 0x88EB; +const uint32_t E_GL_STREAM_READ = 0x88E1; +const uint32_t E_GL_READ_ONLY = 0x88B8; +const uint32_t E_GL_RGB = 0x1907; +const uint32_t E_GL_UNSIGNED_BYTE = 0x1401; + +// ---------------------------------------------------------------------------- +CaptureLibrary::CaptureLibrary(RecorderConfig* rc) +{ + m_recorder_cfg = rc; + m_destroy.store(false); + m_sound_stop.store(true); + m_display_progress.store(false); + m_compress_handle = tjInitCompress(); + m_decompress_handle = tjInitDecompress(); + if (m_recorder_cfg->m_triple_buffering > 0) + { + glGenBuffers(3, m_pbo); + for (int i = 0; i < 3; i++) + { + glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[i]); + glBufferData(GL_PIXEL_PACK_BUFFER, m_recorder_cfg->m_width * + m_recorder_cfg->m_height * 4, NULL, GL_STREAM_READ); + } + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + } + m_capture_thread = std::thread(CaptureLibrary::captureConversion, this); +} // CaptureLibrary + +// ---------------------------------------------------------------------------- +CaptureLibrary::~CaptureLibrary() +{ + m_destroy.store(true); + addFrameBufferImage(NULL, ogrCapturing() > 0 ? -1 : 0); + m_capture_thread.join(); + tjDestroy(m_compress_handle); + tjDestroy(m_decompress_handle); + if (m_recorder_cfg->m_triple_buffering > 0) + { + glDeleteBuffers(3, m_pbo); + } +} // ~CaptureLibrary + +// ---------------------------------------------------------------------------- +void CaptureLibrary::reset() +{ + runCallback(REC_CBT_START_RECORDING, NULL); + m_pbo_use = 0; + m_accumulated_time = 0.; + assert(m_sound_stop.load() && ogrCapturing() == 0); + if (m_recorder_cfg->m_record_audio > 0) + { + m_sound_stop.store(false); + m_audio_enc_thread = std::thread(Recorder::audioRecorder, this); + } + setCapturing(true); + switch (m_recorder_cfg->m_video_format) + { + case REC_VF_VP8: + case REC_VF_VP9: + m_video_enc_thread = std::thread(Recorder::vpxEncoder, this); + break; + case REC_VF_MJPEG: + m_video_enc_thread = std::thread(Recorder::mjpegWriter, this); + break; + case REC_VF_H264: + break; + } +} // reset + +// ---------------------------------------------------------------------------- +int CaptureLibrary::bmpToJPG(uint8_t* raw, unsigned width, unsigned height, + uint8_t** jpeg_buffer, unsigned long* jpeg_size) +{ + int ret = 0; +#ifdef TJFLAG_FASTDCT + ret = tjCompress2(m_compress_handle, raw, width, 0, height, TJPF_RGB, + jpeg_buffer, jpeg_size, TJSAMP_420, + m_recorder_cfg->m_record_jpg_quality, TJFLAG_FASTDCT); +#else + ret = tjCompress2(m_compress_handle, raw, width, 0, height, TJPF_RGB, + jpeg_buffer, jpeg_size, TJSAMP_420, + m_recorder_cfg->m_record_jpg_quality, 0); +#endif + if (ret != 0) + { + char* err = tjGetErrorStr(); + printf("Jpeg encode error: %s.", err); + return ret; + } + return ret; +} // bmpToJPG + +// ---------------------------------------------------------------------------- +int CaptureLibrary::yuvConversion(uint8_t* jpeg_buffer, unsigned jpeg_size, + uint8_t** yuv_buffer, unsigned* yuv_size) +{ + int width, height; + TJSAMP subsample; + int ret = 0; + ret = tjDecompressHeader2(m_decompress_handle, jpeg_buffer, jpeg_size, + &width, &height, (int*)&subsample); + if (ret != 0) + { + char* err = tjGetErrorStr(); + printf("Jpeg decode error: %s.", err); + return ret; + } + *yuv_size = tjBufSizeYUV(width, height, subsample); + *yuv_buffer = new uint8_t[*yuv_size]; + ret = tjDecompressToYUV(m_decompress_handle, jpeg_buffer, jpeg_size, + *yuv_buffer, 0); + if (ret != 0) + { + char* err = tjGetErrorStr(); + printf("YUV conversion error: %s.", err); + return ret; + } + return ret; +} // yuvConversion + +// ---------------------------------------------------------------------------- +int CaptureLibrary::getFrameCount(double rate) +{ + const double frame_rate = 1. / double(m_recorder_cfg->m_record_fps); + m_accumulated_time += rate; + if (m_accumulated_time < frame_rate) + { + return 0; + } + int frame_count = 0; + while (m_accumulated_time >= frame_rate) + { + frame_count++; + m_accumulated_time = m_accumulated_time - frame_rate; + } + return frame_count; +} // getFrameCount + +// ---------------------------------------------------------------------------- +void CaptureLibrary::capture() +{ + int pbo_read = -1; + if (m_pbo_use > 3 && m_pbo_use % 3 == 0) + m_pbo_use = 3; + auto rate = std::chrono::high_resolution_clock::now() - m_framerate_timer; + m_framerate_timer = std::chrono::high_resolution_clock::now(); + const unsigned width = m_recorder_cfg->m_width; + const unsigned height = m_recorder_cfg->m_height; + const bool use_pbo = m_recorder_cfg->m_triple_buffering > 0; + if (m_pbo_use >= 3) + { + int frame_count = getFrameCount(std::chrono::duration_cast + >(rate).count()); + if (frame_count != 0) + { + const unsigned size = width * height * 4; + uint8_t* fbi = new uint8_t[size]; + if (use_pbo) + { + pbo_read = m_pbo_use % 3; + glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_read]); + void* ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); + memcpy(fbi, ptr, size); + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + } + else + { + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, + fbi); + } + addFrameBufferImage(fbi, frame_count); + } + } + int pbo_use = m_pbo_use++ % 3; + if (!use_pbo) + return; + + assert(pbo_read == -1 || pbo_use == pbo_read); + glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_use]); + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, NULL); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); +} // capture + +// ---------------------------------------------------------------------------- +void CaptureLibrary::captureConversion(CaptureLibrary* cl) +{ + setThreadName("captureConvert"); + while (true) + { + std::unique_lock ul(cl->m_fbi_list_mutex); + cl->m_fbi_list_ready.wait(ul, [&cl] + { return !cl->m_fbi_list.empty(); }); + auto& p = cl->m_fbi_list.front(); + uint8_t* fbi = p.first; + int frame_count = p.second; + if (frame_count == -1) + { + cl->m_fbi_list.clear(); + ul.unlock(); + if (cl->m_recorder_cfg->m_record_audio > 0) + { + cl->m_sound_stop.store(true); + cl->m_audio_enc_thread.join(); + } + std::unique_lock ulj(cl->m_jpg_list_mutex); + if (!cl->m_destroy.load() && cl->m_jpg_list.size() > 100) + { + runCallback(REC_CBT_WAIT_RECORDING, NULL); + } + cl->m_display_progress.store(true); + cl->m_jpg_list.emplace_back((uint8_t*)NULL, 0, 0); + cl->m_jpg_list_ready.notify_one(); + ulj.unlock(); + cl->m_video_enc_thread.join(); + cl->m_display_progress.store(false); + std::string f = Recorder::writeMKV(getSavedName() + ".video", + getSavedName() + ".audio"); + if (cl->m_destroy.load()) + { + return; + } + if (f.empty()) + { + runCallback(REC_CBT_ERROR_RECORDING, NULL); + } + else + { + runCallback(REC_CBT_SAVED_RECORDING, f.c_str()); + } + setCapturing(false); + continue; + } + else if (fbi == NULL) + { + cl->m_fbi_list.clear(); + ul.unlock(); + return; + } + const bool too_slow = cl->m_fbi_list.size() > 50; + if (too_slow) + { + runCallback(REC_CBT_SLOW_RECORDING, NULL); + delete [] fbi; + cl->m_fbi_list.pop_front(); + for (auto& p : cl->m_fbi_list) + delete [] p.first; + cl->m_fbi_list.clear(); + ul.unlock(); + continue; + } + cl->m_fbi_list.pop_front(); + ul.unlock(); + + uint8_t* orig_fbi = fbi; + const unsigned width = cl->m_recorder_cfg->m_width; + const unsigned height = cl->m_recorder_cfg->m_height; + const unsigned area = width * height; + const int pitch = width * 3; + uint8_t* p2 = fbi + (height - 1) * pitch; + uint8_t* tmp_buf = new uint8_t[pitch]; + for (unsigned i = 0; i < height; i += 2) + { + memcpy(tmp_buf, fbi, pitch); + memcpy(fbi, p2, pitch); + memcpy(p2, tmp_buf, pitch); + fbi += pitch; + p2 -= pitch; + } + delete [] tmp_buf; + + uint8_t* jpg = NULL; + unsigned long jpg_size = 0; + cl->bmpToJPG(orig_fbi, width, height, &jpg, &jpg_size); + delete[] orig_fbi; + + std::lock_guard lg(cl->m_jpg_list_mutex); + cl->m_jpg_list.emplace_back(jpg, jpg_size, frame_count); + cl->m_jpg_list_ready.notify_one(); + } +} // captureConversion + +#endif diff --git a/src/recorder/capture_library.hpp b/src/recorder/capture_library.hpp new file mode 100644 index 000000000..6892b794c --- /dev/null +++ b/src/recorder/capture_library.hpp @@ -0,0 +1,118 @@ +#ifdef ENABLE_RECORDER +#ifndef HEADER_CAPTURE_LIBRARY_HPP +#define HEADER_CAPTURE_LIBRARY_HPP + +#if defined(_MSC_VER) && _MSC_VER < 1700 + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +#else + #include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct AudioEncoderData +{ + enum AudioType { AT_FLOAT, AT_PCM }; + std::mutex* m_mutex; + std::condition_variable* m_cv; + std::list* m_buf_list; + uint32_t m_sample_rate; + uint32_t m_channels; + uint32_t m_audio_bitrate; + AudioType m_audio_type; +}; + +struct RecorderConfig; +typedef std::list > JPGList; + +class CaptureLibrary +{ +private: + RecorderConfig* m_recorder_cfg; + + std::atomic_bool m_destroy, m_display_progress, m_sound_stop; + + tjhandle m_compress_handle, m_decompress_handle; + + JPGList m_jpg_list; + std::mutex m_jpg_list_mutex; + std::condition_variable m_jpg_list_ready; + + std::list > m_fbi_list; + std::mutex m_fbi_list_mutex; + std::condition_variable m_fbi_list_ready; + + std::thread m_capture_thread, m_audio_enc_thread, m_video_enc_thread; + + uint32_t m_pbo[3]; + + unsigned m_pbo_use; + + std::chrono::high_resolution_clock::time_point m_framerate_timer; + + double m_accumulated_time; + + // ------------------------------------------------------------------------ + int getFrameCount(double rate); + // ------------------------------------------------------------------------ + void addFrameBufferImage(uint8_t* fbi, int frame_count) + { + std::lock_guard lock(m_fbi_list_mutex); + m_fbi_list.emplace_back(fbi, frame_count); + m_fbi_list_ready.notify_one(); + } + +public: + // ------------------------------------------------------------------------ + CaptureLibrary(RecorderConfig* rc); + // ------------------------------------------------------------------------ + ~CaptureLibrary(); + // ------------------------------------------------------------------------ + void capture(); + // ------------------------------------------------------------------------ + void stopCapture() { addFrameBufferImage(NULL, -1); } + // ------------------------------------------------------------------------ + void reset(); + // ------------------------------------------------------------------------ + int bmpToJPG(uint8_t* raw, unsigned width, unsigned height, + uint8_t** jpeg_buffer, unsigned long* jpeg_size); + // ------------------------------------------------------------------------ + int yuvConversion(uint8_t* jpeg_buffer, unsigned jpeg_size, + uint8_t** yuv_buffer, unsigned* yuv_size); + // ------------------------------------------------------------------------ + JPGList* getJPGList() { return &m_jpg_list; } + // ------------------------------------------------------------------------ + std::mutex* getJPGListMutex() { return &m_jpg_list_mutex; } + // ------------------------------------------------------------------------ + std::condition_variable* getJPGListCV() { return &m_jpg_list_ready; } + // ------------------------------------------------------------------------ + bool displayingProgress() const { return m_display_progress.load(); } + // ------------------------------------------------------------------------ + bool getSoundStop() const { return m_sound_stop.load(); } + // ------------------------------------------------------------------------ + bool getDestroy() const { return m_destroy.load(); } + // ------------------------------------------------------------------------ + const RecorderConfig& getRecorderConfig() const { return *m_recorder_cfg; } + // ------------------------------------------------------------------------ + static void captureConversion(CaptureLibrary* cl); + +}; + +#endif + +#endif diff --git a/src/recorder/jpg_writer.cpp b/src/recorder/jpg_writer.cpp deleted file mode 100644 index 6b6c47213..000000000 --- a/src/recorder/jpg_writer.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef ENABLE_RECORDER - -#include "config/user_config.hpp" -#include "graphics/irr_driver.hpp" -#include "recorder/recorder_common.hpp" -#include "utils/log.hpp" -#include "utils/synchronised.hpp" -#include "utils/vs.hpp" - -#include - -namespace Recorder -{ - // ------------------------------------------------------------------------ - void* jpgWriter(void *obj) - { - VS::setThreadName("jpgWriter"); - FILE* jpg_writer = fopen((getRecordingName() + ".video").c_str(), "wb"); - if (jpg_writer == NULL) - { - Log::error("jpgWriter", "Failed to open file for writing"); - return NULL; - } - ThreadData* td = (ThreadData*)obj; - Synchronised > >* - jpg_data = (Synchronised > >*)td->m_data; - pthread_cond_t* cond_request = td->m_request; - int64_t frames_encoded = 0; - while (true) - { - jpg_data->lock(); - bool waiting = jpg_data->getData().empty(); - while (waiting) - { - pthread_cond_wait(cond_request, jpg_data->getMutex()); - waiting = jpg_data->getData().empty(); - } - auto& p = jpg_data->getData().front(); - uint8_t* jpg = std::get<0>(p); - uint32_t jpg_size = std::get<1>(p); - int frame_count = std::get<2>(p); - if (jpg == NULL) - { - jpg_data->getData().clear(); - jpg_data->unlock(); - break; - } - jpg_data->getData().pop_front(); - jpg_data->unlock(); - while (frame_count != 0) - { - fwrite(&jpg_size, 1, sizeof(uint32_t), jpg_writer); - fwrite(&frames_encoded, 1, sizeof(int64_t), jpg_writer); - fwrite(&jpg_size, 1, sizeof(uint32_t), jpg_writer); - fwrite(jpg, 1, jpg_size, jpg_writer); - frame_count--; - frames_encoded++; - } - tjFree(jpg); - } - fclose(jpg_writer); - return NULL; - - } // jpgWriter -} -#endif diff --git a/src/recorder/mjpeg_writer.cpp b/src/recorder/mjpeg_writer.cpp new file mode 100644 index 000000000..93c78afed --- /dev/null +++ b/src/recorder/mjpeg_writer.cpp @@ -0,0 +1,69 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2017 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifdef ENABLE_RECORDER + +#include "capture_library.hpp" +#include "recorder_private.hpp" + +#include + +namespace Recorder +{ + // ------------------------------------------------------------------------ + void mjpegWriter(CaptureLibrary* cl) + { + setThreadName("mjpegWriter"); + FILE* mjpeg_writer = fopen((getSavedName() + ".video").c_str(), "wb"); + if (mjpeg_writer == NULL) + { + printf("Failed to open file for writing mjpeg.\n"); + return; + } + int64_t frames_encoded = 0; + while (true) + { + std::unique_lock ul(*cl->getJPGListMutex()); + cl->getJPGListCV()->wait(ul, [&cl] + { return !cl->getJPGList()->empty(); }); + auto& p = cl->getJPGList()->front(); + uint8_t* jpg = std::get<0>(p); + uint32_t jpg_size = std::get<1>(p); + int frame_count = std::get<2>(p); + if (jpg == NULL) + { + cl->getJPGList()->clear(); + ul.unlock(); + break; + } + cl->getJPGList()->pop_front(); + ul.unlock(); + while (frame_count != 0) + { + fwrite(&jpg_size, 1, sizeof(uint32_t), mjpeg_writer); + fwrite(&frames_encoded, 1, sizeof(int64_t), mjpeg_writer); + fwrite(&jpg_size, 1, sizeof(uint32_t), mjpeg_writer); + fwrite(jpg, 1, jpg_size, mjpeg_writer); + frame_count--; + frames_encoded++; + } + tjFree(jpg); + } + fclose(mjpeg_writer); + } // mjpegWriter +}; +#endif diff --git a/src/recorder/jpg_writer.hpp b/src/recorder/mjpeg_writer.hpp similarity index 87% rename from src/recorder/jpg_writer.hpp rename to src/recorder/mjpeg_writer.hpp index 25fef6b57..c7f261543 100644 --- a/src/recorder/jpg_writer.hpp +++ b/src/recorder/mjpeg_writer.hpp @@ -18,13 +18,16 @@ #ifdef ENABLE_RECORDER -#ifndef HEADER_JPG_WRITER_HPP -#define HEADER_JPG_WRITER_HPP +#ifndef HEADER_MJPEG_WRITER_HPP +#define HEADER_MJPEG_WRITER_HPP + +class CaptureLibrary; namespace Recorder { - void* jpgWriter(void *obj); + void mjpegWriter(CaptureLibrary* cl); }; + #endif #endif diff --git a/src/recorder/mkv_writer.cpp b/src/recorder/mkv_writer.cpp index b2ee9dcb0..004746072 100644 --- a/src/recorder/mkv_writer.cpp +++ b/src/recorder/mkv_writer.cpp @@ -17,12 +17,9 @@ #ifdef ENABLE_RECORDER -#include "config/user_config.hpp" -#include "graphics/irr_driver.hpp" -#include "recorder/recorder_common.hpp" -#include "utils/log.hpp" -#include "utils/string_utils.hpp" +#include "recorder_private.hpp" +#include #include #include #include @@ -35,28 +32,20 @@ namespace Recorder // ------------------------------------------------------------------------ std::string writeMKV(const std::string& video, const std::string& audio) { - time_t rawtime; - time(&rawtime); - tm* timeInfo = localtime(&rawtime); - char time_buffer[256]; - sprintf(time_buffer, "%i.%02i.%02i_%02i.%02i.%02i", - timeInfo->tm_year + 1900, timeInfo->tm_mon + 1, - timeInfo->tm_mday, timeInfo->tm_hour, - timeInfo->tm_min, timeInfo->tm_sec); - std::string no_ext = StringUtils::removeExtension(video); - VideoFormat vf = (VideoFormat)(int)UserConfigParams::m_record_format; - std::string file_name = no_ext + "-" + time_buffer + - (vf == VF_VP8 || vf == VF_VP9 ? ".webm" : ".mkv"); + std::string no_ext = video.substr(0, video.find_last_of(".")); + VideoFormat vf = getConfig()->m_video_format; + std::string file_name = no_ext + + (vf == REC_VF_VP8 || vf == REC_VF_VP9 ? ".webm" : ".mkv"); mkvmuxer::MkvWriter writer; if (!writer.Open(file_name.c_str())) { - Log::error("writeMKV", "Error while opening output file."); + printf("Error while opening output file.\n"); return ""; } mkvmuxer::Segment muxer_segment; if (!muxer_segment.Init(&writer)) { - Log::error("writeMKV", "Could not initialize muxer segment."); + printf("Could not initialize muxer segment.\n");; return ""; } std::list audio_frames; @@ -70,18 +59,18 @@ namespace Recorder uint32_t sample_rate, channels; fread(&sample_rate, 1, sizeof(uint32_t), input); fread(&channels, 1, sizeof(uint32_t), input); - uint64_t aud_track = muxer_segment.AddAudioTrack(sample_rate, - channels, 0); + uint64_t aud_track = muxer_segment.AddAudioTrack(sample_rate, channels, + 0); if (!aud_track) { - Log::error("writeMKV", "Could not add audio track."); + printf("Could not add audio track.\n"); return ""; } mkvmuxer::AudioTrack* const at = static_cast (muxer_segment.GetTrackByNumber(aud_track)); if (!at) { - Log::error("writeMKV", "Could not get audio track."); + printf("Could not get audio track.\n"); return ""; } uint32_t codec_private_size; @@ -89,7 +78,7 @@ namespace Recorder fread(buf, 1, codec_private_size, input); if (!at->SetCodecPrivate(buf, codec_private_size)) { - Log::warn("writeMKV", "Could not add audio private data."); + printf("Could not add audio private data.\n"); return ""; } while (fread(buf, 1, 12, input) == 12) @@ -102,7 +91,7 @@ namespace Recorder mkvmuxer::Frame* audio_frame = new mkvmuxer::Frame(); if (!audio_frame->Init(buf, frame_size)) { - Log::error("writeMKV", "Failed to construct a frame."); + printf("Failed to construct a frame.\n"); return ""; } audio_frame->set_track_number(aud_track); @@ -113,37 +102,36 @@ namespace Recorder fclose(input); if (remove(audio.c_str()) != 0) { - Log::warn("writeMKV", "Failed to remove audio data file"); + printf("Failed to remove audio data file\n"); } } - uint64_t vid_track = muxer_segment.AddVideoTrack( - irr_driver->getActualScreenSize().Width, - irr_driver->getActualScreenSize().Height, 0); + uint64_t vid_track = muxer_segment.AddVideoTrack(getConfig()->m_width, + getConfig()->m_height, 0); if (!vid_track) { - Log::error("writeMKV", "Could not add video track."); + printf("Could not add video track.\n"); return ""; } mkvmuxer::VideoTrack* const vt = static_cast( muxer_segment.GetTrackByNumber(vid_track)); if (!vt) { - Log::error("writeMKV", "Could not get video track."); + printf("Could not get video track.\n"); return ""; } - vt->set_frame_rate(UserConfigParams::m_record_fps); + vt->set_frame_rate(getConfig()->m_record_fps); switch (vf) { - case VF_VP8: + case REC_VF_VP8: vt->set_codec_id("V_VP8"); break; - case VF_VP9: + case REC_VF_VP9: vt->set_codec_id("V_VP9"); break; - case VF_MJPEG: + case REC_VF_MJPEG: vt->set_codec_id("V_MJPEG"); break; - case VF_H264: + case REC_VF_H264: vt->set_codec_id("V_MPEG4/ISO/AVC"); break; } @@ -156,17 +144,17 @@ namespace Recorder memcpy(×tamp, buf + sizeof(uint32_t), sizeof(int64_t)); memcpy(&flag, buf + sizeof(uint32_t) + sizeof(int64_t), sizeof(uint32_t)); - timestamp *= 1000000000ll / UserConfigParams::m_record_fps; + timestamp *= 1000000000ll / getConfig()->m_record_fps; fread(buf, 1, frame_size, input); mkvmuxer::Frame muxer_frame; if (!muxer_frame.Init(buf, frame_size)) { - Log::error("writeMKV", "Failed to construct a frame."); + printf("Failed to construct a frame.\n"); return ""; } muxer_frame.set_track_number(vid_track); muxer_frame.set_timestamp(timestamp); - if (vf == VF_VP8 || vf == VF_VP9) + if (vf == REC_VF_VP8 || vf == REC_VF_VP9) { muxer_frame.set_is_key((flag & VPX_FRAME_IS_KEY) != 0); } @@ -182,7 +170,7 @@ namespace Recorder { if (!muxer_segment.AddGenericFrame(cur_aud_frame)) { - Log::error("writeMKV", "Could not add audio frame."); + printf("Could not add audio frame.\n"); return ""; } delete cur_aud_frame; @@ -197,7 +185,7 @@ namespace Recorder } if (!muxer_segment.AddGenericFrame(&muxer_frame)) { - Log::error("writeMKV", "Could not add video frame."); + printf("Could not add video frame.\n"); return ""; } } @@ -209,15 +197,16 @@ namespace Recorder } if (remove(video.c_str()) != 0) { - Log::warn("writeMKV", "Failed to remove video data file"); + printf("Failed to remove video data file\n"); } if (!muxer_segment.Finalize()) { - Log::error("writeMKV", "Finalization of segment failed."); + printf("Finalization of segment failed.\n"); return ""; } writer.Close(); return file_name; } // writeMKV }; + #endif diff --git a/src/recorder/mkv_writer.hpp b/src/recorder/mkv_writer.hpp index 033068de3..7a3c2b607 100644 --- a/src/recorder/mkv_writer.hpp +++ b/src/recorder/mkv_writer.hpp @@ -26,6 +26,7 @@ namespace Recorder { std::string writeMKV(const std::string& video, const std::string& audio); }; + #endif #endif diff --git a/src/recorder/openglrecorder.h b/src/recorder/openglrecorder.h new file mode 100644 index 000000000..c60374328 --- /dev/null +++ b/src/recorder/openglrecorder.h @@ -0,0 +1,211 @@ +#ifdef ENABLE_RECORDER +#ifndef HEADER_OPENGLRECORDER_H +#define HEADER_OPENGLRECORDER_H + +/** + * \mainpage libopenglrecorder + * + * libopenglrecorder is a library allowing (optional) async readback opengl + * framebuffer with audio recording. It will do video and audio encoding + * together. The user of this library has to setup opengl context himself + * and load suitable callback. All function here should be called by the same + * thread which created the opengl context. + */ + +/** + * List of audio encoder supported by libopenglrecorder. + */ +enum AudioFormat +{ + /** + * Vorbis encoder by libvorbisenc. + */ + REC_AF_VORBIS, +}; + +/** + * List of video encoder supported by libopenglrecorder + */ +enum VideoFormat +{ + /** + * VP8 encoder by libvpx. + */ + REC_VF_VP8, + /** + * VP9 encoder by libvpx. Notice: this is very slow. + */ + REC_VF_VP9, + /** + * MJPEG encoder, it's provided by turbojpeg and will always present. + */ + REC_VF_MJPEG, + /** + * H264 encoder by openh264. + */ + REC_VF_H264 +}; + +/** + * Callback which takes a string pointer to work with. + */ +typedef void(*StringCallback)(const char* s, void* user_data); +/** + * Callback which takes a int to work with. + */ +typedef void(*IntCallback)(const int i, void* user_data); +/** + * Callback which takes nothing (void) to work with. + */ +typedef void(*GeneralCallback)(void* user_data); + +/** + * List of callbacks currently using. + */ +enum CallBackType +{ + /** + * A \ref GeneralCallback which notify the starting of recording. + */ + REC_CBT_START_RECORDING = 0, + /** + * A \ref StringCallback which notify the saved filename of recorded file. + */ + REC_CBT_SAVED_RECORDING, + /** + * A \ref GeneralCallback which notify error when recording. + */ + REC_CBT_ERROR_RECORDING, + /** + * A \ref IntCallback which the tells the progress percentage for video + * encoding after the issue of \ref ogrStopCapture. + */ + REC_CBT_PROGRESS_RECORDING, + /** + * A \ref GeneralCallback which notify user if there is still video + * encoding happening after the issue of \ref ogrStopCapture. + */ + REC_CBT_WAIT_RECORDING, + /** + * A \ref GeneralCallback which notify user if the coversion to jpeg + * from opengl frame buffer image is too slow, so libopenglrecorder will + * drop frames. + */ + REC_CBT_SLOW_RECORDING, + /** + * Total callback numbers. + */ + REC_CBT_COUNT +}; + +/** + * Settings for libopenglrecorder + */ +struct RecorderConfig +{ + /** + * 1 if triple buffering is used when capture the opengl buffer. + * It will create 3 pixel buffer objects for async reading, recommend on. + * 0 otherwise. + */ + int m_triple_buffering; + /** + * 1 if audio is recorded together, it will use wasapi in windows, + * pulseaudio in linux. 0 otherwise. + */ + int m_record_audio; + /** + * Width of the capture, it will be floored down to the closest integer divisble + * by 8 if needed. + */ + unsigned int m_width; + /** + * Height of the capture, it will be floored down to the closest integer divisble + * by 2 if needed. + */ + unsigned int m_height; + /** + * Encoder for video, see \ref VideoFormat. + */ + VideoFormat m_video_format; + /** + * Encoder for video, see \ref AudioFormat. + */ + AudioFormat m_audio_format; + /** + * Bitrate for audio encoding. + */ + unsigned int m_video_bitrate; + /** + * Bitrate for video encoding. + */ + unsigned int m_audio_bitrate; + /** + * Framerate for the video, 30 is recommended. + */ + unsigned int m_record_fps; + /** + * Jpeg quality for the captured image, from 0 to 100. + */ + unsigned int m_record_jpg_quality; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif +/** + * Initialize the configuration, call this first before using the library. + */ +void ogrInitConfig(RecorderConfig*); +/** + * Set the full path with filename for saving the recorded video, excluding + * extension, libopenglrecorder will automatically add .webm or .mkv as needed. + */ +void ogrSetSavedName(const char*); +/** + * Reset libopenglrecorder, call this before first \ref ogrCapture. + */ +void ogrPrepareCapture(void); +/** + * Capture the current frame buffer image as frame, make sure you have called + * \ref ogrPrepareCapture first. + */ +void ogrCapture(void); +/** + * Stop the recorder of libopenglrecorder. + */ +void ogrStopCapture(void); +/** + * Destroy the recorder of libopenglrecorder. + */ +void ogrDestroy(void); +/** + * (Optional) Register the callback(s) for \ref GeneralCallback, you have to + * make sure the enum CallBackType matches the callback type, see + * \ref CallBackType. + */ +void ogrRegGeneralCallback(CallBackType, GeneralCallback, void*); +/** + * (Optional) Register the callback(s) for \ref StringCallback, you have to + * make sure the enum CallBackType matches the callback type, see + * \ref CallBackType. + */ +void ogrRegStringCallback(CallBackType, StringCallback, void*); +/** + * (Optional) Register the callback(s) for \ref IntCallback, you have to + * make sure the enum CallBackType matches the callback type, see + * \ref CallBackType. + */ +void ogrRegIntCallback(CallBackType, IntCallback, void*); +/** + * Return 1 if recording is happening in libopenglrecorder, 0 otherwise. + */ +int ogrCapturing(void); +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/src/recorder/pulseaudio_recorder.cpp b/src/recorder/pulseaudio_recorder.cpp index 6abe955c7..6ba46c94f 100644 --- a/src/recorder/pulseaudio_recorder.cpp +++ b/src/recorder/pulseaudio_recorder.cpp @@ -17,13 +17,10 @@ #if defined(ENABLE_REC_SOUND) && !defined(WIN32) -#include "recorder/vorbis_encoder.hpp" -#include "utils/synchronised.hpp" -#include "utils/log.hpp" -#include "utils/vs.hpp" +#include "capture_library.hpp" +#include "recorder_private.hpp" +#include "vorbis_encoder.hpp" -#include -#include #include #include @@ -153,168 +150,147 @@ namespace Recorder m_dl_handle = dlopen("libpulse.so", RTLD_LAZY); if (m_dl_handle == NULL) { - Log::error("PulseAudioRecorder", "Failed to open PulseAudio" - " library"); + printf("Failed to open PulseAudio library\n"); return false; } pa_stream_new = (pa_stream_new_t)dlsym(m_dl_handle, "pa_stream_new"); if (pa_stream_new == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_stream_new'"); + printf("Cannot load function 'pa_stream_new'\n"); return false; } pa_stream_connect_record = (pa_stream_connect_record_t)dlsym (m_dl_handle, "pa_stream_connect_record"); if (pa_stream_connect_record == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_stream_connect_record'"); + printf("Cannot load function 'pa_stream_connect_record'\n"); return false; } pa_stream_get_state = (pa_stream_get_state_t)dlsym(m_dl_handle, "pa_stream_get_state"); if (pa_stream_get_state == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_stream_get_state'"); + printf("Cannot load function 'pa_stream_get_state'\n"); return false; } pa_stream_readable_size = (pa_stream_readable_size_t)dlsym (m_dl_handle, "pa_stream_readable_size"); if (pa_stream_readable_size == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_stream_readable_size'"); + printf("Cannot load function 'pa_stream_readable_size'\n"); return false; } pa_stream_peek = (pa_stream_peek_t)dlsym(m_dl_handle, "pa_stream_peek"); if (pa_stream_peek == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_stream_peek'"); + printf("Cannot load function 'pa_stream_peek'\n"); return false; } pa_stream_drop = (pa_stream_drop_t)dlsym(m_dl_handle, "pa_stream_drop"); if (pa_stream_drop == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_stream_drop'"); + printf("Cannot load function 'pa_stream_drop'\n"); return false; } pa_stream_disconnect = (pa_stream_disconnect_t)dlsym(m_dl_handle, "pa_stream_disconnect"); if (pa_stream_disconnect == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_stream_disconnect'"); + printf("Cannot load function 'pa_stream_disconnect'\n"); return false; } pa_stream_unref = (pa_stream_unref_t)dlsym(m_dl_handle, "pa_stream_unref"); if (pa_stream_unref == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_stream_unref'"); + printf("Cannot load function 'pa_stream_unref'\n"); return false; } pa_mainloop_new = (pa_mainloop_new_t)dlsym(m_dl_handle, "pa_mainloop_new"); if (pa_mainloop_new == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_mainloop_new'"); + printf("Cannot load function 'pa_mainloop_new'\n"); return false; } pa_mainloop_get_api = (pa_mainloop_get_api_t)dlsym(m_dl_handle, "pa_mainloop_get_api"); if (pa_mainloop_get_api == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_mainloop_get_api'"); + printf("Cannot load function 'pa_mainloop_get_api'\n"); return false; } pa_context_new = (pa_context_new_t)dlsym(m_dl_handle, "pa_context_new"); if (pa_context_new == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_context_new'"); + printf("Cannot load function 'pa_context_new'\n"); return false; } pa_context_connect = (pa_context_connect_t)dlsym(m_dl_handle, "pa_context_connect"); if (pa_context_connect == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_context_connect'"); + printf("Cannot load function 'pa_context_connect'\n"); return false; } pa_mainloop_iterate = (pa_mainloop_iterate_t)dlsym(m_dl_handle, "pa_mainloop_iterate"); if (pa_mainloop_iterate == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_mainloop_iterate'"); + printf("Cannot load function 'pa_mainloop_iterate'\n"); return false; } pa_context_get_state = (pa_context_get_state_t)dlsym(m_dl_handle, "pa_context_get_state"); if (pa_context_get_state == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_context_get_state'"); + printf("Cannot load function 'pa_context_get_state'\n"); return false; } pa_context_get_server_info = (pa_context_get_server_info_t)dlsym (m_dl_handle, "pa_context_get_server_info"); if (pa_context_get_server_info == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_context_get_server_info'"); + printf("Cannot load function 'pa_context_get_server_info'\n"); return false; } pa_operation_get_state = (pa_operation_get_state_t)dlsym (m_dl_handle, "pa_operation_get_state"); if (pa_operation_get_state == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_operation_get_state'"); + printf("Cannot load function 'pa_operation_get_state'\n"); return false; } pa_operation_unref = (pa_operation_unref_t)dlsym(m_dl_handle, "pa_operation_unref"); if (pa_operation_unref == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_operation_unref'"); + printf("Cannot load function 'pa_operation_unref'\n"); return false; } pa_context_disconnect = (pa_context_disconnect_t)dlsym(m_dl_handle, "pa_context_disconnect"); if (pa_context_disconnect == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_context_disconnect'"); + printf("Cannot load function 'pa_context_disconnect'\n"); return false; } pa_context_unref = (pa_context_unref_t)dlsym(m_dl_handle, "pa_context_unref"); if (pa_context_unref == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_context_unref'"); + printf("Cannot load function 'pa_context_unref'\n"); return false; } pa_mainloop_free = (pa_mainloop_free_t)dlsym(m_dl_handle, "pa_mainloop_free"); if (pa_mainloop_free == NULL) { - Log::error("PulseAudioRecorder", "Cannot load function" - " 'pa_mainloop_free'"); + printf("Cannot load function 'pa_mainloop_free'\n"); return false; } return true; @@ -337,14 +313,14 @@ namespace Recorder m_loop = pa_mainloop_new(); if (m_loop == NULL) { - Log::error("PulseAudioRecorder", "Failed to create mainloop"); + printf("Failed to create mainloop\n"); return false; } m_context = pa_context_new(pa_mainloop_get_api(m_loop), "audioRecord"); if (m_context == NULL) { - Log::error("PulseAudioRecorder", "Failed to create context"); + printf("Failed to create context\n"); return false; } pa_context_connect(m_context, NULL, PA_CONTEXT_NOAUTOSPAWN , NULL); @@ -356,8 +332,7 @@ namespace Recorder break; if (!PA_CONTEXT_IS_GOOD(state)) { - Log::error("PulseAudioRecorder", "Failed to connect to" - " context"); + printf("Failed to connect to context\n"); return false; } } @@ -370,7 +345,7 @@ namespace Recorder pa_operation_unref(pa_op); if (m_default_sink.empty()) { - Log::error("PulseAudioRecorder", "Failed to get default sink"); + printf("Failed to get default sink\n"); return false; } m_default_sink += ".monitor"; @@ -382,11 +357,11 @@ namespace Recorder return true; } // load // -------------------------------------------------------------------- - void configAudioType(Recorder::VorbisEncoderData* ved) + void configAudioType(AudioEncoderData* aed) { - ved->m_sample_rate = m_sample_spec.rate; - ved->m_channels = m_sample_spec.channels; - ved->m_audio_type = Recorder::VorbisEncoderData::AT_PCM; + aed->m_sample_rate = m_sample_spec.rate; + aed->m_channels = m_sample_spec.channels; + aed->m_audio_type = AudioEncoderData::AT_PCM; } // configAudioType // -------------------------------------------------------------------- inline void mainLoopIterate() @@ -478,51 +453,59 @@ namespace Recorder // ======================================================================== PulseAudioData g_pa_data; // ======================================================================== - void* audioRecorder(void *obj) + void audioRecorder(CaptureLibrary* cl) { - VS::setThreadName("audioRecorder"); + setThreadName("audioRecorder"); if (!g_pa_data.m_loaded) { if (!g_pa_data.load()) { - Log::error("PulseAudioRecord", "Cannot pulseaudio data"); - return NULL; + printf("Cannot load pulseaudio data.\n"); + return; } } if (g_pa_data.createRecordStream() == false) { - Log::error("PulseAudioRecorder", "Failed to create stream"); + printf("Failed to create audio record stream.\n"); if (g_pa_data.m_stream != NULL) { g_pa_data.removeRecordStream(); } - return NULL; + return; } - Synchronised* idle = (Synchronised*)obj; - Synchronised > pcm_data; - pthread_cond_t enc_request; - pthread_cond_init(&enc_request, NULL); - pthread_t vorbis_enc; - Recorder::VorbisEncoderData ved; - g_pa_data.configAudioType(&ved); - ved.m_data = &pcm_data; - ved.m_enc_request = &enc_request; + std::list pcm_data; + std::mutex pcm_mutex; + std::condition_variable pcm_cv; + std::thread audio_enc_thread; + + AudioEncoderData aed; + g_pa_data.configAudioType(&aed); + aed.m_buf_list = &pcm_data; + aed.m_mutex = &pcm_mutex; + aed.m_cv = &pcm_cv; + aed.m_audio_bitrate = cl->getRecorderConfig().m_audio_bitrate; const unsigned frag_size = 1024 * g_pa_data.m_sample_spec.channels * sizeof(int16_t); - pthread_create(&vorbis_enc, NULL, &Recorder::vorbisEncoder, &ved); + + switch (cl->getRecorderConfig().m_audio_format) + { + case REC_AF_VORBIS: + audio_enc_thread = std::thread(vorbisEncoder, &aed); + break; + } + int8_t* each_pcm_buf = new int8_t[frag_size](); unsigned readed = 0; while (true) { - if (idle->getAtomic()) + if (cl->getSoundStop()) { - pcm_data.lock(); - pcm_data.getData().push_back(each_pcm_buf); - pcm_data.getData().push_back(NULL); - pthread_cond_signal(&enc_request); - pcm_data.unlock(); + std::lock_guard lock(pcm_mutex); + pcm_data.push_back(each_pcm_buf); + pcm_data.push_back(NULL); + pcm_cv.notify_one(); break; } g_pa_data.mainLoopIterate(); @@ -544,10 +527,10 @@ namespace Recorder memcpy(each_pcm_buf + readed, data, copy_size); if (buf_full) { - pcm_data.lock(); - pcm_data.getData().push_back(each_pcm_buf); - pthread_cond_signal(&enc_request); - pcm_data.unlock(); + std::unique_lock ul(pcm_mutex); + pcm_data.push_back(each_pcm_buf); + pcm_cv.notify_one(); + ul.unlock(); each_pcm_buf = new int8_t[frag_size](); readed = (unsigned)bytes - copy_size; memcpy(each_pcm_buf, (uint8_t*)data + copy_size, readed); @@ -558,10 +541,8 @@ namespace Recorder } g_pa_data.dropStream(); } - pthread_join(vorbis_enc, NULL); - pthread_cond_destroy(&enc_request); + audio_enc_thread.join(); g_pa_data.removeRecordStream(); - return NULL; } // audioRecorder } #endif diff --git a/src/recorder/pulseaudio_recorder.hpp b/src/recorder/pulseaudio_recorder.hpp index 86abbc557..a16bf49ea 100644 --- a/src/recorder/pulseaudio_recorder.hpp +++ b/src/recorder/pulseaudio_recorder.hpp @@ -20,12 +20,13 @@ #ifndef HEADER_PULSEAUDIO_RECORD_HPP #define HEADER_PULSEAUDIO_RECORD_HPP +class CaptureLibrary; namespace Recorder { #ifdef ENABLE_REC_SOUND - void* audioRecorder(void *obj); + void audioRecorder(CaptureLibrary* cl); #else - inline void* audioRecorder(void *obj) { return NULL; } + inline void audioRecorder(CaptureLibrary* cl) {} #endif }; diff --git a/src/recorder/recorder.cpp b/src/recorder/recorder.cpp new file mode 100644 index 000000000..d0dd5cc60 --- /dev/null +++ b/src/recorder/recorder.cpp @@ -0,0 +1,265 @@ +#ifdef ENABLE_RECORDER + +#include "capture_library.hpp" +#include "recorder_private.hpp" + +#include +#include +#include +#include + +// ============================================================================ +std::unique_ptr g_recorder_config(nullptr); +// ============================================================================ +std::unique_ptr g_capture_library(nullptr); +// ============================================================================ +std::atomic_bool g_capturing(false); +// ============================================================================ +std::string g_saved_name; +// ============================================================================ +StringCallback g_cb_saved_rec = NULL; +// ============================================================================ +IntCallback g_cb_progress_rec = NULL; +// ============================================================================ +GeneralCallback g_cb_wait_rec = NULL; +// ============================================================================ +GeneralCallback g_cb_start_rec = NULL; +// ============================================================================ +GeneralCallback g_cb_error_rec = NULL; +// ============================================================================ +GeneralCallback g_cb_slow_rec = NULL; +// ============================================================================ +std::array g_all_user_data; +// ============================================================================ +void ogrInitConfig(RecorderConfig* rc) +{ + RecorderConfig* new_rc = new RecorderConfig; + memcpy(new_rc, rc, sizeof(RecorderConfig)); + while (new_rc->m_width % 8 != 0) + { + new_rc->m_width--; + } + while (new_rc->m_height % 2 != 0) + { + new_rc->m_height--; + } + g_recorder_config.reset(new_rc); +} // ogrInitConfig + +// ---------------------------------------------------------------------------- +RecorderConfig* getConfig() +{ + assert(g_recorder_config.get() != nullptr); + return g_recorder_config.get(); +} // getConfig + +// ---------------------------------------------------------------------------- +void ogrSetSavedName(const char* name) +{ + g_saved_name = name; +} // ogrSetSavedName + +// ---------------------------------------------------------------------------- +const std::string& getSavedName() +{ + return g_saved_name; +} // getSavedName + +// ---------------------------------------------------------------------------- +void ogrPrepareCapture(void) +{ + assert(g_recorder_config.get() != nullptr && !g_saved_name.empty()); + if (g_capture_library.get() == nullptr) + { + assert(g_recorder_config.get() != nullptr); + g_capture_library.reset(new CaptureLibrary(getConfig())); + } + g_capture_library.get()->reset(); +} // ogrPrepareCapture + +// ---------------------------------------------------------------------------- +void ogrCapture(void) +{ + g_capture_library.get()->capture(); +} // ogrCapture + +// ---------------------------------------------------------------------------- +void ogrStopCapture(void) +{ + g_capture_library.get()->stopCapture(); +} // ogrStopCapture + +// ---------------------------------------------------------------------------- +void ogrDestroy(void) +{ + delete g_capture_library.release(); +} // ogrDestroy + +// ---------------------------------------------------------------------------- +void ogrRegGeneralCallback(CallBackType cbt, GeneralCallback cb, void* data) +{ + switch (cbt) + { + case REC_CBT_ERROR_RECORDING: + g_cb_error_rec = cb; + g_all_user_data[REC_CBT_ERROR_RECORDING] = data; + break; + case REC_CBT_START_RECORDING: + g_cb_start_rec = cb; + g_all_user_data[REC_CBT_START_RECORDING] = data; + break; + case REC_CBT_SLOW_RECORDING: + g_cb_slow_rec = cb; + g_all_user_data[REC_CBT_SLOW_RECORDING] = data; + break; + case REC_CBT_WAIT_RECORDING: + g_cb_wait_rec = cb; + g_all_user_data[REC_CBT_WAIT_RECORDING] = data; + break; + default: + assert(false && "Wrong callback enum"); + break; + } +} // ogrRegGeneralCallback + +// ---------------------------------------------------------------------------- +void ogrRegStringCallback(CallBackType cbt, StringCallback cb, void* data) +{ + switch (cbt) + { + case REC_CBT_SAVED_RECORDING: + g_cb_saved_rec = cb; + g_all_user_data[REC_CBT_SAVED_RECORDING] = data; + break; + default: + assert(false && "Wrong callback enum"); + break; + } +} // ogrRegStringCallback + +// ---------------------------------------------------------------------------- +void ogrRegIntCallback(CallBackType cbt, IntCallback cb, void* data) +{ + switch (cbt) + { + case REC_CBT_PROGRESS_RECORDING: + g_cb_progress_rec = cb; + g_all_user_data[REC_CBT_PROGRESS_RECORDING] = data; + break; + default: + assert(false && "Wrong callback enum"); + break; + } +} // ogrRegIntCallback + +// ---------------------------------------------------------------------------- +void runCallback(CallBackType cbt, const void* arg) +{ + switch (cbt) + { + case REC_CBT_START_RECORDING: + { + if (g_cb_start_rec == NULL) return; + g_cb_start_rec(g_all_user_data[REC_CBT_START_RECORDING]); + break; + } + case REC_CBT_SAVED_RECORDING: + { + if (g_cb_saved_rec == NULL) return; + const char* s = (const char*)arg; + g_cb_saved_rec(s, g_all_user_data[REC_CBT_SAVED_RECORDING]); + break; + } + case REC_CBT_ERROR_RECORDING: + { + if (g_cb_error_rec == NULL) return; + g_cb_error_rec(g_all_user_data[REC_CBT_ERROR_RECORDING]); + break; + } + case REC_CBT_PROGRESS_RECORDING: + { + if (g_cb_progress_rec == NULL) return; + const int* i = (const int*)arg; + g_cb_progress_rec(*i, g_all_user_data[REC_CBT_PROGRESS_RECORDING]); + break; + } + case REC_CBT_WAIT_RECORDING: + { + if (g_cb_wait_rec == NULL) return; + g_cb_wait_rec(g_all_user_data[REC_CBT_WAIT_RECORDING]); + break; + } + case REC_CBT_SLOW_RECORDING: + { + if (g_cb_slow_rec == NULL) return; + g_cb_slow_rec(g_all_user_data[REC_CBT_SLOW_RECORDING]); + break; + } + default: + break; + } +} // runCallback + +// ---------------------------------------------------------------------------- +int ogrCapturing(void) +{ + return g_capturing.load() ? 1 : 0; +} // ogrCapturing + +// ---------------------------------------------------------------------------- +void setCapturing(bool val) +{ + g_capturing.store(val); +} // setCapturing + +// ---------------------------------------------------------------------------- +/** This function sets the name of this thread in the debugger. + * \param name Name of the thread. + */ +#if defined(_MSC_VER) && defined(DEBUG) +# define WIN32_LEAN_AND_MEAN +# include + + void setThreadName(const char *name) + { + const DWORD MS_VC_EXCEPTION=0x406D1388; +#pragma pack(push,8) + typedef struct tagTHREADNAME_INFO + { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + } THREADNAME_INFO; +#pragma pack(pop) + + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = -1; + info.dwFlags = 0; + + __try + { + RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), + (ULONG_PTR*)&info ); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } + + } // setThreadName +#elif defined(__linux__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__) + void setThreadName(const char* name) + { +#if __GLIBC__ > 2 || __GLIBC_MINOR__ > 11 + pthread_setname_np(pthread_self(), name); +#endif + } // setThreadName +#else + void setThreadName(const char* name) + { + } // setThreadName +#endif + +#endif diff --git a/src/recorder/recorder_common.cpp b/src/recorder/recorder_common.cpp deleted file mode 100644 index d7c0a807d..000000000 --- a/src/recorder/recorder_common.cpp +++ /dev/null @@ -1,401 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef ENABLE_RECORDER - -#include "recorder/recorder_common.hpp" -#include "config/user_config.hpp" -#include "graphics/irr_driver.hpp" -#include "graphics/gl_headers.hpp" -#include "guiengine/message_queue.hpp" -#include "recorder/jpg_writer.hpp" -#include "recorder/mkv_writer.hpp" -#include "recorder/pulseaudio_recorder.hpp" -#include "recorder/vpx_encoder.hpp" -#include "recorder/wasapi_recorder.hpp" -#include "utils/synchronised.hpp" -#include "utils/translation.hpp" -#include "utils/vs.hpp" - -#include -#include -#include - -namespace Recorder -{ - // ======================================================================== - tjhandle g_compress_handle; - // ======================================================================== - Synchronised > > g_jpg_list; - // ======================================================================== - pthread_cond_t g_jpg_request; - // ======================================================================== - ThreadData g_jpg_thread_data; - // ======================================================================== - int bmpToJPG(uint8_t* raw, unsigned width, unsigned height, - uint8_t** jpeg_buffer, unsigned long* jpeg_size) - { - int ret = 0; -#ifdef TJFLAG_FASTDCT - ret = tjCompress2(g_compress_handle, raw, width, 0, height, TJPF_BGR, - jpeg_buffer, jpeg_size, TJSAMP_420, - UserConfigParams::m_recorder_jpg_quality, TJFLAG_FASTDCT); -#else - ret = tjCompress2(g_compress_handle, raw, width, 0, height, TJPF_BGR, - jpeg_buffer, jpeg_size, TJSAMP_420, - UserConfigParams::m_recorder_jpg_quality, 0); -#endif - if (ret != 0) - { - char* err = tjGetErrorStr(); - Log::error("RecorderCommon", "Jpeg encode error: %s.", err); - return ret; - } - return ret; - } // bmpToJPG - // ======================================================================== - pthread_t g_audio_thread; - // ======================================================================== - Synchronised g_video_thread(NULL); - // ======================================================================== - Synchronised g_idle(true); - // ======================================================================== - bool g_destroy; - // ======================================================================== - std::string g_recording_name; - // ======================================================================== - Synchronised g_display_progress(false); - // ======================================================================== - void* fbiConversion(void* obj) - { - VS::setThreadName("fbiConversion"); - ThreadData* td = (ThreadData*)obj; - Synchronised > >* fbi_queue = - (Synchronised > >*)td->m_data; - pthread_cond_t* cond_request = td->m_request; - while (true) - { - fbi_queue->lock(); - bool waiting = fbi_queue->getData().empty(); - while (waiting) - { - pthread_cond_wait(cond_request, fbi_queue->getMutex()); - waiting = fbi_queue->getData().empty(); - } - auto& p = fbi_queue->getData().front(); - uint8_t* fbi = p.first; - int frame_count = p.second; - if (frame_count == -1) - { - fbi_queue->getData().clear(); - fbi_queue->unlock(); - g_idle.setAtomic(true); - pthread_join(g_audio_thread, NULL); - g_jpg_list.lock(); - if (!g_destroy && g_jpg_list.getData().size() > 100) - { - MessageQueue::add(MessageQueue::MT_GENERIC, - _("Please wait while encoding is finished.")); - } - g_jpg_list.getData().emplace_back((uint8_t*)NULL, 0, 0); - pthread_cond_signal(&g_jpg_request); - g_jpg_list.unlock(); - g_display_progress.setAtomic(true); - pthread_join(*g_video_thread.getData(), NULL); - delete g_video_thread.getData(); - g_video_thread.setAtomic(NULL); - std::string f = Recorder::writeMKV(g_recording_name + ".video", - g_recording_name + ".audio"); - g_display_progress.setAtomic(false); - if (g_destroy) - { - return NULL; - } - if (f.empty()) - { - MessageQueue::add(MessageQueue::MT_ERROR, - _("Error when saving video.")); - } - else - { - MessageQueue::add(MessageQueue::MT_GENERIC, - _("Video saved in \"%s\".", f.c_str())); - } - continue; - } - else if (fbi == NULL) - { - fbi_queue->getData().clear(); - fbi_queue->unlock(); - if (g_destroy) - { - return NULL; - } - continue; - } - const bool too_slow = fbi_queue->getData().size() > 50; - if (too_slow) - { - MessageQueue::add(MessageQueue::MT_ERROR, - _("Encoding is too slow, dropping frames.")); - delete [] fbi; - fbi_queue->getData().pop_front(); - for (auto& p : fbi_queue->getData()) - delete [] p.first; - fbi_queue->getData().clear(); - fbi_queue->unlock(); - continue; - } - fbi_queue->getData().pop_front(); - fbi_queue->unlock(); - uint8_t* orig_fbi = fbi; - const unsigned width = irr_driver->getActualScreenSize().Width; - const unsigned height = irr_driver->getActualScreenSize().Height; - const unsigned area = width * height; - int size = area * 4; - int dest = size - 3; - int src = size - 4; - int copied = 0; - while (true) - { - if (copied++ > 1) - memcpy(fbi + dest, fbi + src, 3); - else - memmove(fbi + dest, fbi + src, 3); - if (src == 0) - break; - dest -= 3; - src -= 4; - } - fbi = fbi + area; - const int pitch = width * 3; - uint8_t* p2 = fbi + (height - 1) * pitch; - uint8_t* tmp_buf = new uint8_t[pitch]; - for (unsigned i = 0; i < height; i += 2) - { - memcpy(tmp_buf, fbi, pitch); - memcpy(fbi, p2, pitch); - memcpy(p2, tmp_buf, pitch); - fbi += pitch; - p2 -= pitch; - } - delete [] tmp_buf; - uint8_t* jpg = NULL; - unsigned long jpg_size = 0; - bmpToJPG(orig_fbi + area, width, height, &jpg, &jpg_size); - delete[] orig_fbi; - g_jpg_list.lock(); - g_jpg_list.getData().emplace_back(jpg, jpg_size, frame_count); - pthread_cond_signal(&g_jpg_request); - g_jpg_list.unlock(); - } - return NULL; - } // fbiConversion - // ======================================================================== - struct CommonData : public NoCopy - { - GLuint m_pbo[3]; - unsigned m_pbo_use; - bool m_loaded; - Synchronised > > m_fbi_queue; - pthread_cond_t m_fbi_request; - pthread_t m_fbi_thread; - ThreadData m_common_thread_data; - // -------------------------------------------------------------------- - void addFrameBufferImage(uint8_t* fbi, int frame_count) - { - m_fbi_queue.lock(); - m_fbi_queue.getData().emplace_back(fbi, frame_count); - pthread_cond_signal(&m_fbi_request); - m_fbi_queue.unlock(); - } // addFrameBufferImage - // -------------------------------------------------------------------- - CommonData() - { - m_loaded = false; - m_pbo_use = 0; - g_compress_handle = tjInitCompress(); - } // CommonData - // -------------------------------------------------------------------- - ~CommonData() - { - destroy(); - tjDestroy(g_compress_handle); - } // ~CommonData - // -------------------------------------------------------------------- - void destroy() - { - if (m_loaded) - { - glDeleteBuffers(3, m_pbo); - addFrameBufferImage(NULL, 0); - pthread_join(m_fbi_thread, NULL); - pthread_cond_destroy(&m_fbi_request); - pthread_cond_destroy(&g_jpg_request); - g_destroy = false; - } - m_loaded = false; - } // destroy - // -------------------------------------------------------------------- - void load() - { - if (m_loaded) return; - m_loaded = true; - glGenBuffers(3, m_pbo); - for (int i = 0; i < 3; i++) - { - glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[i]); - glBufferData(GL_PIXEL_PACK_BUFFER, - irr_driver->getActualScreenSize().Width * - irr_driver->getActualScreenSize().Height * 4, NULL, - GL_STREAM_READ); - } - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - pthread_cond_init(&m_fbi_request, NULL); - pthread_cond_init(&g_jpg_request, NULL); - m_common_thread_data.m_data = &m_fbi_queue; - m_common_thread_data.m_request = &m_fbi_request; - g_jpg_thread_data.m_data = &g_jpg_list; - g_jpg_thread_data.m_request = &g_jpg_request; - pthread_create(&m_fbi_thread, NULL, &fbiConversion, - &m_common_thread_data); - } // load - }; - // ======================================================================== - std::chrono::high_resolution_clock::time_point g_framerate_timer; - // ======================================================================== - double g_accumulated_time; - // ======================================================================== - CommonData g_common_data; - // ======================================================================== - void setRecordingName(const std::string& name) - { - g_recording_name = name; - } // setRecordingName - // ------------------------------------------------------------------------ - const std::string& getRecordingName() - { - return g_recording_name; - } // getRecordingName - // ------------------------------------------------------------------------ - void prepareCapture() - { - g_common_data.load(); - g_common_data.m_pbo_use = 0; - g_accumulated_time = 0.; - assert(g_idle.getAtomic() && g_video_thread.getAtomic() == NULL); - g_idle.setAtomic(false); - pthread_create(&g_audio_thread, NULL, &Recorder::audioRecorder, - &g_idle); - g_video_thread.setAtomic(new pthread_t()); - VideoFormat vf = (VideoFormat)(int)UserConfigParams::m_record_format; - switch (vf) - { - case VF_VP8: - case VF_VP9: - pthread_create(g_video_thread.getAtomic(), NULL, - &Recorder::vpxEncoder, &g_jpg_thread_data); - break; - case VF_MJPEG: - pthread_create(g_video_thread.getAtomic(), NULL, - &Recorder::jpgWriter, &g_jpg_thread_data); - break; - case VF_H264: - break; - } - } // prepareCapture - // ------------------------------------------------------------------------ - int getFrameCount(double rate) - { - const double frame_rate = 1. / double(UserConfigParams::m_record_fps); - g_accumulated_time += rate; - if (g_accumulated_time < frame_rate) - { - return 0; - } - int frame_count = 0; - while (g_accumulated_time >= frame_rate) - { - frame_count++; - g_accumulated_time = g_accumulated_time - frame_rate; - } - return frame_count; - } // getFrameCount - // ------------------------------------------------------------------------ - void captureFrameBufferImage() - { - assert(g_common_data.m_loaded); - int pbo_read = -1; - if (g_common_data.m_pbo_use > 3 && g_common_data.m_pbo_use % 3 == 0) - g_common_data.m_pbo_use = 3; - auto rate = std::chrono::high_resolution_clock::now() - - g_framerate_timer; - g_framerate_timer = std::chrono::high_resolution_clock::now(); - glReadBuffer(GL_BACK); - const unsigned width = irr_driver->getActualScreenSize().Width; - const unsigned height = irr_driver->getActualScreenSize().Height; - if (g_common_data.m_pbo_use >= 3) - { - int frame_count = getFrameCount(std::chrono::duration_cast - >(rate).count()); - if (frame_count != 0) - { - pbo_read = g_common_data.m_pbo_use % 3; - glBindBuffer(GL_PIXEL_PACK_BUFFER, - g_common_data.m_pbo[pbo_read]); - void* ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); - const unsigned size = width * height * 4; - uint8_t* fbi = new uint8_t[size]; - memcpy(fbi, ptr, size); - glUnmapBuffer(GL_PIXEL_PACK_BUFFER); - g_common_data.addFrameBufferImage(fbi, frame_count); - } - } - int pbo_use = g_common_data.m_pbo_use++ % 3; - assert(pbo_read == -1 || pbo_use == pbo_read); - glBindBuffer(GL_PIXEL_PACK_BUFFER, g_common_data.m_pbo[pbo_use]); - glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, NULL); - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - } // captureFrameBufferImage - // ------------------------------------------------------------------------ - void stopRecording() - { - if (!isRecording()) - { - g_common_data.addFrameBufferImage(NULL, -1); - } - } // stopRecording - // ------------------------------------------------------------------------ - bool isRecording() - { - return g_video_thread.getAtomic() == NULL; - } // isRecording - // ------------------------------------------------------------------------ - void destroyRecorder() - { - g_destroy = true; - stopRecording(); - g_common_data.destroy(); - } // destroyRecorder - // ------------------------------------------------------------------------ - bool displayProgress() - { - return g_display_progress.getAtomic(); - } // displayProgress - -} -#endif diff --git a/src/recorder/recorder_common.hpp b/src/recorder/recorder_common.hpp deleted file mode 100644 index 4815ee1d1..000000000 --- a/src/recorder/recorder_common.hpp +++ /dev/null @@ -1,60 +0,0 @@ - -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef ENABLE_RECORDER - -#ifndef HEADER_RECORDER_COMMON_HPP -#define HEADER_RECORDER_COMMON_HPP - -#include "utils/no_copy.hpp" - -#include -#include -#include - -namespace Recorder -{ - // ------------------------------------------------------------------------ - enum VideoFormat { VF_VP8, VF_VP9, VF_MJPEG, VF_H264 }; - // ------------------------------------------------------------------------ - struct ThreadData : public NoCopy - { - void* m_data; - pthread_cond_t* m_request; - }; - // ------------------------------------------------------------------------ - void setRecordingName(const std::string& name); - // ------------------------------------------------------------------------ - const std::string& getRecordingName(); - // ------------------------------------------------------------------------ - void prepareCapture(); - // ------------------------------------------------------------------------ - void captureFrameBufferImage(); - // ------------------------------------------------------------------------ - void stopRecording(); - // ------------------------------------------------------------------------ - bool isRecording(); - // ------------------------------------------------------------------------ - void destroyRecorder(); - // ------------------------------------------------------------------------ - bool displayProgress(); - -}; -#endif - -#endif diff --git a/src/recorder/recorder_private.hpp b/src/recorder/recorder_private.hpp new file mode 100644 index 000000000..df0e1515e --- /dev/null +++ b/src/recorder/recorder_private.hpp @@ -0,0 +1,17 @@ +#ifdef ENABLE_RECORDER +#ifndef HEADER_RECORDER_PRIVATE_HPP +#define HEADER_RECORDER_PRIVATE_HPP + +#include "openglrecorder.h" + +#include + +RecorderConfig* getConfig(); +const std::string& getSavedName(); +void setCapturing(bool val); +void setThreadName(const char* name); +void runCallback(CallBackType cbt, const void* arg); + +#endif + +#endif diff --git a/src/recorder/vorbis_encoder.cpp b/src/recorder/vorbis_encoder.cpp index 23d081ab7..cacd641cf 100644 --- a/src/recorder/vorbis_encoder.cpp +++ b/src/recorder/vorbis_encoder.cpp @@ -17,32 +17,29 @@ #ifdef ENABLE_RECORDER -#include "recorder/vorbis_encoder.hpp" -#include "recorder/recorder_common.hpp" -#include "utils/log.hpp" -#include "utils/synchronised.hpp" -#include "utils/vs.hpp" +#include "capture_library.hpp" +#include "recorder_private.hpp" #include #include namespace Recorder { - void* vorbisEncoder(void *obj) + void vorbisEncoder(AudioEncoderData* aed) { - VS::setThreadName("vorbisEncoder"); - VorbisEncoderData* ved = (VorbisEncoderData*)obj; + setThreadName("vorbisEncoder"); vorbis_info vi; vorbis_dsp_state vd; vorbis_block vb; vorbis_info_init(&vi); - vorbis_encode_init(&vi, ved->m_channels, ved->m_sample_rate, -1, - 112000, -1); + vorbis_encode_init(&vi, aed->m_channels, aed->m_sample_rate, -1, + aed->m_audio_bitrate, -1); vorbis_analysis_init(&vd, &vi); vorbis_block_init(&vd, &vb); vorbis_comment vc; vorbis_comment_init(&vc); - vorbis_comment_add_tag(&vc, "ENCODER", "STK vorbis encoder"); + vorbis_comment_add_tag(&vc, "Encoder", + "vorbis encoder by libopenglrecorder"); ogg_packet header; ogg_packet header_comm; ogg_packet header_code; @@ -50,18 +47,17 @@ namespace Recorder &header_code); if (header.bytes > 255 || header_comm.bytes > 255) { - Log::error("vorbisEncoder", "Header is too long."); - return NULL; + printf("Header is too long for vorbis.\n"); + return; } - FILE* vb_data = fopen((getRecordingName() + ".audio").c_str(), "wb"); + FILE* vb_data = fopen((getSavedName() + ".audio").c_str(), "wb"); if (vb_data == NULL) { - Log::error("vorbisEncoder", "Failed to open file for encoding" - " vorbis."); - return NULL; + printf("Failed to open file for encoding vorbis.\n"); + return; } - fwrite(&ved->m_sample_rate, 1, sizeof(uint32_t), vb_data); - fwrite(&ved->m_channels, 1, sizeof(uint32_t), vb_data); + fwrite(&aed->m_sample_rate, 1, sizeof(uint32_t), vb_data); + fwrite(&aed->m_channels, 1, sizeof(uint32_t), vb_data); const uint32_t all = header.bytes + header_comm.bytes + header_code.bytes + 3; fwrite(&all, 1, sizeof(uint32_t), vb_data); @@ -74,24 +70,16 @@ namespace Recorder fwrite(header.packet, 1, header.bytes, vb_data); fwrite(header_comm.packet, 1, header_comm.bytes, vb_data); fwrite(header_code.packet, 1, header_code.bytes, vb_data); - Synchronised >* audio_data = - (Synchronised >*)ved->m_data; - pthread_cond_t* cond_request = ved->m_enc_request; ogg_packet op; int64_t last_timestamp = 0; bool eos = false; while (eos == false) { - audio_data->lock(); - bool waiting = audio_data->getData().empty(); - while (waiting) - { - pthread_cond_wait(cond_request, audio_data->getMutex()); - waiting = audio_data->getData().empty(); - } - int8_t* audio_buf = audio_data->getData().front(); - audio_data->getData().pop_front(); - audio_data->unlock(); + std::unique_lock ul(*aed->m_mutex); + aed->m_cv->wait(ul, [&aed] { return !aed->m_buf_list->empty(); }); + int8_t* audio_buf = aed->m_buf_list->front(); + aed->m_buf_list->pop_front(); + ul.unlock(); if (audio_buf == NULL) { vorbis_analysis_wrote(&vd, 0); @@ -100,8 +88,8 @@ namespace Recorder else { float **buffer = vorbis_analysis_buffer(&vd, 1024); - const unsigned channels = ved->m_channels; - if (ved->m_audio_type == VorbisEncoderData::AT_PCM) + const unsigned channels = aed->m_channels; + if (aed->m_audio_type == AudioEncoderData::AT_PCM) { for (unsigned j = 0; j < channels; j++) { @@ -110,7 +98,7 @@ namespace Recorder int8_t* each_channel = &audio_buf[i * channels * 2 + j * 2]; buffer[j][i] = float((each_channel[1] << 8) | - (0x00ff & (int)each_channel[0])) / 32768.0f; + (0x00ff & (int)each_channel[0])) / 32768.0f; } } } @@ -140,7 +128,7 @@ namespace Recorder fwrite(&last_timestamp, 1, sizeof(int64_t), vb_data); fwrite(op.packet, 1, frame_size, vb_data); double s = (double)op.granulepos / - (double)ved->m_sample_rate * 1000000000.; + (double)aed->m_sample_rate * 1000000000.; last_timestamp = (int64_t)s; } } @@ -152,8 +140,6 @@ namespace Recorder vorbis_comment_clear(&vc); vorbis_info_clear(&vi); fclose(vb_data); - return NULL; - } // vorbisEncoder } #endif diff --git a/src/recorder/vorbis_encoder.hpp b/src/recorder/vorbis_encoder.hpp index 6ac305536..9ba87602e 100644 --- a/src/recorder/vorbis_encoder.hpp +++ b/src/recorder/vorbis_encoder.hpp @@ -20,24 +20,10 @@ #ifndef HEADER_VORBIS_ENCODE_HPP #define HEADER_VORBIS_ENCODE_HPP -#include "utils/no_copy.hpp" -#include "utils/types.hpp" - -#include - +struct AudioEncoderData; namespace Recorder { - struct VorbisEncoderData : public NoCopy - { - enum AudioType { AT_FLOAT, AT_PCM }; - void* m_data; - pthread_cond_t* m_enc_request; - uint32_t m_sample_rate; - uint32_t m_channels; - AudioType m_audio_type; - }; - - void* vorbisEncoder(void *obj); + void vorbisEncoder(AudioEncoderData* aed); }; #endif diff --git a/src/recorder/vpx_encoder.cpp b/src/recorder/vpx_encoder.cpp index 3870c6f09..601fefb77 100644 --- a/src/recorder/vpx_encoder.cpp +++ b/src/recorder/vpx_encoder.cpp @@ -17,65 +17,15 @@ #if defined(ENABLE_RECORDER) && !defined(NO_VPX) -#include "config/user_config.hpp" -#include "graphics/irr_driver.hpp" -#include "recorder/recorder_common.hpp" -#include "utils/log.hpp" -#include "utils/synchronised.hpp" -#include "utils/vs.hpp" +#include "capture_library.hpp" +#include "recorder_private.hpp" -#include -#include #include #include namespace Recorder { - // ======================================================================== - struct JPGDecoder - { - tjhandle m_handle; - // -------------------------------------------------------------------- - JPGDecoder() - { - m_handle = tjInitDecompress(); - } // JPGDecoder - // -------------------------------------------------------------------- - ~JPGDecoder() - { - tjDestroy(m_handle); - } // ~JPGDecoder - // -------------------------------------------------------------------- - int yuvConversion(uint8_t* jpeg_buffer, unsigned jpeg_size, - uint8_t** yuv_buffer, unsigned* yuv_size) - { - int width, height; - TJSAMP subsample; - int ret = 0; - ret = tjDecompressHeader2(m_handle, jpeg_buffer, jpeg_size, &width, - &height, (int*)&subsample); - if (ret != 0) - { - char* err = tjGetErrorStr(); - Log::error("vpxEncoder", "Jpeg decode error: %s.", err); - return ret; - } - *yuv_size = tjBufSizeYUV(width, height, subsample); - *yuv_buffer = new uint8_t[*yuv_size]; - ret = tjDecompressToYUV(m_handle, jpeg_buffer, jpeg_size, - *yuv_buffer, 0); - if (ret != 0) - { - char* err = tjGetErrorStr(); - Log::error("vpxEncoder", "YUV conversion error: %s.", err); - return ret; - } - return ret; - } // yuvConversion - }; - // ======================================================================== - JPGDecoder g_jpg_decoder; - // ======================================================================== + // ------------------------------------------------------------------------ int vpxEncodeFrame(vpx_codec_ctx_t *codec, vpx_image_t *img, int frame_index, FILE *out) { @@ -86,7 +36,7 @@ namespace Recorder 1, 0, VPX_DL_REALTIME); if (res != VPX_CODEC_OK) { - Log::error("vpxEncoder", "Failed to encode frame"); + printf("Failed to encode frame\n"); return -1; } while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != NULL) @@ -104,101 +54,85 @@ namespace Recorder return got_pkts; } // vpxEncodeFrame // ------------------------------------------------------------------------ - void* vpxEncoder(void *obj) + void vpxEncoder(CaptureLibrary* cl) { - VS::setThreadName("vpxEncoder"); - FILE* vpx_data = fopen((getRecordingName() + ".video").c_str(), "wb"); + setThreadName("vpxEncoder"); + FILE* vpx_data = fopen((getSavedName() + ".video").c_str(), "wb"); if (vpx_data == NULL) { - Log::error("vpxEncoder", "Failed to open file for writing"); - return NULL; + printf("Failed to open file for writing vpx.\n"); + return; } - ThreadData* td = (ThreadData*)obj; - Synchronised > >* - jpg_data = (Synchronised > >*)td->m_data; - pthread_cond_t* cond_request = td->m_request; vpx_codec_ctx_t codec; vpx_codec_enc_cfg_t cfg; vpx_codec_iface_t* codec_if = NULL; - VideoFormat vf = (VideoFormat)(int)UserConfigParams::m_record_format; - switch (vf) + switch (cl->getRecorderConfig().m_video_format) { - case VF_VP8: + case REC_VF_VP8: codec_if = vpx_codec_vp8_cx(); break; - case VF_VP9: + case REC_VF_VP9: codec_if = vpx_codec_vp9_cx(); break; - case VF_MJPEG: - case VF_H264: + case REC_VF_MJPEG: + case REC_VF_H264: assert(false); break; } vpx_codec_err_t res = vpx_codec_enc_config_default(codec_if, &cfg, 0); if (res > 0) { - Log::error("vpxEncoder", "Failed to get default codec config."); - return NULL; + printf("Failed to get default vpx codec config.\n"); + return; } - const unsigned width = irr_driver->getActualScreenSize().Width; - const unsigned height = irr_driver->getActualScreenSize().Height; + const unsigned width = cl->getRecorderConfig().m_width; + const unsigned height = cl->getRecorderConfig().m_height; int frames_encoded = 0; cfg.g_w = width; cfg.g_h = height; cfg.g_timebase.num = 1; - cfg.g_timebase.den = UserConfigParams::m_record_fps; - int end_usage = UserConfigParams::m_vp_end_usage; - cfg.rc_end_usage = (vpx_rc_mode)end_usage; - cfg.rc_target_bitrate = UserConfigParams::m_vp_bitrate; + cfg.g_timebase.den = cl->getRecorderConfig().m_record_fps; + cfg.rc_end_usage = VPX_VBR; + cfg.rc_target_bitrate = cl->getRecorderConfig().m_video_bitrate; if (vpx_codec_enc_init(&codec, codec_if, &cfg, 0) > 0) { - Log::error("vpxEncoder", "Failed to initialize encoder"); + printf("Failed to initialize vpx encoder\n"); fclose(vpx_data); - return NULL; + return; } - std::chrono::high_resolution_clock::time_point tp; + float last_size = -1.0f; + int cur_finished_count = 0; while (true) { - jpg_data->lock(); - bool waiting = jpg_data->getData().empty(); - while (waiting) - { - pthread_cond_wait(cond_request, jpg_data->getMutex()); - waiting = jpg_data->getData().empty(); - } - - if (displayProgress()) - { - auto rate = std::chrono::high_resolution_clock::now() - - tp; - double t = std::chrono::duration_cast >(rate).count(); - if (t > 3.) - { - tp = std::chrono::high_resolution_clock::now(); - Log::info("vpxEncoder", "%d frames remaining.", - jpg_data->getData().size()); - } - } - auto& p = jpg_data->getData().front(); + std::unique_lock ul(*cl->getJPGListMutex()); + cl->getJPGListCV()->wait(ul, [&cl] + { return !cl->getJPGList()->empty(); }); + auto& p = cl->getJPGList()->front(); uint8_t* jpg = std::get<0>(p); - unsigned jpg_size = std::get<1>(p); + uint32_t jpg_size = std::get<1>(p); int frame_count = std::get<2>(p); if (jpg == NULL) { - jpg_data->getData().clear(); - jpg_data->unlock(); + cl->getJPGList()->clear(); + ul.unlock(); break; } - jpg_data->getData().pop_front(); - jpg_data->unlock(); + cl->getJPGList()->pop_front(); + ul.unlock(); + if (!cl->getDestroy() && cl->displayingProgress()) + { + if (last_size == -1.0f) + last_size = (float)(cl->getJPGList()->size()); + cur_finished_count += frame_count; + int rate = (int)(cur_finished_count / last_size * 100.0f); + runCallback(REC_CBT_PROGRESS_RECORDING, &rate); + } uint8_t* yuv = NULL; unsigned yuv_size; - int ret = g_jpg_decoder.yuvConversion(jpg, jpg_size, &yuv, + int ret = cl->yuvConversion(jpg, jpg_size, &yuv, &yuv_size); if (ret < 0) { @@ -221,12 +155,10 @@ namespace Recorder while (vpxEncodeFrame(&codec, NULL, -1, vpx_data)); if (vpx_codec_destroy(&codec)) { - Log::error("vpxEncoder", "Failed to destroy codec."); - return NULL; + printf("Failed to destroy vpx codec.\n"); + return; } fclose(vpx_data); - return NULL; - } // vpxEncoder } #endif diff --git a/src/recorder/vpx_encoder.hpp b/src/recorder/vpx_encoder.hpp index 61f1e8d6f..12c935c96 100644 --- a/src/recorder/vpx_encoder.hpp +++ b/src/recorder/vpx_encoder.hpp @@ -21,12 +21,14 @@ #ifndef HEADER_VPX_ENCODER_HPP #define HEADER_VPX_ENCODER_HPP +class CaptureLibrary; + namespace Recorder { #ifdef NO_VPX - inline void* vpxEncoder(void *obj) { return NULL; } + inline void vpxEncoder(CaptureLibrary* cl) {} #else - void* vpxEncoder(void *obj); + void vpxEncoder(CaptureLibrary* cl); #endif }; #endif diff --git a/src/recorder/wasapi_recorder.cpp b/src/recorder/wasapi_recorder.cpp index 86d65bbf7..e81843852 100644 --- a/src/recorder/wasapi_recorder.cpp +++ b/src/recorder/wasapi_recorder.cpp @@ -17,12 +17,9 @@ #if defined(ENABLE_REC_SOUND) && defined(WIN32) -#include "recorder/vorbis_encoder.hpp" -#include "utils/synchronised.hpp" -#include "utils/log.hpp" -#include "utils/vs.hpp" - -#include +#include "capture_library.hpp" +#include "recorder_private.hpp" +#include "vorbis_encoder.hpp" #include #include @@ -31,7 +28,7 @@ #include #if defined (__MINGW32__) || defined(__CYGWIN__) - #include "utils/types.hpp" + #include inline GUID uuidFromString(const char* s) { unsigned long p0; @@ -136,111 +133,114 @@ namespace Recorder // ======================================================================== WasapiData g_wasapi_data; // ======================================================================== - void* audioRecorder(void *obj) + void audioRecorder(CaptureLibrary* cl) { - VS::setThreadName("audioRecorder"); + setThreadName("audioRecorder"); if (!g_wasapi_data.m_loaded) { if (!g_wasapi_data.load()) { - Log::error("WasapiRecorder", "Failed to load wasapi data"); - return NULL; + printf("Failed to load wasapi data.\n"); + return; } } - VorbisEncoderData ved = {}; + AudioEncoderData aed = {}; if (g_wasapi_data.m_wav_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE* wav_for_ext = (WAVEFORMATEXTENSIBLE*)g_wasapi_data.m_wav_format; - ved.m_channels = wav_for_ext->Format.nChannels; - ved.m_sample_rate = wav_for_ext->Format.nSamplesPerSec; + aed.m_channels = wav_for_ext->Format.nChannels; + aed.m_sample_rate = wav_for_ext->Format.nSamplesPerSec; if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_PCM, wav_for_ext->SubFormat)) { - ved.m_audio_type = VorbisEncoderData::AT_PCM; + aed.m_audio_type = AudioEncoderData::AT_PCM; if (wav_for_ext->Format.wBitsPerSample != 16) { - Log::error("WasapiRecorder", "Only 16bit PCM is" - " supported."); - return NULL; + printf("Only 16bit PCM is supported.\n"); + return; } } else if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wav_for_ext ->SubFormat)) { - ved.m_audio_type = VorbisEncoderData::AT_FLOAT; + aed.m_audio_type = AudioEncoderData::AT_FLOAT; if (wav_for_ext->Format.wBitsPerSample != 32) { - Log::error("WasapiRecorder", "Only 32bit float is" - " supported."); - return NULL; + printf("Only 32bit float is supported.\n"); + return; } } else { - Log::error("WasapiRecorder", "Unsupported audio input" - " format."); - return NULL; + printf("Unsupported audio input format.\n"); + return; } } else if (g_wasapi_data.m_wav_format->wFormatTag == WAVE_FORMAT_PCM) { - ved.m_channels = g_wasapi_data.m_wav_format->nChannels; - ved.m_sample_rate = g_wasapi_data.m_wav_format->nSamplesPerSec; - ved.m_audio_type = VorbisEncoderData::AT_PCM; + aed.m_channels = g_wasapi_data.m_wav_format->nChannels; + aed.m_sample_rate = g_wasapi_data.m_wav_format->nSamplesPerSec; + aed.m_audio_type = AudioEncoderData::AT_PCM; if (g_wasapi_data.m_wav_format->wBitsPerSample != 16) { - Log::error("WasapiRecorder", "Only 16bit PCM is supported."); - return NULL; + printf("Only 16bit PCM is supported.\n"); + return; } } else { - Log::error("WasapiRecorder", "Unsupported audio input format"); - return NULL; + printf("Unsupported audio input format\n"); + return; } - if (ved.m_sample_rate > 48000) + if (aed.m_sample_rate > 48000) { - Log::error("WasapiRecorder", "Only support maximum 48000hz sample " - "rate audio."); - return NULL; + printf("Only support maximum 48000hz sample rate audio.\n"); + return; } HRESULT hr = g_wasapi_data.m_client->Reset(); if (FAILED(hr)) { - Log::error("WasapiRecorder", "Failed to reset recorder"); - return NULL; + printf("Failed to reset audio recorder.\n"); + return; } hr = g_wasapi_data.m_client->Start(); if (FAILED(hr)) { - Log::error("WasapiRecorder", "Failed to start recorder"); - return NULL; + printf("Failed to start audio recorder.\n"); + return; } REFERENCE_TIME duration = REFTIMES_PER_SEC * g_wasapi_data.m_buffer_size / g_wasapi_data.m_wav_format ->nSamplesPerSec; - Synchronised* idle = (Synchronised*)obj; - Synchronised > audio_data; - pthread_cond_t enc_request; - pthread_cond_init(&enc_request, NULL); - pthread_t vorbis_enc; - ved.m_data = &audio_data; - ved.m_enc_request = &enc_request; - pthread_create(&vorbis_enc, NULL, &Recorder::vorbisEncoder, &ved); - const unsigned frag_size = 1024 * ved.m_channels * + std::list audio_data; + std::mutex audio_mutex; + std::condition_variable audio_cv; + std::thread audio_enc_thread; + aed.m_buf_list = &audio_data; + aed.m_mutex = &audio_mutex; + aed.m_cv = &audio_cv; + aed.m_audio_bitrate = cl->getRecorderConfig().m_audio_bitrate; + + switch (cl->getRecorderConfig().m_audio_format) + { + case REC_AF_VORBIS: + audio_enc_thread = std::thread(vorbisEncoder, &aed); + break; + } + + const unsigned frag_size = 1024 * aed.m_channels * (g_wasapi_data.m_wav_format->wBitsPerSample / 8); int8_t* each_audio_buf = new int8_t[frag_size](); unsigned readed = 0; while (true) { - if (idle->getAtomic()) + if (cl->getSoundStop()) { - audio_data.lock(); - audio_data.getData().push_back(each_audio_buf); - audio_data.getData().push_back(NULL); - pthread_cond_signal(&enc_request); - audio_data.unlock(); + std::lock_guard lock(audio_mutex); + audio_data.push_back(each_audio_buf); + audio_data.push_back(NULL); + audio_cv.notify_one(); break; } uint32_t packet_length = 0; @@ -248,7 +248,7 @@ namespace Recorder &packet_length); if (FAILED(hr)) { - Log::warn("WasapiRecorder", "Failed to get next packet size"); + printf("Failed to get next audio packet size\n"); } if (packet_length == 0) { @@ -262,9 +262,9 @@ namespace Recorder &packet_length, &flags, NULL, NULL); if (FAILED(hr)) { - Log::warn("WasapiRecorder", "Failed to get buffer"); + printf("Failed to get audio buffer\n"); } - const unsigned bytes = ved.m_channels * (g_wasapi_data.m_wav_format + const unsigned bytes = aed.m_channels * (g_wasapi_data.m_wav_format ->wBitsPerSample / 8) * packet_length; bool buf_full = readed + bytes > frag_size; unsigned copy_size = buf_full ? frag_size - readed : bytes; @@ -274,12 +274,12 @@ namespace Recorder } if (buf_full) { - audio_data.lock(); - audio_data.getData().push_back(each_audio_buf); - pthread_cond_signal(&enc_request); - audio_data.unlock(); + std::unique_lock ul(audio_mutex); + audio_data.push_back(each_audio_buf); + audio_cv.notify_one(); + ul.unlock(); each_audio_buf = new int8_t[frag_size](); - readed = bytes - copy_size; + readed = (unsigned)bytes - copy_size; if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT)) { memcpy(each_audio_buf, (uint8_t*)data + copy_size, readed); @@ -292,18 +292,15 @@ namespace Recorder hr = g_wasapi_data.m_capture_client->ReleaseBuffer(packet_length); if (FAILED(hr)) { - Log::warn("WasapiRecorder", "Failed to release buffer"); + printf("Failed to release audio buffer\n"); } } hr = g_wasapi_data.m_client->Stop(); if (FAILED(hr)) { - Log::warn("WasapiRecorder", "Failed to stop recorder"); + printf("Failed to stop audio recorder\n"); } - pthread_join(vorbis_enc, NULL); - pthread_cond_destroy(&enc_request); - - return NULL; + audio_enc_thread.join(); } // audioRecorder } #endif diff --git a/src/recorder/wasapi_recorder.hpp b/src/recorder/wasapi_recorder.hpp index ac5bc0599..401e08ff0 100644 --- a/src/recorder/wasapi_recorder.hpp +++ b/src/recorder/wasapi_recorder.hpp @@ -20,12 +20,13 @@ #ifndef HEADER_WASAPI_RECORD_HPP #define HEADER_WASAPI_RECORD_HPP +class CaptureLibrary; namespace Recorder { #ifdef ENABLE_REC_SOUND - void* audioRecorder(void *obj); + void audioRecorder(CaptureLibrary* cl); #else - inline void* audioRecorder(void *obj) { return NULL; } + inline void audioRecorder(CaptureLibrary* cl) {} #endif }; From a5759afac79976d5b4fe1d88a8eff88f1bf08920 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 9 Apr 2017 15:28:28 +0800 Subject: [PATCH 02/14] Improvements from Stragus and leyyin --- src/graphics/irr_driver.cpp | 20 +++--- src/recorder/capture_library.cpp | 18 +++--- src/recorder/mkv_writer.cpp | 12 ++-- src/recorder/openglrecorder.h | 50 +++++++++------ src/recorder/pulseaudio_recorder.cpp | 2 +- src/recorder/recorder.cpp | 91 +++++++++++++++++++--------- src/recorder/vorbis_encoder.cpp | 2 +- src/recorder/vpx_encoder.cpp | 10 +-- src/recorder/wasapi_recorder.cpp | 2 +- 9 files changed, 130 insertions(+), 77 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index a01acb61b..719cba17b 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -612,32 +612,36 @@ void IrrDriver::initDevice() cfg.m_height = m_actual_screen_size.Height; int vf = UserConfigParams::m_record_format; cfg.m_video_format = (VideoFormat)vf; - cfg.m_audio_format = REC_AF_VORBIS; + cfg.m_audio_format = OGR_AF_VORBIS; cfg.m_audio_bitrate = 112000; cfg.m_video_bitrate = UserConfigParams::m_vp_bitrate; cfg.m_record_fps = UserConfigParams::m_record_fps; cfg.m_record_jpg_quality = UserConfigParams::m_recorder_jpg_quality; - ogrInitConfig(&cfg); + if (ogrInitConfig(&cfg) == 0) + { + Log::error("irr_driver", + "RecorderConfig is invalid, use the default one."); + } - ogrRegGeneralCallback(REC_CBT_START_RECORDING, + ogrRegGeneralCallback(OGR_CBT_START_RECORDING, [] (void* user_data) { MessageQueue::add (MessageQueue::MT_GENERIC, _("Video recording started.")); }, NULL); - ogrRegGeneralCallback(REC_CBT_ERROR_RECORDING, + ogrRegGeneralCallback(OGR_CBT_ERROR_RECORDING, [] (void* user_data) { MessageQueue::add (MessageQueue::MT_ERROR, _("Error when saving video.")); }, NULL); - ogrRegGeneralCallback(REC_CBT_SLOW_RECORDING, + ogrRegGeneralCallback(OGR_CBT_SLOW_RECORDING, [] (void* user_data) { MessageQueue::add (MessageQueue::MT_ERROR, _("Encoding is too slow, dropping frames.")); }, NULL); - ogrRegGeneralCallback(REC_CBT_WAIT_RECORDING, + ogrRegGeneralCallback(OGR_CBT_WAIT_RECORDING, [] (void* user_data) { MessageQueue::add (MessageQueue::MT_GENERIC, _("Please wait while encoding is finished." )); }, NULL); - ogrRegStringCallback(REC_CBT_SAVED_RECORDING, + ogrRegStringCallback(OGR_CBT_SAVED_RECORDING, [] (const char* s, void* user_data) { MessageQueue::add (MessageQueue::MT_GENERIC, _("Video saved in \"%s\".", s)); }, NULL); - ogrRegIntCallback(REC_CBT_PROGRESS_RECORDING, + ogrRegIntCallback(OGR_CBT_PROGRESS_RECORDING, [] (const int i, void* user_data) { Log::info("Recorder", "%d%% of video encoding finished", i);}, NULL); diff --git a/src/recorder/capture_library.cpp b/src/recorder/capture_library.cpp index 110cfead9..ba8b81786 100644 --- a/src/recorder/capture_library.cpp +++ b/src/recorder/capture_library.cpp @@ -59,7 +59,7 @@ CaptureLibrary::~CaptureLibrary() // ---------------------------------------------------------------------------- void CaptureLibrary::reset() { - runCallback(REC_CBT_START_RECORDING, NULL); + runCallback(OGR_CBT_START_RECORDING, NULL); m_pbo_use = 0; m_accumulated_time = 0.; assert(m_sound_stop.load() && ogrCapturing() == 0); @@ -71,14 +71,14 @@ void CaptureLibrary::reset() setCapturing(true); switch (m_recorder_cfg->m_video_format) { - case REC_VF_VP8: - case REC_VF_VP9: + case OGR_VF_VP8: + case OGR_VF_VP9: m_video_enc_thread = std::thread(Recorder::vpxEncoder, this); break; - case REC_VF_MJPEG: + case OGR_VF_MJPEG: m_video_enc_thread = std::thread(Recorder::mjpegWriter, this); break; - case REC_VF_H264: + case OGR_VF_H264: break; } } // reset @@ -221,7 +221,7 @@ void CaptureLibrary::captureConversion(CaptureLibrary* cl) std::unique_lock ulj(cl->m_jpg_list_mutex); if (!cl->m_destroy.load() && cl->m_jpg_list.size() > 100) { - runCallback(REC_CBT_WAIT_RECORDING, NULL); + runCallback(OGR_CBT_WAIT_RECORDING, NULL); } cl->m_display_progress.store(true); cl->m_jpg_list.emplace_back((uint8_t*)NULL, 0, 0); @@ -237,11 +237,11 @@ void CaptureLibrary::captureConversion(CaptureLibrary* cl) } if (f.empty()) { - runCallback(REC_CBT_ERROR_RECORDING, NULL); + runCallback(OGR_CBT_ERROR_RECORDING, NULL); } else { - runCallback(REC_CBT_SAVED_RECORDING, f.c_str()); + runCallback(OGR_CBT_SAVED_RECORDING, f.c_str()); } setCapturing(false); continue; @@ -255,7 +255,7 @@ void CaptureLibrary::captureConversion(CaptureLibrary* cl) const bool too_slow = cl->m_fbi_list.size() > 50; if (too_slow) { - runCallback(REC_CBT_SLOW_RECORDING, NULL); + runCallback(OGR_CBT_SLOW_RECORDING, NULL); delete [] fbi; cl->m_fbi_list.pop_front(); for (auto& p : cl->m_fbi_list) diff --git a/src/recorder/mkv_writer.cpp b/src/recorder/mkv_writer.cpp index 004746072..3b354897c 100644 --- a/src/recorder/mkv_writer.cpp +++ b/src/recorder/mkv_writer.cpp @@ -35,7 +35,7 @@ namespace Recorder std::string no_ext = video.substr(0, video.find_last_of(".")); VideoFormat vf = getConfig()->m_video_format; std::string file_name = no_ext + - (vf == REC_VF_VP8 || vf == REC_VF_VP9 ? ".webm" : ".mkv"); + (vf == OGR_VF_VP8 || vf == OGR_VF_VP9 ? ".webm" : ".mkv"); mkvmuxer::MkvWriter writer; if (!writer.Open(file_name.c_str())) { @@ -122,16 +122,16 @@ namespace Recorder vt->set_frame_rate(getConfig()->m_record_fps); switch (vf) { - case REC_VF_VP8: + case OGR_VF_VP8: vt->set_codec_id("V_VP8"); break; - case REC_VF_VP9: + case OGR_VF_VP9: vt->set_codec_id("V_VP9"); break; - case REC_VF_MJPEG: + case OGR_VF_MJPEG: vt->set_codec_id("V_MJPEG"); break; - case REC_VF_H264: + case OGR_VF_H264: vt->set_codec_id("V_MPEG4/ISO/AVC"); break; } @@ -154,7 +154,7 @@ namespace Recorder } muxer_frame.set_track_number(vid_track); muxer_frame.set_timestamp(timestamp); - if (vf == REC_VF_VP8 || vf == REC_VF_VP9) + if (vf == OGR_VF_VP8 || vf == OGR_VF_VP9) { muxer_frame.set_is_key((flag & VPX_FRAME_IS_KEY) != 0); } diff --git a/src/recorder/openglrecorder.h b/src/recorder/openglrecorder.h index c60374328..1ef089f7d 100644 --- a/src/recorder/openglrecorder.h +++ b/src/recorder/openglrecorder.h @@ -6,21 +6,27 @@ * \mainpage libopenglrecorder * * libopenglrecorder is a library allowing (optional) async readback opengl - * framebuffer with audio recording. It will do video and audio encoding + * frame buffer with audio recording. It will do video and audio encoding * together. The user of this library has to setup opengl context himself * and load suitable callback. All function here should be called by the same * thread which created the opengl context. */ /** - * List of audio encoder supported by libopenglrecorder. + * List of audio encoder supported by libopenglrecorder, if you want to record + * without sound, just set m_record_audio in \ref RecorderConfig to 0 and use + * any encoder below. */ enum AudioFormat { /** * Vorbis encoder by libvorbisenc. */ - REC_AF_VORBIS, + OGR_AF_VORBIS = 0, + /** + * Total numbers of audio encoder. + */ + OGR_AF_COUNT }; /** @@ -31,19 +37,23 @@ enum VideoFormat /** * VP8 encoder by libvpx. */ - REC_VF_VP8, + OGR_VF_VP8 = 0, /** * VP9 encoder by libvpx. Notice: this is very slow. */ - REC_VF_VP9, + OGR_VF_VP9, /** * MJPEG encoder, it's provided by turbojpeg and will always present. */ - REC_VF_MJPEG, + OGR_VF_MJPEG, /** * H264 encoder by openh264. */ - REC_VF_H264 + OGR_VF_H264, + /** + * Total numbers of video encoder. + */ + OGR_VF_COUNT }; /** @@ -67,35 +77,35 @@ enum CallBackType /** * A \ref GeneralCallback which notify the starting of recording. */ - REC_CBT_START_RECORDING = 0, + OGR_CBT_START_RECORDING = 0, /** * A \ref StringCallback which notify the saved filename of recorded file. */ - REC_CBT_SAVED_RECORDING, + OGR_CBT_SAVED_RECORDING, /** * A \ref GeneralCallback which notify error when recording. */ - REC_CBT_ERROR_RECORDING, + OGR_CBT_ERROR_RECORDING, /** * A \ref IntCallback which the tells the progress percentage for video * encoding after the issue of \ref ogrStopCapture. */ - REC_CBT_PROGRESS_RECORDING, + OGR_CBT_PROGRESS_RECORDING, /** * A \ref GeneralCallback which notify user if there is still video * encoding happening after the issue of \ref ogrStopCapture. */ - REC_CBT_WAIT_RECORDING, + OGR_CBT_WAIT_RECORDING, /** * A \ref GeneralCallback which notify user if the coversion to jpeg * from opengl frame buffer image is too slow, so libopenglrecorder will * drop frames. */ - REC_CBT_SLOW_RECORDING, + OGR_CBT_SLOW_RECORDING, /** * Total callback numbers. */ - REC_CBT_COUNT + OGR_CBT_COUNT }; /** @@ -104,16 +114,16 @@ enum CallBackType struct RecorderConfig { /** - * 1 if triple buffering is used when capture the opengl buffer. + * 1 if triple buffering is used when capture the opengl frame buffer. * It will create 3 pixel buffer objects for async reading, recommend on. * 0 otherwise. */ - int m_triple_buffering; + unsigned int m_triple_buffering; /** * 1 if audio is recorded together, it will use wasapi in windows, - * pulseaudio in linux. 0 otherwise. + * pulseaudio in linux. 0 means no audio will be recorded. */ - int m_record_audio; + unsigned int m_record_audio; /** * Width of the capture, it will be floored down to the closest integer divisble * by 8 if needed. @@ -156,8 +166,10 @@ extern "C" #endif /** * Initialize the configuration, call this first before using the library. + * \return 1 if succesfully configured, 0 otherwise and a default + * configuration will be used. */ -void ogrInitConfig(RecorderConfig*); +int ogrInitConfig(RecorderConfig*); /** * Set the full path with filename for saving the recorded video, excluding * extension, libopenglrecorder will automatically add .webm or .mkv as needed. diff --git a/src/recorder/pulseaudio_recorder.cpp b/src/recorder/pulseaudio_recorder.cpp index 6ba46c94f..1beed24e6 100644 --- a/src/recorder/pulseaudio_recorder.cpp +++ b/src/recorder/pulseaudio_recorder.cpp @@ -491,7 +491,7 @@ namespace Recorder switch (cl->getRecorderConfig().m_audio_format) { - case REC_AF_VORBIS: + case OGR_AF_VORBIS: audio_enc_thread = std::thread(vorbisEncoder, &aed); break; } diff --git a/src/recorder/recorder.cpp b/src/recorder/recorder.cpp index d0dd5cc60..0543f0756 100644 --- a/src/recorder/recorder.cpp +++ b/src/recorder/recorder.cpp @@ -29,11 +29,48 @@ GeneralCallback g_cb_error_rec = NULL; // ============================================================================ GeneralCallback g_cb_slow_rec = NULL; // ============================================================================ -std::array g_all_user_data; +std::array g_all_user_data; // ============================================================================ -void ogrInitConfig(RecorderConfig* rc) +bool validateConfig(RecorderConfig* rc) +{ + if (rc == NULL) + return false; + if (rc->m_triple_buffering > 1 || rc->m_record_audio > 1) + return false; + if (rc->m_width > 16384 || rc->m_height > 16384) + return false; + if (rc->m_video_format >= OGR_VF_COUNT || + rc->m_audio_format >= OGR_AF_COUNT) + return false; + if (rc->m_audio_bitrate == 0 || rc->m_video_bitrate == 0 || + rc->m_record_fps == 0) + return false; + if (rc->m_record_jpg_quality > 100) + return false; + return true; +} // validateConfig + +// ---------------------------------------------------------------------------- +int ogrInitConfig(RecorderConfig* rc) { RecorderConfig* new_rc = new RecorderConfig; + g_recorder_config.reset(new_rc); + + if (!validateConfig(rc)) + { + new_rc->m_triple_buffering = 1; + new_rc->m_record_audio = 0; + new_rc->m_width = 800; + new_rc->m_height = 600; + new_rc->m_video_format = OGR_VF_MJPEG; + new_rc->m_audio_format = OGR_AF_VORBIS; + new_rc->m_audio_bitrate = 112000; + new_rc->m_video_bitrate = 100000; + new_rc->m_record_fps = 30; + new_rc->m_record_jpg_quality = 90; + return 0; + } + memcpy(new_rc, rc, sizeof(RecorderConfig)); while (new_rc->m_width % 8 != 0) { @@ -43,7 +80,7 @@ void ogrInitConfig(RecorderConfig* rc) { new_rc->m_height--; } - g_recorder_config.reset(new_rc); + return 1; } // ogrInitConfig // ---------------------------------------------------------------------------- @@ -100,21 +137,21 @@ void ogrRegGeneralCallback(CallBackType cbt, GeneralCallback cb, void* data) { switch (cbt) { - case REC_CBT_ERROR_RECORDING: + case OGR_CBT_ERROR_RECORDING: g_cb_error_rec = cb; - g_all_user_data[REC_CBT_ERROR_RECORDING] = data; + g_all_user_data[OGR_CBT_ERROR_RECORDING] = data; break; - case REC_CBT_START_RECORDING: + case OGR_CBT_START_RECORDING: g_cb_start_rec = cb; - g_all_user_data[REC_CBT_START_RECORDING] = data; + g_all_user_data[OGR_CBT_START_RECORDING] = data; break; - case REC_CBT_SLOW_RECORDING: + case OGR_CBT_SLOW_RECORDING: g_cb_slow_rec = cb; - g_all_user_data[REC_CBT_SLOW_RECORDING] = data; + g_all_user_data[OGR_CBT_SLOW_RECORDING] = data; break; - case REC_CBT_WAIT_RECORDING: + case OGR_CBT_WAIT_RECORDING: g_cb_wait_rec = cb; - g_all_user_data[REC_CBT_WAIT_RECORDING] = data; + g_all_user_data[OGR_CBT_WAIT_RECORDING] = data; break; default: assert(false && "Wrong callback enum"); @@ -127,9 +164,9 @@ void ogrRegStringCallback(CallBackType cbt, StringCallback cb, void* data) { switch (cbt) { - case REC_CBT_SAVED_RECORDING: + case OGR_CBT_SAVED_RECORDING: g_cb_saved_rec = cb; - g_all_user_data[REC_CBT_SAVED_RECORDING] = data; + g_all_user_data[OGR_CBT_SAVED_RECORDING] = data; break; default: assert(false && "Wrong callback enum"); @@ -142,9 +179,9 @@ void ogrRegIntCallback(CallBackType cbt, IntCallback cb, void* data) { switch (cbt) { - case REC_CBT_PROGRESS_RECORDING: + case OGR_CBT_PROGRESS_RECORDING: g_cb_progress_rec = cb; - g_all_user_data[REC_CBT_PROGRESS_RECORDING] = data; + g_all_user_data[OGR_CBT_PROGRESS_RECORDING] = data; break; default: assert(false && "Wrong callback enum"); @@ -157,42 +194,42 @@ void runCallback(CallBackType cbt, const void* arg) { switch (cbt) { - case REC_CBT_START_RECORDING: + case OGR_CBT_START_RECORDING: { if (g_cb_start_rec == NULL) return; - g_cb_start_rec(g_all_user_data[REC_CBT_START_RECORDING]); + g_cb_start_rec(g_all_user_data[OGR_CBT_START_RECORDING]); break; } - case REC_CBT_SAVED_RECORDING: + case OGR_CBT_SAVED_RECORDING: { if (g_cb_saved_rec == NULL) return; const char* s = (const char*)arg; - g_cb_saved_rec(s, g_all_user_data[REC_CBT_SAVED_RECORDING]); + g_cb_saved_rec(s, g_all_user_data[OGR_CBT_SAVED_RECORDING]); break; } - case REC_CBT_ERROR_RECORDING: + case OGR_CBT_ERROR_RECORDING: { if (g_cb_error_rec == NULL) return; - g_cb_error_rec(g_all_user_data[REC_CBT_ERROR_RECORDING]); + g_cb_error_rec(g_all_user_data[OGR_CBT_ERROR_RECORDING]); break; } - case REC_CBT_PROGRESS_RECORDING: + case OGR_CBT_PROGRESS_RECORDING: { if (g_cb_progress_rec == NULL) return; const int* i = (const int*)arg; - g_cb_progress_rec(*i, g_all_user_data[REC_CBT_PROGRESS_RECORDING]); + g_cb_progress_rec(*i, g_all_user_data[OGR_CBT_PROGRESS_RECORDING]); break; } - case REC_CBT_WAIT_RECORDING: + case OGR_CBT_WAIT_RECORDING: { if (g_cb_wait_rec == NULL) return; - g_cb_wait_rec(g_all_user_data[REC_CBT_WAIT_RECORDING]); + g_cb_wait_rec(g_all_user_data[OGR_CBT_WAIT_RECORDING]); break; } - case REC_CBT_SLOW_RECORDING: + case OGR_CBT_SLOW_RECORDING: { if (g_cb_slow_rec == NULL) return; - g_cb_slow_rec(g_all_user_data[REC_CBT_SLOW_RECORDING]); + g_cb_slow_rec(g_all_user_data[OGR_CBT_SLOW_RECORDING]); break; } default: diff --git a/src/recorder/vorbis_encoder.cpp b/src/recorder/vorbis_encoder.cpp index cacd641cf..33465b6dc 100644 --- a/src/recorder/vorbis_encoder.cpp +++ b/src/recorder/vorbis_encoder.cpp @@ -39,7 +39,7 @@ namespace Recorder vorbis_comment vc; vorbis_comment_init(&vc); vorbis_comment_add_tag(&vc, "Encoder", - "vorbis encoder by libopenglrecorder"); + "Vorbis encoder by libopenglrecorder"); ogg_packet header; ogg_packet header_comm; ogg_packet header_code; diff --git a/src/recorder/vpx_encoder.cpp b/src/recorder/vpx_encoder.cpp index 601fefb77..13d790437 100644 --- a/src/recorder/vpx_encoder.cpp +++ b/src/recorder/vpx_encoder.cpp @@ -69,14 +69,14 @@ namespace Recorder vpx_codec_iface_t* codec_if = NULL; switch (cl->getRecorderConfig().m_video_format) { - case REC_VF_VP8: + case OGR_VF_VP8: codec_if = vpx_codec_vp8_cx(); break; - case REC_VF_VP9: + case OGR_VF_VP9: codec_if = vpx_codec_vp9_cx(); break; - case REC_VF_MJPEG: - case REC_VF_H264: + case OGR_VF_MJPEG: + case OGR_VF_H264: assert(false); break; } @@ -128,7 +128,7 @@ namespace Recorder last_size = (float)(cl->getJPGList()->size()); cur_finished_count += frame_count; int rate = (int)(cur_finished_count / last_size * 100.0f); - runCallback(REC_CBT_PROGRESS_RECORDING, &rate); + runCallback(OGR_CBT_PROGRESS_RECORDING, &rate); } uint8_t* yuv = NULL; unsigned yuv_size; diff --git a/src/recorder/wasapi_recorder.cpp b/src/recorder/wasapi_recorder.cpp index e81843852..8cde14472 100644 --- a/src/recorder/wasapi_recorder.cpp +++ b/src/recorder/wasapi_recorder.cpp @@ -224,7 +224,7 @@ namespace Recorder switch (cl->getRecorderConfig().m_audio_format) { - case REC_AF_VORBIS: + case OGR_AF_VORBIS: audio_enc_thread = std::thread(vorbisEncoder, &aed); break; } From c69ea65ac28bb2f7e472189b76c9860308ebab51 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 9 Apr 2017 16:19:09 +0800 Subject: [PATCH 03/14] Use RGBA for capture --- src/recorder/capture_library.cpp | 15 ++++++++------- src/recorder/mkv_writer.cpp | 2 ++ src/recorder/pulseaudio_recorder.cpp | 2 ++ src/recorder/vpx_encoder.cpp | 3 +-- src/recorder/wasapi_recorder.cpp | 2 ++ 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/recorder/capture_library.cpp b/src/recorder/capture_library.cpp index ba8b81786..5e9dbf85c 100644 --- a/src/recorder/capture_library.cpp +++ b/src/recorder/capture_library.cpp @@ -16,7 +16,7 @@ extern "C" const uint32_t E_GL_PIXEL_PACK_BUFFER = 0x88EB; const uint32_t E_GL_STREAM_READ = 0x88E1; const uint32_t E_GL_READ_ONLY = 0x88B8; -const uint32_t E_GL_RGB = 0x1907; +const uint32_t E_GL_RGBA = 0x1908; const uint32_t E_GL_UNSIGNED_BYTE = 0x1401; // ---------------------------------------------------------------------------- @@ -80,6 +80,8 @@ void CaptureLibrary::reset() break; case OGR_VF_H264: break; + default: + break; } } // reset @@ -89,11 +91,11 @@ int CaptureLibrary::bmpToJPG(uint8_t* raw, unsigned width, unsigned height, { int ret = 0; #ifdef TJFLAG_FASTDCT - ret = tjCompress2(m_compress_handle, raw, width, 0, height, TJPF_RGB, + ret = tjCompress2(m_compress_handle, raw, width, 0, height, TJPF_RGBX, jpeg_buffer, jpeg_size, TJSAMP_420, m_recorder_cfg->m_record_jpg_quality, TJFLAG_FASTDCT); #else - ret = tjCompress2(m_compress_handle, raw, width, 0, height, TJPF_RGB, + ret = tjCompress2(m_compress_handle, raw, width, 0, height, TJPF_RGBX, jpeg_buffer, jpeg_size, TJSAMP_420, m_recorder_cfg->m_record_jpg_quality, 0); #endif @@ -181,7 +183,7 @@ void CaptureLibrary::capture() } else { - glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, fbi); } addFrameBufferImage(fbi, frame_count); @@ -193,7 +195,7 @@ void CaptureLibrary::capture() assert(pbo_read == -1 || pbo_use == pbo_read); glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_use]); - glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, NULL); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } // capture @@ -270,8 +272,7 @@ void CaptureLibrary::captureConversion(CaptureLibrary* cl) uint8_t* orig_fbi = fbi; const unsigned width = cl->m_recorder_cfg->m_width; const unsigned height = cl->m_recorder_cfg->m_height; - const unsigned area = width * height; - const int pitch = width * 3; + const int pitch = width * 4; uint8_t* p2 = fbi + (height - 1) * pitch; uint8_t* tmp_buf = new uint8_t[pitch]; for (unsigned i = 0; i < height; i += 2) diff --git a/src/recorder/mkv_writer.cpp b/src/recorder/mkv_writer.cpp index 3b354897c..9940c3a41 100644 --- a/src/recorder/mkv_writer.cpp +++ b/src/recorder/mkv_writer.cpp @@ -134,6 +134,8 @@ namespace Recorder case OGR_VF_H264: vt->set_codec_id("V_MPEG4/ISO/AVC"); break; + default: + break; } input = fopen(video.c_str(), "rb"); while (fread(buf, 1, 16, input) == 16) diff --git a/src/recorder/pulseaudio_recorder.cpp b/src/recorder/pulseaudio_recorder.cpp index 1beed24e6..88a1aa07f 100644 --- a/src/recorder/pulseaudio_recorder.cpp +++ b/src/recorder/pulseaudio_recorder.cpp @@ -494,6 +494,8 @@ namespace Recorder case OGR_AF_VORBIS: audio_enc_thread = std::thread(vorbisEncoder, &aed); break; + default: + break; } int8_t* each_pcm_buf = new int8_t[frag_size](); diff --git a/src/recorder/vpx_encoder.cpp b/src/recorder/vpx_encoder.cpp index 13d790437..2c679efa3 100644 --- a/src/recorder/vpx_encoder.cpp +++ b/src/recorder/vpx_encoder.cpp @@ -75,8 +75,7 @@ namespace Recorder case OGR_VF_VP9: codec_if = vpx_codec_vp9_cx(); break; - case OGR_VF_MJPEG: - case OGR_VF_H264: + default: assert(false); break; } diff --git a/src/recorder/wasapi_recorder.cpp b/src/recorder/wasapi_recorder.cpp index 8cde14472..151239355 100644 --- a/src/recorder/wasapi_recorder.cpp +++ b/src/recorder/wasapi_recorder.cpp @@ -227,6 +227,8 @@ namespace Recorder case OGR_AF_VORBIS: audio_enc_thread = std::thread(vorbisEncoder, &aed); break; + default: + break; } const unsigned frag_size = 1024 * aed.m_channels * From ab10f335e907766d2177d19936482012fa1b94fc Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Apr 2017 01:26:52 +0800 Subject: [PATCH 04/14] Remove glew include in libopenrecorder --- src/graphics/irr_driver.cpp | 6 +++++ src/recorder/capture_library.cpp | 34 ++++++++++++-------------- src/recorder/mjpeg_writer.cpp | 2 -- src/recorder/openglrecorder.h | 22 +++++++++++++++++ src/recorder/recorder.cpp | 40 ++++++++++++++++++++++++++++++- src/recorder/recorder_private.hpp | 8 +++++++ 6 files changed, 90 insertions(+), 22 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 719cba17b..7aa1ef92d 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -623,6 +623,12 @@ void IrrDriver::initDevice() "RecorderConfig is invalid, use the default one."); } + ogrRegReadPixelsFunction((ogrFucReadPixels)glReadPixels); + ogrRegPBOFunctions((ogrFucGenBuffers)glGenBuffers, + (ogrFucBindBuffer)glBindBuffer, (ogrFucBufferData)glBufferData, + (ogrFucDeleteBuffers)glDeleteBuffers, (ogrFucMapBuffer)glMapBuffer, + (ogrFucUnmapBuffer)glUnmapBuffer); + ogrRegGeneralCallback(OGR_CBT_START_RECORDING, [] (void* user_data) { MessageQueue::add (MessageQueue::MT_GENERIC, _("Video recording started.")); }, NULL); diff --git a/src/recorder/capture_library.cpp b/src/recorder/capture_library.cpp index 5e9dbf85c..9666a3cb6 100644 --- a/src/recorder/capture_library.cpp +++ b/src/recorder/capture_library.cpp @@ -8,11 +8,6 @@ #include "vpx_encoder.hpp" #include "wasapi_recorder.hpp" -extern "C" -{ -#include -} - const uint32_t E_GL_PIXEL_PACK_BUFFER = 0x88EB; const uint32_t E_GL_STREAM_READ = 0x88E1; const uint32_t E_GL_READ_ONLY = 0x88B8; @@ -30,14 +25,14 @@ CaptureLibrary::CaptureLibrary(RecorderConfig* rc) m_decompress_handle = tjInitDecompress(); if (m_recorder_cfg->m_triple_buffering > 0) { - glGenBuffers(3, m_pbo); + ogrGenBuffers(3, m_pbo); for (int i = 0; i < 3; i++) { - glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[i]); - glBufferData(GL_PIXEL_PACK_BUFFER, m_recorder_cfg->m_width * - m_recorder_cfg->m_height * 4, NULL, GL_STREAM_READ); + ogrBindBuffer(E_GL_PIXEL_PACK_BUFFER, m_pbo[i]); + ogrBufferData(E_GL_PIXEL_PACK_BUFFER, m_recorder_cfg->m_width * + m_recorder_cfg->m_height * 4, NULL, E_GL_STREAM_READ); } - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + ogrBindBuffer(E_GL_PIXEL_PACK_BUFFER, 0); } m_capture_thread = std::thread(CaptureLibrary::captureConversion, this); } // CaptureLibrary @@ -52,7 +47,7 @@ CaptureLibrary::~CaptureLibrary() tjDestroy(m_decompress_handle); if (m_recorder_cfg->m_triple_buffering > 0) { - glDeleteBuffers(3, m_pbo); + ogrDeleteBuffers(3, m_pbo); } } // ~CaptureLibrary @@ -176,15 +171,16 @@ void CaptureLibrary::capture() if (use_pbo) { pbo_read = m_pbo_use % 3; - glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_read]); - void* ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); + ogrBindBuffer(E_GL_PIXEL_PACK_BUFFER, m_pbo[pbo_read]); + void* ptr = ogrMapBuffer(E_GL_PIXEL_PACK_BUFFER, + E_GL_READ_ONLY); memcpy(fbi, ptr, size); - glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + ogrUnmapBuffer(E_GL_PIXEL_PACK_BUFFER); } else { - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, - fbi); + ogrReadPixels(0, 0, width, height, E_GL_RGBA, + E_GL_UNSIGNED_BYTE, fbi); } addFrameBufferImage(fbi, frame_count); } @@ -194,9 +190,9 @@ void CaptureLibrary::capture() return; assert(pbo_read == -1 || pbo_use == pbo_read); - glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_use]); - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + ogrBindBuffer(E_GL_PIXEL_PACK_BUFFER, m_pbo[pbo_use]); + ogrReadPixels(0, 0, width, height, E_GL_RGBA, E_GL_UNSIGNED_BYTE, NULL); + ogrBindBuffer(E_GL_PIXEL_PACK_BUFFER, 0); } // capture // ---------------------------------------------------------------------------- diff --git a/src/recorder/mjpeg_writer.cpp b/src/recorder/mjpeg_writer.cpp index 93c78afed..8de7dee5a 100644 --- a/src/recorder/mjpeg_writer.cpp +++ b/src/recorder/mjpeg_writer.cpp @@ -20,8 +20,6 @@ #include "capture_library.hpp" #include "recorder_private.hpp" -#include - namespace Recorder { // ------------------------------------------------------------------------ diff --git a/src/recorder/openglrecorder.h b/src/recorder/openglrecorder.h index 1ef089f7d..34c78d522 100644 --- a/src/recorder/openglrecorder.h +++ b/src/recorder/openglrecorder.h @@ -2,6 +2,8 @@ #ifndef HEADER_OPENGLRECORDER_H #define HEADER_OPENGLRECORDER_H +#include + /** * \mainpage libopenglrecorder * @@ -160,6 +162,16 @@ struct RecorderConfig unsigned int m_record_jpg_quality; }; +/* List of opengl function used by libopenglrecorder: */ +typedef void(*ogrFucReadPixels)(int, int, int, int, unsigned int, unsigned int, + void*); +typedef void(*ogrFucGenBuffers)(int, unsigned int*); +typedef void(*ogrFucBindBuffer)(unsigned int, unsigned int); +typedef void(*ogrFucBufferData)(unsigned int, ptrdiff_t, const void*, unsigned int); +typedef void(*ogrFucDeleteBuffers)(int, const unsigned int*); +typedef void*(*ogrFucMapBuffer)(unsigned int, unsigned int); +typedef void(*ogrFucUnmapBuffer)(unsigned int); + #ifdef __cplusplus extern "C" { @@ -214,6 +226,16 @@ void ogrRegIntCallback(CallBackType, IntCallback, void*); * Return 1 if recording is happening in libopenglrecorder, 0 otherwise. */ int ogrCapturing(void); +/** + * Set opengl function for read pixels (always required). + */ +void ogrRegReadPixelsFunction(ogrFucReadPixels); +/** + * Set opengl functions for using PBOs (required if triple buffering is used). + */ +void ogrRegPBOFunctions(ogrFucGenBuffers, ogrFucBindBuffer, ogrFucBufferData, + ogrFucDeleteBuffers, ogrFucMapBuffer, + ogrFucUnmapBuffer); #ifdef __cplusplus } #endif diff --git a/src/recorder/recorder.cpp b/src/recorder/recorder.cpp index 0543f0756..e09bbb3eb 100644 --- a/src/recorder/recorder.cpp +++ b/src/recorder/recorder.cpp @@ -8,6 +8,14 @@ #include #include +// ============================================================================ +ogrFucReadPixels ogrReadPixels = NULL; +ogrFucGenBuffers ogrGenBuffers = NULL; +ogrFucBindBuffer ogrBindBuffer = NULL; +ogrFucBufferData ogrBufferData = NULL; +ogrFucDeleteBuffers ogrDeleteBuffers = NULL; +ogrFucMapBuffer ogrMapBuffer = NULL; +ogrFucUnmapBuffer ogrUnmapBuffer = NULL; // ============================================================================ std::unique_ptr g_recorder_config(nullptr); // ============================================================================ @@ -105,7 +113,8 @@ const std::string& getSavedName() // ---------------------------------------------------------------------------- void ogrPrepareCapture(void) { - assert(g_recorder_config.get() != nullptr && !g_saved_name.empty()); + assert(g_recorder_config.get() != nullptr && !g_saved_name.empty() && + ogrReadPixels != NULL); if (g_capture_library.get() == nullptr) { assert(g_recorder_config.get() != nullptr); @@ -243,6 +252,35 @@ int ogrCapturing(void) return g_capturing.load() ? 1 : 0; } // ogrCapturing +// ---------------------------------------------------------------------------- +void ogrRegReadPixelsFunction(ogrFucReadPixels read_pixels) +{ + assert(read_pixels != NULL); + ogrReadPixels = read_pixels; +} // ogrRegReadPixelsFunction + +// ---------------------------------------------------------------------------- +void ogrRegPBOFunctions(ogrFucGenBuffers gen_buffers, + ogrFucBindBuffer bind_buffer, + ogrFucBufferData buffer_data, + ogrFucDeleteBuffers delete_buffers, + ogrFucMapBuffer map_buffer, + ogrFucUnmapBuffer unmap_buffer) +{ + assert(gen_buffers != NULL); + ogrGenBuffers = gen_buffers; + assert(bind_buffer != NULL); + ogrBindBuffer = bind_buffer; + assert(buffer_data != NULL); + ogrBufferData = buffer_data; + assert(delete_buffers != NULL); + ogrDeleteBuffers = delete_buffers; + assert(map_buffer != NULL); + ogrMapBuffer = map_buffer; + assert(unmap_buffer != NULL); + ogrUnmapBuffer = unmap_buffer; +} // ogrRegPBOFunctions + // ---------------------------------------------------------------------------- void setCapturing(bool val) { diff --git a/src/recorder/recorder_private.hpp b/src/recorder/recorder_private.hpp index df0e1515e..97cccd2a5 100644 --- a/src/recorder/recorder_private.hpp +++ b/src/recorder/recorder_private.hpp @@ -6,6 +6,14 @@ #include +extern ogrFucReadPixels ogrReadPixels; +extern ogrFucGenBuffers ogrGenBuffers; +extern ogrFucBindBuffer ogrBindBuffer; +extern ogrFucBufferData ogrBufferData; +extern ogrFucDeleteBuffers ogrDeleteBuffers; +extern ogrFucMapBuffer ogrMapBuffer; +extern ogrFucUnmapBuffer ogrUnmapBuffer; + RecorderConfig* getConfig(); const std::string& getSavedName(); void setCapturing(bool val); From bcf996e2910131c0f721938f7dec00ae62716bfc Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Apr 2017 08:53:55 +0800 Subject: [PATCH 05/14] Fix callback --- src/graphics/irr_driver.cpp | 16 +++++++++++++++- src/recorder/capture_library.cpp | 5 +++-- src/recorder/vpx_encoder.cpp | 8 +++++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 7aa1ef92d..da9b13e9b 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -68,6 +68,7 @@ #include "utils/vs.hpp" #include +#include /* Build-time check that the Irrlicht we're building against works for us. * Should help prevent distros building against an incompatible library. @@ -647,9 +648,22 @@ void IrrDriver::initDevice() [] (const char* s, void* user_data) { MessageQueue::add (MessageQueue::MT_GENERIC, _("Video saved in \"%s\".", s)); }, NULL); + static std::chrono::high_resolution_clock::time_point tp; ogrRegIntCallback(OGR_CBT_PROGRESS_RECORDING, [] (const int i, void* user_data) - { Log::info("Recorder", "%d%% of video encoding finished", i);}, NULL); + { + std::chrono::high_resolution_clock::time_point* timer = + (std::chrono::high_resolution_clock::time_point*)user_data; + auto rate = std::chrono::high_resolution_clock::now() - + *timer; + float t = std::chrono::duration_cast >(rate).count(); + if (t > 3.0f) + { + *timer = std::chrono::high_resolution_clock::now(); + Log::info("Recorder", "%d%% of video encoding finished", i); + } + }, &tp); #endif diff --git a/src/recorder/capture_library.cpp b/src/recorder/capture_library.cpp index 9666a3cb6..5db3b0dcd 100644 --- a/src/recorder/capture_library.cpp +++ b/src/recorder/capture_library.cpp @@ -221,10 +221,10 @@ void CaptureLibrary::captureConversion(CaptureLibrary* cl) { runCallback(OGR_CBT_WAIT_RECORDING, NULL); } - cl->m_display_progress.store(true); cl->m_jpg_list.emplace_back((uint8_t*)NULL, 0, 0); cl->m_jpg_list_ready.notify_one(); ulj.unlock(); + cl->m_display_progress.store(!cl->m_destroy.load()); cl->m_video_enc_thread.join(); cl->m_display_progress.store(false); std::string f = Recorder::writeMKV(getSavedName() + ".video", @@ -253,7 +253,8 @@ void CaptureLibrary::captureConversion(CaptureLibrary* cl) const bool too_slow = cl->m_fbi_list.size() > 50; if (too_slow) { - runCallback(OGR_CBT_SLOW_RECORDING, NULL); + if (!cl->m_destroy.load()) + runCallback(OGR_CBT_SLOW_RECORDING, NULL); delete [] fbi; cl->m_fbi_list.pop_front(); for (auto& p : cl->m_fbi_list) diff --git a/src/recorder/vpx_encoder.cpp b/src/recorder/vpx_encoder.cpp index 2c679efa3..46c0ba880 100644 --- a/src/recorder/vpx_encoder.cpp +++ b/src/recorder/vpx_encoder.cpp @@ -117,16 +117,22 @@ namespace Recorder { cl->getJPGList()->clear(); ul.unlock(); + if (cl->displayingProgress()) + { + int rate = 100; + runCallback(OGR_CBT_PROGRESS_RECORDING, &rate); + } break; } cl->getJPGList()->pop_front(); ul.unlock(); - if (!cl->getDestroy() && cl->displayingProgress()) + if (cl->displayingProgress()) { if (last_size == -1.0f) last_size = (float)(cl->getJPGList()->size()); cur_finished_count += frame_count; int rate = (int)(cur_finished_count / last_size * 100.0f); + rate = rate > 100 ? 100 : rate; runCallback(OGR_CBT_PROGRESS_RECORDING, &rate); } uint8_t* yuv = NULL; From f30962b944e4045a572ee0cf5e34d76b668a1fe8 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Apr 2017 10:00:34 +0800 Subject: [PATCH 06/14] Use lambda to workaround __stdcall issue --- src/graphics/irr_driver.cpp | 16 +++++++++++----- src/recorder/openglrecorder.h | 5 +++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index da9b13e9b..558087e69 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -624,11 +624,17 @@ void IrrDriver::initDevice() "RecorderConfig is invalid, use the default one."); } - ogrRegReadPixelsFunction((ogrFucReadPixels)glReadPixels); - ogrRegPBOFunctions((ogrFucGenBuffers)glGenBuffers, - (ogrFucBindBuffer)glBindBuffer, (ogrFucBufferData)glBufferData, - (ogrFucDeleteBuffers)glDeleteBuffers, (ogrFucMapBuffer)glMapBuffer, - (ogrFucUnmapBuffer)glUnmapBuffer); + ogrRegReadPixelsFunction([] + (int x, int y, int w, int h, unsigned int f, unsigned int t, void* d) + { glReadPixels(x, y, w, h, f, t, d); }); + + ogrRegPBOFunctions([](int n, unsigned int* b) { glGenBuffers(n, b); }, + [](unsigned int t, unsigned int b) { glBindBuffer(t, b); }, + [](unsigned int t, ptrdiff_t s, const void* d, unsigned int u) + { glBufferData(t, s, d, u); }, + [](int n, const unsigned int* b) { glDeleteBuffers(n, b); }, + [](unsigned int t, unsigned int a) { return glMapBuffer(t, a); }, + [](unsigned int t) { return glUnmapBuffer(t); }); ogrRegGeneralCallback(OGR_CBT_START_RECORDING, [] (void* user_data) { MessageQueue::add diff --git a/src/recorder/openglrecorder.h b/src/recorder/openglrecorder.h index 34c78d522..884d48574 100644 --- a/src/recorder/openglrecorder.h +++ b/src/recorder/openglrecorder.h @@ -167,10 +167,11 @@ typedef void(*ogrFucReadPixels)(int, int, int, int, unsigned int, unsigned int, void*); typedef void(*ogrFucGenBuffers)(int, unsigned int*); typedef void(*ogrFucBindBuffer)(unsigned int, unsigned int); -typedef void(*ogrFucBufferData)(unsigned int, ptrdiff_t, const void*, unsigned int); +typedef void(*ogrFucBufferData)(unsigned int, ptrdiff_t, const void*, + unsigned int); typedef void(*ogrFucDeleteBuffers)(int, const unsigned int*); typedef void*(*ogrFucMapBuffer)(unsigned int, unsigned int); -typedef void(*ogrFucUnmapBuffer)(unsigned int); +typedef unsigned char(*ogrFucUnmapBuffer)(unsigned int); #ifdef __cplusplus extern "C" From b1f9ce2dbda4dd23fbc0fc4464b307962093d064 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 12 Apr 2017 11:51:59 +0800 Subject: [PATCH 07/14] Remove libwebm and recorder code --- .travis.yml | 6 +- CMakeLists.txt | 41 +- lib/libwebm/CMakeLists.txt | 10 - lib/libwebm/LICENSE.TXT | 30 - lib/libwebm/common/webmids.h | 192 -- lib/libwebm/mkvmuxer/mkvmuxer.cc | 4178 -------------------------- lib/libwebm/mkvmuxer/mkvmuxer.h | 1921 ------------ lib/libwebm/mkvmuxer/mkvmuxertypes.h | 28 - lib/libwebm/mkvmuxer/mkvmuxerutil.cc | 743 ----- lib/libwebm/mkvmuxer/mkvmuxerutil.h | 112 - lib/libwebm/mkvmuxer/mkvwriter.cc | 90 - lib/libwebm/mkvmuxer/mkvwriter.h | 51 - lib/libwebm/mkvparser/mkvparser.h | 1145 ------- src/graphics/irr_driver.cpp | 4 +- src/recorder/capture_library.cpp | 296 -- src/recorder/capture_library.hpp | 118 - src/recorder/mjpeg_writer.cpp | 67 - src/recorder/mjpeg_writer.hpp | 33 - src/recorder/mkv_writer.cpp | 214 -- src/recorder/mkv_writer.hpp | 32 - src/recorder/openglrecorder.h | 246 -- src/recorder/pulseaudio_recorder.cpp | 550 ---- src/recorder/pulseaudio_recorder.hpp | 35 - src/recorder/recorder.cpp | 340 --- src/recorder/recorder_private.hpp | 25 - src/recorder/vorbis_encoder.cpp | 145 - src/recorder/vorbis_encoder.hpp | 31 - src/recorder/vpx_encoder.cpp | 169 -- src/recorder/vpx_encoder.hpp | 36 - src/recorder/wasapi_recorder.cpp | 308 -- src/recorder/wasapi_recorder.hpp | 35 - 31 files changed, 10 insertions(+), 11221 deletions(-) delete mode 100644 lib/libwebm/CMakeLists.txt delete mode 100644 lib/libwebm/LICENSE.TXT delete mode 100644 lib/libwebm/common/webmids.h delete mode 100644 lib/libwebm/mkvmuxer/mkvmuxer.cc delete mode 100644 lib/libwebm/mkvmuxer/mkvmuxer.h delete mode 100644 lib/libwebm/mkvmuxer/mkvmuxertypes.h delete mode 100644 lib/libwebm/mkvmuxer/mkvmuxerutil.cc delete mode 100644 lib/libwebm/mkvmuxer/mkvmuxerutil.h delete mode 100644 lib/libwebm/mkvmuxer/mkvwriter.cc delete mode 100644 lib/libwebm/mkvmuxer/mkvwriter.h delete mode 100644 lib/libwebm/mkvparser/mkvparser.h delete mode 100644 src/recorder/capture_library.cpp delete mode 100644 src/recorder/capture_library.hpp delete mode 100644 src/recorder/mjpeg_writer.cpp delete mode 100644 src/recorder/mjpeg_writer.hpp delete mode 100644 src/recorder/mkv_writer.cpp delete mode 100644 src/recorder/mkv_writer.hpp delete mode 100644 src/recorder/openglrecorder.h delete mode 100644 src/recorder/pulseaudio_recorder.cpp delete mode 100644 src/recorder/pulseaudio_recorder.hpp delete mode 100644 src/recorder/recorder.cpp delete mode 100644 src/recorder/recorder_private.hpp delete mode 100644 src/recorder/vorbis_encoder.cpp delete mode 100644 src/recorder/vorbis_encoder.hpp delete mode 100644 src/recorder/vpx_encoder.cpp delete mode 100644 src/recorder/vpx_encoder.hpp delete mode 100644 src/recorder/wasapi_recorder.cpp delete mode 100644 src/recorder/wasapi_recorder.hpp diff --git a/.travis.yml b/.travis.yml index 51e3988f4..cff4a5183 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,12 +32,8 @@ addons: - libjpeg-dev - libogg-dev - libopenal-dev - - libpulse-dev - libpng-dev - - libturbojpeg - libvorbis-dev - - libvorbisenc2 - - libvpx-dev - libxrandr-dev - mesa-common-dev - pkg-config @@ -57,7 +53,7 @@ before_script: script: - mkdir "build" - cd "build" - - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSERVER_ONLY=$SERVER_ONLY -DCHECK_ASSETS=off -DDISABLE_VPX=on + - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSERVER_ONLY=$SERVER_ONLY -DCHECK_ASSETS=off -DBUILD_RECORDER=off - make VERBOSE=1 -j $THREADS notifications: diff --git a/CMakeLists.txt b/CMakeLists.txt index e4a54b9b0..deaf506ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,10 +24,6 @@ option(ENABLE_NETWORK_MULTIPLAYER "Enable network multiplayer. This will replace CMAKE_DEPENDENT_OPTION(BUILD_RECORDER "Build opengl recorder" ON "NOT SERVER_ONLY;NOT USE_GLES2;NOT APPLE" OFF) -CMAKE_DEPENDENT_OPTION(BUILD_RECORDER_WITH_SOUND "Build opengl recorder with sound" ON - BUILD_RECORDER OFF) -CMAKE_DEPENDENT_OPTION(BUILD_PULSE_WO_DL "If pulseaudio in your distro / system is optional, turn this off to load pulse with libdl" - ON "BUILD_RECORDER_WITH_SOUND;UNIX" OFF) if (UNIX AND NOT APPLE) option(USE_GLES2 "Use OpenGL ES2 renderer" OFF) @@ -88,12 +84,6 @@ endif() if(BUILD_RECORDER) add_definitions(-DENABLE_RECORDER) - if(BUILD_RECORDER_WITH_SOUND) - add_definitions(-DENABLE_REC_SOUND) - if(BUILD_PULSE_WO_DL) - add_definitions(-DENABLE_PULSE_WO_DL) - endif() - endif() endif() # Build the Bullet physics library @@ -145,22 +135,12 @@ else() endif() if(BUILD_RECORDER) - find_library(TURBOJPEG_LIBRARY NAMES turbojpeg libturbojpeg PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") - mark_as_advanced(TURBOJPEG_LIBRARY) - if(UNIX) - include(FindPkgConfig) - pkg_check_modules(VPX vpx) - else() - find_path(VPX_INCLUDEDIR NAMES vpx/vpx_codec.h PATHS "${PROJECT_SOURCE_DIR}/dependencies/include") - find_library(VPX_LIBRARIES NAMES libvpx PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") - endif() - include_directories(${VPX_INCLUDEDIR}) - if(BUILD_RECORDER_WITH_SOUND AND UNIX) - pkg_check_modules(PULSEAUDIO libpulse) - include_directories(${PULSEAUDIO_INCLUDEDIR}) - endif() - add_subdirectory("${PROJECT_SOURCE_DIR}/lib/libwebm") - include_directories("${PROJECT_SOURCE_DIR}/lib/libwebm") + find_library(OPENGLRECORDER_LIBRARY NAMES openglrecorder libopenglrecorder PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") + find_path(OPENGLRECORDER_INCLUDEDIR NAMES openglrecorder.h PATHS "${PROJECT_SOURCE_DIR}/dependencies/include") + find_library(OPENGLRECORDER_LIBRARY NAMES openglrecorder libopenglrecorder PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") + find_path(OPENGLRECORDER_INCLUDEDIR NAMES openglrecorder.h PATHS "${PROJECT_SOURCE_DIR}/dependencies/include") + include_directories(${OPENGLRECORDER_INCLUDEDIR}) + mark_as_advanced(OPENGLRECORDER_LIBRARY OPENGLRECORDER_INCLUDEDIR) endif() if(NOT SERVER_ONLY AND NOT USE_GLES2) @@ -447,14 +427,7 @@ if(UNIX AND NOT APPLE) endif() if(BUILD_RECORDER) - target_link_libraries(supertuxkart webm ${TURBOJPEG_LIBRARY} ${VPX_LIBRARIES}) - if(BUILD_RECORDER_WITH_SOUND AND UNIX) - if(BUILD_PULSE_WO_DL) - target_link_libraries(supertuxkart ${PULSEAUDIO_LIBRARIES}) - else() - target_link_libraries(supertuxkart dl) - endif() - endif() + target_link_libraries(supertuxkart ${OPENGLRECORDER_LIBRARY}) endif() # FreeBSD does not search in /usr/local/lib, but at least Freetype is installed there :( diff --git a/lib/libwebm/CMakeLists.txt b/lib/libwebm/CMakeLists.txt deleted file mode 100644 index 1ca4dbd38..000000000 --- a/lib/libwebm/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 2.6) -if (UNIX OR MINGW) - add_definitions(-std=gnu++0x -O3) -endif() -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -add_library(webm STATIC - mkvmuxer/mkvmuxer.cc - mkvmuxer/mkvmuxerutil.cc - mkvmuxer/mkvwriter.cc -) diff --git a/lib/libwebm/LICENSE.TXT b/lib/libwebm/LICENSE.TXT deleted file mode 100644 index 7a6f99547..000000000 --- a/lib/libwebm/LICENSE.TXT +++ /dev/null @@ -1,30 +0,0 @@ -Copyright (c) 2010, Google Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Google nor the names of its contributors may - be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/lib/libwebm/common/webmids.h b/lib/libwebm/common/webmids.h deleted file mode 100644 index 89d722a71..000000000 --- a/lib/libwebm/common/webmids.h +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) 2012 The WebM project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. - -#ifndef COMMON_WEBMIDS_H_ -#define COMMON_WEBMIDS_H_ - -namespace libwebm { - -enum MkvId { - kMkvEBML = 0x1A45DFA3, - kMkvEBMLVersion = 0x4286, - kMkvEBMLReadVersion = 0x42F7, - kMkvEBMLMaxIDLength = 0x42F2, - kMkvEBMLMaxSizeLength = 0x42F3, - kMkvDocType = 0x4282, - kMkvDocTypeVersion = 0x4287, - kMkvDocTypeReadVersion = 0x4285, - kMkvVoid = 0xEC, - kMkvSignatureSlot = 0x1B538667, - kMkvSignatureAlgo = 0x7E8A, - kMkvSignatureHash = 0x7E9A, - kMkvSignaturePublicKey = 0x7EA5, - kMkvSignature = 0x7EB5, - kMkvSignatureElements = 0x7E5B, - kMkvSignatureElementList = 0x7E7B, - kMkvSignedElement = 0x6532, - // segment - kMkvSegment = 0x18538067, - // Meta Seek Information - kMkvSeekHead = 0x114D9B74, - kMkvSeek = 0x4DBB, - kMkvSeekID = 0x53AB, - kMkvSeekPosition = 0x53AC, - // Segment Information - kMkvInfo = 0x1549A966, - kMkvTimecodeScale = 0x2AD7B1, - kMkvDuration = 0x4489, - kMkvDateUTC = 0x4461, - kMkvTitle = 0x7BA9, - kMkvMuxingApp = 0x4D80, - kMkvWritingApp = 0x5741, - // Cluster - kMkvCluster = 0x1F43B675, - kMkvTimecode = 0xE7, - kMkvPrevSize = 0xAB, - kMkvBlockGroup = 0xA0, - kMkvBlock = 0xA1, - kMkvBlockDuration = 0x9B, - kMkvReferenceBlock = 0xFB, - kMkvLaceNumber = 0xCC, - kMkvSimpleBlock = 0xA3, - kMkvBlockAdditions = 0x75A1, - kMkvBlockMore = 0xA6, - kMkvBlockAddID = 0xEE, - kMkvBlockAdditional = 0xA5, - kMkvDiscardPadding = 0x75A2, - // Track - kMkvTracks = 0x1654AE6B, - kMkvTrackEntry = 0xAE, - kMkvTrackNumber = 0xD7, - kMkvTrackUID = 0x73C5, - kMkvTrackType = 0x83, - kMkvFlagEnabled = 0xB9, - kMkvFlagDefault = 0x88, - kMkvFlagForced = 0x55AA, - kMkvFlagLacing = 0x9C, - kMkvDefaultDuration = 0x23E383, - kMkvMaxBlockAdditionID = 0x55EE, - kMkvName = 0x536E, - kMkvLanguage = 0x22B59C, - kMkvCodecID = 0x86, - kMkvCodecPrivate = 0x63A2, - kMkvCodecName = 0x258688, - kMkvCodecDelay = 0x56AA, - kMkvSeekPreRoll = 0x56BB, - // video - kMkvVideo = 0xE0, - kMkvFlagInterlaced = 0x9A, - kMkvStereoMode = 0x53B8, - kMkvAlphaMode = 0x53C0, - kMkvPixelWidth = 0xB0, - kMkvPixelHeight = 0xBA, - kMkvPixelCropBottom = 0x54AA, - kMkvPixelCropTop = 0x54BB, - kMkvPixelCropLeft = 0x54CC, - kMkvPixelCropRight = 0x54DD, - kMkvDisplayWidth = 0x54B0, - kMkvDisplayHeight = 0x54BA, - kMkvDisplayUnit = 0x54B2, - kMkvAspectRatioType = 0x54B3, - kMkvFrameRate = 0x2383E3, - // end video - // colour - kMkvColour = 0x55B0, - kMkvMatrixCoefficients = 0x55B1, - kMkvBitsPerChannel = 0x55B2, - kMkvChromaSubsamplingHorz = 0x55B3, - kMkvChromaSubsamplingVert = 0x55B4, - kMkvCbSubsamplingHorz = 0x55B5, - kMkvCbSubsamplingVert = 0x55B6, - kMkvChromaSitingHorz = 0x55B7, - kMkvChromaSitingVert = 0x55B8, - kMkvRange = 0x55B9, - kMkvTransferCharacteristics = 0x55BA, - kMkvPrimaries = 0x55BB, - kMkvMaxCLL = 0x55BC, - kMkvMaxFALL = 0x55BD, - // mastering metadata - kMkvMasteringMetadata = 0x55D0, - kMkvPrimaryRChromaticityX = 0x55D1, - kMkvPrimaryRChromaticityY = 0x55D2, - kMkvPrimaryGChromaticityX = 0x55D3, - kMkvPrimaryGChromaticityY = 0x55D4, - kMkvPrimaryBChromaticityX = 0x55D5, - kMkvPrimaryBChromaticityY = 0x55D6, - kMkvWhitePointChromaticityX = 0x55D7, - kMkvWhitePointChromaticityY = 0x55D8, - kMkvLuminanceMax = 0x55D9, - kMkvLuminanceMin = 0x55DA, - // end mastering metadata - // end colour - // projection - kMkvProjection = 0x7670, - kMkvProjectionType = 0x7671, - kMkvProjectionPrivate = 0x7672, - kMkvProjectionPoseYaw = 0x7673, - kMkvProjectionPosePitch = 0x7674, - kMkvProjectionPoseRoll = 0x7675, - // end projection - // audio - kMkvAudio = 0xE1, - kMkvSamplingFrequency = 0xB5, - kMkvOutputSamplingFrequency = 0x78B5, - kMkvChannels = 0x9F, - kMkvBitDepth = 0x6264, - // end audio - // ContentEncodings - kMkvContentEncodings = 0x6D80, - kMkvContentEncoding = 0x6240, - kMkvContentEncodingOrder = 0x5031, - kMkvContentEncodingScope = 0x5032, - kMkvContentEncodingType = 0x5033, - kMkvContentCompression = 0x5034, - kMkvContentCompAlgo = 0x4254, - kMkvContentCompSettings = 0x4255, - kMkvContentEncryption = 0x5035, - kMkvContentEncAlgo = 0x47E1, - kMkvContentEncKeyID = 0x47E2, - kMkvContentSignature = 0x47E3, - kMkvContentSigKeyID = 0x47E4, - kMkvContentSigAlgo = 0x47E5, - kMkvContentSigHashAlgo = 0x47E6, - kMkvContentEncAESSettings = 0x47E7, - kMkvAESSettingsCipherMode = 0x47E8, - kMkvAESSettingsCipherInitData = 0x47E9, - // end ContentEncodings - // Cueing Data - kMkvCues = 0x1C53BB6B, - kMkvCuePoint = 0xBB, - kMkvCueTime = 0xB3, - kMkvCueTrackPositions = 0xB7, - kMkvCueTrack = 0xF7, - kMkvCueClusterPosition = 0xF1, - kMkvCueBlockNumber = 0x5378, - // Chapters - kMkvChapters = 0x1043A770, - kMkvEditionEntry = 0x45B9, - kMkvChapterAtom = 0xB6, - kMkvChapterUID = 0x73C4, - kMkvChapterStringUID = 0x5654, - kMkvChapterTimeStart = 0x91, - kMkvChapterTimeEnd = 0x92, - kMkvChapterDisplay = 0x80, - kMkvChapString = 0x85, - kMkvChapLanguage = 0x437C, - kMkvChapCountry = 0x437E, - // Tags - kMkvTags = 0x1254C367, - kMkvTag = 0x7373, - kMkvSimpleTag = 0x67C8, - kMkvTagName = 0x45A3, - kMkvTagString = 0x4487 -}; - -} // namespace libwebm - -#endif // COMMON_WEBMIDS_H_ diff --git a/lib/libwebm/mkvmuxer/mkvmuxer.cc b/lib/libwebm/mkvmuxer/mkvmuxer.cc deleted file mode 100644 index 953ef0a88..000000000 --- a/lib/libwebm/mkvmuxer/mkvmuxer.cc +++ /dev/null @@ -1,4178 +0,0 @@ -// Copyright (c) 2012 The WebM project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. - -#include "mkvmuxer/mkvmuxer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/webmids.h" -#include "mkvmuxer/mkvmuxerutil.h" -#include "mkvmuxer/mkvwriter.h" -#include "mkvparser/mkvparser.h" - -namespace mkvmuxer { - -#ifndef UINT64_MAX -#define UINT64_MAX 0xffffffffffffffff -#endif -#ifndef UINT64_C -#define UINT64_C(x) ((x) + (UINT64_MAX - UINT64_MAX)) -#endif - -const float PrimaryChromaticity::kChromaticityMin = 0.0f; -const float PrimaryChromaticity::kChromaticityMax = 1.0f; -const float MasteringMetadata::kMinLuminance = 0.0f; -const float MasteringMetadata::kMinLuminanceMax = 999.99f; -const float MasteringMetadata::kMaxLuminanceMax = 9999.99f; -const float MasteringMetadata::kValueNotPresent = FLT_MAX; -const uint64_t Colour::kValueNotPresent = UINT64_MAX; - -namespace { - -const char kDocTypeWebm[] = "webm"; -const char kDocTypeMatroska[] = "matroska"; - -// Deallocate the string designated by |dst|, and then copy the |src| -// string to |dst|. The caller owns both the |src| string and the -// |dst| copy (hence the caller is responsible for eventually -// deallocating the strings, either directly, or indirectly via -// StrCpy). Returns true if the source string was successfully copied -// to the destination. -bool StrCpy(const char* src, char** dst_ptr) { - if (dst_ptr == NULL) - return false; - - char*& dst = *dst_ptr; - - delete[] dst; - dst = NULL; - - if (src == NULL) - return true; - - const size_t size = strlen(src) + 1; - - dst = new (std::nothrow) char[size]; // NOLINT - if (dst == NULL) - return false; - - strcpy(dst, src); // NOLINT - return true; -} - -typedef std::unique_ptr PrimaryChromaticityPtr; -bool CopyChromaticity(const PrimaryChromaticity* src, - PrimaryChromaticityPtr* dst) { - if (!dst) - return false; - - dst->reset(new (std::nothrow) PrimaryChromaticity(src->x(), src->y())); - if (!dst->get()) - return false; - - return true; -} - -} // namespace - -/////////////////////////////////////////////////////////////// -// -// IMkvWriter Class - -IMkvWriter::IMkvWriter() {} - -IMkvWriter::~IMkvWriter() {} - -bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version, - const char* const doc_type) { - // Level 0 - uint64_t size = - EbmlElementSize(libwebm::kMkvEBMLVersion, static_cast(1)); - size += EbmlElementSize(libwebm::kMkvEBMLReadVersion, static_cast(1)); - size += EbmlElementSize(libwebm::kMkvEBMLMaxIDLength, static_cast(4)); - size += - EbmlElementSize(libwebm::kMkvEBMLMaxSizeLength, static_cast(8)); - size += EbmlElementSize(libwebm::kMkvDocType, doc_type); - size += EbmlElementSize(libwebm::kMkvDocTypeVersion, - static_cast(doc_type_version)); - size += - EbmlElementSize(libwebm::kMkvDocTypeReadVersion, static_cast(2)); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvEBML, size)) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvEBMLVersion, - static_cast(1))) { - return false; - } - if (!WriteEbmlElement(writer, libwebm::kMkvEBMLReadVersion, - static_cast(1))) { - return false; - } - if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxIDLength, - static_cast(4))) { - return false; - } - if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxSizeLength, - static_cast(8))) { - return false; - } - if (!WriteEbmlElement(writer, libwebm::kMkvDocType, doc_type)) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeVersion, - static_cast(doc_type_version))) { - return false; - } - if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeReadVersion, - static_cast(2))) { - return false; - } - - return true; -} - -bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version) { - return WriteEbmlHeader(writer, doc_type_version, kDocTypeWebm); -} - -bool WriteEbmlHeader(IMkvWriter* writer) { - return WriteEbmlHeader(writer, mkvmuxer::Segment::kDefaultDocTypeVersion); -} - -bool ChunkedCopy(mkvparser::IMkvReader* source, mkvmuxer::IMkvWriter* dst, - int64_t start, int64_t size) { - // TODO(vigneshv): Check if this is a reasonable value. - const uint32_t kBufSize = 2048; - uint8_t* buf = new uint8_t[kBufSize]; - int64_t offset = start; - while (size > 0) { - const int64_t read_len = (size > kBufSize) ? kBufSize : size; - if (source->Read(offset, static_cast(read_len), buf)) - return false; - dst->Write(buf, static_cast(read_len)); - offset += read_len; - size -= read_len; - } - delete[] buf; - return true; -} - -/////////////////////////////////////////////////////////////// -// -// Frame Class - -Frame::Frame() - : add_id_(0), - additional_(NULL), - additional_length_(0), - duration_(0), - duration_set_(false), - frame_(NULL), - is_key_(false), - length_(0), - track_number_(0), - timestamp_(0), - discard_padding_(0), - reference_block_timestamp_(0), - reference_block_timestamp_set_(false) {} - -Frame::~Frame() { - delete[] frame_; - delete[] additional_; -} - -bool Frame::CopyFrom(const Frame& frame) { - delete[] frame_; - frame_ = NULL; - length_ = 0; - if (frame.length() > 0 && frame.frame() != NULL && - !Init(frame.frame(), frame.length())) { - return false; - } - add_id_ = 0; - delete[] additional_; - additional_ = NULL; - additional_length_ = 0; - if (frame.additional_length() > 0 && frame.additional() != NULL && - !AddAdditionalData(frame.additional(), frame.additional_length(), - frame.add_id())) { - return false; - } - duration_ = frame.duration(); - duration_set_ = frame.duration_set(); - is_key_ = frame.is_key(); - track_number_ = frame.track_number(); - timestamp_ = frame.timestamp(); - discard_padding_ = frame.discard_padding(); - reference_block_timestamp_ = frame.reference_block_timestamp(); - reference_block_timestamp_set_ = frame.reference_block_timestamp_set(); - return true; -} - -bool Frame::Init(const uint8_t* frame, uint64_t length) { - uint8_t* const data = - new (std::nothrow) uint8_t[static_cast(length)]; // NOLINT - if (!data) - return false; - - delete[] frame_; - frame_ = data; - length_ = length; - - memcpy(frame_, frame, static_cast(length_)); - return true; -} - -bool Frame::AddAdditionalData(const uint8_t* additional, uint64_t length, - uint64_t add_id) { - uint8_t* const data = - new (std::nothrow) uint8_t[static_cast(length)]; // NOLINT - if (!data) - return false; - - delete[] additional_; - additional_ = data; - additional_length_ = length; - add_id_ = add_id; - - memcpy(additional_, additional, static_cast(additional_length_)); - return true; -} - -bool Frame::IsValid() const { - if (length_ == 0 || !frame_) { - return false; - } - if ((additional_length_ != 0 && !additional_) || - (additional_ != NULL && additional_length_ == 0)) { - return false; - } - if (track_number_ == 0 || track_number_ > kMaxTrackNumber) { - return false; - } - if (!CanBeSimpleBlock() && !is_key_ && !reference_block_timestamp_set_) { - return false; - } - return true; -} - -bool Frame::CanBeSimpleBlock() const { - return additional_ == NULL && discard_padding_ == 0 && duration_ == 0; -} - -void Frame::set_duration(uint64_t duration) { - duration_ = duration; - duration_set_ = true; -} - -void Frame::set_reference_block_timestamp(int64_t reference_block_timestamp) { - reference_block_timestamp_ = reference_block_timestamp; - reference_block_timestamp_set_ = true; -} - -/////////////////////////////////////////////////////////////// -// -// CuePoint Class - -CuePoint::CuePoint() - : time_(0), - track_(0), - cluster_pos_(0), - block_number_(1), - output_block_number_(true) {} - -CuePoint::~CuePoint() {} - -bool CuePoint::Write(IMkvWriter* writer) const { - if (!writer || track_ < 1 || cluster_pos_ < 1) - return false; - - uint64_t size = EbmlElementSize(libwebm::kMkvCueClusterPosition, - static_cast(cluster_pos_)); - size += EbmlElementSize(libwebm::kMkvCueTrack, static_cast(track_)); - if (output_block_number_ && block_number_ > 1) - size += EbmlElementSize(libwebm::kMkvCueBlockNumber, - static_cast(block_number_)); - const uint64_t track_pos_size = - EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size; - const uint64_t payload_size = - EbmlElementSize(libwebm::kMkvCueTime, static_cast(time_)) + - track_pos_size; - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvCuePoint, payload_size)) - return false; - - const int64_t payload_position = writer->Position(); - if (payload_position < 0) - return false; - - if (!WriteEbmlElement(writer, libwebm::kMkvCueTime, - static_cast(time_))) { - return false; - } - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvCueTrackPositions, size)) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvCueTrack, - static_cast(track_))) { - return false; - } - if (!WriteEbmlElement(writer, libwebm::kMkvCueClusterPosition, - static_cast(cluster_pos_))) { - return false; - } - if (output_block_number_ && block_number_ > 1) { - if (!WriteEbmlElement(writer, libwebm::kMkvCueBlockNumber, - static_cast(block_number_))) { - return false; - } - } - - const int64_t stop_position = writer->Position(); - if (stop_position < 0) - return false; - - if (stop_position - payload_position != static_cast(payload_size)) - return false; - - return true; -} - -uint64_t CuePoint::PayloadSize() const { - uint64_t size = EbmlElementSize(libwebm::kMkvCueClusterPosition, - static_cast(cluster_pos_)); - size += EbmlElementSize(libwebm::kMkvCueTrack, static_cast(track_)); - if (output_block_number_ && block_number_ > 1) - size += EbmlElementSize(libwebm::kMkvCueBlockNumber, - static_cast(block_number_)); - const uint64_t track_pos_size = - EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size; - const uint64_t payload_size = - EbmlElementSize(libwebm::kMkvCueTime, static_cast(time_)) + - track_pos_size; - - return payload_size; -} - -uint64_t CuePoint::Size() const { - const uint64_t payload_size = PayloadSize(); - return EbmlMasterElementSize(libwebm::kMkvCuePoint, payload_size) + - payload_size; -} - -/////////////////////////////////////////////////////////////// -// -// Cues Class - -Cues::Cues() - : cue_entries_capacity_(0), - cue_entries_size_(0), - cue_entries_(NULL), - output_block_number_(true) {} - -Cues::~Cues() { - if (cue_entries_) { - for (int32_t i = 0; i < cue_entries_size_; ++i) { - CuePoint* const cue = cue_entries_[i]; - delete cue; - } - delete[] cue_entries_; - } -} - -bool Cues::AddCue(CuePoint* cue) { - if (!cue) - return false; - - if ((cue_entries_size_ + 1) > cue_entries_capacity_) { - // Add more CuePoints. - const int32_t new_capacity = - (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2; - - if (new_capacity < 1) - return false; - - CuePoint** const cues = - new (std::nothrow) CuePoint*[new_capacity]; // NOLINT - if (!cues) - return false; - - for (int32_t i = 0; i < cue_entries_size_; ++i) { - cues[i] = cue_entries_[i]; - } - - delete[] cue_entries_; - - cue_entries_ = cues; - cue_entries_capacity_ = new_capacity; - } - - cue->set_output_block_number(output_block_number_); - cue_entries_[cue_entries_size_++] = cue; - return true; -} - -CuePoint* Cues::GetCueByIndex(int32_t index) const { - if (cue_entries_ == NULL) - return NULL; - - if (index >= cue_entries_size_) - return NULL; - - return cue_entries_[index]; -} - -uint64_t Cues::Size() { - uint64_t size = 0; - for (int32_t i = 0; i < cue_entries_size_; ++i) - size += GetCueByIndex(i)->Size(); - size += EbmlMasterElementSize(libwebm::kMkvCues, size); - return size; -} - -bool Cues::Write(IMkvWriter* writer) const { - if (!writer) - return false; - - uint64_t size = 0; - for (int32_t i = 0; i < cue_entries_size_; ++i) { - const CuePoint* const cue = GetCueByIndex(i); - - if (!cue) - return false; - - size += cue->Size(); - } - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvCues, size)) - return false; - - const int64_t payload_position = writer->Position(); - if (payload_position < 0) - return false; - - for (int32_t i = 0; i < cue_entries_size_; ++i) { - const CuePoint* const cue = GetCueByIndex(i); - - if (!cue->Write(writer)) - return false; - } - - const int64_t stop_position = writer->Position(); - if (stop_position < 0) - return false; - - if (stop_position - payload_position != static_cast(size)) - return false; - - return true; -} - -/////////////////////////////////////////////////////////////// -// -// ContentEncAESSettings Class - -ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {} - -uint64_t ContentEncAESSettings::Size() const { - const uint64_t payload = PayloadSize(); - const uint64_t size = - EbmlMasterElementSize(libwebm::kMkvContentEncAESSettings, payload) + - payload; - return size; -} - -bool ContentEncAESSettings::Write(IMkvWriter* writer) const { - const uint64_t payload = PayloadSize(); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncAESSettings, - payload)) - return false; - - const int64_t payload_position = writer->Position(); - if (payload_position < 0) - return false; - - if (!WriteEbmlElement(writer, libwebm::kMkvAESSettingsCipherMode, - static_cast(cipher_mode_))) { - return false; - } - - const int64_t stop_position = writer->Position(); - if (stop_position < 0 || - stop_position - payload_position != static_cast(payload)) - return false; - - return true; -} - -uint64_t ContentEncAESSettings::PayloadSize() const { - uint64_t size = EbmlElementSize(libwebm::kMkvAESSettingsCipherMode, - static_cast(cipher_mode_)); - return size; -} - -/////////////////////////////////////////////////////////////// -// -// ContentEncoding Class - -ContentEncoding::ContentEncoding() - : enc_algo_(5), - enc_key_id_(NULL), - encoding_order_(0), - encoding_scope_(1), - encoding_type_(1), - enc_key_id_length_(0) {} - -ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; } - -bool ContentEncoding::SetEncryptionID(const uint8_t* id, uint64_t length) { - if (!id || length < 1) - return false; - - delete[] enc_key_id_; - - enc_key_id_ = - new (std::nothrow) uint8_t[static_cast(length)]; // NOLINT - if (!enc_key_id_) - return false; - - memcpy(enc_key_id_, id, static_cast(length)); - enc_key_id_length_ = length; - - return true; -} - -uint64_t ContentEncoding::Size() const { - const uint64_t encryption_size = EncryptionSize(); - const uint64_t encoding_size = EncodingSize(0, encryption_size); - const uint64_t encodings_size = - EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) + - encoding_size; - - return encodings_size; -} - -bool ContentEncoding::Write(IMkvWriter* writer) const { - const uint64_t encryption_size = EncryptionSize(); - const uint64_t encoding_size = EncodingSize(0, encryption_size); - const uint64_t size = - EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) + - encoding_size; - - const int64_t payload_position = writer->Position(); - if (payload_position < 0) - return false; - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncoding, - encoding_size)) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingOrder, - static_cast(encoding_order_))) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingScope, - static_cast(encoding_scope_))) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingType, - static_cast(encoding_type_))) - return false; - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncryption, - encryption_size)) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvContentEncAlgo, - static_cast(enc_algo_))) { - return false; - } - if (!WriteEbmlElement(writer, libwebm::kMkvContentEncKeyID, enc_key_id_, - enc_key_id_length_)) - return false; - - if (!enc_aes_settings_.Write(writer)) - return false; - - const int64_t stop_position = writer->Position(); - if (stop_position < 0 || - stop_position - payload_position != static_cast(size)) - return false; - - return true; -} - -uint64_t ContentEncoding::EncodingSize(uint64_t compresion_size, - uint64_t encryption_size) const { - // TODO(fgalligan): Add support for compression settings. - if (compresion_size != 0) - return 0; - - uint64_t encoding_size = 0; - - if (encryption_size > 0) { - encoding_size += - EbmlMasterElementSize(libwebm::kMkvContentEncryption, encryption_size) + - encryption_size; - } - encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingType, - static_cast(encoding_type_)); - encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingScope, - static_cast(encoding_scope_)); - encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingOrder, - static_cast(encoding_order_)); - - return encoding_size; -} - -uint64_t ContentEncoding::EncryptionSize() const { - const uint64_t aes_size = enc_aes_settings_.Size(); - - uint64_t encryption_size = EbmlElementSize(libwebm::kMkvContentEncKeyID, - enc_key_id_, enc_key_id_length_); - encryption_size += EbmlElementSize(libwebm::kMkvContentEncAlgo, - static_cast(enc_algo_)); - - return encryption_size + aes_size; -} - -/////////////////////////////////////////////////////////////// -// -// Track Class - -Track::Track(unsigned int* seed) - : codec_id_(NULL), - codec_private_(NULL), - language_(NULL), - max_block_additional_id_(0), - name_(NULL), - number_(0), - type_(0), - uid_(MakeUID(seed)), - codec_delay_(0), - seek_pre_roll_(0), - default_duration_(0), - codec_private_length_(0), - content_encoding_entries_(NULL), - content_encoding_entries_size_(0) {} - -Track::~Track() { - delete[] codec_id_; - delete[] codec_private_; - delete[] language_; - delete[] name_; - - if (content_encoding_entries_) { - for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { - ContentEncoding* const encoding = content_encoding_entries_[i]; - delete encoding; - } - delete[] content_encoding_entries_; - } -} - -bool Track::AddContentEncoding() { - const uint32_t count = content_encoding_entries_size_ + 1; - - ContentEncoding** const content_encoding_entries = - new (std::nothrow) ContentEncoding*[count]; // NOLINT - if (!content_encoding_entries) - return false; - - ContentEncoding* const content_encoding = - new (std::nothrow) ContentEncoding(); // NOLINT - if (!content_encoding) { - delete[] content_encoding_entries; - return false; - } - - for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { - content_encoding_entries[i] = content_encoding_entries_[i]; - } - - delete[] content_encoding_entries_; - - content_encoding_entries_ = content_encoding_entries; - content_encoding_entries_[content_encoding_entries_size_] = content_encoding; - content_encoding_entries_size_ = count; - return true; -} - -ContentEncoding* Track::GetContentEncodingByIndex(uint32_t index) const { - if (content_encoding_entries_ == NULL) - return NULL; - - if (index >= content_encoding_entries_size_) - return NULL; - - return content_encoding_entries_[index]; -} - -uint64_t Track::PayloadSize() const { - uint64_t size = - EbmlElementSize(libwebm::kMkvTrackNumber, static_cast(number_)); - size += EbmlElementSize(libwebm::kMkvTrackUID, static_cast(uid_)); - size += EbmlElementSize(libwebm::kMkvTrackType, static_cast(type_)); - if (codec_id_) - size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_); - if (codec_private_) - size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_, - codec_private_length_); - if (language_) - size += EbmlElementSize(libwebm::kMkvLanguage, language_); - if (name_) - size += EbmlElementSize(libwebm::kMkvName, name_); - if (max_block_additional_id_) { - size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID, - static_cast(max_block_additional_id_)); - } - if (codec_delay_) { - size += EbmlElementSize(libwebm::kMkvCodecDelay, - static_cast(codec_delay_)); - } - if (seek_pre_roll_) { - size += EbmlElementSize(libwebm::kMkvSeekPreRoll, - static_cast(seek_pre_roll_)); - } - if (default_duration_) { - size += EbmlElementSize(libwebm::kMkvDefaultDuration, - static_cast(default_duration_)); - } - - if (content_encoding_entries_size_ > 0) { - uint64_t content_encodings_size = 0; - for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { - ContentEncoding* const encoding = content_encoding_entries_[i]; - content_encodings_size += encoding->Size(); - } - - size += EbmlMasterElementSize(libwebm::kMkvContentEncodings, - content_encodings_size) + - content_encodings_size; - } - - return size; -} - -uint64_t Track::Size() const { - uint64_t size = PayloadSize(); - size += EbmlMasterElementSize(libwebm::kMkvTrackEntry, size); - return size; -} - -bool Track::Write(IMkvWriter* writer) const { - if (!writer) - return false; - - // mandatory elements without a default value. - if (!type_ || !codec_id_) - return false; - - // |size| may be bigger than what is written out in this function because - // derived classes may write out more data in the Track element. - const uint64_t payload_size = PayloadSize(); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvTrackEntry, payload_size)) - return false; - - uint64_t size = - EbmlElementSize(libwebm::kMkvTrackNumber, static_cast(number_)); - size += EbmlElementSize(libwebm::kMkvTrackUID, static_cast(uid_)); - size += EbmlElementSize(libwebm::kMkvTrackType, static_cast(type_)); - if (codec_id_) - size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_); - if (codec_private_) - size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_, - static_cast(codec_private_length_)); - if (language_) - size += EbmlElementSize(libwebm::kMkvLanguage, language_); - if (name_) - size += EbmlElementSize(libwebm::kMkvName, name_); - if (max_block_additional_id_) - size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID, - static_cast(max_block_additional_id_)); - if (codec_delay_) - size += EbmlElementSize(libwebm::kMkvCodecDelay, - static_cast(codec_delay_)); - if (seek_pre_roll_) - size += EbmlElementSize(libwebm::kMkvSeekPreRoll, - static_cast(seek_pre_roll_)); - if (default_duration_) - size += EbmlElementSize(libwebm::kMkvDefaultDuration, - static_cast(default_duration_)); - - const int64_t payload_position = writer->Position(); - if (payload_position < 0) - return false; - - if (!WriteEbmlElement(writer, libwebm::kMkvTrackNumber, - static_cast(number_))) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvTrackUID, - static_cast(uid_))) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvTrackType, - static_cast(type_))) - return false; - if (max_block_additional_id_) { - if (!WriteEbmlElement(writer, libwebm::kMkvMaxBlockAdditionID, - static_cast(max_block_additional_id_))) { - return false; - } - } - if (codec_delay_) { - if (!WriteEbmlElement(writer, libwebm::kMkvCodecDelay, - static_cast(codec_delay_))) - return false; - } - if (seek_pre_roll_) { - if (!WriteEbmlElement(writer, libwebm::kMkvSeekPreRoll, - static_cast(seek_pre_roll_))) - return false; - } - if (default_duration_) { - if (!WriteEbmlElement(writer, libwebm::kMkvDefaultDuration, - static_cast(default_duration_))) - return false; - } - if (codec_id_) { - if (!WriteEbmlElement(writer, libwebm::kMkvCodecID, codec_id_)) - return false; - } - if (codec_private_) { - if (!WriteEbmlElement(writer, libwebm::kMkvCodecPrivate, codec_private_, - static_cast(codec_private_length_))) - return false; - } - if (language_) { - if (!WriteEbmlElement(writer, libwebm::kMkvLanguage, language_)) - return false; - } - if (name_) { - if (!WriteEbmlElement(writer, libwebm::kMkvName, name_)) - return false; - } - - int64_t stop_position = writer->Position(); - if (stop_position < 0 || - stop_position - payload_position != static_cast(size)) - return false; - - if (content_encoding_entries_size_ > 0) { - uint64_t content_encodings_size = 0; - for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { - ContentEncoding* const encoding = content_encoding_entries_[i]; - content_encodings_size += encoding->Size(); - } - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncodings, - content_encodings_size)) - return false; - - for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { - ContentEncoding* const encoding = content_encoding_entries_[i]; - if (!encoding->Write(writer)) - return false; - } - } - - stop_position = writer->Position(); - if (stop_position < 0) - return false; - return true; -} - -bool Track::SetCodecPrivate(const uint8_t* codec_private, uint64_t length) { - if (!codec_private || length < 1) - return false; - - delete[] codec_private_; - - codec_private_ = - new (std::nothrow) uint8_t[static_cast(length)]; // NOLINT - if (!codec_private_) - return false; - - memcpy(codec_private_, codec_private, static_cast(length)); - codec_private_length_ = length; - - return true; -} - -void Track::set_codec_id(const char* codec_id) { - if (codec_id) { - delete[] codec_id_; - - const size_t length = strlen(codec_id) + 1; - codec_id_ = new (std::nothrow) char[length]; // NOLINT - if (codec_id_) { -#ifdef _MSC_VER - strcpy_s(codec_id_, length, codec_id); -#else - strcpy(codec_id_, codec_id); -#endif - } - } -} - -// TODO(fgalligan): Vet the language parameter. -void Track::set_language(const char* language) { - if (language) { - delete[] language_; - - const size_t length = strlen(language) + 1; - language_ = new (std::nothrow) char[length]; // NOLINT - if (language_) { -#ifdef _MSC_VER - strcpy_s(language_, length, language); -#else - strcpy(language_, language); -#endif - } - } -} - -void Track::set_name(const char* name) { - if (name) { - delete[] name_; - - const size_t length = strlen(name) + 1; - name_ = new (std::nothrow) char[length]; // NOLINT - if (name_) { -#ifdef _MSC_VER - strcpy_s(name_, length, name); -#else - strcpy(name_, name); -#endif - } - } -} - -/////////////////////////////////////////////////////////////// -// -// Colour and its child elements - -uint64_t PrimaryChromaticity::PrimaryChromaticitySize( - libwebm::MkvId x_id, libwebm::MkvId y_id) const { - return EbmlElementSize(x_id, x_) + EbmlElementSize(y_id, y_); -} - -bool PrimaryChromaticity::Write(IMkvWriter* writer, libwebm::MkvId x_id, - libwebm::MkvId y_id) const { - if (!Valid()) { - return false; - } - return WriteEbmlElement(writer, x_id, x_) && - WriteEbmlElement(writer, y_id, y_); -} - -bool PrimaryChromaticity::Valid() const { - return (x_ >= kChromaticityMin && x_ <= kChromaticityMax && - y_ >= kChromaticityMin && y_ <= kChromaticityMax); -} - -uint64_t MasteringMetadata::MasteringMetadataSize() const { - uint64_t size = PayloadSize(); - - if (size > 0) - size += EbmlMasterElementSize(libwebm::kMkvMasteringMetadata, size); - - return size; -} - -bool MasteringMetadata::Valid() const { - if (luminance_min_ != kValueNotPresent) { - if (luminance_min_ < kMinLuminance || luminance_min_ > kMinLuminanceMax || - luminance_min_ > luminance_max_) { - return false; - } - } - if (luminance_max_ != kValueNotPresent) { - if (luminance_max_ < kMinLuminance || luminance_max_ > kMaxLuminanceMax || - luminance_max_ < luminance_min_) { - return false; - } - } - if (r_ && !r_->Valid()) - return false; - if (g_ && !g_->Valid()) - return false; - if (b_ && !b_->Valid()) - return false; - if (white_point_ && !white_point_->Valid()) - return false; - - return true; -} - -bool MasteringMetadata::Write(IMkvWriter* writer) const { - const uint64_t size = PayloadSize(); - - // Don't write an empty element. - if (size == 0) - return true; - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvMasteringMetadata, size)) - return false; - if (luminance_max_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvLuminanceMax, luminance_max_)) { - return false; - } - if (luminance_min_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvLuminanceMin, luminance_min_)) { - return false; - } - if (r_ && - !r_->Write(writer, libwebm::kMkvPrimaryRChromaticityX, - libwebm::kMkvPrimaryRChromaticityY)) { - return false; - } - if (g_ && - !g_->Write(writer, libwebm::kMkvPrimaryGChromaticityX, - libwebm::kMkvPrimaryGChromaticityY)) { - return false; - } - if (b_ && - !b_->Write(writer, libwebm::kMkvPrimaryBChromaticityX, - libwebm::kMkvPrimaryBChromaticityY)) { - return false; - } - if (white_point_ && - !white_point_->Write(writer, libwebm::kMkvWhitePointChromaticityX, - libwebm::kMkvWhitePointChromaticityY)) { - return false; - } - - return true; -} - -bool MasteringMetadata::SetChromaticity( - const PrimaryChromaticity* r, const PrimaryChromaticity* g, - const PrimaryChromaticity* b, const PrimaryChromaticity* white_point) { - PrimaryChromaticityPtr r_ptr(nullptr); - if (r) { - if (!CopyChromaticity(r, &r_ptr)) - return false; - } - PrimaryChromaticityPtr g_ptr(nullptr); - if (g) { - if (!CopyChromaticity(g, &g_ptr)) - return false; - } - PrimaryChromaticityPtr b_ptr(nullptr); - if (b) { - if (!CopyChromaticity(b, &b_ptr)) - return false; - } - PrimaryChromaticityPtr wp_ptr(nullptr); - if (white_point) { - if (!CopyChromaticity(white_point, &wp_ptr)) - return false; - } - - r_ = r_ptr.release(); - g_ = g_ptr.release(); - b_ = b_ptr.release(); - white_point_ = wp_ptr.release(); - return true; -} - -uint64_t MasteringMetadata::PayloadSize() const { - uint64_t size = 0; - - if (luminance_max_ != kValueNotPresent) - size += EbmlElementSize(libwebm::kMkvLuminanceMax, luminance_max_); - if (luminance_min_ != kValueNotPresent) - size += EbmlElementSize(libwebm::kMkvLuminanceMin, luminance_min_); - - if (r_) { - size += r_->PrimaryChromaticitySize(libwebm::kMkvPrimaryRChromaticityX, - libwebm::kMkvPrimaryRChromaticityY); - } - if (g_) { - size += g_->PrimaryChromaticitySize(libwebm::kMkvPrimaryGChromaticityX, - libwebm::kMkvPrimaryGChromaticityY); - } - if (b_) { - size += b_->PrimaryChromaticitySize(libwebm::kMkvPrimaryBChromaticityX, - libwebm::kMkvPrimaryBChromaticityY); - } - if (white_point_) { - size += white_point_->PrimaryChromaticitySize( - libwebm::kMkvWhitePointChromaticityX, - libwebm::kMkvWhitePointChromaticityY); - } - - return size; -} - -uint64_t Colour::ColourSize() const { - uint64_t size = PayloadSize(); - - if (size > 0) - size += EbmlMasterElementSize(libwebm::kMkvColour, size); - - return size; -} - -bool Colour::Valid() const { - if (mastering_metadata_ && !mastering_metadata_->Valid()) - return false; - if (matrix_coefficients_ != kValueNotPresent && - !IsMatrixCoefficientsValueValid(matrix_coefficients_)) { - return false; - } - if (chroma_siting_horz_ != kValueNotPresent && - !IsChromaSitingHorzValueValid(chroma_siting_horz_)) { - return false; - } - if (chroma_siting_vert_ != kValueNotPresent && - !IsChromaSitingVertValueValid(chroma_siting_vert_)) { - return false; - } - if (range_ != kValueNotPresent && !IsColourRangeValueValid(range_)) - return false; - if (transfer_characteristics_ != kValueNotPresent && - !IsTransferCharacteristicsValueValid(transfer_characteristics_)) { - return false; - } - if (primaries_ != kValueNotPresent && !IsPrimariesValueValid(primaries_)) - return false; - - return true; -} - -bool Colour::Write(IMkvWriter* writer) const { - const uint64_t size = PayloadSize(); - - // Don't write an empty element. - if (size == 0) - return true; - - // Don't write an invalid element. - if (!Valid()) - return false; - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvColour, size)) - return false; - - if (matrix_coefficients_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvMatrixCoefficients, - static_cast(matrix_coefficients_))) { - return false; - } - if (bits_per_channel_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvBitsPerChannel, - static_cast(bits_per_channel_))) { - return false; - } - if (chroma_subsampling_horz_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingHorz, - static_cast(chroma_subsampling_horz_))) { - return false; - } - if (chroma_subsampling_vert_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingVert, - static_cast(chroma_subsampling_vert_))) { - return false; - } - - if (cb_subsampling_horz_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingHorz, - static_cast(cb_subsampling_horz_))) { - return false; - } - if (cb_subsampling_vert_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingVert, - static_cast(cb_subsampling_vert_))) { - return false; - } - if (chroma_siting_horz_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvChromaSitingHorz, - static_cast(chroma_siting_horz_))) { - return false; - } - if (chroma_siting_vert_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvChromaSitingVert, - static_cast(chroma_siting_vert_))) { - return false; - } - if (range_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvRange, - static_cast(range_))) { - return false; - } - if (transfer_characteristics_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvTransferCharacteristics, - static_cast(transfer_characteristics_))) { - return false; - } - if (primaries_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvPrimaries, - static_cast(primaries_))) { - return false; - } - if (max_cll_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvMaxCLL, - static_cast(max_cll_))) { - return false; - } - if (max_fall_ != kValueNotPresent && - !WriteEbmlElement(writer, libwebm::kMkvMaxFALL, - static_cast(max_fall_))) { - return false; - } - - if (mastering_metadata_ && !mastering_metadata_->Write(writer)) - return false; - - return true; -} - -bool Colour::SetMasteringMetadata(const MasteringMetadata& mastering_metadata) { - std::unique_ptr mm_ptr(new MasteringMetadata()); - if (!mm_ptr.get()) - return false; - - mm_ptr->set_luminance_max(mastering_metadata.luminance_max()); - mm_ptr->set_luminance_min(mastering_metadata.luminance_min()); - - if (!mm_ptr->SetChromaticity(mastering_metadata.r(), mastering_metadata.g(), - mastering_metadata.b(), - mastering_metadata.white_point())) { - return false; - } - - delete mastering_metadata_; - mastering_metadata_ = mm_ptr.release(); - return true; -} - -uint64_t Colour::PayloadSize() const { - uint64_t size = 0; - - if (matrix_coefficients_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvMatrixCoefficients, - static_cast(matrix_coefficients_)); - } - if (bits_per_channel_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvBitsPerChannel, - static_cast(bits_per_channel_)); - } - if (chroma_subsampling_horz_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvChromaSubsamplingHorz, - static_cast(chroma_subsampling_horz_)); - } - if (chroma_subsampling_vert_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvChromaSubsamplingVert, - static_cast(chroma_subsampling_vert_)); - } - if (cb_subsampling_horz_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvCbSubsamplingHorz, - static_cast(cb_subsampling_horz_)); - } - if (cb_subsampling_vert_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvCbSubsamplingVert, - static_cast(cb_subsampling_vert_)); - } - if (chroma_siting_horz_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvChromaSitingHorz, - static_cast(chroma_siting_horz_)); - } - if (chroma_siting_vert_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvChromaSitingVert, - static_cast(chroma_siting_vert_)); - } - if (range_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvRange, static_cast(range_)); - } - if (transfer_characteristics_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvTransferCharacteristics, - static_cast(transfer_characteristics_)); - } - if (primaries_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvPrimaries, - static_cast(primaries_)); - } - if (max_cll_ != kValueNotPresent) { - size += EbmlElementSize(libwebm::kMkvMaxCLL, static_cast(max_cll_)); - } - if (max_fall_ != kValueNotPresent) { - size += - EbmlElementSize(libwebm::kMkvMaxFALL, static_cast(max_fall_)); - } - - if (mastering_metadata_) - size += mastering_metadata_->MasteringMetadataSize(); - - return size; -} - -/////////////////////////////////////////////////////////////// -// -// Projection element - -uint64_t Projection::ProjectionSize() const { - uint64_t size = PayloadSize(); - - if (size > 0) - size += EbmlMasterElementSize(libwebm::kMkvProjection, size); - - return size; -} - -bool Projection::Write(IMkvWriter* writer) const { - const uint64_t size = PayloadSize(); - - // Don't write an empty element. - if (size == 0) - return true; - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvProjection, size)) - return false; - - if (!WriteEbmlElement(writer, libwebm::kMkvProjectionType, - static_cast(type_))) { - return false; - } - - if (private_data_length_ > 0 && private_data_ != NULL && - !WriteEbmlElement(writer, libwebm::kMkvProjectionPrivate, private_data_, - private_data_length_)) { - return false; - } - - if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseYaw, pose_yaw_)) - return false; - - if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPosePitch, - pose_pitch_)) { - return false; - } - - if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseRoll, pose_roll_)) { - return false; - } - - return true; -} - -bool Projection::SetProjectionPrivate(const uint8_t* data, - uint64_t data_length) { - if (data == NULL || data_length == 0) { - return false; - } - - if (data_length != static_cast(data_length)) { - return false; - } - - uint8_t* new_private_data = - new (std::nothrow) uint8_t[static_cast(data_length)]; - if (new_private_data == NULL) { - return false; - } - - delete[] private_data_; - private_data_ = new_private_data; - private_data_length_ = data_length; - memcpy(private_data_, data, static_cast(data_length)); - - return true; -} - -uint64_t Projection::PayloadSize() const { - uint64_t size = - EbmlElementSize(libwebm::kMkvProjection, static_cast(type_)); - - if (private_data_length_ > 0 && private_data_ != NULL) { - size += EbmlElementSize(libwebm::kMkvProjectionPrivate, private_data_, - private_data_length_); - } - - size += EbmlElementSize(libwebm::kMkvProjectionPoseYaw, pose_yaw_); - size += EbmlElementSize(libwebm::kMkvProjectionPosePitch, pose_pitch_); - size += EbmlElementSize(libwebm::kMkvProjectionPoseRoll, pose_roll_); - - return size; -} - -/////////////////////////////////////////////////////////////// -// -// VideoTrack Class - -VideoTrack::VideoTrack(unsigned int* seed) - : Track(seed), - display_height_(0), - display_width_(0), - pixel_height_(0), - pixel_width_(0), - crop_left_(0), - crop_right_(0), - crop_top_(0), - crop_bottom_(0), - frame_rate_(0.0), - height_(0), - stereo_mode_(0), - alpha_mode_(0), - width_(0), - colour_(NULL), - projection_(NULL) {} - -VideoTrack::~VideoTrack() { - delete colour_; - delete projection_; -} - -bool VideoTrack::SetStereoMode(uint64_t stereo_mode) { - if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst && - stereo_mode != kTopBottomRightIsFirst && - stereo_mode != kTopBottomLeftIsFirst && - stereo_mode != kSideBySideRightIsFirst) - return false; - - stereo_mode_ = stereo_mode; - return true; -} - -bool VideoTrack::SetAlphaMode(uint64_t alpha_mode) { - if (alpha_mode != kNoAlpha && alpha_mode != kAlpha) - return false; - - alpha_mode_ = alpha_mode; - return true; -} - -uint64_t VideoTrack::PayloadSize() const { - const uint64_t parent_size = Track::PayloadSize(); - - uint64_t size = VideoPayloadSize(); - size += EbmlMasterElementSize(libwebm::kMkvVideo, size); - - return parent_size + size; -} - -bool VideoTrack::Write(IMkvWriter* writer) const { - if (!Track::Write(writer)) - return false; - - const uint64_t size = VideoPayloadSize(); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvVideo, size)) - return false; - - const int64_t payload_position = writer->Position(); - if (payload_position < 0) - return false; - - if (!WriteEbmlElement( - writer, libwebm::kMkvPixelWidth, - static_cast((pixel_width_ > 0) ? pixel_width_ : width_))) - return false; - if (!WriteEbmlElement( - writer, libwebm::kMkvPixelHeight, - static_cast((pixel_height_ > 0) ? pixel_height_ : height_))) - return false; - if (display_width_ > 0) { - if (!WriteEbmlElement(writer, libwebm::kMkvDisplayWidth, - static_cast(display_width_))) - return false; - } - if (display_height_ > 0) { - if (!WriteEbmlElement(writer, libwebm::kMkvDisplayHeight, - static_cast(display_height_))) - return false; - } - if (crop_left_ > 0) { - if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropLeft, - static_cast(crop_left_))) - return false; - } - if (crop_right_ > 0) { - if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropRight, - static_cast(crop_right_))) - return false; - } - if (crop_top_ > 0) { - if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropTop, - static_cast(crop_top_))) - return false; - } - if (crop_bottom_ > 0) { - if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropBottom, - static_cast(crop_bottom_))) - return false; - } - if (stereo_mode_ > kMono) { - if (!WriteEbmlElement(writer, libwebm::kMkvStereoMode, - static_cast(stereo_mode_))) - return false; - } - if (alpha_mode_ > kNoAlpha) { - if (!WriteEbmlElement(writer, libwebm::kMkvAlphaMode, - static_cast(alpha_mode_))) - return false; - } - if (frame_rate_ > 0.0) { - if (!WriteEbmlElement(writer, libwebm::kMkvFrameRate, - static_cast(frame_rate_))) { - return false; - } - } - if (colour_) { - if (!colour_->Write(writer)) - return false; - } - if (projection_) { - if (!projection_->Write(writer)) - return false; - } - - const int64_t stop_position = writer->Position(); - if (stop_position < 0 || - stop_position - payload_position != static_cast(size)) { - return false; - } - - return true; -} - -bool VideoTrack::SetColour(const Colour& colour) { - std::unique_ptr colour_ptr(new Colour()); - if (!colour_ptr.get()) - return false; - - if (colour.mastering_metadata()) { - if (!colour_ptr->SetMasteringMetadata(*colour.mastering_metadata())) - return false; - } - - colour_ptr->set_matrix_coefficients(colour.matrix_coefficients()); - colour_ptr->set_bits_per_channel(colour.bits_per_channel()); - colour_ptr->set_chroma_subsampling_horz(colour.chroma_subsampling_horz()); - colour_ptr->set_chroma_subsampling_vert(colour.chroma_subsampling_vert()); - colour_ptr->set_cb_subsampling_horz(colour.cb_subsampling_horz()); - colour_ptr->set_cb_subsampling_vert(colour.cb_subsampling_vert()); - colour_ptr->set_chroma_siting_horz(colour.chroma_siting_horz()); - colour_ptr->set_chroma_siting_vert(colour.chroma_siting_vert()); - colour_ptr->set_range(colour.range()); - colour_ptr->set_transfer_characteristics(colour.transfer_characteristics()); - colour_ptr->set_primaries(colour.primaries()); - colour_ptr->set_max_cll(colour.max_cll()); - colour_ptr->set_max_fall(colour.max_fall()); - delete colour_; - colour_ = colour_ptr.release(); - return true; -} - -bool VideoTrack::SetProjection(const Projection& projection) { - std::unique_ptr projection_ptr(new Projection()); - if (!projection_ptr.get()) - return false; - - if (projection.private_data()) { - if (!projection_ptr->SetProjectionPrivate( - projection.private_data(), projection.private_data_length())) { - return false; - } - } - - projection_ptr->set_type(projection.type()); - projection_ptr->set_pose_yaw(projection.pose_yaw()); - projection_ptr->set_pose_pitch(projection.pose_pitch()); - projection_ptr->set_pose_roll(projection.pose_roll()); - delete projection_; - projection_ = projection_ptr.release(); - return true; -} - -uint64_t VideoTrack::VideoPayloadSize() const { - uint64_t size = EbmlElementSize( - libwebm::kMkvPixelWidth, - static_cast((pixel_width_ > 0) ? pixel_width_ : width_)); - size += EbmlElementSize( - libwebm::kMkvPixelHeight, - static_cast((pixel_height_ > 0) ? pixel_height_ : height_)); - if (display_width_ > 0) - size += EbmlElementSize(libwebm::kMkvDisplayWidth, - static_cast(display_width_)); - if (display_height_ > 0) - size += EbmlElementSize(libwebm::kMkvDisplayHeight, - static_cast(display_height_)); - if (crop_left_ > 0) - size += EbmlElementSize(libwebm::kMkvPixelCropLeft, - static_cast(crop_left_)); - if (crop_right_ > 0) - size += EbmlElementSize(libwebm::kMkvPixelCropRight, - static_cast(crop_right_)); - if (crop_top_ > 0) - size += EbmlElementSize(libwebm::kMkvPixelCropTop, - static_cast(crop_top_)); - if (crop_bottom_ > 0) - size += EbmlElementSize(libwebm::kMkvPixelCropBottom, - static_cast(crop_bottom_)); - if (stereo_mode_ > kMono) - size += EbmlElementSize(libwebm::kMkvStereoMode, - static_cast(stereo_mode_)); - if (alpha_mode_ > kNoAlpha) - size += EbmlElementSize(libwebm::kMkvAlphaMode, - static_cast(alpha_mode_)); - if (frame_rate_ > 0.0) - size += EbmlElementSize(libwebm::kMkvFrameRate, - static_cast(frame_rate_)); - if (colour_) - size += colour_->ColourSize(); - if (projection_) - size += projection_->ProjectionSize(); - - return size; -} - -/////////////////////////////////////////////////////////////// -// -// AudioTrack Class - -AudioTrack::AudioTrack(unsigned int* seed) - : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {} - -AudioTrack::~AudioTrack() {} - -uint64_t AudioTrack::PayloadSize() const { - const uint64_t parent_size = Track::PayloadSize(); - - uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency, - static_cast(sample_rate_)); - size += - EbmlElementSize(libwebm::kMkvChannels, static_cast(channels_)); - if (bit_depth_ > 0) - size += - EbmlElementSize(libwebm::kMkvBitDepth, static_cast(bit_depth_)); - size += EbmlMasterElementSize(libwebm::kMkvAudio, size); - - return parent_size + size; -} - -bool AudioTrack::Write(IMkvWriter* writer) const { - if (!Track::Write(writer)) - return false; - - // Calculate AudioSettings size. - uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency, - static_cast(sample_rate_)); - size += - EbmlElementSize(libwebm::kMkvChannels, static_cast(channels_)); - if (bit_depth_ > 0) - size += - EbmlElementSize(libwebm::kMkvBitDepth, static_cast(bit_depth_)); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvAudio, size)) - return false; - - const int64_t payload_position = writer->Position(); - if (payload_position < 0) - return false; - - if (!WriteEbmlElement(writer, libwebm::kMkvSamplingFrequency, - static_cast(sample_rate_))) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvChannels, - static_cast(channels_))) - return false; - if (bit_depth_ > 0) - if (!WriteEbmlElement(writer, libwebm::kMkvBitDepth, - static_cast(bit_depth_))) - return false; - - const int64_t stop_position = writer->Position(); - if (stop_position < 0 || - stop_position - payload_position != static_cast(size)) - return false; - - return true; -} - -/////////////////////////////////////////////////////////////// -// -// Tracks Class - -const char Tracks::kOpusCodecId[] = "A_OPUS"; -const char Tracks::kVorbisCodecId[] = "A_VORBIS"; -const char Tracks::kVp8CodecId[] = "V_VP8"; -const char Tracks::kVp9CodecId[] = "V_VP9"; -const char Tracks::kVp10CodecId[] = "V_VP10"; -const char Tracks::kWebVttCaptionsId[] = "D_WEBVTT/CAPTIONS"; -const char Tracks::kWebVttDescriptionsId[] = "D_WEBVTT/DESCRIPTIONS"; -const char Tracks::kWebVttMetadataId[] = "D_WEBVTT/METADATA"; -const char Tracks::kWebVttSubtitlesId[] = "D_WEBVTT/SUBTITLES"; - -Tracks::Tracks() - : track_entries_(NULL), track_entries_size_(0), wrote_tracks_(false) {} - -Tracks::~Tracks() { - if (track_entries_) { - for (uint32_t i = 0; i < track_entries_size_; ++i) { - Track* const track = track_entries_[i]; - delete track; - } - delete[] track_entries_; - } -} - -bool Tracks::AddTrack(Track* track, int32_t number) { - if (number < 0 || wrote_tracks_) - return false; - - // This muxer only supports track numbers in the range [1, 126], in - // order to be able (to use Matroska integer representation) to - // serialize the block header (of which the track number is a part) - // for a frame using exactly 4 bytes. - - if (number > 0x7E) - return false; - - uint32_t track_num = number; - - if (track_num > 0) { - // Check to make sure a track does not already have |track_num|. - for (uint32_t i = 0; i < track_entries_size_; ++i) { - if (track_entries_[i]->number() == track_num) - return false; - } - } - - const uint32_t count = track_entries_size_ + 1; - - Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT - if (!track_entries) - return false; - - for (uint32_t i = 0; i < track_entries_size_; ++i) { - track_entries[i] = track_entries_[i]; - } - - delete[] track_entries_; - - // Find the lowest availible track number > 0. - if (track_num == 0) { - track_num = count; - - // Check to make sure a track does not already have |track_num|. - bool exit = false; - do { - exit = true; - for (uint32_t i = 0; i < track_entries_size_; ++i) { - if (track_entries[i]->number() == track_num) { - track_num++; - exit = false; - break; - } - } - } while (!exit); - } - track->set_number(track_num); - - track_entries_ = track_entries; - track_entries_[track_entries_size_] = track; - track_entries_size_ = count; - return true; -} - -const Track* Tracks::GetTrackByIndex(uint32_t index) const { - if (track_entries_ == NULL) - return NULL; - - if (index >= track_entries_size_) - return NULL; - - return track_entries_[index]; -} - -Track* Tracks::GetTrackByNumber(uint64_t track_number) const { - const int32_t count = track_entries_size(); - for (int32_t i = 0; i < count; ++i) { - if (track_entries_[i]->number() == track_number) - return track_entries_[i]; - } - - return NULL; -} - -bool Tracks::TrackIsAudio(uint64_t track_number) const { - const Track* const track = GetTrackByNumber(track_number); - - if (track->type() == kAudio) - return true; - - return false; -} - -bool Tracks::TrackIsVideo(uint64_t track_number) const { - const Track* const track = GetTrackByNumber(track_number); - - if (track->type() == kVideo) - return true; - - return false; -} - -bool Tracks::Write(IMkvWriter* writer) const { - uint64_t size = 0; - const int32_t count = track_entries_size(); - for (int32_t i = 0; i < count; ++i) { - const Track* const track = GetTrackByIndex(i); - - if (!track) - return false; - - size += track->Size(); - } - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvTracks, size)) - return false; - - const int64_t payload_position = writer->Position(); - if (payload_position < 0) - return false; - - for (int32_t i = 0; i < count; ++i) { - const Track* const track = GetTrackByIndex(i); - if (!track->Write(writer)) - return false; - } - - const int64_t stop_position = writer->Position(); - if (stop_position < 0 || - stop_position - payload_position != static_cast(size)) - return false; - - wrote_tracks_ = true; - return true; -} - -/////////////////////////////////////////////////////////////// -// -// Chapter Class - -bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); } - -void Chapter::set_time(const Segment& segment, uint64_t start_ns, - uint64_t end_ns) { - const SegmentInfo* const info = segment.GetSegmentInfo(); - const uint64_t timecode_scale = info->timecode_scale(); - start_timecode_ = start_ns / timecode_scale; - end_timecode_ = end_ns / timecode_scale; -} - -bool Chapter::add_string(const char* title, const char* language, - const char* country) { - if (!ExpandDisplaysArray()) - return false; - - Display& d = displays_[displays_count_++]; - d.Init(); - - if (!d.set_title(title)) - return false; - - if (!d.set_language(language)) - return false; - - if (!d.set_country(country)) - return false; - - return true; -} - -Chapter::Chapter() { - // This ctor only constructs the object. Proper initialization is - // done in Init() (called in Chapters::AddChapter()). The only - // reason we bother implementing this ctor is because we had to - // declare it as private (along with the dtor), in order to prevent - // clients from creating Chapter instances (a privelege we grant - // only to the Chapters class). Doing no initialization here also - // means that creating arrays of chapter objects is more efficient, - // because we only initialize each new chapter object as it becomes - // active on the array. -} - -Chapter::~Chapter() {} - -void Chapter::Init(unsigned int* seed) { - id_ = NULL; - start_timecode_ = 0; - end_timecode_ = 0; - displays_ = NULL; - displays_size_ = 0; - displays_count_ = 0; - uid_ = MakeUID(seed); -} - -void Chapter::ShallowCopy(Chapter* dst) const { - dst->id_ = id_; - dst->start_timecode_ = start_timecode_; - dst->end_timecode_ = end_timecode_; - dst->uid_ = uid_; - dst->displays_ = displays_; - dst->displays_size_ = displays_size_; - dst->displays_count_ = displays_count_; -} - -void Chapter::Clear() { - StrCpy(NULL, &id_); - - while (displays_count_ > 0) { - Display& d = displays_[--displays_count_]; - d.Clear(); - } - - delete[] displays_; - displays_ = NULL; - - displays_size_ = 0; -} - -bool Chapter::ExpandDisplaysArray() { - if (displays_size_ > displays_count_) - return true; // nothing to do yet - - const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_; - - Display* const displays = new (std::nothrow) Display[size]; // NOLINT - if (displays == NULL) - return false; - - for (int idx = 0; idx < displays_count_; ++idx) { - displays[idx] = displays_[idx]; // shallow copy - } - - delete[] displays_; - - displays_ = displays; - displays_size_ = size; - - return true; -} - -uint64_t Chapter::WriteAtom(IMkvWriter* writer) const { - uint64_t payload_size = - EbmlElementSize(libwebm::kMkvChapterStringUID, id_) + - EbmlElementSize(libwebm::kMkvChapterUID, static_cast(uid_)) + - EbmlElementSize(libwebm::kMkvChapterTimeStart, - static_cast(start_timecode_)) + - EbmlElementSize(libwebm::kMkvChapterTimeEnd, - static_cast(end_timecode_)); - - for (int idx = 0; idx < displays_count_; ++idx) { - const Display& d = displays_[idx]; - payload_size += d.WriteDisplay(NULL); - } - - const uint64_t atom_size = - EbmlMasterElementSize(libwebm::kMkvChapterAtom, payload_size) + - payload_size; - - if (writer == NULL) - return atom_size; - - const int64_t start = writer->Position(); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterAtom, payload_size)) - return 0; - - if (!WriteEbmlElement(writer, libwebm::kMkvChapterStringUID, id_)) - return 0; - - if (!WriteEbmlElement(writer, libwebm::kMkvChapterUID, - static_cast(uid_))) - return 0; - - if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeStart, - static_cast(start_timecode_))) - return 0; - - if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeEnd, - static_cast(end_timecode_))) - return 0; - - for (int idx = 0; idx < displays_count_; ++idx) { - const Display& d = displays_[idx]; - - if (!d.WriteDisplay(writer)) - return 0; - } - - const int64_t stop = writer->Position(); - - if (stop >= start && uint64_t(stop - start) != atom_size) - return 0; - - return atom_size; -} - -void Chapter::Display::Init() { - title_ = NULL; - language_ = NULL; - country_ = NULL; -} - -void Chapter::Display::Clear() { - StrCpy(NULL, &title_); - StrCpy(NULL, &language_); - StrCpy(NULL, &country_); -} - -bool Chapter::Display::set_title(const char* title) { - return StrCpy(title, &title_); -} - -bool Chapter::Display::set_language(const char* language) { - return StrCpy(language, &language_); -} - -bool Chapter::Display::set_country(const char* country) { - return StrCpy(country, &country_); -} - -uint64_t Chapter::Display::WriteDisplay(IMkvWriter* writer) const { - uint64_t payload_size = EbmlElementSize(libwebm::kMkvChapString, title_); - - if (language_) - payload_size += EbmlElementSize(libwebm::kMkvChapLanguage, language_); - - if (country_) - payload_size += EbmlElementSize(libwebm::kMkvChapCountry, country_); - - const uint64_t display_size = - EbmlMasterElementSize(libwebm::kMkvChapterDisplay, payload_size) + - payload_size; - - if (writer == NULL) - return display_size; - - const int64_t start = writer->Position(); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterDisplay, - payload_size)) - return 0; - - if (!WriteEbmlElement(writer, libwebm::kMkvChapString, title_)) - return 0; - - if (language_) { - if (!WriteEbmlElement(writer, libwebm::kMkvChapLanguage, language_)) - return 0; - } - - if (country_) { - if (!WriteEbmlElement(writer, libwebm::kMkvChapCountry, country_)) - return 0; - } - - const int64_t stop = writer->Position(); - - if (stop >= start && uint64_t(stop - start) != display_size) - return 0; - - return display_size; -} - -/////////////////////////////////////////////////////////////// -// -// Chapters Class - -Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {} - -Chapters::~Chapters() { - while (chapters_count_ > 0) { - Chapter& chapter = chapters_[--chapters_count_]; - chapter.Clear(); - } - - delete[] chapters_; - chapters_ = NULL; -} - -int Chapters::Count() const { return chapters_count_; } - -Chapter* Chapters::AddChapter(unsigned int* seed) { - if (!ExpandChaptersArray()) - return NULL; - - Chapter& chapter = chapters_[chapters_count_++]; - chapter.Init(seed); - - return &chapter; -} - -bool Chapters::Write(IMkvWriter* writer) const { - if (writer == NULL) - return false; - - const uint64_t payload_size = WriteEdition(NULL); // return size only - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapters, payload_size)) - return false; - - const int64_t start = writer->Position(); - - if (WriteEdition(writer) == 0) // error - return false; - - const int64_t stop = writer->Position(); - - if (stop >= start && uint64_t(stop - start) != payload_size) - return false; - - return true; -} - -bool Chapters::ExpandChaptersArray() { - if (chapters_size_ > chapters_count_) - return true; // nothing to do yet - - const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_; - - Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT - if (chapters == NULL) - return false; - - for (int idx = 0; idx < chapters_count_; ++idx) { - const Chapter& src = chapters_[idx]; - Chapter* const dst = chapters + idx; - src.ShallowCopy(dst); - } - - delete[] chapters_; - - chapters_ = chapters; - chapters_size_ = size; - - return true; -} - -uint64_t Chapters::WriteEdition(IMkvWriter* writer) const { - uint64_t payload_size = 0; - - for (int idx = 0; idx < chapters_count_; ++idx) { - const Chapter& chapter = chapters_[idx]; - payload_size += chapter.WriteAtom(NULL); - } - - const uint64_t edition_size = - EbmlMasterElementSize(libwebm::kMkvEditionEntry, payload_size) + - payload_size; - - if (writer == NULL) // return size only - return edition_size; - - const int64_t start = writer->Position(); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvEditionEntry, payload_size)) - return 0; // error - - for (int idx = 0; idx < chapters_count_; ++idx) { - const Chapter& chapter = chapters_[idx]; - - const uint64_t chapter_size = chapter.WriteAtom(writer); - if (chapter_size == 0) // error - return 0; - } - - const int64_t stop = writer->Position(); - - if (stop >= start && uint64_t(stop - start) != edition_size) - return 0; - - return edition_size; -} - -// Tag Class - -bool Tag::add_simple_tag(const char* tag_name, const char* tag_string) { - if (!ExpandSimpleTagsArray()) - return false; - - SimpleTag& st = simple_tags_[simple_tags_count_++]; - st.Init(); - - if (!st.set_tag_name(tag_name)) - return false; - - if (!st.set_tag_string(tag_string)) - return false; - - return true; -} - -Tag::Tag() { - simple_tags_ = NULL; - simple_tags_size_ = 0; - simple_tags_count_ = 0; -} - -Tag::~Tag() {} - -void Tag::ShallowCopy(Tag* dst) const { - dst->simple_tags_ = simple_tags_; - dst->simple_tags_size_ = simple_tags_size_; - dst->simple_tags_count_ = simple_tags_count_; -} - -void Tag::Clear() { - while (simple_tags_count_ > 0) { - SimpleTag& st = simple_tags_[--simple_tags_count_]; - st.Clear(); - } - - delete[] simple_tags_; - simple_tags_ = NULL; - - simple_tags_size_ = 0; -} - -bool Tag::ExpandSimpleTagsArray() { - if (simple_tags_size_ > simple_tags_count_) - return true; // nothing to do yet - - const int size = (simple_tags_size_ == 0) ? 1 : 2 * simple_tags_size_; - - SimpleTag* const simple_tags = new (std::nothrow) SimpleTag[size]; // NOLINT - if (simple_tags == NULL) - return false; - - for (int idx = 0; idx < simple_tags_count_; ++idx) { - simple_tags[idx] = simple_tags_[idx]; // shallow copy - } - - delete[] simple_tags_; - - simple_tags_ = simple_tags; - simple_tags_size_ = size; - - return true; -} - -uint64_t Tag::Write(IMkvWriter* writer) const { - uint64_t payload_size = 0; - - for (int idx = 0; idx < simple_tags_count_; ++idx) { - const SimpleTag& st = simple_tags_[idx]; - payload_size += st.Write(NULL); - } - - const uint64_t tag_size = - EbmlMasterElementSize(libwebm::kMkvTag, payload_size) + payload_size; - - if (writer == NULL) - return tag_size; - - const int64_t start = writer->Position(); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvTag, payload_size)) - return 0; - - for (int idx = 0; idx < simple_tags_count_; ++idx) { - const SimpleTag& st = simple_tags_[idx]; - - if (!st.Write(writer)) - return 0; - } - - const int64_t stop = writer->Position(); - - if (stop >= start && uint64_t(stop - start) != tag_size) - return 0; - - return tag_size; -} - -// Tag::SimpleTag - -void Tag::SimpleTag::Init() { - tag_name_ = NULL; - tag_string_ = NULL; -} - -void Tag::SimpleTag::Clear() { - StrCpy(NULL, &tag_name_); - StrCpy(NULL, &tag_string_); -} - -bool Tag::SimpleTag::set_tag_name(const char* tag_name) { - return StrCpy(tag_name, &tag_name_); -} - -bool Tag::SimpleTag::set_tag_string(const char* tag_string) { - return StrCpy(tag_string, &tag_string_); -} - -uint64_t Tag::SimpleTag::Write(IMkvWriter* writer) const { - uint64_t payload_size = EbmlElementSize(libwebm::kMkvTagName, tag_name_); - - payload_size += EbmlElementSize(libwebm::kMkvTagString, tag_string_); - - const uint64_t simple_tag_size = - EbmlMasterElementSize(libwebm::kMkvSimpleTag, payload_size) + - payload_size; - - if (writer == NULL) - return simple_tag_size; - - const int64_t start = writer->Position(); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvSimpleTag, payload_size)) - return 0; - - if (!WriteEbmlElement(writer, libwebm::kMkvTagName, tag_name_)) - return 0; - - if (!WriteEbmlElement(writer, libwebm::kMkvTagString, tag_string_)) - return 0; - - const int64_t stop = writer->Position(); - - if (stop >= start && uint64_t(stop - start) != simple_tag_size) - return 0; - - return simple_tag_size; -} - -// Tags Class - -Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {} - -Tags::~Tags() { - while (tags_count_ > 0) { - Tag& tag = tags_[--tags_count_]; - tag.Clear(); - } - - delete[] tags_; - tags_ = NULL; -} - -int Tags::Count() const { return tags_count_; } - -Tag* Tags::AddTag() { - if (!ExpandTagsArray()) - return NULL; - - Tag& tag = tags_[tags_count_++]; - - return &tag; -} - -bool Tags::Write(IMkvWriter* writer) const { - if (writer == NULL) - return false; - - uint64_t payload_size = 0; - - for (int idx = 0; idx < tags_count_; ++idx) { - const Tag& tag = tags_[idx]; - payload_size += tag.Write(NULL); - } - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvTags, payload_size)) - return false; - - const int64_t start = writer->Position(); - - for (int idx = 0; idx < tags_count_; ++idx) { - const Tag& tag = tags_[idx]; - - const uint64_t tag_size = tag.Write(writer); - if (tag_size == 0) // error - return 0; - } - - const int64_t stop = writer->Position(); - - if (stop >= start && uint64_t(stop - start) != payload_size) - return false; - - return true; -} - -bool Tags::ExpandTagsArray() { - if (tags_size_ > tags_count_) - return true; // nothing to do yet - - const int size = (tags_size_ == 0) ? 1 : 2 * tags_size_; - - Tag* const tags = new (std::nothrow) Tag[size]; // NOLINT - if (tags == NULL) - return false; - - for (int idx = 0; idx < tags_count_; ++idx) { - const Tag& src = tags_[idx]; - Tag* const dst = tags + idx; - src.ShallowCopy(dst); - } - - delete[] tags_; - - tags_ = tags; - tags_size_ = size; - - return true; -} - -/////////////////////////////////////////////////////////////// -// -// Cluster class - -Cluster::Cluster(uint64_t timecode, int64_t cues_pos, uint64_t timecode_scale, - bool write_last_frame_with_duration, bool fixed_size_timecode) - : blocks_added_(0), - finalized_(false), - fixed_size_timecode_(fixed_size_timecode), - header_written_(false), - payload_size_(0), - position_for_cues_(cues_pos), - size_position_(-1), - timecode_(timecode), - timecode_scale_(timecode_scale), - write_last_frame_with_duration_(write_last_frame_with_duration), - writer_(NULL) {} - -Cluster::~Cluster() { - // Delete any stored frames that are left behind. This will happen if the - // Cluster was not Finalized for whatever reason. - while (!stored_frames_.empty()) { - while (!stored_frames_.begin()->second.empty()) { - delete stored_frames_.begin()->second.front(); - stored_frames_.begin()->second.pop_front(); - } - stored_frames_.erase(stored_frames_.begin()->first); - } -} - -bool Cluster::Init(IMkvWriter* ptr_writer) { - if (!ptr_writer) { - return false; - } - writer_ = ptr_writer; - return true; -} - -bool Cluster::AddFrame(const Frame* const frame) { - return QueueOrWriteFrame(frame); -} - -bool Cluster::AddFrame(const uint8_t* data, uint64_t length, - uint64_t track_number, uint64_t abs_timecode, - bool is_key) { - Frame frame; - if (!frame.Init(data, length)) - return false; - frame.set_track_number(track_number); - frame.set_timestamp(abs_timecode); - frame.set_is_key(is_key); - return QueueOrWriteFrame(&frame); -} - -bool Cluster::AddFrameWithAdditional(const uint8_t* data, uint64_t length, - const uint8_t* additional, - uint64_t additional_length, - uint64_t add_id, uint64_t track_number, - uint64_t abs_timecode, bool is_key) { - if (!additional || additional_length == 0) { - return false; - } - Frame frame; - if (!frame.Init(data, length) || - !frame.AddAdditionalData(additional, additional_length, add_id)) { - return false; - } - frame.set_track_number(track_number); - frame.set_timestamp(abs_timecode); - frame.set_is_key(is_key); - return QueueOrWriteFrame(&frame); -} - -bool Cluster::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length, - int64_t discard_padding, - uint64_t track_number, - uint64_t abs_timecode, bool is_key) { - Frame frame; - if (!frame.Init(data, length)) - return false; - frame.set_discard_padding(discard_padding); - frame.set_track_number(track_number); - frame.set_timestamp(abs_timecode); - frame.set_is_key(is_key); - return QueueOrWriteFrame(&frame); -} - -bool Cluster::AddMetadata(const uint8_t* data, uint64_t length, - uint64_t track_number, uint64_t abs_timecode, - uint64_t duration_timecode) { - Frame frame; - if (!frame.Init(data, length)) - return false; - frame.set_track_number(track_number); - frame.set_timestamp(abs_timecode); - frame.set_duration(duration_timecode); - frame.set_is_key(true); // All metadata blocks are keyframes. - return QueueOrWriteFrame(&frame); -} - -void Cluster::AddPayloadSize(uint64_t size) { payload_size_ += size; } - -bool Cluster::Finalize() { - return !write_last_frame_with_duration_ && Finalize(false, 0); -} - -bool Cluster::Finalize(bool set_last_frame_duration, uint64_t duration) { - if (!writer_ || finalized_) - return false; - - if (write_last_frame_with_duration_) { - // Write out held back Frames. This essentially performs a k-way merge - // across all tracks in the increasing order of timestamps. - while (!stored_frames_.empty()) { - Frame* frame = stored_frames_.begin()->second.front(); - - // Get the next frame to write (frame with least timestamp across all - // tracks). - for (FrameMapIterator frames_iterator = ++stored_frames_.begin(); - frames_iterator != stored_frames_.end(); ++frames_iterator) { - if (frames_iterator->second.front()->timestamp() < frame->timestamp()) { - frame = frames_iterator->second.front(); - } - } - - // Set the duration if it's the last frame for the track. - if (set_last_frame_duration && - stored_frames_[frame->track_number()].size() == 1 && - !frame->duration_set()) { - frame->set_duration(duration - frame->timestamp()); - if (!frame->is_key() && !frame->reference_block_timestamp_set()) { - frame->set_reference_block_timestamp( - last_block_timestamp_[frame->track_number()]); - } - } - - // Write the frame and remove it from |stored_frames_|. - const bool wrote_frame = DoWriteFrame(frame); - stored_frames_[frame->track_number()].pop_front(); - if (stored_frames_[frame->track_number()].empty()) { - stored_frames_.erase(frame->track_number()); - } - delete frame; - if (!wrote_frame) - return false; - } - } - - if (size_position_ == -1) - return false; - - if (writer_->Seekable()) { - const int64_t pos = writer_->Position(); - - if (writer_->Position(size_position_)) - return false; - - if (WriteUIntSize(writer_, payload_size(), 8)) - return false; - - if (writer_->Position(pos)) - return false; - } - - finalized_ = true; - - return true; -} - -uint64_t Cluster::Size() const { - const uint64_t element_size = - EbmlMasterElementSize(libwebm::kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) + - payload_size_; - return element_size; -} - -bool Cluster::PreWriteBlock() { - if (finalized_) - return false; - - if (!header_written_) { - if (!WriteClusterHeader()) - return false; - } - - return true; -} - -void Cluster::PostWriteBlock(uint64_t element_size) { - AddPayloadSize(element_size); - ++blocks_added_; -} - -int64_t Cluster::GetRelativeTimecode(int64_t abs_timecode) const { - const int64_t cluster_timecode = this->Cluster::timecode(); - const int64_t rel_timecode = - static_cast(abs_timecode) - cluster_timecode; - - if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode) - return -1; - - return rel_timecode; -} - -bool Cluster::DoWriteFrame(const Frame* const frame) { - if (!frame || !frame->IsValid()) - return false; - - if (!PreWriteBlock()) - return false; - - const uint64_t element_size = WriteFrame(writer_, frame, this); - if (element_size == 0) - return false; - - PostWriteBlock(element_size); - last_block_timestamp_[frame->track_number()] = frame->timestamp(); - return true; -} - -bool Cluster::QueueOrWriteFrame(const Frame* const frame) { - if (!frame || !frame->IsValid()) - return false; - - // If |write_last_frame_with_duration_| is not set, then write the frame right - // away. - if (!write_last_frame_with_duration_) { - return DoWriteFrame(frame); - } - - // Queue the current frame. - uint64_t track_number = frame->track_number(); - Frame* const frame_to_store = new Frame(); - frame_to_store->CopyFrom(*frame); - stored_frames_[track_number].push_back(frame_to_store); - - // Iterate through all queued frames in the current track except the last one - // and write it if it is okay to do so (i.e.) no other track has an held back - // frame with timestamp <= the timestamp of the frame in question. - std::vector::iterator> frames_to_erase; - for (std::list::iterator - current_track_iterator = stored_frames_[track_number].begin(), - end = --stored_frames_[track_number].end(); - current_track_iterator != end; ++current_track_iterator) { - const Frame* const frame_to_write = *current_track_iterator; - bool okay_to_write = true; - for (FrameMapIterator track_iterator = stored_frames_.begin(); - track_iterator != stored_frames_.end(); ++track_iterator) { - if (track_iterator->first == track_number) { - continue; - } - if (track_iterator->second.front()->timestamp() < - frame_to_write->timestamp()) { - okay_to_write = false; - break; - } - } - if (okay_to_write) { - const bool wrote_frame = DoWriteFrame(frame_to_write); - delete frame_to_write; - if (!wrote_frame) - return false; - frames_to_erase.push_back(current_track_iterator); - } else { - break; - } - } - for (std::vector::iterator>::iterator iterator = - frames_to_erase.begin(); - iterator != frames_to_erase.end(); ++iterator) { - stored_frames_[track_number].erase(*iterator); - } - return true; -} - -bool Cluster::WriteClusterHeader() { - if (finalized_) - return false; - - if (WriteID(writer_, libwebm::kMkvCluster)) - return false; - - // Save for later. - size_position_ = writer_->Position(); - - // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8 - // bytes because we do not know how big our cluster will be. - if (SerializeInt(writer_, kEbmlUnknownValue, 8)) - return false; - - if (!WriteEbmlElement(writer_, libwebm::kMkvTimecode, timecode(), - fixed_size_timecode_ ? 8 : 0)) { - return false; - } - AddPayloadSize(EbmlElementSize(libwebm::kMkvTimecode, timecode(), - fixed_size_timecode_ ? 8 : 0)); - header_written_ = true; - - return true; -} - -/////////////////////////////////////////////////////////////// -// -// SeekHead Class - -SeekHead::SeekHead() : start_pos_(0ULL) { - for (int32_t i = 0; i < kSeekEntryCount; ++i) { - seek_entry_id_[i] = 0; - seek_entry_pos_[i] = 0; - } -} - -SeekHead::~SeekHead() {} - -bool SeekHead::Finalize(IMkvWriter* writer) const { - if (writer->Seekable()) { - if (start_pos_ == -1) - return false; - - uint64_t payload_size = 0; - uint64_t entry_size[kSeekEntryCount]; - - for (int32_t i = 0; i < kSeekEntryCount; ++i) { - if (seek_entry_id_[i] != 0) { - entry_size[i] = EbmlElementSize(libwebm::kMkvSeekID, - static_cast(seek_entry_id_[i])); - entry_size[i] += EbmlElementSize( - libwebm::kMkvSeekPosition, static_cast(seek_entry_pos_[i])); - - payload_size += - EbmlMasterElementSize(libwebm::kMkvSeek, entry_size[i]) + - entry_size[i]; - } - } - - // No SeekHead elements - if (payload_size == 0) - return true; - - const int64_t pos = writer->Position(); - if (writer->Position(start_pos_)) - return false; - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeekHead, payload_size)) - return false; - - for (int32_t i = 0; i < kSeekEntryCount; ++i) { - if (seek_entry_id_[i] != 0) { - if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeek, entry_size[i])) - return false; - - if (!WriteEbmlElement(writer, libwebm::kMkvSeekID, - static_cast(seek_entry_id_[i]))) - return false; - - if (!WriteEbmlElement(writer, libwebm::kMkvSeekPosition, - static_cast(seek_entry_pos_[i]))) - return false; - } - } - - const uint64_t total_entry_size = kSeekEntryCount * MaxEntrySize(); - const uint64_t total_size = - EbmlMasterElementSize(libwebm::kMkvSeekHead, total_entry_size) + - total_entry_size; - const int64_t size_left = total_size - (writer->Position() - start_pos_); - - const uint64_t bytes_written = WriteVoidElement(writer, size_left); - if (!bytes_written) - return false; - - if (writer->Position(pos)) - return false; - } - - return true; -} - -bool SeekHead::Write(IMkvWriter* writer) { - const uint64_t entry_size = kSeekEntryCount * MaxEntrySize(); - const uint64_t size = - EbmlMasterElementSize(libwebm::kMkvSeekHead, entry_size); - - start_pos_ = writer->Position(); - - const uint64_t bytes_written = WriteVoidElement(writer, size + entry_size); - if (!bytes_written) - return false; - - return true; -} - -bool SeekHead::AddSeekEntry(uint32_t id, uint64_t pos) { - for (int32_t i = 0; i < kSeekEntryCount; ++i) { - if (seek_entry_id_[i] == 0) { - seek_entry_id_[i] = id; - seek_entry_pos_[i] = pos; - return true; - } - } - return false; -} - -uint32_t SeekHead::GetId(int index) const { - if (index < 0 || index >= kSeekEntryCount) - return UINT_MAX; - return seek_entry_id_[index]; -} - -uint64_t SeekHead::GetPosition(int index) const { - if (index < 0 || index >= kSeekEntryCount) - return ULLONG_MAX; - return seek_entry_pos_[index]; -} - -bool SeekHead::SetSeekEntry(int index, uint32_t id, uint64_t position) { - if (index < 0 || index >= kSeekEntryCount) - return false; - seek_entry_id_[index] = id; - seek_entry_pos_[index] = position; - return true; -} - -uint64_t SeekHead::MaxEntrySize() const { - const uint64_t max_entry_payload_size = - EbmlElementSize(libwebm::kMkvSeekID, - static_cast(UINT64_C(0xffffffff))) + - EbmlElementSize(libwebm::kMkvSeekPosition, - static_cast(UINT64_C(0xffffffffffffffff))); - const uint64_t max_entry_size = - EbmlMasterElementSize(libwebm::kMkvSeek, max_entry_payload_size) + - max_entry_payload_size; - - return max_entry_size; -} - -/////////////////////////////////////////////////////////////// -// -// SegmentInfo Class - -SegmentInfo::SegmentInfo() - : duration_(-1.0), - muxing_app_(NULL), - timecode_scale_(1000000ULL), - writing_app_(NULL), - date_utc_(LLONG_MIN), - duration_pos_(-1) {} - -SegmentInfo::~SegmentInfo() { - delete[] muxing_app_; - delete[] writing_app_; -} - -bool SegmentInfo::Init() { - int32_t major; - int32_t minor; - int32_t build; - int32_t revision; - GetVersion(&major, &minor, &build, &revision); - char temp[256]; -#ifdef _MSC_VER - sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major, - minor, build, revision); -#else - snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major, - minor, build, revision); -#endif - - const size_t app_len = strlen(temp) + 1; - - delete[] muxing_app_; - - muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT - if (!muxing_app_) - return false; - -#ifdef _MSC_VER - strcpy_s(muxing_app_, app_len, temp); -#else - strcpy(muxing_app_, temp); -#endif - - set_writing_app(temp); - if (!writing_app_) - return false; - return true; -} - -bool SegmentInfo::Finalize(IMkvWriter* writer) const { - if (!writer) - return false; - - if (duration_ > 0.0) { - if (writer->Seekable()) { - if (duration_pos_ == -1) - return false; - - const int64_t pos = writer->Position(); - - if (writer->Position(duration_pos_)) - return false; - - if (!WriteEbmlElement(writer, libwebm::kMkvDuration, - static_cast(duration_))) - return false; - - if (writer->Position(pos)) - return false; - } - } - - return true; -} - -bool SegmentInfo::Write(IMkvWriter* writer) { - if (!writer || !muxing_app_ || !writing_app_) - return false; - - uint64_t size = EbmlElementSize(libwebm::kMkvTimecodeScale, - static_cast(timecode_scale_)); - if (duration_ > 0.0) - size += - EbmlElementSize(libwebm::kMkvDuration, static_cast(duration_)); - if (date_utc_ != LLONG_MIN) - size += EbmlDateElementSize(libwebm::kMkvDateUTC); - size += EbmlElementSize(libwebm::kMkvMuxingApp, muxing_app_); - size += EbmlElementSize(libwebm::kMkvWritingApp, writing_app_); - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvInfo, size)) - return false; - - const int64_t payload_position = writer->Position(); - if (payload_position < 0) - return false; - - if (!WriteEbmlElement(writer, libwebm::kMkvTimecodeScale, - static_cast(timecode_scale_))) - return false; - - if (duration_ > 0.0) { - // Save for later - duration_pos_ = writer->Position(); - - if (!WriteEbmlElement(writer, libwebm::kMkvDuration, - static_cast(duration_))) - return false; - } - - if (date_utc_ != LLONG_MIN) - WriteEbmlDateElement(writer, libwebm::kMkvDateUTC, date_utc_); - - if (!WriteEbmlElement(writer, libwebm::kMkvMuxingApp, muxing_app_)) - return false; - if (!WriteEbmlElement(writer, libwebm::kMkvWritingApp, writing_app_)) - return false; - - const int64_t stop_position = writer->Position(); - if (stop_position < 0 || - stop_position - payload_position != static_cast(size)) - return false; - - return true; -} - -void SegmentInfo::set_muxing_app(const char* app) { - if (app) { - const size_t length = strlen(app) + 1; - char* temp_str = new (std::nothrow) char[length]; // NOLINT - if (!temp_str) - return; - -#ifdef _MSC_VER - strcpy_s(temp_str, length, app); -#else - strcpy(temp_str, app); -#endif - - delete[] muxing_app_; - muxing_app_ = temp_str; - } -} - -void SegmentInfo::set_writing_app(const char* app) { - if (app) { - const size_t length = strlen(app) + 1; - char* temp_str = new (std::nothrow) char[length]; // NOLINT - if (!temp_str) - return; - -#ifdef _MSC_VER - strcpy_s(temp_str, length, app); -#else - strcpy(temp_str, app); -#endif - - delete[] writing_app_; - writing_app_ = temp_str; - } -} - -/////////////////////////////////////////////////////////////// -// -// Segment Class - -Segment::Segment() - : chunk_count_(0), - chunk_name_(NULL), - chunk_writer_cluster_(NULL), - chunk_writer_cues_(NULL), - chunk_writer_header_(NULL), - chunking_(false), - chunking_base_name_(NULL), - cluster_list_(NULL), - cluster_list_capacity_(0), - cluster_list_size_(0), - cues_position_(kAfterClusters), - cues_track_(0), - force_new_cluster_(false), - frames_(NULL), - frames_capacity_(0), - frames_size_(0), - has_video_(false), - header_written_(false), - last_block_duration_(0), - last_timestamp_(0), - max_cluster_duration_(kDefaultMaxClusterDuration), - max_cluster_size_(0), - mode_(kFile), - new_cuepoint_(false), - output_cues_(true), - accurate_cluster_duration_(false), - fixed_size_cluster_timecode_(false), - estimate_file_duration_(false), - payload_pos_(0), - size_position_(0), - doc_type_version_(kDefaultDocTypeVersion), - doc_type_version_written_(0), - duration_(0.0), - writer_cluster_(NULL), - writer_cues_(NULL), - writer_header_(NULL) { - const time_t curr_time = time(NULL); - seed_ = static_cast(curr_time); -#ifdef _WIN32 - srand(seed_); -#endif -} - -Segment::~Segment() { - if (cluster_list_) { - for (int32_t i = 0; i < cluster_list_size_; ++i) { - Cluster* const cluster = cluster_list_[i]; - delete cluster; - } - delete[] cluster_list_; - } - - if (frames_) { - for (int32_t i = 0; i < frames_size_; ++i) { - Frame* const frame = frames_[i]; - delete frame; - } - delete[] frames_; - } - - delete[] chunk_name_; - delete[] chunking_base_name_; - - if (chunk_writer_cluster_) { - chunk_writer_cluster_->Close(); - delete chunk_writer_cluster_; - } - if (chunk_writer_cues_) { - chunk_writer_cues_->Close(); - delete chunk_writer_cues_; - } - if (chunk_writer_header_) { - chunk_writer_header_->Close(); - delete chunk_writer_header_; - } -} - -void Segment::MoveCuesBeforeClustersHelper(uint64_t diff, int32_t index, - uint64_t* cues_size) { - CuePoint* const cue_point = cues_.GetCueByIndex(index); - if (cue_point == NULL) - return; - const uint64_t old_cue_point_size = cue_point->Size(); - const uint64_t cluster_pos = cue_point->cluster_pos() + diff; - cue_point->set_cluster_pos(cluster_pos); // update the new cluster position - // New size of the cue is computed as follows - // Let a = current sum of size of all CuePoints - // Let b = Increase in Cue Point's size due to this iteration - // Let c = Increase in size of Cues Element's length due to this iteration - // (This is computed as CodedSize(a + b) - CodedSize(a)) - // Let d = b + c. Now d is the |diff| passed to the next recursive call. - // Let e = a + b. Now e is the |cues_size| passed to the next recursive - // call. - const uint64_t cue_point_size_diff = cue_point->Size() - old_cue_point_size; - const uint64_t cue_size_diff = - GetCodedUIntSize(*cues_size + cue_point_size_diff) - - GetCodedUIntSize(*cues_size); - *cues_size += cue_point_size_diff; - diff = cue_size_diff + cue_point_size_diff; - if (diff > 0) { - for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) { - MoveCuesBeforeClustersHelper(diff, i, cues_size); - } - } -} - -void Segment::MoveCuesBeforeClusters() { - const uint64_t current_cue_size = cues_.Size(); - uint64_t cue_size = 0; - for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) - cue_size += cues_.GetCueByIndex(i)->Size(); - for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) - MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size); - - // Adjust the Seek Entry to reflect the change in position - // of Cluster and Cues - int32_t cluster_index = 0; - int32_t cues_index = 0; - for (int32_t i = 0; i < SeekHead::kSeekEntryCount; ++i) { - if (seek_head_.GetId(i) == libwebm::kMkvCluster) - cluster_index = i; - if (seek_head_.GetId(i) == libwebm::kMkvCues) - cues_index = i; - } - seek_head_.SetSeekEntry(cues_index, libwebm::kMkvCues, - seek_head_.GetPosition(cluster_index)); - seek_head_.SetSeekEntry(cluster_index, libwebm::kMkvCluster, - cues_.Size() + seek_head_.GetPosition(cues_index)); -} - -bool Segment::Init(IMkvWriter* ptr_writer) { - if (!ptr_writer) { - return false; - } - writer_cluster_ = ptr_writer; - writer_cues_ = ptr_writer; - writer_header_ = ptr_writer; - memset(&track_frames_written_, 0, - sizeof(track_frames_written_[0]) * kMaxTrackNumber); - memset(&last_track_timestamp_, 0, - sizeof(last_track_timestamp_[0]) * kMaxTrackNumber); - return segment_info_.Init(); -} - -bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader, - IMkvWriter* writer) { - if (!writer->Seekable() || chunking_) - return false; - const int64_t cluster_offset = - cluster_list_[0]->size_position() - GetUIntSize(libwebm::kMkvCluster); - - // Copy the headers. - if (!ChunkedCopy(reader, writer, 0, cluster_offset)) - return false; - - // Recompute cue positions and seek entries. - MoveCuesBeforeClusters(); - - // Write cues and seek entries. - // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the - // second time with a different writer object. But the name Finalize() doesn't - // indicate something we want to call more than once. So consider renaming it - // to write() or some such. - if (!cues_.Write(writer) || !seek_head_.Finalize(writer)) - return false; - - // Copy the Clusters. - if (!ChunkedCopy(reader, writer, cluster_offset, - cluster_end_offset_ - cluster_offset)) - return false; - - // Update the Segment size in case the Cues size has changed. - const int64_t pos = writer->Position(); - const int64_t segment_size = writer->Position() - payload_pos_; - if (writer->Position(size_position_) || - WriteUIntSize(writer, segment_size, 8) || writer->Position(pos)) - return false; - return true; -} - -bool Segment::Finalize() { - if (WriteFramesAll() < 0) - return false; - - // In kLive mode, call Cluster::Finalize only if |accurate_cluster_duration_| - // is set. In all other modes, always call Cluster::Finalize. - if ((mode_ == kLive ? accurate_cluster_duration_ : true) && - cluster_list_size_ > 0) { - // Update last cluster's size - Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1]; - - // For the last frame of the last Cluster, we don't write it as a BlockGroup - // with Duration unless the frame itself has duration set explicitly. - if (!old_cluster || !old_cluster->Finalize(false, 0)) - return false; - } - - if (mode_ == kFile) { - if (chunking_ && chunk_writer_cluster_) { - chunk_writer_cluster_->Close(); - chunk_count_++; - } - - double duration = - (static_cast(last_timestamp_) + last_block_duration_) / - segment_info_.timecode_scale(); - if (duration_ > 0.0) { - duration = duration_; - } else { - if (last_block_duration_ == 0 && estimate_file_duration_) { - const int num_tracks = static_cast(tracks_.track_entries_size()); - for (int i = 0; i < num_tracks; ++i) { - if (track_frames_written_[i] < 2) - continue; - - // Estimate the duration for the last block of a Track. - const double nano_per_frame = - static_cast(last_track_timestamp_[i]) / - (track_frames_written_[i] - 1); - const double track_duration = - (last_track_timestamp_[i] + nano_per_frame) / - segment_info_.timecode_scale(); - if (track_duration > duration) - duration = track_duration; - } - } - } - segment_info_.set_duration(duration); - if (!segment_info_.Finalize(writer_header_)) - return false; - - if (output_cues_) - if (!seek_head_.AddSeekEntry(libwebm::kMkvCues, MaxOffset())) - return false; - - if (chunking_) { - if (!chunk_writer_cues_) - return false; - - char* name = NULL; - if (!UpdateChunkName("cues", &name)) - return false; - - const bool cues_open = chunk_writer_cues_->Open(name); - delete[] name; - if (!cues_open) - return false; - } - - cluster_end_offset_ = writer_cluster_->Position(); - - // Write the seek headers and cues - if (output_cues_) - if (!cues_.Write(writer_cues_)) - return false; - - if (!seek_head_.Finalize(writer_header_)) - return false; - - if (writer_header_->Seekable()) { - if (size_position_ == -1) - return false; - - const int64_t segment_size = MaxOffset(); - if (segment_size < 1) - return false; - - const int64_t pos = writer_header_->Position(); - UpdateDocTypeVersion(); - if (doc_type_version_ != doc_type_version_written_) { - if (writer_header_->Position(0)) - return false; - - const char* const doc_type = - DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska; - if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type)) - return false; - if (writer_header_->Position() != ebml_header_size_) - return false; - - doc_type_version_written_ = doc_type_version_; - } - - if (writer_header_->Position(size_position_)) - return false; - - if (WriteUIntSize(writer_header_, segment_size, 8)) - return false; - - if (writer_header_->Position(pos)) - return false; - } - - if (chunking_) { - // Do not close any writers until the segment size has been written, - // otherwise the size may be off. - if (!chunk_writer_cues_ || !chunk_writer_header_) - return false; - - chunk_writer_cues_->Close(); - chunk_writer_header_->Close(); - } - } - - return true; -} - -Track* Segment::AddTrack(int32_t number) { - Track* const track = new (std::nothrow) Track(&seed_); // NOLINT - - if (!track) - return NULL; - - if (!tracks_.AddTrack(track, number)) { - delete track; - return NULL; - } - - return track; -} - -Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); } - -Tag* Segment::AddTag() { return tags_.AddTag(); } - -uint64_t Segment::AddVideoTrack(int32_t width, int32_t height, int32_t number) { - VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT - if (!track) - return 0; - - track->set_type(Tracks::kVideo); - track->set_codec_id(Tracks::kVp8CodecId); - track->set_width(width); - track->set_height(height); - - tracks_.AddTrack(track, number); - has_video_ = true; - - return track->number(); -} - -bool Segment::AddCuePoint(uint64_t timestamp, uint64_t track) { - if (cluster_list_size_ < 1) - return false; - - const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; - if (!cluster) - return false; - - CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT - if (!cue) - return false; - - cue->set_time(timestamp / segment_info_.timecode_scale()); - cue->set_block_number(cluster->blocks_added()); - cue->set_cluster_pos(cluster->position_for_cues()); - cue->set_track(track); - if (!cues_.AddCue(cue)) - return false; - - new_cuepoint_ = false; - return true; -} - -uint64_t Segment::AddAudioTrack(int32_t sample_rate, int32_t channels, - int32_t number) { - AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT - if (!track) - return 0; - - track->set_type(Tracks::kAudio); - track->set_codec_id(Tracks::kVorbisCodecId); - track->set_sample_rate(sample_rate); - track->set_channels(channels); - - tracks_.AddTrack(track, number); - - return track->number(); -} - -bool Segment::AddFrame(const uint8_t* data, uint64_t length, - uint64_t track_number, uint64_t timestamp, bool is_key) { - if (!data) - return false; - - Frame frame; - if (!frame.Init(data, length)) - return false; - frame.set_track_number(track_number); - frame.set_timestamp(timestamp); - frame.set_is_key(is_key); - return AddGenericFrame(&frame); -} - -bool Segment::AddFrameWithAdditional(const uint8_t* data, uint64_t length, - const uint8_t* additional, - uint64_t additional_length, - uint64_t add_id, uint64_t track_number, - uint64_t timestamp, bool is_key) { - if (!data || !additional) - return false; - - Frame frame; - if (!frame.Init(data, length) || - !frame.AddAdditionalData(additional, additional_length, add_id)) { - return false; - } - frame.set_track_number(track_number); - frame.set_timestamp(timestamp); - frame.set_is_key(is_key); - return AddGenericFrame(&frame); -} - -bool Segment::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length, - int64_t discard_padding, - uint64_t track_number, - uint64_t timestamp, bool is_key) { - if (!data) - return false; - - Frame frame; - if (!frame.Init(data, length)) - return false; - frame.set_discard_padding(discard_padding); - frame.set_track_number(track_number); - frame.set_timestamp(timestamp); - frame.set_is_key(is_key); - return AddGenericFrame(&frame); -} - -bool Segment::AddMetadata(const uint8_t* data, uint64_t length, - uint64_t track_number, uint64_t timestamp_ns, - uint64_t duration_ns) { - if (!data) - return false; - - Frame frame; - if (!frame.Init(data, length)) - return false; - frame.set_track_number(track_number); - frame.set_timestamp(timestamp_ns); - frame.set_duration(duration_ns); - frame.set_is_key(true); // All metadata blocks are keyframes. - return AddGenericFrame(&frame); -} - -bool Segment::AddGenericFrame(const Frame* frame) { - if (!frame) - return false; - - if (!CheckHeaderInfo()) - return false; - - // Check for non-monotonically increasing timestamps. - if (frame->timestamp() < last_timestamp_) - return false; - - // Check if the track number is valid. - if (!tracks_.GetTrackByNumber(frame->track_number())) - return false; - - if (frame->discard_padding() != 0) - doc_type_version_ = 4; - - if (cluster_list_size_ > 0) { - const uint64_t timecode_scale = segment_info_.timecode_scale(); - const uint64_t frame_timecode = frame->timestamp() / timecode_scale; - - const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1]; - const uint64_t last_cluster_timecode = last_cluster->timecode(); - - const uint64_t rel_timecode = frame_timecode - last_cluster_timecode; - if (rel_timecode > kMaxBlockTimecode) { - force_new_cluster_ = true; - } - } - - // If the segment has a video track hold onto audio frames to make sure the - // audio that is associated with the start time of a video key-frame is - // muxed into the same cluster. - if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) && - !force_new_cluster_) { - Frame* const new_frame = new (std::nothrow) Frame(); - if (!new_frame || !new_frame->CopyFrom(*frame)) - return false; - if (!QueueFrame(new_frame)) - return false; - track_frames_written_[frame->track_number() - 1]++; - return true; - } - - if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(), - frame->is_key())) { - return false; - } - - if (cluster_list_size_ < 1) - return false; - - Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; - if (!cluster) - return false; - - // If the Frame is not a SimpleBlock, then set the reference_block_timestamp - // if it is not set already. - bool frame_created = false; - if (!frame->CanBeSimpleBlock() && !frame->is_key() && - !frame->reference_block_timestamp_set()) { - Frame* const new_frame = new (std::nothrow) Frame(); - if (!new_frame->CopyFrom(*frame)) - return false; - new_frame->set_reference_block_timestamp( - last_track_timestamp_[frame->track_number() - 1]); - frame = new_frame; - frame_created = true; - } - - if (!cluster->AddFrame(frame)) - return false; - - if (new_cuepoint_ && cues_track_ == frame->track_number()) { - if (!AddCuePoint(frame->timestamp(), cues_track_)) - return false; - } - - last_timestamp_ = frame->timestamp(); - last_track_timestamp_[frame->track_number() - 1] = frame->timestamp(); - last_block_duration_ = frame->duration(); - track_frames_written_[frame->track_number() - 1]++; - - if (frame_created) - delete frame; - return true; -} - -void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; } - -void Segment::AccurateClusterDuration(bool accurate_cluster_duration) { - accurate_cluster_duration_ = accurate_cluster_duration; -} - -void Segment::UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode) { - fixed_size_cluster_timecode_ = fixed_size_cluster_timecode; -} - -bool Segment::SetChunking(bool chunking, const char* filename) { - if (chunk_count_ > 0) - return false; - - if (chunking) { - if (!filename) - return false; - - // Check if we are being set to what is already set. - if (chunking_ && !strcmp(filename, chunking_base_name_)) - return true; - - const size_t name_length = strlen(filename) + 1; - char* const temp = new (std::nothrow) char[name_length]; // NOLINT - if (!temp) - return false; - -#ifdef _MSC_VER - strcpy_s(temp, name_length, filename); -#else - strcpy(temp, filename); -#endif - - delete[] chunking_base_name_; - chunking_base_name_ = temp; - - if (!UpdateChunkName("chk", &chunk_name_)) - return false; - - if (!chunk_writer_cluster_) { - chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT - if (!chunk_writer_cluster_) - return false; - } - - if (!chunk_writer_cues_) { - chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT - if (!chunk_writer_cues_) - return false; - } - - if (!chunk_writer_header_) { - chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT - if (!chunk_writer_header_) - return false; - } - - if (!chunk_writer_cluster_->Open(chunk_name_)) - return false; - - const size_t header_length = strlen(filename) + strlen(".hdr") + 1; - char* const header = new (std::nothrow) char[header_length]; // NOLINT - if (!header) - return false; - -#ifdef _MSC_VER - strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_); - strcat_s(header, header_length, ".hdr"); -#else - strcpy(header, chunking_base_name_); - strcat(header, ".hdr"); -#endif - if (!chunk_writer_header_->Open(header)) { - delete[] header; - return false; - } - - writer_cluster_ = chunk_writer_cluster_; - writer_cues_ = chunk_writer_cues_; - writer_header_ = chunk_writer_header_; - - delete[] header; - } - - chunking_ = chunking; - - return true; -} - -bool Segment::CuesTrack(uint64_t track_number) { - const Track* const track = GetTrackByNumber(track_number); - if (!track) - return false; - - cues_track_ = track_number; - return true; -} - -void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; } - -Track* Segment::GetTrackByNumber(uint64_t track_number) const { - return tracks_.GetTrackByNumber(track_number); -} - -bool Segment::WriteSegmentHeader() { - UpdateDocTypeVersion(); - - const char* const doc_type = - DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska; - if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type)) - return false; - doc_type_version_written_ = doc_type_version_; - ebml_header_size_ = static_cast(writer_header_->Position()); - - // Write "unknown" (-1) as segment size value. If mode is kFile, Segment - // will write over duration when the file is finalized. - if (WriteID(writer_header_, libwebm::kMkvSegment)) - return false; - - // Save for later. - size_position_ = writer_header_->Position(); - - // Write "unknown" (EBML coded -1) as segment size value. We need to write 8 - // bytes because if we are going to overwrite the segment size later we do - // not know how big our segment will be. - if (SerializeInt(writer_header_, kEbmlUnknownValue, 8)) - return false; - - payload_pos_ = writer_header_->Position(); - - if (mode_ == kFile && writer_header_->Seekable()) { - // Set the duration > 0.0 so SegmentInfo will write out the duration. When - // the muxer is done writing we will set the correct duration and have - // SegmentInfo upadte it. - segment_info_.set_duration(1.0); - - if (!seek_head_.Write(writer_header_)) - return false; - } - - if (!seek_head_.AddSeekEntry(libwebm::kMkvInfo, MaxOffset())) - return false; - if (!segment_info_.Write(writer_header_)) - return false; - - if (!seek_head_.AddSeekEntry(libwebm::kMkvTracks, MaxOffset())) - return false; - if (!tracks_.Write(writer_header_)) - return false; - - if (chapters_.Count() > 0) { - if (!seek_head_.AddSeekEntry(libwebm::kMkvChapters, MaxOffset())) - return false; - if (!chapters_.Write(writer_header_)) - return false; - } - - if (tags_.Count() > 0) { - if (!seek_head_.AddSeekEntry(libwebm::kMkvTags, MaxOffset())) - return false; - if (!tags_.Write(writer_header_)) - return false; - } - - if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) { - if (!chunk_writer_header_) - return false; - - chunk_writer_header_->Close(); - } - - header_written_ = true; - - return true; -} - -// Here we are testing whether to create a new cluster, given a frame -// having time frame_timestamp_ns. -// -int Segment::TestFrame(uint64_t track_number, uint64_t frame_timestamp_ns, - bool is_key) const { - if (force_new_cluster_) - return 1; - - // If no clusters have been created yet, then create a new cluster - // and write this frame immediately, in the new cluster. This path - // should only be followed once, the first time we attempt to write - // a frame. - - if (cluster_list_size_ <= 0) - return 1; - - // There exists at least one cluster. We must compare the frame to - // the last cluster, in order to determine whether the frame is - // written to the existing cluster, or that a new cluster should be - // created. - - const uint64_t timecode_scale = segment_info_.timecode_scale(); - const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale; - - const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1]; - const uint64_t last_cluster_timecode = last_cluster->timecode(); - - // For completeness we test for the case when the frame's timecode - // is less than the cluster's timecode. Although in principle that - // is allowed, this muxer doesn't actually write clusters like that, - // so this indicates a bug somewhere in our algorithm. - - if (frame_timecode < last_cluster_timecode) // should never happen - return -1; - - // If the frame has a timestamp significantly larger than the last - // cluster (in Matroska, cluster-relative timestamps are serialized - // using a 16-bit signed integer), then we cannot write this frame - // to that cluster, and so we must create a new cluster. - - const int64_t delta_timecode = frame_timecode - last_cluster_timecode; - - if (delta_timecode > kMaxBlockTimecode) - return 2; - - // We decide to create a new cluster when we have a video keyframe. - // This will flush queued (audio) frames, and write the keyframe - // immediately, in the newly-created cluster. - - if (is_key && tracks_.TrackIsVideo(track_number)) - return 1; - - // Create a new cluster if we have accumulated too many frames - // already, where "too many" is defined as "the total time of frames - // in the cluster exceeds a threshold". - - const uint64_t delta_ns = delta_timecode * timecode_scale; - - if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_) - return 1; - - // This is similar to the case above, with the difference that a new - // cluster is created when the size of the current cluster exceeds a - // threshold. - - const uint64_t cluster_size = last_cluster->payload_size(); - - if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_) - return 1; - - // There's no need to create a new cluster, so emit this frame now. - - return 0; -} - -bool Segment::MakeNewCluster(uint64_t frame_timestamp_ns) { - const int32_t new_size = cluster_list_size_ + 1; - - if (new_size > cluster_list_capacity_) { - // Add more clusters. - const int32_t new_capacity = - (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2; - Cluster** const clusters = - new (std::nothrow) Cluster*[new_capacity]; // NOLINT - if (!clusters) - return false; - - for (int32_t i = 0; i < cluster_list_size_; ++i) { - clusters[i] = cluster_list_[i]; - } - - delete[] cluster_list_; - - cluster_list_ = clusters; - cluster_list_capacity_ = new_capacity; - } - - if (!WriteFramesLessThan(frame_timestamp_ns)) - return false; - - if (cluster_list_size_ > 0) { - // Update old cluster's size - Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1]; - - if (!old_cluster || !old_cluster->Finalize(true, frame_timestamp_ns)) - return false; - } - - if (output_cues_) - new_cuepoint_ = true; - - if (chunking_ && cluster_list_size_ > 0) { - chunk_writer_cluster_->Close(); - chunk_count_++; - - if (!UpdateChunkName("chk", &chunk_name_)) - return false; - if (!chunk_writer_cluster_->Open(chunk_name_)) - return false; - } - - const uint64_t timecode_scale = segment_info_.timecode_scale(); - const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale; - - uint64_t cluster_timecode = frame_timecode; - - if (frames_size_ > 0) { - const Frame* const f = frames_[0]; // earliest queued frame - const uint64_t ns = f->timestamp(); - const uint64_t tc = ns / timecode_scale; - - if (tc < cluster_timecode) - cluster_timecode = tc; - } - - Cluster*& cluster = cluster_list_[cluster_list_size_]; - const int64_t offset = MaxOffset(); - cluster = new (std::nothrow) - Cluster(cluster_timecode, offset, segment_info_.timecode_scale(), - accurate_cluster_duration_, fixed_size_cluster_timecode_); - if (!cluster) - return false; - - if (!cluster->Init(writer_cluster_)) - return false; - - cluster_list_size_ = new_size; - return true; -} - -bool Segment::DoNewClusterProcessing(uint64_t track_number, - uint64_t frame_timestamp_ns, bool is_key) { - for (;;) { - // Based on the characteristics of the current frame and current - // cluster, decide whether to create a new cluster. - const int result = TestFrame(track_number, frame_timestamp_ns, is_key); - if (result < 0) // error - return false; - - // Always set force_new_cluster_ to false after TestFrame. - force_new_cluster_ = false; - - // A non-zero result means create a new cluster. - if (result > 0 && !MakeNewCluster(frame_timestamp_ns)) - return false; - - // Write queued (audio) frames. - const int frame_count = WriteFramesAll(); - if (frame_count < 0) // error - return false; - - // Write the current frame to the current cluster (if TestFrame - // returns 0) or to a newly created cluster (TestFrame returns 1). - if (result <= 1) - return true; - - // TestFrame returned 2, which means there was a large time - // difference between the cluster and the frame itself. Do the - // test again, comparing the frame to the new cluster. - } -} - -bool Segment::CheckHeaderInfo() { - if (!header_written_) { - if (!WriteSegmentHeader()) - return false; - - if (!seek_head_.AddSeekEntry(libwebm::kMkvCluster, MaxOffset())) - return false; - - if (output_cues_ && cues_track_ == 0) { - // Check for a video track - for (uint32_t i = 0; i < tracks_.track_entries_size(); ++i) { - const Track* const track = tracks_.GetTrackByIndex(i); - if (!track) - return false; - - if (tracks_.TrackIsVideo(track->number())) { - cues_track_ = track->number(); - break; - } - } - - // Set first track found - if (cues_track_ == 0) { - const Track* const track = tracks_.GetTrackByIndex(0); - if (!track) - return false; - - cues_track_ = track->number(); - } - } - } - return true; -} - -void Segment::UpdateDocTypeVersion() { - for (uint32_t index = 0; index < tracks_.track_entries_size(); ++index) { - const Track* track = tracks_.GetTrackByIndex(index); - if (track == NULL) - break; - if ((track->codec_delay() || track->seek_pre_roll()) && - doc_type_version_ < 4) { - doc_type_version_ = 4; - break; - } - } -} - -bool Segment::UpdateChunkName(const char* ext, char** name) const { - if (!name || !ext) - return false; - - char ext_chk[64]; -#ifdef _MSC_VER - sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext); -#else - snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext); -#endif - - const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1; - char* const str = new (std::nothrow) char[length]; // NOLINT - if (!str) - return false; - -#ifdef _MSC_VER - strcpy_s(str, length - strlen(ext_chk), chunking_base_name_); - strcat_s(str, length, ext_chk); -#else - strcpy(str, chunking_base_name_); - strcat(str, ext_chk); -#endif - - delete[] * name; - *name = str; - - return true; -} - -int64_t Segment::MaxOffset() { - if (!writer_header_) - return -1; - - int64_t offset = writer_header_->Position() - payload_pos_; - - if (chunking_) { - for (int32_t i = 0; i < cluster_list_size_; ++i) { - Cluster* const cluster = cluster_list_[i]; - offset += cluster->Size(); - } - - if (writer_cues_) - offset += writer_cues_->Position(); - } - - return offset; -} - -bool Segment::QueueFrame(Frame* frame) { - const int32_t new_size = frames_size_ + 1; - - if (new_size > frames_capacity_) { - // Add more frames. - const int32_t new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2; - - if (new_capacity < 1) - return false; - - Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT - if (!frames) - return false; - - for (int32_t i = 0; i < frames_size_; ++i) { - frames[i] = frames_[i]; - } - - delete[] frames_; - frames_ = frames; - frames_capacity_ = new_capacity; - } - - frames_[frames_size_++] = frame; - - return true; -} - -int Segment::WriteFramesAll() { - if (frames_ == NULL) - return 0; - - if (cluster_list_size_ < 1) - return -1; - - Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; - - if (!cluster) - return -1; - - for (int32_t i = 0; i < frames_size_; ++i) { - Frame*& frame = frames_[i]; - // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the - // places where |doc_type_version_| needs to be updated. - if (frame->discard_padding() != 0) - doc_type_version_ = 4; - if (!cluster->AddFrame(frame)) - return -1; - - if (new_cuepoint_ && cues_track_ == frame->track_number()) { - if (!AddCuePoint(frame->timestamp(), cues_track_)) - return -1; - } - - if (frame->timestamp() > last_timestamp_) { - last_timestamp_ = frame->timestamp(); - last_track_timestamp_[frame->track_number() - 1] = frame->timestamp(); - } - - delete frame; - frame = NULL; - } - - const int result = frames_size_; - frames_size_ = 0; - - return result; -} - -bool Segment::WriteFramesLessThan(uint64_t timestamp) { - // Check |cluster_list_size_| to see if this is the first cluster. If it is - // the first cluster the audio frames that are less than the first video - // timesatmp will be written in a later step. - if (frames_size_ > 0 && cluster_list_size_ > 0) { - if (!frames_) - return false; - - Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; - if (!cluster) - return false; - - int32_t shift_left = 0; - - // TODO(fgalligan): Change this to use the durations of frames instead of - // the next frame's start time if the duration is accurate. - for (int32_t i = 1; i < frames_size_; ++i) { - const Frame* const frame_curr = frames_[i]; - - if (frame_curr->timestamp() > timestamp) - break; - - const Frame* const frame_prev = frames_[i - 1]; - if (frame_prev->discard_padding() != 0) - doc_type_version_ = 4; - if (!cluster->AddFrame(frame_prev)) - return false; - - if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) { - if (!AddCuePoint(frame_prev->timestamp(), cues_track_)) - return false; - } - - ++shift_left; - if (frame_prev->timestamp() > last_timestamp_) { - last_timestamp_ = frame_prev->timestamp(); - last_track_timestamp_[frame_prev->track_number() - 1] = - frame_prev->timestamp(); - } - - delete frame_prev; - } - - if (shift_left > 0) { - if (shift_left >= frames_size_) - return false; - - const int32_t new_frames_size = frames_size_ - shift_left; - for (int32_t i = 0; i < new_frames_size; ++i) { - frames_[i] = frames_[i + shift_left]; - } - - frames_size_ = new_frames_size; - } - } - - return true; -} - -bool Segment::DocTypeIsWebm() const { - const int kNumCodecIds = 8; - - // TODO(vigneshv): Tweak .clang-format. - const char* kWebmCodecIds[kNumCodecIds] = { - Tracks::kOpusCodecId, //Tracks::kVorbisCodecId, - Tracks::kVp8CodecId, Tracks::kVp9CodecId, - Tracks::kVp10CodecId, Tracks::kWebVttCaptionsId, - Tracks::kWebVttDescriptionsId, Tracks::kWebVttMetadataId, - Tracks::kWebVttSubtitlesId}; - - const int num_tracks = static_cast(tracks_.track_entries_size()); - for (int track_index = 0; track_index < num_tracks; ++track_index) { - const Track* const track = tracks_.GetTrackByIndex(track_index); - const std::string codec_id = track->codec_id(); - for (int id_index = 0; id_index < kNumCodecIds; ++id_index) { - if (codec_id == kWebmCodecIds[id_index]) { - return true; - } - } - } - - return false; -} - -} // namespace mkvmuxer diff --git a/lib/libwebm/mkvmuxer/mkvmuxer.h b/lib/libwebm/mkvmuxer/mkvmuxer.h deleted file mode 100644 index 46b0029dc..000000000 --- a/lib/libwebm/mkvmuxer/mkvmuxer.h +++ /dev/null @@ -1,1921 +0,0 @@ -// Copyright (c) 2012 The WebM project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. - -#ifndef MKVMUXER_MKVMUXER_H_ -#define MKVMUXER_MKVMUXER_H_ - -#include - -#include -#include -#include - -#include "common/webmids.h" -#include "mkvmuxer/mkvmuxertypes.h" - -// For a description of the WebM elements see -// http://www.webmproject.org/code/specs/container/. - -namespace mkvparser { -class IMkvReader; -} // namespace mkvparser - -namespace mkvmuxer { - -class MkvWriter; -class Segment; - -const uint64_t kMaxTrackNumber = 126; - -/////////////////////////////////////////////////////////////// -// Interface used by the mkvmuxer to write out the Mkv data. -class IMkvWriter { - public: - // Writes out |len| bytes of |buf|. Returns 0 on success. - virtual int32 Write(const void* buf, uint32 len) = 0; - - // Returns the offset of the output position from the beginning of the - // output. - virtual int64 Position() const = 0; - - // Set the current File position. Returns 0 on success. - virtual int32 Position(int64 position) = 0; - - // Returns true if the writer is seekable. - virtual bool Seekable() const = 0; - - // Element start notification. Called whenever an element identifier is about - // to be written to the stream. |element_id| is the element identifier, and - // |position| is the location in the WebM stream where the first octet of the - // element identifier will be written. - // Note: the |MkvId| enumeration in webmids.hpp defines element values. - virtual void ElementStartNotify(uint64 element_id, int64 position) = 0; - - protected: - IMkvWriter(); - virtual ~IMkvWriter(); - - private: - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(IMkvWriter); -}; - -// Writes out the EBML header for a WebM file, but allows caller to specify -// DocType. This function must be called before any other libwebm writing -// functions are called. -bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version, - const char* const doc_type); - -// Writes out the EBML header for a WebM file. This function must be called -// before any other libwebm writing functions are called. -bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version); - -// Deprecated. Writes out EBML header with doc_type_version as -// kDefaultDocTypeVersion. Exists for backward compatibility. -bool WriteEbmlHeader(IMkvWriter* writer); - -// Copies in Chunk from source to destination between the given byte positions -bool ChunkedCopy(mkvparser::IMkvReader* source, IMkvWriter* dst, int64_t start, - int64_t size); - -/////////////////////////////////////////////////////////////// -// Class to hold data the will be written to a block. -class Frame { - public: - Frame(); - ~Frame(); - - // Sets this frame's contents based on |frame|. Returns true on success. On - // failure, this frame's existing contents may be lost. - bool CopyFrom(const Frame& frame); - - // Copies |frame| data into |frame_|. Returns true on success. - bool Init(const uint8_t* frame, uint64_t length); - - // Copies |additional| data into |additional_|. Returns true on success. - bool AddAdditionalData(const uint8_t* additional, uint64_t length, - uint64_t add_id); - - // Returns true if the frame has valid parameters. - bool IsValid() const; - - // Returns true if the frame can be written as a SimpleBlock based on current - // parameters. - bool CanBeSimpleBlock() const; - - uint64_t add_id() const { return add_id_; } - const uint8_t* additional() const { return additional_; } - uint64_t additional_length() const { return additional_length_; } - void set_duration(uint64_t duration); - uint64_t duration() const { return duration_; } - bool duration_set() const { return duration_set_; } - const uint8_t* frame() const { return frame_; } - void set_is_key(bool key) { is_key_ = key; } - bool is_key() const { return is_key_; } - uint64_t length() const { return length_; } - void set_track_number(uint64_t track_number) { track_number_ = track_number; } - uint64_t track_number() const { return track_number_; } - void set_timestamp(uint64_t timestamp) { timestamp_ = timestamp; } - uint64_t timestamp() const { return timestamp_; } - void set_discard_padding(int64_t discard_padding) { - discard_padding_ = discard_padding; - } - int64_t discard_padding() const { return discard_padding_; } - void set_reference_block_timestamp(int64_t reference_block_timestamp); - int64_t reference_block_timestamp() const { - return reference_block_timestamp_; - } - bool reference_block_timestamp_set() const { - return reference_block_timestamp_set_; - } - - private: - // Id of the Additional data. - uint64_t add_id_; - - // Pointer to additional data. Owned by this class. - uint8_t* additional_; - - // Length of the additional data. - uint64_t additional_length_; - - // Duration of the frame in nanoseconds. - uint64_t duration_; - - // Flag indicating that |duration_| has been set. Setting duration causes the - // frame to be written out as a Block with BlockDuration instead of as a - // SimpleBlock. - bool duration_set_; - - // Pointer to the data. Owned by this class. - uint8_t* frame_; - - // Flag telling if the data should set the key flag of a block. - bool is_key_; - - // Length of the data. - uint64_t length_; - - // Mkv track number the data is associated with. - uint64_t track_number_; - - // Timestamp of the data in nanoseconds. - uint64_t timestamp_; - - // Discard padding for the frame. - int64_t discard_padding_; - - // Reference block timestamp. - int64_t reference_block_timestamp_; - - // Flag indicating if |reference_block_timestamp_| has been set. - bool reference_block_timestamp_set_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Frame); -}; - -/////////////////////////////////////////////////////////////// -// Class to hold one cue point in a Cues element. -class CuePoint { - public: - CuePoint(); - ~CuePoint(); - - // Returns the size in bytes for the entire CuePoint element. - uint64_t Size() const; - - // Output the CuePoint element to the writer. Returns true on success. - bool Write(IMkvWriter* writer) const; - - void set_time(uint64_t time) { time_ = time; } - uint64_t time() const { return time_; } - void set_track(uint64_t track) { track_ = track; } - uint64_t track() const { return track_; } - void set_cluster_pos(uint64_t cluster_pos) { cluster_pos_ = cluster_pos; } - uint64_t cluster_pos() const { return cluster_pos_; } - void set_block_number(uint64_t block_number) { block_number_ = block_number; } - uint64_t block_number() const { return block_number_; } - void set_output_block_number(bool output_block_number) { - output_block_number_ = output_block_number; - } - bool output_block_number() const { return output_block_number_; } - - private: - // Returns the size in bytes for the payload of the CuePoint element. - uint64_t PayloadSize() const; - - // Absolute timecode according to the segment time base. - uint64_t time_; - - // The Track element associated with the CuePoint. - uint64_t track_; - - // The position of the Cluster containing the Block. - uint64_t cluster_pos_; - - // Number of the Block within the Cluster, starting from 1. - uint64_t block_number_; - - // If true the muxer will write out the block number for the cue if the - // block number is different than the default of 1. Default is set to true. - bool output_block_number_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(CuePoint); -}; - -/////////////////////////////////////////////////////////////// -// Cues element. -class Cues { - public: - Cues(); - ~Cues(); - - // Adds a cue point to the Cues element. Returns true on success. - bool AddCue(CuePoint* cue); - - // Returns the cue point by index. Returns NULL if there is no cue point - // match. - CuePoint* GetCueByIndex(int32_t index) const; - - // Returns the total size of the Cues element - uint64_t Size(); - - // Output the Cues element to the writer. Returns true on success. - bool Write(IMkvWriter* writer) const; - - int32_t cue_entries_size() const { return cue_entries_size_; } - void set_output_block_number(bool output_block_number) { - output_block_number_ = output_block_number; - } - bool output_block_number() const { return output_block_number_; } - - private: - // Number of allocated elements in |cue_entries_|. - int32_t cue_entries_capacity_; - - // Number of CuePoints in |cue_entries_|. - int32_t cue_entries_size_; - - // CuePoint list. - CuePoint** cue_entries_; - - // If true the muxer will write out the block number for the cue if the - // block number is different than the default of 1. Default is set to true. - bool output_block_number_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cues); -}; - -/////////////////////////////////////////////////////////////// -// ContentEncAESSettings element -class ContentEncAESSettings { - public: - enum { kCTR = 1 }; - - ContentEncAESSettings(); - ~ContentEncAESSettings() {} - - // Returns the size in bytes for the ContentEncAESSettings element. - uint64_t Size() const; - - // Writes out the ContentEncAESSettings element to |writer|. Returns true on - // success. - bool Write(IMkvWriter* writer) const; - - uint64_t cipher_mode() const { return cipher_mode_; } - - private: - // Returns the size in bytes for the payload of the ContentEncAESSettings - // element. - uint64_t PayloadSize() const; - - // Sub elements - uint64_t cipher_mode_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncAESSettings); -}; - -/////////////////////////////////////////////////////////////// -// ContentEncoding element -// Elements used to describe if the track data has been encrypted or -// compressed with zlib or header stripping. -// Currently only whole frames can be encrypted with AES. This dictates that -// ContentEncodingOrder will be 0, ContentEncodingScope will be 1, -// ContentEncodingType will be 1, and ContentEncAlgo will be 5. -class ContentEncoding { - public: - ContentEncoding(); - ~ContentEncoding(); - - // Sets the content encryption id. Copies |length| bytes from |id| to - // |enc_key_id_|. Returns true on success. - bool SetEncryptionID(const uint8_t* id, uint64_t length); - - // Returns the size in bytes for the ContentEncoding element. - uint64_t Size() const; - - // Writes out the ContentEncoding element to |writer|. Returns true on - // success. - bool Write(IMkvWriter* writer) const; - - uint64_t enc_algo() const { return enc_algo_; } - uint64_t encoding_order() const { return encoding_order_; } - uint64_t encoding_scope() const { return encoding_scope_; } - uint64_t encoding_type() const { return encoding_type_; } - ContentEncAESSettings* enc_aes_settings() { return &enc_aes_settings_; } - - private: - // Returns the size in bytes for the encoding elements. - uint64_t EncodingSize(uint64_t compresion_size, - uint64_t encryption_size) const; - - // Returns the size in bytes for the encryption elements. - uint64_t EncryptionSize() const; - - // Track element names - uint64_t enc_algo_; - uint8_t* enc_key_id_; - uint64_t encoding_order_; - uint64_t encoding_scope_; - uint64_t encoding_type_; - - // ContentEncAESSettings element. - ContentEncAESSettings enc_aes_settings_; - - // Size of the ContentEncKeyID data in bytes. - uint64_t enc_key_id_length_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding); -}; - -/////////////////////////////////////////////////////////////// -// Colour element. -class PrimaryChromaticity { - public: - static const float kChromaticityMin; - static const float kChromaticityMax; - - PrimaryChromaticity(float x_val, float y_val) : x_(x_val), y_(y_val) {} - PrimaryChromaticity() : x_(0), y_(0) {} - ~PrimaryChromaticity() {} - - // Returns sum of |x_id| and |y_id| element id sizes and payload sizes. - uint64_t PrimaryChromaticitySize(libwebm::MkvId x_id, - libwebm::MkvId y_id) const; - bool Valid() const; - bool Write(IMkvWriter* writer, libwebm::MkvId x_id, - libwebm::MkvId y_id) const; - - float x() const { return x_; } - void set_x(float new_x) { x_ = new_x; } - float y() const { return y_; } - void set_y(float new_y) { y_ = new_y; } - - private: - float x_; - float y_; -}; - -class MasteringMetadata { - public: - static const float kValueNotPresent; - static const float kMinLuminance; - static const float kMinLuminanceMax; - static const float kMaxLuminanceMax; - - MasteringMetadata() - : luminance_max_(kValueNotPresent), - luminance_min_(kValueNotPresent), - r_(NULL), - g_(NULL), - b_(NULL), - white_point_(NULL) {} - ~MasteringMetadata() { - delete r_; - delete g_; - delete b_; - delete white_point_; - } - - // Returns total size of the MasteringMetadata element. - uint64_t MasteringMetadataSize() const; - bool Valid() const; - bool Write(IMkvWriter* writer) const; - - // Copies non-null chromaticity. - bool SetChromaticity(const PrimaryChromaticity* r, - const PrimaryChromaticity* g, - const PrimaryChromaticity* b, - const PrimaryChromaticity* white_point); - const PrimaryChromaticity* r() const { return r_; } - const PrimaryChromaticity* g() const { return g_; } - const PrimaryChromaticity* b() const { return b_; } - const PrimaryChromaticity* white_point() const { return white_point_; } - - float luminance_max() const { return luminance_max_; } - void set_luminance_max(float luminance_max) { - luminance_max_ = luminance_max; - } - float luminance_min() const { return luminance_min_; } - void set_luminance_min(float luminance_min) { - luminance_min_ = luminance_min; - } - - private: - // Returns size of MasteringMetadata child elements. - uint64_t PayloadSize() const; - - float luminance_max_; - float luminance_min_; - PrimaryChromaticity* r_; - PrimaryChromaticity* g_; - PrimaryChromaticity* b_; - PrimaryChromaticity* white_point_; -}; - -class Colour { - public: - enum MatrixCoefficients { - kGbr = 0, - kBt709 = 1, - kUnspecifiedMc = 2, - kReserved = 3, - kFcc = 4, - kBt470bg = 5, - kSmpte170MMc = 6, - kSmpte240MMc = 7, - kYcocg = 8, - kBt2020NonConstantLuminance = 9, - kBt2020ConstantLuminance = 10, - }; - enum ChromaSitingHorz { - kUnspecifiedCsh = 0, - kLeftCollocated = 1, - kHalfCsh = 2, - }; - enum ChromaSitingVert { - kUnspecifiedCsv = 0, - kTopCollocated = 1, - kHalfCsv = 2, - }; - enum Range { - kUnspecifiedCr = 0, - kBroadcastRange = 1, - kFullRange = 2, - kMcTcDefined = 3, // Defined by MatrixCoefficients/TransferCharacteristics. - }; - enum TransferCharacteristics { - kIturBt709Tc = 1, - kUnspecifiedTc = 2, - kReservedTc = 3, - kGamma22Curve = 4, - kGamma28Curve = 5, - kSmpte170MTc = 6, - kSmpte240MTc = 7, - kLinear = 8, - kLog = 9, - kLogSqrt = 10, - kIec6196624 = 11, - kIturBt1361ExtendedColourGamut = 12, - kIec6196621 = 13, - kIturBt202010bit = 14, - kIturBt202012bit = 15, - kSmpteSt2084 = 16, - kSmpteSt4281Tc = 17, - kAribStdB67Hlg = 18, - }; - enum Primaries { - kReservedP0 = 0, - kIturBt709P = 1, - kUnspecifiedP = 2, - kReservedP3 = 3, - kIturBt470M = 4, - kIturBt470Bg = 5, - kSmpte170MP = 6, - kSmpte240MP = 7, - kFilm = 8, - kIturBt2020 = 9, - kSmpteSt4281P = 10, - kJedecP22Phosphors = 22, - }; - static const uint64_t kValueNotPresent; - Colour() - : matrix_coefficients_(kValueNotPresent), - bits_per_channel_(kValueNotPresent), - chroma_subsampling_horz_(kValueNotPresent), - chroma_subsampling_vert_(kValueNotPresent), - cb_subsampling_horz_(kValueNotPresent), - cb_subsampling_vert_(kValueNotPresent), - chroma_siting_horz_(kValueNotPresent), - chroma_siting_vert_(kValueNotPresent), - range_(kValueNotPresent), - transfer_characteristics_(kValueNotPresent), - primaries_(kValueNotPresent), - max_cll_(kValueNotPresent), - max_fall_(kValueNotPresent), - mastering_metadata_(NULL) {} - ~Colour() { delete mastering_metadata_; } - - // Returns total size of the Colour element. - uint64_t ColourSize() const; - bool Valid() const; - bool Write(IMkvWriter* writer) const; - - // Deep copies |mastering_metadata|. - bool SetMasteringMetadata(const MasteringMetadata& mastering_metadata); - - const MasteringMetadata* mastering_metadata() const { - return mastering_metadata_; - } - - uint64_t matrix_coefficients() const { return matrix_coefficients_; } - void set_matrix_coefficients(uint64_t matrix_coefficients) { - matrix_coefficients_ = matrix_coefficients; - } - uint64_t bits_per_channel() const { return bits_per_channel_; } - void set_bits_per_channel(uint64_t bits_per_channel) { - bits_per_channel_ = bits_per_channel; - } - uint64_t chroma_subsampling_horz() const { return chroma_subsampling_horz_; } - void set_chroma_subsampling_horz(uint64_t chroma_subsampling_horz) { - chroma_subsampling_horz_ = chroma_subsampling_horz; - } - uint64_t chroma_subsampling_vert() const { return chroma_subsampling_vert_; } - void set_chroma_subsampling_vert(uint64_t chroma_subsampling_vert) { - chroma_subsampling_vert_ = chroma_subsampling_vert; - } - uint64_t cb_subsampling_horz() const { return cb_subsampling_horz_; } - void set_cb_subsampling_horz(uint64_t cb_subsampling_horz) { - cb_subsampling_horz_ = cb_subsampling_horz; - } - uint64_t cb_subsampling_vert() const { return cb_subsampling_vert_; } - void set_cb_subsampling_vert(uint64_t cb_subsampling_vert) { - cb_subsampling_vert_ = cb_subsampling_vert; - } - uint64_t chroma_siting_horz() const { return chroma_siting_horz_; } - void set_chroma_siting_horz(uint64_t chroma_siting_horz) { - chroma_siting_horz_ = chroma_siting_horz; - } - uint64_t chroma_siting_vert() const { return chroma_siting_vert_; } - void set_chroma_siting_vert(uint64_t chroma_siting_vert) { - chroma_siting_vert_ = chroma_siting_vert; - } - uint64_t range() const { return range_; } - void set_range(uint64_t range) { range_ = range; } - uint64_t transfer_characteristics() const { - return transfer_characteristics_; - } - void set_transfer_characteristics(uint64_t transfer_characteristics) { - transfer_characteristics_ = transfer_characteristics; - } - uint64_t primaries() const { return primaries_; } - void set_primaries(uint64_t primaries) { primaries_ = primaries; } - uint64_t max_cll() const { return max_cll_; } - void set_max_cll(uint64_t max_cll) { max_cll_ = max_cll; } - uint64_t max_fall() const { return max_fall_; } - void set_max_fall(uint64_t max_fall) { max_fall_ = max_fall; } - - private: - // Returns size of Colour child elements. - uint64_t PayloadSize() const; - - uint64_t matrix_coefficients_; - uint64_t bits_per_channel_; - uint64_t chroma_subsampling_horz_; - uint64_t chroma_subsampling_vert_; - uint64_t cb_subsampling_horz_; - uint64_t cb_subsampling_vert_; - uint64_t chroma_siting_horz_; - uint64_t chroma_siting_vert_; - uint64_t range_; - uint64_t transfer_characteristics_; - uint64_t primaries_; - uint64_t max_cll_; - uint64_t max_fall_; - - MasteringMetadata* mastering_metadata_; -}; - -/////////////////////////////////////////////////////////////// -// Projection element. -class Projection { - public: - enum ProjectionType { - kTypeNotPresent = -1, - kRectangular = 0, - kEquirectangular = 1, - kCubeMap = 2, - kMesh = 3, - }; - static const uint64_t kValueNotPresent; - Projection() - : type_(kRectangular), - pose_yaw_(0.0), - pose_pitch_(0.0), - pose_roll_(0.0), - private_data_(NULL), - private_data_length_(0) {} - ~Projection() { delete[] private_data_; } - - uint64_t ProjectionSize() const; - bool Write(IMkvWriter* writer) const; - - bool SetProjectionPrivate(const uint8_t* private_data, - uint64_t private_data_length); - - ProjectionType type() const { return type_; } - void set_type(ProjectionType type) { type_ = type; } - float pose_yaw() const { return pose_yaw_; } - void set_pose_yaw(float pose_yaw) { pose_yaw_ = pose_yaw; } - float pose_pitch() const { return pose_pitch_; } - void set_pose_pitch(float pose_pitch) { pose_pitch_ = pose_pitch; } - float pose_roll() const { return pose_roll_; } - void set_pose_roll(float pose_roll) { pose_roll_ = pose_roll; } - uint8_t* private_data() const { return private_data_; } - uint64_t private_data_length() const { return private_data_length_; } - - private: - // Returns size of VideoProjection child elements. - uint64_t PayloadSize() const; - - ProjectionType type_; - float pose_yaw_; - float pose_pitch_; - float pose_roll_; - uint8_t* private_data_; - uint64_t private_data_length_; -}; - -/////////////////////////////////////////////////////////////// -// Track element. -class Track { - public: - // The |seed| parameter is used to synthesize a UID for the track. - explicit Track(unsigned int* seed); - virtual ~Track(); - - // Adds a ContentEncoding element to the Track. Returns true on success. - virtual bool AddContentEncoding(); - - // Returns the ContentEncoding by index. Returns NULL if there is no - // ContentEncoding match. - ContentEncoding* GetContentEncodingByIndex(uint32_t index) const; - - // Returns the size in bytes for the payload of the Track element. - virtual uint64_t PayloadSize() const; - - // Returns the size in bytes of the Track element. - virtual uint64_t Size() const; - - // Output the Track element to the writer. Returns true on success. - virtual bool Write(IMkvWriter* writer) const; - - // Sets the CodecPrivate element of the Track element. Copies |length| - // bytes from |codec_private| to |codec_private_|. Returns true on success. - bool SetCodecPrivate(const uint8_t* codec_private, uint64_t length); - - void set_codec_id(const char* codec_id); - const char* codec_id() const { return codec_id_; } - const uint8_t* codec_private() const { return codec_private_; } - void set_language(const char* language); - const char* language() const { return language_; } - void set_max_block_additional_id(uint64_t max_block_additional_id) { - max_block_additional_id_ = max_block_additional_id; - } - uint64_t max_block_additional_id() const { return max_block_additional_id_; } - void set_name(const char* name); - const char* name() const { return name_; } - void set_number(uint64_t number) { number_ = number; } - uint64_t number() const { return number_; } - void set_type(uint64_t type) { type_ = type; } - uint64_t type() const { return type_; } - void set_uid(uint64_t uid) { uid_ = uid; } - uint64_t uid() const { return uid_; } - void set_codec_delay(uint64_t codec_delay) { codec_delay_ = codec_delay; } - uint64_t codec_delay() const { return codec_delay_; } - void set_seek_pre_roll(uint64_t seek_pre_roll) { - seek_pre_roll_ = seek_pre_roll; - } - uint64_t seek_pre_roll() const { return seek_pre_roll_; } - void set_default_duration(uint64_t default_duration) { - default_duration_ = default_duration; - } - uint64_t default_duration() const { return default_duration_; } - - uint64_t codec_private_length() const { return codec_private_length_; } - uint32_t content_encoding_entries_size() const { - return content_encoding_entries_size_; - } - - private: - // Track element names. - char* codec_id_; - uint8_t* codec_private_; - char* language_; - uint64_t max_block_additional_id_; - char* name_; - uint64_t number_; - uint64_t type_; - uint64_t uid_; - uint64_t codec_delay_; - uint64_t seek_pre_roll_; - uint64_t default_duration_; - - // Size of the CodecPrivate data in bytes. - uint64_t codec_private_length_; - - // ContentEncoding element list. - ContentEncoding** content_encoding_entries_; - - // Number of ContentEncoding elements added. - uint32_t content_encoding_entries_size_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Track); -}; - -/////////////////////////////////////////////////////////////// -// Track that has video specific elements. -class VideoTrack : public Track { - public: - // Supported modes for stereo 3D. - enum StereoMode { - kMono = 0, - kSideBySideLeftIsFirst = 1, - kTopBottomRightIsFirst = 2, - kTopBottomLeftIsFirst = 3, - kSideBySideRightIsFirst = 11 - }; - - enum AlphaMode { kNoAlpha = 0, kAlpha = 1 }; - - // The |seed| parameter is used to synthesize a UID for the track. - explicit VideoTrack(unsigned int* seed); - virtual ~VideoTrack(); - - // Returns the size in bytes for the payload of the Track element plus the - // video specific elements. - virtual uint64_t PayloadSize() const; - - // Output the VideoTrack element to the writer. Returns true on success. - virtual bool Write(IMkvWriter* writer) const; - - // Sets the video's stereo mode. Returns true on success. - bool SetStereoMode(uint64_t stereo_mode); - - // Sets the video's alpha mode. Returns true on success. - bool SetAlphaMode(uint64_t alpha_mode); - - void set_display_height(uint64_t height) { display_height_ = height; } - uint64_t display_height() const { return display_height_; } - void set_display_width(uint64_t width) { display_width_ = width; } - uint64_t display_width() const { return display_width_; } - void set_pixel_height(uint64_t height) { pixel_height_ = height; } - uint64_t pixel_height() const { return pixel_height_; } - void set_pixel_width(uint64_t width) { pixel_width_ = width; } - uint64_t pixel_width() const { return pixel_width_; } - - void set_crop_left(uint64_t crop_left) { crop_left_ = crop_left; } - uint64_t crop_left() const { return crop_left_; } - void set_crop_right(uint64_t crop_right) { crop_right_ = crop_right; } - uint64_t crop_right() const { return crop_right_; } - void set_crop_top(uint64_t crop_top) { crop_top_ = crop_top; } - uint64_t crop_top() const { return crop_top_; } - void set_crop_bottom(uint64_t crop_bottom) { crop_bottom_ = crop_bottom; } - uint64_t crop_bottom() const { return crop_bottom_; } - - void set_frame_rate(double frame_rate) { frame_rate_ = frame_rate; } - double frame_rate() const { return frame_rate_; } - void set_height(uint64_t height) { height_ = height; } - uint64_t height() const { return height_; } - uint64_t stereo_mode() { return stereo_mode_; } - uint64_t alpha_mode() { return alpha_mode_; } - void set_width(uint64_t width) { width_ = width; } - uint64_t width() const { return width_; } - - Colour* colour() { return colour_; } - - // Deep copies |colour|. - bool SetColour(const Colour& colour); - - Projection* projection() { return projection_; } - - // Deep copies |projection|. - bool SetProjection(const Projection& projection); - - private: - // Returns the size in bytes of the Video element. - uint64_t VideoPayloadSize() const; - - // Video track element names. - uint64_t display_height_; - uint64_t display_width_; - uint64_t pixel_height_; - uint64_t pixel_width_; - uint64_t crop_left_; - uint64_t crop_right_; - uint64_t crop_top_; - uint64_t crop_bottom_; - double frame_rate_; - uint64_t height_; - uint64_t stereo_mode_; - uint64_t alpha_mode_; - uint64_t width_; - - Colour* colour_; - Projection* projection_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(VideoTrack); -}; - -/////////////////////////////////////////////////////////////// -// Track that has audio specific elements. -class AudioTrack : public Track { - public: - // The |seed| parameter is used to synthesize a UID for the track. - explicit AudioTrack(unsigned int* seed); - virtual ~AudioTrack(); - - // Returns the size in bytes for the payload of the Track element plus the - // audio specific elements. - virtual uint64_t PayloadSize() const; - - // Output the AudioTrack element to the writer. Returns true on success. - virtual bool Write(IMkvWriter* writer) const; - - void set_bit_depth(uint64_t bit_depth) { bit_depth_ = bit_depth; } - uint64_t bit_depth() const { return bit_depth_; } - void set_channels(uint64_t channels) { channels_ = channels; } - uint64_t channels() const { return channels_; } - void set_sample_rate(double sample_rate) { sample_rate_ = sample_rate; } - double sample_rate() const { return sample_rate_; } - - private: - // Audio track element names. - uint64_t bit_depth_; - uint64_t channels_; - double sample_rate_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(AudioTrack); -}; - -/////////////////////////////////////////////////////////////// -// Tracks element -class Tracks { - public: - // Audio and video type defined by the Matroska specs. - enum { kVideo = 0x1, kAudio = 0x2 }; - - static const char kOpusCodecId[]; - static const char kVorbisCodecId[]; - static const char kVp8CodecId[]; - static const char kVp9CodecId[]; - static const char kVp10CodecId[]; - static const char kWebVttCaptionsId[]; - static const char kWebVttDescriptionsId[]; - static const char kWebVttMetadataId[]; - static const char kWebVttSubtitlesId[]; - - Tracks(); - ~Tracks(); - - // Adds a Track element to the Tracks object. |track| will be owned and - // deleted by the Tracks object. Returns true on success. |number| is the - // number to use for the track. |number| must be >= 0. If |number| == 0 - // then the muxer will decide on the track number. - bool AddTrack(Track* track, int32_t number); - - // Returns the track by index. Returns NULL if there is no track match. - const Track* GetTrackByIndex(uint32_t idx) const; - - // Search the Tracks and return the track that matches |tn|. Returns NULL - // if there is no track match. - Track* GetTrackByNumber(uint64_t track_number) const; - - // Returns true if the track number is an audio track. - bool TrackIsAudio(uint64_t track_number) const; - - // Returns true if the track number is a video track. - bool TrackIsVideo(uint64_t track_number) const; - - // Output the Tracks element to the writer. Returns true on success. - bool Write(IMkvWriter* writer) const; - - uint32_t track_entries_size() const { return track_entries_size_; } - - private: - // Track element list. - Track** track_entries_; - - // Number of Track elements added. - uint32_t track_entries_size_; - - // Whether or not Tracks element has already been written via IMkvWriter. - mutable bool wrote_tracks_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tracks); -}; - -/////////////////////////////////////////////////////////////// -// Chapter element -// -class Chapter { - public: - // Set the identifier for this chapter. (This corresponds to the - // Cue Identifier line in WebVTT.) - // TODO(matthewjheaney): the actual serialization of this item in - // MKV is pending. - bool set_id(const char* id); - - // Converts the nanosecond start and stop times of this chapter to - // their corresponding timecode values, and stores them that way. - void set_time(const Segment& segment, uint64_t start_time_ns, - uint64_t end_time_ns); - - // Sets the uid for this chapter. Primarily used to enable - // deterministic output from the muxer. - void set_uid(const uint64_t uid) { uid_ = uid; } - - // Add a title string to this chapter, per the semantics described - // here: - // http://www.matroska.org/technical/specs/index.html - // - // The title ("chapter string") is a UTF-8 string. - // - // The language has ISO 639-2 representation, described here: - // http://www.loc.gov/standards/iso639-2/englangn.html - // http://www.loc.gov/standards/iso639-2/php/English_list.php - // If you specify NULL as the language value, this implies - // English ("eng"). - // - // The country value corresponds to the codes listed here: - // http://www.iana.org/domains/root/db/ - // - // The function returns false if the string could not be allocated. - bool add_string(const char* title, const char* language, const char* country); - - private: - friend class Chapters; - - // For storage of chapter titles that differ by language. - class Display { - public: - // Establish representation invariant for new Display object. - void Init(); - - // Reclaim resources, in anticipation of destruction. - void Clear(); - - // Copies the title to the |title_| member. Returns false on - // error. - bool set_title(const char* title); - - // Copies the language to the |language_| member. Returns false - // on error. - bool set_language(const char* language); - - // Copies the country to the |country_| member. Returns false on - // error. - bool set_country(const char* country); - - // If |writer| is non-NULL, serialize the Display sub-element of - // the Atom into the stream. Returns the Display element size on - // success, 0 if error. - uint64_t WriteDisplay(IMkvWriter* writer) const; - - private: - char* title_; - char* language_; - char* country_; - }; - - Chapter(); - ~Chapter(); - - // Establish the representation invariant for a newly-created - // Chapter object. The |seed| parameter is used to create the UID - // for this chapter atom. - void Init(unsigned int* seed); - - // Copies this Chapter object to a different one. This is used when - // expanding a plain array of Chapter objects (see Chapters). - void ShallowCopy(Chapter* dst) const; - - // Reclaim resources used by this Chapter object, pending its - // destruction. - void Clear(); - - // If there is no storage remaining on the |displays_| array for a - // new display object, creates a new, longer array and copies the - // existing Display objects to the new array. Returns false if the - // array cannot be expanded. - bool ExpandDisplaysArray(); - - // If |writer| is non-NULL, serialize the Atom sub-element into the - // stream. Returns the total size of the element on success, 0 if - // error. - uint64_t WriteAtom(IMkvWriter* writer) const; - - // The string identifier for this chapter (corresponds to WebVTT cue - // identifier). - char* id_; - - // Start timecode of the chapter. - uint64_t start_timecode_; - - // Stop timecode of the chapter. - uint64_t end_timecode_; - - // The binary identifier for this chapter. - uint64_t uid_; - - // The Atom element can contain multiple Display sub-elements, as - // the same logical title can be rendered in different languages. - Display* displays_; - - // The physical length (total size) of the |displays_| array. - int displays_size_; - - // The logical length (number of active elements) on the |displays_| - // array. - int displays_count_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapter); -}; - -/////////////////////////////////////////////////////////////// -// Chapters element -// -class Chapters { - public: - Chapters(); - ~Chapters(); - - Chapter* AddChapter(unsigned int* seed); - - // Returns the number of chapters that have been added. - int Count() const; - - // Output the Chapters element to the writer. Returns true on success. - bool Write(IMkvWriter* writer) const; - - private: - // Expands the chapters_ array if there is not enough space to contain - // another chapter object. Returns true on success. - bool ExpandChaptersArray(); - - // If |writer| is non-NULL, serialize the Edition sub-element of the - // Chapters element into the stream. Returns the Edition element - // size on success, 0 if error. - uint64_t WriteEdition(IMkvWriter* writer) const; - - // Total length of the chapters_ array. - int chapters_size_; - - // Number of active chapters on the chapters_ array. - int chapters_count_; - - // Array for storage of chapter objects. - Chapter* chapters_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapters); -}; - -/////////////////////////////////////////////////////////////// -// Tag element -// -class Tag { - public: - bool add_simple_tag(const char* tag_name, const char* tag_string); - - private: - // Tags calls Clear and the destructor of Tag - friend class Tags; - - // For storage of simple tags - class SimpleTag { - public: - // Establish representation invariant for new SimpleTag object. - void Init(); - - // Reclaim resources, in anticipation of destruction. - void Clear(); - - // Copies the title to the |tag_name_| member. Returns false on - // error. - bool set_tag_name(const char* tag_name); - - // Copies the language to the |tag_string_| member. Returns false - // on error. - bool set_tag_string(const char* tag_string); - - // If |writer| is non-NULL, serialize the SimpleTag sub-element of - // the Atom into the stream. Returns the SimpleTag element size on - // success, 0 if error. - uint64_t Write(IMkvWriter* writer) const; - - private: - char* tag_name_; - char* tag_string_; - }; - - Tag(); - ~Tag(); - - // Copies this Tag object to a different one. This is used when - // expanding a plain array of Tag objects (see Tags). - void ShallowCopy(Tag* dst) const; - - // Reclaim resources used by this Tag object, pending its - // destruction. - void Clear(); - - // If there is no storage remaining on the |simple_tags_| array for a - // new display object, creates a new, longer array and copies the - // existing SimpleTag objects to the new array. Returns false if the - // array cannot be expanded. - bool ExpandSimpleTagsArray(); - - // If |writer| is non-NULL, serialize the Tag sub-element into the - // stream. Returns the total size of the element on success, 0 if - // error. - uint64_t Write(IMkvWriter* writer) const; - - // The Atom element can contain multiple SimpleTag sub-elements - SimpleTag* simple_tags_; - - // The physical length (total size) of the |simple_tags_| array. - int simple_tags_size_; - - // The logical length (number of active elements) on the |simple_tags_| - // array. - int simple_tags_count_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tag); -}; - -/////////////////////////////////////////////////////////////// -// Tags element -// -class Tags { - public: - Tags(); - ~Tags(); - - Tag* AddTag(); - - // Returns the number of tags that have been added. - int Count() const; - - // Output the Tags element to the writer. Returns true on success. - bool Write(IMkvWriter* writer) const; - - private: - // Expands the tags_ array if there is not enough space to contain - // another tag object. Returns true on success. - bool ExpandTagsArray(); - - // Total length of the tags_ array. - int tags_size_; - - // Number of active tags on the tags_ array. - int tags_count_; - - // Array for storage of tag objects. - Tag* tags_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tags); -}; - -/////////////////////////////////////////////////////////////// -// Cluster element -// -// Notes: -// |Init| must be called before any other method in this class. -class Cluster { - public: - // |timecode| is the absolute timecode of the cluster. |cues_pos| is the - // position for the cluster within the segment that should be written in - // the cues element. |timecode_scale| is the timecode scale of the segment. - Cluster(uint64_t timecode, int64_t cues_pos, uint64_t timecode_scale, - bool write_last_frame_with_duration = false, - bool fixed_size_timecode = false); - ~Cluster(); - - bool Init(IMkvWriter* ptr_writer); - - // Adds a frame to be output in the file. The frame is written out through - // |writer_| if successful. Returns true on success. - bool AddFrame(const Frame* frame); - - // Adds a frame to be output in the file. The frame is written out through - // |writer_| if successful. Returns true on success. - // Inputs: - // data: Pointer to the data - // length: Length of the data - // track_number: Track to add the data to. Value returned by Add track - // functions. The range of allowed values is [1, 126]. - // timecode: Absolute (not relative to cluster) timestamp of the - // frame, expressed in timecode units. - // is_key: Flag telling whether or not this frame is a key frame. - bool AddFrame(const uint8_t* data, uint64_t length, uint64_t track_number, - uint64_t timecode, // timecode units (absolute) - bool is_key); - - // Adds a frame to be output in the file. The frame is written out through - // |writer_| if successful. Returns true on success. - // Inputs: - // data: Pointer to the data - // length: Length of the data - // additional: Pointer to the additional data - // additional_length: Length of the additional data - // add_id: Value of BlockAddID element - // track_number: Track to add the data to. Value returned by Add track - // functions. The range of allowed values is [1, 126]. - // abs_timecode: Absolute (not relative to cluster) timestamp of the - // frame, expressed in timecode units. - // is_key: Flag telling whether or not this frame is a key frame. - bool AddFrameWithAdditional(const uint8_t* data, uint64_t length, - const uint8_t* additional, - uint64_t additional_length, uint64_t add_id, - uint64_t track_number, uint64_t abs_timecode, - bool is_key); - - // Adds a frame to be output in the file. The frame is written out through - // |writer_| if successful. Returns true on success. - // Inputs: - // data: Pointer to the data. - // length: Length of the data. - // discard_padding: DiscardPadding element value. - // track_number: Track to add the data to. Value returned by Add track - // functions. The range of allowed values is [1, 126]. - // abs_timecode: Absolute (not relative to cluster) timestamp of the - // frame, expressed in timecode units. - // is_key: Flag telling whether or not this frame is a key frame. - bool AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length, - int64_t discard_padding, - uint64_t track_number, uint64_t abs_timecode, - bool is_key); - - // Writes a frame of metadata to the output medium; returns true on - // success. - // Inputs: - // data: Pointer to the data - // length: Length of the data - // track_number: Track to add the data to. Value returned by Add track - // functions. The range of allowed values is [1, 126]. - // timecode: Absolute (not relative to cluster) timestamp of the - // metadata frame, expressed in timecode units. - // duration: Duration of metadata frame, in timecode units. - // - // The metadata frame is written as a block group, with a duration - // sub-element but no reference time sub-elements (indicating that - // it is considered a keyframe, per Matroska semantics). - bool AddMetadata(const uint8_t* data, uint64_t length, uint64_t track_number, - uint64_t timecode, uint64_t duration); - - // Increments the size of the cluster's data in bytes. - void AddPayloadSize(uint64_t size); - - // Closes the cluster so no more data can be written to it. Will update the - // cluster's size if |writer_| is seekable. Returns true on success. This - // variant of Finalize() fails when |write_last_frame_with_duration_| is set - // to true. - bool Finalize(); - - // Closes the cluster so no more data can be written to it. Will update the - // cluster's size if |writer_| is seekable. Returns true on success. - // Inputs: - // set_last_frame_duration: Boolean indicating whether or not the duration - // of the last frame should be set. If set to - // false, the |duration| value is ignored and - // |write_last_frame_with_duration_| will not be - // honored. - // duration: Duration of the Cluster in timecode scale. - bool Finalize(bool set_last_frame_duration, uint64_t duration); - - // Returns the size in bytes for the entire Cluster element. - uint64_t Size() const; - - // Given |abs_timecode|, calculates timecode relative to most recent timecode. - // Returns -1 on failure, or a relative timecode. - int64_t GetRelativeTimecode(int64_t abs_timecode) const; - - int64_t size_position() const { return size_position_; } - int32_t blocks_added() const { return blocks_added_; } - uint64_t payload_size() const { return payload_size_; } - int64_t position_for_cues() const { return position_for_cues_; } - uint64_t timecode() const { return timecode_; } - uint64_t timecode_scale() const { return timecode_scale_; } - void set_write_last_frame_with_duration(bool write_last_frame_with_duration) { - write_last_frame_with_duration_ = write_last_frame_with_duration; - } - bool write_last_frame_with_duration() const { - return write_last_frame_with_duration_; - } - - private: - // Iterator type for the |stored_frames_| map. - typedef std::map >::iterator FrameMapIterator; - - // Utility method that confirms that blocks can still be added, and that the - // cluster header has been written. Used by |DoWriteFrame*|. Returns true - // when successful. - bool PreWriteBlock(); - - // Utility method used by the |DoWriteFrame*| methods that handles the book - // keeping required after each block is written. - void PostWriteBlock(uint64_t element_size); - - // Does some verification and calls WriteFrame. - bool DoWriteFrame(const Frame* const frame); - - // Either holds back the given frame, or writes it out depending on whether or - // not |write_last_frame_with_duration_| is set. - bool QueueOrWriteFrame(const Frame* const frame); - - // Outputs the Cluster header to |writer_|. Returns true on success. - bool WriteClusterHeader(); - - // Number of blocks added to the cluster. - int32_t blocks_added_; - - // Flag telling if the cluster has been closed. - bool finalized_; - - // Flag indicating whether the cluster's timecode will always be written out - // using 8 bytes. - bool fixed_size_timecode_; - - // Flag telling if the cluster's header has been written. - bool header_written_; - - // The size of the cluster elements in bytes. - uint64_t payload_size_; - - // The file position used for cue points. - const int64_t position_for_cues_; - - // The file position of the cluster's size element. - int64_t size_position_; - - // The absolute timecode of the cluster. - const uint64_t timecode_; - - // The timecode scale of the Segment containing the cluster. - const uint64_t timecode_scale_; - - // Flag indicating whether the last frame of the cluster should be written as - // a Block with Duration. If set to true, then it will result in holding back - // of frames and the parameterized version of Finalize() must be called to - // finish writing the Cluster. - bool write_last_frame_with_duration_; - - // Map used to hold back frames, if required. Track number is the key. - std::map > stored_frames_; - - // Map from track number to the timestamp of the last block written for that - // track. - std::map last_block_timestamp_; - - // Pointer to the writer object. Not owned by this class. - IMkvWriter* writer_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Cluster); -}; - -/////////////////////////////////////////////////////////////// -// SeekHead element -class SeekHead { - public: - SeekHead(); - ~SeekHead(); - - // TODO(fgalligan): Change this to reserve a certain size. Then check how - // big the seek entry to be added is as not every seek entry will be the - // maximum size it could be. - // Adds a seek entry to be written out when the element is finalized. |id| - // must be the coded mkv element id. |pos| is the file position of the - // element. Returns true on success. - bool AddSeekEntry(uint32_t id, uint64_t pos); - - // Writes out SeekHead and SeekEntry elements. Returns true on success. - bool Finalize(IMkvWriter* writer) const; - - // Returns the id of the Seek Entry at the given index. Returns -1 if index is - // out of range. - uint32_t GetId(int index) const; - - // Returns the position of the Seek Entry at the given index. Returns -1 if - // index is out of range. - uint64_t GetPosition(int index) const; - - // Sets the Seek Entry id and position at given index. - // Returns true on success. - bool SetSeekEntry(int index, uint32_t id, uint64_t position); - - // Reserves space by writing out a Void element which will be updated with - // a SeekHead element later. Returns true on success. - bool Write(IMkvWriter* writer); - - // We are going to put a cap on the number of Seek Entries. - const static int32_t kSeekEntryCount = 5; - - private: - // Returns the maximum size in bytes of one seek entry. - uint64_t MaxEntrySize() const; - - // Seek entry id element list. - uint32_t seek_entry_id_[kSeekEntryCount]; - - // Seek entry pos element list. - uint64_t seek_entry_pos_[kSeekEntryCount]; - - // The file position of SeekHead element. - int64_t start_pos_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SeekHead); -}; - -/////////////////////////////////////////////////////////////// -// Segment Information element -class SegmentInfo { - public: - SegmentInfo(); - ~SegmentInfo(); - - // Will update the duration if |duration_| is > 0.0. Returns true on success. - bool Finalize(IMkvWriter* writer) const; - - // Sets |muxing_app_| and |writing_app_|. - bool Init(); - - // Output the Segment Information element to the writer. Returns true on - // success. - bool Write(IMkvWriter* writer); - - void set_duration(double duration) { duration_ = duration; } - double duration() const { return duration_; } - void set_muxing_app(const char* app); - const char* muxing_app() const { return muxing_app_; } - void set_timecode_scale(uint64_t scale) { timecode_scale_ = scale; } - uint64_t timecode_scale() const { return timecode_scale_; } - void set_writing_app(const char* app); - const char* writing_app() const { return writing_app_; } - void set_date_utc(int64_t date_utc) { date_utc_ = date_utc; } - int64_t date_utc() const { return date_utc_; } - - private: - // Segment Information element names. - // Initially set to -1 to signify that a duration has not been set and should - // not be written out. - double duration_; - // Set to libwebm-%d.%d.%d.%d, major, minor, build, revision. - char* muxing_app_; - uint64_t timecode_scale_; - // Initially set to libwebm-%d.%d.%d.%d, major, minor, build, revision. - char* writing_app_; - // LLONG_MIN when DateUTC is not set. - int64_t date_utc_; - - // The file position of the duration element. - int64_t duration_pos_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(SegmentInfo); -}; - -/////////////////////////////////////////////////////////////// -// This class represents the main segment in a WebM file. Currently only -// supports one Segment element. -// -// Notes: -// |Init| must be called before any other method in this class. -class Segment { - public: - enum Mode { kLive = 0x1, kFile = 0x2 }; - - enum CuesPosition { - kAfterClusters = 0x0, // Position Cues after Clusters - Default - kBeforeClusters = 0x1 // Position Cues before Clusters - }; - - static const uint32_t kDefaultDocTypeVersion = 4; - static const uint64_t kDefaultMaxClusterDuration = 30000000000ULL; - - Segment(); - ~Segment(); - - // Initializes |SegmentInfo| and returns result. Always returns false when - // |ptr_writer| is NULL. - bool Init(IMkvWriter* ptr_writer); - - // Adds a generic track to the segment. Returns the newly-allocated - // track object (which is owned by the segment) on success, NULL on - // error. |number| is the number to use for the track. |number| - // must be >= 0. If |number| == 0 then the muxer will decide on the - // track number. - Track* AddTrack(int32_t number); - - // Adds a Vorbis audio track to the segment. Returns the number of the track - // on success, 0 on error. |number| is the number to use for the audio track. - // |number| must be >= 0. If |number| == 0 then the muxer will decide on - // the track number. - uint64_t AddAudioTrack(int32_t sample_rate, int32_t channels, int32_t number); - - // Adds an empty chapter to the chapters of this segment. Returns - // non-NULL on success. After adding the chapter, the caller should - // populate its fields via the Chapter member functions. - Chapter* AddChapter(); - - // Adds an empty tag to the tags of this segment. Returns - // non-NULL on success. After adding the tag, the caller should - // populate its fields via the Tag member functions. - Tag* AddTag(); - - // Adds a cue point to the Cues element. |timestamp| is the time in - // nanoseconds of the cue's time. |track| is the Track of the Cue. This - // function must be called after AddFrame to calculate the correct - // BlockNumber for the CuePoint. Returns true on success. - bool AddCuePoint(uint64_t timestamp, uint64_t track); - - // Adds a frame to be output in the file. Returns true on success. - // Inputs: - // data: Pointer to the data - // length: Length of the data - // track_number: Track to add the data to. Value returned by Add track - // functions. - // timestamp: Timestamp of the frame in nanoseconds from 0. - // is_key: Flag telling whether or not this frame is a key frame. - bool AddFrame(const uint8_t* data, uint64_t length, uint64_t track_number, - uint64_t timestamp_ns, bool is_key); - - // Writes a frame of metadata to the output medium; returns true on - // success. - // Inputs: - // data: Pointer to the data - // length: Length of the data - // track_number: Track to add the data to. Value returned by Add track - // functions. - // timecode: Absolute timestamp of the metadata frame, expressed - // in nanosecond units. - // duration: Duration of metadata frame, in nanosecond units. - // - // The metadata frame is written as a block group, with a duration - // sub-element but no reference time sub-elements (indicating that - // it is considered a keyframe, per Matroska semantics). - bool AddMetadata(const uint8_t* data, uint64_t length, uint64_t track_number, - uint64_t timestamp_ns, uint64_t duration_ns); - - // Writes a frame with additional data to the output medium; returns true on - // success. - // Inputs: - // data: Pointer to the data. - // length: Length of the data. - // additional: Pointer to additional data. - // additional_length: Length of additional data. - // add_id: Additional ID which identifies the type of additional data. - // track_number: Track to add the data to. Value returned by Add track - // functions. - // timestamp: Absolute timestamp of the frame, expressed in nanosecond - // units. - // is_key: Flag telling whether or not this frame is a key frame. - bool AddFrameWithAdditional(const uint8_t* data, uint64_t length, - const uint8_t* additional, - uint64_t additional_length, uint64_t add_id, - uint64_t track_number, uint64_t timestamp, - bool is_key); - - // Writes a frame with DiscardPadding to the output medium; returns true on - // success. - // Inputs: - // data: Pointer to the data. - // length: Length of the data. - // discard_padding: DiscardPadding element value. - // track_number: Track to add the data to. Value returned by Add track - // functions. - // timestamp: Absolute timestamp of the frame, expressed in nanosecond - // units. - // is_key: Flag telling whether or not this frame is a key frame. - bool AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length, - int64_t discard_padding, - uint64_t track_number, uint64_t timestamp, - bool is_key); - - // Writes a Frame to the output medium. Chooses the correct way of writing - // the frame (Block vs SimpleBlock) based on the parameters passed. - // Inputs: - // frame: frame object - bool AddGenericFrame(const Frame* frame); - - // Adds a VP8 video track to the segment. Returns the number of the track on - // success, 0 on error. |number| is the number to use for the video track. - // |number| must be >= 0. If |number| == 0 then the muxer will decide on - // the track number. - uint64_t AddVideoTrack(int32_t width, int32_t height, int32_t number); - - // This function must be called after Finalize() if you need a copy of the - // output with Cues written before the Clusters. It will return false if the - // writer is not seekable of if chunking is set to true. - // Input parameters: - // reader - an IMkvReader object created with the same underlying file of the - // current writer object. Make sure to close the existing writer - // object before creating this so that all the data is properly - // flushed and available for reading. - // writer - an IMkvWriter object pointing to a *different* file than the one - // pointed by the current writer object. This file will contain the - // Cues element before the Clusters. - bool CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader, - IMkvWriter* writer); - - // Sets which track to use for the Cues element. Must have added the track - // before calling this function. Returns true on success. |track_number| is - // returned by the Add track functions. - bool CuesTrack(uint64_t track_number); - - // This will force the muxer to create a new Cluster when the next frame is - // added. - void ForceNewClusterOnNextFrame(); - - // Writes out any frames that have not been written out. Finalizes the last - // cluster. May update the size and duration of the segment. May output the - // Cues element. May finalize the SeekHead element. Returns true on success. - bool Finalize(); - - // Returns the Cues object. - Cues* GetCues() { return &cues_; } - - // Returns the Segment Information object. - const SegmentInfo* GetSegmentInfo() const { return &segment_info_; } - SegmentInfo* GetSegmentInfo() { return &segment_info_; } - - // Search the Tracks and return the track that matches |track_number|. - // Returns NULL if there is no track match. - Track* GetTrackByNumber(uint64_t track_number) const; - - // Toggles whether to output a cues element. - void OutputCues(bool output_cues); - - // Toggles whether to write the last frame in each Cluster with Duration. - void AccurateClusterDuration(bool accurate_cluster_duration); - - // Toggles whether to write the Cluster Timecode using exactly 8 bytes. - void UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode); - - // Sets if the muxer will output files in chunks or not. |chunking| is a - // flag telling whether or not to turn on chunking. |filename| is the base - // filename for the chunk files. The header chunk file will be named - // |filename|.hdr and the data chunks will be named - // |filename|_XXXXXX.chk. Chunking implies that the muxer will be writing - // to files so the muxer will use the default MkvWriter class to control - // what data is written to what files. Returns true on success. - // TODO: Should we change the IMkvWriter Interface to add Open and Close? - // That will force the interface to be dependent on files. - bool SetChunking(bool chunking, const char* filename); - - bool chunking() const { return chunking_; } - uint64_t cues_track() const { return cues_track_; } - void set_max_cluster_duration(uint64_t max_cluster_duration) { - max_cluster_duration_ = max_cluster_duration; - } - uint64_t max_cluster_duration() const { return max_cluster_duration_; } - void set_max_cluster_size(uint64_t max_cluster_size) { - max_cluster_size_ = max_cluster_size; - } - uint64_t max_cluster_size() const { return max_cluster_size_; } - void set_mode(Mode mode) { mode_ = mode; } - Mode mode() const { return mode_; } - CuesPosition cues_position() const { return cues_position_; } - bool output_cues() const { return output_cues_; } - void set_estimate_file_duration(bool estimate_duration) { - estimate_file_duration_ = estimate_duration; - } - bool estimate_file_duration() const { return estimate_file_duration_; } - const SegmentInfo* segment_info() const { return &segment_info_; } - void set_duration(double duration) { duration_ = duration; } - double duration() const { return duration_; } - - // Returns true when codec IDs are valid for WebM. - bool DocTypeIsWebm() const; - - private: - // Checks if header information has been output and initialized. If not it - // will output the Segment element and initialize the SeekHead elment and - // Cues elements. - bool CheckHeaderInfo(); - - // Sets |doc_type_version_| based on the current element requirements. - void UpdateDocTypeVersion(); - - // Sets |name| according to how many chunks have been written. |ext| is the - // file extension. |name| must be deleted by the calling app. Returns true - // on success. - bool UpdateChunkName(const char* ext, char** name) const; - - // Returns the maximum offset within the segment's payload. When chunking - // this function is needed to determine offsets of elements within the - // chunked files. Returns -1 on error. - int64_t MaxOffset(); - - // Adds the frame to our frame array. - bool QueueFrame(Frame* frame); - - // Output all frames that are queued. Returns -1 on error, otherwise - // it returns the number of frames written. - int WriteFramesAll(); - - // Output all frames that are queued that have an end time that is less - // then |timestamp|. Returns true on success and if there are no frames - // queued. - bool WriteFramesLessThan(uint64_t timestamp); - - // Outputs the segment header, Segment Information element, SeekHead element, - // and Tracks element to |writer_|. - bool WriteSegmentHeader(); - - // Given a frame with the specified timestamp (nanosecond units) and - // keyframe status, determine whether a new cluster should be - // created, before writing enqueued frames and the frame itself. The - // function returns one of the following values: - // -1 = error: an out-of-order frame was detected - // 0 = do not create a new cluster, and write frame to the existing cluster - // 1 = create a new cluster, and write frame to that new cluster - // 2 = create a new cluster, and re-run test - int TestFrame(uint64_t track_num, uint64_t timestamp_ns, bool key) const; - - // Create a new cluster, using the earlier of the first enqueued - // frame, or the indicated time. Returns true on success. - bool MakeNewCluster(uint64_t timestamp_ns); - - // Checks whether a new cluster needs to be created, and if so - // creates a new cluster. Returns false if creation of a new cluster - // was necessary but creation was not successful. - bool DoNewClusterProcessing(uint64_t track_num, uint64_t timestamp_ns, - bool key); - - // Adjusts Cue Point values (to place Cues before Clusters) so that they - // reflect the correct offsets. - void MoveCuesBeforeClusters(); - - // This function recursively computes the correct cluster offsets (this is - // done to move the Cues before Clusters). It recursively updates the change - // in size (which indicates a change in cluster offset) until no sizes change. - // Parameters: - // diff - indicates the difference in size of the Cues element that needs to - // accounted for. - // index - index in the list of Cues which is currently being adjusted. - // cue_size - sum of size of all the CuePoint elements. - void MoveCuesBeforeClustersHelper(uint64_t diff, int index, - uint64_t* cue_size); - - // Seeds the random number generator used to make UIDs. - unsigned int seed_; - - // WebM elements - Cues cues_; - SeekHead seek_head_; - SegmentInfo segment_info_; - Tracks tracks_; - Chapters chapters_; - Tags tags_; - - // Number of chunks written. - int chunk_count_; - - // Current chunk filename. - char* chunk_name_; - - // Default MkvWriter object created by this class used for writing clusters - // out in separate files. - MkvWriter* chunk_writer_cluster_; - - // Default MkvWriter object created by this class used for writing Cues - // element out to a file. - MkvWriter* chunk_writer_cues_; - - // Default MkvWriter object created by this class used for writing the - // Matroska header out to a file. - MkvWriter* chunk_writer_header_; - - // Flag telling whether or not the muxer is chunking output to multiple - // files. - bool chunking_; - - // Base filename for the chunked files. - char* chunking_base_name_; - - // File position offset where the Clusters end. - int64_t cluster_end_offset_; - - // List of clusters. - Cluster** cluster_list_; - - // Number of cluster pointers allocated in the cluster list. - int32_t cluster_list_capacity_; - - // Number of clusters in the cluster list. - int32_t cluster_list_size_; - - // Indicates whether Cues should be written before or after Clusters - CuesPosition cues_position_; - - // Track number that is associated with the cues element for this segment. - uint64_t cues_track_; - - // Tells the muxer to force a new cluster on the next Block. - bool force_new_cluster_; - - // List of stored audio frames. These variables are used to store frames so - // the muxer can follow the guideline "Audio blocks that contain the video - // key frame's timecode should be in the same cluster as the video key frame - // block." - Frame** frames_; - - // Number of frame pointers allocated in the frame list. - int32_t frames_capacity_; - - // Number of frames in the frame list. - int32_t frames_size_; - - // Flag telling if a video track has been added to the segment. - bool has_video_; - - // Flag telling if the segment's header has been written. - bool header_written_; - - // Duration of the last block in nanoseconds. - uint64_t last_block_duration_; - - // Last timestamp in nanoseconds added to a cluster. - uint64_t last_timestamp_; - - // Last timestamp in nanoseconds by track number added to a cluster. - uint64_t last_track_timestamp_[kMaxTrackNumber]; - - // Number of frames written per track. - uint64_t track_frames_written_[kMaxTrackNumber]; - - // Maximum time in nanoseconds for a cluster duration. This variable is a - // guideline and some clusters may have a longer duration. Default is 30 - // seconds. - uint64_t max_cluster_duration_; - - // Maximum size in bytes for a cluster. This variable is a guideline and - // some clusters may have a larger size. Default is 0 which signifies that - // the muxer will decide the size. - uint64_t max_cluster_size_; - - // The mode that segment is in. If set to |kLive| the writer must not - // seek backwards. - Mode mode_; - - // Flag telling the muxer that a new cue point should be added. - bool new_cuepoint_; - - // TODO(fgalligan): Should we add support for more than one Cues element? - // Flag whether or not the muxer should output a Cues element. - bool output_cues_; - - // Flag whether or not the last frame in each Cluster will have a Duration - // element in it. - bool accurate_cluster_duration_; - - // Flag whether or not to write the Cluster Timecode using exactly 8 bytes. - bool fixed_size_cluster_timecode_; - - // Flag whether or not to estimate the file duration. - bool estimate_file_duration_; - - // The size of the EBML header, used to validate the header if - // WriteEbmlHeader() is called more than once. - int32_t ebml_header_size_; - - // The file position of the segment's payload. - int64_t payload_pos_; - - // The file position of the element's size. - int64_t size_position_; - - // Current DocTypeVersion (|doc_type_version_|) and that written in - // WriteSegmentHeader(). - // WriteEbmlHeader() will be called from Finalize() if |doc_type_version_| - // differs from |doc_type_version_written_|. - uint32_t doc_type_version_; - uint32_t doc_type_version_written_; - - // If |duration_| is > 0, then explicitly set the duration of the segment. - double duration_; - - // Pointer to the writer objects. Not owned by this class. - IMkvWriter* writer_cluster_; - IMkvWriter* writer_cues_; - IMkvWriter* writer_header_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Segment); -}; - -} // namespace mkvmuxer - -#endif // MKVMUXER_MKVMUXER_H_ diff --git a/lib/libwebm/mkvmuxer/mkvmuxertypes.h b/lib/libwebm/mkvmuxer/mkvmuxertypes.h deleted file mode 100644 index e5db12160..000000000 --- a/lib/libwebm/mkvmuxer/mkvmuxertypes.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2012 The WebM project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. - -#ifndef MKVMUXER_MKVMUXERTYPES_H_ -#define MKVMUXER_MKVMUXERTYPES_H_ - -namespace mkvmuxer { -typedef unsigned char uint8; -typedef short int16; -typedef int int32; -typedef unsigned int uint32; -typedef long long int64; -typedef unsigned long long uint64; -} // namespace mkvmuxer - -// Copied from Chromium basictypes.h -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for a class -#define LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - -#endif // MKVMUXER_MKVMUXERTYPES_HPP_ diff --git a/lib/libwebm/mkvmuxer/mkvmuxerutil.cc b/lib/libwebm/mkvmuxer/mkvmuxerutil.cc deleted file mode 100644 index bd98b1104..000000000 --- a/lib/libwebm/mkvmuxer/mkvmuxerutil.cc +++ /dev/null @@ -1,743 +0,0 @@ -// Copyright (c) 2012 The WebM project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. - -#include "mkvmuxer/mkvmuxerutil.h" - -#ifdef __ANDROID__ -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "common/webmids.h" -#include "mkvmuxer/mkvmuxer.h" -#include "mkvmuxer/mkvwriter.h" - -namespace mkvmuxer { - -namespace { - -// Date elements are always 8 octets in size. -const int kDateElementSize = 8; - -uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode, - uint64 timecode_scale) { - uint64 block_additional_elem_size = 0; - uint64 block_addid_elem_size = 0; - uint64 block_more_payload_size = 0; - uint64 block_more_elem_size = 0; - uint64 block_additions_payload_size = 0; - uint64 block_additions_elem_size = 0; - if (frame->additional()) { - block_additional_elem_size = - EbmlElementSize(libwebm::kMkvBlockAdditional, frame->additional(), - frame->additional_length()); - block_addid_elem_size = EbmlElementSize( - libwebm::kMkvBlockAddID, static_cast(frame->add_id())); - - block_more_payload_size = - block_addid_elem_size + block_additional_elem_size; - block_more_elem_size = - EbmlMasterElementSize(libwebm::kMkvBlockMore, block_more_payload_size) + - block_more_payload_size; - block_additions_payload_size = block_more_elem_size; - block_additions_elem_size = - EbmlMasterElementSize(libwebm::kMkvBlockAdditions, - block_additions_payload_size) + - block_additions_payload_size; - } - - uint64 discard_padding_elem_size = 0; - if (frame->discard_padding() != 0) { - discard_padding_elem_size = - EbmlElementSize(libwebm::kMkvDiscardPadding, - static_cast(frame->discard_padding())); - } - - const uint64 reference_block_timestamp = - frame->reference_block_timestamp() / timecode_scale; - uint64 reference_block_elem_size = 0; - if (!frame->is_key()) { - reference_block_elem_size = - EbmlElementSize(libwebm::kMkvReferenceBlock, reference_block_timestamp); - } - - const uint64 duration = frame->duration() / timecode_scale; - uint64 block_duration_elem_size = 0; - if (duration > 0) - block_duration_elem_size = - EbmlElementSize(libwebm::kMkvBlockDuration, duration); - - const uint64 block_payload_size = 4 + frame->length(); - const uint64 block_elem_size = - EbmlMasterElementSize(libwebm::kMkvBlock, block_payload_size) + - block_payload_size; - - const uint64 block_group_payload_size = - block_elem_size + block_additions_elem_size + block_duration_elem_size + - discard_padding_elem_size + reference_block_elem_size; - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockGroup, - block_group_payload_size)) { - return 0; - } - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlock, block_payload_size)) - return 0; - - if (WriteUInt(writer, frame->track_number())) - return 0; - - if (SerializeInt(writer, timecode, 2)) - return 0; - - // For a Block, flags is always 0. - if (SerializeInt(writer, 0, 1)) - return 0; - - if (writer->Write(frame->frame(), static_cast(frame->length()))) - return 0; - - if (frame->additional()) { - if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockAdditions, - block_additions_payload_size)) { - return 0; - } - - if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockMore, - block_more_payload_size)) - return 0; - - if (!WriteEbmlElement(writer, libwebm::kMkvBlockAddID, - static_cast(frame->add_id()))) - return 0; - - if (!WriteEbmlElement(writer, libwebm::kMkvBlockAdditional, - frame->additional(), frame->additional_length())) { - return 0; - } - } - - if (frame->discard_padding() != 0 && - !WriteEbmlElement(writer, libwebm::kMkvDiscardPadding, - static_cast(frame->discard_padding()))) { - return false; - } - - if (!frame->is_key() && - !WriteEbmlElement(writer, libwebm::kMkvReferenceBlock, - reference_block_timestamp)) { - return false; - } - - if (duration > 0 && - !WriteEbmlElement(writer, libwebm::kMkvBlockDuration, duration)) { - return false; - } - return EbmlMasterElementSize(libwebm::kMkvBlockGroup, - block_group_payload_size) + - block_group_payload_size; -} - -uint64 WriteSimpleBlock(IMkvWriter* writer, const Frame* const frame, - int64 timecode) { - if (WriteID(writer, libwebm::kMkvSimpleBlock)) - return 0; - - const int32 size = static_cast(frame->length()) + 4; - if (WriteUInt(writer, size)) - return 0; - - if (WriteUInt(writer, static_cast(frame->track_number()))) - return 0; - - if (SerializeInt(writer, timecode, 2)) - return 0; - - uint64 flags = 0; - if (frame->is_key()) - flags |= 0x80; - - if (SerializeInt(writer, flags, 1)) - return 0; - - if (writer->Write(frame->frame(), static_cast(frame->length()))) - return 0; - - return GetUIntSize(libwebm::kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 + - frame->length(); -} - -} // namespace - -int32 GetCodedUIntSize(uint64 value) { - if (value < 0x000000000000007FULL) - return 1; - else if (value < 0x0000000000003FFFULL) - return 2; - else if (value < 0x00000000001FFFFFULL) - return 3; - else if (value < 0x000000000FFFFFFFULL) - return 4; - else if (value < 0x00000007FFFFFFFFULL) - return 5; - else if (value < 0x000003FFFFFFFFFFULL) - return 6; - else if (value < 0x0001FFFFFFFFFFFFULL) - return 7; - return 8; -} - -int32 GetUIntSize(uint64 value) { - if (value < 0x0000000000000100ULL) - return 1; - else if (value < 0x0000000000010000ULL) - return 2; - else if (value < 0x0000000001000000ULL) - return 3; - else if (value < 0x0000000100000000ULL) - return 4; - else if (value < 0x0000010000000000ULL) - return 5; - else if (value < 0x0001000000000000ULL) - return 6; - else if (value < 0x0100000000000000ULL) - return 7; - return 8; -} - -int32 GetIntSize(int64 value) { - // Doubling the requested value ensures positive values with their high bit - // set are written with 0-padding to avoid flipping the signedness. - const uint64 v = (value < 0) ? value ^ -1LL : value; - return GetUIntSize(2 * v); -} - -uint64 EbmlMasterElementSize(uint64 type, uint64 value) { - // Size of EBML ID - int32 ebml_size = GetUIntSize(type); - - // Datasize - ebml_size += GetCodedUIntSize(value); - - return ebml_size; -} - -uint64 EbmlElementSize(uint64 type, int64 value) { - // Size of EBML ID - int32 ebml_size = GetUIntSize(type); - - // Datasize - ebml_size += GetIntSize(value); - - // Size of Datasize - ebml_size++; - - return ebml_size; -} - -uint64 EbmlElementSize(uint64 type, uint64 value) { - return EbmlElementSize(type, value, 0); -} - -uint64 EbmlElementSize(uint64 type, uint64 value, uint64 fixed_size) { - // Size of EBML ID - uint64 ebml_size = GetUIntSize(type); - - // Datasize - ebml_size += (fixed_size > 0) ? fixed_size : GetUIntSize(value); - - // Size of Datasize - ebml_size++; - - return ebml_size; -} - -uint64 EbmlElementSize(uint64 type, float /* value */) { - // Size of EBML ID - uint64 ebml_size = GetUIntSize(type); - - // Datasize - ebml_size += sizeof(float); - - // Size of Datasize - ebml_size++; - - return ebml_size; -} - -uint64 EbmlElementSize(uint64 type, const char* value) { - if (!value) - return 0; - - // Size of EBML ID - uint64 ebml_size = GetUIntSize(type); - - // Datasize - ebml_size += strlen(value); - - // Size of Datasize - ebml_size += GetCodedUIntSize(strlen(value)); - - return ebml_size; -} - -uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size) { - if (!value) - return 0; - - // Size of EBML ID - uint64 ebml_size = GetUIntSize(type); - - // Datasize - ebml_size += size; - - // Size of Datasize - ebml_size += GetCodedUIntSize(size); - - return ebml_size; -} - -uint64 EbmlDateElementSize(uint64 type) { - // Size of EBML ID - uint64 ebml_size = GetUIntSize(type); - - // Datasize - ebml_size += kDateElementSize; - - // Size of Datasize - ebml_size++; - - return ebml_size; -} - -int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size) { - if (!writer || size < 1 || size > 8) - return -1; - - for (int32 i = 1; i <= size; ++i) { - const int32 byte_count = size - i; - const int32 bit_count = byte_count * 8; - - const int64 bb = value >> bit_count; - const uint8 b = static_cast(bb); - - const int32 status = writer->Write(&b, 1); - - if (status < 0) - return status; - } - - return 0; -} - -int32 SerializeFloat(IMkvWriter* writer, float f) { - if (!writer) - return -1; - - assert(sizeof(uint32) == sizeof(float)); - // This union is merely used to avoid a reinterpret_cast from float& to - // uint32& which will result in violation of strict aliasing. - union U32 { - uint32 u32; - float f; - } value; - value.f = f; - - for (int32 i = 1; i <= 4; ++i) { - const int32 byte_count = 4 - i; - const int32 bit_count = byte_count * 8; - - const uint8 byte = static_cast(value.u32 >> bit_count); - - const int32 status = writer->Write(&byte, 1); - - if (status < 0) - return status; - } - - return 0; -} - -int32 WriteUInt(IMkvWriter* writer, uint64 value) { - if (!writer) - return -1; - - int32 size = GetCodedUIntSize(value); - - return WriteUIntSize(writer, value, size); -} - -int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size) { - if (!writer || size < 0 || size > 8) - return -1; - - if (size > 0) { - const uint64 bit = 1LL << (size * 7); - - if (value > (bit - 2)) - return -1; - - value |= bit; - } else { - size = 1; - int64 bit; - - for (;;) { - bit = 1LL << (size * 7); - const uint64 max = bit - 2; - - if (value <= max) - break; - - ++size; - } - - if (size > 8) - return false; - - value |= bit; - } - - return SerializeInt(writer, value, size); -} - -int32 WriteID(IMkvWriter* writer, uint64 type) { - if (!writer) - return -1; - - writer->ElementStartNotify(type, writer->Position()); - - const int32 size = GetUIntSize(type); - - return SerializeInt(writer, type, size); -} - -bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 type, uint64 size) { - if (!writer) - return false; - - if (WriteID(writer, type)) - return false; - - if (WriteUInt(writer, size)) - return false; - - return true; -} - -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) { - return WriteEbmlElement(writer, type, value, 0); -} - -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value, - uint64 fixed_size) { - if (!writer) - return false; - - if (WriteID(writer, type)) - return false; - - uint64 size = GetUIntSize(value); - if (fixed_size > 0) { - if (size > fixed_size) - return false; - size = fixed_size; - } - if (WriteUInt(writer, size)) - return false; - - if (SerializeInt(writer, value, static_cast(size))) - return false; - - return true; -} - -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value) { - if (!writer) - return false; - - if (WriteID(writer, type)) - return 0; - - const uint64 size = GetIntSize(value); - if (WriteUInt(writer, size)) - return false; - - if (SerializeInt(writer, value, static_cast(size))) - return false; - - return true; -} - -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value) { - if (!writer) - return false; - - if (WriteID(writer, type)) - return false; - - if (WriteUInt(writer, 4)) - return false; - - if (SerializeFloat(writer, value)) - return false; - - return true; -} - -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value) { - if (!writer || !value) - return false; - - if (WriteID(writer, type)) - return false; - - const uint64 length = strlen(value); - if (WriteUInt(writer, length)) - return false; - - if (writer->Write(value, static_cast(length))) - return false; - - return true; -} - -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value, - uint64 size) { - if (!writer || !value || size < 1) - return false; - - if (WriteID(writer, type)) - return false; - - if (WriteUInt(writer, size)) - return false; - - if (writer->Write(value, static_cast(size))) - return false; - - return true; -} - -bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value) { - if (!writer) - return false; - - if (WriteID(writer, type)) - return false; - - if (WriteUInt(writer, kDateElementSize)) - return false; - - if (SerializeInt(writer, value, kDateElementSize)) - return false; - - return true; -} - -uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame, - Cluster* cluster) { - if (!writer || !frame || !frame->IsValid() || !cluster || - !cluster->timecode_scale()) - return 0; - - // Technically the timecode for a block can be less than the - // timecode for the cluster itself (remember that block timecode - // is a signed, 16-bit integer). However, as a simplification we - // only permit non-negative cluster-relative timecodes for blocks. - const int64 relative_timecode = cluster->GetRelativeTimecode( - frame->timestamp() / cluster->timecode_scale()); - if (relative_timecode < 0 || relative_timecode > kMaxBlockTimecode) - return 0; - - return frame->CanBeSimpleBlock() ? - WriteSimpleBlock(writer, frame, relative_timecode) : - WriteBlock(writer, frame, relative_timecode, - cluster->timecode_scale()); -} - -uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) { - if (!writer) - return false; - - // Subtract one for the void ID and the coded size. - uint64 void_entry_size = size - 1 - GetCodedUIntSize(size - 1); - uint64 void_size = EbmlMasterElementSize(libwebm::kMkvVoid, void_entry_size) + - void_entry_size; - - if (void_size != size) - return 0; - - const int64 payload_position = writer->Position(); - if (payload_position < 0) - return 0; - - if (WriteID(writer, libwebm::kMkvVoid)) - return 0; - - if (WriteUInt(writer, void_entry_size)) - return 0; - - const uint8 value = 0; - for (int32 i = 0; i < static_cast(void_entry_size); ++i) { - if (writer->Write(&value, 1)) - return 0; - } - - const int64 stop_position = writer->Position(); - if (stop_position < 0 || - stop_position - payload_position != static_cast(void_size)) - return 0; - - return void_size; -} - -void GetVersion(int32* major, int32* minor, int32* build, int32* revision) { - *major = 0; - *minor = 2; - *build = 1; - *revision = 0; -} - -uint64 MakeUID(unsigned int* seed) { - uint64 uid = 0; - -#ifdef __MINGW32__ - srand(*seed); -#endif - - for (int i = 0; i < 7; ++i) { // avoid problems with 8-byte values - uid <<= 8; - -// TODO(fgalligan): Move random number generation to platform specific code. -#ifdef _MSC_VER - (void)seed; - const int32 nn = rand(); -#elif __ANDROID__ - (void)seed; - int32 temp_num = 1; - int fd = open("/dev/urandom", O_RDONLY); - if (fd != -1) { - read(fd, &temp_num, sizeof(temp_num)); - close(fd); - } - const int32 nn = temp_num; -#elif defined __MINGW32__ - const int32 nn = rand(); -#else - const int32 nn = rand_r(seed); -#endif - const int32 n = 0xFF & (nn >> 4); // throw away low-order bits - - uid |= n; - } - - return uid; -} - -bool IsMatrixCoefficientsValueValid(uint64_t value) { - switch (value) { - case mkvmuxer::Colour::kGbr: - case mkvmuxer::Colour::kBt709: - case mkvmuxer::Colour::kUnspecifiedMc: - case mkvmuxer::Colour::kReserved: - case mkvmuxer::Colour::kFcc: - case mkvmuxer::Colour::kBt470bg: - case mkvmuxer::Colour::kSmpte170MMc: - case mkvmuxer::Colour::kSmpte240MMc: - case mkvmuxer::Colour::kYcocg: - case mkvmuxer::Colour::kBt2020NonConstantLuminance: - case mkvmuxer::Colour::kBt2020ConstantLuminance: - return true; - } - return false; -} - -bool IsChromaSitingHorzValueValid(uint64_t value) { - switch (value) { - case mkvmuxer::Colour::kUnspecifiedCsh: - case mkvmuxer::Colour::kLeftCollocated: - case mkvmuxer::Colour::kHalfCsh: - return true; - } - return false; -} - -bool IsChromaSitingVertValueValid(uint64_t value) { - switch (value) { - case mkvmuxer::Colour::kUnspecifiedCsv: - case mkvmuxer::Colour::kTopCollocated: - case mkvmuxer::Colour::kHalfCsv: - return true; - } - return false; -} - -bool IsColourRangeValueValid(uint64_t value) { - switch (value) { - case mkvmuxer::Colour::kUnspecifiedCr: - case mkvmuxer::Colour::kBroadcastRange: - case mkvmuxer::Colour::kFullRange: - case mkvmuxer::Colour::kMcTcDefined: - return true; - } - return false; -} - -bool IsTransferCharacteristicsValueValid(uint64_t value) { - switch (value) { - case mkvmuxer::Colour::kIturBt709Tc: - case mkvmuxer::Colour::kUnspecifiedTc: - case mkvmuxer::Colour::kReservedTc: - case mkvmuxer::Colour::kGamma22Curve: - case mkvmuxer::Colour::kGamma28Curve: - case mkvmuxer::Colour::kSmpte170MTc: - case mkvmuxer::Colour::kSmpte240MTc: - case mkvmuxer::Colour::kLinear: - case mkvmuxer::Colour::kLog: - case mkvmuxer::Colour::kLogSqrt: - case mkvmuxer::Colour::kIec6196624: - case mkvmuxer::Colour::kIturBt1361ExtendedColourGamut: - case mkvmuxer::Colour::kIec6196621: - case mkvmuxer::Colour::kIturBt202010bit: - case mkvmuxer::Colour::kIturBt202012bit: - case mkvmuxer::Colour::kSmpteSt2084: - case mkvmuxer::Colour::kSmpteSt4281Tc: - case mkvmuxer::Colour::kAribStdB67Hlg: - return true; - } - return false; -} - -bool IsPrimariesValueValid(uint64_t value) { - switch (value) { - case mkvmuxer::Colour::kReservedP0: - case mkvmuxer::Colour::kIturBt709P: - case mkvmuxer::Colour::kUnspecifiedP: - case mkvmuxer::Colour::kReservedP3: - case mkvmuxer::Colour::kIturBt470M: - case mkvmuxer::Colour::kIturBt470Bg: - case mkvmuxer::Colour::kSmpte170MP: - case mkvmuxer::Colour::kSmpte240MP: - case mkvmuxer::Colour::kFilm: - case mkvmuxer::Colour::kIturBt2020: - case mkvmuxer::Colour::kSmpteSt4281P: - case mkvmuxer::Colour::kJedecP22Phosphors: - return true; - } - return false; -} - -} // namespace mkvmuxer diff --git a/lib/libwebm/mkvmuxer/mkvmuxerutil.h b/lib/libwebm/mkvmuxer/mkvmuxerutil.h deleted file mode 100644 index 132388da5..000000000 --- a/lib/libwebm/mkvmuxer/mkvmuxerutil.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) 2012 The WebM project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -#ifndef MKVMUXER_MKVMUXERUTIL_H_ -#define MKVMUXER_MKVMUXERUTIL_H_ - -#include "mkvmuxertypes.h" - -#include "stdint.h" - -namespace mkvmuxer { -class Cluster; -class Frame; -class IMkvWriter; - -// TODO(tomfinegan): mkvmuxer:: integer types continue to be used here because -// changing them causes pain for downstream projects. It would be nice if a -// solution that allows removal of the mkvmuxer:: integer types while avoiding -// pain for downstream users of libwebm. Considering that mkvmuxerutil.{cc,h} -// are really, for the great majority of cases, EBML size calculation and writer -// functions, perhaps a more EBML focused utility would be the way to go as a -// first step. - -const uint64 kEbmlUnknownValue = 0x01FFFFFFFFFFFFFFULL; -const int64 kMaxBlockTimecode = 0x07FFFLL; - -// Writes out |value| in Big Endian order. Returns 0 on success. -int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size); - -// Returns the size in bytes of the element. -int32 GetUIntSize(uint64 value); -int32 GetIntSize(int64 value); -int32 GetCodedUIntSize(uint64 value); -uint64 EbmlMasterElementSize(uint64 type, uint64 value); -uint64 EbmlElementSize(uint64 type, int64 value); -uint64 EbmlElementSize(uint64 type, uint64 value); -uint64 EbmlElementSize(uint64 type, float value); -uint64 EbmlElementSize(uint64 type, const char* value); -uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size); -uint64 EbmlDateElementSize(uint64 type); - -// Returns the size in bytes of the element assuming that the element was -// written using |fixed_size| bytes. If |fixed_size| is set to zero, then it -// computes the necessary number of bytes based on |value|. -uint64 EbmlElementSize(uint64 type, uint64 value, uint64 fixed_size); - -// Creates an EBML coded number from |value| and writes it out. The size of -// the coded number is determined by the value of |value|. |value| must not -// be in a coded form. Returns 0 on success. -int32 WriteUInt(IMkvWriter* writer, uint64 value); - -// Creates an EBML coded number from |value| and writes it out. The size of -// the coded number is determined by the value of |size|. |value| must not -// be in a coded form. Returns 0 on success. -int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size); - -// Output an Mkv master element. Returns true if the element was written. -bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 value, uint64 size); - -// Outputs an Mkv ID, calls |IMkvWriter::ElementStartNotify|, and passes the -// ID to |SerializeInt|. Returns 0 on success. -int32 WriteID(IMkvWriter* writer, uint64 type); - -// Output an Mkv non-master element. Returns true if the element was written. -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value); -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value); -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value); -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value); -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value, - uint64 size); -bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value); - -// Output an Mkv non-master element using fixed size. The element will be -// written out using exactly |fixed_size| bytes. If |fixed_size| is set to zero -// then it computes the necessary number of bytes based on |value|. Returns true -// if the element was written. -bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value, - uint64 fixed_size); - -// Output a Mkv Frame. It decides the correct element to write (Block vs -// SimpleBlock) based on the parameters of the Frame. -uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame, - Cluster* cluster); - -// Output a void element. |size| must be the entire size in bytes that will be -// void. The function will calculate the size of the void header and subtract -// it from |size|. -uint64 WriteVoidElement(IMkvWriter* writer, uint64 size); - -// Returns the version number of the muxer in |major|, |minor|, |build|, -// and |revision|. -void GetVersion(int32* major, int32* minor, int32* build, int32* revision); - -// Returns a random number to be used for UID, using |seed| to seed -// the random-number generator (see POSIX rand_r() for semantics). -uint64 MakeUID(unsigned int* seed); - -// Colour field validation helpers. All return true when |value| is valid. -bool IsMatrixCoefficientsValueValid(uint64_t value); -bool IsChromaSitingHorzValueValid(uint64_t value); -bool IsChromaSitingVertValueValid(uint64_t value); -bool IsColourRangeValueValid(uint64_t value); -bool IsTransferCharacteristicsValueValid(uint64_t value); -bool IsPrimariesValueValid(uint64_t value); - -} // namespace mkvmuxer - -#endif // MKVMUXER_MKVMUXERUTIL_H_ diff --git a/lib/libwebm/mkvmuxer/mkvwriter.cc b/lib/libwebm/mkvmuxer/mkvwriter.cc deleted file mode 100644 index 84655d802..000000000 --- a/lib/libwebm/mkvmuxer/mkvwriter.cc +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2012 The WebM project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. - -#include "mkvmuxer/mkvwriter.h" - -#include - -#ifdef _MSC_VER -#include // for _SH_DENYWR -#endif - -namespace mkvmuxer { - -MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {} - -MkvWriter::MkvWriter(FILE* fp) : file_(fp), writer_owns_file_(false) {} - -MkvWriter::~MkvWriter() { Close(); } - -int32 MkvWriter::Write(const void* buffer, uint32 length) { - if (!file_) - return -1; - - if (length == 0) - return 0; - - if (buffer == NULL) - return -1; - - const size_t bytes_written = fwrite(buffer, 1, length, file_); - - return (bytes_written == length) ? 0 : -1; -} - -bool MkvWriter::Open(const char* filename) { - if (filename == NULL) - return false; - - if (file_) - return false; - -#ifdef _MSC_VER - file_ = _fsopen(filename, "wb", _SH_DENYWR); -#else - file_ = fopen(filename, "wb"); -#endif - if (file_ == NULL) - return false; - return true; -} - -void MkvWriter::Close() { - if (file_ && writer_owns_file_) { - fclose(file_); - } - file_ = NULL; -} - -int64 MkvWriter::Position() const { - if (!file_) - return 0; - -#ifdef _MSC_VER - return _ftelli64(file_); -#else - return ftell(file_); -#endif -} - -int32 MkvWriter::Position(int64 position) { - if (!file_) - return -1; - -#ifdef _MSC_VER - return _fseeki64(file_, position, SEEK_SET); -#else - return fseeko(file_, static_cast(position), SEEK_SET); -#endif -} - -bool MkvWriter::Seekable() const { return true; } - -void MkvWriter::ElementStartNotify(uint64, int64) {} - -} // namespace mkvmuxer diff --git a/lib/libwebm/mkvmuxer/mkvwriter.h b/lib/libwebm/mkvmuxer/mkvwriter.h deleted file mode 100644 index 4227c6374..000000000 --- a/lib/libwebm/mkvmuxer/mkvwriter.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2012 The WebM project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. - -#ifndef MKVMUXER_MKVWRITER_H_ -#define MKVMUXER_MKVWRITER_H_ - -#include - -#include "mkvmuxer/mkvmuxer.h" -#include "mkvmuxer/mkvmuxertypes.h" - -namespace mkvmuxer { - -// Default implementation of the IMkvWriter interface on Windows. -class MkvWriter : public IMkvWriter { - public: - MkvWriter(); - explicit MkvWriter(FILE* fp); - virtual ~MkvWriter(); - - // IMkvWriter interface - virtual int64 Position() const; - virtual int32 Position(int64 position); - virtual bool Seekable() const; - virtual int32 Write(const void* buffer, uint32 length); - virtual void ElementStartNotify(uint64 element_id, int64 position); - - // Creates and opens a file for writing. |filename| is the name of the file - // to open. This function will overwrite the contents of |filename|. Returns - // true on success. - bool Open(const char* filename); - - // Closes an opened file. - void Close(); - - private: - // File handle to output file. - FILE* file_; - bool writer_owns_file_; - - LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter); -}; - -} // namespace mkvmuxer - -#endif // MKVMUXER_MKVWRITER_H_ diff --git a/lib/libwebm/mkvparser/mkvparser.h b/lib/libwebm/mkvparser/mkvparser.h deleted file mode 100644 index 26c2b7e5e..000000000 --- a/lib/libwebm/mkvparser/mkvparser.h +++ /dev/null @@ -1,1145 +0,0 @@ -// Copyright (c) 2012 The WebM project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -#ifndef MKVPARSER_MKVPARSER_H_ -#define MKVPARSER_MKVPARSER_H_ - -#include - -namespace mkvparser { - -const int E_PARSE_FAILED = -1; -const int E_FILE_FORMAT_INVALID = -2; -const int E_BUFFER_NOT_FULL = -3; - -class IMkvReader { - public: - virtual int Read(long long pos, long len, unsigned char* buf) = 0; - virtual int Length(long long* total, long long* available) = 0; - - protected: - virtual ~IMkvReader(); -}; - -template -Type* SafeArrayAlloc(unsigned long long num_elements, - unsigned long long element_size); -long long GetUIntLength(IMkvReader*, long long, long&); -long long ReadUInt(IMkvReader*, long long, long&); -long long ReadID(IMkvReader* pReader, long long pos, long& len); -long long UnserializeUInt(IMkvReader*, long long pos, long long size); - -long UnserializeFloat(IMkvReader*, long long pos, long long size, double&); -long UnserializeInt(IMkvReader*, long long pos, long long size, - long long& result); - -long UnserializeString(IMkvReader*, long long pos, long long size, char*& str); - -long ParseElementHeader(IMkvReader* pReader, - long long& pos, // consume id and size fields - long long stop, // if you know size of element's parent - long long& id, long long& size); - -bool Match(IMkvReader*, long long&, unsigned long, long long&); -bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&); - -void GetVersion(int& major, int& minor, int& build, int& revision); - -struct EBMLHeader { - EBMLHeader(); - ~EBMLHeader(); - long long m_version; - long long m_readVersion; - long long m_maxIdLength; - long long m_maxSizeLength; - char* m_docType; - long long m_docTypeVersion; - long long m_docTypeReadVersion; - - long long Parse(IMkvReader*, long long&); - void Init(); -}; - -class Segment; -class Track; -class Cluster; - -class Block { - Block(const Block&); - Block& operator=(const Block&); - - public: - const long long m_start; - const long long m_size; - - Block(long long start, long long size, long long discard_padding); - ~Block(); - - long Parse(const Cluster*); - - long long GetTrackNumber() const; - long long GetTimeCode(const Cluster*) const; // absolute, but not scaled - long long GetTime(const Cluster*) const; // absolute, and scaled (ns) - bool IsKey() const; - void SetKey(bool); - bool IsInvisible() const; - - enum Lacing { kLacingNone, kLacingXiph, kLacingFixed, kLacingEbml }; - Lacing GetLacing() const; - - int GetFrameCount() const; // to index frames: [0, count) - - struct Frame { - long long pos; // absolute offset - long len; - - long Read(IMkvReader*, unsigned char*) const; - }; - - const Frame& GetFrame(int frame_index) const; - - long long GetDiscardPadding() const; - - private: - long long m_track; // Track::Number() - short m_timecode; // relative to cluster - unsigned char m_flags; - - Frame* m_frames; - int m_frame_count; - - protected: - const long long m_discard_padding; -}; - -class BlockEntry { - BlockEntry(const BlockEntry&); - BlockEntry& operator=(const BlockEntry&); - - protected: - BlockEntry(Cluster*, long index); - - public: - virtual ~BlockEntry(); - - bool EOS() const { return (GetKind() == kBlockEOS); } - const Cluster* GetCluster() const; - long GetIndex() const; - virtual const Block* GetBlock() const = 0; - - enum Kind { kBlockEOS, kBlockSimple, kBlockGroup }; - virtual Kind GetKind() const = 0; - - protected: - Cluster* const m_pCluster; - const long m_index; -}; - -class SimpleBlock : public BlockEntry { - SimpleBlock(const SimpleBlock&); - SimpleBlock& operator=(const SimpleBlock&); - - public: - SimpleBlock(Cluster*, long index, long long start, long long size); - long Parse(); - - Kind GetKind() const; - const Block* GetBlock() const; - - protected: - Block m_block; -}; - -class BlockGroup : public BlockEntry { - BlockGroup(const BlockGroup&); - BlockGroup& operator=(const BlockGroup&); - - public: - BlockGroup(Cluster*, long index, - long long block_start, // absolute pos of block's payload - long long block_size, // size of block's payload - long long prev, long long next, long long duration, - long long discard_padding); - - long Parse(); - - Kind GetKind() const; - const Block* GetBlock() const; - - long long GetPrevTimeCode() const; // relative to block's time - long long GetNextTimeCode() const; // as above - long long GetDurationTimeCode() const; - - private: - Block m_block; - const long long m_prev; - const long long m_next; - const long long m_duration; -}; - -/////////////////////////////////////////////////////////////// -// ContentEncoding element -// Elements used to describe if the track data has been encrypted or -// compressed with zlib or header stripping. -class ContentEncoding { - public: - enum { kCTR = 1 }; - - ContentEncoding(); - ~ContentEncoding(); - - // ContentCompression element names - struct ContentCompression { - ContentCompression(); - ~ContentCompression(); - - unsigned long long algo; - unsigned char* settings; - long long settings_len; - }; - - // ContentEncAESSettings element names - struct ContentEncAESSettings { - ContentEncAESSettings() : cipher_mode(kCTR) {} - ~ContentEncAESSettings() {} - - unsigned long long cipher_mode; - }; - - // ContentEncryption element names - struct ContentEncryption { - ContentEncryption(); - ~ContentEncryption(); - - unsigned long long algo; - unsigned char* key_id; - long long key_id_len; - unsigned char* signature; - long long signature_len; - unsigned char* sig_key_id; - long long sig_key_id_len; - unsigned long long sig_algo; - unsigned long long sig_hash_algo; - - ContentEncAESSettings aes_settings; - }; - - // Returns ContentCompression represented by |idx|. Returns NULL if |idx| - // is out of bounds. - const ContentCompression* GetCompressionByIndex(unsigned long idx) const; - - // Returns number of ContentCompression elements in this ContentEncoding - // element. - unsigned long GetCompressionCount() const; - - // Parses the ContentCompression element from |pReader|. |start| is the - // starting offset of the ContentCompression payload. |size| is the size in - // bytes of the ContentCompression payload. |compression| is where the parsed - // values will be stored. - long ParseCompressionEntry(long long start, long long size, - IMkvReader* pReader, - ContentCompression* compression); - - // Returns ContentEncryption represented by |idx|. Returns NULL if |idx| - // is out of bounds. - const ContentEncryption* GetEncryptionByIndex(unsigned long idx) const; - - // Returns number of ContentEncryption elements in this ContentEncoding - // element. - unsigned long GetEncryptionCount() const; - - // Parses the ContentEncAESSettings element from |pReader|. |start| is the - // starting offset of the ContentEncAESSettings payload. |size| is the - // size in bytes of the ContentEncAESSettings payload. |encryption| is - // where the parsed values will be stored. - long ParseContentEncAESSettingsEntry(long long start, long long size, - IMkvReader* pReader, - ContentEncAESSettings* aes); - - // Parses the ContentEncoding element from |pReader|. |start| is the - // starting offset of the ContentEncoding payload. |size| is the size in - // bytes of the ContentEncoding payload. Returns true on success. - long ParseContentEncodingEntry(long long start, long long size, - IMkvReader* pReader); - - // Parses the ContentEncryption element from |pReader|. |start| is the - // starting offset of the ContentEncryption payload. |size| is the size in - // bytes of the ContentEncryption payload. |encryption| is where the parsed - // values will be stored. - long ParseEncryptionEntry(long long start, long long size, - IMkvReader* pReader, ContentEncryption* encryption); - - unsigned long long encoding_order() const { return encoding_order_; } - unsigned long long encoding_scope() const { return encoding_scope_; } - unsigned long long encoding_type() const { return encoding_type_; } - - private: - // Member variables for list of ContentCompression elements. - ContentCompression** compression_entries_; - ContentCompression** compression_entries_end_; - - // Member variables for list of ContentEncryption elements. - ContentEncryption** encryption_entries_; - ContentEncryption** encryption_entries_end_; - - // ContentEncoding element names - unsigned long long encoding_order_; - unsigned long long encoding_scope_; - unsigned long long encoding_type_; - - // LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding); - ContentEncoding(const ContentEncoding&); - ContentEncoding& operator=(const ContentEncoding&); -}; - -class Track { - Track(const Track&); - Track& operator=(const Track&); - - public: - class Info; - static long Create(Segment*, const Info&, long long element_start, - long long element_size, Track*&); - - enum Type { kVideo = 1, kAudio = 2, kSubtitle = 0x11, kMetadata = 0x21 }; - - Segment* const m_pSegment; - const long long m_element_start; - const long long m_element_size; - virtual ~Track(); - - long GetType() const; - long GetNumber() const; - unsigned long long GetUid() const; - const char* GetNameAsUTF8() const; - const char* GetLanguage() const; - const char* GetCodecNameAsUTF8() const; - const char* GetCodecId() const; - const unsigned char* GetCodecPrivate(size_t&) const; - bool GetLacing() const; - unsigned long long GetDefaultDuration() const; - unsigned long long GetCodecDelay() const; - unsigned long long GetSeekPreRoll() const; - - const BlockEntry* GetEOS() const; - - struct Settings { - long long start; - long long size; - }; - - class Info { - public: - Info(); - ~Info(); - int Copy(Info&) const; - void Clear(); - long type; - long number; - unsigned long long uid; - unsigned long long defaultDuration; - unsigned long long codecDelay; - unsigned long long seekPreRoll; - char* nameAsUTF8; - char* language; - char* codecId; - char* codecNameAsUTF8; - unsigned char* codecPrivate; - size_t codecPrivateSize; - bool lacing; - Settings settings; - - private: - Info(const Info&); - Info& operator=(const Info&); - int CopyStr(char* Info::*str, Info&) const; - }; - - long GetFirst(const BlockEntry*&) const; - long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const; - virtual bool VetEntry(const BlockEntry*) const; - virtual long Seek(long long time_ns, const BlockEntry*&) const; - - const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const; - unsigned long GetContentEncodingCount() const; - - long ParseContentEncodingsEntry(long long start, long long size); - - protected: - Track(Segment*, long long element_start, long long element_size); - - Info m_info; - - class EOSBlock : public BlockEntry { - public: - EOSBlock(); - - Kind GetKind() const; - const Block* GetBlock() const; - }; - - EOSBlock m_eos; - - private: - ContentEncoding** content_encoding_entries_; - ContentEncoding** content_encoding_entries_end_; -}; - -struct PrimaryChromaticity { - PrimaryChromaticity() : x(0), y(0) {} - ~PrimaryChromaticity() {} - static bool Parse(IMkvReader* reader, long long read_pos, - long long value_size, bool is_x, - PrimaryChromaticity** chromaticity); - float x; - float y; -}; - -struct MasteringMetadata { - static const float kValueNotPresent; - - MasteringMetadata() - : r(NULL), - g(NULL), - b(NULL), - white_point(NULL), - luminance_max(kValueNotPresent), - luminance_min(kValueNotPresent) {} - ~MasteringMetadata() { - delete r; - delete g; - delete b; - delete white_point; - } - - static bool Parse(IMkvReader* reader, long long element_start, - long long element_size, - MasteringMetadata** mastering_metadata); - - PrimaryChromaticity* r; - PrimaryChromaticity* g; - PrimaryChromaticity* b; - PrimaryChromaticity* white_point; - float luminance_max; - float luminance_min; -}; - -struct Colour { - static const long long kValueNotPresent; - - // Unless otherwise noted all values assigned upon construction are the - // equivalent of unspecified/default. - Colour() - : matrix_coefficients(kValueNotPresent), - bits_per_channel(kValueNotPresent), - chroma_subsampling_horz(kValueNotPresent), - chroma_subsampling_vert(kValueNotPresent), - cb_subsampling_horz(kValueNotPresent), - cb_subsampling_vert(kValueNotPresent), - chroma_siting_horz(kValueNotPresent), - chroma_siting_vert(kValueNotPresent), - range(kValueNotPresent), - transfer_characteristics(kValueNotPresent), - primaries(kValueNotPresent), - max_cll(kValueNotPresent), - max_fall(kValueNotPresent), - mastering_metadata(NULL) {} - ~Colour() { - delete mastering_metadata; - mastering_metadata = NULL; - } - - static bool Parse(IMkvReader* reader, long long element_start, - long long element_size, Colour** colour); - - long long matrix_coefficients; - long long bits_per_channel; - long long chroma_subsampling_horz; - long long chroma_subsampling_vert; - long long cb_subsampling_horz; - long long cb_subsampling_vert; - long long chroma_siting_horz; - long long chroma_siting_vert; - long long range; - long long transfer_characteristics; - long long primaries; - long long max_cll; - long long max_fall; - - MasteringMetadata* mastering_metadata; -}; - -struct Projection { - enum ProjectionType { - kTypeNotPresent = -1, - kRectangular = 0, - kEquirectangular = 1, - kCubeMap = 2, - kMesh = 3, - }; - static const float kValueNotPresent; - Projection() - : type(kTypeNotPresent), - private_data(NULL), - private_data_length(0), - pose_yaw(kValueNotPresent), - pose_pitch(kValueNotPresent), - pose_roll(kValueNotPresent) {} - ~Projection() { delete[] private_data; } - static bool Parse(IMkvReader* reader, long long element_start, - long long element_size, Projection** projection); - - ProjectionType type; - unsigned char* private_data; - size_t private_data_length; - float pose_yaw; - float pose_pitch; - float pose_roll; -}; - -class VideoTrack : public Track { - VideoTrack(const VideoTrack&); - VideoTrack& operator=(const VideoTrack&); - - VideoTrack(Segment*, long long element_start, long long element_size); - - public: - virtual ~VideoTrack(); - static long Parse(Segment*, const Info&, long long element_start, - long long element_size, VideoTrack*&); - - long long GetWidth() const; - long long GetHeight() const; - long long GetDisplayWidth() const; - long long GetDisplayHeight() const; - long long GetDisplayUnit() const; - long long GetStereoMode() const; - double GetFrameRate() const; - - bool VetEntry(const BlockEntry*) const; - long Seek(long long time_ns, const BlockEntry*&) const; - - Colour* GetColour() const; - - Projection* GetProjection() const; - - private: - long long m_width; - long long m_height; - long long m_display_width; - long long m_display_height; - long long m_display_unit; - long long m_stereo_mode; - - double m_rate; - - Colour* m_colour; - Projection* m_projection; -}; - -class AudioTrack : public Track { - AudioTrack(const AudioTrack&); - AudioTrack& operator=(const AudioTrack&); - - AudioTrack(Segment*, long long element_start, long long element_size); - - public: - static long Parse(Segment*, const Info&, long long element_start, - long long element_size, AudioTrack*&); - - double GetSamplingRate() const; - long long GetChannels() const; - long long GetBitDepth() const; - - private: - double m_rate; - long long m_channels; - long long m_bitDepth; -}; - -class Tracks { - Tracks(const Tracks&); - Tracks& operator=(const Tracks&); - - public: - Segment* const m_pSegment; - const long long m_start; - const long long m_size; - const long long m_element_start; - const long long m_element_size; - - Tracks(Segment*, long long start, long long size, long long element_start, - long long element_size); - - ~Tracks(); - - long Parse(); - - unsigned long GetTracksCount() const; - - const Track* GetTrackByNumber(long tn) const; - const Track* GetTrackByIndex(unsigned long idx) const; - - private: - Track** m_trackEntries; - Track** m_trackEntriesEnd; - - long ParseTrackEntry(long long payload_start, long long payload_size, - long long element_start, long long element_size, - Track*&) const; -}; - -class Chapters { - Chapters(const Chapters&); - Chapters& operator=(const Chapters&); - - public: - Segment* const m_pSegment; - const long long m_start; - const long long m_size; - const long long m_element_start; - const long long m_element_size; - - Chapters(Segment*, long long payload_start, long long payload_size, - long long element_start, long long element_size); - - ~Chapters(); - - long Parse(); - - class Atom; - class Edition; - - class Display { - friend class Atom; - Display(); - Display(const Display&); - ~Display(); - Display& operator=(const Display&); - - public: - const char* GetString() const; - const char* GetLanguage() const; - const char* GetCountry() const; - - private: - void Init(); - void ShallowCopy(Display&) const; - void Clear(); - long Parse(IMkvReader*, long long pos, long long size); - - char* m_string; - char* m_language; - char* m_country; - }; - - class Atom { - friend class Edition; - Atom(); - Atom(const Atom&); - ~Atom(); - Atom& operator=(const Atom&); - - public: - unsigned long long GetUID() const; - const char* GetStringUID() const; - - long long GetStartTimecode() const; - long long GetStopTimecode() const; - - long long GetStartTime(const Chapters*) const; - long long GetStopTime(const Chapters*) const; - - int GetDisplayCount() const; - const Display* GetDisplay(int index) const; - - private: - void Init(); - void ShallowCopy(Atom&) const; - void Clear(); - long Parse(IMkvReader*, long long pos, long long size); - static long long GetTime(const Chapters*, long long timecode); - - long ParseDisplay(IMkvReader*, long long pos, long long size); - bool ExpandDisplaysArray(); - - char* m_string_uid; - unsigned long long m_uid; - long long m_start_timecode; - long long m_stop_timecode; - - Display* m_displays; - int m_displays_size; - int m_displays_count; - }; - - class Edition { - friend class Chapters; - Edition(); - Edition(const Edition&); - ~Edition(); - Edition& operator=(const Edition&); - - public: - int GetAtomCount() const; - const Atom* GetAtom(int index) const; - - private: - void Init(); - void ShallowCopy(Edition&) const; - void Clear(); - long Parse(IMkvReader*, long long pos, long long size); - - long ParseAtom(IMkvReader*, long long pos, long long size); - bool ExpandAtomsArray(); - - Atom* m_atoms; - int m_atoms_size; - int m_atoms_count; - }; - - int GetEditionCount() const; - const Edition* GetEdition(int index) const; - - private: - long ParseEdition(long long pos, long long size); - bool ExpandEditionsArray(); - - Edition* m_editions; - int m_editions_size; - int m_editions_count; -}; - -class Tags { - Tags(const Tags&); - Tags& operator=(const Tags&); - - public: - Segment* const m_pSegment; - const long long m_start; - const long long m_size; - const long long m_element_start; - const long long m_element_size; - - Tags(Segment*, long long payload_start, long long payload_size, - long long element_start, long long element_size); - - ~Tags(); - - long Parse(); - - class Tag; - class SimpleTag; - - class SimpleTag { - friend class Tag; - SimpleTag(); - SimpleTag(const SimpleTag&); - ~SimpleTag(); - SimpleTag& operator=(const SimpleTag&); - - public: - const char* GetTagName() const; - const char* GetTagString() const; - - private: - void Init(); - void ShallowCopy(SimpleTag&) const; - void Clear(); - long Parse(IMkvReader*, long long pos, long long size); - - char* m_tag_name; - char* m_tag_string; - }; - - class Tag { - friend class Tags; - Tag(); - Tag(const Tag&); - ~Tag(); - Tag& operator=(const Tag&); - - public: - int GetSimpleTagCount() const; - const SimpleTag* GetSimpleTag(int index) const; - - private: - void Init(); - void ShallowCopy(Tag&) const; - void Clear(); - long Parse(IMkvReader*, long long pos, long long size); - - long ParseSimpleTag(IMkvReader*, long long pos, long long size); - bool ExpandSimpleTagsArray(); - - SimpleTag* m_simple_tags; - int m_simple_tags_size; - int m_simple_tags_count; - }; - - int GetTagCount() const; - const Tag* GetTag(int index) const; - - private: - long ParseTag(long long pos, long long size); - bool ExpandTagsArray(); - - Tag* m_tags; - int m_tags_size; - int m_tags_count; -}; - -class SegmentInfo { - SegmentInfo(const SegmentInfo&); - SegmentInfo& operator=(const SegmentInfo&); - - public: - Segment* const m_pSegment; - const long long m_start; - const long long m_size; - const long long m_element_start; - const long long m_element_size; - - SegmentInfo(Segment*, long long start, long long size, - long long element_start, long long element_size); - - ~SegmentInfo(); - - long Parse(); - - long long GetTimeCodeScale() const; - long long GetDuration() const; // scaled - const char* GetMuxingAppAsUTF8() const; - const char* GetWritingAppAsUTF8() const; - const char* GetTitleAsUTF8() const; - - private: - long long m_timecodeScale; - double m_duration; - char* m_pMuxingAppAsUTF8; - char* m_pWritingAppAsUTF8; - char* m_pTitleAsUTF8; -}; - -class SeekHead { - SeekHead(const SeekHead&); - SeekHead& operator=(const SeekHead&); - - public: - Segment* const m_pSegment; - const long long m_start; - const long long m_size; - const long long m_element_start; - const long long m_element_size; - - SeekHead(Segment*, long long start, long long size, long long element_start, - long long element_size); - - ~SeekHead(); - - long Parse(); - - struct Entry { - Entry(); - - // the SeekHead entry payload - long long id; - long long pos; - - // absolute pos of SeekEntry ID - long long element_start; - - // SeekEntry ID size + size size + payload - long long element_size; - }; - - int GetCount() const; - const Entry* GetEntry(int idx) const; - - struct VoidElement { - // absolute pos of Void ID - long long element_start; - - // ID size + size size + payload size - long long element_size; - }; - - int GetVoidElementCount() const; - const VoidElement* GetVoidElement(int idx) const; - - private: - Entry* m_entries; - int m_entry_count; - - VoidElement* m_void_elements; - int m_void_element_count; - - static bool ParseEntry(IMkvReader*, - long long pos, // payload - long long size, Entry*); -}; - -class Cues; -class CuePoint { - friend class Cues; - - CuePoint(long, long long); - ~CuePoint(); - - CuePoint(const CuePoint&); - CuePoint& operator=(const CuePoint&); - - public: - long long m_element_start; - long long m_element_size; - - bool Load(IMkvReader*); - - long long GetTimeCode() const; // absolute but unscaled - long long GetTime(const Segment*) const; // absolute and scaled (ns units) - - struct TrackPosition { - long long m_track; - long long m_pos; // of cluster - long long m_block; - // codec_state //defaults to 0 - // reference = clusters containing req'd referenced blocks - // reftime = timecode of the referenced block - - bool Parse(IMkvReader*, long long, long long); - }; - - const TrackPosition* Find(const Track*) const; - - private: - const long m_index; - long long m_timecode; - TrackPosition* m_track_positions; - size_t m_track_positions_count; -}; - -class Cues { - friend class Segment; - - Cues(Segment*, long long start, long long size, long long element_start, - long long element_size); - ~Cues(); - - Cues(const Cues&); - Cues& operator=(const Cues&); - - public: - Segment* const m_pSegment; - const long long m_start; - const long long m_size; - const long long m_element_start; - const long long m_element_size; - - bool Find( // lower bound of time_ns - long long time_ns, const Track*, const CuePoint*&, - const CuePoint::TrackPosition*&) const; - - const CuePoint* GetFirst() const; - const CuePoint* GetLast() const; - const CuePoint* GetNext(const CuePoint*) const; - - const BlockEntry* GetBlock(const CuePoint*, - const CuePoint::TrackPosition*) const; - - bool LoadCuePoint() const; - long GetCount() const; // loaded only - // long GetTotal() const; //loaded + preloaded - bool DoneParsing() const; - - private: - bool Init() const; - bool PreloadCuePoint(long&, long long) const; - - mutable CuePoint** m_cue_points; - mutable long m_count; - mutable long m_preload_count; - mutable long long m_pos; -}; - -class Cluster { - friend class Segment; - - Cluster(const Cluster&); - Cluster& operator=(const Cluster&); - - public: - Segment* const m_pSegment; - - public: - static Cluster* Create(Segment*, - long index, // index in segment - long long off); // offset relative to segment - // long long element_size); - - Cluster(); // EndOfStream - ~Cluster(); - - bool EOS() const; - - long long GetTimeCode() const; // absolute, but not scaled - long long GetTime() const; // absolute, and scaled (nanosecond units) - long long GetFirstTime() const; // time (ns) of first (earliest) block - long long GetLastTime() const; // time (ns) of last (latest) block - - long GetFirst(const BlockEntry*&) const; - long GetLast(const BlockEntry*&) const; - long GetNext(const BlockEntry* curr, const BlockEntry*& next) const; - - const BlockEntry* GetEntry(const Track*, long long ns = -1) const; - const BlockEntry* GetEntry(const CuePoint&, - const CuePoint::TrackPosition&) const; - // const BlockEntry* GetMaxKey(const VideoTrack*) const; - - // static bool HasBlockEntries(const Segment*, long long); - - static long HasBlockEntries(const Segment*, long long idoff, long long& pos, - long& size); - - long GetEntryCount() const; - - long Load(long long& pos, long& size) const; - - long Parse(long long& pos, long& size) const; - long GetEntry(long index, const mkvparser::BlockEntry*&) const; - - protected: - Cluster(Segment*, long index, long long element_start); - // long long element_size); - - public: - const long long m_element_start; - long long GetPosition() const; // offset relative to segment - - long GetIndex() const; - long long GetElementSize() const; - // long long GetPayloadSize() const; - - // long long Unparsed() const; - - private: - long m_index; - mutable long long m_pos; - // mutable long long m_size; - mutable long long m_element_size; - mutable long long m_timecode; - mutable BlockEntry** m_entries; - mutable long m_entries_size; - mutable long m_entries_count; - - long ParseSimpleBlock(long long, long long&, long&); - long ParseBlockGroup(long long, long long&, long&); - - long CreateBlock(long long id, long long pos, long long size, - long long discard_padding); - long CreateBlockGroup(long long start_offset, long long size, - long long discard_padding); - long CreateSimpleBlock(long long, long long); -}; - -class Segment { - friend class Cues; - friend class Track; - friend class VideoTrack; - - Segment(const Segment&); - Segment& operator=(const Segment&); - - private: - Segment(IMkvReader*, long long elem_start, - // long long elem_size, - long long pos, long long size); - - public: - IMkvReader* const m_pReader; - const long long m_element_start; - // const long long m_element_size; - const long long m_start; // posn of segment payload - const long long m_size; // size of segment payload - Cluster m_eos; // TODO: make private? - - static long long CreateInstance(IMkvReader*, long long, Segment*&); - ~Segment(); - - long Load(); // loads headers and all clusters - - // for incremental loading - // long long Unparsed() const; - bool DoneParsing() const; - long long ParseHeaders(); // stops when first cluster is found - // long FindNextCluster(long long& pos, long& size) const; - long LoadCluster(long long& pos, long& size); // load one cluster - long LoadCluster(); - - long ParseNext(const Cluster* pCurr, const Cluster*& pNext, long long& pos, - long& size); - - const SeekHead* GetSeekHead() const; - const Tracks* GetTracks() const; - const SegmentInfo* GetInfo() const; - const Cues* GetCues() const; - const Chapters* GetChapters() const; - const Tags* GetTags() const; - - long long GetDuration() const; - - unsigned long GetCount() const; - const Cluster* GetFirst() const; - const Cluster* GetLast() const; - const Cluster* GetNext(const Cluster*); - - const Cluster* FindCluster(long long time_nanoseconds) const; - // const BlockEntry* Seek(long long time_nanoseconds, const Track*) const; - - const Cluster* FindOrPreloadCluster(long long pos); - - long ParseCues(long long cues_off, // offset relative to start of segment - long long& parse_pos, long& parse_len); - - private: - long long m_pos; // absolute file posn; what has been consumed so far - Cluster* m_pUnknownSize; - - SeekHead* m_pSeekHead; - SegmentInfo* m_pInfo; - Tracks* m_pTracks; - Cues* m_pCues; - Chapters* m_pChapters; - Tags* m_pTags; - Cluster** m_clusters; - long m_clusterCount; // number of entries for which m_index >= 0 - long m_clusterPreloadCount; // number of entries for which m_index < 0 - long m_clusterSize; // array size - - long DoLoadCluster(long long&, long&); - long DoLoadClusterUnknownSize(long long&, long&); - long DoParseNext(const Cluster*&, long long&, long&); - - bool AppendCluster(Cluster*); - bool PreloadCluster(Cluster*, ptrdiff_t); - - // void ParseSeekHead(long long pos, long long size); - // void ParseSeekEntry(long long pos, long long size); - // void ParseCues(long long); - - const BlockEntry* GetBlock(const CuePoint&, const CuePoint::TrackPosition&); -}; - -} // namespace mkvparser - -inline long mkvparser::Segment::LoadCluster() { - long long pos; - long size; - - return LoadCluster(pos, size); -} - -#endif // MKVPARSER_MKVPARSER_H_ diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 558087e69..36cc6e2b9 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -57,7 +57,6 @@ #include "modes/profile_world.hpp" #include "modes/world.hpp" #include "physics/physics.hpp" -#include "recorder/openglrecorder.h" #include "scriptengine/property_animator.hpp" #include "states_screens/dialogs/confirm_resolution_dialog.hpp" #include "states_screens/state_manager.hpp" @@ -67,8 +66,9 @@ #include "utils/profiler.hpp" #include "utils/vs.hpp" -#include #include +#include +#include /* Build-time check that the Irrlicht we're building against works for us. * Should help prevent distros building against an incompatible library. diff --git a/src/recorder/capture_library.cpp b/src/recorder/capture_library.cpp deleted file mode 100644 index 5db3b0dcd..000000000 --- a/src/recorder/capture_library.cpp +++ /dev/null @@ -1,296 +0,0 @@ -#ifdef ENABLE_RECORDER -#include "capture_library.hpp" - -#include "mjpeg_writer.hpp" -#include "mkv_writer.hpp" -#include "recorder_private.hpp" -#include "pulseaudio_recorder.hpp" -#include "vpx_encoder.hpp" -#include "wasapi_recorder.hpp" - -const uint32_t E_GL_PIXEL_PACK_BUFFER = 0x88EB; -const uint32_t E_GL_STREAM_READ = 0x88E1; -const uint32_t E_GL_READ_ONLY = 0x88B8; -const uint32_t E_GL_RGBA = 0x1908; -const uint32_t E_GL_UNSIGNED_BYTE = 0x1401; - -// ---------------------------------------------------------------------------- -CaptureLibrary::CaptureLibrary(RecorderConfig* rc) -{ - m_recorder_cfg = rc; - m_destroy.store(false); - m_sound_stop.store(true); - m_display_progress.store(false); - m_compress_handle = tjInitCompress(); - m_decompress_handle = tjInitDecompress(); - if (m_recorder_cfg->m_triple_buffering > 0) - { - ogrGenBuffers(3, m_pbo); - for (int i = 0; i < 3; i++) - { - ogrBindBuffer(E_GL_PIXEL_PACK_BUFFER, m_pbo[i]); - ogrBufferData(E_GL_PIXEL_PACK_BUFFER, m_recorder_cfg->m_width * - m_recorder_cfg->m_height * 4, NULL, E_GL_STREAM_READ); - } - ogrBindBuffer(E_GL_PIXEL_PACK_BUFFER, 0); - } - m_capture_thread = std::thread(CaptureLibrary::captureConversion, this); -} // CaptureLibrary - -// ---------------------------------------------------------------------------- -CaptureLibrary::~CaptureLibrary() -{ - m_destroy.store(true); - addFrameBufferImage(NULL, ogrCapturing() > 0 ? -1 : 0); - m_capture_thread.join(); - tjDestroy(m_compress_handle); - tjDestroy(m_decompress_handle); - if (m_recorder_cfg->m_triple_buffering > 0) - { - ogrDeleteBuffers(3, m_pbo); - } -} // ~CaptureLibrary - -// ---------------------------------------------------------------------------- -void CaptureLibrary::reset() -{ - runCallback(OGR_CBT_START_RECORDING, NULL); - m_pbo_use = 0; - m_accumulated_time = 0.; - assert(m_sound_stop.load() && ogrCapturing() == 0); - if (m_recorder_cfg->m_record_audio > 0) - { - m_sound_stop.store(false); - m_audio_enc_thread = std::thread(Recorder::audioRecorder, this); - } - setCapturing(true); - switch (m_recorder_cfg->m_video_format) - { - case OGR_VF_VP8: - case OGR_VF_VP9: - m_video_enc_thread = std::thread(Recorder::vpxEncoder, this); - break; - case OGR_VF_MJPEG: - m_video_enc_thread = std::thread(Recorder::mjpegWriter, this); - break; - case OGR_VF_H264: - break; - default: - break; - } -} // reset - -// ---------------------------------------------------------------------------- -int CaptureLibrary::bmpToJPG(uint8_t* raw, unsigned width, unsigned height, - uint8_t** jpeg_buffer, unsigned long* jpeg_size) -{ - int ret = 0; -#ifdef TJFLAG_FASTDCT - ret = tjCompress2(m_compress_handle, raw, width, 0, height, TJPF_RGBX, - jpeg_buffer, jpeg_size, TJSAMP_420, - m_recorder_cfg->m_record_jpg_quality, TJFLAG_FASTDCT); -#else - ret = tjCompress2(m_compress_handle, raw, width, 0, height, TJPF_RGBX, - jpeg_buffer, jpeg_size, TJSAMP_420, - m_recorder_cfg->m_record_jpg_quality, 0); -#endif - if (ret != 0) - { - char* err = tjGetErrorStr(); - printf("Jpeg encode error: %s.", err); - return ret; - } - return ret; -} // bmpToJPG - -// ---------------------------------------------------------------------------- -int CaptureLibrary::yuvConversion(uint8_t* jpeg_buffer, unsigned jpeg_size, - uint8_t** yuv_buffer, unsigned* yuv_size) -{ - int width, height; - TJSAMP subsample; - int ret = 0; - ret = tjDecompressHeader2(m_decompress_handle, jpeg_buffer, jpeg_size, - &width, &height, (int*)&subsample); - if (ret != 0) - { - char* err = tjGetErrorStr(); - printf("Jpeg decode error: %s.", err); - return ret; - } - *yuv_size = tjBufSizeYUV(width, height, subsample); - *yuv_buffer = new uint8_t[*yuv_size]; - ret = tjDecompressToYUV(m_decompress_handle, jpeg_buffer, jpeg_size, - *yuv_buffer, 0); - if (ret != 0) - { - char* err = tjGetErrorStr(); - printf("YUV conversion error: %s.", err); - return ret; - } - return ret; -} // yuvConversion - -// ---------------------------------------------------------------------------- -int CaptureLibrary::getFrameCount(double rate) -{ - const double frame_rate = 1. / double(m_recorder_cfg->m_record_fps); - m_accumulated_time += rate; - if (m_accumulated_time < frame_rate) - { - return 0; - } - int frame_count = 0; - while (m_accumulated_time >= frame_rate) - { - frame_count++; - m_accumulated_time = m_accumulated_time - frame_rate; - } - return frame_count; -} // getFrameCount - -// ---------------------------------------------------------------------------- -void CaptureLibrary::capture() -{ - int pbo_read = -1; - if (m_pbo_use > 3 && m_pbo_use % 3 == 0) - m_pbo_use = 3; - auto rate = std::chrono::high_resolution_clock::now() - m_framerate_timer; - m_framerate_timer = std::chrono::high_resolution_clock::now(); - const unsigned width = m_recorder_cfg->m_width; - const unsigned height = m_recorder_cfg->m_height; - const bool use_pbo = m_recorder_cfg->m_triple_buffering > 0; - if (m_pbo_use >= 3) - { - int frame_count = getFrameCount(std::chrono::duration_cast - >(rate).count()); - if (frame_count != 0) - { - const unsigned size = width * height * 4; - uint8_t* fbi = new uint8_t[size]; - if (use_pbo) - { - pbo_read = m_pbo_use % 3; - ogrBindBuffer(E_GL_PIXEL_PACK_BUFFER, m_pbo[pbo_read]); - void* ptr = ogrMapBuffer(E_GL_PIXEL_PACK_BUFFER, - E_GL_READ_ONLY); - memcpy(fbi, ptr, size); - ogrUnmapBuffer(E_GL_PIXEL_PACK_BUFFER); - } - else - { - ogrReadPixels(0, 0, width, height, E_GL_RGBA, - E_GL_UNSIGNED_BYTE, fbi); - } - addFrameBufferImage(fbi, frame_count); - } - } - int pbo_use = m_pbo_use++ % 3; - if (!use_pbo) - return; - - assert(pbo_read == -1 || pbo_use == pbo_read); - ogrBindBuffer(E_GL_PIXEL_PACK_BUFFER, m_pbo[pbo_use]); - ogrReadPixels(0, 0, width, height, E_GL_RGBA, E_GL_UNSIGNED_BYTE, NULL); - ogrBindBuffer(E_GL_PIXEL_PACK_BUFFER, 0); -} // capture - -// ---------------------------------------------------------------------------- -void CaptureLibrary::captureConversion(CaptureLibrary* cl) -{ - setThreadName("captureConvert"); - while (true) - { - std::unique_lock ul(cl->m_fbi_list_mutex); - cl->m_fbi_list_ready.wait(ul, [&cl] - { return !cl->m_fbi_list.empty(); }); - auto& p = cl->m_fbi_list.front(); - uint8_t* fbi = p.first; - int frame_count = p.second; - if (frame_count == -1) - { - cl->m_fbi_list.clear(); - ul.unlock(); - if (cl->m_recorder_cfg->m_record_audio > 0) - { - cl->m_sound_stop.store(true); - cl->m_audio_enc_thread.join(); - } - std::unique_lock ulj(cl->m_jpg_list_mutex); - if (!cl->m_destroy.load() && cl->m_jpg_list.size() > 100) - { - runCallback(OGR_CBT_WAIT_RECORDING, NULL); - } - cl->m_jpg_list.emplace_back((uint8_t*)NULL, 0, 0); - cl->m_jpg_list_ready.notify_one(); - ulj.unlock(); - cl->m_display_progress.store(!cl->m_destroy.load()); - cl->m_video_enc_thread.join(); - cl->m_display_progress.store(false); - std::string f = Recorder::writeMKV(getSavedName() + ".video", - getSavedName() + ".audio"); - if (cl->m_destroy.load()) - { - return; - } - if (f.empty()) - { - runCallback(OGR_CBT_ERROR_RECORDING, NULL); - } - else - { - runCallback(OGR_CBT_SAVED_RECORDING, f.c_str()); - } - setCapturing(false); - continue; - } - else if (fbi == NULL) - { - cl->m_fbi_list.clear(); - ul.unlock(); - return; - } - const bool too_slow = cl->m_fbi_list.size() > 50; - if (too_slow) - { - if (!cl->m_destroy.load()) - runCallback(OGR_CBT_SLOW_RECORDING, NULL); - delete [] fbi; - cl->m_fbi_list.pop_front(); - for (auto& p : cl->m_fbi_list) - delete [] p.first; - cl->m_fbi_list.clear(); - ul.unlock(); - continue; - } - cl->m_fbi_list.pop_front(); - ul.unlock(); - - uint8_t* orig_fbi = fbi; - const unsigned width = cl->m_recorder_cfg->m_width; - const unsigned height = cl->m_recorder_cfg->m_height; - const int pitch = width * 4; - uint8_t* p2 = fbi + (height - 1) * pitch; - uint8_t* tmp_buf = new uint8_t[pitch]; - for (unsigned i = 0; i < height; i += 2) - { - memcpy(tmp_buf, fbi, pitch); - memcpy(fbi, p2, pitch); - memcpy(p2, tmp_buf, pitch); - fbi += pitch; - p2 -= pitch; - } - delete [] tmp_buf; - - uint8_t* jpg = NULL; - unsigned long jpg_size = 0; - cl->bmpToJPG(orig_fbi, width, height, &jpg, &jpg_size); - delete[] orig_fbi; - - std::lock_guard lg(cl->m_jpg_list_mutex); - cl->m_jpg_list.emplace_back(jpg, jpg_size, frame_count); - cl->m_jpg_list_ready.notify_one(); - } -} // captureConversion - -#endif diff --git a/src/recorder/capture_library.hpp b/src/recorder/capture_library.hpp deleted file mode 100644 index 6892b794c..000000000 --- a/src/recorder/capture_library.hpp +++ /dev/null @@ -1,118 +0,0 @@ -#ifdef ENABLE_RECORDER -#ifndef HEADER_CAPTURE_LIBRARY_HPP -#define HEADER_CAPTURE_LIBRARY_HPP - -#if defined(_MSC_VER) && _MSC_VER < 1700 - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef __int32 int32_t; - typedef unsigned __int32 uint32_t; - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; -#else - #include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -struct AudioEncoderData -{ - enum AudioType { AT_FLOAT, AT_PCM }; - std::mutex* m_mutex; - std::condition_variable* m_cv; - std::list* m_buf_list; - uint32_t m_sample_rate; - uint32_t m_channels; - uint32_t m_audio_bitrate; - AudioType m_audio_type; -}; - -struct RecorderConfig; -typedef std::list > JPGList; - -class CaptureLibrary -{ -private: - RecorderConfig* m_recorder_cfg; - - std::atomic_bool m_destroy, m_display_progress, m_sound_stop; - - tjhandle m_compress_handle, m_decompress_handle; - - JPGList m_jpg_list; - std::mutex m_jpg_list_mutex; - std::condition_variable m_jpg_list_ready; - - std::list > m_fbi_list; - std::mutex m_fbi_list_mutex; - std::condition_variable m_fbi_list_ready; - - std::thread m_capture_thread, m_audio_enc_thread, m_video_enc_thread; - - uint32_t m_pbo[3]; - - unsigned m_pbo_use; - - std::chrono::high_resolution_clock::time_point m_framerate_timer; - - double m_accumulated_time; - - // ------------------------------------------------------------------------ - int getFrameCount(double rate); - // ------------------------------------------------------------------------ - void addFrameBufferImage(uint8_t* fbi, int frame_count) - { - std::lock_guard lock(m_fbi_list_mutex); - m_fbi_list.emplace_back(fbi, frame_count); - m_fbi_list_ready.notify_one(); - } - -public: - // ------------------------------------------------------------------------ - CaptureLibrary(RecorderConfig* rc); - // ------------------------------------------------------------------------ - ~CaptureLibrary(); - // ------------------------------------------------------------------------ - void capture(); - // ------------------------------------------------------------------------ - void stopCapture() { addFrameBufferImage(NULL, -1); } - // ------------------------------------------------------------------------ - void reset(); - // ------------------------------------------------------------------------ - int bmpToJPG(uint8_t* raw, unsigned width, unsigned height, - uint8_t** jpeg_buffer, unsigned long* jpeg_size); - // ------------------------------------------------------------------------ - int yuvConversion(uint8_t* jpeg_buffer, unsigned jpeg_size, - uint8_t** yuv_buffer, unsigned* yuv_size); - // ------------------------------------------------------------------------ - JPGList* getJPGList() { return &m_jpg_list; } - // ------------------------------------------------------------------------ - std::mutex* getJPGListMutex() { return &m_jpg_list_mutex; } - // ------------------------------------------------------------------------ - std::condition_variable* getJPGListCV() { return &m_jpg_list_ready; } - // ------------------------------------------------------------------------ - bool displayingProgress() const { return m_display_progress.load(); } - // ------------------------------------------------------------------------ - bool getSoundStop() const { return m_sound_stop.load(); } - // ------------------------------------------------------------------------ - bool getDestroy() const { return m_destroy.load(); } - // ------------------------------------------------------------------------ - const RecorderConfig& getRecorderConfig() const { return *m_recorder_cfg; } - // ------------------------------------------------------------------------ - static void captureConversion(CaptureLibrary* cl); - -}; - -#endif - -#endif diff --git a/src/recorder/mjpeg_writer.cpp b/src/recorder/mjpeg_writer.cpp deleted file mode 100644 index 8de7dee5a..000000000 --- a/src/recorder/mjpeg_writer.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef ENABLE_RECORDER - -#include "capture_library.hpp" -#include "recorder_private.hpp" - -namespace Recorder -{ - // ------------------------------------------------------------------------ - void mjpegWriter(CaptureLibrary* cl) - { - setThreadName("mjpegWriter"); - FILE* mjpeg_writer = fopen((getSavedName() + ".video").c_str(), "wb"); - if (mjpeg_writer == NULL) - { - printf("Failed to open file for writing mjpeg.\n"); - return; - } - int64_t frames_encoded = 0; - while (true) - { - std::unique_lock ul(*cl->getJPGListMutex()); - cl->getJPGListCV()->wait(ul, [&cl] - { return !cl->getJPGList()->empty(); }); - auto& p = cl->getJPGList()->front(); - uint8_t* jpg = std::get<0>(p); - uint32_t jpg_size = std::get<1>(p); - int frame_count = std::get<2>(p); - if (jpg == NULL) - { - cl->getJPGList()->clear(); - ul.unlock(); - break; - } - cl->getJPGList()->pop_front(); - ul.unlock(); - while (frame_count != 0) - { - fwrite(&jpg_size, 1, sizeof(uint32_t), mjpeg_writer); - fwrite(&frames_encoded, 1, sizeof(int64_t), mjpeg_writer); - fwrite(&jpg_size, 1, sizeof(uint32_t), mjpeg_writer); - fwrite(jpg, 1, jpg_size, mjpeg_writer); - frame_count--; - frames_encoded++; - } - tjFree(jpg); - } - fclose(mjpeg_writer); - } // mjpegWriter -}; -#endif diff --git a/src/recorder/mjpeg_writer.hpp b/src/recorder/mjpeg_writer.hpp deleted file mode 100644 index c7f261543..000000000 --- a/src/recorder/mjpeg_writer.hpp +++ /dev/null @@ -1,33 +0,0 @@ - -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef ENABLE_RECORDER - -#ifndef HEADER_MJPEG_WRITER_HPP -#define HEADER_MJPEG_WRITER_HPP - -class CaptureLibrary; - -namespace Recorder -{ - void mjpegWriter(CaptureLibrary* cl); -}; - -#endif - -#endif diff --git a/src/recorder/mkv_writer.cpp b/src/recorder/mkv_writer.cpp deleted file mode 100644 index 9940c3a41..000000000 --- a/src/recorder/mkv_writer.cpp +++ /dev/null @@ -1,214 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef ENABLE_RECORDER - -#include "recorder_private.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace Recorder -{ - // ------------------------------------------------------------------------ - std::string writeMKV(const std::string& video, const std::string& audio) - { - std::string no_ext = video.substr(0, video.find_last_of(".")); - VideoFormat vf = getConfig()->m_video_format; - std::string file_name = no_ext + - (vf == OGR_VF_VP8 || vf == OGR_VF_VP9 ? ".webm" : ".mkv"); - mkvmuxer::MkvWriter writer; - if (!writer.Open(file_name.c_str())) - { - printf("Error while opening output file.\n"); - return ""; - } - mkvmuxer::Segment muxer_segment; - if (!muxer_segment.Init(&writer)) - { - printf("Could not initialize muxer segment.\n");; - return ""; - } - std::list audio_frames; - uint8_t* buf = (uint8_t*)malloc(1024 * 1024); - FILE* input = NULL; - struct stat st; - int result = stat(audio.c_str(), &st); - if (result == 0) - { - input = fopen(audio.c_str(), "rb"); - uint32_t sample_rate, channels; - fread(&sample_rate, 1, sizeof(uint32_t), input); - fread(&channels, 1, sizeof(uint32_t), input); - uint64_t aud_track = muxer_segment.AddAudioTrack(sample_rate, channels, - 0); - if (!aud_track) - { - printf("Could not add audio track.\n"); - return ""; - } - mkvmuxer::AudioTrack* const at = static_cast - (muxer_segment.GetTrackByNumber(aud_track)); - if (!at) - { - printf("Could not get audio track.\n"); - return ""; - } - uint32_t codec_private_size; - fread(&codec_private_size, 1, sizeof(uint32_t), input); - fread(buf, 1, codec_private_size, input); - if (!at->SetCodecPrivate(buf, codec_private_size)) - { - printf("Could not add audio private data.\n"); - return ""; - } - while (fread(buf, 1, 12, input) == 12) - { - uint32_t frame_size; - int64_t timestamp; - memcpy(&frame_size, buf, sizeof(uint32_t)); - memcpy(×tamp, buf + sizeof(uint32_t), sizeof(int64_t)); - fread(buf, 1, frame_size, input); - mkvmuxer::Frame* audio_frame = new mkvmuxer::Frame(); - if (!audio_frame->Init(buf, frame_size)) - { - printf("Failed to construct a frame.\n"); - return ""; - } - audio_frame->set_track_number(aud_track); - audio_frame->set_timestamp(timestamp); - audio_frame->set_is_key(true); - audio_frames.push_back(audio_frame); - } - fclose(input); - if (remove(audio.c_str()) != 0) - { - printf("Failed to remove audio data file\n"); - } - } - uint64_t vid_track = muxer_segment.AddVideoTrack(getConfig()->m_width, - getConfig()->m_height, 0); - if (!vid_track) - { - printf("Could not add video track.\n"); - return ""; - } - mkvmuxer::VideoTrack* const vt = static_cast( - muxer_segment.GetTrackByNumber(vid_track)); - if (!vt) - { - printf("Could not get video track.\n"); - return ""; - } - vt->set_frame_rate(getConfig()->m_record_fps); - switch (vf) - { - case OGR_VF_VP8: - vt->set_codec_id("V_VP8"); - break; - case OGR_VF_VP9: - vt->set_codec_id("V_VP9"); - break; - case OGR_VF_MJPEG: - vt->set_codec_id("V_MJPEG"); - break; - case OGR_VF_H264: - vt->set_codec_id("V_MPEG4/ISO/AVC"); - break; - default: - break; - } - input = fopen(video.c_str(), "rb"); - while (fread(buf, 1, 16, input) == 16) - { - uint32_t frame_size, flag; - int64_t timestamp; - memcpy(&frame_size, buf, sizeof(uint32_t)); - memcpy(×tamp, buf + sizeof(uint32_t), sizeof(int64_t)); - memcpy(&flag, buf + sizeof(uint32_t) + sizeof(int64_t), - sizeof(uint32_t)); - timestamp *= 1000000000ll / getConfig()->m_record_fps; - fread(buf, 1, frame_size, input); - mkvmuxer::Frame muxer_frame; - if (!muxer_frame.Init(buf, frame_size)) - { - printf("Failed to construct a frame.\n"); - return ""; - } - muxer_frame.set_track_number(vid_track); - muxer_frame.set_timestamp(timestamp); - if (vf == OGR_VF_VP8 || vf == OGR_VF_VP9) - { - muxer_frame.set_is_key((flag & VPX_FRAME_IS_KEY) != 0); - } - else - { - muxer_frame.set_is_key(true); - } - mkvmuxer::Frame* cur_aud_frame = - audio_frames.empty() ? NULL : audio_frames.front(); - if (cur_aud_frame != NULL) - { - while (cur_aud_frame->timestamp() < (uint64_t)timestamp) - { - if (!muxer_segment.AddGenericFrame(cur_aud_frame)) - { - printf("Could not add audio frame.\n"); - return ""; - } - delete cur_aud_frame; - audio_frames.pop_front(); - if (audio_frames.empty()) - { - cur_aud_frame = NULL; - break; - } - cur_aud_frame = audio_frames.front(); - } - } - if (!muxer_segment.AddGenericFrame(&muxer_frame)) - { - printf("Could not add video frame.\n"); - return ""; - } - } - free(buf); - fclose(input); - for (mkvmuxer::Frame* aud_frame : audio_frames) - { - delete aud_frame; - } - if (remove(video.c_str()) != 0) - { - printf("Failed to remove video data file\n"); - } - if (!muxer_segment.Finalize()) - { - printf("Finalization of segment failed.\n"); - return ""; - } - writer.Close(); - return file_name; - } // writeMKV -}; - -#endif diff --git a/src/recorder/mkv_writer.hpp b/src/recorder/mkv_writer.hpp deleted file mode 100644 index 7a3c2b607..000000000 --- a/src/recorder/mkv_writer.hpp +++ /dev/null @@ -1,32 +0,0 @@ - -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef ENABLE_RECORDER - -#ifndef HEADER_MKV_WRITER_HPP -#define HEADER_MKV_WRITER_HPP -#include - -namespace Recorder -{ - std::string writeMKV(const std::string& video, const std::string& audio); -}; - -#endif - -#endif diff --git a/src/recorder/openglrecorder.h b/src/recorder/openglrecorder.h deleted file mode 100644 index 884d48574..000000000 --- a/src/recorder/openglrecorder.h +++ /dev/null @@ -1,246 +0,0 @@ -#ifdef ENABLE_RECORDER -#ifndef HEADER_OPENGLRECORDER_H -#define HEADER_OPENGLRECORDER_H - -#include - -/** - * \mainpage libopenglrecorder - * - * libopenglrecorder is a library allowing (optional) async readback opengl - * frame buffer with audio recording. It will do video and audio encoding - * together. The user of this library has to setup opengl context himself - * and load suitable callback. All function here should be called by the same - * thread which created the opengl context. - */ - -/** - * List of audio encoder supported by libopenglrecorder, if you want to record - * without sound, just set m_record_audio in \ref RecorderConfig to 0 and use - * any encoder below. - */ -enum AudioFormat -{ - /** - * Vorbis encoder by libvorbisenc. - */ - OGR_AF_VORBIS = 0, - /** - * Total numbers of audio encoder. - */ - OGR_AF_COUNT -}; - -/** - * List of video encoder supported by libopenglrecorder - */ -enum VideoFormat -{ - /** - * VP8 encoder by libvpx. - */ - OGR_VF_VP8 = 0, - /** - * VP9 encoder by libvpx. Notice: this is very slow. - */ - OGR_VF_VP9, - /** - * MJPEG encoder, it's provided by turbojpeg and will always present. - */ - OGR_VF_MJPEG, - /** - * H264 encoder by openh264. - */ - OGR_VF_H264, - /** - * Total numbers of video encoder. - */ - OGR_VF_COUNT -}; - -/** - * Callback which takes a string pointer to work with. - */ -typedef void(*StringCallback)(const char* s, void* user_data); -/** - * Callback which takes a int to work with. - */ -typedef void(*IntCallback)(const int i, void* user_data); -/** - * Callback which takes nothing (void) to work with. - */ -typedef void(*GeneralCallback)(void* user_data); - -/** - * List of callbacks currently using. - */ -enum CallBackType -{ - /** - * A \ref GeneralCallback which notify the starting of recording. - */ - OGR_CBT_START_RECORDING = 0, - /** - * A \ref StringCallback which notify the saved filename of recorded file. - */ - OGR_CBT_SAVED_RECORDING, - /** - * A \ref GeneralCallback which notify error when recording. - */ - OGR_CBT_ERROR_RECORDING, - /** - * A \ref IntCallback which the tells the progress percentage for video - * encoding after the issue of \ref ogrStopCapture. - */ - OGR_CBT_PROGRESS_RECORDING, - /** - * A \ref GeneralCallback which notify user if there is still video - * encoding happening after the issue of \ref ogrStopCapture. - */ - OGR_CBT_WAIT_RECORDING, - /** - * A \ref GeneralCallback which notify user if the coversion to jpeg - * from opengl frame buffer image is too slow, so libopenglrecorder will - * drop frames. - */ - OGR_CBT_SLOW_RECORDING, - /** - * Total callback numbers. - */ - OGR_CBT_COUNT -}; - -/** - * Settings for libopenglrecorder - */ -struct RecorderConfig -{ - /** - * 1 if triple buffering is used when capture the opengl frame buffer. - * It will create 3 pixel buffer objects for async reading, recommend on. - * 0 otherwise. - */ - unsigned int m_triple_buffering; - /** - * 1 if audio is recorded together, it will use wasapi in windows, - * pulseaudio in linux. 0 means no audio will be recorded. - */ - unsigned int m_record_audio; - /** - * Width of the capture, it will be floored down to the closest integer divisble - * by 8 if needed. - */ - unsigned int m_width; - /** - * Height of the capture, it will be floored down to the closest integer divisble - * by 2 if needed. - */ - unsigned int m_height; - /** - * Encoder for video, see \ref VideoFormat. - */ - VideoFormat m_video_format; - /** - * Encoder for video, see \ref AudioFormat. - */ - AudioFormat m_audio_format; - /** - * Bitrate for audio encoding. - */ - unsigned int m_video_bitrate; - /** - * Bitrate for video encoding. - */ - unsigned int m_audio_bitrate; - /** - * Framerate for the video, 30 is recommended. - */ - unsigned int m_record_fps; - /** - * Jpeg quality for the captured image, from 0 to 100. - */ - unsigned int m_record_jpg_quality; -}; - -/* List of opengl function used by libopenglrecorder: */ -typedef void(*ogrFucReadPixels)(int, int, int, int, unsigned int, unsigned int, - void*); -typedef void(*ogrFucGenBuffers)(int, unsigned int*); -typedef void(*ogrFucBindBuffer)(unsigned int, unsigned int); -typedef void(*ogrFucBufferData)(unsigned int, ptrdiff_t, const void*, - unsigned int); -typedef void(*ogrFucDeleteBuffers)(int, const unsigned int*); -typedef void*(*ogrFucMapBuffer)(unsigned int, unsigned int); -typedef unsigned char(*ogrFucUnmapBuffer)(unsigned int); - -#ifdef __cplusplus -extern "C" -{ -#endif -/** - * Initialize the configuration, call this first before using the library. - * \return 1 if succesfully configured, 0 otherwise and a default - * configuration will be used. - */ -int ogrInitConfig(RecorderConfig*); -/** - * Set the full path with filename for saving the recorded video, excluding - * extension, libopenglrecorder will automatically add .webm or .mkv as needed. - */ -void ogrSetSavedName(const char*); -/** - * Reset libopenglrecorder, call this before first \ref ogrCapture. - */ -void ogrPrepareCapture(void); -/** - * Capture the current frame buffer image as frame, make sure you have called - * \ref ogrPrepareCapture first. - */ -void ogrCapture(void); -/** - * Stop the recorder of libopenglrecorder. - */ -void ogrStopCapture(void); -/** - * Destroy the recorder of libopenglrecorder. - */ -void ogrDestroy(void); -/** - * (Optional) Register the callback(s) for \ref GeneralCallback, you have to - * make sure the enum CallBackType matches the callback type, see - * \ref CallBackType. - */ -void ogrRegGeneralCallback(CallBackType, GeneralCallback, void*); -/** - * (Optional) Register the callback(s) for \ref StringCallback, you have to - * make sure the enum CallBackType matches the callback type, see - * \ref CallBackType. - */ -void ogrRegStringCallback(CallBackType, StringCallback, void*); -/** - * (Optional) Register the callback(s) for \ref IntCallback, you have to - * make sure the enum CallBackType matches the callback type, see - * \ref CallBackType. - */ -void ogrRegIntCallback(CallBackType, IntCallback, void*); -/** - * Return 1 if recording is happening in libopenglrecorder, 0 otherwise. - */ -int ogrCapturing(void); -/** - * Set opengl function for read pixels (always required). - */ -void ogrRegReadPixelsFunction(ogrFucReadPixels); -/** - * Set opengl functions for using PBOs (required if triple buffering is used). - */ -void ogrRegPBOFunctions(ogrFucGenBuffers, ogrFucBindBuffer, ogrFucBufferData, - ogrFucDeleteBuffers, ogrFucMapBuffer, - ogrFucUnmapBuffer); -#ifdef __cplusplus -} -#endif - -#endif - -#endif diff --git a/src/recorder/pulseaudio_recorder.cpp b/src/recorder/pulseaudio_recorder.cpp deleted file mode 100644 index 88a1aa07f..000000000 --- a/src/recorder/pulseaudio_recorder.cpp +++ /dev/null @@ -1,550 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#if defined(ENABLE_REC_SOUND) && !defined(WIN32) - -#include "capture_library.hpp" -#include "recorder_private.hpp" -#include "vorbis_encoder.hpp" - -#include -#include - -#ifndef ENABLE_PULSE_WO_DL -#include -#endif - -namespace Recorder -{ - // ======================================================================== - void serverInfoCallBack(pa_context* c, const pa_server_info* i, void* data) - { - *(std::string*)data = i->default_sink_name; - } // serverInfoCallBack - // ======================================================================== - struct PulseAudioData - { - bool m_loaded; - pa_mainloop* m_loop; - pa_context* m_context; - pa_stream* m_stream; - pa_sample_spec m_sample_spec; - std::string m_default_sink; -#ifndef ENABLE_PULSE_WO_DL - void* m_dl_handle; - - typedef pa_stream* (*pa_stream_new_t)(pa_context*, const char*, - const pa_sample_spec*, const pa_channel_map*); - pa_stream_new_t pa_stream_new; - - typedef int (*pa_stream_connect_record_t)(pa_stream*, const char*, - const pa_buffer_attr*, pa_stream_flags_t); - pa_stream_connect_record_t pa_stream_connect_record; - - typedef pa_stream_state_t (*pa_stream_get_state_t)(pa_stream*); - pa_stream_get_state_t pa_stream_get_state; - - typedef size_t (*pa_stream_readable_size_t)(pa_stream*); - pa_stream_readable_size_t pa_stream_readable_size; - - typedef int (*pa_stream_peek_t)(pa_stream*, const void**, size_t*); - pa_stream_peek_t pa_stream_peek; - - typedef int (*pa_stream_drop_t)(pa_stream*); - pa_stream_drop_t pa_stream_drop; - - typedef int (*pa_stream_disconnect_t)(pa_stream*); - pa_stream_disconnect_t pa_stream_disconnect; - - typedef void (*pa_stream_unref_t)(pa_stream*); - pa_stream_unref_t pa_stream_unref; - - typedef pa_mainloop* (*pa_mainloop_new_t)(void); - pa_mainloop_new_t pa_mainloop_new; - - typedef pa_mainloop_api* (*pa_mainloop_get_api_t)(pa_mainloop*); - pa_mainloop_get_api_t pa_mainloop_get_api; - - typedef pa_context* (*pa_context_new_t)(pa_mainloop_api*, const char*); - pa_context_new_t pa_context_new; - - typedef int (*pa_context_connect_t)(pa_context*, const char*, - pa_context_flags_t, const pa_spawn_api*); - pa_context_connect_t pa_context_connect; - - typedef int (*pa_mainloop_iterate_t)(pa_mainloop*, int, int*); - pa_mainloop_iterate_t pa_mainloop_iterate; - - typedef pa_context_state_t (*pa_context_get_state_t)(pa_context*); - pa_context_get_state_t pa_context_get_state; - - typedef pa_operation* (*pa_context_get_server_info_t)(pa_context*, - pa_server_info_cb_t, void*); - pa_context_get_server_info_t pa_context_get_server_info; - - typedef pa_operation_state_t (*pa_operation_get_state_t) - (pa_operation*); - pa_operation_get_state_t pa_operation_get_state; - - typedef void (*pa_operation_unref_t)(pa_operation*); - pa_operation_unref_t pa_operation_unref; - - typedef void (*pa_context_disconnect_t)(pa_context*); - pa_context_disconnect_t pa_context_disconnect; - - typedef void (*pa_context_unref_t)(pa_context*); - pa_context_unref_t pa_context_unref; - - typedef void (*pa_mainloop_free_t)(pa_mainloop*); - pa_mainloop_free_t pa_mainloop_free; -#endif - // -------------------------------------------------------------------- - PulseAudioData() - { - m_loaded = false; - m_loop = NULL; - m_context = NULL; - m_stream = NULL; -#ifndef ENABLE_PULSE_WO_DL - m_dl_handle = NULL; - pa_stream_new = NULL; - pa_stream_connect_record = NULL; - pa_stream_get_state = NULL; - pa_stream_readable_size = NULL; - pa_stream_peek = NULL; - pa_stream_drop = NULL; - pa_stream_disconnect = NULL; - pa_stream_unref = NULL; - pa_mainloop_new = NULL; - pa_mainloop_get_api = NULL; - pa_context_new = NULL; - pa_context_connect = NULL; - pa_mainloop_iterate = NULL; - pa_context_get_state = NULL; - pa_context_get_server_info = NULL; - pa_operation_get_state = NULL; - pa_operation_unref = NULL; - pa_context_disconnect = NULL; - pa_context_unref = NULL; - pa_mainloop_free = NULL; -#endif - } // PulseAudioData - // -------------------------------------------------------------------- -#ifndef ENABLE_PULSE_WO_DL - bool loadPulseAudioLibrary() - { - m_dl_handle = dlopen("libpulse.so", RTLD_LAZY); - if (m_dl_handle == NULL) - { - printf("Failed to open PulseAudio library\n"); - return false; - } - pa_stream_new = (pa_stream_new_t)dlsym(m_dl_handle, - "pa_stream_new"); - if (pa_stream_new == NULL) - { - printf("Cannot load function 'pa_stream_new'\n"); - return false; - } - pa_stream_connect_record = (pa_stream_connect_record_t)dlsym - (m_dl_handle, "pa_stream_connect_record"); - if (pa_stream_connect_record == NULL) - { - printf("Cannot load function 'pa_stream_connect_record'\n"); - return false; - } - pa_stream_get_state = (pa_stream_get_state_t)dlsym(m_dl_handle, - "pa_stream_get_state"); - if (pa_stream_get_state == NULL) - { - printf("Cannot load function 'pa_stream_get_state'\n"); - return false; - } - pa_stream_readable_size = (pa_stream_readable_size_t)dlsym - (m_dl_handle, "pa_stream_readable_size"); - if (pa_stream_readable_size == NULL) - { - printf("Cannot load function 'pa_stream_readable_size'\n"); - return false; - } - pa_stream_peek = (pa_stream_peek_t)dlsym(m_dl_handle, - "pa_stream_peek"); - if (pa_stream_peek == NULL) - { - printf("Cannot load function 'pa_stream_peek'\n"); - return false; - } - pa_stream_drop = (pa_stream_drop_t)dlsym(m_dl_handle, - "pa_stream_drop"); - if (pa_stream_drop == NULL) - { - printf("Cannot load function 'pa_stream_drop'\n"); - return false; - } - pa_stream_disconnect = (pa_stream_disconnect_t)dlsym(m_dl_handle, - "pa_stream_disconnect"); - if (pa_stream_disconnect == NULL) - { - printf("Cannot load function 'pa_stream_disconnect'\n"); - return false; - } - pa_stream_unref = (pa_stream_unref_t)dlsym(m_dl_handle, - "pa_stream_unref"); - if (pa_stream_unref == NULL) - { - printf("Cannot load function 'pa_stream_unref'\n"); - return false; - } - pa_mainloop_new = (pa_mainloop_new_t)dlsym(m_dl_handle, - "pa_mainloop_new"); - if (pa_mainloop_new == NULL) - { - printf("Cannot load function 'pa_mainloop_new'\n"); - return false; - } - pa_mainloop_get_api = (pa_mainloop_get_api_t)dlsym(m_dl_handle, - "pa_mainloop_get_api"); - if (pa_mainloop_get_api == NULL) - { - printf("Cannot load function 'pa_mainloop_get_api'\n"); - return false; - } - pa_context_new = (pa_context_new_t)dlsym(m_dl_handle, - "pa_context_new"); - if (pa_context_new == NULL) - { - printf("Cannot load function 'pa_context_new'\n"); - return false; - } - pa_context_connect = (pa_context_connect_t)dlsym(m_dl_handle, - "pa_context_connect"); - if (pa_context_connect == NULL) - { - printf("Cannot load function 'pa_context_connect'\n"); - return false; - } - pa_mainloop_iterate = (pa_mainloop_iterate_t)dlsym(m_dl_handle, - "pa_mainloop_iterate"); - if (pa_mainloop_iterate == NULL) - { - printf("Cannot load function 'pa_mainloop_iterate'\n"); - return false; - } - pa_context_get_state = (pa_context_get_state_t)dlsym(m_dl_handle, - "pa_context_get_state"); - if (pa_context_get_state == NULL) - { - printf("Cannot load function 'pa_context_get_state'\n"); - return false; - } - pa_context_get_server_info = (pa_context_get_server_info_t)dlsym - (m_dl_handle, "pa_context_get_server_info"); - if (pa_context_get_server_info == NULL) - { - printf("Cannot load function 'pa_context_get_server_info'\n"); - return false; - } - pa_operation_get_state = (pa_operation_get_state_t)dlsym - (m_dl_handle, "pa_operation_get_state"); - if (pa_operation_get_state == NULL) - { - printf("Cannot load function 'pa_operation_get_state'\n"); - return false; - } - pa_operation_unref = (pa_operation_unref_t)dlsym(m_dl_handle, - "pa_operation_unref"); - if (pa_operation_unref == NULL) - { - printf("Cannot load function 'pa_operation_unref'\n"); - return false; - } - pa_context_disconnect = (pa_context_disconnect_t)dlsym(m_dl_handle, - "pa_context_disconnect"); - if (pa_context_disconnect == NULL) - { - printf("Cannot load function 'pa_context_disconnect'\n"); - return false; - } - pa_context_unref = (pa_context_unref_t)dlsym(m_dl_handle, - "pa_context_unref"); - if (pa_context_unref == NULL) - { - printf("Cannot load function 'pa_context_unref'\n"); - return false; - } - pa_mainloop_free = (pa_mainloop_free_t)dlsym(m_dl_handle, - "pa_mainloop_free"); - if (pa_mainloop_free == NULL) - { - printf("Cannot load function 'pa_mainloop_free'\n"); - return false; - } - return true; - } // loadPulseAudioLibrary -#endif - // -------------------------------------------------------------------- - bool load() - { -#ifndef ENABLE_PULSE_WO_DL - if (!loadPulseAudioLibrary()) - { - if (m_dl_handle != NULL) - { - dlclose(m_dl_handle); - m_dl_handle = NULL; - } - return false; - } -#endif - m_loop = pa_mainloop_new(); - if (m_loop == NULL) - { - printf("Failed to create mainloop\n"); - return false; - } - m_context = pa_context_new(pa_mainloop_get_api(m_loop), - "audioRecord"); - if (m_context == NULL) - { - printf("Failed to create context\n"); - return false; - } - pa_context_connect(m_context, NULL, PA_CONTEXT_NOAUTOSPAWN , NULL); - while (true) - { - while (pa_mainloop_iterate(m_loop, 0, NULL) > 0); - pa_context_state_t state = pa_context_get_state(m_context); - if (state == PA_CONTEXT_READY) - break; - if (!PA_CONTEXT_IS_GOOD(state)) - { - printf("Failed to connect to context\n"); - return false; - } - } - pa_operation* pa_op = pa_context_get_server_info(m_context, - serverInfoCallBack, &m_default_sink); - enum pa_operation_state op_state; - while ((op_state = - pa_operation_get_state(pa_op)) == PA_OPERATION_RUNNING) - pa_mainloop_iterate(m_loop, 0, NULL); - pa_operation_unref(pa_op); - if (m_default_sink.empty()) - { - printf("Failed to get default sink\n"); - return false; - } - m_default_sink += ".monitor"; - m_sample_spec.format = PA_SAMPLE_S16LE; - m_sample_spec.rate = 44100; - m_sample_spec.channels = 2; - - m_loaded = true; - return true; - } // load - // -------------------------------------------------------------------- - void configAudioType(AudioEncoderData* aed) - { - aed->m_sample_rate = m_sample_spec.rate; - aed->m_channels = m_sample_spec.channels; - aed->m_audio_type = AudioEncoderData::AT_PCM; - } // configAudioType - // -------------------------------------------------------------------- - inline void mainLoopIterate() - { - while (pa_mainloop_iterate(m_loop, 0, NULL) > 0); - } // mainLoopIterate - // -------------------------------------------------------------------- - bool createRecordStream() - { - assert(m_stream == NULL); - m_stream = pa_stream_new(m_context, "input", &m_sample_spec, NULL); - if (m_stream == NULL) - { - return false; - } - pa_buffer_attr buf_attr; - const unsigned frag_size = 1024 * m_sample_spec.channels * - sizeof(int16_t); - buf_attr.fragsize = frag_size; - const unsigned max_uint = -1; - buf_attr.maxlength = max_uint; - buf_attr.minreq = max_uint; - buf_attr.prebuf = max_uint; - buf_attr.tlength = max_uint; - pa_stream_connect_record(m_stream, m_default_sink.c_str(), - &buf_attr, (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY)); - while (true) - { - mainLoopIterate(); - pa_stream_state_t state = pa_stream_get_state(m_stream); - if (state == PA_STREAM_READY) - break; - if (!PA_STREAM_IS_GOOD(state)) - { - return false; - } - } - return true; - } // createRecordStream - // -------------------------------------------------------------------- - void removeRecordStream() - { - assert(m_stream != NULL); - pa_stream_disconnect(m_stream); - pa_stream_unref(m_stream); - m_stream = NULL; - } // removeRecordStream - // -------------------------------------------------------------------- - inline size_t getReadableSize() - { - assert(m_stream != NULL); - return pa_stream_readable_size(m_stream); - } // removeRecordStream - // -------------------------------------------------------------------- - inline void peekStream(const void** data, size_t* bytes) - { - assert(m_stream != NULL); - pa_stream_peek(m_stream, data, bytes); - } // peekStream - // -------------------------------------------------------------------- - inline void dropStream() - { - assert(m_stream != NULL); - pa_stream_drop(m_stream); - } // dropStream - // -------------------------------------------------------------------- - ~PulseAudioData() - { - if (m_loaded) - { - if (m_context != NULL) - { - pa_context_disconnect(m_context); - pa_context_unref(m_context); - } - if (m_loop != NULL) - { - pa_mainloop_free(m_loop); - } -#ifndef ENABLE_PULSE_WO_DL - if (m_dl_handle != NULL) - { - dlclose(m_dl_handle); - } -#endif - } - } // ~PulseAudioData - }; - // ======================================================================== - PulseAudioData g_pa_data; - // ======================================================================== - void audioRecorder(CaptureLibrary* cl) - { - setThreadName("audioRecorder"); - if (!g_pa_data.m_loaded) - { - if (!g_pa_data.load()) - { - printf("Cannot load pulseaudio data.\n"); - return; - } - } - - if (g_pa_data.createRecordStream() == false) - { - printf("Failed to create audio record stream.\n"); - if (g_pa_data.m_stream != NULL) - { - g_pa_data.removeRecordStream(); - } - return; - } - - std::list pcm_data; - std::mutex pcm_mutex; - std::condition_variable pcm_cv; - std::thread audio_enc_thread; - - AudioEncoderData aed; - g_pa_data.configAudioType(&aed); - aed.m_buf_list = &pcm_data; - aed.m_mutex = &pcm_mutex; - aed.m_cv = &pcm_cv; - aed.m_audio_bitrate = cl->getRecorderConfig().m_audio_bitrate; - const unsigned frag_size = 1024 * g_pa_data.m_sample_spec.channels * - sizeof(int16_t); - - switch (cl->getRecorderConfig().m_audio_format) - { - case OGR_AF_VORBIS: - audio_enc_thread = std::thread(vorbisEncoder, &aed); - break; - default: - break; - } - - int8_t* each_pcm_buf = new int8_t[frag_size](); - unsigned readed = 0; - while (true) - { - if (cl->getSoundStop()) - { - std::lock_guard lock(pcm_mutex); - pcm_data.push_back(each_pcm_buf); - pcm_data.push_back(NULL); - pcm_cv.notify_one(); - break; - } - g_pa_data.mainLoopIterate(); - const void* data; - size_t bytes; - size_t readable = g_pa_data.getReadableSize(); - if (readable == 0) - continue; - g_pa_data.peekStream(&data, &bytes); - if (data == NULL) - { - if (bytes > 0) - g_pa_data.dropStream(); - continue; - } - bool buf_full = readed + (unsigned)bytes > frag_size; - unsigned copy_size = buf_full ? - frag_size - readed : (unsigned)bytes; - memcpy(each_pcm_buf + readed, data, copy_size); - if (buf_full) - { - std::unique_lock ul(pcm_mutex); - pcm_data.push_back(each_pcm_buf); - pcm_cv.notify_one(); - ul.unlock(); - each_pcm_buf = new int8_t[frag_size](); - readed = (unsigned)bytes - copy_size; - memcpy(each_pcm_buf, (uint8_t*)data + copy_size, readed); - } - else - { - readed += (unsigned)bytes; - } - g_pa_data.dropStream(); - } - audio_enc_thread.join(); - g_pa_data.removeRecordStream(); - } // audioRecorder -} -#endif diff --git a/src/recorder/pulseaudio_recorder.hpp b/src/recorder/pulseaudio_recorder.hpp deleted file mode 100644 index a16bf49ea..000000000 --- a/src/recorder/pulseaudio_recorder.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef WIN32 - -#ifndef HEADER_PULSEAUDIO_RECORD_HPP -#define HEADER_PULSEAUDIO_RECORD_HPP - -class CaptureLibrary; -namespace Recorder -{ -#ifdef ENABLE_REC_SOUND - void audioRecorder(CaptureLibrary* cl); -#else - inline void audioRecorder(CaptureLibrary* cl) {} -#endif -}; - -#endif - -#endif diff --git a/src/recorder/recorder.cpp b/src/recorder/recorder.cpp deleted file mode 100644 index e09bbb3eb..000000000 --- a/src/recorder/recorder.cpp +++ /dev/null @@ -1,340 +0,0 @@ -#ifdef ENABLE_RECORDER - -#include "capture_library.hpp" -#include "recorder_private.hpp" - -#include -#include -#include -#include - -// ============================================================================ -ogrFucReadPixels ogrReadPixels = NULL; -ogrFucGenBuffers ogrGenBuffers = NULL; -ogrFucBindBuffer ogrBindBuffer = NULL; -ogrFucBufferData ogrBufferData = NULL; -ogrFucDeleteBuffers ogrDeleteBuffers = NULL; -ogrFucMapBuffer ogrMapBuffer = NULL; -ogrFucUnmapBuffer ogrUnmapBuffer = NULL; -// ============================================================================ -std::unique_ptr g_recorder_config(nullptr); -// ============================================================================ -std::unique_ptr g_capture_library(nullptr); -// ============================================================================ -std::atomic_bool g_capturing(false); -// ============================================================================ -std::string g_saved_name; -// ============================================================================ -StringCallback g_cb_saved_rec = NULL; -// ============================================================================ -IntCallback g_cb_progress_rec = NULL; -// ============================================================================ -GeneralCallback g_cb_wait_rec = NULL; -// ============================================================================ -GeneralCallback g_cb_start_rec = NULL; -// ============================================================================ -GeneralCallback g_cb_error_rec = NULL; -// ============================================================================ -GeneralCallback g_cb_slow_rec = NULL; -// ============================================================================ -std::array g_all_user_data; -// ============================================================================ -bool validateConfig(RecorderConfig* rc) -{ - if (rc == NULL) - return false; - if (rc->m_triple_buffering > 1 || rc->m_record_audio > 1) - return false; - if (rc->m_width > 16384 || rc->m_height > 16384) - return false; - if (rc->m_video_format >= OGR_VF_COUNT || - rc->m_audio_format >= OGR_AF_COUNT) - return false; - if (rc->m_audio_bitrate == 0 || rc->m_video_bitrate == 0 || - rc->m_record_fps == 0) - return false; - if (rc->m_record_jpg_quality > 100) - return false; - return true; -} // validateConfig - -// ---------------------------------------------------------------------------- -int ogrInitConfig(RecorderConfig* rc) -{ - RecorderConfig* new_rc = new RecorderConfig; - g_recorder_config.reset(new_rc); - - if (!validateConfig(rc)) - { - new_rc->m_triple_buffering = 1; - new_rc->m_record_audio = 0; - new_rc->m_width = 800; - new_rc->m_height = 600; - new_rc->m_video_format = OGR_VF_MJPEG; - new_rc->m_audio_format = OGR_AF_VORBIS; - new_rc->m_audio_bitrate = 112000; - new_rc->m_video_bitrate = 100000; - new_rc->m_record_fps = 30; - new_rc->m_record_jpg_quality = 90; - return 0; - } - - memcpy(new_rc, rc, sizeof(RecorderConfig)); - while (new_rc->m_width % 8 != 0) - { - new_rc->m_width--; - } - while (new_rc->m_height % 2 != 0) - { - new_rc->m_height--; - } - return 1; -} // ogrInitConfig - -// ---------------------------------------------------------------------------- -RecorderConfig* getConfig() -{ - assert(g_recorder_config.get() != nullptr); - return g_recorder_config.get(); -} // getConfig - -// ---------------------------------------------------------------------------- -void ogrSetSavedName(const char* name) -{ - g_saved_name = name; -} // ogrSetSavedName - -// ---------------------------------------------------------------------------- -const std::string& getSavedName() -{ - return g_saved_name; -} // getSavedName - -// ---------------------------------------------------------------------------- -void ogrPrepareCapture(void) -{ - assert(g_recorder_config.get() != nullptr && !g_saved_name.empty() && - ogrReadPixels != NULL); - if (g_capture_library.get() == nullptr) - { - assert(g_recorder_config.get() != nullptr); - g_capture_library.reset(new CaptureLibrary(getConfig())); - } - g_capture_library.get()->reset(); -} // ogrPrepareCapture - -// ---------------------------------------------------------------------------- -void ogrCapture(void) -{ - g_capture_library.get()->capture(); -} // ogrCapture - -// ---------------------------------------------------------------------------- -void ogrStopCapture(void) -{ - g_capture_library.get()->stopCapture(); -} // ogrStopCapture - -// ---------------------------------------------------------------------------- -void ogrDestroy(void) -{ - delete g_capture_library.release(); -} // ogrDestroy - -// ---------------------------------------------------------------------------- -void ogrRegGeneralCallback(CallBackType cbt, GeneralCallback cb, void* data) -{ - switch (cbt) - { - case OGR_CBT_ERROR_RECORDING: - g_cb_error_rec = cb; - g_all_user_data[OGR_CBT_ERROR_RECORDING] = data; - break; - case OGR_CBT_START_RECORDING: - g_cb_start_rec = cb; - g_all_user_data[OGR_CBT_START_RECORDING] = data; - break; - case OGR_CBT_SLOW_RECORDING: - g_cb_slow_rec = cb; - g_all_user_data[OGR_CBT_SLOW_RECORDING] = data; - break; - case OGR_CBT_WAIT_RECORDING: - g_cb_wait_rec = cb; - g_all_user_data[OGR_CBT_WAIT_RECORDING] = data; - break; - default: - assert(false && "Wrong callback enum"); - break; - } -} // ogrRegGeneralCallback - -// ---------------------------------------------------------------------------- -void ogrRegStringCallback(CallBackType cbt, StringCallback cb, void* data) -{ - switch (cbt) - { - case OGR_CBT_SAVED_RECORDING: - g_cb_saved_rec = cb; - g_all_user_data[OGR_CBT_SAVED_RECORDING] = data; - break; - default: - assert(false && "Wrong callback enum"); - break; - } -} // ogrRegStringCallback - -// ---------------------------------------------------------------------------- -void ogrRegIntCallback(CallBackType cbt, IntCallback cb, void* data) -{ - switch (cbt) - { - case OGR_CBT_PROGRESS_RECORDING: - g_cb_progress_rec = cb; - g_all_user_data[OGR_CBT_PROGRESS_RECORDING] = data; - break; - default: - assert(false && "Wrong callback enum"); - break; - } -} // ogrRegIntCallback - -// ---------------------------------------------------------------------------- -void runCallback(CallBackType cbt, const void* arg) -{ - switch (cbt) - { - case OGR_CBT_START_RECORDING: - { - if (g_cb_start_rec == NULL) return; - g_cb_start_rec(g_all_user_data[OGR_CBT_START_RECORDING]); - break; - } - case OGR_CBT_SAVED_RECORDING: - { - if (g_cb_saved_rec == NULL) return; - const char* s = (const char*)arg; - g_cb_saved_rec(s, g_all_user_data[OGR_CBT_SAVED_RECORDING]); - break; - } - case OGR_CBT_ERROR_RECORDING: - { - if (g_cb_error_rec == NULL) return; - g_cb_error_rec(g_all_user_data[OGR_CBT_ERROR_RECORDING]); - break; - } - case OGR_CBT_PROGRESS_RECORDING: - { - if (g_cb_progress_rec == NULL) return; - const int* i = (const int*)arg; - g_cb_progress_rec(*i, g_all_user_data[OGR_CBT_PROGRESS_RECORDING]); - break; - } - case OGR_CBT_WAIT_RECORDING: - { - if (g_cb_wait_rec == NULL) return; - g_cb_wait_rec(g_all_user_data[OGR_CBT_WAIT_RECORDING]); - break; - } - case OGR_CBT_SLOW_RECORDING: - { - if (g_cb_slow_rec == NULL) return; - g_cb_slow_rec(g_all_user_data[OGR_CBT_SLOW_RECORDING]); - break; - } - default: - break; - } -} // runCallback - -// ---------------------------------------------------------------------------- -int ogrCapturing(void) -{ - return g_capturing.load() ? 1 : 0; -} // ogrCapturing - -// ---------------------------------------------------------------------------- -void ogrRegReadPixelsFunction(ogrFucReadPixels read_pixels) -{ - assert(read_pixels != NULL); - ogrReadPixels = read_pixels; -} // ogrRegReadPixelsFunction - -// ---------------------------------------------------------------------------- -void ogrRegPBOFunctions(ogrFucGenBuffers gen_buffers, - ogrFucBindBuffer bind_buffer, - ogrFucBufferData buffer_data, - ogrFucDeleteBuffers delete_buffers, - ogrFucMapBuffer map_buffer, - ogrFucUnmapBuffer unmap_buffer) -{ - assert(gen_buffers != NULL); - ogrGenBuffers = gen_buffers; - assert(bind_buffer != NULL); - ogrBindBuffer = bind_buffer; - assert(buffer_data != NULL); - ogrBufferData = buffer_data; - assert(delete_buffers != NULL); - ogrDeleteBuffers = delete_buffers; - assert(map_buffer != NULL); - ogrMapBuffer = map_buffer; - assert(unmap_buffer != NULL); - ogrUnmapBuffer = unmap_buffer; -} // ogrRegPBOFunctions - -// ---------------------------------------------------------------------------- -void setCapturing(bool val) -{ - g_capturing.store(val); -} // setCapturing - -// ---------------------------------------------------------------------------- -/** This function sets the name of this thread in the debugger. - * \param name Name of the thread. - */ -#if defined(_MSC_VER) && defined(DEBUG) -# define WIN32_LEAN_AND_MEAN -# include - - void setThreadName(const char *name) - { - const DWORD MS_VC_EXCEPTION=0x406D1388; -#pragma pack(push,8) - typedef struct tagTHREADNAME_INFO - { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. - } THREADNAME_INFO; -#pragma pack(pop) - - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = name; - info.dwThreadID = -1; - info.dwFlags = 0; - - __try - { - RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), - (ULONG_PTR*)&info ); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } - - } // setThreadName -#elif defined(__linux__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__) - void setThreadName(const char* name) - { -#if __GLIBC__ > 2 || __GLIBC_MINOR__ > 11 - pthread_setname_np(pthread_self(), name); -#endif - } // setThreadName -#else - void setThreadName(const char* name) - { - } // setThreadName -#endif - -#endif diff --git a/src/recorder/recorder_private.hpp b/src/recorder/recorder_private.hpp deleted file mode 100644 index 97cccd2a5..000000000 --- a/src/recorder/recorder_private.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifdef ENABLE_RECORDER -#ifndef HEADER_RECORDER_PRIVATE_HPP -#define HEADER_RECORDER_PRIVATE_HPP - -#include "openglrecorder.h" - -#include - -extern ogrFucReadPixels ogrReadPixels; -extern ogrFucGenBuffers ogrGenBuffers; -extern ogrFucBindBuffer ogrBindBuffer; -extern ogrFucBufferData ogrBufferData; -extern ogrFucDeleteBuffers ogrDeleteBuffers; -extern ogrFucMapBuffer ogrMapBuffer; -extern ogrFucUnmapBuffer ogrUnmapBuffer; - -RecorderConfig* getConfig(); -const std::string& getSavedName(); -void setCapturing(bool val); -void setThreadName(const char* name); -void runCallback(CallBackType cbt, const void* arg); - -#endif - -#endif diff --git a/src/recorder/vorbis_encoder.cpp b/src/recorder/vorbis_encoder.cpp deleted file mode 100644 index 33465b6dc..000000000 --- a/src/recorder/vorbis_encoder.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef ENABLE_RECORDER - -#include "capture_library.hpp" -#include "recorder_private.hpp" - -#include -#include - -namespace Recorder -{ - void vorbisEncoder(AudioEncoderData* aed) - { - setThreadName("vorbisEncoder"); - vorbis_info vi; - vorbis_dsp_state vd; - vorbis_block vb; - vorbis_info_init(&vi); - vorbis_encode_init(&vi, aed->m_channels, aed->m_sample_rate, -1, - aed->m_audio_bitrate, -1); - vorbis_analysis_init(&vd, &vi); - vorbis_block_init(&vd, &vb); - vorbis_comment vc; - vorbis_comment_init(&vc); - vorbis_comment_add_tag(&vc, "Encoder", - "Vorbis encoder by libopenglrecorder"); - ogg_packet header; - ogg_packet header_comm; - ogg_packet header_code; - vorbis_analysis_headerout(&vd, &vc, &header, &header_comm, - &header_code); - if (header.bytes > 255 || header_comm.bytes > 255) - { - printf("Header is too long for vorbis.\n"); - return; - } - FILE* vb_data = fopen((getSavedName() + ".audio").c_str(), "wb"); - if (vb_data == NULL) - { - printf("Failed to open file for encoding vorbis.\n"); - return; - } - fwrite(&aed->m_sample_rate, 1, sizeof(uint32_t), vb_data); - fwrite(&aed->m_channels, 1, sizeof(uint32_t), vb_data); - const uint32_t all = header.bytes + header_comm.bytes + - header_code.bytes + 3; - fwrite(&all, 1, sizeof(uint32_t), vb_data); - uint8_t size = 2; - fwrite(&size, 1, sizeof(uint8_t), vb_data); - size = (uint8_t)header.bytes; - fwrite(&size, 1, sizeof(uint8_t), vb_data); - size = (uint8_t)header_comm.bytes; - fwrite(&size, 1, sizeof(uint8_t), vb_data); - fwrite(header.packet, 1, header.bytes, vb_data); - fwrite(header_comm.packet, 1, header_comm.bytes, vb_data); - fwrite(header_code.packet, 1, header_code.bytes, vb_data); - ogg_packet op; - int64_t last_timestamp = 0; - bool eos = false; - while (eos == false) - { - std::unique_lock ul(*aed->m_mutex); - aed->m_cv->wait(ul, [&aed] { return !aed->m_buf_list->empty(); }); - int8_t* audio_buf = aed->m_buf_list->front(); - aed->m_buf_list->pop_front(); - ul.unlock(); - if (audio_buf == NULL) - { - vorbis_analysis_wrote(&vd, 0); - eos = true; - } - else - { - float **buffer = vorbis_analysis_buffer(&vd, 1024); - const unsigned channels = aed->m_channels; - if (aed->m_audio_type == AudioEncoderData::AT_PCM) - { - for (unsigned j = 0; j < channels; j++) - { - for (unsigned i = 0; i < 1024; i++) - { - int8_t* each_channel = - &audio_buf[i * channels * 2 + j * 2]; - buffer[j][i] = float((each_channel[1] << 8) | - (0x00ff & (int)each_channel[0])) / 32768.0f; - } - } - } - else - { - for (unsigned j = 0; j < channels; j++) - { - for (unsigned i = 0; i < 1024; i++) - { - float* fbuf = reinterpret_cast(audio_buf); - buffer[j][i] = fbuf[i * channels + j]; - } - } - } - vorbis_analysis_wrote(&vd, 1024); - } - while (vorbis_analysis_blockout(&vd, &vb) == 1) - { - vorbis_analysis(&vb, NULL); - vorbis_bitrate_addblock(&vb); - while (vorbis_bitrate_flushpacket(&vd, &op)) - { - if (op.granulepos > 0) - { - uint32_t frame_size = (uint32_t)op.bytes; - fwrite(&frame_size, 1, sizeof(uint32_t), vb_data); - fwrite(&last_timestamp, 1, sizeof(int64_t), vb_data); - fwrite(op.packet, 1, frame_size, vb_data); - double s = (double)op.granulepos / - (double)aed->m_sample_rate * 1000000000.; - last_timestamp = (int64_t)s; - } - } - } - delete[] audio_buf; - } - vorbis_block_clear(&vb); - vorbis_dsp_clear(&vd); - vorbis_comment_clear(&vc); - vorbis_info_clear(&vi); - fclose(vb_data); - } // vorbisEncoder -} -#endif diff --git a/src/recorder/vorbis_encoder.hpp b/src/recorder/vorbis_encoder.hpp deleted file mode 100644 index 9ba87602e..000000000 --- a/src/recorder/vorbis_encoder.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef ENABLE_RECORDER - -#ifndef HEADER_VORBIS_ENCODE_HPP -#define HEADER_VORBIS_ENCODE_HPP - -struct AudioEncoderData; -namespace Recorder -{ - void vorbisEncoder(AudioEncoderData* aed); -}; - -#endif - -#endif diff --git a/src/recorder/vpx_encoder.cpp b/src/recorder/vpx_encoder.cpp deleted file mode 100644 index 46c0ba880..000000000 --- a/src/recorder/vpx_encoder.cpp +++ /dev/null @@ -1,169 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#if defined(ENABLE_RECORDER) && !defined(NO_VPX) - -#include "capture_library.hpp" -#include "recorder_private.hpp" - -#include -#include - -namespace Recorder -{ - // ------------------------------------------------------------------------ - int vpxEncodeFrame(vpx_codec_ctx_t *codec, vpx_image_t *img, - int frame_index, FILE *out) - { - int got_pkts = 0; - vpx_codec_iter_t iter = NULL; - const vpx_codec_cx_pkt_t *pkt = NULL; - const vpx_codec_err_t res = vpx_codec_encode(codec, img, frame_index, - 1, 0, VPX_DL_REALTIME); - if (res != VPX_CODEC_OK) - { - printf("Failed to encode frame\n"); - return -1; - } - while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != NULL) - { - got_pkts = 1; - if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) - { - fwrite(&pkt->data.frame.sz, 1, sizeof(uint32_t), out); - fwrite(&pkt->data.frame.pts, 1, sizeof(int64_t), out); - fwrite(&pkt->data.frame.flags, 1, - sizeof(vpx_codec_frame_flags_t), out); - fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, out); - } - } - return got_pkts; - } // vpxEncodeFrame - // ------------------------------------------------------------------------ - void vpxEncoder(CaptureLibrary* cl) - { - setThreadName("vpxEncoder"); - FILE* vpx_data = fopen((getSavedName() + ".video").c_str(), "wb"); - if (vpx_data == NULL) - { - printf("Failed to open file for writing vpx.\n"); - return; - } - - vpx_codec_ctx_t codec; - vpx_codec_enc_cfg_t cfg; - vpx_codec_iface_t* codec_if = NULL; - switch (cl->getRecorderConfig().m_video_format) - { - case OGR_VF_VP8: - codec_if = vpx_codec_vp8_cx(); - break; - case OGR_VF_VP9: - codec_if = vpx_codec_vp9_cx(); - break; - default: - assert(false); - break; - } - vpx_codec_err_t res = vpx_codec_enc_config_default(codec_if, &cfg, 0); - if (res > 0) - { - printf("Failed to get default vpx codec config.\n"); - return; - } - - const unsigned width = cl->getRecorderConfig().m_width; - const unsigned height = cl->getRecorderConfig().m_height; - int frames_encoded = 0; - cfg.g_w = width; - cfg.g_h = height; - cfg.g_timebase.num = 1; - cfg.g_timebase.den = cl->getRecorderConfig().m_record_fps; - cfg.rc_end_usage = VPX_VBR; - cfg.rc_target_bitrate = cl->getRecorderConfig().m_video_bitrate; - - if (vpx_codec_enc_init(&codec, codec_if, &cfg, 0) > 0) - { - printf("Failed to initialize vpx encoder\n"); - fclose(vpx_data); - return; - } - float last_size = -1.0f; - int cur_finished_count = 0; - while (true) - { - std::unique_lock ul(*cl->getJPGListMutex()); - cl->getJPGListCV()->wait(ul, [&cl] - { return !cl->getJPGList()->empty(); }); - auto& p = cl->getJPGList()->front(); - uint8_t* jpg = std::get<0>(p); - uint32_t jpg_size = std::get<1>(p); - int frame_count = std::get<2>(p); - if (jpg == NULL) - { - cl->getJPGList()->clear(); - ul.unlock(); - if (cl->displayingProgress()) - { - int rate = 100; - runCallback(OGR_CBT_PROGRESS_RECORDING, &rate); - } - break; - } - cl->getJPGList()->pop_front(); - ul.unlock(); - if (cl->displayingProgress()) - { - if (last_size == -1.0f) - last_size = (float)(cl->getJPGList()->size()); - cur_finished_count += frame_count; - int rate = (int)(cur_finished_count / last_size * 100.0f); - rate = rate > 100 ? 100 : rate; - runCallback(OGR_CBT_PROGRESS_RECORDING, &rate); - } - uint8_t* yuv = NULL; - unsigned yuv_size; - int ret = cl->yuvConversion(jpg, jpg_size, &yuv, - &yuv_size); - if (ret < 0) - { - delete [] yuv; - tjFree(jpg); - continue; - } - assert(yuv_size != 0); - tjFree(jpg); - vpx_image_t each_frame; - vpx_img_wrap(&each_frame, VPX_IMG_FMT_I420, width, height, 1, yuv); - while (frame_count != 0) - { - vpxEncodeFrame(&codec, &each_frame, frames_encoded++, vpx_data); - frame_count--; - } - delete [] yuv; - } - - while (vpxEncodeFrame(&codec, NULL, -1, vpx_data)); - if (vpx_codec_destroy(&codec)) - { - printf("Failed to destroy vpx codec.\n"); - return; - } - fclose(vpx_data); - } // vpxEncoder -} -#endif diff --git a/src/recorder/vpx_encoder.hpp b/src/recorder/vpx_encoder.hpp deleted file mode 100644 index 12c935c96..000000000 --- a/src/recorder/vpx_encoder.hpp +++ /dev/null @@ -1,36 +0,0 @@ - -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef ENABLE_RECORDER - -#ifndef HEADER_VPX_ENCODER_HPP -#define HEADER_VPX_ENCODER_HPP - -class CaptureLibrary; - -namespace Recorder -{ -#ifdef NO_VPX - inline void vpxEncoder(CaptureLibrary* cl) {} -#else - void vpxEncoder(CaptureLibrary* cl); -#endif -}; -#endif - -#endif diff --git a/src/recorder/wasapi_recorder.cpp b/src/recorder/wasapi_recorder.cpp deleted file mode 100644 index 151239355..000000000 --- a/src/recorder/wasapi_recorder.cpp +++ /dev/null @@ -1,308 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#if defined(ENABLE_REC_SOUND) && defined(WIN32) - -#include "capture_library.hpp" -#include "recorder_private.hpp" -#include "vorbis_encoder.hpp" - -#include -#include -#include -#include -#include - -#if defined (__MINGW32__) || defined(__CYGWIN__) - #include - inline GUID uuidFromString(const char* s) - { - unsigned long p0; - unsigned int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10; - sscanf(s, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", - &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10); - GUID g = { p0, (uint16_t)p1, (uint16_t)p2, { (uint8_t)p3, (uint8_t)p4, - (uint8_t)p5, (uint8_t)p6, (uint8_t)p7, (uint8_t)p8, (uint8_t)p9, - (uint8_t)p10 }}; - return g; - } - #undef KSDATAFORMAT_SUBTYPE_PCM - #define KSDATAFORMAT_SUBTYPE_PCM \ - uuidFromString("00000001-0000-0010-8000-00aa00389b71") - #undef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT - #define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT \ - uuidFromString("00000003-0000-0010-8000-00aa00389b71") -#endif - -namespace Recorder -{ - // ======================================================================== - const REFERENCE_TIME REFTIMES_PER_SEC = 10000000; - // ======================================================================== - struct WasapiData - { - bool m_loaded; - IMMDeviceEnumerator* m_dev_enum; - IMMDevice* m_dev; - IAudioClient* m_client; - IAudioCaptureClient* m_capture_client; - WAVEFORMATEX* m_wav_format; - uint32_t m_buffer_size; - // -------------------------------------------------------------------- - WasapiData() - { - m_loaded = false; - m_dev_enum = NULL; - m_dev = NULL; - m_client = NULL; - m_capture_client = NULL; - m_wav_format = NULL; - } // WasapiData - // -------------------------------------------------------------------- - bool load() - { - HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, - CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), - (void**)&m_dev_enum); - if (FAILED(hr)) - return false; - - hr = m_dev_enum->GetDefaultAudioEndpoint(eRender, eConsole, - &m_dev); - if (FAILED(hr)) - return false; - - hr = m_dev->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, - (void**)&m_client); - if (FAILED(hr)) - return false; - - hr = m_client->GetMixFormat(&m_wav_format); - if (FAILED(hr)) - return false; - - hr = m_client->Initialize(AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_LOOPBACK, REFTIMES_PER_SEC, 0, - m_wav_format, NULL); - if (FAILED(hr)) - return false; - - hr = m_client->GetBufferSize(&m_buffer_size); - if (FAILED(hr)) - return false; - - hr = m_client->GetService(__uuidof(IAudioCaptureClient), - (void**)&m_capture_client); - if (FAILED(hr)) - return false; - - m_loaded = true; - return true; - } // load - // -------------------------------------------------------------------- - ~WasapiData() - { - if (m_loaded) - { - CoTaskMemFree(m_wav_format); - if (m_dev_enum) - m_dev_enum->Release(); - if (m_dev) - m_dev->Release(); - if (m_client) - m_client->Release(); - if (m_capture_client) - m_capture_client->Release(); - } - } // ~WasapiData - }; - // ======================================================================== - WasapiData g_wasapi_data; - // ======================================================================== - void audioRecorder(CaptureLibrary* cl) - { - setThreadName("audioRecorder"); - if (!g_wasapi_data.m_loaded) - { - if (!g_wasapi_data.load()) - { - printf("Failed to load wasapi data.\n"); - return; - } - } - AudioEncoderData aed = {}; - if (g_wasapi_data.m_wav_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) - { - WAVEFORMATEXTENSIBLE* wav_for_ext = - (WAVEFORMATEXTENSIBLE*)g_wasapi_data.m_wav_format; - aed.m_channels = wav_for_ext->Format.nChannels; - aed.m_sample_rate = wav_for_ext->Format.nSamplesPerSec; - if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_PCM, wav_for_ext->SubFormat)) - { - aed.m_audio_type = AudioEncoderData::AT_PCM; - if (wav_for_ext->Format.wBitsPerSample != 16) - { - printf("Only 16bit PCM is supported.\n"); - return; - } - } - else if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wav_for_ext - ->SubFormat)) - { - aed.m_audio_type = AudioEncoderData::AT_FLOAT; - if (wav_for_ext->Format.wBitsPerSample != 32) - { - printf("Only 32bit float is supported.\n"); - return; - } - } - else - { - printf("Unsupported audio input format.\n"); - return; - } - } - else if (g_wasapi_data.m_wav_format->wFormatTag == WAVE_FORMAT_PCM) - { - aed.m_channels = g_wasapi_data.m_wav_format->nChannels; - aed.m_sample_rate = g_wasapi_data.m_wav_format->nSamplesPerSec; - aed.m_audio_type = AudioEncoderData::AT_PCM; - if (g_wasapi_data.m_wav_format->wBitsPerSample != 16) - { - printf("Only 16bit PCM is supported.\n"); - return; - } - } - else - { - printf("Unsupported audio input format\n"); - return; - } - if (aed.m_sample_rate > 48000) - { - printf("Only support maximum 48000hz sample rate audio.\n"); - return; - } - HRESULT hr = g_wasapi_data.m_client->Reset(); - if (FAILED(hr)) - { - printf("Failed to reset audio recorder.\n"); - return; - } - hr = g_wasapi_data.m_client->Start(); - if (FAILED(hr)) - { - printf("Failed to start audio recorder.\n"); - return; - } - REFERENCE_TIME duration = REFTIMES_PER_SEC * - g_wasapi_data.m_buffer_size / g_wasapi_data.m_wav_format - ->nSamplesPerSec; - - std::list audio_data; - std::mutex audio_mutex; - std::condition_variable audio_cv; - std::thread audio_enc_thread; - aed.m_buf_list = &audio_data; - aed.m_mutex = &audio_mutex; - aed.m_cv = &audio_cv; - aed.m_audio_bitrate = cl->getRecorderConfig().m_audio_bitrate; - - switch (cl->getRecorderConfig().m_audio_format) - { - case OGR_AF_VORBIS: - audio_enc_thread = std::thread(vorbisEncoder, &aed); - break; - default: - break; - } - - const unsigned frag_size = 1024 * aed.m_channels * - (g_wasapi_data.m_wav_format->wBitsPerSample / 8); - int8_t* each_audio_buf = new int8_t[frag_size](); - unsigned readed = 0; - while (true) - { - if (cl->getSoundStop()) - { - std::lock_guard lock(audio_mutex); - audio_data.push_back(each_audio_buf); - audio_data.push_back(NULL); - audio_cv.notify_one(); - break; - } - uint32_t packet_length = 0; - hr = g_wasapi_data.m_capture_client->GetNextPacketSize( - &packet_length); - if (FAILED(hr)) - { - printf("Failed to get next audio packet size\n"); - } - if (packet_length == 0) - { - REFERENCE_TIME sleep_time = duration / 10000 / 2; - Sleep((uint32_t)sleep_time); - continue; - } - BYTE* data; - DWORD flags; - hr = g_wasapi_data.m_capture_client->GetBuffer(&data, - &packet_length, &flags, NULL, NULL); - if (FAILED(hr)) - { - printf("Failed to get audio buffer\n"); - } - const unsigned bytes = aed.m_channels * (g_wasapi_data.m_wav_format - ->wBitsPerSample / 8) * packet_length; - bool buf_full = readed + bytes > frag_size; - unsigned copy_size = buf_full ? frag_size - readed : bytes; - if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT)) - { - memcpy(each_audio_buf + readed, data, copy_size); - } - if (buf_full) - { - std::unique_lock ul(audio_mutex); - audio_data.push_back(each_audio_buf); - audio_cv.notify_one(); - ul.unlock(); - each_audio_buf = new int8_t[frag_size](); - readed = (unsigned)bytes - copy_size; - if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT)) - { - memcpy(each_audio_buf, (uint8_t*)data + copy_size, readed); - } - } - else - { - readed += bytes; - } - hr = g_wasapi_data.m_capture_client->ReleaseBuffer(packet_length); - if (FAILED(hr)) - { - printf("Failed to release audio buffer\n"); - } - } - hr = g_wasapi_data.m_client->Stop(); - if (FAILED(hr)) - { - printf("Failed to stop audio recorder\n"); - } - audio_enc_thread.join(); - } // audioRecorder -} -#endif diff --git a/src/recorder/wasapi_recorder.hpp b/src/recorder/wasapi_recorder.hpp deleted file mode 100644 index 401e08ff0..000000000 --- a/src/recorder/wasapi_recorder.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2017 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifdef WIN32 - -#ifndef HEADER_WASAPI_RECORD_HPP -#define HEADER_WASAPI_RECORD_HPP - -class CaptureLibrary; -namespace Recorder -{ -#ifdef ENABLE_REC_SOUND - void audioRecorder(CaptureLibrary* cl); -#else - inline void audioRecorder(CaptureLibrary* cl) {} -#endif -}; - -#endif - -#endif From fe0fe828a007d53bf1cc7043dac8a6ebc7b2d3a2 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 12 Apr 2017 13:41:14 +0800 Subject: [PATCH 08/14] Fix travis build --- src/graphics/irr_driver.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 36cc6e2b9..15c8409a3 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -66,9 +66,12 @@ #include "utils/profiler.hpp" #include "utils/vs.hpp" -#include #include + +#ifdef ENABLE_RECORDER +#include #include +#endif /* Build-time check that the Irrlicht we're building against works for us. * Should help prevent distros building against an incompatible library. From 4de0ca83aa14672f9303c6e15bbd26a67359d5f1 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 13 Apr 2017 13:59:36 +0800 Subject: [PATCH 09/14] Move capture right before glfencesync if possible --- CMakeLists.txt | 8 +++++--- src/graphics/draw_calls.cpp | 12 ++++++++++++ src/graphics/irr_driver.cpp | 19 ++++++------------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index deaf506ea..fdc6f611c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,11 +134,13 @@ else() include_directories(${JPEG_INCLUDE_DIR}) endif() -if(BUILD_RECORDER) - find_library(OPENGLRECORDER_LIBRARY NAMES openglrecorder libopenglrecorder PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") - find_path(OPENGLRECORDER_INCLUDEDIR NAMES openglrecorder.h PATHS "${PROJECT_SOURCE_DIR}/dependencies/include") +if (BUILD_RECORDER) find_library(OPENGLRECORDER_LIBRARY NAMES openglrecorder libopenglrecorder PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") find_path(OPENGLRECORDER_INCLUDEDIR NAMES openglrecorder.h PATHS "${PROJECT_SOURCE_DIR}/dependencies/include") + if (NOT OPENGLRECORDER_LIBRARY OR NOT OPENGLRECORDER_INCLUDEDIR) + message(FATAL_ERROR "libopenglrecorder not found. " + "Either install libopenglrecorder or disable ingame recorder with -DBUILD_RECORDER=0") + endif() include_directories(${OPENGLRECORDER_INCLUDEDIR}) mark_as_advanced(OPENGLRECORDER_LIBRARY OPENGLRECORDER_INCLUDEDIR) endif() diff --git a/src/graphics/draw_calls.cpp b/src/graphics/draw_calls.cpp index 49d4ed167..5b4b4706c 100644 --- a/src/graphics/draw_calls.cpp +++ b/src/graphics/draw_calls.cpp @@ -34,6 +34,9 @@ #include "utils/profiler.hpp" #include +#ifdef ENABLE_RECORDER +#include +#endif // ---------------------------------------------------------------------------- void DrawCalls::clearLists() @@ -646,6 +649,15 @@ void DrawCalls::prepareDrawCalls( ShadowMatrices& shadow_matrices, PROFILER_POP_CPU_MARKER(); irr_driver->setSkinningJoint(getSkinningOffset()); +#ifdef ENABLE_RECORDER + if (irr_driver->isRecording()) + { + PROFILER_PUSH_CPU_MARKER("- Recording", 0x0, 0x50, 0x40); + ogrCapture(); + PROFILER_POP_CPU_MARKER(); + } +#endif + // Add a 1 s timeout if (!m_sync) m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 15c8409a3..677ba1cfb 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -642,17 +642,13 @@ void IrrDriver::initDevice() ogrRegGeneralCallback(OGR_CBT_START_RECORDING, [] (void* user_data) { MessageQueue::add (MessageQueue::MT_GENERIC, _("Video recording started.")); }, NULL); - ogrRegGeneralCallback(OGR_CBT_ERROR_RECORDING, - [] (void* user_data) { MessageQueue::add - (MessageQueue::MT_ERROR, _("Error when saving video.")); }, NULL); - ogrRegGeneralCallback(OGR_CBT_SLOW_RECORDING, - [] (void* user_data) { MessageQueue::add - (MessageQueue::MT_ERROR, _("Encoding is too slow, dropping frames.")); - }, NULL); ogrRegGeneralCallback(OGR_CBT_WAIT_RECORDING, [] (void* user_data) { MessageQueue::add (MessageQueue::MT_GENERIC, _("Please wait while encoding is finished." )); }, NULL); + ogrRegStringCallback(OGR_CBT_ERROR_RECORDING, + [](const char* s, void* user_data) + { Log::error("openglrecorder", "%s", s); }, NULL); ogrRegStringCallback(OGR_CBT_SAVED_RECORDING, [] (const char* s, void* user_data) { MessageQueue::add (MessageQueue::MT_GENERIC, _("Video saved in \"%s\".", s)); @@ -1968,7 +1964,7 @@ void IrrDriver::update(float dt) //if(World::getWorld() && World::getWorld()->isRacePhase()) // printRenderStats(); #ifdef ENABLE_RECORDER - if (m_recording) + if (!world && m_recording) ogrCapture(); #endif } // update @@ -1982,13 +1978,11 @@ void IrrDriver::setRecording(bool val) Log::warn("irr_driver", "PBO extension missing, can't record video."); return; } - if (m_recording == val) + if (val == (ogrCapturing() == 1)) return; + m_recording = val; if (val == true) { - if (ogrCapturing() > 0) - return; - m_recording = val; std::string track_name = World::getWorld() != NULL ? race_manager->getTrackName() : "menu"; time_t rawtime; @@ -2005,7 +1999,6 @@ void IrrDriver::setRecording(bool val) } else { - m_recording = val; ogrStopCapture(); } #endif From f3e0d2f9e954714bb916ca702ab8cbf6233edc8b Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 13 Apr 2017 15:13:47 +0800 Subject: [PATCH 10/14] Revert move capture right before glfencesync if possible --- src/graphics/draw_calls.cpp | 12 ------------ src/graphics/irr_driver.cpp | 6 +++++- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/graphics/draw_calls.cpp b/src/graphics/draw_calls.cpp index 5b4b4706c..49d4ed167 100644 --- a/src/graphics/draw_calls.cpp +++ b/src/graphics/draw_calls.cpp @@ -34,9 +34,6 @@ #include "utils/profiler.hpp" #include -#ifdef ENABLE_RECORDER -#include -#endif // ---------------------------------------------------------------------------- void DrawCalls::clearLists() @@ -649,15 +646,6 @@ void DrawCalls::prepareDrawCalls( ShadowMatrices& shadow_matrices, PROFILER_POP_CPU_MARKER(); irr_driver->setSkinningJoint(getSkinningOffset()); -#ifdef ENABLE_RECORDER - if (irr_driver->isRecording()) - { - PROFILER_PUSH_CPU_MARKER("- Recording", 0x0, 0x50, 0x40); - ogrCapture(); - PROFILER_POP_CPU_MARKER(); - } -#endif - // Add a 1 s timeout if (!m_sync) m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 677ba1cfb..edd4e57aa 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -1964,8 +1964,12 @@ void IrrDriver::update(float dt) //if(World::getWorld() && World::getWorld()->isRacePhase()) // printRenderStats(); #ifdef ENABLE_RECORDER - if (!world && m_recording) + if (m_recording) + { + PROFILER_PUSH_CPU_MARKER("- Recording", 0x0, 0x50, 0x40); ogrCapture(); + PROFILER_POP_CPU_MARKER(); + } #endif } // update From 487d007b179b5dcf27997b1a20edafb0ff46124c Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 15 Apr 2017 16:30:27 +0800 Subject: [PATCH 11/14] Try to use progress bar to indicate the encoding progress Need an updated libopenglrecorder --- src/graphics/irr_driver.cpp | 25 +-- src/guiengine/message_queue.cpp | 259 ++++++++++++++++++++++---------- src/guiengine/message_queue.hpp | 10 +- src/guiengine/skin.cpp | 13 ++ src/guiengine/skin.hpp | 2 + 5 files changed, 204 insertions(+), 105 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index edd4e57aa..ca993afb7 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -160,6 +160,9 @@ IrrDriver::IrrDriver() */ IrrDriver::~IrrDriver() { +#ifdef ENABLE_RECORDER + ogrDestroy(); +#endif assert(m_device != NULL); m_device->drop(); m_device = NULL; @@ -173,9 +176,6 @@ IrrDriver::~IrrDriver() #endif delete m_wind; delete m_renderer; -#ifdef ENABLE_RECORDER - ogrDestroy(); -#endif } // ~IrrDriver // ---------------------------------------------------------------------------- @@ -642,10 +642,6 @@ void IrrDriver::initDevice() ogrRegGeneralCallback(OGR_CBT_START_RECORDING, [] (void* user_data) { MessageQueue::add (MessageQueue::MT_GENERIC, _("Video recording started.")); }, NULL); - ogrRegGeneralCallback(OGR_CBT_WAIT_RECORDING, - [] (void* user_data) { MessageQueue::add - (MessageQueue::MT_GENERIC, _("Please wait while encoding is finished." - )); }, NULL); ogrRegStringCallback(OGR_CBT_ERROR_RECORDING, [](const char* s, void* user_data) { Log::error("openglrecorder", "%s", s); }, NULL); @@ -653,22 +649,9 @@ void IrrDriver::initDevice() [] (const char* s, void* user_data) { MessageQueue::add (MessageQueue::MT_GENERIC, _("Video saved in \"%s\".", s)); }, NULL); - static std::chrono::high_resolution_clock::time_point tp; ogrRegIntCallback(OGR_CBT_PROGRESS_RECORDING, [] (const int i, void* user_data) - { - std::chrono::high_resolution_clock::time_point* timer = - (std::chrono::high_resolution_clock::time_point*)user_data; - auto rate = std::chrono::high_resolution_clock::now() - - *timer; - float t = std::chrono::duration_cast >(rate).count(); - if (t > 3.0f) - { - *timer = std::chrono::high_resolution_clock::now(); - Log::info("Recorder", "%d%% of video encoding finished", i); - } - }, &tp); + { MessageQueue::showProgressBar(i, _("Encoding progress:")); }, NULL); #endif diff --git a/src/guiengine/message_queue.cpp b/src/guiengine/message_queue.cpp index 2063d1e2a..660c31c4f 100644 --- a/src/guiengine/message_queue.cpp +++ b/src/guiengine/message_queue.cpp @@ -22,6 +22,7 @@ #include "graphics/irr_driver.hpp" #include "guiengine/engine.hpp" +#include "guiengine/scalable_font.hpp" #include "guiengine/skin.hpp" #include "utils/synchronised.hpp" #include "utils/translation.hpp" @@ -30,23 +31,66 @@ #include "IGUIEnvironment.h" #include "IGUIStaticText.h" +#include + using namespace GUIEngine; namespace MessageQueue { // ============================================================================ -/** The area which the message is drawn. */ -core::recti g_area; +/** The label widget used to show the current message. */ +SkinWidgetContainer* g_container = NULL; // ============================================================================ -/** A small helper class to store and sort messages to be displayed. */ +/** A base class for any messages. */ class Message { +protected: + /** The message. */ + core::stringw m_message; + + /** If this < 0, remove this message from queue. */ + float m_display_timer; + + /** The area which the message is drawn. */ + core::recti m_area; + +private: + /** Tell if this message has been initialized. */ + bool m_inited; + +public: + Message(float timer) : m_display_timer(timer), m_inited(false) {} + // ------------------------------------------------------------------------ + virtual ~Message() {} + // ------------------------------------------------------------------------ + virtual MessageQueue::MessageType getMessageType() const = 0; + // ------------------------------------------------------------------------ + virtual void init() = 0; + // ------------------------------------------------------------------------ + virtual void draw(float dt) + { + if (!m_inited) + { + m_inited = true; + init(); + } + m_display_timer -= dt; + } + // ------------------------------------------------------------------------ + bool canBeRemoved() const { return m_display_timer < 0.0f; } + // ------------------------------------------------------------------------ + virtual void remove() {} + +}; + +// ============================================================================ +/** A small helper class to store and sort text messages to be displayed. */ +class TextMessage : public Message +{ private: /** The type of the message. */ MessageQueue::MessageType m_message_type; - /** The message. */ - core::stringw m_message; /** The render type of the message: either achievement-message::neutral * or friend-message::neutral. */ @@ -56,42 +100,38 @@ private: gui::IGUIStaticText* m_text; public: - Message(MessageQueue::MessageType mt, const core::stringw &message) + TextMessage(MessageQueue::MessageType mt, const core::stringw &message) : + Message(5.0f) { m_message_type = mt; m_message = message; m_text = NULL; - if(mt==MessageQueue::MT_ACHIEVEMENT) + assert(mt != MessageQueue::MT_PROGRESS); + if (mt == MessageQueue::MT_ACHIEVEMENT) m_render_type = "achievement-message::neutral"; - else if (mt==MessageQueue::MT_ERROR) + else if (mt == MessageQueue::MT_ERROR) m_render_type = "error-message::neutral"; - else if (mt==MessageQueue::MT_GENERIC) + else if (mt == MessageQueue::MT_GENERIC) m_render_type = "generic-message::neutral"; else m_render_type = "friend-message::neutral"; } // Message // ------------------------------------------------------------------------ - ~Message() + ~TextMessage() { assert(m_text != NULL); m_text->drop(); } - /** Returns the message. */ - const core::stringw & getMessage() const { return m_message; } // ------------------------------------------------------------------------ - /** Returns the type of the message (achievement or friend). */ - MessageQueue::MessageType getMessageType() const { return m_message_type; } - // ------------------------------------------------------------------------ - /** Returns the render type: either achievement-message::neutral or - * friend-message::neutral (see skin for details). */ - const std::string &getRenderType() const - { - return m_render_type; - } + /** Returns the type of the message.*/ + virtual MessageQueue::MessageType getMessageType() const + { return m_message_type; } // ------------------------------------------------------------------------ /** Init the message text, do linebreak as required. */ - void init() + virtual void init() { + if (m_text) + m_text->drop(); const GUIEngine::BoxRenderParams &brp = GUIEngine::getSkin()->getBoxRenderParams(m_render_type); const unsigned width = irr_driver->getActualScreenSize().Width; @@ -107,21 +147,25 @@ public: dim.Width += brp.m_left_border + brp.m_right_border; int x = (width - dim.Width) / 2; int y = height - int(1.5f * dim.Height); - g_area = irr::core::recti(x, y, x + dim.Width, y + dim.Height); - m_text->setRelativePosition(g_area); + m_area = irr::core::recti(x, y, x + dim.Width, y + dim.Height); + m_text->setRelativePosition(m_area); m_text->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); m_text->grab(); m_text->remove(); } // ------------------------------------------------------------------------ /** Draw the message. */ - void draw() + virtual void draw(float dt) { + Message::draw(dt); + GUIEngine::getSkin()->drawMessage(g_container, m_area, m_render_type); assert(m_text != NULL); m_text->draw(); } + // ------------------------------------------------------------------------ + virtual void remove() { delete this; } -}; // class Message +}; // class TextMessage // ============================================================================ /** A function class to compare messages, required for priority_queue. */ @@ -142,30 +186,87 @@ public: Synchronised, CompareMessages> > g_all_messages; -/** How long the current message has been displayed. The special value - * -1 indicates that a new message was added when the queue was empty. */ -float g_current_display_time = -1.0f; - -/** How long the current message should be displaed. */ -float g_max_display_time = 5.0f; - -/** The label widget used to show the current message. */ -SkinWidgetContainer *g_container = NULL; +// ============================================================================ +/** Add any message to the message queue. + * \param message Any message. + */ +void privateAdd(Message* m) +{ + g_all_messages.lock(); + g_all_messages.getData().push(m); + g_all_messages.unlock(); +} // privateAdd // ============================================================================ - -void createLabel(Message *message) +/** A class which display a progress bar in game, only one can be displayed. */ +class ProgressBarMessage : public Message { - if(!g_container) - g_container = new SkinWidgetContainer(); +private: + std::atomic_int_fast8_t m_progress_value; - g_current_display_time = 0.0f; - // Maybe make this time dependent on message length as well? - g_max_display_time = 5.0f; - message->init(); -} // createLabel + bool m_showing; -// ---------------------------------------------------------------------------- + SkinWidgetContainer m_swc; +public: + ProgressBarMessage() : + Message(9999999.9f) + { + m_progress_value.store(0); + m_showing = false; + } // ProgressBarMessage + // ------------------------------------------------------------------------ + ~ProgressBarMessage() {} + // ------------------------------------------------------------------------ + /** Returns the type of the message.*/ + virtual MessageQueue::MessageType getMessageType() const + { return MT_PROGRESS; } + // ------------------------------------------------------------------------ + virtual void init() + { + const unsigned width = irr_driver->getActualScreenSize().Width; + const unsigned height = irr_driver->getActualScreenSize().Height; + core::dimension2du dim(width * 0.75f, height * 0.05f); + int x = (width - dim.Width) / 2; + int y = height - int(1.5f * dim.Height); + m_area = irr::core::recti(x, y, x + dim.Width, + y + dim.Height); + } + // ------------------------------------------------------------------------ + virtual void draw(float dt) + { + Message::draw(dt); + m_display_timer = 9999999.9f; + GUIEngine::getSkin()->drawProgress(&m_swc, m_area, + m_progress_value.load()); + video::SColor color(255, 0, 0, 0); + GUIEngine::getFont()->draw(m_message, m_area, color, true, true); + if (m_progress_value.load() >= 100) + { + m_display_timer = -1.0f; + m_showing = false; + } + } + // ------------------------------------------------------------------------ + void setProgress(int progress, const wchar_t* msg) + { + if (progress < 0) + return; + if (progress > 100) + progress = 100; + m_progress_value.store((int_fast8_t)progress); + if (!m_showing && progress == 0) + { + m_showing = true; + m_message = msg; + privateAdd(this); + } + } +}; // class ProgressBarMessage + +// ============================================================================ +/** One instance of progress bar. */ +ProgressBarMessage g_progress_bar_msg; +// ============================================================================ /** Called when the screen resolution is changed to compute the new * position of the message. */ void updatePosition() @@ -177,28 +278,19 @@ void updatePosition() g_all_messages.unlock(); return; } - Message *last = g_all_messages.getData().top(); - createLabel(last); + g_all_messages.getData().top()->init(); g_all_messages.unlock(); } // updatePosition // ---------------------------------------------------------------------------- -/** Adds a message to the message queue. +/** Adds a Text message to the message queue. * \param mt The MessageType of the message. * \param message The actual message. */ void add(MessageType mt, const irr::core::stringw &message) { - Message *m = new Message(mt, message); - g_all_messages.lock(); - if (g_all_messages.getData().empty()) - { - // Indicate that there is a new message, which should - // which needs a new label etc. to be computed. - g_current_display_time =-1.0f; - } - g_all_messages.getData().push(m); - g_all_messages.unlock(); + Message *m = new TextMessage(mt, message); + privateAdd(m); } // add // ---------------------------------------------------------------------------- @@ -210,40 +302,41 @@ void add(MessageType mt, const irr::core::stringw &message) */ void update(float dt) { + if (!g_container) + g_container = new SkinWidgetContainer(); + g_all_messages.lock(); bool empty = g_all_messages.getData().empty(); - g_all_messages.unlock(); - if (empty) return; - - g_all_messages.lock(); - g_current_display_time += dt; - if (g_current_display_time > g_max_display_time) + if (empty) + { + g_all_messages.unlock(); + return; + } + + Message* current = g_all_messages.getData().top(); + current->draw(dt); + + if (current->canBeRemoved()) { - Message *last = g_all_messages.getData().top(); g_all_messages.getData().pop(); - delete last; - if (g_all_messages.getData().empty()) - { - g_all_messages.unlock(); - return; - } - g_current_display_time = -1.0f; - } - - Message *current = g_all_messages.getData().top(); - // Create new data for the display. - if (g_current_display_time < 0) - { - createLabel(current); + current->remove(); } g_all_messages.unlock(); - GUIEngine::getSkin()->drawMessage(g_container, g_area, - current->getRenderType()); - current->draw(); - } // update +// ---------------------------------------------------------------------------- +/** The time a user firstly call this function with a zero progress, a progress + * bar will display in the game, the time the value of progress become 100, + * the progress bar will disappear. So make sure only 1 progress bar is being + * used each time. + * \param progress Progress from 0 to 100. + */ +void showProgressBar(int progress, const wchar_t* msg) +{ + g_progress_bar_msg.setProgress(progress, msg); +} // showProgressBar + } // namespace GUIEngine // ---------------------------------------------------------------------------- diff --git a/src/guiengine/message_queue.hpp b/src/guiengine/message_queue.hpp index e6f027522..863f3d96e 100644 --- a/src/guiengine/message_queue.hpp +++ b/src/guiengine/message_queue.hpp @@ -34,9 +34,17 @@ namespace MessageQueue * different look. This type is used to sort the messages, so it is * important that messages that need to be shown as early as possible * will be listed last (i.e. have highest priority). */ - enum MessageType { MT_FRIEND, MT_ACHIEVEMENT, MT_ERROR, MT_GENERIC}; + enum MessageType + { + MT_FRIEND, + MT_ACHIEVEMENT, + MT_ERROR, + MT_GENERIC, + MT_PROGRESS + }; void add(MessageType mt, const core::stringw &message); + void showProgressBar(int progress, const wchar_t* msg); void updatePosition(); void update(float dt); diff --git a/src/guiengine/skin.cpp b/src/guiengine/skin.cpp index 1ca7cabc4..6de386ca7 100644 --- a/src/guiengine/skin.cpp +++ b/src/guiengine/skin.cpp @@ -839,6 +839,19 @@ void Skin::drawProgress(Widget* w, const core::recti &rect, } } // drawProgress +// ---------------------------------------------------------------------------- +void Skin::drawProgress(SkinWidgetContainer* swc, + const core::rect< s32 > &rect, int progress) +{ + drawBoxFromStretchableTexture(swc, rect, + SkinConfig::m_render_params["progress::neutral"]); + core::recti rect2 = rect; + rect2.LowerRightCorner.X -= (rect.getWidth()) + - progress * rect.getWidth() / 100; + drawBoxFromStretchableTexture(swc, rect2, + SkinConfig::m_render_params["progress::fill"]); +} // drawProgress + // ---------------------------------------------------------------------------- /** * @param focused whether this element is focus by the master player (focus diff --git a/src/guiengine/skin.hpp b/src/guiengine/skin.hpp index 208a2ad21..0fc4dc135 100644 --- a/src/guiengine/skin.hpp +++ b/src/guiengine/skin.hpp @@ -336,6 +336,8 @@ namespace GUIEngine void drawBgImage(); void drawBGFadeColor(); void drawBadgeOn(const Widget* widget, const core::rect& rect); + void drawProgress(SkinWidgetContainer* swc, + const core::rect< s32 > &rect, int progress); // irrlicht's callbacks virtual void draw2DRectangle (gui::IGUIElement *element, From 2da86cab087aa30330f8d94d0a3d3c3f33f9674f Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sun, 16 Apr 2017 21:17:13 -0400 Subject: [PATCH 12/14] Cleanup hack that seems unecessary --- src/guiengine/skin.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/guiengine/skin.cpp b/src/guiengine/skin.cpp index 6de386ca7..706e6ede8 100644 --- a/src/guiengine/skin.cpp +++ b/src/guiengine/skin.cpp @@ -819,12 +819,11 @@ void Skin::drawProgress(Widget* w, const core::recti &rect, drawBoxFromStretchableTexture(w, rect, SkinConfig::m_render_params["progress::neutral"], w->m_deactivated); - //the " - 10" is a dirty hack to avoid to have the right arrow - // before the left one - //FIXME + core::recti rect2 = rect; - rect2.LowerRightCorner.X -= (rect.getWidth() - 10) - - progress->getValue()*rect.getWidth()/100; + + rect2.LowerRightCorner.X -= rect.getWidth() + - progress->getValue()*rect.getWidth() / 100; drawBoxFromStretchableTexture(w, rect2, SkinConfig::m_render_params["progress::fill"], From 6911db0ac288d9c06fbb30ad06ebe20c17cc0dd1 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 17 Apr 2017 09:28:41 +0800 Subject: [PATCH 13/14] Remove duplicated code --- src/guiengine/message_queue.cpp | 2 +- src/guiengine/skin.cpp | 30 +++++++----------------------- src/guiengine/skin.hpp | 5 +++-- 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/guiengine/message_queue.cpp b/src/guiengine/message_queue.cpp index 660c31c4f..d4fec45c3 100644 --- a/src/guiengine/message_queue.cpp +++ b/src/guiengine/message_queue.cpp @@ -236,7 +236,7 @@ public: { Message::draw(dt); m_display_timer = 9999999.9f; - GUIEngine::getSkin()->drawProgress(&m_swc, m_area, + GUIEngine::getSkin()->drawProgressBarInScreen(&m_swc, m_area, m_progress_value.load()); video::SColor color(255, 0, 0, 0); GUIEngine::getFont()->draw(m_message, m_area, color, true, true); diff --git a/src/guiengine/skin.cpp b/src/guiengine/skin.cpp index 706e6ede8..012d3778b 100644 --- a/src/guiengine/skin.cpp +++ b/src/guiengine/skin.cpp @@ -816,39 +816,23 @@ void Skin::drawProgress(Widget* w, const core::recti &rect, else { ProgressBarWidget * progress = (ProgressBarWidget*)w; - drawBoxFromStretchableTexture(w, rect, - SkinConfig::m_render_params["progress::neutral"], - w->m_deactivated); - - core::recti rect2 = rect; - - rect2.LowerRightCorner.X -= rect.getWidth() - - progress->getValue()*rect.getWidth() / 100; - - drawBoxFromStretchableTexture(w, rect2, - SkinConfig::m_render_params["progress::fill"], - w->m_deactivated); -#if 0 - draw2DImage( - SkinConfig::m_render_params["progress::fill"].getImage(), - sized_rect, core::recti(0,0,progress->m_w, progress->m_h), - 0 /* no clipping */, colors, true); -#endif - + drawProgressBarInScreen(w, rect, progress->getValue(), + w->m_deactivated); } } // drawProgress // ---------------------------------------------------------------------------- -void Skin::drawProgress(SkinWidgetContainer* swc, - const core::rect< s32 > &rect, int progress) +void Skin::drawProgressBarInScreen(SkinWidgetContainer* swc, + const core::rect< s32 > &rect, int progress, + bool deactivated) { drawBoxFromStretchableTexture(swc, rect, - SkinConfig::m_render_params["progress::neutral"]); + SkinConfig::m_render_params["progress::neutral"], deactivated); core::recti rect2 = rect; rect2.LowerRightCorner.X -= (rect.getWidth()) - progress * rect.getWidth() / 100; drawBoxFromStretchableTexture(swc, rect2, - SkinConfig::m_render_params["progress::fill"]); + SkinConfig::m_render_params["progress::fill"], deactivated); } // drawProgress // ---------------------------------------------------------------------------- diff --git a/src/guiengine/skin.hpp b/src/guiengine/skin.hpp index 0fc4dc135..a10a0ac8e 100644 --- a/src/guiengine/skin.hpp +++ b/src/guiengine/skin.hpp @@ -336,8 +336,9 @@ namespace GUIEngine void drawBgImage(); void drawBGFadeColor(); void drawBadgeOn(const Widget* widget, const core::rect& rect); - void drawProgress(SkinWidgetContainer* swc, - const core::rect< s32 > &rect, int progress); + void drawProgressBarInScreen(SkinWidgetContainer* swc, + const core::rect< s32 > &rect, + int progress, bool deactivated = false); // irrlicht's callbacks virtual void draw2DRectangle (gui::IGUIElement *element, From 0e15e6de75e67e0cefd96d766102f60d131892a7 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 17 Apr 2017 09:47:03 +0800 Subject: [PATCH 14/14] Update readme for libopenglrecorder --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index cabde871e..2c45116ed 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,12 @@ libcurl4-gnutls-dev libfreetype6-dev libfribidi-dev libgl1-mesa-dev \ libjpeg-dev libogg-dev libopenal-dev libpng-dev libvorbis-dev libxrandr-dev \ mesa-common-dev pkg-config zlib1g-dev ``` +### In-game recorder + +In order to build the in-game recorder for STK, you have to install +libopenglrecorder from your distribution or compile it yourself, get it [here](https://github.com/Benau/libopenglrecorder): +Compilation instruction is explained there, if you don't need such feature, +pass `-DBUILD_RECORDER=off` to cmake. ### Compiling