PageRenderTime 64ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/WebM/CWebMWrapper.cpp

https://github.com/Aboutdept/Plugin_Videoplayer
C++ | 798 lines | 587 code | 143 blank | 68 comment | 94 complexity | e885d0c11488dd18c4e335d048298434 MD5 | raw file
  1. /* Videoplayer_Plugin - for licensing and copyright see license.txt */
  2. #include <Stdafx.h>
  3. #include "CWebMWrapper.h"
  4. #include <CPluginVideoplayer.h>
  5. #include <CVideoplayerSystem.h>
  6. namespace VideoplayerPlugin
  7. {
  8. CWebMWrapper::CWebMWrapper( int nVideoId )
  9. {
  10. m_iCE3Tex = 0;
  11. m_pCE3Tex = NULL;
  12. m_sCE3Tex = "";
  13. m_bSkippable = true;
  14. m_bSkipping = false;
  15. m_bPaused = true;
  16. m_nVideoId = nVideoId;
  17. m_fSpeed = 1;
  18. m_nWidth = 0;
  19. m_nHeight = 0;
  20. m_nRendererWidth = 0;
  21. m_nRendererHeight = 0;
  22. m_eTS = VTS_Default;
  23. m_eDM = VDM_Default;
  24. m_VRenderer = NULL;
  25. }
  26. CWebMWrapper::~CWebMWrapper()
  27. {
  28. Close();
  29. SAFE_RELEASE( m_VRenderer );
  30. }
  31. void CWebMWrapper::Close()
  32. {
  33. m_bPaused = true;
  34. m_Sound.Close();
  35. ReleaseResources( true );
  36. #ifdef _DEBUG
  37. gPlugin->LogAlways( "Close id(%d)", m_nVideoId );
  38. #endif
  39. m_bSkipping = false;
  40. m_bSkippable = true;
  41. }
  42. bool CWebMWrapper::ReleaseResources( bool bResetOverride )
  43. {
  44. gVideoplayerSystem->RestoreMaterials( this, bResetOverride ); // restore materials using this video
  45. SAFE_RELEASE( m_VRenderer );
  46. m_iCE3Tex = 0;
  47. m_sCE3Tex = "";
  48. m_pCE3Tex = NULL;
  49. m_nRendererWidth = 0;
  50. m_nRendererHeight = 0;
  51. return true;
  52. }
  53. bool CWebMWrapper::CreateResources()
  54. {
  55. // needed for 2d placement
  56. m_nRendererWidth = gEnv->pRenderer->GetWidth();
  57. m_nRendererHeight = gEnv->pRenderer->GetHeight();
  58. // release old data
  59. m_pCE3Tex = NULL;
  60. SAFE_RELEASE( m_VRenderer );
  61. // create new data
  62. if ( m_VRenderer = createVideoRenderer( VRT_AUTO ) )
  63. {
  64. if ( m_VRenderer->CreateResources( m_decoder.m_nWidth, m_decoder.m_nHeight, m_nWidth, m_nHeight ) )
  65. {
  66. m_pCE3Tex = reinterpret_cast<ITexture*>( m_VRenderer->GetRenderTarget( VRT_CE3 ) );
  67. }
  68. }
  69. // override material with the new textures
  70. if ( m_pCE3Tex )
  71. {
  72. m_sCE3Tex = m_pCE3Tex->GetName();
  73. m_iCE3Tex = m_pCE3Tex->GetTextureID();
  74. gVideoplayerSystem->OverrideMaterials( this );
  75. return m_pCE3Tex;
  76. }
  77. else
  78. {
  79. gPlugin->LogError( "Could not create texture." );
  80. }
  81. return false;
  82. }
  83. ISoundplayer* CWebMWrapper::GetSoundplayer()
  84. {
  85. return &m_Sound;
  86. }
  87. bool CWebMWrapper::ReOpen()
  88. {
  89. // TODO
  90. //Open(const char* sFile, const char* sSound, bool bLoop, eTimeSource eTS, float fStartAt, float fEndAfter, int nCustomWidth, int nCustomHeight)
  91. return true;
  92. }
  93. bool CWebMWrapper::Open( const char* sFile, const char* sSound, bool bLoop, bool bSkippable, bool bBlockGame, eTimeSource eTS, eDropMode eDM, float fStartAt, float fEndAfter, int nCustomWidth, int nCustomHeight )
  94. {
  95. Close();
  96. SetTimesource( eTS );
  97. m_eDM = eDM;
  98. if ( EXIT_SUCCESS == m_decoder.open( ( char* )sFile, bLoop, fStartAt, fEndAfter, this ) )
  99. {
  100. m_nWidth = nCustomWidth > 0 ? nCustomWidth : m_decoder.m_nWidth;
  101. m_nHeight = nCustomHeight > 0 ? nCustomHeight : m_decoder.m_nHeight;
  102. m_bSkippable = bSkippable;
  103. CreateResources();
  104. }
  105. m_Sound.Open( sSound, this, bLoop );
  106. gPlugin->LogAlways( "Open id(%d) file(%s) sound(%s)", m_nVideoId, sFile, sSound );
  107. return m_pCE3Tex;
  108. }
  109. void CWebMWrapper::SetTimesource( eTimeSource eTS )
  110. {
  111. if ( IsPlaying() )
  112. {
  113. // needs to be resynchronized
  114. Pause();
  115. m_eTS = eTS;
  116. Resume();
  117. }
  118. else
  119. {
  120. m_eTS = eTS;
  121. }
  122. }
  123. void CWebMWrapper::SetSpeed( float fSpeed )
  124. {
  125. if ( fabs( m_fSpeed - 1 ) > 0.05 || fabs( fSpeed - 1 ) > 0.05 )
  126. {
  127. // modify the speed only if there is a difference
  128. m_fSpeed = fSpeed;
  129. }
  130. }
  131. void CWebMWrapper::Skip( bool bForce )
  132. {
  133. if ( m_bSkippable || bForce )
  134. {
  135. m_bSkipping = true;
  136. }
  137. }
  138. bool CWebMWrapper::IsActive()
  139. {
  140. return m_decoder.isOpen();
  141. }
  142. bool CWebMWrapper::IsPlaying()
  143. {
  144. return IsActive() ? !m_bPaused : false;
  145. }
  146. void CWebMWrapper::Resume()
  147. {
  148. m_bPaused = false;
  149. // initialize timers
  150. if ( m_eTS & VTS_SystemTime )
  151. {
  152. vpx_usec_timer_start( &m_timer );
  153. }
  154. m_fTimer = m_decoder.getPosition();
  155. m_fTimerNextFrame = m_fTimer - GetFrameDuration(); // forces output of next frame
  156. // resume playback at last position
  157. m_Sound.Resume();
  158. #if defined(_DEBUG)
  159. gPlugin->LogAlways( "Resume id(%d) video(%.2fs) sound(%.2fs) duration(%.2fs)", m_nVideoId, GetPosition(), m_Sound.GetPosition(), GetDuration() );
  160. #endif
  161. }
  162. void CWebMWrapper::Pause()
  163. {
  164. m_bPaused = true;
  165. m_Sound.Pause();
  166. #if defined(_DEBUG)
  167. gPlugin->LogAlways( "Pause id(%d) video(%.2fs) sound(%.2fs) duration(%.2fs)", m_nVideoId, GetPosition(), m_Sound.GetPosition(), GetDuration() );
  168. #endif
  169. }
  170. int CWebMWrapper::GetId()
  171. {
  172. return m_nVideoId;
  173. }
  174. bool CWebMWrapper::OverrideMaterial( SMaterialOverride& mOverride )
  175. {
  176. #if defined(VP_DISABLE_OVERRIDE)
  177. return false;
  178. #endif
  179. if ( !m_pCE3Tex )
  180. {
  181. return false;
  182. }
  183. // get ShaderItem
  184. const SShaderItem& shaderItem( mOverride.pMaterial->GetShaderItem() );
  185. if ( !shaderItem.m_pShaderResources )
  186. {
  187. return false;
  188. }
  189. SInputShaderResources ResTemp( shaderItem.m_pShaderResources );
  190. const char* sShaderName = shaderItem.m_pShader ? shaderItem.m_pShader->GetName() : NULL;
  191. IShader* pShader = shaderItem.m_pShader; //TODOTODO: Read Shader gen params etc
  192. uint64 uGenerationMask = pShader ? pShader->GetGenerationMask() : 0;
  193. if ( mOverride.bRecommendedSettings )
  194. {
  195. // TODO maybe sometime decide this based on tone mapping settings
  196. // general
  197. sShaderName = "Illum"; // "Monitor"
  198. pShader = gEnv->pRenderer->EF_LoadShader( sShaderName ); // Important to release it (else crash on exit)
  199. //uGenerationMask = pShader->GetGenerationMask();
  200. SAFE_RELEASE( pShader ); // Important else CE3 will crash on exit
  201. ResTemp.m_LMaterial.m_SpecShininess = 0;
  202. //Res.m_LMaterial.m_Specular = ColorF(0,0,0,0);
  203. //Res.m_LMaterial.m_Emission = ColorF(0,0,0,0);
  204. // "Monitor"
  205. //Res.m_GlowAmount = 0.5;
  206. //Res.m_LMaterial.m_Diffuse = ColorF(1,1,1,1);
  207. // Laptop Screen Wars
  208. //Res.m_GlowAmount = 0;
  209. //Res.m_LMaterial.m_Diffuse = ColorF(0.5,0.5,0.5);
  210. // Billboard Screen C2 "Monitor" looks best for general use imho
  211. ResTemp.m_GlowAmount = 0.66;
  212. ResTemp.m_LMaterial.m_Diffuse = ColorF( 0.5, 0.5, 0.5, 0 );
  213. }
  214. bool bRet = false;
  215. // if pShader
  216. // now replace texture slot by switching it in the sampler
  217. SEfResTexture* pTex = &ResTemp.m_Textures[mOverride.nTextureslot];
  218. STexSamplerRT* pSamp = pTex ? &pTex->m_Sampler : NULL;
  219. if ( m_pCE3Tex && pSamp )
  220. {
  221. #if CDK_VERSION >= 350
  222. // 3.5 crashes else on map change -> release orginal texture
  223. SAFE_RELEASE( pSamp->m_pITex );
  224. #endif
  225. pSamp->m_pITex = m_pCE3Tex;
  226. ULONG nrefcount = m_pCE3Tex->AddRef();
  227. pTex->m_TexFlags = FT_USAGE_RENDERTARGET;
  228. pTex->m_Name = m_sCE3Tex; // name is important
  229. mOverride.pMaterial->AssignShaderItem( gEnv->pRenderer->EF_LoadShaderItem( sShaderName, true, 0, &ResTemp, uGenerationMask ) );// TODOTODO Generation params pShader->GetGenerationParams()
  230. bRet = true;
  231. }
  232. // end if pShader
  233. return bRet;
  234. }
  235. void CWebMWrapper::Draw2D( S2DVideo& info )
  236. {
  237. if ( m_pCE3Tex )
  238. {
  239. m_nRendererWidth = gEnv->pRenderer->GetWidth();
  240. m_nRendererHeight = gEnv->pRenderer->GetHeight();
  241. float fWidth = 0;
  242. float fHeight = 0;
  243. float fWidthOffset = 0;
  244. float fHeightOffset = 0;
  245. float fSourceAR = info.fCustomAR >= 0.1 ? info.fCustomAR : ( float )m_nWidth / ( float )m_nHeight;
  246. float fDestinationAR = ( float )m_nRendererWidth / ( float )m_nRendererHeight;
  247. float fAR = fSourceAR / fDestinationAR;
  248. RectF fArea;
  249. fArea.w = info.fRelWidth * VIRTUAL_SCREEN_WIDTH;
  250. fArea.h = info.fRelHeight * VIRTUAL_SCREEN_HEIGHT;
  251. fArea.x = info.fRelLeft * VIRTUAL_SCREEN_WIDTH;
  252. fArea.y = info.fRelTop * VIRTUAL_SCREEN_HEIGHT;
  253. switch ( info.nResizeMode )
  254. {
  255. case VRM_Original: // TODO center in area... (maybe optional parameter)
  256. fWidth = m_nWidth * float( VIRTUAL_SCREEN_WIDTH / float( m_nRendererWidth ) );
  257. fHeight = m_nHeight * float( VIRTUAL_SCREEN_HEIGHT / float( m_nRendererHeight ) );
  258. break;
  259. case VRM_Stretch: // doesn't keep AR
  260. fWidth = fArea.w;
  261. fHeight = fArea.h;
  262. break;
  263. case VRM_TouchInside: // Border
  264. if ( fAR >= 0.99 )
  265. {
  266. // Calculate based on width
  267. fWidth = fArea.w; // fit
  268. fHeight = fArea.h * ( 1.0 / fAR ); // border
  269. fHeightOffset = ( fArea.h - fHeight ) / 2.0; // border
  270. }
  271. else
  272. {
  273. // Calculate based on height
  274. fHeight = fArea.h; // fit
  275. fWidth = fArea.w * fAR; // border
  276. fWidthOffset = ( fArea.w - fWidth ) / 2.0; // border
  277. }
  278. break;
  279. case VRM_TouchOutside: // Crop until AR fits
  280. if ( fAR >= 0.99 ) // TODO real crop by modifying texture coords (maybe optional)
  281. {
  282. // Calculate based on height
  283. fHeight = fArea.h; // fit
  284. fWidth = fArea.w * fAR; // crop
  285. fWidthOffset = ( fArea.w - fWidth ) / 2.0; // crop (place outside)
  286. }
  287. else
  288. {
  289. // Calculate based on width
  290. fWidth = fArea.w; // fit
  291. fHeight = fArea.h * ( 1.0 / fAR ); // crop (place outside)
  292. fHeightOffset = ( fArea.h - fHeight ) / 2.0; // crop
  293. }
  294. break;
  295. }
  296. float fLeft = fArea.x + fWidthOffset;
  297. float fTop = fArea.y + fHeightOffset;
  298. #if !defined(DISABLE_2DDRAWING)
  299. // height and width are in virtual resolution so they are now converted and ready to use
  300. if ( info.cRGBA.a >= 0.01 )
  301. {
  302. bool bDrawBG = info.cBG_RGBA.a >= 0.01 && ( info.nResizeMode == VRM_TouchInside || info.nResizeMode == VRM_Original );
  303. if ( gVideoplayerSystem->IsGameLoopActive() && info.nZPos == VZP_AboveMenu )
  304. {
  305. if ( bDrawBG )
  306. {
  307. // Draw background if enabled
  308. gEnv->pRenderer->Push2dImage(
  309. fArea.x,
  310. fArea.y,
  311. fArea.w,
  312. fArea.h,
  313. #if CDK_VERSION >= 340
  314. gEnv->pRenderer->GetWhiteTextureId(),
  315. #else
  316. -1,
  317. #endif
  318. 0, 1, 1, 0,
  319. info.fAngle,
  320. info.cBG_RGBA.r, info.cBG_RGBA.g, info.cBG_RGBA.b, info.cBG_RGBA.a,
  321. 0 );
  322. }
  323. // Draw video
  324. gEnv->pRenderer->Push2dImage(
  325. fLeft,
  326. fTop,
  327. fWidth,
  328. fHeight,
  329. m_iCE3Tex,
  330. 0, 1, 1, 0,
  331. info.fAngle,
  332. info.cRGBA.r, info.cRGBA.g, info.cRGBA.b, info.cRGBA.a,
  333. 0 );
  334. }
  335. else
  336. {
  337. if ( bDrawBG )
  338. {
  339. // Draw background if enabled
  340. gEnv->pRenderer->Draw2dImage(
  341. fArea.x,
  342. fArea.y,
  343. fArea.w,
  344. fArea.h,
  345. #if CDK_VERSION >= 340
  346. gEnv->pRenderer->GetWhiteTextureId(),
  347. #else
  348. -1,
  349. #endif
  350. 0, 1, 1, 0,
  351. info.fAngle,
  352. info.cBG_RGBA.r, info.cBG_RGBA.g, info.cBG_RGBA.b, info.cBG_RGBA.a,
  353. 0 );
  354. }
  355. // Draw video
  356. gEnv->pRenderer->Draw2dImage(
  357. fLeft,
  358. fTop,
  359. fWidth,
  360. fHeight,
  361. m_iCE3Tex,
  362. 0, 1, 1, 0,
  363. info.fAngle,
  364. info.cRGBA.r, info.cRGBA.g, info.cRGBA.b, info.cRGBA.a,
  365. 0 );
  366. }
  367. }
  368. #endif
  369. }
  370. }
  371. ITexture* CWebMWrapper::GetTexture()
  372. {
  373. return m_pCE3Tex;
  374. }
  375. float CWebMWrapper::GetEnd()
  376. {
  377. if ( !m_decoder.isOpen() )
  378. {
  379. return -1;
  380. }
  381. if ( m_decoder.m_fEndAfter > VIDEO_EPSILON )
  382. {
  383. return min( m_decoder.getDuration(), m_decoder.m_fEndAfter );
  384. }
  385. return m_decoder.getDuration();
  386. }
  387. float CWebMWrapper::GetStart()
  388. {
  389. if ( !m_decoder.isOpen() )
  390. {
  391. return -1;
  392. }
  393. if ( m_decoder.m_fStartAt > VIDEO_EPSILON )
  394. {
  395. return m_decoder.m_fStartAt;
  396. }
  397. return 0;
  398. }
  399. float CWebMWrapper::GetDuration()
  400. {
  401. return m_decoder.isOpen() ? m_decoder.getDuration() : -1;
  402. }
  403. float CWebMWrapper::GetPosition()
  404. {
  405. return m_decoder.isOpen() ? m_decoder.getPosition() : -1;
  406. }
  407. float CWebMWrapper::GetFPS()
  408. {
  409. return m_decoder.isOpen() ? m_decoder.getFPS() : 0;
  410. }
  411. unsigned CWebMWrapper::GetHeight()
  412. {
  413. return m_pCE3Tex ? m_decoder.m_nHeight : 0;
  414. }
  415. unsigned CWebMWrapper::GetWidth()
  416. {
  417. return m_pCE3Tex ? m_decoder.m_nWidth : 0;
  418. }
  419. // IVideoplayerEventListener
  420. void CWebMWrapper::RegisterListener( IVideoplayerEventListener* item )
  421. {
  422. vecQueue.push_back( item );
  423. }
  424. void CWebMWrapper::UnregisterListener( IVideoplayerEventListener* item )
  425. {
  426. for ( std::vector<IVideoplayerEventListener*>::const_iterator iterQueue = vecQueue.begin(); iterQueue != vecQueue.end(); iterQueue++ )
  427. {
  428. if ( ( *iterQueue ) == item )
  429. {
  430. iterQueue = vecQueue.erase( iterQueue );
  431. if ( iterQueue == vecQueue.end() )
  432. {
  433. break;
  434. }
  435. }
  436. }
  437. }
  438. bool CWebMWrapper::Seek( float fPos )
  439. {
  440. bool bRet = ( 0 == m_decoder.seek( fPos ) );
  441. return bRet;
  442. }
  443. void CWebMWrapper::OnStart()
  444. {
  445. // resume playback
  446. Resume();
  447. // broadcast start event
  448. for ( std::vector<IVideoplayerEventListener*>::const_iterator iterQueue = vecQueue.begin(); iterQueue != vecQueue.end(); ++iterQueue )
  449. {
  450. ( *iterQueue )->OnStart();
  451. }
  452. #if defined(_DEBUG)
  453. gPlugin->LogAlways( "OnStart id(%d) video(%.2fs) sound(%.2fs) duration(%.2fs)", m_nVideoId, GetPosition(), m_Sound.GetPosition(), GetDuration() );
  454. #endif
  455. }
  456. void CWebMWrapper::OnEnd()
  457. {
  458. // pause playback
  459. if ( m_decoder.m_bLoop )
  460. {
  461. m_Sound.Pause();
  462. }
  463. else
  464. {
  465. Pause();
  466. }
  467. // broadcast end event
  468. for ( std::vector<IVideoplayerEventListener*>::const_iterator iterQueue = vecQueue.begin(); iterQueue != vecQueue.end(); ++iterQueue )
  469. {
  470. ( *iterQueue )->OnEnd();
  471. }
  472. #if defined(_DEBUG)
  473. gPlugin->LogAlways( "OnEnd id(%d) video(%.2fs) sound(%.2fs) duration(%.2fs)", m_nVideoId, GetPosition(), m_Sound.GetPosition(), GetDuration() );
  474. #endif
  475. }
  476. void CWebMWrapper::OnSeek()
  477. {
  478. // read at least one frame to get position
  479. bool bDirty;
  480. m_decoder.readFrame( NULL, bDirty, false, true );
  481. // reset timers
  482. m_fTimer = m_decoder.getPosition();
  483. m_fTimerNextFrame = m_fTimer - GetFrameDuration(); // forces output of next frame
  484. // seek synchronized sound
  485. m_Sound.Seek( m_fTimer );
  486. // broadcast seek event
  487. for ( std::vector<IVideoplayerEventListener*>::const_iterator iterQueue = vecQueue.begin(); iterQueue != vecQueue.end(); ++iterQueue )
  488. {
  489. ( *iterQueue )->OnSeek();
  490. }
  491. // restart timer
  492. if ( m_eTS & VTS_SystemTime )
  493. {
  494. vpx_usec_timer_start( &m_timer );
  495. }
  496. #if defined(_DEBUG)
  497. gPlugin->LogAlways( "OnSeek id(%d) video(%.2fs) sound(%.2fs) duration(%.2fs)", m_nVideoId, GetPosition(), m_Sound.GetPosition(), GetDuration() );
  498. #endif
  499. }
  500. float CWebMWrapper::GetFrameDuration()
  501. {
  502. if ( !( ( m_eTS | VTS_Sound ) && m_Sound.IsActive() ) )
  503. {
  504. // only modify based on speed if the time source is based on sound (but not implemented anyways)
  505. return 1.0f / ( m_fSpeed * m_decoder.getFPS() );
  506. }
  507. else
  508. {
  509. return 1.0f / m_decoder.getFPS();
  510. }
  511. }
  512. void CWebMWrapper::Advance( float fDeltaTime )
  513. {
  514. if ( m_bSkipping )
  515. {
  516. // Dispatch events to listeners
  517. OnEnd();
  518. Close();
  519. return;
  520. }
  521. if ( m_iCE3Tex > 0 && !m_bPaused && m_decoder.isOpen() )
  522. {
  523. // decoder is in initialized state
  524. float fActualDelta = 0.0f;
  525. float fSoundPos = 0.0f;
  526. bool bSoundPlaying = m_Sound.IsPlaying(); // this will also resume playback if an inconsistent play state is detected
  527. // set if videos should keep playing
  528. // (sound and game are not working in editing mode and need to be suppressed to go into callback mode)
  529. bool bEditorPlayback = gVideoplayerSystem->m_bEditing && gVideoplayerSystem->vp_playbackmode == VPM_KeepPlaying;
  530. if ( !bEditorPlayback && ( m_eTS & VTS_Sound ) && bSoundPlaying && m_fTimer > VIDEO_EPSILON && ( fSoundPos = m_Sound.GetPosition() ) > VIDEO_EPSILON )
  531. {
  532. fActualDelta = fSoundPos - m_fTimer; // no speed since pitch doesn't work on sounds...
  533. // needed if sound end should trigger end/loop event
  534. if ( fActualDelta < -VIDEO_TIMEOUT )
  535. {
  536. goto videoend;
  537. }
  538. }
  539. else if ( bEditorPlayback || ( m_eTS & VTS_SystemTime ) )
  540. {
  541. vpx_usec_timer_mark( &m_timer );
  542. fActualDelta = float( vpx_usec_timer_elapsed( &m_timer ) ) / MICROSECOND;
  543. // in initialization phase of editor playback the timer first needs to be initialized
  544. if ( bEditorPlayback && fabs( fActualDelta ) > gVideoplayerSystem->vp_dropthreshold )
  545. {
  546. fActualDelta = 0;
  547. }
  548. vpx_usec_timer_start( &m_timer );
  549. if ( !( ( m_eTS | VTS_Sound ) && m_Sound.IsActive() ) )
  550. {
  551. fActualDelta *= m_fSpeed;
  552. }
  553. }
  554. else if ( !bEditorPlayback && ( m_eTS & VTS_GameTime ) )
  555. {
  556. fActualDelta = fDeltaTime;
  557. if ( !( ( m_eTS | VTS_Sound ) && m_Sound.IsActive() ) )
  558. {
  559. fActualDelta *= m_fSpeed;
  560. }
  561. }
  562. else
  563. {
  564. fActualDelta = max( fDeltaTime, VIDEO_EPSILON ); // use game time or fall back
  565. }
  566. // Handle cases related to video end
  567. float fEnd = GetEnd();
  568. if ( fEnd > VIDEO_EPSILON )
  569. {
  570. // Somehow we shot over the end (e.g. seek/drop)
  571. if ( m_fTimerNextFrame > ( fEnd + VIDEO_TIMEOUT ) )
  572. {
  573. goto videoend;
  574. }
  575. // If near the end guide video definitely to end so the end event is triggered (sound end could be different from video end or custom end)
  576. if ( fEnd < ( m_fTimerNextFrame + VIDEO_TIMEOUT ) && ( fActualDelta < VIDEO_EPSILON ) )
  577. {
  578. fActualDelta = max( fDeltaTime, VIDEO_EPSILON ); // use game time or fall back
  579. }
  580. }
  581. m_fTimer += max( fActualDelta, 0.0f );
  582. // CryLogAlways(PLUGIN_CONSOLE_PREFIX "Advance id(%d) delta(%.2fs) current(%.2fs) target(%.2fs)", m_nVideoId, fActualDelta, m_fTimerNextFrame, m_fTimer);
  583. float fDifference = max( 0.0f, m_fTimer - m_fTimerNextFrame );
  584. // Which actions should be taken
  585. bool bNeedSeek = fDifference >= gVideoplayerSystem->vp_seekthreshold;
  586. bool bNeedDrop = fDifference >= gVideoplayerSystem->vp_dropthreshold;
  587. unsigned uFrames = 0.5f + ( fDifference / GetFrameDuration() );
  588. unsigned uMaxDrop = 0.5f + ( gVideoplayerSystem->vp_dropmaxduration / GetFrameDuration() );
  589. if ( bNeedSeek && ( m_eDM & VDM_Seek ) )
  590. {
  591. // Trigger seek
  592. #ifdef _DEBUG
  593. gPlugin->LogWarning( "Advance Seek id(%d) frames(%u) diff(%.2f) current(%.2fs) target(%.2fs) fActualDelta(%.4f) fEnd(%.2f)", m_nVideoId, uFrames, fDifference, m_fTimerNextFrame, m_fTimer, fActualDelta, fEnd );
  594. #endif
  595. // This fixes a small problem in splashscreen videos (where high system load causes framedrop but a seek will jump to beginning because of no keyframes)
  596. if ( fEnd > VIDEO_EPSILON && ( ( m_fTimer > ( fEnd - VIDEO_EPSILON ) ) || ( fEnd < gVideoplayerSystem->vp_seekthreshold * 4 ) ) )
  597. {
  598. #ifdef _DEBUG
  599. gPlugin->LogWarning( "Advance Seek ignored due to near end/short video" );
  600. #endif
  601. m_fTimer = m_fTimerNextFrame;
  602. uFrames = 1;
  603. uMaxDrop = 0;
  604. bNeedDrop = false;
  605. bNeedSeek = false;
  606. }
  607. else
  608. {
  609. Seek( m_fTimer );
  610. return;
  611. }
  612. }
  613. bool bDirty;
  614. if ( bNeedDrop && ( m_eDM & ( VDM_Drop | VDM_DropOutput ) ) )
  615. {
  616. // Trigger frame drop
  617. #ifdef _DEBUG
  618. gPlugin->LogWarning( "Advance Drop id(%d) frames(%u) diff(%.2f) current(%.2fs) target(%.2fs)", m_nVideoId, uFrames, fDifference, m_fTimerNextFrame, m_fTimer );
  619. #endif
  620. // read until one frame to be rendered is left (drop data) or the max drop duration is reached
  621. while ( uFrames > 1 && uMaxDrop > 0 )
  622. {
  623. // TODO: see http://code.google.com/p/webm/issues/detail?id=471
  624. //m_decoder.readFrame( NULL, bDirty, m_eDM & VDM_Drop, m_eDM & VDM_DropOutput ); // drop frame
  625. m_decoder.readFrame( NULL, bDirty, false, true ); // drop frame
  626. --uFrames;
  627. --uMaxDrop;
  628. }
  629. }
  630. // output the current frame
  631. if ( uFrames )
  632. {
  633. vpx_image_t* img = NULL;
  634. // decode the next frame
  635. if ( !m_decoder.readFrame( &img, bDirty ) )
  636. {
  637. m_fTimerNextFrame = m_decoder.getPosition() + GetFrameDuration(); // output next frame
  638. // decoded frame needs now to be transfered into video memory
  639. if ( m_VRenderer && img && bDirty )
  640. {
  641. m_VRenderer->RenderFrame( img ); // let the video renderer handle this
  642. }
  643. }
  644. }
  645. }
  646. return;
  647. videoend:
  648. OnEnd(); // dispatch events to listeners
  649. if ( m_decoder.m_bLoop )
  650. {
  651. Seek( 0.0f ); // seek to beginning
  652. OnStart(); // dispatch events to listeners
  653. }
  654. }
  655. }