mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-19 23:06:49 +00:00
mssdemux: more manifest parsing and helper functions
Now the mss manifest is able to generate the files urls
This commit is contained in:
parent
5291985efe
commit
6b63a7dc81
2 changed files with 170 additions and 8 deletions
|
@ -27,11 +27,41 @@
|
||||||
|
|
||||||
#include "gstmssmanifest.h"
|
#include "gstmssmanifest.h"
|
||||||
|
|
||||||
|
#define MSS_NODE_STREAM_FRAGMENT "c"
|
||||||
|
#define MSS_NODE_STREAM_QUALITY "QualityLevel"
|
||||||
|
|
||||||
|
#define MSS_PROP_BITRATE "Bitrate"
|
||||||
|
#define MSS_PROP_DURATION "d"
|
||||||
|
#define MSS_PROP_NUMBER "n"
|
||||||
|
#define MSS_PROP_TIME "t"
|
||||||
|
#define MSS_PROP_URL "Url"
|
||||||
|
|
||||||
|
/* TODO check if atoi is successful? */
|
||||||
|
|
||||||
|
typedef struct _GstMssManifestStreamFragment
|
||||||
|
{
|
||||||
|
guint number;
|
||||||
|
guint64 time;
|
||||||
|
guint64 duration;
|
||||||
|
} GstMssManifestStreamFragment;
|
||||||
|
|
||||||
struct _GstMssManifestStream
|
struct _GstMssManifestStream
|
||||||
{
|
{
|
||||||
xmlNodePtr xmlnode;
|
xmlNodePtr xmlnode;
|
||||||
|
|
||||||
gint selectedQualityIndex;
|
gint selectedQualityIndex;
|
||||||
|
|
||||||
|
GList *fragments;
|
||||||
|
GList *qualities;
|
||||||
|
|
||||||
|
gchar *url;
|
||||||
|
|
||||||
|
GList *current_fragment;
|
||||||
|
GList *current_quality;
|
||||||
|
|
||||||
|
/* TODO move this to somewhere static */
|
||||||
|
GRegex *regex_bitrate;
|
||||||
|
GRegex *regex_position;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstMssManifest
|
struct _GstMssManifest
|
||||||
|
@ -42,6 +72,89 @@ struct _GstMssManifest
|
||||||
GSList *streams;
|
GSList *streams;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
node_has_type (xmlNodePtr node, const gchar * name)
|
||||||
|
{
|
||||||
|
return strcmp ((gchar *) node->name, name) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_gst_mss_manifest_stream_init (GstMssManifestStream * stream, xmlNodePtr node)
|
||||||
|
{
|
||||||
|
xmlNodePtr iter;
|
||||||
|
GstMssManifestStreamFragment *previous_fragment = NULL;
|
||||||
|
guint fragment_number = 0;
|
||||||
|
guint fragment_time_accum = 0;
|
||||||
|
GError *gerror = NULL;
|
||||||
|
|
||||||
|
stream->xmlnode = node;
|
||||||
|
|
||||||
|
/* get the base url path generator */
|
||||||
|
stream->url = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_URL);
|
||||||
|
|
||||||
|
for (iter = node->children; iter; iter = iter->next) {
|
||||||
|
if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
|
||||||
|
gchar *duration_str;
|
||||||
|
gchar *time_str;
|
||||||
|
gchar *seqnum_str;
|
||||||
|
GstMssManifestStreamFragment *fragment =
|
||||||
|
g_new (GstMssManifestStreamFragment, 1);
|
||||||
|
|
||||||
|
duration_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_DURATION);
|
||||||
|
time_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_TIME);
|
||||||
|
seqnum_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_NUMBER);
|
||||||
|
|
||||||
|
/* use the node's seq number or use the previous + 1 */
|
||||||
|
if (seqnum_str) {
|
||||||
|
fragment->number = atoi (seqnum_str);
|
||||||
|
g_free (seqnum_str);
|
||||||
|
} else {
|
||||||
|
fragment->number = fragment_number;
|
||||||
|
}
|
||||||
|
fragment_number = fragment->number + 1;
|
||||||
|
|
||||||
|
if (time_str) {
|
||||||
|
fragment->time = atoi (time_str);
|
||||||
|
g_free (time_str);
|
||||||
|
} else {
|
||||||
|
fragment->time = fragment_time_accum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we have a previous fragment, means we need to set its duration */
|
||||||
|
if (previous_fragment)
|
||||||
|
previous_fragment->duration = fragment->time - previous_fragment->time;
|
||||||
|
|
||||||
|
if (duration_str) {
|
||||||
|
fragment->duration = atoi (duration_str);
|
||||||
|
|
||||||
|
previous_fragment = NULL;
|
||||||
|
fragment_time_accum += fragment->duration;
|
||||||
|
g_free (duration_str);
|
||||||
|
} else {
|
||||||
|
/* store to set the duration at the next iteration */
|
||||||
|
previous_fragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we reverse it later */
|
||||||
|
stream->fragments = g_list_prepend (stream->fragments, fragment);
|
||||||
|
|
||||||
|
} else if (node_has_type (iter, MSS_NODE_STREAM_QUALITY)) {
|
||||||
|
stream->qualities = g_list_prepend (stream->qualities, iter);
|
||||||
|
} else {
|
||||||
|
/* TODO gst log this */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->fragments = g_list_reverse (stream->fragments);
|
||||||
|
stream->qualities = g_list_reverse (stream->qualities);
|
||||||
|
|
||||||
|
stream->current_fragment = stream->fragments;
|
||||||
|
stream->current_quality = stream->qualities;
|
||||||
|
|
||||||
|
stream->regex_bitrate = g_regex_new ("\\{[Bb]itrate\\}", 0, 0, &gerror);
|
||||||
|
stream->regex_position = g_regex_new ("\\{start[ _]time\\}", 0, 0, &gerror);
|
||||||
|
}
|
||||||
|
|
||||||
GstMssManifest *
|
GstMssManifest *
|
||||||
gst_mss_manifest_new (const GstBuffer * data)
|
gst_mss_manifest_new (const GstBuffer * data)
|
||||||
{
|
{
|
||||||
|
@ -61,19 +174,31 @@ gst_mss_manifest_new (const GstBuffer * data)
|
||||||
GstMssManifestStream *stream = g_new0 (GstMssManifestStream, 1);
|
GstMssManifestStream *stream = g_new0 (GstMssManifestStream, 1);
|
||||||
|
|
||||||
manifest->streams = g_slist_append (manifest->streams, stream);
|
manifest->streams = g_slist_append (manifest->streams, stream);
|
||||||
stream->xmlnode = nodeiter;
|
_gst_mss_manifest_stream_init (stream, nodeiter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return manifest;
|
return manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_mss_manifest_stream_free (GstMssManifestStream * stream)
|
||||||
|
{
|
||||||
|
g_list_free_full (stream->fragments, g_free);
|
||||||
|
g_list_free (stream->qualities);
|
||||||
|
g_free (stream->url);
|
||||||
|
g_regex_unref (stream->regex_position);
|
||||||
|
g_regex_unref (stream->regex_bitrate);
|
||||||
|
g_free (stream);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
gst_mss_manifest_free (GstMssManifest * manifest)
|
gst_mss_manifest_free (GstMssManifest * manifest)
|
||||||
{
|
{
|
||||||
g_return_if_fail (manifest != NULL);
|
g_return_if_fail (manifest != NULL);
|
||||||
|
|
||||||
g_slist_free_full (manifest->streams, g_free);
|
g_slist_free_full (manifest->streams,
|
||||||
|
(GDestroyNotify) gst_mss_manifest_stream_free);
|
||||||
|
|
||||||
xmlFreeDoc (manifest->xml);
|
xmlFreeDoc (manifest->xml);
|
||||||
g_free (manifest);
|
g_free (manifest);
|
||||||
|
@ -208,12 +333,7 @@ gst_mss_manifest_stream_get_caps (GstMssManifestStream * stream)
|
||||||
{
|
{
|
||||||
GstMssManifestStreamType streamtype =
|
GstMssManifestStreamType streamtype =
|
||||||
gst_mss_manifest_stream_get_type (stream);
|
gst_mss_manifest_stream_get_type (stream);
|
||||||
|
xmlNodePtr qualitylevel = stream->current_quality->data;
|
||||||
/* TODO properly get the stream */
|
|
||||||
xmlNodePtr qualitylevel = stream->xmlnode->children;
|
|
||||||
while (strcmp ((gchar *) qualitylevel->name, "QualityLevel")) {
|
|
||||||
qualitylevel = qualitylevel->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (streamtype == MSS_STREAM_TYPE_VIDEO)
|
if (streamtype == MSS_STREAM_TYPE_VIDEO)
|
||||||
return
|
return
|
||||||
|
@ -227,6 +347,46 @@ gst_mss_manifest_stream_get_caps (GstMssManifestStream * stream)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GstFlowReturn
|
||||||
|
gst_mss_manifest_stream_get_fragment_url (GstMssManifestStream * stream,
|
||||||
|
gchar ** url)
|
||||||
|
{
|
||||||
|
gchar *tmp;
|
||||||
|
gchar *bitrate_str;
|
||||||
|
gchar *start_time_str;
|
||||||
|
GstMssManifestStreamFragment *fragment = stream->current_fragment->data;
|
||||||
|
|
||||||
|
if (stream->current_fragment == NULL) /* stream is over */
|
||||||
|
return GST_FLOW_UNEXPECTED;
|
||||||
|
|
||||||
|
bitrate_str =
|
||||||
|
(gchar *) xmlGetProp (stream->current_quality->data,
|
||||||
|
(xmlChar *) MSS_PROP_BITRATE);
|
||||||
|
start_time_str = g_strdup_printf ("%" G_GUINT64_FORMAT, fragment->time);
|
||||||
|
|
||||||
|
tmp = g_regex_replace_literal (stream->regex_bitrate, stream->url,
|
||||||
|
strlen (stream->url), 0, bitrate_str, 0, NULL);
|
||||||
|
*url = g_regex_replace_literal (stream->regex_position, tmp,
|
||||||
|
strlen (tmp), 0, start_time_str, 0, NULL);
|
||||||
|
|
||||||
|
g_free (tmp);
|
||||||
|
g_free (start_time_str);
|
||||||
|
g_free (bitrate_str);
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstFlowReturn
|
||||||
|
gst_mss_manifest_stream_advance_fragment (GstMssManifestStream * stream)
|
||||||
|
{
|
||||||
|
if (stream->current_fragment == NULL)
|
||||||
|
return GST_FLOW_UNEXPECTED;
|
||||||
|
|
||||||
|
stream->current_fragment = g_list_next (stream->current_fragment);
|
||||||
|
if (stream->current_fragment == NULL);
|
||||||
|
return GST_FLOW_UNEXPECTED;
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
const gchar *
|
const gchar *
|
||||||
gst_mss_manifest_stream_type_name (GstMssManifestStreamType streamtype)
|
gst_mss_manifest_stream_type_name (GstMssManifestStreamType streamtype)
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,6 +44,8 @@ GSList * gst_mss_manifest_get_streams (GstMssManifest * manifest);
|
||||||
|
|
||||||
GstMssManifestStreamType gst_mss_manifest_stream_get_type (GstMssManifestStream *stream);
|
GstMssManifestStreamType gst_mss_manifest_stream_get_type (GstMssManifestStream *stream);
|
||||||
GstCaps * gst_mss_manifest_stream_get_caps (GstMssManifestStream * stream);
|
GstCaps * gst_mss_manifest_stream_get_caps (GstMssManifestStream * stream);
|
||||||
|
GstFlowReturn gst_mss_manifest_stream_get_fragment_url (GstMssManifestStream * stream, gchar ** url);
|
||||||
|
GstFlowReturn gst_mss_manifest_stream_advance_fragment (GstMssManifestStream * stream);
|
||||||
|
|
||||||
const gchar * gst_mss_manifest_stream_type_name (GstMssManifestStreamType streamtype);
|
const gchar * gst_mss_manifest_stream_type_name (GstMssManifestStreamType streamtype);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue