mirror of
https://github.com/FunKey-Project/RetroFE.git
synced 2026-03-20 19:02:42 +01:00
Port from git
This commit is contained in:
351
Source/Video/GStreamerVideo.cpp
Normal file
351
Source/Video/GStreamerVideo.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
/* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.txt', which is part of this source code package.
|
||||
*/
|
||||
#include "GStreamerVideo.h"
|
||||
#include "../Graphics/ViewInfo.h"
|
||||
#include "../Graphics/Component/Image.h"
|
||||
#include "../Database/Configuration.h"
|
||||
#include "../Utility/Log.h"
|
||||
#include "../SDL.h"
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <gst/app/gstappsink.h>
|
||||
|
||||
bool GStreamerVideo::Initialized = false;
|
||||
|
||||
//todo: this started out as sandbox code. This class needs to be refactored
|
||||
|
||||
// MUST match video size
|
||||
gboolean GStreamerVideo::BusCallback(GstBus *bus, GstMessage *msg, gpointer data)
|
||||
{
|
||||
// this callback only needs to be defined so we can loop the video once it completes
|
||||
return TRUE;
|
||||
}
|
||||
GStreamerVideo::GStreamerVideo()
|
||||
: Playbin(NULL)
|
||||
, VideoBin(NULL)
|
||||
, VideoSink(NULL)
|
||||
, VideoConvert(NULL)
|
||||
, VideoConvertCaps(NULL)
|
||||
, VideoBus(NULL)
|
||||
, Texture(NULL)
|
||||
, Height(0)
|
||||
, Width(0)
|
||||
, VideoBuffer(NULL)
|
||||
, FrameReady(false)
|
||||
, IsPlaying(false)
|
||||
, PlayCount(0)
|
||||
, NumLoops(0)
|
||||
{
|
||||
}
|
||||
GStreamerVideo::~GStreamerVideo()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void GStreamerVideo::SetNumLoops(int n)
|
||||
{
|
||||
NumLoops = n;
|
||||
}
|
||||
|
||||
SDL_Texture *GStreamerVideo::GetTexture() const
|
||||
{
|
||||
return Texture;
|
||||
}
|
||||
|
||||
void GStreamerVideo::ProcessNewBuffer (GstElement *fakesink, GstBuffer *buf, GstPad *new_pad, gpointer userdata)
|
||||
{
|
||||
GStreamerVideo *video = (GStreamerVideo *)userdata;
|
||||
GstMapInfo map;
|
||||
SDL_LockMutex(SDL::GetMutex());
|
||||
|
||||
if (!video->FrameReady && video && video->IsPlaying && gst_buffer_map (buf, &map, GST_MAP_READ))
|
||||
{
|
||||
if(!video->Width || !video->Height)
|
||||
{
|
||||
GstCaps *caps = gst_pad_get_current_caps (new_pad);
|
||||
GstStructure *s = gst_caps_get_structure(caps, 0);
|
||||
|
||||
gst_structure_get_int(s, "width", &video->Width);
|
||||
gst_structure_get_int(s, "height", &video->Height);
|
||||
}
|
||||
|
||||
if(video->Height && video->Width)
|
||||
{
|
||||
if(!video->VideoBuffer)
|
||||
{
|
||||
video->VideoBuffer = new char[map.size];
|
||||
}
|
||||
memcpy(video->VideoBuffer, map.data, map.size);
|
||||
gst_buffer_unmap(buf, &map);
|
||||
video->FrameReady = true;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(SDL::GetMutex());
|
||||
}
|
||||
|
||||
|
||||
bool GStreamerVideo::Initialize()
|
||||
{
|
||||
bool retVal = true;
|
||||
|
||||
std::string path = Configuration::GetAbsolutePath() + "/Core";
|
||||
gst_init(NULL, NULL);
|
||||
GstRegistry *registry = gst_registry_get();
|
||||
gst_registry_scan_path(registry, path.c_str());
|
||||
|
||||
Initialized = true;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool GStreamerVideo::DeInitialize()
|
||||
{
|
||||
gst_deinit();
|
||||
Initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool GStreamerVideo::Stop()
|
||||
{
|
||||
if(!Initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(VideoSink)
|
||||
{
|
||||
g_object_set(G_OBJECT(VideoSink), "signal-handoffs", FALSE, NULL);
|
||||
}
|
||||
|
||||
if(Playbin)
|
||||
{
|
||||
(void)gst_element_set_state(Playbin, GST_STATE_NULL);
|
||||
}
|
||||
|
||||
FreeElements();
|
||||
|
||||
IsPlaying = false;
|
||||
|
||||
if(Texture)
|
||||
{
|
||||
SDL_DestroyTexture(Texture);
|
||||
Texture = NULL;
|
||||
}
|
||||
IsPlaying = false;
|
||||
Height = 0;
|
||||
Width = 0;
|
||||
FrameReady = false;
|
||||
|
||||
if(VideoBuffer)
|
||||
{
|
||||
delete VideoBuffer;
|
||||
VideoBuffer = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GStreamerVideo::Play(std::string file)
|
||||
{
|
||||
PlayCount = 0;
|
||||
|
||||
if(!Initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentFile = file;
|
||||
|
||||
const gchar *uriFile = gst_filename_to_uri (file.c_str(), NULL);
|
||||
if(!uriFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Configuration::ConvertToAbsolutePath(Configuration::GetAbsolutePath(), file);
|
||||
file = uriFile;
|
||||
|
||||
// Pipeline = gst_pipeline_new("pipeline");
|
||||
Playbin = gst_element_factory_make("playbin", "player");
|
||||
VideoBin = gst_bin_new("SinkBin");
|
||||
VideoSink = gst_element_factory_make("fakesink", "video_sink");
|
||||
VideoConvert = gst_element_factory_make("capsfilter", "video_convert");
|
||||
VideoConvertCaps = gst_caps_from_string("video/x-raw,format=(string)YUY2");
|
||||
Height = 0;
|
||||
Width = 0;
|
||||
if(!Playbin)
|
||||
{
|
||||
Logger::Write(Logger::ZONE_DEBUG, "Video", "Could not create playbin");
|
||||
FreeElements();
|
||||
return false;
|
||||
}
|
||||
if(!VideoSink)
|
||||
{
|
||||
Logger::Write(Logger::ZONE_DEBUG, "Video", "Could not create video sink");
|
||||
FreeElements();
|
||||
return false;
|
||||
}
|
||||
if(!VideoConvert)
|
||||
{
|
||||
Logger::Write(Logger::ZONE_DEBUG, "Video", "Could not create video converter");
|
||||
FreeElements();
|
||||
return false;
|
||||
}
|
||||
if(!VideoConvertCaps)
|
||||
{
|
||||
Logger::Write(Logger::ZONE_DEBUG, "Video", "Could not create video caps");
|
||||
FreeElements();
|
||||
return false;
|
||||
}
|
||||
|
||||
gst_bin_add_many(GST_BIN(VideoBin), VideoConvert, VideoSink, NULL);
|
||||
gst_element_link_filtered(VideoConvert, VideoSink, VideoConvertCaps);
|
||||
GstPad *videoConvertSinkPad = gst_element_get_static_pad(VideoConvert, "sink");
|
||||
|
||||
if(!videoConvertSinkPad)
|
||||
{
|
||||
Logger::Write(Logger::ZONE_DEBUG, "Video", "Could not get video convert sink pad");
|
||||
FreeElements();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
g_object_set(G_OBJECT(VideoSink), "sync", TRUE, "qos", TRUE, NULL);
|
||||
|
||||
GstPad *videoSinkPad = gst_ghost_pad_new("sink", videoConvertSinkPad);
|
||||
if(!videoSinkPad)
|
||||
{
|
||||
Logger::Write(Logger::ZONE_DEBUG, "Video", "Could not get video bin sink pad");
|
||||
FreeElements();
|
||||
gst_object_unref(videoConvertSinkPad);
|
||||
videoConvertSinkPad = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
gst_element_add_pad(VideoBin, videoSinkPad);
|
||||
gst_object_unref(videoConvertSinkPad);
|
||||
videoConvertSinkPad = NULL;
|
||||
|
||||
g_object_set(G_OBJECT(Playbin), "uri", file.c_str(), "video-sink", VideoBin, NULL);
|
||||
|
||||
IsPlaying = true;
|
||||
|
||||
|
||||
g_object_set(G_OBJECT(VideoSink), "signal-handoffs", TRUE, NULL);
|
||||
g_signal_connect(VideoSink, "handoff", G_CALLBACK(ProcessNewBuffer), this);
|
||||
|
||||
VideoBus = gst_pipeline_get_bus(GST_PIPELINE(Playbin));
|
||||
gst_bus_add_watch(VideoBus, &BusCallback, this);
|
||||
|
||||
/* Start playing */
|
||||
GstStateChangeReturn playState = gst_element_set_state(GST_ELEMENT(Playbin), GST_STATE_PLAYING);
|
||||
if (playState != GST_STATE_CHANGE_ASYNC)
|
||||
{
|
||||
IsPlaying = false;
|
||||
std::stringstream ss;
|
||||
ss << "Unable to set the pipeline to the playing state: ";
|
||||
ss << playState;
|
||||
Logger::Write(Logger::ZONE_ERROR, "Video", ss.str());
|
||||
FreeElements();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GStreamerVideo::FreeElements()
|
||||
{
|
||||
if(VideoBin)
|
||||
{
|
||||
gst_object_unref(VideoBin);
|
||||
VideoBin = NULL;
|
||||
}
|
||||
if(VideoSink)
|
||||
{
|
||||
gst_object_unref(VideoSink);
|
||||
VideoSink = NULL;
|
||||
}
|
||||
if(VideoConvert)
|
||||
{
|
||||
gst_object_unref(VideoConvert);
|
||||
VideoConvert = NULL;
|
||||
}
|
||||
if(VideoConvertCaps)
|
||||
{
|
||||
gst_object_unref(VideoConvertCaps);
|
||||
VideoConvertCaps = NULL;
|
||||
}
|
||||
if(Playbin)
|
||||
{
|
||||
gst_object_unref(Playbin);
|
||||
Playbin = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GStreamerVideo::Draw()
|
||||
{
|
||||
FrameReady = false;
|
||||
}
|
||||
|
||||
void GStreamerVideo::Update(float dt)
|
||||
{
|
||||
SDL_LockMutex(SDL::GetMutex());
|
||||
if(!Texture && Width != 0 && Height != 0)
|
||||
{
|
||||
Texture = SDL_CreateTexture(SDL::GetRenderer(), SDL_PIXELFORMAT_YUY2,
|
||||
SDL_TEXTUREACCESS_STREAMING, Width, Height);
|
||||
SDL_SetTextureBlendMode(Texture, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
|
||||
if(VideoBuffer && FrameReady && Texture && Width && Height)
|
||||
{
|
||||
//todo: change to width of cap
|
||||
void *pixels;
|
||||
int pitch;
|
||||
SDL_LockTexture(Texture, NULL, &pixels, &pitch);
|
||||
memcpy(pixels, VideoBuffer, Width*Height*2); //todo: magic number
|
||||
SDL_UnlockTexture(Texture);
|
||||
}
|
||||
SDL_UnlockMutex(SDL::GetMutex());
|
||||
|
||||
|
||||
if(VideoBus)
|
||||
{
|
||||
GstMessage *msg = gst_bus_pop(VideoBus);
|
||||
if(msg)
|
||||
{
|
||||
if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_EOS)
|
||||
{
|
||||
Logger::Write(Logger::ZONE_ERROR, "Video", "EOS!");
|
||||
|
||||
PlayCount++;
|
||||
|
||||
//todo: nesting hazard
|
||||
// if number of loops is 0, set to infinite (todo: this is misleading, rename variable)
|
||||
if(!NumLoops || NumLoops > PlayCount)
|
||||
{
|
||||
gst_element_seek(Playbin,
|
||||
1.0,
|
||||
GST_FORMAT_TIME,
|
||||
GST_SEEK_FLAG_FLUSH,
|
||||
GST_SEEK_TYPE_SET,
|
||||
0,
|
||||
GST_SEEK_TYPE_NONE,
|
||||
GST_CLOCK_TIME_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
gst_message_unref(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
50
Source/Video/GStreamerVideo.h
Normal file
50
Source/Video/GStreamerVideo.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.txt', which is part of this source code package.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "IVideo.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <gst/gst.h>
|
||||
#include <gst/app/gstappsink.h>
|
||||
}
|
||||
|
||||
|
||||
class GStreamerVideo : public IVideo
|
||||
{
|
||||
public:
|
||||
GStreamerVideo();
|
||||
~GStreamerVideo();
|
||||
bool Initialize();
|
||||
bool Play(std::string file);
|
||||
bool Stop();
|
||||
bool DeInitialize();
|
||||
SDL_Texture *GetTexture() const;
|
||||
void Update(float dt);
|
||||
void Draw();
|
||||
void SetNumLoops(int n);
|
||||
void FreeElements();
|
||||
|
||||
private:
|
||||
static void ProcessNewBuffer (GstElement *fakesink, GstBuffer *buf, GstPad *pad, gpointer data);
|
||||
static gboolean BusCallback(GstBus *bus, GstMessage *msg, gpointer data);
|
||||
|
||||
GstElement *Playbin;
|
||||
GstElement *VideoBin;
|
||||
GstElement *VideoSink;
|
||||
GstElement *VideoConvert;
|
||||
GstCaps *VideoConvertCaps;
|
||||
GstBus *VideoBus;
|
||||
SDL_Texture* Texture;
|
||||
gint Height;
|
||||
gint Width;
|
||||
char *VideoBuffer;
|
||||
bool FrameReady;
|
||||
bool IsPlaying;
|
||||
static bool Initialized;
|
||||
int PlayCount;
|
||||
std::string CurrentFile;
|
||||
int NumLoops;
|
||||
};
|
||||
20
Source/Video/IVideo.h
Normal file
20
Source/Video/IVideo.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.txt', which is part of this source code package.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <string>
|
||||
|
||||
class IVideo
|
||||
{
|
||||
public:
|
||||
virtual ~IVideo() {}
|
||||
virtual bool Initialize() = 0;
|
||||
virtual bool Play(std::string file) = 0;
|
||||
virtual bool Stop() = 0;
|
||||
virtual bool DeInitialize() = 0;
|
||||
virtual SDL_Texture *GetTexture() const = 0;
|
||||
virtual void Update(float dt) = 0;
|
||||
virtual void Draw() = 0;
|
||||
};
|
||||
33
Source/Video/VideoFactory.cpp
Normal file
33
Source/Video/VideoFactory.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.txt', which is part of this source code package.
|
||||
*/
|
||||
#include "VideoFactory.h"
|
||||
#include "IVideo.h"
|
||||
#include "GStreamerVideo.h"
|
||||
|
||||
bool VideoFactory::Enabled = true;
|
||||
int VideoFactory::NumLoops = 0;
|
||||
|
||||
IVideo *VideoFactory::CreateVideo()
|
||||
{
|
||||
IVideo *instance = NULL;
|
||||
|
||||
if(Enabled)
|
||||
{
|
||||
instance = new GStreamerVideo();
|
||||
instance->Initialize();
|
||||
((GStreamerVideo *)(instance))->SetNumLoops(NumLoops);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void VideoFactory::SetEnabled(bool enabled)
|
||||
{
|
||||
Enabled = enabled;
|
||||
}
|
||||
|
||||
void VideoFactory::SetNumLoops(int numLoops)
|
||||
{
|
||||
NumLoops = numLoops;
|
||||
}
|
||||
18
Source/Video/VideoFactory.h
Normal file
18
Source/Video/VideoFactory.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.txt', which is part of this source code package.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class IVideo;
|
||||
|
||||
class VideoFactory
|
||||
{
|
||||
public:
|
||||
IVideo *CreateVideo();
|
||||
static void SetEnabled(bool enabled);
|
||||
static void SetNumLoops(int numLoops);
|
||||
|
||||
private:
|
||||
static bool Enabled;
|
||||
static int NumLoops;
|
||||
};
|
||||
Reference in New Issue
Block a user