VirtualBox

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

Last change on this file since 77110 was 76760, checked in by vboxsync, 6 years ago

Main/Recording: thin out the excessive includes so that it doesn't hide missing includes in totally unrelated code, and adjust that code to do the right thing

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