public void Render() { if (_player == null) { return; } lock (_subtitleLock) { if (_clearOnNextRender) { //Log.Debug("SubtitleRenderer: clearOnNextRender"); _clearOnNextRender = false; if (_subTexture != null) { _subTexture.SafeDispose(); } _subTexture = null; _currentSubtitle = null; } if (_renderSubtitles == false) { return; } // ugly temp! bool timeForNext = false; if (_subtitles.Count > 0) { Subtitle next = _subtitles.First.Value; if (next.presentTime <= _player.StreamPosition) { timeForNext = true; } } _posOnLastRender = _player.StreamPosition; // Check for subtitle if we dont have one currently or if the current one is beyond its timeout if (_currentSubtitle == null || _currentSubtitle.presentTime + _currentSubtitle.timeOut <= _player.StreamPosition || timeForNext) { //Log.Debug("-Current position: "); if (_currentSubtitle != null && !timeForNext) { //Log.Debug("-Current subtitle : " + currentSubtitle.ToString() + " time out expired"); _currentSubtitle = null; } if (timeForNext) { //if (currentSubtitle != null) Log.Debug("-Current subtitle : " + currentSubtitle.ToString() + " TIME FOR NEXT!"); } Subtitle next = null; while (_subtitles.Count > 0) { next = _subtitles.First.Value; //Log.Debug("-next from queue: " + next.ToString()); // if the next should be displayed now or previously if (next.presentTime <= _player.StreamPosition) { // remove from queue _subtitles.RemoveFirst(); // if it is not too late for this sub to be displayed, break // otherwise continue if (next.presentTime + next.timeOut >= _player.StreamPosition) { _currentSubtitle = next; break; } } // next wants to be displayed in the future so break else { //Log.Debug("-next is in the future"); break; } } // if currentSubtitle is non-null we have a new subtitle if (_currentSubtitle != null) { SetSubtitle(_currentSubtitle); } else { return; } } VertexFormats vertexFormat = GUIGraphicsContext.DX9Device.VertexFormat; try { int wx = 0, wy = 0, wwidth = 0, wheight = 0; float rationW = 1, rationH = 1; Rectangle src, dst; if (VMR9Util.g_vmr9 != null) { VMR9Util.g_vmr9.GetVideoWindows(out src, out dst); rationH = dst.Height/(float) _currentSubtitle.screenHeight; // Get the location to render the subtitle to for blu-ray if (_currentSubtitle.horizontalPosition != 0) { rationW = dst.Width/(float) _currentSubtitle.screenWidth; wx = dst.X + (int) (rationW*(float) _currentSubtitle.horizontalPosition); } else { rationW = rationH; wx = dst.X + (int) ((dst.Width - _currentSubtitle.width*rationW)/2); } wy = dst.Y + (int) (rationH*_currentSubtitle.firstScanLine); wwidth = (int) (_currentSubtitle.width*rationW); wheight = (int) (_currentSubtitle.height*rationH); // make sure the vertex buffer is ready and correct for the coordinates CreateVertexBuffer(wx, wy, wwidth, wheight); } // Log.Debug("Subtitle render target: wx = {0} wy = {1} ww = {2} wh = {3}", wx, wy, wwidth, wheight); // enable alpha blending so that the subtitle is rendered with transparent background DXNative.FontEngineSetRenderState((int)D3DRENDERSTATETYPE.D3DRS_ALPHABLENDENABLE, 1); // Make sure D3D objects haven't been disposed for some reason. This would cause // an access violation on native side, causing Skin Engine to halt rendering if (_subTexture != null && (!_subTexture.Disposed && !_vertexBuffer.Disposed)) { GUIGraphicsContext.DX9Device.SetStreamSource(0, _vertexBuffer, 0); GUIGraphicsContext.DX9Device.SetTexture(0, _subTexture); GUIGraphicsContext.DX9Device.VertexFormat = CustomVertex.TransformedTextured.Format; GUIGraphicsContext.DX9Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2); } else { Log.Debug("Subtitle renderer: D3D resource was disposed! Not trying to render the texture"); } } catch (Exception e) { Log.Error(e); } try { // Restore device settings GUIGraphicsContext.DX9Device.SetTexture(0, null); GUIGraphicsContext.DX9Device.VertexFormat = vertexFormat; } catch (Exception e) { Log.Error(e); } } // end of lock (subtitle) }
public void OnTextSubtitle(ref TEXT_SUBTITLE sub) { try { if (sub.page == _activeSubPage) { Log.Debug("Page: " + sub.page); Log.Debug("Character table: " + sub.encoding); Log.Debug("Timeout: " + sub.timeOut); Log.Debug("Timestamp: " + sub.timeStamp); Log.Debug("Language: " + sub.language); String content = sub.text; if (content == null) { Log.Error("OnTextSubtitle: sub.txt == null!"); return; } Log.Debug("Content: "); if (content.Trim().Length > 0) // debug log subtitles { StringTokenizer st = new StringTokenizer(content, new char[] {'\n'}); while (st.HasMore) { Log.Debug(st.NextToken()); } } else { Log.Debug("Page: <BLANK PAGE>"); } } } catch (Exception e) { Log.Error("Problem with TEXT_SUBTITLE"); Log.Error(e); } try { // if we dont need the subtitle if (!_renderSubtitles || _useBitmap || (_activeSubPage != sub.page)) { // //chemelli: too much logging. You can check if logs have: // Log.Debug("Page: " + sub.page); or Log.Debug("Page: <BLANK PAGE>"); // and // Log.Debug("Text subtitle (page {0}) ACCEPTED: [...] // to know the evaluation of this if block // //Log.Debug("Text subtitle (page {0}) discarded: useBitmap is {1} and activeSubPage is {2}", sub.page, useBitmap, // activeSubPage); return; } Log.Debug("Text subtitle (page {0}) ACCEPTED: useBitmap is {1} and activeSubPage is {2}", sub.page, _useBitmap, _activeSubPage); Subtitle subtitle = new Subtitle(); // TODO - RenderText should directly draw to a D3D texture subtitle.subBitmap = RenderText(sub.lc); subtitle.timeOut = sub.timeOut; subtitle.presentTime = sub.timeStamp / 90000.0f + _startPos; subtitle.height = 576; subtitle.width = 720; subtitle.screenHeight = 576; subtitle.screenWidth = 720; subtitle.firstScanLine = 0; subtitle.horizontalPosition = 0; Texture texture = null; try { // allocate new texture texture = new Texture(GUIGraphicsContext.DX9Device, subtitle.subBitmap.Width, subtitle.subBitmap.Height, 1, Usage.Dynamic, Format.A8R8G8B8, Pool.Default); int pitch; using (GraphicsStream a = texture.LockRectangle(0, LockFlags.Discard, out pitch)) { BitmapData bd = subtitle.subBitmap.LockBits(new Rectangle(0, 0, subtitle.subBitmap.Width, subtitle.subBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); // Quick copy of content unsafe { byte* to = (byte*)a.InternalDataPointer; byte* from = (byte*)bd.Scan0.ToPointer(); for (int y = 0; y < bd.Height; ++y) { for (int x = 0; x < bd.Width * 4; ++x) { to[pitch * y + x] = from[y * bd.Stride + x]; } } } texture.UnlockRectangle(0); subtitle.subBitmap.UnlockBits(bd); subtitle.subBitmap.SafeDispose(); subtitle.subBitmap = null; subtitle.texture = texture; a.Close(); } } catch (Exception e) { Log.Debug("SubtitleRenderer: Failed to create subtitle surface!"); Log.Error(e); return; } AddSubtitle(subtitle); } catch (Exception e) { Log.Error("Problem processing text subtitle"); Log.Error(e); } }
private void SetSubtitle(Subtitle subtitle) { try { lock (_subtitleLock) { Log.Debug("SubtitleRenderer: SetSubtitle : " + subtitle.ToString()); // dispose of old subtitle _subTexture.SafeDispose(); _subTexture = null; // set new subtitle if (subtitle != null) { _subTexture = subtitle.texture; _currentSubtitle = subtitle; _currentSubtitle.subBitmap.SafeDispose(); _currentSubtitle.subBitmap = null; } } } catch (Exception e) { Log.Error("SubtitleRenderer: SetSubtitle exception: {0} {1}", e.Message, e.StackTrace); } }
/// <summary> /// Callback from subtitle filter, alerting us that a new subtitle is available /// It receives the new subtitle as the argument sub, which data is only valid /// for the duration of OnSubtitle. /// </summary> /// <returns></returns> public int OnSubtitle(ref NATIVE_SUBTITLE sub) { if (!_useBitmap || !_renderSubtitles) { return 0; // TODO: Might be good to let this cache and then check in Render method because bitmap subs arrive a while before display } Log.Debug("OnSubtitle - stream position " + _player.StreamPosition); lock (_alert) { try { Log.Debug("SubtitleRenderer: Bitmap: bpp=" + sub.bmBitsPixel + " planes " + sub.bmPlanes + " dim = " + sub.bmWidth + " x " + sub.bmHeight + " stride : " + sub.bmWidthBytes); Log.Debug("SubtitleRenderer: to = " + sub.timeOut + " ts=" + sub.timeStamp + " fsl=" + sub.firstScanLine + " h pos=" + sub.horizontalPosition + " (startPos = " + _startPos + ")"); Subtitle subtitle = new Subtitle(); subtitle.subBitmap = new Bitmap(sub.bmWidth, sub.bmHeight, PixelFormat.Format32bppArgb); subtitle.timeOut = sub.timeOut; subtitle.presentTime = ((double)sub.timeStamp / 1000.0f) + _startPos; // compute present time in SECONDS subtitle.height = (uint)sub.bmHeight; subtitle.width = (uint)sub.bmWidth; subtitle.screenHeight = (uint)sub.screenHeight; subtitle.screenWidth = (uint)sub.screenWidth; subtitle.firstScanLine = sub.firstScanLine; subtitle.horizontalPosition = sub.horizontalPosition; subtitle.id = _subCounter++; //Log.Debug("Received Subtitle : " + subtitle.ToString()); Texture texture = null; try { // allocate new texture texture = new Texture(GUIGraphicsContext.DX9Device, (int)subtitle.width, (int)subtitle.height, 1, Usage.Dynamic, Format.A8R8G8B8, GUIGraphicsContext.GetTexturePoolType()); if (texture == null) { Log.Debug("OnSubtitle: Failed to create new texture!"); return 0; } int pitch; using (GraphicsStream a = texture.LockRectangle(0, LockFlags.Discard, out pitch)) { // Quick copy of content unsafe { byte* to = (byte*)a.InternalDataPointer; byte* from = (byte*)sub.bmBits; for (int y = 0; y < sub.bmHeight; ++y) { for (int x = 0; x < sub.bmWidth * 4; ++x) { to[pitch * y + x] = from[y * sub.bmWidthBytes + x]; } } } a.Close(); } texture.UnlockRectangle(0); subtitle.texture = texture; } catch (Exception) { Log.Debug("OnSubtitle: Failed to copy bitmap data!"); return 0; } AddSubtitle(subtitle); } catch (Exception e) { Log.Error(e); } } return 0; }
private void AddSubtitle(Subtitle sub) { lock (_subtitleLock) { while (_subtitles.Count >= MAX_SUBTITLES_IN_QUEUE) { Log.Debug("SubtitleRenderer: Subtitle queue too big, discarding first element"); _subtitles.First.Value.SafeDispose(); _subtitles.RemoveFirst(); } _subtitles.AddLast(sub); Log.Debug("SubtitleRenderer: Subtitle added, now have " + _subtitles.Count + " subtitles in cache"); } }
public void Render() { if (_player == null) { return; } lock (_subtitleLock) { if (_clearOnNextRender) { //Log.Debug("SubtitleRenderer: clearOnNextRender"); _clearOnNextRender = false; if (_subTexture != null) { _subTexture.SafeDispose(); } _subTexture = null; _currentSubtitle = null; } if (_renderSubtitles == false) { return; } // ugly temp! bool timeForNext = false; if (_subtitles.Count > 0) { Subtitle next = _subtitles.First.Value; if (next.presentTime <= _player.StreamPosition) { timeForNext = true; } } _posOnLastRender = _player.StreamPosition; // Check for subtitle if we dont have one currently or if the current one is beyond its timeout if (_currentSubtitle == null || _currentSubtitle.presentTime + _currentSubtitle.timeOut <= _player.StreamPosition || timeForNext) { //Log.Debug("-Current position: "); if (_currentSubtitle != null && !timeForNext) { //Log.Debug("-Current subtitle : " + currentSubtitle.ToString() + " time out expired"); _currentSubtitle = null; } if (timeForNext) { //if (currentSubtitle != null) Log.Debug("-Current subtitle : " + currentSubtitle.ToString() + " TIME FOR NEXT!"); } Subtitle next = null; while (_subtitles.Count > 0) { next = _subtitles.First.Value; //Log.Debug("-next from queue: " + next.ToString()); // if the next should be displayed now or previously if (next.presentTime <= _player.StreamPosition) { // remove from queue _subtitles.RemoveFirst(); // if it is not too late for this sub to be displayed, break // otherwise continue if (next.presentTime + next.timeOut >= _player.StreamPosition) { _currentSubtitle = next; break; } } // next wants to be displayed in the future so break else { //Log.Debug("-next is in the future"); break; } } // if currentSubtitle is non-null we have a new subtitle if (_currentSubtitle != null) { SetSubtitle(_currentSubtitle); } else { return; } } //bool alphaTest = false; bool alphaBlend = false; VertexFormats vertexFormat = CustomVertex.TransformedColoredTextured.Format; try { // store current settings so they can be restored when we are done alphaBlend = GUIGraphicsContext.DX9Device.GetRenderStateBoolean(RenderStates.AlphaBlendEnable); vertexFormat = GUIGraphicsContext.DX9Device.VertexFormat; int wx = 0, wy = 0, wwidth = 0, wheight = 0; float rationW = 1, rationH = 1; if (GUIGraphicsContext.IsFullScreenVideo) { rationH = GUIGraphicsContext.Height / (float)_currentSubtitle.screenHeight; rationW = rationH; // Get the location to render the subtitle to wx = GUIGraphicsContext.OverScanLeft + (int)(((float)(GUIGraphicsContext.Width - _currentSubtitle.width * rationW)) / 2); wy = GUIGraphicsContext.OverScanTop + (int)(rationH * (float)_currentSubtitle.firstScanLine); } else // Video overlay { rationH = GUIGraphicsContext.VideoWindow.Height / (float)_currentSubtitle.screenHeight; rationW = rationH; wx = GUIGraphicsContext.VideoWindow.Right - (GUIGraphicsContext.VideoWindow.Width / 2) - (int)(((float)_currentSubtitle.width * rationW) / 2); wy = GUIGraphicsContext.VideoWindow.Top + (int)(rationH * (float)_currentSubtitle.firstScanLine); } wwidth = (int)((float)_currentSubtitle.width * rationW); wheight = (int)((float)_currentSubtitle.height * rationH); // make sure the vertex buffer is ready and correct for the coordinates CreateVertexBuffer(wx, wy, wwidth, wheight); // Log.Debug("Subtitle render target: wx = {0} wy = {1} ww = {2} wh = {3}", wx, wy, wwidth, wheight); // enable alpha blending so that the subtitle is rendered with transparent background FontEngineSetAlphaBlend(1); //TRUE // Make sure D3D objects haven't been disposed for some reason. This would cause // an access violation on native side, causing Skin Engine to halt rendering if (!_subTexture.Disposed && !_vertexBuffer.Disposed) { GUIGraphicsContext.DX9Device.SetStreamSource(0, _vertexBuffer, 0); GUIGraphicsContext.DX9Device.SetTexture(0, _subTexture); GUIGraphicsContext.DX9Device.VertexFormat = CustomVertex.TransformedTextured.Format; GUIGraphicsContext.DX9Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2); } else { Log.Debug("Subtitle renderer: D3D resource was disposed! Not trying to render the texture"); } } catch (Exception e) { Log.Error(e); } try { // Restore device settings GUIGraphicsContext.DX9Device.SetTexture(0, null); GUIGraphicsContext.DX9Device.VertexFormat = vertexFormat; } catch (Exception e) { Log.Error(e); } } // end of lock (subtitle) }
public void OnTextSubtitle(ref TEXT_SUBTITLE sub) { try { if (sub.page == _activeSubPage) { Log.Debug("Page: " + sub.page); Log.Debug("Character table: " + sub.encoding); Log.Debug("Timeout: " + sub.timeOut); Log.Debug("Timestamp: " + sub.timeStamp); Log.Debug("Language: " + sub.language); String content = sub.text; if (content == null) { Log.Error("OnTextSubtitle: sub.txt == null!"); return; } Log.Debug("Content: "); if (content.Trim().Length > 0) // debug log subtitles { StringTokenizer st = new StringTokenizer(content, new char[] {'\n'}); while (st.HasMore) { Log.Debug(st.NextToken()); } } else { Log.Debug("Page: <BLANK PAGE>"); } } } catch (Exception e) { Log.Error("Problem with TEXT_SUBTITLE"); Log.Error(e); } try { // if we dont need the subtitle if (!_renderSubtitles || _useBitmap || (_activeSubPage != sub.page)) { // //chemelli: too much logging. You can check if logs have: // Log.Debug("Page: " + sub.page); or Log.Debug("Page: <BLANK PAGE>"); // and // Log.Debug("Text subtitle (page {0}) ACCEPTED: [...] // to know the evaluation of this if block // //Log.Debug("Text subtitle (page {0}) discarded: useBitmap is {1} and activeSubPage is {2}", sub.page, useBitmap, // activeSubPage); return; } Log.Debug("Text subtitle (page {0}) ACCEPTED: useBitmap is {1} and activeSubPage is {2}", sub.page, _useBitmap, _activeSubPage); Subtitle subtitle = new Subtitle(); // TODO - RenderText should directly draw to a D3D texture subtitle.subBitmap = RenderText(sub.lc); subtitle.timeOut = sub.timeOut; subtitle.presentTime = sub.timeStamp / 90000.0f + _startPos; subtitle.height = 576; subtitle.width = 720; subtitle.screenHeight = 576; subtitle.screenWidth = 720; subtitle.firstScanLine = 0; Texture texture = null; try { // allocate new texture texture = new Texture(GUIGraphicsContext.DX9Device, subtitle.subBitmap.Width, subtitle.subBitmap.Height, 1, Usage.Dynamic, Format.A8R8G8B8, Pool.Default); int pitch; using (GraphicsStream a = texture.LockRectangle(0, LockFlags.Discard, out pitch)) { BitmapData bd = subtitle.subBitmap.LockBits(new Rectangle(0, 0, subtitle.subBitmap.Width, subtitle.subBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); // Quick copy of content unsafe { byte* to = (byte*)a.InternalDataPointer; byte* from = (byte*)bd.Scan0.ToPointer(); for (int y = 0; y < bd.Height; ++y) { for (int x = 0; x < bd.Width * 4; ++x) { to[pitch * y + x] = from[y * bd.Stride + x]; } } } texture.UnlockRectangle(0); subtitle.subBitmap.UnlockBits(bd); subtitle.subBitmap.SafeDispose(); subtitle.subBitmap = null; subtitle.texture = texture; a.Close(); } } catch (Exception e) { Log.Debug("SubtitleRenderer: Failed to create subtitle surface!"); Log.Error(e); return; } AddSubtitle(subtitle); } catch (Exception e) { Log.Error("Problem processing text subtitle"); Log.Error(e); } }
/// <summary> /// Callback from subtitle filter, alerting us that a new subtitle is available /// It receives the new subtitle as the argument sub, which data is only valid /// for the duration of OnSubtitle. /// </summary> /// <returns></returns> public int OnSubtitle(ref NATIVE_SUBTITLE sub) { if (!_useBitmap || !_renderSubtitles) { return 0; // TODO: Might be good to let this cache and then check in Render method because bitmap subs arrive a while before display } Log.Debug("OnSubtitle - stream position " + _player.StreamPosition); lock (_alert) { try { Log.Debug("SubtitleRenderer: Bitmap: bpp=" + sub.bmBitsPixel + " planes " + sub.bmPlanes + " dim = " + sub.bmWidth + " x " + sub.bmHeight + " stride : " + sub.bmWidthBytes); Log.Debug("SubtitleRenderer: to = " + sub.timeOut + " ts=" + sub.timeStamp + " fsl=" + sub.firstScanLine + " (startPos = " + _startPos + ")"); Subtitle subtitle = new Subtitle(); subtitle.subBitmap = new Bitmap(sub.bmWidth, sub.bmHeight, PixelFormat.Format32bppArgb); subtitle.timeOut = sub.timeOut; subtitle.presentTime = ((double)sub.timeStamp / 1000.0f) + _startPos; // compute present time in SECONDS subtitle.height = (uint)sub.bmHeight; subtitle.width = (uint)sub.bmWidth; subtitle.screenHeight = (uint)sub.screenHeight; subtitle.screenWidth = (uint)sub.screenWidth; subtitle.firstScanLine = sub.firstScanLine; subtitle.id = _subCounter++; //Log.Debug("Received Subtitle : " + subtitle.ToString()); Texture texture = null; try { // allocate new texture texture = new Texture(GUIGraphicsContext.DX9Device, (int)subtitle.width, (int)subtitle.height, 1, Usage.Dynamic, Format.A8R8G8B8, Pool.Default); if (texture == null) { Log.Debug("OnSubtitle: Failed to create new texture!"); return 0; } int pitch; using (GraphicsStream a = texture.LockRectangle(0, LockFlags.Discard, out pitch)) { // Quick copy of content unsafe { byte* to = (byte*)a.InternalDataPointer; byte* from = (byte*)sub.bmBits; for (int y = 0; y < sub.bmHeight; ++y) { for (int x = 0; x < sub.bmWidth * 4; ++x) { to[pitch * y + x] = from[y * sub.bmWidthBytes + x]; } } } a.Close(); } texture.UnlockRectangle(0); subtitle.texture = texture; } catch (Exception) { Log.Debug("OnSubtitle: Failed to copy bitmap data!"); return 0; } AddSubtitle(subtitle); } catch (Exception e) { Log.Error(e); } } return 0; }