VirtualBox

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

Last change on this file since 82422 was 82422, checked in by vboxsync, 5 years ago

Main/Recording: Cleanup; greatly reduced the include maze.

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