VirtualBox

source: vbox/trunk/src/VBox/Main/include/WebMWriter.h

Last change on this file was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
RevLine 
[69684]1/* $Id: WebMWriter.h 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * WebMWriter.h - WebM container handling.
4 */
5
6/*
[98103]7 * Copyright (C) 2013-2023 Oracle and/or its affiliates.
[69684]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[69684]26 */
27
[76562]28#ifndef MAIN_INCLUDED_WebMWriter_h
29#define MAIN_INCLUDED_WebMWriter_h
[76487]30#ifndef RT_WITHOUT_PRAGMA_ONCE
31# pragma once
32#endif
[69684]33
[96175]34#include <iprt/buildconfig.h>
35#include <iprt/mem.h>
36#include <iprt/rand.h>
37#include <iprt/string.h>
38
39#include "VBox/com/VirtualBox.h"
40#include <VBox/version.h>
41
[69684]42#include "EBMLWriter.h"
[82422]43#include "EBML_MKV.h"
[69684]44
[76760]45#include <queue>
46#include <map>
47#include <list>
48
[69684]49#ifdef VBOX_WITH_LIBVPX
50# ifdef _MSC_VER
51# pragma warning(push)
52# pragma warning(disable: 4668) /* vpx_codec.h(64) : warning C4668: '__GNUC__' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */
53# include <vpx/vpx_encoder.h>
54# pragma warning(pop)
55# else
56# include <vpx/vpx_encoder.h>
57# endif
58#endif /* VBOX_WITH_LIBVPX */
59
60/** No flags specified. */
61#define VBOX_WEBM_BLOCK_FLAG_NONE 0
62/** Invisible block which can be skipped. */
63#define VBOX_WEBM_BLOCK_FLAG_INVISIBLE 0x08
64/** The block marks a key frame. */
65#define VBOX_WEBM_BLOCK_FLAG_KEY_FRAME 0x80
66
67/** The default timecode scale factor for WebM -- all timecodes in the segments are expressed in ms.
68 * This allows every cluster to have blocks with positive values up to 32.767 seconds. */
69#define VBOX_WEBM_TIMECODESCALE_FACTOR_MS 1000000
70
71/** Maximum time (in ms) a cluster can store. */
72#define VBOX_WEBM_CLUSTER_MAX_LEN_MS INT16_MAX
73
74/** Maximum time a block can store.
75 * With signed 16-bit timecodes and a default timecode scale of 1ms per unit this makes 65536ms. */
76#define VBOX_WEBM_BLOCK_MAX_LEN_MS UINT16_MAX
77
[96175]78#ifdef VBOX_WITH_LIBVORBIS
79# pragma pack(push)
80# pragma pack(1)
81 /** Ogg Vorbis codec private data within the MKV (WEBM) container.
82 * Taken from: https://www.matroska.org/technical/codec_specs.html */
83 typedef struct WEBMOGGVORBISPRIVDATA
84 {
85 WEBMOGGVORBISPRIVDATA(uint32_t a_cbHdrIdent, uint32_t a_cbHdrComments, uint32_t a_cbHdrSetup)
86 : cbHdrIdent(a_cbHdrIdent)
87 , cbHdrComments(a_cbHdrComments)
88 {
[96210]89 RT_NOREF(a_cbHdrSetup);
90
[96175]91 /* We supply 3 headers total: The "real" header, comments header + setup header. */
92 cHeaders = 3 /* Headers */ - 1; /* Note: Always "minus one" here. */
[69684]93
[96175]94 Assert(a_cbHdrIdent <= UINT8_MAX);
95 Assert(a_cbHdrComments <= UINT8_MAX);
96 Assert(a_cbHdrSetup <= _8K);
97 Assert(a_cbHdrIdent + a_cbHdrComments + a_cbHdrSetup <= sizeof(abHdr));
98 }
99
100 /** Number of private headers - 1. */
101 uint8_t cHeaders;
102 /** Size of identification header (in bytes). */
103 uint8_t cbHdrIdent;
104 /** < Size of comments header (in bytes). */
105 uint8_t cbHdrComments;
106 /** < Header code area. */
107 uint8_t abHdr[UINT8_MAX /* Header */ + UINT8_MAX /* Comments header */ + _8K /* Setup header */];
108
109 } WEBMOGGVORBISPRIVDATA, *PWEBMOGGVORBISPRIVDATA;
110# pragma pack(pop)
111#endif
112
[69684]113class WebMWriter : public EBMLWriter
114{
115
116public:
117
[70622]118 /** Defines an absolute WebM timecode (Block + Cluster). */
119 typedef uint64_t WebMTimecodeAbs;
[69684]120
[70622]121 /** Defines a relative WebM timecode (Block). */
122 typedef uint16_t WebMTimecodeRel;
123
[69684]124 /** Defines the WebM block flags data type. */
125 typedef uint8_t WebMBlockFlags;
126
127 /**
128 * Track type enumeration.
129 */
130 enum WebMTrackType
131 {
132 /** Unknown / invalid type. */
133 WebMTrackType_Invalid = 0,
134 /** Only writes audio. */
135 WebMTrackType_Audio = 1,
136 /** Only writes video. */
137 WebMTrackType_Video = 2
138 };
139
140 struct WebMTrack;
141
142 /**
143 * Structure for defining a WebM simple block.
144 */
145 struct WebMSimpleBlock
146 {
147 WebMSimpleBlock(WebMTrack *a_pTrack,
[70622]148 WebMTimecodeAbs a_tcAbsPTSMs, const void *a_pvData, size_t a_cbData, WebMBlockFlags a_fFlags)
[69684]149 : pTrack(a_pTrack)
150 {
[70622]151 Data.tcAbsPTSMs = a_tcAbsPTSMs;
152 Data.cb = a_cbData;
153 Data.fFlags = a_fFlags;
[69684]154
155 if (Data.cb)
156 {
157 Data.pv = RTMemDup(a_pvData, a_cbData);
158 if (!Data.pv)
159 throw;
160 }
161 }
162
163 virtual ~WebMSimpleBlock()
164 {
165 if (Data.pv)
166 {
167 Assert(Data.cb);
168 RTMemFree(Data.pv);
169 }
170 }
171
172 WebMTrack *pTrack;
173
174 /** Actual simple block data. */
175 struct
176 {
[70622]177 WebMTimecodeAbs tcAbsPTSMs;
178 WebMTimecodeRel tcRelToClusterMs;
[69684]179 void *pv;
180 size_t cb;
181 WebMBlockFlags fFlags;
182 } Data;
183 };
184
185 /** A simple block queue.*/
186 typedef std::queue<WebMSimpleBlock *> WebMSimpleBlockQueue;
187
188 /** Structure for queuing all simple blocks bound to a single timecode.
189 * This can happen if multiple tracks are being involved. */
190 struct WebMTimecodeBlocks
191 {
192 WebMTimecodeBlocks(void)
193 : fClusterNeeded(false)
194 , fClusterStarted(false) { }
195
196 /** The actual block queue for this timecode. */
197 WebMSimpleBlockQueue Queue;
198 /** Whether a new cluster is needed for this timecode or not. */
199 bool fClusterNeeded;
200 /** Whether a new cluster already has been started for this timecode or not. */
201 bool fClusterStarted;
202
203 /**
204 * Enqueues a simple block into the internal queue.
205 *
206 * @param a_pBlock Block to enqueue and take ownership of.
207 */
208 void Enqueue(WebMSimpleBlock *a_pBlock)
209 {
210 Queue.push(a_pBlock);
211
212 if (a_pBlock->Data.fFlags & VBOX_WEBM_BLOCK_FLAG_KEY_FRAME)
213 fClusterNeeded = true;
214 }
215 };
216
217 /** A block map containing all currently queued blocks.
218 * The key specifies a unique timecode, whereas the value
219 * is a queue of blocks which all correlate to the key (timecode). */
[70622]220 typedef std::map<WebMTimecodeAbs, WebMTimecodeBlocks> WebMBlockMap;
[69684]221
222 /**
223 * Structure for defining a WebM (encoding) queue.
224 */
225 struct WebMQueue
226 {
227 WebMQueue(void)
[70622]228 : tcAbsLastBlockWrittenMs(0)
229 , tsLastProcessedMs(0) { }
[69684]230
231 /** Blocks as FIFO (queue). */
[70622]232 WebMBlockMap Map;
233 /** Absolute timecode (in ms) of last written block to queue. */
234 WebMTimecodeAbs tcAbsLastBlockWrittenMs;
[69684]235 /** Time stamp (in ms) of when the queue was processed last. */
[70622]236 uint64_t tsLastProcessedMs;
[69684]237 };
238
239 /**
240 * Structure for keeping a WebM track entry.
241 */
242 struct WebMTrack
243 {
[96175]244 WebMTrack(WebMTrackType a_enmType, PRECORDINGCODEC pTheCodec, uint8_t a_uTrack, uint64_t a_offID)
[69684]245 : enmType(a_enmType)
[96175]246 , pCodec(pTheCodec)
[69684]247 , uTrack(a_uTrack)
248 , offUUID(a_offID)
249 , cTotalBlocks(0)
[70622]250 , tcAbsLastWrittenMs(0)
[69684]251 {
252 uUUID = RTRandU32();
253 }
254
255 /** The type of this track. */
[96175]256 WebMTrackType enmType;
257 /** Pointer to codec data to use. */
258 PRECORDINGCODEC pCodec;
[69684]259 /** Track parameters. */
260 union
261 {
262 struct
263 {
264 /** Sample rate of input data. */
265 uint32_t uHz;
266 /** Duration of the frame in samples (per channel).
267 * Valid frame size are:
268 *
269 * ms Frame size
270 * 2.5 120
271 * 5 240
272 * 10 480
273 * 20 (Default) 960
274 * 40 1920
275 * 60 2880
276 */
277 uint16_t framesPerBlock;
278 /** How many milliseconds (ms) one written (simple) block represents. */
279 uint16_t msPerBlock;
280 } Audio;
281 };
282 /** This track's track number. Also used as key in track map. */
283 uint8_t uTrack;
284 /** The track's "UUID".
285 * Needed in case this track gets mux'ed with tracks from other files. Not really unique though. */
286 uint32_t uUUID;
287 /** Absolute offset in file of track UUID.
288 * Needed to write the hash sum within the footer. */
289 uint64_t offUUID;
290 /** Total number of blocks. */
291 uint64_t cTotalBlocks;
[70622]292 /** Absoute timecode (in ms) of last write. */
293 WebMTimecodeAbs tcAbsLastWrittenMs;
[69684]294 };
295
296 /**
[75030]297 * Structure for a single cue point track position entry.
298 */
299 struct WebMCueTrackPosEntry
300 {
301 WebMCueTrackPosEntry(uint64_t a_offCluster)
302 : offCluster(a_offCluster) { }
303
304 /** Offset (in bytes) of the related cluster containing the given position. */
305 uint64_t offCluster;
306 };
307
308 /** Map for keeping track position entries for a single cue point.
309 * The key is the track number (*not* UUID!). */
310 typedef std::map<uint8_t, WebMCueTrackPosEntry *> WebMCueTrackPosMap;
311
312 /**
[69684]313 * Structure for keeping a cue point.
314 */
315 struct WebMCuePoint
316 {
[75030]317 WebMCuePoint(WebMTimecodeAbs a_tcAbs)
318 : tcAbs(a_tcAbs) { }
[69684]319
[75030]320 virtual ~WebMCuePoint()
321 {
322 Clear();
323 }
324
325 void Clear(void)
326 {
327 WebMCueTrackPosMap::iterator itTrackPos = Pos.begin();
328 while (itTrackPos != Pos.end())
329 {
330 WebMCueTrackPosEntry *pTrackPos = itTrackPos->second;
331 AssertPtr(pTrackPos);
332 delete pTrackPos;
333
334 Pos.erase(itTrackPos);
335 itTrackPos = Pos.begin();
336 }
337
338 Assert(Pos.empty());
339 }
340
341 /** Map containing all track positions for this specific cue point. */
342 WebMCueTrackPosMap Pos;
[70622]343 /** Absolute time code according to the segment time base. */
[75030]344 WebMTimecodeAbs tcAbs;
[69684]345 };
346
[75030]347 /** List of cue points. */
348 typedef std::list<WebMCuePoint *> WebMCuePointList;
349
[69684]350 /**
351 * Structure for keeping a WebM cluster entry.
352 */
353 struct WebMCluster
354 {
355 WebMCluster(void)
356 : uID(0)
357 , offStart(0)
358 , fOpen(false)
[70622]359 , tcAbsStartMs(0)
[69684]360 , cBlocks(0) { }
361
362 /** This cluster's ID. */
[70622]363 uint64_t uID;
[69684]364 /** Absolute offset (in bytes) of this cluster.
365 * Needed for seeking info table. */
[70622]366 uint64_t offStart;
[69684]367 /** Whether this cluster element is opened currently. */
[70622]368 bool fOpen;
369 /** Absolute timecode (in ms) when this cluster starts. */
370 WebMTimecodeAbs tcAbsStartMs;
[75041]371 /** Absolute timecode (in ms) of when last written to this cluster. */
372 WebMTimecodeAbs tcAbsLastWrittenMs;
[69684]373 /** Number of (simple) blocks in this cluster. */
[70622]374 uint64_t cBlocks;
[69684]375 };
376
377 /**
378 * Structure for keeping a WebM segment entry.
379 *
380 * Current we're only using one segment.
381 */
382 struct WebMSegment
383 {
384 WebMSegment(void)
[96324]385 : m_tcAbsStartMs(0)
386 , m_tcAbsLastWrittenMs(0)
387 , m_offStart(0)
388 , m_offInfo(0)
389 , m_offSeekInfo(0)
390 , m_offTracks(0)
391 , m_offCues(0)
392 , m_cClusters(0)
[69684]393 {
[96324]394 m_uTimecodeScaleFactor = VBOX_WEBM_TIMECODESCALE_FACTOR_MS;
[69684]395
[96324]396 LogFunc(("Default timecode scale is: %RU64ns\n", m_uTimecodeScaleFactor));
[69684]397 }
398
[75032]399 virtual ~WebMSegment()
400 {
[75047]401 uninit();
[75032]402 }
403
[75047]404 /**
405 * Initializes a segment.
406 *
[96230]407 * @returns VBox status code.
[75047]408 */
[69684]409 int init(void)
410 {
[96324]411 return RTCritSectInit(&m_CritSect);
[69684]412 }
413
[75047]414 /**
415 * Uninitializes a segment.
416 */
417 void uninit(void)
[69684]418 {
[75032]419 clear();
420
[96324]421 RTCritSectDelete(&m_CritSect);
[69684]422 }
423
[75032]424 /**
425 * Clear the segment's data by removing (and freeing) all data.
426 */
427 void clear(void)
428 {
[96324]429 WebMCuePointList::iterator itCuePoint = m_lstCuePoints.begin();
430 while (itCuePoint != m_lstCuePoints.end())
[75032]431 {
432 WebMCuePoint *pCuePoint = (*itCuePoint);
433 AssertPtr(pCuePoint);
434 delete pCuePoint;
435
[96324]436 m_lstCuePoints.erase(itCuePoint);
437 itCuePoint = m_lstCuePoints.begin();
[75032]438 }
439
[96324]440 Assert(m_lstCuePoints.empty());
[75032]441 }
442
[69684]443 /** Critical section for serializing access to this segment. */
[96324]444 RTCRITSECT m_CritSect;
[69684]445
446 /** The timecode scale factor of this segment. */
[96324]447 uint64_t m_uTimecodeScaleFactor;
[69684]448
[70622]449 /** Absolute timecode (in ms) when starting this segment. */
[96324]450 WebMTimecodeAbs m_tcAbsStartMs;
[70622]451 /** Absolute timecode (in ms) of last write. */
[96324]452 WebMTimecodeAbs m_tcAbsLastWrittenMs;
[69684]453
454 /** Absolute offset (in bytes) of CurSeg. */
[96324]455 uint64_t m_offStart;
[69684]456 /** Absolute offset (in bytes) of general info. */
[96324]457 uint64_t m_offInfo;
[69684]458 /** Absolute offset (in bytes) of seeking info. */
[96324]459 uint64_t m_offSeekInfo;
[69684]460 /** Absolute offset (in bytes) of tracks. */
[96324]461 uint64_t m_offTracks;
[69684]462 /** Absolute offset (in bytes) of cues table. */
[96324]463 uint64_t m_offCues;
[69684]464 /** List of cue points. Needed for seeking table. */
[96324]465 WebMCuePointList m_lstCuePoints;
[69684]466
467 /** Total number of clusters. */
[96324]468 uint64_t m_cClusters;
[69684]469
470 /** Map of tracks.
471 * The key marks the track number (*not* the UUID!). */
[96324]472 std::map <uint8_t, WebMTrack *> m_mapTracks;
[69684]473
474 /** Current cluster which is being handled.
475 *
476 * Note that we don't need (and shouldn't need, as this can be a *lot* of data!) a
477 * list of all clusters. */
[96324]478 WebMCluster m_CurCluster;
[69684]479
[96324]480 WebMQueue m_queueBlocks;
[69684]481
[96324]482 } m_CurSeg;
[69684]483
484 /** Audio codec to use. */
[96175]485 RecordingAudioCodec_T m_enmAudioCodec;
[69684]486 /** Video codec to use. */
[96175]487 RecordingVideoCodec_T m_enmVideoCodec;
[69684]488
489 /** Whether we're currently in the tracks section. */
490 bool m_fInTracksSection;
491
[70622]492 /** Size of timecodes (in bytes). */
[69684]493 size_t m_cbTimecode;
494 /** Maximum value a timecode can have. */
495 uint32_t m_uTimecodeMax;
496
497#ifdef VBOX_WITH_LIBVPX
498 /**
499 * Block data for VP8-encoded video data.
500 */
501 struct BlockData_VP8
502 {
503 const vpx_codec_enc_cfg_t *pCfg;
504 const vpx_codec_cx_pkt_t *pPkt;
505 };
506#endif /* VBOX_WITH_LIBVPX */
507
508 /**
[96175]509 * Block data for encoded audio data.
[69684]510 */
[96175]511 struct BlockData_Audio
[69684]512 {
[96175]513 /** Pointer to encoded audio data. */
[69684]514 const void *pvData;
[96175]515 /** Size (in bytes) of encoded audio data. */
[69684]516 size_t cbData;
[96175]517 /** PTS (in ms) of encoded audio data. */
[69684]518 uint64_t uPTSMs;
519 };
520
521public:
522
523 WebMWriter();
524
525 virtual ~WebMWriter();
526
527public:
528
529 int OpenEx(const char *a_pszFilename, PRTFILE a_phFile,
[96175]530 RecordingAudioCodec_T a_enmAudioCodec, RecordingVideoCodec_T a_enmVideoCodec);
[69684]531
532 int Open(const char *a_pszFilename, uint64_t a_fOpen,
[96175]533 RecordingAudioCodec_T a_enmAudioCodec, RecordingVideoCodec_T a_enmVideoCodec);
[69684]534
535 int Close(void);
536
[96175]537 int AddAudioTrack(PRECORDINGCODEC pCodec, uint16_t uHz, uint8_t cChannels, uint8_t cBits, uint8_t *puTrack);
[69684]538
[96175]539 int AddVideoTrack(PRECORDINGCODEC pCodec, uint16_t uWidth, uint16_t uHeight, uint32_t uFPS, uint8_t *puTrack);
[69684]540
[96229]541 int WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs, WebMBlockFlags uFlags);
[69684]542
[76760]543 const com::Utf8Str& GetFileName(void);
[69684]544
545 uint64_t GetFileSize(void);
546
547 uint64_t GetAvailableSpace(void);
548
[95665]549 /**
550 * Returns the number of written WebM clusters.
551 *
552 * @returns Number of written WebM clusters; 0 when no clusters written (empty file).
553 */
[96324]554 uint64_t GetClusters(void) const { return m_CurSeg.m_cClusters; }
[95665]555
[69684]556protected:
557
[96175]558 int init(RecordingAudioCodec_T a_enmAudioCodec, RecordingVideoCodec_T a_enmVideoCodec);
[69684]559
560 void destroy(void);
561
562 int writeHeader(void);
563
[70622]564 void writeSeekHeader(void);
[69684]565
566 int writeFooter(void);
567
568 int writeSimpleBlockEBML(WebMTrack *a_pTrack, WebMSimpleBlock *a_pBlock);
569
570 int writeSimpleBlockQueued(WebMTrack *a_pTrack, WebMSimpleBlock *a_pBlock);
571
[75031]572 int processQueue(WebMQueue *pQueue, bool fForce);
[69684]573
574protected:
575
576 typedef std::map <uint8_t, WebMTrack *> WebMTracks;
577};
578
[76562]579#endif /* !MAIN_INCLUDED_WebMWriter_h */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use