[Bug 733727] glimagesink GstVideoOverlay not working on Windows OS
GStreamer (bugzilla.gnome.org)
bugzilla at gnome.org
Tue Dec 16 08:02:24 PST 2014
https://bugzilla.gnome.org/show_bug.cgi?id=733727
GStreamer | gst-plugins-bad | 1.4.0
--- Comment #7 from Marc Krämer <marc.kraemer at web.de> 2014-12-16 16:02:19 UTC ---
Okay, i have stripped down my code to the essential parts and into a single c#
winform.
Additional my complete bugdemo project as single zip (without
gstreamer-binaries). If you use it, you only have to take Step 4
-----------------8<-------------------------------------------------------------
To use it:
- Step 1: Create a Winform Application within Visual Studio or Sharp Develop
- Step 2: Add the following Assemblys to your Projekt: glib-sharp.dll +
gstreamer-sharp.dll
- Step 3: Replace your MainForm Code with mine (attention: not the namespace)
- Step 4: Change Settings:
- private const string filepathvideo MUST be your video
- private const videosinktype cfgVideosinkType SHOULD be set to
videosinktype.glimagesink OR videosinktype.d3dvideosink (Depends on what you
want to test)
- Change Environment Path within Method InitializeEnvironment() to your
specific setup
-----------------8<-------------------------------------------------------------
/*
* Created by SharpDevelop.
* User: marc
* Date: 16.12.2014
* Time: 14:55
*
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Security.Permissions;
using Gst;
using GLib;
namespace gstreamerbug
{
/// <summary>
/// Winform Gstreamer for Bugreporting
/// </summary>
public partial class MainForm : Form, IDisposable
{
// CHANGE the following line
private const string filepathvideo = @"d:\t2.mp4";
// CHANGE the following line
private const videosinktype cfgVideosinkType =
videosinktype.glimagesink;
// CHANGE Environment Path within Method InitializeEnvironment()
public Control videodisplay;
public Button startVideoButton;
public Button pauseVideoButton;
public Label initializingMessage;
private System.Threading.Thread mainGlibThread;
private GLib.MainLoop mainLoop;
private Gst.Pipeline currentPipeline = null;
private Gst.Base.BaseSrc filesrc;
private Gst.Bin decodebin;
private Gst.Element timeoverlay;
private Gst.Element videobalance;
private Gst.Video.VideoSink glimagesink;
private Gst.Base.BaseTransform audioconvert;
private Gst.Element volume;
private Gst.Bin autoaudiosink;
private ulong handle;
public MainForm()
{
// Nothing bug-relevant happens here.
InitializeComponent();
// Initialize Controls
videodisplay = new Control() { BackColor=Color.Black };
startVideoButton = new Button() { Text="Start", AutoSize=true};
pauseVideoButton = new Button() { Text="Pause", AutoSize=true};
initializingMessage = new Label() { Text="Initializing... please
wait", AutoSize=false, TextAlign= ContentAlignment.MiddleCenter, Dock=
DockStyle.Fill};
// Add Controls to Form
this.SuspendLayout();
this.Controls.Add(initializingMessage);
this.Controls.Add(videodisplay);
this.Controls.Add(startVideoButton);
this.Controls.Add(pauseVideoButton);
this.ResumeLayout();
SubscribeButtonClicks();
ResizeControls();
initializeEnvironment();
}
protected override void OnShown(EventArgs e)
{
// Let Form paint Controls before long idle time of gstreamer
initialisation
System.Windows.Forms.Application.DoEvents();
// Assign Handle to prevent Cross-Threading Access
handle = (ulong) videodisplay.Handle;
// Initialize Gstreamer
InitializeGstreamer();
// Initialize Mainloop
InitializeMainloop();
// Build Pipeline
BuildPipeline();
// Finaly hide initialisation message
initializingMessage.Visible=false;
base.OnShown(e);
}
#region Resizing Controls
protected override void OnResize(EventArgs e)
{
ResizeControls();
base.OnResize(e);
}
private void ResizeControls()
{
// Terrible, i know. But: No real-world application, just for
bugreporting
int w = this.ClientSize.Width;
int h = this.ClientSize.Height;
int displayH = h - (startVideoButton.Height * 2);
int buttonT = displayH + (startVideoButton.Height / 2);
if(displayH < 0) { displayH = 0; }
if(videodisplay.Top != 0) { videodisplay.Top=0; }
if(videodisplay.Left != 0) { videodisplay.Left=0; }
if(videodisplay.Width != w) { videodisplay.Width=w; }
if(videodisplay.Height != displayH) { videodisplay.Height =
displayH; };
if(startVideoButton.Top != buttonT) { startVideoButton.Top =
buttonT; }
if(startVideoButton.Left != 0) { startVideoButton.Left = 0; }
if(pauseVideoButton.Top != buttonT) { pauseVideoButton.Top =
buttonT;}
if(pauseVideoButton.Left != startVideoButton.Width) {
pauseVideoButton.Left = startVideoButton.Width; }
}
#endregion
#region Initializing Gstreamer
private void initializeEnvironment()
{
string path =
System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
@"lib\gstreamer\w32\");
string pluginpath =
System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
@"lib\gstreamer\w32\lib\gstreamer-1.0\");
string registry = System.IO.Path.Combine(path, "registry.bin");
Environment.SetEnvironmentVariable("PATH",
Environment.GetEnvironmentVariable("PATH") + ";"+path);
Environment.SetEnvironmentVariable("GST_PLUGIN_PATH", pluginpath
+";"+Environment.GetEnvironmentVariable("GST_PLUGIN_PATH"));
Environment.SetEnvironmentVariable("GST_PLUGIN_SYSTEM_PATH_1_0",
pluginpath
+";"+Environment.GetEnvironmentVariable("GST_PLUGIN_SYSTEM_PATH_1_0"));
Environment.SetEnvironmentVariable("GST_PLUGIN_SYSTEM_PATH",
pluginpath +";"+Environment.GetEnvironmentVariable("GST_PLUGIN_SYSTEM_PATH"));
Environment.SetEnvironmentVariable("GST_DEBUG","*:4");
Environment.SetEnvironmentVariable("GST_DEBUG_FILE",System.IO.Path.Combine(path,
"gstreamer.log"));
Environment.SetEnvironmentVariable("GST_REGISTRY", registry);
}
private void InitializeGstreamer()
{
// gst_init
Gst.Application.Init();
// initialize objectmanager. otherwise bus subscription will
fail
GtkSharp.GstreamerSharp.ObjectManager.Initialize();
}
private void InitializeMainloop()
{
mainLoop = new GLib.MainLoop();
mainGlibThread = new System.Threading.Thread(mainLoop.Run);
mainGlibThread.Start();
}
#endregion
#region Build Pipeline
private void BuildPipeline()
{
/*
* gst-launch-1.0.exe -v filesrc location=d:\t.avi ! decodebin
name="dec" ! timeoverlay ! glimagesink dec. ! audioconvert ! autoaudiosink
*/
filesrc = (Gst.Base.BaseSrc)
Gst.ElementFactory.Make("filesrc","filesrc");
decodebin = (Gst.Bin)
Gst.ElementFactory.Make("decodebin","decodebin");
timeoverlay = Gst.ElementFactory.Make("timeoverlay",
"timeoverlay");
videobalance = Gst.ElementFactory.Make("videobalance",
"videobalance");
switch(cfgVideosinkType)
{
case videosinktype.glimagesink:
glimagesink = (Gst.Video.VideoSink)
Gst.ElementFactory.Make("glimagesink","glimagesink");
break;
case videosinktype.d3dvideosink:
glimagesink = (Gst.Video.VideoSink)
Gst.ElementFactory.Make("d3dvideosink","d3dvideosink");
break;
}
audioconvert = (Gst.Base.BaseTransform)
Gst.ElementFactory.Make("audioconvert","audioconvert");
volume = (Gst.Element) Gst.ElementFactory.Make("volume", "volume");
autoaudiosink = (Gst.Bin) Gst.ElementFactory.Make("autoaudiosink",
"autoaudiosink");
currentPipeline = new Gst.Pipeline("pipeline");
currentPipeline.Add(filesrc, decodebin, timeoverlay, videobalance,
glimagesink, audioconvert, volume, autoaudiosink);
filesrc["location"] = filepathvideo;
decodebin.PadAdded += new PadAddedHandler(decodebin_PadAdded);
bool b1 = filesrc.Link(decodebin);
// decodebin Link yet not possible
bool b2 = timeoverlay.Link(videobalance);
bool b4 = videobalance.Link(glimagesink);
bool b5 = audioconvert.Link(volume);
bool b6 = volume.Link(autoaudiosink);
SubscribeBusMessage();
SubscribeBusSyncMessage();
TryPause();
}
/// <summary>
///
/// </summary>
/// <param name="o"></param>
/// <param name="args"></param>
/// <remarks>
/// decodebin has no static src pads (see gst-inspect decodebin)
/// decodebin has "sometimes-pad"
/// </remarks>
void decodebin_PadAdded(object o, PadAddedArgs args)
{
Gst.Element src = (Element)o;
Gst.GhostPad newPad = (Gst.GhostPad) args.NewPad;
if(newPad.Target.ParentElement is Gst.Audio.AudioDecoder)
{
var sinkpad = audioconvert.GetStaticPad("sink");
var newlink = newPad.Link(sinkpad);
}
if(newPad.Target.ParentElement is Gst.Video.VideoDecoder)
{
// Das funktioniert:
bool b1 = decodebin.Link(timeoverlay);
// Das funktioniert (seltsamerweise) nicht
// var p = timeoverlay.GetStaticPad("sink");
// var r = newPad.Link(p);
}
}
/// <summary>
///
/// </summary>
/// <remarks>
/// Indeed the application needs to set its Window identifier at the
right time to avoid internal Window creation
/// from the video sink element. To solve this issue a GstMessage is
posted on the bus to inform the application
/// that it should set the Window identifier immediately.
///
/// API:
http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-libs/html/gst-plugins-base-libs-gstvideooverlay.html
/// </remarks>
/// <param name="o"></param>
/// <param name="args"></param>
private void bus_SyncMessage(object o, SyncMessageArgs args)
{
// Convenience function to check if the given message is a
"prepare-window-handle" message from a GstVideoOverlay.
if(Gst.Video.Global.IsVideoOverlayPrepareWindowHandleMessage(args.Message))
{
Element src = (Gst.Element) args.Message.Src;
#if DEBUG
System.Diagnostics.Debug.WriteLine("Message
'prepare-window-handle' received by: "+src.Name+" "+src.ToString());
#endif
if(src != null && (src is Gst.Video.VideoSink | src is
Gst.Bin))
{
// Try to set Aspect Ratio
try
{
src["force-aspect-ratio"] = true;
}
catch (PropertyNotFoundException) {}
// Try to set Overlay
try
{
Gst.Video.VideoOverlayAdapter overlay_ = new
Gst.Video.VideoOverlayAdapter(src.Handle);
overlay_.WindowHandle = handle;
overlay_.HandleEvents(true);
}
catch(Exception ex) { }
}
}
}
#endregion
private void startVideo_Click(object sender, EventArgs e)
{
TryPlay();
}
private void pauseVideoButton_Click(object sender, EventArgs e)
{
TryPause();
}
private bool TryPlay()
{
bool retval = false;
if(currentPipeline != null)
{
StateChangeReturn s =
currentPipeline.SetState(Gst.State.Playing);
if(s == StateChangeReturn.Success) { retval = true; }
retval = true;
}
return retval;
}
public bool TryPause()
{
bool retval = false;
if(currentPipeline != null)
{
currentPipeline.SetState(Gst.State.Paused);
}
return retval;
}
private void HandleMessage (object o, MessageArgs args)
{
var msg = args.Message;
switch (msg.Type)
{
case MessageType.Error:
//
GLib.GException err;
string debug;
msg.ParseError (out err, out debug);
if(debug == null) { debug = "none"; }
System.Diagnostics.Debug.WriteLine ("Error received from
element {0}: {1}", msg.Src, err.Message);
System.Diagnostics.Debug.WriteLine ("Debugging information:
"+ debug);
break;
}
args.RetVal = true;
}
private void SubscribeButtonClicks()
{
startVideoButton.Click += new EventHandler(startVideo_Click);
pauseVideoButton.Click += new EventHandler(pauseVideoButton_Click);
}
private void UnsubscribeButtonClicks()
{
startVideoButton.Click -= new EventHandler(startVideo_Click);
pauseVideoButton.Click -= new EventHandler(pauseVideoButton_Click);
}
private void SubscribeBusMessage()
{
Bus bus = currentPipeline.Bus;
bus.AddSignalWatch ();
bus.Message += HandleMessage;
}
private void UnsubscribeBusMessage()
{
Bus bus = currentPipeline.Bus;
bus.Message -= HandleMessage;
bus.RemoveSignalWatch();
}
private void SubscribeBusSyncMessage()
{
Bus bus = currentPipeline.Bus;
bus.EnableSyncMessageEmission();
bus.SyncMessage += new SyncMessageHandler(bus_SyncMessage);
}
private void UnsubscribeBusSyncMessage()
{
Bus bus = currentPipeline.Bus;
bus.SyncMessage -= new SyncMessageHandler(bus_SyncMessage);
bus.DisableSyncMessageEmission();
}
[SecurityPermissionAttribute(SecurityAction.Demand, ControlThread =
true)]
private void KillThatCat()
{
if(mainGlibThread != null)
{
mainGlibThread.Abort();
}
}
public new void Dispose()
{
UnsubscribeButtonClicks();
UnsubscribeBusSyncMessage();
UnsubscribeBusMessage();
// easy, but not correct
if(currentPipeline != null) { currentPipeline.Dispose(); }
if(mainGlibThread != null) { mainGlibThread.Abort(); }
if(mainGlibThread != null)
{
// Die Glib.Mainloop kann sehr penetrant darin sein sich
nicht beenden zu wollen
KillThatCat();
}
base.Dispose();
}
}
internal enum videosinktype
{
glimagesink,
d3dvideosink
}
}
--
Configure bugmail: https://bugzilla.gnome.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the QA contact for the bug.
You are the assignee for the bug.
More information about the gstreamer-bugs
mailing list