gstreamer/gst/mpegtsdemux/mpegtspacketizer.c

2421 lines
82 KiB
C
Raw Normal View History

/*
* mpegtspacketizer.c -
* Copyright (C) 2007, 2008 Alessandro Decina, Zaheer Merali
*
* Authors:
* Zaheer Merali <zaheerabbas at merali dot org>
* Alessandro Decina <alessandro@nnva.org>
*
* 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 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; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include <stdlib.h>
2012-02-16 12:54:24 +00:00
/* Skew calculation pameters */
#define MAX_TIME (2 * GST_SECOND)
2012-02-16 12:54:24 +00:00
/* maximal PCR time */
#define PCR_MAX_VALUE (((((guint64)1)<<33) * 300) + 298)
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
#define PCR_GST_MAX_VALUE (PCR_MAX_VALUE * GST_MSECOND / (PCR_MSECOND))
#define PTS_DTS_MAX_VALUE (((guint64)1) << 33)
#include "mpegtspacketizer.h"
#include "gstmpegdesc.h"
GST_DEBUG_CATEGORY_STATIC (mpegts_packetizer_debug);
#define GST_CAT_DEFAULT mpegts_packetizer_debug
static void _init_local (void);
G_DEFINE_TYPE_EXTENDED (MpegTSPacketizer2, mpegts_packetizer, G_TYPE_OBJECT, 0,
_init_local ());
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
#define ABSDIFF(a,b) ((a) < (b) ? (b) - (a) : (a) - (b))
static void mpegts_packetizer_dispose (GObject * object);
static void mpegts_packetizer_finalize (GObject * object);
static GstClockTime calculate_skew (MpegTSPCR * pcr, guint64 pcrtime,
GstClockTime time);
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
static void _close_current_group (MpegTSPCR * pcrtable);
static void record_pcr (MpegTSPacketizer2 * packetizer, MpegTSPCR * pcrtable,
guint64 pcr, guint64 offset);
#define CONTINUITY_UNSET 255
#define VERSION_NUMBER_UNSET 255
#define TABLE_ID_UNSET 0xFF
#define PACKET_SYNC_BYTE 0x47
static inline MpegTSPCR *
get_pcr_table (MpegTSPacketizer2 * packetizer, guint16 pid)
{
MpegTSPCR *res;
res = packetizer->observations[packetizer->pcrtablelut[pid]];
if (G_UNLIKELY (res == NULL)) {
/* If we don't have a PCR table for the requested PID, create one .. */
res = g_new0 (MpegTSPCR, 1);
/* Add it to the last table position */
packetizer->observations[packetizer->lastobsid] = res;
/* Update the pcrtablelut */
packetizer->pcrtablelut[pid] = packetizer->lastobsid;
/* And increment the last know slot */
packetizer->lastobsid++;
/* Finally set the default values */
res->pid = pid;
res->base_time = GST_CLOCK_TIME_NONE;
res->base_pcrtime = GST_CLOCK_TIME_NONE;
res->last_pcrtime = GST_CLOCK_TIME_NONE;
res->window_pos = 0;
res->window_filling = TRUE;
res->window_min = 0;
res->skew = 0;
res->prev_send_diff = GST_CLOCK_TIME_NONE;
res->prev_out_time = GST_CLOCK_TIME_NONE;
res->pcroffset = 0;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
res->current = g_slice_new0 (PCROffsetCurrent);
}
return res;
}
static void
pcr_offset_group_free (PCROffsetGroup * group)
{
g_free (group->values);
g_slice_free (PCROffsetGroup, group);
}
static void
flush_observations (MpegTSPacketizer2 * packetizer)
{
gint i;
for (i = 0; i < packetizer->lastobsid; i++) {
g_list_free_full (packetizer->observations[i]->groups,
(GDestroyNotify) pcr_offset_group_free);
if (packetizer->observations[i]->current)
g_slice_free (PCROffsetCurrent, packetizer->observations[i]->current);
g_free (packetizer->observations[i]);
packetizer->observations[i] = NULL;
}
memset (packetizer->pcrtablelut, 0xff, 0x2000);
packetizer->lastobsid = 0;
}
static inline MpegTSPacketizerStreamSubtable *
find_subtable (GSList * subtables, guint8 table_id, guint16 subtable_extension)
{
GSList *tmp;
/* FIXME: Make this an array ! */
for (tmp = subtables; tmp; tmp = tmp->next) {
MpegTSPacketizerStreamSubtable *sub =
(MpegTSPacketizerStreamSubtable *) tmp->data;
if (sub->table_id == table_id
&& sub->subtable_extension == subtable_extension)
return sub;
}
return FALSE;
}
static gboolean
seen_section_before (MpegTSPacketizerStream * stream, guint8 table_id,
guint16 subtable_extension, guint8 version_number, guint8 section_number,
guint8 last_section_number)
{
MpegTSPacketizerStreamSubtable *subtable;
/* Check if we've seen this table_id/subtable_extension first */
subtable = find_subtable (stream->subtables, table_id, subtable_extension);
if (!subtable) {
GST_DEBUG ("Haven't seen subtable");
return FALSE;
}
/* If we have, check it has the same version_number */
if (subtable->version_number != version_number) {
GST_DEBUG ("Different version number");
return FALSE;
}
/* Did the number of sections change ? */
if (subtable->last_section_number != last_section_number) {
GST_DEBUG ("Different last_section_number");
return FALSE;
}
/* Finally return whether we saw that section or not */
return MPEGTS_BIT_IS_SET (subtable->seen_section, section_number);
}
static MpegTSPacketizerStreamSubtable *
mpegts_packetizer_stream_subtable_new (guint8 table_id,
guint16 subtable_extension, guint8 last_section_number)
{
MpegTSPacketizerStreamSubtable *subtable;
subtable = g_new0 (MpegTSPacketizerStreamSubtable, 1);
subtable->version_number = VERSION_NUMBER_UNSET;
subtable->table_id = table_id;
subtable->subtable_extension = subtable_extension;
subtable->last_section_number = last_section_number;
return subtable;
}
static MpegTSPacketizerStream *
mpegts_packetizer_stream_new (guint16 pid)
{
MpegTSPacketizerStream *stream;
stream = (MpegTSPacketizerStream *) g_new0 (MpegTSPacketizerStream, 1);
stream->continuity_counter = CONTINUITY_UNSET;
stream->subtables = NULL;
stream->table_id = TABLE_ID_UNSET;
stream->pid = pid;
return stream;
}
static void
mpegts_packetizer_clear_section (MpegTSPacketizerStream * stream)
{
stream->continuity_counter = CONTINUITY_UNSET;
stream->section_length = 0;
stream->section_offset = 0;
stream->table_id = TABLE_ID_UNSET;
if (stream->section_data)
g_free (stream->section_data);
stream->section_data = NULL;
}
static void
mpegts_packetizer_stream_subtable_free (MpegTSPacketizerStreamSubtable *
subtable)
{
g_free (subtable);
}
static void
mpegts_packetizer_stream_free (MpegTSPacketizerStream * stream)
{
mpegts_packetizer_clear_section (stream);
if (stream->section_data)
g_free (stream->section_data);
g_slist_foreach (stream->subtables,
(GFunc) mpegts_packetizer_stream_subtable_free, NULL);
g_slist_free (stream->subtables);
g_free (stream);
}
static void
mpegts_packetizer_class_init (MpegTSPacketizer2Class * klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = mpegts_packetizer_dispose;
gobject_class->finalize = mpegts_packetizer_finalize;
}
static void
mpegts_packetizer_init (MpegTSPacketizer2 * packetizer)
{
packetizer->adapter = gst_adapter_new ();
packetizer->offset = 0;
packetizer->empty = TRUE;
packetizer->streams = g_new0 (MpegTSPacketizerStream *, 8192);
packetizer->packet_size = 0;
packetizer->calculate_skew = FALSE;
packetizer->calculate_offset = FALSE;
packetizer->map_data = NULL;
packetizer->map_size = 0;
packetizer->map_offset = 0;
packetizer->need_sync = FALSE;
memset (packetizer->pcrtablelut, 0xff, 0x2000);
memset (packetizer->observations, 0x0, sizeof (packetizer->observations));
packetizer->lastobsid = 0;
packetizer->nb_seen_offsets = 0;
packetizer->refoffset = -1;
packetizer->last_in_time = GST_CLOCK_TIME_NONE;
}
static void
mpegts_packetizer_dispose (GObject * object)
{
MpegTSPacketizer2 *packetizer = GST_MPEGTS_PACKETIZER (object);
if (!packetizer->disposed) {
if (packetizer->packet_size)
packetizer->packet_size = 0;
if (packetizer->streams) {
int i;
for (i = 0; i < 8192; i++) {
if (packetizer->streams[i])
mpegts_packetizer_stream_free (packetizer->streams[i]);
}
g_free (packetizer->streams);
}
gst_adapter_clear (packetizer->adapter);
g_object_unref (packetizer->adapter);
packetizer->disposed = TRUE;
packetizer->offset = 0;
packetizer->empty = TRUE;
flush_observations (packetizer);
}
if (G_OBJECT_CLASS (mpegts_packetizer_parent_class)->dispose)
G_OBJECT_CLASS (mpegts_packetizer_parent_class)->dispose (object);
}
static void
mpegts_packetizer_finalize (GObject * object)
{
if (G_OBJECT_CLASS (mpegts_packetizer_parent_class)->finalize)
G_OBJECT_CLASS (mpegts_packetizer_parent_class)->finalize (object);
}
static inline guint64
mpegts_packetizer_compute_pcr (const guint8 * data)
{
guint32 pcr1;
guint16 pcr2;
guint64 pcr, pcr_ext;
pcr1 = GST_READ_UINT32_BE (data);
pcr2 = GST_READ_UINT16_BE (data + 4);
pcr = ((guint64) pcr1) << 1;
pcr |= (pcr2 & 0x8000) >> 15;
pcr_ext = (pcr2 & 0x01ff);
return pcr * 300 + pcr_ext % 300;
}
static gboolean
mpegts_packetizer_parse_adaptation_field_control (MpegTSPacketizer2 *
packetizer, MpegTSPacketizerPacket * packet)
{
guint8 length, afcflags;
guint8 *data;
length = *packet->data++;
/* an adaptation field with length 0 is valid and
* can be used to insert a single stuffing byte */
if (!length) {
packet->afc_flags = 0;
return TRUE;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
if ((packet->scram_afc_cc & 0x30) == 0x20) {
/* no payload, adaptation field of 183 bytes */
if (length > 183) {
GST_WARNING ("PID %d afc == 0x%02x and length %d > 183",
packet->pid, packet->scram_afc_cc & 0x30, length);
return FALSE;
}
if (length != 183) {
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GST_WARNING ("PID %d afc == 0x%02x and length %d != 183",
packet->pid, packet->scram_afc_cc & 0x30, length);
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GST_MEMDUMP ("Unknown payload", packet->data + length,
packet->data_end - packet->data - length);
}
} else if (length > 182) {
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GST_WARNING ("PID %d afc == 0x%02x and length %d > 182",
packet->pid, packet->scram_afc_cc & 0x30, length);
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
return FALSE;
}
if (packet->data + length > packet->data_end) {
GST_DEBUG ("PID %d afc length %d overflows the buffer current %d max %d",
packet->pid, length, (gint) (packet->data - packet->data_start),
(gint) (packet->data_end - packet->data_start));
return FALSE;
}
data = packet->data;
packet->data += length;
afcflags = packet->afc_flags = *data++;
GST_DEBUG ("flags: %s%s%s%s%s%s%s%s%s",
afcflags & 0x80 ? "discontinuity " : "",
afcflags & 0x40 ? "random_access " : "",
afcflags & 0x20 ? "elementary_stream_priority " : "",
afcflags & 0x10 ? "PCR " : "",
afcflags & 0x08 ? "OPCR " : "",
afcflags & 0x04 ? "splicing_point " : "",
afcflags & 0x02 ? "transport_private_data " : "",
afcflags & 0x01 ? "extension " : "", afcflags == 0x00 ? "<none>" : "");
/* PCR */
if (afcflags & MPEGTS_AFC_PCR_FLAG) {
MpegTSPCR *pcrtable = NULL;
packet->pcr = mpegts_packetizer_compute_pcr (data);
data += 6;
GST_DEBUG ("pcr 0x%04x %" G_GUINT64_FORMAT " (%" GST_TIME_FORMAT
") offset:%" G_GUINT64_FORMAT, packet->pid, packet->pcr,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (packet->pcr)), packet->offset);
if (packetizer->calculate_skew
&& GST_CLOCK_TIME_IS_VALID (packetizer->last_in_time)) {
pcrtable = get_pcr_table (packetizer, packet->pid);
calculate_skew (pcrtable, packet->pcr, packetizer->last_in_time);
}
if (packetizer->calculate_offset) {
if (!pcrtable)
pcrtable = get_pcr_table (packetizer, packet->pid);
record_pcr (packetizer, pcrtable, packet->pcr, packet->offset);
}
}
#ifndef GST_DISABLE_GST_DEBUG
/* OPCR */
if (afcflags & MPEGTS_AFC_OPCR_FLAG) {
/* Note: We don't use/need opcr for the time being */
guint64 opcr = mpegts_packetizer_compute_pcr (data);
data += 6;
GST_DEBUG ("opcr %" G_GUINT64_FORMAT " (%" GST_TIME_FORMAT ")",
opcr, GST_TIME_ARGS (PCRTIME_TO_GSTTIME (opcr)));
}
if (afcflags & MPEGTS_AFC_SPLICING_POINT_FLAG) {
GST_DEBUG ("splice_countdown: %u", *data++);
}
if (afcflags & MPEGTS_AFC_TRANSPORT_PRIVATE_DATA_FLAG) {
guint8 len = *data++;
GST_MEMDUMP ("private data", data, len);
data += len;
}
if (afcflags & MPEGTS_AFC_EXTENSION_FLAG) {
guint8 extlen = *data++;
guint8 flags = *data++;
GST_DEBUG ("extension size:%d flags : %s%s%s", extlen,
flags & 0x80 ? "ltw " : "",
flags & 0x40 ? "piecewise_rate " : "",
flags & 0x20 ? "seamless_splice " : "");
if (flags & 0x80) {
GST_DEBUG ("legal time window: valid_flag:%d offset:%d", *data >> 7,
GST_READ_UINT16_BE (data) & 0x7fff);
data += 2;
}
}
#endif
return TRUE;
}
static MpegTSPacketizerPacketReturn
mpegts_packetizer_parse_packet (MpegTSPacketizer2 * packetizer,
MpegTSPacketizerPacket * packet)
{
guint8 *data;
guint8 tmp;
data = packet->data_start;
data += 1;
tmp = *data;
/* transport_error_indicator 1 */
if (G_UNLIKELY (tmp & 0x80))
return PACKET_BAD;
/* payload_unit_start_indicator 1 */
packet->payload_unit_start_indicator = tmp & 0x40;
/* transport_priority 1 */
/* PID 13 */
packet->pid = GST_READ_UINT16_BE (data) & 0x1FFF;
data += 2;
packet->scram_afc_cc = tmp = *data++;
/* transport_scrambling_control 2 */
if (G_UNLIKELY (tmp & 0xc0))
return PACKET_BAD;
packet->data = data;
packet->afc_flags = 0;
packet->pcr = G_MAXUINT64;
if (FLAGS_HAS_AFC (tmp)) {
if (!mpegts_packetizer_parse_adaptation_field_control (packetizer, packet))
return FALSE;
}
if (FLAGS_HAS_PAYLOAD (tmp))
packet->payload = packet->data;
else
packet->payload = NULL;
return PACKET_OK;
}
static GstMpegtsSection *
mpegts_packetizer_parse_section_header (MpegTSPacketizer2 * packetizer,
MpegTSPacketizerStream * stream)
{
MpegTSPacketizerStreamSubtable *subtable;
GstMpegtsSection *res;
subtable =
find_subtable (stream->subtables, stream->table_id,
stream->subtable_extension);
if (subtable) {
GST_DEBUG ("Found previous subtable_extension:0x%04x",
stream->subtable_extension);
if (G_UNLIKELY (stream->version_number != subtable->version_number)) {
/* If the version number changed, reset the subtable */
subtable->version_number = stream->version_number;
subtable->last_section_number = stream->last_section_number;
memset (subtable->seen_section, 0, 32);
}
} else {
GST_DEBUG ("Appending new subtable_extension: 0x%04x",
stream->subtable_extension);
subtable = mpegts_packetizer_stream_subtable_new (stream->table_id,
stream->subtable_extension, stream->last_section_number);
subtable->version_number = stream->version_number;
stream->subtables = g_slist_prepend (stream->subtables, subtable);
}
GST_MEMDUMP ("Full section data", stream->section_data,
stream->section_length);
/* TODO ? : Replace this by an efficient version (where we provide all
* pre-parsed header data) */
res =
gst_mpegts_section_new (stream->pid, stream->section_data,
stream->section_length);
stream->section_data = NULL;
mpegts_packetizer_clear_section (stream);
if (res) {
/* NOTE : Due to the new mpegts-si system, There is a insanely low probability
* that we might have gotten a section that was corrupted (i.e. wrong crc)
* and that we consider it as seen.
*
* The reason why we consider this as acceptable is because all the previous
* checks were already done:
* * transport layer checks (DVB)
* * 0x47 validation
* * continuity counter validation
* * subtable validation
* * section_number validation
* * section_length validation
*
* The probability of this happening vs the overhead of doing CRC checks
* on all sections (including those we would not use) is just not worth it.
* */
MPEGTS_BIT_SET (subtable->seen_section, stream->section_number);
res->offset = stream->offset;
}
return res;
}
void
mpegts_packetizer_clear (MpegTSPacketizer2 * packetizer)
{
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
guint i;
if (packetizer->packet_size)
packetizer->packet_size = 0;
if (packetizer->streams) {
int i;
for (i = 0; i < 8192; i++) {
if (packetizer->streams[i]) {
mpegts_packetizer_stream_free (packetizer->streams[i]);
}
}
memset (packetizer->streams, 0, 8192 * sizeof (MpegTSPacketizerStream *));
}
gst_adapter_clear (packetizer->adapter);
packetizer->offset = 0;
packetizer->empty = TRUE;
packetizer->need_sync = FALSE;
packetizer->map_data = NULL;
packetizer->map_size = 0;
packetizer->map_offset = 0;
packetizer->last_in_time = GST_CLOCK_TIME_NONE;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* Close current PCR group */
for (i = 0; i < MAX_PCR_OBS_CHANNELS; i++) {
if (packetizer->observations[i])
_close_current_group (packetizer->observations[i]);
else
break;
}
}
void
mpegts_packetizer_flush (MpegTSPacketizer2 * packetizer, gboolean hard)
{
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
guint i;
GST_DEBUG ("Flushing");
if (packetizer->streams) {
for (i = 0; i < 8192; i++) {
if (packetizer->streams[i]) {
mpegts_packetizer_clear_section (packetizer->streams[i]);
}
}
}
gst_adapter_clear (packetizer->adapter);
packetizer->offset = 0;
packetizer->empty = TRUE;
packetizer->need_sync = FALSE;
packetizer->map_data = NULL;
packetizer->map_size = 0;
packetizer->map_offset = 0;
packetizer->last_in_time = GST_CLOCK_TIME_NONE;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* Close current PCR group */
for (i = 0; i < MAX_PCR_OBS_CHANNELS; i++) {
if (packetizer->observations[i])
_close_current_group (packetizer->observations[i]);
else
break;
}
if (hard) {
/* For pull mode seeks in tsdemux the observation must be preserved */
flush_observations (packetizer);
}
}
void
mpegts_packetizer_remove_stream (MpegTSPacketizer2 * packetizer, gint16 pid)
{
MpegTSPacketizerStream *stream = packetizer->streams[pid];
if (stream) {
GST_INFO ("Removing stream for PID %d", pid);
mpegts_packetizer_stream_free (stream);
packetizer->streams[pid] = NULL;
}
}
MpegTSPacketizer2 *
mpegts_packetizer_new (void)
{
MpegTSPacketizer2 *packetizer;
packetizer =
GST_MPEGTS_PACKETIZER (g_object_new (GST_TYPE_MPEGTS_PACKETIZER, NULL));
return packetizer;
}
void
mpegts_packetizer_push (MpegTSPacketizer2 * packetizer, GstBuffer * buffer)
{
if (G_UNLIKELY (packetizer->empty)) {
packetizer->empty = FALSE;
packetizer->offset = GST_BUFFER_OFFSET (buffer);
}
GST_DEBUG ("Pushing %" G_GSIZE_FORMAT " byte from offset %"
G_GUINT64_FORMAT, gst_buffer_get_size (buffer),
GST_BUFFER_OFFSET (buffer));
gst_adapter_push (packetizer->adapter, buffer);
/* If buffer timestamp is valid, store it */
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer)))
packetizer->last_in_time = GST_BUFFER_TIMESTAMP (buffer);
}
static void
mpegts_packetizer_flush_bytes (MpegTSPacketizer2 * packetizer, gsize size)
{
if (size > 0) {
GST_LOG ("flushing %" G_GSIZE_FORMAT " bytes from adapter", size);
gst_adapter_flush (packetizer->adapter, size);
}
packetizer->map_data = NULL;
packetizer->map_size = 0;
packetizer->map_offset = 0;
}
static gboolean
mpegts_packetizer_map (MpegTSPacketizer2 * packetizer, gsize size)
{
gsize available;
if (packetizer->map_size - packetizer->map_offset >= size)
return TRUE;
mpegts_packetizer_flush_bytes (packetizer, packetizer->map_offset);
available = gst_adapter_available (packetizer->adapter);
if (available < size)
return FALSE;
packetizer->map_data =
(guint8 *) gst_adapter_map (packetizer->adapter, available);
if (!packetizer->map_data)
return FALSE;
packetizer->map_size = available;
packetizer->map_offset = 0;
GST_LOG ("mapped %" G_GSIZE_FORMAT " bytes from adapter", available);
return TRUE;
}
static gboolean
mpegts_try_discover_packet_size (MpegTSPacketizer2 * packetizer)
{
guint8 *data;
gsize size, i, j;
static const guint psizes[] = {
MPEGTS_NORMAL_PACKETSIZE,
MPEGTS_M2TS_PACKETSIZE,
MPEGTS_DVB_ASI_PACKETSIZE,
MPEGTS_ATSC_PACKETSIZE
};
if (!mpegts_packetizer_map (packetizer, 4 * MPEGTS_MAX_PACKETSIZE))
return FALSE;
size = packetizer->map_size - packetizer->map_offset;
data = packetizer->map_data + packetizer->map_offset;
for (i = 0; i + 3 * MPEGTS_MAX_PACKETSIZE < size; i++) {
/* find a sync byte */
if (data[i] != PACKET_SYNC_BYTE)
continue;
/* check for 4 consecutive sync bytes with each possible packet size */
for (j = 0; j < G_N_ELEMENTS (psizes); j++) {
guint packet_size = psizes[j];
if (data[i + packet_size] == PACKET_SYNC_BYTE &&
data[i + 2 * packet_size] == PACKET_SYNC_BYTE &&
data[i + 3 * packet_size] == PACKET_SYNC_BYTE) {
packetizer->packet_size = packet_size;
goto out;
}
}
}
out:
packetizer->map_offset += i;
if (packetizer->packet_size == 0) {
GST_DEBUG ("Could not determine packet size in %" G_GSIZE_FORMAT
" bytes buffer, flush %" G_GSIZE_FORMAT " bytes", size, i);
mpegts_packetizer_flush_bytes (packetizer, packetizer->map_offset);
return FALSE;
}
GST_INFO ("have packetsize detected: %u bytes", packetizer->packet_size);
if (packetizer->packet_size == MPEGTS_M2TS_PACKETSIZE &&
packetizer->map_offset >= 4)
packetizer->map_offset -= 4;
return TRUE;
}
static gboolean
mpegts_packetizer_sync (MpegTSPacketizer2 * packetizer)
{
gboolean found = FALSE;
guint8 *data;
guint packet_size;
gsize size, sync_offset, i;
packet_size = packetizer->packet_size;
if (!mpegts_packetizer_map (packetizer, 3 * packet_size))
return FALSE;
size = packetizer->map_size - packetizer->map_offset;
data = packetizer->map_data + packetizer->map_offset;
if (packet_size == MPEGTS_M2TS_PACKETSIZE)
sync_offset = 4;
else
sync_offset = 0;
for (i = sync_offset; i + 2 * packet_size < size; i++) {
if (data[i] == PACKET_SYNC_BYTE &&
data[i + packet_size] == PACKET_SYNC_BYTE &&
data[i + 2 * packet_size] == PACKET_SYNC_BYTE) {
found = TRUE;
break;
}
}
packetizer->map_offset += i - sync_offset;
if (!found)
mpegts_packetizer_flush_bytes (packetizer, packetizer->map_offset);
return found;
}
MpegTSPacketizerPacketReturn
mpegts_packetizer_next_packet (MpegTSPacketizer2 * packetizer,
MpegTSPacketizerPacket * packet)
{
guint8 *packet_data;
guint packet_size;
gsize sync_offset;
packet_size = packetizer->packet_size;
if (G_UNLIKELY (!packet_size)) {
if (!mpegts_try_discover_packet_size (packetizer))
return PACKET_NEED_MORE;
packet_size = packetizer->packet_size;
}
/* M2TS packets don't start with the sync byte, all other variants do */
if (packet_size == MPEGTS_M2TS_PACKETSIZE)
sync_offset = 4;
else
sync_offset = 0;
while (1) {
if (packetizer->need_sync) {
if (!mpegts_packetizer_sync (packetizer))
return PACKET_NEED_MORE;
packetizer->need_sync = FALSE;
}
if (!mpegts_packetizer_map (packetizer, packet_size))
return PACKET_NEED_MORE;
packet_data = &packetizer->map_data[packetizer->map_offset + sync_offset];
/* Check sync byte */
if (G_UNLIKELY (*packet_data != PACKET_SYNC_BYTE)) {
GST_DEBUG ("lost sync");
packetizer->need_sync = TRUE;
} else {
/* ALL mpeg-ts variants contain 188 bytes of data. Those with bigger
* packet sizes contain either extra data (timesync, FEC, ..) either
* before or after the data */
packet->data_start = packet_data;
packet->data_end = packet->data_start + 188;
packet->offset = packetizer->offset;
GST_LOG ("offset %" G_GUINT64_FORMAT, packet->offset);
packetizer->offset += packet_size;
GST_MEMDUMP ("data_start", packet->data_start, 16);
return mpegts_packetizer_parse_packet (packetizer, packet);
}
}
}
MpegTSPacketizerPacketReturn
mpegts_packetizer_process_next_packet (MpegTSPacketizer2 * packetizer)
{
MpegTSPacketizerPacket packet;
MpegTSPacketizerPacketReturn ret;
ret = mpegts_packetizer_next_packet (packetizer, &packet);
if (ret != PACKET_NEED_MORE)
mpegts_packetizer_clear_packet (packetizer, &packet);
return ret;
}
void
mpegts_packetizer_clear_packet (MpegTSPacketizer2 * packetizer,
MpegTSPacketizerPacket * packet)
{
guint8 packet_size = packetizer->packet_size;
if (packetizer->map_data) {
packetizer->map_offset += packet_size;
if (packetizer->map_size - packetizer->map_offset < packet_size)
mpegts_packetizer_flush_bytes (packetizer, packetizer->map_offset);
}
}
gboolean
mpegts_packetizer_has_packets (MpegTSPacketizer2 * packetizer)
{
if (G_UNLIKELY (!packetizer->packet_size)) {
if (!mpegts_try_discover_packet_size (packetizer))
return FALSE;
}
return gst_adapter_available (packetizer->adapter) >= packetizer->packet_size;
}
/*
* Ideally it should just return a section if:
* * The section is complete
* * The section is valid (sanity checks for length for example)
* * The section applies now (current_next_indicator)
* * The section is an update or was never seen
*
* The section should be a new GstMpegtsSection:
* * properly initialized
* * With pid, table_id AND section_type set (move logic from mpegtsbase)
* * With data copied into it (yes, minor overhead)
*
* In all other cases it should just return NULL
*
* If more than one section is available, the 'remaining' field will
* be set to the beginning of a valid GList containing other sections.
* */
GstMpegtsSection *
mpegts_packetizer_push_section (MpegTSPacketizer2 * packetizer,
MpegTSPacketizerPacket * packet, GList ** remaining)
{
GstMpegtsSection *section;
GstMpegtsSection *res = NULL;
MpegTSPacketizerStream *stream;
gboolean long_packet;
guint8 pointer = 0, table_id;
guint16 subtable_extension = 0;
gsize to_read;
guint section_length;
/* data points to the current read location
* data_start points to the beginning of the data to accumulate */
guint8 *data, *data_start;
guint8 packet_cc;
GList *others = NULL;
guint8 version_number, section_number, last_section_number;
data = packet->data;
packet_cc = FLAGS_CONTINUITY_COUNTER (packet->scram_afc_cc);
/* Get our filter */
stream = packetizer->streams[packet->pid];
if (G_UNLIKELY (stream == NULL)) {
if (!packet->payload_unit_start_indicator) {
/* Early exit (we need to start with a section start) */
GST_DEBUG ("PID 0x%04x waiting for section start", packet->pid);
goto out;
}
stream = mpegts_packetizer_stream_new (packet->pid);
packetizer->streams[packet->pid] = stream;
}
GST_MEMDUMP ("Full packet data", packet->data,
packet->data_end - packet->data);
/* This function is split into several parts:
*
* Pre checks (packet-wide). Determines where we go next
* accumulate_data: store data and check if section is complete
* section_start: handle beginning of a section, if needed loop back to
* accumulate_data
*
* The trigger that makes the loop stop and return is if:
* 1) We do not have enough data for the current packet
* 2) There is remaining data after a packet which is only made
* of stuffing bytes (0xff).
*
* Pre-loop checks, related to the whole incoming packet:
*
* If there is a CC-discont:
* If it is a PUSI, skip the pointer and handle section_start
* If not a PUSI, reset and return nothing
* If there is not a CC-discont:
* If it is a PUSI
* If pointer, accumulate that data and check for complete section
* (loop)
* If it is not a PUSI
* Accumulate the expected data and check for complete section
* (loop)
*
**/
if (packet->payload_unit_start_indicator) {
pointer = *data++;
/* If the pointer is zero, we're guaranteed to be able to handle it */
if (pointer == 0) {
GST_LOG
("PID 0x%04x PUSI and pointer == 0, skipping straight to section_start parsing",
packet->pid);
goto section_start;
}
}
if (stream->continuity_counter == CONTINUITY_UNSET ||
(stream->continuity_counter + 1) % 16 != packet_cc) {
if (stream->continuity_counter != CONTINUITY_UNSET)
GST_WARNING ("PID 0x%04x section discontinuity (%d vs %d)", packet->pid,
stream->continuity_counter, packet_cc);
mpegts_packetizer_clear_section (stream);
/* If not a PUSI, not much we can do */
if (!packet->payload_unit_start_indicator) {
GST_LOG ("PID 0x%04x continuity discont/unset and not PUSI, bailing out",
packet->pid);
goto out;
}
/* If PUSI, skip pointer data and carry on to section start */
data += pointer;
pointer = 0;
GST_LOG ("discont, but PUSI, skipped %d bytes and doing section start",
pointer);
goto section_start;
}
GST_LOG ("Accumulating data from beginning of packet");
data_start = data;
accumulate_data:
/* If not the beginning of a new section, accumulate what we have */
stream->continuity_counter = packet_cc;
to_read = MIN (stream->section_length - stream->section_offset,
packet->data_end - data_start);
memcpy (stream->section_data + stream->section_offset, data_start, to_read);
stream->section_offset += to_read;
/* Point data to after the data we accumulated */
data = data_start + to_read;
GST_DEBUG ("Appending data (need %d, have %d)", stream->section_length,
stream->section_offset);
/* Check if we have enough */
if (stream->section_offset < stream->section_length) {
GST_DEBUG ("PID 0x%04x, section not complete (Got %d, need %d)",
stream->pid, stream->section_offset, stream->section_length);
goto out;
}
/* Small sanity check. We should have collected *exactly* the right amount */
if (G_UNLIKELY (stream->section_offset != stream->section_length))
GST_WARNING ("PID 0x%04x Accumulated too much data (%d vs %d) !",
stream->pid, stream->section_offset, stream->section_length);
GST_DEBUG ("PID 0x%04x Section complete", stream->pid);
if ((section = mpegts_packetizer_parse_section_header (packetizer, stream))) {
if (res)
others = g_list_append (others, section);
else
res = section;
}
/* FIXME : We need at least 8 bytes with current algorithm :(
* We might end up losing sections that start across two packets (srsl...) */
if (data > packet->data_end - 8 || *data == 0xff) {
/* flush stuffing bytes and leave */
mpegts_packetizer_clear_section (stream);
goto out;
}
/* We have more data to process ... */
GST_DEBUG ("PID 0x%04x, More section present in packet (remaining bytes:%"
G_GSIZE_FORMAT ")", stream->pid, packet->data_end - data);
section_start:
GST_MEMDUMP ("section_start", data, packet->data_end - data);
data_start = data;
/* Beginning of a new section */
/*
* section_syntax_indicator means that the header is of the following format:
* * table_id (8bit)
* * section_syntax_indicator (1bit) == 0
* * reserved/private fields (3bit)
* * section_length (12bit)
* * data (of size section_length)
* * NO CRC !
*/
long_packet = data[1] & 0x80;
/* Fast path for short packets */
if (!long_packet) {
/* We can create the section now (function will check for size) */
GST_DEBUG ("Short packet");
section_length = (GST_READ_UINT16_BE (data + 1) & 0xfff) + 3;
/* Only do fast-path if we have enough byte */
if (section_length < packet->data_end - data) {
if ((section =
gst_mpegts_section_new (packet->pid, g_memdup (data,
section_length), section_length))) {
GST_DEBUG ("PID 0x%04x Short section complete !", packet->pid);
section->offset = packet->offset;
if (res)
others = g_list_append (others, section);
else
res = section;
}
/* Advance reader and potentially read another section */
data += section_length;
if (data < packet->data_end && *data != 0xff)
goto section_start;
/* If not, exit */
goto out;
}
/* We don't have enough bytes to do short section shortcut */
}
/* Beginning of a new section, do as much pre-parsing as possible */
/* table_id : 8 bit */
table_id = *data++;
/* section_syntax_indicator : 1 bit
* other_fields (reserved) : 3 bit
* section_length : 12 bit */
section_length = (GST_READ_UINT16_BE (data) & 0x0FFF) + 3;
data += 2;
if (long_packet) {
/* subtable_extension (always present, we are in a long section) */
/* subtable extension : 16 bit */
subtable_extension = GST_READ_UINT16_BE (data);
data += 2;
/* reserved : 2 bit
* version_number : 5 bit
* current_next_indicator : 1 bit */
/* Bail out now if current_next_indicator == 0 */
if (G_UNLIKELY (!(*data & 0x01))) {
GST_DEBUG
("PID 0x%04x table_id 0x%02x section does not apply (current_next_indicator == 0)",
packet->pid, table_id);
goto out;
}
version_number = *data++ >> 1 & 0x1f;
/* section_number : 8 bit */
section_number = *data++;
/* last_section_number : 8 bit */
last_section_number = *data++;
} else {
subtable_extension = 0;
version_number = 0;
section_number = 0;
last_section_number = 0;
}
GST_DEBUG
("PID 0x%04x length:%d table_id:0x%02x subtable_extension:0x%04x version_number:%d section_number:%d(last:%d)",
packet->pid, section_length, table_id, subtable_extension, version_number,
section_number, last_section_number);
to_read = MIN (section_length, packet->data_end - data_start);
/* Check as early as possible whether we already saw this section
* i.e. that we saw a subtable with:
* * same subtable_extension (might be zero)
* * same version_number
* * same last_section_number
* * same section_number was seen
*/
if (seen_section_before (stream, table_id, subtable_extension,
version_number, section_number, last_section_number)) {
GST_DEBUG
("PID 0x%04x Already processed table_id:0x%02x subtable_extension:0x%04x, version_number:%d, section_number:%d",
packet->pid, table_id, subtable_extension, version_number,
section_number);
/* skip data and see if we have more sections after */
data = data_start + to_read;
if (data == packet->data_end || *data == 0xff)
goto out;
goto section_start;
}
if (G_UNLIKELY (section_number > last_section_number)) {
GST_WARNING
("PID 0x%04x corrupted packet (section_number:%d > last_section_number:%d)",
packet->pid, section_number, last_section_number);
goto out;
}
/* Copy over already parsed values */
stream->table_id = table_id;
stream->section_length = section_length;
stream->version_number = version_number;
stream->subtable_extension = subtable_extension;
stream->section_number = section_number;
stream->last_section_number = last_section_number;
stream->offset = packet->offset;
/* Create enough room to store chunks of sections */
stream->section_data = g_malloc (stream->section_length);
stream->section_offset = 0;
/* Finally, accumulate and check if we parsed enough */
goto accumulate_data;
out:
packet->data = data;
*remaining = others;
GST_DEBUG ("result: %p", res);
return res;
}
static void
_init_local (void)
{
GST_DEBUG_CATEGORY_INIT (mpegts_packetizer_debug, "mpegtspacketizer", 0,
"MPEG transport stream parser");
}
static void
mpegts_packetizer_resync (MpegTSPCR * pcr, GstClockTime time,
GstClockTime gstpcrtime, gboolean reset_skew)
{
pcr->base_time = time;
pcr->base_pcrtime = gstpcrtime;
pcr->prev_out_time = GST_CLOCK_TIME_NONE;
pcr->prev_send_diff = GST_CLOCK_TIME_NONE;
if (reset_skew) {
pcr->window_filling = TRUE;
pcr->window_pos = 0;
pcr->window_min = 0;
pcr->window_size = 0;
pcr->skew = 0;
}
}
/* Code mostly copied from -good/gst/rtpmanager/rtpjitterbuffer.c */
/* For the clock skew we use a windowed low point averaging algorithm as can be
* found in Fober, Orlarey and Letz, 2005, "Real Time Clock Skew Estimation
* over Network Delays":
* http://www.grame.fr/Ressources/pub/TR-050601.pdf
* http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.102.1546
*
* The idea is that the jitter is composed of:
*
* J = N + n
*
* D : a constant network delay.
* n : random added noise. The noise is concentrated around 0
*
* In the receiver we can track the elapsed time at the sender with:
*
* send_diff(i) = (Tsi - Ts0);
*
* Tsi : The time at the sender at packet i
* Ts0 : The time at the sender at the first packet
*
* This is the difference between the RTP timestamp in the first received packet
* and the current packet.
*
* At the receiver we have to deal with the jitter introduced by the network.
*
* recv_diff(i) = (Tri - Tr0)
*
* Tri : The time at the receiver at packet i
* Tr0 : The time at the receiver at the first packet
*
* Both of these values contain a jitter Ji, a jitter for packet i, so we can
* write:
*
* recv_diff(i) = (Cri + D + ni) - (Cr0 + D + n0))
*
* Cri : The time of the clock at the receiver for packet i
* D + ni : The jitter when receiving packet i
*
* We see that the network delay is irrelevant here as we can elliminate D:
*
* recv_diff(i) = (Cri + ni) - (Cr0 + n0))
*
* The drift is now expressed as:
*
* Drift(i) = recv_diff(i) - send_diff(i);
*
* We now keep the W latest values of Drift and find the minimum (this is the
* one with the lowest network jitter and thus the one which is least affected
* by it). We average this lowest value to smooth out the resulting network skew.
*
* Both the window and the weighting used for averaging influence the accuracy
* of the drift estimation. Finding the correct parameters turns out to be a
* compromise between accuracy and inertia.
*
* We use a 2 second window or up to 512 data points, which is statistically big
* enough to catch spikes (FIXME, detect spikes).
* We also use a rather large weighting factor (125) to smoothly adapt. During
* startup, when filling the window, we use a parabolic weighting factor, the
* more the window is filled, the faster we move to the detected possible skew.
*
* Returns: @time adjusted with the clock skew.
*/
static GstClockTime
calculate_skew (MpegTSPCR * pcr, guint64 pcrtime, GstClockTime time)
{
guint64 send_diff, recv_diff;
gint64 delta;
gint64 old;
gint pos, i;
GstClockTime gstpcrtime, out_time;
#ifndef GST_DISABLE_GST_DEBUG
guint64 slope;
#endif
gstpcrtime = PCRTIME_TO_GSTTIME (pcrtime) + pcr->pcroffset;
/* first time, lock on to time and gstpcrtime */
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pcr->base_time))) {
pcr->base_time = time;
pcr->prev_out_time = GST_CLOCK_TIME_NONE;
GST_DEBUG ("Taking new base time %" GST_TIME_FORMAT, GST_TIME_ARGS (time));
}
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pcr->base_pcrtime))) {
pcr->base_pcrtime = gstpcrtime;
pcr->prev_send_diff = -1;
GST_DEBUG ("Taking new base pcrtime %" GST_TIME_FORMAT,
GST_TIME_ARGS (gstpcrtime));
}
/* Handle PCR wraparound and resets */
if (GST_CLOCK_TIME_IS_VALID (pcr->last_pcrtime) &&
gstpcrtime < pcr->last_pcrtime) {
if (pcr->last_pcrtime - gstpcrtime > PCR_GST_MAX_VALUE / 2) {
/* PCR wraparound */
GST_DEBUG ("PCR wrap");
pcr->pcroffset += PCR_GST_MAX_VALUE;
gstpcrtime = PCRTIME_TO_GSTTIME (pcrtime) + pcr->pcroffset;
send_diff = gstpcrtime - pcr->base_pcrtime;
} else if (GST_CLOCK_TIME_IS_VALID (time)
&& pcr->last_pcrtime - gstpcrtime > 15 * GST_SECOND) {
/* Assume a reset */
GST_DEBUG ("PCR reset");
/* Calculate PCR we would have expected for the given input time,
* essentially applying the reverse correction process
*
* We want to find the PCR offset to apply
* pcroffset = (corrected) gstpcrtime - (received) gstpcrtime
*
* send_diff = (corrected) gstpcrtime - pcr->base_pcrtime
* recv_diff = time - pcr->base_time
* out_time = pcr->base_time + send_diff
*
* We are assuming that send_diff == recv_diff
* (corrected) gstpcrtime - pcr->base_pcrtime = time - pcr->base_time
* Giving us:
* (corrected) gstpcrtime = time - pcr->base_time + pcr->base_pcrtime
*
* And therefore:
* pcroffset = time - pcr->base_time + pcr->base_pcrtime - (received) gstpcrtime
**/
pcr->pcroffset += time - pcr->base_time + pcr->base_pcrtime - gstpcrtime;
gstpcrtime = PCRTIME_TO_GSTTIME (pcrtime) + pcr->pcroffset;
send_diff = gstpcrtime - pcr->base_pcrtime;
GST_DEBUG ("Introduced offset is now %" GST_TIME_FORMAT
" corrected pcr time %" GST_TIME_FORMAT,
GST_TIME_ARGS (pcr->pcroffset), GST_TIME_ARGS (gstpcrtime));
} else {
GST_WARNING ("backward timestamps at server but no timestamps");
send_diff = 0;
/* at least try to get a new timestamp.. */
pcr->base_time = GST_CLOCK_TIME_NONE;
}
} else
send_diff = gstpcrtime - pcr->base_pcrtime;
GST_DEBUG ("gstpcr %" GST_TIME_FORMAT ", buftime %" GST_TIME_FORMAT
", base %" GST_TIME_FORMAT ", send_diff %" GST_TIME_FORMAT,
GST_TIME_ARGS (gstpcrtime), GST_TIME_ARGS (time),
GST_TIME_ARGS (pcr->base_pcrtime), GST_TIME_ARGS (send_diff));
/* keep track of the last extended pcrtime */
pcr->last_pcrtime = gstpcrtime;
/* we don't have an arrival timestamp so we can't do skew detection. we
* should still apply a timestamp based on RTP timestamp and base_time */
if (!GST_CLOCK_TIME_IS_VALID (time)
|| !GST_CLOCK_TIME_IS_VALID (pcr->base_time))
goto no_skew;
/* elapsed time at receiver, includes the jitter */
recv_diff = time - pcr->base_time;
/* Ignore packets received at 100% the same time (i.e. from the same input buffer) */
if (G_UNLIKELY (time == pcr->prev_in_time
&& GST_CLOCK_TIME_IS_VALID (pcr->prev_in_time)))
goto no_skew;
/* measure the diff */
delta = ((gint64) recv_diff) - ((gint64) send_diff);
#ifndef GST_DISABLE_GST_DEBUG
/* measure the slope, this gives a rought estimate between the sender speed
* and the receiver speed. This should be approximately 8, higher values
* indicate a burst (especially when the connection starts) */
slope = recv_diff > 0 ? (send_diff * 8) / recv_diff : 8;
#endif
GST_DEBUG ("time %" GST_TIME_FORMAT ", base %" GST_TIME_FORMAT
", recv_diff %" GST_TIME_FORMAT ", slope %" G_GUINT64_FORMAT,
GST_TIME_ARGS (time), GST_TIME_ARGS (pcr->base_time),
GST_TIME_ARGS (recv_diff), slope);
/* if the difference between the sender timeline and the receiver timeline
* changed too quickly we have to resync because the server likely restarted
* its timestamps. */
if (ABS (delta - pcr->skew) > GST_SECOND) {
GST_WARNING ("delta - skew: %" GST_TIME_FORMAT " too big, reset skew",
GST_TIME_ARGS (delta - pcr->skew));
mpegts_packetizer_resync (pcr, time, gstpcrtime, TRUE);
send_diff = 0;
delta = 0;
}
pos = pcr->window_pos;
if (G_UNLIKELY (pcr->window_filling)) {
/* we are filling the window */
GST_DEBUG ("filling %d, delta %" G_GINT64_FORMAT, pos, delta);
pcr->window[pos++] = delta;
/* calc the min delta we observed */
if (G_UNLIKELY (pos == 1 || delta < pcr->window_min))
pcr->window_min = delta;
if (G_UNLIKELY (send_diff >= MAX_TIME || pos >= MAX_WINDOW)) {
pcr->window_size = pos;
/* window filled */
GST_DEBUG ("min %" G_GINT64_FORMAT, pcr->window_min);
/* the skew is now the min */
pcr->skew = pcr->window_min;
pcr->window_filling = FALSE;
} else {
gint perc_time, perc_window, perc;
/* figure out how much we filled the window, this depends on the amount of
* time we have or the max number of points we keep. */
perc_time = send_diff * 100 / MAX_TIME;
perc_window = pos * 100 / MAX_WINDOW;
perc = MAX (perc_time, perc_window);
/* make a parabolic function, the closer we get to the MAX, the more value
* we give to the scaling factor of the new value */
perc = perc * perc;
/* quickly go to the min value when we are filling up, slowly when we are
* just starting because we're not sure it's a good value yet. */
pcr->skew =
(perc * pcr->window_min + ((10000 - perc) * pcr->skew)) / 10000;
pcr->window_size = pos + 1;
}
} else {
/* pick old value and store new value. We keep the previous value in order
* to quickly check if the min of the window changed */
old = pcr->window[pos];
pcr->window[pos++] = delta;
if (G_UNLIKELY (delta <= pcr->window_min)) {
/* if the new value we inserted is smaller or equal to the current min,
* it becomes the new min */
pcr->window_min = delta;
} else if (G_UNLIKELY (old == pcr->window_min)) {
gint64 min = G_MAXINT64;
/* if we removed the old min, we have to find a new min */
for (i = 0; i < pcr->window_size; i++) {
/* we found another value equal to the old min, we can stop searching now */
if (pcr->window[i] == old) {
min = old;
break;
}
if (pcr->window[i] < min)
min = pcr->window[i];
}
pcr->window_min = min;
}
/* average the min values */
pcr->skew = (pcr->window_min + (124 * pcr->skew)) / 125;
GST_DEBUG ("delta %" G_GINT64_FORMAT ", new min: %" G_GINT64_FORMAT,
delta, pcr->window_min);
}
/* wrap around in the window */
if (G_UNLIKELY (pos >= pcr->window_size))
pos = 0;
pcr->window_pos = pos;
no_skew:
/* the output time is defined as the base timestamp plus the PCR time
* adjusted for the clock skew .*/
if (pcr->base_time != -1) {
out_time = pcr->base_time + send_diff;
/* skew can be negative and we don't want to make invalid timestamps */
if (pcr->skew < 0 && out_time < -pcr->skew) {
out_time = 0;
} else {
out_time += pcr->skew;
}
/* check if timestamps are not going backwards, we can only check this if we
* have a previous out time and a previous send_diff */
if (G_LIKELY (pcr->prev_out_time != -1 && pcr->prev_send_diff != -1)) {
/* now check for backwards timestamps */
if (G_UNLIKELY (
/* if the server timestamps went up and the out_time backwards */
(send_diff > pcr->prev_send_diff
&& out_time < pcr->prev_out_time) ||
/* if the server timestamps went backwards and the out_time forwards */
(send_diff < pcr->prev_send_diff
&& out_time > pcr->prev_out_time) ||
/* if the server timestamps did not change */
send_diff == pcr->prev_send_diff)) {
GST_DEBUG ("backwards timestamps, using previous time");
out_time = GSTTIME_TO_MPEGTIME (out_time);
}
}
} else {
/* We simply use the pcrtime without applying any skew compensation */
out_time = time;
}
pcr->prev_out_time = out_time;
pcr->prev_in_time = time;
pcr->prev_send_diff = send_diff;
GST_DEBUG ("skew %" G_GINT64_FORMAT ", out %" GST_TIME_FORMAT,
pcr->skew, GST_TIME_ARGS (out_time));
return out_time;
}
static void
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
_reevaluate_group_pcr_offset (MpegTSPCR * pcrtable, PCROffsetGroup * group)
{
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
PCROffsetGroup *prev = NULL;
#ifndef GST_DISABLE_GST_DEBUG
PCROffsetGroup *first = pcrtable->groups->data;
#endif
PCROffsetCurrent *current = pcrtable->current;
GList *tmp;
/* Go over all ESTIMATED groups until the target group */
for (tmp = pcrtable->groups; tmp; tmp = tmp->next) {
PCROffsetGroup *cur = (PCROffsetGroup *) tmp->data;
/* Skip groups that don't need re-evaluation */
if (!(cur->flags & PCR_GROUP_FLAG_ESTIMATED)) {
GST_DEBUG ("Skipping group %p pcr_offset (currently %" GST_TIME_FORMAT
")", cur, GST_TIME_ARGS (PCRTIME_TO_GSTTIME (cur->pcr_offset)));
prev = cur;
continue;
}
/* This should not happen ! The first group is *always* correct (zero) */
if (G_UNLIKELY (prev == NULL)) {
GST_ERROR ("First PCR Group was not estimated (bug). Setting to zero");
cur->pcr_offset = 0;
cur->flags &= ~PCR_GROUP_FLAG_ESTIMATED;
return;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* Finally do the estimation of this group's PCR offset based on the
* previous group information */
GST_DEBUG ("Re-evaluating group %p pcr_offset (currently %" GST_TIME_FORMAT
")", group, GST_TIME_ARGS (PCRTIME_TO_GSTTIME (cur->pcr_offset)));
GST_DEBUG ("cur->first_pcr:%" GST_TIME_FORMAT " prev->first_pcr:%"
GST_TIME_FORMAT, GST_TIME_ARGS (PCRTIME_TO_GSTTIME (cur->first_pcr)),
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (prev->first_pcr)));
if (G_UNLIKELY (cur->first_pcr < prev->first_pcr)) {
guint64 prevbr, lastbr;
guint64 prevpcr;
guint64 prevoffset, lastoffset;
/* Take the previous group pcr_offset and figure out how much to add
* to it for the current group */
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* Right now we do a dumb bitrate estimation
* estimate bitrate (prev - first) : bitrate from the start
* estimate bitrate (prev) : bitrate of previous group
* estimate bitrate (last - first) : bitrate from previous group
*
* We will use raw (non-corrected/non-absolute) PCR values in a first time
* to detect wraparound/resets/gaps...
*
* We will use the corrected/asolute PCR values to calculate
* bitrate and estimate the target group pcr_offset.
* */
/* If the current window estimator is over the previous group, used those
* values as the latest (since they are more recent) */
if (current->group == prev && current->pending[current->last].offset) {
prevoffset =
current->pending[current->last].offset + prev->first_offset;
prevpcr = current->pending[current->last].pcr + prev->first_pcr;
/* prevbr: bitrate(prev) */
prevbr =
gst_util_uint64_scale (PCR_SECOND,
current->pending[current->last].offset,
current->pending[current->last].pcr);
GST_DEBUG ("Previous group bitrate (%u / %" GST_TIME_FORMAT ") : %"
G_GUINT64_FORMAT, current->pending[current->last].offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (current->pending[current->
last].pcr)), prevbr);
} else if (prev->values[prev->last_value].offset) {
prevoffset = prev->values[prev->last_value].offset + prev->first_offset;
prevpcr = prev->values[prev->last_value].pcr + prev->first_pcr;
/* prevbr: bitrate(prev) (FIXME : Cache) */
prevbr =
gst_util_uint64_scale (PCR_SECOND,
prev->values[prev->last_value].offset,
prev->values[prev->last_value].pcr);
GST_DEBUG ("Previous group bitrate (%u / %" GST_TIME_FORMAT ") : %"
G_GUINT64_FORMAT, prev->values[prev->last_value].offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (prev->values[prev->
last_value].pcr)), prevbr);
} else {
GST_DEBUG ("Using overall bitrate");
prevoffset = prev->values[prev->last_value].offset + prev->first_offset;
prevpcr = prev->values[prev->last_value].pcr + prev->first_pcr;
prevbr = gst_util_uint64_scale (PCR_SECOND,
prev->first_offset, prev->pcr_offset);
}
lastoffset = cur->values[cur->last_value].offset + cur->first_offset;
GST_DEBUG ("Offset first:%" G_GUINT64_FORMAT " prev:%" G_GUINT64_FORMAT
" cur:%" G_GUINT64_FORMAT, first->first_offset, prevoffset,
lastoffset);
GST_DEBUG ("PCR first:%" GST_TIME_FORMAT " prev:%" GST_TIME_FORMAT
" cur:%" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (first->first_pcr)),
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (prevpcr)),
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (cur->values[cur->last_value].pcr +
cur->first_pcr)));
if (prevpcr - cur->first_pcr > (PCR_MAX_VALUE * 9 / 10)) {
gfloat diffprev;
guint64 guess_offset;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* Let's assume there is a PCR wraparound between the previous and current
* group.
* [ prev ]... PCR_MAX | 0 ...[ current ]
* The estimated pcr_offset would therefore be:
* current.first + (PCR_MAX_VALUE - prev.first)
*
* 1) Check if bitrate(prev) would be consistent with bitrate (cur - prev)
*/
guess_offset = PCR_MAX_VALUE - prev->first_pcr + cur->first_pcr;
lastbr = gst_util_uint64_scale (PCR_SECOND, lastoffset - prevoffset,
guess_offset + cur->values[cur->last_value].pcr - (prevpcr -
prev->first_pcr));
GST_DEBUG ("Wraparound prev-cur (guess_offset:%" GST_TIME_FORMAT
") bitrate:%" G_GUINT64_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (guess_offset)), lastbr);
diffprev = (float) 100.0 *(ABSDIFF (prevbr, lastbr)) / (float) prevbr;
GST_DEBUG ("Difference with previous bitrate:%f", diffprev);
if (diffprev < 10.0) {
GST_DEBUG ("Difference < 10.0, Setting pcr_offset to %"
G_GUINT64_FORMAT, guess_offset);
cur->pcr_offset = guess_offset;
if (diffprev < 1.0) {
GST_DEBUG ("Difference < 1.0, Removing ESTIMATED flags");
cur->flags &= ~PCR_GROUP_FLAG_ESTIMATED;
}
}
/* Indicate the the previous group is before a wrapover */
prev->flags |= PCR_GROUP_FLAG_WRAPOVER;
} else {
guint64 resetprev;
/* Let's assume there was a PCR reset between the previous and current
* group
* [ prev ] ... x | x - reset ... [ current ]
*
* The estimated pcr_offset would then be
* = current.first - (x - reset) + (x - prev.first) + 100ms (for safety)
* = current.first + reset - prev.first + 100ms (for safety)
*/
/* In order to calculate the reset, we estimate what the PCR would have
* been by using prevbr */
/* FIXME : Which bitrate should we use ??? */
GST_DEBUG ("Using prevbr:%" G_GUINT64_FORMAT " and taking offsetdiff:%"
G_GUINT64_FORMAT, prevbr, cur->first_offset - prev->first_offset);
resetprev =
gst_util_uint64_scale (PCR_SECOND,
cur->first_offset - prev->first_offset, prevbr);
GST_DEBUG ("Estimated full PCR for offset %" G_GUINT64_FORMAT
", using prevbr:%"
GST_TIME_FORMAT, cur->first_offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (resetprev)));
cur->pcr_offset = prev->pcr_offset + resetprev + 100 * PCR_MSECOND;
GST_DEBUG ("Adjusted group PCR_offset to %" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (cur->pcr_offset)));
/* Indicate the the previous group is before a reset */
prev->flags |= PCR_GROUP_FLAG_RESET;
}
} else {
/* FIXME : Detect gaps if bitrate difference is really too big ? */
cur->pcr_offset = prev->pcr_offset + cur->first_pcr - prev->first_pcr;
GST_DEBUG ("Assuming there is no gap, setting pcr_offset to %"
GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (cur->pcr_offset)));
/* Remove the reset and wrapover flag (if it was previously there) */
prev->flags &= ~PCR_GROUP_FLAG_RESET;
prev->flags &= ~PCR_GROUP_FLAG_WRAPOVER;
}
/* Remember prev for the next group evaluation */
prev = cur;
}
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
static PCROffsetGroup *
_new_group (guint64 pcr, guint64 offset, guint64 pcr_offset, guint flags)
{
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
PCROffsetGroup *group = g_slice_new0 (PCROffsetGroup);
GST_DEBUG ("Input PCR %" GST_TIME_FORMAT " offset:%" G_GUINT64_FORMAT
" pcr_offset:%" G_GUINT64_FORMAT " flags:%d",
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (pcr)), offset, pcr_offset, flags);
group->flags = flags;
group->values = g_new0 (PCROffset, DEFAULT_ALLOCATED_OFFSET);
/* The first pcr/offset diff is always 0/0 */
group->values[0].pcr = group->values[0].offset = 0;
group->nb_allocated = DEFAULT_ALLOCATED_OFFSET;
/* Store the full values */
group->first_pcr = pcr;
group->first_offset = offset;
group->pcr_offset = pcr_offset;
GST_DEBUG ("Created group starting with pcr:%" GST_TIME_FORMAT " offset:%"
G_GUINT64_FORMAT " pcr_offset:%" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->first_pcr)),
group->first_offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->pcr_offset)));
return group;
}
static void
_insert_group_after (MpegTSPCR * pcrtable, PCROffsetGroup * group,
PCROffsetGroup * prev)
{
if (prev == NULL) {
/* First group */
pcrtable->groups = g_list_prepend (pcrtable->groups, group);
} else {
GList *tmp, *toinsert, *prevlist = NULL, *nextlist = NULL;
/* Insert before next and prev */
for (tmp = pcrtable->groups; tmp; tmp = tmp->next) {
if (tmp->data == prev) {
prevlist = tmp;
nextlist = tmp->next;
break;
}
}
if (!prevlist) {
/* The non NULL prev given isn't in the list */
GST_WARNING ("Request to insert before a group which isn't in the list");
pcrtable->groups = g_list_prepend (pcrtable->groups, group);
} else {
toinsert = g_list_append (NULL, group);
toinsert->next = nextlist;
toinsert->prev = prevlist;
prevlist->next = toinsert;
if (nextlist)
nextlist->prev = toinsert;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
}
}
static void
_use_group (MpegTSPCR * pcrtable, PCROffsetGroup * group)
{
PCROffsetCurrent *current = pcrtable->current;
memset (current, 0, sizeof (PCROffsetCurrent));
current->group = group;
current->pending[0] = group->values[group->last_value];
current->last_value = current->pending[0];
current->write = 1;
current->prev = group->values[group->last_value];
current->first_pcr = group->first_pcr;
current->first_offset = group->first_offset;
}
/* Create a new group with the specified values after prev
* Set current to that new group */
static void
_set_current_group (MpegTSPCR * pcrtable,
PCROffsetGroup * prev, guint64 pcr, guint64 offset, gboolean contiguous)
{
PCROffsetGroup *group;
guint flags = 0;
guint64 pcr_offset = 0;
/* Handle wraparound/gap (only if contiguous with previous group) */
if (contiguous) {
guint64 lastpcr = prev->first_pcr + prev->values[prev->last_value].pcr;
/* Set CLOSED flag on previous group and remember pcr_offset */
prev->flags |= PCR_GROUP_FLAG_CLOSED;
pcr_offset = prev->pcr_offset;
/* Wraparound ? */
if (lastpcr > pcr) {
/* In offset-mode, a PCR wraparound is only actually consistent if
* we have a very high confidence (99% right now, might need to change
* later) */
if (lastpcr - pcr > (PCR_MAX_VALUE * 99 / 100)) {
GST_WARNING ("WRAPAROUND detected. diff %" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (lastpcr - pcr)));
/* The previous group closed at PCR_MAX_VALUE */
pcr_offset += PCR_MAX_VALUE - prev->first_pcr + pcr;
} else {
GST_WARNING ("RESET detected. diff %" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (lastpcr - pcr)));
/* The previous group closed at the raw last_pcr diff (+100ms for safety) */
pcr_offset += prev->values[prev->last_value].pcr + 100 * PCR_MSECOND;
}
} else if (lastpcr < pcr - 500 * PCR_MSECOND) {
GST_WARNING ("GAP detected. diff %" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (pcr - lastpcr)));
/* The previous group closed at the raw last_pcr diff (+500ms for safety) */
pcr_offset += prev->values[prev->last_value].pcr + 500 * PCR_MSECOND;
} else
/* Normal continuation (contiguous in time) */
pcr_offset += pcr - prev->first_pcr;
} else if (prev != NULL)
/* If we are not contiguous and it's not the first group, the pcr_offset
* will be estimated */
flags = PCR_GROUP_FLAG_ESTIMATED;
group = _new_group (pcr, offset, pcr_offset, flags);
_use_group (pcrtable, group);
_insert_group_after (pcrtable, group, prev);
if (!contiguous)
_reevaluate_group_pcr_offset (pcrtable, group);
}
static inline void
_append_group_values (PCROffsetGroup * group, PCROffset pcroffset)
{
group->last_value++;
/* Resize values if needed */
if (G_UNLIKELY (group->nb_allocated == group->last_value)) {
group->nb_allocated += DEFAULT_ALLOCATED_OFFSET;
group->values =
g_realloc (group->values, group->nb_allocated * sizeof (PCROffset));
}
group->values[group->last_value] = pcroffset;
GST_DEBUG ("First PCR:%" GST_TIME_FORMAT " offset:%" G_GUINT64_FORMAT
" PCR_offset:%" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->first_pcr)),
group->first_offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->pcr_offset)));
GST_DEBUG ("Last PCR: +%" GST_TIME_FORMAT " offset: +%u",
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (pcroffset.pcr)), pcroffset.offset);
}
/* Move last values from current (if any) to the current group
* and reset current.
* Note: This does not set the CLOSED flag (since we have no next
* contiguous group) */
static void
_close_current_group (MpegTSPCR * pcrtable)
{
PCROffsetCurrent *current = pcrtable->current;
PCROffsetGroup *group = current->group;
if (group == NULL)
return;
GST_DEBUG ("Closing group and resetting current");
/* Store last values */
_append_group_values (group, current->pending[current->last]);
memset (current, 0, sizeof (PCROffsetCurrent));
/* And re-evaluate all groups */
}
static void
record_pcr (MpegTSPacketizer2 * packetizer, MpegTSPCR * pcrtable,
guint64 pcr, guint64 offset)
{
PCROffsetCurrent *current = pcrtable->current;
gint64 corpcr, coroffset;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
packetizer->nb_seen_offsets += 1;
/* FIXME : Invert logic later (probability is higher that we have a
* current estimator) */
/* Check for current */
if (G_UNLIKELY (current->group == NULL)) {
PCROffsetGroup *prev = NULL;
GList *tmp;
/* No current estimator. This happens for the initial value, or after
* discont and flushes. Figure out where we need to record this position.
*
* Possible choices:
* 1) No groups at all:
* Create a new group with pcr/offset
* Initialize current to that group
* 2) Entirely within an existing group
* bail out (FIXME: Make this detection faster)
* 3) Not in any group
* Create a new group with pcr/offset at the right position
* Initialize current to that group
*/
GST_DEBUG ("No current window estimator, Checking for group to use");
for (tmp = pcrtable->groups; tmp; tmp = tmp->next) {
PCROffsetGroup *group = (PCROffsetGroup *) tmp->data;
GST_DEBUG ("First PCR:%" GST_TIME_FORMAT " offset:%" G_GUINT64_FORMAT
" PCR_offset:%" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->first_pcr)),
group->first_offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->pcr_offset)));
GST_DEBUG ("Last PCR: +%" GST_TIME_FORMAT " offset: +%u",
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->values[group->
last_value].pcr)),
group->values[group->last_value].offset);
/* Check if before group */
if (offset < group->first_offset) {
GST_DEBUG ("offset is before that group");
break;
}
/* Check if within group */
if (offset <=
(group->values[group->last_value].offset + group->first_offset)) {
GST_DEBUG ("Already observed PCR offset %" G_GUINT64_FORMAT, offset);
return;
}
/* Check if just after group (i.e. continuation of it) */
if (!(group->flags & PCR_GROUP_FLAG_CLOSED) &&
pcr - group->first_pcr - group->values[group->last_value].pcr <=
100 * PCR_MSECOND) {
GST_DEBUG ("Continuation of existing group");
_use_group (pcrtable, group);
return;
}
/* Else after group */
prev = group;
}
_set_current_group (pcrtable, prev, pcr, offset, FALSE);
return;
}
corpcr = pcr - current->first_pcr;
coroffset = offset - current->first_offset;
/* FIXME : Detect if we've gone into the next group !
* FIXME : Close group when that happens */
GST_DEBUG ("first:%d, last:%d, write:%d", current->first, current->last,
current->write);
GST_DEBUG ("First PCR:%" GST_TIME_FORMAT " offset:%" G_GUINT64_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (current->first_pcr)),
current->first_offset);
GST_DEBUG ("Last PCR: +%" GST_TIME_FORMAT " offset: +%u",
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (current->pending[current->last].pcr)),
2014-06-10 12:27:53 +00:00
current->pending[current->last].offset);
GST_DEBUG ("To add (corrected) PCR:%" GST_TIME_FORMAT " offset:%"
G_GINT64_FORMAT, GST_TIME_ARGS (PCRTIME_TO_GSTTIME (corpcr)), coroffset);
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* Do we need to close the current group ? */
/* Check for wrapover/discont */
if (G_UNLIKELY (corpcr < current->pending[current->last].pcr)) {
/* FIXME : ignore very small deltas (< 500ms ?) which are most likely
* stray values */
GST_DEBUG
("PCR smaller than previously observed one, handling discont/wrapover");
/* Take values from current and put them in the current group (closing it) */
/* Create new group with new pcr/offset just after the current group
* and mark it as a wrapover */
/* Initialize current to that group with new values */
_append_group_values (current->group, current->pending[current->last]);
_set_current_group (pcrtable, current->group, pcr, offset, TRUE);
return;
}
/* If PCR diff is greater than 500ms, create new group */
if (G_UNLIKELY (corpcr - current->pending[current->last].pcr >
500 * PCR_MSECOND)) {
GST_DEBUG ("New PCR more than 500ms away, handling discont");
/* Take values from current and put them in the current group (closing it) */
/* Create new group with pcr/offset just after the current group
* and mark it as a discont */
/* Initialize current to that group with new values */
_append_group_values (current->group, current->pending[current->last]);
_set_current_group (pcrtable, current->group, pcr, offset, TRUE);
return;
}
if (G_UNLIKELY (corpcr == current->last_value.pcr)) {
GST_DEBUG ("Ignoring same PCR (stream is drunk)");
return;
}
/* update current window */
current->pending[current->write].pcr = corpcr;
current->pending[current->write].offset = coroffset;
current->last_value = current->pending[current->write];
current->last = (current->last + 1) % PCR_BITRATE_NEEDED;
current->write = (current->write + 1) % PCR_BITRATE_NEEDED;
GST_DEBUG ("first:%d, last:%d, write:%d", current->first, current->last,
current->write);
GST_DEBUG ("First PCR:%" GST_TIME_FORMAT " offset:%" G_GUINT64_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (current->first_pcr)),
current->first_offset);
GST_DEBUG ("Last PCR: +%" GST_TIME_FORMAT " offset: +%u",
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (current->pending[current->last].pcr)),
current->pending[current->last].offset);
/* If we haven't stored enough values, bail out */
if (current->write != current->first) {
GST_DEBUG
("Not enough observations to calculate bitrate (first:%d, last:%d)",
current->first, current->last);
return;
}
/* If we are at least 1s away from reference value AND we have filled our
* window, we can start comparing bitrates */
if (current->pending[current->first].pcr - current->prev.pcr > PCR_SECOND) {
/* Calculate window bitrate */
current->cur_bitrate = gst_util_uint64_scale (PCR_SECOND,
current->pending[current->last].offset -
current->pending[current->first].offset,
current->pending[current->last].pcr -
current->pending[current->first].pcr);
GST_DEBUG ("Current bitrate is now %" G_GUINT64_FORMAT,
current->cur_bitrate);
/* Calculate previous bitrate */
current->prev_bitrate =
gst_util_uint64_scale (PCR_SECOND,
current->pending[current->first].offset - current->prev.offset,
current->pending[current->first].pcr - current->prev.pcr);
GST_DEBUG ("Previous group bitrate now %" G_GUINT64_FORMAT,
current->prev_bitrate);
/* FIXME : Better bitrate changes ? Currently 10% changes */
if (ABSDIFF (current->cur_bitrate,
current->prev_bitrate) * 10 > current->prev_bitrate) {
GST_DEBUG ("Current bitrate changed by more than 10%% (old:%"
G_GUINT64_FORMAT " new:%" G_GUINT64_FORMAT ")", current->prev_bitrate,
current->cur_bitrate);
/* If we detected a change in bitrate, this means that
* d(first - prev) is a different bitrate than d(last - first).
*
* Two conclusions can be made:
* 1) d(first - prev) is a complete bitrate "chain" (values between the
* reference value and first pending value have consistent bitrate).
* 2) next values (from second pending value onwards) will no longer have
* the same bitrate.
*
* The question remains as to how long the new bitrate change is going to
* last for (it might be short or longer term). For this we need to restart
* bitrate estimation.
*
* * We move over first to the last value of group (a new chain ends and
* starts from there)
* * We remember that last group value as our new window reference
* * We restart our window filing from the last observed value
*
* Once our new window is filled we will end up in two different scenarios:
* 1) Either the bitrate change was consistent, and therefore the bitrate
* will have remained constant over at least 2 window length
* 2) The bitrate change was very short (1 window duration) and we will
* close that chain and restart again.
* X) And of course if any discont/gaps/wrapover happen in the meantime they
* will also close the group.
*/
_append_group_values (current->group, current->pending[current->first]);
current->prev = current->pending[current->first];
current->first = current->last;
current->write = (current->first + 1) % PCR_BITRATE_NEEDED;
return;
}
}
/* Update read position */
current->first = (current->first + 1) % PCR_BITRATE_NEEDED;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* convert specified offset into stream time */
GstClockTime
mpegts_packetizer_offset_to_ts (MpegTSPacketizer2 * packetizer,
guint64 offset, guint16 pid)
{
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
PCROffsetGroup *last;
MpegTSPCR *pcrtable;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GList *tmp;
GstClockTime res;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
guint64 lastpcr, lastoffset;
GST_DEBUG ("offset %" G_GUINT64_FORMAT, offset);
if (G_UNLIKELY (!packetizer->calculate_offset))
return GST_CLOCK_TIME_NONE;
if (G_UNLIKELY (packetizer->refoffset == -1))
return GST_CLOCK_TIME_NONE;
if (G_UNLIKELY (offset < packetizer->refoffset))
return GST_CLOCK_TIME_NONE;
pcrtable = get_pcr_table (packetizer, pid);
if (g_list_length (pcrtable->groups) < 1) {
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GST_WARNING ("Not enough observations to return a duration estimate");
return GST_CLOCK_TIME_NONE;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
}
if (g_list_length (pcrtable->groups) > 1) {
GST_LOG ("Using last group");
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* FIXME : Refine this later to use neighbouring groups */
tmp = g_list_last (pcrtable->groups);
last = tmp->data;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
if (G_UNLIKELY (last->flags & PCR_GROUP_FLAG_ESTIMATED))
_reevaluate_group_pcr_offset (pcrtable, last);
/* lastpcr is the full value in PCR from the first first chunk of data */
lastpcr = last->values[last->last_value].pcr + last->pcr_offset;
/* lastoffset is the full offset from the first chunk of data */
lastoffset =
last->values[last->last_value].offset + last->first_offset -
packetizer->refoffset;
} else {
PCROffsetCurrent *current = pcrtable->current;
/* If doing progressive read, use current */
GST_LOG ("Using current group");
lastpcr = current->group->pcr_offset + current->pending[current->last].pcr;
lastoffset = current->first_offset + current->pending[current->last].offset;
}
GST_DEBUG ("lastpcr:%" GST_TIME_FORMAT " lastoffset:%" G_GUINT64_FORMAT
" refoffset:%" G_GUINT64_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (lastpcr)), lastoffset,
packetizer->refoffset);
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* Convert byte difference into time difference (and transformed from 27MHz to 1GHz) */
res =
PCRTIME_TO_GSTTIME (gst_util_uint64_scale (offset - packetizer->refoffset,
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
lastpcr, lastoffset));
GST_DEBUG ("Returning timestamp %" GST_TIME_FORMAT " for offset %"
G_GUINT64_FORMAT, GST_TIME_ARGS (res), offset);
return res;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* Input : local PTS (in GHz units)
* Return : Stream time (in GHz units) */
GstClockTime
mpegts_packetizer_pts_to_ts (MpegTSPacketizer2 * packetizer,
GstClockTime pts, guint16 pcr_pid)
{
GstClockTime res = GST_CLOCK_TIME_NONE;
MpegTSPCR *pcrtable = get_pcr_table (packetizer, pcr_pid);
/* Use clock skew if present */
if (packetizer->calculate_skew
&& GST_CLOCK_TIME_IS_VALID (pcrtable->base_time)) {
GST_DEBUG ("pts %" GST_TIME_FORMAT " base_pcrtime:%" GST_TIME_FORMAT
" base_time:%" GST_TIME_FORMAT " pcroffset:%" GST_TIME_FORMAT,
GST_TIME_ARGS (pts),
GST_TIME_ARGS (pcrtable->base_pcrtime),
GST_TIME_ARGS (pcrtable->base_time),
GST_TIME_ARGS (pcrtable->pcroffset));
res = pts + pcrtable->pcroffset;
/* Don't return anything if we differ too much against last seen PCR */
/* FIXME : Ideally we want to figure out whether we have a wraparound or
* a reset so we can provide actual values.
* That being said, this will only happen for the small interval of time
* where PTS/DTS are wrapping just before we see the first reset/wrap PCR
*/
if (G_UNLIKELY (ABSDIFF (res, pcrtable->last_pcrtime) > 15 * GST_SECOND))
res = GST_CLOCK_TIME_NONE;
else
res += pcrtable->base_time + pcrtable->skew - pcrtable->base_pcrtime;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
} else if (packetizer->calculate_offset && pcrtable->groups) {
gint64 refpcr = G_MAXINT64, refpcroffset;
PCROffsetGroup *group = pcrtable->current->group;
/* Generic calculation:
* Stream Time = PTS - first group PCR + group PCR_offset
*
* In case of wrapover:
* Stream Time = PTS + MAX_PCR - first group PCR + group PCR_offset
* (which we actually do by using first group PCR -= MAX_PCR in order
* to end up with the same calculation as for non-wrapover) */
if (group) {
/* If we have a current group the value is pretty much guaranteed */
GST_DEBUG ("Using current First PCR:%" GST_TIME_FORMAT " offset:%"
G_GUINT64_FORMAT " PCR_offset:%" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->first_pcr)),
group->first_offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->pcr_offset)));
refpcr = group->first_pcr;
refpcroffset = group->pcr_offset;
if (pts < PCRTIME_TO_GSTTIME (refpcr)) {
/* Only apply wrapover if we're certain it is, and avoid
* returning bogus values if it's a PTS/DTS which is *just*
* before the start of the current group
*/
if (PCRTIME_TO_GSTTIME (refpcr) - pts > GST_SECOND) {
pts += PCR_GST_MAX_VALUE;
} else
refpcr = G_MAXINT64;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
} else {
GList *tmp;
/* Otherwise, find a suitable group */
GST_DEBUG ("Find group for current offset %" G_GUINT64_FORMAT,
packetizer->offset);
for (tmp = pcrtable->groups; tmp; tmp = tmp->next) {
PCROffsetGroup *tgroup = tmp->data;
GST_DEBUG ("Trying First PCR:%" GST_TIME_FORMAT " offset:%"
G_GUINT64_FORMAT " PCR_offset:%" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (tgroup->first_pcr)),
tgroup->first_offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (tgroup->pcr_offset)));
/* Gone too far ? */
if (tgroup->first_offset > packetizer->offset) {
/* If there isn't a pending reset, use that value */
if (group) {
GST_DEBUG ("PTS is %" GST_TIME_FORMAT " into group",
GST_TIME_ARGS (pts - PCRTIME_TO_GSTTIME (group->first_pcr)));
}
break;
}
group = tgroup;
/* In that group ? */
if (group->first_offset + group->values[group->last_value].offset >
packetizer->offset) {
GST_DEBUG ("PTS is %" GST_TIME_FORMAT " into group",
GST_TIME_ARGS (pts - PCRTIME_TO_GSTTIME (group->first_pcr)));
break;
}
}
if (group && !(group->flags & PCR_GROUP_FLAG_RESET)) {
GST_DEBUG ("Using group !");
refpcr = group->first_pcr;
refpcroffset = group->pcr_offset;
if (pts < refpcr)
refpcr -= PCR_MAX_VALUE;
}
}
if (refpcr != G_MAXINT64)
res =
pts - PCRTIME_TO_GSTTIME (refpcr) + PCRTIME_TO_GSTTIME (refpcroffset);
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
else
GST_WARNING ("No groups, can't calculate timestamp");
} else
GST_WARNING ("Not enough information to calculate proper timestamp");
GST_DEBUG ("Returning timestamp %" GST_TIME_FORMAT " for pts %"
GST_TIME_FORMAT " pcr_pid:0x%04x", GST_TIME_ARGS (res),
GST_TIME_ARGS (pts), pcr_pid);
return res;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* Stream time to offset */
guint64
mpegts_packetizer_ts_to_offset (MpegTSPacketizer2 * packetizer,
GstClockTime ts, guint16 pcr_pid)
{
MpegTSPCR *pcrtable;
guint64 res;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
PCROffsetGroup *nextgroup = NULL, *prevgroup = NULL;
guint64 querypcr, firstpcr, lastpcr, firstoffset, lastoffset;
PCROffsetCurrent *current;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GList *tmp;
if (!packetizer->calculate_offset)
return -1;
pcrtable = get_pcr_table (packetizer, pcr_pid);
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
if (pcrtable->groups == NULL)
return -1;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
querypcr = GSTTIME_TO_PCRTIME (ts);
GST_DEBUG ("Searching offset for ts %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
/* First check if we're within the current pending group */
current = pcrtable->current;
if (current && current->group && (querypcr >= current->group->pcr_offset) &&
querypcr - current->group->pcr_offset <=
current->pending[current->last].pcr) {
GST_DEBUG ("pcr is in current group");
nextgroup = current->group;
goto calculate_points;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
/* Find the neighbouring groups */
for (tmp = pcrtable->groups; tmp; tmp = tmp->next) {
nextgroup = (PCROffsetGroup *) tmp->data;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GST_DEBUG ("Trying group PCR %" GST_TIME_FORMAT " (offset %"
G_GUINT64_FORMAT " pcr_offset %" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (nextgroup->first_pcr)),
nextgroup->first_offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (nextgroup->pcr_offset)));
/* Check if we've gone too far */
if (nextgroup->pcr_offset > querypcr) {
GST_DEBUG ("pcr is before that group");
break;
}
if (tmp->next == NULL) {
GST_DEBUG ("pcr is beyond last group");
break;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
prevgroup = nextgroup;
/* Maybe it's in this group */
if (nextgroup->values[nextgroup->last_value].pcr +
nextgroup->pcr_offset >= querypcr) {
GST_DEBUG ("pcr is in that group");
break;
}
}
calculate_points:
GST_DEBUG ("nextgroup:%p, prevgroup:%p", nextgroup, prevgroup);
if (nextgroup == prevgroup || prevgroup == NULL) {
/* We use the current group to calculate position:
* * if the PCR is within this group
* * if there is only one group to use for calculation
*/
GST_DEBUG ("In group or after last one");
lastoffset = firstoffset = nextgroup->first_offset;
lastpcr = firstpcr = nextgroup->pcr_offset;
if (current && nextgroup == current->group) {
lastoffset += current->pending[current->last].offset;
lastpcr += current->pending[current->last].pcr;
} else {
lastoffset += nextgroup->values[nextgroup->last_value].offset;
lastpcr += nextgroup->values[nextgroup->last_value].pcr;
}
} else {
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GST_DEBUG ("Between group");
lastoffset = nextgroup->first_offset;
lastpcr = nextgroup->pcr_offset;
firstoffset =
prevgroup->values[prevgroup->last_value].offset +
prevgroup->first_offset;
firstpcr =
prevgroup->values[prevgroup->last_value].pcr + prevgroup->pcr_offset;
}
GST_DEBUG ("Using prev PCR %" G_GUINT64_FORMAT " offset %" G_GUINT64_FORMAT,
firstpcr, firstoffset);
GST_DEBUG ("Using last PCR %" G_GUINT64_FORMAT " offset %" G_GUINT64_FORMAT,
lastpcr, lastoffset);
res = firstoffset;
if (lastpcr != firstpcr)
res += gst_util_uint64_scale (querypcr - firstpcr,
lastoffset - firstoffset, lastpcr - firstpcr);
GST_DEBUG ("Returning offset %" G_GUINT64_FORMAT " for ts %"
GST_TIME_FORMAT, res, GST_TIME_ARGS (ts));
return res;
}
void
mpegts_packetizer_set_reference_offset (MpegTSPacketizer2 * packetizer,
guint64 refoffset)
{
GST_DEBUG ("Setting reference offset to %" G_GUINT64_FORMAT, refoffset);
packetizer->refoffset = refoffset;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
void
mpegts_packetizer_set_current_pcr_offset (MpegTSPacketizer2 * packetizer,
GstClockTime offset, guint16 pcr_pid)
{
guint64 pcr_offset;
gint64 delta;
MpegTSPCR *pcrtable;
PCROffsetGroup *group;
GList *tmp;
gboolean apply = FALSE;
/* fast path */
pcrtable = get_pcr_table (packetizer, pcr_pid);
if (pcrtable == NULL || pcrtable->current->group == NULL)
return;
pcr_offset = GSTTIME_TO_PCRTIME (offset);
/* Pick delta from *first* group */
if (pcrtable->groups)
group = pcrtable->groups->data;
else
group = pcrtable->current->group;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GST_DEBUG ("Current group PCR %" GST_TIME_FORMAT " (offset %"
G_GUINT64_FORMAT " pcr_offset %" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->first_pcr)),
group->first_offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (group->pcr_offset)));
/* Remember the difference between previous initial pcr_offset and
* new initial pcr_offset */
delta = pcr_offset - group->pcr_offset;
if (delta == 0) {
GST_DEBUG ("No shift to apply");
return;
}
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GST_DEBUG ("Shifting groups by %" GST_TIME_FORMAT
" for new initial pcr_offset %" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (delta)), GST_TIME_ARGS (offset));
for (tmp = pcrtable->groups; tmp; tmp = tmp->next) {
PCROffsetGroup *tgroup = (tmp->data);
if (tgroup == group)
apply = TRUE;
if (apply) {
tgroup->pcr_offset += delta;
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
GST_DEBUG ("Update group PCR %" GST_TIME_FORMAT " (offset %"
G_GUINT64_FORMAT " pcr_offset %" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (tgroup->first_pcr)),
tgroup->first_offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (tgroup->pcr_offset)));
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
} else
GST_DEBUG ("Not modifying group PCR %" GST_TIME_FORMAT " (offset %"
G_GUINT64_FORMAT " pcr_offset %" GST_TIME_FORMAT,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (tgroup->first_pcr)),
tgroup->first_offset,
GST_TIME_ARGS (PCRTIME_TO_GSTTIME (tgroup->pcr_offset)));
mpegtsdemux: New PCR<=>Offset estimation code This allows: * Better duration estimation * More accurate PCR location * Overall more accurate running-time location and calculation Location and values of PCR are recorded in groups (PCROffsetGroup) with notable PCR/Offset observations in them (when bitrate changed for example). PCR and offset are stored as 32bit values to reduce memory usage (they are differences against that group's first_{pcr|offset}. Those groups each contain a global PCR offset (pcr_offset) which indicates how far in the stream that group is. Whenever new PCR values are observed, we store them in a sliding window estimator (PCROffsetGroupCurrent). When a reset/wrapover/gap is detected, we close the current group with current values and start a new one (the pcr_offset of that new group is also calculated). When a notable change in bitrate is observed (+/- 10%), we record new values in the current group. This is a compromise between storing all PCR/offset observations and none, while at the same time providing better information for running-time<=>offset calculation in VBR streams. Whenever a new non-contiguous group is start (due to seeking for example) we re-evaluate the pcr_offset of each groups. This allows detecting as quickly as possible PCR wrapover/reset. When wanting to find the offset of a certain running-time, one can iterate the groups by looking at the pcr_offset (which in essence *is* the running-time of that group in the overall stream). Once a group (or neighbouring groups if the running-time is between two groups) is found, once can use the recorded values to find the most accurate offset. Right now this code is only used in pull-mode , but could also be activated later on for any seekable stream, like live timeshift with queue2. Future improvements: * some heuristics to "compress" the stored values in groups so as to keep the memory usage down while still keeping a decent amount of notable points. * After a seek compare expected and obtained PCR/Offset and if the difference is too big, re-calculate position with newly observed values and seek to that more accurate position. Note that this code will *not* provide keyframe-accurate seeking, but will allow a much more accurate PCR/running-time/offset location on any random stream. For past (observed) values it will be as accurate as can be. For future values it will be better than the current situation. Finally the more you seek, the more accurate your positioning will be.
2013-07-26 05:54:30 +00:00
}
}