diff --git a/GstreamerHelper/GstreamerHelper.vcxproj b/GstreamerHelper/GstreamerHelper.vcxproj index 4ae10323..746f8d6a 100644 --- a/GstreamerHelper/GstreamerHelper.vcxproj +++ b/GstreamerHelper/GstreamerHelper.vcxproj @@ -122,7 +122,7 @@ Windows true - gstreamer-1.0.lib;gstcontroller-1.0.lib;gobject-2.0.lib;glib-2.0.lib;%(AdditionalDependencies) + gstreamer-1.0.lib;gstcontroller-1.0.lib;gobject-2.0.lib;glib-2.0.lib;gstapp-1.0.lib;%(AdditionalDependencies) diff --git a/GstreamerHelper/GstreamerVideo.cpp b/GstreamerHelper/GstreamerVideo.cpp index 24881aa5..a8edd0d9 100644 --- a/GstreamerHelper/GstreamerVideo.cpp +++ b/GstreamerHelper/GstreamerVideo.cpp @@ -55,9 +55,11 @@ DllExport bool CloseVideo(int Stream) map::iterator it = VideoStreams.find(Stream); if(it != VideoStreams.end()) { - return it->second->CloseVideo(); + delete it->second; + VideoStreams.erase(it); } else return false; + return true; } DllExport int GetVideoNumStreams() @@ -75,12 +77,12 @@ DllExport float GetVideoLength(int Stream) else return -1.0; } -DllExport UINT8* GetFrame(int Stream, float Time, float &VideoTime) +DllExport guint8* GetFrame(int Stream, float Time, float &VideoTime, int &Size, int &Width, int &Height) { map::iterator it = VideoStreams.find(Stream); if(it != VideoStreams.end()) { - return it->second->GetFrame(Time, VideoTime); + return it->second->GetFrame(Time, VideoTime, Size, Width, Height); } } @@ -126,7 +128,30 @@ DllExport bool Finished(int Stream) map::iterator it = VideoStreams.find(Stream); if(it != VideoStreams.end()) { - return it->second->Finished(); + return it->second->IsFinished(); } else return true; } + +DllExport void UpdateVideo(void) +{ + map::iterator p = VideoStreams.begin(); + + while(p != VideoStreams.end()) + { + if(p->second->Closed) + { + delete p->second; + VideoStreams.erase(p++); + } + else { + p->second->UpdateVideo(); + ++p; + } + } +} + +void LogVideoError(const char* msg) +{ + Log(msg); +} \ No newline at end of file diff --git a/GstreamerHelper/GstreamerVideo.h b/GstreamerHelper/GstreamerVideo.h index 57930064..4b14b4cf 100644 --- a/GstreamerHelper/GstreamerVideo.h +++ b/GstreamerHelper/GstreamerVideo.h @@ -20,7 +20,7 @@ using namespace std; DllExport int GetVideoNumStreams(); DllExport float GetVideoLength(int StreamID); - DllExport UINT8* GetFrame(int StreamID, float Time, float &VideoTime); + DllExport guint8* GetFrame(int StreamID, float Time, float &VideoTime, int &Size, int &Width, int &Height); DllExport bool Skip(int StreamID, float Start, float Gap); DllExport void SetVideoLoop(int StreamID, bool Loop); DllExport void PauseVideo(int StreamID); @@ -28,5 +28,7 @@ using namespace std; DllExport bool Finished(int StreamID); + DllExport void UpdateVideo(); + void LogVideoError(const char* msg); diff --git a/GstreamerHelper/GstreamerVideoStream.cpp b/GstreamerHelper/GstreamerVideoStream.cpp index c23f90d0..63c954b8 100644 --- a/GstreamerHelper/GstreamerVideoStream.cpp +++ b/GstreamerHelper/GstreamerVideoStream.cpp @@ -1,56 +1,190 @@ #include "stdafx.h" #include "GstreamerVideoStream.h" +typedef enum { + GST_PLAY_FLAG_VIDEO = (1 << 0), + GST_PLAY_FLAG_AUDIO = (1 << 1), + GST_PLAY_FLAG_TEXT = (1 << 2), + GST_PLAY_FLAG_VIS = (1 << 3), + GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4), + GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5), + GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6), + GST_PLAY_FLAG_DOWNLOAD = (1 << 7), + GST_PLAY_FLAG_BUFFERING = (1 << 8), + GST_PLAY_FLAG_DEINTERLACE = (1 << 9), + GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10) +} GstPlayFlags; GstreamerVideoStream::GstreamerVideoStream(int ID) { this->ID = ID; + + Loop = false; + + Running = true; + + Paused = false; + Finished = false; + Closed = false; + + Duration = -1.0; } GstreamerVideoStream::~GstreamerVideoStream(void) { + CloseVideo(); } int GstreamerVideoStream::LoadVideo(const wchar_t* Filename) { + Element = gst_element_factory_make("playbin", "playbin"); + + + Appsink = (GstAppSink*) gst_element_factory_make("appsink", "appsink"); + + Bus = gst_element_get_bus(Element); + if(!Element) + { + LogVideoError ("Could not create element!"); + return -1; + } + g_object_set(Element, "uri", Filename, NULL); + //g_object_set(Element, "video-sink", Appsink, NULL); + g_object_set(Element, "flags", GST_PLAY_FLAG_VIDEO, NULL); + + //GstCaps *caps = gst_caps_new_any(); + + //gst_caps_set_value(caps, "format", (GValue*)"RGBA"); + //gst_app_sink_set_caps(Appsink, caps); + + gst_element_set_state(Element, GST_STATE_PLAYING); + //gst_bus_timed_pop_filtered(Bus, -1, GST_MESSAGE_ASYNC_DONE); + Paused = true; + RefreshDuration(); + return ID; } - bool GstreamerVideoStream::CloseVideo() { + if(Element) + gst_element_set_state(Element, GST_STATE_NULL); + if(Appsink) + gst_object_unref(Appsink); + if(Bus) + gst_object_unref(Bus); + if(Element) + gst_object_unref(Element); return true; } float GstreamerVideoStream::GetVideoLength() { - return 0.0; + if(Duration <= 0) + RefreshDuration(); + return Duration; } -UINT8* GstreamerVideoStream::GetFrame(float Time, float &VideoTime) +guint8* GstreamerVideoStream::GetFrame(float Time, float &VideoTime, int &Size, int &Width, int &Height) { + /*GstSample *sample; + GstBuffer *buffer; + GstElement *sink; + + sample = gst_app_sink_pull_sample(Appsink); + buffer = gst_sample_get_buffer(sample); + + g_object_get(Element, "video-sink", &sink, NULL); + + gst_buffer_map(buffer, &Mapinfo, GST_MAP_READ); + + VideoTime = (float) (buffer->pts / GST_SECOND); + Size = Mapinfo.size; + return Mapinfo.data; */ return 0; } bool GstreamerVideoStream::Skip(float Start, float Gap) { - return true; + if(!gst_element_seek_simple(Element, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE), (Start + Gap) * GST_SECOND)) + { + LogVideoError("Seek failed"); + return true; + } + return false; } void GstreamerVideoStream::SetVideoLoop(bool Loop) { + this->Loop = Loop; } void GstreamerVideoStream::PauseVideo() { + if(Element) + gst_element_set_state(Element, GST_STATE_PAUSED); } void GstreamerVideoStream::ResumeVideo() { + if(Element) + gst_element_set_state(Element, GST_STATE_PLAYING); } -bool GstreamerVideoStream::Finished() +bool GstreamerVideoStream::IsFinished() { - return true; + return Finished; } + +void GstreamerVideoStream::RefreshDuration() +{ + if(Element) + { + gint64 time; + if(!gst_element_query_duration(Element, GST_FORMAT_TIME, &time)) { + LogVideoError("Could not query duration"); + } + Duration = (gfloat)((gdouble)(time/GST_SECOND)); + } +} + +void GstreamerVideoStream::UpdateVideo() +{ + if(Running) { + Message = gst_bus_pop (Bus); + + /* Parse message */ + if (Message != NULL) { + GError *err; + gchar *debug_info; + string m; + + switch (GST_MESSAGE_TYPE (Message)) { + case GST_MESSAGE_ERROR: + gst_message_parse_error (Message, &err, &debug_info); + m = err->message; + LogVideoError(m.c_str()); + g_clear_error (&err); + g_free (debug_info); + break; + case GST_MESSAGE_DURATION_CHANGED: + RefreshDuration(); + break; + case GST_MESSAGE_STATE_CHANGED: + break; + case GST_MESSAGE_EOS: + if(Loop) + Skip(0, 0); + else + Finished = true; + break; + default: + /* We should not reach here */ + //LogError ("Unexpected message received.\n"); + break; + } + gst_message_unref (Message); + } + } +} \ No newline at end of file diff --git a/GstreamerHelper/GstreamerVideoStream.h b/GstreamerHelper/GstreamerVideoStream.h index a355a254..296a8225 100644 --- a/GstreamerHelper/GstreamerVideoStream.h +++ b/GstreamerHelper/GstreamerVideoStream.h @@ -1,11 +1,15 @@ #pragma once #include "gst/gst.h" +#include "GstreamerVideo.h" +#include "gst\app\gstappsink.h" + using namespace std; class GstreamerVideoStream { public: int ID; + bool Closed; GstreamerVideoStream(int ID); ~GstreamerVideoStream(void); @@ -14,12 +18,33 @@ public: bool CloseVideo(); float GetVideoLength(); - UINT8* GetFrame(float Time, float &VideoTime); + guint8* GetFrame(float Time, float &VideoTime, int &Size, int &Width, int &Height); bool Skip(float Start, float Gap); void SetVideoLoop(bool Loop); void PauseVideo(); void ResumeVideo(); - bool Finished(); + void UpdateVideo(); + + bool IsFinished(); + +private: + GstAppSink *Appsink; + GstElement *Element; + GstBus *Bus; + GstMessage *Message; + GstMapInfo Mapinfo; + + gboolean Loop; + + gboolean Running; + + gboolean Paused; + gboolean Finished; + + gfloat Duration; + + void RefreshDuration(); }; + diff --git a/Output/x64/gstreamer/GstreamerHelper.dll b/Output/x64/gstreamer/GstreamerHelper.dll index 22465ffd..92fa8881 100644 Binary files a/Output/x64/gstreamer/GstreamerHelper.dll and b/Output/x64/gstreamer/GstreamerHelper.dll differ diff --git a/Vocaluxe/Base/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index b85b2a9b..6433af02 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -319,6 +319,7 @@ namespace Vocaluxe.Base Mouse.CopyEvents(); Keys.CopyEvents(); + CVideo.Update(); CSound.Update(); CBackgroundMusic.Update(); CInput.Update(); diff --git a/Vocaluxe/Base/CVideo.cs b/Vocaluxe/Base/CVideo.cs index 766e90d2..811b5387 100644 --- a/Vocaluxe/Base/CVideo.cs +++ b/Vocaluxe/Base/CVideo.cs @@ -95,6 +95,11 @@ namespace Vocaluxe.Base return _VideoDecoder.Finished(StreamID); } + public static void Update() + { + _VideoDecoder.Update(); + } + #endregion Interface #endregion VideoDecoder diff --git a/Vocaluxe/Lib/Video/CVideoDecoder.cs b/Vocaluxe/Lib/Video/CVideoDecoder.cs index da4150db..52197a08 100644 --- a/Vocaluxe/Lib/Video/CVideoDecoder.cs +++ b/Vocaluxe/Lib/Video/CVideoDecoder.cs @@ -68,6 +68,10 @@ namespace Vocaluxe.Lib.Video return false; } + public virtual void Update() + { + } + protected bool AlreadyAdded(int StreamID) { foreach (VideoStreams st in _Streams) diff --git a/Vocaluxe/Lib/Video/CVideoDecoderGstreamer.cs b/Vocaluxe/Lib/Video/CVideoDecoderGstreamer.cs index 391a0f09..81db3594 100644 --- a/Vocaluxe/Lib/Video/CVideoDecoderGstreamer.cs +++ b/Vocaluxe/Lib/Video/CVideoDecoderGstreamer.cs @@ -1,15 +1,31 @@ using System; using System.Collections.Generic; using System.Text; +using Vocaluxe.Base; using Vocaluxe.Lib.Video.Gstreamer; +using Vocaluxe.Menu; namespace Vocaluxe.Lib.Video { class CVideoDecoderGstreamer : IVideoDecoder { + #region log + public CGstreamerVideoWrapper.LogCallback Log; + + private void LogHandler(string text) + { + CLog.LogError(text); + + } + #endregion log + public bool Init() { - return CGstreamerVideoWrapper.InitVideo(); + bool retval = CGstreamerVideoWrapper.InitVideo(); + Log = new CGstreamerVideoWrapper.LogCallback(LogHandler); + GC.SuppressFinalize(Log); + CGstreamerVideoWrapper.SetVideoLogCallback(Log); + return retval; } public void CloseAll() @@ -19,7 +35,15 @@ namespace Vocaluxe.Lib.Video public int Load(string VideoFileName) { - return CGstreamerVideoWrapper.LoadVideo(VideoFileName); + int i = -1; + try + { + Uri u = new Uri(VideoFileName); + i = CGstreamerVideoWrapper.LoadVideo(u.AbsoluteUri); + return i; + } + catch (Exception e) { } + return i; } public bool Close(int StreamID) @@ -37,9 +61,11 @@ namespace Vocaluxe.Lib.Video return CGstreamerVideoWrapper.GetVideoLength(StreamID); } - public bool GetFrame(int StreamID, ref Menu.STexture Frame, float Time, ref float VideoTime) + public bool GetFrame(int StreamID, ref STexture Frame, float Time, ref float VideoTime) { - IntPtr buffer = CGstreamerVideoWrapper.GetFrame(StreamID, Time, ref VideoTime); + //IntPtr buffer = CGstreamerVideoWrapper.GetFrame(StreamID, Time, ref VideoTime); + byte[] data = new byte[4 * 4 * 4]; + UploadNewFrame(ref Frame, ref data, 4, 4); return true; } @@ -67,5 +93,23 @@ namespace Vocaluxe.Lib.Video { return CGstreamerVideoWrapper.Finished(StreamID); } + + public void Update() + { + CGstreamerVideoWrapper.UpdateVideo(); + } + + private void UploadNewFrame(ref STexture frame, ref byte[] Data, int Width, int Height) + { + if (frame.index == -1 || Width != frame.width || Height != frame.height) + { + CDraw.RemoveTexture(ref frame); + frame = CDraw.AddTexture(Width, Height, ref Data); + } + else + { + CDraw.UpdateTexture(ref frame, ref Data); + } + } } } diff --git a/Vocaluxe/Lib/Video/Gstreamer/CGstreamerVideoWrapper.cs b/Vocaluxe/Lib/Video/Gstreamer/CGstreamerVideoWrapper.cs index ea9b775e..2a53d404 100644 --- a/Vocaluxe/Lib/Video/Gstreamer/CGstreamerVideoWrapper.cs +++ b/Vocaluxe/Lib/Video/Gstreamer/CGstreamerVideoWrapper.cs @@ -43,8 +43,16 @@ namespace Vocaluxe.Lib.Video.Gstreamer [DllImport(Dll)] public static extern float GetVideoLength(int StreamID); - [DllImport(Dll)] - public static extern IntPtr GetFrame(int StreamID, float Time, ref float VideoTime); + [DllImport(Dll, EntryPoint="GetFrame")] + public static extern IntPtr GetFrameNative(int StreamID, float Time, ref float VideoTime, ref int Size, ref int Width, ref int Height); + + public static byte[] GetFrame(int StreamID, float Time, ref float VideoTime, ref int Size, ref int Width, ref int Height) + { + IntPtr buf = GetFrameNative(StreamID, Time, ref VideoTime, ref Size, ref Width, ref Height); + byte[] buffer = new byte[Size]; + Marshal.Copy(buf, buffer, 0, Size); + return buffer; + } [DllImport(Dll)] public static extern bool Skip(int StreamID, float Start, float Gap); @@ -61,5 +69,8 @@ namespace Vocaluxe.Lib.Video.Gstreamer [DllImport(Dll)] public static extern bool Finished(int StreamID); + [DllImport(Dll)] + public static extern bool UpdateVideo(); + } } diff --git a/Vocaluxe/Lib/Video/IVideoDecoder.cs b/Vocaluxe/Lib/Video/IVideoDecoder.cs index 2f12f2b3..783e9a84 100644 --- a/Vocaluxe/Lib/Video/IVideoDecoder.cs +++ b/Vocaluxe/Lib/Video/IVideoDecoder.cs @@ -36,5 +36,7 @@ namespace Vocaluxe.Lib.Video void Resume(int StreamID); bool Finished(int StreamID); + + void Update(); } } diff --git a/gstreamerhelper/x64/lib/gstapp-1.0.lib b/gstreamerhelper/x64/lib/gstapp-1.0.lib new file mode 100644 index 00000000..9d5f770b Binary files /dev/null and b/gstreamerhelper/x64/lib/gstapp-1.0.lib differ