diff --git a/ext/dash/Makefile.am b/ext/dash/Makefile.am index 936aabfd92..9d4e306e92 100644 --- a/ext/dash/Makefile.am +++ b/ext/dash/Makefile.am @@ -4,12 +4,14 @@ plugin_LTLIBRARIES = libgstdashdemux.la libgstdashdemux_la_SOURCES = \ gstmpdparser.c \ gstdashdemux.c \ + gstisoff.c \ gstplugin.c # headers we need but don't want installed noinst_HEADERS = \ gstmpdparser.h \ gstdashdemux.h \ + gstisoff.h \ gstdash_debug.h # compiler and linker flags used to compile this plugin, set in configure.ac diff --git a/ext/dash/gstdashdemux.c b/ext/dash/gstdashdemux.c index 4e4d71aa34..db3ece1e65 100644 --- a/ext/dash/gstdashdemux.c +++ b/ext/dash/gstdashdemux.c @@ -211,9 +211,12 @@ gst_dash_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream * stream); static void gst_dash_demux_advance_period (GstAdaptiveDemux * demux); static gboolean gst_dash_demux_has_next_period (GstAdaptiveDemux * demux); +static GstFlowReturn gst_dash_demux_chunk_received (GstAdaptiveDemux * demux, + GstAdaptiveDemuxStream * stream, GstBuffer ** chunk); /* GstDashDemux */ static gboolean gst_dash_demux_setup_all_streams (GstDashDemux * demux); +static void gst_dash_demux_stream_free (GstAdaptiveDemuxStream * stream); static GstCaps *gst_dash_demux_get_input_caps (GstDashDemux * demux, GstActiveStream * stream); @@ -315,6 +318,7 @@ gst_dash_demux_class_init (GstDashDemuxClass * klass) gst_dash_demux_stream_select_bitrate; gstadaptivedemux_class->stream_update_fragment_info = gst_dash_demux_stream_update_fragment_info; + gstadaptivedemux_class->stream_free = gst_dash_demux_stream_free; } static void @@ -458,6 +462,7 @@ gst_dash_demux_setup_all_streams (GstDashDemux * demux) gst_adaptive_demux_stream_set_tags (GST_ADAPTIVE_DEMUX_STREAM_CAST (stream), tags); stream->index = i; + gst_isoff_sidx_parser_init (&stream->sidx_parser); } return TRUE; @@ -567,6 +572,7 @@ done: static gboolean gst_dash_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf) { + GstAdaptiveDemuxClass *klass; GstDashDemux *dashdemux = GST_DASH_DEMUX_CAST (demux); gboolean ret = FALSE; gchar *manifest; @@ -586,6 +592,12 @@ gst_dash_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf) if (gst_buffer_map (buf, &mapinfo, GST_MAP_READ)) { manifest = (gchar *) mapinfo.data; if (gst_mpd_parse (dashdemux->client, manifest, mapinfo.size)) { + if (gst_mpd_client_has_isoff_ondemand_profile (dashdemux->client)) { + klass = GST_ADAPTIVE_DEMUX_GET_CLASS (dashdemux); + + klass->chunk_received = gst_dash_demux_chunk_received; + } + if (gst_mpd_client_setup_media_presentation (dashdemux->client)) { ret = TRUE; } else { @@ -1096,3 +1108,22 @@ gst_dash_demux_advance_period (GstAdaptiveDemux * demux) gst_dash_demux_setup_all_streams (dashdemux); gst_mpd_client_set_segment_index_for_all_streams (dashdemux->client, 0); } + +static GstFlowReturn +gst_dash_demux_chunk_received (GstAdaptiveDemux * demux, + GstAdaptiveDemuxStream * stream, GstBuffer ** chunk) +{ + GstDashDemuxStream *dash_stream = (GstDashDemuxStream *) stream; + if (stream->downloading_index) { + gst_isoff_sidx_parser_add_buffer (&dash_stream->sidx_parser, *chunk); + } + return GST_FLOW_OK; +} + +static void +gst_dash_demux_stream_free (GstAdaptiveDemuxStream * stream) +{ + GstDashDemuxStream *dash_stream = (GstDashDemuxStream *) stream; + + gst_isoff_sidx_parser_clear (&dash_stream->sidx_parser); +} diff --git a/ext/dash/gstdashdemux.h b/ext/dash/gstdashdemux.h index 1afdc8410c..2ed507b0b4 100644 --- a/ext/dash/gstdashdemux.h +++ b/ext/dash/gstdashdemux.h @@ -35,6 +35,7 @@ #include #include #include "gstmpdparser.h" +#include "gstisoff.h" #include G_BEGIN_DECLS @@ -63,6 +64,9 @@ struct _GstDashDemuxStream GstActiveStream *active_stream; GstMediaFragmentInfo current_fragment; + + /* index parsing */ + GstSidxParser sidx_parser; }; /** diff --git a/ext/dash/gstisoff.c b/ext/dash/gstisoff.c new file mode 100644 index 0000000000..d4726443c3 --- /dev/null +++ b/ext/dash/gstisoff.c @@ -0,0 +1,187 @@ +/* + * ISO File Format parsing library + * + * gstisoff.h + * + * Copyright (C) 2015 Samsung Electronics. All rights reserved. + * Author: Thiago Santos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library (COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gstisoff.h" +#include + +void +gst_isoff_sidx_parser_init (GstSidxParser * parser) +{ + parser->status = GST_ISOFF_SIDX_PARSER_INIT; + parser->cumulative_entry_size = 0; + parser->sidx.entries = NULL; + parser->sidx.entries_count = 0; +} + +void +gst_isoff_sidx_parser_clear (GstSidxParser * parser) +{ + g_free (parser->sidx.entries); + parser->sidx.entries = NULL; +} + +static void +gst_isoff_parse_sidx_entry (GstSidxBoxEntry * entry, GstByteReader * reader) +{ + guint32 aux; + + aux = gst_byte_reader_get_uint32_be_unchecked (reader); + entry->ref_type = aux >> 31; + entry->size = aux & 0x7FFFFFFF; + entry->duration = gst_byte_reader_get_uint32_be_unchecked (reader); + aux = gst_byte_reader_get_uint32_be_unchecked (reader); + entry->starts_with_sap = aux >> 31; + entry->sap_type = ((aux >> 28) & 0x7); + entry->sap_delta_time = aux & 0xFFFFFFF; +} + +GstIsoffParserResult +gst_isoff_sidx_parser_add_buffer (GstSidxParser * parser, GstBuffer * buffer, + guint * consumed) +{ + GstIsoffParserResult res = GST_ISOFF_PARSER_OK; + GstByteReader reader; + GstMapInfo info; + gsize remaining; + guint32 fourcc; + + if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) { + *consumed = 0; + return GST_ISOFF_PARSER_ERROR; + } + + gst_byte_reader_init (&reader, info.data, info.size); + + switch (parser->status) { + case GST_ISOFF_SIDX_PARSER_INIT: + if (gst_byte_reader_get_remaining (&reader) < GST_ISOFF_FULL_BOX_SIZE) { + break; + } + + parser->size = gst_byte_reader_get_uint32_be_unchecked (&reader); + fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader); + if (fourcc != GST_ISOFF_FOURCC_SIDX) { + res = GST_ISOFF_PARSER_UNEXPECTED; + gst_byte_reader_set_pos (&reader, 0); + break; + } + if (parser->size == 1) { + if (gst_byte_reader_get_remaining (&reader) < 12) { + gst_byte_reader_set_pos (&reader, 0); + break; + } + + parser->size = gst_byte_reader_get_uint64_be_unchecked (&reader); + } + if (parser->size == 0) { + res = GST_ISOFF_PARSER_ERROR; + gst_byte_reader_set_pos (&reader, 0); + break; + } + parser->sidx.version = gst_byte_reader_get_uint8_unchecked (&reader); + parser->sidx.flags = gst_byte_reader_get_uint24_le_unchecked (&reader); + + parser->status = GST_ISOFF_SIDX_PARSER_HEADER; + + case GST_ISOFF_SIDX_PARSER_HEADER: + remaining = gst_byte_reader_get_remaining (&reader); + if (remaining < 12 + (parser->sidx.version == 0 ? 8 : 16)) { + break; + } + + parser->sidx.ref_id = gst_byte_reader_get_uint32_be_unchecked (&reader); + parser->sidx.timescale = + gst_byte_reader_get_uint32_be_unchecked (&reader); + if (parser->sidx.version == 0) { + parser->sidx.earliest_pts = + gst_byte_reader_get_uint32_be_unchecked (&reader); + parser->sidx.first_offset = parser->sidx.earliest_pts = + gst_byte_reader_get_uint32_be_unchecked (&reader); + } else { + parser->sidx.earliest_pts = + gst_byte_reader_get_uint64_be_unchecked (&reader); + parser->sidx.first_offset = + gst_byte_reader_get_uint64_be_unchecked (&reader); + } + /* skip 2 reserved bytes */ + gst_byte_reader_skip_unchecked (&reader, 2); + parser->sidx.entries_count = + gst_byte_reader_get_uint16_be_unchecked (&reader); + + GST_LOG ("Timescale: %" G_GUINT32_FORMAT, parser->sidx.timescale); + GST_LOG ("Earliest pts: %" G_GUINT64_FORMAT, parser->sidx.earliest_pts); + GST_LOG ("First offset: %" G_GUINT64_FORMAT, parser->sidx.first_offset); + + parser->cumulative_pts = + gst_util_uint64_scale_int_round (parser->sidx.earliest_pts, + GST_SECOND, parser->sidx.timescale); + + if (parser->sidx.entries_count) { + parser->sidx.entries = + g_malloc (sizeof (GstSidxBoxEntry) * parser->sidx.entries_count); + } + parser->sidx.entry_index = 0; + + parser->status = GST_ISOFF_SIDX_PARSER_DATA; + + case GST_ISOFF_SIDX_PARSER_DATA: + while (parser->sidx.entry_index < parser->sidx.entries_count) { + GstSidxBoxEntry *entry = + &parser->sidx.entries[parser->sidx.entry_index]; + + remaining = gst_byte_reader_get_remaining (&reader);; + if (remaining < 12) + break; + + entry->offset = parser->cumulative_entry_size; + entry->pts = parser->cumulative_pts; + gst_isoff_parse_sidx_entry (entry, &reader); + entry->duration = gst_util_uint64_scale_int_round (entry->duration, + GST_SECOND, parser->sidx.timescale); + parser->cumulative_entry_size += entry->size; + parser->cumulative_pts += entry->duration; + + GST_LOG ("Sidx entry %d) offset: %" G_GUINT64_FORMAT ", pts: %" + GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT " - size %" + G_GUINT32_FORMAT, parser->sidx.entry_index, entry->offset, + GST_TIME_ARGS (entry->pts), GST_TIME_ARGS (entry->duration), + entry->size); + + parser->sidx.entry_index++; + } + + if (parser->sidx.entry_index == parser->sidx.entries_count) + parser->status = GST_ISOFF_SIDX_PARSER_FINISHED; + else + break; + case GST_ISOFF_SIDX_PARSER_FINISHED: + parser->sidx.entry_index = 0; + res = GST_ISOFF_PARSER_DONE; + break; + } + + *consumed = gst_byte_reader_get_pos (&reader); + gst_buffer_unmap (buffer, &info); + return res; +} diff --git a/ext/dash/gstisoff.h b/ext/dash/gstisoff.h new file mode 100644 index 0000000000..e535c69c0d --- /dev/null +++ b/ext/dash/gstisoff.h @@ -0,0 +1,99 @@ +/* + * ISO File Format parsing library + * + * gstisoff.h + * + * Copyright (C) 2015 Samsung Electronics. All rights reserved. + * Author: Thiago Santos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library (COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_ISOFF_H__ +#define __GST_ISOFF_H__ + +#include + +G_BEGIN_DECLS + +typedef enum { + GST_ISOFF_PARSER_OK, + GST_ISOFF_PARSER_DONE, + GST_ISOFF_PARSER_UNEXPECTED, + GST_ISOFF_PARSER_ERROR +} GstIsoffParserResult; + +/* this is the minimum size, it can be larger if it + * uses extended size or type */ +#define GST_ISOFF_FULL_BOX_SIZE 12 + +#define GST_ISOFF_FOURCC_SIDX GST_MAKE_FOURCC('s','i','d','x') +typedef struct _GstSidxBoxEntry +{ + gboolean ref_type; + guint32 size; + GstClockTime duration; + gboolean starts_with_sap; + guint8 sap_type; + guint32 sap_delta_time; + + guint64 offset; + GstClockTime pts; +} GstSidxBoxEntry; + +typedef struct _GstSidxBox +{ + guint8 version; + guint32 flags; + + guint32 ref_id; + guint32 timescale; + guint64 earliest_pts; + guint64 first_offset; + + gint entry_index; + gint entries_count; + + GstSidxBoxEntry *entries; +} GstSidxBox; + +typedef enum _GstSidxParserStatus +{ + GST_ISOFF_SIDX_PARSER_INIT, + GST_ISOFF_SIDX_PARSER_HEADER, + GST_ISOFF_SIDX_PARSER_DATA, + GST_ISOFF_SIDX_PARSER_FINISHED +} GstSidxParserStatus; + +typedef struct _GstSidxParser +{ + GstSidxParserStatus status; + + guint64 size; + guint64 cumulative_entry_size; + guint64 cumulative_pts; + + GstSidxBox sidx; +} GstSidxParser; + +void gst_isoff_sidx_parser_init (GstSidxParser * parser); +void gst_isoff_sidx_parser_clear (GstSidxParser * parser); +GstIsoffParserResult gst_isoff_sidx_parser_add_buffer (GstSidxParser * parser, GstBuffer * buf, guint * consumed); + +G_END_DECLS + +#endif /* __GST_ISOFF_H__ */ +