mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-09 05:38:58 +00:00
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.
374 lines
12 KiB
C
374 lines
12 KiB
C
/*
|
|
* mpegtspacketizer.h -
|
|
* Copyright (C) 2007 Alessandro Decina
|
|
*
|
|
* Authors:
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef GST_MPEGTS_PACKETIZER_H
|
|
#define GST_MPEGTS_PACKETIZER_H
|
|
|
|
#include <gst/gst.h>
|
|
#include <gst/base/gstadapter.h>
|
|
#include <glib.h>
|
|
|
|
#include <gst/mpegts/mpegts.h>
|
|
#include "gstmpegdefs.h"
|
|
|
|
#define MPEGTS_NORMAL_PACKETSIZE 188
|
|
#define MPEGTS_M2TS_PACKETSIZE 192
|
|
#define MPEGTS_DVB_ASI_PACKETSIZE 204
|
|
#define MPEGTS_ATSC_PACKETSIZE 208
|
|
|
|
#define MPEGTS_MIN_PACKETSIZE MPEGTS_NORMAL_PACKETSIZE
|
|
#define MPEGTS_MAX_PACKETSIZE MPEGTS_ATSC_PACKETSIZE
|
|
|
|
#define MPEGTS_AFC_DISCONTINUITY_FLAG 0x80
|
|
#define MPEGTS_AFC_RANDOM_ACCES_FLAGS 0x40
|
|
#define MPEGTS_AFC_ELEMENTARY_STREAM_PRIORITY 0x20
|
|
#define MPEGTS_AFC_PCR_FLAG 0x10
|
|
#define MPEGTS_AFC_OPCR_FLAG 0x08
|
|
#define MPEGTS_AFC_SPLICING_POINT_FLAG 0x04
|
|
#define MPEGTS_AFC_TRANSPORT_PRIVATE_DATA_FLAG 0x02
|
|
#define MPEGTS_AFC_EXTENSION_FLAG 0x01
|
|
|
|
#define MAX_WINDOW 512
|
|
|
|
G_BEGIN_DECLS
|
|
|
|
#define GST_TYPE_MPEGTS_PACKETIZER \
|
|
(mpegts_packetizer_get_type())
|
|
#define GST_MPEGTS_PACKETIZER(obj) \
|
|
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MPEGTS_PACKETIZER,MpegTSPacketizer2))
|
|
#define GST_MPEGTS_PACKETIZER_CLASS(klass) \
|
|
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MPEGTS_PACKETIZER,MpegTSPacketizer2Class))
|
|
#define GST_IS_MPEGTS_PACKETIZER(obj) \
|
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MPEGTS_PACKETIZER))
|
|
#define GST_IS_MPEGTS_PACKETIZER_CLASS(klass) \
|
|
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MPEGTS_PACKETIZER))
|
|
|
|
typedef struct _MpegTSPacketizer2 MpegTSPacketizer2;
|
|
typedef struct _MpegTSPacketizer2Class MpegTSPacketizer2Class;
|
|
|
|
typedef struct
|
|
{
|
|
guint16 pid;
|
|
guint continuity_counter;
|
|
|
|
/* Section data (always newly allocated) */
|
|
guint8 *section_data;
|
|
/* Current offset in section_data */
|
|
guint16 section_offset;
|
|
|
|
/* Values for pending section */
|
|
/* table_id of the pending section_data */
|
|
guint8 table_id;
|
|
guint section_length;
|
|
guint8 version_number;
|
|
guint16 subtable_extension;
|
|
guint8 section_number;
|
|
guint8 last_section_number;
|
|
|
|
GSList *subtables;
|
|
|
|
/* Upstream offset of the data contained in the section */
|
|
guint64 offset;
|
|
} MpegTSPacketizerStream;
|
|
|
|
/* Maximum number of MpegTSPcr
|
|
* 256 should be sufficient for most multiplexes */
|
|
#define MAX_PCR_OBS_CHANNELS 256
|
|
|
|
/* PCR/offset structure */
|
|
typedef struct _PCROffset
|
|
{
|
|
/* PCR value (units: 1/27MHz) */
|
|
guint32 pcr;
|
|
|
|
/* The offset (units: bytes) */
|
|
guint32 offset;
|
|
} PCROffset;
|
|
|
|
/* Flags used on groups */
|
|
enum
|
|
{
|
|
/* Closed groups: There is a contiguous next group */
|
|
PCR_GROUP_FLAG_CLOSED = 1 << 0,
|
|
/* estimated: the pcr_offset has been estimated and is not
|
|
* guaranteed to be 100% accurate */
|
|
PCR_GROUP_FLAG_ESTIMATED = 1 << 1,
|
|
/* reset: there is a pcr reset between the end of this
|
|
* group and the next one.
|
|
* This flag is exclusive with CLOSED. */
|
|
PCR_GROUP_FLAG_RESET = 1 << 2,
|
|
/* reset: there is a pcr wrapover between the end of this
|
|
* group and the next one.
|
|
* This flag is exclusive with CLOSED. */
|
|
PCR_GROUP_FLAG_WRAPOVER = 1 << 3
|
|
};
|
|
|
|
|
|
|
|
/* PCROffsetGroup: A group of PCR observations.
|
|
* All values in a group have got the same reference pcr and
|
|
* byte offset (first_pcr/first_offset).
|
|
*/
|
|
#define DEFAULT_ALLOCATED_OFFSET 16
|
|
typedef struct _PCROffsetGroup
|
|
{
|
|
/* Flags (see PCR_GROUP_FLAG_* above) */
|
|
guint flags;
|
|
|
|
/* First raw PCR of this group. Units: 1/27MHz.
|
|
* All values[].pcr are differences against first_pcr */
|
|
guint64 first_pcr;
|
|
/* Offset of this group in bytes.
|
|
* All values[].offset are differences against first_offset */
|
|
guint64 first_offset;
|
|
|
|
/* Dynamically allocated table of PCROffset */
|
|
PCROffset *values;
|
|
/* number of PCROffset allocated in values */
|
|
guint nb_allocated;
|
|
/* number of *actual* PCROffset contained in values */
|
|
guint last_value;
|
|
|
|
/* Offset since the very first PCR value observed in the whole
|
|
* stream. Units: 1/27MHz.
|
|
* This will take into account gaps/wraparounds/resets/... and is
|
|
* used to determine running times.
|
|
* The value is only guaranteed to be 100% accurate if the group
|
|
* does not have the ESTIMATED flag.
|
|
* If the value is estimated, the pcr_offset shall be recalculated
|
|
* (based on previous groups) whenever it is accessed.
|
|
*/
|
|
guint64 pcr_offset;
|
|
|
|
/* FIXME : Cache group bitrate ? */
|
|
} PCROffsetGroup;
|
|
|
|
/* Number of PCRs needed before bitrate estimation can start */
|
|
/* Note: the reason we use 10 is because PCR should normally be
|
|
* received at least every 100ms so this gives us close to
|
|
* a 1s moving window to calculate bitrate */
|
|
#define PCR_BITRATE_NEEDED 10
|
|
|
|
/* PCROffsetCurrent: The PCR/Offset window iterator
|
|
* This is used to estimate/observe incoming PCR/offset values
|
|
* Points to a group (which it is filling) */
|
|
typedef struct _PCROffsetCurrent
|
|
{
|
|
/* The PCROffsetGroup we are filling.
|
|
* If NULL, a group needs to be identified */
|
|
PCROffsetGroup *group;
|
|
|
|
/* Table of pending values we are iterating over */
|
|
PCROffset pending[PCR_BITRATE_NEEDED];
|
|
|
|
/* base offset/pcr from the group */
|
|
guint64 first_pcr;
|
|
guint64 first_offset;
|
|
|
|
/* The previous reference PCROffset
|
|
* This corresponds to the last entry of the group we are filling
|
|
* and is used to calculate prev_bitrate */
|
|
PCROffset prev;
|
|
|
|
/* The last PCROffset in pending[] */
|
|
PCROffset last_value;
|
|
|
|
/* Location of first pending PCR/offset observation in pending */
|
|
guint first;
|
|
/* Location of last pending PCR/offset observation in pending */
|
|
guint last;
|
|
/* Location of next write in pending */
|
|
guint write;
|
|
|
|
/* bitrate is always in bytes per second */
|
|
|
|
/* cur_bitrate is the bitrate of the pending values: d(last-first) */
|
|
guint64 cur_bitrate;
|
|
|
|
/* prev_bitrate is the bitrate between reference PCROffset
|
|
* and the first pending value. Used to detect changes
|
|
* in bitrate */
|
|
guint64 prev_bitrate;
|
|
} PCROffsetCurrent;
|
|
|
|
typedef struct _MpegTSPCR
|
|
{
|
|
guint16 pid;
|
|
|
|
/* Following variables are only active/used when
|
|
* calculate_skew is TRUE */
|
|
GstClockTime base_time;
|
|
GstClockTime base_pcrtime;
|
|
GstClockTime prev_out_time;
|
|
GstClockTime prev_in_time;
|
|
GstClockTime last_pcrtime;
|
|
gint64 window[MAX_WINDOW];
|
|
guint window_pos;
|
|
guint window_size;
|
|
gboolean window_filling;
|
|
gint64 window_min;
|
|
gint64 skew;
|
|
gint64 prev_send_diff;
|
|
|
|
/* Offset to apply to PCR to handle wraparounds */
|
|
guint64 pcroffset;
|
|
|
|
/* Used for bitrate calculation */
|
|
/* List of PCR/offset observations */
|
|
GList *groups;
|
|
|
|
/* Current PCR/offset observations (used to update pcroffsets) */
|
|
PCROffsetCurrent *current;
|
|
} MpegTSPCR;
|
|
|
|
struct _MpegTSPacketizer2 {
|
|
GObject parent;
|
|
|
|
GstAdapter *adapter;
|
|
/* streams hashed by pid */
|
|
/* FIXME : be more memory efficient (see how it's done in mpegtsbase) */
|
|
MpegTSPacketizerStream **streams;
|
|
gboolean disposed;
|
|
guint16 packet_size;
|
|
|
|
/* current offset of the tip of the adapter */
|
|
guint64 offset;
|
|
gboolean empty;
|
|
|
|
/* clock skew calculation */
|
|
gboolean calculate_skew;
|
|
|
|
/* offset/bitrate calculator */
|
|
gboolean calculate_offset;
|
|
|
|
/* Shortcuts for adapter usage */
|
|
guint8 *map_data;
|
|
gsize map_offset;
|
|
gsize map_size;
|
|
gboolean need_sync;
|
|
|
|
/* Reference offset */
|
|
guint64 refoffset;
|
|
|
|
/* Number of seen pcr/offset observations (FIXME : kill later) */
|
|
guint nb_seen_offsets;
|
|
|
|
/* Last inputted timestamp */
|
|
GstClockTime last_in_time;
|
|
|
|
/* offset to observations table */
|
|
guint8 pcrtablelut[0x2000];
|
|
MpegTSPCR *observations[MAX_PCR_OBS_CHANNELS];
|
|
guint8 lastobsid;
|
|
};
|
|
|
|
struct _MpegTSPacketizer2Class {
|
|
GObjectClass object_class;
|
|
};
|
|
|
|
#define FLAGS_SCRAMBLED(f) (f & 0xc0)
|
|
#define FLAGS_HAS_AFC(f) (f & 0x20)
|
|
#define FLAGS_HAS_PAYLOAD(f) (f & 0x10)
|
|
#define FLAGS_CONTINUITY_COUNTER(f) (f & 0x0f)
|
|
|
|
typedef struct
|
|
{
|
|
gint16 pid;
|
|
guint8 payload_unit_start_indicator;
|
|
guint8 scram_afc_cc;
|
|
guint8 *payload;
|
|
|
|
guint8 *data_start;
|
|
guint8 *data_end;
|
|
guint8 *data;
|
|
|
|
guint8 afc_flags;
|
|
guint64 pcr;
|
|
guint64 offset;
|
|
} MpegTSPacketizerPacket;
|
|
|
|
typedef struct
|
|
{
|
|
guint8 table_id;
|
|
/* the spec says sub_table_extension is the fourth and fifth byte of a
|
|
* section when the section_syntax_indicator is set to a value of "1". If
|
|
* section_syntax_indicator is 0, sub_table_extension will be set to 0 */
|
|
guint16 subtable_extension;
|
|
guint8 version_number;
|
|
guint8 last_section_number;
|
|
/* table of bits, whether the section was seen or not.
|
|
* Use MPEGTS_BIT_* macros to check */
|
|
/* Size is 32, because there's a maximum of 256 (32*8) section_number */
|
|
guint8 seen_section[32];
|
|
} MpegTSPacketizerStreamSubtable;
|
|
|
|
#define MPEGTS_BIT_SET(field, offs) ((field)[(offs) >> 3] |= (1 << ((offs) & 0x7)))
|
|
#define MPEGTS_BIT_UNSET(field, offs) ((field)[(offs) >> 3] &= ~(1 << ((offs) & 0x7)))
|
|
#define MPEGTS_BIT_IS_SET(field, offs) ((field)[(offs) >> 3] & (1 << ((offs) & 0x7)))
|
|
|
|
typedef enum {
|
|
PACKET_BAD = FALSE,
|
|
PACKET_OK = TRUE,
|
|
PACKET_NEED_MORE
|
|
} MpegTSPacketizerPacketReturn;
|
|
|
|
G_GNUC_INTERNAL GType mpegts_packetizer_get_type(void);
|
|
|
|
G_GNUC_INTERNAL MpegTSPacketizer2 *mpegts_packetizer_new (void);
|
|
G_GNUC_INTERNAL void mpegts_packetizer_clear (MpegTSPacketizer2 *packetizer);
|
|
G_GNUC_INTERNAL void mpegts_packetizer_flush (MpegTSPacketizer2 *packetizer, gboolean hard);
|
|
G_GNUC_INTERNAL void mpegts_packetizer_push (MpegTSPacketizer2 *packetizer, GstBuffer *buffer);
|
|
G_GNUC_INTERNAL gboolean mpegts_packetizer_has_packets (MpegTSPacketizer2 *packetizer);
|
|
G_GNUC_INTERNAL MpegTSPacketizerPacketReturn mpegts_packetizer_next_packet (MpegTSPacketizer2 *packetizer,
|
|
MpegTSPacketizerPacket *packet);
|
|
G_GNUC_INTERNAL MpegTSPacketizerPacketReturn
|
|
mpegts_packetizer_process_next_packet(MpegTSPacketizer2 * packetizer);
|
|
G_GNUC_INTERNAL void mpegts_packetizer_clear_packet (MpegTSPacketizer2 *packetizer,
|
|
MpegTSPacketizerPacket *packet);
|
|
G_GNUC_INTERNAL void mpegts_packetizer_remove_stream(MpegTSPacketizer2 *packetizer,
|
|
gint16 pid);
|
|
|
|
G_GNUC_INTERNAL GstMpegTsSection *mpegts_packetizer_push_section (MpegTSPacketizer2 *packetzer,
|
|
MpegTSPacketizerPacket *packet, GList **remaining);
|
|
|
|
/* Only valid if calculate_offset is TRUE */
|
|
G_GNUC_INTERNAL guint mpegts_packetizer_get_seen_pcr (MpegTSPacketizer2 *packetizer);
|
|
|
|
G_GNUC_INTERNAL GstClockTime
|
|
mpegts_packetizer_offset_to_ts (MpegTSPacketizer2 * packetizer,
|
|
guint64 offset, guint16 pcr_pid);
|
|
G_GNUC_INTERNAL guint64
|
|
mpegts_packetizer_ts_to_offset (MpegTSPacketizer2 * packetizer,
|
|
GstClockTime ts, guint16 pcr_pid);
|
|
G_GNUC_INTERNAL GstClockTime
|
|
mpegts_packetizer_pts_to_ts (MpegTSPacketizer2 * packetizer,
|
|
GstClockTime pts, guint16 pcr_pid);
|
|
G_GNUC_INTERNAL void
|
|
mpegts_packetizer_set_current_pcr_offset (MpegTSPacketizer2 * packetizer,
|
|
GstClockTime offset, guint16 pcr_pid);
|
|
G_GNUC_INTERNAL void
|
|
mpegts_packetizer_set_reference_offset (MpegTSPacketizer2 * packetizer,
|
|
guint64 refoffset);
|
|
G_END_DECLS
|
|
|
|
#endif /* GST_MPEGTS_PACKETIZER_H */
|