VirtualBox

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

最後變更 在這個檔案從96175是 96175,由 vboxsync 提交於 3 年 前

Recording: Implemented support for Vorbis codec (provided by libvorbis, not enabled by default yet). This also makes all the codec handling more abstract by using a simple codec wrapper, to keep other places free from codec-specific as much as possible. Initial implementation works and output files are being recognized by media players, but there still are some timing bugs to resolve, as well as optimizing the performance. bugref:10275

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette