VirtualBox

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

Last change on this file since 94521 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

  • 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 93115 2022-01-01 11:31:46Z 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
555protected:
556
557 int init(void);
558
559 void destroy(void);
560
561 int writeHeader(void);
562
563 void writeSeekHeader(void);
564
565 int writeFooter(void);
566
567 int writeSimpleBlockEBML(WebMTrack *a_pTrack, WebMSimpleBlock *a_pBlock);
568
569 int writeSimpleBlockQueued(WebMTrack *a_pTrack, WebMSimpleBlock *a_pBlock);
570
571#ifdef VBOX_WITH_LIBVPX
572 int writeSimpleBlockVP8(WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt);
573#endif
574
575#ifdef VBOX_WITH_LIBOPUS
576 int writeSimpleBlockOpus(WebMTrack *a_pTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs);
577#endif
578
579 int processQueue(WebMQueue *pQueue, bool fForce);
580
581protected:
582
583 typedef std::map <uint8_t, WebMTrack *> WebMTracks;
584};
585
586#endif /* !MAIN_INCLUDED_WebMWriter_h */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use