VirtualBox

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

Last change on this file since 96324 was 96324, checked in by vboxsync, 3 years ago

Recording/Main: Renaming (use m_ prefixes for class variables).

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette