VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/EbmlWriter.cpp@ 47469

Last change on this file since 47469 was 45924, checked in by vboxsync, 11 years ago

warning

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
  • Property svn:mergeinfo set to (toggle deleted branches)
    /branches/VBox-3.0/src/VBox/Frontends/VBoxHeadless/VideoCapture/EbmlWriter.cpp58652,​70973
    /branches/VBox-3.2/src/VBox/Frontends/VBoxHeadless/VideoCapture/EbmlWriter.cpp66309,​66318
    /branches/VBox-4.0/src/VBox/Frontends/VBoxHeadless/VideoCapture/EbmlWriter.cpp70873
    /branches/VBox-4.1/src/VBox/Frontends/VBoxHeadless/VideoCapture/EbmlWriter.cpp74233
    /branches/dsen/gui/src/VBox/Frontends/VBoxHeadless/VideoCapture/EbmlWriter.cpp79076-79078,​79089,​79109-79110,​79112-79113,​79127-79130,​79134,​79141,​79151,​79155,​79157-79159,​79193,​79197
    /branches/dsen/gui2/src/VBox/Frontends/VBoxHeadless/VideoCapture/EbmlWriter.cpp79224,​79228,​79233,​79235,​79258,​79262-79263,​79273,​79341,​79345,​79354,​79357,​79387-79388,​79559-79569,​79572-79573,​79578,​79581-79582,​79590-79591,​79598-79599,​79602-79603,​79605-79606,​79632,​79635,​79637,​79644
    /branches/dsen/gui3/src/VBox/Frontends/VBoxHeadless/VideoCapture/EbmlWriter.cpp79645-79692
