VlcUserControl.cs
Jump to navigation
Jump to search
/***************************************************************************** * VlcUserControl.cs: VlcUserControl partial class definition ***************************************************************************** * Copyright (C) 2006 Chris Meadowcroft * * Authors: Chris Meadowcroft * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; namespace VLanControl { [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] public partial class VlcUserControl : UserControl, IPlayer2 { private bool isPaused; private NativeLibVlc nativeVlc = new NativeLibVlc(); private bool useMpegVbrOffset; private bool usingPausedPosition; private int pausedTime; private double pausedPosition; private int shuttleSeconds; private bool useVlcCrop = false; private int cropTop; private int cropBottom; private int cropLeft; private int cropRight; const long SavingPostionTimespan = 5000000L; // .5 seconds in ticks public VlcUserControl() { InitializeComponent(); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); this.nativeVlc.Initialize(); this.innerVlc.Bounds = this.ClientRectangle; this.nativeVlc.VideoOutput = this.innerVlc; } public void DisplayMessage(String message) { this.nativeVlc.ShowMessage(message); } public Size VideoSize { get { return this.nativeVlc.VideoSize; } } public void PrecomputeCrop(Size videoSize, int cropLeft, int cropRight, int cropTop, int cropBottom) { this.cropTop = cropTop; this.cropBottom = cropBottom; this.cropLeft = cropLeft; this.cropRight = cropRight; ComputeCrop(videoSize); } public bool ComputeCrop() { if(!this.useVlcCrop && this.IsHandleCreated && (this.nativeVlc.VideoOutput != null) && (this.State != PlayerState.None)) { Size vidSize = this.nativeVlc.VideoSize; if(!vidSize.IsEmpty) { ComputeCrop(this.nativeVlc.VideoSize); return true; } } return false; } private void ComputeCrop(Size videoSize) { Size mySize = ClientSize; Debug.WriteLine("Video Sizes = " + videoSize.ToString() + ", " + mySize.ToString()); this.topBlocker.Visible = false; this.bottomBlocker.Visible = false; this.leftBlocker.Visible = false; this.rightBlocker.Visible = false; double myAspect = Convert.ToDouble(mySize.Width) / Convert.ToDouble(mySize.Height); if((this.cropTop == 0) && (this.cropBottom == 0) && (this.cropLeft == 0) && (this.cropRight == 0)) { double vlcAspect = Convert.ToDouble(videoSize.Width) / Convert.ToDouble(videoSize.Height); if(vlcAspect > myAspect) { // gray borders on the top and bottom int perfectHeight = Convert.ToInt32(mySize.Width / vlcAspect); this.innerVlc.Bounds = new Rectangle(new Point(0, (mySize.Height - perfectHeight) / 2), new Size(mySize.Width, perfectHeight)); Debug.WriteLine("vlcAspect > myAspect " + this.innerVlc.Bounds.ToString()); } else { // gray borders on the left and right int perfectWidth = Convert.ToInt32(mySize.Height * vlcAspect); this.innerVlc.Bounds = new Rectangle(new Point((mySize.Width - perfectWidth) / 2, 0), new Size(perfectWidth, mySize.Height)); Debug.WriteLine("vlcAspect < myAspect " + this.innerVlc.Bounds.ToString()); } } else { double realVlcAspect = Convert.ToDouble(videoSize.Width - this.cropRight - this.cropLeft) / Convert.ToDouble(videoSize.Height - this.cropTop - this.cropBottom); if(realVlcAspect > myAspect) { // gray borders on the top and bottom, and possible blocking panels // first, position and size the innerVlc as if there was no top or bottom cropping int videoPixelWidth = videoSize.Width - this.cropRight - this.cropLeft; double fakeVlcAspect = Convert.ToDouble(videoPixelWidth) / Convert.ToDouble(videoSize.Height); int perfectHeight = Convert.ToInt32(mySize.Width / fakeVlcAspect); double widthScaling = Convert.ToDouble(mySize.Width) / Convert.ToDouble(videoPixelWidth); int innerVlcLeft = -Convert.ToInt32(Convert.ToDouble(this.cropLeft) * widthScaling); int innerVlcWidth = Convert.ToInt32(Convert.ToDouble(videoSize.Width) * widthScaling); int videoTop = (mySize.Height - perfectHeight) / 2; this.innerVlc.Bounds = new Rectangle(new Point(innerVlcLeft, videoTop), new Size(innerVlcWidth, perfectHeight)); // then, add blocking panels if necessary for the top and bottom cropping if(this.cropTop != 0) { double heightScaling = Convert.ToDouble(perfectHeight) / Convert.ToDouble(videoSize.Height); int blockerHeight = Convert.ToInt32(Convert.ToDouble(this.cropTop) * heightScaling); this.topBlocker.Bounds = new Rectangle( new Point(0, videoTop), new Size(mySize.Width, blockerHeight)); this.topBlocker.Visible = true; } if(this.cropBottom != 0) { double heightScaling = Convert.ToDouble(perfectHeight) / Convert.ToDouble(videoSize.Height); int blockerHeight = Convert.ToInt32(Convert.ToDouble(this.cropBottom) * heightScaling); this.bottomBlocker.Bounds = new Rectangle( new Point(0, mySize.Height - videoTop - blockerHeight), new Size(mySize.Width, blockerHeight)); this.bottomBlocker.Visible = true; } } else { // gray borders on the left and right, and possible blocking panels // first, position and size the innerVlc as if there was no left or right cropping int videoPixelHeight = videoSize.Height - this.cropBottom - this.cropTop; double fakeVlcAspect = Convert.ToDouble(videoSize.Width) / Convert.ToDouble(videoPixelHeight); int perfectWidth = Convert.ToInt32(mySize.Height * fakeVlcAspect); double heightScaling = Convert.ToDouble(mySize.Height) / Convert.ToDouble(videoPixelHeight); int innerVlcTop = -Convert.ToInt32(Convert.ToDouble(this.cropTop) * heightScaling); int innerVlcHeight = Convert.ToInt32(Convert.ToDouble(videoSize.Height) * heightScaling); int videoWidth = (mySize.Width - perfectWidth) / 2; this.innerVlc.Bounds = new Rectangle(new Point(videoWidth, innerVlcTop), new Size(perfectWidth, innerVlcHeight)); // then, add blocking panels if necessary for the left and right cropping if(this.cropLeft != 0) { double widthScaling = Convert.ToDouble(perfectWidth) / Convert.ToDouble(videoSize.Width); int blockerWidth = Convert.ToInt32(Convert.ToDouble(this.cropLeft) * widthScaling); this.leftBlocker.Bounds = new Rectangle( new Point(videoWidth, 0), new Size(blockerWidth, mySize.Height)); this.leftBlocker.Visible = true; } if(this.cropRight != 0) { double widthScaling = Convert.ToDouble(perfectWidth) / Convert.ToDouble(videoSize.Width); int blockerWidth = Convert.ToInt32(Convert.ToDouble(this.cropRight) * widthScaling); this.rightBlocker.Bounds = new Rectangle( new Point(mySize.Width - videoWidth - blockerWidth, 0), new Size(blockerWidth, mySize.Height)); this.rightBlocker.Visible = true; } } } } public bool SetConfigVariable(String name, String value) { return this.nativeVlc.SetConfigVariable(name, value) == VlcError.Success; } public int Time { get { try { return this.nativeVlc.Time; } catch(Exception) { return 0; } } } public double Position { get { return this.nativeVlc.Position; } } public void MoveToPosition(TrackPosition newTrackPosition) { if((newTrackPosition.position >= 0.0d) && (newTrackPosition.position < 1.0d)) { this.nativeVlc.Position = newTrackPosition.position; if(this.IsPaused) { this.usingPausedPosition = true; this.pausedTime = newTrackPosition.time; this.pausedPosition = newTrackPosition.position; this.shuttleSeconds = 0; } } } private TrackPosition CalculateShuttle(int origTime, double origPosition, int offsetSeconds) { int newTime = origTime + offsetSeconds; double positionPerTime; if(origPosition > .5d) { positionPerTime = origPosition / Convert.ToDouble(origTime); } else { positionPerTime = (1.0d - origPosition) / Convert.ToDouble(this.nativeVlc.Length - origTime); } double newPosition = origPosition + Convert.ToDouble(offsetSeconds) * positionPerTime; Debug.WriteLine(String.Format("CalculateShuttle time={0} pos={1}", newTime, newPosition)); return new TrackPosition(newTime, newPosition); } public TrackPosition Shuttle(int offsetSeconds) { if(!this.IsPaused) { TrackPosition newPosition = CalculateShuttle(this.nativeVlc.Time, this.nativeVlc.Position, offsetSeconds); if(this.useMpegVbrOffset) { this.nativeVlc.Position = newPosition.position; } else { this.nativeVlc.Time = newPosition.time; } return newPosition; } if(!this.usingPausedPosition) { this.usingPausedPosition = true; this.pausedTime = this.nativeVlc.Time; this.pausedPosition = this.nativeVlc.Position; this.shuttleSeconds = 0; } this.shuttleSeconds += offsetSeconds; return CalculateShuttle(this.pausedTime, this.pausedPosition, this.shuttleSeconds); } public int Volume { get { return this.nativeVlc.Volume; } set { this.nativeVlc.Volume = value; } } const int MinRate = -2; const int MaxRate = 2; const int NormalRate = 0; public void GetRates(out int minRate, out int maxRate, out int normalRate) { minRate = MinRate; maxRate = MaxRate; normalRate = NormalRate; } public int Rate { get { // 1000 is normal, 2000 is half speed, 500 is double speed, etc. int bigRate = this.nativeVlc.GetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "rate", 1000); if(bigRate > 3000) { return -2; } if(bigRate > 1500) { return -1; } if(bigRate > 750) { return 0; } if(bigRate > 400) { return 1; } return 2; } set { int newRate = NormalRate; switch(value) { case 2: newRate = 250; break; case 1: newRate = 500; break; case 0: newRate = 1000; break; case -1: newRate = 2000; break; case -2: newRate = 4000; break; default: throw new ArgumentOutOfRangeException(); } this.nativeVlc.SetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "rate", newRate); } } public int Length { get { try { return this.nativeVlc.Length; } catch(Exception) { return -1; } } set { this.nativeVlc.SetArtificialLength(value); } } public double TimeScaling { get { return this.nativeVlc.TimeScaling; } set { this.nativeVlc.TimeScaling = value; } } public PlayerState State { get { int state = this.nativeVlc.GetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "state", (int)InputState.INIT_S); switch((InputState)state) { case InputState.PAUSE_S: return PlayerState.Paused; case InputState.PLAYING_S: return PlayerState.Playing; default: return PlayerState.None; } } } public bool IsPlaying { get { return this.State == PlayerState.Playing; } } public bool IsPaused { get { return this.State == PlayerState.Paused; } } public bool IsMute { get { return (this.nativeVlc.Volume == 0); } } public void ToggleMute() { this.nativeVlc.ToggleVolumeMute(); } public void DeinterlaceModes(out String[] choices, out String[] choiceText) { this.nativeVlc.GetVlcVariableChoiceList(ObjectType.VLC_OBJECT_VOUT, "deinterlace", out choices, out choiceText); } public String DeinterlaceMode { get { return this.nativeVlc.GetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, "deinterlace", String.Empty); } set { this.nativeVlc.SetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, "deinterlace", value); } } public void AspectRatios(out String[] choices, out String[] choiceText) { this.nativeVlc.GetVlcVariableChoiceList(ObjectType.VLC_OBJECT_VOUT, "aspect-ratio", out choices, out choiceText); } public String AspectRatio { get { return this.nativeVlc.GetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, "aspect-ratio", String.Empty); } set { this.nativeVlc.SetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, "aspect-ratio", value); } } public void CropModes(out String[] choices, out String[] choiceText) { this.nativeVlc.GetVlcVariableChoiceList(ObjectType.VLC_OBJECT_VOUT, "crop", out choices, out choiceText); } public String CropMode { get { return this.nativeVlc.GetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, "crop", String.Empty); } set { this.nativeVlc.SetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, "crop", value); } } public int CroppingLeft { get { if(this.useVlcCrop) { return this.nativeVlc.GetVlcObjectInt(ObjectType.VLC_OBJECT_VOUT, "crop-left", 0); } else { return this.cropLeft; } } set { if(this.useVlcCrop) { this.nativeVlc.SetVlcObjectInt(ObjectType.VLC_OBJECT_VOUT, "crop-left", value); } else { this.cropLeft = value; ComputeCrop(); } } } public int CroppingRight { get { if(this.useVlcCrop) { return this.nativeVlc.GetVlcObjectInt(ObjectType.VLC_OBJECT_VOUT, "crop-right", 0); } else { return this.cropRight; } } set { if(this.useVlcCrop) { this.nativeVlc.SetVlcObjectInt(ObjectType.VLC_OBJECT_VOUT, "crop-right", value); } else { this.cropRight = value; ComputeCrop(); } } } public int CroppingTop { get { if(this.useVlcCrop) { return this.nativeVlc.GetVlcObjectInt(ObjectType.VLC_OBJECT_VOUT, "crop-top", 0); } else { return this.cropTop; } } set { if(this.useVlcCrop) { this.nativeVlc.SetVlcObjectInt(ObjectType.VLC_OBJECT_VOUT, "crop-top", value); } else { this.cropTop = value; ComputeCrop(); } } } public int CroppingBottom { get { if(this.useVlcCrop) { return this.nativeVlc.GetVlcObjectInt(ObjectType.VLC_OBJECT_VOUT, "crop-bottom", 0); } else { return this.cropBottom; } } set { if(this.useVlcCrop) { this.nativeVlc.SetVlcObjectInt(ObjectType.VLC_OBJECT_VOUT, "crop-bottom", value); } else { this.cropBottom = value; ComputeCrop(); } } } public void AudioTracks(out int[] trackIds, out String[] trackNames) { this.nativeVlc.GetVlcVariableChoiceList(ObjectType.VLC_OBJECT_INPUT, "audio-es", out trackIds, out trackNames); } public int AudioTrack { get { return this.nativeVlc.GetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "audio-es", -1); } set { this.nativeVlc.SetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "audio-es", value); } } public void SubTitleTracks(out int[] trackIds, out String[] trackNames) { this.nativeVlc.GetVlcVariableChoiceList(ObjectType.VLC_OBJECT_INPUT, "spu-es", out trackIds, out trackNames); } public int SubTitleTrack { get { return this.nativeVlc.GetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "spu-es", -1); } set { this.nativeVlc.SetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "spu-es", value); } } public void Programs(out int[] trackIds, out String[] trackNames) { this.nativeVlc.GetVlcVariableChoiceList(ObjectType.VLC_OBJECT_INPUT, "program", out trackIds, out trackNames); } public int Program { get { return this.nativeVlc.GetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "program", -1); } set { this.nativeVlc.SetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "program", value); } } public void RotateCropModes() { this.nativeVlc.PressKey("key-crop"); } public int AudioDelay { get { long delay = this.nativeVlc.GetVlcObjectLong(ObjectType.VLC_OBJECT_INPUT, "audio-delay", 0L); return Convert.ToInt32(delay / 1000L); } set { long delay = Convert.ToInt64(value) * 1000L; this.nativeVlc.SetVlcObjectLong(ObjectType.VLC_OBJECT_INPUT, "audio-delay", delay); } } public int SubTitleDelay { get { long delay = this.nativeVlc.GetVlcObjectLong(ObjectType.VLC_OBJECT_INPUT, "spu-delay", 0L); return Convert.ToInt32(delay / 1000L); } set { long delay = Convert.ToInt64(value) * 1000L; this.nativeVlc.SetVlcObjectLong(ObjectType.VLC_OBJECT_INPUT, "spu-delay", delay); } } public int ChapterCount { get { int[] chapterIds; String[] chapterText; this.nativeVlc.GetVlcVariableChoiceList(ObjectType.VLC_OBJECT_INPUT, "chapter", out chapterIds, out chapterText); return (chapterIds.Length > 0) ? chapterIds.Length - 1 : 0; } } public int Chapter { get { return this.nativeVlc.GetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "chapter", -1); } set { this.nativeVlc.SetVlcObjectInt(ObjectType.VLC_OBJECT_INPUT, "chapter", value); } } private void RepositionAfterPause() { if(this.usingPausedPosition && (this.shuttleSeconds != 0)) { TrackPosition newPosition = CalculateShuttle(this.pausedTime, this.pausedPosition, this.shuttleSeconds); if(this.useMpegVbrOffset) { this.nativeVlc.Position = newPosition.position; } else { this.nativeVlc.Time = newPosition.time; } } this.usingPausedPosition = false; } public void Play() { this.isPaused = false; this.nativeVlc.Play(); RepositionAfterPause(); } public void ClearPlayList() { this.usingPausedPosition = false; //Debug.WriteLine("ClearPlayList"); this.nativeVlc.PlaylistClear(); } public int AddToPlayList(String fileName, String title, String[] options) { this.usingPausedPosition = false; int index = 0; this.nativeVlc.AddTarget(fileName, options, ref index); Debug.WriteLine("Added to Playlist " + fileName); Debug.WriteLine("index = " + index.ToString()); return index; } public void PlayItem(int itemId) { this.usingPausedPosition = false; this.isPaused = false; int currentIndex = this.nativeVlc.PlaylistIndex; Debug.WriteLine("PlayItem index = " + itemId.ToString(), " currentIndex = " + currentIndex.ToString()); if(currentIndex < 0) { this.nativeVlc.Play(); } else { //this.nativeVlc.PlaylistPrevious(); this.nativeVlc.Play(itemId); } } public void Stop() { this.usingPausedPosition = false; this.nativeVlc.Stop(); this.isPaused = false; } public void TogglePause() { bool wasPaused = this.IsPaused; this.nativeVlc.Pause(); if(wasPaused) { RepositionAfterPause(); } this.isPaused = !wasPaused; } public void RotateSubtitles() { this.nativeVlc.PressKey("key-subtitle-track"); } public void RotateAudioTrack() { this.nativeVlc.PressKey("key-audio-track"); } public void RotateDeinterlaceMode() { this.nativeVlc.PressKey("key-deinterlace"); } public void RotateAspectRatio() { this.nativeVlc.PressKey("key-aspect-ratio"); } public void CropTop() { //this.nativeVlc.PressKey("key-crop-top"); this.CroppingTop = this.CroppingTop + 1; } public void UnCropTop() { //this.nativeVlc.PressKey("key-uncrop-top"); int crop = this.CroppingTop - 1; if(crop >= 0) { this.CroppingTop = crop; } } public void CropBottom() { //this.nativeVlc.PressKey("key-crop-bottom"); this.CroppingBottom = this.CroppingBottom + 1; } public void UnCropBottom() { //this.nativeVlc.PressKey("key-uncrop-bottom"); int crop = this.CroppingBottom - 1; if(crop >= 0) { this.CroppingBottom = crop; } } public void CropLeft() { //this.nativeVlc.PressKey("key-crop-left"); this.CroppingLeft = this.CroppingLeft + 1; } public void UnCropLeft() { //this.nativeVlc.PressKey("key-uncrop-left"); int crop = this.CroppingLeft - 1; if(crop >= 0) { this.CroppingLeft = crop; } } public void CropRight() { //this.nativeVlc.PressKey("key-crop-right"); this.CroppingRight = this.CroppingRight + 1; } public void UnCropRight() { //this.nativeVlc.PressKey("key-uncrop-right"); int crop = this.CroppingRight - 1; if(crop >= 0) { this.CroppingRight = crop; } } public bool UseMpegVbrOffset { get { return this.useMpegVbrOffset; } set { this.useMpegVbrOffset = value; } } public void NextDvdTrack() { this.nativeVlc.PressKey("key-title-next"); } public void PreviousDvdTrack() { this.nativeVlc.PressKey("key-title-prev"); } public void NextDvdChapter() { this.nativeVlc.PressKey("key-chapter-next"); } public void PreviousDvdChapter() { this.nativeVlc.PressKey("key-chapter-prev"); } private void VlcUserControl_Resize(object sender, EventArgs e) { if(!ComputeCrop()) { this.innerVlc.Bounds = this.ClientRectangle; } } } }