From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Main/include/WebMWriter.h | 579 +++++++++++++++++++++++++++++++++++++ 1 file changed, 579 insertions(+) create mode 100644 src/VBox/Main/include/WebMWriter.h (limited to 'src/VBox/Main/include/WebMWriter.h') diff --git a/src/VBox/Main/include/WebMWriter.h b/src/VBox/Main/include/WebMWriter.h new file mode 100644 index 00000000..67d4b128 --- /dev/null +++ b/src/VBox/Main/include/WebMWriter.h @@ -0,0 +1,579 @@ +/* $Id: WebMWriter.h $ */ +/** @file + * WebMWriter.h - WebM container handling. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * 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, in version 3 of the + * License. + * + * 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, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifndef MAIN_INCLUDED_WebMWriter_h +#define MAIN_INCLUDED_WebMWriter_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include +#include +#include +#include + +#include "VBox/com/VirtualBox.h" +#include + +#include "EBMLWriter.h" +#include "EBML_MKV.h" + +#include +#include +#include + +#ifdef VBOX_WITH_LIBVPX +# ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4668) /* vpx_codec.h(64) : warning C4668: '__GNUC__' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */ +# include +# pragma warning(pop) +# else +# include +# endif +#endif /* VBOX_WITH_LIBVPX */ + +/** No flags specified. */ +#define VBOX_WEBM_BLOCK_FLAG_NONE 0 +/** Invisible block which can be skipped. */ +#define VBOX_WEBM_BLOCK_FLAG_INVISIBLE 0x08 +/** The block marks a key frame. */ +#define VBOX_WEBM_BLOCK_FLAG_KEY_FRAME 0x80 + +/** The default timecode scale factor for WebM -- all timecodes in the segments are expressed in ms. + * This allows every cluster to have blocks with positive values up to 32.767 seconds. */ +#define VBOX_WEBM_TIMECODESCALE_FACTOR_MS 1000000 + +/** Maximum time (in ms) a cluster can store. */ +#define VBOX_WEBM_CLUSTER_MAX_LEN_MS INT16_MAX + +/** Maximum time a block can store. + * With signed 16-bit timecodes and a default timecode scale of 1ms per unit this makes 65536ms. */ +#define VBOX_WEBM_BLOCK_MAX_LEN_MS UINT16_MAX + +#ifdef VBOX_WITH_LIBVORBIS +# pragma pack(push) +# pragma pack(1) + /** Ogg Vorbis codec private data within the MKV (WEBM) container. + * Taken from: https://www.matroska.org/technical/codec_specs.html */ + typedef struct WEBMOGGVORBISPRIVDATA + { + WEBMOGGVORBISPRIVDATA(uint32_t a_cbHdrIdent, uint32_t a_cbHdrComments, uint32_t a_cbHdrSetup) + : cbHdrIdent(a_cbHdrIdent) + , cbHdrComments(a_cbHdrComments) + { + RT_NOREF(a_cbHdrSetup); + + /* We supply 3 headers total: The "real" header, comments header + setup header. */ + cHeaders = 3 /* Headers */ - 1; /* Note: Always "minus one" here. */ + + Assert(a_cbHdrIdent <= UINT8_MAX); + Assert(a_cbHdrComments <= UINT8_MAX); + Assert(a_cbHdrSetup <= _8K); + Assert(a_cbHdrIdent + a_cbHdrComments + a_cbHdrSetup <= sizeof(abHdr)); + } + + /** Number of private headers - 1. */ + uint8_t cHeaders; + /** Size of identification header (in bytes). */ + uint8_t cbHdrIdent; + /** < Size of comments header (in bytes). */ + uint8_t cbHdrComments; + /** < Header code area. */ + uint8_t abHdr[UINT8_MAX /* Header */ + UINT8_MAX /* Comments header */ + _8K /* Setup header */]; + + } WEBMOGGVORBISPRIVDATA, *PWEBMOGGVORBISPRIVDATA; +# pragma pack(pop) +#endif + +class WebMWriter : public EBMLWriter +{ + +public: + + /** Defines an absolute WebM timecode (Block + Cluster). */ + typedef uint64_t WebMTimecodeAbs; + + /** Defines a relative WebM timecode (Block). */ + typedef uint16_t WebMTimecodeRel; + + /** Defines the WebM block flags data type. */ + typedef uint8_t WebMBlockFlags; + + /** + * Track type enumeration. + */ + enum WebMTrackType + { + /** Unknown / invalid type. */ + WebMTrackType_Invalid = 0, + /** Only writes audio. */ + WebMTrackType_Audio = 1, + /** Only writes video. */ + WebMTrackType_Video = 2 + }; + + struct WebMTrack; + + /** + * Structure for defining a WebM simple block. + */ + struct WebMSimpleBlock + { + WebMSimpleBlock(WebMTrack *a_pTrack, + WebMTimecodeAbs a_tcAbsPTSMs, const void *a_pvData, size_t a_cbData, WebMBlockFlags a_fFlags) + : pTrack(a_pTrack) + { + Data.tcAbsPTSMs = a_tcAbsPTSMs; + Data.cb = a_cbData; + Data.fFlags = a_fFlags; + + if (Data.cb) + { + Data.pv = RTMemDup(a_pvData, a_cbData); + if (!Data.pv) + throw; + } + } + + virtual ~WebMSimpleBlock() + { + if (Data.pv) + { + Assert(Data.cb); + RTMemFree(Data.pv); + } + } + + WebMTrack *pTrack; + + /** Actual simple block data. */ + struct + { + WebMTimecodeAbs tcAbsPTSMs; + WebMTimecodeRel tcRelToClusterMs; + void *pv; + size_t cb; + WebMBlockFlags fFlags; + } Data; + }; + + /** A simple block queue.*/ + typedef std::queue WebMSimpleBlockQueue; + + /** Structure for queuing all simple blocks bound to a single timecode. + * This can happen if multiple tracks are being involved. */ + struct WebMTimecodeBlocks + { + WebMTimecodeBlocks(void) + : fClusterNeeded(false) + , fClusterStarted(false) { } + + /** The actual block queue for this timecode. */ + WebMSimpleBlockQueue Queue; + /** Whether a new cluster is needed for this timecode or not. */ + bool fClusterNeeded; + /** Whether a new cluster already has been started for this timecode or not. */ + bool fClusterStarted; + + /** + * Enqueues a simple block into the internal queue. + * + * @param a_pBlock Block to enqueue and take ownership of. + */ + void Enqueue(WebMSimpleBlock *a_pBlock) + { + Queue.push(a_pBlock); + + if (a_pBlock->Data.fFlags & VBOX_WEBM_BLOCK_FLAG_KEY_FRAME) + fClusterNeeded = true; + } + }; + + /** A block map containing all currently queued blocks. + * The key specifies a unique timecode, whereas the value + * is a queue of blocks which all correlate to the key (timecode). */ + typedef std::map WebMBlockMap; + + /** + * Structure for defining a WebM (encoding) queue. + */ + struct WebMQueue + { + WebMQueue(void) + : tcAbsLastBlockWrittenMs(0) + , tsLastProcessedMs(0) { } + + /** Blocks as FIFO (queue). */ + WebMBlockMap Map; + /** Absolute timecode (in ms) of last written block to queue. */ + WebMTimecodeAbs tcAbsLastBlockWrittenMs; + /** Time stamp (in ms) of when the queue was processed last. */ + uint64_t tsLastProcessedMs; + }; + + /** + * Structure for keeping a WebM track entry. + */ + struct WebMTrack + { + WebMTrack(WebMTrackType a_enmType, PRECORDINGCODEC pTheCodec, uint8_t a_uTrack, uint64_t a_offID) + : enmType(a_enmType) + , pCodec(pTheCodec) + , uTrack(a_uTrack) + , offUUID(a_offID) + , cTotalBlocks(0) + , tcAbsLastWrittenMs(0) + { + uUUID = RTRandU32(); + } + + /** The type of this track. */ + WebMTrackType enmType; + /** Pointer to codec data to use. */ + PRECORDINGCODEC pCodec; + /** Track parameters. */ + union + { + struct + { + /** Sample rate of input data. */ + uint32_t uHz; + /** Duration of the frame in samples (per channel). + * Valid frame size are: + * + * ms Frame size + * 2.5 120 + * 5 240 + * 10 480 + * 20 (Default) 960 + * 40 1920 + * 60 2880 + */ + uint16_t framesPerBlock; + /** How many milliseconds (ms) one written (simple) block represents. */ + uint16_t msPerBlock; + } Audio; + }; + /** This track's track number. Also used as key in track map. */ + uint8_t uTrack; + /** The track's "UUID". + * Needed in case this track gets mux'ed with tracks from other files. Not really unique though. */ + uint32_t uUUID; + /** Absolute offset in file of track UUID. + * Needed to write the hash sum within the footer. */ + uint64_t offUUID; + /** Total number of blocks. */ + uint64_t cTotalBlocks; + /** Absoute timecode (in ms) of last write. */ + WebMTimecodeAbs tcAbsLastWrittenMs; + }; + + /** + * Structure for a single cue point track position entry. + */ + struct WebMCueTrackPosEntry + { + WebMCueTrackPosEntry(uint64_t a_offCluster) + : offCluster(a_offCluster) { } + + /** Offset (in bytes) of the related cluster containing the given position. */ + uint64_t offCluster; + }; + + /** Map for keeping track position entries for a single cue point. + * The key is the track number (*not* UUID!). */ + typedef std::map WebMCueTrackPosMap; + + /** + * Structure for keeping a cue point. + */ + struct WebMCuePoint + { + WebMCuePoint(WebMTimecodeAbs a_tcAbs) + : tcAbs(a_tcAbs) { } + + virtual ~WebMCuePoint() + { + Clear(); + } + + void Clear(void) + { + WebMCueTrackPosMap::iterator itTrackPos = Pos.begin(); + while (itTrackPos != Pos.end()) + { + WebMCueTrackPosEntry *pTrackPos = itTrackPos->second; + AssertPtr(pTrackPos); + delete pTrackPos; + + Pos.erase(itTrackPos); + itTrackPos = Pos.begin(); + } + + Assert(Pos.empty()); + } + + /** Map containing all track positions for this specific cue point. */ + WebMCueTrackPosMap Pos; + /** Absolute time code according to the segment time base. */ + WebMTimecodeAbs tcAbs; + }; + + /** List of cue points. */ + typedef std::list WebMCuePointList; + + /** + * Structure for keeping a WebM cluster entry. + */ + struct WebMCluster + { + WebMCluster(void) + : uID(0) + , offStart(0) + , fOpen(false) + , tcAbsStartMs(0) + , cBlocks(0) { } + + /** This cluster's ID. */ + uint64_t uID; + /** Absolute offset (in bytes) of this cluster. + * Needed for seeking info table. */ + uint64_t offStart; + /** Whether this cluster element is opened currently. */ + bool fOpen; + /** Absolute timecode (in ms) when this cluster starts. */ + WebMTimecodeAbs tcAbsStartMs; + /** Absolute timecode (in ms) of when last written to this cluster. */ + WebMTimecodeAbs tcAbsLastWrittenMs; + /** Number of (simple) blocks in this cluster. */ + uint64_t cBlocks; + }; + + /** + * Structure for keeping a WebM segment entry. + * + * Current we're only using one segment. + */ + struct WebMSegment + { + WebMSegment(void) + : m_tcAbsStartMs(0) + , m_tcAbsLastWrittenMs(0) + , m_offStart(0) + , m_offInfo(0) + , m_offSeekInfo(0) + , m_offTracks(0) + , m_offCues(0) + , m_cClusters(0) + { + m_uTimecodeScaleFactor = VBOX_WEBM_TIMECODESCALE_FACTOR_MS; + + LogFunc(("Default timecode scale is: %RU64ns\n", m_uTimecodeScaleFactor)); + } + + virtual ~WebMSegment() + { + uninit(); + } + + /** + * Initializes a segment. + * + * @returns VBox status code. + */ + int init(void) + { + return RTCritSectInit(&m_CritSect); + } + + /** + * Uninitializes a segment. + */ + void uninit(void) + { + clear(); + + RTCritSectDelete(&m_CritSect); + } + + /** + * Clear the segment's data by removing (and freeing) all data. + */ + void clear(void) + { + WebMCuePointList::iterator itCuePoint = m_lstCuePoints.begin(); + while (itCuePoint != m_lstCuePoints.end()) + { + WebMCuePoint *pCuePoint = (*itCuePoint); + AssertPtr(pCuePoint); + delete pCuePoint; + + m_lstCuePoints.erase(itCuePoint); + itCuePoint = m_lstCuePoints.begin(); + } + + Assert(m_lstCuePoints.empty()); + } + + /** Critical section for serializing access to this segment. */ + RTCRITSECT m_CritSect; + + /** The timecode scale factor of this segment. */ + uint64_t m_uTimecodeScaleFactor; + + /** Absolute timecode (in ms) when starting this segment. */ + WebMTimecodeAbs m_tcAbsStartMs; + /** Absolute timecode (in ms) of last write. */ + WebMTimecodeAbs m_tcAbsLastWrittenMs; + + /** Absolute offset (in bytes) of CurSeg. */ + uint64_t m_offStart; + /** Absolute offset (in bytes) of general info. */ + uint64_t m_offInfo; + /** Absolute offset (in bytes) of seeking info. */ + uint64_t m_offSeekInfo; + /** Absolute offset (in bytes) of tracks. */ + uint64_t m_offTracks; + /** Absolute offset (in bytes) of cues table. */ + uint64_t m_offCues; + /** List of cue points. Needed for seeking table. */ + WebMCuePointList m_lstCuePoints; + + /** Total number of clusters. */ + uint64_t m_cClusters; + + /** Map of tracks. + * The key marks the track number (*not* the UUID!). */ + std::map m_mapTracks; + + /** Current cluster which is being handled. + * + * Note that we don't need (and shouldn't need, as this can be a *lot* of data!) a + * list of all clusters. */ + WebMCluster m_CurCluster; + + WebMQueue m_queueBlocks; + + } m_CurSeg; + + /** Audio codec to use. */ + RecordingAudioCodec_T m_enmAudioCodec; + /** Video codec to use. */ + RecordingVideoCodec_T m_enmVideoCodec; + + /** Whether we're currently in the tracks section. */ + bool m_fInTracksSection; + + /** Size of timecodes (in bytes). */ + size_t m_cbTimecode; + /** Maximum value a timecode can have. */ + uint32_t m_uTimecodeMax; + +#ifdef VBOX_WITH_LIBVPX + /** + * Block data for VP8-encoded video data. + */ + struct BlockData_VP8 + { + const vpx_codec_enc_cfg_t *pCfg; + const vpx_codec_cx_pkt_t *pPkt; + }; +#endif /* VBOX_WITH_LIBVPX */ + + /** + * Block data for encoded audio data. + */ + struct BlockData_Audio + { + /** Pointer to encoded audio data. */ + const void *pvData; + /** Size (in bytes) of encoded audio data. */ + size_t cbData; + /** PTS (in ms) of encoded audio data. */ + uint64_t uPTSMs; + }; + +public: + + WebMWriter(); + + virtual ~WebMWriter(); + +public: + + int OpenEx(const char *a_pszFilename, PRTFILE a_phFile, + RecordingAudioCodec_T a_enmAudioCodec, RecordingVideoCodec_T a_enmVideoCodec); + + int Open(const char *a_pszFilename, uint64_t a_fOpen, + RecordingAudioCodec_T a_enmAudioCodec, RecordingVideoCodec_T a_enmVideoCodec); + + int Close(void); + + int AddAudioTrack(PRECORDINGCODEC pCodec, uint16_t uHz, uint8_t cChannels, uint8_t cBits, uint8_t *puTrack); + + int AddVideoTrack(PRECORDINGCODEC pCodec, uint16_t uWidth, uint16_t uHeight, uint32_t uFPS, uint8_t *puTrack); + + int WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs, WebMBlockFlags uFlags); + + const com::Utf8Str& GetFileName(void); + + uint64_t GetFileSize(void); + + uint64_t GetAvailableSpace(void); + + /** + * Returns the number of written WebM clusters. + * + * @returns Number of written WebM clusters; 0 when no clusters written (empty file). + */ + uint64_t GetClusters(void) const { return m_CurSeg.m_cClusters; } + +protected: + + int init(RecordingAudioCodec_T a_enmAudioCodec, RecordingVideoCodec_T a_enmVideoCodec); + + void destroy(void); + + int writeHeader(void); + + void writeSeekHeader(void); + + int writeFooter(void); + + int writeSimpleBlockEBML(WebMTrack *a_pTrack, WebMSimpleBlock *a_pBlock); + + int writeSimpleBlockQueued(WebMTrack *a_pTrack, WebMSimpleBlock *a_pBlock); + + int processQueue(WebMQueue *pQueue, bool fForce); + +protected: + + typedef std::map WebMTracks; +}; + +#endif /* !MAIN_INCLUDED_WebMWriter_h */ -- cgit v1.2.3