VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 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
Line 
1/* $Id: WebMWriter.h 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * WebMWriter.h - WebM container handling.
4 */
5
6/*
7 * Copyright (C) 2013-2023 Oracle and/or its affiliates.
8 *
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
26 */
27
28#ifndef MAIN_INCLUDED_WebMWriter_h
29#define MAIN_INCLUDED_WebMWriter_h
30#ifndef RT_WITHOUT_PRAGMA_ONCE
31# pragma once
32#endif
33
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
42#include "EBMLWriter.h"
43#include "EBML_MKV.h"
44
45#include <queue>
46#include <map>
47#include <list>
48
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
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 {
89 RT_NOREF(a_cbHdrSetup);
90
91 /* We supply 3 headers total: The "real" header, comments header + setup header. */
92 cHeaders = 3 /* Headers */ - 1; /* Note: Always "minus one" here. */
93
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
113class WebMWriter : public EBMLWriter
114{
115
116public:
117
118 /** Defines an absolute WebM timecode (Block + Cluster). */
119 typedef uint64_t WebMTimecodeAbs;
120
121 /** Defines a relative WebM timecode (Block). */
122 typedef uint16_t WebMTimecodeRel;
123
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,
148 WebMTimecodeAbs a_tcAbsPTSMs, const void *a_pvData, size_t a_cbData, WebMBlockFlags a_fFlags)
149 : pTrack(a_pTrack)
150 {
151 Data.tcAbsPTSMs = a_tcAbsPTSMs;
152 Data.cb = a_cbData;
153 Data.fFlags = a_fFlags;
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 {
177 WebMTimecodeAbs tcAbsPTSMs;
178 WebMTimecodeRel tcRelToClusterMs;
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). */
220 typedef std::map<WebMTimecodeAbs, WebMTimecodeBlocks> WebMBlockMap;
221
222 /**
223 * Structure for defining a WebM (encoding) queue.
224 */
225 struct WebMQueue
226 {
227 WebMQueue(void)
228 : tcAbsLastBlockWrittenMs(0)
229 , tsLastProcessedMs(0) { }
230
231 /** Blocks as FIFO (queue). */
232 WebMBlockMap Map;
233 /** Absolute timecode (in ms) of last written block to queue. */
234 WebMTimecodeAbs tcAbsLastBlockWrittenMs;
235 /** Time stamp (in ms) of when the queue was processed last. */
236 uint64_t tsLastProcessedMs;
237 };
238
239 /**
240 * Structure for keeping a WebM track entry.
241 */
242 struct WebMTrack
243 {
244 WebMTrack(WebMTrackType a_enmType, PRECORDINGCODEC pTheCodec, uint8_t a_uTrack, uint64_t a_offID)
245 : enmType(a_enmType)
246 , pCodec(pTheCodec)
247 , uTrack(a_uTrack)
248 , offUUID(a_offID)
249 , cTotalBlocks(0)
250 , tcAbsLastWrittenMs(0)
251 {
252 uUUID = RTRandU32();
253 }
254
255 /** The type of this track. */
256 WebMTrackType enmType;
257 /** Pointer to codec data to use. */
258 PRECORDINGCODEC pCodec;
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;
292 /** Absoute timecode (in ms) of last write. */
293 WebMTimecodeAbs tcAbsLastWrittenMs;
294 };
295
296 /**
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 /**
313 * Structure for keeping a cue point.
314 */
315 struct WebMCuePoint
316 {
317 WebMCuePoint(WebMTimecodeAbs a_tcAbs)
318 : tcAbs(a_tcAbs) { }
319
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;
343 /** Absolute time code according to the segment time base. */
344 WebMTimecodeAbs tcAbs;
345 };
346
347 /** List of cue points. */
348 typedef std::list<WebMCuePoint *> WebMCuePointList;
349
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)
359 , tcAbsStartMs(0)
360 , cBlocks(0) { }
361
362 /** This cluster's ID. */
363 uint64_t uID;
364 /** Absolute offset (in bytes) of this cluster.
365 * Needed for seeking info table. */
366 uint64_t offStart;
367 /** Whether this cluster element is opened currently. */
368 bool fOpen;
369 /** Absolute timecode (in ms) when this cluster starts. */
370 WebMTimecodeAbs tcAbsStartMs;
371 /** Absolute timecode (in ms) of when last written to this cluster. */
372 WebMTimecodeAbs tcAbsLastWrittenMs;
373 /** Number of (simple) blocks in this cluster. */
374 uint64_t cBlocks;
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)
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)
393 {
394 m_uTimecodeScaleFactor = VBOX_WEBM_TIMECODESCALE_FACTOR_MS;
395
396 LogFunc(("Default timecode scale is: %RU64ns\n", m_uTimecodeScaleFactor));
397 }
398
399 virtual ~WebMSegment()
400 {
401 uninit();
402 }
403
404 /**
405 * Initializes a segment.
406 *
407 * @returns VBox status code.
408 */
409 int init(void)
410 {
411 return RTCritSectInit(&m_CritSect);
412 }
413
414 /**
415 * Uninitializes a segment.
416 */
417 void uninit(void)
418 {
419 clear();
420
421 RTCritSectDelete(&m_CritSect);
422 }
423
424 /**
425 * Clear the segment's data by removing (and freeing) all data.
426 */
427 void clear(void)
428 {
429 WebMCuePointList::iterator itCuePoint = m_lstCuePoints.begin();
430 while (itCuePoint != m_lstCuePoints.end())
431 {
432 WebMCuePoint *pCuePoint = (*itCuePoint);
433 AssertPtr(pCuePoint);
434 delete pCuePoint;
435
436 m_lstCuePoints.erase(itCuePoint);
437 itCuePoint = m_lstCuePoints.begin();
438 }
439
440 Assert(m_lstCuePoints.empty());
441 }
442
443 /** Critical section for serializing access to this segment. */
444 RTCRITSECT m_CritSect;
445
446 /** The timecode scale factor of this segment. */
447 uint64_t m_uTimecodeScaleFactor;
448
449 /** Absolute timecode (in ms) when starting this segment. */
450 WebMTimecodeAbs m_tcAbsStartMs;
451 /** Absolute timecode (in ms) of last write. */
452 WebMTimecodeAbs m_tcAbsLastWrittenMs;
453
454 /** Absolute offset (in bytes) of CurSeg. */
455 uint64_t m_offStart;
456 /** Absolute offset (in bytes) of general info. */
457 uint64_t m_offInfo;
458 /** Absolute offset (in bytes) of seeking info. */
459 uint64_t m_offSeekInfo;
460 /** Absolute offset (in bytes) of tracks. */
461 uint64_t m_offTracks;
462 /** Absolute offset (in bytes) of cues table. */
463 uint64_t m_offCues;
464 /** List of cue points. Needed for seeking table. */
465 WebMCuePointList m_lstCuePoints;
466
467 /** Total number of clusters. */
468 uint64_t m_cClusters;
469
470 /** Map of tracks.
471 * The key marks the track number (*not* the UUID!). */
472 std::map <uint8_t, WebMTrack *> m_mapTracks;
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. */
478 WebMCluster m_CurCluster;
479
480 WebMQueue m_queueBlocks;
481
482 } m_CurSeg;
483
484 /** Audio codec to use. */
485 RecordingAudioCodec_T m_enmAudioCodec;
486 /** Video codec to use. */
487 RecordingVideoCodec_T m_enmVideoCodec;
488
489 /** Whether we're currently in the tracks section. */
490 bool m_fInTracksSection;
491
492 /** Size of timecodes (in bytes). */
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 /**
509 * Block data for encoded audio data.
510 */
511 struct BlockData_Audio
512 {
513 /** Pointer to encoded audio data. */
514 const void *pvData;
515 /** Size (in bytes) of encoded audio data. */
516 size_t cbData;
517 /** PTS (in ms) of encoded audio data. */
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,
530 RecordingAudioCodec_T a_enmAudioCodec, RecordingVideoCodec_T a_enmVideoCodec);
531
532 int Open(const char *a_pszFilename, uint64_t a_fOpen,
533 RecordingAudioCodec_T a_enmAudioCodec, RecordingVideoCodec_T a_enmVideoCodec);
534
535 int Close(void);
536
537 int AddAudioTrack(PRECORDINGCODEC pCodec, uint16_t uHz, uint8_t cChannels, uint8_t cBits, uint8_t *puTrack);
538
539 int AddVideoTrack(PRECORDINGCODEC pCodec, uint16_t uWidth, uint16_t uHeight, uint32_t uFPS, uint8_t *puTrack);
540
541 int WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs, WebMBlockFlags uFlags);
542
543 const com::Utf8Str& GetFileName(void);
544
545 uint64_t GetFileSize(void);
546
547 uint64_t GetAvailableSpace(void);
548
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 */
554 uint64_t GetClusters(void) const { return m_CurSeg.m_cClusters; }
555
556protected:
557
558 int init(RecordingAudioCodec_T a_enmAudioCodec, RecordingVideoCodec_T a_enmVideoCodec);
559
560 void destroy(void);
561
562 int writeHeader(void);
563
564 void writeSeekHeader(void);
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
572 int processQueue(WebMQueue *pQueue, bool fForce);
573
574protected:
575
576 typedef std::map <uint8_t, WebMTrack *> WebMTracks;
577};
578
579#endif /* !MAIN_INCLUDED_WebMWriter_h */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use