dashdemux: parse the sidx index from isobmff streams

Allows dashdemux to identify the subsegments in the stream and
switch bitrates when needed
This commit is contained in:
Thiago Santos 2015-01-12 11:57:02 -03:00
parent b40e5decb2
commit f5b98f24c2
5 changed files with 323 additions and 0 deletions

View file

@ -4,12 +4,14 @@ plugin_LTLIBRARIES = libgstdashdemux.la
libgstdashdemux_la_SOURCES = \ libgstdashdemux_la_SOURCES = \
gstmpdparser.c \ gstmpdparser.c \
gstdashdemux.c \ gstdashdemux.c \
gstisoff.c \
gstplugin.c gstplugin.c
# headers we need but don't want installed # headers we need but don't want installed
noinst_HEADERS = \ noinst_HEADERS = \
gstmpdparser.h \ gstmpdparser.h \
gstdashdemux.h \ gstdashdemux.h \
gstisoff.h \
gstdash_debug.h gstdash_debug.h
# compiler and linker flags used to compile this plugin, set in configure.ac # compiler and linker flags used to compile this plugin, set in configure.ac

View file

@ -211,9 +211,12 @@ gst_dash_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream *
stream); stream);
static void gst_dash_demux_advance_period (GstAdaptiveDemux * demux); static void gst_dash_demux_advance_period (GstAdaptiveDemux * demux);
static gboolean gst_dash_demux_has_next_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 */ /* GstDashDemux */
static gboolean gst_dash_demux_setup_all_streams (GstDashDemux * demux); 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, static GstCaps *gst_dash_demux_get_input_caps (GstDashDemux * demux,
GstActiveStream * stream); GstActiveStream * stream);
@ -315,6 +318,7 @@ gst_dash_demux_class_init (GstDashDemuxClass * klass)
gst_dash_demux_stream_select_bitrate; gst_dash_demux_stream_select_bitrate;
gstadaptivedemux_class->stream_update_fragment_info = gstadaptivedemux_class->stream_update_fragment_info =
gst_dash_demux_stream_update_fragment_info; gst_dash_demux_stream_update_fragment_info;
gstadaptivedemux_class->stream_free = gst_dash_demux_stream_free;
} }
static void 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 gst_adaptive_demux_stream_set_tags (GST_ADAPTIVE_DEMUX_STREAM_CAST
(stream), tags); (stream), tags);
stream->index = i; stream->index = i;
gst_isoff_sidx_parser_init (&stream->sidx_parser);
} }
return TRUE; return TRUE;
@ -567,6 +572,7 @@ done:
static gboolean static gboolean
gst_dash_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf) gst_dash_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
{ {
GstAdaptiveDemuxClass *klass;
GstDashDemux *dashdemux = GST_DASH_DEMUX_CAST (demux); GstDashDemux *dashdemux = GST_DASH_DEMUX_CAST (demux);
gboolean ret = FALSE; gboolean ret = FALSE;
gchar *manifest; gchar *manifest;
@ -586,6 +592,12 @@ gst_dash_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
if (gst_buffer_map (buf, &mapinfo, GST_MAP_READ)) { if (gst_buffer_map (buf, &mapinfo, GST_MAP_READ)) {
manifest = (gchar *) mapinfo.data; manifest = (gchar *) mapinfo.data;
if (gst_mpd_parse (dashdemux->client, manifest, mapinfo.size)) { 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)) { if (gst_mpd_client_setup_media_presentation (dashdemux->client)) {
ret = TRUE; ret = TRUE;
} else { } else {
@ -1096,3 +1108,22 @@ gst_dash_demux_advance_period (GstAdaptiveDemux * demux)
gst_dash_demux_setup_all_streams (dashdemux); gst_dash_demux_setup_all_streams (dashdemux);
gst_mpd_client_set_segment_index_for_all_streams (dashdemux->client, 0); 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);
}

View file

@ -35,6 +35,7 @@
#include <gst/base/gstadapter.h> #include <gst/base/gstadapter.h>
#include <gst/base/gstdataqueue.h> #include <gst/base/gstdataqueue.h>
#include "gstmpdparser.h" #include "gstmpdparser.h"
#include "gstisoff.h"
#include <gst/uridownloader/gsturidownloader.h> #include <gst/uridownloader/gsturidownloader.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -63,6 +64,9 @@ struct _GstDashDemuxStream
GstActiveStream *active_stream; GstActiveStream *active_stream;
GstMediaFragmentInfo current_fragment; GstMediaFragmentInfo current_fragment;
/* index parsing */
GstSidxParser sidx_parser;
}; };
/** /**

187
ext/dash/gstisoff.c Normal file
View file

@ -0,0 +1,187 @@
/*
* ISO File Format parsing library
*
* gstisoff.h
*
* Copyright (C) 2015 Samsung Electronics. All rights reserved.
* Author: Thiago Santos <thiagoss@osg.samsung.com>
*
* 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 <gst/base/gstbytereader.h>
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;
}

99
ext/dash/gstisoff.h Normal file
View file

@ -0,0 +1,99 @@
/*
* ISO File Format parsing library
*
* gstisoff.h
*
* Copyright (C) 2015 Samsung Electronics. All rights reserved.
* Author: Thiago Santos <thiagoss@osg.samsung.com>
*
* 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 <gst/gst.h>
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__ */