- Timestamp:
- Jan 9, 2017 3:57:02 PM (8 years ago)
- Location:
- trunk/src/VBox/Main/src-client
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp
r65205 r65212 194 194 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 195 195 196 pThis->enmMode = AVRECMODE_AUDIO; 196 pThis->enmMode = AVRECMODE_AUDIO; /** @todo Fix mode! */ 197 197 198 198 int rc; … … 205 205 { 206 206 pThis->pEBML = new WebMWriter(); 207 pThis->pEBML->create("/tmp/test.webm", WebMWriter::Mode_Audio); /** @todo FIX! */ 207 rc = pThis->pEBML->create("/tmp/test.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, 208 WebMWriter::Mode_Audio, /** @todo Fix path! */ 209 WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None); 208 210 break; 209 211 } … … 211 213 case AVRECMODE_AUDIO_VIDEO: 212 214 { 215 rc = VERR_NOT_SUPPORTED; 213 216 break; 214 217 } -
trunk/src/VBox/Main/src-client/EbmlWriter.cpp
r65198 r65212 49 49 50 50 /** Creates EBML output file. */ 51 inline int create(const char *a_pszFilename )52 { 53 return RTFileOpen(&m_File, a_pszFilename, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);51 inline int create(const char *a_pszFilename, uint64_t fOpen) 52 { 53 return RTFileOpen(&m_File, a_pszFilename, fOpen); 54 54 } 55 55 … … 143 143 writeSize(sizeof(double)); 144 144 writeUnsignedInteger(*reinterpret_cast<uint64_t*>(&value)); 145 return *this; 146 } 147 148 /** Serializes binary data. */ 149 inline Ebml &serializeData(EbmlClassId classId, const void *pvData, size_t cbData) 150 { 151 writeClassId(classId); 152 writeSize(cbData); 153 write(pvData, cbData); 145 154 return *this; 146 155 } … … 227 236 }; 228 237 238 #ifdef VBOX_WITH_AUDIO_VIDEOREC 239 # pragma pack(push) 240 # pragma pack(1) 241 /** Opus codec private data. 242 * Taken from: https://wiki.xiph.org/MatroskaOpus */ 243 struct OpusPrivData 244 { 245 uint8_t au8Head[8] = { 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64 }; 246 uint8_t u8Version = 1; 247 uint8_t c8Channels; 248 uint16_t u16PreSkip = 0; 249 uint32_t u32SampleRate; 250 uint16_t u16Gain = 0; 251 uint8_t u8Mapping_family = 0; 252 }; 253 # pragma pack(pop) 254 #endif /* VBOX_WITH_AUDIO_VIDEOREC */ 255 229 256 /** Operation mode. */ 230 WebMWriter::Mode m_enmMode; 231 232 bool m_fDebug; 233 int64_t m_iLastPtsMs; 234 int64_t m_iInitialPtsMs; 235 vpx_rational_t m_Framerate; 236 237 uint64_t m_uPositionReference; 238 uint64_t m_uSeekInfoPos; 239 uint64_t m_uSegmentInfoPos; 240 uint64_t m_uTrackPos; 241 uint64_t m_uCuePos; 242 uint64_t m_uClusterPos; 243 244 uint64_t m_uTrackIdPos; 245 246 uint64_t m_uStartSegment; 247 248 uint32_t m_uClusterTimecode; 249 bool m_bClusterOpen; 250 251 std::list<CueEntry> m_CueList; 252 253 Ebml m_Ebml; 257 WebMWriter::Mode m_enmMode; 258 /** Audio codec to use. */ 259 WebMWriter::AudioCodec m_enmAudioCodec; 260 /** Video codec to use. */ 261 WebMWriter::VideoCodec m_enmVideoCodec; 262 263 bool m_fDebug; 264 265 /** Timestamp of initial PTS (Presentation Time Stamp). */ 266 int64_t m_tsInitialPtsMs; 267 /** Timestamp of last written PTS (Presentation Time Stamp). */ 268 int64_t m_tsLastPtsMs; 269 270 vpx_rational_t m_Framerate; 271 272 /** Start offset (in bytes) of current segment. */ 273 uint64_t m_offSegCurStart; 274 275 /** Start offset (in bytes) of seeking info segment. */ 276 uint64_t m_offSegSeekInfoStart; 277 /** Offset (in bytes) for current seek info element. */ 278 uint64_t m_offSeekInfo; 279 280 /** Start offset (in bytes) of tracks segment. */ 281 uint64_t m_offSegTracksStart; 282 283 /** Absolute position of cue segment. */ 284 uint64_t m_uCuePos; 285 /** List of cue points. Needed for seeking table. */ 286 std::list<CueEntry> m_lstCue; 287 288 uint64_t m_uTrackIdPos; 289 290 /** Timestamp (in ms) when the current cluster has been opened. */ 291 uint32_t m_tsClusterOpenMs; 292 /** Whether we're currently in an opened cluster segment. */ 293 bool m_fClusterOpen; 294 /** Absolute position (in bytes) of current cluster within file. 295 * Needed for seeking info table. */ 296 uint64_t m_offSegClusterStart; 297 298 Ebml m_Ebml; 254 299 255 300 public: 256 301 257 302 WebMWriter_Impl() : 258 m_enmMode(WebMWriter::Mode_Unknown), 259 m_fDebug(false), 260 m_iLastPtsMs(-1), 261 m_iInitialPtsMs(-1), 262 m_Framerate(), 263 m_uPositionReference(0), 264 m_uSeekInfoPos(0), 265 m_uSegmentInfoPos(0), 266 m_uTrackPos(0), 267 m_uCuePos(0), 268 m_uClusterPos(0), 269 m_uTrackIdPos(0), 270 m_uStartSegment(0), 271 m_uClusterTimecode(0), 272 m_bClusterOpen(false) {} 303 m_enmMode(WebMWriter::Mode_Unknown) 304 , m_fDebug(false) 305 , m_tsInitialPtsMs(-1) 306 , m_tsLastPtsMs(-1) 307 , m_Framerate() 308 , m_offSegCurStart(0) 309 , m_offSegSeekInfoStart(0) 310 , m_offSeekInfo(0) 311 , m_offSegTracksStart(0) 312 , m_uCuePos(0) 313 , m_uTrackIdPos(0) 314 , m_tsClusterOpenMs(0) 315 , m_fClusterOpen(false) 316 , m_offSegClusterStart(0) {} 273 317 274 318 void writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const struct vpx_rational *a_pFps) … … 286 330 m_Ebml.subStart(Segment); 287 331 288 m_uPositionReference = RTFileTell(m_Ebml.getFile());289 332 m_Framerate = *a_pFps; 290 333 334 /* Save offset of current segment. */ 335 m_offSegCurStart = RTFileTell(m_Ebml.getFile()); 336 291 337 writeSeekInfo(); 292 338 293 m_uTrackPos = RTFileTell(m_Ebml.getFile()); 339 /* Save offset of upcoming tracks segment. */ 340 m_offSegTracksStart = RTFileTell(m_Ebml.getFile()); 294 341 295 342 m_Ebml.subStart(Tracks); … … 329 376 /** @todo Implement track's "Language" property? Currently this defaults to English ("eng"). */ 330 377 378 OpusPrivData opusPrivData; 379 331 380 m_Ebml.serializeUnsignedInteger(TrackUID, 1 /* UID */, 4) 332 381 .serializeUnsignedInteger(TrackType, 2 /* Audio */) 333 382 .serializeString(CodecID, "A_OPUS") 383 .serializeData(CodecPrivate, &opusPrivData, sizeof(opusPrivData)) 334 384 .subStart(Audio) 335 385 .serializeFloat(SamplingFrequency, 44100.0) … … 347 397 void writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt) 348 398 { 399 /* Calculate the PTS of this frame in milliseconds. */ 400 int64_t iPtsMs = a_pPkt->data.frame.pts * 1000 401 * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den; 402 403 if (iPtsMs <= m_tsLastPtsMs) 404 iPtsMs = m_tsLastPtsMs + 1; 405 406 m_tsLastPtsMs = iPtsMs; 407 408 if (m_tsInitialPtsMs < 0) 409 m_tsInitialPtsMs = m_tsLastPtsMs; 410 411 /* Calculate the relative time of this block. */ 349 412 uint16_t uBlockTimecode = 0; 350 int64_t iPtsMs; 351 bool bStartCluster = false; 352 353 /* Calculate the PTS of this frame in milliseconds. */ 354 iPtsMs = a_pPkt->data.frame.pts * 1000 355 * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den; 356 if (iPtsMs <= m_iLastPtsMs) 357 iPtsMs = m_iLastPtsMs + 1; 358 m_iLastPtsMs = iPtsMs; 359 360 if (m_iInitialPtsMs < 0) 361 m_iInitialPtsMs = m_iLastPtsMs; 362 363 /* Calculate the relative time of this block. */ 364 if (iPtsMs - m_uClusterTimecode > 65536) 365 bStartCluster = 1; 413 bool fClusterStart = false; 414 415 if (iPtsMs - m_tsClusterOpenMs > 65536) 416 fClusterStart = true; 366 417 else 367 uBlockTimecode = static_cast<uint16_t>(iPtsMs - m_uClusterTimecode); 368 369 int fKeyframe = (a_pPkt->data.frame.flags & VPX_FRAME_IS_KEY); 370 if (bStartCluster || fKeyframe) 418 uBlockTimecode = static_cast<uint16_t>(iPtsMs - m_tsClusterOpenMs); 419 420 bool fKeyframe = RT_BOOL(a_pPkt->data.frame.flags & VPX_FRAME_IS_KEY); 421 422 if ( fClusterStart 423 || fKeyframe) 371 424 { 372 if (m_ bClusterOpen)425 if (m_fClusterOpen) 373 426 m_Ebml.subEnd(Cluster); 374 427 375 428 /* Open a new cluster. */ 376 429 uBlockTimecode = 0; 377 m_ bClusterOpen = true;378 m_ uClusterTimecode= (uint32_t)iPtsMs;379 m_ uClusterPos= RTFileTell(m_Ebml.getFile());430 m_fClusterOpen = true; 431 m_tsClusterOpenMs = (uint32_t)iPtsMs; 432 m_offSegClusterStart = RTFileTell(m_Ebml.getFile()); 380 433 m_Ebml.subStart(Cluster) 381 .serializeUnsignedInteger(Timecode, m_ uClusterTimecode);434 .serializeUnsignedInteger(Timecode, m_tsClusterOpenMs); 382 435 383 436 /* Save a cue point if this is a keyframe. */ 384 437 if (fKeyframe) 385 438 { 386 CueEntry cue(m_ uClusterTimecode, m_uClusterPos);387 m_ CueList.push_back(cue);439 CueEntry cue(m_tsClusterOpenMs, m_offSegClusterStart); 440 m_lstCue.push_back(cue); 388 441 } 389 442 } … … 400 453 void writeFooter(uint32_t a_u64Hash) 401 454 { 402 if (m_ bClusterOpen)455 if (m_fClusterOpen) 403 456 m_Ebml.subEnd(Cluster); 404 457 405 458 m_uCuePos = RTFileTell(m_Ebml.getFile()); 406 459 m_Ebml.subStart(Cues); 407 for (std::list<CueEntry>::iterator it = m_ CueList.begin(); it != m_CueList.end(); ++it)460 for (std::list<CueEntry>::iterator it = m_lstCue.begin(); it != m_lstCue.end(); ++it) 408 461 { 409 462 m_Ebml.subStart(CuePoint) … … 411 464 .subStart(CueTrackPositions) 412 465 .serializeUnsignedInteger(CueTrack, 1) 413 .serializeUnsignedInteger(CueClusterPosition, it->loc - m_ uPositionReference, 8)466 .serializeUnsignedInteger(CueClusterPosition, it->loc - m_offSegCurStart, 8) 414 467 .subEnd(CueTrackPositions) 415 468 .subEnd(CuePoint); … … 434 487 private: 435 488 436 void writeSeekInfo( )489 void writeSeekInfo(void) 437 490 { 438 491 uint64_t uPos = RTFileTell(m_Ebml.getFile()); 439 if (m_ uSeekInfoPos)440 RTFileSeek(m_Ebml.getFile(), m_ uSeekInfoPos, RTFILE_SEEK_BEGIN, NULL);492 if (m_offSegSeekInfoStart) 493 RTFileSeek(m_Ebml.getFile(), m_offSegSeekInfoStart, RTFILE_SEEK_BEGIN, NULL); 441 494 else 442 m_ uSeekInfoPos= uPos;495 m_offSegSeekInfoStart = uPos; 443 496 444 497 m_Ebml.subStart(SeekHead) … … 446 499 .subStart(Seek) 447 500 .serializeUnsignedInteger(SeekID, Tracks) 448 .serializeUnsignedInteger(SeekPosition, m_ uTrackPos - m_uPositionReference, 8)501 .serializeUnsignedInteger(SeekPosition, m_offSegTracksStart - m_offSegCurStart, 8) 449 502 .subEnd(Seek) 450 503 451 504 .subStart(Seek) 452 505 .serializeUnsignedInteger(SeekID, Cues) 453 .serializeUnsignedInteger(SeekPosition, m_uCuePos - m_ uPositionReference, 8)506 .serializeUnsignedInteger(SeekPosition, m_uCuePos - m_offSegCurStart, 8) 454 507 .subEnd(Seek) 455 508 456 509 .subStart(Seek) 457 510 .serializeUnsignedInteger(SeekID, Info) 458 .serializeUnsignedInteger(SeekPosition, m_ uSegmentInfoPos - m_uPositionReference, 8)511 .serializeUnsignedInteger(SeekPosition, m_offSeekInfo - m_offSegCurStart, 8) 459 512 .subEnd(Seek) 460 513 461 514 .subEnd(SeekHead); 462 515 463 516 int64_t iFrameTime = (int64_t)1000 * m_Framerate.den / m_Framerate.num; 464 m_ uSegmentInfoPos= RTFileTell(m_Ebml.getFile());517 m_offSeekInfo = RTFileTell(m_Ebml.getFile()); 465 518 466 519 char szVersion[64]; … … 470 523 m_Ebml.subStart(Info) 471 524 .serializeUnsignedInteger(TimecodeScale, 1000000) 472 .serializeFloat(Segment_Duration, m_ iLastPtsMs + iFrameTime - m_iInitialPtsMs)525 .serializeFloat(Segment_Duration, m_tsLastPtsMs + iFrameTime - m_tsInitialPtsMs) 473 526 .serializeString(MuxingApp, szVersion) 474 527 .serializeString(WritingApp, szVersion) … … 477 530 }; 478 531 479 WebMWriter::WebMWriter() : m_Impl(new WebMWriter_Impl()) {} 480 481 WebMWriter::~WebMWriter() 482 { 483 delete m_Impl; 484 } 485 486 int WebMWriter::create(const char *a_pszFilename, WebMWriter::Mode a_enmMode) 487 { 488 m_Impl->m_enmMode = a_enmMode; 489 490 return m_Impl->m_Ebml.create(a_pszFilename); 491 } 492 493 void WebMWriter::close() 494 { 495 m_Impl->m_Ebml.close(); 532 WebMWriter::WebMWriter(void) : m_pImpl(new WebMWriter_Impl()) {} 533 534 WebMWriter::~WebMWriter(void) 535 { 536 if (m_pImpl) 537 delete m_pImpl; 538 } 539 540 int WebMWriter::create(const char *a_pszFilename, uint64_t a_fOpen, WebMWriter::Mode a_enmMode, 541 WebMWriter::AudioCodec a_enmAudioCodec, WebMWriter::VideoCodec a_enmVideoCodec) 542 { 543 m_pImpl->m_enmMode = a_enmMode; 544 m_pImpl->m_enmAudioCodec = a_enmAudioCodec; 545 m_pImpl->m_enmVideoCodec = a_enmVideoCodec; 546 547 return m_pImpl->m_Ebml.create(a_pszFilename, a_fOpen); 548 } 549 550 void WebMWriter::close(void) 551 { 552 m_pImpl->m_Ebml.close(); 496 553 } 497 554 … … 500 557 try 501 558 { 502 m_ Impl->writeHeader(a_pCfg, a_pFps);559 m_pImpl->writeHeader(a_pCfg, a_pFps); 503 560 } 504 561 catch(int rc) … … 513 570 try 514 571 { 515 m_ Impl->writeBlock(a_pCfg, a_pPkt);572 m_pImpl->writeBlock(a_pCfg, a_pPkt); 516 573 } 517 574 catch(int rc) … … 526 583 try 527 584 { 528 m_ Impl->writeFooter(a_u64Hash);585 m_pImpl->writeFooter(a_u64Hash); 529 586 } 530 587 catch(int rc) … … 537 594 uint64_t WebMWriter::getFileSize() 538 595 { 539 return m_ Impl->m_Ebml.getFileSize();596 return m_pImpl->m_Ebml.getFileSize(); 540 597 } 541 598 542 599 uint64_t WebMWriter::getAvailableSpace() 543 600 { 544 return m_ Impl->m_Ebml.getAvailableSpace();545 } 546 601 return m_pImpl->m_Ebml.getAvailableSpace(); 602 } 603 -
trunk/src/VBox/Main/src-client/EbmlWriter.h
r65197 r65212 28 28 #endif 29 29 30 #include <iprt/file.h> 31 30 32 class WebMWriter_Impl; 31 33 … … 34 36 35 37 public: 38 39 /** 40 * Supported audio codecs. 41 */ 42 enum AudioCodec 43 { 44 /** No audio codec specified. */ 45 AudioCodec_Unknown = 0, 46 /** Opus. */ 47 AudioCodec_Opus = 1 48 }; 49 50 /** 51 * Supported video codecs. 52 */ 53 enum VideoCodec 54 { 55 /** No video codec specified. */ 56 VideoCodec_None = 0, 57 /** VP8. */ 58 VideoCodec_VP8 = 1 59 }; 60 61 struct BlockData 62 { 63 void *pvData; 64 size_t cbData; 65 }; 36 66 37 67 /** … … 44 74 /** Only writes audio. */ 45 75 Mode_Audio = 1, 46 /** Only Writes video. */76 /** Only writes video. */ 47 77 Mode_Video = 2, 48 78 /** Writes audio and video. */ … … 59 89 * 60 90 * @param a_pszFilename Name of the file to create. 91 * @param a_fOpen File open mode of type RTFILE_O_. 61 92 * @param a_enmMode Operation mode. 93 * @param a_enmAudioCodec Audio codec to use. 94 * @param a_enmVideoCodec Video codec to use. 62 95 * 63 96 * @returns VBox status code. */ 64 int create(const char *a_pszFilename, WebMWriter::Mode a_enmMode); 97 int create(const char *a_pszFilename, uint64_t a_fOpen, WebMWriter::Mode a_enmMode, 98 WebMWriter::AudioCodec a_enmAudioCodec, WebMWriter::VideoCodec a_enmVideoCodec); 65 99 66 /* Closes output file. */100 /** Closes output file. */ 67 101 void close(); 68 102 … … 116 150 /** WebMWriter implementation. 117 151 * To isolate some include files. */ 118 WebMWriter_Impl *m_ Impl;152 WebMWriter_Impl *m_pImpl; 119 153 120 154 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(WebMWriter); -
trunk/src/VBox/Main/src-client/VideoRec.cpp
r65197 r65212 647 647 * other important file, causing unintentional data loss. */ 648 648 649 int rc = pStream->pEBML->create(pszFile, WebMWriter::Mode_AudioVideo); /** @todo Make mode configurable. */ 649 /** @todo Make mode configurable. */ 650 #ifdef VBOX_WITH_AUDIO_VIDEOREC 651 WebMWriter::Mode enmMode = WebMWriter::Mode_AudioVideo; 652 #else 653 WebMWriter::Mode enmMode = WebMWriter::Mode_Video; 654 #endif 655 656 int rc = pStream->pEBML->create(pszFile, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, enmMode, 657 WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_VP8); 650 658 if (RT_FAILURE(rc)) 651 659 {
Note:
See TracChangeset
for help on using the changeset viewer.