mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
Add seeking support to mmssrc. Fixes bug #469930.
Add proper seeking support to mmssrc and clean up some code. This requires libmms >= 0.4.
This commit is contained in:
parent
eac03fc77e
commit
20b715ac79
3 changed files with 200 additions and 62 deletions
|
@ -820,7 +820,7 @@ dnl *** libmms ***
|
||||||
translit(dnm, m, l) AM_CONDITIONAL(USE_LIBMMS, true)
|
translit(dnm, m, l) AM_CONDITIONAL(USE_LIBMMS, true)
|
||||||
AG_GST_CHECK_FEATURE(LIBMMS, [mms protocol library], libmms, [
|
AG_GST_CHECK_FEATURE(LIBMMS, [mms protocol library], libmms, [
|
||||||
dnl check with pkg-config first
|
dnl check with pkg-config first
|
||||||
PKG_CHECK_MODULES(LIBMMS, libmms >= 0.2, HAVE_LIBMMS="yes", [
|
PKG_CHECK_MODULES(LIBMMS, libmms >= 0.4, HAVE_LIBMMS="yes", [
|
||||||
HAVE_LIBMMS="no"
|
HAVE_LIBMMS="no"
|
||||||
AC_MSG_RESULT(no)
|
AC_MSG_RESULT(no)
|
||||||
])
|
])
|
||||||
|
|
|
@ -65,6 +65,12 @@ static gboolean gst_mms_src_query (GstPad * pad, GstQuery * query);
|
||||||
|
|
||||||
static gboolean gst_mms_start (GstBaseSrc * bsrc);
|
static gboolean gst_mms_start (GstBaseSrc * bsrc);
|
||||||
static gboolean gst_mms_stop (GstBaseSrc * bsrc);
|
static gboolean gst_mms_stop (GstBaseSrc * bsrc);
|
||||||
|
static gboolean gst_mms_is_seekable (GstBaseSrc * src);
|
||||||
|
static gboolean gst_mms_get_size (GstBaseSrc * src, guint64 * size);
|
||||||
|
static gboolean gst_mms_prepare_seek_segment (GstBaseSrc * src,
|
||||||
|
GstEvent * event, GstSegment * segment);
|
||||||
|
static gboolean gst_mms_do_seek (GstBaseSrc * src, GstSegment * segment);
|
||||||
|
|
||||||
static GstFlowReturn gst_mms_create (GstPushSrc * psrc, GstBuffer ** buf);
|
static GstFlowReturn gst_mms_create (GstPushSrc * psrc, GstBuffer ** buf);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -127,6 +133,11 @@ gst_mms_class_init (GstMMSClass * klass)
|
||||||
|
|
||||||
gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_mms_create);
|
gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_mms_create);
|
||||||
|
|
||||||
|
gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_mms_is_seekable);
|
||||||
|
gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_mms_get_size);
|
||||||
|
gstbasesrc_class->prepare_seek_segment =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_mms_prepare_seek_segment);
|
||||||
|
gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_mms_do_seek);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize the new element
|
/* initialize the new element
|
||||||
|
@ -143,10 +154,9 @@ gst_mms_init (GstMMS * mmssrc, GstMMSClass * g_class)
|
||||||
GST_DEBUG_FUNCPTR (gst_mms_get_query_types));
|
GST_DEBUG_FUNCPTR (gst_mms_get_query_types));
|
||||||
|
|
||||||
mmssrc->uri_name = NULL;
|
mmssrc->uri_name = NULL;
|
||||||
|
mmssrc->current_connection_uri_name = NULL;
|
||||||
mmssrc->connection = NULL;
|
mmssrc->connection = NULL;
|
||||||
mmssrc->connection_h = NULL;
|
|
||||||
mmssrc->connection_speed = DEFAULT_CONNECTION_SPEED;
|
mmssrc->connection_speed = DEFAULT_CONNECTION_SPEED;
|
||||||
GST_BASE_SRC (mmssrc)->blocksize = 2048;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -154,6 +164,18 @@ gst_mms_finalize (GObject * gobject)
|
||||||
{
|
{
|
||||||
GstMMS *mmssrc = GST_MMS (gobject);
|
GstMMS *mmssrc = GST_MMS (gobject);
|
||||||
|
|
||||||
|
/* We may still have a connection open, as we preserve unused / pristine
|
||||||
|
open connections in stop to reuse them in start. */
|
||||||
|
if (mmssrc->connection) {
|
||||||
|
mmsx_close (mmssrc->connection);
|
||||||
|
mmssrc->connection = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mmssrc->current_connection_uri_name) {
|
||||||
|
g_free (mmssrc->current_connection_uri_name);
|
||||||
|
mmssrc->current_connection_uri_name = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (mmssrc->uri_name) {
|
if (mmssrc->uri_name) {
|
||||||
g_free (mmssrc->uri_name);
|
g_free (mmssrc->uri_name);
|
||||||
mmssrc->uri_name = NULL;
|
mmssrc->uri_name = NULL;
|
||||||
|
@ -196,26 +218,28 @@ gst_mms_src_query (GstPad * pad, GstQuery * query)
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (mmssrc->connection) {
|
value = (gint64) mmsx_get_current_pos (mmssrc->connection);
|
||||||
value = (gint64) mms_get_current_pos (mmssrc->connection);
|
|
||||||
} else {
|
|
||||||
value = (gint64) mmsh_get_current_pos (mmssrc->connection_h);
|
|
||||||
}
|
|
||||||
gst_query_set_position (query, format, value);
|
gst_query_set_position (query, format, value);
|
||||||
break;
|
break;
|
||||||
case GST_QUERY_DURATION:
|
case GST_QUERY_DURATION:
|
||||||
gst_query_parse_duration (query, &format, &value);
|
if (!mmsx_get_seekable (mmssrc->connection)) {
|
||||||
if (format != GST_FORMAT_BYTES) {
|
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (mmssrc->connection) {
|
gst_query_parse_duration (query, &format, &value);
|
||||||
value = (gint64) mms_get_length (mmssrc->connection);
|
switch (format) {
|
||||||
} else {
|
case GST_FORMAT_BYTES:
|
||||||
value = (gint64) mmsh_get_length (mmssrc->connection_h);
|
value = (gint64) mmsx_get_length (mmssrc->connection);
|
||||||
}
|
|
||||||
gst_query_set_duration (query, format, value);
|
gst_query_set_duration (query, format, value);
|
||||||
break;
|
break;
|
||||||
|
case GST_FORMAT_TIME:
|
||||||
|
value = mmsx_get_time_length (mmssrc->connection) * GST_SECOND;
|
||||||
|
gst_query_set_duration (query, format, value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
res = FALSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
break;
|
break;
|
||||||
|
@ -226,6 +250,89 @@ gst_mms_src_query (GstPad * pad, GstQuery * query)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_mms_prepare_seek_segment (GstBaseSrc * src, GstEvent * event,
|
||||||
|
GstSegment * segment)
|
||||||
|
{
|
||||||
|
GstSeekType cur_type, stop_type;
|
||||||
|
gint64 cur, stop;
|
||||||
|
GstSeekFlags flags;
|
||||||
|
GstFormat seek_format;
|
||||||
|
gdouble rate;
|
||||||
|
GstMMS *mmssrc = GST_MMS (src);
|
||||||
|
|
||||||
|
gst_event_parse_seek (event, &rate, &seek_format, &flags,
|
||||||
|
&cur_type, &cur, &stop_type, &stop);
|
||||||
|
|
||||||
|
if (seek_format != GST_FORMAT_BYTES && seek_format != GST_FORMAT_TIME) {
|
||||||
|
GST_LOG_OBJECT (mmssrc, "Only byte or time seeking is supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stop_type != GST_SEEK_TYPE_NONE) {
|
||||||
|
GST_LOG_OBJECT (mmssrc, "Stop seeking not supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_type != GST_SEEK_TYPE_NONE && cur_type != GST_SEEK_TYPE_SET) {
|
||||||
|
GST_LOG_OBJECT (mmssrc, "Only absolute seeking is supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We would like to convert from GST_FORMAT_TIME to GST_FORMAT_BYTES here
|
||||||
|
when needed, but we cannot as to do that we need to actually do the seek,
|
||||||
|
so we handle this in do_seek instead. */
|
||||||
|
|
||||||
|
/* FIXME implement relative seeking, we could do any needed relevant
|
||||||
|
seeking calculations here (in seek_format metrics), before the re-init
|
||||||
|
of the segment. */
|
||||||
|
|
||||||
|
gst_segment_init (segment, seek_format);
|
||||||
|
gst_segment_set_seek (segment, rate, seek_format, flags, cur_type, cur,
|
||||||
|
stop_type, stop, NULL);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_mms_do_seek (GstBaseSrc * src, GstSegment * segment)
|
||||||
|
{
|
||||||
|
mms_off_t start;
|
||||||
|
GstMMS *mmssrc = GST_MMS (src);
|
||||||
|
|
||||||
|
if (segment->format == GST_FORMAT_TIME) {
|
||||||
|
if (!mmsx_time_seek (NULL, mmssrc->connection,
|
||||||
|
(double) segment->start / GST_SECOND)) {
|
||||||
|
GST_LOG_OBJECT (mmssrc, "mmsx_time_seek() failed");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
start = mmsx_get_current_pos (mmssrc->connection);
|
||||||
|
GST_LOG_OBJECT (mmssrc, "sought to %f sec, offset after seek: %lld\n",
|
||||||
|
(double) segment->start / GST_SECOND, start);
|
||||||
|
} else if (segment->format == GST_FORMAT_BYTES) {
|
||||||
|
start = mmsx_seek (NULL, mmssrc->connection, segment->start, SEEK_SET);
|
||||||
|
/* mmsx_seek will close and reopen the connection when seeking with the
|
||||||
|
mmsh protocol, if the reopening fails this is indicated with -1 */
|
||||||
|
if (start == -1) {
|
||||||
|
GST_DEBUG_OBJECT (mmssrc, "connection broken during seek");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
GST_DEBUG_OBJECT (mmssrc, "sought to: %llu bytes, result: %lld",
|
||||||
|
segment->start, start);
|
||||||
|
} else {
|
||||||
|
GST_DEBUG_OBJECT (mmssrc, "unsupported seek segment format: %d",
|
||||||
|
(int) segment->format);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
gst_segment_init (segment, GST_FORMAT_BYTES);
|
||||||
|
gst_segment_set_seek (segment, segment->rate, GST_FORMAT_BYTES,
|
||||||
|
segment->flags, GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_NONE,
|
||||||
|
segment->stop, NULL);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* get function
|
/* get function
|
||||||
* this function generates new data when needed
|
* this function generates new data when needed
|
||||||
*/
|
*/
|
||||||
|
@ -238,35 +345,39 @@ gst_mms_create (GstPushSrc * psrc, GstBuffer ** buf)
|
||||||
guint8 *data;
|
guint8 *data;
|
||||||
guint blocksize;
|
guint blocksize;
|
||||||
gint result;
|
gint result;
|
||||||
|
mms_off_t offset;
|
||||||
|
|
||||||
|
*buf = NULL;
|
||||||
|
|
||||||
mmssrc = GST_MMS (psrc);
|
mmssrc = GST_MMS (psrc);
|
||||||
|
|
||||||
GST_OBJECT_LOCK (mmssrc);
|
offset = mmsx_get_current_pos (mmssrc->connection);
|
||||||
blocksize = GST_BASE_SRC (mmssrc)->blocksize;
|
|
||||||
GST_OBJECT_UNLOCK (mmssrc);
|
/* Check if a seek perhaps has wrecked our connection */
|
||||||
|
if (offset == -1) {
|
||||||
|
GST_DEBUG_OBJECT (mmssrc,
|
||||||
|
"connection broken (probably an error during mmsx_seek_time during a convert query) returning FLOW_ERROR");
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Choose blocksize best for optimum performance */
|
||||||
|
if (offset == 0)
|
||||||
|
blocksize = mmsx_get_asf_header_len (mmssrc->connection);
|
||||||
|
else
|
||||||
|
blocksize = mmsx_get_asf_packet_len (mmssrc->connection);
|
||||||
|
|
||||||
*buf = gst_buffer_new_and_alloc (blocksize);
|
*buf = gst_buffer_new_and_alloc (blocksize);
|
||||||
|
|
||||||
data = GST_BUFFER_DATA (*buf);
|
data = GST_BUFFER_DATA (*buf);
|
||||||
GST_BUFFER_SIZE (*buf) = 0;
|
GST_BUFFER_SIZE (*buf) = 0;
|
||||||
GST_LOG_OBJECT (mmssrc, "reading %d bytes", blocksize);
|
GST_LOG_OBJECT (mmssrc, "reading %d bytes", blocksize);
|
||||||
if (mmssrc->connection) {
|
result = mmsx_read (NULL, mmssrc->connection, (char *) data, blocksize);
|
||||||
result = mms_read (NULL, mmssrc->connection, (char *) data, blocksize);
|
|
||||||
} else {
|
|
||||||
result = mmsh_read (NULL, mmssrc->connection_h, (char *) data, blocksize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* EOS? */
|
/* EOS? */
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
goto eos;
|
goto eos;
|
||||||
|
|
||||||
if (mmssrc->connection) {
|
GST_BUFFER_OFFSET (*buf) = offset;
|
||||||
GST_BUFFER_OFFSET (*buf) =
|
|
||||||
mms_get_current_pos (mmssrc->connection) - result;
|
|
||||||
} else {
|
|
||||||
GST_BUFFER_OFFSET (*buf) =
|
|
||||||
mmsh_get_current_pos (mmssrc->connection_h) - result;
|
|
||||||
}
|
|
||||||
GST_BUFFER_SIZE (*buf) = result;
|
GST_BUFFER_SIZE (*buf) = result;
|
||||||
|
|
||||||
GST_LOG_OBJECT (mmssrc, "Returning buffer with offset %" G_GINT64_FORMAT
|
GST_LOG_OBJECT (mmssrc, "Returning buffer with offset %" G_GINT64_FORMAT
|
||||||
|
@ -285,6 +396,28 @@ eos:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_mms_is_seekable (GstBaseSrc * src)
|
||||||
|
{
|
||||||
|
GstMMS *mmssrc = GST_MMS (src);
|
||||||
|
|
||||||
|
return mmsx_get_seekable (mmssrc->connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_mms_get_size (GstBaseSrc * src, guint64 * size)
|
||||||
|
{
|
||||||
|
GstMMS *mmssrc = GST_MMS (src);
|
||||||
|
|
||||||
|
/* non seekable usually means live streams, and get_length() returns,
|
||||||
|
erm, interesting values for live streams */
|
||||||
|
if (!mmsx_get_seekable (mmssrc->connection))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
*size = mmsx_get_length (mmssrc->connection);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_mms_start (GstBaseSrc * bsrc)
|
gst_mms_start (GstBaseSrc * bsrc)
|
||||||
{
|
{
|
||||||
|
@ -301,27 +434,35 @@ gst_mms_start (GstBaseSrc * bsrc)
|
||||||
else
|
else
|
||||||
bandwidth_avail = G_MAXINT;
|
bandwidth_avail = G_MAXINT;
|
||||||
|
|
||||||
|
/* If we already have a connection, and the uri isn't changed, reuse it,
|
||||||
|
as connecting is expensive. */
|
||||||
|
if (mms->connection) {
|
||||||
|
if (!strcmp (mms->uri_name, mms->current_connection_uri_name)) {
|
||||||
|
GST_DEBUG_OBJECT (mms, "Reusing existing connection for %s",
|
||||||
|
mms->uri_name);
|
||||||
|
return TRUE;
|
||||||
|
} else {
|
||||||
|
mmsx_close (mms->connection);
|
||||||
|
g_free (mms->current_connection_uri_name);
|
||||||
|
mms->current_connection_uri_name = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* FIXME: pass some sane arguments here */
|
/* FIXME: pass some sane arguments here */
|
||||||
GST_DEBUG_OBJECT (mms,
|
GST_DEBUG_OBJECT (mms,
|
||||||
"Trying mms_connect (%s) with bandwidth constraint of %d bps",
|
"Trying mms_connect (%s) with bandwidth constraint of %d bps",
|
||||||
mms->uri_name, bandwidth_avail);
|
mms->uri_name, bandwidth_avail);
|
||||||
mms->connection = mms_connect (NULL, NULL, mms->uri_name, bandwidth_avail);
|
mms->connection = mmsx_connect (NULL, NULL, mms->uri_name, bandwidth_avail);
|
||||||
if (mms->connection)
|
if (mms->connection) {
|
||||||
goto success;
|
/* Save the uri name so that it can be checked for connection reusing,
|
||||||
|
see above. */
|
||||||
GST_DEBUG_OBJECT (mms,
|
mms->current_connection_uri_name = g_strdup (mms->uri_name);
|
||||||
"Trying mmsh_connect (%s) with bandwidth constraint of %d bps",
|
|
||||||
mms->uri_name, bandwidth_avail);
|
|
||||||
mms->connection_h = mmsh_connect (NULL, NULL, mms->uri_name, bandwidth_avail);
|
|
||||||
if (!mms->connection_h)
|
|
||||||
goto no_connect;
|
|
||||||
|
|
||||||
/* fall through */
|
|
||||||
|
|
||||||
success:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (mms, "Connect successful");
|
GST_DEBUG_OBJECT (mms, "Connect successful");
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
} else {
|
||||||
|
GST_ELEMENT_ERROR (mms, RESOURCE, OPEN_READ,
|
||||||
|
("Could not connect to this stream"), (NULL));
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
no_uri:
|
no_uri:
|
||||||
|
@ -330,13 +471,6 @@ no_uri:
|
||||||
("No URI to open specified"), (NULL));
|
("No URI to open specified"), (NULL));
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
no_connect:
|
|
||||||
{
|
|
||||||
GST_ELEMENT_ERROR (mms, RESOURCE, OPEN_READ,
|
|
||||||
("Could not connect to this stream"), (NULL));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -346,12 +480,17 @@ gst_mms_stop (GstBaseSrc * bsrc)
|
||||||
|
|
||||||
mms = GST_MMS (bsrc);
|
mms = GST_MMS (bsrc);
|
||||||
if (mms->connection != NULL) {
|
if (mms->connection != NULL) {
|
||||||
mms_close (mms->connection);
|
/* Check if the connection is still pristine, that is if no more then
|
||||||
|
just the mmslib cached asf header has been read. If it is still pristine
|
||||||
|
preserve it as we often are re-started with the same URL and connecting
|
||||||
|
is expensive */
|
||||||
|
if (mmsx_get_current_pos (mms->connection) >
|
||||||
|
mmsx_get_asf_header_len (mms->connection)) {
|
||||||
|
mmsx_close (mms->connection);
|
||||||
mms->connection = NULL;
|
mms->connection = NULL;
|
||||||
|
g_free (mms->current_connection_uri_name);
|
||||||
|
mms->current_connection_uri_name = NULL;
|
||||||
}
|
}
|
||||||
if (mms->connection_h != NULL) {
|
|
||||||
mmsh_close (mms->connection_h);
|
|
||||||
mms->connection_h = NULL;
|
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
#define __GST_MMS_H__
|
#define __GST_MMS_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <libmms/mms.h>
|
#include <libmms/mmsx.h>
|
||||||
#include <libmms/mmsh.h>
|
|
||||||
#include <gst/base/gstpushsrc.h>
|
#include <gst/base/gstpushsrc.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
@ -32,10 +31,10 @@ struct _GstMMS
|
||||||
GstPushSrc parent;
|
GstPushSrc parent;
|
||||||
|
|
||||||
gchar *uri_name;
|
gchar *uri_name;
|
||||||
|
gchar *current_connection_uri_name;
|
||||||
guint connection_speed;
|
guint connection_speed;
|
||||||
|
|
||||||
mms_t *connection;
|
mmsx_t *connection;
|
||||||
mmsh_t *connection_h;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstMMSClass
|
struct _GstMMSClass
|
||||||
|
|
Loading…
Reference in a new issue