Files
Vocaluxe/GstreamerHelper/GstreamerAudioStream.cpp
T

331 lines
7.3 KiB
C++

#include "stdafx.h"
#include "GstreamerAudioStream.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;
GstreamerAudioStream::GstreamerAudioStream(int id)
{
ID = id;
MaxVolume = 1.0;
Volume = 1.0;
Duration = -1;
Loop = false;
Running = true;
Finished = false;
//Fading
FadeTimer = g_timer_new();
FadeTime = 0.0;
TargetVolume = 1.0;
StartVolume = 1.0;
CloseStreamAfterFade = false;
PauseStreamAfterFade = false;
Fading = false;
Closed = false;
}
GstreamerAudioStream::~GstreamerAudioStream(void)
{
Close();
}
// Loads a stream but does not wait for it to get initialized,
// so querying duration or position directly after initialization might
// return strange values! To work around this use the prescan load function!
int GstreamerAudioStream::Load(const wchar_t* Media)
{
Element = gst_element_factory_make("playbin", "playbin");
Convert = gst_element_factory_make("audioconvert", "audioconvert");
Audiosink = gst_element_factory_make("directsoundsink", "directsoundsink");
SinkBin = gst_bin_new("SinkBin");
gst_bin_add_many (GST_BIN (SinkBin), Convert, Audiosink, NULL);
gst_element_link_many (Convert, Audiosink, NULL);
Pad = gst_element_get_static_pad (Convert, "sink");
GhostPad = gst_ghost_pad_new ("sink", Pad);
gst_pad_set_active (GhostPad, TRUE);
gst_element_add_pad (SinkBin, GhostPad);
Bus = gst_element_get_bus(Element);
if(!Element)
{
LogError ("Could not create element!");
return -1;
}
Running = true;
g_object_set(Element, "uri", Media, NULL);
g_object_set(Element, "audio-sink", SinkBin, NULL);
g_object_set(Element, "flags", GST_PLAY_FLAG_AUDIO, NULL);
gst_element_set_state(Element, GST_STATE_PAUSED);
//gst_bus_timed_pop_filtered(Bus, -1, GST_MESSAGE_ASYNC_DONE);
RefreshDuration();
return ID;
}
//Loads a stream and waits for its initialization
int GstreamerAudioStream::Load(const wchar_t* Media, bool Prescan)
{
Load(Media);
if(Prescan)
gst_bus_timed_pop_filtered(Bus, -1, GST_MESSAGE_ASYNC_DONE);
return ID;
}
void GstreamerAudioStream::Close(void)
{
if(Element)
gst_element_set_state(Element, GST_STATE_NULL);
if(Bus)
gst_object_unref(Bus);
if(Element)
gst_object_unref(Element);
if(Message)
gst_message_unref(Message);
if(Convert)
gst_object_unref(Convert);
if(Audiosink)
gst_object_unref(Audiosink);
if(Pad)
gst_object_unref(Pad);
if(GhostPad)
gst_object_unref(GhostPad);
if(SinkBin)
gst_object_unref(SinkBin);
if(FadeTimer)
g_timer_destroy (FadeTimer);
Running = false;
Closed = true;
}
void GstreamerAudioStream::Play(void)
{
if(Element)
{
PauseStreamAfterFade = false;
Running = true;
GstStateChangeReturn ret;
if(Element)
ret = gst_element_set_state(Element, GST_STATE_PLAYING);
if(ret == GST_STATE_CHANGE_ASYNC)
gst_bus_timed_pop_filtered(Bus, -1, GST_MESSAGE_ASYNC_DONE);
SetStreamVolume(Volume * 100.0);
}
}
void GstreamerAudioStream::Play(bool Loop)
{
this->Loop = Loop;
Play();
}
void GstreamerAudioStream::Pause()
{
Running = true;
if(Element)
gst_element_set_state(Element, GST_STATE_PAUSED);
SetStreamVolume(Volume * 100.0);
}
void GstreamerAudioStream::Stop()
{
if(Element)
{
gst_element_set_state(Element, GST_STATE_NULL);
gst_element_seek_simple(Element, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, 0);
}
}
void GstreamerAudioStream::SetStreamVolume(float Volume)
{
this->Volume = Volume / 100.0;
if(Element)
{
g_object_set(this->Element, "volume", (gdouble)(Volume / 100.0) * MaxVolume, NULL);
}
}
void GstreamerAudioStream::SetStreamVolumeMax(float MaxVolume)
{
this->MaxVolume = (gdouble) (MaxVolume / 100.0);
if(Element)
{
g_object_set(this->Element, "volume", Volume * this->MaxVolume, NULL);
}
}
float GstreamerAudioStream::GetLength()
{
if(Duration <= 0)
RefreshDuration();
return Duration;
}
void GstreamerAudioStream::RefreshDuration()
{
if(Element)
{
gint64 time;
if(!gst_element_query_duration(Element, GST_FORMAT_TIME, &time)) {
LogError("Could not query duration");
}
Duration = (gfloat)((gdouble)(time/GST_SECOND));
}
}
float GstreamerAudioStream::GetPosition()
{
if(Element)
{
gint64 time;
gst_element_query_position(Element, GST_FORMAT_TIME, &time);
return (float)((gdouble)time/GST_SECOND);
}
else return -1;
}
void GstreamerAudioStream::SetPosition(float Position)
{
if(Element)
{
//Not sure if we need GST_SEEK_FLAG_ACCURATE
if(!gst_element_seek_simple(Element, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE), Position * GST_SECOND))
LogError("Seek failed");
}
}
void GstreamerAudioStream::Fade(float TargetVolume, float Seconds)
{
Fading = true;
g_timer_stop(FadeTimer);
StartVolume = Volume;
this->TargetVolume = TargetVolume / 100.0;
FadeTime = (gdouble)Seconds;
g_timer_start(FadeTimer);
}
void GstreamerAudioStream::FadeAndPause(float TargetVolume, float Seconds)
{
PauseStreamAfterFade = true;
Fade(TargetVolume, Seconds);
}
void GstreamerAudioStream::FadeAndStop(float TargetVolume, float Seconds)
{
CloseStreamAfterFade = true;
Fade(TargetVolume, Seconds);
}
bool GstreamerAudioStream::IsPlaying()
{
GstStateChangeReturn ret;
GstState current, pending;
//This might block!!!
ret = gst_element_get_state (Element, &current, &pending, GST_CLOCK_TIME_NONE);
return current == GST_STATE_PLAYING && !Finished;
}
bool GstreamerAudioStream::IsPaused()
{
GstStateChangeReturn ret;
GstState current, pending;
//This might block!!!
ret = gst_element_get_state (Element, &current, &pending, GST_CLOCK_TIME_NONE);
return current == GST_STATE_PAUSED && !Finished;
}
bool GstreamerAudioStream::IsFinished()
{
return Finished;
}
void GstreamerAudioStream::UpdateVolume()
{
if (Fading)
{
if (g_timer_elapsed(FadeTimer, NULL) < FadeTime) {
gdouble vol = (StartVolume + (TargetVolume - StartVolume) * (g_timer_elapsed(FadeTimer, NULL) / FadeTime));
SetStreamVolume(100.0 * vol);
}
else
{
SetStreamVolume (TargetVolume * 100.0);
g_timer_stop(FadeTimer);
Fading = false;
if (CloseStreamAfterFade)
{
Closed = true;
}
if (PauseStreamAfterFade)
Pause();
}
}
}
void GstreamerAudioStream::Update()
{
if(Running) {
UpdateVolume();
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;
LogError(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)
SetPosition(0);
else
Finished = true;
break;
default:
/* We should not reach here */
//LogError ("Unexpected message received.\n");
break;
}
gst_message_unref (Message);
}
}
}