VirtualBox

Changeset 69683 in vbox


Ignore:
Timestamp:
Nov 14, 2017 11:09:16 AM (7 years ago)
Author:
vboxsync
Message:

VideoRec/Main: Factored out the WebMWriter class from the EBMLWriter one and got rid of the extra Impl class and instead derive WebMWriter from EBMLWriter. Saves quite a bit of code and should make the stuff a lot easier to read and maintain. No functional code changes.

Location:
trunk/src/VBox/Main
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/Makefile.kmk

    r69241 r69683  
    782782ifdef VBOX_WITH_VIDEOREC
    783783   VBoxC_SOURCES += \
    784         src-client/EBMLWriter.cpp
     784        src-client/EBMLWriter.cpp \
     785        src-client/WebMWriter.cpp
    785786  ifdef VBOX_WITH_LIBVPX
    786787   VBoxC_SDKS += VBOX_VPX
  • trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp

    r69240 r69683  
    9090
    9191#include "../../Devices/Audio/DrvAudio.h"
    92 #include "EBMLWriter.h"
     92#include "WebMWriter.h"
    9393
    9494#include <iprt/mem.h>
  • trunk/src/VBox/Main/src-client/EBMLWriter.cpp

    r69195 r69683  
    11/* $Id$ */
    22/** @file
    3  * EbmlWriter.cpp - EBML writer + WebM container handling.
     3 * EBMLWriter.cpp - EBML writer implementation.
    44 */
    55
     
    2222 */
    2323
     24#ifdef LOG_GROUP
     25# undef LOG_GROUP
     26#endif
    2427#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
    2528#include "LoggingNew.h"
     
    5255#define VBOX_EBMLWRITER_FLAG_HANDLE_INHERITED   RT_BIT(0)
    5356
    54 class EBMLWriter
    55 {
    56 public:
    57     typedef uint32_t EbmlClassId;
    58 
    59 private:
    60 
    61     struct EbmlSubElement
     57/** Creates an EBML output file using an existing, open file handle. */
     58int EBMLWriter::createEx(const char *a_pszFile, PRTFILE phFile)
     59{
     60    AssertPtrReturn(phFile, VERR_INVALID_POINTER);
     61
     62    m_hFile   = *phFile;
     63    m_fFlags |= VBOX_EBMLWRITER_FLAG_HANDLE_INHERITED;
     64    m_strFile = a_pszFile;
     65
     66    return VINF_SUCCESS;
     67}
     68
     69/** Creates an EBML output file using a file name. */
     70int EBMLWriter::create(const char *a_pszFile, uint64_t fOpen)
     71{
     72    int rc = RTFileOpen(&m_hFile, a_pszFile, fOpen);
     73    if (RT_SUCCESS(rc))
     74        m_strFile = a_pszFile;
     75
     76    return rc;
     77}
     78
     79/** Returns available space on storage. */
     80uint64_t EBMLWriter::getAvailableSpace(void)
     81{
     82    RTFOFF pcbFree;
     83    int rc = RTFileQueryFsSizes(m_hFile, NULL, &pcbFree, 0, 0);
     84    return (RT_SUCCESS(rc)? (uint64_t)pcbFree : UINT64_MAX);
     85}
     86
     87/** Closes the file. */
     88void EBMLWriter::close(void)
     89{
     90    if (!isOpen())
     91        return;
     92
     93    AssertMsg(m_Elements.size() == 0,
     94              ("%zu elements are not closed yet (next element to close is 0x%x)\n",
     95               m_Elements.size(), m_Elements.top().classId));
     96
     97    if (!(m_fFlags & VBOX_EBMLWRITER_FLAG_HANDLE_INHERITED))
    6298    {
    63         uint64_t offset;
    64         EbmlClassId classId;
    65         EbmlSubElement(uint64_t offs, EbmlClassId cid) : offset(offs), classId(cid) {}
    66     };
    67 
    68     /** Stack of EBML sub elements. */
    69     std::stack<EbmlSubElement> m_Elements;
    70     /** The file's handle. */
    71     RTFILE                     m_hFile;
    72     /** The file's name (path). */
    73     Utf8Str                    m_strFile;
    74     /** Flags. */
    75     uint32_t                   m_fFlags;
    76 
    77 public:
    78 
    79     EBMLWriter(void)
    80         : m_hFile(NIL_RTFILE)
    81         , m_fFlags(VBOX_EBMLWRITER_FLAG_NONE) { }
    82 
    83     virtual ~EBMLWriter(void) { close(); }
    84 
    85 public:
    86 
    87     /** Creates an EBML output file using an existing, open file handle. */
    88     inline int createEx(const char *a_pszFile, PRTFILE phFile)
     99        RTFileClose(m_hFile);
     100        m_hFile = NIL_RTFILE;
     101    }
     102
     103    m_fFlags  = VBOX_EBMLWRITER_FLAG_NONE;
     104    m_strFile = "";
     105}
     106
     107/** Starts an EBML sub-element. */
     108EBMLWriter& EBMLWriter::subStart(EbmlClassId classId)
     109{
     110    writeClassId(classId);
     111    /* store the current file offset. */
     112    m_Elements.push(EbmlSubElement(RTFileTell(m_hFile), classId));
     113    /* Indicates that size of the element
     114     * is unkown (as according to EBML specs).
     115     */
     116    writeUnsignedInteger(UINT64_C(0x01FFFFFFFFFFFFFF));
     117    return *this;
     118}
     119
     120/** Ends an EBML sub-element. */
     121EBMLWriter& EBMLWriter::subEnd(EbmlClassId classId)
     122{
     123#ifdef VBOX_STRICT
     124    /* Class ID on the top of the stack should match the class ID passed
     125     * to the function. Otherwise it may mean that we have a bug in the code.
     126     */
     127    AssertMsg(!m_Elements.empty(), ("No elements to close anymore\n"));
     128    AssertMsg(m_Elements.top().classId == classId,
     129              ("Ending sub element 0x%x is in wrong order (next to close is 0x%x)\n", classId, m_Elements.top().classId));
     130#else
     131    RT_NOREF(classId);
     132#endif
     133
     134    uint64_t uPos = RTFileTell(m_hFile);
     135    uint64_t uSize = uPos - m_Elements.top().offset - 8;
     136    RTFileSeek(m_hFile, m_Elements.top().offset, RTFILE_SEEK_BEGIN, NULL);
     137
     138    /* Make sure that size will be serialized as uint64_t. */
     139    writeUnsignedInteger(uSize | UINT64_C(0x0100000000000000));
     140    RTFileSeek(m_hFile, uPos, RTFILE_SEEK_BEGIN, NULL);
     141    m_Elements.pop();
     142    return *this;
     143}
     144
     145/** Serializes a null-terminated string. */
     146EBMLWriter& EBMLWriter::serializeString(EbmlClassId classId, const char *str)
     147{
     148    writeClassId(classId);
     149    uint64_t size = strlen(str);
     150    writeSize(size);
     151    write(str, size);
     152    return *this;
     153}
     154
     155/** Serializes an UNSIGNED integer.
     156 *  If size is zero then it will be detected automatically. */
     157EBMLWriter& EBMLWriter::serializeUnsignedInteger(EbmlClassId classId, uint64_t parm, size_t size /* = 0 */)
     158{
     159    writeClassId(classId);
     160    if (!size) size = getSizeOfUInt(parm);
     161    writeSize(size);
     162    writeUnsignedInteger(parm, size);
     163    return *this;
     164}
     165
     166/** Serializes a floating point value.
     167 *
     168 * Only 8-bytes double precision values are supported
     169 * by this function.
     170 */
     171EBMLWriter& EBMLWriter::serializeFloat(EbmlClassId classId, float value)
     172{
     173    writeClassId(classId);
     174    Assert(sizeof(uint32_t) == sizeof(float));
     175    writeSize(sizeof(float));
     176
     177    union
    89178    {
    90         AssertPtrReturn(phFile, VERR_INVALID_POINTER);
    91 
    92         m_hFile   = *phFile;
    93         m_fFlags |= VBOX_EBMLWRITER_FLAG_HANDLE_INHERITED;
    94         m_strFile = a_pszFile;
    95 
    96         return VINF_SUCCESS;
    97     }
    98 
    99     /** Creates an EBML output file using a file name. */
    100     inline int create(const char *a_pszFile, uint64_t fOpen)
    101     {
    102         int rc = RTFileOpen(&m_hFile, a_pszFile, fOpen);
    103         if (RT_SUCCESS(rc))
    104             m_strFile = a_pszFile;
    105 
    106         return rc;
    107     }
    108 
    109     /** Returns the file name. */
    110     inline const Utf8Str& getFileName(void)
    111     {
    112         return m_strFile;
    113     }
    114 
    115     /** Returns file size. */
    116     inline uint64_t getFileSize(void)
    117     {
    118         return RTFileTell(m_hFile);
    119     }
    120 
    121     /** Get reference to file descriptor */
    122     inline const RTFILE &getFile(void)
    123     {
    124         return m_hFile;
    125     }
    126 
    127     /** Returns available space on storage. */
    128     inline uint64_t getAvailableSpace(void)
    129     {
    130         RTFOFF pcbFree;
    131         int rc = RTFileQueryFsSizes(m_hFile, NULL, &pcbFree, 0, 0);
    132         return (RT_SUCCESS(rc)? (uint64_t)pcbFree : UINT64_MAX);
    133     }
    134 
    135     /** Closes the file. */
    136     inline void close(void)
    137     {
    138         if (!isOpen())
    139             return;
    140 
    141         AssertMsg(m_Elements.size() == 0,
    142                   ("%zu elements are not closed yet (next element to close is 0x%x)\n",
    143                    m_Elements.size(), m_Elements.top().classId));
    144 
    145         if (!(m_fFlags & VBOX_EBMLWRITER_FLAG_HANDLE_INHERITED))
    146         {
    147             RTFileClose(m_hFile);
    148             m_hFile = NIL_RTFILE;
    149         }
    150 
    151         m_fFlags  = VBOX_EBMLWRITER_FLAG_NONE;
    152         m_strFile = "";
    153     }
    154 
    155     /**
    156      * Returns whether the file is open or not.
    157      *
    158      * @returns True if open, false if not.
     179        float   f;
     180        uint8_t u8[4];
     181    } u;
     182
     183    u.f = value;
     184
     185    for (int i = 3; i >= 0; i--) /* Converts values to big endian. */
     186        write(&u.u8[i], 1);
     187
     188    return *this;
     189}
     190
     191/** Serializes binary data. */
     192EBMLWriter& EBMLWriter::serializeData(EbmlClassId classId, const void *pvData, size_t cbData)
     193{
     194    writeClassId(classId);
     195    writeSize(cbData);
     196    write(pvData, cbData);
     197    return *this;
     198}
     199
     200/** Writes raw data to file. */
     201int EBMLWriter::write(const void *data, size_t size)
     202{
     203    return RTFileWrite(m_hFile, data, size, NULL);
     204}
     205
     206/** Writes an unsigned integer of variable of fixed size. */
     207void EBMLWriter::writeUnsignedInteger(uint64_t value, size_t size /* = sizeof(uint64_t) */)
     208{
     209    /* convert to big-endian */
     210    value = RT_H2BE_U64(value);
     211    write(reinterpret_cast<uint8_t*>(&value) + sizeof(value) - size, size);
     212}
     213
     214/** Writes EBML class ID to file.
     215 *
     216 * EBML ID already has a UTF8-like represenation
     217 * so getSizeOfUInt is used to determine
     218 * the number of its bytes.
     219 */
     220void EBMLWriter::writeClassId(EbmlClassId parm)
     221{
     222    writeUnsignedInteger(parm, getSizeOfUInt(parm));
     223}
     224
     225/** Writes data size value. */
     226void EBMLWriter::writeSize(uint64_t parm)
     227{
     228    /* The following expression defines the size of the value that will be serialized
     229     * as an EBML UTF-8 like integer (with trailing bits represeting its size):
     230      1xxx xxxx                                                                              - value 0 to  2^7-2
     231      01xx xxxx  xxxx xxxx                                                                   - value 0 to 2^14-2
     232      001x xxxx  xxxx xxxx  xxxx xxxx                                                        - value 0 to 2^21-2
     233      0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                             - value 0 to 2^28-2
     234      0000 1xxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                  - value 0 to 2^35-2
     235      0000 01xx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                       - value 0 to 2^42-2
     236      0000 001x  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx            - value 0 to 2^49-2
     237      0000 0001  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - value 0 to 2^56-2
    159238     */
    160     inline bool isOpen(void)
    161     {
    162         return RTFileIsValid(m_hFile);
    163     }
    164 
    165     /** Starts an EBML sub-element. */
    166     inline EBMLWriter &subStart(EbmlClassId classId)
    167     {
    168         writeClassId(classId);
    169         /* store the current file offset. */
    170         m_Elements.push(EbmlSubElement(RTFileTell(m_hFile), classId));
    171         /* Indicates that size of the element
    172          * is unkown (as according to EBML specs).
    173          */
    174         writeUnsignedInteger(UINT64_C(0x01FFFFFFFFFFFFFF));
    175         return *this;
    176     }
    177 
    178     /** Ends an EBML sub-element. */
    179     inline EBMLWriter &subEnd(EbmlClassId classId)
    180     {
    181 #ifdef VBOX_STRICT
    182         /* Class ID on the top of the stack should match the class ID passed
    183          * to the function. Otherwise it may mean that we have a bug in the code.
    184          */
    185         AssertMsg(!m_Elements.empty(), ("No elements to close anymore\n"));
    186         AssertMsg(m_Elements.top().classId == classId,
    187                   ("Ending sub element 0x%x is in wrong order (next to close is 0x%x)\n", classId, m_Elements.top().classId));
    188 #else
    189         RT_NOREF(classId);
    190 #endif
    191 
    192         uint64_t uPos = RTFileTell(m_hFile);
    193         uint64_t uSize = uPos - m_Elements.top().offset - 8;
    194         RTFileSeek(m_hFile, m_Elements.top().offset, RTFILE_SEEK_BEGIN, NULL);
    195 
    196         /* Make sure that size will be serialized as uint64_t. */
    197         writeUnsignedInteger(uSize | UINT64_C(0x0100000000000000));
    198         RTFileSeek(m_hFile, uPos, RTFILE_SEEK_BEGIN, NULL);
    199         m_Elements.pop();
    200         return *this;
    201     }
    202 
    203     /** Serializes a null-terminated string. */
    204     inline EBMLWriter &serializeString(EbmlClassId classId, const char *str)
    205     {
    206         writeClassId(classId);
    207         uint64_t size = strlen(str);
    208         writeSize(size);
    209         write(str, size);
    210         return *this;
    211     }
    212 
    213     /* Serializes an UNSIGNED integer
    214      * If size is zero then it will be detected automatically. */
    215     inline EBMLWriter &serializeUnsignedInteger(EbmlClassId classId, uint64_t parm, size_t size = 0)
    216     {
    217         writeClassId(classId);
    218         if (!size) size = getSizeOfUInt(parm);
    219         writeSize(size);
    220         writeUnsignedInteger(parm, size);
    221         return *this;
    222     }
    223 
    224     /** Serializes a floating point value.
    225      *
    226      * Only 8-bytes double precision values are supported
    227      * by this function.
    228      */
    229     inline EBMLWriter &serializeFloat(EbmlClassId classId, float value)
    230     {
    231         writeClassId(classId);
    232         Assert(sizeof(uint32_t) == sizeof(float));
    233         writeSize(sizeof(float));
    234 
    235         union
    236         {
    237             float   f;
    238             uint8_t u8[4];
    239         } u;
    240 
    241         u.f = value;
    242 
    243         for (int i = 3; i >= 0; i--) /* Converts values to big endian. */
    244             write(&u.u8[i], 1);
    245 
    246         return *this;
    247     }
    248 
    249     /** Serializes binary data. */
    250     inline EBMLWriter &serializeData(EbmlClassId classId, const void *pvData, size_t cbData)
    251     {
    252         writeClassId(classId);
    253         writeSize(cbData);
    254         write(pvData, cbData);
    255         return *this;
    256     }
    257 
    258     /** Writes raw data to file. */
    259     inline int write(const void *data, size_t size)
    260     {
    261         return RTFileWrite(m_hFile, data, size, NULL);
    262     }
    263 
    264     /** Writes an unsigned integer of variable of fixed size. */
    265     inline void writeUnsignedInteger(uint64_t value, size_t size = sizeof(uint64_t))
    266     {
    267         /* convert to big-endian */
    268         value = RT_H2BE_U64(value);
    269         write(reinterpret_cast<uint8_t*>(&value) + sizeof(value) - size, size);
    270     }
    271 
    272     /** Writes EBML class ID to file.
    273      *
    274      * EBML ID already has a UTF8-like represenation
    275      * so getSizeOfUInt is used to determine
    276      * the number of its bytes.
    277      */
    278     inline void writeClassId(EbmlClassId parm)
    279     {
    280         writeUnsignedInteger(parm, getSizeOfUInt(parm));
    281     }
    282 
    283     /** Writes data size value. */
    284     inline void writeSize(uint64_t parm)
    285     {
    286         /* The following expression defines the size of the value that will be serialized
    287          * as an EBML UTF-8 like integer (with trailing bits represeting its size):
    288           1xxx xxxx                                                                              - value 0 to  2^7-2
    289           01xx xxxx  xxxx xxxx                                                                   - value 0 to 2^14-2
    290           001x xxxx  xxxx xxxx  xxxx xxxx                                                        - value 0 to 2^21-2
    291           0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                             - value 0 to 2^28-2
    292           0000 1xxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                  - value 0 to 2^35-2
    293           0000 01xx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                       - value 0 to 2^42-2
    294           0000 001x  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx            - value 0 to 2^49-2
    295           0000 0001  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - value 0 to 2^56-2
    296          */
    297         size_t size = 8 - ! (parm & (UINT64_MAX << 49)) - ! (parm & (UINT64_MAX << 42)) -
    298                           ! (parm & (UINT64_MAX << 35)) - ! (parm & (UINT64_MAX << 28)) -
    299                           ! (parm & (UINT64_MAX << 21)) - ! (parm & (UINT64_MAX << 14)) -
    300                           ! (parm & (UINT64_MAX << 7));
    301         /* One is subtracted in order to avoid loosing significant bit when size = 8. */
    302         uint64_t mask = RT_BIT_64(size * 8 - 1);
    303         writeUnsignedInteger((parm & (((mask << 1) - 1) >> size)) | (mask >> (size - 1)), size);
    304     }
    305 
    306     /** Size calculation for variable size UNSIGNED integer.
    307      *
    308      * The function defines the size of the number by trimming
    309      * consequent trailing zero bytes starting from the most significant.
    310      * The following statement is always true:
    311      * 1 <= getSizeOfUInt(arg) <= 8.
    312      *
    313      * Every !(arg & (UINT64_MAX << X)) expression gives one
    314      * if an only if all the bits from X to 63 are set to zero.
    315      */
    316     static inline size_t getSizeOfUInt(uint64_t arg)
    317     {
    318         return 8 - ! (arg & (UINT64_MAX << 56)) - ! (arg & (UINT64_MAX << 48)) -
    319                    ! (arg & (UINT64_MAX << 40)) - ! (arg & (UINT64_MAX << 32)) -
    320                    ! (arg & (UINT64_MAX << 24)) - ! (arg & (UINT64_MAX << 16)) -
    321                    ! (arg & (UINT64_MAX << 8));
    322     }
    323 
    324 private:
    325     void operator=(const EBMLWriter &);
    326 
    327 };
    328 
    329 /** No flags specified. */
    330 #define VBOX_WEBM_BLOCK_FLAG_NONE           0
    331 /** Invisible block which can be skipped. */
    332 #define VBOX_WEBM_BLOCK_FLAG_INVISIBLE      0x08
    333 /** The block marks a key frame. */
    334 #define VBOX_WEBM_BLOCK_FLAG_KEY_FRAME      0x80
    335 
    336 /** The default timecode scale factor for WebM -- all timecodes in the segments are expressed in ms.
    337  *  This allows every cluster to have blocks with positive values up to 32.767 seconds. */
    338 #define VBOX_WEBM_TIMECODESCALE_FACTOR_MS   1000000
    339 
    340 /** Maximum time (in ms) a cluster can store. */
    341 #define VBOX_WEBM_CLUSTER_MAX_LEN_MS        INT16_MAX
    342 
    343 /** Maximum time a block can store.
    344  *  With signed 16-bit timecodes and a default timecode scale of 1ms per unit this makes 65536ms. */
    345 #define VBOX_WEBM_BLOCK_MAX_LEN_MS          UINT16_MAX
    346 
    347 #ifdef VBOX_WITH_LIBOPUS
    348 # pragma pack(push)
    349 # pragma pack(1)
    350     /** Opus codec private data within the MKV (WEBM) container.
    351      *  Taken from: https://wiki.xiph.org/MatroskaOpus */
    352     typedef struct WEBMOPUSPRIVDATA
    353     {
    354         WEBMOPUSPRIVDATA(uint32_t a_u32SampleRate, uint8_t a_u8Channels)
    355         {
    356             au64Head        = RT_MAKE_U64_FROM_U8('O', 'p', 'u', 's', 'H', 'e', 'a', 'd');
    357             u8Version       = 1;
    358             u8Channels      = a_u8Channels;
    359             u16PreSkip      = 0;
    360             u32SampleRate   = a_u32SampleRate;
    361             u16Gain         = 0;
    362             u8MappingFamily = 0;
    363         }
    364 
    365         uint64_t au64Head;          /**< Defaults to "OpusHead". */
    366         uint8_t  u8Version;         /**< Must be set to 1. */
    367         uint8_t  u8Channels;
    368         uint16_t u16PreSkip;
    369         /** Sample rate *before* encoding to Opus.
    370          *  Note: This rate has nothing to do with the playback rate later! */
    371         uint32_t u32SampleRate;
    372         uint16_t u16Gain;
    373         /** Must stay 0 -- otherwise a mapping table must be appended
    374          *  right after this header. */
    375         uint8_t  u8MappingFamily;
    376     } WEBMOPUSPRIVDATA, *PWEBMOPUSPRIVDATA;
    377     AssertCompileSize(WEBMOPUSPRIVDATA, 19);
    378 # pragma pack(pop)
    379 #endif /* VBOX_WITH_LIBOPUS */
    380 
    381 class WebMWriter_Impl
    382 {
    383     /** Defines a WebM timecode. */
    384     typedef uint16_t WebMTimecode;
    385 
    386     /** Defines the WebM block flags data type. */
    387     typedef uint8_t  WebMBlockFlags;
    388 
    389     /**
    390      * Track type enumeration.
    391      */
    392     enum WebMTrackType
    393     {
    394         /** Unknown / invalid type. */
    395         WebMTrackType_Invalid     = 0,
    396         /** Only writes audio. */
    397         WebMTrackType_Audio       = 1,
    398         /** Only writes video. */
    399         WebMTrackType_Video       = 2
    400     };
    401 
    402     struct WebMTrack;
    403 
    404     /**
    405      * Structure for defining a WebM simple block.
    406      */
    407     struct WebMSimpleBlock
    408     {
    409         WebMSimpleBlock(WebMTrack *a_pTrack,
    410                         WebMTimecode a_tcPTSMs, const void *a_pvData, size_t a_cbData, WebMBlockFlags a_fFlags)
    411             : pTrack(a_pTrack)
    412         {
    413             Data.tcPTSMs = a_tcPTSMs;
    414             Data.cb      = a_cbData;
    415             Data.fFlags  = a_fFlags;
    416 
    417             if (Data.cb)
    418             {
    419                 Data.pv = RTMemDup(a_pvData, a_cbData);
    420                 if (!Data.pv)
    421                     throw;
    422             }
    423         }
    424 
    425         virtual ~WebMSimpleBlock()
    426         {
    427             if (Data.pv)
    428             {
    429                 Assert(Data.cb);
    430                 RTMemFree(Data.pv);
    431             }
    432         }
    433 
    434         WebMTrack    *pTrack;
    435 
    436         /** Actual simple block data. */
    437         struct
    438         {
    439             WebMTimecode   tcPTSMs;
    440             WebMTimecode   tcRelToClusterMs;
    441             void          *pv;
    442             size_t         cb;
    443             WebMBlockFlags fFlags;
    444         } Data;
    445     };
    446 
    447     /** A simple block queue.*/
    448     typedef std::queue<WebMSimpleBlock *> WebMSimpleBlockQueue;
    449 
    450     /** Structure for queuing all simple blocks bound to a single timecode.
    451      *  This can happen if multiple tracks are being involved. */
    452     struct WebMTimecodeBlocks
    453     {
    454         WebMTimecodeBlocks(void)
    455             : fClusterNeeded(false)
    456             , fClusterStarted(false) { }
    457 
    458         /** The actual block queue for this timecode. */
    459         WebMSimpleBlockQueue Queue;
    460         /** Whether a new cluster is needed for this timecode or not. */
    461         bool                 fClusterNeeded;
    462         /** Whether a new cluster already has been started for this timecode or not. */
    463         bool                 fClusterStarted;
    464 
    465         /**
    466          * Enqueues a simple block into the internal queue.
    467          *
    468          * @param   a_pBlock    Block to enqueue and take ownership of.
    469          */
    470         void Enqueue(WebMSimpleBlock *a_pBlock)
    471         {
    472             Queue.push(a_pBlock);
    473 
    474             if (a_pBlock->Data.fFlags & VBOX_WEBM_BLOCK_FLAG_KEY_FRAME)
    475                 fClusterNeeded = true;
    476         }
    477     };
    478 
    479     /** A block map containing all currently queued blocks.
    480      *  The key specifies a unique timecode, whereas the value
    481      *  is a queue of blocks which all correlate to the key (timecode). */
    482     typedef std::map<WebMTimecode, WebMTimecodeBlocks> WebMBlockMap;
    483 
    484     /**
    485      * Structure for defining a WebM (encoding) queue.
    486      */
    487     struct WebMQueue
    488     {
    489         WebMQueue(void)
    490             : tcLastBlockWrittenMs(0)
    491             , tslastProcessedMs(0) { }
    492 
    493         /** Blocks as FIFO (queue). */
    494         WebMBlockMap Map;
    495         /** Timecode (in ms) of last written block to queue. */
    496         WebMTimecode tcLastBlockWrittenMs;
    497         /** Time stamp (in ms) of when the queue was processed last. */
    498         uint64_t     tslastProcessedMs;
    499     };
    500 
    501     /**
    502      * Structure for keeping a WebM track entry.
    503      */
    504     struct WebMTrack
    505     {
    506         WebMTrack(WebMTrackType a_enmType, uint8_t a_uTrack, uint64_t a_offID)
    507             : enmType(a_enmType)
    508             , uTrack(a_uTrack)
    509             , offUUID(a_offID)
    510             , cTotalBlocks(0)
    511             , tcLastWrittenMs(0)
    512         {
    513             uUUID = RTRandU32();
    514         }
    515 
    516         /** The type of this track. */
    517         WebMTrackType enmType;
    518         /** Track parameters. */
    519         union
    520         {
    521             struct
    522             {
    523                 /** Sample rate of input data. */
    524                 uint32_t uHz;
    525                 /** Duration of the frame in samples (per channel).
    526                  *  Valid frame size are:
    527                  *
    528                  *  ms           Frame size
    529                  *  2.5          120
    530                  *  5            240
    531                  *  10           480
    532                  *  20 (Default) 960
    533                  *  40           1920
    534                  *  60           2880
    535                  */
    536                 uint16_t framesPerBlock;
    537                 /** How many milliseconds (ms) one written (simple) block represents. */
    538                 uint16_t msPerBlock;
    539             } Audio;
    540         };
    541         /** This track's track number. Also used as key in track map. */
    542         uint8_t             uTrack;
    543         /** The track's "UUID".
    544          *  Needed in case this track gets mux'ed with tracks from other files. Not really unique though. */
    545         uint32_t            uUUID;
    546         /** Absolute offset in file of track UUID.
    547          *  Needed to write the hash sum within the footer. */
    548         uint64_t            offUUID;
    549         /** Total number of blocks. */
    550         uint64_t            cTotalBlocks;
    551         /** Timecode (in ms) of last write. */
    552         WebMTimecode        tcLastWrittenMs;
    553     };
    554 
    555     /**
    556      * Structure for keeping a cue point.
    557      */
    558     struct WebMCuePoint
    559     {
    560         WebMCuePoint(WebMTrack *a_pTrack, uint64_t a_offCluster, WebMTimecode a_tcAbs)
    561             : pTrack(a_pTrack)
    562             , offCluster(a_offCluster), tcAbs(a_tcAbs) { }
    563 
    564         /** Associated track. */
    565         WebMTrack   *pTrack;
    566         /** Offset (in bytes) of the related cluster containing the given position. */
    567         uint64_t     offCluster;
    568         /** Time code (absolute) of this cue point. */
    569         WebMTimecode tcAbs;
    570     };
    571 
    572     /**
    573      * Structure for keeping a WebM cluster entry.
    574      */
    575     struct WebMCluster
    576     {
    577         WebMCluster(void)
    578             : uID(0)
    579             , offStart(0)
    580             , fOpen(false)
    581             , tcStartMs(0)
    582             , cBlocks(0) { }
    583 
    584         /** This cluster's ID. */
    585         uint64_t      uID;
    586         /** Absolute offset (in bytes) of this cluster.
    587          *  Needed for seeking info table. */
    588         uint64_t      offStart;
    589         /** Whether this cluster element is opened currently. */
    590         bool          fOpen;
    591         /** Timecode (in ms) when this cluster starts. */
    592         WebMTimecode  tcStartMs;
    593         /** Number of (simple) blocks in this cluster. */
    594         uint64_t      cBlocks;
    595     };
    596 
    597     /**
    598      * Structure for keeping a WebM segment entry.
    599      *
    600      * Current we're only using one segment.
    601      */
    602     struct WebMSegment
    603     {
    604         WebMSegment(void)
    605             : tcStartMs(0)
    606             , tcLastWrittenMs(0)
    607             , offStart(0)
    608             , offInfo(0)
    609             , offSeekInfo(0)
    610             , offTracks(0)
    611             , offCues(0)
    612         {
    613             uTimecodeScaleFactor = VBOX_WEBM_TIMECODESCALE_FACTOR_MS;
    614 
    615             LogFunc(("Default timecode scale is: %RU64ns\n", uTimecodeScaleFactor));
    616         }
    617 
    618         int init(void)
    619         {
    620             return RTCritSectInit(&CritSect);
    621         }
    622 
    623         void destroy(void)
    624         {
    625             RTCritSectDelete(&CritSect);
    626         }
    627 
    628         /** Critical section for serializing access to this segment. */
    629         RTCRITSECT                      CritSect;
    630 
    631         /** The timecode scale factor of this segment. */
    632         uint64_t                        uTimecodeScaleFactor;
    633 
    634         /** Timecode (in ms) when starting this segment. */
    635         WebMTimecode                    tcStartMs;
    636         /** Timecode (in ms) of last write. */
    637         WebMTimecode                    tcLastWrittenMs;
    638 
    639         /** Absolute offset (in bytes) of CurSeg. */
    640         uint64_t                        offStart;
    641         /** Absolute offset (in bytes) of general info. */
    642         uint64_t                        offInfo;
    643         /** Absolute offset (in bytes) of seeking info. */
    644         uint64_t                        offSeekInfo;
    645         /** Absolute offset (in bytes) of tracks. */
    646         uint64_t                        offTracks;
    647         /** Absolute offset (in bytes) of cues table. */
    648         uint64_t                        offCues;
    649         /** List of cue points. Needed for seeking table. */
    650         std::list<WebMCuePoint>         lstCues;
    651 
    652         /** Total number of clusters. */
    653         uint64_t                        cClusters;
    654 
    655         /** Map of tracks.
    656          *  The key marks the track number (*not* the UUID!). */
    657         std::map <uint8_t, WebMTrack *> mapTracks;
    658 
    659         /** Current cluster which is being handled.
    660          *
    661          *  Note that we don't need (and shouldn't need, as this can be a *lot* of data!) a
    662          *  list of all clusters. */
    663         WebMCluster                     CurCluster;
    664 
    665         WebMQueue                       queueBlocks;
    666 
    667     } CurSeg;
    668 
    669     /** Audio codec to use. */
    670     WebMWriter::AudioCodec      m_enmAudioCodec;
    671     /** Video codec to use. */
    672     WebMWriter::VideoCodec      m_enmVideoCodec;
    673 
    674     /** Whether we're currently in the tracks section. */
    675     bool                        m_fInTracksSection;
    676 
    677     /** Size of timecodes in bytes. */
    678     size_t                      m_cbTimecode;
    679     /** Maximum value a timecode can have. */
    680     uint32_t                    m_uTimecodeMax;
    681 
    682     EBMLWriter                  m_Ebml;
    683 
    684 public:
    685 
    686     typedef std::map <uint8_t, WebMTrack *> WebMTracks;
    687 
    688 public:
    689 
    690     WebMWriter_Impl() :
    691         m_fInTracksSection(false)
    692     {
    693         /* Size (in bytes) of time code to write. We use 2 bytes (16 bit) by default. */
    694         m_cbTimecode   = 2;
    695         m_uTimecodeMax = UINT16_MAX;
    696     }
    697 
    698     virtual ~WebMWriter_Impl()
    699     {
    700         close();
    701     }
    702 
    703     int init(void)
    704     {
    705         return CurSeg.init();
    706     }
    707 
    708     void destroy(void)
    709     {
    710         CurSeg.destroy();
    711     }
    712 
    713     /**
    714      * Adds an audio track.
    715      *
    716      * @returns IPRT status code.
    717      * @param   uHz             Input sampling rate.
    718      *                          Must be supported by the selected audio codec.
    719      * @param   cChannels       Number of input audio channels.
    720      * @param   cBits           Number of input bits per channel.
    721      * @param   puTrack         Track number on successful creation. Optional.
    722      */
    723     int AddAudioTrack(uint16_t uHz, uint8_t cChannels, uint8_t cBits, uint8_t *puTrack)
    724     {
    725 #ifdef VBOX_WITH_LIBOPUS
    726         int rc;
    727 
    728         /*
    729          * Check if the requested codec rate is supported.
    730          *
    731          * Only the following values are supported by an Opus standard build
    732          * -- every other rate only is supported by a custom build.
    733          */
    734         switch (uHz)
    735         {
    736             case 48000:
    737             case 24000:
    738             case 16000:
    739             case 12000:
    740             case  8000:
    741                 rc = VINF_SUCCESS;
    742                 break;
    743 
    744             default:
    745                 rc = VERR_NOT_SUPPORTED;
    746                 break;
    747         }
    748 
    749         if (RT_FAILURE(rc))
    750             return rc;
    751 
    752         const uint8_t uTrack = (uint8_t)CurSeg.mapTracks.size();
    753 
    754         m_Ebml.subStart(MkvElem_TrackEntry);
    755 
    756         m_Ebml.serializeUnsignedInteger(MkvElem_TrackNumber, (uint8_t)uTrack);
    757         m_Ebml.serializeString         (MkvElem_Language,    "und" /* "Undefined"; see ISO-639-2. */);
    758         m_Ebml.serializeUnsignedInteger(MkvElem_FlagLacing,  (uint8_t)0);
    759 
    760         WebMTrack *pTrack = new WebMTrack(WebMTrackType_Audio, uTrack, RTFileTell(m_Ebml.getFile()));
    761 
    762         pTrack->Audio.uHz            = uHz;
    763         pTrack->Audio.msPerBlock     = 20; /** Opus uses 20ms by default. Make this configurable? */
    764         pTrack->Audio.framesPerBlock = uHz / (1000 /* s in ms */ / pTrack->Audio.msPerBlock);
    765 
    766         WEBMOPUSPRIVDATA opusPrivData(uHz, cChannels);
    767 
    768         LogFunc(("Opus @ %RU16Hz (%RU16ms + %RU16 frames per block)\n",
    769                  pTrack->Audio.uHz, pTrack->Audio.msPerBlock, pTrack->Audio.framesPerBlock));
    770 
    771         m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID,     pTrack->uUUID, 4)
    772               .serializeUnsignedInteger(MkvElem_TrackType,    2 /* Audio */)
    773               .serializeString(MkvElem_CodecID,               "A_OPUS")
    774               .serializeData(MkvElem_CodecPrivate,            &opusPrivData, sizeof(opusPrivData))
    775               .serializeUnsignedInteger(MkvElem_CodecDelay,   0)
    776               .serializeUnsignedInteger(MkvElem_SeekPreRoll,  80 * 1000000) /* 80ms in ns. */
    777               .subStart(MkvElem_Audio)
    778                   .serializeFloat(MkvElem_SamplingFrequency,  (float)uHz)
    779                   .serializeUnsignedInteger(MkvElem_Channels, cChannels)
    780                   .serializeUnsignedInteger(MkvElem_BitDepth, cBits)
    781               .subEnd(MkvElem_Audio)
    782               .subEnd(MkvElem_TrackEntry);
    783 
    784         CurSeg.mapTracks[uTrack] = pTrack;
    785 
    786         if (puTrack)
    787             *puTrack = uTrack;
    788 
    789         return VINF_SUCCESS;
    790 #else
    791         RT_NOREF(uHz, cChannels, cBits, puTrack);
    792         return VERR_NOT_SUPPORTED;
    793 #endif
    794     }
    795 
    796     int AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS, uint8_t *puTrack)
    797     {
    798 #ifdef VBOX_WITH_LIBVPX
    799         RT_NOREF(dbFPS);
    800 
    801         const uint8_t uTrack = (uint8_t)CurSeg.mapTracks.size();
    802 
    803         m_Ebml.subStart(MkvElem_TrackEntry);
    804 
    805         m_Ebml.serializeUnsignedInteger(MkvElem_TrackNumber, (uint8_t)uTrack);
    806         m_Ebml.serializeString         (MkvElem_Language,    "und" /* "Undefined"; see ISO-639-2. */);
    807         m_Ebml.serializeUnsignedInteger(MkvElem_FlagLacing,  (uint8_t)0);
    808 
    809         WebMTrack *pTrack = new WebMTrack(WebMTrackType_Video, uTrack, RTFileTell(m_Ebml.getFile()));
    810 
    811         /** @todo Resolve codec type. */
    812         m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID,    pTrack->uUUID /* UID */, 4)
    813               .serializeUnsignedInteger(MkvElem_TrackType,   1 /* Video */)
    814               .serializeString(MkvElem_CodecID,              "V_VP8")
    815               .subStart(MkvElem_Video)
    816                   .serializeUnsignedInteger(MkvElem_PixelWidth,  uWidth)
    817                   .serializeUnsignedInteger(MkvElem_PixelHeight, uHeight)
    818               .subEnd(MkvElem_Video);
    819 
    820         m_Ebml.subEnd(MkvElem_TrackEntry);
    821 
    822         CurSeg.mapTracks[uTrack] = pTrack;
    823 
    824         if (puTrack)
    825             *puTrack = uTrack;
    826 
    827         return VINF_SUCCESS;
    828 #else
    829         RT_NOREF(uWidth, uHeight, dbFPS, puTrack);
    830         return VERR_NOT_SUPPORTED;
    831 #endif
    832     }
    833 
    834     /**
    835      * Writes the WebM file header.
    836      *
    837      * @returns IPRT status code.
    838      */
    839     int writeHeader(void)
    840     {
    841         LogFunc(("Header @ %RU64\n", RTFileTell(m_Ebml.getFile())));
    842 
    843         m_Ebml.subStart(MkvElem_EBML)
    844               .serializeUnsignedInteger(MkvElem_EBMLVersion, 1)
    845               .serializeUnsignedInteger(MkvElem_EBMLReadVersion, 1)
    846               .serializeUnsignedInteger(MkvElem_EBMLMaxIDLength, 4)
    847               .serializeUnsignedInteger(MkvElem_EBMLMaxSizeLength, 8)
    848               .serializeString(MkvElem_DocType, "webm")
    849               .serializeUnsignedInteger(MkvElem_DocTypeVersion, 2)
    850               .serializeUnsignedInteger(MkvElem_DocTypeReadVersion, 2)
    851               .subEnd(MkvElem_EBML);
    852 
    853         m_Ebml.subStart(MkvElem_Segment);
    854 
    855         /* Save offset of current segment. */
    856         CurSeg.offStart = RTFileTell(m_Ebml.getFile());
    857 
    858         writeSegSeekInfo();
    859 
    860         /* Save offset of upcoming tracks segment. */
    861         CurSeg.offTracks = RTFileTell(m_Ebml.getFile());
    862 
    863         /* The tracks segment starts right after this header. */
    864         m_Ebml.subStart(MkvElem_Tracks);
    865         m_fInTracksSection = true;
    866 
    867         return VINF_SUCCESS;
    868     }
    869 
    870     /**
    871      * Writes a simple block into the EBML structure.
    872      *
    873      * @returns IPRT status code.
    874      * @param   a_pTrack        Track the simple block is assigned to.
    875      * @param   a_pBlock        Simple block to write.
    876      */
    877     int writeSimpleBlockEBML(WebMTrack *a_pTrack, WebMSimpleBlock *a_pBlock)
    878     {
    879 #ifdef LOG_ENABLED
    880         WebMCluster &Cluster = CurSeg.CurCluster;
    881 
    882         Log3Func(("[T%RU8C%RU64] Off=%RU64, PTS=%RU16, RelToClusterMs=%RU16, %zu bytes\n",
    883                   a_pTrack->uTrack, Cluster.uID, RTFileTell(m_Ebml.getFile()),
    884                   a_pBlock->Data.tcPTSMs, a_pBlock->Data.tcRelToClusterMs, a_pBlock->Data.cb));
    885 #endif
    886         /*
    887          * Write a "Simple Block".
    888          */
    889         m_Ebml.writeClassId(MkvElem_SimpleBlock);
    890         /* Block size. */
    891         m_Ebml.writeUnsignedInteger(0x10000000u | (  1                 /* Track number size. */
    892                                                    + m_cbTimecode      /* Timecode size .*/
    893                                                    + 1                 /* Flags size. */
    894                                                    + a_pBlock->Data.cb /* Actual frame data size. */),  4);
    895         /* Track number. */
    896         m_Ebml.writeSize(a_pTrack->uTrack);
    897         /* Timecode (relative to cluster opening timecode). */
    898         m_Ebml.writeUnsignedInteger(a_pBlock->Data.tcRelToClusterMs, m_cbTimecode);
    899         /* Flags. */
    900         m_Ebml.writeUnsignedInteger(a_pBlock->Data.fFlags, 1);
    901         /* Frame data. */
    902         m_Ebml.write(a_pBlock->Data.pv, a_pBlock->Data.cb);
    903 
    904         return VINF_SUCCESS;
    905     }
    906 
    907     /**
    908      * Writes a simple block and enqueues it into the segment's render queue.
    909      *
    910      * @returns IPRT status code.
    911      * @param   a_pTrack        Track the simple block is assigned to.
    912      * @param   a_pBlock        Simple block to write and enqueue.
    913      */
    914     int writeSimpleBlockQueued(WebMTrack *a_pTrack, WebMSimpleBlock *a_pBlock)
    915     {
    916         RT_NOREF(a_pTrack);
    917 
    918         int rc = VINF_SUCCESS;
    919 
    920         try
    921         {
    922             const WebMTimecode tcMap = a_pBlock->Data.tcPTSMs;
    923 
    924             /* See if we already have an entry for the specified timecode in our queue. */
    925             WebMBlockMap::iterator itQueue = CurSeg.queueBlocks.Map.find(tcMap);
    926             if (itQueue != CurSeg.queueBlocks.Map.end()) /* Use existing queue. */
    927             {
    928                 WebMTimecodeBlocks &Blocks = itQueue->second;
    929                 Blocks.Enqueue(a_pBlock);
    930             }
    931             else /* Create a new timecode entry. */
    932             {
    933                 WebMTimecodeBlocks Blocks;
    934                 Blocks.Enqueue(a_pBlock);
    935 
    936                 CurSeg.queueBlocks.Map[tcMap] = Blocks;
    937             }
    938 
    939             processQueues(&CurSeg.queueBlocks, false /* fForce */);
    940         }
    941         catch(...)
    942         {
    943             delete a_pBlock;
    944             a_pBlock = NULL;
    945 
    946             rc = VERR_NO_MEMORY;
    947         }
    948 
    949         return rc;
    950     }
    951 
    952 #ifdef VBOX_WITH_LIBVPX
    953     /**
    954      * Writes VPX (VP8 video) simple data block.
    955      *
    956      * @returns IPRT status code.
    957      * @param   a_pTrack        Track ID to write data to.
    958      * @param   a_pCfg          VPX encoder configuration to use.
    959      * @param   a_pPkt          VPX packet video data packet to write.
    960      */
    961     int writeSimpleBlockVP8(WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
    962     {
    963         RT_NOREF(a_pTrack);
    964 
    965         /* Calculate the PTS of this frame (in ms). */
    966         WebMTimecode tcPTSMs = a_pPkt->data.frame.pts * 1000
    967                              * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;
    968 
    969         if (   tcPTSMs
    970             && tcPTSMs <= a_pTrack->tcLastWrittenMs)
    971         {
    972             tcPTSMs = a_pTrack->tcLastWrittenMs + 1;
    973         }
    974 
    975         const bool fKeyframe = RT_BOOL(a_pPkt->data.frame.flags & VPX_FRAME_IS_KEY);
    976 
    977         uint8_t fFlags = VBOX_WEBM_BLOCK_FLAG_NONE;
    978         if (fKeyframe)
    979             fFlags |= VBOX_WEBM_BLOCK_FLAG_KEY_FRAME;
    980         if (a_pPkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
    981             fFlags |= VBOX_WEBM_BLOCK_FLAG_INVISIBLE;
    982 
    983         return writeSimpleBlockQueued(a_pTrack,
    984                                       new WebMSimpleBlock(a_pTrack,
    985                                                           tcPTSMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags));
    986     }
    987 #endif /* VBOX_WITH_LIBVPX */
    988 
    989 #ifdef VBOX_WITH_LIBOPUS
    990     /**
    991      * Writes an Opus (audio) simple data block.
    992      *
    993      * @returns IPRT status code.
    994      * @param   a_pTrack        Track ID to write data to.
    995      * @param   pvData          Pointer to simple data block to write.
    996      * @param   cbData          Size (in bytes) of simple data block to write.
    997      * @param   uPTSMs          PTS of simple data block.
    998      *
    999      * @remarks Audio blocks that have same absolute timecode as video blocks SHOULD be written before the video blocks.
    1000      */
    1001     int writeSimpleBlockOpus(WebMTrack *a_pTrack, const void *pvData, size_t cbData, WebMTimecode uPTSMs)
    1002     {
    1003         AssertPtrReturn(a_pTrack, VERR_INVALID_POINTER);
    1004         AssertPtrReturn(pvData,   VERR_INVALID_POINTER);
    1005         AssertReturn(cbData,      VERR_INVALID_PARAMETER);
    1006 
    1007         /* Every Opus frame is a key frame. */
    1008         const uint8_t fFlags = VBOX_WEBM_BLOCK_FLAG_KEY_FRAME;
    1009 
    1010         return writeSimpleBlockQueued(a_pTrack,
    1011                                       new WebMSimpleBlock(a_pTrack,
    1012                                                           uPTSMs, pvData, cbData, fFlags));
    1013     }
    1014 #endif /* VBOX_WITH_LIBOPUS */
    1015 
    1016     /**
    1017      * Writes a data block to the specified track.
    1018      *
    1019      * @returns IPRT status code.
    1020      * @param   uTrack          Track ID to write data to.
    1021      * @param   pvData          Pointer to data block to write.
    1022      * @param   cbData          Size (in bytes) of data block to write.
    1023      */
    1024     int WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData)
    1025     {
    1026         RT_NOREF(cbData); /* Only needed for assertions for now. */
    1027 
    1028         int rc = RTCritSectEnter(&CurSeg.CritSect);
    1029         AssertRC(rc);
    1030 
    1031         WebMTracks::iterator itTrack = CurSeg.mapTracks.find(uTrack);
    1032         if (itTrack == CurSeg.mapTracks.end())
    1033         {
    1034             RTCritSectLeave(&CurSeg.CritSect);
    1035             return VERR_NOT_FOUND;
    1036         }
    1037 
    1038         WebMTrack *pTrack = itTrack->second;
    1039         AssertPtr(pTrack);
    1040 
    1041         if (m_fInTracksSection)
    1042         {
    1043             m_Ebml.subEnd(MkvElem_Tracks);
    1044             m_fInTracksSection = false;
    1045         }
    1046 
    1047         switch (pTrack->enmType)
    1048         {
    1049 
    1050             case WebMTrackType_Audio:
    1051             {
    1052 #ifdef VBOX_WITH_LIBOPUS
    1053                 if (m_enmAudioCodec == WebMWriter::AudioCodec_Opus)
    1054                 {
    1055                     Assert(cbData == sizeof(WebMWriter::BlockData_Opus));
    1056                     WebMWriter::BlockData_Opus *pData = (WebMWriter::BlockData_Opus *)pvData;
    1057                     rc = writeSimpleBlockOpus(pTrack, pData->pvData, pData->cbData, pData->uPTSMs);
    1058                 }
    1059                 else
    1060 #endif /* VBOX_WITH_LIBOPUS */
    1061                     rc = VERR_NOT_SUPPORTED;
    1062                 break;
    1063             }
    1064 
    1065             case WebMTrackType_Video:
    1066             {
    1067 #ifdef VBOX_WITH_LIBVPX
    1068                 if (m_enmVideoCodec == WebMWriter::VideoCodec_VP8)
    1069                 {
    1070                     Assert(cbData == sizeof(WebMWriter::BlockData_VP8));
    1071                     WebMWriter::BlockData_VP8 *pData = (WebMWriter::BlockData_VP8 *)pvData;
    1072                     rc = writeSimpleBlockVP8(pTrack, pData->pCfg, pData->pPkt);
    1073                 }
    1074                 else
    1075 #endif /* VBOX_WITH_LIBVPX */
    1076                     rc = VERR_NOT_SUPPORTED;
    1077                 break;
    1078             }
    1079 
    1080             default:
    1081                 rc = VERR_NOT_SUPPORTED;
    1082                 break;
    1083         }
    1084 
    1085         int rc2 = RTCritSectLeave(&CurSeg.CritSect);
    1086         AssertRC(rc2);
    1087 
    1088         return rc;
    1089     }
    1090 
    1091     /**
    1092      * Processes a render queue.
    1093      *
    1094      * @returns IPRT status code.
    1095      * @param   pQueue          Queue to process.
    1096      * @param   fForce          Whether forcing to process the render queue or not.
    1097      *                          Needed to drain the queues when terminating.
    1098      */
    1099     int processQueues(WebMQueue *pQueue, bool fForce)
    1100     {
    1101         if (pQueue->tslastProcessedMs == 0)
    1102             pQueue->tslastProcessedMs = RTTimeMilliTS();
    1103 
    1104         if (!fForce)
    1105         {
    1106             /* Only process when we reached a certain threshold. */
    1107             if (RTTimeMilliTS() - pQueue->tslastProcessedMs < 5000 /* ms */ /** @todo Make this configurable */)
    1108                 return VINF_SUCCESS;
    1109         }
    1110 
    1111         WebMCluster &Cluster = CurSeg.CurCluster;
    1112 
    1113         /* Iterate through the block map. */
    1114         WebMBlockMap::iterator it = pQueue->Map.begin();
    1115         while (it != CurSeg.queueBlocks.Map.end())
    1116         {
    1117             WebMTimecode       mapTC     = it->first;
    1118             RT_NOREF(mapTC);
    1119             WebMTimecodeBlocks mapBlocks = it->second;
    1120 
    1121             /* Whether to start a new cluster or not. */
    1122             bool fClusterStart = false;
    1123 
    1124             /* No blocks written yet? Start a new cluster. */
    1125             if (Cluster.cBlocks == 0)
    1126                 fClusterStart = true;
    1127 
    1128             /* Did we reach the maximum a cluster can hold? Use a new cluster then. */
    1129             if (mapTC - Cluster.tcStartMs > VBOX_WEBM_CLUSTER_MAX_LEN_MS)
    1130                 fClusterStart = true;
    1131 
    1132             /* If the block map indicates that a cluster is needed for this timecode, create one. */
    1133             if (mapBlocks.fClusterNeeded)
    1134                 fClusterStart = true;
    1135 
    1136             if (   fClusterStart
    1137                 && !mapBlocks.fClusterStarted)
    1138             {
    1139                 if (Cluster.fOpen) /* Close current cluster first. */
    1140                 {
    1141                     /* Make sure that the current cluster contained some data.  */
    1142                     Assert(Cluster.offStart);
    1143                     Assert(Cluster.cBlocks);
    1144 
    1145                     m_Ebml.subEnd(MkvElem_Cluster);
    1146                     Cluster.fOpen = false;
    1147                 }
    1148 
    1149                 Cluster.fOpen     = true;
    1150                 Cluster.uID       = CurSeg.cClusters;
    1151                 Cluster.tcStartMs = mapTC;
    1152                 Cluster.offStart  = RTFileTell(m_Ebml.getFile());
    1153                 Cluster.cBlocks   = 0;
    1154 
    1155                 if (CurSeg.cClusters)
    1156                     AssertMsg(Cluster.tcStartMs, ("[C%RU64] @ %RU64 starting timecode is 0 which is invalid\n",
    1157                                                   Cluster.uID, Cluster.offStart));
    1158 
    1159                 Log2Func(("[C%RU64] Start @ %RU64ms (map TC is %RU64) / %RU64 bytes\n",
    1160                           Cluster.uID, Cluster.tcStartMs, mapTC, Cluster.offStart));
    1161 
    1162                 m_Ebml.subStart(MkvElem_Cluster)
    1163                       .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tcStartMs);
    1164 
    1165                 CurSeg.cClusters++;
    1166 
    1167                 mapBlocks.fClusterStarted = true;
    1168             }
    1169 
    1170             /* Iterate through all blocks related to the current timecode. */
    1171             while (!mapBlocks.Queue.empty())
    1172             {
    1173                 WebMSimpleBlock *pBlock = mapBlocks.Queue.front();
    1174                 AssertPtr(pBlock);
    1175 
    1176                 WebMTrack       *pTrack = pBlock->pTrack;
    1177                 AssertPtr(pTrack);
    1178 
    1179                 /* Calculate the block's relative time code to the current cluster's starting time code. */
    1180                 Assert(pBlock->Data.tcPTSMs >= Cluster.tcStartMs);
    1181                 pBlock->Data.tcRelToClusterMs = pBlock->Data.tcPTSMs - Cluster.tcStartMs;
    1182 
    1183                 int rc2 = writeSimpleBlockEBML(pTrack, pBlock);
    1184                 AssertRC(rc2);
    1185 
    1186                 Cluster.cBlocks++;
    1187 
    1188                 pTrack->cTotalBlocks++;
    1189                 pTrack->tcLastWrittenMs = pBlock->Data.tcPTSMs;
    1190 
    1191                 if (CurSeg.tcLastWrittenMs < pTrack->tcLastWrittenMs)
    1192                     CurSeg.tcLastWrittenMs = pTrack->tcLastWrittenMs;
    1193 
    1194                 /* Save a cue point if this is a keyframe. */
    1195                 if (pBlock->Data.fFlags & VBOX_WEBM_BLOCK_FLAG_KEY_FRAME)
    1196                 {
    1197                     WebMCuePoint cue(pBlock->pTrack, Cluster.offStart, pBlock->Data.tcPTSMs);
    1198                     CurSeg.lstCues.push_back(cue);
    1199                 }
    1200 
    1201                 delete pBlock;
    1202                 pBlock = NULL;
    1203 
    1204                 mapBlocks.Queue.pop();
    1205             }
    1206 
    1207             Assert(mapBlocks.Queue.empty());
    1208 
    1209             CurSeg.queueBlocks.Map.erase(it);
    1210 
    1211             it = CurSeg.queueBlocks.Map.begin();
    1212         }
    1213 
    1214         Assert(CurSeg.queueBlocks.Map.empty());
    1215 
    1216         pQueue->tslastProcessedMs = RTTimeMilliTS();
    1217 
    1218         return VINF_SUCCESS;
    1219     }
    1220 
    1221     /**
    1222      * Writes the WebM footer.
    1223      *
    1224      * @returns IPRT status code.
    1225      */
    1226     int writeFooter(void)
    1227     {
    1228         AssertReturn(m_Ebml.isOpen(), VERR_WRONG_ORDER);
    1229 
    1230         if (m_fInTracksSection)
    1231         {
    1232             m_Ebml.subEnd(MkvElem_Tracks);
    1233             m_fInTracksSection = false;
    1234         }
    1235 
    1236         if (CurSeg.CurCluster.fOpen)
    1237         {
    1238             m_Ebml.subEnd(MkvElem_Cluster);
    1239             CurSeg.CurCluster.fOpen = false;
    1240         }
    1241 
    1242         /*
    1243          * Write Cues element.
    1244          */
    1245         LogFunc(("Cues @ %RU64\n", RTFileTell(m_Ebml.getFile())));
    1246 
    1247         CurSeg.offCues = RTFileTell(m_Ebml.getFile());
    1248 
    1249         m_Ebml.subStart(MkvElem_Cues);
    1250 
    1251         std::list<WebMCuePoint>::iterator itCuePoint = CurSeg.lstCues.begin();
    1252         while (itCuePoint != CurSeg.lstCues.end())
    1253         {
    1254             /* Sanity. */
    1255             AssertPtr(itCuePoint->pTrack);
    1256 
    1257             LogFunc(("CuePoint @ %RU64: Track #%RU8 (Cluster @ %RU64, TC %RU64)\n",
    1258                      RTFileTell(m_Ebml.getFile()), itCuePoint->pTrack->uTrack,
    1259                      itCuePoint->offCluster, itCuePoint->tcAbs));
    1260 
    1261             m_Ebml.subStart(MkvElem_CuePoint)
    1262                       .serializeUnsignedInteger(MkvElem_CueTime,                itCuePoint->tcAbs)
    1263                       .subStart(MkvElem_CueTrackPositions)
    1264                           .serializeUnsignedInteger(MkvElem_CueTrack,           itCuePoint->pTrack->uTrack)
    1265                           .serializeUnsignedInteger(MkvElem_CueClusterPosition, itCuePoint->offCluster, 8)
    1266                   .subEnd(MkvElem_CueTrackPositions)
    1267                   .subEnd(MkvElem_CuePoint);
    1268 
    1269             itCuePoint++;
    1270         }
    1271 
    1272         m_Ebml.subEnd(MkvElem_Cues);
    1273         m_Ebml.subEnd(MkvElem_Segment);
    1274 
    1275         /*
    1276          * Re-Update SeekHead / Info elements.
    1277          */
    1278 
    1279         writeSegSeekInfo();
    1280 
    1281         return RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL);
    1282     }
    1283 
    1284     /**
    1285      * Closes the WebM file and drains all queues.
    1286      *
    1287      * @returns IPRT status code.
    1288      */
    1289     int close(void)
    1290     {
    1291         if (!m_Ebml.isOpen())
    1292             return VINF_SUCCESS;
    1293 
    1294         LogFunc(("\n"));
    1295 
    1296         /* Make sure to drain all queues. */
    1297         processQueues(&CurSeg.queueBlocks, true /* fForce */);
    1298 
    1299         writeFooter();
    1300 
    1301         WebMTracks::iterator itTrack = CurSeg.mapTracks.begin();
    1302         for (; itTrack != CurSeg.mapTracks.end(); ++itTrack)
    1303         {
    1304             WebMTrack *pTrack = itTrack->second;
    1305 
    1306             delete pTrack;
    1307             CurSeg.mapTracks.erase(itTrack);
    1308         }
    1309 
    1310         Assert(CurSeg.queueBlocks.Map.size() == 0);
    1311         Assert(CurSeg.mapTracks.size() == 0);
    1312 
    1313         m_Ebml.close();
    1314 
    1315         return VINF_SUCCESS;
    1316     }
    1317 
    1318     friend class Ebml;
    1319     friend class WebMWriter;
    1320 
    1321 private:
    1322 
    1323     /**
    1324      * Writes the segment's seek information and cue points.
    1325      */
    1326     void writeSegSeekInfo(void)
    1327     {
    1328         if (CurSeg.offSeekInfo)
    1329             RTFileSeek(m_Ebml.getFile(), CurSeg.offSeekInfo, RTFILE_SEEK_BEGIN, NULL);
    1330         else
    1331             CurSeg.offSeekInfo = RTFileTell(m_Ebml.getFile());
    1332 
    1333         LogFunc(("SeekHead @ %RU64\n", CurSeg.offSeekInfo));
    1334 
    1335         m_Ebml.subStart(MkvElem_SeekHead);
    1336 
    1337         m_Ebml.subStart(MkvElem_Seek)
    1338               .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Tracks)
    1339               .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offTracks - CurSeg.offStart, 8)
    1340               .subEnd(MkvElem_Seek);
    1341 
    1342         Assert(CurSeg.offCues - CurSeg.offStart > 0); /* Sanity. */
    1343 
    1344         m_Ebml.subStart(MkvElem_Seek)
    1345               .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Cues)
    1346               .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offCues - CurSeg.offStart, 8)
    1347               .subEnd(MkvElem_Seek);
    1348 
    1349         m_Ebml.subStart(MkvElem_Seek)
    1350               .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Info)
    1351               .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offInfo - CurSeg.offStart, 8)
    1352               .subEnd(MkvElem_Seek);
    1353 
    1354         m_Ebml.subEnd(MkvElem_SeekHead);
    1355 
    1356         //int64_t iFrameTime = (int64_t)1000 * 1 / 25; //m_Framerate.den / m_Framerate.num; /** @todo Fix this! */
    1357         CurSeg.offInfo = RTFileTell(m_Ebml.getFile());
    1358 
    1359         LogFunc(("Info @ %RU64\n", CurSeg.offInfo));
    1360 
    1361         char szMux[64];
    1362         RTStrPrintf(szMux, sizeof(szMux),
    1363 #ifdef VBOX_WITH_LIBVPX
    1364                      "vpxenc%s", vpx_codec_version_str());
    1365 #else
    1366                      "unknown");
    1367 #endif
    1368         char szApp[64];
    1369         RTStrPrintf(szApp, sizeof(szApp), VBOX_PRODUCT " %sr%u", VBOX_VERSION_STRING, RTBldCfgRevision());
    1370 
    1371         const WebMTimecode tcDuration = CurSeg.tcLastWrittenMs - CurSeg.tcStartMs;
    1372 
    1373         if (!CurSeg.lstCues.empty())
    1374         {
    1375             LogFunc(("tcDuration=%RU64\n", tcDuration));
    1376             AssertMsg(tcDuration, ("Segment seems to be empty\n"));
    1377         }
    1378 
    1379         m_Ebml.subStart(MkvElem_Info)
    1380               .serializeUnsignedInteger(MkvElem_TimecodeScale, CurSeg.uTimecodeScaleFactor)
    1381               .serializeFloat(MkvElem_Segment_Duration, tcDuration)
    1382               .serializeString(MkvElem_MuxingApp, szMux)
    1383               .serializeString(MkvElem_WritingApp, szApp)
    1384               .subEnd(MkvElem_Info);
    1385     }
    1386 };
    1387 
    1388 WebMWriter::WebMWriter(void)
    1389     : m_pImpl(new WebMWriter_Impl()) {}
    1390 
    1391 WebMWriter::~WebMWriter(void)
    1392 {
    1393     if (m_pImpl)
    1394     {
    1395         m_pImpl->destroy();
    1396 
    1397         delete m_pImpl;
    1398     }
    1399 }
    1400 
    1401 int WebMWriter::OpenEx(const char *a_pszFilename, PRTFILE a_phFile,
    1402                        WebMWriter::AudioCodec a_enmAudioCodec, WebMWriter::VideoCodec a_enmVideoCodec)
    1403 {
    1404     try
    1405     {
    1406         m_pImpl->m_enmAudioCodec = a_enmAudioCodec;
    1407         m_pImpl->m_enmVideoCodec = a_enmVideoCodec;
    1408 
    1409         LogFunc(("Creating '%s'\n", a_pszFilename));
    1410 
    1411         int rc = m_pImpl->m_Ebml.createEx(a_pszFilename, a_phFile);
    1412         if (RT_SUCCESS(rc))
    1413         {
    1414             rc = m_pImpl->init();
    1415             if (RT_SUCCESS(rc))
    1416                 rc = m_pImpl->writeHeader();
    1417         }
    1418     }
    1419     catch(int rc)
    1420     {
    1421         return rc;
    1422     }
    1423     return VINF_SUCCESS;
    1424 }
    1425 
    1426 int WebMWriter::Open(const char *a_pszFilename, uint64_t a_fOpen,
    1427                      WebMWriter::AudioCodec a_enmAudioCodec, WebMWriter::VideoCodec a_enmVideoCodec)
    1428 {
    1429     try
    1430     {
    1431         m_pImpl->m_enmAudioCodec = a_enmAudioCodec;
    1432         m_pImpl->m_enmVideoCodec = a_enmVideoCodec;
    1433 
    1434         LogFunc(("Creating '%s'\n", a_pszFilename));
    1435 
    1436         int rc = m_pImpl->m_Ebml.create(a_pszFilename, a_fOpen);
    1437         if (RT_SUCCESS(rc))
    1438         {
    1439             rc = m_pImpl->init();
    1440             if (RT_SUCCESS(rc))
    1441                 rc = m_pImpl->writeHeader();
    1442         }
    1443     }
    1444     catch(int rc)
    1445     {
    1446         return rc;
    1447     }
    1448     return VINF_SUCCESS;
    1449 }
    1450 
    1451 int WebMWriter::Close(void)
    1452 {
    1453     return m_pImpl->close();
    1454 }
    1455 
    1456 int WebMWriter::AddAudioTrack(uint16_t uHz, uint8_t cChannels, uint8_t cBitDepth, uint8_t *puTrack)
    1457 {
    1458     return m_pImpl->AddAudioTrack(uHz, cChannels, cBitDepth, puTrack);
    1459 }
    1460 
    1461 int WebMWriter::AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS, uint8_t *puTrack)
    1462 {
    1463     return m_pImpl->AddVideoTrack(uWidth, uHeight, dbFPS, puTrack);
    1464 }
    1465 
    1466 int WebMWriter::WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData)
    1467 {
    1468     int rc;
    1469 
    1470     try
    1471     {
    1472         rc = m_pImpl->WriteBlock(uTrack, pvData, cbData);
    1473     }
    1474     catch(int rc2)
    1475     {
    1476         rc = rc2;
    1477     }
    1478     return rc;
    1479 }
    1480 
    1481 const Utf8Str& WebMWriter::GetFileName(void)
    1482 {
    1483     return m_pImpl->m_Ebml.getFileName();
    1484 }
    1485 
    1486 uint64_t WebMWriter::GetFileSize(void)
    1487 {
    1488     return m_pImpl->m_Ebml.getFileSize();
    1489 }
    1490 
    1491 uint64_t WebMWriter::GetAvailableSpace(void)
    1492 {
    1493     return m_pImpl->m_Ebml.getAvailableSpace();
    1494 }
    1495 
     239    size_t size = 8 - ! (parm & (UINT64_MAX << 49)) - ! (parm & (UINT64_MAX << 42)) -
     240                      ! (parm & (UINT64_MAX << 35)) - ! (parm & (UINT64_MAX << 28)) -
     241                      ! (parm & (UINT64_MAX << 21)) - ! (parm & (UINT64_MAX << 14)) -
     242                      ! (parm & (UINT64_MAX << 7));
     243    /* One is subtracted in order to avoid loosing significant bit when size = 8. */
     244    uint64_t mask = RT_BIT_64(size * 8 - 1);
     245    writeUnsignedInteger((parm & (((mask << 1) - 1) >> size)) | (mask >> (size - 1)), size);
     246}
     247
     248/** Size calculation for variable size UNSIGNED integer.
     249 *
     250 * The function defines the size of the number by trimming
     251 * consequent trailing zero bytes starting from the most significant.
     252 * The following statement is always true:
     253 * 1 <= getSizeOfUInt(arg) <= 8.
     254 *
     255 * Every !(arg & (UINT64_MAX << X)) expression gives one
     256 * if an only if all the bits from X to 63 are set to zero.
     257 */
     258size_t EBMLWriter::getSizeOfUInt(uint64_t arg)
     259{
     260    return 8 - ! (arg & (UINT64_MAX << 56)) - ! (arg & (UINT64_MAX << 48)) -
     261               ! (arg & (UINT64_MAX << 40)) - ! (arg & (UINT64_MAX << 32)) -
     262               ! (arg & (UINT64_MAX << 24)) - ! (arg & (UINT64_MAX << 16)) -
     263               ! (arg & (UINT64_MAX << 8));
     264}
     265
  • trunk/src/VBox/Main/src-client/EBMLWriter.h

    r69192 r69683  
    11/* $Id$ */
    22/** @file
    3  * EbmlWriter.h - EBML writer + WebM container.
     3 * EBMLWriter.h - EBML writer.
    44 */
    55
     
    1919#define ____EBMLWRITER
    2020
    21 #ifdef VBOX_WITH_LIBVPX
    22 # ifdef _MSC_VER
    23 #  pragma warning(push)
    24 #  pragma warning(disable: 4668) /* vpx_codec.h(64) : warning C4668: '__GNUC__' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */
    25 #  include <vpx/vpx_encoder.h>
    26 #  pragma warning(pop)
    27 # else
    28 #  include <vpx/vpx_encoder.h>
    29 # endif
    30 #endif /* VBOX_WITH_LIBVPX */
    31 
    3221#include <iprt/file.h>
    33 
    3422#include <VBox/com/string.h> /* For Utf8Str. */
    3523
    3624using namespace com;
    3725
    38 class WebMWriter_Impl;
     26#ifdef LOG_GROUP
     27# undef LOG_GROUP
     28#endif
     29#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
     30#include "LoggingNew.h"
    3931
    40 class WebMWriter
     32#include <list>
     33#include <map>
     34#include <queue>
     35#include <stack>
     36
     37#include <math.h> /* For lround.h. */
     38
     39#include <iprt/asm.h>
     40#include <iprt/buildconfig.h>
     41#include <iprt/cdefs.h>
     42#include <iprt/critsect.h>
     43#include <iprt/err.h>
     44#include <iprt/file.h>
     45#include <iprt/rand.h>
     46#include <iprt/string.h>
     47
     48#include <VBox/log.h>
     49#include <VBox/version.h>
     50
     51/** No flags set. */
     52#define VBOX_EBMLWRITER_FLAG_NONE               0
     53/** The file handle was inherited. */
     54#define VBOX_EBMLWRITER_FLAG_HANDLE_INHERITED   RT_BIT(0)
     55
     56class EBMLWriter
    4157{
     58public:
     59    typedef uint32_t EbmlClassId;
     60
     61private:
     62
     63    struct EbmlSubElement
     64    {
     65        uint64_t offset;
     66        EbmlClassId classId;
     67        EbmlSubElement(uint64_t offs, EbmlClassId cid) : offset(offs), classId(cid) {}
     68    };
     69
     70    /** Stack of EBML sub elements. */
     71    std::stack<EbmlSubElement> m_Elements;
     72    /** The file's handle. */
     73    RTFILE                     m_hFile;
     74    /** The file's name (path). */
     75    Utf8Str                    m_strFile;
     76    /** Flags. */
     77    uint32_t                   m_fFlags;
    4278
    4379public:
    4480
    45     /**
    46      * Supported audio codecs.
    47      */
    48     enum AudioCodec
    49     {
    50         /** No audio codec specified. */
    51         AudioCodec_None = 0,
    52         /** Opus. */
    53         AudioCodec_Opus = 1
    54     };
     81    EBMLWriter(void)
     82        : m_hFile(NIL_RTFILE)
     83        , m_fFlags(VBOX_EBMLWRITER_FLAG_NONE) { }
    5584
    56     /**
    57      * Supported video codecs.
    58      */
    59     enum VideoCodec
    60     {
    61         /** No video codec specified. */
    62         VideoCodec_None = 0,
    63         /** VP8. */
    64         VideoCodec_VP8  = 1
    65     };
    66 
    67 #ifdef VBOX_WITH_LIBVPX
    68     /**
    69      * Block data for VP8-encoded video data.
    70      */
    71     struct BlockData_VP8
    72     {
    73         const vpx_codec_enc_cfg_t *pCfg;
    74         const vpx_codec_cx_pkt_t  *pPkt;
    75     };
    76 #endif /* VBOX_WITH_LIBVPX */
    77 
    78 #ifdef VBOX_WITH_LIBOPUS
    79     /**
    80      * Block data for Opus-encoded audio data.
    81      */
    82     struct BlockData_Opus
    83     {
    84         /** Pointer to encoded Opus audio data. */
    85         const void *pvData;
    86         /** Size (in bytes) of encoded Opus audio data. */
    87         size_t      cbData;
    88         /** PTS (in ms) of encoded Opus audio data. */
    89         uint64_t    uPTSMs;
    90     };
    91 #endif /* VBOX_WITH_LIBOPUS */
     85    virtual ~EBMLWriter(void) { close(); }
    9286
    9387public:
    9488
    95     WebMWriter();
    96     virtual ~WebMWriter();
     89    int createEx(const char *a_pszFile, PRTFILE phFile);
     90
     91    int create(const char *a_pszFile, uint64_t fOpen);
     92
     93    void close(void);
     94
     95    /** Returns the file name. */
     96    const Utf8Str& getFileName(void) { return m_strFile; }
     97
     98    /** Returns file size. */
     99    uint64_t getFileSize(void) { return RTFileTell(m_hFile); }
     100
     101    /** Get reference to file descriptor */
     102    inline const RTFILE &getFile(void) { return m_hFile; }
     103
     104    /** Returns available space on storage. */
     105    uint64_t getAvailableSpace(void);
    97106
    98107    /**
    99      * Opens (creates) an output file using an already open file handle.
     108     * Returns whether the file is open or not.
    100109     *
    101      * @param   a_pszFilename   Name of the file the file handle points at.
    102      * @param   a_phFile        Pointer to open file handle to use.
    103      * @param   a_enmAudioCodec Audio codec to use.
    104      * @param   a_enmVideoCodec Video codec to use.
    105      *
    106      * @returns VBox status code. */
    107     int OpenEx(const char *a_pszFilename, PRTFILE a_phFile,
    108                WebMWriter::AudioCodec a_enmAudioCodec, WebMWriter::VideoCodec a_enmVideoCodec);
     110     * @returns True if open, false if not.
     111     */
     112    bool isOpen(void) { return RTFileIsValid(m_hFile); }
    109113
    110     /**
    111      * Opens an output file.
    112      *
    113      * @param   a_pszFilename   Name of the file to create.
    114      * @param   a_fOpen         File open mode of type RTFILE_O_.
    115      * @param   a_enmAudioCodec Audio codec to use.
    116      * @param   a_enmVideoCodec Video codec to use.
    117      *
    118      * @returns VBox status code. */
    119     int Open(const char *a_pszFilename, uint64_t a_fOpen,
    120              WebMWriter::AudioCodec a_enmAudioCodec, WebMWriter::VideoCodec a_enmVideoCodec);
     114public:
    121115
    122     /** Closes output file. */
    123     int Close(void);
     116    EBMLWriter &subStart(EbmlClassId classId);
    124117
    125     int AddAudioTrack(uint16_t uHz, uint8_t cChannels, uint8_t cBits, uint8_t *puTrack);
     118    EBMLWriter &subEnd(EbmlClassId classId);
    126119
    127     int AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS, uint8_t *puTrack);
     120    EBMLWriter &serializeString(EbmlClassId classId, const char *str);
    128121
    129     /**
    130      * Writes a block of compressed data.
    131      *
    132      * @param uTrack            Track number to write data to.
    133      * @param pvData            Pointer to block data to write.
    134      * @param cbData            Size (in bytes) of block data to write.
    135      *
    136      * @returns VBox status code.
    137      */
    138     int WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData);
     122    EBMLWriter &serializeUnsignedInteger(EbmlClassId classId, uint64_t parm, size_t size = 0);
    139123
    140     /**
    141      * Gets file name.
    142      *
    143      * @returns File name as UTF-8 string.
    144      */
    145     const Utf8Str& GetFileName(void);
     124    EBMLWriter &serializeFloat(EbmlClassId classId, float value);
    146125
    147     /**
    148      * Gets current output file size.
    149      *
    150      * @returns File size in bytes.
    151      */
    152     uint64_t GetFileSize(void);
     126    EBMLWriter &serializeData(EbmlClassId classId, const void *pvData, size_t cbData);
    153127
    154     /**
    155      * Gets current free storage space available for the file.
    156      *
    157      * @returns Available storage free space.
    158      */
    159     uint64_t GetAvailableSpace(void);
     128    int write(const void *data, size_t size);
     129
     130    void writeUnsignedInteger(uint64_t value, size_t size = sizeof(uint64_t));
     131
     132    void writeClassId(EbmlClassId parm);
     133
     134    void writeSize(uint64_t parm);
     135
     136    static inline size_t getSizeOfUInt(uint64_t arg);
    160137
    161138private:
    162139
    163     /** WebMWriter implementation.
    164      *  To isolate some include files. */
    165     WebMWriter_Impl *m_pImpl;
    166 
    167     DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(WebMWriter);
     140    void operator=(const EBMLWriter &);
    168141};
    169142
  • trunk/src/VBox/Main/src-client/VideoRec.cpp

    r69240 r69683  
    1616 */
    1717
     18#ifdef LOG_GROUP
     19# undef LOG_GROUP
     20#endif
    1821#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
    1922#include "LoggingNew.h"
     
    3235#include <VBox/com/VirtualBox.h>
    3336
    34 #include "EBMLWriter.h"
     37#include "WebMWriter.h"
    3538#include "VideoRec.h"
    3639
Note: See TracChangeset for help on using the changeset viewer.

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