Changeset 65389 in vbox
- Timestamp:
- Jan 20, 2017 2:03:51 PM (8 years ago)
- Location:
- trunk/src/VBox/Main/src-client
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp
r65353 r65389 75 75 { 76 76 /** Encoder we're going to use. */ 77 OpusEncoder *pEnc; 77 OpusEncoder *pEnc; 78 /** The encoding rate to use. */ 79 uint16_t uHz; 80 /** Duration of the frame in samples (per channel). 81 * Valid frame size are: 82 * 83 * ms Frame size 84 * 2.5 120 85 * 5 240 86 * 10 480 87 * 20 (Default) 960 88 * 40 1920 89 * 60 2880 90 */ 91 uint32_t csFrame; 92 /** The maximum frame size (in samples) we can handle. */ 93 uint32_t csFrameMax; 94 # ifdef DEBUG /** @todo Make these a STAM value? */ 95 /** Number of frames encoded. */ 96 uint64_t cEncFrames; 97 /** Total time (in ms) of already encoded audio data. */ 98 uint64_t msEncTotal; 99 # endif 78 100 } Opus; 79 101 #endif /* VBOX_WITH_LIBOPUS */ … … 90 112 /** The PCM properties of this stream. */ 91 113 PDMAUDIOPCMPROPS Props; 92 uint64_t old_ticks;93 uint64_t cSamplesSentPerSec;94 114 /** Codec-specific data. 95 115 * As every stream can be different, one codec per stream is needed. */ … … 136 156 PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream; 137 157 138 int rc; 158 int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props); 159 if (RT_FAILURE(rc)) 160 return rc; 139 161 140 162 #ifdef VBOX_WITH_LIBOPUS 141 163 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 142 164 165 uint16_t uHz = pStreamOut->Props.uHz; 166 167 /* Opus only supports certain input sample rates in an efficient manner. 168 * So make sure that we use those by resampling the data to the requested rate. */ 169 if (uHz > 24000) uHz = 48000; 170 else if (uHz > 16000) uHz = 24000; 171 else if (uHz > 12000) uHz = 16000; 172 else if (uHz > 8000 ) uHz = 12000; 173 else uHz = 8000; 174 175 LogFunc(("%RU16Hz -> %RU16Hz\n", pStreamOut->Props.uHz, uHz)); 176 177 pStreamOut->Codec.Opus.uHz = uHz; 178 pStreamOut->Codec.Opus.csFrame = uHz / 50; 179 #ifdef DEBUG 180 pStreamOut->Codec.Opus.cEncFrames = 0; 181 pStreamOut->Codec.Opus.msEncTotal = 0; 182 #endif 183 184 /* Calculate the maximum frame size. */ 185 pStreamOut->Codec.Opus.csFrameMax = 48000 /* Maximum sample rate Opus can handle */ 186 * pStreamOut->Props.cChannels; /* Number of channels */ 187 188 /* If we only record audio, create our own WebM writer instance here. */ 143 189 if (pThis->enmMode == AVRECMODE_AUDIO) 144 190 { … … 147 193 WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None); 148 194 if (RT_SUCCESS(rc)) 149 rc = pStreamOut->pWebM->AddAudioTrack(44100, 2, 16, &pStreamOut->uTrack); 195 rc = pStreamOut->pWebM->AddAudioTrack(pStreamOut->Codec.Opus.uHz, pStreamOut->Props.cChannels, pStreamOut->Props.cBits, 196 &pStreamOut->uTrack); 150 197 } 151 198 … … 153 200 return rc; 154 201 155 rc = RTCircBufCreate(&pStreamOut->pCircBuf, _4K); 156 if (RT_FAILURE(rc)) 157 return rc; 158 159 rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props); 202 rc = RTCircBufCreate(&pStreamOut->pCircBuf, (pStreamOut->Codec.Opus.csFrame * pStreamOut->Props.cChannels) * sizeof(uint16_t)); 160 203 if (RT_SUCCESS(rc)) 161 204 { … … 163 206 164 207 int orc; 165 pEnc = opus_encoder_create( 48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);208 pEnc = opus_encoder_create(pStreamOut->Codec.Opus.uHz, pStreamOut->Props.cChannels, OPUS_APPLICATION_AUDIO, &orc); 166 209 if (orc != OPUS_OK) 167 210 { … … 172 215 AssertPtr(pEnc); 173 216 174 #if 0 175 orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(DrvAudioHlpCalcBitrate())); 217 opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(196000)); /** @todo Make this configurable. */ 176 218 if (orc != OPUS_OK) 177 219 { … … 181 223 else 182 224 { 183 #endif 184 pStreamOut->Codec.Opus.pEnc = pEnc; 185 186 if (pCfgAcq) 187 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */ 225 pStreamOut->Codec.Opus.pEnc = pEnc; 226 227 if (pCfgAcq) 228 { 229 /* Make sure to let the driver backend know that we need the audio data in 230 * a specific sampling rate Opus is optimized for. */ 231 pCfgAcq->uHz = pStreamOut->Codec.Opus.uHz; 232 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */ 233 } 234 235 LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 channels, %RU16 bpS\n", 236 uHz, pStreamOut->Props.cChannels, pStreamOut->Props.cBits)); 237 } 188 238 } 189 239 #else … … 315 365 316 366 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 367 RT_NOREF(pThis); 317 368 PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream; 318 369 319 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf); 320 321 uint64_t now = PDMDrvHlpTMGetVirtualTime(pThis->pDrvIns); 322 uint64_t ticks = now - pStreamOut->old_ticks; 323 uint64_t ticks_per_second = PDMDrvHlpTMGetVirtualFreq(pThis->pDrvIns); 324 325 /* Minimize the rounding error: samples = int((ticks * freq) / ticks_per_second + 0.5). */ 326 uint32_t cSamplesPlayed = (int)((2 * ticks * pStreamOut->Props.uHz + ticks_per_second) / ticks_per_second / 2); 327 328 /* Don't play more than available. */ 329 if (cSamplesPlayed > cLive) 330 cSamplesPlayed = cLive; 331 332 /* Remember when samples were consumed. */ 333 pStreamOut->old_ticks = now; 334 335 int cSamplesToSend = cSamplesPlayed; 336 337 LogFlowFunc(("uFreq=%RU32, cChan=%RU8, cBits=%RU8, fSigned=%RTbool, cSamplesToSend=%RU32\n", 338 pStreamOut->Props.uHz, pStreamOut->Props.cChannels, 339 pStreamOut->Props.cBits, pStreamOut->Props.fSigned, cSamplesToSend)); 370 uint32_t csLive = AudioMixBufUsed(&pStream->MixBuf); 371 if (!csLive) 372 { 373 Log3Func(("No live samples, skipping\n")); 374 if (pcbWritten) 375 *pcbWritten = 0; 376 return VINF_SUCCESS; 377 } 378 379 int rc; 340 380 341 381 uint32_t csReadTotal = 0; 342 343 int rc;344 382 345 383 /* … … 347 385 */ 348 386 #ifdef VBOX_WITH_LIBOPUS 349 350 uint16_t cbFrameSize = 960; /** @todo 20ms worth of audio data. */351 352 387 PRTCIRCBUF pCircBuf = pStreamOut->pCircBuf; 353 388 354 while (RTCircBufUsed(pCircBuf) < cbFrameSize) 355 { 356 void *pvBuf; 357 size_t cbBuf; 389 void *pvBuf; 390 size_t cbBuf; 391 392 /* 393 * Fetch as much as we can into our internal ring buffer. 394 */ 395 while (RTCircBufFree(pCircBuf)) 396 { 358 397 RTCircBufAcquireWriteBlock(pCircBuf, RTCircBufFree(pCircBuf), &pvBuf, &cbBuf); 359 398 … … 364 403 uint32_t csRead = 0; 365 404 rc = AudioMixBufReadCirc(&pStream->MixBuf, pvBuf, cbBuf, &csRead); 366 if (RT_SUCCESS(rc)) 405 if ( RT_SUCCESS(rc) 406 && csRead) 367 407 { 368 AudioMixBufFinish(&pStream->MixBuf, csRead); 369 370 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, csRead); 408 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, csRead); 371 409 csReadTotal += csRead; 372 410 } … … 380 418 break; 381 419 } 382 383 if (RTCircBufUsed(pCircBuf) >= cbFrameSize) 384 { 385 uint8_t abSrc[_4K]; 386 size_t cbSrc = 0; 387 388 while (cbSrc < cbFrameSize) 420 } 421 422 if (csReadTotal) 423 AudioMixBufFinish(&pStream->MixBuf, csReadTotal); 424 425 /* 426 * Process our internal ring buffer and encode the data. 427 */ 428 429 uint8_t abSrc[_64K]; /** @todo Fix! */ 430 size_t cbSrc; 431 432 const uint32_t csFrame = pStreamOut->Codec.Opus.csFrame; 433 const uint32_t cbFrame = AUDIOMIXBUF_S2B(&pStream->MixBuf, csFrame); 434 435 while (RTCircBufUsed(pCircBuf) >= cbFrame) 436 { 437 cbSrc = 0; 438 439 while (cbSrc < cbFrame) 440 { 441 RTCircBufAcquireReadBlock(pCircBuf, cbFrame - cbSrc, &pvBuf, &cbBuf); 442 443 if (cbBuf) 389 444 { 390 RTCircBufAcquireReadBlock(pCircBuf, cbFrameSize - cbSrc, &pvBuf, &cbBuf); 391 392 if (cbBuf) 393 { 394 memcpy(&abSrc[cbSrc], pvBuf, cbBuf); 395 396 cbSrc += cbBuf; 397 Assert(cbSrc <= sizeof(abSrc)); 398 } 399 400 RTCircBufReleaseReadBlock(pCircBuf, cbBuf); 401 402 if (!cbBuf) 403 break; 445 memcpy(&abSrc[cbSrc], pvBuf, cbBuf); 446 447 cbSrc += cbBuf; 448 Assert(cbSrc <= sizeof(abSrc)); 404 449 } 405 450 406 /* 407 * Opus always encodes PER FRAME, that is, 2.5, 5, 10, 20, 40 or 60 ms of audio data. 408 * 409 * A packet can have up to 120ms worth of audio data. 410 * Anything > 120ms of data will result in a "corrupted package" error message by 411 * by decoding application. 412 */ 413 uint8_t abDst[_4K]; 414 size_t cbDst = sizeof(abDst); 415 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. */ 418 opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc, 419 (opus_int16 *)abSrc, cbSrc, abDst, cbDst); 420 if (cbWritten > 0) 421 { 422 cbDst = cbWritten; 423 Assert(cbDst <= sizeof(abDst)); 424 425 /* Call the WebM writer to actually write the encoded audio data. */ 426 WebMWriter::BlockData_Opus blockData = { abDst, cbDst }; 427 rc = pStreamOut->pWebM->WriteBlock(pStreamOut->uTrack, &blockData, sizeof(blockData)); 428 AssertRC(rc); 429 } 430 else if (cbWritten < 0) 431 { 432 AssertMsgFailed(("Encoding failed: %s\n", opus_strerror(cbWritten))); 433 rc = VERR_INVALID_PARAMETER; 434 } 435 436 if (RT_FAILURE(rc)) 451 RTCircBufReleaseReadBlock(pCircBuf, cbBuf); 452 453 if (!cbBuf) 437 454 break; 438 455 } 456 457 #ifdef DEBUG_andy 458 RTFILE fh; 459 RTFileOpen(&fh, "/tmp/acap.pcm", RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 460 RTFileWrite(fh, abSrc, cbSrc, NULL); 461 RTFileClose(fh); 462 #endif 463 464 Assert(cbSrc == cbFrame); 465 466 /* 467 * Opus always encodes PER FRAME, that is, exactly 2.5, 5, 10, 20, 40 or 60 ms of audio data. 468 * 469 * A packet can have up to 120ms worth of audio data. 470 * Anything > 120ms of data will result in a "corrupted package" error message by 471 * by decoding application. 472 */ 473 uint8_t abDst[_64K]; /** @todo Fix! */ 474 size_t cbDst = sizeof(abDst); 475 476 /* Call the encoder to encode one frame per iteration. */ 477 opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc, 478 (opus_int16 *)abSrc, csFrame, abDst, cbDst); 479 if (cbWritten > 0) 480 { 481 #ifdef DEBUG 482 /* Get overall frames encoded. */ 483 uint32_t cEncFrames = opus_packet_get_nb_frames(abDst, cbDst); 484 uint32_t cEncSamplesPerFrame = opus_packet_get_samples_per_frame(abDst, pStreamOut->Codec.Opus.uHz); 485 uint32_t csEnc = cEncFrames * cEncSamplesPerFrame; 486 487 pStreamOut->Codec.Opus.cEncFrames += cEncFrames; 488 pStreamOut->Codec.Opus.msEncTotal += 20 /* Default 20 ms */ * cEncFrames; 489 490 LogFunc(("%RU64ms [%RU64 frames]: cbSrc=%zu, cbDst=%zu, cEncFrames=%RU32, cEncSamplesPerFrame=%RU32, csEnc=%RU32\n", 491 pStreamOut->Codec.Opus.msEncTotal, pStreamOut->Codec.Opus.cEncFrames, 492 cbSrc, cbDst, cEncFrames, cEncSamplesPerFrame, csEnc)); 493 #endif 494 Assert((uint32_t)cbWritten <= cbDst); 495 cbDst = RT_MIN((uint32_t)cbWritten, cbDst); /* Update cbDst to actual bytes encoded (written). */ 496 497 /* Call the WebM writer to actually write the encoded audio data. */ 498 WebMWriter::BlockData_Opus blockData = { abDst, cbDst }; 499 rc = pStreamOut->pWebM->WriteBlock(pStreamOut->uTrack, &blockData, sizeof(blockData)); 500 AssertRC(rc); 501 } 502 else if (cbWritten < 0) 503 { 504 AssertMsgFailed(("Encoding failed: %s\n", opus_strerror(cbWritten))); 505 rc = VERR_INVALID_PARAMETER; 506 } 507 508 if (RT_FAILURE(rc)) 509 break; 439 510 } 440 511 #else -
trunk/src/VBox/Main/src-client/EbmlWriter.cpp
r65362 r65389 20 20 #include <map> 21 21 #include <stack> 22 23 #include <math.h> /* For lround.h. */ 24 22 25 #include <iprt/asm.h> 23 26 #include <iprt/buildconfig.h> … … 176 179 * by this function. 177 180 */ 178 inline Ebml &serializeFloat(EbmlClassId classId, doublevalue)181 inline Ebml &serializeFloat(EbmlClassId classId, float value) 179 182 { 180 183 writeClassId(classId); 181 writeSize(sizeof(double)); 182 writeUnsignedInteger(*reinterpret_cast<uint64_t*>(&value)); 184 Assert(sizeof(uint32_t) == sizeof(float)); 185 writeSize(sizeof(float)); 186 187 union 188 { 189 float f; 190 uint8_t u8[4]; 191 } u; 192 193 u.f = value; 194 195 for (int8_t i = 3; i >= 0; i--) /* Converts values to big endian. */ 196 write(&u.u8[i], 1); 197 183 198 return *this; 184 199 } … … 314 329 { 315 330 /** Sample rate of input data. */ 316 uint16_t uHzIn; 317 /** Sample rate the codec is using. */ 318 uint16_t uHzCodec; 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! */ 322 size_t cbFrameSize; 331 uint16_t uHz; 332 /** Duration of the frame in samples (per channel). 333 * Valid frame size are: 334 * 335 * ms Frame size 336 * 2.5 120 337 * 5 240 338 * 10 480 339 * 20 (Default) 960 340 * 40 1920 341 * 60 2880 342 */ 343 uint16_t csFrame; 323 344 } Audio; 324 345 }; … … 346 367 , offCluster(0) 347 368 , fOpen(false) 348 , tsStartMs(0) 349 , tsEndMs(0) { } 369 , tcStart(0) 370 , tcLast(0) 371 , cBlocks(0) 372 , cbData(0) { } 350 373 351 374 /** This cluster's ID. */ … … 356 379 /** Whether this cluster element is opened currently. */ 357 380 bool fOpen; 358 /** Timestamp (in ms) when starting this cluster. */ 359 uint64_t tsStartMs; 360 /** Timestamp (in ms) when this cluster ended. */ 361 uint64_t tsEndMs; 381 /** Timecode when starting this cluster. */ 382 uint64_t tcStart; 383 /** Timecode when this cluster was last touched. */ 384 uint64_t tcLast; 385 /** Number of (simple) blocks in this cluster. */ 386 uint64_t cBlocks; 387 /** Size (in bytes) of data already written. */ 388 uint64_t cbData; 362 389 }; 363 390 … … 370 397 { 371 398 WebMSegment(void) 372 : offStart(0) 399 : tcStart(UINT64_MAX) 400 , tcEnd(UINT64_MAX) 401 , offStart(0) 373 402 , offInfo(0) 374 403 , offSeekInfo(0) 375 404 , offTracks(0) 376 , offCues(0) { } 405 , offCues(0) 406 { 407 /* This is the default for WebM -- all timecodes in the segments are expressed in ms. 408 * This allows every cluster to have blocks with positive values up to 32.767 seconds. */ 409 uTimecodeScaleFactor = 1000000; 410 //m_uTimecodeScaleFactor = lround(1000000000 /* ns */ / 48000); 411 412 LogFunc(("Default timecode scale is: %RU64ns\n", uTimecodeScaleFactor)); 413 } 414 415 /** The timecode scale factor of this segment. 416 * By default this is 1000000, which is 1ms per timecode unit. */ 417 uint64_t uTimecodeScaleFactor; 418 419 /** Timecode when starting this segment. */ 420 uint64_t tcStart; 421 /** Timecode when this segment ended. */ 422 uint64_t tcEnd; 377 423 378 424 /** Absolute offset (in bytes) of CurSeg. */ … … 434 480 WebMWriter::VideoCodec m_enmVideoCodec; 435 481 436 /** This PTS (Presentation Time Stamp) acts as our master clock for synchronizing streams. */437 uint64_t m_uPts;438 /** Timestamp (in ms) of initial PTS. */439 int64_t m_tsInitialPtsMs;440 /** Timestamp (in ms) of last written PTS. */441 int64_t m_tsLastPtsMs;442 443 482 /** Whether we're currently in the tracks section. */ 444 483 bool m_fInTracksSection; … … 448 487 /** Maximum value a timecode can have. */ 449 488 uint32_t m_uTimecodeMax; 450 /** The timecode scale factor. */451 uint64_t m_uTimecodeScaleNs;452 489 453 490 Ebml m_Ebml; … … 460 497 461 498 WebMWriter_Impl() : 462 m_uPts(0) 463 , m_tsInitialPtsMs(-1) 464 , m_tsLastPtsMs(-1) 465 , m_fInTracksSection(false) 499 m_fInTracksSection(false) 466 500 { 467 501 /* Size (in bytes) of time code to write. We use 2 bytes (16 bit) by default. */ 468 502 m_cbTimecode = 2; 469 503 m_uTimecodeMax = UINT16_MAX; 470 471 /* This is the default for WebM -- all timecodes in the segments are expressed in ms.472 * This allows every cluster to have blocks with positive values up to 32.767 seconds. */473 m_uTimecodeScaleNs = 1000000;474 504 } 475 505 … … 479 509 } 480 510 511 /** 512 * Adds an audio track. 513 * 514 * @returns IPRT status code. 515 * @param uHz Input sampling rate. 516 * @param cChannels Number of input audio channels. 517 * @param cBits Number of input bits per channel. 518 * @param puTrack Track number on successful creation. Optional. 519 */ 481 520 int AddAudioTrack(uint16_t uHz, uint8_t cChannels, uint8_t cBits, uint8_t *puTrack) 482 521 { 483 522 #ifdef VBOX_WITH_LIBOPUS 523 int rc; 524 525 /* 526 * Check if the requested codec rate is supported. 527 * 528 * Only the following values are supported by an Opus standard build 529 * -- every other rate only is supported by a custom build. 530 */ 531 switch (uHz) 532 { 533 case 48000: 534 case 24000: 535 case 16000: 536 case 12000: 537 case 8000: 538 rc = VINF_SUCCESS; 539 break; 540 541 default: 542 rc = VERR_NOT_SUPPORTED; 543 break; 544 } 545 546 if (RT_FAILURE(rc)) 547 return rc; 548 484 549 m_Ebml.subStart(MkvElem_TrackEntry); 485 550 m_Ebml.serializeUnsignedInteger(MkvElem_TrackNumber, (uint8_t)CurSeg.mapTracks.size()); … … 490 555 WebMTrack *pTrack = new WebMTrack(WebMTrackType_Audio, uTrack, RTFileTell(m_Ebml.getFile())); 491 556 492 /* Clamp the codec rate (Hz) value if it reaches a certain threshold. */ 493 if (uHz > 24000) pTrack->Audio.uHzCodec = 48000; 494 else if (uHz > 16000) pTrack->Audio.uHzCodec = 24000; 495 else if (uHz > 12000) pTrack->Audio.uHzCodec = 16000; 496 else if (uHz > 8000 ) pTrack->Audio.uHzCodec = 12000; 497 else pTrack->Audio.uHzCodec = 8000; 498 499 pTrack->Audio.uHzIn = uHz; 500 501 /** @todo 960 bytes is 20ms worth of audio data. Make this configurable. */ 502 pTrack->Audio.cbFrameSize = 960 /* Bytes */ / (48000 /* Hz */ / pTrack->Audio.uHzCodec); 503 504 /** @todo Resolve codec type. */ 557 pTrack->Audio.uHz = uHz; 558 pTrack->Audio.csFrame = pTrack->Audio.uHz / 50; /** @todo 20 ms of audio data. Make this configurable? */ 559 505 560 OpusPrivData opusPrivData(uHz, cChannels); 506 561 507 m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID, 4) 508 .serializeUnsignedInteger(MkvElem_TrackType, 2 /* Audio */) 509 .serializeString(MkvElem_CodecID, "A_OPUS") 510 .serializeData(MkvElem_CodecPrivate, &opusPrivData, sizeof(opusPrivData)) 511 .serializeUnsignedInteger(MkvElem_CodecDelay, 0) 512 .serializeUnsignedInteger(MkvElem_SeekPreRoll, 80000000) /* 80.000ms */ 562 LogFunc(("Opus @ %RU16Hz (Frame size is %RU16 samples / channel))\n", pTrack->Audio.uHz, pTrack->Audio.csFrame)); 563 564 m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID, 4) 565 .serializeUnsignedInteger(MkvElem_TrackType, 2 /* Audio */) 566 .serializeString(MkvElem_CodecID, "A_OPUS") 567 .serializeData(MkvElem_CodecPrivate, &opusPrivData, sizeof(opusPrivData)) 568 .serializeUnsignedInteger(MkvElem_CodecDelay, 0) 569 .serializeUnsignedInteger(MkvElem_SeekPreRoll, 80000000) /* 80ms in ns. */ 513 570 .subStart(MkvElem_Audio) 514 .serializeFloat(MkvElem_SamplingFrequency, (float) pTrack->Audio.uHzCodec)571 .serializeFloat(MkvElem_SamplingFrequency, (float)uHz) 515 572 .serializeUnsignedInteger(MkvElem_Channels, cChannels) 516 573 .serializeUnsignedInteger(MkvElem_BitDepth, cBits) … … 519 576 520 577 CurSeg.mapTracks[uTrack] = pTrack; 578 579 Assert(uTrack == 0); 521 580 522 581 if (puTrack) … … 541 600 542 601 /** @todo Resolve codec type. */ 543 m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID /* UID */, 4)544 .serializeUnsignedInteger(MkvElem_TrackType, 1 /* Video */)545 .serializeString(MkvElem_CodecID, "V_VP8")602 m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID /* UID */, 4) 603 .serializeUnsignedInteger(MkvElem_TrackType, 1 /* Video */) 604 .serializeString(MkvElem_CodecID, "V_VP8") 546 605 .subStart(MkvElem_Video) 547 .serializeUnsignedInteger(MkvElem_PixelWidth, uWidth)606 .serializeUnsignedInteger(MkvElem_PixelWidth, uWidth) 548 607 .serializeUnsignedInteger(MkvElem_PixelHeight, uHeight) 549 .serializeFloat(MkvElem_FrameRate, dbFPS)608 .serializeFloat(MkvElem_FrameRate, dbFPS) 550 609 .subEnd(MkvElem_Video) 551 610 .subEnd(MkvElem_TrackEntry); … … 630 689 631 690 /* Calculate the PTS of this frame in milliseconds. */ 632 int64_t tsPtsMs= a_pPkt->data.frame.pts * 1000633 * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;634 635 if (t sPtsMs <= m_tsLastPtsMs)636 t sPtsMs = m_tsLastPtsMs+ 1;637 638 m_tsLastPtsMs = tsPtsMs;639 640 if ( m_tsInitialPtsMs < 0)641 m_tsInitialPtsMs = m_tsLastPtsMs;691 uint64_t tcPTS = a_pPkt->data.frame.pts * 1000 692 * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den; 693 694 if (tcPTS <= CurSeg.CurCluster.tcLast) 695 tcPTS = CurSeg.CurCluster.tcLast + 1; 696 697 CurSeg.CurCluster.tcLast = tcPTS; 698 699 if (CurSeg.CurCluster.tcStart == UINT64_MAX) 700 CurSeg.CurCluster.tcStart = CurSeg.CurCluster.tcLast; 642 701 643 702 /* Calculate the relative time of this block. */ 644 uint16_t t sBlockMs= 0;703 uint16_t tcBlock = 0; 645 704 bool fClusterStart = false; 646 705 647 706 /* Did we reach the maximum our timecode can hold? Use a new cluster then. */ 648 if (t sPtsMs - CurSeg.CurCluster.tsStartMs> m_uTimecodeMax)707 if (tcPTS - CurSeg.CurCluster.tcStart > m_uTimecodeMax) 649 708 fClusterStart = true; 650 709 else 651 710 { 652 711 /* Calculate the block's timecode, which is relative to the current cluster's starting timecode. */ 653 t sBlockMs = static_cast<uint16_t>(tsPtsMs - CurSeg.CurCluster.tsStartMs);712 tcBlock = static_cast<uint16_t>(tcPTS - CurSeg.CurCluster.tcStart); 654 713 } 655 714 … … 669 728 } 670 729 671 t sBlockMs= 0;730 tcBlock = 0; 672 731 673 732 /* Open a new cluster. */ 674 733 Cluster.fOpen = true; 675 Cluster.t sStartMs = tsPtsMs;734 Cluster.tcStart = tcPTS; 676 735 Cluster.offCluster = RTFileTell(m_Ebml.getFile()); 677 678 LogFunc(("Cluster @ %RU64\n", Cluster.offCluster)); 736 Cluster.cBlocks = 0; 737 Cluster.cbData = 0; 738 739 LogFunc(("[C%RU64] Start @ tc=%RU64 off=%RU64\n", a_pTrack->cTotalClusters, Cluster.tcStart, Cluster.offCluster)); 679 740 680 741 m_Ebml.subStart(MkvElem_Cluster) 681 .serializeUnsignedInteger(MkvElem_Timecode, Cluster.t sStartMs);742 .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tcStart); 682 743 683 744 /* Save a cue point if this is a keyframe. */ 684 745 if (fKeyframe) 685 746 { 686 WebMCueEntry cue(Cluster.t sStartMs, Cluster.offCluster);747 WebMCueEntry cue(Cluster.tcStart, Cluster.offCluster); 687 748 CurSeg.lstCues.push_back(cue); 688 749 } 689 750 } 751 752 LogFunc(("tcPTS=%RU64, s=%RU64, e=%RU64\n", tcPTS, CurSeg.CurCluster.tcStart, CurSeg.CurCluster.tcLast)); 690 753 691 754 uint8_t fFlags = 0; … … 695 758 fFlags |= 0x08; /* Invisible frame. */ 696 759 697 return writeSimpleBlockInternal(a_pTrack, t sBlockMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags);760 return writeSimpleBlockInternal(a_pTrack, tcBlock, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags); 698 761 } 699 762 #endif /* VBOX_WITH_LIBVPX */ … … 707 770 AssertReturn(cbData, VERR_INVALID_PARAMETER); 708 771 709 //static uint64_t s_uTimecode = 0;710 /* For now this is constant while encoding (CBR), but might change to VBR later. */711 //s_uTimecode += a_pTrack->Audio.cbFrameSize * 48000 / a_pTrack->Audio.uHzCodec;712 713 //static uint64_t s_uPts = 0;714 715 int64_t tsPtsMs = 1000000000 * a_pTrack->cTotalBlocks / 48000;716 717 m_tsLastPtsMs = tsPtsMs;718 719 if (m_tsInitialPtsMs < 0)720 m_tsInitialPtsMs = m_tsLastPtsMs;721 722 772 WebMCluster &Cluster = CurSeg.CurCluster; 723 773 724 /* Calculate the relative time of this block. */ 725 uint16_t tsBlockMs = 0; 774 /* Calculate the PTS. */ 775 /* Make sure to round the result. This is very important! */ 776 uint64_t tcPTSRaw = lround((CurSeg.uTimecodeScaleFactor * 1000 * Cluster.cbData) / a_pTrack->Audio.uHz); 777 778 uint64_t tcPTS = /*Cluster.tcStart +*/ lround(tcPTSRaw / CurSeg.uTimecodeScaleFactor); 779 780 if (Cluster.tcStart == UINT64_MAX) 781 Cluster.tcStart = tcPTS; 782 783 LogFunc(("tcPTSRaw=%RU64, tcPTS=%RU64, cbData=%RU64\n", tcPTSRaw, tcPTS, Cluster.cbData)); 784 785 Cluster.tcLast = tcPTS; 786 787 uint16_t tcBlock; 726 788 bool fClusterStart = false; 727 789 … … 730 792 731 793 /* Did we reach the maximum our timecode can hold? Use a new cluster then. */ 732 if (tsPtsMs - Cluster.tsStartMs > m_uTimecodeMax) 794 if (tcPTS - Cluster.tcStart > m_uTimecodeMax) 795 { 796 tcBlock = 0; 797 733 798 fClusterStart = true; 799 } 734 800 else 735 801 { 736 802 /* Calculate the block's timecode, which is relative to the Cluster timecode. */ 737 t sBlockMs = static_cast<uint16_t>(tsPtsMs - Cluster.tsStartMs);803 tcBlock = static_cast<uint16_t>(tcPTS - Cluster.tcStart); 738 804 } 739 805 740 806 if (fClusterStart) 741 807 { 742 a_pTrack->cTotalClusters++;743 744 808 if (Cluster.fOpen) /* Close current cluster first. */ 745 809 { … … 748 812 } 749 813 750 t sBlockMs= 0;814 tcBlock = 0; 751 815 752 816 Cluster.fOpen = true; 753 Cluster.t sStartMs = tsPtsMs;817 Cluster.tcStart = tcPTS; 754 818 Cluster.offCluster = RTFileTell(m_Ebml.getFile()); 755 756 LogFunc(("Cluster @ %RU64\n", Cluster.offCluster)); 819 Cluster.cBlocks = 0; 820 Cluster.cbData = 0; 821 822 LogFunc(("[C%RU64] Start @ tc=%RU64 off=%RU64\n", a_pTrack->cTotalClusters, Cluster.tcStart, Cluster.offCluster)); 757 823 758 824 m_Ebml.subStart(MkvElem_Cluster) 759 .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tsStartMs); 760 } 761 762 LogFunc(("Cluster %RU64 - Block %RU64: -> %RU64ms\n", 763 a_pTrack->cTotalClusters, a_pTrack->cTotalBlocks, a_pTrack->cTotalBlocks * 20)); 764 765 return writeSimpleBlockInternal(a_pTrack, tsBlockMs, pvData, cbData, 0 /* Flags */); 825 .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tcStart); 826 827 a_pTrack->cTotalClusters++; 828 } 829 830 LogFunc(("tcPTS=%RU64, s=%RU64, e=%RU64\n", tcPTS, CurSeg.CurCluster.tcStart, CurSeg.CurCluster.tcLast)); 831 832 Cluster.cBlocks += 1; 833 Cluster.cbData += cbData; 834 835 return writeSimpleBlockInternal(a_pTrack, tcBlock, pvData, cbData, 0x80 /* Flags */); 766 836 } 767 837 #endif /* VBOX_WITH_LIBOPUS */ … … 930 1000 .subEnd(MkvElem_SeekHead); 931 1001 932 int64_t iFrameTime = (int64_t)1000 * 1 / 25; //m_Framerate.den / m_Framerate.num; /** @todo Fix this! */1002 //int64_t iFrameTime = (int64_t)1000 * 1 / 25; //m_Framerate.den / m_Framerate.num; /** @todo Fix this! */ 933 1003 CurSeg.offInfo = RTFileTell(m_Ebml.getFile()); 934 1004 … … 945 1015 RTStrPrintf(szApp, sizeof(szApp), VBOX_PRODUCT " %sr%u", VBOX_VERSION_STRING, RTBldCfgRevision()); 946 1016 1017 LogFunc(("Duration=%RU64\n", CurSeg.tcEnd - CurSeg.tcStart)); 1018 947 1019 m_Ebml.subStart(MkvElem_Info) 948 .serializeUnsignedInteger(MkvElem_TimecodeScale, m_uTimecodeScaleNs)949 .serializeFloat(MkvElem_Segment_Duration, m_tsLastPtsMs + iFrameTime - m_tsInitialPtsMs)1020 .serializeUnsignedInteger(MkvElem_TimecodeScale, CurSeg.uTimecodeScaleFactor) 1021 .serializeFloat(MkvElem_Segment_Duration, CurSeg.tcEnd /*+ iFrameTime*/ - CurSeg.tcStart) 950 1022 .serializeString(MkvElem_MuxingApp, szMux) 951 1023 .serializeString(MkvElem_WritingApp, szApp)
Note:
See TracChangeset
for help on using the changeset viewer.