VirtualBox

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

Last change on this file since 95753 was 95665, checked in by vboxsync, 2 years ago

Recording/Main: Fixed a file sharing violation when closing recorded WebM files, which don't contain any clusters and thus getting deleted again. This needs to be done by the recording stream and not in the WebM writer class, as the WebM writer only inherited the file handle. ​bugref:9286

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

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