File size: 16.2 KB
Line 
1/* $Id: EbmlWriter.cpp 45924 2013-05-06 19:34:30Z vboxsync $ */
2/** @file
3 * EbmlWriter.cpp - EBML writer + WebM container
4 */
5
6/*
7 * Copyright (C) 2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*
19 * This code is based on:
20 *
21 * Copyright (c) 2010 The WebM project authors. All Rights Reserved.
22 * Use of this source code is governed by a BSD-style license
23 * that can be found in the LICENSE file in the root of the source
24 * tree. An additional intellectual property rights grant can be found
25 * in the file PATENTS. All contributing project authors may
26 * be found in the AUTHORS file in the root of the source tree.
27 */
28
29#include "EbmlWriter.h"
30#include <iprt/asm.h>
31#include <iprt/mem.h>
32#include <iprt/string.h>
33#include <VBox/log.h>
34
35static int ebml_Write(EbmlGlobal *glob, const void *pv, size_t cb)
36{
37 return RTFileWrite(glob->file, pv, cb, NULL);
38}
39
40static int ebml_WriteU8(EbmlGlobal *glob, uint8_t u8)
41{
42 return ebml_Write(glob, &u8, 1);
43}
44
45static int ebml_WriteU16(EbmlGlobal *glob, uint16_t u16)
46{
47 return ebml_Write(glob, &u16, 2);
48}
49
50static int ebml_WriteU32(EbmlGlobal *glob, uint32_t u32)
51{
52 return ebml_Write(glob, &u32, 4);
53}
54
55static int ebml_WriteU64(EbmlGlobal *glob, uint64_t u64)
56{
57 return ebml_Write(glob, &u64, 8);
58}
59
60static int ebml_Serialize(EbmlGlobal *glob, const uint8_t *pb, size_t cb)
61{
62 for (; cb; cb--)
63 {
64 int rc = ebml_WriteU8(glob, pb[cb-1]);
65 if (RT_FAILURE(rc))
66 return rc;
67 }
68 return VINF_SUCCESS;
69}
70
71static int ebml_WriteID(EbmlGlobal *glob, uint32_t class_id)
72{
73 int rc;
74 if (class_id >= 0x01000000)
75 rc = ebml_WriteU32(glob, RT_H2BE_U32(class_id));
76 else if (class_id >= 0x00010000)
77 rc = ebml_Serialize(glob, (uint8_t*)&class_id, 3);
78 else if (class_id >= 0x00000100)
79 rc = ebml_WriteU16(glob, RT_H2BE_U16((uint16_t)class_id));
80 else
81 rc = ebml_WriteU8(glob, (uint8_t)class_id);
82 return rc;
83}
84
85static int ebml_SerializeUnsigned32(EbmlGlobal *glob, uint32_t class_id, uint32_t ui)
86{
87 int rc = ebml_WriteID(glob, class_id);
88 rc = ebml_WriteU8(glob, 4 | 0x80);
89 if (RT_SUCCESS(rc))
90 rc = ebml_WriteU32(glob, RT_H2BE_U32(ui));
91 return rc;
92}
93
94static int ebml_StartSubElement(EbmlGlobal *glob, uint64_t *ebmlLoc, uint32_t class_id)
95{
96 // todo this is always taking 8 bytes, this may need later optimization
97 // this is a key that says lenght unknown
98 uint64_t unknownLen = UINT64_C(0x01FFFFFFFFFFFFFF);
99
100 ebml_WriteID(glob, class_id);
101 *ebmlLoc = RTFileTell(glob->file);
102 return ebml_WriteU64(glob, RT_H2BE_U64(unknownLen));
103}
104
105static int ebml_EndSubElement(EbmlGlobal *glob, uint64_t ebmlLoc)
106{
107 /* Save the current file pointer */
108 uint64_t pos = RTFileTell(glob->file);
109
110 /* Calculate the size of this element */
111 uint64_t size = pos - ebmlLoc - 8;
112 size |= UINT64_C(0x0100000000000000);
113
114 /* Seek back to the beginning of the element and write the new size */
115 RTFileSeek(glob->file, ebmlLoc, RTFILE_SEEK_BEGIN, NULL);
116 int rc = ebml_WriteU64(glob, RT_H2BE_U64(size));
117
118 /* Reset the file pointer */
119 RTFileSeek(glob->file, pos, RTFILE_SEEK_BEGIN, NULL);
120
121 return rc;
122}
123
124static int ebml_WriteLen(EbmlGlobal *glob, uint64_t val)
125{
126 //TODO check and make sure we are not > than 0x0100000000000000LLU
127 size_t size = 8;
128 uint64_t minVal = UINT64_C(0x00000000000000ff); //mask to compare for byte size
129
130 for (size = 1; size < 8; size ++)
131 {
132 if (val < minVal)
133 break;
134
135 minVal = (minVal << 7);
136 }
137
138 val |= (UINT64_C(0x000000000000080) << ((size - 1) * 7));
139
140 return ebml_Serialize(glob, (uint8_t *)&val, size);
141}
142
143static int ebml_WriteString(EbmlGlobal *glob, const char *str)
144{
145 const size_t cb = strlen(str);
146 int rc = ebml_WriteLen(glob, cb);
147 //TODO: it's not clear from the spec whether the nul terminator
148 //should be serialized too. For now we omit the null terminator.
149 if (RT_SUCCESS(rc))
150 rc = ebml_Write(glob, str, cb);
151 return rc;
152}
153
154int Ebml_SerializeUnsigned64(EbmlGlobal *glob, uint32_t class_id, uint64_t ui)
155{
156 int rc = ebml_WriteID(glob, class_id);
157 if (RT_SUCCESS(rc))
158 rc = ebml_WriteU8(glob, 8 | 0x80);
159 if (RT_SUCCESS(rc))
160 rc = ebml_WriteU64(glob, RT_H2BE_U64(ui));
161 return rc;
162}
163
164int Ebml_SerializeUnsigned(EbmlGlobal *glob, uint32_t class_id, uint32_t ui)
165{
166 int rc = ebml_WriteID(glob, class_id);
167 if (RT_FAILURE(rc))
168 return rc;
169
170 uint32_t minVal = 0x7fLU; //mask to compare for byte size
171 size_t size = 8; //size in bytes to output
172 for (size = 1; size < 4; size ++)
173 {
174 if (ui < minVal)
175 break;
176
177 minVal <<= 7;
178 }
179
180 rc = ebml_WriteU8(glob, 0x80 | size);
181 if (RT_SUCCESS(rc))
182 rc = ebml_Serialize(glob, (uint8_t*)&ui, size);
183 return rc;
184}
185
186//TODO: perhaps this is a poor name for this id serializer helper function
187int Ebml_SerializeBinary(EbmlGlobal *glob, uint32_t class_id, uint32_t bin)
188{
189 int size;
190 for (size = 4; size > 1; size--)
191 {
192 if (bin & 0x000000ff << ((size-1) * 8))
193 break;
194 }
195 int rc = ebml_WriteID(glob, class_id);
196 if (RT_SUCCESS(rc))
197 rc = ebml_WriteLen(glob, size);
198 if (RT_SUCCESS(rc))
199 rc = ebml_WriteID(glob, bin);
200 return rc;
201}
202
203int Ebml_SerializeFloat(EbmlGlobal *glob, uint32_t class_id, double d)
204{
205 int rc = ebml_WriteID(glob, class_id);
206 if (RT_SUCCESS(rc))
207 rc = ebml_WriteU8(glob, 0x80 | 8);
208 if (RT_SUCCESS(rc))
209 rc = ebml_WriteU64(glob, RT_H2BE_U64(*(uint64_t*)&d));
210 return rc;
211}
212
213int Ebml_SerializeString(EbmlGlobal *glob, uint32_t class_id, const char *s)
214{
215 int rc = ebml_WriteID(glob, class_id);
216 if (RT_SUCCESS(rc))
217 rc = ebml_WriteString(glob, s);
218 return rc;
219}
220
221int Ebml_WriteWebMSeekElement(EbmlGlobal *ebml, uint32_t id, uint64_t pos)
222{
223 uint64_t offset = pos - ebml->position_reference;
224 uint64_t start;
225 int rc = ebml_StartSubElement(ebml, &start, Seek);
226 if (RT_SUCCESS(rc))
227 rc = Ebml_SerializeBinary(ebml, SeekID, id);
228 if (RT_SUCCESS(rc))
229 rc = Ebml_SerializeUnsigned64(ebml, SeekPosition, offset);
230 if (RT_SUCCESS(rc))
231 rc = ebml_EndSubElement(ebml, start);
232 return rc;
233}
234
235int Ebml_WriteWebMSeekInfo(EbmlGlobal *ebml)
236{
237 int rc = VINF_SUCCESS;
238
239 /* Save the current file pointer */
240 uint64_t pos = RTFileTell(ebml->file);
241
242 if (ebml->seek_info_pos)
243 rc = RTFileSeek(ebml->file, ebml->seek_info_pos, RTFILE_SEEK_BEGIN, NULL);
244 else
245 ebml->seek_info_pos = pos;
246
247 {
248 uint64_t start;
249
250 if (RT_SUCCESS(rc))
251 rc = ebml_StartSubElement(ebml, &start, SeekHead);
252 if (RT_SUCCESS(rc))
253 rc = Ebml_WriteWebMSeekElement(ebml, Tracks, ebml->track_pos);
254 if (RT_SUCCESS(rc))
255 rc = Ebml_WriteWebMSeekElement(ebml, Cues, ebml->cue_pos);
256 if (RT_SUCCESS(rc))
257 rc = Ebml_WriteWebMSeekElement(ebml, Info, ebml->segment_info_pos);
258 if (RT_SUCCESS(rc))
259 rc = ebml_EndSubElement(ebml, start);
260 }
261 {
262 //segment info
263 uint64_t startInfo;
264 uint64_t frame_time;
265
266 frame_time = (uint64_t)1000 * ebml->framerate.den / ebml->framerate.num;
267 ebml->segment_info_pos = RTFileTell(ebml->file);
268 if (RT_SUCCESS(rc))
269 rc = ebml_StartSubElement(ebml, &startInfo, Info);
270 if (RT_SUCCESS(rc))
271 rc = Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000);
272 if (RT_SUCCESS(rc))
273 rc = Ebml_SerializeFloat(ebml, Segment_Duration,
274 (double)(ebml->last_pts_ms + frame_time));
275 char szVersion[64];
276 RTStrPrintf(szVersion, sizeof(szVersion), "vpxenc%",
277 ebml->debug ? vpx_codec_version_str() : "");
278 if (RT_SUCCESS(rc))
279 rc = Ebml_SerializeString(ebml, MuxingApp, szVersion);
280 if (RT_SUCCESS(rc))
281 rc = Ebml_SerializeString(ebml, WritingApp, szVersion);
282 if (RT_SUCCESS(rc))
283 rc = ebml_EndSubElement(ebml, startInfo);
284 }
285 return rc;
286}
287
288int Ebml_WriteWebMFileHeader(EbmlGlobal *glob,
289 const vpx_codec_enc_cfg_t *cfg,
290 const struct vpx_rational *fps)
291{
292 int rc = VINF_SUCCESS;
293 {
294 uint64_t start;
295 rc = ebml_StartSubElement(glob, &start, EBML);
296 if (RT_SUCCESS(rc))
297 rc = Ebml_SerializeUnsigned(glob, EBMLVersion, 1);
298 if (RT_SUCCESS(rc))
299 rc = Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1);
300 if (RT_SUCCESS(rc))
301 rc = Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4);
302 if (RT_SUCCESS(rc))
303 rc = Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8);
304 if (RT_SUCCESS(rc))
305 rc = Ebml_SerializeString(glob, DocType, "webm");
306 if (RT_SUCCESS(rc))
307 rc = Ebml_SerializeUnsigned(glob, DocTypeVersion, 2);
308 if (RT_SUCCESS(rc))
309 rc = Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2);
310 if (RT_SUCCESS(rc))
311 rc = ebml_EndSubElement(glob, start);
312 }
313 {
314 if (RT_SUCCESS(rc))
315 rc = ebml_StartSubElement(glob, &glob->startSegment, Segment);
316 glob->position_reference = RTFileTell(glob->file);
317 glob->framerate = *fps;
318 if (RT_SUCCESS(rc))
319 rc = Ebml_WriteWebMSeekInfo(glob);
320 {
321 uint64_t trackStart;
322 glob->track_pos = RTFileTell(glob->file);
323 if (RT_SUCCESS(rc))
324 rc = ebml_StartSubElement(glob, &trackStart, Tracks);
325 {
326 uint32_t trackNumber = 1;
327 uint32_t trackID = 0;
328
329 uint64_t start;
330 if (RT_SUCCESS(rc))
331 rc = ebml_StartSubElement(glob, &start, TrackEntry);
332 if (RT_SUCCESS(rc))
333 rc = Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber);
334 glob->track_id_pos = RTFileTell(glob->file);
335 ebml_SerializeUnsigned32(glob, TrackUID, trackID);
336 Ebml_SerializeUnsigned(glob, TrackType, 1); //video is always 1
337 Ebml_SerializeString(glob, CodecID, "V_VP8");
338 {
339 uint64_t videoStart;
340 if (RT_SUCCESS(rc))
341 rc = ebml_StartSubElement(glob, &videoStart, Video);
342 uint32_t pixelWidth = cfg->g_w;
343 if (RT_SUCCESS(rc))
344 rc = Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
345 uint32_t pixelHeight = cfg->g_h;
346 if (RT_SUCCESS(rc))
347 rc = Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
348 double frameRate = (double)fps->num / (double)fps->den;
349 if (RT_SUCCESS(rc))
350 rc = Ebml_SerializeFloat(glob, FrameRate, frameRate);
351 if (RT_SUCCESS(rc))
352 rc = ebml_EndSubElement(glob, videoStart);
353 }
354 if (RT_SUCCESS(rc))
355 rc = ebml_EndSubElement(glob, start); //Track Entry
356 }
357 if (RT_SUCCESS(rc))
358 rc = ebml_EndSubElement(glob, trackStart);
359 }
360 // segment element is open
361 }
362 return rc;
363}
364
365int Ebml_WriteWebMBlock(EbmlGlobal *glob,
366 const vpx_codec_enc_cfg_t *cfg,
367 const vpx_codec_cx_pkt_t *pkt)
368{
369 uint16_t block_timecode = 0;
370 int64_t pts_ms;
371 int start_cluster = 0;
372 int rc = VINF_SUCCESS;
373
374 /* Calculate the PTS of this frame in milliseconds */
375 pts_ms = pkt->data.frame.pts * 1000
376 * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den;
377 if (pts_ms <= glob->last_pts_ms)
378 pts_ms = glob->last_pts_ms + 1;
379 glob->last_pts_ms = pts_ms;
380
381 /* Calculate the relative time of this block */
382 if (pts_ms - glob->cluster_timecode > 65536)
383 start_cluster = 1;
384 else
385 block_timecode = (uint16_t)(pts_ms - glob->cluster_timecode);
386
387 int fKeyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
388 if (start_cluster || fKeyframe)
389 {
390 if (glob->cluster_open)
391 rc = ebml_EndSubElement(glob, glob->startCluster);
392
393 /* Open the new cluster */
394 block_timecode = 0;
395 glob->cluster_open = 1;
396 glob->cluster_timecode = (uint32_t)pts_ms;
397 glob->cluster_pos = RTFileTell(glob->file);
398 if (RT_SUCCESS(rc))
399 rc = ebml_StartSubElement(glob, &glob->startCluster, Cluster); //cluster
400 if (RT_SUCCESS(rc))
401 rc = Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode);
402
403 /* Save a cue point if this is a keyframe. */
404 if (fKeyframe)
405 {
406 struct cue_entry *cue;
407
408 glob->cue_list = (cue_entry*)RTMemRealloc(glob->cue_list, (glob->cues+1) * sizeof(cue_entry));
409 cue = &glob->cue_list[glob->cues];
410 cue->time = glob->cluster_timecode;
411 cue->loc = glob->cluster_pos;
412 glob->cues++;
413 }
414 }
415
416 /* Write the Simple Block */
417 if (RT_SUCCESS(rc))
418 rc = ebml_WriteID(glob, SimpleBlock);
419
420 uint32_t block_length = pkt->data.frame.sz + 4;
421 block_length |= 0x10000000;
422 if (RT_SUCCESS(rc))
423 rc = ebml_WriteU32(glob, RT_H2BE_U32(block_length));
424
425 uint8_t track_number = 0x80 | 1;
426 if (RT_SUCCESS(rc))
427 rc = ebml_WriteU8(glob, track_number);
428
429 if (RT_SUCCESS(rc))
430 rc = ebml_WriteU16(glob, RT_H2BE_U16(block_timecode));
431
432 uint8_t flags = 0;
433 if (fKeyframe)
434 flags |= 0x80;
435 if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
436 flags |= 0x08;
437 if (RT_SUCCESS(rc))
438 rc = ebml_WriteU8(glob, flags);
439
440 if (RT_SUCCESS(rc))
441 rc = ebml_Write(glob, pkt->data.frame.buf, pkt->data.frame.sz);
442
443 return rc;
444}
445
446int Ebml_WriteWebMFileFooter(EbmlGlobal *glob, long hash)
447{
448 int rc = VINF_SUCCESS;
449 if (glob->cluster_open)
450 rc = ebml_EndSubElement(glob, glob->startCluster);
451
452 {
453 uint64_t start;
454
455 glob->cue_pos = RTFileTell(glob->file);
456 if (RT_SUCCESS(rc))
457 rc = ebml_StartSubElement(glob, &start, Cues);
458 for (unsigned i = 0; i < glob->cues; i++)
459 {
460 struct cue_entry *cue = &glob->cue_list[i];
461 uint64_t startSub;
462
463 if (RT_SUCCESS(rc))
464 rc = ebml_StartSubElement(glob, &startSub, CuePoint);
465 {
466 uint64_t startSubsub;
467
468 if (RT_SUCCESS(rc))
469 rc = Ebml_SerializeUnsigned(glob, CueTime, cue->time);
470 if (RT_SUCCESS(rc))
471 rc = ebml_StartSubElement(glob, &startSubsub, CueTrackPositions);
472 if (RT_SUCCESS(rc))
473 rc = Ebml_SerializeUnsigned(glob, CueTrack, 1);
474 if (RT_SUCCESS(rc))
475 rc = Ebml_SerializeUnsigned64(glob, CueClusterPosition,
476 cue->loc - glob->position_reference);
477 //Ebml_SerializeUnsigned(glob, CueBlockNumber, cue->blockNumber);
478 if (RT_SUCCESS(rc))
479 rc = ebml_EndSubElement(glob, startSubsub);
480 }
481 if (RT_SUCCESS(rc))
482 rc = ebml_EndSubElement(glob, startSub);
483 }
484 if (RT_SUCCESS(rc))
485 rc = ebml_EndSubElement(glob, start);
486 }
487
488 if (RT_SUCCESS(rc))
489 rc = ebml_EndSubElement(glob, glob->startSegment);
490
491 /* Patch up the seek info block */
492 if (RT_SUCCESS(rc))
493 rc = Ebml_WriteWebMSeekInfo(glob);
494
495 /* Patch up the track id */
496 if (RT_SUCCESS(rc))
497 rc = RTFileSeek(glob->file, glob->track_id_pos, RTFILE_SEEK_BEGIN, NULL);
498 if (RT_SUCCESS(rc))
499 rc = ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash);
500
501 if (RT_SUCCESS(rc))
502 rc = RTFileSeek(glob->file, 0, RTFILE_SEEK_END, NULL);
503 return rc;
504}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use