VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/WebMWriter.h@ 73768

Last change on this file since 73768 was 70623, checked in by vboxsync, 6 years ago

VideoRec/WebMWriter: Fixes required for some players (e.g latest Firefox).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use