Changeset 65353 in vbox for trunk/src/VBox/Main
- Timestamp:
- Jan 17, 2017 4:58:01 PM (8 years ago)
- Location:
- trunk/src/VBox/Main/src-client
- Files:
-
- 1 added
- 1 deleted
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp
r65330 r65353 97 97 /** (Audio) frame buffer. */ 98 98 PRTCIRCBUF pCircBuf; 99 uint8_t *pvFrameBuf;100 size_t cbFrameBufSize;101 size_t cbFrameBufUsed;102 size_t offFrameBufRead;103 size_t offFrameBufWrite;99 /** Pointer to WebM container to write recorded audio data to. 100 * See the AVRECMODE enumeration for more information. */ 101 WebMWriter *pWebM; 102 /** Assigned track number from WebM container. */ 103 uint8_t uTrack; 104 104 } AVRECSTREAMOUT, *PAVRECSTREAMOUT; 105 105 … … 119 119 /** Recording mode. */ 120 120 AVRECMODE enmMode; 121 /** Pointer to WebM container to write recorded audio data to.122 * See the AVRECMODE enumeration for more information. */123 WebMWriter *pEBML;124 /** Track number for audio data. */125 uint8_t uTrack;126 121 } DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC; 127 122 … … 144 139 145 140 #ifdef VBOX_WITH_LIBOPUS 146 RTCircBufCreate(&pStreamOut->pCircBuf, _4K); 141 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 142 143 if (pThis->enmMode == AVRECMODE_AUDIO) 144 { 145 pStreamOut->pWebM = new WebMWriter(); 146 rc = pStreamOut->pWebM->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */ 147 WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None); 148 if (RT_SUCCESS(rc)) 149 rc = pStreamOut->pWebM->AddAudioTrack(44100, 2, 16, &pStreamOut->uTrack); 150 } 151 152 if (RT_FAILURE(rc)) 153 return rc; 154 155 rc = RTCircBufCreate(&pStreamOut->pCircBuf, _4K); 156 if (RT_FAILURE(rc)) 157 return rc; 147 158 148 159 rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props); 149 160 if (RT_SUCCESS(rc)) 150 161 { 151 size_t cbFrameBuf = sizeof(uint8_t) * _4K; /** @todo Make this configurable */ 152 153 pStreamOut->pvFrameBuf = (uint8_t *)RTMemAlloc(cbFrameBuf); 154 if (pStreamOut->pvFrameBuf) 155 { 156 pStreamOut->cbFrameBufSize = cbFrameBuf; 157 pStreamOut->cbFrameBufUsed = 0; 158 pStreamOut->offFrameBufRead = 0; 159 pStreamOut->offFrameBufWrite = 0; 160 161 OpusEncoder *pEnc = NULL; 162 163 int orc; 164 pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc); 165 if (orc != OPUS_OK) 166 { 167 LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc))); 168 return VERR_AUDIO_BACKEND_INIT_FAILED; 169 } 170 171 AssertPtr(pEnc); 172 173 #if 0 174 orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(DrvAudioHlpCalcBitrate())); 175 if (orc != OPUS_OK) 176 { 177 LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc))); 178 rc = VERR_AUDIO_BACKEND_INIT_FAILED; 179 } 180 else 181 { 182 #endif 183 pStreamOut->Codec.Opus.pEnc = pEnc; 184 185 if (pCfgAcq) 186 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */ 187 // } 162 OpusEncoder *pEnc = NULL; 163 164 int orc; 165 pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc); 166 if (orc != OPUS_OK) 167 { 168 LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc))); 169 return VERR_AUDIO_BACKEND_INIT_FAILED; 188 170 } 171 172 AssertPtr(pEnc); 173 174 #if 0 175 orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(DrvAudioHlpCalcBitrate())); 176 if (orc != OPUS_OK) 177 { 178 LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc))); 179 rc = VERR_AUDIO_BACKEND_INIT_FAILED; 180 } 181 else 182 { 183 #endif 184 pStreamOut->Codec.Opus.pEnc = pEnc; 185 186 if (pCfgAcq) 187 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */ 189 188 } 190 189 #else … … 209 208 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd)); 210 209 211 AudioMixBufReset(&pStream->MixBuf); 210 switch (enmStreamCmd) 211 { 212 case PDMAUDIOSTREAMCMD_ENABLE: 213 case PDMAUDIOSTREAMCMD_RESUME: 214 break; 215 216 case PDMAUDIOSTREAMCMD_DISABLE: 217 { 218 AudioMixBufReset(&pStream->MixBuf); 219 break; 220 } 221 222 case PDMAUDIOSTREAMCMD_PAUSE: 223 break; 224 225 default: 226 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd)); 227 break; 228 } 212 229 213 230 return VINF_SUCCESS; … … 234 251 switch (pThis->enmMode) 235 252 { 253 /* In audio-only mode we're creating our own WebM writer instance, 254 * as we don't have to synchronize with any external source, such as video recording data.*/ 236 255 case AVRECMODE_AUDIO: 237 256 { 238 pThis->pEBML = new WebMWriter(); 239 rc = pThis->pEBML->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */ 240 WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None); 241 if (RT_SUCCESS(rc)) 242 rc = pThis->pEBML->AddAudioTrack(44100, 2, 16, &pThis->uTrack); 257 rc = VINF_SUCCESS; 243 258 break; 244 259 } … … 333 348 #ifdef VBOX_WITH_LIBOPUS 334 349 335 /** @todo For now we ASSUME 25 FPS. */ 336 uint16_t cbFrameSize = (/*pStreamOut->Props.uHz*/ 48000 / 25 /* FPS */); 350 uint16_t cbFrameSize = 960; /** @todo 20ms worth of audio data. */ 337 351 338 352 PRTCIRCBUF pCircBuf = pStreamOut->pCircBuf; 339 353 340 while ( pStreamOut->cbFrameBufUsed< cbFrameSize)354 while (RTCircBufUsed(pCircBuf) < cbFrameSize) 341 355 { 342 356 void *pvBuf; … … 400 414 size_t cbDst = sizeof(abDst); 401 415 402 /* Call the encoder to encode o ur input samples. */403 opus_encoder_ctl(pStreamOut->Codec.Opus.pEnc, OPUS_SET_BITRATE(196000)); /** @todo */416 /* Call the encoder to encode one frame per iteration. */ 417 opus_encoder_ctl(pStreamOut->Codec.Opus.pEnc, OPUS_SET_BITRATE(196000)); /** @todo Only needed for VBR encoding. */ 404 418 opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc, 405 419 (opus_int16 *)abSrc, cbSrc, abDst, cbDst); 406 420 if (cbWritten > 0) 407 421 { 422 cbDst = cbWritten; 423 Assert(cbDst <= sizeof(abDst)); 424 408 425 /* Call the WebM writer to actually write the encoded audio data. */ 409 426 WebMWriter::BlockData_Opus blockData = { abDst, cbDst }; 410 rc = p This->pEBML->WriteBlock(pThis->uTrack, &blockData, sizeof(blockData));427 rc = pStreamOut->pWebM->WriteBlock(pStreamOut->uTrack, &blockData, sizeof(blockData)); 411 428 AssertRC(rc); 412 429 } … … 449 466 } 450 467 451 if (pStreamOut->pvFrameBuf)452 {453 Assert(pStreamOut->cbFrameBufSize);454 RTMemFree(pStreamOut->pvFrameBuf);455 pStreamOut->pvFrameBuf = NULL;456 }457 458 468 #ifdef VBOX_WITH_LIBOPUS 459 469 if (pStreamOut->Codec.Opus.pEnc) … … 463 473 } 464 474 #endif 475 476 switch (pThis->enmMode) 477 { 478 case AVRECMODE_AUDIO: 479 { 480 if (pStreamOut->pWebM) 481 pStreamOut->pWebM->Close(); 482 break; 483 } 484 485 case AVRECMODE_AUDIO_VIDEO: 486 default: 487 break; 488 } 465 489 466 490 return VINF_SUCCESS; … … 494 518 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 495 519 496 int rc;497 498 520 switch (pThis->enmMode) 499 521 { 500 522 case AVRECMODE_AUDIO: 501 { 502 if (pThis->pEBML) 503 { 504 pThis->pEBML->Close(); 505 506 delete pThis->pEBML; 507 pThis->pEBML = NULL; 508 } 523 case AVRECMODE_AUDIO_VIDEO: 524 default: 509 525 break; 510 } 511 512 case AVRECMODE_AUDIO_VIDEO: 513 { 514 break; 515 } 516 517 default: 518 rc = VERR_NOT_SUPPORTED; 519 break; 520 } 521 522 LogFlowFuncLeaveRC(rc); 526 } 523 527 } 524 528 -
trunk/src/VBox/Main/src-client/EbmlWriter.cpp
r65340 r65353 32 32 33 33 #include "EbmlWriter.h" 34 #include "Ebml IDs.h"34 #include "EbmlMkvIDs.h" 35 35 #include "Logging.h" 36 36 … … 143 143 RTFileSeek(m_File, m_Elements.top().offset, RTFILE_SEEK_BEGIN, NULL); 144 144 145 /* make sure that size will be serialized as uint64*/145 /* Make sure that size will be serialized as uint64_t. */ 146 146 writeUnsignedInteger(uSize | UINT64_C(0x0100000000000000)); 147 147 RTFileSeek(m_File, uPos, RTFILE_SEEK_BEGIN, NULL); … … 292 292 293 293 /** 294 * Structure for keeping a track entry.294 * Structure for keeping a WebM track entry. 295 295 */ 296 296 struct WebMTrack … … 299 299 : enmType(a_enmType) 300 300 , uTrack(a_uTrack) 301 , offID(a_offID) 301 , offUUID(a_offID) 302 , cTotalClusters(0) 303 , cTotalBlocks(0) 302 304 { 303 305 uUUID = RTRandU32(); … … 315 317 /** Sample rate the codec is using. */ 316 318 uint16_t uHzCodec; 317 /** Frame size (in bytes), based on the codec sample rate. */ 319 /** Frame size (in bytes), based on the codec sample rate. 320 * Note: For now this does *not* change dynamically, in other words, 321 * we do CBR (and not VBR) here! */ 318 322 size_t cbFrameSize; 319 323 } Audio; … … 324 328 * Needed in case this track gets mux'ed with tracks from other files. Not really unique though. */ 325 329 uint32_t uUUID; 326 /** Absolute offset in file of track ID.330 /** Absolute offset in file of track UUID. 327 331 * Needed to write the hash sum within the footer. */ 328 uint64_t offID; 332 uint64_t offUUID; 333 /** Total number of clusters. */ 334 uint64_t cTotalClusters; 335 /** Total number of blocks. */ 336 uint64_t cTotalBlocks; 329 337 }; 338 339 /** 340 * Structure for keeping a WebM cluster entry. 341 */ 342 struct WebMCluster 343 { 344 WebMCluster(void) 345 : uID(0) 346 , offCluster(0) 347 , fOpen(false) 348 , tsStart(0) 349 , tsEnd(0) { } 350 351 uint64_t uID; 352 /** Absolute offset (in bytes) of current cluster. 353 * Needed for seeking info table. */ 354 uint64_t offCluster; 355 bool fOpen; 356 uint64_t tsStart; 357 uint64_t tsEnd; 358 }; 359 360 /** 361 * Structure for keeping a WebM segment entry. 362 * 363 * Current we're only using one segment. 364 */ 365 struct WebMSegment 366 { 367 WebMSegment(void) 368 : offStart(0) 369 , offInfo(0) 370 , offSeekInfo(0) 371 , offTracks(0) 372 , offCues(0) { } 373 374 /** Absolute offset (in bytes) of CurSeg. */ 375 uint64_t offStart; 376 /** Absolute offset (in bytes) of general info. */ 377 uint64_t offInfo; 378 /** Absolute offset (in bytes) of seeking info. */ 379 uint64_t offSeekInfo; 380 /** Absolute offset (in bytes) of tracks. */ 381 uint64_t offTracks; 382 /** Absolute offset (in bytes) of cues table. */ 383 uint64_t offCues; 384 /** List of cue points. Needed for seeking table. */ 385 std::list<WebMCueEntry> lstCues; 386 387 /** Map of tracks. 388 * The key marks the track number (*not* the UUID!). */ 389 std::map <uint8_t, WebMTrack *> mapTracks; 390 391 /** Current cluster which is being handled. 392 * 393 * Note that we don't need (and shouldn't need, as this can be a *lot* of data!) a 394 * list of all clusters. */ 395 WebMCluster CurCluster; 396 397 } CurSeg; 330 398 331 399 #ifdef VBOX_WITH_LIBOPUS … … 369 437 int64_t m_tsLastPtsMs; 370 438 371 /** Start offset (in bytes) of current segment. */ 372 uint64_t m_offSegCurStart; 373 374 /** Start offset (in bytes) of seeking info segment. */ 375 uint64_t m_offSegSeekInfoStart; 376 /** Start offset (in bytes) of general info segment. */ 377 uint64_t m_offSegInfoStart; 378 379 /** Start offset (in bytes) of tracks segment. */ 380 uint64_t m_offSegTracksStart; 381 382 /** Absolute position of cue segment. */ 383 uint64_t m_offSegCuesStart; 384 /** List of cue points. Needed for seeking table. */ 385 std::list<WebMCueEntry> m_lstCue; 386 387 /** Map of tracks. 388 * The key marks the track number (*not* the UID!). */ 389 std::map <uint8_t, WebMTrack *> m_mapTracks; 390 /** Whether we're currently in an opened tracks segment. */ 391 bool m_fTracksOpen; 392 393 /** Timestamp (in ms) when the current cluster has been opened. */ 394 uint32_t m_tsClusterOpenMs; 395 /** Whether we're currently in an opened cluster segment. */ 396 bool m_fClusterOpen; 397 /** Absolute position (in bytes) of current cluster within file. 398 * Needed for seeking info table. */ 399 uint64_t m_offSegClusterStart; 439 /** Whether we're currently in the tracks section. */ 440 bool m_fInTracksSection; 441 442 /** Size of timecodes in bytes. */ 443 size_t m_cbTimecode; 444 /** Maximum value a timecode can have. */ 445 uint32_t m_uTimecodeMax; 446 /** The timecode scale factor. */ 447 uint64_t m_uTimecodeScaleNs; 400 448 401 449 Ebml m_Ebml; … … 411 459 , m_tsInitialPtsMs(-1) 412 460 , m_tsLastPtsMs(-1) 413 , m_offSegCurStart(0) 414 , m_offSegSeekInfoStart(0) 415 , m_offSegInfoStart(0) 416 , m_offSegTracksStart(0) 417 , m_offSegCuesStart(0) 418 , m_fTracksOpen(false) 419 , m_tsClusterOpenMs(0) 420 , m_fClusterOpen(false) 421 , m_offSegClusterStart(0) {} 461 , m_fInTracksSection(false) 462 { 463 /* Size (in bytes) of time code to write. We use 2 bytes (16 bit) by default. */ 464 m_cbTimecode = 2; 465 m_uTimecodeMax = UINT16_MAX; 466 467 /* This is the default for WebM -- all timecodes in the segments are expressed in ms. 468 * This allows every cluster to have blocks with positive values up to 32.767 seconds. */ 469 m_uTimecodeScaleNs = 1000000; 470 } 422 471 423 472 virtual ~WebMWriter_Impl() … … 429 478 { 430 479 #ifdef VBOX_WITH_LIBOPUS 431 m_Ebml.subStart( TrackEntry);432 m_Ebml.serializeUnsignedInteger( TrackNumber, (uint8_t)m_mapTracks.size());480 m_Ebml.subStart(MkvElem_TrackEntry); 481 m_Ebml.serializeUnsignedInteger(MkvElem_TrackNumber, (uint8_t)CurSeg.mapTracks.size()); 433 482 /** @todo Implement track's "Language" property? Currently this defaults to English ("eng"). */ 434 483 435 uint8_t uTrack = (uint8_t) m_mapTracks.size();484 uint8_t uTrack = (uint8_t)CurSeg.mapTracks.size(); 436 485 437 486 WebMTrack *pTrack = new WebMTrack(WebMTrackType_Audio, uTrack, RTFileTell(m_Ebml.getFile())); 438 487 439 /* Clamp the codec rate value if it reaches a certain threshold. */488 /* Clamp the codec rate (Hz) value if it reaches a certain threshold. */ 440 489 if (uHz > 24000) pTrack->Audio.uHzCodec = 48000; 441 490 else if (uHz > 16000) pTrack->Audio.uHzCodec = 24000; … … 446 495 pTrack->Audio.uHzIn = uHz; 447 496 448 /** @todo 1920 bytes is 40ms worth of audio data. Make this configurable. */449 pTrack->Audio.cbFrameSize = 1920 / (48000/ pTrack->Audio.uHzCodec);497 /** @todo 960 bytes is 20ms worth of audio data. Make this configurable. */ 498 pTrack->Audio.cbFrameSize = 960 /* Bytes */ / (48000 /* Hz */ / pTrack->Audio.uHzCodec); 450 499 451 500 /** @todo Resolve codec type. */ 452 501 OpusPrivData opusPrivData(uHz, cChannels); 453 502 454 m_Ebml.serializeUnsignedInteger( TrackUID, pTrack->uUUID, 4)455 .serializeUnsignedInteger( TrackType, 2 /* Audio */)456 .serializeString( CodecID, "A_OPUS")457 .serializeData( CodecPrivate, &opusPrivData, sizeof(opusPrivData))458 .serializeUnsignedInteger( CodecDelay,0)459 .serializeUnsignedInteger( SeekPreRoll, 80000000)460 .subStart( Audio)461 .serializeFloat( SamplingFrequency, (float)pTrack->Audio.uHzCodec)462 .serializeUnsignedInteger( Channels, cChannels)463 .serializeUnsignedInteger( BitDepth, cBits)464 .subEnd( Audio)465 .subEnd( TrackEntry);466 467 m_mapTracks[uTrack] = pTrack;503 m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID, 4) 504 .serializeUnsignedInteger(MkvElem_TrackType, 2 /* Audio */) 505 .serializeString(MkvElem_CodecID, "A_OPUS") 506 .serializeData(MkvElem_CodecPrivate, &opusPrivData, sizeof(opusPrivData)) 507 .serializeUnsignedInteger(MkvElem_CodecDelay, 0) 508 .serializeUnsignedInteger(MkvElem_SeekPreRoll, 80000000) /* 80.000ms */ 509 .subStart(MkvElem_Audio) 510 .serializeFloat(MkvElem_SamplingFrequency, (float)pTrack->Audio.uHzCodec) 511 .serializeUnsignedInteger(MkvElem_Channels, cChannels) 512 .serializeUnsignedInteger(MkvElem_BitDepth, cBits) 513 .subEnd(MkvElem_Audio) 514 .subEnd(MkvElem_TrackEntry); 515 516 CurSeg.mapTracks[uTrack] = pTrack; 468 517 469 518 if (puTrack) … … 479 528 int AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS, uint8_t *puTrack) 480 529 { 481 m_Ebml.subStart( TrackEntry);482 m_Ebml.serializeUnsignedInteger( TrackNumber, (uint8_t)m_mapTracks.size());483 484 uint8_t uTrack = (uint8_t) m_mapTracks.size();530 m_Ebml.subStart(MkvElem_TrackEntry); 531 m_Ebml.serializeUnsignedInteger(MkvElem_TrackNumber, (uint8_t)CurSeg.mapTracks.size()); 532 533 uint8_t uTrack = (uint8_t)CurSeg.mapTracks.size(); 485 534 486 535 WebMTrack *pTrack = new WebMTrack(WebMTrackType_Video, uTrack, RTFileTell(m_Ebml.getFile())); 487 536 488 537 /** @todo Resolve codec type. */ 489 m_Ebml.serializeUnsignedInteger( TrackUID, pTrack->uUUID /* UID */, 4)490 .serializeUnsignedInteger( TrackType, 1 /* Video */)491 .serializeString( CodecID, "V_VP8")492 .subStart( Video)493 .serializeUnsignedInteger( PixelWidth, uWidth)494 .serializeUnsignedInteger( PixelHeight, uHeight)495 .serializeFloat( FrameRate, dbFPS)496 .subEnd( Video)497 .subEnd( TrackEntry);498 499 m_mapTracks[uTrack] = pTrack;538 m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID /* UID */, 4) 539 .serializeUnsignedInteger(MkvElem_TrackType, 1 /* Video */) 540 .serializeString(MkvElem_CodecID, "V_VP8") 541 .subStart(MkvElem_Video) 542 .serializeUnsignedInteger(MkvElem_PixelWidth, uWidth) 543 .serializeUnsignedInteger(MkvElem_PixelHeight, uHeight) 544 .serializeFloat(MkvElem_FrameRate, dbFPS) 545 .subEnd(MkvElem_Video) 546 .subEnd(MkvElem_TrackEntry); 547 548 CurSeg.mapTracks[uTrack] = pTrack; 500 549 501 550 if (puTrack) … … 509 558 LogFunc(("Header @ %RU64\n", RTFileTell(m_Ebml.getFile()))); 510 559 511 m_Ebml.subStart( EBML)512 .serializeUnsignedInteger( EBMLVersion, 1)513 .serializeUnsignedInteger( EBMLReadVersion, 1)514 .serializeUnsignedInteger( EBMLMaxIDLength, 4)515 .serializeUnsignedInteger( EBMLMaxSizeLength, 8)516 .serializeString( DocType, "webm")517 .serializeUnsignedInteger( DocTypeVersion, 2)518 .serializeUnsignedInteger( DocTypeReadVersion, 2)519 .subEnd( EBML);520 521 m_Ebml.subStart( Segment);560 m_Ebml.subStart(MkvElem_EBML) 561 .serializeUnsignedInteger(MkvElem_EBMLVersion, 1) 562 .serializeUnsignedInteger(MkvElem_EBMLReadVersion, 1) 563 .serializeUnsignedInteger(MkvElem_EBMLMaxIDLength, 4) 564 .serializeUnsignedInteger(MkvElem_EBMLMaxSizeLength, 8) 565 .serializeString(MkvElem_DocType, "webm") 566 .serializeUnsignedInteger(MkvElem_DocTypeVersion, 2) 567 .serializeUnsignedInteger(MkvElem_DocTypeReadVersion, 2) 568 .subEnd(MkvElem_EBML); 569 570 m_Ebml.subStart(MkvElem_Segment); 522 571 523 572 /* Save offset of current segment. */ 524 m_offSegCurStart = RTFileTell(m_Ebml.getFile());525 526 writeSe ekInfo();573 CurSeg.offStart = RTFileTell(m_Ebml.getFile()); 574 575 writeSegSeekInfo(); 527 576 528 577 /* Save offset of upcoming tracks segment. */ 529 m_offSegTracksStart= RTFileTell(m_Ebml.getFile());578 CurSeg.offTracks = RTFileTell(m_Ebml.getFile()); 530 579 531 580 /* The tracks segment starts right after this header. */ 532 m_Ebml.subStart( Tracks);533 m_f TracksOpen = true;581 m_Ebml.subStart(MkvElem_Tracks); 582 m_fInTracksSection = true; 534 583 535 584 return VINF_SUCCESS; 536 585 } 537 586 538 int writeSimpleBlockInternal(uint8_t uTrackID, uint16_t uTimecode, const void *pvData, size_t cbData, uint8_t fFlags) 539 { 540 LogFunc(("SimpleBlock @ %RU64 (T%RU8, TS=%RU16, %zu bytes)\n", RTFileTell(m_Ebml.getFile()), uTrackID, uTimecode, cbData)); 587 int writeSimpleBlockInternal(WebMTrack *a_pTrack, uint64_t a_uTimecode, 588 const void *a_pvData, size_t a_cbData, uint8_t a_fFlags) 589 { 590 LogFunc(("SimpleBlock @ %RU64 (T%RU8, TS=%RU64, %zu bytes)\n", 591 RTFileTell(m_Ebml.getFile()), a_pTrack->uTrack, a_uTimecode, a_cbData)); 592 593 /** @todo Mask out non-valid timecode bits, e.g. the upper 48 bits for a (default) 16-bit timecode. */ 594 Assert(a_uTimecode <= m_uTimecodeMax); 541 595 542 596 /* Write a "Simple Block". */ 543 m_Ebml.writeClassId( SimpleBlock);597 m_Ebml.writeClassId(MkvElem_SimpleBlock); 544 598 /* Block size. */ 545 m_Ebml.writeUnsignedInteger(0x10000000u | ( 1 /* Track number. */546 + 2 /* Timecode .*/547 + 1 /* Flags. */548 + cbData /* Actual frame data. */), 4);599 m_Ebml.writeUnsignedInteger(0x10000000u | ( 1 /* Track number size. */ 600 + m_cbTimecode /* Timecode size .*/ 601 + 1 /* Flags size. */ 602 + a_cbData /* Actual frame data size. */), 4); 549 603 /* Track number. */ 550 m_Ebml.writeSize( uTrackID);604 m_Ebml.writeSize(a_pTrack->uTrack); 551 605 /* Timecode (relative to cluster opening timecode). */ 552 m_Ebml.writeUnsignedInteger( uTimecode, 2);606 m_Ebml.writeUnsignedInteger(a_uTimecode, m_cbTimecode); 553 607 /* Flags. */ 554 m_Ebml.writeUnsignedInteger( fFlags, 1);608 m_Ebml.writeUnsignedInteger(a_fFlags, 1); 555 609 /* Frame data. */ 556 m_Ebml.write(pvData, cbData); 610 m_Ebml.write(a_pvData, a_cbData); 611 612 a_pTrack->cTotalBlocks++; 557 613 558 614 return VINF_SUCCESS; 559 615 } 560 616 561 int writeBlockVP8( constWebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)617 int writeBlockVP8(WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt) 562 618 { 563 619 RT_NOREF(a_pTrack); … … 579 635 bool fClusterStart = false; 580 636 581 if (tsPtsMs - m_tsClusterOpenMs > 65536) 637 /* Did we reach the maximum our timecode can hold? Use a new cluster then. */ 638 if (tsPtsMs - CurSeg.CurCluster.tsStart > m_uTimecodeMax) 582 639 fClusterStart = true; 583 640 else 584 641 { 585 /* Calculate the block's timecode, which is relative to the Clustertimecode. */586 tsBlockMs = static_cast<uint16_t>(tsPtsMs - m_tsClusterOpenMs);642 /* Calculate the block's timecode, which is relative to the current cluster's starting timecode. */ 643 tsBlockMs = static_cast<uint16_t>(tsPtsMs - CurSeg.CurCluster.tsStart); 587 644 } 588 645 … … 592 649 || fKeyframe) 593 650 { 594 if (m_fClusterOpen) /* Close current cluster first. */ 595 m_Ebml.subEnd(Cluster); 651 WebMCluster &Cluster = CurSeg.CurCluster; 652 653 a_pTrack->cTotalClusters++; 654 655 if (Cluster.fOpen) /* Close current cluster first. */ 656 { 657 m_Ebml.subEnd(MkvElem_Cluster); 658 Cluster.fOpen = false; 659 } 596 660 597 661 tsBlockMs = 0; 598 662 599 663 /* Open a new cluster. */ 600 m_fClusterOpen= true;601 m_tsClusterOpenMs = (uint32_t)tsPtsMs;602 m_offSegClusterStart= RTFileTell(m_Ebml.getFile());603 604 LogFunc(("Cluster @ %RU64\n", m_offSegClusterStart));605 606 m_Ebml.subStart( Cluster)607 .serializeUnsignedInteger( Timecode, m_tsClusterOpenMs);664 Cluster.fOpen = true; 665 Cluster.tsStart = tsPtsMs; 666 Cluster.offCluster = RTFileTell(m_Ebml.getFile()); 667 668 LogFunc(("Cluster @ %RU64\n", Cluster.offCluster)); 669 670 m_Ebml.subStart(MkvElem_Cluster) 671 .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tsStart); 608 672 609 673 /* Save a cue point if this is a keyframe. */ 610 674 if (fKeyframe) 611 675 { 612 WebMCueEntry cue( m_tsClusterOpenMs, m_offSegClusterStart);613 m_lstCue.push_back(cue);676 WebMCueEntry cue(Cluster.tsStart, Cluster.offCluster); 677 CurSeg.lstCues.push_back(cue); 614 678 } 615 679 } … … 621 685 fFlags |= 0x08; /* Invisible frame. */ 622 686 623 return writeSimpleBlockInternal(a_pTrack ->uTrack, tsBlockMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags);687 return writeSimpleBlockInternal(a_pTrack, tsBlockMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags); 624 688 } 625 689 626 690 #ifdef VBOX_WITH_LIBOPUS 627 691 /* Audio blocks that have same absolute timecode as video blocks SHOULD be written before the video blocks. */ 628 int writeBlockOpus( constWebMTrack *a_pTrack, const void *pvData, size_t cbData)692 int writeBlockOpus(WebMTrack *a_pTrack, const void *pvData, size_t cbData) 629 693 { 630 694 AssertPtrReturn(a_pTrack, VERR_INVALID_POINTER); 631 AssertReturn(a_pTrack->Audio.cbFrameSize == cbData, VERR_INVALID_PARAMETER); 632 633 static uint16_t s_uTimecode = 0; 634 635 if (!m_fClusterOpen) 636 { 637 m_Ebml.subStart(Cluster) 638 .serializeUnsignedInteger(Timecode, 0); 639 m_fClusterOpen = true; 695 AssertPtrReturn(pvData, VERR_INVALID_POINTER); 696 AssertReturn(cbData, VERR_INVALID_PARAMETER); 697 698 //static uint64_t s_uTimecode = 0; 699 /* For now this is constant while encoding (CBR), but might change to VBR later. */ 700 //s_uTimecode += a_pTrack->Audio.cbFrameSize * 48000 / a_pTrack->Audio.uHzCodec; 701 702 //static uint64_t s_uPts = 0; 703 704 int64_t tsPtsMs = 1000000000 * a_pTrack->cTotalBlocks / 48000; 705 706 m_tsLastPtsMs = tsPtsMs; 707 708 if (m_tsInitialPtsMs < 0) 709 m_tsInitialPtsMs = m_tsLastPtsMs; 710 711 WebMCluster &Cluster = CurSeg.CurCluster; 712 713 /* Calculate the relative time of this block. */ 714 uint16_t tsBlockMs = 0; 715 bool fClusterStart = false; 716 717 if (a_pTrack->cTotalBlocks == 0) 718 fClusterStart = true; 719 720 /* Did we reach the maximum our timecode can hold? Use a new cluster then. */ 721 if (tsPtsMs - Cluster.tsStart > m_uTimecodeMax) 722 fClusterStart = true; 723 else 724 { 725 /* Calculate the block's timecode, which is relative to the Cluster timecode. */ 726 tsBlockMs = static_cast<uint16_t>(tsPtsMs - Cluster.tsStart); 640 727 } 641 728 642 uint64_t ts = (s_uTimecode * 1000 * 1000 * 1000) / 48000; 643 644 LogFunc(("ts=%RU64 (timecode %RU16)\n", ts, s_uTimecode)); 645 646 s_uTimecode += a_pTrack->Audio.cbFrameSize; 647 648 return writeSimpleBlockInternal(a_pTrack->uTrack, ts, pvData, cbData, 0 /* Flags */); 729 if (fClusterStart) 730 { 731 a_pTrack->cTotalClusters++; 732 733 if (Cluster.fOpen) /* Close current cluster first. */ 734 { 735 m_Ebml.subEnd(MkvElem_Cluster); 736 Cluster.fOpen = false; 737 } 738 739 tsBlockMs = 0; 740 741 Cluster.fOpen = true; 742 Cluster.tsStart = tsPtsMs; 743 Cluster.offCluster = RTFileTell(m_Ebml.getFile()); 744 745 LogFunc(("Cluster @ %RU64\n", Cluster.offCluster)); 746 747 m_Ebml.subStart(MkvElem_Cluster) 748 .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tsStart); 749 } 750 751 LogFunc(("Cluster %RU64 - Block %RU64: -> %RU64ms\n", 752 a_pTrack->cTotalClusters, a_pTrack->cTotalBlocks, a_pTrack->cTotalBlocks * 20)); 753 754 return writeSimpleBlockInternal(a_pTrack, tsBlockMs, pvData, cbData, 0 /* Flags */); 649 755 } 650 756 #endif … … 654 760 RT_NOREF(cbData); /* Only needed for assertions for now. */ 655 761 656 WebMTracks:: const_iterator itTrack = m_mapTracks.find(uTrack);657 if (itTrack == m_mapTracks.end())762 WebMTracks::iterator itTrack = CurSeg.mapTracks.find(uTrack); 763 if (itTrack == CurSeg.mapTracks.end()) 658 764 return VERR_NOT_FOUND; 659 765 660 constWebMTrack *pTrack = itTrack->second;766 WebMTrack *pTrack = itTrack->second; 661 767 AssertPtr(pTrack); 662 768 663 769 int rc; 664 770 665 if (m_f TracksOpen)666 { 667 m_Ebml.subEnd( Tracks);668 m_f TracksOpen = false;771 if (m_fInTracksSection) 772 { 773 m_Ebml.subEnd(MkvElem_Tracks); 774 m_fInTracksSection = false; 669 775 } 670 776 … … 710 816 int writeFooter(void) 711 817 { 712 if (m_f TracksOpen)713 { 714 m_Ebml.subEnd( Tracks);715 m_f TracksOpen = false;818 if (m_fInTracksSection) 819 { 820 m_Ebml.subEnd(MkvElem_Tracks); 821 m_fInTracksSection = false; 716 822 } 717 823 718 if ( m_fClusterOpen)719 { 720 m_Ebml.subEnd( Cluster);721 m_fClusterOpen = false;824 if (CurSeg.CurCluster.fOpen) 825 { 826 m_Ebml.subEnd(MkvElem_Cluster); 827 CurSeg.CurCluster.fOpen = false; 722 828 } 723 829 724 830 /* 725 * Write Cues segment.831 * Write Cues element. 726 832 */ 727 833 LogFunc(("Cues @ %RU64\n", RTFileTell(m_Ebml.getFile()))); 728 834 729 m_offSegCuesStart = RTFileTell(m_Ebml.getFile()); 730 731 m_Ebml.subStart(Cues); 732 733 for (std::list<WebMCueEntry>::iterator it = m_lstCue.begin(); it != m_lstCue.end(); ++it) 734 { 735 m_Ebml.subStart(CuePoint) 736 .serializeUnsignedInteger(CueTime, it->time) 737 .subStart(CueTrackPositions) 738 .serializeUnsignedInteger(CueTrack, 1) 739 .serializeUnsignedInteger(CueClusterPosition, it->loc - m_offSegCurStart, 8) 740 .subEnd(CueTrackPositions) 741 .subEnd(CuePoint); 835 CurSeg.offCues = RTFileTell(m_Ebml.getFile()); 836 837 m_Ebml.subStart(MkvElem_Cues); 838 839 std::list<WebMCueEntry>::iterator itCuePoint = CurSeg.lstCues.begin(); 840 while (itCuePoint != CurSeg.lstCues.end()) 841 { 842 m_Ebml.subStart(MkvElem_CuePoint) 843 .serializeUnsignedInteger(MkvElem_CueTime, itCuePoint->time) 844 .subStart(MkvElem_CueTrackPositions) 845 .serializeUnsignedInteger(MkvElem_CueTrack, 1) 846 .serializeUnsignedInteger(MkvElem_CueClusterPosition, itCuePoint->loc - CurSeg.offStart, 8) 847 .subEnd(MkvElem_CueTrackPositions) 848 .subEnd(MkvElem_CuePoint); 849 850 itCuePoint++; 742 851 } 743 852 744 m_Ebml.subEnd( Cues)745 .subEnd( Segment);853 m_Ebml.subEnd(MkvElem_Cues) 854 .subEnd(MkvElem_Segment); 746 855 747 856 /* 748 * Update SeekHead / Info segment.857 * Re-Update SeekHead / Info elements. 749 858 */ 750 859 751 writeSe ekInfo();860 writeSegSeekInfo(); 752 861 753 862 return RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL); … … 756 865 int close(void) 757 866 { 758 WebMTracks::iterator itTrack = m_mapTracks.begin();759 for (; itTrack != m_mapTracks.end(); ++itTrack)867 WebMTracks::iterator itTrack = CurSeg.mapTracks.begin(); 868 for (; itTrack != CurSeg.mapTracks.end(); ++itTrack) 760 869 { 761 870 delete itTrack->second; 762 m_mapTracks.erase(itTrack);871 CurSeg.mapTracks.erase(itTrack); 763 872 } 764 873 765 Assert( m_mapTracks.size() == 0);874 Assert(CurSeg.mapTracks.size() == 0); 766 875 767 876 m_Ebml.close(); … … 775 884 private: 776 885 777 void writeSeekInfo(void) 778 { 779 if (m_offSegSeekInfoStart) 780 RTFileSeek(m_Ebml.getFile(), m_offSegSeekInfoStart, RTFILE_SEEK_BEGIN, NULL); 886 /** 887 * Writes the segment's seek information and cue points. 888 * 889 * @returns IPRT status code. 890 */ 891 void writeSegSeekInfo(void) 892 { 893 if (CurSeg.offSeekInfo) 894 RTFileSeek(m_Ebml.getFile(), CurSeg.offSeekInfo, RTFILE_SEEK_BEGIN, NULL); 781 895 else 782 m_offSegSeekInfoStart= RTFileTell(m_Ebml.getFile());783 784 LogFunc(("SeekHead @ %RU64\n", m_offSegSeekInfoStart));785 786 m_Ebml.subStart( SeekHead)787 788 .subStart( Seek)789 .serializeUnsignedInteger( SeekID,Tracks)790 .serializeUnsignedInteger( SeekPosition, m_offSegTracksStart - m_offSegCurStart, 8)791 .subEnd( Seek)792 793 .subStart( Seek)794 .serializeUnsignedInteger( SeekID,Cues)795 .serializeUnsignedInteger( SeekPosition, m_offSegCuesStart - m_offSegCurStart, 8)796 .subEnd( Seek)797 798 .subStart( Seek)799 .serializeUnsignedInteger( SeekID,Info)800 .serializeUnsignedInteger( SeekPosition, m_offSegInfoStart - m_offSegCurStart, 8)801 .subEnd( Seek)802 803 .subEnd( SeekHead);896 CurSeg.offSeekInfo = RTFileTell(m_Ebml.getFile()); 897 898 LogFunc(("SeekHead @ %RU64\n", CurSeg.offSeekInfo)); 899 900 m_Ebml.subStart(MkvElem_SeekHead) 901 902 .subStart(MkvElem_Seek) 903 .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Tracks) 904 .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offTracks - CurSeg.offStart, 8) 905 .subEnd(MkvElem_Seek) 906 907 .subStart(MkvElem_Seek) 908 .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Cues) 909 .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offCues - CurSeg.offStart, 8) 910 .subEnd(MkvElem_Seek) 911 912 .subStart(MkvElem_Seek) 913 .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Info) 914 .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offInfo - CurSeg.offStart, 8) 915 .subEnd(MkvElem_Seek) 916 917 .subEnd(MkvElem_SeekHead); 804 918 805 919 int64_t iFrameTime = (int64_t)1000 * 1 / 25; //m_Framerate.den / m_Framerate.num; /** @todo Fix this! */ 806 m_offSegInfoStart= RTFileTell(m_Ebml.getFile());807 808 LogFunc(("Info @ %RU64\n", m_offSegInfoStart));920 CurSeg.offInfo = RTFileTell(m_Ebml.getFile()); 921 922 LogFunc(("Info @ %RU64\n", CurSeg.offInfo)); 809 923 810 924 char szMux[64]; … … 814 928 RTStrPrintf(szApp, sizeof(szApp), VBOX_PRODUCT " %sr%u", VBOX_VERSION_STRING, RTBldCfgRevision()); 815 929 816 m_Ebml.subStart( Info)817 .serializeUnsignedInteger( TimecodeScale, 1000000)818 .serializeFloat( Segment_Duration, m_tsLastPtsMs + iFrameTime - m_tsInitialPtsMs)819 .serializeString(M uxingApp, szMux)820 .serializeString( WritingApp, szApp)821 .subEnd( Info);930 m_Ebml.subStart(MkvElem_Info) 931 .serializeUnsignedInteger(MkvElem_TimecodeScale, m_uTimecodeScaleNs) 932 .serializeFloat(MkvElem_Segment_Duration, m_tsLastPtsMs + iFrameTime - m_tsInitialPtsMs) 933 .serializeString(MkvElem_MuxingApp, szMux) 934 .serializeString(MkvElem_WritingApp, szApp) 935 .subEnd(MkvElem_Info); 822 936 } 823 937 };
Note:
See TracChangeset
for help on using the changeset viewer.