297 lines
6.5 KiB
C++
297 lines
6.5 KiB
C++
#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;
|
|
Copying = true;
|
|
|
|
BufferFull = false;
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
FrameBuffer[i].Sample = NULL;
|
|
FrameBuffer[i].Buffer = NULL;
|
|
FrameBuffer[i].Displayed = true;
|
|
}
|
|
|
|
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 || !Appsink)
|
|
{
|
|
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);
|
|
g_object_set (Appsink, "emit-signals", FALSE, "sync", FALSE, NULL);
|
|
|
|
GstCaps *caps;
|
|
caps = gst_caps_new_simple ("video/x-raw",
|
|
"format", G_TYPE_STRING, "BGRA", //We should use RGB for better performance here! Implement later
|
|
NULL);
|
|
|
|
gst_app_sink_set_caps(Appsink, caps);
|
|
|
|
gst_app_sink_set_drop(Appsink, false);
|
|
gst_app_sink_set_max_buffers(Appsink, 5);
|
|
|
|
gst_element_set_state(Element, GST_STATE_PLAYING);
|
|
//gst_element_set_state(GST_ELEMENT (Appsink), GST_STATE_PLAYING);
|
|
gst_bus_timed_pop_filtered(Bus, -1, GST_MESSAGE_ASYNC_DONE);
|
|
CopyThread = thread (&GstreamerVideoStream::Copy, this);
|
|
|
|
RefreshDuration();
|
|
|
|
return ID;
|
|
}
|
|
|
|
void GstreamerVideoStream::Copy()
|
|
{
|
|
while(Copying)
|
|
{
|
|
int num = -1;
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
if(FrameBuffer[i].Displayed)
|
|
{
|
|
|
|
num = i;
|
|
GstSample *sample;
|
|
sample = gst_app_sink_pull_sample(Appsink);
|
|
Mutex.lock();
|
|
if(FrameBuffer[i].Buffer)
|
|
{
|
|
if(FrameBuffer[i].Memory.data)
|
|
gst_buffer_unmap(FrameBuffer[i].Buffer, &FrameBuffer[i].Memory);
|
|
gst_buffer_unref(FrameBuffer[i].Buffer);
|
|
}
|
|
//if(FrameBuffer[i].Sample)
|
|
//gst_sample_unref(FrameBuffer[i].Sample);
|
|
|
|
|
|
FrameBuffer[i].Displayed = false;
|
|
FrameBuffer[i].Sample = sample;
|
|
Mutex.unlock();
|
|
|
|
//gst_element_send_event (Element, gst_event_new_step (GST_FORMAT_BUFFERS, 1, 1.0, TRUE, FALSE));
|
|
break;
|
|
}
|
|
}
|
|
if(num == -1)
|
|
BufferFull = true;
|
|
}
|
|
}
|
|
|
|
bool GstreamerVideoStream::CloseVideo()
|
|
{
|
|
Copying = false;
|
|
CopyThread.join();
|
|
|
|
for(int i= 0; i< 5; i++)
|
|
{
|
|
if(FrameBuffer[i].Buffer)
|
|
{
|
|
if(FrameBuffer[i].Memory.data)
|
|
gst_buffer_unmap(FrameBuffer[i].Buffer, &FrameBuffer[i].Memory);
|
|
gst_buffer_unref(FrameBuffer[i].Buffer);
|
|
}
|
|
//if(FrameBuffer[i].Sample)
|
|
//gst_sample_unref(FrameBuffer[i].Sample);
|
|
}
|
|
|
|
if(Element)
|
|
gst_element_set_state(Element, GST_STATE_NULL);
|
|
|
|
if(Appsink)
|
|
gst_object_unref(Appsink);
|
|
if(Bus)
|
|
gst_object_unref(Bus);
|
|
//if(Buffer && &Mapinfo)
|
|
//gst_buffer_unmap(Buffer, &Mapinfo);
|
|
|
|
|
|
if(Element)
|
|
gst_object_unref(Element);
|
|
return true;
|
|
}
|
|
|
|
float GstreamerVideoStream::GetVideoLength()
|
|
{
|
|
if(Duration <= 0)
|
|
RefreshDuration();
|
|
return Duration;
|
|
}
|
|
|
|
struct ApplicationFrame GstreamerVideoStream::GetFrame(float Time)
|
|
{
|
|
float VideoTime = 0.0;
|
|
int Width = 0;
|
|
int Height = 0;
|
|
int Size = -1;
|
|
|
|
Mutex.lock();
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
if(!FrameBuffer[i].Displayed)
|
|
{
|
|
if(FrameBuffer[i].Sample) {
|
|
GstBuffer *Buffer = gst_sample_get_buffer(FrameBuffer[i].Sample);
|
|
GstMapInfo Mapinfo;
|
|
|
|
GstCaps *BufferCaps = gst_sample_get_caps (FrameBuffer[i].Sample);
|
|
GstStructure *BufferStructure = gst_caps_get_structure (BufferCaps, 0);
|
|
|
|
gst_buffer_map(Buffer, &Mapinfo, GST_MAP_READ);
|
|
|
|
Size = (int) Mapinfo.size;
|
|
gst_structure_get_int (BufferStructure, "width", &Width);
|
|
gst_structure_get_int (BufferStructure, "height", &Height);
|
|
VideoTime = (float) (Buffer->pts / GST_SECOND);
|
|
|
|
ReturnFrame.data = Mapinfo.data;
|
|
ReturnFrame.height = Height;
|
|
ReturnFrame.width = Width;
|
|
ReturnFrame.size = Size;
|
|
ReturnFrame.videotime = VideoTime;
|
|
|
|
FrameBuffer[i].Memory = Mapinfo;
|
|
FrameBuffer[i].Buffer = Buffer;
|
|
|
|
if(BufferCaps)
|
|
gst_caps_unref(BufferCaps);
|
|
|
|
if(BufferStructure)
|
|
gst_object_unref(BufferStructure);
|
|
}
|
|
FrameBuffer[i].Displayed = true;
|
|
break;
|
|
}
|
|
}
|
|
Mutex.unlock();
|
|
return ReturnFrame;
|
|
}
|
|
|
|
bool GstreamerVideoStream::Skip(float Start, float Gap)
|
|
{
|
|
if(!gst_element_seek_simple(Element, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE), (gint64)((Start + Gap) * GST_SECOND)))
|
|
{
|
|
LogVideoError("Seek failed");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void GstreamerVideoStream::SetVideoLoop(bool Loop)
|
|
{
|
|
if(Loop)
|
|
g_object_set(Element, "sync", TRUE, NULL);
|
|
else
|
|
g_object_set(Element, "sync", FALSE, NULL);
|
|
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::IsFinished()
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
} |