VirtualBox

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

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